From e30523672889ce19a781f16a402994f9e51793c0 Mon Sep 17 00:00:00 2001 From: Tobias Krause Date: Wed, 2 Feb 2022 21:37:10 +0100 Subject: [PATCH] tictactoe: hard ai sets first empy field if theres no better option --- .../java/de/tims/tictactoe/ai/AIHard.java | 70 ++++++++++++++----- .../java/de/tims/tictactoe/ai/AIHardTest.java | 11 +++ 2 files changed, 64 insertions(+), 17 deletions(-) diff --git a/src/main/java/de/tims/tictactoe/ai/AIHard.java b/src/main/java/de/tims/tictactoe/ai/AIHard.java index 5c29316..34e1c26 100644 --- a/src/main/java/de/tims/tictactoe/ai/AIHard.java +++ b/src/main/java/de/tims/tictactoe/ai/AIHard.java @@ -19,13 +19,17 @@ public class AIHard implements TicTacToeAI { @Override public void calculateNextMove() { + char[][] board = gl.getBoard(); + int row; + int col; + int charsInRow = 0; int charsInCol = 0; int charsInDiag = 0; - char[][] board = gl.getBoard(); + char actualChar; for (int i = 0; i < 2; i++) { - char actualChar = (i == 0) ? AI_CHAR : PLAYER_CHAR; + actualChar = (i == 0) ? AI_CHAR : PLAYER_CHAR; for (int j = 0; j < BOARD_SIZE; j++) { charsInRow = countCharsInRow(j, actualChar); @@ -35,8 +39,11 @@ public class AIHard implements TicTacToeAI { charsInDiag = countCharsInDiag(j, actualChar); if (charsInDiag == BOARD_SIZE - 1) { for (int k = 0; k < BOARD_SIZE; k++) { - if (board[k][(j == 0) ? k : BOARD_SIZE - 1 - k] == EMPTY_CHAR) { - gl.setField(k, (j == 0) ? k : BOARD_SIZE - 1 -k, AI_CHAR); + row = k; + col = (j == 0) ? k : BOARD_SIZE - 1 - k; + + if (board[row][col] == EMPTY_CHAR) { + gl.setField(row, col, AI_CHAR); return; } } @@ -45,8 +52,11 @@ public class AIHard implements TicTacToeAI { if (charsInRow == BOARD_SIZE - 1 || charsInCol == BOARD_SIZE - 1) { for (int k = 0; k < BOARD_SIZE; k++) { - if (board[(charsInRow == BOARD_SIZE - 1) ? j : k][(charsInRow == BOARD_SIZE - 1) ? k : j] == EMPTY_CHAR) { - gl.setField((charsInRow == BOARD_SIZE - 1) ? j : k, (charsInRow == BOARD_SIZE - 1) ? k : j, AI_CHAR); + row = (charsInRow == BOARD_SIZE - 1) ? j : k; + col = (charsInRow == BOARD_SIZE - 1) ? k : j; + + if (board[row][col] == EMPTY_CHAR) { + gl.setField(row, col, AI_CHAR); return; } } @@ -56,26 +66,37 @@ public class AIHard implements TicTacToeAI { if (board[BOARD_SIZE / 2][BOARD_SIZE / 2] == EMPTY_CHAR) { gl.setField(BOARD_SIZE / 2, BOARD_SIZE / 2, AI_CHAR); + return; } else if (board[BOARD_SIZE / 2][BOARD_SIZE / 2] == AI_CHAR && (board[0][0] == AI_CHAR || board[0][BOARD_SIZE - 1] == AI_CHAR || board[BOARD_SIZE - 1][0] == AI_CHAR || board[BOARD_SIZE - 1][BOARD_SIZE - 1] == AI_CHAR)) { + int onwCharsInRow = 0; + int ownCharsInCol = 0; + int emptyCharsInRow = 0; + int emptyCharsInCol = 0; + for (int i = BOARD_SIZE - 2; i > 0; i--) { for (int j = 0; j < BOARD_SIZE; j += BOARD_SIZE - 1) { for (int k = 1; k < BOARD_SIZE - 1; k++) { - if (countCharsInRow(j, AI_CHAR) == i && countCharsInCol(k, AI_CHAR) == i && countCharsInRow(j, EMPTY_CHAR) == BOARD_SIZE -i && countCharsInCol(k, EMPTY_CHAR) == BOARD_SIZE - i) { - gl.setField(j, k, AI_CHAR); - return; - } - - if (countCharsInRow(k, AI_CHAR) == i && countCharsInCol(j, AI_CHAR) == i && countCharsInRow(k, EMPTY_CHAR) == BOARD_SIZE -i && countCharsInCol(j, EMPTY_CHAR) == BOARD_SIZE - i) { - gl.setField(k, j, AI_CHAR); - return; + for (int l = 0; l < 2; l++) { + row = (l == 0) ? j : k; + col = (l == 0) ? k : j; + + onwCharsInRow = countCharsInRow(row, AI_CHAR); + ownCharsInCol = countCharsInCol(col, AI_CHAR); + emptyCharsInRow = countCharsInRow(row, EMPTY_CHAR); + emptyCharsInCol = countCharsInCol(col, EMPTY_CHAR); + + if (onwCharsInRow >= i && ownCharsInCol >= i && emptyCharsInRow >= BOARD_SIZE - onwCharsInRow && emptyCharsInCol == BOARD_SIZE - ownCharsInCol) { + gl.setField(row, col, AI_CHAR); + return; + } } } } } } else { boolean emptyEdgeFound = false; - int row = -1; - int col = -1; + row = -1; + col = -1; int prioRow = -1; int prioCol = -1; @@ -99,7 +120,22 @@ public class AIHard implements TicTacToeAI { } } - gl.setField((prioRow != -1) ? prioRow : row, (prioCol != -1) ? prioCol : col, AI_CHAR); + if (row != -1 && col != -1) { + row = (prioRow != -1) ? prioRow : row; + col = (prioCol != -1) ? prioCol : col; + + gl.setField(row, col, AI_CHAR); + return; + } + } + + for (row = 0; row < BOARD_SIZE; row++) { + for (col = 0; col < BOARD_SIZE; col++) { + if (board[row][col] == EMPTY_CHAR) { + gl.setField(row, col, AI_CHAR); + return; + } + } } } diff --git a/src/test/java/de/tims/tictactoe/ai/AIHardTest.java b/src/test/java/de/tims/tictactoe/ai/AIHardTest.java index 8c21419..fc9b48d 100644 --- a/src/test/java/de/tims/tictactoe/ai/AIHardTest.java +++ b/src/test/java/de/tims/tictactoe/ai/AIHardTest.java @@ -165,6 +165,17 @@ class AIHardTest { verify(gl, times(1)).setField(1, 2, realChar); } + @Test + void setFirstFreeFieldIfNoBetterOpportunities() { + char realChar = 'o'; + doReturn(new char[][] { {'o', 'x', 'x'}, {'x', 'o', 'o'}, {'o', '-', 'x'} }).when(gl).getBoard(); + + TicTacToeAI ai = new AIHard(gl); + ai.calculateNextMove(); + + verify(gl, times(1)).setField(2, 1, realChar); + } + @ParameterizedTest @MethodSource("testCasesForCountCharsInRow") void countCharsInRowTest(String testName, char[][] board, int rowNum, char charToCount, int expectedResult) {