reversi.c 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  1. // Game "Reversi" for Flipper Zero
  2. // Copyright 2023 Dmitry Matyukhin
  3. #include "reversi.h"
  4. // Psst! Most of this file was written with Copilot
  5. const int weights[BOARD_SIZE][BOARD_SIZE] = {
  6. {100, -10, 10, 10, 10, 10, -10, 100},
  7. {-10, -20, -5, -5, -5, -5, -20, -10},
  8. {10, -5, 5, 1, 1, 5, -5, 10},
  9. {10, -5, 1, 0, 0, 1, -5, 10},
  10. {10, -5, 1, 0, 0, 1, -5, 10},
  11. {10, -5, 5, 1, 1, 5, -5, 10},
  12. {-10, -20, -5, -5, -5, -5, -20, -10},
  13. {100, -10, 10, 10, 10, 10, -10, 100}};
  14. // Check if the move is legal by checking if it results in any opponent pieces being captured
  15. bool is_legal_move(int8_t board[BOARD_SIZE][BOARD_SIZE], int row, int col, int player) {
  16. if(board[row][col] != 0) return false;
  17. int opponent = -player;
  18. for(int i = -1; i <= 1; i++) {
  19. for(int j = -1; j <= 1; j++) {
  20. if(i == 0 && j == 0) continue;
  21. int r = row + i, c = col + j;
  22. if(r >= 0 && r < BOARD_SIZE && c >= 0 && c < BOARD_SIZE && board[r][c] == opponent) {
  23. int k = 2;
  24. while(true) {
  25. r += i;
  26. c += j;
  27. if(r < 0 || r >= BOARD_SIZE || c < 0 || c >= BOARD_SIZE) break;
  28. if(board[r][c] == player) return true;
  29. if(board[r][c] == 0) break;
  30. k++;
  31. }
  32. }
  33. }
  34. }
  35. return false;
  36. }
  37. // Check if the game is over by checking if there are no more moves left for
  38. // either player
  39. bool is_game_over(int8_t board[BOARD_SIZE][BOARD_SIZE]) {
  40. for(int i = 0; i < BOARD_SIZE; i++) {
  41. for(int j = 0; j < BOARD_SIZE; j++) {
  42. if(is_legal_move(board, i, j, BLACK) || is_legal_move(board, i, j, WHITE)) {
  43. return false;
  44. }
  45. }
  46. }
  47. return true;
  48. }
  49. bool has_legal_moves(int8_t board[BOARD_SIZE][BOARD_SIZE], int8_t player_color) {
  50. for(int i = 0; i < BOARD_SIZE; i++) {
  51. for(int j = 0; j < BOARD_SIZE; j++) {
  52. if(is_legal_move(board, i, j, player_color)) {
  53. return true;
  54. }
  55. }
  56. }
  57. return false;
  58. }
  59. int evaluate_board(int8_t board[BOARD_SIZE][BOARD_SIZE], int player) {
  60. int score = 0;
  61. for(int i = 0; i < BOARD_SIZE; i++) {
  62. for(int j = 0; j < BOARD_SIZE; j++) {
  63. if(board[i][j] == player) {
  64. score += weights[i][j];
  65. } else if(board[i][j] == -player) {
  66. score -= weights[i][j];
  67. }
  68. }
  69. }
  70. return score;
  71. }
  72. // Make a move on the board and capture any opponent pieces
  73. void make_move(
  74. GameState* game_state,
  75. int8_t board[BOARD_SIZE][BOARD_SIZE],
  76. int x,
  77. int y,
  78. int player) {
  79. board[x][y] = player;
  80. int opponent = -player;
  81. for(int i = -1; i <= 1; i++) {
  82. for(int j = -1; j <= 1; j++) {
  83. if(i == 0 && j == 0) continue;
  84. int r = x + i, c = y + j;
  85. if(r >= 0 && r < BOARD_SIZE && c >= 0 && c < BOARD_SIZE && board[r][c] == opponent) {
  86. int k = 2;
  87. while(true) {
  88. r += i;
  89. c += j;
  90. if(r < 0 || r >= BOARD_SIZE || c < 0 || c >= BOARD_SIZE) break;
  91. if(board[r][c] == player) {
  92. r -= i;
  93. c -= j;
  94. while(r != x || c != y) {
  95. board[r][c] = player;
  96. r -= i;
  97. c -= j;
  98. }
  99. break;
  100. }
  101. if(board[r][c] == 0) break;
  102. k++;
  103. }
  104. }
  105. }
  106. }
  107. game_state->is_game_over = is_game_over(game_state->board);
  108. }
  109. void init_game(GameState* state) {
  110. for(int i = 0; i < BOARD_SIZE; i++) {
  111. for(int j = 0; j < BOARD_SIZE; j++) {
  112. state->board[i][j] = 0;
  113. }
  114. }
  115. // Place the initial pieces
  116. int mid = BOARD_SIZE / 2;
  117. state->board[mid - 1][mid - 1] = WHITE;
  118. state->board[mid][mid] = WHITE;
  119. state->board[mid - 1][mid] = BLACK;
  120. state->board[mid][mid - 1] = BLACK;
  121. state->cursor_x = mid - 1;
  122. state->cursor_y = mid + 1;
  123. // Set up turn order
  124. state->human_color = WHITE;
  125. state->current_player = WHITE;
  126. state->is_game_over = false;
  127. }
  128. void human_move(GameState* game_state) {
  129. if(game_state->current_player != game_state->human_color) {
  130. return;
  131. }
  132. if(is_legal_move(
  133. game_state->board,
  134. game_state->cursor_x,
  135. game_state->cursor_y,
  136. game_state->current_player)) {
  137. make_move(
  138. game_state,
  139. game_state->board,
  140. game_state->cursor_x,
  141. game_state->cursor_y,
  142. game_state->current_player);
  143. game_state->current_player = -game_state->current_player;
  144. }
  145. }
  146. int minimax(
  147. GameState* game_state,
  148. int8_t board[BOARD_SIZE][BOARD_SIZE],
  149. int depth,
  150. bool is_maximizing,
  151. int player,
  152. int alpha,
  153. int beta) {
  154. if(depth == 0 || is_game_over(board)) {
  155. return evaluate_board(board, player);
  156. }
  157. if(is_maximizing) {
  158. int max_eval = -1000000;
  159. for(int i = 0; i < BOARD_SIZE; i++) {
  160. for(int j = 0; j < BOARD_SIZE; j++) {
  161. if(is_legal_move(board, i, j, player)) {
  162. int8_t temp_board[BOARD_SIZE][BOARD_SIZE];
  163. memcpy(temp_board, board, sizeof(temp_board));
  164. make_move(game_state, temp_board, i, j, player);
  165. int eval =
  166. minimax(game_state, temp_board, depth - 1, false, -player, alpha, beta);
  167. max_eval = max(max_eval, eval);
  168. alpha = max(alpha, eval);
  169. if(beta <= alpha) {
  170. break;
  171. }
  172. }
  173. }
  174. }
  175. return max_eval;
  176. } else {
  177. int min_eval = 1000000;
  178. for(int i = 0; i < BOARD_SIZE; i++) {
  179. for(int j = 0; j < BOARD_SIZE; j++) {
  180. if(is_legal_move(board, i, j, -player)) {
  181. int8_t temp_board[BOARD_SIZE][BOARD_SIZE];
  182. memcpy(temp_board, board, sizeof(temp_board));
  183. make_move(game_state, temp_board, i, j, -player);
  184. int eval =
  185. minimax(game_state, temp_board, depth - 1, true, player, alpha, beta);
  186. min_eval = min(min_eval, eval);
  187. beta = min(beta, eval);
  188. if(beta <= alpha) {
  189. break;
  190. }
  191. }
  192. }
  193. }
  194. return min_eval;
  195. }
  196. }
  197. void computer_move(GameState* game_state) {
  198. if(game_state->current_player == game_state->human_color) {
  199. return;
  200. }
  201. int best_row = -1, best_col = -1, best_score = -1000000;
  202. for(int i = 0; i < BOARD_SIZE; i++) {
  203. for(int j = 0; j < BOARD_SIZE; j++) {
  204. if(is_legal_move(game_state->board, i, j, game_state->current_player)) {
  205. int8_t temp_board[BOARD_SIZE][BOARD_SIZE];
  206. memcpy(temp_board, game_state->board, sizeof(temp_board));
  207. make_move(game_state, temp_board, i, j, game_state->current_player);
  208. int score = minimax(
  209. game_state,
  210. temp_board,
  211. 3,
  212. false,
  213. -game_state->current_player,
  214. -1000000,
  215. 1000000);
  216. if(score > best_score) {
  217. best_score = score;
  218. best_row = i;
  219. best_col = j;
  220. }
  221. }
  222. }
  223. }
  224. if(best_row != -1) {
  225. make_move(game_state, game_state->board, best_row, best_col, game_state->current_player);
  226. }
  227. if(has_legal_moves(game_state->board, game_state->human_color)) {
  228. game_state->current_player = -game_state->current_player;
  229. }
  230. }