]> git.lizzy.rs Git - ttfe.git/blob - main.c
Colored numbers
[ttfe.git] / main.c
1 #include "main.h"
2
3 #define DBG 0
4
5 bool move_possible_north(board *b);
6 bool move_possible_south(board *b);
7 bool move_possible_east(board *b);
8 bool move_possible_west(board *b);
9
10 void move_north(board *b);
11 void move_south(board *b);
12 void move_east(board *b);
13 void move_west(board *b);
14 void merge(board *b, const int d);
15 void merge_north(board *b);
16 void merge_south(board *b);
17 void merge_east(board *b);
18 void merge_west(board *b);
19
20 void place_new_piece(board *b);
21 void game_loop(board *b);
22
23 board* new_board() {
24         board *tmp = malloc(sizeof(board));
25         init_board(tmp);
26         return tmp;
27 }
28
29 void init_board(board *b) {
30         for(int i = 0; i < 4; ++i) {
31                 for(int j = 0; j < 4; ++j)
32                         b->x[i][j] = 0;
33         }
34         b->points = 0;
35         b->num_p = 0;
36         srand((uint)time(NULL));
37         place_new_piece(b);
38         // second piece will be placed in game_loop
39 }
40
41 void game_start() {
42         printf("\e[?1049h\e[?25l]");
43         struct termios oldtio, newtio;
44         tcgetattr(STDIN_FILENO, &oldtio);
45         newtio = oldtio;
46         newtio.c_lflag &= ~(ICANON | ECHO);
47         tcsetattr(STDIN_FILENO, TCSANOW, &newtio);
48         board *b = new_board();
49         game_loop(b);
50         printf("\e[?1049l\e[?25h");
51         tcsetattr(STDIN_FILENO, TCSANOW, &oldtio);
52         print_score(b);
53         free_board(b);
54 }
55
56 enum direction get_input()
57 {
58         switch(fgetc(stdin)) {
59                 case 'a':
60                         return west;
61                 case 's':
62                         return south;
63                 case 'w':
64                         return north;
65                 case 'd':
66                         return east;
67                 case 'q':
68                         return quit;
69                 default:
70                         return get_input();
71         }
72 }
73
74 void game_loop(board *b) {
75         while(move_possible_any(b)) {
76                 place_new_piece(b);
77                 print_board(b);
78                 while(true) {
79                         enum direction d = get_input();
80
81                         if (d == quit) {
82                                 return;
83                         } else if(move_possible(b, d)) {
84                                 make_move(b, d);
85                                 break;
86                         }
87                 }
88         }
89 }
90
91 void place_new_piece(board *b) {
92         int prob = rand() % 100;
93         int possibs[32];
94         uint val = 0;
95         int cnt = 0;
96         int pair = 0;
97         int rx = 0, ry = 0;
98         for(int i = 0; i < 4; ++i) {
99                 for(int j = 0; j < 4; ++j) {
100                         if(b->x[i][j] == 0) {
101                                 possibs[2*cnt] = i;
102                                 possibs[2*cnt+1] = j;
103                                 cnt++;
104                         }
105                 }
106         }
107         assert(cnt > 0);
108         pair = rand() % cnt;
109         rx = possibs[2*pair];
110         ry = possibs[2*pair+1];
111         assert(b->x[rx][ry] == 0);
112         if(prob > 9)
113                 val = 2;
114         else
115                 val = 4;
116         b->x[rx][ry] = val;
117         b->num_p++;
118 }
119
120 void free_board(board *b) {
121         free(b);
122 }
123
124 void make_move(board *b, const int d) {
125         bool ok = (d == south) ||
126                 (d == east) ||
127                 (d == north) ||
128                 (d == west);
129         assert(ok);
130         /*
131          * checked before already
132          * if(!move_possible(b, d))
133          *      return;
134         */
135         move(b, d);
136         merge(b, d);
137         move(b, d);
138 }
139
140 bool move_possible_any(board *b) {
141         if(b->num_p < 16)
142                 return true;
143         if(move_possible_south(b))
144                 return true;
145         if(move_possible_east(b))
146                 return true;
147         if(move_possible_north(b))
148                 return true;
149         if(move_possible_west(b))
150                 return true;
151         return false;
152 }
153
154 bool move_possible(board *b, const int d) {
155         bool ok = (d == south) ||
156                 (d == east) ||
157                 (d == north) ||
158                 (d == west);
159         assert(ok);
160         switch(d) {
161                 case north:
162                         return move_possible_north(b);
163                 case south:
164                         return move_possible_south(b);
165                 case east:
166                         return move_possible_east(b);
167                 case west:
168                         return move_possible_west(b);
169                 default: // will never execute anyway
170                         return false;
171         }
172 }
173
174 void move(board *b, const int d) {
175         bool ok = (d == south) ||
176                 (d == east) ||
177                 (d == north) ||
178                 (d == west);
179         assert(ok);
180         switch(d) {
181                 case north:
182                         move_north(b);
183                         break;
184                 case south:
185                         move_south(b);
186                         break;
187                 case east:
188                          move_east(b);
189                         break;
190                 case west:
191                         move_west(b);
192                         break;
193                 default: // will never execute anyway
194                         return;
195         }
196 }
197
198 void merge(board *b, const int d) {
199         bool ok = (d == south) ||
200                 (d == east) ||
201                 (d == north) ||
202                 (d == west);
203         assert(ok);
204         switch(d) {
205                 case north:
206                         merge_north(b);
207                         break;
208                 case south:
209                         merge_south(b);
210                         break;
211                 case east:
212                         merge_east(b);
213                         break;
214                 case west:
215                         merge_west(b);
216                         break;
217                 default: // will never execute anyway
218                         return;
219         }
220 }
221
222 bool move_possible_south(board *b) {
223         for(int i = 0; i < 4; ++i) {
224                 for(int j = 0; j < 3; ++j) {
225                         if(b->x[i][j] != 0) {
226                                 if(b->x[i][j+1] == 0)
227                                         return true;
228                                 if(b->x[i][j] == b->x[i][j+1])
229                                         return true;
230                         }
231                 }
232         }
233         return false;
234 }
235
236 bool move_possible_north(board *b) {
237         for(int i = 0; i < 4; ++i) {
238                 for(int j = 1; j < 4; ++j) {
239                         if(b->x[i][j] != 0) {
240                                 if(b->x[i][j-1] == 0)
241                                         return true;
242                                 if(b->x[i][j] == b->x[i][j-1])
243                                         return true;
244                         }
245                 }
246         }
247         return false;
248 }
249
250 bool move_possible_east(board *b) {
251         for(int i = 0; i < 3; ++i) {
252                 for(int j = 0; j < 4; ++j) {
253                         if(b->x[i][j] != 0) {
254                                 if(b->x[i+1][j] == 0)
255                                         return true;
256                                 if(b->x[i+1][j] == b->x[i][j])
257                                         return true;
258                         }
259                 }
260         }
261         return false;
262 }
263
264 bool move_possible_west(board *b) {
265         for(int i = 1; i < 4; ++i) {
266                 for(int j = 0; j < 4; ++j) {
267                         if(b->x[i][j] != 0) {
268                                 if(b->x[i-1][j] == 0)
269                                         return true;
270                                 if(b->x[i-1][j] == b->x[i][j])
271                                         return true;
272                         }
273                 }
274         }
275         return false;
276 }
277
278 void move_north(board *b) {
279         int k;
280         for(int i = 0; i < 4; ++i) {
281                 for(int j = 1; j < 4; ++j) {
282                         if(b->x[i][j] != 0) {
283                                 k = j;
284                                 while(b->x[i][k-1] == 0) {
285                                         b->x[i][k-1] = b->x[i][k];
286                                         b->x[i][k] = 0;
287                                         k--;
288                                         if(k < 1)
289                                                 break;
290                                 }
291                         }
292                 }
293         }
294 }
295
296 void move_south(board *b) {
297         int k;
298         for(int i = 0; i < 4; ++i) {
299                 for(int j = 2; j >= 0; --j) {
300                         if(b->x[i][j] != 0) {
301                                 k = j;
302                                 while(b->x[i][k+1] == 0) {
303                                         b->x[i][k+1] = b->x[i][k];
304                                         b->x[i][k] = 0;
305                                         k++;
306                                         if(k > 2)
307                                                 break;
308                                 }
309                         }
310                 }
311         }
312 }
313
314 void move_east(board *b) {
315         int k;
316         for(int i = 2; i >= 0; --i) {
317                 for(int j = 0; j < 4; ++j) {
318                         if(b->x[i][j] != 0) {
319                                 k = i;
320                                 while(b->x[k+1][j] == 0) {
321                                         b->x[k+1][j] = b->x[k][j];
322                                         b->x[k][j] = 0;
323                                         k++;
324                                         if(k > 2)
325                                                 break;
326                                 }
327                         }
328                 }
329         }
330 }
331
332 void move_west(board *b) {
333         int k;
334         for(int i = 1; i < 4; ++i) {
335                 for(int j = 0; j < 4; ++j) {
336                         if(b->x[i][j] != 0) {
337                                 k = i;
338                                 while(b->x[k-1][j] == 0) {
339                                         b->x[k-1][j] = b->x[k][j];
340                                         b->x[k][j] = 0;
341                                         k--;
342                                         if(k < 1)
343                                                 break;
344                                 }
345                         }
346                 }
347         }
348 }
349
350 void merge_north(board *b) {
351         for(int i = 0; i < 4; ++i) {
352                 for(int j = 1; j < 4; ++j) {
353                         if(b->x[i][j] != 0 && b->x[i][j] == b->x[i][j-1]) {
354                                 b->x[i][j-1] *= 2;
355                                 b->x[i][j] = 0;
356                                 b->num_p--;
357                                 b->points += b->x[i][j-1];
358                         }
359                 }
360         }
361 }
362
363 void merge_south(board *b) {
364         for(int i = 0; i < 4; ++i) {
365                 for(int j = 2; j >= 0; --j) {
366                         if(b->x[i][j] != 0 && b->x[i][j] == b->x[i][j+1]) {
367                                 b->x[i][j+1] *= 2;
368                                 b->x[i][j] = 0;
369                                 b->num_p--;
370                                 b->points += b->x[i][j+1];
371                         }
372                 }
373         }
374 }
375
376 void merge_east(board *b) {
377         for(int i = 2; i >= 0; --i) {
378                 for(int j = 0; j < 4; ++j) {
379                         if(b->x[i][j] != 0 && b->x[i+1][j] == b->x[i][j]) {
380                                 b->x[i+1][j] *= 2;
381                                 b->points += b->x[i+1][j];
382                                 b->num_p--;
383                                 b->x[i][j] = 0;
384                         }
385                 }
386         }
387 }
388
389 void merge_west(board *b) {
390         for(int i = 1; i < 4; ++i) {
391                 for(int j = 0; j < 4; ++j) {
392                         if(b->x[i][j] != 0 && b->x[i-1][j] == b->x[i][j]) {
393                                 b->x[i-1][j] *= 2;
394                                 b->points += b->x[i-1][j];
395                                 b->num_p--;
396                                 b->x[i][j] = 0;
397                         }
398                 }
399         }
400 }
401
402 void center_print(uint n, int width)
403 {
404     char s[20] = {'\0'};
405     int len;
406     sprintf(s, "%u", n);
407     len = strlen(s);
408     if (len >= width)  {
409         printf("%s", s);
410     } else {
411         int remaining = width - len;
412         int spaces_right = remaining / 2;
413         int spaces_left = remaining - spaces_right;
414         printf("%*s%s%*s", spaces_left, "", s, spaces_right, "");
415     }
416 }
417
418 void print_sep(const char *left, const char *right, const char *cross, const char *line)
419 {
420         printf("%s", left);
421         for(int i = 0; i < 4; i++) {
422                 for(int j = 0; j < 6; j++)
423                         printf("%s", line);
424                 if(i == 3)
425                         printf("%s", right);
426                 else
427                         printf("%s", cross);
428         }
429         printf("\n");
430 }
431
432 uint mylog2(uint n) {
433         uint i;
434
435         for (i = 0; ! (n & 1); i++)
436                 n >>= 1;
437
438         return i;
439 }
440
441 void print_board_line(board *b, int l) {
442         printf("\u2503");
443
444         for(int i = 0; i < 4; i++) {
445                 uint n = b->x[i][l];
446
447                 if(n == 0) {
448                         printf("      ");
449                 } else {
450                         uint c = mylog2(n);
451
452                         if (c > 6) {
453                                 c -= 6;
454                                 printf("\e[1m");
455                         }
456
457                         printf("\e[3%1um", c);
458                         center_print(n, 6);
459                         printf("\e[0m");
460                 }
461
462                 if(i == 3)
463                         printf("\u2503");
464                 else
465                         printf("\u2502");
466         }
467
468         printf("\n");
469         print_sep("\u2503", "\u2503", "\u2502", " ");
470
471         if(l == 3)
472                 print_sep("\u2517", "\u251B", "\u2537", "\u2501");
473         else
474                 print_sep("\u2520", "\u2528", "\u253C", "\u2500");
475 }
476
477 void print_board(board *b) {
478         printf("\e[2J\e[0;0H");
479         printf("\e[1mScore: \e[0m%u\n", b->points);
480         print_sep("\u250F", "\u2513", "\u252F", "\u2501");
481         for(int i = 0; i < 4; ++i) {
482                 print_board_line(b, i);
483         }
484 }
485
486 void print_score(board *b) {
487         printf("\e[1m\e[91mGame Over\e[0m\n\e[1mScore: \e[0m%u\n", b->points);
488 }
489
490 void merge_test1() {
491         board *b = new_board();
492         b->x[2][0] = 2;
493         b->x[3][0] = 2;
494         merge_east(b);
495         assert(b->x[3][0] == 4);
496 }
497
498 void merge_test2() {
499         board *b = new_board();
500         b->x[2][0] = 2;
501         b->x[3][0] = 2;
502         print_board(b);
503         move_west(b);
504         print_board(b);
505         merge_west(b);
506         print_board(b);
507         assert(b->x[0][0] == 4);
508 }
509
510 void merge_test3() {
511         board *b = new_board();
512         b->x[0][0] = 2;
513         b->x[0][1] = 2;
514         print_board(b);
515         merge_north(b);
516         print_board(b);
517         assert(b->x[0][0] == 4);
518 }
519
520 void merge_test4() {
521         board *b = new_board();
522         b->x[0][2] = 2;
523         b->x[0][3] = 2;
524         print_board(b);
525         merge_south(b);
526         print_board(b);
527         assert(b->x[0][3] == 4);
528 }
529
530 void move_merge_test1() {
531         board *b = new_board();
532         b->x[2][0] = 4;
533         b->x[3][0] = 2;
534         b->x[3][2] = 2;
535         print_board(b);
536         move_south(b);
537         print_board(b);
538         merge_south(b);
539         print_board(b);
540         assert(b->x[3][3] == 4);
541 }
542
543 void move_north_test() {
544         board *b = new_board();
545         b->x[2][3] = 8;
546         b->x[2][2] = 0;
547         b->x[2][1] = 0;
548         b->x[2][0] = 0;
549         b->x[0][1] = 2;
550         b->x[0][0] = 0;
551         b->x[3][0] = 4;
552         b->x[3][1] = 2;
553         b->x[3][2] = 3;
554         b->x[3][3] = 16;
555         print_board(b);
556         move_north(b);
557         merge_north(b);
558         move_north(b);
559         print_board(b);
560         assert(b->x[2][0] == 8);
561         assert(b->x[0][0] == 2);
562 }
563
564 void move_merge_test2() {
565         board *b = new_board();
566         b->x[3][0] = 2;
567         b->x[3][1] = 2;
568         b->x[3][2] = 4;
569         b->x[3][3] = 32;
570         print_board(b);
571         move_south(b);
572         merge_south(b);
573         move_south(b);
574         print_board(b);
575         assert(b->x[3][1] == 4);
576 }
577
578 void move_merge_test3() {
579         board *b = new_board();
580         b->x[0][0] = 0;
581         b->x[0][1] = 0;
582         b->x[0][2] = 0;
583         b->x[0][3] = 2;
584         b->x[1][0] = 0;
585         b->x[1][1] = 0;
586         b->x[1][2] = 0;
587         b->x[1][3] = 8;
588         b->x[2][0] = 0;
589         b->x[2][1] = 0;
590         b->x[2][2] = 0;
591         b->x[2][3] = 4;
592         b->x[3][0] = 2;
593         b->x[3][1] = 2;
594         b->x[3][2] = 4;
595         b->x[3][3] = 16;
596         print_board(b);
597         move_south(b);
598         merge_south(b);
599         move_south(b);
600         print_board(b);
601         assert(b->x[3][1] == 4);
602 }
603
604 void run_tests() {
605         /*
606         merge_test1();
607         merge_test2();
608         merge_test3();
609         merge_test4();
610         move_merge_test1();
611         move_north_test();
612         */
613         move_merge_test2();
614         move_merge_test3();
615 }
616
617 int main() {
618 #if DBG
619         run_tests();
620 #else
621         game_start();
622 #endif
623 }