From 743c4dd1132f21a580b6624e07f8b177396b855b Mon Sep 17 00:00:00 2001 From: Alex Pearwin Date: Wed, 25 Oct 2023 15:12:52 +0100 Subject: [PATCH] Comments. --- game.exs | 66 +++++++++++++++++++++++++++++++++++--------------------- mix.exs | 3 ++- mix.lock | 2 ++ 3 files changed, 45 insertions(+), 26 deletions(-) diff --git a/game.exs b/game.exs index 13c613e..ade5c6e 100644 --- a/game.exs +++ b/game.exs @@ -1,20 +1,23 @@ defmodule Game do @default_options [ # Number of cells per row and per column. - board_dimensions: {3, 2}, + board_dimensions: {4, 4}, # Value of the singular piece present at the beginning of the game. starting_number: 2, # Value of the piece randomly inserted into the board at the beginning of # each turn. turn_start_number: 1, # Value of the piece which, when present on the board, results in a win. - winning_number: 4 + winning_number: 2048 ] @cell_size 5 def init(opts \\ []) do opts = Keyword.validate!(opts, @default_options) + # TODO validate that value options are all powers of two and that starting + # and turn start values are both less than winning value. + game = %{ board: starting_board(opts[:board_dimensions], opts[:starting_number]), score: 0, @@ -104,13 +107,11 @@ defmodule Game do end defp render_board(%{cells: cells, dimensions: {num_rows, num_cols}}) do - Enum.map(1..num_rows, fn row -> - Enum.map(1..num_cols, fn col -> + Enum.map_join(1..num_rows, "\n", fn row -> + Enum.map_join(1..num_cols, "", fn col -> render_cell(cells, {row, col}) end) - |> Enum.join("") end) - |> Enum.join("\n") end defp render_cell(cells, coordinate) do @@ -151,35 +152,50 @@ defmodule Game do end defp merge_values(%{cells: cells} = board, move) do + # For each row, we run a two-pointer algorithm where: + # + # * Pointer #1 iterates through the row. + # * Pointer #2 points to the latest non-empty, non-modified cell behind pointer #1. + # + # As #1 iterates, if its current cell is not empty and: + # + # * Has the same value as the cell of #2: the cell of #1 will be merged into that of #2 (the #2 cell + # value will be doubled and the #1 cell will be emptied) and the #2 pointer will + # be nullified. Or; + # * Does not have the same value as the cell of #2: the #2 pointer + # is updated to point to #1 before #1 continues its iteration. updates = rows_for_move(board, move) |> Enum.map(fn row -> Enum.map(row, fn coord -> {coord, cells[coord]} end) end) - |> Enum.flat_map(fn row -> - new_row = Enum.into(row, %{}) - - {new_row, _} = - Enum.reduce(row, {new_row, nil}, fn {coord, current_value}, - {new_row, last_non_empty_coord} -> - if is_nil(current_value) do - {new_row, last_non_empty_coord} - else - if current_value == new_row[last_non_empty_coord] do - {%{new_row | last_non_empty_coord => 2 * current_value, coord => nil}, nil} - else - {new_row, coord} - end - end - end) - - Map.to_list(new_row) - end) + |> Enum.flat_map(&merge_row_values(&1)) |> Enum.into(%{}) update_board(board, updates) end + defp merge_row_values(row) do + # row is a list of {{row, col}, value} elements. + new_row = Enum.into(row, %{}) + + {new_row, _} = + Enum.reduce(row, {new_row, nil}, fn {coord, current_value}, + {new_row, last_non_empty_coord} -> + if is_nil(current_value) do + {new_row, last_non_empty_coord} + else + if current_value == new_row[last_non_empty_coord] do + {%{new_row | last_non_empty_coord => 2 * current_value, coord => nil}, nil} + else + {new_row, coord} + end + end + end) + + Map.to_list(new_row) + end + defp move_values(%{cells: cells} = board, move) do # Conceptually, for each 'row' of values being moved: # 1. Create a new row with all non-empty cells. diff --git a/mix.exs b/mix.exs index ad4b834..862a833 100644 --- a/mix.exs +++ b/mix.exs @@ -50,7 +50,8 @@ defmodule TwentyFourtyEight.MixProject do {:gettext, "~> 0.20"}, {:jason, "~> 1.2"}, {:dns_cluster, "~> 0.1.1"}, - {:plug_cowboy, "~> 2.5"} + {:plug_cowboy, "~> 2.5"}, + {:credo, "~> 1.7", only: [:dev, :test], runtime: false} ] end diff --git a/mix.lock b/mix.lock index 26d2cd4..00bd47c 100644 --- a/mix.lock +++ b/mix.lock @@ -1,8 +1,10 @@ %{ + "bunt": {:hex, :bunt, "0.2.1", "e2d4792f7bc0ced7583ab54922808919518d0e57ee162901a16a1b6664ef3b14", [:mix], [], "hexpm", "a330bfb4245239787b15005e66ae6845c9cd524a288f0d141c148b02603777a5"}, "castore": {:hex, :castore, "1.0.4", "ff4d0fb2e6411c0479b1d965a814ea6d00e51eb2f58697446e9c41a97d940b28", [:mix], [], "hexpm", "9418c1b8144e11656f0be99943db4caf04612e3eaecefb5dae9a2a87565584f8"}, "cowboy": {:hex, :cowboy, "2.10.0", "ff9ffeff91dae4ae270dd975642997afe2a1179d94b1887863e43f681a203e26", [:make, :rebar3], [{:cowlib, "2.12.1", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "1.8.0", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "3afdccb7183cc6f143cb14d3cf51fa00e53db9ec80cdcd525482f5e99bc41d6b"}, "cowboy_telemetry": {:hex, :cowboy_telemetry, "0.4.0", "f239f68b588efa7707abce16a84d0d2acf3a0f50571f8bb7f56a15865aae820c", [:rebar3], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "7d98bac1ee4565d31b62d59f8823dfd8356a169e7fcbb83831b8a5397404c9de"}, "cowlib": {:hex, :cowlib, "2.12.1", "a9fa9a625f1d2025fe6b462cb865881329b5caff8f1854d1cbc9f9533f00e1e1", [:make, :rebar3], [], "hexpm", "163b73f6367a7341b33c794c4e88e7dbfe6498ac42dcd69ef44c5bc5507c8db0"}, + "credo": {:hex, :credo, "1.7.1", "6e26bbcc9e22eefbff7e43188e69924e78818e2fe6282487d0703652bc20fd62", [:mix], [{:bunt, "~> 0.2.1", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2.8", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "e9871c6095a4c0381c89b6aa98bc6260a8ba6addccf7f6a53da8849c748a58a2"}, "db_connection": {:hex, :db_connection, "2.6.0", "77d835c472b5b67fc4f29556dee74bf511bbafecdcaf98c27d27fa5918152086", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "c2f992d15725e721ec7fbc1189d4ecdb8afef76648c746a8e1cad35e3b8a35f3"}, "decimal": {:hex, :decimal, "2.1.1", "5611dca5d4b2c3dd497dec8f68751f1f1a54755e8ed2a966c2633cf885973ad6", [:mix], [], "hexpm", "53cfe5f497ed0e7771ae1a475575603d77425099ba5faef9394932b35020ffcc"}, "dns_cluster": {:hex, :dns_cluster, "0.1.1", "73b4b2c3ec692f8a64276c43f8c929733a9ab9ac48c34e4c0b3d9d1b5cd69155", [:mix], [], "hexpm", "03a3f6ff16dcbb53e219b99c7af6aab29eb6b88acf80164b4bd76ac18dc890b3"},