Skip to content

Commit

Permalink
Add Game of Life exercise (#1227)
Browse files Browse the repository at this point in the history
  • Loading branch information
tofische authored Apr 12, 2024
1 parent 45c6c83 commit 4ac7423
Show file tree
Hide file tree
Showing 11 changed files with 275 additions and 0 deletions.
9 changes: 9 additions & 0 deletions config.json
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,15 @@
"difficulty": 7,
"topics": []
},
{
"slug": "game-of-life",
"name": "Game of Life",
"uuid": "eb221634-15c6-463c-8268-54a3c3aa0c66",
"practices": [],
"prerequisites": [],
"difficulty": 7,
"topics": []
},
{
"slug": "perfect-numbers",
"name": "Perfect Numbers",
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
23 changes: 23 additions & 0 deletions exercises/practice/game-of-life/.meta/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"authors": [
"tofische"
],
"files": {
"solution": [
"src/GameOfLife.hs",
"package.yaml"
],
"test": [
"test/Tests.hs"
],
"example": [
".meta/examples/success-standard/src/GameOfLife.hs"
],
"invalidator": [
"stack.yaml"
]
},
"blurb": "Implement Conway's Game of Life.",
"source": "Wikipedia",
"source_url": "https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
name: food-chain

dependencies:
- base

library:
exposed-modules: GameOfLife
source-dirs: src

tests:
test:
main: Tests.hs
source-dirs: test
dependencies:
- game-of-life
- hspec
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
module GameOfLife (tick) where

import Data.Maybe (mapMaybe)

tick :: [[Int]] -> [[Int]]
tick matrix = [[ nextGen x (i, j)
| (j, x ) <- zip [0..] xs ]
| (i, xs) <- zip [0..] matrix ]
where
living = length . filter (== 1) . mapMaybe (`lookup` cells) . neighbors

neighbors (i, j) = [ (i - 1, j - 1), (i - 1, j), (i - 1, j + 1)
, (i , j - 1), (i , j + 1)
, (i + 1, j - 1), (i + 1, j), (i + 1, j + 1) ]

nextGen cell position =
let around = living position in
if (cell == 1 && (around == 2 || around == 3)) || (cell == 0 && around == 3) then 1 else 0

cells = [ ((i, j), x)
| (i, xs) <- zip [0 :: Int ..] matrix
, (j, x ) <- zip [0 :: Int ..] xs ]
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"
21 changes: 21 additions & 0 deletions exercises/practice/game-of-life/package.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
name: game-of-life
version: 1.0.0.0

dependencies:
- base

library:
exposed-modules: GameOfLife
source-dirs: src
ghc-options: -Wall
# dependencies:
# - foo # List here the packages you
# - bar # want to use in your solution.

tests:
test:
main: Tests.hs
source-dirs: test
dependencies:
- game-of-life
- hspec
4 changes: 4 additions & 0 deletions exercises/practice/game-of-life/src/GameOfLife.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
module GameOfLife (tick) where

tick :: [[Int]] -> [[Int]]
tick = error "You need to implement this function."
1 change: 1 addition & 0 deletions exercises/practice/game-of-life/stack.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
resolver: lts-20.18
125 changes: 125 additions & 0 deletions exercises/practice/game-of-life/test/Tests.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
{-# OPTIONS_GHC -fno-warn-type-defaults #-}
{-# LANGUAGE RecordWildCards #-}

import Data.Foldable (for_)
import Test.Hspec (Spec, describe, it, shouldBe)
import Test.Hspec.Runner (configFailFast, defaultConfig, hspecWith)

import GameOfLife (tick)

main :: IO ()
main = hspecWith defaultConfig {configFailFast = True} specs

specs :: Spec
specs = describe "tick" $ for_ cases test
where

test Case{..} = it description assertion
where
assertion = tick matrix `shouldBe` expected

data Case = Case { description :: String
, matrix :: [[Int]]
, expected :: [[Int]]
}

cases :: [Case]
cases = [ Case { description = "Empty matrix"
, matrix = []
, expected = []
}
, Case { description = "Live cells with zero live neighbors die"
, matrix = [
[0, 0, 0],
[0, 1, 0],
[0, 0, 0]
]
, expected = [
[0, 0, 0],
[0, 0, 0],
[0, 0, 0]
]
}
, Case { description = "Live cells with only one live neighbor die"
, matrix = [
[0, 0, 0],
[0, 1, 0],
[0, 1, 0]
]
, expected = [
[0, 0, 0],
[0, 0, 0],
[0, 0, 0]
]
}
, Case { description = "Live cells with two live neighbors stay alive"
, matrix = [
[1, 0, 1],
[1, 0, 1],
[1, 0, 1]
]
, expected = [
[0, 0, 0],
[1, 0, 1],
[0, 0, 0]
]
}
, Case { description = "Live cells with three live neighbors stay alive"
, matrix = [
[0, 1, 0],
[1, 0, 0],
[1, 1, 0]
]
, expected = [
[0, 0, 0],
[1, 0, 0],
[1, 1, 0]
]
}
, Case { description = "Dead cells with three live neighbors become alive"
, matrix = [
[1, 1, 0],
[0, 0, 0],
[1, 0, 0]
]
, expected = [
[0, 0, 0],
[1, 1, 0],
[0, 0, 0]
]
}
, Case { description = "Live cells with four or more neighbors die"
, matrix = [
[1, 1, 1],
[1, 1, 1],
[1, 1, 1]
]
, expected = [
[1, 0, 1],
[0, 0, 0],
[1, 0, 1]
]
}
, Case { description = "Bigger matrix"
, matrix = [
[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]
]
, expected = [
[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]
]
}
]

0 comments on commit 4ac7423

Please sign in to comment.