diff --git a/config.json b/config.json index 63cf96959..1bf16b9f6 100644 --- a/config.json +++ b/config.json @@ -2313,6 +2313,22 @@ ], "difficulty": 5 }, + { + "slug": "game-of-life", + "name": "Game of Life", + "uuid": "32d53ab8-abea-4483-a8cb-ee3b908a9dd7", + "practices": [ + "enum" + ], + "prerequisites": [ + "enum", + "lists", + "pattern-matching", + "pipe-operator", + "multiple-clause-functions" + ], + "difficulty": 6 + }, { "slug": "knapsack", "name": "Knapsack", 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/.formatter.exs b/exercises/practice/game-of-life/.formatter.exs new file mode 100644 index 000000000..d2cda26ed --- /dev/null +++ b/exercises/practice/game-of-life/.formatter.exs @@ -0,0 +1,4 @@ +# Used by "mix format" +[ + inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"] +] diff --git a/exercises/practice/game-of-life/.gitignore b/exercises/practice/game-of-life/.gitignore new file mode 100644 index 000000000..5535af6bc --- /dev/null +++ b/exercises/practice/game-of-life/.gitignore @@ -0,0 +1,26 @@ +# The directory Mix will write compiled artifacts to. +/_build/ + +# If you run "mix test --cover", coverage assets end up here. +/cover/ + +# The directory Mix downloads your dependencies sources to. +/deps/ + +# Where third-party dependencies like ExDoc output generated docs. +/doc/ + +# Ignore .fetch files in case you like to edit your project deps locally. +/.fetch + +# If the VM crashes, it generates a dump, let's ignore it too. +erl_crash.dump + +# Also ignore archive artifacts (built via "mix archive.build"). +*.ez + +# Ignore package tarball (built via "mix hex.build"). +game_of_life-*.tar + +# Temporary files, for example, from tests. +/tmp/ 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..c5d4e4d8b --- /dev/null +++ b/exercises/practice/game-of-life/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "jiegillet" + ], + "files": { + "solution": [ + "lib/game_of_life.ex" + ], + "test": [ + "test/game_of_life_test.exs" + ], + "example": [ + ".meta/example.ex" + ] + }, + "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/example.ex b/exercises/practice/game-of-life/.meta/example.ex new file mode 100644 index 000000000..33fe04a80 --- /dev/null +++ b/exercises/practice/game-of-life/.meta/example.ex @@ -0,0 +1,44 @@ +defmodule GameOfLife do + @doc """ + Apply the rules of Conway's Game of Life to a grid of cells + """ + + @spec tick(matrix :: list(list(0 | 1))) :: list(list(0 | 1)) + def tick([]), do: [] + + def tick(matrix) do + neighbors = count_neighbors(matrix) + + Enum.zip_with(matrix, neighbors, fn row_matrix, row_neighbor -> + Enum.zip_with(row_matrix, row_neighbor, &rule/2) + end) + end + + defp count_neighbors(matrix) do + shift_left = shift_left(matrix) + shift_right = shift_right(matrix) + + shifts = [ + shift_left, + shift_right, + shift_up(matrix), + shift_down(matrix), + shift_left |> shift_up(), + shift_left |> shift_down(), + shift_right |> shift_up(), + shift_right |> shift_down() + ] + + Enum.zip_with(shifts, fn rows -> Enum.zip_with(rows, &Enum.sum/1) end) + end + + defp shift_right(matrix), do: Enum.map(matrix, fn row -> [0 | row] end) + defp shift_left(matrix), do: Enum.map(matrix, fn row -> Enum.drop(row, 1) ++ [0] end) + defp shift_up([first | rest]), do: rest ++ [Enum.map(first, fn _ -> 0 end)] + defp shift_down([first | _] = matrix), do: [Enum.map(first, fn _ -> 0 end) | matrix] + + defp rule(1, 2), do: 1 + defp rule(1, 3), do: 1 + defp rule(0, 3), do: 1 + defp rule(_cell, _live_neighbors), do: 0 +end 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/lib/game_of_life.ex b/exercises/practice/game-of-life/lib/game_of_life.ex new file mode 100644 index 000000000..2d47ce255 --- /dev/null +++ b/exercises/practice/game-of-life/lib/game_of_life.ex @@ -0,0 +1,9 @@ +defmodule GameOfLife do + @doc """ + Apply the rules of Conway's Game of Life to a grid of cells + """ + + @spec tick(matrix :: list(list(0 | 1))) :: list(list(0 | 1)) + def tick(matrix) do + end +end diff --git a/exercises/practice/game-of-life/mix.exs b/exercises/practice/game-of-life/mix.exs new file mode 100644 index 000000000..91b15ac62 --- /dev/null +++ b/exercises/practice/game-of-life/mix.exs @@ -0,0 +1,28 @@ +defmodule GameOfLife.MixProject do + use Mix.Project + + def project do + [ + app: :game_of_life, + version: "0.1.0", + # elixir: "~> 1.10", + start_permanent: Mix.env() == :prod, + deps: deps() + ] + end + + # Run "mix help compile.app" to learn about applications. + def application do + [ + extra_applications: [:logger] + ] + end + + # Run "mix help deps" to learn about dependencies. + defp deps do + [ + # {:dep_from_hexpm, "~> 0.3.0"}, + # {:dep_from_git, git: "https://github.com/elixir-lang/my_dep.git", tag: "0.1.0"} + ] + end +end diff --git a/exercises/practice/game-of-life/test/game_of_life_test.exs b/exercises/practice/game-of-life/test/game_of_life_test.exs new file mode 100644 index 000000000..70e1b0cc7 --- /dev/null +++ b/exercises/practice/game-of-life/test/game_of_life_test.exs @@ -0,0 +1,83 @@ +defmodule GameOfLifeTest do + use ExUnit.Case + + # @tag :pending + test "empty matrix" do + assert GameOfLife.tick([]) == [] + end + + @tag :pending + test "live cells with zero live neighbors die" do + matrix = [[0, 0, 0], [0, 1, 0], [0, 0, 0]] + output = [[0, 0, 0], [0, 0, 0], [0, 0, 0]] + + assert GameOfLife.tick(matrix) == output + end + + @tag :pending + test "live cells with only one live neighbor die" do + matrix = [[0, 0, 0], [0, 1, 0], [0, 1, 0]] + output = [[0, 0, 0], [0, 0, 0], [0, 0, 0]] + + assert GameOfLife.tick(matrix) == output + end + + @tag :pending + test "live cells with two live neighbors stay alive" do + matrix = [[1, 0, 1], [1, 0, 1], [1, 0, 1]] + output = [[0, 0, 0], [1, 0, 1], [0, 0, 0]] + + assert GameOfLife.tick(matrix) == output + end + + @tag :pending + test "live cells with three live neighbors stay alive" do + matrix = [[0, 1, 0], [1, 0, 0], [1, 1, 0]] + output = [[0, 0, 0], [1, 0, 0], [1, 1, 0]] + + assert GameOfLife.tick(matrix) == output + end + + @tag :pending + test "dead cells with three live neighbors become alive" do + matrix = [[1, 1, 0], [0, 0, 0], [1, 0, 0]] + output = [[0, 0, 0], [1, 1, 0], [0, 0, 0]] + + assert GameOfLife.tick(matrix) == output + end + + @tag :pending + test "live cells with four or more neighbors die" do + matrix = [[1, 1, 1], [1, 1, 1], [1, 1, 1]] + output = [[1, 0, 1], [0, 0, 0], [1, 0, 1]] + + assert GameOfLife.tick(matrix) == output + end + + @tag :pending + test "bigger matrix" do + 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] + ] + + output = [ + [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] + ] + + assert GameOfLife.tick(matrix) == output + end +end diff --git a/exercises/practice/game-of-life/test/test_helper.exs b/exercises/practice/game-of-life/test/test_helper.exs new file mode 100644 index 000000000..35fc5bff8 --- /dev/null +++ b/exercises/practice/game-of-life/test/test_helper.exs @@ -0,0 +1,2 @@ +ExUnit.start() +ExUnit.configure(exclude: :pending, trace: true)