Cells can have their values set when ranging over them

plus more replacement of y, x loops with cell ranges, adding static
neighbor coords.
This commit is contained in:
Dan Buch 2012-12-11 22:30:34 -05:00
parent 078efbe057
commit 8ba87ccc81
3 changed files with 203 additions and 99 deletions

View File

@ -377,14 +377,78 @@ func TestStateCanMutate(t *testing.T) {
return return
} }
curCksum := state.Checksum() curCksum, err := state.Checksum()
if err != nil {
t.Error(err)
return
}
err = state.Mutate() err = state.Mutate()
if err != nil { if err != nil {
t.Error(err) t.Error(err)
return return
} }
if curCksum == state.Checksum() { newCksum, err := state.Checksum()
t.Errorf("Checksum did not change! %v", curCksum) if err != nil {
t.Error(err)
return
}
if curCksum == newCksum {
t.Errorf("Checksum did not change! %v", newCksum)
}
}
func TestStateProvidesCellIteratorChannel(t *testing.T) {
state, err := newTestGameState()
if err != nil {
t.Error(err)
return
}
cells, err := state.Cells()
if err != nil {
t.Error(err)
return
}
cellCount := 0
for _ = range cells {
cellCount++
}
if cellCount != 256 {
t.Fail()
}
}
func TestGameStateCellsCanHaveTheirValueSet(t *testing.T) {
state, err := newTestGameState()
if err != nil {
t.Error(err)
return
}
cells, err := state.Cells()
if err != nil {
t.Error(err)
return
}
for cell := range cells {
cell.SetValue(1)
}
cells, err = state.Cells()
if err != nil {
t.Error(err)
return
}
for cell := range cells {
if cell.Value != 1 {
t.Fail()
}
} }
} }

View File

