Compare commits

...

22 Commits

Author SHA1 Message Date
6eeba8f9d0 better logging 2024-04-15 01:39:59 +02:00
2bb893e9a8 local framerate constant 2024-04-15 01:39:43 +02:00
b462335db4 bugfix: cursor positioning and printing 2024-04-15 01:32:56 +02:00
00886d2e07 boundary wrapping 2024-04-15 01:26:37 +02:00
0c3d18afbe bugfix: mixed up dimensions 2024-04-15 01:26:20 +02:00
93322ad555 simulate in console 2024-04-15 01:25:33 +02:00
27f84eca71 installed packages 2024-04-15 01:22:32 +02:00
9f2f76aa70 localize grid variable 2024-04-15 00:55:34 +02:00
60a7e38c67 implement matrix display 2024-04-14 22:46:27 +02:00
0ad98fe992 clean up and added pixel endpoint 2024-04-14 22:45:50 +02:00
1138458e0a better i/o handling 2024-04-14 22:34:59 +02:00
c97d6c68e9 bugfix: don't want on terminated routine 2024-04-14 22:34:31 +02:00
34d9491421 bugfix: save new generation 2024-04-14 22:34:16 +02:00
287c65c28e bugfix: working 'alive' character 2024-04-14 22:02:46 +02:00
511a837495 randomize cell state 2024-04-14 22:01:42 +02:00
6e88f0f750 better time handling 2024-04-14 22:00:37 +02:00
3403886e48 insert setup call and wait for routine 2024-04-14 21:48:03 +02:00
30e6c4a35b comment old code for reference 2024-04-14 21:47:48 +02:00
80984f8243 prepare displaying game of life grid 2024-04-14 21:47:13 +02:00
b66f12b7c6 better setup structure 2024-04-14 21:29:40 +02:00
e57b501178 implement conways game of life 2024-04-14 21:20:26 +02:00
cbae0f6732 update packages 2024-04-14 21:19:07 +02:00
5 changed files with 160 additions and 33 deletions

View File

