Skip to content

Commit

Permalink
add game-of-life
Browse files Browse the repository at this point in the history
  • Loading branch information
glennj committed Apr 11, 2024
1 parent 9e60457 commit e01bd54
Show file tree
Hide file tree
Showing 10 changed files with 271 additions and 1 deletion.
8 changes: 8 additions & 0 deletions config.json
Original file line number Diff line number Diff line change
Expand Up @@ -1622,6 +1622,14 @@
"practices": [],
"prerequisites": [],
"difficulty": 5
},
{
"slug": "game-of-life",
"name": "Conway's Game of Life",
"uuid": "2aa71bec-4479-432f-a2e3-ed88c9508553",
"practices": [],
"prerequisites": [],
"difficulty": 5
}
],
"foregone": [
Expand Down
11 changes: 11 additions & 0 deletions exercises/practice/game-of-life/.docs/instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Instructions

After each generation, the cells interact with their eight neighbors, which are cells adjacent horizontally, vertically, or diagonally.

The following rules are applied to each cell:

- Any live cell with two or three live neighbors lives on.
- Any dead cell with exactly three live neighbors becomes a live cell.
- All other cells die or stay dead.

Given a matrix of 1s and 0s (corresponding to live and dead cells), apply the rules to each cell, and return the next generation.
9 changes: 9 additions & 0 deletions exercises/practice/game-of-life/.docs/introduction.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Introduction

[Conway's Game of Life][game-of-life] is a fascinating cellular automaton created by the British mathematician John Horton Conway in 1970.

The game consists of a two-dimensional grid of cells that can either be "alive" or "dead."

After each generation, the cells interact with their eight neighbors via a set of rules, which define the new generation.

[game-of-life]: https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life
19 changes: 19 additions & 0 deletions exercises/practice/game-of-life/.meta/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"authors": [
"glennj"
],
"files": {
"solution": [
"./game-of-life.tcl"
],
"test": [
"./game-of-life.test"
],
"example": [
".meta/example.tcl"
]
},
"blurb": "Implement Conway's Game of Life.",
"source": "Wikipedia",
"source_url": "https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life"
}
35 changes: 35 additions & 0 deletions exercises/practice/game-of-life/.meta/example.tcl
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
namespace eval GameOfLife {
namespace export tick

proc tick {matrix} {
set nrows [llength $matrix]
set ncols [llength [lindex $matrix 0]]
set next [lrepeat $nrows [lrepeat $ncols 0]]

for {set r 0} {$r < $nrows} {incr r} {
for {set c 0} {$c < $ncols} {incr c} {
switch [neighbourCount $matrix $r $c] {
3 { lset next $r $c 1 }
2 { lset next $r $c [lindex $matrix $r $c] }
}
}
}
return $next
}

proc neighbourCount {matrix r c} {
set count 0
foreach dr {-1 0 1} {
foreach dc {-1 0 1} {
if {!($dr == 0 && $dc == 0)} {
set value [lindex $matrix $r+$dr $c+$dc]
# if indices outside of matrix, value is empty string
incr count [expr {$value eq "" ? 0 : $value}]
}
}
}
return $count
}
}

namespace import GameOfLife::tick
34 changes: 34 additions & 0 deletions exercises/practice/game-of-life/.meta/tests.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# This is an auto-generated file.
#
# Regenerating this file via `configlet sync` will:
# - Recreate every `description` key/value pair
# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications
# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion)
# - Preserve any other key/value pair
#
# As user-added comments (using the # character) will be removed when this file
# is regenerated, comments can be added via a `comment` key.

[ae86ea7d-bd07-4357-90b3-ac7d256bd5c5]
description = "empty matrix"

[4ea5ccb7-7b73-4281-954a-bed1b0f139a5]
description = "live cells with zero live neighbors die"

[df245adc-14ff-4f9c-b2ae-f465ef5321b2]
description = "live cells with only one live neighbor die"

[2a713b56-283c-48c8-adae-1d21306c80ae]
description = "live cells with two live neighbors stay alive"

[86d5c5a5-ab7b-41a1-8907-c9b3fc5e9dae]
description = "live cells with three live neighbors stay alive"

[015f60ac-39d8-4c6c-8328-57f334fc9f89]
description = "dead cells with three live neighbors become alive"

[2ee69c00-9d41-4b8b-89da-5832e735ccf1]
description = "live cells with four or more neighbors die"

[a79b42be-ed6c-4e27-9206-43da08697ef6]
description = "bigger matrix"
3 changes: 3 additions & 0 deletions exercises/practice/game-of-life/game-of-life.tcl
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
proc tick {matrix} {
throw {NOT_IMPLEMENTED} "Implement this procedure."
}
115 changes: 115 additions & 0 deletions exercises/practice/game-of-life/game-of-life.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
#!/usr/bin/env tclsh
package require tcltest
namespace import ::tcltest::*
source testHelpers.tcl

############################################################
source "game-of-life.tcl"

test game-of-life-1 "empty matrix" -body {
tick {}
} -match listOfLists -returnCodes ok -result {}

skip game-of-life-2
test game-of-life-2 "live cells with zero live neighbors die" -body {
tick {
{0 0 0}
{0 1 0}
{0 0 0}
}
} -match listOfLists -returnCodes ok -result {
{0 0 0}
{0 0 0}
{0 0 0}
}

skip game-of-life-3
test game-of-life-3 "live cells with only one live neighbor die" -body {
tick {
{0 0 0}
{0 1 0}
{0 1 0}
}
} -match listOfLists -returnCodes ok -result {
{0 0 0}
{0 0 0}
{0 0 0}
}

skip game-of-life-4
test game-of-life-4 "live cells with two live neighbors stay alive" -body {
tick {
{1 0 1}
{1 0 1}
{1 0 1}
}
} -match listOfLists -returnCodes ok -result {
{0 0 0}
{1 0 1}
{0 0 0}
}

skip game-of-life-5
test game-of-life-5 "live cells with three live neighbors stay alive" -body {
tick {
{0 1 0}
{1 0 0}
{1 1 0}
}
} -match listOfLists -returnCodes ok -result {
{0 0 0}
{1 0 0}
{1 1 0}
}

skip game-of-life-6
test game-of-life-6 "dead cells with three live neighbors become alive" -body {
tick {
{1 1 0}
{0 0 0}
{1 0 0}
}
} -match listOfLists -returnCodes ok -result {
{0 0 0}
{1 1 0}
{0 0 0}
}

skip game-of-life-7
test game-of-life-7 "live cells with four or more neighbors die" -body {
tick {
{1 1 1}
{1 1 1}
{1 1 1}
}
} -match listOfLists -returnCodes ok -result {
{1 0 1}
{0 0 0}
{1 0 1}
}

skip game-of-life-8
test game-of-life-8 "bigger matrix" -body {
tick {
{1 1 0 1 1 0 0 0}
{1 0 1 1 0 0 0 0}
{1 1 1 0 0 1 1 1}
{0 0 0 0 0 1 1 0}
{1 0 0 0 1 1 0 0}
{1 1 0 0 0 1 1 1}
{0 0 1 0 1 0 0 1}
{1 0 0 0 0 0 1 1}
}
} -match listOfLists -returnCodes ok -result {
{1 1 0 1 1 0 0 0}
{0 0 0 0 0 1 1 0}
{1 0 1 1 1 1 0 1}
{1 0 0 0 0 0 0 1}
{1 1 0 0 1 0 0 1}
{1 1 0 1 0 0 0 1}
{1 0 0 0 0 0 0 0}
{0 0 0 0 0 0 1 1}
}


cleanupTests
37 changes: 37 additions & 0 deletions exercises/practice/game-of-life/testHelpers.tcl
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#############################################################
# Override some tcltest procs with additional functionality

# Allow an environment variable to override `skip`
proc skip {patternList} {
if { [info exists ::env(RUN_ALL)]
&& [string is boolean -strict $::env(RUN_ALL)]
&& $::env(RUN_ALL)
} then return else {
uplevel 1 [list ::tcltest::skip $patternList]
}
}

# Exit non-zero if any tests fail.
# The cleanupTests resets the numTests array, so capture it first.
proc cleanupTests {} {
set failed [expr {$::tcltest::numTests(Failed) > 0}]
uplevel 1 ::tcltest::cleanupTests
if {$failed} then {exit 1}
}


# Compare two ordered lists without comparing the lists themselves
# as strings. Calls itself recursively.
proc listOfListsMatch {expected actual} {
set procname [lindex [info level 0] 0]
if {[llength $expected] != [llength $actual]} {
return false
}
foreach e $expected a $actual {
if {[llength $e] > 1 ? (![$procname $e $a]) : ($e != $a)} {
return false
}
}
return true
}
customMatch listOfLists listOfListsMatch
1 change: 0 additions & 1 deletion notes-on-unimplemented-exercises.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
## Merely unimplemented

* [bottle-song][bottle-song]
* [game-of-life][game-of-life]
* [ledger][ledger]
- a tedious-to-create refactoring exercise
* [lens-person][lens-person]
Expand Down

0 comments on commit e01bd54

Please sign in to comment.