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