1 /*
2  _  _  _
3 (_'| |(_'
4 ._):_:._)
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 #include <curses.h>
14 #include <string.h>
15 #include <stdlib.h>
16 #include <limits.h>
17 #include <time.h>
18 #include <signal.h>
19 #include "config.h"
20 #define NOTHING 123
21 
22 #ifdef NO_VLA
23 #define len 5
24 #define wid 6
25 #else
26 int len,wid;
27 #endif
28 
29 int py,px;
30 chtype colors[6]={A_BOLD};
31 int score[2] ={0};
32 int computer[2]={0};
33 char so[2] = {'S','O'};
34 
rd(char board[len][wid],int y,int x)35 char rd(char board[len][wid],int y, int x){
36 	if(y<0 || x<0 || y>= len || x>=wid)
37 		return NOTHING;
38 	else
39 		return board[y][x];
40 }
color(byte colored[len][wid],int y,int x,bool side)41 void color(byte colored[len][wid],int y,int x,bool side){
42 	if(colored[y][x] == !side || colored[y][x]==2)
43 		colored[y][x]=2;
44 	else
45 		colored[y][x]=side;
46 }
rectangle(int sy,int sx)47 void rectangle(int sy,int sx){
48 	for(int y=0;y<=len+1;++y){
49 		mvaddch(sy+y,sx,ACS_VLINE);
50 		mvaddch(sy+y,sx+wid*2,ACS_VLINE);
51 	}
52 	for(int x=0;x<=wid*2;++x){
53 		mvaddch(sy,sx+x,ACS_HLINE);
54 		mvaddch(sy+len+1,sx+x,ACS_HLINE);
55 	}
56 	mvaddch(sy,sx,ACS_ULCORNER);
57 	mvaddch(sy+len+1,sx,ACS_LLCORNER);
58 	mvaddch(sy,sx+wid*2,ACS_URCORNER);
59 	mvaddch(sy+len+1,sx+wid*2,ACS_LRCORNER);
60 }
61 
draw(int sy,int sx,char board[len][wid],byte colored[len][wid])62 void draw(int sy,int sx,char board[len][wid],byte colored[len][wid]){
63 	rectangle(sy,sx);
64 	chtype attr ;
65 	char prnt;
66 	int y,x;
67 	for(y=0;y<len;++y){
68 		for(x=0;x<wid;++x){
69 			attr=A_NORMAL;
70 			if(y==py && x==px)
71 				attr |= A_STANDOUT;
72 			if(colored[y][x]>=0)
73 				attr |= colors[colored[y][x]];
74 			if( board[y][x] )
75 				prnt = board[y][x];
76 			else
77 				prnt = '_';
78 			mvaddch(sy+1+y,sx+x*2+1,attr|prnt);
79 		}
80 	}
81 }
82 
did_sos(char board[len][wid],int y,int x)83 byte did_sos(char board[len][wid], int y , int x ){
84 	byte dy,dx;
85 	byte soses=0;
86 	if(board[y][x]== 'S'){
87 		for(dy=-1;dy<2;++dy){
88 			for(dx=-1;dx<2;++dx){
89 				if(rd(board,y+dy,x+dx)=='O' && rd(board,y+2*dy,x+2*dx) == 'S' )
90 					++soses;
91 			}
92 		}
93 		return soses;
94 	}
95 	else if(board[y][x]== 'O'){
96 		for(dy=-1;dy<2;++dy){
97 			for(dx=-1;dx<2;++dx){
98 				if(rd(board,y+dy,x+dx)=='S' && rd(board,y-dy,x-dx) =='S')
99 					++soses;
100 			}
101 		}
102 		return soses/2;
103 	}
104 	return 0;
105 }
color_sos(char board[len][wid],byte colored[len][wid],int y,int x,bool side)106 void color_sos(char board[len][wid],byte colored[len][wid], int y , int x ,bool side){
107 	byte dy,dx;
108 	if(board[y][x]== 'S'){
109 		for(dy=-1;dy<2;++dy){
110 			for(dx=-1;dx<2;++dx){
111 				if(rd(board,y+dy,x+dx)=='O' && rd(board,y+2*dy,x+2*dx) == 'S' ){
112 					color(colored,y,x,side);
113 			       		color(colored,y+dy,x+dx,side);
114 					color(colored,y+2*dy,x+2*dx,side);
115 				}
116 			}
117 		}
118 	}
119 	else if(board[y][x]== 'O'){
120 		for(dy=-1;dy<2;++dy){
121 			for(dx=-1;dx<2;++dx){
122 				if(rd(board,y+dy,x+dx)=='S' && rd(board,y-dy,x-dx) =='S'){
123 					color(colored,y,x,side);
124 			      		color(colored,y+dy,x+dx,side);
125 					color(colored,y-dy,x-dx,side);
126 				}
127 			}
128 		}
129 	}
130 }
randmove(int * y,int * x,byte * c)131 void randmove(int* y,int* x,byte* c){
132 	*y=rand()%len;
133 	*x=rand()%wid;
134 	*c=rand()%2;
135 }
decide(char board[len][wid],byte colored[len][wid],byte depth,byte side)136 int decide ( char board[len][wid],byte colored[len][wid], byte depth , byte side ){ //the move is imaginary if side is negative
137 	int adv,bestadv;
138 	int oppadv;
139 	int besty,bestx;
140 	char bestchar;
141 	byte c;
142 	oppadv=adv=bestadv=INT_MIN;
143 	besty=bestx=-1;
144 	int y,x;
145 
146 	int ry,rx;
147 	byte rc;
148 	randmove(&ry,&rx,&rc);//provides efficient randomization
149 	for(y=0;y<len;++y){
150 		for(x=0;x<wid;++x){
151 			if(!board[y][x]){
152 				for(c=0;c<2;++c){
153 					board[y][x]=so[c];
154 					adv=did_sos(board,y,x);
155 					if(depth>0)
156 						oppadv= decide(board,NULL,depth-1,-1);
157 					if(depth>0 && oppadv != INT_MIN)//this has no meanings if the opponet cannot move
158 						adv-=1*oppadv;
159 					if(besty<0 ||adv>bestadv || (adv==bestadv && y==ry && x==rx &&  c==rc /*c==0*/) ){
160 						bestadv=adv;
161 						besty=y;
162 						bestx=x;
163 						bestchar=so[c];
164 					}
165 					board[y][x]=0;
166 				}
167 			}
168 		}
169 	}
170 	if(besty>=0 && side >= 0 ){
171 		board[besty][bestx]=bestchar;
172 		score[side]+= did_sos(board,besty,bestx);
173 		color_sos(board,colored,besty,bestx,side);
174 	}
175 	return bestadv;
176 }
isfilled(char board[len][wid])177 bool isfilled(char board[len][wid]){
178 	int y,x;
179 	for(y=0;y<len;++y)
180 		for(x=0;x<wid;++x)
181 			if(!board[y][x])
182 				return 0;
183 	return 1;
184 }
sigint_handler(int x)185 void sigint_handler(int x){
186 	endwin();
187 	puts("Quit.");
188 	exit(x);
189 }
mouseinput(int sy,int sx)190 void mouseinput(int sy,int sx){
191 #ifndef NO_MOUSE
192 	MEVENT minput;
193 	#ifdef PDCURSES
194 	nc_getmouse(&minput);
195 	#else
196 	getmouse(&minput);
197 	#endif
198 	if( minput.y-4-sy <len && minput.x-1-sx<wid*2){
199 		py=minput.y-4-sy;
200 		px=(minput.x-1-sx)/2;
201 	}
202 	else
203 		return;
204 	if(minput.bstate & BUTTON1_CLICKED)
205 		ungetch('S');
206 	if(minput.bstate & (BUTTON2_CLICKED|BUTTON3_CLICKED) )
207 		ungetch('O');
208 #endif
209 }
help(void)210 void help(void){
211 	erase();
212 	mvprintw(0,0," _  _  _");
213 	mvprintw(1,0,"(_'| |(_' ");
214 	mvprintw(2,0,"._):_:._) ");
215 	attron(A_BOLD);
216 	mvprintw(3,0,"  **** THE CONTROLS ****");
217 	mvprintw(9,0,"YOU CAN ALSO USE THE MOUSE!");
218 	attroff(A_BOLD);
219 	mvprintw(4,0,"hjkl/ARROW KEYS : Move cursor");
220 	mvprintw(5,0,"S & O : Write S or O");
221 	mvprintw(6,0,"q : Quit");
222 	mvprintw(7,0,"F1 & F2: Help on controls & gameplay");
223 	mvprintw(8,0,"PgDn,PgUp,<,> : Scroll");
224 	mvprintw(11,0,"Press a key to continue");
225 	refresh();
226 	getch();
227 	erase();
228 }
gameplay(void)229 void gameplay(void){
230 	erase();
231 	mvprintw(0,0," _  _  _");
232 	mvprintw(1,0,"(_'| |(_' ");
233 	mvprintw(2,0,"._):_:._) ");
234 	attron(A_BOLD);
235 	mvprintw(3,0,"  **** THE GAMEPLAY ****");
236 	attroff(A_BOLD);
237 	move(4,0);
238 	printw("The game is similar to Tic Tac Toe:\n");
239 	printw("The players write S and O in the squares\n");
240 	printw("and making the straight connected sequence\n");
241 	printw("S-O-S makes you a score; obviously, the\n");
242 	printw("player with a higher score wins.");
243 	refresh();
244 	getch();
245 	erase();
246 }
main(int argc,char ** argv)247 int main(int argc, char** argv){
248 	int dpt=1;
249 	signal(SIGINT,sigint_handler);
250 #ifndef NO_VLA
251 	if(argc>4 || (argc==2 && !strcmp("help",argv[1])) ){
252 		printf("Usage: %s [len wid [AIpower]]\n",argv[0]);
253 		return EXIT_FAILURE;
254 	}
255 	if(argc==2){
256 		puts("Give both dimensions.");
257 		return EXIT_FAILURE;
258 	}
259 	if(argc>=3){
260 		bool lool = sscanf(argv[1],"%d",&len) && sscanf(argv[2],"%d",&wid);
261 		if(!lool){
262 			puts("Invalid input.");
263 			return EXIT_FAILURE;
264 		}
265 		if(len<3 || wid<3 || len>300 || wid>300){
266 			puts("At least one of your given dimensions is either too small or too big.");
267 			return EXIT_FAILURE;
268 		}
269 
270 	}
271 	else{
272 		len=5;
273 		wid=6;
274 	}
275 	if(argc==4){
276 		if( !sscanf(argv[3],"%d",&dpt)){
277 			puts("Invalid input.");
278 			return EXIT_FAILURE;
279 		}
280 		if( dpt<1 || dpt>= 127){
281 			puts("That should be between 1 and 127.");
282 			return EXIT_FAILURE;
283 		}
284 	}
285 #else
286 	if(argc>2 || (argc==2 && !strcmp("help",argv[1])) ){
287 		printf("Usage: %s [AIpower]\n",argv[0]);
288 		return EXIT_FAILURE;
289 	}
290 
291 	if(argc==2){
292 		if( !sscanf(argv[1],"%d",&dpt)){
293 			puts("Invalid input.");
294 			return EXIT_FAILURE;
295 		}
296 		if( dpt<1 || dpt>= 127){
297 			puts("That should be between 1 and 127.");
298 			return EXIT_FAILURE;
299 		}
300 	}
301 
302 #endif
303 	srand(time(NULL)%UINT_MAX);
304 	int input;
305 	initscr();
306 #ifndef NO_MOUSE
307 	mousemask(ALL_MOUSE_EVENTS,NULL);
308 #endif
309 	curs_set(0);
310 	noecho();
311 	cbreak();
312 	keypad(stdscr,1);
313 	printw("Black plays first.\n Choose the type of the blue player(H/c)\n" );
314 	refresh();
315 	input=getch();
316 	if(input=='c'){
317 		computer[0]=dpt;
318 		printw("Computer.\n");
319 	}
320 	else{
321 		computer[0]=0;
322 		printw("Human.\n");
323 	}
324 	refresh();
325 	printw("Choose the type of the yellow player(h/C)\n");
326 	refresh();
327 	input=getch();
328 	if(input=='h'){
329 		computer[1]=0;
330 		printw("Human.\n");
331 	}
332 	else{
333 		computer[1]=dpt;
334 		printw("Computer.\n");
335 	}
336 	if(has_colors()){
337 		start_color();
338 		use_default_colors();
339 		init_pair(1,COLOR_BLUE,-1);
340 		init_pair(2,COLOR_YELLOW,-1);
341 		init_pair(3,COLOR_GREEN,-1);
342 		for(byte b= 0;b<6;++b){
343 			colors[b]=COLOR_PAIR(b+1);
344 		}
345 
346 	}
347 	int sy,sx;
348 	Start:
349 	sy=sx=0;//for scrolling
350 	py=px=0;
351 	char board[len][wid];
352 	byte colored[len][wid];
353 	bool t=1;
354 	score[0]=score[1]=0;
355 	memset(board,0,len*wid);
356 	memset(colored,-1,len*wid);
357 	Turn:
358 	erase();
359 	mvprintw(sy+0,sx+0," _  _  _");
360 	mvprintw(sy+1,sx+0,"(_'| |(_'  %d vs %d \n",score[0],score[1]);
361 	mvprintw(sy+2,sx+0,"._):_:._) \n");
362 	draw(sy+3,sx+0,board,colored);
363 	if( isfilled(board) )
364 		goto End;
365 	refresh();
366 	t=!t;
367 	if(computer[t]){
368 		mvprintw(sy+len+5,sx+0,"Thinking...");
369 		refresh();
370 		decide(board,colored,dpt,t);
371 		goto Turn;
372 	}
373 	//else
374 	while(1){
375 		erase();
376        		mvprintw(sy+0,sx+0," _  _  _");
377 		mvprintw(sy+1,sx+0,"(_'| |(_'  %d vs %d \n",score[0],score[1]);
378 		mvprintw(sy+2,sx+0,"._):_:._) \n");
379 		draw(sy+3,sx+0,board,colored);
380 		refresh();
381 		input = getch();
382 		if( input==KEY_PPAGE && LINES< len+3){//the board starts in 3
383 			sy+=10;
384 			if(sy>0)
385 				sy=0;
386 		}
387 		if( input==KEY_NPAGE && LINES< len+3){
388 			sy-=10;
389 			if(sy< -(len+3) )
390 				sy=-(len+3);
391 		}
392 		if( input=='<' && COLS< wid*2+1){
393 			sx+=10;
394 			if(sx>0)
395 				sx=0;
396 		}
397 		if( input=='>' && COLS< wid*2+1){
398 			sx-=10;
399 			if(sx< -(wid*2+1))
400 				sx=-(wid*2+1);
401 		}
402 		if( input==KEY_F(1) || input=='?')
403 			help();
404 		if( input==KEY_F(2) )
405 			gameplay();
406 		if( input==KEY_MOUSE )
407 			mouseinput(sy,sx);
408 		if( (input=='k' || input==KEY_UP) && py>0)
409 			--py;
410 		if( (input=='j' || input==KEY_DOWN) && py<len-1)
411 			++py;
412 		if( (input=='h' || input==KEY_LEFT) && px>0)
413 			--px;
414 		if( (input=='l' || input==KEY_RIGHT) && px<wid-1)
415 			++px;
416 		if( input=='q')
417 			sigint_handler(0);
418 		if(!board[py][px] && (input=='s'||input=='S'||input=='o'||input=='O') ){
419 			if(input=='s'||input=='S')
420 				board[py][px]='S';
421 			else
422 				board[py][px]='O';
423 			score[t]+=did_sos(board,py,px);
424 			color_sos(board,colored,py,px,t);
425 			goto Turn;
426 		}
427 	}
428 	End:
429 	if( score[1] == score[0])
430 		mvprintw(sy+len+5,sx+0,"Draw!!");
431 	else
432 		mvprintw(sy+len+5,sx+0,"Player %d won the game!",(score[1]>score[0]) +1);
433 	printw(" Wanna play again?(y/n)");
434 	curs_set(1);
435 	flushinp();
436 	input=getch();
437 	curs_set(0);
438 	if(input != 'N' && input != 'n' && input!='q')
439 		goto Start;
440 	endwin();
441 	return EXIT_SUCCESS;
442 }
443