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