commit b3d3893f89067fa04f2a6c7a35cd2a75fe63f566
parent 1ab6db826eac5460a6a030aa062c16410c9bf4c5
Author: Wilson Gheen <wilson@wilsonrgheen.com>
Date: Sat, 17 Dec 2022 07:23:33 -0600
Add unit testing and begin to implement finding near-wins
Diffstat:
M | tictactoe.c | | | 72 | ++++++++++++++++++++++++++++++++++++++++++++++++++---------------------- |
M | tictactoe.h | | | 25 | +++++++++++++++++++++++-- |
A | unit_test.c | | | 46 | ++++++++++++++++++++++++++++++++++++++++++++++ |
3 files changed, 119 insertions(+), 24 deletions(-)
diff --git a/tictactoe.c b/tictactoe.c
@@ -8,7 +8,7 @@
#define SIDE_BY_SIDE 0
-void print_board_offset(char *board, int offx, int offy, int clear) {
+static void print_board_offset(char *board, int offx, int offy, int clear) {
if(clear) tb_clear();
print_box_offset(7, 7, offx, offy, 0);
for(int i=0; i < 9; i++) {
@@ -16,17 +16,52 @@ void print_board_offset(char *board, int offx, int offy, int clear) {
}
tb_present();
}
-void print_board(char *board) {
+static void print_board(char *board) {
+#ifndef UNIT_TEST
print_board_offset(board, 0, 0, 1);
+#endif
}
-int main(void) {
+static void get_near_wins(char *b, char *winner) {
+ char winning_moves[9];
+ char center = b[4];
+ for(int i=-4; center != ' ' && i < 0; i++) {
+ int s2 = 4+i;
+ int s3 = 4-i;
+ if(b[s2] == center && b[s3] == center) {
+ *winner = center;
+ return;
+ }
+ if(b[s2] == center || b[s3] == center) {
+ if(b[s2] == ' ')
+ winning_moves[s2] = center;
+ else if(b[s3] == ' ')
+ winning_moves[s3] = center;
+ }
+ }
+ for(int i=1; i <= 7; i+=2){
+ if(b[i] == ' ') continue;
+ int distance = ((i+1) & 4) ? 3 : 1;
+ int s2 = i - distance;
+ int s3 = i + distance;
+ if(b[s2] == b[i] && b[s3] == b[i]) {
+ *winner = b[i];
+ return;
+ }
+ if(b[s2] == b[i] || b[s3] == b[i]) {
+ if(b[s2] == ' ')
+ winning_moves[s2] = b[i];
+ else if(b[s3] == ' ')
+ winning_moves[s3] = b[i];
+ }
+ }
+}
+
+char play(void (*get_move)(char*, char)) {
struct tb_event ev;
char board[9] = " ";
char winner = 0;
char last_played = 'O';
- void (*get_move)(char*, char) = get_move_from_human;
- tb_init();
if(!SIDE_BY_SIDE)
print_board(board);
for(int turns=0; !winner && turns <= 9; turns++) {
@@ -36,24 +71,16 @@ int main(void) {
else
print_board(board);
tb_peek_event(&ev, 300);
- char center = board[4];
- for(int i=-4; center != ' ' && i < 0; i++) {
- if(board[4+i] == center && board[4-i] == center) {
- winner = center;
- break;
- }
- }
- for(int i=1; !winner && i <= 7; i+=2){
- if(board[i] == ' ') continue;
- if((i+1) & 4) { /* column */
- if(board[i-3] == board[i] && board[i+3] == board[i])
- winner = board[i];
- } else { /* row */
- if(board[i-1] == board[i] && board[i+1] == board[i])
- winner = board[i];
- }
- }
+ get_near_wins(board, &winner);
}
+ return winner;
+}
+
+#ifndef UNIT_TEST
+int main(void) {
+ struct tb_event ev;
+ tb_init();
+ char winner = play(get_random_move);
if(winner)
print_msg("%c wins.", winner);
else
@@ -63,3 +90,4 @@ int main(void) {
if (ev.type == TB_EVENT_KEY && ev.key == TB_KEY_CTRL_C)
tb_shutdown();
}
+#endif
diff --git a/tictactoe.h b/tictactoe.h
@@ -1,4 +1,4 @@
-int m = 0;
+#include <string.h>
#define SCRATCH_BOARD "XXOOOXXOX"
#define X_WINS_COLUMN "XXOXOOXOX"
@@ -8,8 +8,13 @@ int m = 0;
#define O_WINS_DIAG "OXXOOXX O"
#define O_WINS_ROW " XXXOXOOO"
+#define scase(s1, s2) if (!strcmp(s1, s2))
+
+/*typedef void (*board_fn)(char *, char);*/
+static char the_desired_board[32];
+
void get_move_preset_board(char *board, char player) {
- char desired_board[9] = SCRATCH_BOARD;
+ char *desired_board = the_desired_board;
for(int i=0; i < 9; i++) {
if(desired_board[i] == player && board[i] == ' ') {
board[i] = player;
@@ -17,6 +22,22 @@ void get_move_preset_board(char *board, char player) {
}
}
}
+void set_board(char *board_name) {
+ scase(board_name, "scratch_board")
+ strcpy(the_desired_board, SCRATCH_BOARD);
+ scase(board_name, "x_wins_column")
+ strcpy(the_desired_board, X_WINS_COLUMN);
+ scase(board_name, "x_wins_diag")
+ strcpy(the_desired_board, X_WINS_DIAG);
+ scase(board_name, "x_wins_row")
+ strcpy(the_desired_board, X_WINS_ROW);
+ scase(board_name, "o_wins_column")
+ strcpy(the_desired_board, O_WINS_COLUMN);
+ scase(board_name, "o_wins_diag")
+ strcpy(the_desired_board, O_WINS_DIAG);
+ scase(board_name, "o_wins_row")
+ strcpy(the_desired_board, O_WINS_ROW);
+}
void get_move_from_human(char *board, char player) {
int n;
diff --git a/unit_test.c b/unit_test.c
@@ -0,0 +1,46 @@
+#include <stdio.h>
+#define UNIT_TEST
+
+#include "tictactoe.c"
+
+struct test_board { char *name; char *expected_result; };
+
+struct test_board test_boards[] = {
+ {"scratch_board", "SCRATCH"},
+ {"x_wins_column", "X WINS" },
+ {"x_wins_diag", "X WINS" },
+ {"x_wins_row", "X WINS" },
+ {"o_wins_column", "O WINS" },
+ {"o_wins_diag", "O WINS" },
+ {"o_wins_row", "O WINS" },
+};
+
+int main() {
+ int failures = 0;
+ int len = sizeof test_boards / sizeof test_boards[0];
+ int i = 0;
+ while(i < len) {
+ struct test_board *b = test_boards + (i++);
+ char actual_result[8];
+ printf("TEST %d\n", i);
+ puts( "--------");
+ printf("Testing %s -- expecting '%s' ...\n", b->name, b->expected_result);
+ set_board(b->name);
+ char winner = play(get_move_preset_board);
+ if(winner)
+ sprintf(actual_result, "%c WINS", winner);
+ else
+ sprintf(actual_result, "SCRATCH");
+ printf("Result was: %s\n", actual_result);
+ printf("Test %d ", i);
+ if(strcmp(actual_result, b->expected_result)) {
+ puts("FAILURE\n");
+ failures++;
+ }
+ else puts("SUCCESS\n");
+ }
+ puts("-----------------------------------------------------------");
+ printf("\n### SUITE %s ###\n", failures ? "FAILURE" : "SUCCESS");
+ printf("%d tests: %d failed, %d succeeded\n", i, failures, i - failures);
+ return (i - failures == 0) ? 2 : (failures && 1);
+}