You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

269 lines
6.7 KiB

defmodule TetrisuiWeb.TetrisLive do
use Phoenix.LiveView
use TetrisuiWeb, :live_view
import Phoenix.HTML, only: [raw: 1]
@debug true
@box_width 20
@box_height 20
defp mount_test(_params,_session, socket) do
{
:ok,
assign(socket,
brick: Tetris.Brick.new_random |> Tetris.Brick.to_string,
tetromino: [
{1, 1, :orange}, {2, 1, :orange}, {3, 1, :red} , {4, 1, :blue},
],
name: "julz"
)
}
end
def mount(_params, _session, socket) do
socket2 = assign(socket,appstate: "loading")
case connected?(socket2) do
true -> {:ok, start_game(socket2)}
false -> {:ok, socket2}
end
end
defp start_game(socket) do
:timer.send_interval 1000, self(), :tick
assign(socket,
appstate: "loaded",
state: :starting,
)
end
defp new_game(socket) do
assign(socket,
appstate: "loaded",
state: :playing,
score: 0,
bottom: %{},
game_over: false
)
|> new_block
|> show
end
def new_block(socket) do
brick =
Tetris.Brick.new_random()
|> Map.put(:location, {3, -3})
assign(socket,brick: brick)
end
def show(socket) do
brick = socket.assigns.brick
points_with_colour =
brick
|> Tetris.Brick.prepare
|> Tetris.Points.move_to_location(brick.location)
|> Tetris.Points.with_colour(colour(brick))
assign(socket, tetromino: points_with_colour)
end
def render(%{appstate: "loading"}=assigns) do
~L"""
<h1>Tetris </h1>
<div id='appstate'>Loading...</div>
<div>
</div>
"""
end
def render(%{state: :starting}=assigns) do
~L"""
<h1>Welcome to Tetris </h1>
<div id='appstate'></div>
<div>
<button phx-click="start">Start</button>
</div>
"""
end
def render(%{appstate: "loaded", state: :playing}=assigns) do
#<pre><%= @brick%></pre>
#<div phx-keydown="keydown" tabindex="0">
~L"""
<h1>Tetris <%= @score %></h1>
<div id='appstate'></div>
<div phx-window-keydown="keydown">
<%= raw svg_head()%>
<%= raw boxes(@tetromino)%>
<%= raw boxes(Map.values(@bottom)) %>
<%= raw svg_foot()%>
</div>
<%= debug(assigns) %>
"""
end
def render(%{appstate: "loaded", state: :game_over}=assigns) do
#<pre><%= @brick%></pre>
#<div phx-keydown="keydown" tabindex="0">
~L"""
<h1>Tetris - GAME OVER - score: <%= @score %> </h1>
<div id='appstate'></div>
<button phx-click="start">Play Again</button>
<%= debug(assigns) %>
"""
end
#def render(%{appstate: _other}=assigns) do
# ~L"<h1>tetrisui - unknown app state <%= @appstate %></h1>"
#end
def svg_head() do
"""
<svg
version="1.0"
style="background-color: #E8E8E8"
id="Layer_1"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
width="200" height="400"
viewBox="0 0 200 400"
xml:space="preserve">
"""
end
def svg_foot(), do: "</svg>"
def boxes(points_with_colours) do
points_with_colours
|> Enum.map( fn {x,y,colour} ->
box({x,y}, colour)
end)
|> Enum.join("\n")
end
def box(point,colour) do
"""
#{square(point, shades(colour).light)}
#{triangle(point, shades(colour).dark)}
"""
end
def square(point,shade) do
{x,y} = to_pixels(point)
"""
<rect
x="#{x+1}" y="#{y+1}"
style="fill:##{shade};"
width="#{@box_width - 2}" height="#{@box_height - 1}"/>
"""
end
def triangle(point,shade) do
{x,y} = to_pixels(point)
{w,h} = {@box_width, @box_height}
"""
<polyline
style="fill:##{shade}"
points="#{x + 1}, #{y + 1} #{x + w},#{y+1} #{x + w} ,#{y + h}" />
"""
end
defp to_pixels({x,y}), do: {(x-1) * @box_width, (y-1) * @box_height}
defp shades(:red ), do: %{ light: "DB7160", dark: "AB574B"}
defp shades(:blue ), do: %{ light: "83C1C8", dark: "66969C"}
defp shades(:green ), do: %{ light: "8BBF57", dark: "769359"}
defp shades(:orange ), do: %{ light: "CB8E4E", dark: "AC7842"}
defp shades(:grey ), do: %{ light: "A1A09E", dark: "7F7F7E"}
defp colour(%{name: :t}), do: :red
defp colour(%{name: :i}), do: :blue
defp colour(%{name: :l}), do: :green
defp colour(%{name: :o}), do: :orange
defp colour(%{name: :z}), do: :grey
def drop(:playing, socket, fast) do
old_brick = socket.assigns.brick
response =
Tetris.drop(
old_brick,
socket.assigns.bottom,
colour(old_brick)
)
bonus = if fast, do: 2, else: 0
state = if response.game_over, do: :game_over, else: :playing
socket
|> assign(
brick: response.brick,
bottom: response.bottom,
score: socket.assigns.score + response.score + bonus,
state: state
)
|> show
end
def drop(_not_playing, socket, _dontcare), do: socket
def move(direction, socket) do
socket
|> do_move(direction)
|> show
end
def do_move(%{assigns: %{brick: brick, bottom: bottom}}=socket, :left) do
assign(socket, brick: brick |> Tetris.try_left(bottom))
end
def do_move(%{assigns: %{brick: brick, bottom: bottom}}=socket, :right) do
assign(socket, brick: brick |> Tetris.try_right(bottom))
end
def do_move(%{assigns: %{brick: brick, bottom: bottom}}=socket, :turn) do
assign(socket, brick: brick |> Tetris.try_spin(bottom))
end
def handle_event("keydown", %{"key" => "ArrowLeft"}, socket) do
{:noreply, move(:left,socket)}
#{:noreply, assign(socket, tetromino: [])}
end
def handle_event("keydown", %{"key" => "ArrowRight"}, socket) do
{:noreply, move(:right,socket)}
end
def handle_event("keydown", %{"key" => "ArrowUp"}, socket) do
{:noreply, move(:turn,socket)}
end
def handle_event("keydown", %{"key" => "ArrowDown"}, socket) do
{:noreply, drop(socket.assigns.state,socket, :true)}
end
def handle_event("keydown", _other, socket) do
{:noreply, socket}
end
def handle_event("start", _other, socket) do
{:noreply, new_game(socket)}
end
def handle_info(:tick, socket) do
{:noreply, drop(socket.assigns.state, socket, :false)}
end
def debug(assigns), do: debug(assigns, @debug, Mix.env)
def debug(assigns, true, :dev) do
~L"""
<pre>
Brick : <%= raw( @tetromino |> inspect) %>
Bottom: <%= raw( @bottom |> inspect) %>
</pre>
"""
#"<h1> Debugging</h1>"
end
def debug(assigns, _, _), do: ""
end