A bunch more work towards achieving torus

This commit is contained in:
Dan Buch 2012-12-16 12:17:44 -05:00
parent 7567f1c991
commit 8056d86cbf
6 changed files with 137 additions and 378 deletions

View File

@ -31,122 +31,68 @@ func init() {
s.Set(15, 15, 1)
}
func newTestGameState() (*GameState, error) {
func newTestGameState() *GameState {
state := NewGameState(16, 16)
err := state.Import(TEST_SIMPLE_INITIAL_STATE)
if err != nil {
return nil, err
}
return state, nil
state.Import(TEST_SIMPLE_INITIAL_STATE)
return state
}
func newTestGameOfLife() (*GameOfLife, error) {
func newTestGameOfLife() *GameOfLife {
game := NewGameOfLife(16, 16)
err := game.State.Import(TEST_SIMPLE_INITIAL_STATE)
if err != nil {
return nil, err
}
return game, nil
game.State.Import(TEST_SIMPLE_INITIAL_STATE)
return game
}
func TestLiveCellWithFewerThanTwoLiveNeighborsDies(t *testing.T) {
game, err := newTestGameOfLife()
if err != nil {
t.Error(err)
return
}
game := newTestGameOfLife()
game.EvaluateGeneration()
value, err := game.State.Get(3, 2)
if err != nil {
t.Error(err)
return
}
if value != 0 {
t.Errorf("%v != 0", value)
cell := game.State.Get(3, 2)
if cell.Value != 0 {
t.Errorf("%v != 0", cell.Value)
return
}
}
func TestLiveCellWithTwoOrThreeLiveNeighborsLivesOn(t *testing.T) {
game, err := newTestGameOfLife()
if err != nil {
t.Error(err)
return
}
game := newTestGameOfLife()
game.EvaluateGeneration()
value, err := game.State.Get(0, 0)
if err != nil {
t.Error(err)
return
}
if value != 1 {
t.Errorf("%v != 1", value)
cell := game.State.Get(0, 0)
if cell.Value != 1 {
t.Errorf("%v != 1", cell.Value)
return
}
}
func TestLiveCellWithMoreThanThreeLiveNeighborsDies(t *testing.T) {
game, err := newTestGameOfLife()
if err != nil {
t.Error(err)
return
}
game := newTestGameOfLife()
game.EvaluateGeneration()
value, err := game.State.Get(0, 1)
if err != nil {
t.Error(err)
return
}
if value != 0 {
t.Errorf("%v != 0", value)
cell := game.State.Get(0, 1)
if cell.Value != 0 {
t.Errorf("%v != 0", cell.Value)
return
}
}
func TestDeadCellWithExactlyThreeLiveNeighborsBecomesAlive(t *testing.T) {
game, err := newTestGameOfLife()
if err != nil {
t.Error(err)
return
}
game := newTestGameOfLife()
game.EvaluateGeneration()
value, err := game.State.Get(3, 1)
if err != nil {
t.Error(err)
return
}
if value != 1 {
t.Errorf("%v != 1", value)
cell := game.State.Get(3, 1)
if cell.Value != 1 {
t.Errorf("%v != 1", cell.Value)
return
}
}
func TestGameOfLifeCanDisplayItself(t *testing.T) {
game, err := newTestGameOfLife()
if err != nil {
t.Error(err)
return
}
game := newTestGameOfLife()
grid := fmt.Sprintf("%s\n", game)
@ -158,11 +104,7 @@ func TestGameOfLifeCanDisplayItself(t *testing.T) {
}
func TestNewGameOfLifeHasCorrectDimensions(t *testing.T) {
game, err := newTestGameOfLife()
if err != nil {
t.Error(err)
return
}
game := newTestGameOfLife()
if game.State.Height() != 16 || game.State.Width() != 16 {
t.Fail()
@ -170,12 +112,7 @@ func TestNewGameOfLifeHasCorrectDimensions(t *testing.T) {
}
func TestNewGameOfLifeHasGameStateOfSameDimensions(t *testing.T) {
game, err := newTestGameOfLife()
if err != nil {
t.Error(err)
return
}
game := newTestGameOfLife()
if len(game.State.Rows) < 16 {
t.Fail()
@ -187,41 +124,33 @@ func TestNewGameOfLifeHasGameStateOfSameDimensions(t *testing.T) {
}
func TestGameStateCanImportState(t *testing.T) {
game, err := newTestGameOfLife()
game := newTestGameOfLife()
if err != nil {
if game.State.Get(0, 0).Value != 1 {
t.Fail()
}
if value, err := game.State.Get(0, 0); err != nil || value != 1 {
if game.State.Get(1, 0).Value != 1 {
t.Fail()
}
if value, err := game.State.Get(1, 0); err != nil || value != 1 {
if game.State.Get(0, 1).Value != 1 {
t.Fail()
}
if value, err := game.State.Get(0, 1); err != nil || value != 1 {
if game.State.Get(1, 1).Value != 1 {
t.Fail()
}
if value, err := game.State.Get(1, 1); err != nil || value != 1 {
t.Fail()
}
if value, err := game.State.Get(2, 2); err != nil || value != 1 {
if game.State.Get(2, 2).Value != 1 {
t.Fail()
}
}
func TestGameOfLifeCanImportRandomState(t *testing.T) {
game, err := newTestGameOfLife()
if err != nil {
t.Error(err)
return
}
game := newTestGameOfLife()
err = game.ImportRandomState()
err := game.ImportRandomState()
if err != nil {
t.Error(err)
return
@ -231,13 +160,8 @@ func TestGameOfLifeCanImportRandomState(t *testing.T) {
for y := 0; y < 16; y++ {
for x := 0; x < 16; x++ {
value, err := game.State.Get(x, y)
if err != nil {
t.Error(err)
return
}
if value == 0 {
cell := game.State.Get(x, y)
if cell.Value == 0 {
zeroCount++
}
}
@ -249,11 +173,7 @@ func TestGameOfLifeCanImportRandomState(t *testing.T) {
}
func TestGameStateCanDisplayItself(t *testing.T) {
state, err := newTestGameState()
if err != nil {
t.Error(err)
return
}
state := newTestGameState()
grid := fmt.Sprintf("%s\n", state)
lenGrid := len(grid)
@ -274,7 +194,7 @@ func TestNewGameStatesAreAllDead(t *testing.T) {
state := NewGameState(4, 4)
for x := 0; x < 3; x++ {
for y := 0; y < 3; y++ {
if value, err := state.Get(x, y); err != nil || value != 0 {
if state.Get(x, y).Value != 0 {
t.Fail()
}
}
@ -283,29 +203,25 @@ func TestNewGameStatesAreAllDead(t *testing.T) {
func TestGameStateCanGetCellValueByCoords(t *testing.T) {
state := NewGameState(8, 8)
if value, err := state.Get(2, 2); err != nil || value != 0 {
if state.Get(2, 2).Value != 0 {
t.Fail()
}
}
func TestGameStateCanSetCellValueByCoords(t *testing.T) {
state := NewGameState(8, 8)
if err := state.Set(2, 5, 1); err != nil {
t.Fail()
}
state.Set(2, 5, 1)
if value, err := state.Get(2, 5); err != nil || value != 1 {
if state.Get(2, 5).Value != 1 {
t.Fail()
}
}
func TestGameStateCanEnlivenCellsByCoords(t *testing.T) {
state := NewGameState(8, 8)
if err := state.Enliven(0, 1); err != nil {
t.Fail()
}
state.Enliven(0, 1)
if value, err := state.Get(0, 1); err != nil || value != 1 {
if state.Get(0, 1).Value != 1 {
t.Fail()
}
}
@ -313,19 +229,15 @@ func TestGameStateCanEnlivenCellsByCoords(t *testing.T) {
func TestGameStateCanDeadenCellsByCoords(t *testing.T) {
state := NewGameState(8, 8)
if err := state.Enliven(0, 1); err != nil {
state.Enliven(0, 1)
if state.Get(0, 1).Value != 1 {
t.Fail()
}
if value, err := state.Get(0, 1); err != nil || value != 1 {
t.Fail()
}
state.Deaden(0, 1)
if err := state.Deaden(0, 1); err != nil {
t.Fail()
}
if value, err := state.Get(0, 1); err != nil || value != 0 {
if state.Get(0, 1).Value != 0 {
t.Fail()
}
}
@ -335,7 +247,7 @@ func TestNewGenerationScoreCardIsAllZeros(t *testing.T) {
for y := 0; y < gen.Height(); y++ {
for x := 0; x < gen.Width(); x++ {
if value, err := gen.Get(x, y); err != nil || value != 0 {
if gen.Get(x, y).Value != 0 {
t.Fail()
}
}
@ -345,38 +257,25 @@ func TestNewGenerationScoreCardIsAllZeros(t *testing.T) {
func TestGenerationScoreCardCalculateResultsInCorrectNeighborCounts(t *testing.T) {
genScore := NewGenerationScoreCard(16, 16)
state, err := newTestGameState()
if err != nil {
t.Error(err)
return
}
state := newTestGameState()
genScore.Calculate(state)
value, err := genScore.Get(0, 0)
if err != nil {
t.Error(err)
return
} else if value != 3 {
t.Errorf("[0, 0] value %v != 3", value)
cell := genScore.Get(0, 0)
if cell.Value != 3 {
t.Errorf("[0, 0] value %v != 3", cell.Value)
return
}
value, err = genScore.Get(3, 2)
if err != nil {
t.Error(err)
return
} else if value != 1 {
t.Errorf("[3, 2] value %d != 1", value)
cell = genScore.Get(3, 2)
if cell.Value != 1 {
t.Errorf("[3, 2] value %d != 1", cell.Value)
return
}
}
func TestStateCanMutate(t *testing.T) {
state, err := newTestGameState()
if err != nil {
t.Error(err)
return
}
state := newTestGameState()
curCksum, err := state.Checksum()
if err != nil {
@ -384,11 +283,7 @@ func TestStateCanMutate(t *testing.T) {
return
}
err = state.Mutate()
if err != nil {
t.Error(err)
return
}
state.Mutate()
newCksum, err := state.Checksum()
if err != nil {
@ -402,11 +297,7 @@ func TestStateCanMutate(t *testing.T) {
}
func TestStateProvidesCellIteratorChannel(t *testing.T) {
state, err := newTestGameState()
if err != nil {
t.Error(err)
return
}
state := newTestGameState()
cells, err := state.Cells()
if err != nil {
@ -425,11 +316,7 @@ func TestStateProvidesCellIteratorChannel(t *testing.T) {
}
func TestGameStateCellsCanHaveTheirValueSet(t *testing.T) {
state, err := newTestGameState()
if err != nil {
t.Error(err)
return
}
state := newTestGameState()
cells, err := state.Cells()
if err != nil {
@ -438,7 +325,7 @@ func TestGameStateCellsCanHaveTheirValueSet(t *testing.T) {
}
for cell := range cells {
cell.SetValue(1)
cell.Value = 1
}
cells, err = state.Cells()
@ -454,12 +341,16 @@ func TestGameStateCellsCanHaveTheirValueSet(t *testing.T) {
}
}
func TestGameOfLifeEmitsGenerationTicksUntilStasis(t *testing.T) {
game, err := newTestGameOfLife()
if err != nil {
t.Error(err)
return
func TestGameStateCellsCanBeFetchedByXAndYCoords(t *testing.T) {
state := newTestGameState()
if state.Get(3, 4) == nil {
t.Fail()
}
}
func TestGameOfLifeEmitsGenerationTicksUntilStasis(t *testing.T) {
game := newTestGameOfLife()
genTicks, err := game.Generations()
if err != nil {
@ -482,85 +373,21 @@ func TestGameOfLifeEmitsGenerationTicksUntilStasis(t *testing.T) {
}
}
func TestGameStateRowsCanBeGottenByYCoord(t *testing.T) {
state, err := newTestGameState()
if err != nil {
t.Error(err)
return
}
func TestGameStateCoordsAreNeverOutOfBounds(t *testing.T) {
state := newTestGameState()
row := state.GetRow(1)
oobX, oobY := state.Width()+rand.Intn(99), state.Height()+rand.Intn(99)
if len(row.Cells) < 1 {
newVal := rand.Intn(9999)
state.Set(oobX, oobY, newVal)
cell := state.Get(oobX, oobY)
if cell == nil {
t.Fail()
}
}
func TestGameStateRowsCanBeGottenByYCoordsBeyondBounds(t *testing.T) {
state, err := newTestGameState()
if err != nil {
t.Error(err)
return
}
newVal := rand.Intn(9999)
err = state.Set(0, 2, newVal)
if err != nil {
t.Error(err)
return
}
row := state.GetRow(state.Height() + 2)
if row.Cells[0] != newVal {
t.Errorf("(0, 2) != %d: %d", newVal, row.Cells[0])
return
}
}
func TestGameStateRowCellsCanBeGottenByXCoord(t *testing.T) {
state, err := newTestGameState()
if err != nil {
t.Error(err)
return
}
newVal := rand.Intn(9999)
err = state.Set(4, 0, newVal)
if err != nil {
t.Error(err)
return
}
row := state.GetRow(0)
cell := row.GetCell(4)
if cell.Value != newVal {
t.Errorf("(0, 4) != %d: %d", newVal, cell.Value)
}
}
func TestGameStateRowCellsCanBeGottenByXCoordBeyondBounds(t *testing.T) {
state, err := newTestGameState()
if err != nil {
t.Error(err)
return
}
newVal := rand.Intn(9999)
err = state.Set(9, 2, newVal)
if err != nil {
t.Error(err)
return
}
row := state.GetRow(2)
cell := row.GetCell(state.Width() + 9)
if cell.Value != newVal {
t.Errorf("(9, 2) != %d: %d", newVal, cell.Value)
t.Errorf("(%d, %d) != %d: %d", oobX, oobY, newVal, cell.Value)
}
}

View File

@ -51,8 +51,8 @@ func RunConsoleGame(height, width, sleepMs int, mutate bool) (int, error) {
return 0, nil
}
func (game *GameOfLife) Mutate() error {
return game.State.Mutate()
func (game *GameOfLife) Mutate() {
game.State.Mutate()
}
func (game *GameOfLife) Checksum() (string, error) {
@ -79,18 +79,11 @@ func (game *GameOfLife) EvaluateGeneration() error {
for cell := range cells {
score := cell.Value
curState, err := game.State.Get(cell.X, cell.Y)
if err != nil {
return err
}
if curState == 1 {
cell := game.State.Get(cell.X, cell.Y)
if cell.Value == 1 {
if score < 2 {
err = game.State.Deaden(cell.X, cell.Y)
if err != nil {
return err
}
game.State.Deaden(cell.X, cell.Y)
continue
}
@ -99,19 +92,13 @@ func (game *GameOfLife) EvaluateGeneration() error {
}
if score > 3 {
err = game.State.Deaden(cell.X, cell.Y)
if err != nil {
return err
}
game.State.Deaden(cell.X, cell.Y)
continue
}
} else if curState == 0 {
} else if cell.Value == 0 {
if score == 3 {
err = game.State.Enliven(cell.X, cell.Y)
if err != nil {
return err
}
game.State.Enliven(cell.X, cell.Y)
continue
} else {
continue
@ -122,8 +109,8 @@ func (game *GameOfLife) EvaluateGeneration() error {
return nil
}
func (game *GameOfLife) ImportState(state *GameState) error {
return game.State.Import(state)
func (game *GameOfLife) ImportState(state *GameState) {
game.State.Import(state)
}
func (game *GameOfLife) ImportRandomState() error {
@ -144,10 +131,11 @@ func (game *GameOfLife) ImportRandomState() error {
}
for cell := range cells {
cell.SetValue(rand.Intn(2))
cell.Value = rand.Intn(2)
}
return game.ImportState(randState)
game.ImportState(randState)
return nil
}
func (game *GameOfLife) String() string {

View File

@ -14,6 +14,17 @@ const (
LIVE_CELL = " "
)
type GameStateCell struct {
Value int
X int
Y int
}
type GameStateRow struct {
Y int
Cells []*GameStateCell
}
type GameState struct {
Rows []*GameStateRow
}
@ -23,7 +34,11 @@ func NewGameState(height, width int) *GameState {
for i := 0; i < height; i++ {
row := &GameStateRow{Y: i}
for j := 0; j < width; j++ {
row.Cells = append(row.Cells, 0)
row.Cells = append(row.Cells, &GameStateCell{
Value: 0,
X: j,
Y: i,
})
}
state.Rows = append(state.Rows, row)
}
@ -51,50 +66,36 @@ func (state *GameState) GetRow(y int) *GameStateRow {
return state.Rows[y]
}
func (state *GameState) Set(x, y, value int) error {
if x < 0 || y < 0 {
errMsg := fmt.Sprintf("coordinates (%v, %v) are out of bounds!", x, y)
return errors.New(errMsg)
}
func (state *GameState) Set(x, y, value int) {
cell := state.Get(x, y)
cell.Value = value
}
func (state *GameState) Get(x, y int) *GameStateCell {
row := state.GetRow(y)
lenCells := len(row.Cells)
if x+1 > len(row.Cells) {
errMsg := fmt.Sprintf("x coordinate %v is out of bounds!", x)
return errors.New(errMsg)
if x+1 > lenCells {
return state.Get(x%lenCells, row.Y)
}
row.Cells[x] = value
return nil
return row.Cells[x]
}
func (state *GameState) Get(x, y int) (int, error) {
row := state.GetRow(y)
if len(row.Cells) < x+1 {
return -1, errors.New("x coordinate is out of bounds!")
}
return row.Cells[x], nil
func (state *GameState) Enliven(x, y int) {
state.Set(x, y, 1)
}
func (state *GameState) Enliven(x, y int) error {
return state.Set(x, y, 1)
func (state *GameState) Deaden(x, y int) {
state.Set(x, y, 0)
}
func (state *GameState) Deaden(x, y int) error {
return state.Set(x, y, 0)
}
func (state *GameState) Import(other *GameState) (err error) {
func (state *GameState) Import(other *GameState) {
for y, row := range other.Rows {
for x, cell := range row.Cells {
if err = state.Set(x, y, cell); err != nil {
return err
state.Set(x, y, cell.Value)
}
}
}
return nil
}
func (state *GameState) Cells() (<-chan *GameStateCell, error) {
@ -113,19 +114,8 @@ func (state *GameState) Cells() (<-chan *GameStateCell, error) {
width := state.Width()
for y := 0; y < height; y++ {
row := state.GetRow(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,
cellmates: row.Cells,
}
c <- state.Get(x, y)
}
}
}(state, cells)
@ -133,18 +123,12 @@ func (state *GameState) Cells() (<-chan *GameStateCell, error) {
return (<-chan *GameStateCell)(cells), nil
}
func (state *GameState) Mutate() error {
func (state *GameState) Mutate() {
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
cell := state.Get(randX, randY)
state.Set(randX, randY, int(math.Abs(float64(cell.Value-1))))
}
func (state *GameState) Checksum() (string, error) {
@ -171,12 +155,8 @@ func (state *GameState) String() string {
for x := 0; x < width; x++ {
stringVal := DEAD_CELL
value, err := state.Get(x, y)
if err != nil {
return ""
}
if value == 1 {
cell := state.Get(x, y)
if cell.Value == 1 {
stringVal = LIVE_CELL
}
cells = append(cells, stringVal)

View File

@ -1,12 +0,0 @@
package conway
type GameStateCell struct {
Value int
X int
Y int
cellmates []int
}
func (cell *GameStateCell) SetValue(value int) {
cell.cellmates[cell.X] = value
}

View File

@ -1,21 +0,0 @@
package conway
type GameStateRow struct {
Y int
Cells []int
}
func (row *GameStateRow) GetCell(x int) *GameStateCell {
lenCells := len(row.Cells)
if x+1 > lenCells {
return row.GetCell(x % lenCells)
}
return &GameStateCell{
Value: row.Cells[x],
X: x,
Y: row.Y,
cellmates: row.Cells,
}
}

View File

@ -27,7 +27,11 @@ func NewGenerationScoreCard(height, width int) *GenerationScoreCard {
for i := 0; i < height; i++ {
row := &GameStateRow{Y: i}
for j := 0; j < width; j++ {
row.Cells = append(row.Cells, 0)
row.Cells = append(row.Cells, &GameStateCell{
Value: 0,
X: j,
Y: i,
})
}
genScore.Rows = append(genScore.Rows, row)
}
@ -58,15 +62,8 @@ func (genScore *GenerationScoreCard) Calculate(state *GameState) error {
continue
}
curScore, err := genScore.Get(xTarget, yTarget)
if err != nil {
continue
}
err = genScore.Set(xTarget, yTarget, curScore+1)
if err != nil {
return err
}
cell := genScore.Get(xTarget, yTarget)
genScore.Set(xTarget, yTarget, cell.Value+1)
}
}