Thumbnail

rani/games.git

Clone URL: https://git.buni.party/rani/games.git

Viewing file on branch master

1#include <stdlib.h>
2#include <ncurses.h>
3#include <stdbool.h>
4
5
6/* BEGIN CONFIG */
7#ifndef HIGHLIGHT
8#define HIGHLIGHT false
9#endif /* HIGHLIGHT */
10
11#ifndef X
12#define X 16
13#endif /* X */
14
15#ifndef Y
16#define Y 16
17#endif /* Y */
18
19#ifndef SHIFT_DELAY
20#define SHIFT_DELAY 25
21#endif /* SHIFT_DELAY */
22
23#ifndef MATCH_DELAY
24#define MATCH_DELAY 750
25#endif /* MATCH_DELAY */
26
27#ifndef BAD_MATCH_DELAY
28#define BAD_MATCH_DELAY 750
29#endif /* BAD_MATCH_DELAY */
30
31#ifndef GRID
32#define GRID true
33#endif /* GRID */
34
35#ifndef SEED
36#include <time.h>
37#define SEED time(NULL)
38#endif /* SEED */
39/* END CONFIG */
40
41#define SQUARE_C '#'
42#define CIRCLE_C 'O'
43#define TRIANGLE_C 'A'
44#define RHOMBUS_C '='
45#define DIAMOND_C 'm'
46#define HEXAGON_C '&'
47
48
49#define POP (1<<9)
50
51#define ISPOP(g) (g & POP)
52#define GETGEM(g) (g & 0xFF)
53
54#define GETCOLOR(g) (COLOR_PAIR(g + 1))
55#define VALID(x, y) (x < X && y < Y && x >= 0 && y >= 0)
56
57
58enum gem {
59 SQUARE,
60 CIRCLE,
61 TRIANGLE,
62 RHOMBUS,
63 DIAMOND,
64 HEXAGON,
65 LAST_GEM
66};
67
68
69enum {
70 RED = 1,
71 GREEN,
72 YELLOW,
73 BLUE,
74 MAGENTA,
75 CYAN,
76};
77
78
79struct coord {
80 int x, y;
81};
82
83
84char gemc[] = {
85 [SQUARE] = SQUARE_C,
86 [CIRCLE] = CIRCLE_C,
87 [TRIANGLE] = TRIANGLE_C,
88 [RHOMBUS] = RHOMBUS_C,
89 [DIAMOND] = DIAMOND_C,
90 [HEXAGON] = HEXAGON_C,
91};
92
93const struct coord invc = {-1, -1};
94enum gem grid[X][Y];
95struct coord first = {-1, -1};
96int score = 0;
97
98
99int abs(int n) {
100 return n < 0 ? -n : n;
101}
102
103
104int nlen(int n) {
105 int len = 0;
106 while (n > 0) {
107 len++;
108 n /= 10;
109 }
110 return len;
111}
112
113
114void newgem(struct coord c) {
115 int x = c.x;
116 int y = c.y;
117
118 bool done;
119 do {
120 done = true;
121 grid[x][y] = rand() % LAST_GEM;
122 if (VALID(x - 1, y) && grid[x][y] == grid[x - 1][y]) done = false;
123 if (VALID(x, y - 1) && grid[x][y] == grid[x][y - 1]) done = false;
124 } while (!done);
125}
126
127
128void display(void) {
129 erase();
130 if (GRID) {
131 for (int x = 0; x < X; x++) addstr("+ - ");
132 addstr("+\n");
133 } else addch('\n');
134 for (int y = 0; y < Y; y++) {
135 for (int x = 0; x < X; x++) {
136 if (GRID) addstr("| ");
137 else addstr(" ");
138 int cp = GETCOLOR(GETGEM(grid[x][y]));
139 bool reverse = false;
140 if (HIGHLIGHT) reverse = true;;
141 if (first.x == x && first.y == y) reverse = !reverse;
142 if (ISPOP(grid[x][y])) reverse = !reverse;
143 if (reverse) cp |= A_REVERSE;
144 attron(cp);
145 addch(gemc[GETGEM(grid[x][y])]);
146 attroff(cp);
147 addch(' ');
148 }
149 if (GRID) {
150 addstr("|\n");
151 for (int x = 0; x < X; x++) addstr("+ - ");
152 addstr("+\n");
153 } else addstr("\n\n");
154 }
155
156 attron(A_REVERSE);
157 move(Y*2+1, (X*4+2)/2 - (nlen(score) + 8)/2);
158 printw("Score: %d", score);
159 attroff(A_REVERSE);
160 move(Y*2+1, 0);
161 addstr("r to restart");
162
163 refresh();
164}
165
166
167int checkvert(int x, int y, struct coord vc[]) {
168 int s = 0;
169
170 int n = y;
171 while (VALID(x, n) && grid[x][n] == grid[x][y]) {
172 vc[s++] = (struct coord){x, n};
173 n++;
174 }
175 n = y - 1;
176 while (VALID(x, n) && grid[x][n] == grid[x][y]) {
177 vc[s++] = (struct coord){x, n};
178 n--;
179 }
180
181 return s;
182}
183
184
185int checkhoriz(int x, int y, struct coord hc[]) {
186 int s = 0;
187
188 int n = x;
189 while (VALID(n, y) && grid[n][y] == grid[x][y]) {
190 hc[s++] = (struct coord){n, y};
191 n++;
192 }
193 n = x - 1;
194 while (VALID(n, y) && grid[n][y] == grid[x][y]) {
195 hc[s++] = (struct coord){n, y};
196 n--;
197 }
198
199 return s;
200}
201
202
203void shift(struct coord s[], int n) {
204 for (int i = 0; i < n; i++) {
205 struct coord sc = s[i];
206 for (int n = sc.y; n > 0; n--) {
207 grid[sc.x][n] = grid[sc.x][n - 1];
208 display();
209 napms(SHIFT_DELAY);
210 }
211
212 newgem((struct coord){sc.x, 0});
213 }
214}
215
216
217void makepop(struct coord s[], int n) {
218 for (int i = 0; i < n; i++) {
219 struct coord c = s[i];
220 grid[c.x][c.y] |= POP;
221 }
222}
223
224
225void sort(struct coord s[], int n) {
226 for (int i = 0; i < n; i++) {
227 for (int j = 0; j < n - 1; j++) {
228 if (s[j].y > s[j + 1].y) {
229 struct coord t = s[j];
230 s[j] = s[j + 1];
231 s[j + 1] = t;
232 }
233 }
234 }
235}
236
237
238int main(void) {
239 srand(SEED);
240
241 initscr();
242 noecho();
243 curs_set(0);
244 keypad(stdscr, TRUE);
245
246 mousemask(BUTTON1_CLICKED, NULL);
247
248 use_default_colors();
249 start_color();
250 init_pair(RED, COLOR_RED, -1);
251 init_pair(GREEN, COLOR_GREEN, -1);
252 init_pair(YELLOW, COLOR_YELLOW, -1);
253 init_pair(BLUE, COLOR_BLUE, -1);
254 init_pair(MAGENTA, COLOR_MAGENTA, -1);
255 init_pair(CYAN, COLOR_CYAN, -1);
256
257 restart:
258 while (true) {
259 // generate a grid where each element is unique from its direct neighbors
260 for (int x = 0; x < X; x++) {
261 for (int y = 0; y < Y; y++) {
262 newgem((struct coord){x, y});
263 }
264 }
265
266 first = invc;
267
268 while (true) {
269 display();
270
271
272 switch (getch()) {
273 case 'r':
274 goto restart;
275 case KEY_MOUSE:;
276 MEVENT e;
277 if (getmouse(&e) != OK) break;
278
279 // get and VALIDate x and y coords
280 if (e.x % 4 == 0 || e.y % 2 == 0) break;
281 int x = (e.x - 2) / 4;
282 int y = (e.y - 1) / 2;
283 if ((e.x - 1) % 4 == 0) x++;
284 if (!VALID(x, y)) break;
285
286 if (first.x == -1 && first.y == -1) {
287 first.x = x;
288 first.y = y;
289 break;
290 }
291
292 if (first.x == x && first.y == y) {
293 first = invc;
294 break;
295 }
296
297 if (abs(first.x - x) + abs(first.y - y) > 1) {
298 first = invc;
299 break;
300 }
301
302 // swap the gems and see what happens
303 enum gem t = grid[first.x][first.y];
304 grid[first.x][first.y] = grid[x][y];
305 grid[x][y] = t;
306
307 struct coord vc[Y];
308 int v = checkvert(x, y, vc);
309 struct coord hc[X];
310 int h = checkhoriz(x, y, hc);
311
312 struct coord rc[Y + X];
313 int r = 0;
314 if (v >= 3) {
315 sort(vc, v);
316 while (v--) {
317 rc[r] = vc[r];
318 r++;
319 }
320 }
321
322 if (h >= 3) {
323 int i = 0;
324 while (h--) {
325 rc[r] = hc[i];
326 i++;
327 r++;
328 }
329 }
330
331 if (r >= 3) {
332 first = invc;
333 makepop(rc, r);
334 display();
335 napms(MATCH_DELAY);
336 shift(rc, r);
337
338 score += 1000;
339 r -= 3;
340 score += 2000*r*r;
341 } else {
342 // no matches
343 struct coord c = first;
344 first.x = x;
345 first.y = y;
346 display();
347 napms(BAD_MATCH_DELAY);
348 first = c;
349 enum gem t = grid[first.x][first.y];
350 grid[first.x][first.y] = grid[x][y];
351 grid[x][y] = t;
352
353 first = invc;
354 }
355
356 // hacky, ugly way to clear all gems
357 int nc;
358 do {
359 nc = 0;
360
361 for (int nx = 0; nx < X; nx++) {
362 for (int ny = 0; ny < Y; ny++) {
363 struct coord vc[Y];
364 int v = checkvert(nx, ny, vc);
365 struct coord hc[X];
366 int h = checkhoriz(nx, ny, hc);
367 struct coord rc[Y + X];
368 int r = 0;
369 if (v >= 3) {
370 sort(vc, v);
371 while (v--) {
372 rc[r] = vc[r];
373 r++;
374 }
375 }
376
377 if (h >= 3) {
378 int i = 0;
379 while (h--) {
380 rc[r] = hc[i];
381 i++;
382 r++;
383 }
384 }
385
386 if (r >= 3) {
387 nc += r;
388 first = invc;
389 makepop(rc, r);
390 display();
391 napms(MATCH_DELAY);
392 shift(rc, r);
393
394 score += 1000;
395 r -= 3;
396 score += 2000*r*r;
397 }
398 }
399 }
400 } while (nc != 0);
401 break;
402 case 'q':
403 goto terminate;
404 }
405 }
406 }
407
408 terminate:
409 echo();
410 curs_set(1);
411 endwin();
412}
413