Решение на Game of Life от Станислав Гатев

Обратно към всички решения

Към профила на Станислав Гатев

Резултати

  • 10 точки от тестове
  • 0 бонус точки
  • 10 точки общо
  • 14 успешни тест(а)
  • 0 неуспешни тест(а)

Код

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[[2]int64]uint{}
updateColumn := func(cell [2]int64, side int64) {
histogram[[2]int64{cell[0] - 1, cell[1] + side}]++
histogram[[2]int64{cell[0], cell[1] + side}]++
histogram[[2]int64{cell[0] + 1, cell[1] + side}]++
}
alive := map[[2]int64]struct{}{}
for _, cell := range state.Living {
updateColumn(cell, -1)
updateColumn(cell, 0)
updateColumn(cell, 1)
histogram[cell]--
alive[cell] = struct{}{}
}
var living [][2]int64
for cell, value := range histogram {
_, isAlive := alive[cell]
if (value == 2 && isAlive) || value == 3 {
living = append(living, cell)
}
}
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)
}

Лог от изпълнението

PASS
ok  	_/tmp/d20160126-5892-18a07z9	0.004s
PASS
ok  	_/tmp/d20160126-5892-18a07z9	0.005s
PASS
ok  	_/tmp/d20160126-5892-18a07z9	0.015s
PASS
ok  	_/tmp/d20160126-5892-18a07z9	0.008s
PASS
ok  	_/tmp/d20160126-5892-18a07z9	0.007s
PASS
ok  	_/tmp/d20160126-5892-18a07z9	0.006s
PASS
ok  	_/tmp/d20160126-5892-18a07z9	0.006s
PASS
ok  	_/tmp/d20160126-5892-18a07z9	0.011s
PASS
ok  	_/tmp/d20160126-5892-18a07z9	0.006s
PASS
ok  	_/tmp/d20160126-5892-18a07z9	0.010s
PASS
ok  	_/tmp/d20160126-5892-18a07z9	0.009s
PASS
ok  	_/tmp/d20160126-5892-18a07z9	0.009s
PASS
ok  	_/tmp/d20160126-5892-18a07z9	0.011s
PASS
ok  	_/tmp/d20160126-5892-18a07z9	0.065s

История (2 версии и 1 коментар)

Станислав обнови решението на 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 щеше да най - добрия подход из между решенията!

Пиша ти за един съвет. Опитвал ли си метода си за пресмятане на нова генерация? Моето предположение е, че си го пропуснал в тестовете си :) Напиши няколко които го проверяват конкретно.

Станислав обнови решението на 26.01.2016 12:17 (преди над 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{}
+ histogram := map[[2]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]++
+ histogram[[2]int64{cell[0] - 1, cell[1] + side}]++
+ histogram[[2]int64{cell[0], cell[1] + side}]++
+ histogram[[2]int64{cell[0] + 1, cell[1] + side}]++
}
+ alive := map[[2]int64]struct{}{}
for _, cell := range state.Living {
- initRow(cell)
-
updateColumn(cell, -1)
updateColumn(cell, 0)
updateColumn(cell, 1)
- histogram[cell[0]][cell[1]]--
+ histogram[cell]--
+ alive[cell] = struct{}{}
}
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})
- }
+ for cell, value := range histogram {
+ _, isAlive := alive[cell]
+ if (value == 2 && isAlive) || value == 3 {
+ living = append(living, cell)
}
}
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)
}