auto global game of life
This commit is contained in:
@@ -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 */
|
||||||
|
|||||||
32
js/boxes.js
32
js/boxes.js
@@ -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);
|
||||||
|
|||||||
@@ -7,8 +7,9 @@ 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
18
src/boxes/box-ticker.go
Normal 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")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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,47 +74,76 @@ func randomizeBoxes(count int) []Box {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func EqualRuns(a, b map[int]bool) bool {
|
||||||
|
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)
|
||||||
|
boxes := GetBoxes()
|
||||||
|
index := 0
|
||||||
|
for i := 0; i < 32; i++ {
|
||||||
|
for j := 0; j < 32; j++ {
|
||||||
|
matrix[fmt.Sprintf("%d-%d", i, j)] = boxes[index]
|
||||||
|
index++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
nextGen := make([]Box, 0)
|
||||||
|
for id, item := range matrix {
|
||||||
|
nextItem := shouldBeAlive(matrix, id)
|
||||||
|
if nextItem.Value != item.Value {
|
||||||
|
nextGen = append(nextGen, shouldBeAlive(matrix, id))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
nextMessage := ""
|
||||||
|
for _, item := range nextGen {
|
||||||
|
item.persist()
|
||||||
|
nextMessage += item.serialize()
|
||||||
|
}
|
||||||
|
|
||||||
|
CheckRepeats()
|
||||||
|
broadcastMessage(nextMessage)
|
||||||
|
}
|
||||||
|
|
||||||
func handleMessage(msg string, ws *websocket.Conn) error {
|
func handleMessage(msg string, ws *websocket.Conn) error {
|
||||||
if msg == "gol" {
|
if strings.Contains(msg, "b:") {
|
||||||
matrix := make(map[string]Box)
|
|
||||||
boxes := GetBoxes()
|
|
||||||
index := 0
|
|
||||||
for i := 0; i < 32; i++ {
|
|
||||||
for j := 0; j < 32; j++ {
|
|
||||||
matrix[fmt.Sprintf("%d-%d", i, j)] = boxes[index]
|
|
||||||
index++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
nextGen := make([]Box, 0)
|
|
||||||
for id, item := range matrix {
|
|
||||||
nextItem := shouldBeAlive(matrix, id)
|
|
||||||
if nextItem.Value != item.Value {
|
|
||||||
nextGen = append(nextGen, shouldBeAlive(matrix, id))
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
nextMessage := ""
|
|
||||||
for _, item := range nextGen {
|
|
||||||
item.persist()
|
|
||||||
nextMessage += item.serialize()
|
|
||||||
}
|
|
||||||
broadcastMessage(nextMessage)
|
|
||||||
|
|
||||||
} else if strings.Contains(msg, "r:") {
|
|
||||||
count, err := strconv.Atoi(strings.Split(msg, ":")[1])
|
|
||||||
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
|
||||||
|
|||||||
@@ -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"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
Reference in New Issue
Block a user