tetris_text.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505
  1. /* Copyright 2017 Dan Amlund Thomsen
  2. *
  3. * This program is free software: you can redistribute it and/or modify
  4. * it under the terms of the GNU General Public License as published by
  5. * the Free Software Foundation, either version 2 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 General Public License for more details.
  12. *
  13. * You should have received a copy of the GNU General Public License
  14. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  15. */
  16. #include <stdio.h>
  17. #include <stdlib.h>
  18. #include <stdarg.h>
  19. #include <stdint.h>
  20. #include "tetris_text.h"
  21. static char empty_piece[7][7] = { { 0, 0, 0, 0, 0, 0, 0 },
  22. { 0, 0, 0, 0, 0, 0, 0 },
  23. { 0, 0, 0, 0, 0, 0, 0 },
  24. { 0, 0, 0, 0, 0, 0, 0 },
  25. { 0, 0, 0, 0, 0, 0, 0 },
  26. { 0, 0, 0, 0, 0, 0, 0 },
  27. { 0, 0, 0, 0, 0, 0, 0 } };
  28. static char temp_piece[7][7];
  29. static int curx = 0;
  30. static int cury = 0;
  31. static void clear_piece(char piece[7][7]) {
  32. for (int y = 0; y < 7; y++) {
  33. for (int x = 0; x < 7; x++) {
  34. piece[x][y] = 0;
  35. }
  36. }
  37. }
  38. static void copy_piece_from_to(char from[7][7], char to[7][7]) {
  39. for (int y = 0; y < 7; y++) {
  40. for (int x = 0; x < 7; x++) {
  41. to[x][y] = from[x][y];
  42. }
  43. }
  44. }
  45. static void rotate_piece(char piece[7][7]) {
  46. // transpose
  47. for (int y = 0; y < 7; y++) {
  48. for (int x = y + 1; x < 7; x++) {
  49. char tmp = piece[y][x];
  50. piece[y][x] = piece[x][y];
  51. piece[x][y] = tmp;
  52. }
  53. }
  54. // reverse rows
  55. for (int y = 0; y < 7; y++) {
  56. for (int x = 0; x < 3; x++) {
  57. char tmp = piece[y][6 - x];
  58. piece[y][6 - x] = piece[y][x];
  59. piece[y][x] = tmp;
  60. }
  61. }
  62. }
  63. static char get_shape_char(int shape) {
  64. switch (shape) {
  65. case 0: return 'I';
  66. case 1: return 'J';
  67. case 2: return 'L';
  68. case 3: return 'O';
  69. case 4: return 'S';
  70. case 5: return 'T';
  71. case 6: return 'Z';
  72. }
  73. return 'Q';
  74. }
  75. static void set_piece(char piece[7][7], int shape, int rotation) {
  76. clear_piece(piece);
  77. switch (shape) {
  78. case 0:
  79. if (rotation % 2 == 0) {
  80. // xxXx
  81. piece[3][1] = 1;
  82. piece[3][2] = 1;
  83. piece[3][3] = 1;
  84. piece[3][4] = 1;
  85. } else {
  86. // x
  87. // x
  88. // X
  89. // x
  90. piece[1][3] = 1;
  91. piece[2][3] = 1;
  92. piece[3][3] = 1;
  93. piece[4][3] = 1;
  94. }
  95. break;
  96. case 1:
  97. // xXx
  98. // x
  99. piece[3][2] = 1;
  100. piece[3][3] = 1;
  101. piece[3][4] = 1;
  102. piece[4][4] = 1;
  103. for (int i = 0; i < rotation; i++) {
  104. rotate_piece(piece);
  105. }
  106. break;
  107. case 2:
  108. // xXx
  109. // x
  110. piece[3][2] = 1;
  111. piece[3][3] = 1;
  112. piece[3][4] = 1;
  113. piece[4][2] = 1;
  114. for (int i = 0; i < rotation; i++) {
  115. rotate_piece(piece);
  116. }
  117. break;
  118. case 3:
  119. // xX
  120. // xx
  121. piece[3][2] = 1;
  122. piece[3][3] = 1;
  123. piece[4][2] = 1;
  124. piece[4][3] = 1;
  125. break;
  126. case 4:
  127. if (rotation % 2 == 0) {
  128. // xX
  129. // xx
  130. piece[3][2] = 1;
  131. piece[3][3] = 1;
  132. piece[4][3] = 1;
  133. piece[4][4] = 1;
  134. } else {
  135. // x
  136. // xX
  137. // x
  138. piece[2][3] = 1;
  139. piece[3][2] = 1;
  140. piece[3][3] = 1;
  141. piece[4][2] = 1;
  142. }
  143. break;
  144. case 5:
  145. // xXx
  146. // x
  147. piece[3][2] = 1;
  148. piece[3][3] = 1;
  149. piece[3][4] = 1;
  150. piece[4][3] = 1;
  151. for (int i = 0; i < rotation; i++) {
  152. rotate_piece(piece);
  153. }
  154. break;
  155. case 6:
  156. if (rotation % 2 == 0) {
  157. // Xx
  158. // xx
  159. piece[3][3] = 1;
  160. piece[3][4] = 1;
  161. piece[4][2] = 1;
  162. piece[4][3] = 1;
  163. } else {
  164. // x
  165. // Xx
  166. // x
  167. piece[2][3] = 1;
  168. piece[3][3] = 1;
  169. piece[3][4] = 1;
  170. piece[4][4] = 1;
  171. }
  172. break;
  173. }
  174. }
  175. static void send_deletes(int deletes) {
  176. for (int i = 0; i < deletes; i++) {
  177. tetris_send_delete();
  178. }
  179. }
  180. static void send_backspaces(int backspaces) {
  181. for (int i = 0; i < backspaces; i++) {
  182. tetris_send_backspace();
  183. curx--;
  184. }
  185. }
  186. static void send_goto_xy(int x, int y) {
  187. while (curx < x) {
  188. tetris_send_right();
  189. curx++;
  190. }
  191. while (curx > x) {
  192. tetris_send_left();
  193. curx--;
  194. }
  195. while (cury < y) {
  196. tetris_send_down();
  197. cury++;
  198. }
  199. while (cury > y) {
  200. tetris_send_up();
  201. cury--;
  202. }
  203. }
  204. static void draw_row(char c, const char oldrow[7], const char newrow[7], int x, int y) {
  205. char str[2] = { c, 0 };
  206. char row_is_del[7] = { 0 };
  207. int first = -1;
  208. int last = -1;
  209. for (int px = 0; px < 7; px++) {
  210. if (oldrow[px] && !newrow[px]) {
  211. row_is_del[px] = 1;
  212. }
  213. if (newrow[px] || oldrow[px]) {
  214. if (first == -1) first = px;
  215. last = px;
  216. }
  217. }
  218. if (first >= 0) {
  219. if (curx > x + last + 1) {
  220. send_goto_xy(x + last + 1, cury);
  221. }
  222. if (curx < x + first) {
  223. send_goto_xy(x + first, cury);
  224. }
  225. send_goto_xy(curx, y);
  226. send_deletes((x + last + 1) - curx);
  227. send_backspaces(curx - (x + first));
  228. for (int i = first; i <= last; i++) {
  229. if (row_is_del[i]) {
  230. tetris_send_string(".");
  231. } else {
  232. tetris_send_string(str);
  233. }
  234. curx++;
  235. }
  236. }
  237. }
  238. static void move_piece_from_to(char from[7][7], char to[7][7], int xadd, int yadd) {
  239. for (int y = 0; y < 7; y++) {
  240. for (int x = 0; x < 7; x++) {
  241. if (x + xadd >= 0 && x + xadd < 7 && y + yadd >= 0 && y + yadd < 7) {
  242. to[y][x] = from[y + yadd][x + xadd];
  243. } else {
  244. to[y][x] = 0;
  245. }
  246. }
  247. }
  248. }
  249. static void draw_piece(char c, int x, int y, char oldpiece[7][7], char piece[7][7]) {
  250. for (int py = 0; py < 7; py++) {
  251. draw_row(c, oldpiece[py], piece[py], x, y + py);
  252. }
  253. }
  254. static void draw_piece_moved(char c, int x, int y, char piece[7][7], int oldxadd, int oldyadd) {
  255. move_piece_from_to(piece, temp_piece, oldxadd, oldyadd);
  256. draw_piece(c, x, y, temp_piece, piece);
  257. }
  258. static int is_piece_hitting(char board[20][10], char piece[7][7], int x, int y) {
  259. for (int py = 0; py < 7; py++) {
  260. for (int px = 0; px < 7; px++) {
  261. if (piece[py][px] &&
  262. (px + x >= 10 || px + x < 0
  263. || py + y >= 20 || py + y < 0
  264. || board[py + y][px + x])) {
  265. return 1;
  266. }
  267. }
  268. }
  269. return 0;
  270. }
  271. static void add_piece_to_board(char piece[7][7], char board[20][10], int x, int y) {
  272. for (int py = 0; py < 7; py++) {
  273. for (int px = 0; px < 7; px++) {
  274. if (piece[py][px]) {
  275. board[py + y][px + x] = piece[py][px];
  276. }
  277. }
  278. }
  279. }
  280. static void draw_board_line(void) {
  281. //send_string("l l");
  282. tetris_send_string("l..........l");
  283. tetris_send_newline();
  284. }
  285. static void init(void) {
  286. for (int i = 0; i < 20; i++) {
  287. draw_board_line();
  288. }
  289. tetris_send_string("doooooooooob");
  290. curx = 12;
  291. cury = 20;
  292. }
  293. static int get_piece_min_y(char piece[7][7]) {
  294. for (int y = 0; y < 7; y++) {
  295. for (int x = 0; x < 7; x++) {
  296. if (piece[y][x])
  297. return y;
  298. }
  299. }
  300. return 0;
  301. }
  302. static int clear_lines(char board[20][10]) {
  303. int cleared_lines = 0;
  304. for (int y = 19; y >= 0; y--) {
  305. char isfull = 1;
  306. for (int x = 0; x < 10; x++) {
  307. if (!board[y][x]) {
  308. isfull = 0;
  309. }
  310. }
  311. if (isfull) {
  312. // delete clear line
  313. send_goto_xy(12, y);
  314. send_backspaces(12); // delete line contents
  315. // delete newline
  316. tetris_send_backspace();
  317. cury--;
  318. curx = 12;
  319. cleared_lines++;
  320. } else {
  321. if (cleared_lines > 0) {
  322. // move cleared lines down on board
  323. for (int x = 0; x < 10; x++) {
  324. board[y + cleared_lines][x] = board[y][x];
  325. }
  326. }
  327. }
  328. }
  329. // clear cleared top lines
  330. for (int y = 0; y < cleared_lines; y++) {
  331. for (int x = 0; x < 10; x++) {
  332. board[y][x] = 0;
  333. }
  334. }
  335. if (cleared_lines > 0) {
  336. send_goto_xy(0, 0);
  337. for (int i = 0; i < cleared_lines; i++) {
  338. draw_board_line();
  339. curx = 0;
  340. cury++;
  341. }
  342. }
  343. return cleared_lines;
  344. }
  345. static uint8_t myrandom(uint8_t seed) {
  346. uint8_t out = seed >> 1;
  347. if (seed & 1) {
  348. out = out ^ 0xB8;
  349. }
  350. return out;
  351. }
  352. static char piece[7][7];
  353. static char board[20][10];
  354. static uint8_t r;
  355. static int score;
  356. static int x;
  357. static int y;
  358. static int shape;
  359. static int rotation;
  360. static int time;
  361. static int next_down;
  362. static int down_delay;
  363. static int first_run;
  364. static int game_over;
  365. void tetris_start(uint8_t seed) {
  366. for (int y = 0; y < 20; y++) {
  367. for (int x = 0; x < 10; x++) {
  368. board[y][x] = 0;
  369. }
  370. }
  371. clear_piece(piece);
  372. init();
  373. game_over = 0;
  374. r = seed;
  375. score = 0;
  376. copy_piece_from_to(empty_piece, piece);
  377. x = 0;
  378. y = 0;
  379. shape = 0;
  380. rotation = 0;
  381. time = 0;
  382. next_down = 0;
  383. down_delay = -1;
  384. first_run = 1;
  385. }
  386. int tetris_tick(int ms_since_previous_tick) {
  387. if (game_over) {
  388. return 0;
  389. }
  390. time += ms_since_previous_tick;
  391. if (first_run || time > next_down) {
  392. if (first_run || is_piece_hitting(board, piece, x, y + 1)) {
  393. first_run = 0;
  394. add_piece_to_board(piece, board, x, y);
  395. score += clear_lines(board);
  396. down_delay = 500 - score * 10;
  397. if (down_delay < 100) {
  398. down_delay = 100;
  399. }
  400. rotation = 0;
  401. shape = r % 7;
  402. r = myrandom(r);
  403. set_piece(piece, shape, rotation);
  404. x = 1;
  405. y = - get_piece_min_y(piece);
  406. draw_piece_moved(get_shape_char(shape), 1 + x, y, piece, 0, 0);
  407. if (is_piece_hitting(board, piece, x, y)) {
  408. game_over = 1;
  409. send_goto_xy(12, 10);
  410. tetris_send_string(" game over");
  411. tetris_send_down();
  412. tetris_send_string(" score ");
  413. char tmp[10];
  414. sprintf(tmp, "%d", score);
  415. tetris_send_string(tmp);
  416. return 0;
  417. }
  418. } else {
  419. y++;
  420. draw_piece_moved(get_shape_char(shape), 1 + x, y, piece, 0, +1);
  421. }
  422. next_down = time + down_delay;
  423. } else {
  424. switch (tetris_get_keypress()) {
  425. case 1: { // up
  426. int oldrotation = rotation;
  427. rotation = (rotation + 1) % 4;
  428. copy_piece_from_to(piece, temp_piece);
  429. set_piece(piece, shape, rotation);
  430. if (is_piece_hitting(board, piece, x, y)) {
  431. rotation = oldrotation;
  432. set_piece(piece, shape, rotation);
  433. } else {
  434. draw_piece(get_shape_char(shape), 1 + x, y, temp_piece, piece);
  435. }
  436. break;
  437. }
  438. case 2: // left
  439. if (!is_piece_hitting(board, piece, x - 1, y)) {
  440. x--;
  441. draw_piece_moved(get_shape_char(shape), 1 + x, y, piece, -1, 0);
  442. }
  443. break;
  444. case 3: {// down
  445. int starty = y;
  446. while (!is_piece_hitting(board, piece, x, y + 1)) {
  447. y++;
  448. }
  449. draw_piece(get_shape_char(shape), x + 1, starty, piece, empty_piece);
  450. draw_piece(get_shape_char(shape), x + 1, y, empty_piece, piece);
  451. next_down = time + down_delay;
  452. break;
  453. }
  454. case 4: // right
  455. if (!is_piece_hitting(board, piece, x + 1, y)) {
  456. x++;
  457. draw_piece_moved(get_shape_char(shape), 1 + x, y, piece, 1, 0);
  458. }
  459. break;
  460. }
  461. }
  462. return 1;
  463. }