Решение на Game of Life от Анонимен Потребител 1

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

Към профила на Анонимен Потребител 1

Резултати

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

Код

package main
import (
"encoding/json"
"fmt"
"html"
"io"
"io/ioutil"
"net/http"
"strconv"
)
type GameOfLifeHandler struct {
Living [][2]int64 `json:"living"`
Generation int `json:"generation"`
}
type Coordinate struct {
X int64 `json:"x"`
Y int64 `json:"y"`
}
func NewGameOfLifeHandler(m [][2]int64) *GameOfLifeHandler {
return &GameOfLifeHandler{m, 0}
}
func (h *GameOfLifeHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
path := html.EscapeString(r.URL.Path)
w.Header().Set("Content-Type", "application/json")
if r.Method == "GET" {
if path == "/cell/status/" {
xVal := r.FormValue("x")
yVal := r.FormValue("y")
x, e1 := strconv.ParseInt(xVal, 10, 64)
y, e2 := strconv.ParseInt(yVal, 10, 64)
if e1 != nil || e2 != nil {
http.Error(w, "Invalid x and y coordinates.", http.StatusBadRequest)
return
}
io.WriteString(w, fmt.Sprintf("{\"alive\": %t}", h.isAlive(x, y)))
} else if path == "/generation" {
b, _ := json.Marshal(h)
w.Write(b)
}
} else if r.Method == "POST" {
switch path {
case "/cells":
coords := make([]Coordinate, 0)
bs, _ := ioutil.ReadAll(r.Body)
err := json.Unmarshal(bs, &coords)
if err != nil {
http.Error(w, "Wrong JSON format.", http.StatusBadRequest)
return
}
if h == nil {
h = NewGameOfLifeHandler([][2]int64{})
}
for _, coord := range coords {
if !h.isAlive(coord.X, coord.Y) {
h.Living = append(h.Living, [2]int64{coord.X, coord.Y})
}
}
w.WriteHeader(http.StatusCreated)
case "/generation/evolve/":
h.evolve()
w.WriteHeader(http.StatusNoContent)
case "/reset":
h.Living = h.Living[:0]
h.Generation = 0
w.WriteHeader(http.StatusNoContent)
}
}
}
func (h *GameOfLifeHandler) evolve() {
h.Generation += 1
newSlice := make([][2]int64, len(h.Living), 2*cap(h.Living))
copy(newSlice, h.Living)
deleted := 0
// step 1 - kill over- or underpopulated cells
for i, coord := range h.Living {
x := coord[0]
y := coord[1]
neighbors := h.countNeighbors(x, y)
if neighbors < 2 || neighbors > 3 {
newSlice = append(newSlice[:i-deleted], newSlice[i-deleted+1:]...)
deleted += 1
}
}
// step 2 - reproduce dead cells with 3 neighbours
for _, coord := range h.Living {
x := coord[0]
y := coord[1]
if h.shouldReproduce(x-1, y-1) {
newSlice = append(newSlice, [2]int64{x - 1, y - 1})
}
if h.shouldReproduce(x, y-1) {
newSlice = append(newSlice, [2]int64{x, y - 1})
}
if h.shouldReproduce(x+1, y-1) {
newSlice = append(newSlice, [2]int64{x + 1, y - 1})
}
if h.shouldReproduce(x-1, y) {
newSlice = append(newSlice, [2]int64{x - 1, y})
}
if h.shouldReproduce(x+1, y) {
newSlice = append(newSlice, [2]int64{x + 1, y})
}
if h.shouldReproduce(x-1, y+1) {
newSlice = append(newSlice, [2]int64{x - 1, y + 1})
}
if h.shouldReproduce(x, y+1) {
newSlice = append(newSlice, [2]int64{x, y + 1})
}
if h.shouldReproduce(x+1, y+1) {
newSlice = append(newSlice, [2]int64{x + 1, y + 1})
}
}
copy(h.Living, newSlice)
}
func (h *GameOfLifeHandler) shouldReproduce(x int64, y int64) bool {
return !h.isAlive(x, y) && h.countNeighbors(x, y) == 3
}
func (h *GameOfLifeHandler) countNeighbors(x int64, y int64) int {
count := 0
for _, coord := range h.Living {
if coord[0] == x && coord[1] == y {
continue
}
deltaX := coord[0] - x
deltaY := coord[1] - y
if deltaX >= -1 && deltaX <= 1 && deltaY >= -1 && deltaY <= 1 {
count += 1
}
}
return count
}
func (h *GameOfLifeHandler) isAlive(x int64, y int64) bool {
for _, coord := range h.Living {
if coord[0] == x && coord[1] == y {
return true
}
}
return false
}

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

