diff --git a/lib/bottom.ex b/lib/bottom.ex index e2386de..4bde983 100644 --- a/lib/bottom.ex +++ b/lib/bottom.ex @@ -15,4 +15,57 @@ defmodule Tetris.Bottom do Enum.any?(points, &collides?(bottom,&1)) end + def complete_ys(bottom) do + bottom + |> Map.keys + |> Enum.map(&elem(&1, 1)) + |> Enum.uniq + |> Enum.filter(fn row -> complete?(bottom, row) end) + end + + def complete?(bottom, row) do + count = + bottom + |> Map.keys + |> Enum.filter(fn {_x,y} -> y == row end) + |> Enum.count + + count == 10 + end + + def collapse_row(bottom, row) do + bad_keys = + bottom + |> Map.keys + |> Enum.filter(fn {_x,y} -> y == row end) + + bottom + |> Map.drop(bad_keys) + |> Enum.map(&move_bad_points_up(&1, row)) + |> Map.new + + end + + def move_bad_points_up({{x, y}, {x, y, colour}}, row) when y < row do + #move_bad_poitns_down ??? + {{x, y+1}, {x, y+1, colour}} + end + def move_bad_points_up(key_value, _row) do + key_value + end + + def full_collapse(bottom) do + # a bottom less completed row + rows = + bottom + |> complete_ys() + |> Enum.sort + + new_bottom = + Enum.reduce(rows, bottom, &collapse_row(&2, &1)) + + {Enum.count(rows), new_bottom} + end + + end diff --git a/lib/tetris.ex b/lib/tetris.ex index 4039349..625b50b 100644 --- a/lib/tetris.ex +++ b/lib/tetris.ex @@ -8,7 +8,59 @@ defmodule Tetris do end - def try_move(brick, bottom, f) do + + + def drop(brick, bottom, colour) do + new_brick = + Brick.down(brick) + maybe_do_drop( + Bottom.collides?(bottom, prepare(new_brick)), + bottom, + brick, + new_brick, + colour + ) + end + + def maybe_do_drop(true=_collided,bottom, old_brick, _new_block, colour) do + new_brick = Brick.new_random + + points = + old_brick + |> prepare + |> Points.with_colour(colour) + + {count, new_bottom} = + bottom + |> Bottom.merge(points) + |> Bottom.full_collapse + + %{ + brick: new_brick, + bottom: new_bottom, + score: score(count), + game_over: Bottom.collides?(new_bottom, prepare(new_brick)) + } + end + def maybe_do_drop(false=_collided,bottom, _old_brick, new_block, _colour) do + %{ + brick: new_block, + bottom: bottom, + score: 1, + game_over: false + } + end + + def score(0), do: 0 + def score(count) do + 100 * round(:math.pow(2, count)) + end + + def try_left(brick, bottom), do: try_move(brick,bottom, &Brick.left/1) + def try_right(brick, bottom), do: try_move(brick,bottom, &Brick.right/1) + def try_spin(brick, bottom), do: try_move(brick,bottom, &Brick.spin_90/1) + + defp try_move(brick, bottom, f) do new_brick = f.(brick) if Bottom.collides?(bottom, prepare(new_brick)) do @@ -17,9 +69,4 @@ defmodule Tetris do new_brick end end - - def try_left(brick, bottom), do: try_move(brick,bottom, &Brick.left/1) - def try_right(brick, bottom), do: try_move(brick,bottom, &Brick.right/1) - def try_spin(brick, bottom), do: try_move(brick,bottom, &Brick.spin_90/1) - end diff --git a/test/bottom_test.exs b/test/bottom_test.exs index 857c95f..513a9bc 100644 --- a/test/bottom_test.exs +++ b/test/bottom_test.exs @@ -26,4 +26,39 @@ defmodule BottomTest do } assert actual == expected end + + test "compute complete ys" do + bottom = new_bottom(20, [{{19, 19}, {19, 19, :red}}]) + + assert complete_ys(bottom) == [20] + end + + test "collapse single row" do + bottom = new_bottom(20, [{{19, 19}, {19, 19, :red}}]) + actual = Map.keys(collapse_row(bottom, 20)) + refute {19,19} in actual + assert {19, 20} in actual + assert Enum.count(actual) == 1 + #IO.inspect collapse_row(bottom, 20) + end + + + test "full collapse with single row" do + bottom = new_bottom(20, [{{19, 19}, {19, 19, :red}}]) + {actual_count, actual_bottom} = full_collapse(bottom) + assert actual_count == 1 + assert {19,20} in Map.keys(actual_bottom) + end + + + + def new_bottom(complete_row, xtras) do + (xtras ++ + (1..10 + |> Enum.map( fn x -> + {{x, complete_row}, {x,complete_row, :red}} + end))) + |> Map.new + end + end diff --git a/test/tetris_test.exs b/test/tetris_test.exs index bedade2..7ee8807 100644 --- a/test/tetris_test.exs +++ b/test/tetris_test.exs @@ -22,5 +22,49 @@ defmodule TetrisTest do assert actual == brick end + test "drops without merging" do + brick = Brick.new(%{location: {5,5}}) + bottom = %{} + + expected = %{ + brick: Brick.down(brick), + bottom: %{}, + score: 1, + game_over: false + } + + actual = drop(brick, bottom, :red) + + assert actual == expected + end + + test "drops and merges" do + brick = Brick.new(%{location: {5,16}}) + bottom = %{} + + %{score: score, bottom: actual_bottom} = + Tetris.drop(brick, bottom, :red) + + assert Map.get(actual_bottom, {7,20}) == {7,20, :red} + assert score == 0 + end + + test "drops and merges with collapse" do + brick = Brick.new(%{location: {5,16}}) + bottom = + for x <- 1..10, y <- 17..20, x != 7 do + {{x,y}, {x,y, :red}} + end + |> Map.new + + + %{score: score, bottom: actual_bottom} = + Tetris.drop(brick, bottom, :red) + + assert actual_bottom == %{} + assert score == 1600 + end + + end