1 /*
2 |\/|
3 |  |UNCHER
4 
5 Authored by abakh <abakh@tuta.io>
6 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.
7 
8 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/>.
9 
10 
11 */
12 #include <curses.h>
13 #include <string.h>
14 #include <stdlib.h>
15 #include <stdbool.h>
16 #include <limits.h>
17 #include <time.h>
18 #include <signal.h>
19 #include <unistd.h>
20 #include "config.h"
21 #include "common.h"
22 #define SAVE_TO_NUM 10
23 #define MINLEN 10
24 #define MAXLEN 24
25 #define MINWID 40
26 #define MAXWID 80
27 enum {UP=1,RIGHT,DOWN,LEFT,FOOD,SUPERFOOD,TRAP};
28 
29 /* The Plan9 compiler can not handle VLAs */
30 #ifdef NO_VLA
31 #define len 36
32 #define wid 80
33 
34 #ifdef Plan9
usleep(long usec)35 int usleep(long usec) {
36     int second = usec/1000000;
37     long nano = usec*1000 - second*1000000;
38     struct timespec sleepy = {0};
39     sleepy.tv_sec = second;
40     sleepy.tv_nsec = nano;
41     nanosleep(&sleepy, (struct timespec *) NULL);
42     return 0;
43 }
44 #endif
45 
46 
47 #else
48 int len,wid;
49 #endif//NO_VLA
50 
51 int py,px;//pointer
52 
53 byte pse_msg=20;//flashing animations might hurt some people
54 bool epilepsy=0;
55 char alt_animation[4]={'-','\\','|','/'};
56 
57 int immunity;
58 byte direction;
59 long score;
60 chtype colors[6]={0};
61 
62 FILE* scorefile;
63 
logo(void)64 void logo(void){
65 	mvaddstr(1,0,"|\\/|");
66 	mvaddstr(2,0,"|  |UNCHER");
67 }
68 
save_score(void)69 byte save_score(void){
70 	return fallback_to_home("muncher_scores",score,SAVE_TO_NUM);
71 
72 }
73 
show_scores(byte playerrank)74 void show_scores(byte playerrank){
75 	erase();
76 	logo();
77 	if(playerrank==FOPEN_FAIL){
78 		mvaddstr(3,0,"Could not open score file");
79 		printw("\nHowever, your score is %ld.",score);
80 		refresh();
81 		return;
82 	}
83 	if(playerrank == 0){
84 		char formername[60]={0};
85 		long formerscore=0;
86 		rewind(score_file);
87 		fscanf(score_file,"%*s : %*d\n");
88 		move(3,0);
89 		byte b=0;
90 		if ( fscanf(score_file,"%s : %ld\n",formername,&formerscore)==2){
91 			halfdelay(1);
92 			printw("*****CONGRATULATIONS!****\n");
93 			printw("              You bet the\n");
94 			printw("                 previous\n");
95 			printw("                   record\n");
96 			printw("                       of\n");
97 			printw("           %14ld\n",formerscore);
98 			printw("                  held by\n");
99 			printw("              %11s\n",formername);
100 			printw("               \n");
101 			printw("               \n");
102 			printw("*************************\n");
103 			printw("Press a key to proceed:");
104 			Effect:
105 			move(4,0);
106 			attron(colors[b]);
107 			mvprintw(4,0, "     _____ ");
108 			mvprintw(5,0, "   .'     |");
109 			mvprintw(6,0, " .'       |");
110 			mvprintw(7,0, " |  .|    |");
111 			mvprintw(8,0, " |.' |    |");
112 			mvprintw(9,0, "     |    |");
113 			mvprintw(10,0,"  ___|    |___");
114 			mvprintw(11,0," |            |");
115 			mvprintw(12,0," |____________|");
116 			attroff(colors[b]);
117 			b=(b+1)%6;
118 			if(getch()==ERR)
119 				goto Effect;
120 			nocbreak();
121 			cbreak();
122 			erase();
123 			logo();
124 		}
125 	}
126 	//scorefile is still open with w+
127 	move(3,0);
128 	char pname[60] = {0};
129 	long pscore=0;
130 	byte rank=0;
131 	rewind(score_file);
132 	printw(">*>*>Top %d<*<*<\n",SAVE_TO_NUM);
133 	while( rank<SAVE_TO_NUM && fscanf(score_file,"%s : %ld\n",pname,&pscore) == 2){
134 		if(rank == playerrank)
135 			printw(">>>");
136 		printw("%d) %s : %ld\n",rank+1,pname,pscore);
137 		++rank;
138 	}
139 	addch('\n');
140 	refresh();
141 }
rectangle(void)142 void rectangle(void){
143 	for(int y=0;y<=len;++y){
144 		mvaddch(3+y,0,ACS_VLINE);
145 		mvaddch(4+y,1+wid,ACS_VLINE);
146 	}
147 	for(int x=0;x<=wid;++x){
148 		mvaddch(3,x,ACS_HLINE);
149 		mvaddch(4+len,x,ACS_HLINE);
150 	}
151 	mvaddch(3,0,ACS_ULCORNER);
152 	mvaddch(4+len,0,ACS_LLCORNER);
153 	mvaddch(3,1+wid,ACS_URCORNER);
154 	mvaddch(4+len,1+wid,ACS_LRCORNER);
155 }
place_food(byte board[len][wid])156 void place_food(byte board[len][wid]){
157 	int y,x;
158 	do{
159 		y=rand()%len;
160 		x=rand()%wid;
161 	}while(y==py && x==px);
162 	board[y][x]=FOOD;
163 
164 	byte num;
165 	if(score<300)
166 		num=rand()%2;
167 	else if(score<500)
168 		num=1+rand()%2;
169 	else if(score<1000)
170 		num=2+rand()%4;
171 	else if(score<2000)
172 		num=5+rand()%6;
173 	else
174 		num=10+rand()%11;
175 
176 	while(num){
177 		Again:
178 		y=rand()%len;
179 		x=rand()%wid;
180 		if(abs(y-py)<4 && abs(x-px)<7)
181 			goto Again;
182 		if(board[y][x]==FOOD)
183 			goto Again;
184 		board[y][x]=TRAP;
185 
186 		--num;
187 	}
188 	if(score>2000 && !(rand()%5)){
189 		do{
190 			y=rand()%len;
191 			x=rand()%wid;
192 		}while(y==py && x==px && board[y][x]!=FOOD);
193 		board[y][x]=SUPERFOOD;
194 	}
195 }
draw(byte board[len][wid])196 void draw(byte board[len][wid]){
197 	int y,x;
198 	static byte effect=0;
199 	chtype prnt;
200 	rectangle();
201 	for(y=0;y<len;++y){
202 		for(x=0;x<wid;++x){
203 			if(y==py && x==px){
204 				prnt='r'|colors[2]|A_STANDOUT;
205 				if(immunity){
206 					if(epilepsy)
207 						prnt='r';
208 					else
209 						prnt='r'|colors[effect]|A_BOLD;
210 				}
211 			}
212 			else if(board[y][x]==TRAP)
213 				prnt='^'|colors[((y*x)/15)%6];
214 			else if(board[y][x]==FOOD)
215 				prnt='%'|colors[(y+x)%6];
216 			else if(board[y][x]==SUPERFOOD){
217 				if(epilepsy)
218 					prnt=alt_animation[effect/10];
219 				else
220 					prnt='%'|colors[effect];
221 			}
222 			else
223 				prnt= ' ';
224 			mvaddch(4+y,x+1,prnt);
225 		}
226 	}
227 	if(epilepsy)
228 		effect=(effect+1)%40;
229 	else
230 		effect=(effect+1)%6;
231 	if(pse_msg && !epilepsy){
232 		mvprintw(len+5,0,"Suffering PSE? Press e.");
233 		--pse_msg;
234 	}
235 }
help(void)236 void help(void){
237 	nocbreak();
238 	cbreak();
239 	erase();
240 	logo();
241 	attron(A_BOLD);
242 	mvprintw(3,0,"  **** THE CONTROLS ****");
243 	attroff(A_BOLD);
244 	mvprintw(4,0,"hjkl/ARROW KEYS : Change direction");
245 	mvprintw(5,0,"q : Quit");
246 	mvprintw(6,0,"F1 & F2: Help on controls & gameplay");
247 	mvprintw(8,0,"Press a key to continue");
248 	refresh();
249 	getch();
250 	erase();
251 	halfdelay(1);
252 }
gameplay(void)253 void gameplay(void){
254 	nocbreak();
255 	cbreak();
256 	erase();
257 	logo();
258 	attron(A_BOLD);
259 	mvprintw(3,0,"  **** THE GAMEPLAY ****");
260 	attroff(A_BOLD);
261 	move(4,0);
262 	printw("Eat the food and avoid the traps.\n");
263 	refresh();
264 	getch();
265 	halfdelay(1);
266 }
sigint_handler(int x)267 void sigint_handler(int x){
268 	endwin();
269 	puts("Quit.");
270 	exit(x);
271 }
main(int argc,char ** argv)272 int main(int argc, char** argv){
273 	bool autoset=0;
274 	signal(SIGINT,sigint_handler);
275 #ifndef NO_VLA
276 	if(argc>3 || (argc==2 && !strcmp("help",argv[1])) ){
277 		printf("Usage: %s [len wid]\n",argv[0]);
278 		return EXIT_FAILURE;
279 	}
280 	if(argc==2){
281 		puts("Give both dimensions.");
282 		return EXIT_FAILURE;
283 	}
284 	if(argc==3){
285 		bool lool = sscanf(argv[1],"%d",&len) && sscanf(argv[2],"%d",&wid);
286 		if(!lool){
287 			puts("Invalid input.");
288 			return EXIT_FAILURE;
289 		}
290 		if(len<MINLEN || wid<MINWID || len>500 || wid>500){
291 			puts("At least one of your given dimensions is either too small or too big.");
292 			return EXIT_FAILURE;
293 		}
294 	}
295 	else{
296 		autoset=1;
297 	}
298 #endif
299 	initscr();
300 #ifndef NO_VLA
301 	if(autoset){
302 		len=LINES-7;
303 		if(len<MINLEN)
304 			len=MINLEN;
305 		else if(len>MAXLEN)
306 			len=MAXLEN;
307 
308 		wid=COLS-5;
309 		if(wid<MINWID)
310 			wid=MINWID;
311 		else if(wid>MAXWID)
312 			wid=MAXWID;
313 	}
314 #endif
315 	srand(time(NULL)%UINT_MAX);
316 	byte board[len][wid];
317 	bool halfspeed=0;
318 	int constant=150*(80*24)/(len*wid);
319 	initscr();
320 	noecho();
321 	cbreak();
322 	keypad(stdscr,1);
323 	if(has_colors()){
324 		start_color();
325 		use_default_colors();
326 		init_pair(1,COLOR_BLUE,-1);
327 		init_pair(2,COLOR_GREEN,-1);
328 		init_pair(3,COLOR_YELLOW,-1);
329 		init_pair(4,COLOR_CYAN,-1);
330 		init_pair(5,COLOR_MAGENTA,-1);
331 		init_pair(6,COLOR_RED,-1);
332 		for(byte b= 0;b<6;++b){
333 			colors[b]=COLOR_PAIR(b+1);
334 		}
335 
336 	}
337 	Start:
338 	curs_set(0);
339 	halfdelay(1);
340 	score=direction=immunity=0;
341 	py=len/2;
342 	px=wid/2;
343 	memset(board,0,len*wid);
344 	place_food(board);
345 	int preinput,input=0;
346 	while(1){
347 		erase();
348 		logo();
349 		mvprintw(1,11,"Score:%ld",score);
350 		if(immunity)
351 			mvprintw(2,11,"Immunity:%d",immunity);
352 		draw(board);
353 		refresh();
354 		if( board[py][px]==FOOD ){
355 			score+= constant;
356 			board[py][px]=0;
357 			if(!epilepsy){
358 				for(byte b=0;b<6;++b){
359 					mvaddch(4+py,px+1,'r'|colors[b]|A_STANDOUT);
360 					refresh();
361 					usleep(100000/5);
362 				}
363 			}
364 			place_food(board);
365 		}
366 		if( board[py][px]==SUPERFOOD ){
367 			immunity+=(len+wid)/2;
368 			board[py][px]=0;
369 		}
370 		if(board[py][px]==TRAP){
371 			if(immunity)
372 				board[py][px]=0;
373 			else
374 				break;
375 		}
376 		if(px<0 || px>=wid)
377 			break;
378 		halfspeed=!halfspeed;
379 		preinput=input;
380 		input = getch();
381 		if( input == KEY_F(1) || input=='?' )
382 			help();
383 		if( input==KEY_F(2) )
384 			gameplay();
385 		if( (input=='k' || input==KEY_UP) && py>0 ){
386 			direction=UP;
387 			halfspeed=1;
388 		}
389 		if( (input=='j' || input==KEY_DOWN) && py<len-1 ){
390 			direction=DOWN;
391 			halfspeed=1;
392 		}
393 		if( (input=='h' || input==KEY_LEFT) && px>0 )
394 			direction=LEFT;
395 		if( (input=='l' || input==KEY_RIGHT) && px<wid-1 )
396 			direction=RIGHT;
397 		if( input=='e')
398 			epilepsy=1;
399 		if( input=='q')
400 			sigint_handler(0);
401 		if( input=='p'){
402 			nocbreak();
403 			cbreak();
404 			erase();
405 			logo();
406 			attron(A_BOLD);
407 			mvaddstr(1,11,"PAUSED");
408 			attroff(A_BOLD);
409 			getch();
410 			halfdelay(1);
411 		}
412 		if(input!=ERR){
413 			if(preinput==input){//if it wasn't there, hitting two keys in less than 0.1 sec would not work
414 				usleep(100000);
415 				flushinp();
416 			}
417 		}
418 		if(direction==UP && halfspeed){
419 			if(!py)
420 				break;
421 			--py;
422 		}
423 		else if(direction==DOWN && halfspeed){
424 			if(py==len-1)
425 				break;
426 			++py;
427 		}
428 		else if(direction==LEFT){
429 			if(!px)
430 				break;
431 			--px;
432 		}
433 		else if(direction==RIGHT){
434 			if(px==wid-1)
435 				break;
436 			++px;
437 		}
438 		if(immunity)
439 			--immunity;
440 	}
441 	nocbreak();
442 	cbreak();
443 	draw(board);
444 	refresh();
445 	mvprintw(len+5,0,"Game over! Press a key to see the high scores:");
446 	getch();
447 	show_scores(save_score());
448 	printw("Game over! Wanna play again?(y/n)");
449 	curs_set(1);
450 	input=getch();
451 	if( input!= 'N' &&  input!= 'n' && input!='q')
452 		goto Start;
453 	endwin();
454 	return EXIT_SUCCESS;
455 }
456 
457