Станислав обнови решението на 25.01.2016 14:38 (преди над 2 години)
+package main
+
+import (
+ "encoding/json"
+ "fmt"
+ "io/ioutil"
+ "net/http"
+ "strconv"
+ "sync"
+)
+
+// ResponseBuilder constructs an HTTP response.
+type ResponseBuilder struct {
+ writer http.ResponseWriter
+}
+
+// Respond generates a custom HTTP response.
+func (rb *ResponseBuilder) Respond(statusCode int, body []byte) {
+ rb.writer.Header().Set("Content-Type", "application/json")
+
+ rb.writer.WriteHeader(statusCode)
+ if body != nil {
+ rb.writer.Write(body)
+ }
+}
+
+// RespondError generates an HTTP error response.
+func (rb *ResponseBuilder) RespondError(err error) {
+ rb.Respond(http.StatusBadRequest, []byte(err.Error()))
+}
+
+// RespondSuccess generates an HTTP success response.
+func (rb *ResponseBuilder) RespondSuccess(body []byte) {
+ var statusCode int
+ if body == nil {
+ statusCode = http.StatusNoContent
+ } else {
+ statusCode = http.StatusOK
+ }
+
+ rb.Respond(statusCode, body)
+}
+
+// GameOfLife represents the state of a particular Game of Life instance.
+type GameOfLife struct {
+ sync.RWMutex
+
+ Generation uint
+ Living [][2]int64
+}
+
+// GameOfLifeAction represents an HTTP request for interacting with a
+// Game of Life instance.
+type GameOfLifeAction func(*http.Request, *GameOfLife, *ResponseBuilder)
+
+// CellStatusAction responds with the status of a cell.
+func CellStatusAction(
+ request *http.Request,
+ state *GameOfLife,
+ response *ResponseBuilder) {
+
+ state.RLock()
+ defer state.RUnlock()
+
+ type cellStatusResponse struct {
+ Alive bool `json:"alive"`
+ }
+
+ params := request.URL.Query()
+
+ var x int64
+ if xParam, ok := params["x"]; ok && len(xParam) > 0 {
+ var err error
+ x, err = strconv.ParseInt(xParam[0], 10, 0)
+ if err != nil {
+ response.RespondError(err)
+ return
+ }
+ } else {
+ response.RespondError(fmt.Errorf("Missing x parameter."))
+ return
+ }
+
+ var y int64
+ if yParam, ok := params["y"]; ok && len(yParam) > 0 {
+ var err error
+ y, err = strconv.ParseInt(yParam[0], 10, 0)
+ if err != nil {
+ response.RespondError(err)
+ return
+ }
+ } else {
+ response.RespondError(fmt.Errorf("Missing y parameter."))
+ return
+ }
+
+ alive := false
+ for _, cell := range state.Living {
+ if x == cell[0] && y == cell[1] {
+ alive = true
+ break
+ }
+ }
+
+ body, err := json.Marshal(cellStatusResponse{Alive: alive})
+ if err != nil {
+ response.RespondError(err)
+ return
+ }
+
+ response.RespondSuccess(body)
+}
+
+// GenerationAction responds with the current state of a Game of Life instance.
+func GenerationAction(
+ request *http.Request,
+ state *GameOfLife,
+ response *ResponseBuilder) {
+
+ state.RLock()
+ defer state.RUnlock()
+
+ body, err := json.Marshal(state)
+ if err != nil {
+ response.RespondError(err)
+ return
+ }
+
+ response.RespondSuccess(body)
+}
+
+// AddCellsAction adds living cells to a Game of Life instance.
+func AddCellsAction(
+ request *http.Request,
+ state *GameOfLife,
+ response *ResponseBuilder) {
+
+ state.Lock()
+ defer state.Unlock()
+
+ type cell struct {
+ X int64 `json:"x"`
+ Y int64 `json:"y"`
+ }
+
+ defer request.Body.Close()
+ body, err := ioutil.ReadAll(request.Body)
+ if err != nil {
+ response.RespondError(err)
+ return
+ }
+
+ var cells []cell
+ err = json.Unmarshal(body, &cells)
+ if err != nil {
+ response.RespondError(err)
+ return
+ }
+
+ for _, cell := range cells {
+ state.Living = append(state.Living, [2]int64{cell.X, cell.Y})
+ }
+
+ response.Respond(http.StatusCreated, nil)
+}
+
+// EvolveGenerationAction switches to the next iteration of a Game of Life instance.
+func EvolveGenerationAction(
+ request *http.Request,
+ state *GameOfLife,
+ response *ResponseBuilder) {
+
+ state.Lock()
+ defer state.Unlock()
+
+ histogram := map[int64]map[int64]uint{}
+
+ initRow := func(cell [2]int64) {
+ if histogram[cell[0]] == nil {
+ histogram[cell[0]-1] = map[int64]uint{}
+ histogram[cell[0]] = map[int64]uint{}
+ histogram[cell[0]+1] = map[int64]uint{}
+ }
+ }
+
+ updateColumn := func(cell [2]int64, side int64) {
+ histogram[cell[0]-1][cell[1]+side]++
+ histogram[cell[0]][cell[1]+side]++
+ histogram[cell[0]+1][cell[1]+side]++
+ }
+
+ for _, cell := range state.Living {
+ initRow(cell)
+
+ updateColumn(cell, -1)
+ updateColumn(cell, 0)
+ updateColumn(cell, 1)
+
+ histogram[cell[0]][cell[1]]--
+ }
+
+ var living [][2]int64
+ for x, row := range histogram {
+ for y, value := range row {
+ if value == 2 || value == 3 {
+ living = append(living, [2]int64{x, y})
+ }
+ }
+ }
+
+ state.Generation++
+ state.Living = living
+
+ response.RespondSuccess(nil)
+}
+
+// ResetAction resets a Game of Life instance to generation zero.
+func ResetAction(
+ request *http.Request,
+ state *GameOfLife,
+ response *ResponseBuilder) {
+
+ state.Lock()
+ defer state.Unlock()
+
+ state.Generation = 0
+ state.Living = nil
+
+ response.RespondSuccess(nil)
+}
+
+// GameOfLifeHandler is an HTTP handler presenting interface to a
+// Game of Life instance.
+type GameOfLifeHandler struct {
+ state *GameOfLife
+
+ actions map[string]map[string]GameOfLifeAction
+}
+
+// NewGameOfLifeHandler creates a new GameOfLifeHandler instance
+// passing an initial living cells configuration.
+func NewGameOfLifeHandler(initialCells [][2]int64) *GameOfLifeHandler {
+ return &GameOfLifeHandler{
+ state: &GameOfLife{
+ Generation: 0,
+ Living: initialCells,
+ },
+ actions: map[string]map[string]GameOfLifeAction{
+ "/cell/status/": map[string]GameOfLifeAction{
+ "GET": CellStatusAction,
+ },
+ "/generation/": map[string]GameOfLifeAction{
+ "GET": GenerationAction,
+ },
+ "/cells/": map[string]GameOfLifeAction{
+ "POST": AddCellsAction,
+ },
+ "/generation/evolve/": map[string]GameOfLifeAction{
+ "POST": EvolveGenerationAction,
+ },
+ "/reset/": map[string]GameOfLifeAction{
+ "POST": ResetAction,
+ },
+ },
+ }
+}
+
+// ServeHTTP handles an HTTP request.
+func (g *GameOfLifeHandler) ServeHTTP(
+ writer http.ResponseWriter,
+ request *http.Request) {
+
+ response := &ResponseBuilder{writer: writer}
+ action, ok := g.actions[request.URL.Path]
+ if !ok {
+ response.Respond(http.StatusNotFound,
+ []byte("URL is not supported."))
+ return
+ }
+
+ handler, ok := action[request.Method]
+ if !ok {
+ response.Respond(http.StatusMethodNotAllowed,
+ []byte("Method is not supported."))
+ return
+ }
+
+ handler(request, g.state, response)
+}
Добро решение, харесва ми map-a с методи. Ако нямаше ServeMux щеше да най - добрия подход из между решенията!
Пиша ти за един съвет. Опитвал ли си метода си за пресмятане на нова генерация? Моето предположение е, че си го пропуснал в тестовете си :) Напиши няколко които го проверяват конкретно.