commit cf00dc57ba94a13493711cb8646b0711ab7a7765 Author: Julian Noble Date: Sun Sep 11 03:04:31 2022 +1000 Tetris basic model diff --git a/.formatter.exs b/.formatter.exs new file mode 100644 index 0000000..d2cda26 --- /dev/null +++ b/.formatter.exs @@ -0,0 +1,4 @@ +# Used by "mix format" +[ + inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"] +] diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7f84502 --- /dev/null +++ b/.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"). +tetris-*.tar + +# Temporary files, for example, from tests. +/tmp/ diff --git a/README.md b/README.md new file mode 100644 index 0000000..29a0de9 --- /dev/null +++ b/README.md @@ -0,0 +1,21 @@ +# Tetris + +**TODO: Add description** + +## Installation + +If [available in Hex](https://hex.pm/docs/publish), the package can be installed +by adding `tetris` to your list of dependencies in `mix.exs`: + +```elixir +def deps do + [ + {:tetris, "~> 0.1.0"} + ] +end +``` + +Documentation can be generated with [ExDoc](https://github.com/elixir-lang/ex_doc) +and published on [HexDocs](https://hexdocs.pm). Once published, the docs can +be found at . + diff --git a/git.exe.stackdump b/git.exe.stackdump new file mode 100644 index 0000000..b03cfb7 --- /dev/null +++ b/git.exe.stackdump @@ -0,0 +1,19 @@ +Stack trace: +Frame Function Args +000FFFFBBF0 00180062DF7 (000FFFFBDF8, 00000000002, 00000000002, 000FFFFDE50) +00000000000 00180064EE5 (00000000064, 00000000000, 00000000420, 00000000000) +000FFFFC300 00180136238 (00100000000, 000FFFFC708, 000FFFFC770, 000FFFFC798) +00000000041 0018013186B (00100640281, 00000000000, 00000000000, 000FFFFC770) +000FFFFC798 00180131C75 (000FFFFC770, 000FFFFC798, 001006F6B40, 000FFFFC750) +000FFFFC798 00180218948 (00180111A72, 000FFFFC750, 00180194D3B, 000FFFFC6D8) +000FFFFC798 00100668785 (00180249BA0, 001006E0C1D, 001006EB201, 0018027FF30) +000FFFFC798 001005EF056 (00000000002, 0010068C902, 000000000AC, 00000323731) +00100747200 001005A21A2 (000FFFFCC58, 00000000000, 0010061F260, 0010068D3B0) +00100747200 0010042D381 (0018005DAE7, 008000628B5, 0010060E7D0, 001007477C0) +00100747200 0010040203B (00180249BA0, 00000000000, 001800DB27E, 00000000030) +000FFFFCC58 001004025A3 (000FFFFCC58, 04949435341, 000FFFFCC70, 00180328FE0) +000FFFFCD30 001006686E4 (00000000020, 00000000000, 00180049B25, 000FFFFCC70) +000FFFFCD30 00180049B91 (00000000000, 00000000000, 00000000000, 00000000000) +000FFFFFFF0 00180047716 (00000000000, 00000000000, 00000000000, 00000000000) +000FFFFFFF0 001800477C4 (00000000000, 00000000000, 00000000000, 00000000000) +End of stack trace diff --git a/lib/brick.ex b/lib/brick.ex new file mode 100644 index 0000000..acedf07 --- /dev/null +++ b/lib/brick.ex @@ -0,0 +1,152 @@ +defmodule Tetris.Brick do + alias Tetris.Points + + @x_centre 40 + + defstruct [ + name: :i, + location: {40,0}, + rotation: 0, + reflection: false + ] + + def new(attributes \\ []), do: __struct__(attributes) + + def new_random() do + %__MODULE__{ + name: random_name(), + location: {40,0}, + rotation: random_rotation(), + reflection: random_reflection() + } + end + + def random_name() do + ~w(i l z o t)a + |> Enum.random() + end + + def random_rotation() do + [0, 90, 180, 270] + |> Enum.random() + end + + + def random_reflection() do + [true, false] + |> Enum.random + end + + def left(brick) do + %{brick| location: point_left(brick.location)} + end + + def right(brick) do + %{brick| location: point_right(brick.location)} + end + + def down(brick) do + %{brick| location: point_down(brick.location)} + end + + def point_down({x,y}) do + {x,y+1} + end + def point_left({x,y}) do + {x-1,y} + end + def point_right({x,y}) do + {x+1,y} + end + + def spin_90(brick) do + %{brick| rotation: rotate(brick.rotation)} + end + def rotate(270), do: 0 + def rotate(degrees), do: degrees + 90 + + + def shape(%{name: :l}) do + [ + {2,1}, + {2,2}, + {2,3}, {3,3} + ] + end + def shape(%{name: :i}) do + [ + {2,1}, + {2,2}, + {2,3}, + {2,4} + ] + end + def shape(%{name: :o}) do + [ + {2,2}, {3,2}, + {2,3}, {3,3} + ] + end + def shape(%{name: :z}) do + [ + {2,2}, + {2,3}, {3,3}, + {3,4} + ] + end + def shape(%{name: :t}) do + [ + {2,1}, + {2,2}, {3,2} , + {2,3} + ] + end + + def prepare(brick) do + brick + |> shape + |> Points.rotate(brick.rotation) + |> Points.mirror(brick.reflection) + end + + def to_string(brick) do + brick + |> prepare + |> Points.to_string + end + + def print(brick) do + brick + |> prepare + |> Points.print + + brick + end + + def colour(%{name: :i}), do: :blue + def colour(%{name: :l}), do: :green + def colour(%{name: :z}), do: :orange + def colour(%{name: :o}), do: :red + def colour(%{name: :t}), do: :yellow + + + + def x_centre(), do: @x_centre + + defimpl Inspect do + import Inspect.Algebra + def inspect(brick, _opts) do + concat([ + "\n", + Tetris.Brick.to_string(brick), + "\n", + "location : #{inspect(brick.location)}", + "\n", + "reflection: #{inspect(brick.reflection)}", + "\n", + "rotation : #{inspect(brick.rotation)}" + ]) + end + end + +end diff --git a/lib/points.ex b/lib/points.ex new file mode 100644 index 0000000..4f1a258 --- /dev/null +++ b/lib/points.ex @@ -0,0 +1,68 @@ +defmodule Tetris.Points do + + def move_to_location(points, {x,y}=_location) do + Enum.map(points,fn {dx, dy} -> {dx + x, dy + y} end ) + end + + def transpose(points) do + points + |> Enum.map( fn {x,y} -> {y,x} end ) + end + def mirror(points) do + points + |> Enum.map( fn{x,y} -> {5-x,y} end ) + end + def mirror(points, false), do: points + def mirror(points, true), do: mirror points + + def flip(points) do + points + |> Enum.map( fn{x,y} -> {x,5-y} end ) + end + + def rotate_90(points) do + #by 90 degrees clockwise + points + |> transpose + |> mirror + end + + def rotate(points, 0), do: points + def rotate(points, degrees) do + rotate( + rotate_90(points), + degrees - 90 + ) + end + + def with_colour(points, colour) do + Enum.map(points,fn point -> add_colour(point, colour) end) + end + + defp add_colour({_x, _y, _c} = point, _colour), do: point + defp add_colour({x,y}, colour), do: {x,y,colour} + + + + #šŸ”“ + #ā¬œ + #šŸŸ„ + def to_string(points) do + map = + points + |> Enum.map(fn key -> {key, "šŸŸ„"} end) + |> Map.new() + + for y <- (1 .. 4), x <- (1 .. 4) do + Map.get(map,{x,y},"ā¬œ") + end + |> Enum.chunk_every(4) + |> Enum.map(&Enum.join/1) + |> Enum.join("\n") + end + + def print(points) do + IO.puts __MODULE__.to_string(points) + points + end +end diff --git a/lib/tetris.ex b/lib/tetris.ex new file mode 100644 index 0000000..ba5aa00 --- /dev/null +++ b/lib/tetris.ex @@ -0,0 +1,6 @@ +defmodule Tetris do + + def hello do + :world + end +end diff --git a/mix.exs b/mix.exs new file mode 100644 index 0000000..57de2c0 --- /dev/null +++ b/mix.exs @@ -0,0 +1,28 @@ +defmodule Tetris.MixProject do + use Mix.Project + + def project do + [ + app: :tetris, + version: "0.1.0", + elixir: "~> 1.14", + 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/test/brick_test.exs b/test/brick_test.exs new file mode 100644 index 0000000..d1e0247 --- /dev/null +++ b/test/brick_test.exs @@ -0,0 +1,98 @@ +defmodule BrickTest do + use ExUnit.Case + + import Tetris.Brick + alias Tetris.Points + + def new_brick(attributes \\[]), do: new(attributes) + + test "Creates a new brick" do + assert new_brick().name == :i + end + + test "Create a new random brick" do + actual = new_random() + + assert actual.name in [:i, :l, :z, :t, :o] + assert actual.rotation in [0, 90, 180, 270] + assert actual.reflection in [true, false] + + end + + test "should manipulate brick" do + actual = + new_brick() + |> left + |> right + |> right + |> down + |> spin_90 + |> spin_90 + + assert actual.location == {x_centre() + 1,1} + assert actual.rotation == 180 + end + + test "should return points for o shape" do + points = + new_brick(name: :o) + |> shape() + + assert {3,3} in points + end + + + test "should translate a list of points" do + actual_points = + new_brick() + |> shape + |> Points.move_to_location({1,1}) + |> Points.move_to_location({0,1}) + + assert actual_points == [{3, 3}, {3, 4}, {3, 5}, {3, 6}] + end + + test "should mirror flip rotate and rotate" do + [{1,1}] + |> Points.mirror + |> assert_point({4,1}) + |> Points. flip + |> assert_point({4,4}) + |> Points.rotate_90 + |> assert_point({1,4}) + |> Points.rotate_90 + |> assert_point({1,1}) + end + + def assert_point([actual],expected) do + assert actual == expected + [actual] + end + + test "should convert brick to string" do + actual = new_brick() |> Tetris.Brick.to_string + + expected = "ā¬œšŸŸ„ā¬œā¬œ\nā¬œšŸŸ„ā¬œā¬œ\nā¬œšŸŸ„ā¬œā¬œ\nā¬œšŸŸ„ā¬œā¬œ" + + assert actual == expected + end + + test "should inspect bricks" do + actual = new_brick() |> inspect + + expected = + """ + + ā¬œšŸŸ„ā¬œā¬œ + ā¬œšŸŸ„ā¬œā¬œ + ā¬œšŸŸ„ā¬œā¬œ + ā¬œšŸŸ„ā¬œā¬œ + location : {#{x_centre()}, 0} + reflection: false + rotation : 0 + """ + assert "#{actual}\n" == expected + end + + +end diff --git a/test/test_helper.exs b/test/test_helper.exs new file mode 100644 index 0000000..869559e --- /dev/null +++ b/test/test_helper.exs @@ -0,0 +1 @@ +ExUnit.start() diff --git a/test/tetris_test.exs b/test/tetris_test.exs new file mode 100644 index 0000000..731ca15 --- /dev/null +++ b/test/tetris_test.exs @@ -0,0 +1,7 @@ +defmodule TetrisTest do + use ExUnit.Case + + test "greets the world" do + assert Tetris.hello() == :world + end +end