diff --git a/conway/go/conway_test.go b/conway/go/conway_test.go index c8b0945..ba275e4 100644 --- a/conway/go/conway_test.go +++ b/conway/go/conway_test.go @@ -1,16 +1,73 @@ package conway_test import ( - "testing" + "testing" ) import ( - . "github.com/meatballhat/box-o-sand/conway/go" + . "github.com/meatballhat/box-o-sand/conway/go" ) -func TestNewGameOfLifeIsNonEmpty(t *testing.T) { - game := NewGameOfLife() - if game.Height < 1 || game.Width < 1 { - t.Fail() - } +var TEST_SIMPLE_INITIAL_STATE = NewGameState(16, 16) + +func init() { + tsis := TEST_SIMPLE_INITIAL_STATE + tsis.Set(0, 0, 1) + tsis.Set(1, 0, 1) + tsis.Set(0, 1, 1) + tsis.Set(1, 1, 1) +} + +func TestNewGameOfLifeHasCorrectDimensions(t *testing.T) { + game := NewGameOfLife(16, 16) + if game.Height != 16 || game.Width != 16 { + t.Fail() + } +} + +func TestLiveCellWithFewerThanTwoLiveNeighborsDies(t *testing.T) { + state := NewGameState(16, 16) + state.ImportState(TEST_SIMPLE_INITIAL_STATE) +} + +func TestNewGameStatesAreAllDead(t *testing.T) { + state := NewGameState(4, 4) + for x := 0; x < 3; x++ { + for y := 0; y < 3; y++ { + if state.Rows[y].Cols[x] != 0 { + t.Fail() + } + } + } +} + +func TestGameStateCanEnlivenCellsByCoords(t *testing.T) { + state := NewGameState(8, 8) + if err := state.Enliven(0, 1); err != nil { + t.Fail() + } + + if state.Rows[1].Cols[0] != 1 { + t.Fail() + } +} + +func TestGameStateCanDeadenCellsByCoords(t *testing.T) { + state := NewGameState(8, 8) + + if err := state.Enliven(0, 1); err != nil { + t.Fail() + } + + if state.Rows[1].Cols[0] != 1 { + t.Fail() + } + + if err := state.Deaden(0, 1); err != nil { + t.Fail() + } + + if state.Rows[1].Cols[0] != 0 { + t.Fail() + } } diff --git a/conway/go/game_of_life.go b/conway/go/game_of_life.go index f945798..e60100a 100644 --- a/conway/go/game_of_life.go +++ b/conway/go/game_of_life.go @@ -1,13 +1,91 @@ package conway +import ( + "errors" +) + type GameOfLife struct { - Height uint8 - Width uint8 + Height int + Width int +} + +type GameGridRow struct { + Cols []int +} + +type GameState struct { + Rows []*GameGridRow +} + +func NewGameState(height, width int) *GameState { + state := &GameState{} + for i := 0; i < height; i++ { + row := &GameGridRow{} + for j := 0; j < width; j++ { + row.Cols = append(row.Cols, 0) + } + state.Rows = append(state.Rows, row) + } + return state } -func NewGameOfLife() *GameOfLife { +func (state *GameState) GetRow(y int) (*GameGridRow, error) { + if len(state.Rows) < y+1 { + return nil, errors.New("y coordinate is out of bounds!") + } + + return state.Rows[y], nil +} + +func (state *GameState) Set(x, y, value int) error { + row, err := state.GetRow(y) + if err != nil { + return err + } + + if len(row.Cols) < x+1 { + return errors.New("x coordinate is out of bounds!") + } + + row.Cols[x] = value + return nil +} + +func (state *GameState) Get(x, y int) (int, error) { + row, err := state.GetRow(y) + if err != nil { + return -1, err + } + + if len(row.Cols) < x+1 { + return -1, errors.New("x coordinate is out of bounds!") + } + + return row.Cols[x], nil +} + +func (state *GameState) Enliven(x, y int) error { + return state.Set(x, y, 1) +} + +func (state *GameState) Deaden(x, y int) error { + return state.Set(x, y, 0) +} + +func NewGameOfLife(height, width int) *GameOfLife { return &GameOfLife{ - Height: 16, - Width: 16, + Height: height, + Width: width, + } +} + +func (state *GameState) ImportState(other *GameState) (err error) { + for y, row := range other.Rows { + for x, cell := range row.Cols { + if err = state.Set(x, y, cell); err != nil { + return err + } + } } + return nil }