Last active
June 15, 2020 09:05
-
-
Save sandeshsoni/14859f37872c191aefebb129b4647085 to your computer and use it in GitHub Desktop.
Tetris models
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
defmodule Tetris.Core.Board do | |
alias Tetris.Core.Shape | |
@default_width 40 | |
@default_height 50 | |
defstruct ~w( | |
tiles | |
seq_map | |
width | |
height | |
indexor | |
# empty_lane_ids | |
lanes | |
)a | |
# def new(input_width // @default_width, input_height // @default_height ) do | |
def new(input_width \\ @default_width, input_height \\ @default_height ) do | |
%__MODULE__{ | |
width: input_width, | |
height: input_height, | |
lanes: %{}, | |
# empty_lane_ids: Enum.map(1..input_height, &(&1)), | |
indexor: %{} | |
# Map.new(empty_lanes: Enum.map(1..input_height, &(&1))) | |
} | |
end | |
def check_tile_slot_empty(board,{x, y} = tile) do | |
index_y = Map.get(board.indexor, y, 0) | |
tile_color = Map.get(board.lanes, index_y, %{}) |> Map.get(x, :empty) | |
if tile_color == :empty do | |
true | |
else | |
false | |
end | |
end | |
# lane_seq = {:empty, :empty, :empty, lane_14rand0m, lane_12rand0m} | |
# tiles: {1,2} => :red | |
# {x, y} | |
# lane_11rand0m = %{0: :blue, 1: :nil, 2: green, 3: yello} | |
# lane_12rand0m = %{0: :red, 1: :blue, 2: nil, 3: yello} | |
# lane_14rand0m = %{0: :red, 1: :red, 2: green, 3: yello} | |
# def process do | |
# board = %{1 => %{1 => :a, 2 => :b, 3 => nil}} | |
# access_element = board[1][3] | |
# # store lane here | |
# seq_abc = [{:b, 1}, {:a, 3}] | |
# board_seq_map = %{19 => 1, 20 => 4, empties: [1,2]} | |
# end | |
# defp intersection_check? do | |
# board = %{} | |
# seq_map = %{19 => 1, 20 => 4, empties: [1,2]} | |
# shape_coords = [{1,1}, {2,3}] | |
# board = Map.put(board, 4, %{1 => :red}) | |
# board[ seq_map[20]][1] | |
# Enum.any?(shape_coords, fn coord -> tile_present_in(coord, board) end) | |
# end | |
# defp tile_present_in(coord, board) do | |
# false | |
# end | |
def display_lane_tiles(board, lane_no) do | |
index_y = Map.get(board.indexor, lane_no, 0) | |
Map.get(board.lanes, index_y, %{}) | |
end | |
# get all matured lanes | |
# defp any_lane_matured? do | |
# board = %{} | |
# # lanes_where shaped dropped | |
# # 19, 20 | |
# end | |
# defp on_matured do | |
# lanes = %{19 => 17, empty: [19, 1,2]} | |
# # change key or copy paste to new location | |
# # if 17 and 19 are matured, | |
# # shift down 18 by 1, all above 16 by 2 | |
# end | |
# lane_seq[4] | |
end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
defmodule Tetris.Boundary.BoardManager do | |
alias Tetris.Core.{Board, Shape} | |
alias Tetris.Boundary.{Rules} | |
def add(%Board{lanes: board_lanes} = board, | |
%Shape{ | |
color: shape_color, coordinates: coordinates} = shape, | |
{offset_x, offset_y}) do | |
if Rules.shape_outside_board?(board, shape, {offset_x, offset_y}) do | |
{:error, :out_of_board} | |
else | |
tiles = Enum.map(coordinates, fn {x,y} -> {x + offset_x, y + offset_y} end) | |
u_board = Enum.reduce(tiles, board, fn tile, acc_board -> add_tile_to_board(acc_board, tile, shape_color ) end) | |
{:ok, u_board} | |
end | |
end | |
def add_tile_to_board(board, {x,y} = tile, color) do | |
generate_random_uniq = fn -> | |
Integer.to_string(:rand.uniform(4294967296), 32) | |
end | |
u_indexor = Map.put_new_lazy(board.indexor, y, generate_random_uniq) | |
lane_key = u_indexor[y] | |
y_lane = Map.get(board.lanes, lane_key, %{}) | |
y_lane_with_tile_added = Map.put(y_lane, x, color) | |
u_lanes = Map.put(board.lanes, lane_key, y_lane_with_tile_added) | |
%Board{ board | indexor: u_indexor, lanes: u_lanes} | |
end | |
def remove_lanes_from_board(board, lane_indexes) do | |
input_lane_indexes_keys = board.indexor | |
|> Map.take(lane_indexes) | |
|> Map.values | |
u_lanes = Map.drop(board.lanes, input_lane_indexes_keys) | |
no_of_lanes_in_lane_index_smaller_than = fn(index_key) -> | |
Enum.count(lane_indexes, fn(x) -> (x > index_key) end) | |
end | |
u_indexor = board.indexor | |
|> Map.drop(lane_indexes) | |
|> Enum.reduce(%{}, fn ({index_key, index_val}, acc) -> | |
Map.put(acc, | |
index_key + no_of_lanes_in_lane_index_smaller_than.(index_key), | |
index_val | |
) | |
end) | |
%Board{ board | indexor: u_indexor, lanes: u_lanes | |
} | |
end | |
end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
defmodule Tetris.Core.Shape do | |
defstruct ~w[ | |
coordinates | |
color | |
length | |
]a | |
# rotate | |
def rotate(%__MODULE__{coordinates: coordinates, | |
length: length | |
} = shape) do | |
new_coords = coordinates | |
|> Enum.map(fn{x, y} -> {length - y, x} end) | |
%__MODULE__{ shape | coordinates: MapSet.new(new_coords) } | |
end | |
@doc """ | |
Generates a random Shape every time. | |
You might want different orientation so you can rotate it as you want. | |
""" | |
def new_random do | |
[:l_shape, :s_shape, :t_shape, :box_shape, :line_shape] | |
|> Enum.random | |
|> new | |
end | |
@doc """ | |
coordinate {x,y} | |
x, y. from 0,0 | |
l * * | |
l * * | |
l l * | |
L - shape | |
""" | |
def new(:l_shape) do | |
%__MODULE__{ | |
coordinates: MapSet.new([ {0,0}, | |
{0,1}, | |
{0,2},{1,2} | |
]), | |
length: 2, | |
color: :red | |
} | |
end | |
@doc """ | |
coordinate {x,y} | |
x, y. from 0,0 | |
l * * * | |
l * * * | |
l * * * | |
l * * * | |
L - shape | |
""" | |
def new(:line_shape) do | |
%__MODULE__{ | |
coordinates: MapSet.new([ {0,0}, | |
{0,1}, | |
{0,2}, | |
{0,3} | |
]), | |
length: 4, | |
color: :red | |
} | |
end | |
@doc """ | |
{x, y} | |
b b * | |
b b * | |
S - shape | |
""" | |
def new(:box_shape) do | |
%__MODULE__{ | |
coordinates: MapSet.new([{0,0}, {1,0}, | |
{0,1}, {1,1} | |
]), | |
length: 1, | |
# offset_x: starting_point_x, | |
# offset_y: starting_point_y, | |
color: :yellow | |
} | |
end | |
# {x, y} | |
# s * * | |
# s s * | |
# * s * | |
# S - shape | |
def new(:s_shape) do | |
%__MODULE__{ | |
coordinates: MapSet.new([{0,0}, {0,1}, {1,1}, {1,2}]), | |
length: 2, | |
# offset_x: starting_point_x, | |
# offset_y: starting_point_y, | |
color: :blue | |
} | |
end | |
def with_offset_counted(%__MODULE__{coordinates: coordinates} = _shape, input_offset_x, input_offset_y) do | |
Enum.map(coordinates, fn {x,y} -> {x + input_offset_x, y + input_offset_y} end) | |
end | |
# coordinate {x,y} | |
# x, y. from 0,0 | |
# * t * | |
# t t t | |
# * * * | |
# t - shape | |
def new(:t_shape) do | |
%__MODULE__{ | |
coordinates: MapSet.new([ {1,0}, | |
{0,1},{1,1},{2,1} | |
]), | |
length: 2, | |
# offset_x: starting_point_x, | |
# offset_y: starting_point_y, | |
color: :green | |
} | |
end | |
# def new(shape, starting_point_x \\ 0, starting_point_y \\ 0) do | |
# end | |
end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
defmodule Tetris.Core.Game do | |
alias Tetris.Core.{Board, Shape} | |
defstruct ~w[ | |
state_change_listener | |
current_state | |
active_shape | |
next_shape | |
board | |
score | |
game_shapes | |
game_over | |
offset_x | |
offset_y | |
]a | |
def new do | |
%__MODULE__{ | |
board: Board.new, | |
score: 0 | |
} | |
end | |
def new(opts) do | |
def_opts = %{ score: 0, | |
board: Board.new(Map.get(opts, :width, 50), Map.get(opts, :height, 50)), | |
offset_x: 15, | |
offset_y: 5, | |
active_shape: Shape.new(:s_shape), | |
next_shape: Shape.new(:l_shape), | |
current_state: :initiated | |
} | |
struct(__MODULE__, Map.merge(def_opts, opts)) | |
end | |
end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
defmodule Tetris.Boundary.GameLogic do | |
alias Tetris.Core.{Board, Shape, Game} | |
alias Tetris.Boundary.{Rules, BoardManager} | |
def rotate(%Game{ offset_x: offset_x, | |
offset_y: offset_y, | |
active_shape: %Shape{coordinates: coordinates} = active_shape, | |
board: board | |
} = game) do | |
with rotated_shape <- Shape.rotate(active_shape), | |
coordinates <- {offset_x, offset_y}, | |
{:ok, _coordinates} <- Rules.validate_shape_position(board, rotated_shape, coordinates), | |
{:ok, _coordinates} <- Rules.detect_colission(board, rotated_shape, coordinates), | |
{:ok, _updated_game} <- Rules.not_touches_ground(board, rotated_shape, coordinates) | |
do | |
%Game{game | active_shape: rotated_shape} | |
else | |
{:error, :outside} -> game | |
{:error, :tile_present} -> game | |
{:error, :tile_below} -> game | |
{:error, :touches_ground} -> game | |
end | |
end | |
@doc """ | |
next frame, coordinate will be { x - 1, y} | |
Let us name is u_coordinate | |
On move left, | |
first check if the shape goes outside the board. both left and right hand side. | |
If there is a tile on u_coordinate, retain old state | |
""" | |
def move(%Game{ offset_x: offset_x, | |
offset_y: offset_y, | |
active_shape: shape, | |
board: board | |
} = game, :left) do | |
# def move(offset_x, offset_y, shape, board, :left) do | |
with u_coordinates <- { offset_x - 1, offset_y}, | |
{:ok, coordinates} <- Rules.validate_shape_position(board, shape, u_coordinates), | |
{:ok, coordinates} <- Rules.detect_colission(board, shape, u_coordinates) | |
do | |
{u_offset_x, u_offset_y} = u_coordinates | |
%Game{game | offset_x: u_offset_x, offset_y: u_offset_y} | |
else | |
{:error, :outside} -> game | |
{:error, :tile_present} -> game | |
end | |
end | |
defp step_for_tile_below(game, shape, {offset_x, offset_y} = coordinates) do | |
{:ok, b_w_s} = BoardManager.add(game.board, game.active_shape, coordinates) | |
lanes = Rules.lanes_matured_with_shape_at(b_w_s, shape, coordinates) | |
if length(lanes) == 0 do | |
%Game{ game | | |
offset_x: 5, | |
offset_y: 1, | |
active_shape: game.next_shape, | |
next_shape: Shape.new_random(), | |
board: b_w_s | |
} | |
else | |
%Game{ game | | |
offset_x: 5, | |
offset_y: 1, | |
score: (game.score + length(lanes) * game.board.width ), | |
active_shape: game.next_shape, | |
next_shape: Shape.new_random(), | |
# board: b_w_s | |
board: BoardManager.remove_lanes_from_board(b_w_s, lanes) | |
} | |
end | |
end | |
def declare_game_over(game) do | |
%Game{game | current_state: :game_over} | |
end | |
defp move_for_touched_ground(game, shape, {offset_x, offset_y} = coordinates) do | |
{:ok, b_w_s} = BoardManager.add(game.board, game.active_shape, coordinates) | |
lanes = Rules.lanes_matured_with_shape_at(b_w_s, shape, coordinates) | |
if length(lanes) == 0 do | |
%Game{ game | | |
offset_x: 5, | |
offset_y: 1, | |
active_shape: game.next_shape, | |
next_shape: Shape.new_random(), | |
board: b_w_s | |
} | |
else | |
%Game{ game | | |
offset_x: 5, | |
offset_y: 1, | |
score: (game.score + length(lanes) * game.board.width ), | |
active_shape: game.next_shape, | |
next_shape: Shape.new_random(), | |
board: BoardManager.remove_lanes_from_board(b_w_s, lanes) | |
} | |
end | |
end | |
@doc """ | |
next frame, coordinate will be { x + 1, y} | |
Let us name is u_coordinate | |
first check if the shape goes outside the board. both left and right hand side. | |
next, If there is a tile on u_coordinate, retain old state | |
""" | |
def move(%Game{ offset_x: offset_x, | |
offset_y: offset_y, | |
active_shape: shape, | |
board: board | |
} = game, :right) do | |
with u_coordinates <- { offset_x + 1, offset_y}, | |
{:ok, coordinates} <- Rules.validate_shape_position(board, shape, u_coordinates), | |
{:ok, coordinates} <- Rules.detect_colission(board, shape, u_coordinates) | |
do | |
{u_offset_x, u_offset_y} = u_coordinates | |
%Game{game | offset_x: u_offset_x, offset_y: u_offset_y} | |
else | |
{:error, :outside} -> game | |
{:error, :tile_present} -> game | |
end | |
end | |
@doc """ | |
next frame, coordinate will be { x, y + 1 } | |
Let us name is u_coordinate | |
first check if the shape goes outside the board. | |
next, If there is a tile on u_coordinate, retain old state | |
next, If it is bottom of board, retain old state | |
""" | |
def move(%Game{ offset_x: offset_x, | |
offset_y: offset_y, | |
active_shape: shape, | |
board: board | |
} = game, :down) do | |
with u_coordinates <- { offset_x, offset_y + 1}, | |
{:ok, _coordinates} <- Rules.validate_shape_position(board, shape, u_coordinates), | |
{:ok, _coordinates} <- Rules.detect_colission(board, shape, u_coordinates), | |
{:ok, _updated_game} <- Rules.not_touches_ground(board, shape, u_coordinates) | |
do | |
{u_offset_x, u_offset_y} = u_coordinates | |
%Game{game | offset_x: u_offset_x, offset_y: u_offset_y} | |
else | |
{:error, :outside} -> game | |
{:error, :tile_present} -> game | |
{:error, :touches_ground} -> game | |
end | |
end | |
@doc """ | |
next frame, coordinate will be { x, y + 1 } | |
Let us name is u_coordinate | |
first check if the shape goes outside the board. | |
next, If there is a tile on u_coordinate, add the shape to board with original coordinates. | |
next, If it is bottom of board, add shape to board with bottom coordinate. | |
If cannnot move left, right or bottom, i.e tile surround everywhere, declare game over. | |
if tile_below, set state game_over and offset_y is 1, over. | |
game.gamestate should be :finish | |
""" | |
def move(%Game{ offset_x: offset_x, | |
offset_y: offset_y, | |
active_shape: shape, | |
board: board | |
} = game, :gravity) do | |
with u_coordinates <- { offset_x, offset_y + 1}, | |
{:ok, _updated_game} <- Rules.not_touches_ground(board, shape, u_coordinates), | |
{:ok, _} <- Rules.gravity_pull?(board, shape, u_coordinates) | |
do | |
{u_offset_x, u_offset_y} = u_coordinates | |
%Game{game | offset_x: u_offset_x, offset_y: u_offset_y} | |
else | |
{:error, :touches_ground} -> move_for_touched_ground(game, shape, {offset_x, offset_y}) | |
{:error, :tile_below} -> step_for_tile_below(game, shape, {offset_x, offset_y}) | |
{:error, :game_over} -> declare_game_over(game) | |
end | |
end | |
end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
defmodule Tetris.Boundary.GameLogic do | |
alias Tetris.Core.{Board, Shape, Game} | |
alias Tetris.Boundary.{Rules, BoardManager} | |
def rotate(%Game{ offset_x: offset_x, | |
offset_y: offset_y, | |
active_shape: %Shape{coordinates: coordinates} = active_shape, | |
board: board | |
} = game) do | |
with rotated_shape <- Shape.rotate(active_shape), | |
coordinates <- {offset_x, offset_y}, | |
{:ok, _coordinates} <- Rules.validate_shape_position(board, rotated_shape, coordinates), | |
{:ok, _coordinates} <- Rules.detect_colission(board, rotated_shape, coordinates), | |
{:ok, _updated_game} <- Rules.not_touches_ground(board, rotated_shape, coordinates) | |
do | |
%Game{game | active_shape: rotated_shape} | |
else | |
{:error, :outside} -> game | |
{:error, :tile_present} -> game | |
{:error, :tile_below} -> game | |
{:error, :touches_ground} -> game | |
end | |
end | |
@doc """ | |
next frame, coordinate will be { x - 1, y} | |
Let us name is u_coordinate | |
On move left, | |
first check if the shape goes outside the board. both left and right hand side. | |
If there is a tile on u_coordinate, retain old state | |
""" | |
def move(%Game{ offset_x: offset_x, | |
offset_y: offset_y, | |
active_shape: shape, | |
board: board | |
} = game, :left) do | |
# def move(offset_x, offset_y, shape, board, :left) do | |
with u_coordinates <- { offset_x - 1, offset_y}, | |
{:ok, coordinates} <- Rules.validate_shape_position(board, shape, u_coordinates), | |
{:ok, coordinates} <- Rules.detect_colission(board, shape, u_coordinates) | |
do | |
{u_offset_x, u_offset_y} = u_coordinates | |
%Game{game | offset_x: u_offset_x, offset_y: u_offset_y} | |
else | |
{:error, :outside} -> game | |
{:error, :tile_present} -> game | |
end | |
end | |
defp step_for_tile_below(game, shape, {offset_x, offset_y} = coordinates) do | |
{:ok, b_w_s} = BoardManager.add(game.board, game.active_shape, coordinates) | |
lanes = Rules.lanes_matured_with_shape_at(b_w_s, shape, coordinates) | |
if length(lanes) == 0 do | |
%Game{ game | | |
offset_x: 5, | |
offset_y: 1, | |
active_shape: game.next_shape, | |
next_shape: Shape.new_random(), | |
board: b_w_s | |
} | |
else | |
%Game{ game | | |
offset_x: 5, | |
offset_y: 1, | |
score: (game.score + length(lanes) * game.board.width ), | |
active_shape: game.next_shape, | |
next_shape: Shape.new_random(), | |
# board: b_w_s | |
board: BoardManager.remove_lanes_from_board(b_w_s, lanes) | |
} | |
end | |
end | |
def declare_game_over(game) do | |
%Game{game | current_state: :game_over} | |
end | |
defp move_for_touched_ground(game, shape, {offset_x, offset_y} = coordinates) do | |
{:ok, b_w_s} = BoardManager.add(game.board, game.active_shape, coordinates) | |
lanes = Rules.lanes_matured_with_shape_at(b_w_s, shape, coordinates) | |
if length(lanes) == 0 do | |
%Game{ game | | |
offset_x: 5, | |
offset_y: 1, | |
active_shape: game.next_shape, | |
next_shape: Shape.new_random(), | |
board: b_w_s | |
} | |
else | |
%Game{ game | | |
offset_x: 5, | |
offset_y: 1, | |
score: (game.score + length(lanes) * game.board.width ), | |
active_shape: game.next_shape, | |
next_shape: Shape.new_random(), | |
board: BoardManager.remove_lanes_from_board(b_w_s, lanes) | |
} | |
end | |
end | |
@doc """ | |
next frame, coordinate will be { x + 1, y} | |
Let us name is u_coordinate | |
first check if the shape goes outside the board. both left and right hand side. | |
next, If there is a tile on u_coordinate, retain old state | |
""" | |
def move(%Game{ offset_x: offset_x, | |
offset_y: offset_y, | |
active_shape: shape, | |
board: board | |
} = game, :right) do | |
with u_coordinates <- { offset_x + 1, offset_y}, | |
{:ok, coordinates} <- Rules.validate_shape_position(board, shape, u_coordinates), | |
{:ok, coordinates} <- Rules.detect_colission(board, shape, u_coordinates) | |
do | |
{u_offset_x, u_offset_y} = u_coordinates | |
%Game{game | offset_x: u_offset_x, offset_y: u_offset_y} | |
else | |
{:error, :outside} -> game | |
{:error, :tile_present} -> game | |
end | |
end | |
@doc """ | |
next frame, coordinate will be { x, y + 1 } | |
Let us name is u_coordinate | |
first check if the shape goes outside the board. | |
next, If there is a tile on u_coordinate, retain old state | |
next, If it is bottom of board, retain old state | |
""" | |
def move(%Game{ offset_x: offset_x, | |
offset_y: offset_y, | |
active_shape: shape, | |
board: board | |
} = game, :down) do | |
with u_coordinates <- { offset_x, offset_y + 1}, | |
{:ok, _coordinates} <- Rules.validate_shape_position(board, shape, u_coordinates), | |
{:ok, _coordinates} <- Rules.detect_colission(board, shape, u_coordinates), | |
{:ok, _updated_game} <- Rules.not_touches_ground(board, shape, u_coordinates) | |
do | |
{u_offset_x, u_offset_y} = u_coordinates | |
%Game{game | offset_x: u_offset_x, offset_y: u_offset_y} | |
else | |
{:error, :outside} -> game | |
{:error, :tile_present} -> game | |
{:error, :touches_ground} -> game | |
end | |
end | |
@doc """ | |
next frame, coordinate will be { x, y + 1 } | |
Let us name is u_coordinate | |
first check if the shape goes outside the board. | |
next, If there is a tile on u_coordinate, add the shape to board with original coordinates. | |
next, If it is bottom of board, add shape to board with bottom coordinate. | |
If cannnot move left, right or bottom, i.e tile surround everywhere, declare game over. | |
if tile_below, set state game_over and offset_y is 1, over. | |
game.gamestate should be :finish | |
""" | |
def move(%Game{ offset_x: offset_x, | |
offset_y: offset_y, | |
active_shape: shape, | |
board: board | |
} = game, :gravity) do | |
with u_coordinates <- { offset_x, offset_y + 1}, | |
{:ok, _updated_game} <- Rules.not_touches_ground(board, shape, u_coordinates), | |
{:ok, _} <- Rules.gravity_pull?(board, shape, u_coordinates) | |
do | |
{u_offset_x, u_offset_y} = u_coordinates | |
%Game{game | offset_x: u_offset_x, offset_y: u_offset_y} | |
else | |
{:error, :touches_ground} -> move_for_touched_ground(game, shape, {offset_x, offset_y}) | |
{:error, :tile_below} -> step_for_tile_below(game, shape, {offset_x, offset_y}) | |
{:error, :game_over} -> declare_game_over(game) | |
end | |
end | |
end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
defmodule Tetris.Boundary.Rules do | |
alias Tetris.Core.Board | |
# def touches_side_boundary?(board, shape) do | |
def shape_outside_board?(board, shape, {x_coordinate, y_coordinate}) do | |
Tetris.Core.Shape.with_offset_counted(shape, x_coordinate, y_coordinate) | |
|> Enum.reduce_while(false, fn {x, _y}, acc -> if (x > 0 and x <= (board.width)), do: {:cont, false}, else: {:halt, true} end) | |
end | |
### api | |
def validate_shape_position(board, shape, coordinates) do | |
# IO.puts "validate shape position" | |
if shape_outside_board?(board, shape, coordinates) do | |
{:error, :outside} | |
else | |
{:ok, coordinates} | |
end | |
end | |
#### API | |
# collision on x axis. | |
def detect_colission(board, shape, coordinates) do | |
if shape_collides_with_board_tiles?(board, shape, coordinates) do | |
{:error, :tile_present} | |
else | |
{:ok, coordinates} | |
end | |
end | |
### API | |
# bottom of the board | |
def not_touches_ground(board, shape, coordinates) do | |
if touches_footer?(board, shape, coordinates) do | |
{:error, :touches_ground} | |
else | |
{:ok, board} | |
end | |
end | |
### API | |
# If there is a tile below, then gravity stops. place the tile there. | |
# If tile is present below where y axis is less then shape height, | |
# then it means there is no place left for new shap and game is over. | |
def gravity_pull?(board, shape, {offset_x, offset_y} = coordinates) do | |
if shape_collides_with_board_tiles?(board, shape, coordinates) do | |
if offset_y <= shape.length do | |
{:error, :game_over} | |
else | |
{:error, :tile_below} | |
end | |
else | |
{:ok, board} | |
end | |
end | |
def lanes_matured_with_shape_at(board, shape,{_offset_x, offset_y} = coordinates) do | |
# refactor, indexor not needed | |
length_lane = fn(lane_no) -> | |
idx = Map.get(board.indexor, lane_no, 0) | |
len = Enum.count( Map.get(board.lanes, idx, %{})) | |
# IO.puts len | |
len | |
end | |
shape_lanes = shape.coordinates | |
|> Enum.map(fn {_x, y} -> (y + offset_y) end) | |
|> Enum.uniq | |
|> Enum.filter(fn lane -> (length_lane.(lane) == board.width) end) | |
end | |
defp shape_collides_with_board_tiles?(board, shape, {offset_x, offset_y} = offsets) do | |
# Tetris.Core.Shape.with_offset_counted(shape, x_coordinate, y_coordinate) | |
shape.coordinates | |
|> Enum.reduce_while(false, fn {x, y}, acc -> if tile_slot_empty?(board, {x + offset_x, y + offset_y}), do: {:cont, false}, else: {:halt, true} end) | |
end | |
# so that can drop and move | |
def touches_footer?(board, shape, {x_coordinate, y_coordinate}) do | |
Tetris.Core.Shape.with_offset_counted(shape, x_coordinate, y_coordinate) | |
# |> Enum.reduce_while(false, fn {_x, y}, acc -> if (y == board.height), do: {:cont, false}, else: {:halt, true} end) | |
|> Enum.reduce_while(false, fn {_x, y}, acc -> if (y > board.height), do: {:halt, true}, else: {:cont, false} end) | |
end | |
defp tile_slot_empty?(board,{x, y}) do | |
index_y = Map.get(board.indexor, y, 0) | |
tile_color = Map.get(board.lanes, index_y, %{}) |> Map.get(x, :empty) | |
if tile_color == :empty do | |
true | |
else | |
false | |
end | |
end | |
# def collides_with_board_tiles?(board, shape, {x_coordinate, y_coordinate}) do | |
# Tetris.Core.Shape.with_offset_counted(shape, x_coordinate, y_coordinate) | |
# |> Enum.reduce_while(false, fn {_x, y}, acc -> if (y < board.height), do: {:cont, false}, else: {:halt, true} end) | |
# end | |
# def touches_y?(board, shape, {x_coordinate, y_coordinate}) do | |
# Tetris.Core.Shape.with_offset_counted(shape, x_coordinate, y_coordinate) | |
# |> Enum.reduce_while(false, fn {x, y}, acc -> if Board.check_tile_slot_empty(board, {x, y}), do: {:cont, false}, else: {:halt, true} end) | |
# end | |
# def intersection_x?(board, shape, {x_coordinate, y_coordinate}) do | |
# Tetris.Core.Shape.with_offset_counted(shape, x_coordinate, y_coordinate) | |
# |> Enum.reduce_while(false, fn {x, y}, acc -> if Board.check_tile_slot_empty(board, {x, y}), do: {:cont, false}, else: {:halt, true} end) | |
# end | |
# def intersection_y?(board, shape, {x_coordinate, y_coordinate}) do | |
# Tetris.Core.Shape.with_offset_counted(shape, x_coordinate, y_coordinate) | |
# |> Enum.reduce_while(false, fn {x, y}, acc -> if Board.check_tile_slot_empty(board, {x, y}), do: {:cont, false}, else: {:halt, true} end) | |
# end | |
end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
defmodule ElixirconfEuWeb.Tetris.Index do | |
use Phoenix.LiveView | |
alias Tetris.Core.{Board, Game, Shape} | |
alias Tetris.Boundary.{GameLogic} | |
def mount(_session, socket) do | |
# {:ok, game_session_pid} = Tetris.start_game_session(%{listener_pid: self()}) | |
game = Game.new(%{ width: 20, height: 15}) | |
new_socket = generate_socket_from_game(socket, game) | |
# new_socket = generate_socket_from_state(socket, :sys.get_state(game_session_pid)) | |
# |> assign(:game_id, game_session_pid) | |
Process.send_after(self(), :tick, 500) | |
{:ok, new_socket} | |
end | |
# def handle_info({:state_change, new_state}, socket) do | |
# {:noreply, generate_socket_from_state(socket, new_state)} | |
# end | |
def handle_event("tetris", "rotate", socket) do | |
# game_session_id = socket.assigns.game_id | |
# Tetris.rotate(game_session_id) | |
{:noreply, socket} | |
end | |
def handle_info(:tick, socket) do | |
game_state = generate_game_from_socket(socket) | |
# res = GameLogic.move(game, :gravity) | |
state_after_move = GameLogic.move(game_state, :gravity) | |
if state_after_move.current_state == :game_over do | |
IO.puts "Game Over..." | |
else | |
Process.send_after(self(), :tick, 500) | |
end | |
# notify_game_changed(state_after_move, state_after_move) | |
# {:noreply, state_after_move} | |
updated_socket = generate_socket_from_game(socket, state_after_move) | |
{:noreply, updated_socket} | |
end | |
def handle_event("move", %{"code" => key}, socket) do | |
# game_session_id = socket.assigns.game_id | |
ox = socket.assigns.offset_x | |
oy = socket.assigns.offset_y | |
active_shape = socket.assigns.active_shape | |
board = socket.assigns.board | |
IO.puts "move ... #{ key } " | |
# game = %Game{offset_x: ox, | |
# offset_y: oy, | |
# active_shape: active_shape, | |
# board: board | |
# } | |
game = generate_game_from_socket(socket) | |
res = case key do | |
"ArrowRight" -> GameLogic.move(game, :right) | |
"ArrowLeft" -> GameLogic.move(game, :left) | |
# "ArrowLeft" -> GameLogic.move(ox, oy, active_shape, board, :left) | |
# "ArrowLeft" -> Tetris.move(game_session_id, :left) | |
# "ArrowUp" -> Tetris.rotate(game_session_id) | |
"ArrowUp" -> GameLogic.rotate(game) | |
"ArrowDown" -> GameLogic.move(game, :down) | |
_ -> nil | |
end | |
# IO.puts inspect(res) | |
updated_socket = generate_socket_from_game(socket, res) | |
{:noreply, updated_socket} | |
# {:noreply, socket} | |
end | |
defp generate_game_from_socket(socket) do | |
game_over = socket.assigns.game_over | |
board = socket.assigns.board | |
active_shape = socket.assigns.active_shape | |
next_shape = socket.assigns.next_shape | |
score = socket.assigns.score | |
offset_x = socket.assigns.offset_x | |
offset_y = socket.assigns.offset_y | |
# lanes = socket.assigns.board.lanes | |
# indexor = socket.assigns.board.indexor | |
# new_game: true, | |
# speed: 600 | |
%Game{offset_x: offset_x, | |
offset_y: offset_y, | |
active_shape: active_shape, | |
board: board, | |
game_over: game_over, | |
next_shape: next_shape, | |
score: score, | |
} | |
end | |
defp generate_socket_from_game(socket, game) do | |
assign(socket, | |
game_over: game.game_over, | |
board: game.board, | |
active_shape: game.active_shape, | |
next_shape: game.next_shape, | |
# state_change_listener: game.state_change_listener, | |
score: game.score, | |
game_over: game.game_over, | |
offset_x: game.offset_x, | |
offset_y: game.offset_y, | |
lanes: game.board.lanes, | |
indexor: game.board.indexor, | |
new_game: true, | |
speed: 600 | |
) | |
end | |
def render(assigns) do | |
ElixirconfEuWeb.TetrisView.render("tetris-game.html", assigns) | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment