Ангел обнови решението на 26.01.2016 14:46 (преди над 2 години)
+package main
+
+import (
+ "encoding/json"
+ "fmt"
+ "io/ioutil"
+ "net/http"
+ "os"
+ "strconv"
+)
+
+type GameOfLifeHandler struct {
+ game *GameOfLife
+ handler *http.ServeMux
+}
+
+func NewGameOfLifeHandler(cells [][2]int64) *GameOfLifeHandler {
+ g := &GameOfLifeHandler{}
+
+ g.game = NewGameOfLife()
+ for _, cell := range cells {
+ g.game.SpawnAt(cell[0], cell[1])
+ }
+
+ g.handler = http.NewServeMux()
+
+ g.handler.HandleFunc("/cell/status/", func(w http.ResponseWriter, req *http.Request) {
+ if req.Method != "GET" {
+ w.WriteHeader(http.StatusMethodNotAllowed)
+ return
+ }
+
+ req.ParseForm()
+ x, err := strconv.ParseInt(req.Form.Get("x"), 10, 64)
+ if err != nil {
+ w.WriteHeader(http.StatusBadRequest)
+ fmt.Fprintf(w, "%s", err)
+ }
+ y, err := strconv.ParseInt(req.Form.Get("y"), 10, 64)
+ if err != nil {
+ w.WriteHeader(http.StatusBadRequest)
+ fmt.Fprintf(w, "%s", err)
+ return
+ }
+ data, err := json.Marshal(map[string]bool{
+ "alive": g.game.AliveAt(x, y),
+ })
+ if err != nil {
+ w.WriteHeader(http.StatusBadRequest)
+ fmt.Fprintf(w, "%s", err)
+ return
+ }
+ w.Write(data)
+ })
+
+ g.handler.HandleFunc("/generation/", func(w http.ResponseWriter, req *http.Request) {
+ if req.Method != "GET" {
+ w.WriteHeader(http.StatusMethodNotAllowed)
+ return
+ }
+
+ data, err := json.Marshal(map[string]interface{}{
+ "generation": g.game.Generation,
+ "living": g.game.Alive(),
+ })
+ if err != nil {
+ w.WriteHeader(http.StatusBadRequest)
+ fmt.Fprintf(w, "%s", err)
+ return
+ }
+ w.Write(data)
+ })
+
+ g.handler.HandleFunc("/generation/evolve/", func(w http.ResponseWriter, req *http.Request) {
+ if req.Method != "POST" {
+ fmt.Fprintf(os.Stderr, "wrong method: %s\n", req.Method)
+ w.WriteHeader(http.StatusMethodNotAllowed)
+ return
+ }
+
+ g.game.Evolve()
+ w.WriteHeader(http.StatusNoContent)
+ })
+
+ g.handler.HandleFunc("/reset/", func(w http.ResponseWriter, req *http.Request) {
+ if req.Method != "POST" {
+ w.WriteHeader(http.StatusMethodNotAllowed)
+ return
+ }
+
+ g.game.BigBang()
+ w.WriteHeader(http.StatusNoContent)
+ })
+
+ g.handler.HandleFunc("/cells/", func(w http.ResponseWriter, req *http.Request) {
+ if req.Method != "POST" {
+ w.WriteHeader(http.StatusMethodNotAllowed)
+ return
+ }
+
+ data, err := ioutil.ReadAll(req.Body)
+ if err != nil {
+ w.WriteHeader(http.StatusBadRequest)
+ fmt.Fprintf(w, "%s", err)
+ return
+ }
+
+ cells := make([]struct {
+ X int64 `json:"x"`
+ Y int64 `json:"y"`
+ }, 0)
+
+ err = json.Unmarshal(data, &cells)
+ if err != nil {
+ w.WriteHeader(http.StatusBadRequest)
+ fmt.Fprintf(w, "%s", err)
+ return
+ }
+
+ for _, cell := range cells {
+ g.game.SpawnAt(cell.X, cell.Y)
+ }
+
+ w.WriteHeader(http.StatusCreated)
+ })
+
+ return g
+}
+
+func (g *GameOfLifeHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
+ g.handler.ServeHTTP(w, req)
+}
+
+type GameOfLife struct {
+ board map[[2]int64]struct{}
+ Generation int
+}
+
+func NewGameOfLife() *GameOfLife {
+ g := &GameOfLife{}
+ g.BigBang()
+ return g
+}
+
+func (g *GameOfLife) AliveAt(x, y int64) bool {
+ _, ok := g.board[[2]int64{x, y}]
+ return ok
+}
+
+func (g *GameOfLife) Alive() [][2]int64 {
+ cells := make([][2]int64, len(g.board))
+ i := 0
+ for cell := range g.board {
+ cells[i] = cell
+ i++
+ }
+ return cells
+}
+
+func (g *GameOfLife) SpawnAt(x, y int64) {
+ g.board[[2]int64{x, y}] = struct{}{}
+}
+
+func (g *GameOfLife) BigBang() {
+ g.board = make(map[[2]int64]struct{})
+ g.Generation = 0
+}
+
+func (g *GameOfLife) Evolve() {
+ newBoard := make(map[[2]int64]struct{})
+
+ for cell := range g.board {
+ if g.cellWillSpawn(cell[0], cell[1]) {
+ newBoard[cell] = struct{}{}
+ }
+ for _, neighbour := range neighbours(cell[0], cell[1]) {
+ if g.cellWillSpawn(neighbour[0], neighbour[1]) {
+ newBoard[neighbour] = struct{}{}
+ }
+ }
+ }
+
+ g.board = newBoard
+ g.Generation++
+}
+
+func (g *GameOfLife) cellWillSpawn(x, y int64) bool {
+ aliveNeighbours := 0
+ for _, neighbour := range neighbours(x, y) {
+ if g.AliveAt(neighbour[0], neighbour[1]) {
+ aliveNeighbours++
+ }
+ }
+
+ if g.AliveAt(x, y) {
+ if aliveNeighbours < 2 {
+ return false // underpopulation
+ }
+ if aliveNeighbours > 3 {
+ return false // overpopulation
+ }
+ return true
+ } else {
+ if aliveNeighbours == 3 {
+ return true
+ }
+ return false
+ }
+}
+
+func neighbours(x, y int64) [][2]int64 {
+ return [][2]int64{
+ {x - 1, y},
+ {x, y - 1},
+ {x + 1, y},
+ {x, y + 1},
+ {x + 1, y + 1},
+ {x - 1, y - 1},
+ {x + 1, y - 1},
+ {x - 1, y + 1},
+ }
+}
За съжаление имаш race condition при достъпа на board
map-a ти. Тестовете не го хващат всеки път, за това аз ще ти махна една точка.