2048 over SSH https://ascii.town
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2048.c 6.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286
  1. /* Copyright (C) 2018 C. McEnroe <june@causal.agency>
  2. *
  3. * This program is free software: you can redistribute it and/or modify
  4. * it under the terms of the GNU Affero General Public License as published by
  5. * the Free Software Foundation, either version 3 of the License, or
  6. * (at your option) any later version.
  7. *
  8. * This program is distributed in the hope that it will be useful,
  9. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. * GNU Affero General Public License for more details.
  12. *
  13. * You should have received a copy of the GNU Affero General Public License
  14. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  15. */
  16. #include <curses.h>
  17. #include <stdbool.h>
  18. #include <stdio.h>
  19. #include <stdlib.h>
  20. typedef unsigned uint;
  21. static uint score;
  22. static uint grid[4][4];
  23. static bool gameOver(void) {
  24. for (uint y = 0; y < 4; ++y) {
  25. for (uint x = 0; x < 4; ++x) {
  26. if (!grid[y][x]) return false;
  27. }
  28. }
  29. for (uint y = 0; y < 4; ++y) {
  30. for (uint x = 0; x < 3; ++x) {
  31. if (grid[y][x] == grid[y][x + 1]) return false;
  32. }
  33. }
  34. for (uint x = 0; x < 4; ++x) {
  35. for (uint y = 0; y < 3; ++y) {
  36. if (grid[y][x] == grid[y + 1][x]) return false;
  37. }
  38. }
  39. return true;
  40. }
  41. static void spawn(void) {
  42. uint y, x;
  43. do {
  44. y = arc4random_uniform(4);
  45. x = arc4random_uniform(4);
  46. } while (grid[y][x]);
  47. grid[y][x] = (arc4random_uniform(10) ? 1 : 2);
  48. }
  49. static bool slideLeft(void) {
  50. bool slid = false;
  51. for (uint y = 0; y < 4; ++y) {
  52. uint x = 0;
  53. for (uint i = 0; i < 4; ++i) {
  54. if (grid[y][i] != grid[y][x]) slid = true;
  55. if (grid[y][i]) grid[y][x++] = grid[y][i];
  56. }
  57. while (x < 4) grid[y][x++] = 0;
  58. }
  59. return slid;
  60. }
  61. static bool slideRight(void) {
  62. bool slid = false;
  63. for (uint y = 0; y < 4; ++y) {
  64. uint x = 3;
  65. for (uint i = 3; i < 4; --i) {
  66. if (grid[y][i] != grid[y][x]) slid = true;
  67. if (grid[y][i]) grid[y][x--] = grid[y][i];
  68. }
  69. while (x < 4) grid[y][x--] = 0;
  70. }
  71. return slid;
  72. }
  73. static bool slideUp(void) {
  74. bool slid = false;
  75. for (uint x = 0; x < 4; ++x) {
  76. uint y = 0;
  77. for (uint i = 0; i < 4; ++i) {
  78. if (grid[i][x] != grid[y][x]) slid = true;
  79. if (grid[i][x]) grid[y++][x] = grid[i][x];
  80. }
  81. while (y < 4) grid[y++][x] = 0;
  82. }
  83. return slid;
  84. }
  85. static bool slideDown(void) {
  86. bool slid = false;
  87. for (uint x = 0; x < 4; ++x) {
  88. uint y = 3;
  89. for (uint i = 3; i < 4; --i) {
  90. if (grid[i][x] != grid[y][x]) slid = true;
  91. if (grid[i][x]) grid[y--][x] = grid[i][x];
  92. }
  93. while (y < 4) grid[y--][x] = 0;
  94. }
  95. return slid;
  96. }
  97. static bool mergeLeft(void) {
  98. bool merged = false;
  99. for (uint y = 0; y < 4; ++y) {
  100. for (uint x = 0; x < 3; ++x) {
  101. if (!grid[y][x]) continue;
  102. if (grid[y][x] != grid[y][x + 1]) continue;
  103. score += 1 << ++grid[y][x];
  104. grid[y][x + 1] = 0;
  105. merged = true;
  106. }
  107. }
  108. return merged;
  109. }
  110. static bool mergeRight(void) {
  111. bool merged = false;
  112. for (uint y = 0; y < 4; ++y) {
  113. for (uint x = 3; x > 0; --x) {
  114. if (!grid[y][x]) continue;
  115. if (grid[y][x] != grid[y][x - 1]) continue;
  116. score += 1 << ++grid[y][x];
  117. grid[y][x - 1] = 0;
  118. merged = true;
  119. }
  120. }
  121. return merged;
  122. }
  123. static bool mergeUp(void) {
  124. bool merged = false;
  125. for (uint x = 0; x < 4; ++x) {
  126. for (uint y = 0; y < 3; ++y) {
  127. if (!grid[y][x]) continue;
  128. if (grid[y][x] != grid[y + 1][x]) continue;
  129. score += 1 << ++grid[y][x];
  130. grid[y + 1][x] = 0;
  131. merged = true;
  132. }
  133. }
  134. return merged;
  135. }
  136. static bool mergeDown(void) {
  137. bool merged = false;
  138. for (uint x = 0; x < 4; ++x) {
  139. for (uint y = 3; y > 0; --y) {
  140. if (!grid[y][x]) continue;
  141. if (grid[y][x] != grid[y - 1][x]) continue;
  142. score += 1 << ++grid[y][x];
  143. grid[y - 1][x] = 0;
  144. merged = true;
  145. }
  146. }
  147. return merged;
  148. }
  149. static bool left(void) {
  150. return slideLeft() | mergeLeft() | slideLeft();
  151. }
  152. static bool right(void) {
  153. return slideRight() | mergeRight() | slideRight();
  154. }
  155. static bool up(void) {
  156. return slideUp() | mergeUp() | slideUp();
  157. }
  158. static bool down(void) {
  159. return slideDown() | mergeDown() | slideDown();
  160. }
  161. static void curse(void) {
  162. initscr();
  163. cbreak();
  164. noecho();
  165. curs_set(0);
  166. keypad(stdscr, true);
  167. leaveok(stdscr, true);
  168. start_color();
  169. use_default_colors();
  170. short bright = (COLORS > 8 ? 8 : 0);
  171. init_pair(1, bright + COLOR_WHITE, COLOR_RED);
  172. init_pair(2, bright + COLOR_WHITE, COLOR_GREEN);
  173. init_pair(3, bright + COLOR_WHITE, COLOR_YELLOW);
  174. init_pair(4, bright + COLOR_WHITE, COLOR_BLUE);
  175. init_pair(5, bright + COLOR_WHITE, COLOR_MAGENTA);
  176. init_pair(6, bright + COLOR_WHITE, COLOR_CYAN);
  177. init_pair(7, bright + COLOR_WHITE, bright + COLOR_RED);
  178. init_pair(8, bright + COLOR_WHITE, bright + COLOR_GREEN);
  179. init_pair(9, bright + COLOR_WHITE, bright + COLOR_YELLOW);
  180. init_pair(10, bright + COLOR_WHITE, bright + COLOR_BLUE);
  181. init_pair(11, bright + COLOR_WHITE, bright + COLOR_MAGENTA);
  182. init_pair(12, bright + COLOR_WHITE, bright + COLOR_CYAN);
  183. init_pair(13, COLOR_WHITE, COLOR_BLACK);
  184. }
  185. static void addchn(char ch, uint n) {
  186. for (uint i = 0; i < n; ++i) {
  187. addch(ch);
  188. }
  189. }
  190. enum {
  191. TileHeight = 3,
  192. TileWidth = 7,
  193. GridY = 2,
  194. GridX = 2,
  195. ScoreY = 0,
  196. ScoreX = GridX + 4 * TileWidth - 10,
  197. HelpY = GridY,
  198. HelpX = GridX + 5 * TileWidth,
  199. };
  200. static void drawTile(uint y, uint x) {
  201. if (grid[y][x]) {
  202. attr_set(A_BOLD, 1 + (grid[y][x] - 1) % 12, NULL);
  203. } else {
  204. attr_set(A_NORMAL, 13, NULL);
  205. }
  206. char buf[8];
  207. int len = snprintf(buf, sizeof(buf), "%d", 1 << grid[y][x]);
  208. if (!grid[y][x]) buf[0] = '.';
  209. move(GridY + TileHeight * y, GridX + TileWidth * x);
  210. addchn(' ', TileWidth);
  211. move(GridY + TileHeight * y + 1, GridX + TileWidth * x);
  212. addchn(' ', (TileWidth - len + 1) / 2);
  213. addstr(buf);
  214. addchn(' ', (TileWidth - len) / 2);
  215. move(GridY + TileHeight * y + 2, GridX + TileWidth * x);
  216. addchn(' ', TileWidth);
  217. }
  218. static void draw(void) {
  219. char buf[11];
  220. snprintf(buf, sizeof(buf), "%10d", score);
  221. attr_set(A_NORMAL, 0, NULL);
  222. mvaddstr(ScoreY, ScoreX, buf);
  223. for (uint y = 0; y < 4; ++y) {
  224. for (uint x = 0; x < 4; ++x) {
  225. drawTile(y, x);
  226. }
  227. }
  228. }
  229. static void drawHelp(void) {
  230. attr_set(A_NORMAL, 0, NULL);
  231. mvaddstr(HelpY + 0, HelpX, "Use the arrow keys to");
  232. mvaddstr(HelpY + 1, HelpX, "slide and merge tiles.");
  233. mvaddstr(HelpY + 2, HelpX, "Press q to quit.");
  234. }
  235. static void drawGameOver(void) {
  236. attr_set(A_NORMAL, 0, NULL);
  237. mvaddstr(HelpY + 0, HelpX, "Game over! Press q to");
  238. mvaddstr(HelpY + 1, HelpX, "view the scoreboard.");
  239. }
  240. static bool input(void) {
  241. switch (getch()) {
  242. break; case 'h': case KEY_LEFT: if (left()) spawn();
  243. break; case 'j': case KEY_DOWN: if (down()) spawn();
  244. break; case 'k': case KEY_UP: if (up()) spawn();
  245. break; case 'l': case KEY_RIGHT: if (right()) spawn();
  246. break; case 'q': return false;
  247. break; case ERR: exit(EXIT_FAILURE);
  248. }
  249. return true;
  250. }
  251. uint play2048(void) {
  252. curse();
  253. spawn();
  254. spawn();
  255. drawHelp();
  256. uint help = 0;
  257. do {
  258. if (help++ == 3) erase();
  259. if (gameOver()) drawGameOver();
  260. draw();
  261. } while (input());
  262. return score;
  263. }