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