diff --git a/config.json b/config.json index 38eb1277a..5607887c3 100644 --- a/config.json +++ b/config.json @@ -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", diff --git a/exercises/practice/game-of-life/.docs/instructions.md b/exercises/practice/game-of-life/.docs/instructions.md new file mode 100644 index 000000000..495314064 --- /dev/null +++ b/exercises/practice/game-of-life/.docs/instructions.md @@ -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. diff --git a/exercises/practice/game-of-life/.docs/introduction.md b/exercises/practice/game-of-life/.docs/introduction.md new file mode 100644 index 000000000..2347b936e --- /dev/null +++ b/exercises/practice/game-of-life/.docs/introduction.md @@ -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 diff --git a/exercises/practice/game-of-life/.meta/config.json b/exercises/practice/game-of-life/.meta/config.json new file mode 100644 index 000000000..b71d8fa13 --- /dev/null +++ b/exercises/practice/game-of-life/.meta/config.json @@ -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" +} diff --git a/exercises/practice/game-of-life/.meta/examples/success-standard/package.yaml b/exercises/practice/game-of-life/.meta/examples/success-standard/package.yaml new file mode 100644 index 000000000..cc9e251d7 --- /dev/null +++ b/exercises/practice/game-of-life/.meta/examples/success-standard/package.yaml @@ -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 diff --git a/exercises/practice/game-of-life/.meta/examples/success-standard/src/GameOfLife.hs b/exercises/practice/game-of-life/.meta/examples/success-standard/src/GameOfLife.hs new file mode 100644 index 000000000..f7a64667e --- /dev/null +++ b/exercises/practice/game-of-life/.meta/examples/success-standard/src/GameOfLife.hs @@ -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 ] diff --git a/exercises/practice/game-of-life/.meta/tests.toml b/exercises/practice/game-of-life/.meta/tests.toml new file mode 100644 index 000000000..398cd4546 --- /dev/null +++ b/exercises/practice/game-of-life/.meta/tests.toml @@ -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" diff --git a/exercises/practice/game-of-life/package.yaml b/exercises/practice/game-of-life/package.yaml new file mode 100644 index 000000000..101d17516 --- /dev/null +++ b/exercises/practice/game-of-life/package.yaml @@ -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 diff --git a/exercises/practice/game-of-life/src/GameOfLife.hs b/exercises/practice/game-of-life/src/GameOfLife.hs new file mode 100644 index 000000000..63211ab8e --- /dev/null +++ b/exercises/practice/game-of-life/src/GameOfLife.hs @@ -0,0 +1,4 @@ +module GameOfLife (tick) where + +tick :: [[Int]] -> [[Int]] +tick = error "You need to implement this function." diff --git a/exercises/practice/game-of-life/stack.yaml b/exercises/practice/game-of-life/stack.yaml new file mode 100644 index 000000000..115878212 --- /dev/null +++ b/exercises/practice/game-of-life/stack.yaml @@ -0,0 +1 @@ +resolver: lts-20.18 diff --git a/exercises/practice/game-of-life/test/Tests.hs b/exercises/practice/game-of-life/test/Tests.hs new file mode 100644 index 000000000..8ca7db958 --- /dev/null +++ b/exercises/practice/game-of-life/test/Tests.hs @@ -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] + ] + } + ]