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