PASS
ok  	_/tmp/d20160126-5892-134x1kc	0.006s
PASS
ok  	_/tmp/d20160126-5892-134x1kc	0.006s
--- FAIL: TestCreatingEmptyBoard (0.00s)
	solution_test.go:77: Error decoding json: unexpected end of JSON input
FAIL
exit status 1
FAIL	_/tmp/d20160126-5892-134x1kc	0.007s
PASS
ok  	_/tmp/d20160126-5892-134x1kc	0.008s
--- FAIL: TestGenerationCounting (0.00s)
	solution_test.go:157: error unmarshaling generation status: unexpected end of JSON input
		Full response: 
FAIL
exit status 1
FAIL	_/tmp/d20160126-5892-134x1kc	0.026s
--- FAIL: TestBoardReset (0.00s)
	solution_test.go:180: Expected clearing the board to respond with 204 but it was 200
	solution_test.go:186: error unmarshaling generation status: unexpected end of JSON input
		Full response: 
FAIL
exit status 1
FAIL	_/tmp/d20160126-5892-134x1kc	0.007s
--- FAIL: TestAddingCells (0.00s)
	solution_test.go:225: Expected status Created but it was 200 with response: 
	solution_test.go:231: Error getting generation status: error unmarshaling generation status: unexpected end of JSON input
		Full response: 
FAIL
exit status 1
FAIL	_/tmp/d20160126-5892-134x1kc	0.007s
--- FAIL: TestWrongHTTPMethods (0.00s)
	solution_test.go:300: Expected method not allowed (405) for GET /generation/evolve/ but it was 200
	solution_test.go:300: Expected method not allowed (405) for PUT /generation/evolve/ but it was 200
	solution_test.go:300: Expected method not allowed (405) for DELETE /generation/evolve/ but it was 200
	solution_test.go:300: Expected method not allowed (405) for POST /generation/ but it was 200
	solution_test.go:300: Expected method not allowed (405) for PUT /generation/ but it was 200
	solution_test.go:300: Expected method not allowed (405) for DELETE /generation/ but it was 200
	solution_test.go:300: Expected method not allowed (405) for GET /cells/ but it was 200
	solution_test.go:300: Expected method not allowed (405) for PUT /cells/ but it was 200
	solution_test.go:300: Expected method not allowed (405) for DELETE /cells/ but it was 200
	solution_test.go:300: Expected method not allowed (405) for GET /reset/ but it was 200
	solution_test.go:300: Expected method not allowed (405) for PUT /reset/ but it was 200
	solution_test.go:300: Expected method not allowed (405) for DELETE /reset/ but it was 200
	solution_test.go:300: Expected method not allowed (405) for POST /cell/status/ but it was 200
	solution_test.go:300: Expected method not allowed (405) for PUT /cell/status/ but it was 200
	solution_test.go:300: Expected method not allowed (405) for DELETE /cell/status/ but it was 200
FAIL
exit status 1
FAIL	_/tmp/d20160126-5892-134x1kc	0.010s
--- FAIL: TestWrongURLs (0.00s)
	solution_test.go:324: Did not receive Not Found (404) but 200 for /some/url/
	solution_test.go:324: Did not receive Not Found (404) but 200 for /other/
	solution_test.go:324: Did not receive Not Found (404) but 200 for /does/not/exists/
FAIL
exit status 1
FAIL	_/tmp/d20160126-5892-134x1kc	0.007s
PASS
ok  	_/tmp/d20160126-5892-134x1kc	0.008s
--- FAIL: TestBoardEvolutionBasicRules (0.00s)
	solution_test.go:727: Expected cell [1 1] to be dead in test "Dies of underpopulation"
	solution_test.go:716: Expected cell [1 0] to be alive in test "Alive if has enough neighbours"
FAIL
exit status 1
FAIL	_/tmp/d20160126-5892-134x1kc	0.010s
PASS
ok  	_/tmp/d20160126-5892-134x1kc	0.010s
PASS
ok  	_/tmp/d20160126-5892-134x1kc	0.012s
PASS
ok  	_/tmp/d20160126-5892-134x1kc	0.058s

История (5 версии и 3 коментара)

Антоан обнови решението на 17.01.2016 01:16 (преди над 2 години)

