Main.c 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371
  1. #include <locale.h>
  2. #include <ncurses.h>
  3. #include <stdio.h>
  4. #include <stdlib.h>
  5. #include <time.h>
  6. #include <unistd.h>
  7. typedef enum { MOVING, STILL, EMPTY, WALL } FieldState;
  8. static FieldState** fields;
  9. static int width;
  10. static int height;
  11. static int objectX;
  12. static int objectY;
  13. static int movingObject[8];
  14. static void (*rotateFunction)(void);
  15. static uint32_t seed = 0;
  16. static void initRandom(void) {
  17. while(seed == 0) {
  18. struct timespec time;
  19. timespec_get(&time, TIME_UTC);
  20. seed = (uint32_t)time.tv_nsec;
  21. }
  22. }
  23. static int nextRandom(int max) {
  24. seed = seed * 0x915f77f5 + 1;
  25. int32_t random = ((int32_t)(seed >> 16)) % max;
  26. return random;
  27. }
  28. static void initGamefield(int w, int h) {
  29. width = w;
  30. height = h;
  31. fields = (FieldState**)malloc(sizeof(FieldState*) * (size_t)height);
  32. for(int y = 0; y < height; y++) {
  33. fields[y] = (FieldState*)malloc(sizeof(FieldState) * (size_t)width);
  34. for(int x = 0; x < width; x++) {
  35. fields[y][x] = EMPTY;
  36. }
  37. }
  38. }
  39. static void printGamefield(void) {
  40. clear();
  41. static const char* MAP[] = {"\u2592", "\u2593", " "};
  42. static const char* wall = "\u2588";
  43. int maxX = 0;
  44. int maxY = 0;
  45. getmaxyx(stdscr, maxY, maxX);
  46. int factorX = maxX / (width + 2);
  47. int factorY = maxY / (height + 2);
  48. while(factorY > 1 && factorY * 2 > factorX) {
  49. factorY--;
  50. }
  51. factorX = factorY * 2;
  52. for(int x = 0; x < (width + 2) * factorX; x++) {
  53. for(int y = 0; y < factorY; y++) {
  54. mvaddstr(y, x, wall);
  55. mvaddstr(y + (height + 1) * factorY, x, wall);
  56. }
  57. }
  58. for(int y = 0; y < height; y++) {
  59. for(int wy = y * factorY; wy < (y + 1) * factorY; wy++) {
  60. mvaddstr(wy + factorY, 0, wall);
  61. for(int x = 1; x < factorX; x++) {
  62. addstr(wall);
  63. }
  64. for(int x = 0; x < width; x++) {
  65. for(int w = 0; w < factorX; w++) {
  66. addstr(MAP[fields[y][x]]);
  67. }
  68. }
  69. for(int x = 0; x < factorX; x++) {
  70. addstr(wall);
  71. }
  72. }
  73. }
  74. }
  75. static void deleteGamefield(void) {
  76. for(int i = 0; i < height; i++) {
  77. free(fields[i]);
  78. }
  79. free(fields);
  80. }
  81. static void setField(int x, int y, FieldState state) {
  82. if(x >= 0 && y >= 0 && x < width && y < height) {
  83. fields[y][x] = state;
  84. }
  85. }
  86. static FieldState getField(int x, int y) {
  87. if(x >= 0 && y >= 0 && x < width && y < height) {
  88. return fields[y][x];
  89. }
  90. return WALL;
  91. }
  92. static int canMove(int x, int y) {
  93. FieldState t = getField(x, y);
  94. return t == EMPTY || t == MOVING;
  95. }
  96. static void rotate33(void) {
  97. int data[8];
  98. for(int i = 0; i < 8; i += 2) {
  99. data[i] = movingObject[i + 1] - objectY + objectX;
  100. data[i + 1] = 2 - (movingObject[i] - objectX) + objectY;
  101. if(!canMove(data[i], data[i + 1])) {
  102. return;
  103. }
  104. }
  105. for(int i = 0; i < 8; i += 2) {
  106. setField(movingObject[i], movingObject[i + 1], EMPTY);
  107. movingObject[i] = data[i];
  108. movingObject[i + 1] = data[i + 1];
  109. }
  110. for(int i = 0; i < 8; i += 2) {
  111. setField(movingObject[i], movingObject[i + 1], MOVING);
  112. }
  113. }
  114. static void rotate41(void) {
  115. int data[8];
  116. for(int i = 0; i < 8; i += 2) {
  117. data[i] = movingObject[i + 1] - objectY + objectX;
  118. data[i + 1] = movingObject[i] - objectX + objectY;
  119. if(!canMove(data[i], data[i + 1])) {
  120. return;
  121. }
  122. }
  123. for(int i = 0; i < 8; i += 2) {
  124. setField(movingObject[i], movingObject[i + 1], EMPTY);
  125. movingObject[i] = data[i];
  126. movingObject[i + 1] = data[i + 1];
  127. }
  128. for(int i = 0; i < 8; i += 2) {
  129. setField(movingObject[i], movingObject[i + 1], MOVING);
  130. }
  131. }
  132. static void rotate22(void) {
  133. // nothing to do
  134. }
  135. static void rotate(void) {
  136. rotateFunction();
  137. }
  138. static void spawnObject(int x, int y) {
  139. objectX = x;
  140. objectY = y;
  141. int u = nextRandom(8);
  142. objects[u]++;
  143. switch(u) {
  144. // .X.
  145. // XXX
  146. // ...
  147. case 0:
  148. movingObject[0] = x + 1;
  149. movingObject[1] = y + 0;
  150. movingObject[2] = x + 0;
  151. movingObject[3] = y + 1;
  152. movingObject[4] = x + 1;
  153. movingObject[5] = y + 1;
  154. movingObject[6] = x + 2;
  155. movingObject[7] = y + 1;
  156. rotateFunction = rotate33;
  157. break;
  158. // .X.
  159. // .X.
  160. // .XX
  161. case 1:
  162. movingObject[0] = x + 1;
  163. movingObject[1] = y + 0;
  164. movingObject[2] = x + 1;
  165. movingObject[3] = y + 1;
  166. movingObject[4] = x + 1;
  167. movingObject[5] = y + 2;
  168. movingObject[6] = x + 2;
  169. movingObject[7] = y + 2;
  170. rotateFunction = rotate33;
  171. break;
  172. case 2:
  173. // .X.
  174. // .X.
  175. // XX.
  176. movingObject[0] = x + 1;
  177. movingObject[1] = y + 0;
  178. movingObject[2] = x + 1;
  179. movingObject[3] = y + 1;
  180. movingObject[4] = x + 1;
  181. movingObject[5] = y + 2;
  182. movingObject[6] = x + 0;
  183. movingObject[7] = y + 2;
  184. rotateFunction = rotate33;
  185. break;
  186. // X..
  187. // XX.
  188. // .X.
  189. case 3:
  190. movingObject[0] = x + 0;
  191. movingObject[1] = y + 0;
  192. movingObject[2] = x + 0;
  193. movingObject[3] = y + 1;
  194. movingObject[4] = x + 1;
  195. movingObject[5] = y + 1;
  196. movingObject[6] = x + 1;
  197. movingObject[7] = y + 2;
  198. rotateFunction = rotate33;
  199. break;
  200. // .X.
  201. // XX.
  202. // X..
  203. case 4:
  204. movingObject[0] = x + 1;
  205. movingObject[1] = y + 0;
  206. movingObject[2] = x + 0;
  207. movingObject[3] = y + 1;
  208. movingObject[4] = x + 1;
  209. movingObject[5] = y + 1;
  210. movingObject[6] = x + 0;
  211. movingObject[7] = y + 2;
  212. rotateFunction = rotate33;
  213. break;
  214. // XX
  215. // XX
  216. case 5:
  217. movingObject[0] = x + 0;
  218. movingObject[1] = y + 0;
  219. movingObject[2] = x + 1;
  220. movingObject[3] = y + 0;
  221. movingObject[4] = x + 0;
  222. movingObject[5] = y + 1;
  223. movingObject[6] = x + 1;
  224. movingObject[7] = y + 1;
  225. rotateFunction = rotate22;
  226. break;
  227. // .X..
  228. // .X..
  229. // .X..
  230. // .X..
  231. case 6:
  232. case 7:
  233. movingObject[0] = x + 0;
  234. movingObject[1] = y + 0;
  235. movingObject[2] = x + 0;
  236. movingObject[3] = y + 1;
  237. movingObject[4] = x + 0;
  238. movingObject[5] = y + 2;
  239. movingObject[6] = x + 0;
  240. movingObject[7] = y + 3;
  241. rotateFunction = rotate41;
  242. break;
  243. }
  244. for(int i = 0; i < 8; i += 2) {
  245. setField(movingObject[i], movingObject[i + 1], MOVING);
  246. }
  247. }
  248. static int moveObject(int x, int y) {
  249. for(int i = 0; i < 8; i += 2) {
  250. if(!canMove(movingObject[i] + x, movingObject[i + 1] + y)) {
  251. return 0;
  252. }
  253. }
  254. objectX += x;
  255. objectY += y;
  256. for(int i = 0; i < 8; i += 2) {
  257. setField(movingObject[i], movingObject[i + 1], EMPTY);
  258. movingObject[i] += x;
  259. movingObject[i + 1] += y;
  260. }
  261. for(int i = 0; i < 8; i += 2) {
  262. setField(movingObject[i], movingObject[i + 1], MOVING);
  263. }
  264. return 1;
  265. }
  266. static void removeRows(void) {
  267. for(int y = 0; y < height; y++) {
  268. int remove = 1;
  269. for(int x = 0; x < width; x++) {
  270. remove = remove && getField(x, y) == STILL;
  271. }
  272. if(remove) {
  273. for(int ry = y; ry > 0; ry--) {
  274. for(int rx = 0; rx < width; rx++) {
  275. setField(rx, ry, getField(rx, ry - 1));
  276. }
  277. }
  278. }
  279. }
  280. }
  281. static void onObjectFall(void) {
  282. if(!moveObject(0, 1)) {
  283. for(int i = 0; i < 8; i += 2) {
  284. setField(movingObject[i], movingObject[i + 1], STILL);
  285. }
  286. removeRows();
  287. spawnObject(0, 0);
  288. }
  289. }
  290. int main(void) {
  291. initRandom();
  292. setlocale(LC_ALL, "en_US.UTF-8");
  293. initscr();
  294. cbreak();
  295. keypad(stdscr, TRUE);
  296. noecho();
  297. nodelay(stdscr, TRUE);
  298. curs_set(0);
  299. initGamefield(10, 18);
  300. spawnObject(0, 0);
  301. long lag = 0;
  302. clock_t lastTime = clock();
  303. long ticks = 1000000;
  304. int running = 1;
  305. while(running) {
  306. int key = getch();
  307. if(key == KEY_LEFT) {
  308. moveObject(-1, 0);
  309. } else if(key == KEY_RIGHT) {
  310. moveObject(1, 0);
  311. } else if(key == KEY_DOWN) {
  312. onObjectFall();
  313. } else if(key == KEY_UP) {
  314. rotate();
  315. } else if(key == 'q') {
  316. running = 0;
  317. }
  318. clock_t time = clock();
  319. lag += (time - lastTime);
  320. lastTime = time;
  321. if(lag > ticks) {
  322. lag -= ticks;
  323. onObjectFall();
  324. }
  325. printGamefield();
  326. printw("%ld", lag);
  327. refresh();
  328. // keep the game from taking a bit too much cpu
  329. usleep(1000);
  330. lag += 1000;
  331. }
  332. endwin();
  333. deleteGamefield();
  334. return 0;
  335. }