blob: 42cd171b0e41708606c9d7bffd0585f7770ffbdc [file] [log] [blame]
Simon Glassb158b8f2023-02-07 14:33:53 -07001// SPDX-License-Identifier: MIT
2// SPDX-FileCopyrightText: © 2014 Maurits van der Schee
3
4/* Console version of the game "2048" for GNU/Linux */
5
Simon Glassb158b8f2023-02-07 14:33:53 -07006#include <cli.h>
7#include <command.h>
8#include <rand.h>
Tom Rinidec7ea02024-05-20 13:35:03 -06009#include <vsprintf.h>
Simon Glassb158b8f2023-02-07 14:33:53 -070010#include <linux/delay.h>
11
12#define SIZE 4
13static uint score;
14
15static void getColor(uint value, char *color, size_t length)
16{
17 u8 original[] = {
18 8, 255, 1, 255, 2, 255, 3, 255,
19 4, 255, 5, 255, 6, 255, 7, 255,
20 9, 0, 10, 0, 11, 0, 12, 0, 13,
21 0, 14, 0, 255, 0, 255, 0};
22 u8 *scheme = original;
23 u8 *background = scheme + 0;
24 u8 *foreground = scheme + 1;
25
26 if (value > 0) {
27 while (value >>= 1) {
28 if (background + 2 < scheme + sizeof(original)) {
29 background += 2;
30 foreground += 2;
31 }
32 }
33 }
34 snprintf(color, length, "\033[38;5;%d;48;5;%dm", *foreground,
35 *background);
36}
37
38static void drawBoard(u16 board[SIZE][SIZE])
39{
40 int x, y;
41 char color[40], reset[] = "\033[0m";
42
43 printf("\033[H");
44 printf("2048.c %17d pts\n\n", score);
45
46 for (y = 0; y < SIZE; y++) {
47 for (x = 0; x < SIZE; x++) {
48 getColor(board[x][y], color, 40);
49 printf("%s", color);
50 printf(" ");
51 printf("%s", reset);
52 }
53 printf("\n");
54 for (x = 0; x < SIZE; x++) {
55 getColor(board[x][y], color, 40);
56 printf("%s", color);
57 if (board[x][y] != 0) {
58 char s[8];
59 s8 t;
60
61 snprintf(s, 8, "%u", board[x][y]);
62 t = 7 - strlen(s);
63 printf("%*s%s%*s", t - t / 2, "", s, t / 2, "");
64 } else {
65 printf(" · ");
66 }
67 printf("%s", reset);
68 }
69 printf("\n");
70 for (x = 0; x < SIZE; x++) {
71 getColor(board[x][y], color, 40);
72 printf("%s", color);
73 printf(" ");
74 printf("%s", reset);
75 }
76 printf("\n");
77 }
78 printf("\n");
79 printf(" ←, ↑, →, ↓ or q \n");
80 printf("\033[A");
81}
82
83static int8_t findTarget(u16 array[SIZE], int x, int stop)
84{
85 int t;
86
87 /* if the position is already on the first, don't evaluate */
88 if (x == 0)
89 return x;
90 for (t = x - 1; t >= 0; t--) {
91 if (array[t]) {
92 if (array[t] != array[x]) {
93 /* merge is not possible, take next position */
94 return t + 1;
95 }
96 return t;
97 }
98
99 /* we should not slide further, return this one */
100 if (t == stop)
101 return t;
102 }
103 /* we did not find a */
104 return x;
105}
106
107static bool slideArray(u16 array[SIZE])
108{
109 bool success = false;
110 int x, t, stop = 0;
111
112 for (x = 0; x < SIZE; x++) {
113 if (array[x] != 0) {
114 t = findTarget(array, x, stop);
115 /*
116 * if target is not original position, then move or
117 * merge
118 */
119 if (t != x) {
120 /*
121 * if target is not zero, set stop to avoid
122 * double merge
123 */
124 if (array[t]) {
125 score += array[t] + array[x];
126 stop = t + 1;
127 }
128 array[t] += array[x];
129 array[x] = 0;
130 success = true;
131 }
132 }
133 }
134 return success;
135}
136
137static void rotateBoard(u16 board[SIZE][SIZE])
138{
139 s8 i, j, n = SIZE;
140 int tmp;
141
142 for (i = 0; i < n / 2; i++) {
143 for (j = i; j < n - i - 1; j++) {
144 tmp = board[i][j];
145 board[i][j] = board[j][n - i - 1];
146 board[j][n - i - 1] = board[n - i - 1][n - j - 1];
147 board[n - i - 1][n - j - 1] = board[n - j - 1][i];
148 board[n - j - 1][i] = tmp;
149 }
150 }
151}
152
153static bool moveUp(u16 board[SIZE][SIZE])
154{
155 bool success = false;
156 int x;
157
158 for (x = 0; x < SIZE; x++)
159 success |= slideArray(board[x]);
160
161 return success;
162}
163
164static bool moveLeft(u16 board[SIZE][SIZE])
165{
166 bool success;
167
168 rotateBoard(board);
169 success = moveUp(board);
170 rotateBoard(board);
171 rotateBoard(board);
172 rotateBoard(board);
173 return success;
174}
175
176static bool moveDown(u16 board[SIZE][SIZE])
177{
178 bool success;
179
180 rotateBoard(board);
181 rotateBoard(board);
182 success = moveUp(board);
183 rotateBoard(board);
184 rotateBoard(board);
185 return success;
186}
187
188static bool moveRight(u16 board[SIZE][SIZE])
189{
190 bool success;
191
192 rotateBoard(board);
193 rotateBoard(board);
194 rotateBoard(board);
195 success = moveUp(board);
196 rotateBoard(board);
197 return success;
198}
199
200static bool findPairDown(u16 board[SIZE][SIZE])
201{
202 bool success = false;
203 int x, y;
204
205 for (x = 0; x < SIZE; x++) {
206 for (y = 0; y < SIZE - 1; y++) {
207 if (board[x][y] == board[x][y + 1])
208 return true;
209 }
210 }
211
212 return success;
213}
214
215static int16_t countEmpty(u16 board[SIZE][SIZE])
216{
217 int x, y;
218 int count = 0;
219
220 for (x = 0; x < SIZE; x++) {
221 for (y = 0; y < SIZE; y++) {
222 if (board[x][y] == 0)
223 count++;
224 }
225 }
226 return count;
227}
228
229static bool gameEnded(u16 board[SIZE][SIZE])
230{
231 bool ended = true;
232
233 if (countEmpty(board) > 0)
234 return false;
235 if (findPairDown(board))
236 return false;
237 rotateBoard(board);
238 if (findPairDown(board))
239 ended = false;
240 rotateBoard(board);
241 rotateBoard(board);
242 rotateBoard(board);
243
244 return ended;
245}
246
247static void addRandom(u16 board[SIZE][SIZE])
248{
249 int x, y;
250 int r, len = 0;
251 u16 n, list[SIZE * SIZE][2];
252
253 for (x = 0; x < SIZE; x++) {
254 for (y = 0; y < SIZE; y++) {
255 if (board[x][y] == 0) {
256 list[len][0] = x;
257 list[len][1] = y;
258 len++;
259 }
260 }
261 }
262
263 if (len > 0) {
264 r = rand() % len;
265 x = list[r][0];
266 y = list[r][1];
267 n = ((rand() % 10) / 9 + 1) * 2;
268 board[x][y] = n;
269 }
270}
271
272static int test(void)
273{
274 u16 array[SIZE];
275 u16 data[] = {
276 0, 0, 0, 2, 2, 0, 0, 0,
277 0, 0, 2, 2, 4, 0, 0, 0,
278 0, 2, 0, 2, 4, 0, 0, 0,
279 2, 0, 0, 2, 4, 0, 0, 0,
280 2, 0, 2, 0, 4, 0, 0, 0,
281 2, 2, 2, 0, 4, 2, 0, 0,
282 2, 0, 2, 2, 4, 2, 0, 0,
283 2, 2, 0, 2, 4, 2, 0, 0,
284 2, 2, 2, 2, 4, 4, 0, 0,
285 4, 4, 2, 2, 8, 4, 0, 0,
286 2, 2, 4, 4, 4, 8, 0, 0,
287 8, 0, 2, 2, 8, 4, 0, 0,
288 4, 0, 2, 2, 4, 4, 0, 0
289 };
290 u16 *in, *out;
291 u16 t, tests;
292 int i;
293 bool success = true;
294
295 tests = (sizeof(data) / sizeof(data[0])) / (2 * SIZE);
296 for (t = 0; t < tests; t++) {
297 in = data + t * 2 * SIZE;
298 out = in + SIZE;
299 for (i = 0; i < SIZE; i++)
300 array[i] = in[i];
301 slideArray(array);
302 for (i = 0; i < SIZE; i++) {
303 if (array[i] != out[i])
304 success = false;
305 }
306 if (!success) {
307 for (i = 0; i < SIZE; i++)
308 printf("%d ", in[i]);
309 printf(" = > ");
310 for (i = 0; i < SIZE; i++)
311 printf("%d ", array[i]);
312 printf("expected ");
313 for (i = 0; i < SIZE; i++)
314 printf("%d ", in[i]);
315 printf(" = > ");
316 for (i = 0; i < SIZE; i++)
317 printf("%d ", out[i]);
318 printf("\n");
319 break;
320 }
321 }
322 if (success)
323 printf("All %u tests executed successfully\n", tests);
324
325 return !success;
326}
327
328static int do_2048(struct cmd_tbl *cmdtp, int flag, int argc,
329 char *const argv[])
330{
331 struct cli_ch_state cch_s, *cch = &cch_s;
332 u16 board[SIZE][SIZE];
333 bool success;
334
335 if (argc == 2 && strcmp(argv[1], "test") == 0)
336 return test();
337
338 score = 0;
339
340 printf("\033[?25l\033[2J\033[H");
341
342 memset(board, 0, sizeof(board));
343 addRandom(board);
344 addRandom(board);
345 drawBoard(board);
346 cli_ch_init(cch);
347 while (true) {
348 int c;
349
350 c = cli_ch_process(cch, 0);
351 if (!c) {
352 c = getchar();
353 c = cli_ch_process(cch, c);
354 }
355 switch (c) {
356 case CTL_CH('b'): /* left arrow */
357 success = moveLeft(board);
358 break;
359 case CTL_CH('f'): /* right arrow */
360 success = moveRight(board);
361 break;
362 case CTL_CH('p'):/* up arrow */
363 success = moveUp(board);
364 break;
365 case CTL_CH('n'): /* down arrow */
366 success = moveDown(board);
367 break;
368 default:
369 success = false;
370 }
371 if (success) {
372 drawBoard(board);
373 mdelay(150);
374 addRandom(board);
375 drawBoard(board);
376 if (gameEnded(board)) {
377 printf(" GAME OVER \n");
378 break;
379 }
380 }
381 if (c == 'q') {
382 printf(" QUIT \n");
383 break;
384 }
385 }
386
387 printf("\033[?25h");
388
389 return 0;
390}
391
392U_BOOT_CMD(
393 2048, 2, 1, do_2048,
394 "The 2048 game",
395 "Use your arrow keys to move the tiles. When two tiles with "
396 "the same number touch, they merge into one!"
397);