commit be1fb705d3a882cb48e650355d4cba5352c45604
parent 7d8009aecfd298c86419f3e99249f9f08bc90e83
Author: Wilson Gheen <wilson@wilsonrgheen.com>
Date: Sun, 18 Dec 2022 06:14:40 -0600
Fix up get_winner...() to detect moves that win for either player; add "AI" that blocks if it can't win this turn
Diffstat:
3 files changed, 49 insertions(+), 11 deletions(-)
diff --git a/tictactoe.c b/tictactoe.c
@@ -6,20 +6,20 @@
#include "../life/print_box.h"
#include "tictactoe.h"
-#define SIDE_BY_SIDE 0
+#define SIDE_BY_SIDE 1
static void print_board_offset(char *board, int offx, int offy, int clear) {
+#ifndef NO_PRINT_BOARD
if(clear) tb_clear();
print_box_offset(7, 7, offx, offy, 0);
for(int i=0; i < 9; i++) {
print_bw(((i%3) * 2) + 2 + offx, (i/3*2) + 2 + offy, board[i]);
}
tb_present();
+#endif
}
static void print_board(char *board) {
-#ifndef UNIT_TEST
print_board_offset(board, 0, 0, 1);
-#endif
}
char play(char (*get_move)(char*, char)) {
diff --git a/tictactoe.h b/tictactoe.h
@@ -11,6 +11,7 @@
#define O_WINS_ROW " XXXOXOOO"
#define scase(s1, s2) if (!strcmp(s1, s2))
+/*#define NO_PRINT_BOARD*/
/*typedef void (*board_fn)(char *, char);*/
static char the_desired_board[32];
@@ -26,6 +27,7 @@ static int get_rand(int max_excl) {
char get_winner_or_winning_moves(char *b, char *winning_moves) {
char center = b[4];
_Bool add_to_wm = 0;
+ /* Check status of squares on either side of center: row, column and diagonal */
for(int i=-4; i < 0; i++) {
int sq1 = 4+i;
int sq3 = 4-i;
@@ -37,18 +39,21 @@ char get_winner_or_winning_moves(char *b, char *winning_moves) {
}
if(winning_moves == NULL) continue;
if(add_to_wm) {
- winning_moves[i] = b[sq1];
+ /* Normally means `w...[4] = b[s...]`, but allows us
+ * to detect later on if it is a winning move for both */
+ winning_moves[4] |= b[sq1];
add_to_wm = 0;
}
else if(center == ' ') continue;
else if((b[sq1] == center) ^ (b[sq3] == center)) {
if(b[sq1] == ' ')
- winning_moves[sq1] = center;
+ winning_moves[sq1] |= center;
else if(b[sq3] == ' ')
- winning_moves[sq3] = center;
+ winning_moves[sq3] |= center;
}
}
for(int i=1; i <= 7; i+=2){
+ /* i&4 -> side column, else upper or lower row */
int distance = ((i+1) & 4) ? 3 : 1;
int sq1 = i - distance;
int sq3 = i + distance;
@@ -60,15 +65,15 @@ char get_winner_or_winning_moves(char *b, char *winning_moves) {
}
if(winning_moves == NULL) continue;
if(add_to_wm) {
- winning_moves[i] = b[sq1];
+ winning_moves[i] |= b[sq1];
add_to_wm = 0;
}
else if(b[i] == ' ') continue;
else if((b[sq1] == b[i]) ^ (b[sq3] == b[i])) {
if(b[sq1] == ' ')
- winning_moves[sq1] = b[i];
+ winning_moves[sq1] |= b[i];
else if(b[sq3] == ' ')
- winning_moves[sq3] = b[i];
+ winning_moves[sq3] |= b[i];
}
}
return 0;
@@ -101,6 +106,13 @@ char get_move_preset_board(char *board, char player) {
}
return 0;
}
+char get_these_moves(char *board, char player) {
+ /* Automate any specific sequence of moves, for debugging */
+ static int moves[9] = {1, 2, 5, 6, 7,};
+ static int m = 0;
+ board[moves[m++]] = player;
+ return 0;
+}
char get_move_from_human(char *board, char player) {
int n;
print_msg("%c's move? [1-9]: ", player);
@@ -118,18 +130,43 @@ char get_random_move(char *board, char player) {
board[legal_moves[get_rand(lm_ind)]] = player;
return get_winner_or_winning_moves(board, NULL);
}
-char get_winning_move_else_random(char *board, char player) {
+static char try_get_winning_move(char *board, char player) {
char winning_moves[9] = "";
int my_winning_moves[5];
int mwm_ind = 0;
get_winner_or_winning_moves(board, winning_moves);
for(int i=0; i < 9; i++)
- if(winning_moves[i] == player)
+ if((winning_moves[i] & player) == player)
my_winning_moves[mwm_ind++] = i;
if(mwm_ind) {
board[my_winning_moves[get_rand(mwm_ind)]] = player;
return player;
}
+ return 0;
+}
+char get_winning_move_else_random(char *board, char player) {
+ char winner = try_get_winning_move(board, player);
+ if(winner)
+ return winner;
+ else
+ return get_random_move(board, player);
+}
+char get_winning_move_else_block_else_random(char *board, char player) {
+ char winner = try_get_winning_move(board, player);
+ if(winner) return winner;
+ char winning_moves[9] = "";
+ int his_winning_moves[5];
+ int hwm_ind = 0;
+ char opponent = (player == 'O' ? 'X' : 'O');
+ get_winner_or_winning_moves(board, winning_moves);
+ for(int i=0; i < 9; i++)
+ if(winning_moves[i] == opponent)
+ his_winning_moves[hwm_ind++] = i;
+ if(hwm_ind) {
+ board[his_winning_moves[get_rand(hwm_ind)]] = player;
+ /*N.B. This will not be reached if the move is a winning move for both players */
+ return 0;
+ }
else
return get_random_move(board, player);
}
diff --git a/unit_test.c b/unit_test.c
@@ -1,5 +1,6 @@
#include <stdio.h>
#define UNIT_TEST
+#define NO_PRINT_BOARD
#include "tictactoe.c"