feat: Finished and added tests for palette implementation
This commit is contained in:
parent
d1d35c2479
commit
0d6ff2cf96
@ -5,47 +5,82 @@ package world
|
||||
//
|
||||
// This palette is unique to each section, and allows ranges of blocks to be
|
||||
// changed in constant time, with the downside of having to "compact" the palette
|
||||
type SectionPalette []BlockID
|
||||
type SectionPalette struct {
|
||||
ids map[PaletteIndex]BlockID
|
||||
}
|
||||
|
||||
// `Compact` removes all duplicate states from a palette and returns a new palette
|
||||
func (p SectionPalette) Compact() SectionPalette {
|
||||
ids := make(map[BlockID]bool)
|
||||
func NewSectionPalette() SectionPalette {
|
||||
return SectionPalette{
|
||||
ids: make(map[PaletteIndex]BlockID),
|
||||
}
|
||||
}
|
||||
|
||||
// `Compact` removes all duplicate states from a palette and returns if the
|
||||
// palette was modified
|
||||
func (p *SectionPalette) Compact() bool {
|
||||
newIds := make(map[BlockID]PaletteIndex)
|
||||
|
||||
var wasResized bool
|
||||
|
||||
// Filter out the duplicate block ids
|
||||
for _, blockId := range p {
|
||||
ids[blockId] = true
|
||||
for index, blockId := range p.ids {
|
||||
newIds[blockId] = index
|
||||
}
|
||||
|
||||
var np SectionPalette
|
||||
|
||||
for blockId := range ids {
|
||||
np = append(np, blockId)
|
||||
// If there was not a resize, return instantly
|
||||
if len(newIds) != len(p.ids) {
|
||||
wasResized = true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
|
||||
return np
|
||||
ids := make(map[PaletteIndex]BlockID)
|
||||
|
||||
for blockId, index := range newIds {
|
||||
ids[index] = blockId
|
||||
}
|
||||
|
||||
p.ids = ids
|
||||
|
||||
return wasResized
|
||||
}
|
||||
|
||||
// `IndexFor` returns the palette index for a specified block id
|
||||
//
|
||||
// If the block id does not exist, it is placed in the palette and the index
|
||||
// is returned
|
||||
func (p SectionPalette) IndexFor(state BlockID) PaletteIndex {
|
||||
func (p *SectionPalette) IndexFor(state BlockID) PaletteIndex {
|
||||
var maxIndex PaletteIndex
|
||||
|
||||
// If the state is already in the palette, return it
|
||||
for index, blockId := range p {
|
||||
for index, blockId := range p.ids {
|
||||
if index > maxIndex {
|
||||
maxIndex = index
|
||||
}
|
||||
|
||||
if state == blockId {
|
||||
return PaletteIndex(index)
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, insert it into the palette and return the index
|
||||
p = append(p, state)
|
||||
p.ids[maxIndex+1] = state
|
||||
|
||||
return PaletteIndex(len(p) - 1)
|
||||
return maxIndex + 1
|
||||
}
|
||||
|
||||
// `State` returns the state for a palette's index
|
||||
func (p SectionPalette) State(index PaletteIndex) BlockID {
|
||||
return p[index]
|
||||
return p.ids[index]
|
||||
}
|
||||
|
||||
// `ReplaceIndex` replaces the block state at the given palette' index
|
||||
//
|
||||
// This function is used to quickly zero a section with a given block state in
|
||||
// constant time, and should not be used for any other purpose. Otherwise, set
|
||||
// the block state within the section to the result of `IndexFor`.
|
||||
func (p *SectionPalette) ReplaceIndex(index PaletteIndex, state BlockID) {
|
||||
p.ids[index] = state
|
||||
}
|
||||
|
||||
type PaletteIndex byte
|
||||
|
64
world/palette_test.go
Normal file
64
world/palette_test.go
Normal file
@ -0,0 +1,64 @@
|
||||
package world
|
||||
|
||||
import (
|
||||
"runtime/debug"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func newPaletteWith(ids []BlockID) *SectionPalette {
|
||||
p := NewSectionPalette()
|
||||
|
||||
for _, id := range ids {
|
||||
p.IndexFor(id)
|
||||
}
|
||||
|
||||
return &p
|
||||
}
|
||||
|
||||
func checkPaletteEqual(t *testing.T, p *SectionPalette, index PaletteIndex, expected BlockID) {
|
||||
if p.State(index) != expected {
|
||||
debug.PrintStack()
|
||||
t.Fatalf("Expected to get state %v at index %v, got %v", expected, index, p.State(index))
|
||||
}
|
||||
}
|
||||
|
||||
func TestInsertPalette(t *testing.T) {
|
||||
p := NewSectionPalette()
|
||||
|
||||
ids := []BlockID{Empty, Generic}
|
||||
|
||||
for _, id := range ids {
|
||||
index := p.IndexFor(id)
|
||||
if id != p.State(index) {
|
||||
t.Fatalf("Fetching index for id %v: got index %v which returned %v", id, index, p.State(index))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestReplaceCompactPalettte(t *testing.T) {
|
||||
p := newPaletteWith([]BlockID{Empty, Generic})
|
||||
|
||||
zeroIndex := p.IndexFor(Empty)
|
||||
checkPaletteEqual(t, p, zeroIndex, Empty)
|
||||
|
||||
// Zero out the chunk
|
||||
genIndex := p.IndexFor(Generic)
|
||||
checkPaletteEqual(t, p, genIndex, Generic)
|
||||
p.ReplaceIndex(genIndex, Empty)
|
||||
|
||||
// Check that everything now returns zero
|
||||
checkPaletteEqual(t, p, zeroIndex, Empty)
|
||||
checkPaletteEqual(t, p, genIndex, Empty)
|
||||
|
||||
// Test for compaction
|
||||
wasCompacted := p.Compact()
|
||||
if !wasCompacted {
|
||||
t.Fatalf("Palette should have been compacted")
|
||||
}
|
||||
|
||||
// Test to see that we don't compact again
|
||||
wasCompacted = p.Compact()
|
||||
if wasCompacted {
|
||||
t.Fatalf("Palette should have not been compacted again")
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user