@ -1,7 +1,9 @@
package main package main
import ( import (
"github.com/go-resty/resty/v2" "errors"
"math/rand"
"time"
) )
type Cell struct { type Cell struct {
@ -10,20 +12,101 @@ type Cell struct {
live bool live bool
} }
func (c *Cell) NeighborCount() int { func (c *Cell) NeighborCount(arr [][]Cell) int {
// Read grid dimensions
width := len(arr)
height := len(arr[0])
count := 0
// Iterate through 3x3 neighboring grid
for i := -1; i <= 1; i++ {
for j := -1; j <= 1; j++ {
// Ignore own position
if i == 0 && j == 0 {
continue
}
// Get neighbor coordinates
nx := (c.x + i + width) % width
ny := (c.y + j + height) % height
// Count if neighbor is alive
if arr[nx][ny].live {
count++
}
}
}
return count
} }
func coordinatesToIndex(x int, y int) int { func initGrid(width, height int, parent ...[][]Cell) ([][]Cell, error) {
return x + y*width exists := len(parent) == 1
if exists && len(parent) > 1 {
return nil, errors.New("Too many parents specified")
}
// Make first dimension
cells := make([][]Cell, width)
for i := 0; i < width; i++ {
// Make second dimension
cells[i] = make([]Cell, height)
for j := 0; j < height; j++ {
// Make cells
cells[i][j].x = i
cells[i][j].y = j
// If specified, copy state from parent
if exists {
cells[i][j].live = parent[0][i][j].live
} else {
cells[i][j].live = rand.Intn(2) == 1
}
}
}
return cells, nil
} }
var grid []Cell func setup(callback func([][]Cell), width, height int, FPS time.Duration) chan bool {
// Initialize grid
grid, _ := initGrid(width, height)
func setup(client *resty.Client, url, width int, height int) { // Prepare ticker and finishing flag
ticker := time.NewTicker((1000 / FPS) * time.Millisecond)
done := make(chan bool)
// Run game loop
go func() {
for {
select {
case <-done:
return
case <-ticker.C:
grid = draw(grid, callback)
}
}
}()
// Return flag to be stopped outside
return done
} }
func draw() { func draw(grid [][]Cell, callback func([][]Cell)) [][]Cell {
generation, _ := initGrid(len(grid), len(grid[0]), grid)
// Iterate through grid
for i := 0; i < len(grid); i++ {
for j := 0; j < len(grid[0]); j++ {
// Count neighbors
cout := grid[i][j].NeighborCount(grid)
// Get state
curr := grid[i][j].live
// Apply rules
died := curr && (cout < 2 || cout > 3)
born := !curr && cout == 3
next := curr && !died || born
// Set state
generation[i][j].live = next
// A * !(A * (B + C)) + !A * D
// = A * !B * !C + !A * D
// curr && cout >= 2 && cout <= 3 || !curr && cout == 3
}
}
callback(generation)
return generation
} }

11
go.mod
View File

@ -3,7 +3,12 @@ module example.com/MatrixGameOfLife
go 1.22.1 go 1.22.1
require ( require (
github.com/go-resty/resty/v2 v2.12.0 // indirect github.com/go-resty/resty/v2 v2.12.0
github.com/joho/godotenv v1.5.1 // indirect github.com/joho/godotenv v1.5.1
golang.org/x/net v0.24.0 // indirect github.com/buger/goterm v1.0.4
)
require (
golang.org/x/net v0.24.0 // indirect
golang.org/x/sys v0.19.0 // indirect
) )

6
go.sum
View File

@ -1,3 +1,5 @@
github.com/buger/goterm v1.0.4 h1:Z9YvGmOih81P0FbVtEYTFF6YsSgxSUKEhf/f9bTMXbY=
github.com/buger/goterm v1.0.4/go.mod h1:HiFWV3xnkolgrBV3mY8m0X0Pumt4zg4QhbdOzQtB8tE=
github.com/go-resty/resty/v2 v2.12.0 h1:rsVL8P90LFvkUYq/V5BTVe203WfRIU4gvcf+yfzJzGA= github.com/go-resty/resty/v2 v2.12.0 h1:rsVL8P90LFvkUYq/V5BTVe203WfRIU4gvcf+yfzJzGA=
github.com/go-resty/resty/v2 v2.12.0/go.mod h1:o0yGPrkS3lOe1+eFajk6kBW8ScXzwU3hD69/gt2yB/0= github.com/go-resty/resty/v2 v2.12.0/go.mod h1:o0yGPrkS3lOe1+eFajk6kBW8ScXzwU3hD69/gt2yB/0=
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
@ -23,6 +25,7 @@ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210331175145-43e1dd70ce54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@ -30,6 +33,8 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
@ -42,6 +47,7 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=

54
main.go
View File

@ -1,13 +1,42 @@
package main package main
import ( import (
"fmt"
"log" "log"
"github.com/buger/goterm"
"github.com/go-resty/resty/v2" "github.com/go-resty/resty/v2"
"github.com/joho/godotenv" "github.com/joho/godotenv"
) )
func output(client *resty.Client, url string, width, height int, arr [][]Cell) {
goterm.MoveCursor(1, 2)
// Prepare instructions for matrix
instructions := make([]interface{}, 0)
// Append all live cells as pixel instructions
for j := 0; j < height; j++ {
for i := 0; i < width; i++ {
if arr[i][j].live {
instructions = append(instructions, Pixel{X: i, Y: j, Endpoint: PIXEL})
goterm.Print("X")
continue
}
goterm.Print(" ")
}
goterm.Println()
}
// Append update instruction
instructions = append(instructions, Update{Endpoint: UPDATE})
// Send to matrix
sendRequest(client, url, instructions)
// Update terminal UI
goterm.Flush()
}
func main() { func main() {
const FPS = 10
// Load .env file if it exists // Load .env file if it exists
err := godotenv.Load(".env") err := godotenv.Load(".env")
if err != nil { if err != nil {
@ -16,23 +45,20 @@ func main() {
// Load env server data // Load env server data
url, width, height := loadMatrixData() url, width, height := loadMatrixData()
log.Printf("At '%s': %d x %d\n", url, width, height) goterm.Printf("On '%s' at [%d x %d] with %d FPS\n", url, width, height, FPS)
// Initialize resty client // Initialize resty client
client := resty.New() client := resty.New()
// Initialize pixel color
sendRequest(client, url, []interface{}{Color{R: 255, G: 255, B: 255, Endpoint: COLOR}})
// Initialize custom matrix data // Run Game of Life
col := Color{R: 255, G: 0, B: 255, Endpoint: COLOR} done := setup(func(c [][]Cell) {
rct := Rectangle{X: 0, Y: 0, W: 10, H: 10, Endpoint: RECTANGLE} output(client, url, width, height, c)
}, width, height, FPS)
// Send request to remote server // Wait for user input to quit
sendRequest(client, url, []interface{}{col, rct}) fmt.Scanln()
// Stop Game of Life, wait for routine
// Initialize custom matrix data done <- true
col = Color{R: 255, G: 255, B: 0, Endpoint: COLOR}
rct = Rectangle{X: 15, Y: 15, W: 15, H: 15, Endpoint: RECTANGLE}
upd := Update{Endpoint: UPDATE}
// Send request to remote server
sendRequest(client, url, []interface{}{col, rct, upd})
} }

View File

@ -9,24 +9,31 @@ type Endpoint string
const ( const (
RECTANGLE Endpoint = "rectangle" RECTANGLE Endpoint = "rectangle"
UPDATE Endpoint = "update" UPDATE Endpoint = "update"
PIXEL Endpoint = "pixel"
COLOR Endpoint = "color" COLOR Endpoint = "color"
) )
type Rectangle struct {
X int `json:"x"`
Y int `json:"y"`
W uint `json:"w"`
H uint `json:"h"`
Endpoint Endpoint `json:"endpoint"`
}
type Update struct { type Update struct {
Endpoint Endpoint `json:"endpoint"` Endpoint Endpoint `json:"endpoint"`
} }
type Pixel struct {
X int `json:"x"`
Y int `json:"y"`
Endpoint Endpoint `json:"endpoint"`
}
type Color struct { type Color struct {
R uint8 `json:"r"` R uint8 `json:"r"`
G uint8 `json:"g"` G uint8 `json:"g"`
B uint8 `json:"b"` B uint8 `json:"b"`
Endpoint Endpoint `json:"endpoint"` Endpoint Endpoint `json:"endpoint"`
} }
type Rectangle struct {
X int `json:"x"`
Y int `json:"y"`
W uint `json:"w"`
H uint `json:"h"`
Endpoint Endpoint `json:"endpoint"`
}