+package main
+
+import (
+ "encoding/json"
+ "fmt"
+ "html"
+ "io/ioutil"
+ "net/http"
+ "strconv"
+)
+
+type GameOfLifeHandler struct {
+ Living [][2]int64 `json:"living"`
+ Generation int `json:"generation"`
+}
+
+type Coordinate struct {
+ X int64 `json:"x"`
+ Y int64 `json:"y"`
+}
+
+func NewGameOfLifeHandler(m [][2]int64) *GameOfLifeHandler {
+ return &GameOfLifeHandler{m, 0}
+}
+
+func (h *GameOfLifeHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+ path := html.EscapeString(r.URL.Path)
+ w.Header().Set("Content-Type", "application/json")
+
+ if r.Method == "GET" {
+ if path == "/cell/status/" {
+
+ xVal := r.FormValue("x")
+ yVal := r.FormValue("y")
+
+ x, e1 := strconv.ParseInt(xVal, 10, 64)
+ y, e2 := strconv.ParseInt(yVal, 10, 64)
+
+ if e1 != nil || e2 != nil {
+ w.Write([]byte("Invalid x and y coordinates."))
+ w.WriteHeader(400)
+ return
+ }
+
+ w.Write([]byte(fmt.Sprintf("{\"alive\": %t}", h.isAlive(x, y))))
+ w.WriteHeader(204)
+ } else if path == "/generation" {
+ b, _ := json.Marshal(h)
+ w.Write([]byte(b))
+ w.WriteHeader(204)
+ }
+ } else if r.Method == "POST" {
+ switch path {
+ case "/cells":
+ coords := make([]Coordinate, 0)
+ bs, _ := ioutil.ReadAll(r.Body)
+ err := json.Unmarshal(bs, &coords)
+ if err != nil {
+ w.Write([]byte("Wrong JSON format."))
+ w.WriteHeader(400)
+ return
+ }
+
+ if h == nil {
+ h = NewGameOfLifeHandler([][2]int64{})
+ }
+
+ for _, coord := range coords {
+ if !h.isAlive(coord.X, coord.Y) {
+ h.Living = append(h.Living, [2]int64{coord.X, coord.Y})
+ }
+ }
+
+ w.WriteHeader(201)
+ case "/generation/evolve/":
+ h.evolve()
+ w.WriteHeader(204)
+
+ case "/reset":
+ h.Living = h.Living[:0]
+ h.Generation = 0
+ w.WriteHeader(204)
+ }
+ }
+}
+
+func (h *GameOfLifeHandler) evolve() {
+ h.Generation += 1
+
+ newSlice := make([][2]int64, len(h.Living), 2*cap(h.Living))
+ copy(newSlice, h.Living)
+
+ deleted := 0
+
+ // step 1 - kill over- or underpopulated cells
+ for i, coord := range h.Living {
+ x := coord[0]
+ y := coord[1]
+ neighbors := h.countNeighbors(x, y)
+
+ if neighbors < 2 || neighbors > 3 {
+ newSlice = append(newSlice[:i-deleted], newSlice[i-deleted+1:]...)
+ deleted += 1
+ }
+ }
+
+ // step 2 - reproduce dead cells with 3 neighbours
+ for _, coord := range h.Living {
+ x := coord[0]
+ y := coord[1]
+
+ if h.shouldReproduce(x-1, y-1) {
+ newSlice = append(newSlice, [2]int64{x - 1, y - 1})
+ }
+
+ if h.shouldReproduce(x, y-1) {
+ newSlice = append(newSlice, [2]int64{x, y - 1})
+ }
+
+ if h.shouldReproduce(x+1, y-1) {
+ newSlice = append(newSlice, [2]int64{x + 1, y - 1})
+ }
+
+ if h.shouldReproduce(x-1, y) {
+ newSlice = append(newSlice, [2]int64{x - 1, y})
+ }
+
+ if h.shouldReproduce(x+1, y) {
+ newSlice = append(newSlice, [2]int64{x + 1, y})
+ }
+
+ if h.shouldReproduce(x-1, y+1) {
+ newSlice = append(newSlice, [2]int64{x - 1, y + 1})
+ }
+
+ if h.shouldReproduce(x, y+1) {
+ newSlice = append(newSlice, [2]int64{x, y + 1})
+ }
+
+ if h.shouldReproduce(x+1, y+1) {
+ newSlice = append(newSlice, [2]int64{x + 1, y + 1})
+ }
+ }
+
+ copy(h.Living, newSlice)
+}
+
+func (h *GameOfLifeHandler) shouldReproduce(x int64, y int64) bool {
+ return !h.isAlive(x, y) && h.countNeighbors(x, y) == 3
+}
+
+func (h *GameOfLifeHandler) countNeighbors(x int64, y int64) int {
+ count := 0
+ for _, coord := range h.Living {
+ if coord[0] == x && coord[1] == y {
+ continue
+ }
+
+ deltaX := coord[0] - x
+ deltaY := coord[1] - y
+ if deltaX >= -1 && deltaX <= 1 && deltaY >= -1 && deltaY <= 1 {
+ count += 1
+ }
+ }
+
+ return count
+}
+
+func (h *GameOfLifeHandler) isAlive(x int64, y int64) bool {
+ for _, coord := range h.Living {
+ if coord[0] == x && coord[1] == y {
+ return true
+ }
+ }
+
+ return false
+}

