How to resolve the algorithm One-dimensional cellular automata step by step in the Go programming language
How to resolve the algorithm One-dimensional cellular automata step by step in the Go programming language
Table of Contents
Problem Statement
Assume an array of cells with an initial distribution of live and dead cells, and imaginary cells off the end of the array having fixed values. Cells in the next generation of the array are calculated based on the value of the cell and its left and right nearest neighbours in the current generation. If, in the following table, a live cell is represented by 1 and a dead cell by 0 then to generate the value of the cell at a particular index in the array of cellular values you use the following table:
Let's start with the solution:
Step by Step solution about How to resolve the algorithm One-dimensional cellular automata step by step in the Go programming language
The provided code implements a 1D cellular automata called Rule 184. It simulates the evolution of a row of cells, where each cell can be either alive (indicated with a #) or dead (indicated with a _). The initial state of the automaton is given by the start
variable, and the evolution is determined by the following rules:
- If a cell is alive and both its left and right neighbors are alive, it dies (overpopulation).
- If a cell is alive and either its left or right neighbor is alive, it survives (survival).
- If a cell is dead and both its left and right neighbors are alive, it comes alive (birth).
- If a cell is dead and either its left or right neighbor is alive, it remain dead (stay dead).
First implementation:
The first implementation uses a generator function (newGenerator
) to create a closure that generates the next state of the cellular automata given the current state and the parameters for the off-left, off-right, and dead cells. The closure maintains the current state in two variables, g0
(a string) and g1
(a byte slice). For each iteration, it computes the next state of each cell in g1
based on the rules described above. It then updates g0
to the new state and returns the substring of g0
that represents the row of cells.
Second implementation:
The second implementation uses goroutines and synchronization primitives to simulate the cellular automata in parallel. It creates a byte slice a
that represents the current state, including the off-left and off-right cells. It then starts a goroutine for each cell in the row. Each goroutine continuously reads the current state of its own cell and its two neighbors, computes the next state based on the rules, and updates the shared a
slice with the new state.
To control the synchronization between the goroutines, two sync.WaitGroup
are used: read
and write
. Before each iteration, the read
wait group is incremented to indicate that all goroutines need to read the current state before proceeding. After all goroutines have read the state, the read
wait group is decremented to allow them to proceed with the computation.
Similarly, before writing the new state to the shared a
slice, the write
wait group is incremented to indicate that all goroutines need to finish writing before proceeding to the next iteration. After all goroutines have written the new state, the write
wait group is decremented to allow them to start the next iteration.
Both implementations print the current state of the cellular automata for the first 10 iterations, showcasing the evolution of the patterns. The initial state is "##########", which represents a row of cells with some alive cells and some dead cells. As the iterations progress, the pattern evolves, with some cells dying, some surviving, and some being born.
Source code in the go programming language
package main
import "fmt"
const (
start = "_###_##_#_#_#_#__#__"
offLeft = '_'
offRight = '_'
dead = '_'
)
func main() {
fmt.Println(start)
g := newGenerator(start, offLeft, offRight, dead)
for i := 0; i < 10; i++ {
fmt.Println(g())
}
}
func newGenerator(start string, offLeft, offRight, dead byte) func() string {
g0 := string(offLeft) + start + string(offRight)
g1 := []byte(g0)
last := len(g0) - 1
return func() string {
for i := 1; i < last; i++ {
switch l := g0[i-1]; {
case l != g0[i+1]:
g1[i] = g0[i]
case g0[i] == dead:
g1[i] = l
default:
g1[i] = dead
}
}
g0 = string(g1)
return g0[1:last]
}
}
package main
import (
"fmt"
"sync"
)
const (
start = "_###_##_#_#_#_#__#__"
offLeft = '_'
offRight = '_'
dead = '_'
)
func main() {
fmt.Println(start)
a := make([]byte, len(start)+2)
a[0] = offLeft
copy(a[1:], start)
a[len(a)-1] = offRight
var read, write sync.WaitGroup
read.Add(len(start) + 1)
for i := 1; i <= len(start); i++ {
go cell(a[i-1:i+2], &read, &write)
}
for i := 0; i < 10; i++ {
write.Add(len(start) + 1)
read.Done()
read.Wait()
read.Add(len(start) + 1)
write.Done()
write.Wait()
fmt.Println(string(a[1 : len(a)-1]))
}
}
func cell(kernel []byte, read, write *sync.WaitGroup) {
var next byte
for {
l, v, r := kernel[0], kernel[1], kernel[2]
read.Done()
switch {
case l != r:
next = v
case v == dead:
next = l
default:
next = dead
}
read.Wait()
kernel[1] = next
write.Done()
write.Wait()
}
}
You may also check:How to resolve the algorithm Strip comments from a string step by step in the VBScript programming language
You may also check:How to resolve the algorithm Peano curve step by step in the XPL0 programming language
You may also check:How to resolve the algorithm Loops/Nested step by step in the MAXScript programming language
You may also check:How to resolve the algorithm Universal Turing machine step by step in the Julia programming language
You may also check:How to resolve the algorithm Mouse position step by step in the Gambas programming language