Skip to content

Instantly share code, notes, and snippets.

@sandeshsoni
Last active June 15, 2020 09:05
Show Gist options
  • Save sandeshsoni/14859f37872c191aefebb129b4647085 to your computer and use it in GitHub Desktop.
Save sandeshsoni/14859f37872c191aefebb129b4647085 to your computer and use it in GitHub Desktop.
Tetris models
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
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
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
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
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
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
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
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