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