1 /*
2 _ _
3 |_) (_
4 | \ed_)quare
5
6 Authored by abakh <abakh@tuta.io>
7 To the extent possible under law, the author(s) have dedicated all copyright and related and neighboring rights to this software to the public domain worldwide. This software is distributed without any warranty.
8
9 You should have received a copy of the CC0 Public Domain Dedication along with this software. If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
10
11
12 */
13 #include <curses.h>
14 #include <string.h>
15 #include <stdlib.h>
16 #include <limits.h>
17 #include <time.h>
18 #include <signal.h>
19 #include "config.h"
20 #define LEN 35
21 #define WID 50
22 #define RLEN LEN //real
23 #define RWID WID
24 #define DEAD 0
25 #define ALIVE 1
26 #define RED 2
27
28 int level;
29 byte py,px;
30 byte cy,cx;//cross
31 bool coherent;//square's coherence
32 int anum,rnum;//reds and otherwise alive cell counts
33 chtype colors[6]={0};
cp(byte a[RLEN][RWID],byte b[RLEN][RWID])34 void cp(byte a[RLEN][RWID],byte b[RLEN][RWID]){
35 byte y,x;
36 for(y=0;y<RLEN;++y)
37 for(x=0;x<RWID;++x)
38 b[y][x]=a[y][x];
39 }
logo(void)40 void logo(void){
41 move(0,0);
42 addstr(" _ _\n");
43 addstr("|_) (_\n");
44 addstr("| \\ED_)QUARE");
45 }
rectangle(int sy,int sx)46 void rectangle(int sy,int sx){
47 for(int y=0;y<=LEN;++y){
48 mvaddch(sy+y,sx,ACS_VLINE);
49 mvaddch(sy+y,sx+WID+1,ACS_VLINE);
50 }
51 for(int x=0;x<=WID;++x){
52 mvaddch(sy,sx+x,ACS_HLINE);
53 mvaddch(sy+LEN+1,sx+x,ACS_HLINE);
54 }
55 mvaddch(sy,sx,ACS_ULCORNER);
56 mvaddch(sy+LEN+1,sx,ACS_LLCORNER);
57 mvaddch(sy,sx+WID+1,ACS_URCORNER);
58 mvaddch(sy+LEN+1,sx+WID+1,ACS_LRCORNER);
59 }
count(byte board[LEN][WID])60 void count(byte board[LEN][WID]){
61 byte y,x;
62 anum=rnum=0;
63 for(y=0;y<LEN;++y){
64 for(x=0;x<WID;++x){
65 if(board[y][x]==ALIVE)
66 ++anum;
67 else if(board[y][x]==RED)
68 ++rnum;
69 }
70 }
71 }
72 //display
draw(byte board[RLEN][RWID])73 void draw(byte board[RLEN][RWID]){
74 rectangle(3,0);
75 chtype prnt;
76 byte y,x;
77 for(y=0;y<LEN;++y){
78 for(x=0;x<WID;++x){
79 if(y==cy && x==cx){
80 prnt='X';
81 if(board[y][x]==ALIVE)
82 prnt|=A_STANDOUT;
83 else if(board[y][x]==RED)
84 prnt|=colors[3]|A_STANDOUT;
85 }
86 else{
87 if(board[y][x]==ALIVE)
88 prnt=ACS_BLOCK;
89 else if(board[y][x]==RED){
90 if(coherent)
91 prnt=' '|A_STANDOUT|colors[3];
92 else
93 prnt='O'|colors[3];
94 }
95 else
96 prnt=' ';
97 }
98 mvaddch(4+y,x+1,prnt);
99 }
100 }
101 }
rand_level(byte board[RLEN][RWID])102 void rand_level(byte board[RLEN][RWID]){
103 byte y,x;
104 for(y=0;y<LEN/2;++y){
105 for(x=0;x<WID;++x){
106 if(rand()%2){
107 if(rand()%3)
108 board[y][x]=ALIVE;
109 }
110 else
111 board[y][x]=DEAD;
112 }
113 }
114 }
live(byte board[RLEN][RWID])115 void live(byte board[RLEN][RWID]){
116 byte y,x;
117 byte dy,dx;//delta
118 byte ry,rx;
119 byte alives,reds;
120 byte preboard[RLEN][RWID];
121 cp(board,preboard);
122 for(y=0;y<LEN;++y){
123 for(x=0;x<WID;++x){
124 alives=reds=0;
125 for(dy=-1;dy<2;++dy){
126 for(dx=-1;dx<2;++dx){
127 if(!dy && !dx)
128 continue;
129 ry=y+dy;
130 rx=x+dx;
131 if(ry==-1)
132 ry=LEN-1;
133 else if(ry==LEN)
134 ry=0;
135 if(rx==-1)
136 rx=WID-1;
137 else if(rx==WID)
138 rx=0;
139
140 if(preboard[ry][rx]==ALIVE)
141 ++alives;
142 else if(preboard[ry][rx]==RED)
143 ++reds;
144 }
145 }
146 if(board[y][x]){
147 if(alives+reds==2 || alives+reds==3){
148 if(reds>alives)
149 board[y][x]=RED;
150 else if(alives>reds)
151 board[y][x]=ALIVE;
152 }
153 else{
154 if(coherent && board[y][x]==RED)
155 coherent=0;
156 board[y][x]=DEAD;
157 }
158 }
159 else if(alives+reds==3){
160 if(alives>reds)
161 board[y][x]=ALIVE;
162 else
163 board[y][x]=RED;
164 }
165 }
166 }
167 }
add_line(byte board[LEN][WID],byte line,const char * str)168 void add_line(byte board[LEN][WID],byte line,const char* str){
169 for(byte x=0;str[x]!='\0';++x){
170 if(str[x]=='#')
171 board[line][x]=ALIVE;
172 /*else
173 board[line][x]=0;*/
174 }
175 }
new_level(byte board[LEN][WID])176 void new_level(byte board[LEN][WID]){
177 ++level;
178 memset(board,0,RLEN*RWID);
179 switch(level){
180 case 0:
181 cy=12;
182 cx=RWID/2;
183 add_line(board,5, " #### #");
184 add_line(board,6, " #### #");
185 add_line(board,7, " # # ");
186 add_line(board,8, " # ## # ## # ##");
187 add_line(board,9, " # # # ## # ## #");
188 add_line(board,10," # # # # # # # #");
189 add_line(board,11," ### ## # # # #");
190
191 add_line(board,15," #### ");
192 add_line(board,16," # # ");
193 add_line(board,17," # ## # ## # # ## # #");
194 add_line(board,18," # # # ## # # # # # # # #");
195 add_line(board,19," # # # # # # # # # # # # #");
196 add_line(board,20," #### ## # # # # ## # ###");
197 add_line(board,21," #");
198 add_line(board,22," # #");
199 add_line(board,23," ##");
200 break;
201 case 1:
202 cy=12;
203 cx=RWID/2;
204 add_line(board,5, " # # # #");
205 add_line(board,6, " # # ## # ");
206 add_line(board,7, " # # # ## ### # # ## ## # # ##");
207 add_line(board,8, " # # # # # # # # ## # # # ##");
208 add_line(board,9, " # # # # # # # # # # # # #");
209 add_line(board,10," # # ## # ## # # # # # # #");
210
211 add_line(board,15," #### # ");
212 add_line(board,16," # # # ");
213 add_line(board,17," # # # ## # # # ## # ## # #");
214 add_line(board,18," ##### ## # # # # # # # # # #");
215 add_line(board,19," # # # # # # # # # # # #");
216 add_line(board,20," # # # # ## # # ## #");
217 break;
218 case 2:
219 cy= 12;
220 cx= 10;
221 add_line(board,3, " ## # #");
222 add_line(board,4, " ## # # ");
223 add_line(board,5, " # # ");
224 add_line(board,6, " # # # # ");
225 add_line(board,7, " ### ### ");
226 add_line(board,17," ## ## ");
227 add_line(board,18," # # # #");
228 add_line(board,19," # # # # ");
229 add_line(board,20," # # ");
230 add_line(board,21," ### ### ");
231 add_line(board,22," ### ### ");
232 add_line(board,23," ## ## ");
233 add_line(board,24," ## ## ");
234 add_line(board,25," # ## ## # ");
235 add_line(board,26," ### ###");
236 add_line(board,27," # #");
237
238 add_line(board,30," ##");
239 add_line(board,31," ##");
240 break;
241 case 3:
242 cy=RLEN/2;
243 cx=RWID/2;
244 add_line(board,0, " ");
245 add_line(board,1, " # # ");
246 add_line(board,2, " # # ");
247 add_line(board,3, " ### ### ");
248 add_line(board,4, " # # ");
249 add_line(board,5, " # # ");
250 add_line(board,6, " ### ### ");
251 add_line(board,7, " # # ");
252 add_line(board,8, " # # ");
253 add_line(board,9, " ### ### ");
254 add_line(board,10," # # ");
255 add_line(board,11," # # ");
256 add_line(board,12," ### ### ");
257 add_line(board,13," # # ");
258 add_line(board,14," # #");
259 add_line(board,15," ### ###");
260 add_line(board,17," ");
261 add_line(board,18," # ");
262 add_line(board,19," # ");
263 add_line(board,20," ### ");
264 add_line(board,21," # ");
265 add_line(board,22," # ");
266 add_line(board,23," ### ");
267 add_line(board,24," # ");
268 add_line(board,25," # ");
269 add_line(board,26," ### ");
270 add_line(board,27," # ");
271 add_line(board,28," # ");
272 add_line(board,29," ### ");
273 add_line(board,30," # ");
274 add_line(board,31," # ");
275 add_line(board,32," ### ");
276 break;
277 case 4:
278 cy=rand()%(RLEN/2);
279 cx=rand()%(RWID/2);
280 add_line(board,0, " ");
281 add_line(board,1, " ");
282 add_line(board,2, " ");
283 add_line(board,3, " ");
284 add_line(board,4, " ");
285 add_line(board,5, " ");
286 add_line(board,6, " ");
287 add_line(board,0, " # # # # ");
288 add_line(board,1, " # | | # # # ");
289 add_line(board,2, " # # | | # # # # # # ");
290 add_line(board,3 ," #### | | #### #### #### ");
291 add_line(board,11," ");
292 add_line(board,12," ");
293 add_line(board,13," ");
294 add_line(board,8 ," # # # # ");
295 add_line(board,9 ," # # # # ");
296 add_line(board,10," # # # # # # # # ");
297 add_line(board,11," #### #### #### #### ");
298 add_line(board,19," ");
299 add_line(board,20," ");
300 add_line(board,16," # # # # ");
301 add_line(board,17," #| | # # # ");
302 add_line(board,18," # #| | # # # # # # ");
303 add_line(board,19," ####| | #### #### #### ");
304 add_line(board,25," ");
305 add_line(board,26," ");
306 add_line(board,27," ");
307 add_line(board,28," ");
308 add_line(board,25," # # ");
309 add_line(board,26," # # ");
310 add_line(board,27," # # # # ");
311 add_line(board,28," #### #### ");
312 //add_line(board,5," #");
313 //add_line(board,6," ##");
314 //add_line(board,7," ##");
315 break;
316 default:
317 srand(level);
318 rand_level(board);
319 }
320 }
rm_square(byte board[LEN][WID],byte prey,byte prex)321 void rm_square(byte board[LEN][WID],byte prey,byte prex){
322 byte dy,dx,ry,rx;
323 for(dy=0;dy<2;++dy){
324 for(dx=0;dx<2;++dx){
325 ry=prey+dy;
326 if(ry==-1)
327 ry=LEN-1;
328 else if(ry==LEN)
329 ry=0;
330 rx=prex+dx;
331 if(rx==-1)
332 rx=WID-1;
333 else if(rx==WID)
334 rx=0;
335 board[ry][rx]=DEAD;
336 }
337 }
338 }
mk_square(byte board[LEN][WID])339 void mk_square(byte board[LEN][WID]){
340 byte dy,dx,ry,rx;
341 for(dy=0;dy<2;++dy){
342 for(dx=0;dx<2;++dx){
343 ry=py+dy;
344 if(ry==-1)
345 ry=LEN-1;
346 else if(ry==LEN)
347 ry=0;
348 rx=px+dx;
349 if(rx==-1)
350 rx=WID-1;
351 else if(rx==WID)
352 rx=0;
353 board[ry][rx]=RED;
354 }
355 }
356 }
357 //detect if there is a square and enable the player to move
reemerge(byte board[LEN][WID])358 void reemerge(byte board[LEN][WID]){
359 byte y,x,dy,dx,ry,rx;
360 for(y=0;y<LEN;++y)
361 for(x=0;x<WID;++x)
362 if(board[y][x]==RED)
363 goto FoundTheFirst;
364 FoundTheFirst:
365 for(dy=0;dy<2;++dy){
366 for(dx=0;dx<2;++dx){
367 ry=y+dy;
368 if(ry==-1)
369 ry=LEN-1;
370 else if(ry==LEN)
371 ry=0;
372 rx=x+dx;
373 if(rx==-1)
374 rx=WID-1;
375 else if(rx==WID)
376 rx=0;
377 if(board[ry][rx]!=RED){
378 if(!y){
379 y=LEN-1;//the square can be divided at both sides of the border, this prevents failing
380 //it goes to look from the upper-left corner of the square as it would for other squares
381 goto FoundTheFirst;
382 }
383 if(!x){
384 x=WID-1;
385 goto FoundTheFirst;
386 }
387 return;
388 }
389 }
390 }
391 py=y;
392 px=x;
393 coherent=1;
394 }
sigint_handler(int x)395 void sigint_handler(int x){
396 endwin();
397 puts("Quit.");
398 exit(x);
399 }
400 /*void mouseinput(int sy, int sx){
401 MEVENT minput;
402 #ifdef PDCURSES
403 nc_getmouse(&minput);
404 #else
405 getmouse(&minput);
406 #endif
407 if( minput.y-4-sy<LEN && minput.x-1-sx<WID*2){
408 py=minput.y-4-sy;
409 px=(minput.x-1-sx)/2;
410 }
411 else
412 return;
413 if(minput.bstate & BUTTON1_CLICKED)
414 ungetch('\n');
415 if(minput.bstate & (BUTTON2_CLICKED|BUTTON3_CLICKED) )
416 ungetch(' ');
417 }*/
help(void)418 void help(void){
419 erase();
420 logo();
421 nocbreak();
422 attron(A_BOLD);
423 mvprintw(3,0," **** THE CONTROLS ****");
424 attroff(A_BOLD);
425 mvprintw(4,0,"hjkl/ARROW KEYS : Move square");
426 mvprintw(5,0,"q : Quit");
427 mvprintw(6,0,"F1 & F2 : Help on controls & gameplay");
428 mvprintw(8,0,"Press a key to continue");
429 refresh();
430 cbreak();
431 getch();
432 halfdelay(1);
433 erase();
434 }
gameplay(void)435 void gameplay(void){
436 erase();
437 logo();
438 nocbreak();
439 attron(A_BOLD);
440 mvprintw(3,0," **** THE GAMEPLAY ****");
441 attroff(A_BOLD);
442 mvprintw(4,0,"Move the square and catch the X or outnumber the\n");
443 printw( "white cells with those of your own,\n");
444 printw( "in the environment of Conway's game of life.\n");
445 refresh();
446 cbreak();
447 getch();
448 halfdelay(1);
449 erase();
450 }
main(void)451 int main(void){
452 signal(SIGINT,sigint_handler);
453 srand(time(NULL)%UINT_MAX);
454 initscr();
455 noecho();
456 cbreak();
457 keypad(stdscr,1);
458 if(has_colors()){
459 start_color();
460 use_default_colors();
461 init_pair(1,COLOR_BLUE,-1);
462 init_pair(2,COLOR_GREEN,-1);
463 init_pair(3,COLOR_YELLOW,-1);
464 init_pair(4,COLOR_RED,-1);
465 init_pair(5,COLOR_RED,COLOR_YELLOW);
466 init_pair(6,COLOR_RED,COLOR_MAGENTA);
467 for(byte b= 0;b<6;++b){
468 colors[b]=COLOR_PAIR(b+1);
469 }
470
471 }
472 byte board[RLEN][RWID];
473 memset(board,0,RLEN*RWID);
474 int input=0;
475 int prey,prex;
476 int cinred;
477 Start:
478 curs_set(0);
479 halfdelay(9);
480 cinred=0;
481 py=LEN*3/4;
482 px=WID/2;
483 curs_set(0);
484 level=-1;
485 new_level(board);
486 mk_square(board);
487 while(1){
488 switch(rand()%5){//move the cross
489 case 0:
490 ++cx;
491 if(cx==WID)
492 cx=0;
493 break;
494 case 1:
495 --cy;
496 if(cy==-1)
497 cy=LEN-1;
498 break;
499 case 2:
500 --cx;
501 if(cx==-1)
502 cx=WID-1;
503 break;
504 case 3:
505 ++cy;
506 if(cy==LEN)
507 cy=0;
508 break;
509 case 4:
510 ;//stay there
511 }
512 if(board[cy][cx]==RED)
513 ++cinred;
514 else
515 cinred=0;
516 count(board);
517 if(rnum!=4)
518 coherent=0;
519 if(!coherent && rnum==4)
520 reemerge(board);
521 erase();
522 logo();
523 draw(board);
524 refresh();
525 if(rnum>anum||cinred==2){
526 mvprintw(LEN+5,0,"Well done! Press a key to continue:");
527 curs_set(1);
528 getch();
529 curs_set(0);
530 new_level(board);
531 py=LEN*3/4;
532 px=WID/2;
533 mk_square(board);
534 continue;
535 }
536 else if(!rnum){
537 move(LEN+5,0);
538 printw("You have lost The Game");
539 if(rand()%5==0)
540 printw(" (and RedSquare)");
541 printw(". ");
542 break;
543 }
544 halfdelay(9);
545 input = getch();
546 live(board);
547 count(board);//apparently this should come at both sides of live+draw. resulting from trial and error.
548 if(rnum!=4)//the square has participated in life reactions if so
549 coherent=0;
550 if(!coherent && rnum==4)//there can be a square
551 reemerge(board);
552
553 if( input==KEY_F(1) || input=='?' )
554 help();
555 if( input==KEY_F(2) )
556 gameplay();
557 prey=py;
558 prex=px;
559 if(input=='k' || input==KEY_UP){
560 --py;
561 if(py==-1)
562 py=LEN-1;
563 }
564 else if(input=='j' || input==KEY_DOWN){
565 ++py;
566 if(py==LEN)
567 py=0;
568 }
569 else if(input=='h' || input==KEY_LEFT){
570 --px;
571 if(px==-1)
572 px=WID-1;
573 }
574 else if(input=='l' || input==KEY_RIGHT){
575 ++px;
576 if(px==WID)
577 px=0;
578 }
579 else
580 goto DidntMove;
581 if(coherent){
582 rm_square(board,prey,prex);
583 mk_square(board);
584 }
585 DidntMove:
586 if( input=='q')
587 sigint_handler(0);
588 if( input=='p'){
589 nocbreak();
590 cbreak();
591 erase();
592 logo();
593 attron(A_BOLD);
594 addstr("\n PAUSED");
595 attroff(A_BOLD);
596 refresh();
597
598 getch();
599
600 halfdelay(9);
601 }
602
603 }
604
605 printw("Wanna play again?(y/n)");
606 nocbreak();
607 cbreak();
608 curs_set(1);
609 flushinp();
610
611 input=getch();
612
613 if(input != 'N' && input != 'n' && input != 'q')
614 goto Start;
615 endwin();
616 return EXIT_SUCCESS;
617 }
618