Mного добре! Подхода от slice с координати на живи клетки прави решението ти да заема много малко памет. Дори си се сетил да сложиш Content-Type.

Обаче мисля, че можеш да го направиш по - хубаво. Имаш и много време за това т.к. си предал задачата доволно рано.

Мисля си, че секцията с няколко вложени if-а и switch-а е доста трудна за четене. Ако разгледаш внимателно http пакета ще намериш полезни неща, които ще ти да позволят на пренапишеш тази секция по - ясно.

Виждам, че използваш fmt за писане на отговори. Разгледай този пакет по - внимателно и ще намериш по - хубав начин да напишеш неща като w.Write([]byte(fmt.Sprintf(...))), които виждам. Може да стане не само по - четимо, но и доста по - ефективно.

Също така си мисля, че не използваш WriteHeader по начина, по който си възнамерявал. Прочети внимателно документацията на ResponseWriter. И поглед на константите ще е полезен.

Давам ти само насоки вместо да ти казвам кое, според мен, е добра идея. Сигурен съм, че ще ги намериш сам и процеса ще ти е по - интересен по този начин.

Антоан обнови решението на 18.01.2016 01:05 (преди над 2 години)

package main
import (
"encoding/json"
"fmt"
"html"
+ "io"
"io/ioutil"
"net/http"
"strconv"
)
type GameOfLifeHandler struct {
Living [][2]int64 `json:"living"`
Generation int `json:"generation"`
}
type Coordinate struct {
X int64 `json:"x"`
Y int64 `json:"y"`
}
func NewGameOfLifeHandler(m [][2]int64) *GameOfLifeHandler {
return &GameOfLifeHandler{m, 0}
}
func (h *GameOfLifeHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
path := html.EscapeString(r.URL.Path)
w.Header().Set("Content-Type", "application/json")
if r.Method == "GET" {
if path == "/cell/status/" {
xVal := r.FormValue("x")
yVal := r.FormValue("y")
x, e1 := strconv.ParseInt(xVal, 10, 64)
y, e2 := strconv.ParseInt(yVal, 10, 64)
if e1 != nil || e2 != nil {
- w.Write([]byte("Invalid x and y coordinates."))
- w.WriteHeader(400)
+ http.Error(w, "Invalid x and y coordinates.", http.StatusBadRequest)
return
}
- w.Write([]byte(fmt.Sprintf("{\"alive\": %t}", h.isAlive(x, y))))
+ io.WriteString(w, fmt.Sprintf("{\"alive\": %t}", h.isAlive(x, y)))
w.WriteHeader(204)
} else if path == "/generation" {
b, _ := json.Marshal(h)
w.Write([]byte(b))
w.WriteHeader(204)
}
} else if r.Method == "POST" {
switch path {
case "/cells":
coords := make([]Coordinate, 0)
bs, _ := ioutil.ReadAll(r.Body)
err := json.Unmarshal(bs, &coords)
if err != nil {
- w.Write([]byte("Wrong JSON format."))
- w.WriteHeader(400)
+ http.Error(w, "Wrong JSON format.", http.StatusBadRequest)
return
}
if h == nil {
h = NewGameOfLifeHandler([][2]int64{})
}
for _, coord := range coords {
if !h.isAlive(coord.X, coord.Y) {
h.Living = append(h.Living, [2]int64{coord.X, coord.Y})
}
}
w.WriteHeader(201)
case "/generation/evolve/":
h.evolve()
w.WriteHeader(204)
case "/reset":
h.Living = h.Living[:0]
h.Generation = 0
w.WriteHeader(204)
}
}
}
func (h *GameOfLifeHandler) evolve() {
h.Generation += 1
newSlice := make([][2]int64, len(h.Living), 2*cap(h.Living))
copy(newSlice, h.Living)
deleted := 0
// step 1 - kill over- or underpopulated cells
for i, coord := range h.Living {
x := coord[0]
y := coord[1]
neighbors := h.countNeighbors(x, y)
if neighbors < 2 || neighbors > 3 {
newSlice = append(newSlice[:i-deleted], newSlice[i-deleted+1:]...)
deleted += 1
}
}
// step 2 - reproduce dead cells with 3 neighbours
for _, coord := range h.Living {
x := coord[0]
y := coord[1]
if h.shouldReproduce(x-1, y-1) {
newSlice = append(newSlice, [2]int64{x - 1, y - 1})
}
if h.shouldReproduce(x, y-1) {
newSlice = append(newSlice, [2]int64{x, y - 1})
}
if h.shouldReproduce(x+1, y-1) {
newSlice = append(newSlice, [2]int64{x + 1, y - 1})
}
if h.shouldReproduce(x-1, y) {
newSlice = append(newSlice, [2]int64{x - 1, y})
}
if h.shouldReproduce(x+1, y) {
newSlice = append(newSlice, [2]int64{x + 1, y})
}
if h.shouldReproduce(x-1, y+1) {
newSlice = append(newSlice, [2]int64{x - 1, y + 1})
}
if h.shouldReproduce(x, y+1) {
newSlice = append(newSlice, [2]int64{x, y + 1})
}
if h.shouldReproduce(x+1, y+1) {
newSlice = append(newSlice, [2]int64{x + 1, y + 1})
}
}
copy(h.Living, newSlice)
}
func (h *GameOfLifeHandler) shouldReproduce(x int64, y int64) bool {
return !h.isAlive(x, y) && h.countNeighbors(x, y) == 3
}
func (h *GameOfLifeHandler) countNeighbors(x int64, y int64) int {
count := 0
for _, coord := range h.Living {
if coord[0] == x && coord[1] == y {
continue
}
deltaX := coord[0] - x
deltaY := coord[1] - y
if deltaX >= -1 && deltaX <= 1 && deltaY >= -1 && deltaY <= 1 {
count += 1
}
}
return count
}
func (h *GameOfLifeHandler) isAlive(x int64, y int64) bool {
for _, coord := range h.Living {
if coord[0] == x && coord[1] == y {
return true
}
}
return false
-}
+}

