diff --git a/conway/go/conway_test.go b/conway/go/conway_test.go index c1f5ad2..a044858 100644 --- a/conway/go/conway_test.go +++ b/conway/go/conway_test.go @@ -341,3 +341,24 @@ func TestGenerationScoreCardCalculateResultsInCorrectNeighborCounts(t *testing.T return } } + +func TestStateCanMutate(t *testing.T) { + state := NewGameState(16, 16) + + err := state.Import(TEST_SIMPLE_INITIAL_STATE) + if err != nil { + t.Error(err) + return + } + + curCksum := state.Checksum() + err = state.Mutate() + if err != nil { + t.Error(err) + return + } + + if curCksum == state.Checksum() { + t.Errorf("Checksum did not change! %v", curCksum) + } +} diff --git a/conway/go/conways-game-of-life/main.go b/conway/go/conways-game-of-life/main.go index 9ba03a0..97eb454 100644 --- a/conway/go/conways-game-of-life/main.go +++ b/conway/go/conways-game-of-life/main.go @@ -14,6 +14,7 @@ import ( var ( height = flag.Int("height", 40, "Game height") width = flag.Int("width", 80, "Game width") + mutate = flag.Bool("mutate", false, "Mutate every other generation") sleepMs = flag.Int("sleep.millis", 200, "Millisecond sleep interval per generation") ) @@ -34,6 +35,9 @@ func main() { fmt.Printf("\nGeneration %v\n%v\n", generations, now) fmt.Println(game) game.EvaluateGeneration() + if *mutate && generations%2 == 0 { + game.Mutate() + } generations++ } } diff --git a/conway/go/game_of_life.go b/conway/go/game_of_life.go index 1fc36a3..7b53b8b 100644 --- a/conway/go/game_of_life.go +++ b/conway/go/game_of_life.go @@ -1,8 +1,10 @@ package conway import ( + "crypto/sha1" "errors" "fmt" + "math" "math/rand" "strings" ) @@ -11,6 +13,12 @@ type GameOfLife struct { State *GameState } +type GameStateCell struct { + Value int + X int + Y int +} + type gameGridRow struct { Cols []int } @@ -106,6 +114,63 @@ func (state *GameState) Import(other *GameState) (err error) { return nil } +func (state *GameState) Cells(cells chan *GameStateCell) error { + height := state.Height() + width := state.Width() + + if height < 0 || width < 0 { + return errors.New("state has invalid dimensions!") + } + + defer close(cells) + + for y := 0; y < height; y++ { + for x := 0; x < width; x++ { + value, err := state.Get(x, y) + if err != nil { + panic(err) + } + cells <- &GameStateCell{ + Value: value, + X: x, + Y: y, + } + } + } + + return nil +} + +func (game *GameOfLife) Mutate() error { + return game.State.Mutate() +} + +func (state *GameState) Mutate() error { + randX := rand.Intn(state.Width()) + randY := rand.Intn(state.Height()) + + curVal, err := state.Get(randX, randY) + if err != nil { + return err + } + + err = state.Set(randX, randY, int(math.Abs(float64(curVal-1)))) + + return nil +} + +func (state *GameState) Checksum() string { + ck := sha1.New() + cells := make(chan *GameStateCell) + + go state.Cells(cells) + + for cell := range cells { + fmt.Fprintf(ck, "%v", cell.Value) + } + return fmt.Sprintf("%x", ck.Sum(nil)) +} + func NewGameOfLife(height, width int) *GameOfLife { return &GameOfLife{ State: NewGameState(height, width),