Othello-Reversi.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442
  1. // OTHELLO/REVERSI V1.6 (C) 2000 by Zeljko Juric
  2. // You need the TIGCC cross-compiler to compile it
  3. // Only use one of these at a time if you want to save space and increase speed
  4. #define USE_TI89
  5. #define USE_TI92PLUS
  6. #define USE_V200
  7. #define MIN_AMS 100
  8. #include <string.h>
  9. #include <setjmp.h>
  10. #include <graph.h>
  11. #include <kbd.h>
  12. #include <compat.h>
  13. // Key codes
  14. #define ESC_KEY 264 // Key codes
  15. #define ENTER_KEY 13
  16. // Macro for selecting right constants for both TI-89 and TI-92 Plus
  17. #define Q89_92(x,y) (TI89?(x):(y))
  18. // Type definitinos
  19. typedef enum Bool BOOL;
  20. typedef enum {WHITE,BLACK,BLANK_NEAR,BLANK_FAR} FIGURE;
  21. typedef char BOARD[10][10];
  22. // Forward declarations
  23. void draw_figure(int,int,char);
  24. void draw_str(int,int,const char*);
  25. void draw_line(int,int,int,int);
  26. void display_board(void);
  27. void one_player_game(void);
  28. void two_players_game(void);
  29. void input_move(int*,int*);
  30. int try_a_move(int,int,char);
  31. int minmax_search(char,int,int,int*,int*);
  32. int getch(void);
  33. int convert_key(int);
  34. BOOL human_move(char);
  35. BOOL computer_move(void);
  36. // Global and static data must have initializers in "nostub" mode.
  37. BOARD board={};
  38. int level=0;
  39. unsigned int rand_seed=0;
  40. char jmp_buffer[56]={};
  41. BOOL black_on_turn=FALSE,two_players=FALSE,escaped=FALSE;
  42. BOOL black_played=TRUE,white_played=TRUE;
  43. // Macro for randomly selecting TRUE or FALSE (this is a very bad algorithm,
  44. // but good enough for a purpose in this game)
  45. #define random ((rand_seed=75*rand_seed+1)>=32768)
  46. // Main program
  47. void _main(void)
  48. {
  49. SCR_RECT screen_area={{0,0,Q89_92(159,239),Q89_92(99,127)}};
  50. BOOL restore=FALSE;
  51. int i,j,score=0,answer=0;
  52. const char *level_names[]={"NOVICE","ELEMENTARY","MEDIUM","GOOD","ADVANCED",
  53. "EXPERT"};
  54. LCD_BUFFER scr_buf;
  55. BOARD board_save;
  56. LCD_save(scr_buf);
  57. if(!setjmp(jmp_buffer))
  58. {
  59. if(escaped)
  60. {
  61. display_board();
  62. draw_str(5,Q89_92(90,118),"RESTORE A BREAKED GAME (Y/N)?");
  63. while(answer!='y'&&answer!='n')
  64. answer=getch()|32;
  65. restore=(answer=='y');
  66. }
  67. escaped=FALSE;
  68. if(!restore)
  69. {
  70. black_on_turn=FALSE; black_played=TRUE; white_played=TRUE;
  71. for(i=0;i<=9;i++)
  72. for(j=0;j<=9;j++)
  73. board[i][j]=BLANK_FAR;
  74. for(i=3;i<=6;i++)
  75. for(j=3;j<=6;j++)
  76. board[i][j]=BLANK_NEAR;
  77. board[5][4]=board[4][5]=BLACK;
  78. board[4][4]=board[5][5]=WHITE;
  79. display_board();
  80. if(TI89)
  81. FontSetSys(F_6x8);
  82. draw_str(Q89_92(5,120),90,"[1] or [2] players?");
  83. while(answer<'1'||answer>'2')
  84. answer=getch();
  85. if(!(two_players=answer=='2'))
  86. {
  87. display_board();
  88. if(TI89)
  89. FontSetSys(F_6x8);
  90. draw_str(Q89_92(5,130),90,"Level (1-6)?");
  91. answer=0;
  92. while(answer<'1'||answer>'6')
  93. answer=getch();
  94. level=answer-'0';
  95. display_board();
  96. if(!TI89)
  97. {
  98. draw_str(130,90,"Level: ");
  99. draw_str(172,90,level_names[level-1]);
  100. }
  101. draw_str(5,Q89_92(90,118),
  102. "DO YOU WANT TO HAVE A FIRST MOVE (Y/N)?");
  103. while(answer!='y'&&answer!='n')
  104. answer=getch()|32;
  105. black_on_turn=(answer=='y');
  106. }
  107. }
  108. display_board();
  109. memcpy(board_save,board,sizeof(BOARD));
  110. escaped=TRUE;
  111. while(black_played||white_played)
  112. {
  113. if(TI89)
  114. draw_str(5,90,"Made using TI-GCC compiler by Xavier Vassor");
  115. else
  116. {
  117. FontSetSys(F_4x6);
  118. draw_str(5,118,
  119. "Compiled using TI-GCC compiler by Xavier Vassor & Jean Canazzi");
  120. FontSetSys(F_6x8);
  121. }
  122. if(two_players)
  123. draw_str(Q89_92(93,130),Q89_92(70,90),"TWO-PLAYER MODE");
  124. else
  125. {
  126. draw_str(Q89_92(93,130),Q89_92(70,90),"Level: ");
  127. draw_str(Q89_92(117,172),Q89_92(70,90),level_names[level-1]);
  128. }
  129. if(black_on_turn)
  130. black_played=human_move(BLACK);
  131. else if(two_players)
  132. white_played=human_move(WHITE);
  133. else
  134. white_played=computer_move();
  135. display_board();
  136. memcpy(board_save,board,sizeof(BOARD));
  137. black_on_turn=!black_on_turn;
  138. }
  139. escaped=FALSE;
  140. FontSetSys(Q89_92(F_6x8,F_8x10));
  141. draw_str(Q89_92(93,130),Q89_92(42,50),"Game over!");
  142. for(i=1;i<=8;i++)
  143. for(j=1;j<=8;j++)
  144. score+=(board[i][j]==WHITE)-(board[i][j]==BLACK);
  145. FontSetSys(Q89_92(F_8x10-two_players,F_8x10));
  146. draw_str(Q89_92(93,130),Q89_92(55,65),
  147. score?((score>0)?(two_players?"White won!":"I won!"):
  148. (two_players?"Black won!":"You won!")):"Draw!");
  149. getch();
  150. }
  151. for(i=1;i<Q89_92(100,140);i++)
  152. DrawClipEllipse(Q89_92(80,120),Q89_92(50,64),i,i,&screen_area,A_NORMAL);
  153. LCD_restore(scr_buf);
  154. memcpy(board,board_save,sizeof(BOARD));
  155. }
  156. // Tries to perform a move, and returns an advance in the material plus
  157. // extra bonus for corners (returns zero if the move is not possible)
  158. int try_a_move(int i,int j,char figure)
  159. {
  160. int captured,loc_i,loc_j,i_step,j_step,p,q,score=0;
  161. char next;
  162. if(board[i][j]==BLANK_NEAR)
  163. {
  164. for(i_step=-1;i_step<=1;i_step++)
  165. for(j_step=-1;j_step<=1;j_step++)
  166. if(i_step||j_step)
  167. {
  168. loc_i=i; loc_j=j; captured=0;
  169. do
  170. {
  171. next=board[loc_i+=i_step][loc_j+=j_step];
  172. if(next>=BLANK_NEAR)
  173. captured=0;
  174. else if(next!=figure)
  175. captured++;
  176. } while(next==!figure);
  177. if(captured)
  178. {
  179. loc_i=i; loc_j=j;
  180. while(board[loc_i+=i_step][loc_j+=j_step]!=figure)
  181. board[loc_i][loc_j]=figure;
  182. score+=captured;
  183. }
  184. }
  185. if(score)
  186. {
  187. board[i][j]=figure;
  188. for(p=i-1;p<=i+1;p++)
  189. for(q=j-1;q<=j+1;q++)
  190. if(board[p][q]==BLANK_FAR)
  191. board[p][q]=BLANK_NEAR;
  192. if((i==1||i==8)&&(j==1||j==8))
  193. score+=3;
  194. score+=!score;
  195. }
  196. }
  197. return score;
  198. }
  199. // Performs a human move, and returns FALSE if there is no moves avaliable
  200. BOOL human_move(char figure)
  201. {
  202. int i,j,score,char1,char2;
  203. BOARD board_save;
  204. memcpy(board_save,board,sizeof(BOARD));
  205. if(two_players)
  206. draw_str(Q89_92(93,130),Q89_92(34,46),
  207. black_on_turn?">> BLACK <<":">> WHITE <<");
  208. for(i=1;i<=8;i++)
  209. for(j=1;j<=8;j++)
  210. if(try_a_move(i,j,figure))
  211. {
  212. memcpy(board,board_save,sizeof(BOARD));
  213. do
  214. {
  215. input_move(&char1,&char2);
  216. if(!(score=try_a_move(char2-'0',char1-'A'+1,figure)))
  217. draw_str(Q89_92(93,130),Q89_92(55,68),"WRONG MOVE!");
  218. } while(!score);
  219. return TRUE;
  220. }
  221. draw_str(Q89_92(93,130),Q89_92(42,56),"You must pass!");
  222. getch();
  223. return FALSE;
  224. }
  225. // Recursive minmax search for a best move with alpha pruning: returns best
  226. // minmax value, and as a side effect, returns coordinates of such best move
  227. int minmax_search(char figure,int depth,int alpha,int *best_i,int *best_j)
  228. {
  229. int i,j,max=-1000,score,dummy;
  230. BOARD board_save;
  231. *best_i=0; *best_j=0;
  232. memcpy(board_save,board,sizeof(BOARD));
  233. if(kbhit())
  234. if(ngetchx()==ESC_KEY)
  235. longjmp(jmp_buffer,1);
  236. for(i=1;i<=8;i++)
  237. for(j=1;j<=8;j++)
  238. if((score=try_a_move(i,j,figure))) // Yes, "=", not "==" !
  239. {
  240. if(depth!=level)
  241. score-=minmax_search(!figure,depth+1,score-max,&dummy,&dummy);
  242. if(level==1)
  243. score+=5*((i==1||i==8)+(j==1||j==8)-(i==2||i==7)-(j==2||j==7));
  244. if(score>max)
  245. {
  246. max=score; *best_i=i; *best_j=j;
  247. }
  248. else if((score==max)&&random)
  249. {
  250. *best_i=i; *best_j=j;
  251. }
  252. memcpy(board,board_save,sizeof(BOARD));
  253. if(score>=alpha)
  254. return max;
  255. }
  256. if(!*best_i)
  257. return 0;
  258. return max;
  259. }
  260. // Performs computer move, and returns FALSE if there is no avaliable moves
  261. BOOL computer_move(void)
  262. {
  263. int best_i,best_j;
  264. char str[50];
  265. draw_str(Q89_92(93,130),Q89_92(42,56),"Thinking...");
  266. minmax_search(WHITE,1,1000,&best_i,&best_j);
  267. if(!best_i)
  268. draw_str(Q89_92(93,130),Q89_92(50,66),"I must pass!");
  269. else
  270. {
  271. try_a_move(best_i,best_j,WHITE);
  272. sprintf(str,"My move: %c%d",best_j-1+'A',best_i);
  273. draw_str(Q89_92(93,130),Q89_92(50,68),str);
  274. }
  275. getch();
  276. return best_i!=0;
  277. }
  278. // Asks the user for a move, and put its coordinates in char1 and char2
  279. void input_move(int *char1,int *char2)
  280. {
  281. char tmp[]=" _";
  282. int key=0;
  283. while(key!=ENTER_KEY)
  284. {
  285. draw_str(Q89_92(93,130),Q89_92(42,56),"Your move: _ ");
  286. *char1=key&0xffdf;
  287. while(*char1<'A'||*char1>'H')
  288. *char1=Q89_92(convert_key(getch()),getch())&0xffdf;
  289. *tmp=*char1;
  290. draw_str(Q89_92(133,196),Q89_92(42,56),tmp);
  291. *char2=0;
  292. while(*char2<'1'||*char2>'8')
  293. *char2=getch();
  294. *tmp=*char2;
  295. draw_str(Q89_92(133+FontCharWidth(*char1),202),Q89_92(42,56),tmp);
  296. key=getch();
  297. }
  298. }
  299. // Routine for getting a key, with handling ESC keypress
  300. int getch(void)
  301. {
  302. int i;
  303. if ((i=ngetchx())!=ESC_KEY)
  304. return i;
  305. else
  306. longjmp(jmp_buffer,1);
  307. return 0;
  308. }
  309. #ifdef USE_TI89
  310. // Converts non-alpha keys into alpha ones on TI-89 (for easier typing)
  311. // '='->'A' '('->'B' ')'->'C' ','->'D' '/'->'E' '|'-> 'F' '7'->'G' '8'->'H'
  312. int convert_key(int key)
  313. {
  314. int i;
  315. for(i=0;i<8;i++)
  316. if(key=="=(),/|78"[i])
  317. return 'A'+i;
  318. return key;
  319. }
  320. #endif
  321. // Displays the board
  322. void display_board(void)
  323. {
  324. int i,j,black_score=0,white_score=0;
  325. char xs[]="A",ys[]="1";
  326. char str[50];
  327. ClrScr();
  328. FontSetSys(Q89_92(F_4x6,F_6x8));
  329. draw_str(5,1,"OTHELLO/REVERSI V1.5 (C) Zeljko Juric");
  330. for(i=Q89_92(16,23);i<=Q89_92(80,111);i+=Q89_92(8,11))
  331. {
  332. draw_line(Q89_92(16,23),Q89_92(i+2,i),Q89_92(80,111),Q89_92(i+2,i));
  333. draw_line(i,Q89_92(18,23),i,Q89_92(82,111));
  334. if(i!=Q89_92(80,111))
  335. {
  336. draw_str(i+Q89_92(2,3),Q89_92(12,14),xs); (*xs)++;
  337. draw_str(Q89_92(10,14),i+Q89_92(4,3),ys); (*ys)++;
  338. }
  339. }
  340. for(i=1;i<=8;i++)
  341. for(j=1;j<=8;j++)
  342. {
  343. draw_figure(Q89_92(8*j+10,11*j+14),Q89_92(8*i+12,11*i+14),
  344. board[i][j]);
  345. black_score+=(board[i][j]==BLACK);
  346. white_score+=(board[i][j]==WHITE);
  347. }
  348. sprintf(str,two_players?"White: %d Black: %d":"Me: %d You: %d",
  349. white_score,black_score);
  350. draw_str(Q89_92(93,130-11*two_players),Q89_92(20,30),str);
  351. }
  352. // Draws a figure on screen
  353. void draw_figure(int x,int y,char c)
  354. {
  355. int i;
  356. if(c==WHITE||c==BLACK)
  357. {
  358. draw_line(x+Q89_92(1,2),y,x+Q89_92(3,5),y);
  359. draw_line(x+Q89_92(1,2),y+Q89_92(4,7),x+Q89_92(3,5),y+Q89_92(4,7));
  360. }
  361. if(c==WHITE)
  362. {
  363. if(TI89)
  364. {
  365. draw_line(x,y+1,x,y+3); draw_line(x+4,y+1,x+4,y+3);
  366. }
  367. else
  368. {
  369. draw_line(x,y+2,x+2,y); draw_line(x+5,y,x+7,y+2);
  370. draw_line(x,y+2,x,y+5); draw_line(x+7,y+2,x+7,y+5);
  371. draw_line(x,y+5,x+2,y+7); draw_line(x+5,y+7,x+7,y+5);
  372. }
  373. }
  374. else if(c==BLACK)
  375. {
  376. for(i=y+Q89_92(1,2);i<=y+Q89_92(3,5);i++)
  377. draw_line(x,i,x+Q89_92(4,7),i);
  378. if(!TI89)
  379. draw_line(x+1,y+1,x+6,y+1); draw_line(x+1,y+6,x+6,y+6);
  380. }
  381. }
  382. // Drawing lines and strings are used so often in this game, so defining them
  383. // as a short subroutines which just calls the ROM equivalents makes the whole
  384. // program a bit shorter
  385. void draw_str(int x,int y,const char *str)
  386. {
  387. DrawStr(x,y,str,A_REPLACE);
  388. }
  389. void draw_line(int x1,int y1,int x2,int y2)
  390. {
  391. DrawLine(x1,y1,x2,y2,A_NORMAL);
  392. }
  393. // Please mail any comments, suggestions, questions and bug reports to:
  394. // zjuric@utic.net.ba
  395. // Zeljko Juric
  396. // Sarajevo
  397. // Bosnia & Herzegovina