-
Notifications
You must be signed in to change notification settings - Fork 3
/
Elixir 2048.exs
115 lines (103 loc) · 3.04 KB
/
Elixir 2048.exs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
defmodule Game2048 do
@size 4
@range 0..@size-1
def play(goal \\ 2048), do: setup() |> play(goal)
defp play(board, goal) do
show(board)
cond do
goal in Map.values(board) ->
IO.puts "You win!"
exit(:normal)
0 in Map.values(board) or combinable?(board) ->
moved = move(board, keyin())
if moved == board, do: play(board, goal), else: add_tile(moved) |> play(goal)
true ->
IO.puts "Game Over!"
exit(:normal)
end
end
defp setup do
(for i <- @range, j <- @range, into: %{}, do: {{i,j},0})
|> add_tile
|> add_tile
end
defp add_tile(board) do
position = blank_space(board) |> Enum.random
tile = if :rand.uniform(10)==1, do: 4, else: 2
%{board | position => tile}
end
defp blank_space(board) do
for {key, 0} <- board, do: key
end
defp keyin do
key = IO.gets("key in wasd or q: ")
case String.first(key) do
"w" -> :up
"a" -> :left
"s" -> :down
"d" -> :right
"q" -> exit(:normal)
_ -> keyin()
end
end
defp move(board, :up) do
Enum.reduce(@range, board, fn j,acc ->
Enum.map(@range, fn i -> acc[{i,j}] end)
|> move_and_combine
|> Enum.with_index
|> Enum.reduce(acc, fn {v,i},map -> Map.put(map, {i,j}, v) end)
end)
end
defp move(board, :down) do
Enum.reduce(@range, board, fn j,acc ->
Enum.map(@size-1..0, fn i -> acc[{i,j}] end)
|> move_and_combine
|> Enum.reverse
|> Enum.with_index
|> Enum.reduce(acc, fn {v,i},map -> Map.put(map, {i,j}, v) end)
end)
end
defp move(board, :left) do
Enum.reduce(@range, board, fn i,acc ->
Enum.map(@range, fn j -> acc[{i,j}] end)
|> move_and_combine
|> Enum.with_index
|> Enum.reduce(acc, fn {v,j},map -> Map.put(map, {i,j}, v) end)
end)
end
defp move(board, :right) do
Enum.reduce(@range, board, fn i,acc ->
Enum.map(@size-1..0, fn j -> acc[{i,j}] end)
|> move_and_combine
|> Enum.reverse
|> Enum.with_index
|> Enum.reduce(acc, fn {v,j},map -> Map.put(map, {i,j}, v) end)
end)
end
defp move_and_combine(tiles) do
(Enum.filter(tiles, &(&1>0)) ++ [0,0,0,0])
|> Enum.take(@size)
|> case do
[a,a,b,b] -> [a*2, b*2, 0, 0]
[a,a,b,c] -> [a*2, b, c, 0]
[a,b,b,c] -> [a, b*2, c, 0]
[a,b,c,c] -> [a, b, c*2, 0]
x -> x
end
end
defp combinable?(board) do
Enum.any?(for i <- @range, j <- 0..@size-2, do: board[{i,j}]==board[{i,j+1}]) or
Enum.any?(for j <- @range, i <- 0..@size-2, do: board[{i,j}]==board[{i+1,j}])
end
@frame String.duplicate("+----", @size) <> "+"
@format (String.duplicate("|~4w", @size) <> "|") |> to_charlist # before 1.3 to_char_list
defp show(board) do
Enum.each(@range, fn i ->
IO.puts @frame
row = for j <- @range, do: board[{i,j}]
IO.puts (:io_lib.fwrite @format, row) |> to_string |> String.replace(" 0|", " |")
end)
IO.puts @frame
end
end
Game2048.play 512