Several iterations of Conway's Game Of Life with Ballerina programming language.
All versions apply the following rules:
- Any live cell with two or three live neighbours survives.
- Any dead cell with three live neighbours becomes a live cell.
- All other live cells die in the next generation. Similarly, all other dead cells stay dead.
The coordinate system is programmer-friendly with origo ({x:0, y:0}
) in top-left corner and increasing into right and down:
―――――――――――――――――――――――
| 0,0 | 0,1 | 0,2 | ...
―――――――――――――――――――
| 1,0 | 1,1 | ...
―――――――――――――
| 2,0 | ...
―――――――
| ...
Error handling is kept simple with panics in all error scenarios (checkpanic
keyword).
Versions A (version-a?.bal
) are bare implementations and versions B (version-b?.bal
) have features creeped in.
Note that there is no checks that the combination of the command line arguments or the values of the arguments are sensible.
These are my first implementations of this game ever.
Used Ballerina version:
$ bal version
Ballerina 2201.1.0 (Swan Lake Update 1)
Language specification 2022R2
Update Tool 1.3.9
- The seed is hardcoded (initialization of the module global
grid
variable). - Uses two fixed size grids (two dimensional boolean arrays).
- The
grid
is a global state. - First finds out all neighbour cells and then checks in a separate step which of those are alive.
All neighbour cell coordinates are hardcoded with the following algorithm:
- Top row: left hand cell (#1)
- Top row: right hand cell (#2)
- Top row: all middle cells (#3)
- Bottom row: left hand cell (#4)
- Bottom row: right hand cell (#5)
- Bottom row: all middle cells (#6)
- All middle rows: left hand cell (#7)
- All middle rows: right hand cell (#8)
- All middle rows: all middle cells (#9)
―――――――――――――――――
| 1 | 3 | 3 | 2 |
―――――――――――――――――
| 7 | 9 | 9 | 8 |
―――――――――――――――――
| 7 | 9 | 9 | 8 |
―――――――――――――――――
| 4 | 6 | 6 | 5 |
―――――――――――――――――
Run:
bal run version-a1.bal
Changes compared to version A1:
- Removed
emptyGrid
initialization variable as[[DEAD]]
is effectively the same. One could also use[[]]
because the implicit default value forboolean
isfalse
but I prefer[[DEAD]]
because it is more explict. - Simplified main loop.
- Count the number of alive neighbours in one step.
Run:
bal run version-a2.bal
Changes compared to version A2:
- Instead of hard coding neighbour cell coordinates are (mostly) calculated.
- Global
grid
variable removed. After that there is no more global state thus all functions exceptmain()
(becauseio:readln()
is not isolated) could beisolated
. - Implementation of function
numberOfLiveCells()
couldn't usereduce()
because there is a pending specification issue with anonymous functions and isolation. - Added function comments. Ballerina has a built-in documentation framework. It doesn't work with a single file programs but the Ballerina Flavored Markup could still be used.
Run:
bal run version-a3.bal
Changes compared to version A3:
- Using a set of alive cells instead of two dimensional array of all cells. However Ballerina doesn't have set data structure and map only supports string keys. Hence the set (
aliveCells
variable) has been implemented with Ballerina's very powerful table data structure. - Implemented
numberOfLiveCells()
asreduce()
inmain()
. Now the function name no more documents the purpose of the function but the name of the result variablenumberOfLiveNeighbours
does. As a bonus this also works around the specification issue with anonymous functions and isolation. - Generates the neighbour cell coordinates with a query expression that is Ballerina's realization of a list comprehension.
Run:
bal run version-a4.bal
Changes compared to version A4:
- Grid size increased to 9x9 and default seed changed.
- Added an option to start with a random seed. Every first generation cell has 30% chance to be alive.
- Added an option to load the first generation from a file. Note that the grid size have to be 9x9. You can load files created with
--saveprefix
option. - Added an option to save generations to files.
- Added
player.sh
to show the generation files.
Run:
# start with hard-coded seed
bal run version-b1.bal
# start with a random seed
bal run version-b1.bal -- --random
# save all generations also to files with a prefix
bal run version-b1.bal -- --random --saveprefix=$(date +%Y%m%d-%H%M%S)-9x9-
# load the first generation from a file
bal run version-b1.bal -- --load=<FILENAME>
Show saved generation files:
./player.sh <FILES>
For now this is the final version.
Changes compared to version B1:
- Example generations added to
gen/
directory. See them withplayer.sh
. - Stops automatically when
- all cells are dead
- two consecutive generations are identical (the cells have stabilized)
- the generation has already appeared earlier in the history (the generation cycle)
- Variable grid size:
- if
--load
option is used the grid size is read from the file --size
option sets the grid size- defaults to 9x9
- if
- Reports the number of alive cells.
Run:
# start with a custom grid size
bal run version-b2.bal -- -- size 12
See Version B1 for other run options.