-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain.go
154 lines (130 loc) · 3.08 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
package main
import (
"fmt"
"math/rand"
"os"
"strconv"
"strings"
"time"
)
type cell struct {
x int
y int
alive bool
nextAliveStatus bool
}
type game struct {
rows int
cols int
cellsMatrix [][]*cell
}
func (game *game) genRandomState() {
// Generate a random alive value for every cell
for row := range game.cellsMatrix {
for _, cell := range game.cellsMatrix[row] {
rand.Seed(time.Now().UnixNano())
cell.alive = rand.Intn(8) == 1
}
}
}
func (game *game) getAliveCellNeighbors(cell *cell) int {
// Find the number of alive cell neighbors of the given cell
var aliveNeighbors int
around := [8][2]int{{-1, -1}, {-1, 0}, {-1, 1}, {0, -1}, {0, 1}, {1, -1}, {1, 0}, {1, 1}}
for _, positions := range around {
x := cell.x + positions[1]
y := cell.y + positions[0]
// Handle cases when we hit a wall
if x < 0 || y < 0 || x >= game.cols || y >= game.rows {
continue
}
if game.cellsMatrix[y][x].alive {
aliveNeighbors++
}
}
return aliveNeighbors
}
func (game *game) NextGeneration() {
// Calculate the next generation of the game following the 4 Conway's Game of Life rules
for row := range game.cellsMatrix {
for _, cell := range game.cellsMatrix[row] {
aliveNeighbors := game.getAliveCellNeighbors(cell)
if cell.alive && aliveNeighbors <= 2 {
cell.nextAliveStatus = false
}
if cell.alive && aliveNeighbors > 3 {
cell.nextAliveStatus = false
}
if cell.alive && (aliveNeighbors == 2 || aliveNeighbors == 3) {
cell.nextAliveStatus = true
}
if !cell.alive && aliveNeighbors == 3 {
cell.nextAliveStatus = true
}
}
}
// TODO: This is probably bad, look for a better and optimized way
for row := range game.cellsMatrix {
for _, cell := range game.cellsMatrix[row] {
cell.alive = cell.nextAliveStatus
}
}
}
func NewGame(rows int, cols int) *game {
game := game{
rows: rows,
cols: cols,
}
// Create cells acording to the given rows and columns
for r := 0; r <= rows-1; r++ {
var newRow []*cell
for c := 0; c <= cols-1; c++ {
newRow = append(newRow, &cell{x: c, y: r, alive: false})
}
game.cellsMatrix = append(game.cellsMatrix, newRow)
}
game.genRandomState()
return &game
}
func renderGame(game *game) {
// Prints a char if the cell is alive
var renderString strings.Builder
for row := range game.cellsMatrix {
for _, cell := range game.cellsMatrix[row] {
if cell.alive {
renderString.WriteString("██")
} else {
renderString.WriteString(" ")
}
}
renderString.WriteString("\n")
}
fmt.Printf("%s", renderString.String())
}
func main() {
var err error
var cols int
var rows int
// Validate and use args for setting cols and rows
if len(os.Args) == 3 {
cols, err = strconv.Atoi(os.Args[1])
if err != nil {
panic("Invalid arguments")
}
rows, err = strconv.Atoi(os.Args[2])
if err != nil {
panic("Invalid arguments")
}
} else {
cols = 50
rows = 20
}
game := NewGame(rows, cols)
// Loop
for {
fmt.Print("\033[H\033[2J")
renderGame(game)
game.NextGeneration()
time.Sleep(100 * time.Millisecond)
}
}