Антоан обнови решението на 18.01.2016 01:13 (преди над 2 години)

package main
import (
"encoding/json"
"fmt"
"html"
"io"
"io/ioutil"
"net/http"
"strconv"
)
type GameOfLifeHandler struct {
Living [][2]int64 `json:"living"`
Generation int `json:"generation"`
}
type Coordinate struct {
X int64 `json:"x"`
Y int64 `json:"y"`
}
func NewGameOfLifeHandler(m [][2]int64) *GameOfLifeHandler {
return &GameOfLifeHandler{m, 0}
}
func (h *GameOfLifeHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
path := html.EscapeString(r.URL.Path)
w.Header().Set("Content-Type", "application/json")
if r.Method == "GET" {
if path == "/cell/status/" {
xVal := r.FormValue("x")
yVal := r.FormValue("y")
x, e1 := strconv.ParseInt(xVal, 10, 64)
y, e2 := strconv.ParseInt(yVal, 10, 64)
if e1 != nil || e2 != nil {
http.Error(w, "Invalid x and y coordinates.", http.StatusBadRequest)
return
}
io.WriteString(w, fmt.Sprintf("{\"alive\": %t}", h.isAlive(x, y)))
- w.WriteHeader(204)
+ w.WriteHeader(http.StatusNoContent)
} else if path == "/generation" {
b, _ := json.Marshal(h)
- w.Write([]byte(b))
- w.WriteHeader(204)
+ w.Write(b)
+ w.WriteHeader(http.StatusNoContent)
}
} else if r.Method == "POST" {
switch path {
case "/cells":
coords := make([]Coordinate, 0)
bs, _ := ioutil.ReadAll(r.Body)
err := json.Unmarshal(bs, &coords)
if err != nil {
http.Error(w, "Wrong JSON format.", http.StatusBadRequest)
return
}
if h == nil {
h = NewGameOfLifeHandler([][2]int64{})
}
for _, coord := range coords {
if !h.isAlive(coord.X, coord.Y) {
h.Living = append(h.Living, [2]int64{coord.X, coord.Y})
}
}
- w.WriteHeader(201)
+ w.WriteHeader(http.StatusCreated)
case "/generation/evolve/":
h.evolve()
- w.WriteHeader(204)
+ w.WriteHeader(http.StatusNoContent)
case "/reset":
h.Living = h.Living[:0]
h.Generation = 0
- w.WriteHeader(204)
+ w.WriteHeader(http.StatusNoContent)
}
}
}
func (h *GameOfLifeHandler) evolve() {
h.Generation += 1
newSlice := make([][2]int64, len(h.Living), 2*cap(h.Living))
copy(newSlice, h.Living)
deleted := 0
// step 1 - kill over- or underpopulated cells
for i, coord := range h.Living {
x := coord[0]
y := coord[1]
neighbors := h.countNeighbors(x, y)
if neighbors < 2 || neighbors > 3 {
newSlice = append(newSlice[:i-deleted], newSlice[i-deleted+1:]...)
deleted += 1
}
}
// step 2 - reproduce dead cells with 3 neighbours
for _, coord := range h.Living {
x := coord[0]
y := coord[1]
if h.shouldReproduce(x-1, y-1) {
newSlice = append(newSlice, [2]int64{x - 1, y - 1})
}
if h.shouldReproduce(x, y-1) {
newSlice = append(newSlice, [2]int64{x, y - 1})
}
if h.shouldReproduce(x+1, y-1) {
newSlice = append(newSlice, [2]int64{x + 1, y - 1})
}
if h.shouldReproduce(x-1, y) {
newSlice = append(newSlice, [2]int64{x - 1, y})
}
if h.shouldReproduce(x+1, y) {
newSlice = append(newSlice, [2]int64{x + 1, y})
}
if h.shouldReproduce(x-1, y+1) {
newSlice = append(newSlice, [2]int64{x - 1, y + 1})
}
if h.shouldReproduce(x, y+1) {
newSlice = append(newSlice, [2]int64{x, y + 1})
}
if h.shouldReproduce(x+1, y+1) {
newSlice = append(newSlice, [2]int64{x + 1, y + 1})
}
}
copy(h.Living, newSlice)
}
func (h *GameOfLifeHandler) shouldReproduce(x int64, y int64) bool {
return !h.isAlive(x, y) && h.countNeighbors(x, y) == 3
}
func (h *GameOfLifeHandler) countNeighbors(x int64, y int64) int {
count := 0
for _, coord := range h.Living {
if coord[0] == x && coord[1] == y {
continue
}
deltaX := coord[0] - x
deltaY := coord[1] - y
if deltaX >= -1 && deltaX <= 1 && deltaY >= -1 && deltaY <= 1 {
count += 1
}
}
return count
}
func (h *GameOfLifeHandler) isAlive(x int64, y int64) bool {
for _, coord := range h.Living {
if coord[0] == x && coord[1] == y {
return true
}
}
return false
}

