1 /*
2  _
3 |_)
4 | \ABBITHOLE
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 compile with -lncurses
13 */
14 
15 #include <curses.h>
16 #include <string.h>
17 #include <stdlib.h>
18 #include <limits.h>
19 #include <time.h>
20 #include <signal.h>
21 #include "config.h"
22 #define UP 1
23 #define RIGHT 2
24 #define DOWN 4
25 #define LEFT 8
26 #define VISITED 16
27 #define CARROT 32
28 typedef unsigned char bitbox;
29 
30 /* The Plan9 compiler can not handle VLAs */
31 #ifdef NO_VLA
32 #define len 10
33 #define wid 20
34 #else
35 int len,wid;
36 #endif
37 int py,px;
38 
39 chtype colors[6]={0};
40 
41 typedef struct point{
42 	int y;
43 	int x;
44 } point;
45 
MID(int y,int x,bitbox direction)46 point MID(int y,int x,bitbox direction){//move in direction
47 	point pt = {y,x};
48 	switch(direction){
49 		case UP:
50 			--pt.y;
51 			return pt;
52 		case DOWN:
53 			++pt.y;
54 			return pt;
55 		case LEFT:
56 			--pt.x;
57 			return pt;
58 		case RIGHT:
59 			++pt.x;
60 			return pt;
61 	}
62 	return pt;
63 }
rectangle(int sy,int sx)64 void rectangle(int sy,int sx){
65 	for(int y=0;y<=len*2;++y){
66 		mvaddch(sy+y,sx,ACS_VLINE);
67 		mvaddch(sy+y,sx+wid*2,ACS_VLINE);
68 	}
69 	for(int x=0;x<=wid*2;++x){
70 		mvaddch(sy,sx+x,ACS_HLINE);
71 		mvaddch(sy+len*2,sx+x,ACS_HLINE);
72 	}
73 	mvaddch(sy,sx,ACS_ULCORNER);
74 	mvaddch(sy+len*2,sx,ACS_LLCORNER);
75 	mvaddch(sy,sx+wid*2,ACS_URCORNER);
76 	mvaddch(sy+len*2,sx+wid*2,ACS_LRCORNER);
77 }
78 
draw(int sy,int sx,bitbox board[len][wid])79 void draw(int sy,int sx,bitbox board[len][wid]){
80 	int y,x;
81 	bitbox d;
82 	chtype prnt;
83 	point pt;
84 	for(y=0;y<len;++y){
85 		for(x=0;x<wid;++x){
86 			prnt=0;
87 			if( board[y][x] & CARROT )
88 				prnt='%'|A_BOLD|colors[3];
89 			else if(y==py && x==px)
90 				prnt= 'r'|A_REVERSE;
91 			if( board[y][x] & VISITED ){
92 				if(y!=py || x!=px)
93 					prnt='.'|A_REVERSE;
94 				for(d=1;d<32;d=d << 1){
95 					if(board[y][x] & d){
96 						pt=MID(sy+1+y*2,sx+x*2+1,d);
97 						mvaddch(pt.y,pt.x,' '|A_REVERSE);
98 					}
99 				}
100 			}
101 			if(prnt)
102 				mvaddch(sy+1+y*2,sx+x*2+1,prnt);
103 		}
104 	}
105 	rectangle(sy,sx);
106 }
make_maze(bitbox board[len][wid],point f)107 void make_maze(bitbox board[len][wid],point f){
108 	byte ds_tried=0;
109 	byte dnumber=rand()%4;
110 	bitbox direction= 1 << (dnumber);
111 	while( direction == board[f.y][f.x] )
112 		direction= 1 << (dnumber=rand()%4);
113 
114 	point pt = MID(f.y,f.x,direction);
115 	while(ds_tried<4){
116 		if(pt.y<0 || pt.y==len || pt.x<0 || pt.x==wid || board[pt.y][pt.x])
117 			;
118 		else{ //if the tile exists and is empty
119 			board[f.y][f.x] |= direction;
120 			board[pt.y][pt.x]= 1 << ( (dnumber+2)%4 );//direction's reverse
121 			make_maze(board,pt);//recursion
122 		}
123 		direction= 1 << (dnumber= (dnumber+1)%4 );
124 		pt= MID(f.y,f.x,direction);
125 		++ds_tried;
126 	}
127 }
carrotify(bitbox board[len][wid],int count)128 void carrotify(bitbox board[len][wid],int count){
129 	int y,x,c=count;
130 	while(c){
131 		y=rand()%len;
132 		x=rand()%wid;
133 		while( board[y][x] & CARROT ){
134 			y=rand()%len;
135 			x=rand()%wid;
136 		}
137 		board[y][x] |= CARROT;
138 		--c;
139 	}
140 }
help(void)141 void help(void){
142 	erase();
143 	mvprintw(0,0," _ ");
144 	mvprintw(1,0,"|_)");
145 	mvprintw(2,0,"| \\ABBITHOLE");
146 	attron(A_BOLD);
147 	mvprintw(3,0,"  **** THE CONTROLS ****");
148 	attroff(A_BOLD);
149 	mvprintw(4,0,"hjkl/ARROW KEYS : Move cursor");
150 	mvprintw(5,0,"q : Quit");
151 	mvprintw(6,0,"F1 & F2: Help on controls & gameplay (viewing these pages doesn't pause the timer!)");
152 	mvprintw(7,0,"PgDn,PgUp,<,> : Scroll");
153 	mvprintw(9,0,"Press a key to continue");
154 
155 	refresh();
156 	while ( getch()==ERR );
157 	erase();
158 }
gameplay(void)159 void gameplay(void){
160 	erase();
161 	mvprintw(0,0," _ ");
162 	mvprintw(1,0,"|_)");
163 	mvprintw(2,0,"| \\ABBITHOLE");
164 	attron(A_BOLD);
165 	mvprintw(3,0,"  **** THE GAMEPLAY ****");
166 	attroff(A_BOLD);
167 	move(4,0);
168 	printw("Try to gather all the carrots in the maze\n");
169 	printw("in the given time. The determining factors\n");
170 	printw("are your choice of paths and the speed of\n ");
171 	printw("your fingers.\n");
172 	refresh();
173 	while ( getch()==ERR );
174 	erase();
175 }
sigint_handler(int x)176 void sigint_handler(int x){
177 	endwin();
178 	puts("Quit.");
179 	exit(x);
180 }
main(int argc,char ** argv)181 int main(int argc, char** argv){
182 	bool autoset=0;
183 	signal(SIGINT,sigint_handler);
184 #ifndef NO_VLA
185 	if(argc>3 || (argc==2 && !strcmp("help",argv[1])) ){
186 		printf("Usage: %s [len wid]\n",argv[0]);
187 		return EXIT_FAILURE;
188 	}
189 	if(argc==2){
190 		puts("Give both dimensions.");
191 		return EXIT_FAILURE;
192 	}
193 	if(argc==3){
194 		bool lool = sscanf(argv[1],"%d",&len) && sscanf(argv[2],"%d",&wid);
195 		if(!lool){
196 			puts("Invalid input.");
197 			return EXIT_FAILURE;
198 		}
199 		if(len<5 || wid<5 || len>800 || wid>800){
200 			puts("At least one of your given dimensions is either too small or too big.");
201 			return EXIT_FAILURE;
202 		}
203 
204 	}
205 	else{
206 		autoset=1;
207 	}
208 #endif
209 	initscr();
210 #ifndef NO_VLA
211 	if(autoset){
212 		if((LINES-7)/2 < 10)
213 			len=10;
214 		else
215 			len=(LINES-7)/2;
216 
217 		if((COLS-5)/2 < 20)
218 			wid=20;
219 		else
220 			wid=(COLS-5)/2;
221 	}
222 #endif
223 	int carrot_count= (len*wid)/50;
224 	int carrots_found;
225 	time_t tstart , now, giventime=len*wid/5;
226 	srand(time(NULL)%UINT_MAX);
227 	point start={0,0};
228 	bitbox board[len][wid];
229 	int sy,sx;
230 	Start:
231 	tstart = time(NULL);
232 	carrots_found=0;
233 	initscr();
234 	curs_set(0);
235 	noecho();
236 	cbreak();
237 	halfdelay(3);
238 	keypad(stdscr,1);
239 	if(has_colors()){
240 		start_color();
241 		use_default_colors();
242 		init_pair(1,COLOR_BLUE,-1);
243 		init_pair(2,COLOR_GREEN,-1);
244 		init_pair(3,COLOR_YELLOW,-1);
245 		init_pair(4,COLOR_RED,-1);
246 		init_pair(5,COLOR_RED,COLOR_YELLOW);
247 		init_pair(6,COLOR_RED,COLOR_MAGENTA);
248 		for(byte b= 0;b<6;++b){
249 			colors[b]=COLOR_PAIR(b+1);
250 		}
251 
252 	}
253 	sy=sx=0;
254 	py=px=0;
255 	memset(board,0,len*wid);
256 	make_maze(board,start);
257 	carrotify(board,carrot_count);
258 	int input;
259 	while(1){
260 		board[py][px] |= VISITED;
261 		if( board[py][px] & CARROT ){
262 			++carrots_found;
263 			board[py][px] &= ~CARROT;
264 		}
265 		now=time(NULL);
266 		erase();
267 		mvprintw(sy+0,sx+0," _ ");
268 		mvprintw(sy+1,sx+0,"|_)          Time left    :%ld",giventime-(now-tstart));
269 		mvprintw(sy+2,sx+0,"| \\ABBITHOLE Carrots left :%d",carrot_count-carrots_found);
270 		draw(sy+3,sx+0,board);
271 		refresh();
272 		if(carrots_found==carrot_count || now-tstart == giventime){
273 			flushinp();
274 			break;
275 		}
276 		input = getch();
277 		if( input==KEY_PPAGE && LINES< len+3){//the board starts in 3
278 			sy+=10;
279 			if(sy>0)
280 				sy=0;
281 		}
282 		if( input==KEY_NPAGE && LINES< len+3){
283 			sy-=10;
284 			if(sy< -(len+3) )
285 				sy=-(len+3);
286 		}
287 		if( input=='<' && COLS< wid*2+1){
288 			sx+=10;
289 			if(sx>0)
290 				sx=0;
291 		}
292 		if( input=='>' && COLS< wid*2+1){
293 			sx-=10;
294 			if(sx< -(wid*2+1))
295 				sx=-(wid*2+1);
296 		}
297 		if( input == KEY_F(2) )
298 			gameplay();
299 		if( input == KEY_F(1) || input=='?' )
300 			help();
301 		if( (input=='k' || input==KEY_UP) && py>0 && (board[py][px]&UP) )
302 			--py;
303 		if( (input=='j' || input==KEY_DOWN) && py<len-1 && (board[py][px]&DOWN) )
304 			++py;
305 		if( (input=='h' || input==KEY_LEFT) && px>0 && (board[py][px]&LEFT) )
306 			--px;
307 		if( (input=='l' || input==KEY_RIGHT) && px<wid-1 && (board[py][px]&RIGHT) )
308 			++px;
309 		if( input=='q')
310 			sigint_handler(0);
311 		if( board[py][px] & CARROT ){
312 			++carrots_found;
313 			board[py][px] &= ~CARROT;
314 		}
315 	}
316 	End:
317 	nocbreak();
318 	cbreak();
319 	draw(3,0,board);
320 	refresh();
321 	if(carrots_found==carrot_count)
322 		mvprintw(len*2+4,0,"YAY!!");
323 	else
324 		mvprintw(len*2+4,0,"You gathered %2.1f%% of the carrots in %d seconds.",(float) carrots_found*100/carrot_count,giventime);
325 
326 	printw(" Wanna play again?(y/n)");
327 	curs_set(1);
328 	input=getch();
329 	if(input == 'Y' || input == 'y')
330 		goto Start;
331 	else if( input!= 'N' &&  input!= 'n' && input!='q')
332 		goto End;
333 	endwin();
334 	return EXIT_SUCCESS;
335 }
336