Skip to content

Commit

Permalink
Merge pull request #920 from vaskoz/day455
Browse files Browse the repository at this point in the history
Day455
  • Loading branch information
vaskoz committed Feb 19, 2020
2 parents 671d36a + 2178b67 commit 93f43dd
Show file tree
Hide file tree
Showing 3 changed files with 197 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -452,4 +452,5 @@ problems from
* [Day 452](https://github.com/vaskoz/dailycodingproblem-go/issues/913)
* [Day 453](https://github.com/vaskoz/dailycodingproblem-go/issues/915)
* [Day 454](https://github.com/vaskoz/dailycodingproblem-go/issues/917)
* [Day 455](https://github.com/vaskoz/dailycodingproblem-go/issues/919)

152 changes: 152 additions & 0 deletions day455/problem.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
package day455

import (
"math"
"strings"
)

// Coord represents a coordinate in the game of life.
type Coord struct {
X, Y int64
}

// GameOfLife represents the game state.
// '*' represents a live cell and '.' represents a dead cell.
type GameOfLife struct {
living map[Coord]struct{}
minX, minY, maxX, maxY int64
}

// NewGameOfLife parses a starting board.
// The given board's lower left coordinate with be 0,0
func NewGameOfLife(board string) *GameOfLife {
gol := &GameOfLife{living: make(map[Coord]struct{})}
rows := strings.Split(board, "\n")
gol.maxY = int64(len(rows))

for y := 0; y < len(rows); y++ {
for x, val := range rows[len(rows)-1-y] {
if val == '*' {
gol.living[Coord{int64(x), int64(y)}] = struct{}{}
if int64(x) >= gol.maxX {
gol.maxX = int64(x) + 1
}
}
}
}

gol.minX, gol.minY = -1, -1

return gol
}

// String returns the string representation of the board now.
func (gol *GameOfLife) String() string {
var sb strings.Builder

for y := gol.maxY - 1; y > gol.minY; y-- {
for x := gol.minX + 1; x < gol.maxX; x++ {
if _, alive := gol.living[Coord{x, y}]; alive {
sb.WriteRune('*') // nolint: gosec
} else {
sb.WriteRune('.') // nolint: gosec
}
}
sb.WriteRune('\n') // nolint: gosec
}

return sb.String()
}

// Step executes a single step of the simulation.
func (gol *GameOfLife) Step() {
birth, death := make([]Coord, 0), make([]Coord, 0)

for coord := range gol.living {
if gol.willDie(coord) {
death = append(death, coord)
}

birth = append(birth, gol.willBeBorn(coord)...)
}

for _, d := range death {
delete(gol.living, d)
}

for _, b := range birth {
gol.living[b] = struct{}{}
}

gol.minX, gol.minY, gol.maxX, gol.maxY = math.MaxInt64, math.MaxInt64, math.MinInt64, math.MinInt64

for coord := range gol.living {
if coord.X <= gol.minX {
gol.minX = coord.X - 1
}

if coord.X >= gol.maxX {
gol.maxX = coord.X + 1
}

if coord.Y <= gol.minY {
gol.minY = coord.Y - 1
}

if coord.Y >= gol.maxY {
gol.maxY = coord.Y + 1
}
}
}

func (gol *GameOfLife) livingNeighbors(coord Coord) int {
livingNeighbors := 0

for x := coord.X - 1; x <= coord.X+1; x++ {
for y := coord.Y - 1; y <= coord.Y+1; y++ {
if x == coord.X && y == coord.Y {
continue
}

if _, alive := gol.living[Coord{x, y}]; alive {
livingNeighbors++
}
}
}

return livingNeighbors
}

func (gol *GameOfLife) willBeBorn(coord Coord) []Coord {
birth := make([]Coord, 0)
checked := make(map[Coord]struct{})

for x := coord.X - 1; x <= coord.X+1; x++ {
for y := coord.Y - 1; y <= coord.Y+1; y++ {
if x == coord.X && y == coord.Y {
continue
}

coord := Coord{x, y}

if _, alive := gol.living[coord]; alive {
continue
}

if _, done := checked[coord]; !done {
checked[coord] = struct{}{}

if living := gol.livingNeighbors(coord); living == 3 {
birth = append(birth, coord)
}
}
}
}

return birth
}

func (gol *GameOfLife) willDie(coord Coord) bool {
living := gol.livingNeighbors(coord)
return living < 2 || living > 3
}
44 changes: 44 additions & 0 deletions day455/problem_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package day455

import "testing"

// nolint
var testcases = []struct {
board string
steps int
expected string
}{
{"*\n*\n*", 1, "***\n"},
{"*\n*\n*", 2, "*\n*\n*\n"},
{"**\n**", 2, "**\n**\n"},
{"**\n*.", 1, "**\n**\n"},
{"..*\n.*.\n*..", 1, "*\n"},
{"**.\n*.*\n.*.", 2, "**.\n*.*\n.*.\n"},
{"**.\n*.*\n.*.", 20, "**.\n*.*\n.*.\n"},
}

func TestGameOfLife(t *testing.T) {
t.Parallel()

for n, tc := range testcases {
gol := NewGameOfLife(tc.board)

for i := 0; i < tc.steps; i++ {
gol.Step()
}

if result := gol.String(); result != tc.expected {
t.Errorf("TC%d Expected %v got %v", n, tc.expected, result)
}
}
}

func BenchmarkGameOfLife(b *testing.B) {
for i := 0; i < b.N; i++ {
for _, tc := range testcases {
gol := NewGameOfLife(tc.board)
gol.Step()
_ = gol.String()
}
}
}

0 comments on commit 93f43dd

Please sign in to comment.