-
Notifications
You must be signed in to change notification settings - Fork 0
/
2048.mar
235 lines (213 loc) · 5.8 KB
/
2048.mar
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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
import stdlib.mar
import ui.mar
| Board
var size = 4
struct Board { tiles: Matrix[Tile] }
enum Tile { free, number: Int }
fun empty_board(): Board {
Board { tiles = filled_matrix(size @ size, Tile.free) }
}
fun generate(s: Static[Board], random: &Random, complexity: Int): Board {
var board = empty_board()
for x in 0..size do
for y in 0..size do
if random.next_bool() then
board.&.set(x @ y, Tile.number(2 ** random.next_int(1..10)))
board
}
fun mutate(board: Board, random: &Random, temperature: Int): Board {
var all_free = true
for y in 0..size do
for x in 0..size do
if board.get(x @ y) is number then all_free = false
if all_free then return board
var copy = board.copy()
loop {
var point = random.next_point(board.tiles.size())
if board.get(point) is number(n) then {
var new_n = n / 2
board.&.set(point, if new_n == 1 then Tile.free else Tile.number(new_n))
break
}
}
copy
}
fun copy(board: Board): Board { Board { tiles = board.tiles.copy() } }
fun copy_to(board: Board, other: &Board) {
for x in 0..size do
for y in 0..size do
other.&.set(x @ y, board.get(x @ y))
}
fun ==(a: Tile, b: Tile): Bool {
switch a
case free if b is free then true else false
case number(a) if b is number(b) then a == b else false
}
fun ==(a: Board, b: Board): Bool {
a.tiles == b.tiles
}
fun get(board: Board, pos: Point): Tile {
board.tiles.get(pos)
}
fun set(board: &Board, pos: Point, tile: Tile) {
board.tiles.&.get_ref(pos).* = tile
}
fun add_new_2(board: Board, random: &Random): Board {
var board = board.copy()
loop {
var point = random.next_point(board.tiles.size())
if board.get(point) is free then {
board.&.set(point, Tile.number(2))
break
}
}
board
}
enum State { running, won, lost }
fun state(board: Board): State {
for x in 0..size do
for y in 0..size do
if board.get(x @ y) is number(n) then
if n == 2048 then
return State.won
for x in 0..size do
for y in 0..size do
if board.get(x @ y) is free then return State.running
for y in 0..size do
for x in 0..{size - 1} do
if board.get(x @ y) == board.get({x + 1} @ y) then return State.running
for x in 0..size do
for y in 0..{size - 1} do
if board.get(x @ y) == board.get(x @ {y + 1}) then return State.running
State.lost
}
fun compress(old: Board): Board {
var new = empty_board()
for y in 0..size do {
var i = 0
for x in 0..size do {
if old.get(x @ y) is number(n) then {
new.&.set(i @ y, old.get(x @ y))
i = i + 1
}
}
}
new
}
fun merge(old: Board): Board {
var new = old.copy()
for y in 0..size do
for x in 0..{size - 1} do {
if new.get(x @ y) is number(a) then
if new.get({x + 1} @ y) is number(b) then
if a == b then {
new.&.set(x @ y, Tile.number(a + b))
new.&.set({x + 1} @ y, Tile.free)
}
}
new
}
fun reverse(old: Board): Board {
var new = empty_board()
for x in 0..size do
for y in 0..size do
new.&.set(x @ y, old.get({size - 1 - x} @ y))
new
}
fun transpose(old: Board): Board {
var new = empty_board()
for x in 0..size do
for y in 0..size do
new.&.set(x @ y, old.get(y @ x))
new
}
fun move_left(board: Board): Board { board.compress().merge().compress() }
fun move_right(board: Board): Board { board.reverse().move_left().reverse() }
fun move_up(board: Board): Board { board.transpose().move_left().transpose() }
fun move_down(board: Board): Board { board.transpose().move_right().transpose() }
| Rendering
var background_color = color(16#1c1c1e)
var empty_color = color(16#f2f2f7)
var number_colors = list(
color(16#eec60a), | 2: yellow
color(16#ff9f0a), | 4: orange
color(16#ff5959), | 8: red
color(16#d189f5), | 16: purple
color(16#1a94ff), | 32: blue
color(16#41d496), | 64: green
)
fun draw(texture: &Texture, board: Board) {
var offset = 10 @ 10
var grid = 70
texture.&.fill(background_color)
for y in 0..size do
for x in 0..size do
switch board.get(x @ y)
case free {
texture.&.draw({{x @ y} * grid + offset}.by(grid @ grid), empty_color)
}
case number(n) {
texture.&.draw(
{{x @ y} * grid + offset}.by(grid @ grid),
number_colors.get(log_2(n) - 1 % number_colors.len))
texture.draw(
"{n}", {x @ y} * grid + offset + {10 @ 10}, 2, background_color)
}
}
fun write[W](writer: W, board: Board) {
for y in 0..size do {
for x in 0..size do {
writer."{
switch board.get(x @ y)
case free "____ "
case number(n) "{n.format().pad_right(4)} "
}"
}
writer."\n"
}
}
fun write_debug[W](writer: W, board: Board) {
for y in 0..size do {
for x in 0..size do {
writer."{
switch board.get(x @ y)
case free "_"
case number(n) "{n.format()}"
}"
if x < {size - 1} then writer." "
}
if y < {size - 1} then writer." | "
}
}
fun main(): Never {
var random = random_number_generator(10)
var board = empty_board().add_new_2(random.&)
var screen = screen_texture()
loop {
eprintln("Drawing board")
println(board)
screen.&.draw(board)
screen.show()
if get_pressed_key() is some(key) then {
var checkpoint = heap_checkpoint()
var new =
switch key
case w board.move_up()
case a board.move_left()
case s board.move_down()
case d board.move_right()
case q break
default {
eprintln("Pressed key: {key.debug()}")
continue
}
if board != new then {
new = new.add_new_2(random.&)
new.copy_to(board.&)
}
free_everything_allocated_after(checkpoint)
}
}
println("Thanks for playing!")
exit(0)
}