1 // SPDX-License-Identifier: MIT
2 // SPDX-FileCopyrightText: © 2014 Maurits van der Schee
3 
4 /* Console version of the game "2048" for GNU/Linux */
5 
6 #include <cli.h>
7 #include <command.h>
8 #include <rand.h>
9 #include <vsprintf.h>
10 #include <linux/delay.h>
11 #include <linux/string.h>
12 
13 #define SIZE 4
14 static uint score;
15 
getColor(uint value,char * color,size_t length)16 static 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 
drawBoard(u16 board[SIZE][SIZE])39 static 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 
findTarget(u16 array[SIZE],int x,int stop)84 static 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 
slideArray(u16 array[SIZE])108 static 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 
rotateBoard(u16 board[SIZE][SIZE])138 static 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 
moveUp(u16 board[SIZE][SIZE])154 static 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 
moveLeft(u16 board[SIZE][SIZE])165 static 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 
moveDown(u16 board[SIZE][SIZE])177 static 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 
moveRight(u16 board[SIZE][SIZE])189 static 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 
findPairDown(u16 board[SIZE][SIZE])201 static 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 
countEmpty(u16 board[SIZE][SIZE])216 static 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 
gameEnded(u16 board[SIZE][SIZE])230 static 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 
addRandom(u16 board[SIZE][SIZE])248 static 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 
test(void)273 static 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 
do_2048(struct cmd_tbl * cmdtp,int flag,int argc,char * const argv[])329 static 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 
393 U_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 );
399