3e013ce1a5
Breaking out console runner into its own file, adding an empty web runner, and starting to test that games and game states are able to represent themselves as images.
419 lines
6.9 KiB
Go
419 lines
6.9 KiB
Go
package conway_test
|
|
|
|
import (
|
|
"fmt"
|
|
"math/rand"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
import (
|
|
. "github.com/meatballhat/box-o-sand/conway"
|
|
)
|
|
|
|
var TEST_SIMPLE_INITIAL_STATE = NewGameState(16, 16)
|
|
|
|
func init() {
|
|
s := TEST_SIMPLE_INITIAL_STATE
|
|
s.Set(0, 0, 1)
|
|
s.Set(0, 1, 1)
|
|
s.Set(0, 2, 1)
|
|
s.Set(1, 0, 1)
|
|
s.Set(1, 1, 1)
|
|
s.Set(2, 2, 1)
|
|
s.Set(3, 0, 1)
|
|
s.Set(3, 2, 1)
|
|
s.Set(13, 13, 1)
|
|
s.Set(13, 14, 1)
|
|
s.Set(14, 14, 1)
|
|
s.Set(14, 15, 1)
|
|
s.Set(15, 13, 1)
|
|
s.Set(15, 15, 1)
|
|
}
|
|
|
|
func newTestGameState() *GameState {
|
|
state := NewGameState(16, 16)
|
|
state.Import(TEST_SIMPLE_INITIAL_STATE)
|
|
return state
|
|
}
|
|
|
|
func newTestGameOfLife() *GameOfLife {
|
|
game := NewGameOfLife(16, 16)
|
|
game.State.Import(TEST_SIMPLE_INITIAL_STATE)
|
|
return game
|
|
}
|
|
|
|
func TestLiveCellWithFewerThanTwoLiveNeighborsDies(t *testing.T) {
|
|
game := newTestGameOfLife()
|
|
|
|
game.EvaluateGeneration()
|
|
|
|
cell := game.State.Get(3, 2)
|
|
if cell.Value != 0 {
|
|
t.Errorf("%v != 0", cell.Value)
|
|
return
|
|
}
|
|
}
|
|
|
|
func TestLiveCellWithTwoOrThreeLiveNeighborsLivesOn(t *testing.T) {
|
|
game := newTestGameOfLife()
|
|
|
|
game.EvaluateGeneration()
|
|
|
|
cell := game.State.Get(0, 2)
|
|
if cell.Value != 1 {
|
|
t.Errorf("%v != 1", cell.Value)
|
|
return
|
|
}
|
|
}
|
|
|
|
func TestLiveCellWithMoreThanThreeLiveNeighborsDies(t *testing.T) {
|
|
game := newTestGameOfLife()
|
|
|
|
game.EvaluateGeneration()
|
|
|
|
cell := game.State.Get(0, 1)
|
|
if cell.Value != 0 {
|
|
t.Errorf("%v != 0", cell.Value)
|
|
return
|
|
}
|
|
}
|
|
|
|
func TestDeadCellWithExactlyThreeLiveNeighborsBecomesAlive(t *testing.T) {
|
|
game := newTestGameOfLife()
|
|
|
|
game.EvaluateGeneration()
|
|
|
|
cell := game.State.Get(3, 1)
|
|
if cell.Value != 1 {
|
|
t.Errorf("%v != 1", cell.Value)
|
|
return
|
|
}
|
|
}
|
|
|
|
func TestGameOfLifeCanDisplayItselfAsAString(t *testing.T) {
|
|
game := newTestGameOfLife()
|
|
|
|
grid := fmt.Sprintf("%s\n", game)
|
|
|
|
gridLen := len(grid)
|
|
if gridLen < 256 {
|
|
t.Errorf("%v < 256", gridLen)
|
|
t.Fail()
|
|
}
|
|
}
|
|
|
|
func TestGameOfLifeCanDisplayItselfAsAnImage(t *testing.T) {
|
|
t.Fail()
|
|
}
|
|
|
|
func TestNewGameOfLifeHasCorrectDimensions(t *testing.T) {
|
|
game := newTestGameOfLife()
|
|
|
|
if game.State.Height() != 16 || game.State.Width() != 16 {
|
|
t.Fail()
|
|
}
|
|
}
|
|
|
|
func TestNewGameOfLifeHasGameStateOfSameDimensions(t *testing.T) {
|
|
game := newTestGameOfLife()
|
|
|
|
if len(game.State.Rows) < 16 {
|
|
t.Fail()
|
|
}
|
|
|
|
if len(game.State.Rows[0].Cells) < 16 {
|
|
t.Fail()
|
|
}
|
|
}
|
|
|
|
func TestGameStateCanImportState(t *testing.T) {
|
|
game := newTestGameOfLife()
|
|
|
|
if game.State.Get(0, 0).Value != 1 {
|
|
t.Fail()
|
|
}
|
|
|
|
if game.State.Get(1, 0).Value != 1 {
|
|
t.Fail()
|
|
}
|
|
|
|
if game.State.Get(0, 1).Value != 1 {
|
|
t.Fail()
|
|
}
|
|
|
|
if game.State.Get(1, 1).Value != 1 {
|
|
t.Fail()
|
|
}
|
|
|
|
if game.State.Get(2, 2).Value != 1 {
|
|
t.Fail()
|
|
}
|
|
}
|
|
|
|
func TestGameOfLifeCanImportRandomState(t *testing.T) {
|
|
game := newTestGameOfLife()
|
|
|
|
err := game.ImportRandomState()
|
|
if err != nil {
|
|
t.Error(err)
|
|
return
|
|
}
|
|
|
|
zeroCount := 0
|
|
|
|
for y := 0; y < 16; y++ {
|
|
for x := 0; x < 16; x++ {
|
|
cell := game.State.Get(x, y)
|
|
if cell.Value == 0 {
|
|
zeroCount++
|
|
}
|
|
}
|
|
}
|
|
|
|
if zeroCount == 256 {
|
|
t.Fail()
|
|
}
|
|
}
|
|
|
|
func TestGameStateCanDisplayItselfAsAString(t *testing.T) {
|
|
state := newTestGameState()
|
|
|
|
grid := fmt.Sprintf("%s\n", state)
|
|
lenGrid := len(grid)
|
|
|
|
if lenGrid < 256 {
|
|
t.Errorf("%v < 256", lenGrid)
|
|
return
|
|
}
|
|
|
|
nLines := strings.Count(grid, "\n")
|
|
if nLines < 16 {
|
|
t.Errorf("%v < 16 (lines)", nLines)
|
|
return
|
|
}
|
|
}
|
|
|
|
func TestGameStateCanDisplayItselfAsAnImage(t *testing.T) {
|
|
state := newTestGameState()
|
|
|
|
img, err := state.Image(1, 1)
|
|
if err != nil {
|
|
t.Error(err)
|
|
return
|
|
}
|
|
|
|
bounds := img.Bounds()
|
|
if bounds.Max.Y < 16 {
|
|
t.Errorf("image.Max.Y < 16!: %d", bounds.Max.Y)
|
|
return
|
|
}
|
|
|
|
if bounds.Max.X < 16 {
|
|
t.Errorf("image.Max.X < 16!: %d", bounds.Max.X)
|
|
return
|
|
}
|
|
}
|
|
|
|
func TestNewGameStatesAreAllDead(t *testing.T) {
|
|
state := NewGameState(4, 4)
|
|
for x := 0; x < 3; x++ {
|
|
for y := 0; y < 3; y++ {
|
|
if state.Get(x, y).Value != 0 {
|
|
t.Fail()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestGameStateCanGetCellValueByCoords(t *testing.T) {
|
|
state := NewGameState(8, 8)
|
|
if state.Get(2, 2).Value != 0 {
|
|
t.Fail()
|
|
}
|
|
}
|
|
|
|
func TestGameStateCanSetCellValueByCoords(t *testing.T) {
|
|
state := NewGameState(8, 8)
|
|
state.Set(2, 5, 1)
|
|
|
|
if state.Get(2, 5).Value != 1 {
|
|
t.Fail()
|
|
}
|
|
}
|
|
|
|
func TestGameStateCanEnlivenCellsByCoords(t *testing.T) {
|
|
state := NewGameState(8, 8)
|
|
state.Enliven(0, 1)
|
|
|
|
if state.Get(0, 1).Value != 1 {
|
|
t.Fail()
|
|
}
|
|
}
|
|
|
|
func TestGameStateCanDeadenCellsByCoords(t *testing.T) {
|
|
state := NewGameState(8, 8)
|
|
|
|
state.Enliven(0, 1)
|
|
|
|
if state.Get(0, 1).Value != 1 {
|
|
t.Fail()
|
|
}
|
|
|
|
state.Deaden(0, 1)
|
|
|
|
if state.Get(0, 1).Value != 0 {
|
|
t.Fail()
|
|
}
|
|
}
|
|
|
|
func TestNewGenerationScoreCardIsAllZeros(t *testing.T) {
|
|
gen := NewGenerationScoreCard(4, 4)
|
|
|
|
for y := 0; y < gen.Height(); y++ {
|
|
for x := 0; x < gen.Width(); x++ {
|
|
if gen.Get(x, y).Value != 0 {
|
|
t.Fail()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestGenerationScoreCardCalculateResultsInCorrectNeighborCounts(t *testing.T) {
|
|
genScore := NewGenerationScoreCard(16, 16)
|
|
|
|
state := newTestGameState()
|
|
|
|
genScore.Calculate(state)
|
|
|
|
cell := genScore.Get(0, 0)
|
|
if cell.Value != 4 {
|
|
t.Errorf("[0, 0] value %v != 4", cell.Value)
|
|
return
|
|
}
|
|
|
|
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 := newTestGameState()
|
|
|
|
curCksum, err := state.Checksum()
|
|
if err != nil {
|
|
t.Error(err)
|
|
return
|
|
}
|
|
|
|
state.Mutate()
|
|
|
|
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 := newTestGameState()
|
|
|
|
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 := newTestGameState()
|
|
|
|
cells, err := state.Cells()
|
|
if err != nil {
|
|
t.Error(err)
|
|
return
|
|
}
|
|
|
|
for cell := range cells {
|
|
cell.Value = 1
|
|
}
|
|
|
|
cells, err = state.Cells()
|
|
if err != nil {
|
|
t.Error(err)
|
|
return
|
|
}
|
|
|
|
for cell := range cells {
|
|
if cell.Value != 1 {
|
|
t.Fail()
|
|
}
|
|
}
|
|
}
|
|
|
|
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 {
|
|
t.Error(err)
|
|
return
|
|
}
|
|
|
|
nGens := 0
|
|
|
|
for gen := range genTicks {
|
|
if gen.N > 3000 {
|
|
t.Errorf("%v is too many generations!", gen.N)
|
|
return
|
|
}
|
|
nGens = gen.N
|
|
}
|
|
|
|
if nGens < 3 {
|
|
t.Errorf("%v is too few generations!", nGens)
|
|
}
|
|
}
|
|
|
|
func TestGameStateCoordsAreNeverOutOfBounds(t *testing.T) {
|
|
state := newTestGameState()
|
|
|
|
oobX, oobY := state.Width()+rand.Intn(99), state.Height()+rand.Intn(99)
|
|
|
|
newVal := rand.Intn(9999)
|
|
|
|
state.Set(oobX, oobY, newVal)
|
|
cell := state.Get(oobX, oobY)
|
|
|
|
if cell == nil {
|
|
t.Fail()
|
|
}
|
|
|
|
if cell.Value != newVal {
|
|
t.Errorf("(%d, %d) != %d: %d", oobX, oobY, newVal, cell.Value)
|
|
}
|
|
}
|