From 8ba87ccc81dd773a4e835366df6342257e9855b7 Mon Sep 17 00:00:00 2001 From: Dan Buch Date: Tue, 11 Dec 2012 22:30:34 -0500 Subject: [PATCH] Cells can have their values set when ranging over them plus more replacement of y, x loops with cell ranges, adding static neighbor coords. --- conway/conway_test.go | 70 ++++++++- conway/conways-game-of-life/main.go | 13 +- conway/game_of_life.go | 219 ++++++++++++++++------------ 3 files changed, 203 insertions(+), 99 deletions(-) diff --git a/conway/conway_test.go b/conway/conway_test.go index 8d2848d..d6c3e89 100644 --- a/conway/conway_test.go +++ b/conway/conway_test.go @@ -377,14 +377,78 @@ func TestStateCanMutate(t *testing.T) { return } - curCksum := state.Checksum() + curCksum, err := state.Checksum() + if err != nil { + t.Error(err) + return + } + err = state.Mutate() if err != nil { t.Error(err) return } - if curCksum == state.Checksum() { - t.Errorf("Checksum did not change! %v", curCksum) + newCksum, err := state.Checksum() + 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() + } } } diff --git a/conway/conways-game-of-life/main.go b/conway/conways-game-of-life/main.go index 8eac099..f8bb76a 100644 --- a/conway/conways-game-of-life/main.go +++ b/conway/conways-game-of-life/main.go @@ -40,7 +40,12 @@ func main() { checksums := make([]string, 4) 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) generations := 0 @@ -50,7 +55,11 @@ func main() { 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 { fmt.Println("Stasis!") diff --git a/conway/game_of_life.go b/conway/game_of_life.go index e477141..d41b4da 100644 --- a/conway/game_of_life.go +++ b/conway/game_of_life.go @@ -15,10 +15,28 @@ const ( 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() { rand.Seed(time.Now().UTC().UnixNano()) } +type neighbor struct { + X int + Y int +} + type GameOfLife struct { State *GameState } @@ -27,6 +45,7 @@ type GameStateCell struct { Value int X int Y int + cols []int } type gameGridRow struct { @@ -124,31 +143,43 @@ func (state *GameState) Import(other *GameState) (err error) { return nil } -func (state *GameState) Cells(cells chan *GameStateCell) error { +func (state *GameState) Cells() (<-chan *GameStateCell, error) { height := state.Height() width := state.Width() 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) - for y := 0; y < height; y++ { - for x := 0; x < width; x++ { - value, err := state.Get(x, y) + go func(state *GameState, c chan *GameStateCell) { + defer close(c) + height := state.Height() + width := state.Width() + + for y := 0; y < height; y++ { + row, err := state.GetRow(y) if err != nil { panic(err) } - cells <- &GameStateCell{ - Value: value, - X: x, - Y: y, + + for x := 0; x < width; x++ { + value, err := state.Get(x, y) + if err != nil { + panic(err) + } + c <- &GameStateCell{ + Value: value, + X: x, + Y: y, + cols: row.Cols, + } } } - } + }(state, cells) - return nil + return (<-chan *GameStateCell)(cells), nil } func (game *GameOfLife) Mutate() error { @@ -169,20 +200,21 @@ func (state *GameState) Mutate() error { return nil } -func (game *GameOfLife) Checksum() string { +func (game *GameOfLife) Checksum() (string, error) { return game.State.Checksum() } -func (state *GameState) Checksum() string { +func (state *GameState) Checksum() (string, error) { ck := sha1.New() - cells := make(chan *GameStateCell) - - go state.Cells(cells) + cells, err := state.Cells() + if err != nil { + return "", err + } for cell := range cells { 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 { @@ -197,50 +229,50 @@ func (game *GameOfLife) EvaluateGeneration() error { genScore := NewGenerationScoreCard(height, width) genScore.Calculate(game.State) - for y := 0; y < height; y++ { - for x := 0; x < width; x++ { - score, err := genScore.Get(x, y) - if err != nil { - return err - } + cells, err := genScore.Cells() + if err != nil { + return err + } - curState, err := game.State.Get(x, y) - if err != nil { - return err - } + for cell := range cells { + score := cell.Value - if curState == 1 { + curState, err := game.State.Get(cell.X, cell.Y) + if err != nil { + return err + } - if score < 2 { - err = game.State.Deaden(x, y) - if err != nil { - return err - } - continue - } + if curState == 1 { - if score == 2 || score == 3 { - continue + if score < 2 { + err = game.State.Deaden(cell.X, cell.Y) + if err != nil { + return err } + continue + } - if score > 3 { - err = game.State.Deaden(x, y) - if err != nil { - return err - } - continue + if score == 2 || score == 3 { + continue + } + + if score > 3 { + err = game.State.Deaden(cell.X, cell.Y) + if err != nil { + return err } - } else if curState == 0 { - - if score == 3 { - err = game.State.Enliven(x, y) - if err != nil { - return err - } - continue - } else { - continue + continue + } + } else if curState == 0 { + + if score == 3 { + err = game.State.Enliven(cell.X, cell.Y) + if err != nil { + return err } + continue + } else { + continue } } } @@ -264,13 +296,13 @@ func (game *GameOfLife) ImportRandomState() error { randState := NewGameState(height, width) - for y := 0; y < height; y++ { - for x := 0; x < width; x++ { - err := randState.Set(x, y, rand.Intn(2)) - if err != nil { - return err - } - } + cells, err := randState.Cells() + if err != nil { + return err + } + + for cell := range cells { + cell.SetValue(rand.Intn(2)) } return game.ImportState(randState) @@ -280,6 +312,10 @@ func (game *GameOfLife) String() string { return fmt.Sprintf("%s\n", game.State) } +func (cell *GameStateCell) SetValue(value int) { + cell.cols[cell.X] = value +} + func (state *GameState) String() string { var rows []string @@ -319,42 +355,37 @@ func NewGenerationScoreCard(height, width int) *GenerationScoreCard { } func (genScore *GenerationScoreCard) Calculate(state *GameState) error { - stateHeight, stateWidth := state.Height(), state.Width() - for y := 0; y < stateHeight; y++ { - for x := 0; x < stateWidth; x++ { - value, err := state.Get(x, y) + stateWidth := state.Width() + stateCells, err := state.Cells() + if err != nil { + return err + } + + for stateCell := range stateCells { + if stateCell.Value == 0 { + continue + } + + for _, neighbor := range neighbors { + xTarget := stateCell.X + neighbor.X + yTarget := stateCell.Y + neighbor.Y + + if xTarget < 0 || yTarget < 0 { + continue + } + + if xTarget+1 > stateWidth || yTarget+1 > stateWidth { + continue + } + + curScore, err := genScore.Get(xTarget, yTarget) if err != nil { - return err + continue } - if value == 1 { - for yOffset := -1; yOffset < 2; yOffset++ { - for xOffset := -1; xOffset < 2; xOffset++ { - xTarget := x + xOffset - yTarget := y + yOffset - - if xTarget < 0 || yTarget < 0 { - continue - } - - if xTarget+1 > stateWidth || yTarget+1 > stateWidth { - continue - } - - if xTarget == x && yTarget == y { - 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) - if err != nil { - return err - } - } - } + err = genScore.Set(xTarget, yTarget, curScore+1) + if err != nil { + return err } } }