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