Антоан обнови решението на 18.01.2016 01:19 (преди над 2 години)

package main
import (
"encoding/json"
"fmt"
"html"
"io"
"io/ioutil"
"net/http"
"strconv"
)
type GameOfLifeHandler struct {
Living [][2]int64 `json:"living"`
Generation int `json:"generation"`
}
type Coordinate struct {
X int64 `json:"x"`
Y int64 `json:"y"`
}
func NewGameOfLifeHandler(m [][2]int64) *GameOfLifeHandler {
return &GameOfLifeHandler{m, 0}
}
func (h *GameOfLifeHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
path := html.EscapeString(r.URL.Path)
w.Header().Set("Content-Type", "application/json")
if r.Method == "GET" {
if path == "/cell/status/" {
xVal := r.FormValue("x")
yVal := r.FormValue("y")
x, e1 := strconv.ParseInt(xVal, 10, 64)
y, e2 := strconv.ParseInt(yVal, 10, 64)
if e1 != nil || e2 != nil {
http.Error(w, "Invalid x and y coordinates.", http.StatusBadRequest)
return
}
+ w.WriteHeader(http.StatusNoContent)
io.WriteString(w, fmt.Sprintf("{\"alive\": %t}", h.isAlive(x, y)))
- w.WriteHeader(http.StatusNoContent)
} else if path == "/generation" {
b, _ := json.Marshal(h)
- w.Write(b)
- w.WriteHeader(http.StatusNoContent)
+ w.WriteHeader(http.StatusNoContent)
+ w.Write(b)
}
} else if r.Method == "POST" {
switch path {
case "/cells":
coords := make([]Coordinate, 0)
bs, _ := ioutil.ReadAll(r.Body)
err := json.Unmarshal(bs, &coords)
if err != nil {
http.Error(w, "Wrong JSON format.", http.StatusBadRequest)
return
}
if h == nil {
h = NewGameOfLifeHandler([][2]int64{})
}
for _, coord := range coords {
if !h.isAlive(coord.X, coord.Y) {
h.Living = append(h.Living, [2]int64{coord.X, coord.Y})
}
}
w.WriteHeader(http.StatusCreated)
case "/generation/evolve/":
h.evolve()
w.WriteHeader(http.StatusNoContent)
case "/reset":
h.Living = h.Living[:0]
h.Generation = 0
w.WriteHeader(http.StatusNoContent)
}
}
}
func (h *GameOfLifeHandler) evolve() {
h.Generation += 1
newSlice := make([][2]int64, len(h.Living), 2*cap(h.Living))
copy(newSlice, h.Living)
deleted := 0
// step 1 - kill over- or underpopulated cells
for i, coord := range h.Living {
x := coord[0]
y := coord[1]
neighbors := h.countNeighbors(x, y)
if neighbors < 2 || neighbors > 3 {
newSlice = append(newSlice[:i-deleted], newSlice[i-deleted+1:]...)
deleted += 1
}
}
// step 2 - reproduce dead cells with 3 neighbours
for _, coord := range h.Living {
x := coord[0]
y := coord[1]
if h.shouldReproduce(x-1, y-1) {
newSlice = append(newSlice, [2]int64{x - 1, y - 1})
}
if h.shouldReproduce(x, y-1) {
newSlice = append(newSlice, [2]int64{x, y - 1})
}
if h.shouldReproduce(x+1, y-1) {
newSlice = append(newSlice, [2]int64{x + 1, y - 1})
}
if h.shouldReproduce(x-1, y) {
newSlice = append(newSlice, [2]int64{x - 1, y})
}
if h.shouldReproduce(x+1, y) {
newSlice = append(newSlice, [2]int64{x + 1, y})
}
if h.shouldReproduce(x-1, y+1) {
newSlice = append(newSlice, [2]int64{x - 1, y + 1})
}
if h.shouldReproduce(x, y+1) {
newSlice = append(newSlice, [2]int64{x, y + 1})
}
if h.shouldReproduce(x+1, y+1) {
newSlice = append(newSlice, [2]int64{x + 1, y + 1})
}
}
copy(h.Living, newSlice)
}
func (h *GameOfLifeHandler) shouldReproduce(x int64, y int64) bool {
return !h.isAlive(x, y) && h.countNeighbors(x, y) == 3
}
func (h *GameOfLifeHandler) countNeighbors(x int64, y int64) int {
count := 0
for _, coord := range h.Living {
if coord[0] == x && coord[1] == y {
continue
}
deltaX := coord[0] - x
deltaY := coord[1] - y
if deltaX >= -1 && deltaX <= 1 && deltaY >= -1 && deltaY <= 1 {
count += 1
}
}
return count
}
func (h *GameOfLifeHandler) isAlive(x int64, y int64) bool {
for _, coord := range h.Living {
if coord[0] == x && coord[1] == y {
return true
}
}
return false
-}
+}

