1 /*
2 _
3 |_)
4 | IPES
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
14 #include <curses.h>
15 #include <string.h>
16 #include <stdlib.h>
17 #include <limits.h>
18 #include <time.h>
19 #include <signal.h>
20 #include "config.h"
21 #include "common.h"
22 #define UP 1
23 #define RIGHT 2
24 #define DOWN 4
25 #define LEFT 8
26 #define CROSSOVER 15
27 #define FILLED 16
28 #define FLOWDELAY 5
29 #define DELAY 3
30 #define SAVE_TO_NUM 10
31 #define SY 0
32 #define SX 7
33 typedef unsigned char bitbox;
34
35 /* The Plan9 compiler can not handle VLAs */
36 #ifdef NO_VLA
37 #define wid 20
38 #define len 14
39 #else
40 int len,wid;
41 #endif
42
43 int py,px,fy,fx;//p: pointer f: fluid
44 bitbox tocome[5]={0};//the row of pipes in the left side
45 chtype green=A_BOLD;//will use bold font instead of green if colors are not available
46 long score;
47
logo(void)48 void logo(void){
49 mvprintw(0,0," _ ");
50 mvprintw(1,0,"|_)");
51 mvprintw(2,0,"| IPES");
52 }
53
save_score(void)54 byte save_score(void){
55 return fallback_to_home("pipes_scores",score,SAVE_TO_NUM);
56
57 }
58
show_scores(byte playerrank)59 void show_scores(byte playerrank){
60 erase();
61 logo();
62 if(playerrank==FOPEN_FAIL){
63 mvaddstr(SY,SX,"Couldn't open scorefile");
64 mvprintw(SY+1,SX,"However, your score is %ld.",score);
65 refresh();
66 return;
67 }
68 if(playerrank == 0){
69 char formername[60]={0};
70 long formerscore=0;
71 rewind(score_file);
72 fscanf(score_file,"%*s : %*d");
73 if ( fscanf(score_file,"%s : %ld",formername,&formerscore)==2 && formerscore>0){
74 byte a = (len-9)/2;
75 attron(A_BOLD);
76 mvprintw(SY,SX, "**** ***");
77 mvprintw(SY+len+1,SX,"***********************");
78 attroff(A_BOLD);
79 attron(green);
80 mvprintw(SY,SX+4,"CONGRATULATIONS!");
81 attroff(green);
82
83 mvprintw(SY+a+1,SX," _____ You bet the");
84 mvprintw(SY+a+2,SX," .' | previous");
85 mvprintw(SY+a+3,SX," .' | record");
86 mvprintw(SY+a+4,SX," | .| | of");
87 mvprintw(SY+a+5,SX," |.' | |%11ld",formerscore);
88 mvprintw(SY+a+6,SX," | | held by");
89 mvprintw(SY+a+7,SX," ___| |___%7s!",formername);
90 mvprintw(SY+a+8,SX," | |");
91 mvprintw(SY+a+9,SX," |____________|");
92 mvprintw(len+2,0,"Game over! Press a key to proceed:");
93 refresh();
94 getch();
95 erase();
96 logo();
97 }
98
99 }
100 attron(A_BOLD);
101 mvprintw(3,0," HIGH");
102 mvprintw(4,0,"SCORES");
103 attroff(A_BOLD);
104 //scorefile is still open with w+
105 char pname[60] = {0};
106 long pscore=0;
107 byte rank=0;
108 rewind(score_file);
109 while( rank<SAVE_TO_NUM && fscanf(score_file,"%s : %ld\n",pname,&pscore) == 2){
110 move(SY+1+rank,SX+1);
111 attron(green);
112 if(rank == playerrank)
113 printw(">>>");
114 printw("%d",rank+1);
115 attroff(green);
116 printw(") %s : %ld",pname,pscore);
117 ++rank;
118 }
119 fclose(score_file);
120 refresh();
121 }
122 //move in direction
MID(bitbox direction)123 void MID(bitbox direction){
124 switch(direction){
125 case UP:
126 --fy;
127 break;
128 case DOWN:
129 ++fy;
130 break;
131 case LEFT:
132 --fx;
133 break;
134 case RIGHT:
135 ++fx;
136 break;
137 }
138 }
opposite(bitbox direction)139 bitbox opposite(bitbox direction){
140 switch(direction){
141 case UP:
142 return DOWN;
143 case DOWN:
144 return UP;
145 case LEFT:
146 return RIGHT;
147 case RIGHT:
148 return LEFT;
149 }
150 return 0;
151 }
rectangle(void)152 void rectangle(void){
153 for(int y=0;y<=len;++y){
154 mvaddch(SY+y,SX,ACS_VLINE);
155 mvaddch(SY+y,SX+wid+1,ACS_VLINE);
156 }
157 for(int x=0;x<=wid;++x){
158 mvaddch(SY,SX+x,ACS_HLINE);
159 mvaddch(SY+len+1,SX+x,ACS_HLINE);
160 }
161 mvaddch(SY,SX,ACS_ULCORNER);
162 mvaddch(SY+len+1,SX,ACS_LLCORNER);
163 mvaddch(SY,SX+wid+1,ACS_URCORNER);
164 mvaddch(SY+len+1,SX+wid+1,ACS_LRCORNER);
165 }
166 //this generates the pipes...
pipegen(void)167 bitbox pipegen(void){
168 if(rand()%17){//17 so all forms have the same chance
169 byte a=rand()%4;
170 byte b;
171 do{
172 b=rand()%4;
173 }while(b==a);
174 return (1 << a) | ( 1 << b);
175 }
176 else
177 return CROSSOVER;//could not be generated like that
178
179 }
180 //.. and this is only for display
addpipe(int y,int x,bitbox pipe,bool highlight)181 void addpipe(int y,int x,bitbox pipe , bool highlight){
182 bitbox p= pipe & ~FILLED;
183 chtype foo ;
184 switch(p){
185 case UP|RIGHT :
186 foo= ACS_LLCORNER;
187 break;
188 case UP|DOWN :
189 foo=ACS_VLINE;
190 break;
191 case UP|LEFT :
192 foo=ACS_LRCORNER;
193 break;
194 case DOWN|RIGHT :
195 foo =ACS_ULCORNER;
196 break;
197 case DOWN|LEFT :
198 foo=ACS_URCORNER;
199 break;
200 case LEFT|RIGHT:
201 foo=ACS_HLINE;
202 break;
203 case RIGHT:
204 foo = '>';
205 break;
206 case LEFT:
207 foo = '<';
208 break;
209 case UP:
210 foo = '^';
211 break;
212 case DOWN:
213 foo = 'v';
214 break;
215 case CROSSOVER: //all
216 foo = ACS_PLUS;
217 break;
218 default:
219 foo = ' ';
220 break;
221 }
222 if( pipe & FILLED )
223 foo |= green;
224 mvaddch(y,x, foo|(highlight*A_REVERSE) );
225 }
226 //display
draw(bitbox board[len][wid])227 void draw(bitbox board[len][wid]){
228 int y,x;
229 for(y=0;y<len;++y){
230 for(x=0;x<wid;++x){
231 addpipe(SY+1+y,SX+x+1,board[y][x], (y==py&&x==px) );//its highlighted when y==py and x==px
232 }
233 }
234 rectangle();
235 }
236
mouseinput(void)237 void mouseinput(void){
238 #ifndef NO_MOUSE
239 MEVENT minput;
240 #ifdef PDCURSES
241 nc_getmouse(&minput);
242 #else
243 getmouse(&minput);
244 #endif
245 if( minput.y-4 <len && minput.x-1<wid*2){
246 py=minput.y-(1+SY);
247 px=minput.x-(1+SX);
248 }
249 else
250 return;
251 if(minput.bstate & BUTTON1_CLICKED)
252 ungetch('\n');
253 #endif //NO_MOUSE
254 }
255 //peacefully close when ^C is pressed
sigint_handler(int x)256 void sigint_handler(int x){
257 endwin();
258 puts("Quit.");
259 exit(x);
260 }
help(void)261 void help(void){
262 erase();
263 logo();
264 attron(A_BOLD);
265 mvprintw(SY,SX+5,"-* *-");
266 mvprintw(3,0," HELP");
267 mvprintw(4,0," PAGE");
268 mvprintw(SY+7,SX,"YOU CAN ALSO USE THE MOUSE!");
269 attroff(A_BOLD);
270 attron(green);
271 mvprintw(SY,SX+7,"THE CONTROLS");
272 attroff(green);
273 mvprintw(SY+1,SX,"RETURN/ENTER : Place/Replace a pipe");
274 mvprintw(SY+2,SX,"hjkl/ARROW KEYS : Move cursor");
275 mvprintw(SY+3,SX,"p : Pause");
276 mvprintw(SY+4,SX,"q : Quit");
277 mvprintw(SY+5,SX,"f : Toggle fast flow");
278 mvprintw(SY+6,SX,"g : Go! (End the countdown.)");
279 mvprintw(SY+6,SX,"F1 & F2 : Help on controls & gameplay");
280 mvprintw(SY+9,SX,"Press a key to continue");
281 refresh();
282 while(getch()==ERR);
283 erase();
284 }
gameplay(void)285 void gameplay(void){
286 erase();
287 logo();
288 attron(A_BOLD);
289 mvprintw(SY,SX+5,"-* *-");
290 mvprintw(3,0," HELP");
291 mvprintw(4,0," PAGE");
292 attroff(A_BOLD);
293 attron(green);
294 mvprintw(SY,SX+7,"THE GAMEPLAY");
295 attroff(green);
296 mvprintw(SY+1,SX,"Keep maintaining the pipeline and");
297 mvprintw(SY+2,SX,"don't let the sewage leak.");
298 refresh();
299 while(getch()==ERR);
300 erase();
301 }
main(int argc,char ** argv)302 int main(int argc, char** argv){
303 signal(SIGINT,sigint_handler);
304 #ifndef NO_VLA
305 if(argc>3 || (argc==2 && !strcmp("help",argv[1])) ){
306 printf("Usage: %s [len wid]\n",argv[0]);
307 return EXIT_FAILURE;
308 }
309 if(argc==2){
310 puts("Give both dimensions.");
311 return EXIT_FAILURE;
312 }
313 if(argc==3){
314 bool lool = sscanf(argv[1],"%d",&len) && sscanf(argv[2],"%d",&wid);
315 if(!lool){
316 puts("Invalid input.");
317 return EXIT_FAILURE;
318 }
319 if(len<5 || wid<5 || len>1000 || wid>1000){
320 puts("At least one of your given dimensions is too small or too big.");
321 return EXIT_FAILURE;
322 }
323
324 }
325 else{
326 wid=20;
327 len=14;
328 }
329 #endif
330 initscr();
331 #ifndef NO_MOUSE
332 mousemask(ALL_MOUSE_EVENTS,NULL);
333 #endif
334 time_t tstart , now, lasttime, giventime=len*wid/4;
335 srand(time(NULL)%UINT_MAX);
336 bitbox direction,board[len][wid];
337 int input;
338 byte foo;
339 bool flow,fast;
340 Start:
341 flow=0;
342 fast=0;
343 score=0;
344 memset(board,0,len*wid);
345 fy=1+(rand()%(len-2) );
346 fx=1+(rand()%(wid-2) );
347 board[fy][fx]= 1 << (rand()%4);
348 direction= board[fy][fx];
349 board[fy][fx]|=FILLED;
350 for(foo=0;foo<5;++foo)
351 tocome[foo]=pipegen();
352 tstart = time(NULL);
353 lasttime=0;
354 initscr();
355 curs_set(0);
356 noecho();
357 cbreak();
358 halfdelay(DELAY);
359 keypad(stdscr,1);
360 if(has_colors()){
361 start_color();
362 use_default_colors();
363 init_pair(2,COLOR_GREEN,-1);
364 green=COLOR_PAIR(2);
365
366 }
367 py=px=0;
368 while(1){
369 now=time(NULL);
370 erase();
371 logo();
372 if(fast){
373 attron(A_BOLD);
374 mvprintw(3,0," FAST");
375 attroff(A_BOLD);
376 }
377
378 if(!flow && giventime >= now-tstart){
379 mvprintw(4,0,"Time:%ld",giventime-(now-tstart));
380 mvprintw(5,0,"Score:");
381 mvprintw(6,0,"%ld",score);
382 }
383 else{
384 mvprintw(4,0,"Score:");
385 mvprintw(5,0,"%ld",score);
386 }
387 for(foo=0;foo<5;++foo)
388 addpipe(11-foo,4,tocome[foo],0);
389 draw(board);
390 refresh();
391
392 if(now-tstart == giventime){
393 flow=1;
394 }
395 if(flow && (fast || ( !(now%FLOWDELAY)&& now!=lasttime ) )){
396 lasttime = now;
397 MID(direction);
398 if(fy<len && fx<wid && fy>=0&& fx>=0 && ( board[fy][fx]&opposite(direction) ) ){
399 if(board[fy][fx] != CROSSOVER && board[fy][fx] != (CROSSOVER|FILLED) )
400 direction = board[fy][fx] & ~opposite(direction);
401 ++score;
402 if(fast)
403 ++score;
404 }
405 else
406 goto End;
407 board[fy][fx]|=FILLED;
408 }
409
410 input = getch();
411 if( input == KEY_F(1) || input=='?' ){
412 help();
413 if(!flow)
414 tstart += time(NULL)-now;
415 }
416 if( input == KEY_F(2) ){
417 gameplay();
418 if(!flow)
419 tstart += time(NULL)-now;
420 }
421 if( input == KEY_MOUSE )
422 mouseinput();
423 if( (input=='k' || input==KEY_UP) && py>0 )
424 --py;
425 if( (input=='j' || input==KEY_DOWN) && py<len-1 )
426 ++py;
427 if( (input=='h' || input==KEY_LEFT) && px>0 )
428 --px;
429 if( (input=='l' || input==KEY_RIGHT) && px<wid-1 )
430 ++px;
431 if( (input == '\n'||input==KEY_ENTER) && !(board[py][px] & FILLED) ){
432 if(board[py][px])
433 score-=3;
434 board[py][px]=tocome[0];
435 for(foo=0;foo<4;++foo)
436 tocome[foo]=tocome[foo+1];
437 tocome[4]= pipegen();
438 }
439 if( input=='f' ){
440 if(fast){
441 halfdelay(DELAY);
442 fast=0;
443 }
444 else{
445 halfdelay(1);
446 fast=1;
447 }
448 }
449 if( input=='p'){
450 erase();
451 logo();
452 attron(A_BOLD);
453 mvprintw(3,0,"PAUSED");
454 attroff(A_BOLD);
455 refresh();
456 while(getch()==ERR);
457 if(!flow)//pausing should not affect the countdown
458 tstart += time(NULL)-now;//now is not right now
459 continue;
460 }
461 if(!flow && input == 'g' )
462 flow=1;
463 if( score < -1000)
464 goto End;
465 if( input=='q'){
466 nocbreak();
467 cbreak();
468 curs_set(1);
469 mvprintw(len+2,0,"Do you want to see the high scores?(y/n)");
470 input=getch();
471 if(input == 'N' || input=='n' || input =='q')
472 sigint_handler(EXIT_SUCCESS);
473
474 show_scores(save_score());
475 mvprintw(len+2,0,"Press a key to exit:");
476 refresh();
477 getch();
478 sigint_handler(EXIT_SUCCESS);
479 }
480
481 }
482 End:
483 nocbreak();
484 cbreak();
485 curs_set(1);
486 attron(A_BOLD|green);
487 mvprintw(3,0," OOPS!");
488 attroff(A_BOLD|green);
489 draw(board);
490 mvprintw(len+2,0,"Game over! Press a key to see the high scores:");
491 getch();
492 show_scores(save_score());
493 mvprintw(len+2,0,"Game over!");
494 printw(" Wanna play again?(y/n)");
495 input=getch();
496 if( input!= 'N' && input!= 'n' && input!='q')
497 goto Start;
498 endwin();
499 return EXIT_SUCCESS;
500 }
501