Добромир обнови решението на 26.01.2016 11:46 (преди над 2 години)
+package main
+
+import (
+ "bytes"
+ "encoding/json"
+ "net/http"
+ "net/url"
+ "strconv"
+ "strings"
+ "sync"
+)
+
+type cell struct {
+ x, y int64
+}
+
+type board struct {
+ cells map[cell]bool
+}
+
+func (board *board) cellAlive(position cell) bool {
+ _, ok := board.cells[position]
+ return ok
+}
+
+func (board *board) neighborsAlive(position cell) int64 {
+ var neighborsAlive int64 = 0
+ var i, j int64
+
+ for i = -1; i <= 1; i++ {
+ for j = -1; j <= 1; j++ {
+ currentCell := cell{position.x + i, position.y + j}
+
+ if (j != 0 || i != 0) && board.cellAlive(currentCell) {
+ neighborsAlive++
+ }
+ }
+ }
+
+ return neighborsAlive
+}
+
+func neighbors(position cell) []cell {
+ neighbors := make([]cell, 100)
+ var i, j int64
+
+ for i = -1; i <= 1; i++ {
+ for j = -1; j <= 1; j++ {
+ if (j != 0 || i != 0) && (position.x+i >= 0 && position.y+j >= 0) {
+ neighbor := cell{position.x + i, position.y + j}
+ neighbors = append(neighbors, neighbor)
+ }
+ }
+ }
+ return neighbors
+}
+
+func (board *board) nextGeneration() {
+ cells := make(map[cell]bool, 10)
+
+ for currentCell, _ := range board.cells {
+ for _, neighbor := range neighbors(currentCell) {
+ alive := board.neighborsAlive(neighbor)
+
+ if alive == 3 || alive == 2 && board.cellAlive(neighbor) {
+ cells[neighbor] = true
+ }
+
+ }
+ }
+
+ board.cells = cells
+}
+
+type GameOfLifeHandler struct {
+ lock sync.RWMutex
+ generation int64
+ gameBoard *board
+}
+
+func NewGameOfLifeHandler(boardState [][2]int64) *GameOfLifeHandler {
+ gameBoard := make(map[cell]bool, 100)
+
+ for _, position := range boardState {
+
+ cell := cell{position[0], position[1]}
+ gameBoard[cell] = true
+ }
+
+ game := new(GameOfLifeHandler)
+
+ game.generation = 0
+ game.gameBoard = new(board)
+ game.gameBoard.cells = gameBoard
+
+ return game
+}
+
+type response struct {
+ code int
+ body string
+}
+
+type cellStatusResponse struct {
+ Alive bool `json:"alive"`
+}
+
+type aliveCellsResponse struct {
+ Generation int64 `json:"generation"`
+ Cells [][2]int64 `json:"living"`
+}
+
+type gameCell struct {
+ X int64
+ Y int64
+}
+
+func (game *GameOfLifeHandler) cellStatusHandler(url *url.URL) response {
+ game.lock.RLock()
+ defer game.lock.RUnlock()
+
+ q := url.Query()
+
+ x, err := strconv.ParseInt(q.Get("x"), 10, 64)
+ if err != nil {
+ return response{400, err.Error()}
+ }
+
+ y, err := strconv.ParseInt(q.Get("y"), 10, 64)
+ if err != nil {
+ return response{400, err.Error()}
+ }
+
+ cell := cell{x, y}
+ alive := cellStatusResponse{game.gameBoard.cellAlive(cell)}
+ body, err := json.Marshal(alive)
+
+ if err != nil {
+ return response{500, err.Error()}
+ }
+
+ return response{200, string(body)}
+}
+
+func (gameHandler *GameOfLifeHandler) cellsAliveHandler(url *url.URL) response {
+ gameHandler.lock.RLock()
+ defer gameHandler.lock.RUnlock()
+
+ cells := make([][2]int64, 0, 10)
+
+ for cell, _ := range gameHandler.gameBoard.cells {
+ el := [2]int64{cell.x, cell.y}
+ cells = append(cells, el)
+ }
+
+ aliveCells := aliveCellsResponse{gameHandler.generation, cells}
+
+ responseBody, err := json.Marshal(aliveCells)
+
+ if err != nil {
+ return response{500, err.Error()}
+ }
+
+ return response{200, string(responseBody)}
+}
+
+func (game *GameOfLifeHandler) addCellsHandler(request *http.Request) response {
+ game.lock.Lock()
+ defer game.lock.Unlock()
+
+ if request.ContentLength == 0 {
+ return response{400, "Request body empty!"}
+ }
+
+ byteArr := make([]byte, 0, request.ContentLength)
+ jsonBuffer := bytes.NewBuffer(byteArr)
+ _, err := jsonBuffer.ReadFrom(request.Body)
+
+ if err != nil {
+ return response{500, err.Error()}
+ }
+
+ var cellsToAdd []gameCell
+ err = json.Unmarshal(jsonBuffer.Bytes(), &cellsToAdd)
+
+ if err != nil {
+ return response{400, err.Error()}
+ }
+
+ for _, newCell := range cellsToAdd {
+ cell := cell{newCell.X, newCell.Y}
+ game.gameBoard.cells[cell] = true
+ }
+
+ return response{201, string(jsonBuffer.Bytes())}
+}
+
+func (game *GameOfLifeHandler) evolveHandler() response {
+ game.lock.Lock()
+ defer game.lock.Unlock()
+
+ game.generation++
+ game.gameBoard.nextGeneration()
+
+ return response{204, ""}
+}
+
+func (game *GameOfLifeHandler) resetHandler() response {
+ game.lock.Lock()
+ defer game.lock.Unlock()
+
+ gameBoard := make(map[cell]bool, 100)
+
+ game.generation = 0
+ game.gameBoard = new(board)
+ game.gameBoard.cells = gameBoard
+
+ return response{204, ""}
+}
+
+func (gameHandler *GameOfLifeHandler) requestDispatcher(
+ request *http.Request) response {
+
+ if strings.EqualFold("GET", request.Method) {
+
+ if request.URL.Path == "/cell/status/" {
+
+ return gameHandler.cellStatusHandler(request.URL)
+
+ } else if request.URL.Path == "/generation/" {
+ return gameHandler.cellsAliveHandler(request.URL)
+ }
+
+ return response{404, "Not found!"}
+
+ } else if strings.EqualFold("POST", request.Method) {
+
+ if request.URL.Path == "/cells/" {
+
+ return gameHandler.addCellsHandler(request)
+
+ } else if request.URL.Path == "/generation/evolve/" {
+
+ return gameHandler.evolveHandler()
+
+ } else if request.URL.Path == "/reset/" {
+
+ }
+
+ return response{404, "Not found!"}
+ }
+
+ return response{405, "Unknown method!"}
+}
+
+func (gameHandler *GameOfLifeHandler) ServeHTTP(
+ response http.ResponseWriter, request *http.Request) {
+
+ resp := gameHandler.requestDispatcher(request)
+
+ response.WriteHeader(resp.code)
+ response.Write([]byte(resp.body))
+}