1
2// SPDX-License-Identifier: MIT
3// SPDX-FileCopyrightText: © 2014 Maurits van der Schee
4
5/* Console version of the game "2048" for MOS */
6
7// Adapted from the u-boot project
8// https://github.com/u-boot/u-boot/commit/a554ee7edee8e10b38c6899ad8556daf58ca3afe
9
10#include <mos/types.h>
11#include <stdio.h>
12#include <string.h>
13
14#define SIZE 4
15static u32 score;
16
17// a very pseudo random number generator
18should_inline u32 rand(void)
19{
20 u32 result;
21#if defined(__x86_64__)
22 __asm__ volatile("rdtsc" : "=a"(result) : : "edx");
23#elif defined(__riscv)
24 __asm__ volatile("rdcycle %0" : "=r"(result));
25#else
26#error "unsupported architecture"
27#endif
28 result ^= (ptr_t) &result;
29 return result;
30}
31
32static void getColor(u32 value, char *color, size_t length)
33{
34 u8 original[] = { 8, 255, 1, 255, 2, 255, 3, 255, 4, 255, 5, 255, 6, 255, 7, 255, 9, 0, 10, 0, 11, 0, 12, 0, 13, 0, 14, 0, 255, 0, 255, 0 };
35 u8 *scheme = original;
36 u8 *background = scheme + 0;
37 u8 *foreground = scheme + 1;
38
39 if (value > 0)
40 {
41 while (value >>= 1)
42 {
43 if (background + 2 < scheme + sizeof(original))
44 {
45 background += 2;
46 foreground += 2;
47 }
48 }
49 }
50 snprintf(buffer: color, max_size: length, format: "\033[38;5;%d;48;5;%dm", *foreground, *background);
51}
52
53static void drawBoard(u16 board[SIZE][SIZE])
54{
55 int x, y;
56 char color[40], reset[] = "\033[0m";
57
58 printf(format: "\033[H");
59 printf(format: "2048.c %17d pts\n\n", score);
60
61 for (y = 0; y < SIZE; y++)
62 {
63 for (x = 0; x < SIZE; x++)
64 {
65 getColor(value: board[x][y], color, length: 40);
66 printf(format: "%s", color);
67 printf(format: " ");
68 printf(format: "%s", reset);
69 }
70 printf(format: "\n");
71 for (x = 0; x < SIZE; x++)
72 {
73 getColor(value: board[x][y], color, length: 40);
74 printf(format: "%s", color);
75 if (board[x][y] != 0)
76 {
77 char s[8];
78 s8 t;
79
80 snprintf(buffer: s, max_size: 8, format: "%u", board[x][y]);
81 t = 7 - strlen(s);
82 printf(format: "%*s%s%*s", t - t / 2, "", s, t / 2, "");
83 }
84 else
85 {
86 printf(format: " · ");
87 }
88 printf(format: "%s", reset);
89 }
90 printf(format: "\n");
91 for (x = 0; x < SIZE; x++)
92 {
93 getColor(value: board[x][y], color, length: 40);
94 printf(format: "%s", color);
95 printf(format: " ");
96 printf(format: "%s", reset);
97 }
98 printf(format: "\n");
99 }
100 printf(format: "\n");
101 printf(format: " ←, ↑, →, ↓ or q \n");
102 printf(format: "\033[A");
103}
104
105static s8 findTarget(u16 array[SIZE], int x, int stop)
106{
107 int t;
108
109 /* if the position is already on the first, don't evaluate */
110 if (x == 0)
111 return x;
112 for (t = x - 1; t >= 0; t--)
113 {
114 if (array[t])
115 {
116 if (array[t] != array[x])
117 {
118 /* merge is not possible, take next position */
119 return t + 1;
120 }
121 return t;
122 }
123
124 /* we should not slide further, return this one */
125 if (t == stop)
126 return t;
127 }
128 /* we did not find a */
129 return x;
130}
131
132static bool slideArray(u16 array[SIZE])
133{
134 bool success = false;
135 int x, t, stop = 0;
136
137 for (x = 0; x < SIZE; x++)
138 {
139 if (array[x] != 0)
140 {
141 t = findTarget(array, x, stop);
142 /*
143 * if target is not original position, then move or
144 * merge
145 */
146 if (t != x)
147 {
148 /*
149 * if target is not zero, set stop to avoid
150 * double merge
151 */
152 if (array[t])
153 {
154 score += array[t] + array[x];
155 stop = t + 1;
156 }
157 array[t] += array[x];
158 array[x] = 0;
159 success = true;
160 }
161 }
162 }
163 return success;
164}
165
166static void rotateBoard(u16 board[SIZE][SIZE])
167{
168 s8 i, j, n = SIZE;
169 int tmp;
170
171 for (i = 0; i < n / 2; i++)
172 {
173 for (j = i; j < n - i - 1; j++)
174 {
175 tmp = board[i][j];
176 board[i][j] = board[j][n - i - 1];
177 board[j][n - i - 1] = board[n - i - 1][n - j - 1];
178 board[n - i - 1][n - j - 1] = board[n - j - 1][i];
179 board[n - j - 1][i] = tmp;
180 }
181 }
182}
183
184static bool moveUp(u16 board[SIZE][SIZE])
185{
186 bool success = false;
187 int x;
188
189 for (x = 0; x < SIZE; x++)
190 success |= slideArray(array: board[x]);
191
192 return success;
193}
194
195static bool moveLeft(u16 board[SIZE][SIZE])
196{
197 bool success;
198
199 rotateBoard(board);
200 success = moveUp(board);
201 rotateBoard(board);
202 rotateBoard(board);
203 rotateBoard(board);
204 return success;
205}
206
207static bool moveDown(u16 board[SIZE][SIZE])
208{
209 bool success;
210
211 rotateBoard(board);
212 rotateBoard(board);
213 success = moveUp(board);
214 rotateBoard(board);
215 rotateBoard(board);
216 return success;
217}
218
219static bool moveRight(u16 board[SIZE][SIZE])
220{
221 bool success;
222
223 rotateBoard(board);
224 rotateBoard(board);
225 rotateBoard(board);
226 success = moveUp(board);
227 rotateBoard(board);
228 return success;
229}
230
231static bool findPairDown(u16 board[SIZE][SIZE])
232{
233 bool success = false;
234 int x, y;
235
236 for (x = 0; x < SIZE; x++)
237 {
238 for (y = 0; y < SIZE - 1; y++)
239 {
240 if (board[x][y] == board[x][y + 1])
241 return true;
242 }
243 }
244
245 return success;
246}
247
248static s16 countEmpty(u16 board[SIZE][SIZE])
249{
250 int x, y;
251 int count = 0;
252
253 for (x = 0; x < SIZE; x++)
254 {
255 for (y = 0; y < SIZE; y++)
256 {
257 if (board[x][y] == 0)
258 count++;
259 }
260 }
261 return count;
262}
263
264static bool gameEnded(u16 board[SIZE][SIZE])
265{
266 bool ended = true;
267
268 if (countEmpty(board) > 0)
269 return false;
270 if (findPairDown(board))
271 return false;
272 rotateBoard(board);
273 if (findPairDown(board))
274 ended = false;
275 rotateBoard(board);
276 rotateBoard(board);
277 rotateBoard(board);
278
279 return ended;
280}
281
282static void addRandom(u16 board[SIZE][SIZE])
283{
284 int x, y;
285 int r, len = 0;
286 u16 n, list[SIZE * SIZE][2];
287
288 for (x = 0; x < SIZE; x++)
289 {
290 for (y = 0; y < SIZE; y++)
291 {
292 if (board[x][y] == 0)
293 {
294 list[len][0] = x;
295 list[len][1] = y;
296 len++;
297 }
298 }
299 }
300
301 if (len > 0)
302 {
303 r = rand() % len;
304 x = list[r][0];
305 y = list[r][1];
306 n = ((rand() % 10) / 9 + 1) * 2;
307 board[x][y] = n;
308 }
309}
310
311int main(int argc, char *argv[])
312{
313 MOS_UNUSED(argc);
314 MOS_UNUSED(argv);
315
316 u16 board[SIZE][SIZE];
317 bool success;
318
319 score = 0;
320
321 printf(format: "\033[?25l\033[2J\033[H");
322
323 memset(dest: board, c: 0, size: sizeof(board));
324 addRandom(board);
325 addRandom(board);
326 drawBoard(board);
327
328 while (true)
329 {
330 char c = getchar();
331 if (c == 'q')
332 {
333 printf(format: " QUIT \n");
334 break;
335 }
336
337 if (c != '\033')
338 continue;
339
340 c = getchar();
341 if (c != '[')
342 continue;
343
344 c = getchar(); // A = up, B = down, C = right, D = left
345 switch (c)
346 {
347 case 'A': success = moveUp(board); break;
348 case 'B': success = moveDown(board); break;
349 case 'C': success = moveRight(board); break;
350 case 'D': success = moveLeft(board); break;
351 default: success = false;
352 }
353
354 if (success)
355 {
356 drawBoard(board);
357 addRandom(board);
358 drawBoard(board);
359 if (gameEnded(board))
360 {
361 printf(format: " GAME OVER \n");
362 break;
363 }
364 }
365 }
366
367 printf(format: "\033[?25h");
368
369 return 0;
370}
371