@ -40,7 +40,12 @@ func main() {
checksums := make([]string, 4) checksums := make([]string, 4)
checksums[0], checksums[1], checksums[2], checksums[3] = "foo", "bar", "baz", "qwx" checksums[0], checksums[1], checksums[2], checksums[3] = "foo", "bar", "baz", "qwx"
pushChecksum(game.Checksum(), checksums) ck, err := game.Checksum()
if err != nil {
fmt.Fprintf(os.Stderr, "NAY NAY: %v\n", err)
os.Exit(3)
}
pushChecksum(ck, checksums)
ticks := time.Tick(time.Duration(*sleepMs) * time.Millisecond) ticks := time.Tick(time.Duration(*sleepMs) * time.Millisecond)
generations := 0 generations := 0
@ -50,7 +55,11 @@ func main() {
game.EvaluateGeneration() game.EvaluateGeneration()
curChecksum := game.Checksum() curChecksum, err := game.Checksum()
if err != nil {
fmt.Fprintf(os.Stderr, "Failed to calculate current game checksum: %v\n", err)
os.Exit(4)
}
if checksums[0] == curChecksum || checksums[1] == curChecksum { if checksums[0] == curChecksum || checksums[1] == curChecksum {
fmt.Println("Stasis!") fmt.Println("Stasis!")

View File

@ -15,10 +15,28 @@ const (
LIVE_CELL = " " LIVE_CELL = " "
) )
var (
neighbors = []neighbor{
neighbor{-1, -1},
neighbor{-1, 0},
neighbor{-1, 1},
neighbor{0, -1},
neighbor{0, 1},
neighbor{1, -1},
neighbor{1, 0},
neighbor{1, 1},
}
)
func init() { func init() {
rand.Seed(time.Now().UTC().UnixNano()) rand.Seed(time.Now().UTC().UnixNano())
} }
type neighbor struct {
X int
Y int
}
type GameOfLife struct { type GameOfLife struct {
State *GameState State *GameState
} }
@ -27,6 +45,7 @@ type GameStateCell struct {
Value int Value int
X int X int
Y int Y int
cols []int
} }
type gameGridRow struct { type gameGridRow struct {
@ -124,31 +143,43 @@ func (state *GameState) Import(other *GameState) (err error) {
return nil return nil
} }
func (state *GameState) Cells(cells chan *GameStateCell) error { func (state *GameState) Cells() (<-chan *GameStateCell, error) {
height := state.Height() height := state.Height()
width := state.Width() width := state.Width()
if height < 0 || width < 0 { if height < 0 || width < 0 {
return errors.New("state has invalid dimensions!") return nil, errors.New("state has invalid dimensions!")
} }
defer close(cells) cells := make(chan *GameStateCell)
go func(state *GameState, c chan *GameStateCell) {
defer close(c)
height := state.Height()
width := state.Width()
for y := 0; y < height; y++ { for y := 0; y < height; y++ {
row, err := state.GetRow(y)
if err != nil {
panic(err)
}
for x := 0; x < width; x++ { for x := 0; x < width; x++ {
value, err := state.Get(x, y) value, err := state.Get(x, y)
if err != nil { if err != nil {
panic(err) panic(err)
} }
cells <- &GameStateCell{ c <- &GameStateCell{
Value: value, Value: value,
X: x, X: x,
Y: y, Y: y,
cols: row.Cols,
} }
} }
} }
}(state, cells)
return nil return (<-chan *GameStateCell)(cells), nil
} }
func (game *GameOfLife) Mutate() error { func (game *GameOfLife) Mutate() error {
@ -169,20 +200,21 @@ func (state *GameState) Mutate() error {
return nil return nil
} }
func (game *GameOfLife) Checksum() string { func (game *GameOfLife) Checksum() (string, error) {
return game.State.Checksum() return game.State.Checksum()
} }
func (state *GameState) Checksum() string { func (state *GameState) Checksum() (string, error) {
ck := sha1.New() ck := sha1.New()
cells := make(chan *GameStateCell) cells, err := state.Cells()
if err != nil {
go state.Cells(cells) return "", err
}
for cell := range cells { for cell := range cells {
fmt.Fprintf(ck, "%v", cell.Value) fmt.Fprintf(ck, "%v", cell.Value)
} }
return fmt.Sprintf("%x", ck.Sum(nil)) return fmt.Sprintf("%x", ck.Sum(nil)), nil
} }
func NewGameOfLife(height, width int) *GameOfLife { func NewGameOfLife(height, width int) *GameOfLife {
@ -197,14 +229,15 @@ func (game *GameOfLife) EvaluateGeneration() error {
genScore := NewGenerationScoreCard(height, width) genScore := NewGenerationScoreCard(height, width)
genScore.Calculate(game.State) genScore.Calculate(game.State)
for y := 0; y < height; y++ { cells, err := genScore.Cells()
for x := 0; x < width; x++ {
score, err := genScore.Get(x, y)
if err != nil { if err != nil {
return err return err
} }
curState, err := game.State.Get(x, y) for cell := range cells {
score := cell.Value
curState, err := game.State.Get(cell.X, cell.Y)
if err != nil { if err != nil {
return err return err
} }
@ -212,7 +245,7 @@ func (game *GameOfLife) EvaluateGeneration() error {
if curState == 1 { if curState == 1 {
if score < 2 { if score < 2 {
err = game.State.Deaden(x, y) err = game.State.Deaden(cell.X, cell.Y)
if err != nil { if err != nil {
return err return err
} }
@ -224,7 +257,7 @@ func (game *GameOfLife) EvaluateGeneration() error {
} }
if score > 3 { if score > 3 {
err = game.State.Deaden(x, y) err = game.State.Deaden(cell.X, cell.Y)
if err != nil { if err != nil {
return err return err
} }
@ -233,7 +266,7 @@ func (game *GameOfLife) EvaluateGeneration() error {
} else if curState == 0 { } else if curState == 0 {
if score == 3 { if score == 3 {
err = game.State.Enliven(x, y) err = game.State.Enliven(cell.X, cell.Y)
if err != nil { if err != nil {
return err return err
} }
@ -243,7 +276,6 @@ func (game *GameOfLife) EvaluateGeneration() error {
} }
} }
} }
}
return nil return nil
} }
@ -264,13 +296,13 @@ func (game *GameOfLife) ImportRandomState() error {
randState := NewGameState(height, width) randState := NewGameState(height, width)
for y := 0; y < height; y++ { cells, err := randState.Cells()
for x := 0; x < width; x++ {
err := randState.Set(x, y, rand.Intn(2))
if err != nil { if err != nil {
return err return err
} }
}
for cell := range cells {
cell.SetValue(rand.Intn(2))
} }
return game.ImportState(randState) return game.ImportState(randState)
@ -280,6 +312,10 @@ func (game *GameOfLife) String() string {
return fmt.Sprintf("%s\n", game.State) return fmt.Sprintf("%s\n", game.State)
} }
func (cell *GameStateCell) SetValue(value int) {
cell.cols[cell.X] = value
}
func (state *GameState) String() string { func (state *GameState) String() string {
var rows []string var rows []string
@ -319,19 +355,20 @@ func NewGenerationScoreCard(height, width int) *GenerationScoreCard {
} }
func (genScore *GenerationScoreCard) Calculate(state *GameState) error { func (genScore *GenerationScoreCard) Calculate(state *GameState) error {
stateHeight, stateWidth := state.Height(), state.Width() stateWidth := state.Width()
for y := 0; y < stateHeight; y++ { stateCells, err := state.Cells()
for x := 0; x < stateWidth; x++ {
value, err := state.Get(x, y)
if err != nil { if err != nil {
return err return err
} }
if value == 1 { for stateCell := range stateCells {
for yOffset := -1; yOffset < 2; yOffset++ { if stateCell.Value == 0 {
for xOffset := -1; xOffset < 2; xOffset++ { continue
xTarget := x + xOffset }
yTarget := y + yOffset
for _, neighbor := range neighbors {
xTarget := stateCell.X + neighbor.X
yTarget := stateCell.Y + neighbor.Y
if xTarget < 0 || yTarget < 0 { if xTarget < 0 || yTarget < 0 {
continue continue
@ -341,23 +378,17 @@ func (genScore *GenerationScoreCard) Calculate(state *GameState) error {
continue continue
} }
if xTarget == x && yTarget == y { curScore, err := genScore.Get(xTarget, yTarget)
if err != nil {
continue continue
} }
curScore, err := genScore.Get(xTarget, yTarget)
if err != nil {
continue // assume out of bounds, which is stinky
}
err = genScore.Set(xTarget, yTarget, curScore+1) err = genScore.Set(xTarget, yTarget, curScore+1)
if err != nil { if err != nil {
return err return err
} }
} }
} }
}
}
}
return nil return nil
} }