diff --git a/conway/.gitignore b/conway/.gitignore deleted file mode 100644 index 7103906..0000000 --- a/conway/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/web_assets/web_assets diff --git a/conway/Makefile b/conway/Makefile deleted file mode 100644 index a13870d..0000000 --- a/conway/Makefile +++ /dev/null @@ -1,24 +0,0 @@ -LIBS := \ - github.com/meatballhat/box-o-sand/conway -TARGETS := \ - $(LIBS) \ - github.com/meatballhat/box-o-sand/conway/conways-game-of-life - -all: test - -test: build - go test -x -v -test.parallel=4 $(LIBS) - -build: web_assets.go deps - go install -x $(TARGETS) - -web_assets.go: $(wildcard web_assets/*.*) - ./gen-web-assets - -clean: - go clean -x -i $(TARGETS) - -deps: - go get -n -x $(TARGETS) - -.PHONY: all build clean deps test diff --git a/conway/console.go b/conway/console.go deleted file mode 100644 index 57a1673..0000000 --- a/conway/console.go +++ /dev/null @@ -1,47 +0,0 @@ -package conway - -import ( - "fmt" - "math/rand" - "time" -) - -func init() { - rand.Seed(time.Now().UTC().UnixNano()) -} - -func RunConsoleGame(height, width, sleepMs int, mutate bool) (int, error) { - sleepInterval := time.Duration(sleepMs * 1000 * 1000) - - game := NewGameOfLife(height, width) - err := game.ImportRandomState() - if err != nil { - return 2, err - } - - genTicks, err := game.Generations() - if err != nil { - return 3, err - } - - for genTick := range genTicks { - fmt.Printf("\nGeneration %v\n%v\n", genTick.N, time.Now()) - fmt.Println(game) - - if genTick.Error != nil { - return 4, genTick.Error - } - - if len(genTick.Message) > 0 { - fmt.Println(genTick.Message) - } - - if mutate && genTick.N%2 == 0 { - game.Mutate() - } - - time.Sleep(sleepInterval) - } - - return 0, nil -} diff --git a/conway/conway_test.go b/conway/conway_test.go deleted file mode 100644 index 1879beb..0000000 --- a/conway/conway_test.go +++ /dev/null @@ -1,593 +0,0 @@ -package conway_test - -import ( - "bytes" - "encoding/base64" - "encoding/json" - "fmt" - "image/png" - "math/rand" - "net/http" - "regexp" - "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) { - game := newTestGameOfLife() - - img, err := game.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 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) - } -} - -type testResponseWriter struct { - Status int - Headers http.Header - Body []byte -} - -func (w *testResponseWriter) WriteHeader(status int) { - w.Status = status -} - -func (w *testResponseWriter) Header() http.Header { - return w.Headers -} - -func (w *testResponseWriter) Write(bytes []byte) (int, error) { - w.Body = append(w.Body, bytes[:]...) - return len(bytes), nil -} - -func newTestResponseWriter() *testResponseWriter { - return &testResponseWriter{ - Body: []byte(""), - Headers: make(map[string][]string), - } -} - -func TestHandleWebGameRootReturnsIndexPageForGET(t *testing.T) { - req, err := http.NewRequest("GET", "http://localhost:9775/", nil) - if err != nil { - t.Error(err) - return - } - - w := newTestResponseWriter() - HandleWebGameRoot(w, req) - - if w.Status != http.StatusOK { - t.Errorf("response status != %d: %d", http.StatusOK, w.Status) - return - } - - matched, err := regexp.MatchString("

Conway's Game of Life

", string(w.Body)) - if err != nil { - t.Error(err) - return - } - - if !matched { - t.Errorf("Body did not match expected

element") - return - } -} - -func TestHandleWebGameRootReturns405ForNonGET(t *testing.T) { - req, err := http.NewRequest("OPTIONS", "http://localhost:9775/", nil) - if err != nil { - t.Error(err) - return - } - - w := newTestResponseWriter() - HandleWebGameRoot(w, req) - - if w.Status != http.StatusMethodNotAllowed { - t.Fail() - return - } - - matched, err := regexp.MatchString("Nope", string(w.Body)) - if err != nil { - t.Error(err) - return - } - - if !matched { - t.Fail() - return - } -} - -func TestHandleWebGameStateGETReturnsRandomImage(t *testing.T) { - req, err := http.NewRequest("GET", "http://localhost:9775/state", nil) - if err != nil { - t.Error(err) - return - } - - w := newTestResponseWriter() - HandleWebGameState(w, req) - - if w.Status != http.StatusOK { - t.Errorf("status != %d: %d", http.StatusOK, w.Status) - return - } - - webState := &WebGameState{} - err = json.Unmarshal(w.Body, webState) - if err != nil { - t.Error(err) - return - } - - imgB64 := []byte(webState.Img) - imgBytes := make([]byte, len(imgB64)) - - _, err = base64.StdEncoding.Decode(imgBytes, imgB64) - if err != nil { - t.Error(err) - return - } - - img, err := png.Decode(bytes.NewBuffer(imgBytes)) - if err != nil { - t.Error(err) - return - } - - bounds := img.Bounds() - - if bounds.Max.Y < 80 || bounds.Max.X < 80 { - t.Errorf("Image has incorrect bounds: %+v", bounds) - return - } -} - -func TestHandleWebGameStatePOSTWithStateReturnsNextGeneration(t *testing.T) { - state := newTestGameState() - webGameState := &WebGameState{State: state, Img: ""} - - stateJson, err := json.Marshal(webGameState) - if err != nil { - t.Error(err) - return - } - - reqBuf := bytes.NewBuffer(stateJson) - - req, err := http.NewRequest("POST", "http://localhost:9975/state", reqBuf) - if err != nil { - t.Errorf("err != nil: %+v", err) - return - } - - w := newTestResponseWriter() - HandleWebGameState(w, req) - - if w.Status != http.StatusCreated { - t.Errorf("response status != %d: %d", http.StatusCreated, w.Status) - return - } -} diff --git a/conway/conways-game-of-life/main.go b/conway/conways-game-of-life/main.go deleted file mode 100644 index f099de6..0000000 --- a/conway/conways-game-of-life/main.go +++ /dev/null @@ -1,45 +0,0 @@ -package main - -import ( - "flag" - "fmt" - "os" -) - -import ( - . "github.com/meatballhat/box-o-sand/conway" -) - -var ( - height = flag.Int("height", 40, "Game height") - width = flag.Int("width", 80, "Game width") - mutate = flag.Bool("mutate", false, "Mutate every other generation") - web = flag.Bool("web", false, "Run a web-based game.") - addr = flag.String("addr", ":9775", - "Address for server of web-based game (ignored if running a console game.)") - sleepMs = flag.Int("sleep.ms", 200, - "Millisecond sleep interval per generation") -) - -func main() { - flag.Parse() - - var ( - retCode int - err error - ) - - if *web { - retCode, err = RunWebGame(*addr, *height, *width, *sleepMs, *mutate) - if err != nil { - fmt.Fprintf(os.Stderr, "ERROR: %v\n", err) - } - } else { - retCode, err = RunConsoleGame(*height, *width, *sleepMs, *mutate) - if err != nil { - fmt.Fprintf(os.Stderr, "ERROR: %v\n", err) - } - } - - os.Exit(retCode) -} diff --git a/conway/doc.go b/conway/doc.go deleted file mode 100644 index cc0c940..0000000 --- a/conway/doc.go +++ /dev/null @@ -1,4 +0,0 @@ -/* -Conway's Game of Life in Go (console and web.) -*/ -package conway diff --git a/conway/game_of_life.go b/conway/game_of_life.go deleted file mode 100644 index 1a5ff6e..0000000 --- a/conway/game_of_life.go +++ /dev/null @@ -1,190 +0,0 @@ -package conway - -import ( - "errors" - "fmt" - "image" - "math/rand" -) - -type GameOfLife struct { - State *GameState -} - -func (game *GameOfLife) Mutate() { - game.State.Mutate() -} - -func (game *GameOfLife) Checksum() (string, error) { - return game.State.Checksum() -} - -func NewGameOfLife(height, width int) *GameOfLife { - return &GameOfLife{ - State: NewGameState(height, width), - } -} - -func (game *GameOfLife) EvaluateGeneration() error { - height, width := game.State.Height(), game.State.Width() - - genScore := NewGenerationScoreCard(height, width) - genScore.Calculate(game.State) - - cells, err := genScore.Cells() - if err != nil { - return err - } - - for cell := range cells { - score := cell.Value - - cell := game.State.Get(cell.X, cell.Y) - - if cell.Value == 1 { - if score < 2 { - game.State.Deaden(cell.X, cell.Y) - continue - } - - if score == 2 || score == 3 { - continue - } - - if score > 3 { - game.State.Deaden(cell.X, cell.Y) - continue - } - } else if cell.Value == 0 { - - if score == 3 { - game.State.Enliven(cell.X, cell.Y) - continue - } else { - continue - } - } - } - - return nil -} - -func (game *GameOfLife) ImportState(state *GameState) { - game.State.Import(state) -} - -func (game *GameOfLife) ImportRandomState() error { - height := game.State.Height() - width := game.State.Width() - - if height < 0 || width < 0 { - errStr := fmt.Sprintf("current game has invalid dimensions! %vx%v", - height, width) - return errors.New(errStr) - } - - randState := NewGameState(height, width) - - cells, err := randState.Cells() - if err != nil { - return err - } - - for cell := range cells { - cell.Value = rand.Intn(2) - } - - game.ImportState(randState) - return nil -} - -func (game *GameOfLife) String() string { - return fmt.Sprintf("%s\n", game.State) -} - -func (game *GameOfLife) Image(xMult, yMult int) (*image.Gray, error) { - return game.State.Image(xMult, yMult) -} - -func (game *GameOfLife) emitGenerations(genTicks chan *GenerationTick) { - defer close(genTicks) - n := 0 - - checksums := make([]string, 4) - checksums[0], checksums[1], checksums[2], checksums[3] = "foo", "bar", "baz", "qwx" - ck, err := game.Checksum() - if err != nil { - genTicks <- &GenerationTick{ - N: n, - Error: err, - Message: "NAY NAY", - } - } - pushChecksum(ck, checksums) - - for { - game.EvaluateGeneration() - - curChecksum, err := game.Checksum() - if err != nil { - genTicks <- &GenerationTick{ - N: n, - Error: err, - Message: "Failed to calculate current game checksum", - } - break - } - - if checksums[0] == curChecksum || checksums[1] == curChecksum { - genTicks <- &GenerationTick{ - N: n, - Error: nil, - Message: "Stasis!", - } - break - } - - if checksums[2] == curChecksum { - genTicks <- &GenerationTick{ - N: n, - Error: nil, - Message: "Stasis with 2-period oscillator(s)!", - } - break - } - - if checksums[3] == curChecksum { - genTicks <- &GenerationTick{ - N: n, - Error: nil, - Message: "Stasis with 3-period oscillator(s)!", - } - break - } - - pushChecksum(curChecksum, checksums) - - genTicks <- &GenerationTick{ - N: n, - Error: nil, - Message: "", - } - - n++ - } -} - -func (game *GameOfLife) Generations() (<-chan *GenerationTick, error) { - genTicks := make(chan *GenerationTick) - go game.emitGenerations(genTicks) - return (<-chan *GenerationTick)(genTicks), nil -} - -func pushChecksum(checksum string, checksums []string) { - head := make([]string, 3) - copy(head, checksums[:3]) - checksums[0] = checksum - checksums[1] = head[0] - checksums[2] = head[1] - checksums[3] = head[2] -} diff --git a/conway/game_state.go b/conway/game_state.go deleted file mode 100644 index 70ba6ef..0000000 --- a/conway/game_state.go +++ /dev/null @@ -1,206 +0,0 @@ -package conway - -import ( - "crypto/sha1" - "errors" - "fmt" - "image" - "image/color" - "image/draw" - "math" - "math/rand" - "strings" -) - -const ( - deadCell = "■" - liveCell = " " -) - -type GameStateCell struct { - Value int `json:"v"` - X int `json:"x"` - Y int `json:"y"` -} - -type GameStateRow struct { - Y int `json:"y"` - Cells []*GameStateCell `json:"c"` -} - -type GameState struct { - Rows []*GameStateRow `json:"r"` -} - -func NewGameState(height, width int) *GameState { - state := &GameState{} - for i := 0; i < height; i++ { - row := &GameStateRow{Y: i} - for j := 0; j < width; j++ { - row.Cells = append(row.Cells, &GameStateCell{ - Value: 0, - X: j, - Y: i, - }) - } - state.Rows = append(state.Rows, row) - } - return state -} - -func (state *GameState) Height() int { - return len(state.Rows) -} - -func (state *GameState) Width() int { - if len(state.Rows) < 1 { - return -1 - } - return len(state.Rows[0].Cells) -} - -func (state *GameState) GetRow(y int) *GameStateRow { - lenRows := len(state.Rows) - - if y < 0 { - return state.GetRow(lenRows + y) - } - - if y+1 > lenRows { - return state.GetRow(y % lenRows) - } - - return state.Rows[y] -} - -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 < 0 { - return state.Get(lenCells+x, row.Y) - } - - if x+1 > lenCells { - return state.Get(x%lenCells, row.Y) - } - - return row.Cells[x] -} - -func (state *GameState) Enliven(x, y int) { - state.Set(x, y, 1) -} - -func (state *GameState) Deaden(x, y int) { - state.Set(x, y, 0) -} - -func (state *GameState) Import(other *GameState) { - for y, row := range other.Rows { - for x, cell := range row.Cells { - state.Set(x, y, cell.Value) - } - } -} - -func (state *GameState) Cells() (<-chan *GameStateCell, error) { - height := state.Height() - width := state.Width() - - if height < 0 || width < 0 { - return nil, errors.New("state has invalid dimensions!") - } - - cells := make(chan *GameStateCell) - - go func(state *GameState, c chan *GameStateCell) { - defer close(c) - height := state.Height() - width := state.Width() - - for y := 0; y < height; y++ { - for x := 0; x < width; x++ { - c <- state.Get(x, y) - } - } - }(state, cells) - - return (<-chan *GameStateCell)(cells), nil -} - -func (state *GameState) Mutate() { - randX := rand.Intn(state.Width()) - randY := rand.Intn(state.Height()) - - cell := state.Get(randX, randY) - state.Set(randX, randY, int(math.Abs(float64(cell.Value-1)))) -} - -func (state *GameState) Checksum() (string, error) { - ck := sha1.New() - 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)), nil -} - -func (state *GameState) String() string { - var rows []string - - height := state.Height() - width := state.Width() - - for y := 0; y < height; y++ { - var cells []string - for x := 0; x < width; x++ { - stringVal := deadCell - - cell := state.Get(x, y) - if cell.Value == 1 { - stringVal = liveCell - } - cells = append(cells, stringVal) - } - rows = append(rows, strings.Join(cells, "")) - } - return strings.Join(rows, "\n") -} - -func (state *GameState) Image(xMult, yMult int) (*image.Gray, error) { - img := image.NewGray(image.Rect(0, 0, - (state.Height()*xMult)+1, (state.Width()*yMult)+1)) - draw.Draw(img, img.Bounds(), &image.Uniform{color.Gray16{0xff}}, - image.ZP, draw.Src) - - cells, err := state.Cells() - if err != nil { - return nil, err - } - - black := &image.Uniform{color.Black} - white := &image.Uniform{color.White} - - for cell := range cells { - color := white - if cell.Value == 1 { - color = black - } - - square := image.Rect((cell.X*xMult)+1, (cell.Y*yMult)+1, - (cell.X*xMult)+xMult, (cell.Y*yMult)+yMult) - draw.Draw(img, square, color, image.ZP, draw.Src) - } - - return img, nil -} diff --git a/conway/gen-web-assets b/conway/gen-web-assets deleted file mode 100755 index 2223023..0000000 --- a/conway/gen-web-assets +++ /dev/null @@ -1,29 +0,0 @@ -#!/bin/sh -set -e - -case "$1" in - "shell") -cat > _tmp.go < web_assets.go -rm -v _tmp.go - ;; - *) -cd web_assets -go build -x . -./web_assets index.html > _tmp.go -gofmt < _tmp.go > ../web_assets.go -rm -vf _tmp.go - ;; -esac diff --git a/conway/generation_score_card.go b/conway/generation_score_card.go deleted file mode 100644 index 8c308f5..0000000 --- a/conway/generation_score_card.go +++ /dev/null @@ -1,62 +0,0 @@ -package conway - -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}, - } -) - -type GenerationScoreCard struct { - GameState -} - -type neighbor struct { - X int - Y int -} - -func NewGenerationScoreCard(height, width int) *GenerationScoreCard { - genScore := &GenerationScoreCard{} - for i := 0; i < height; i++ { - row := &GameStateRow{Y: i} - for j := 0; j < width; j++ { - row.Cells = append(row.Cells, &GameStateCell{ - Value: 0, - X: j, - Y: i, - }) - } - genScore.Rows = append(genScore.Rows, row) - } - return genScore -} - -func (genScore *GenerationScoreCard) Calculate(state *GameState) error { - 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 - - cell := genScore.Get(xTarget, yTarget) - genScore.Set(xTarget, yTarget, cell.Value+1) - } - } - - return nil -} diff --git a/conway/generation_tick.go b/conway/generation_tick.go deleted file mode 100644 index af25e9e..0000000 --- a/conway/generation_tick.go +++ /dev/null @@ -1,7 +0,0 @@ -package conway - -type GenerationTick struct { - N int - Error error - Message string -} diff --git a/conway/web.go b/conway/web.go deleted file mode 100644 index b339a6e..0000000 --- a/conway/web.go +++ /dev/null @@ -1,272 +0,0 @@ -package conway - -import ( - "bytes" - "compress/gzip" - "encoding/base64" - "encoding/json" - "errors" - "fmt" - "image/png" - "io/ioutil" - "log" - "math/rand" - "net/http" - "net/url" - "strconv" - "time" -) - -type WebGameParams struct { - Height int - Width int - Xmul int - Ymul int -} - -type WebGameState struct { - State *GameState `json:"s"` - Img string `json:"i"` -} - -type webAssetStrings struct { - IndexHTML []byte - NormalizeCSS []byte - JqueryMinJS []byte -} - -var WebAssets webAssetStrings - -func init() { - rand.Seed(time.Now().UTC().UnixNano()) - idxHt, err := fromGzB64(gameOfLifeIndexHtml) - if err != nil { - log.Fatal("Failed to decode internal index.html: ", err) - } - - WebAssets.IndexHTML = idxHt - - normCss, err := fromGzB64(normalizeCss) - if err != nil { - log.Fatal("Failed to decode internal normalize.css: ", err) - } - WebAssets.NormalizeCSS = normCss - - jqmin, err := fromGzB64(jqueryMinJs) - if err != nil { - log.Fatal("Failed to decode internal jquery.min.js: ", err) - } - WebAssets.JqueryMinJS = jqmin -} - -func fromGzB64(input string) ([]byte, error) { - decoded, err := base64.StdEncoding.DecodeString(input) - if err != nil { - return []byte(""), err - } - - buf := bytes.NewBuffer(decoded) - zReader, err := gzip.NewReader(buf) - if err != nil { - return []byte(""), err - } - - defer zReader.Close() - - gunzipped, err := ioutil.ReadAll(zReader) - if err != nil { - return []byte(""), err - } - - return gunzipped, nil -} - -func NewWebGameParams(q url.Values) *WebGameParams { - params := &WebGameParams{ - Height: 50, - Width: 50, - Xmul: 10, - Ymul: 10, - } - - h := q.Get("h") - if len(h) > 0 { - if i, err := strconv.Atoi(h); err == nil { - params.Height = i - } - } - - w := q.Get("w") - if len(w) > 0 { - if i, err := strconv.Atoi(w); err == nil { - params.Width = i - } - } - - xm := q.Get("xm") - if len(xm) > 0 { - if i, err := strconv.Atoi(xm); err == nil { - params.Xmul = i - } - } - - ym := q.Get("ym") - if len(ym) > 0 { - if i, err := strconv.Atoi(ym); err == nil { - params.Ymul = i - } - } - - return params -} - -func handle405(method, uri string, w http.ResponseWriter) { - w.Header().Set("Content-Type", "text/plain") - w.WriteHeader(http.StatusMethodNotAllowed) - w.Write([]byte(fmt.Sprintf("Nope. %v not allowed at %v\n", method, uri))) -} - -func handle500(err error, w http.ResponseWriter) { - log.Println("ERROR:", err) - w.Header().Set("Content-Type", "text/plain") - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(fmt.Sprintf("We exploded!: %v\n", err))) -} - -func HandleWebGameRoot(w http.ResponseWriter, req *http.Request) { - if req.Method != "GET" { - handle405(req.Method, req.RequestURI, w) - return - } - - w.Header().Set("Content-Type", "text/html") - w.WriteHeader(http.StatusOK) - w.Write(WebAssets.IndexHTML) - return -} - -func HandleWebGameStatic(w http.ResponseWriter, req *http.Request) { - if req.Method != "GET" { - handle405(req.Method, req.RequestURI, w) - return - } - - if req.URL.Path == "/static/normalize.css" { - w.Header().Set("Content-Type", "text/css") - w.WriteHeader(http.StatusOK) - w.Write(WebAssets.NormalizeCSS) - return - } - - if req.URL.Path == "/static/jquery.min.js" { - w.Header().Set("Content-Type", "text/javascript") - w.WriteHeader(http.StatusOK) - w.Write(WebAssets.JqueryMinJS) - return - } - - w.Header().Set("Content-Type", "text/plain") - w.WriteHeader(http.StatusNotFound) - w.Write([]byte(fmt.Sprintf("Don't have: %v\n", req.URL.Path))) -} - -func HandleWebGameState(w http.ResponseWriter, req *http.Request) { - switch req.Method { - case "GET": - handleWebGameStateGET(w, req) - return - case "POST": - handleWebGameStatePOST(w, req) - return - default: - handle405(req.Method, req.RequestURI, w) - return - } -} - -func handleWebGameStateGET(w http.ResponseWriter, req *http.Request) { - q := req.URL.Query() - params := NewWebGameParams(q) - - game := NewGameOfLife(params.Height, params.Width) - err := game.ImportRandomState() - if err != nil { - handle500(err, w) - return - } - - sendWebGameStateJSONPayload(w, http.StatusOK, game, params) -} - -func handleWebGameStatePOST(w http.ResponseWriter, req *http.Request) { - q := req.URL.Query() - params := NewWebGameParams(q) - - defer func() { - r := recover() - if r != nil { - handle500(errors.New(fmt.Sprintf("OH NOES! %v", r)), w) - } - }() - - body, err := ioutil.ReadAll(req.Body) - if err != nil { - handle500(err, w) - } - - webGameState := &WebGameState{} - err = json.Unmarshal(body, webGameState) - if err != nil { - handle500(err, w) - } - - height, width := webGameState.State.Height(), webGameState.State.Width() - game := NewGameOfLife(height, width) - game.ImportState(webGameState.State) - - game.EvaluateGeneration() - - sendWebGameStateJSONPayload(w, http.StatusCreated, game, params) -} - -func sendWebGameStateJSONPayload(w http.ResponseWriter, status int, game *GameOfLife, params *WebGameParams) { - img, err := game.Image(params.Xmul, params.Ymul) - - if err != nil { - handle500(err, w) - return - } - - log.Println("Serving state image.") - - var pngBuf bytes.Buffer - err = png.Encode(&pngBuf, img) - if err != nil { - handle500(err, w) - } - - imgB64 := base64.StdEncoding.EncodeToString(pngBuf.Bytes()) - webGameState := &WebGameState{State: game.State, Img: imgB64} - wgsJson, err := json.Marshal(webGameState) - - if err != nil { - handle500(err, w) - } - - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(status) - w.Write(wgsJson) -} - -func RunWebGame(address string, height, width, sleepMs int, mutate bool) (int, error) { - http.HandleFunc("/", HandleWebGameRoot) - http.HandleFunc("/state", HandleWebGameState) - http.HandleFunc("/static/", HandleWebGameStatic) - - fmt.Printf("Serving on %v\n", address) - err := http.ListenAndServe(address, nil) - if err != nil { - return 1, err - } - return 0, nil -} diff --git a/conway/web_assets.go b/conway/web_assets.go deleted file mode 100644 index f375e58..0000000 --- a/conway/web_assets.go +++ /dev/null @@ -1,646 +0,0 @@ -package conway - -// WARNING: GENERATED FILE, NERDS! 2012-12-26 10:57:04.843003 -0500 EST - -const ( - gameOfLifeIndexHtml = ` -H4sIAAAJbogC/5RV32/bNhB+719xUwJIxlQpCboBsRW/dMOWYWiDxAO2p4GRzjJdilTJU1ov8P8+ -Ur8tu0HjB1ni3X3f3cfjMfnhl4/vV//c/QobKsTyTdL8ASQbZJl7sa/ESeDyvZJf2M438BsrENQa -/uRrTOLG2DgKLj8B7Uq88Qi/Upwa44FGceMZ2gk0G0TyYKNxfePFhhjxNJZKF0zw/zCqveMWyqSa -lzQG27In1qx6YHQ6IGw/V6h3UcFltDXeMokbr+8BanwA0ko/WDCEG3jeL4bF2yK3S563eNOurSuZ -ElcSSlVWwkbcSk6ciTo6mMFz6wdwHuVIfzx8/BD4daLohxY7BCXv2tB7NKWSBmcd4f6IJVcr9cHm -3MDXMCEIpcpDplIZCvpvgJ5xtOZSiQxpLnO+3gXPZg61k01JVkKEcDUbu3cpBBkjFgK1SVQmhO3n -v3+/Hyfgfk9MN3hWr+MSv4myOADhawim1TU/g7TiBaqKgj4z6/WCQAuwlf10cTGh2I++9uOC/a1R -0u8XXtiUV5R3sEvWijILul4LwUVFZpRg33G1hQ8Gp+55XV1jPw/8s/rzX17k/gjBKdg7RgJlThtI -4PJQ0QlUYkGAZ+6UtpDuHPqziJWlzXilgoEvVZIYl6j92Yh2UHUgZ0Q68O1JtY3vu4LmvGA5xqXM -F4/M4M/vQh9+BJSpyvCv+9ugKXoMq5EqLVudjjbk/KAV+qDTJ3MAbWrB0taXCp5+eqmhur1y3TSB -KIUdhq+AsO2hq0Og7m06sdysnI7RblI9qmw3KtbZ39rxmcs5pCgJ9ZFMZ/2WjeIKpnNuYy6AVaSG -yr7wjDZzuLy4eld+XZykEbimY5JJdxxRvSVVzuHqcoxasiyz46gxXY8tj0pnqBuDDQGjBM/g7Pr6 -emBulXNi1RdW3N1YidOo1TLjT3Vn94n1Qiaby29dadbSOY3jtRLDPjiaisiOA2d2zeAt7+wziZvl -036u77zlg31O/ZLYUh3RTlT1Tnj3rxaxrtvmX9/h/wcAAP//rQ2TxtsHAAA=` - normalizeCss = ` -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` - jqueryMinJs = ` -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 -f8vzG5z8/wAAAP//TQkipcVtAQA=` -) diff --git a/conway/web_assets/gen.go b/conway/web_assets/gen.go deleted file mode 100644 index 3109bac..0000000 --- a/conway/web_assets/gen.go +++ /dev/null @@ -1,183 +0,0 @@ -package main - -import ( - "bufio" - "bytes" - "compress/gzip" - "encoding/base64" - "errors" - "fmt" - "io" - "io/ioutil" - "log" - "net/http" - "os" - "strings" - "text/template" - "time" -) - -const b64Width = 76 - -var ( - webAssetsTmpl = template.Must(template.New("web_assets").Parse("" + - "package conway\n" + - "// WARNING: GENERATED FILE, NERDS! {{.Now}}\n" + - "\n" + - "const (\n" + - " gameOfLifeIndexHtml = `{{.IndexHTML}}`\n" + - " normalizeCss = `{{.NormalizeCSS}}`\n" + - " jqueryMinJs = `{{.JqueryMinJS}}`\n" + - ")\n")) - normalizeCssUrl = "http://necolas.github.com/normalize.css/2.0.1/normalize.css" - jqueryMinJsUrl = "http://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js" -) - -type webAssetStrings struct { - IndexHTML string - NormalizeCSS string - JqueryMinJS string - Now time.Time -} - -func die(err error) { - log.Fatal("CRUD:", err) -} - -func toB64(input []byte) string { - var ( - err error - out bytes.Buffer - ) - encoded := base64.StdEncoding.EncodeToString(input) - lenEncoded := len(encoded) - - outbuf := bufio.NewWriter(&out) - inbuf := strings.NewReader(encoded) - - outbuf.WriteByte('\n') - if err != nil { - die(err) - } - - nWritten := 0 - for { - for n := 0; n < b64Width; n++ { - c, err := inbuf.ReadByte() - if err == io.EOF { - break - } - - if err != nil { - die(err) - } - - err = outbuf.WriteByte(c) - if err != nil { - die(err) - } - - nWritten++ - } - - _, err := inbuf.ReadByte() - if err == io.EOF { - break - } - - if err != nil { - die(err) - } - - err = inbuf.UnreadByte() - if err != nil { - die(err) - } - - err = outbuf.WriteByte('\n') - if err != nil { - die(err) - } - } - - if nWritten != lenEncoded { - die(errors.New(fmt.Sprintf("base64-encoded length = %d, but wrote %d", - lenEncoded, nWritten))) - } - - outbuf.Flush() - return string(out.Bytes()) -} - -func toGz(input []byte) []byte { - var buf bytes.Buffer - - zwriter, err := gzip.NewWriterLevel(&buf, gzip.BestCompression) - if err != nil { - die(err) - } - - zbuf := bufio.NewWriter(zwriter) - - toWrite := len(input) - for written := 0; written < toWrite; { - n, err := zbuf.Write(input[written:]) - if err != nil { - die(err) - } - written += n - } - - zbuf.Flush() - zwriter.Close() - return buf.Bytes() -} - -func fetchUrl(url string) []byte { - resp, err := http.Get(url) - if err != nil { - die(err) - } - - defer resp.Body.Close() - - body, err := ioutil.ReadAll(resp.Body) - if err != nil { - die(err) - } - - return body -} - -func fetchIndexHtml(indexHtmlPath string) []byte { - fd, err := os.Open(indexHtmlPath) - if err != nil { - die(err) - } - - body, err := ioutil.ReadAll(fd) - if err != nil { - die(err) - } - - return body -} - -func toB64Gz(input []byte) string { - return string(toB64(toGz(input))) -} - -func main() { - if len(os.Args) < 2 { - die(errors.New(fmt.Sprintf("Usage: %s ", os.Args[0]))) - } - - assets := &webAssetStrings{ - IndexHTML: toB64Gz(fetchIndexHtml(os.Args[1])), - NormalizeCSS: toB64Gz(fetchUrl(normalizeCssUrl)), - JqueryMinJS: toB64Gz(fetchUrl(jqueryMinJsUrl)), - Now: time.Now(), - } - - webAssetsTmpl.Execute(os.Stdout, assets) -} diff --git a/conway/web_assets/index.html b/conway/web_assets/index.html deleted file mode 100644 index 238659d..0000000 --- a/conway/web_assets/index.html +++ /dev/null @@ -1,75 +0,0 @@ - - - - Conway's Game of Life - - - - - - -
-

Conway's Game of Life

-
- - -
-
-
-
- -