Благодаря за обратната връзка. Мисля, че схванах почти всички насоки, които ми даде.

Относно преработването на кода със switch-а, единственото нещо, за което се сещам, е да използвам ServeMux, но тогава ще трябва да имам отделен handler за всяка заявка, а искаме handler-a да бъде само един - GameOfLifeHandler. Явно пропускам нещо.

EDIT: Ами ако GameOfLifeHandler наследява ServeMux и единствено във функцията NewGameOfLifeHandler добавя методите с mux.HandleFunc? Това използвана практика ли е в Go?

Да, това би бил много хубав подход!

Сега, на по - внимателно прочитане на кода ти, забелязах още няколко неща.

Не използваш gofmt и поради това кода ти страда от презентационни проблеми :Д Забележи колко странно е форматиран на сайта. Добра практика ще е да започнеш да го ползваш на всеки save на файла. Редактора ти трябва да може да ти помогне с това.

Пробвай по - внимателно отговорите на решението ти в случаите, в които дъската е празна, както и отговорите на GET /cell/status. Може би, напиши си тест за тях. Мога да ти кажа, че имаш regression след като направи последната промяна. Тестовете ти вероятно щяха да го намерят.

Антоан обнови решението на 26.01.2016 12:24 (преди над 2 години)

package main
import (
"encoding/json"
"fmt"
"html"
"io"
"io/ioutil"
"net/http"
"strconv"
)
type GameOfLifeHandler struct {
Living [][2]int64 `json:"living"`
Generation int `json:"generation"`
}
type Coordinate struct {
X int64 `json:"x"`
Y int64 `json:"y"`
}
func NewGameOfLifeHandler(m [][2]int64) *GameOfLifeHandler {
return &GameOfLifeHandler{m, 0}
}
func (h *GameOfLifeHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
path := html.EscapeString(r.URL.Path)
w.Header().Set("Content-Type", "application/json")
if r.Method == "GET" {
if path == "/cell/status/" {
xVal := r.FormValue("x")
yVal := r.FormValue("y")
x, e1 := strconv.ParseInt(xVal, 10, 64)
y, e2 := strconv.ParseInt(yVal, 10, 64)
if e1 != nil || e2 != nil {
http.Error(w, "Invalid x and y coordinates.", http.StatusBadRequest)
return
}
- w.WriteHeader(http.StatusNoContent)
io.WriteString(w, fmt.Sprintf("{\"alive\": %t}", h.isAlive(x, y)))
} else if path == "/generation" {
b, _ := json.Marshal(h)
- w.WriteHeader(http.StatusNoContent)
- w.Write(b)
+ w.Write(b)
}
} else if r.Method == "POST" {
switch path {
case "/cells":
coords := make([]Coordinate, 0)
bs, _ := ioutil.ReadAll(r.Body)
err := json.Unmarshal(bs, &coords)
if err != nil {
http.Error(w, "Wrong JSON format.", http.StatusBadRequest)
return
}
if h == nil {
h = NewGameOfLifeHandler([][2]int64{})
}
for _, coord := range coords {
if !h.isAlive(coord.X, coord.Y) {
h.Living = append(h.Living, [2]int64{coord.X, coord.Y})
}
}
w.WriteHeader(http.StatusCreated)
case "/generation/evolve/":
h.evolve()
w.WriteHeader(http.StatusNoContent)
case "/reset":
h.Living = h.Living[:0]
h.Generation = 0
w.WriteHeader(http.StatusNoContent)
}
}
}
func (h *GameOfLifeHandler) evolve() {
h.Generation += 1
newSlice := make([][2]int64, len(h.Living), 2*cap(h.Living))
copy(newSlice, h.Living)
deleted := 0
// step 1 - kill over- or underpopulated cells
for i, coord := range h.Living {
x := coord[0]
y := coord[1]
neighbors := h.countNeighbors(x, y)
if neighbors < 2 || neighbors > 3 {
newSlice = append(newSlice[:i-deleted], newSlice[i-deleted+1:]...)
deleted += 1
}
}
// step 2 - reproduce dead cells with 3 neighbours
for _, coord := range h.Living {
x := coord[0]
y := coord[1]
if h.shouldReproduce(x-1, y-1) {
newSlice = append(newSlice, [2]int64{x - 1, y - 1})
}
if h.shouldReproduce(x, y-1) {
newSlice = append(newSlice, [2]int64{x, y - 1})
}
if h.shouldReproduce(x+1, y-1) {
newSlice = append(newSlice, [2]int64{x + 1, y - 1})
}
if h.shouldReproduce(x-1, y) {
newSlice = append(newSlice, [2]int64{x - 1, y})
}
if h.shouldReproduce(x+1, y) {
newSlice = append(newSlice, [2]int64{x + 1, y})
}
if h.shouldReproduce(x-1, y+1) {
newSlice = append(newSlice, [2]int64{x - 1, y + 1})
}
if h.shouldReproduce(x, y+1) {
newSlice = append(newSlice, [2]int64{x, y + 1})
}
if h.shouldReproduce(x+1, y+1) {
newSlice = append(newSlice, [2]int64{x + 1, y + 1})
}
}
copy(h.Living, newSlice)
}
func (h *GameOfLifeHandler) shouldReproduce(x int64, y int64) bool {
return !h.isAlive(x, y) && h.countNeighbors(x, y) == 3
}
func (h *GameOfLifeHandler) countNeighbors(x int64, y int64) int {
count := 0
for _, coord := range h.Living {
if coord[0] == x && coord[1] == y {
continue
}
deltaX := coord[0] - x
deltaY := coord[1] - y
if deltaX >= -1 && deltaX <= 1 && deltaY >= -1 && deltaY <= 1 {
count += 1
}
}
return count
}
func (h *GameOfLifeHandler) isAlive(x int64, y int64) bool {
for _, coord := range h.Living {
if coord[0] == x && coord[1] == y {
return true
}
}
return false
-}
+}