Skip to content

Commit d873116

Browse files
authored
Add game of life exercise (#874)
1 parent 3c13d7b commit d873116

File tree

10 files changed

+233
-0
lines changed

10 files changed

+233
-0
lines changed

config.json

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1230,6 +1230,17 @@
12301230
"algorithms"
12311231
]
12321232
},
1233+
{
1234+
"slug": "game-of-life",
1235+
"name": "Conway's Game of Life",
1236+
"uuid": "ea97e140-6759-4267-8c71-2d3340a33b9c",
1237+
"practices": [],
1238+
"prerequisites": [],
1239+
"difficulty": 5,
1240+
"topics": [
1241+
"arrays"
1242+
]
1243+
},
12331244
{
12341245
"slug": "bowling",
12351246
"name": "Bowling",
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# Instructions
2+
3+
After each generation, the cells interact with their eight neighbors, which are cells adjacent horizontally, vertically, or diagonally.
4+
5+
The following rules are applied to each cell:
6+
7+
- Any live cell with two or three live neighbors lives on.
8+
- Any dead cell with exactly three live neighbors becomes a live cell.
9+
- All other cells die or stay dead.
10+
11+
Given a matrix of 1s and 0s (corresponding to live and dead cells), apply the rules to each cell, and return the next generation.
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# Introduction
2+
3+
[Conway's Game of Life][game-of-life] is a fascinating cellular automaton created by the British mathematician John Horton Conway in 1970.
4+
5+
The game consists of a two-dimensional grid of cells that can either be "alive" or "dead."
6+
7+
After each generation, the cells interact with their eight neighbors via a set of rules, which define the new generation.
8+
9+
[game-of-life]: https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
2+
func tick(_ matrix: [[Int]]) -> [[Int]] {
3+
let neighbors = [
4+
(-1, -1), (-1, 0), (-1, 1),
5+
( 0, -1), ( 0, 1),
6+
( 1, -1), ( 1, 0), ( 1, 1)
7+
]
8+
9+
var result = matrix
10+
for (rowIndex, row) in matrix.enumerated() {
11+
for (columnIndex, cellValue) in row.enumerated() {
12+
var neighborsAlive = 0
13+
for (dx, dy) in neighbors {
14+
let x = rowIndex + dx
15+
let y = columnIndex + dy
16+
guard x >= 0, x < result.count, y >= 0, y < row.count else {
17+
continue
18+
}
19+
neighborsAlive += matrix[x][y]
20+
}
21+
22+
if cellValue == 1 && (neighborsAlive != 2 && neighborsAlive != 3) {
23+
result[rowIndex][columnIndex] = 0
24+
}
25+
else if cellValue == 0 && neighborsAlive == 3 {
26+
result[rowIndex][columnIndex] = 1
27+
}
28+
}
29+
}
30+
return result
31+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"authors": [
3+
"Sencudra"
4+
],
5+
"files": {
6+
"solution": [
7+
"Sources/GameOfLife/GameOfLife.swift"
8+
],
9+
"test": [
10+
"Tests/GameOfLifeTests/GameOfLifeTests.swift"
11+
],
12+
"example": [
13+
".meta/Sources/GameOfLife/GameOfLifeExample.swift"
14+
]
15+
},
16+
"blurb": "Implement Conway's Game of Life.",
17+
"source": "Wikipedia",
18+
"source_url": "https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life"
19+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import Testing
2+
import Foundation
3+
@testable import {{ exercise|camelCase }}
4+
5+
let RUNALL = Bool(ProcessInfo.processInfo.environment["RUNALL", default: "false"]) ?? false
6+
7+
@Suite
8+
struct {{ exercise|camelCase }}Tests {
9+
{% for case in cases %}
10+
{% if forloop.first -%}
11+
@Test("{{ case.description }}")
12+
{% else -%}
13+
@Test("{{ case.description }}", .enabled(if: RUNALL))
14+
{% endif -%}
15+
func test{{ case.description|camelCase }}() {
16+
let initial: [[Int]] = {{ case.input.matrix }}
17+
let expected: [[Int]] = {{ case.expected }}
18+
#expect({{ case.property }}(initial) == expected)
19+
}
20+
{% endfor -%}
21+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# This is an auto-generated file.
2+
#
3+
# Regenerating this file via `configlet sync` will:
4+
# - Recreate every `description` key/value pair
5+
# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications
6+
# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion)
7+
# - Preserve any other key/value pair
8+
#
9+
# As user-added comments (using the # character) will be removed when this file
10+
# is regenerated, comments can be added via a `comment` key.
11+
12+
[ae86ea7d-bd07-4357-90b3-ac7d256bd5c5]
13+
description = "empty matrix"
14+
15+
[4ea5ccb7-7b73-4281-954a-bed1b0f139a5]
16+
description = "live cells with zero live neighbors die"
17+
18+
[df245adc-14ff-4f9c-b2ae-f465ef5321b2]
19+
description = "live cells with only one live neighbor die"
20+
21+
[2a713b56-283c-48c8-adae-1d21306c80ae]
22+
description = "live cells with two live neighbors stay alive"
23+
24+
[86d5c5a5-ab7b-41a1-8907-c9b3fc5e9dae]
25+
description = "live cells with three live neighbors stay alive"
26+
27+
[015f60ac-39d8-4c6c-8328-57f334fc9f89]
28+
description = "dead cells with three live neighbors become alive"
29+
30+
[2ee69c00-9d41-4b8b-89da-5832e735ccf1]
31+
description = "live cells with four or more neighbors die"
32+
33+
[a79b42be-ed6c-4e27-9206-43da08697ef6]
34+
description = "bigger matrix"
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// swift-tools-version:6.0
2+
3+
import PackageDescription
4+
5+
let package = Package(
6+
name: "GameOfLife",
7+
products: [
8+
.library(
9+
name: "GameOfLife",
10+
targets: ["GameOfLife"])
11+
],
12+
dependencies: [],
13+
targets: [
14+
.target(
15+
name: "GameOfLife",
16+
dependencies: []),
17+
.testTarget(
18+
name: "GameOfLifeTests",
19+
dependencies: ["GameOfLife"]),
20+
]
21+
)
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
2+
// Write your code for the 'Game Of Life' exercise here.
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import Foundation
2+
import Testing
3+
4+
@testable import GameOfLife
5+
6+
let RUNALL = Bool(ProcessInfo.processInfo.environment["RUNALL", default: "false"]) ?? false
7+
8+
@Suite
9+
struct GameOfLifeTests {
10+
11+
@Test("empty matrix")
12+
func testEmptyMatrix() {
13+
let initial: [[Int]] = []
14+
let expected: [[Int]] = []
15+
#expect(tick(initial) == expected)
16+
}
17+
18+
@Test("live cells with zero live neighbors die", .enabled(if: RUNALL))
19+
func testLiveCellsWithZeroLiveNeighborsDie() {
20+
let initial: [[Int]] = [[0, 0, 0], [0, 1, 0], [0, 0, 0]]
21+
let expected: [[Int]] = [[0, 0, 0], [0, 0, 0], [0, 0, 0]]
22+
#expect(tick(initial) == expected)
23+
}
24+
25+
@Test("live cells with only one live neighbor die", .enabled(if: RUNALL))
26+
func testLiveCellsWithOnlyOneLiveNeighborDie() {
27+
let initial: [[Int]] = [[0, 0, 0], [0, 1, 0], [0, 1, 0]]
28+
let expected: [[Int]] = [[0, 0, 0], [0, 0, 0], [0, 0, 0]]
29+
#expect(tick(initial) == expected)
30+
}
31+
32+
@Test("live cells with two live neighbors stay alive", .enabled(if: RUNALL))
33+
func testLiveCellsWithTwoLiveNeighborsStayAlive() {
34+
let initial: [[Int]] = [[1, 0, 1], [1, 0, 1], [1, 0, 1]]
35+
let expected: [[Int]] = [[0, 0, 0], [1, 0, 1], [0, 0, 0]]
36+
#expect(tick(initial) == expected)
37+
}
38+
39+
@Test("live cells with three live neighbors stay alive", .enabled(if: RUNALL))
40+
func testLiveCellsWithThreeLiveNeighborsStayAlive() {
41+
let initial: [[Int]] = [[0, 1, 0], [1, 0, 0], [1, 1, 0]]
42+
let expected: [[Int]] = [[0, 0, 0], [1, 0, 0], [1, 1, 0]]
43+
#expect(tick(initial) == expected)
44+
}
45+
46+
@Test("dead cells with three live neighbors become alive", .enabled(if: RUNALL))
47+
func testDeadCellsWithThreeLiveNeighborsBecomeAlive() {
48+
let initial: [[Int]] = [[1, 1, 0], [0, 0, 0], [1, 0, 0]]
49+
let expected: [[Int]] = [[0, 0, 0], [1, 1, 0], [0, 0, 0]]
50+
#expect(tick(initial) == expected)
51+
}
52+
53+
@Test("live cells with four or more neighbors die", .enabled(if: RUNALL))
54+
func testLiveCellsWithFourOrMoreNeighborsDie() {
55+
let initial: [[Int]] = [[1, 1, 1], [1, 1, 1], [1, 1, 1]]
56+
let expected: [[Int]] = [[1, 0, 1], [0, 0, 0], [1, 0, 1]]
57+
#expect(tick(initial) == expected)
58+
}
59+
60+
@Test("bigger matrix", .enabled(if: RUNALL))
61+
func testBiggerMatrix() {
62+
let initial: [[Int]] = [
63+
[1, 1, 0, 1, 1, 0, 0, 0], [1, 0, 1, 1, 0, 0, 0, 0], [1, 1, 1, 0, 0, 1, 1, 1],
64+
[0, 0, 0, 0, 0, 1, 1, 0], [1, 0, 0, 0, 1, 1, 0, 0], [1, 1, 0, 0, 0, 1, 1, 1],
65+
[0, 0, 1, 0, 1, 0, 0, 1], [1, 0, 0, 0, 0, 0, 1, 1],
66+
]
67+
let expected: [[Int]] = [
68+
[1, 1, 0, 1, 1, 0, 0, 0], [0, 0, 0, 0, 0, 1, 1, 0], [1, 0, 1, 1, 1, 1, 0, 1],
69+
[1, 0, 0, 0, 0, 0, 0, 1], [1, 1, 0, 0, 1, 0, 0, 1], [1, 1, 0, 1, 0, 0, 0, 1],
70+
[1, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 1, 1],
71+
]
72+
#expect(tick(initial) == expected)
73+
}
74+
}

0 commit comments

Comments
 (0)