Skip to content

Commit f3f2eb1

Browse files
author
oxe-i
committed
make example implementation more clear
1 parent cebc9eb commit f3f2eb1

File tree

1 file changed

+46
-8
lines changed

1 file changed

+46
-8
lines changed

exercises/practice/game-of-life/.meta/Example.lean

Lines changed: 46 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,56 @@
11
namespace GameOfLife
22

3-
def countNeighbors (x : Nat) (y : Nat) (matrix : Array (Array Bool)) : Nat :=
3+
/-
4+
The neighbors of a cell at (x, y) are at (x - 1, y - 1) ... (x + 1, y + 1)
5+
But those values might be out of bounds. So this exercise is all about bounds checking.
6+
7+
The main idea here is to use arr[idx]? to automate this check.
8+
It returns an Option where an invalid index would result in none.
9+
10+
However, idx must be a Nat and in Lean 0 - 1 == 0. Even making a round trip from Int won't solve this.
11+
One way to approach the problem is do the lower half of bounds checking manually. This is what `getBounds` do.
12+
Another way would be to erase duplicates coordinates by using, for example, List.removeDup.
13+
14+
The notation (a...=b) is a Std.Rcc, a type of Range inclusive in both a and b.
15+
It is roughly equivalent to [a:b+1], but [x:y] only works with natural numbers and is exclusive on y.
16+
-/
17+
def getBounds (x : Nat) (y : Nat) : List Nat × List Nat :=
418
let (dxs, dys) := match x, y with
5-
| 0, 0 => ((x...=x+1), (y...=y+1))
6-
| 0, ny + 1 => ((x...=x+1), (ny...=y+1))
7-
| nx + 1, 0 => ((nx...=x+1), (y...=y+1))
8-
| nx + 1, ny + 1 => ((nx...=x+1), (ny...=y+1))
9-
dxs.toList.foldr (fun i1 => dys.toList.foldr (fun i2 is => is.push (i1, i2))) #[]
10-
|> (·.filterMap (fun (ox, oy) => if ox == x && oy == y then none else matrix[ox]? >>= (·[oy]?)))
19+
| 0, 0 => (x...=x+1, y...=y+1)
20+
| 0, ny + 1 => (x...=x+1, ny...=y+1)
21+
| nx + 1, 0 => (nx...=x+1, y...=y+1)
22+
| nx + 1, ny + 1 => (nx...=x+1, ny...=y+1)
23+
(dxs.toList, dys.toList)
24+
25+
/-
26+
Bounds are transformed to an Array of coordinates
27+
-/
28+
def getNeighbors (x : Nat) (y : Nat) : Array (Nat × Nat) :=
29+
let (dxs, dys) := getBounds x y
30+
dxs.foldr (fun i1 => dys.foldr (fun i2 is => is.push (i1, i2))) #[]
31+
32+
/-
33+
This removes the original (x, y) from the neighbors and performs upper bound checking
34+
35+
The notation >>= is equivalent to monadic bind. For an Option, `x >>= f` is equivalent to:
36+
37+
```
38+
match x with
39+
| none => none
40+
| some v => some (f v)
41+
```
42+
-/
43+
def getValidNeighbors (x : Nat) (y : Nat) (matrix : Array (Array Bool)) : Array Bool :=
44+
let neighbors := getNeighbors x y
45+
neighbors.filterMap (fun (ox, oy) => if ox == x && oy == y then none else matrix[ox]? >>= (·[oy]?))
46+
47+
def countLiveNeighbors (x : Nat) (y : Nat) (matrix : Array (Array Bool)) : Nat :=
48+
getValidNeighbors x y matrix
1149
|> (·.count true)
1250

1351
def tick (matrix : Array (Array Bool)) : Array (Array Bool) :=
1452
matrix.mapIdx (fun x row => row.mapIdx (fun y e =>
15-
match countNeighbors x y matrix with
53+
match countLiveNeighbors x y matrix with
1654
| 3 => true
1755
| 2 => e
1856
| _ => false

0 commit comments

Comments
 (0)