auto global game of life

This commit is contained in:
JurajKubrican
2025-08-04 16:28:42 +02:00
parent a393b03d98
commit b13c8c4f14
7 changed files with 147 additions and 68 deletions

View File

@@ -4,7 +4,45 @@
} }
.boxes input{ .boxes input{
margin: 0; margin: 0;
appearance: none;
-webkit-appearance: none;
-moz-appearance: none;
width: 20px;
height: 20px;
background: transparent;
border: none;
position: relative;
cursor: pointer;
} }
.boxes input[type="checkbox"]:checked {
background: transparent;
}
.boxes input[type="checkbox"]:checked::before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 1px;
background: #4a7c59;
transform-origin: 0 0;
transform: rotate(45deg) scaleX(1.414);
}
.boxes input[type="checkbox"]:checked::after {
content: '';
position: absolute;
top: 0;
right: 0;
width: 100%;
height: 1px;
background: #4a7c59;
transform-origin: 100% 0;
transform: rotate(-45deg) scaleX(1.414);
}
.boxes{ .boxes{
display: grid; display: grid;
grid-template-columns: repeat(32, 1fr); /* 32 equal columns */ grid-template-columns: repeat(32, 1fr); /* 32 equal columns */

View File

@@ -33,22 +33,22 @@
socket.send("b:" + id + ":" + value); socket.send("b:" + id + ":" + value);
}); });
}); });
const autoPlayEl = document.querySelector("#randomize"); // const autoPlayEl = document.querySelector("#randomize");
autoPlayEl?.addEventListener("click", (e) => socket.send("r:1000")); // autoPlayEl?.addEventListener("click", (e) => socket.send("r:1000"));
var golTimer = undefined; // var golTimer = undefined;
const handleGol = (el) => { // const handleGol = (el) => {
if (el.checked) { // if (el.checked) {
golTimer = setInterval(() => { // golTimer = setInterval(() => {
socket.send("gol"); // socket.send("gol");
}, 500); // }, 500);
} // }
else { // else {
clearInterval(golTimer); // clearInterval(golTimer);
} // }
}; // };
const golEl = document.querySelector("#game-of-life"); // const golEl = document.querySelector("#game-of-life");
handleGol(golEl); // handleGol(golEl);
golEl.addEventListener("change", (e) => handleGol(e.target)); // golEl.addEventListener("change", (e) => handleGol(e.target));
const container = document.querySelector('.boxes'); const container = document.querySelector('.boxes');
const resizeObserver = new ResizeObserver((entries) => { const resizeObserver = new ResizeObserver((entries) => {
const entry = entries.at(0); const entry = entries.at(0);

View File

@@ -9,6 +9,7 @@ import (
var ( var (
random = rand.New(rand.NewSource(time.Now().UnixNano())) random = rand.New(rand.NewSource(time.Now().UnixNano()))
store = initStore() store = initStore()
repeatStore = make([]map[int]bool, 0)
) )
func initStore() []Box { func initStore() []Box {
@@ -33,6 +34,5 @@ func (box Box) persist() {
func getBox(id int) Box { func getBox(id int) Box {
box := store[id] box := store[id]
return box return box
} }

18
src/boxes/box-ticker.go Normal file
View File

@@ -0,0 +1,18 @@
package boxes
import (
"time"
)
var (
ticker = time.Tick(time.Millisecond * 200)
)
func RegisterTicker() {
for range ticker {
if len(wsConnections) > 0 {
GameOfLifeTick()
// fmt.Print("g")
}
}
}

View File

@@ -61,7 +61,7 @@ func broadcastMessage(message string) {
} }
} }
func randomizeBoxes(count int) []Box { func RandomizeBoxes(count int) []Box {
boxes := make([]Box, count) boxes := make([]Box, count)
for i := 0; i < count; i++ { for i := 0; i < count; i++ {
index := random.Int() % 1000 index := random.Int() % 1000
@@ -74,8 +74,46 @@ func randomizeBoxes(count int) []Box {
} }
func handleMessage(msg string, ws *websocket.Conn) error { func EqualRuns(a, b map[int]bool) bool {
if msg == "gol" { for key, v := range a {
if v != b[key] {
return false
}
}
return true
}
func getMap(a []Box) map[int]bool {
res := make(map[int]bool, 0)
for _, val := range a {
if val.Value {
res[val.Id] = val.Value
}
}
return res
}
func CheckRepeats() {
currentRun := getMap(store)
if len(repeatStore) < 2 {
repeatStore = append(repeatStore, currentRun)
return
}
for _, run := range repeatStore {
if EqualRuns(run, currentRun) {
RandomizeBoxes(30)
}
}
repeatStore = append(repeatStore, currentRun)
if len(repeatStore) > 100 {
repeatStore = repeatStore[1:]
}
}
func GameOfLifeTick() {
matrix := make(map[string]Box) matrix := make(map[string]Box)
boxes := GetBoxes() boxes := GetBoxes()
index := 0 index := 0
@@ -99,22 +137,13 @@ func handleMessage(msg string, ws *websocket.Conn) error {
item.persist() item.persist()
nextMessage += item.serialize() nextMessage += item.serialize()
} }
CheckRepeats()
broadcastMessage(nextMessage) broadcastMessage(nextMessage)
}
} else if strings.Contains(msg, "r:") { func handleMessage(msg string, ws *websocket.Conn) error {
count, err := strconv.Atoi(strings.Split(msg, ":")[1]) if strings.Contains(msg, "b:") {
if err != nil {
log.Errorf("Failed to parse randomize count: %v", err)
}
boxes := randomizeBoxes(count)
message := ""
for _, box := range boxes {
message += box.serialize()
}
broadcastMessage(message)
} else if strings.Contains(msg, "b:") {
box, err := deserializeBox(msg) box, err := deserializeBox(msg)
if err != nil { if err != nil {
return err return err

View File

@@ -66,6 +66,8 @@ func main() {
e.GET("/draw", draw.Page) e.GET("/draw", draw.Page)
e.GET("/draw/ws", draw.InitWs) e.GET("/draw/ws", draw.InitWs)
go boxes.RegisterTicker()
e.Logger.Fatal(e.Start(":54321")) e.Logger.Fatal(e.Start(":54321"))
} }

View File

@@ -1,14 +1,6 @@
{{block "boxes" .}} {{block "boxes" .}}
<link rel="stylesheet" href="/css/boxes.css?v={{.BuildNumber}}" /> <link rel="stylesheet" href="/css/boxes.css?v={{.BuildNumber}}" />
<div class="boxes-container"> <div class="boxes-container">
<label>
<Button id="randomize">Randomize</Button>
</label>
<label>
Auto play Game of Life
<input type="checkbox" id="game-of-life" />
</label>
<div class="boxes"> <div class="boxes">
{{range .Boxes}}<input {{range .Boxes}}<input
type="checkbox" type="checkbox"