Jenkins
3 years ago
8 changed files with 1095 additions and 0 deletions
-
183src/main/java/de/tims/tictactoe/GameLogic.java
-
22src/main/java/de/tims/tictactoe/ShowGUI.java
-
34src/main/java/de/tims/tictactoe/ai/AIEasy.java
-
190src/main/java/de/tims/tictactoe/ai/AIHard.java
-
5src/main/java/de/tims/tictactoe/ai/TicTacToeAI.java
-
337src/test/java/de/tims/tictactoe/GameLogicTest.java
-
62src/test/java/de/tims/tictactoe/ai/AIEasyTest.java
-
262src/test/java/de/tims/tictactoe/ai/AIHardTest.java
@ -0,0 +1,183 @@ |
|||||
|
package de.tims.tictactoe; |
||||
|
|
||||
|
import java.awt.GridLayout; |
||||
|
import java.awt.event.ActionEvent; |
||||
|
import java.awt.event.ActionListener; |
||||
|
|
||||
|
import javax.swing.JButton; |
||||
|
import javax.swing.JOptionPane; |
||||
|
import javax.swing.JPanel; |
||||
|
|
||||
|
public class GameLogic implements ActionListener { |
||||
|
|
||||
|
private static final char EMPTY_FIELD = '-'; |
||||
|
private static final char PLAYER_1 = 'x'; |
||||
|
private static final char PLAYER_2 = 'o'; |
||||
|
private char[][] board; |
||||
|
private final char[] occupiedFields = { PLAYER_1, PLAYER_2 }; |
||||
|
private char currentPlayer = PLAYER_1; |
||||
|
private boolean gui = false; |
||||
|
|
||||
|
private JButton[][] fields; |
||||
|
private JPanel contentPanel; |
||||
|
|
||||
|
public GameLogic(int size) { |
||||
|
if (size < 3) { |
||||
|
size = 3; |
||||
|
} |
||||
|
this.board = new char[size][size]; |
||||
|
this.resetBoard(); |
||||
|
} |
||||
|
|
||||
|
public GameLogic(char[][] board) { |
||||
|
this.board = board; |
||||
|
} |
||||
|
|
||||
|
public char[][] getBoard() { |
||||
|
return this.board; |
||||
|
} |
||||
|
|
||||
|
public int countFields() { |
||||
|
return this.board[0].length * this.board.length; |
||||
|
} |
||||
|
|
||||
|
public void setField(int column, int row, char player) { |
||||
|
if (this.fieldIsEmpty(column, row)) { |
||||
|
this.board[column][row] = player; |
||||
|
if (gui) { |
||||
|
this.fields[column][row].setText("" + this.getCurrentPlayer()); |
||||
|
this.updateGUI(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public boolean fieldIsEmpty(int column, int row) { |
||||
|
for (char field : this.occupiedFields) { |
||||
|
if (this.board[column][row] == field) |
||||
|
return false; |
||||
|
} |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
public boolean checkForWin(char player) { |
||||
|
boolean won = false; |
||||
|
int countFields = 0; |
||||
|
|
||||
|
// check columns |
||||
|
for (int i = 0; i < this.board.length; i++) { |
||||
|
for (int j = 0; j < this.board[0].length; j++) { |
||||
|
if (this.board[j][i] == player) |
||||
|
countFields++; |
||||
|
} |
||||
|
if (countFields == this.board.length) |
||||
|
return won = true; |
||||
|
countFields = 0; |
||||
|
} |
||||
|
// check rows |
||||
|
for (int i = 0; i < this.board[0].length; i++) { |
||||
|
for (int j = 0; j < this.board.length; j++) { |
||||
|
if (this.board[i][j] == player) |
||||
|
countFields++; |
||||
|
} |
||||
|
if (countFields == this.board.length) |
||||
|
return won = true; |
||||
|
countFields = 0; |
||||
|
} |
||||
|
|
||||
|
// check diagonal left |
||||
|
for (int i = this.board.length - 1, j = this.board.length - 1; i >= 0; i--, j--) { |
||||
|
if (this.board[i][j] == player) |
||||
|
countFields++; |
||||
|
} |
||||
|
if (countFields == this.board.length) |
||||
|
return won = true; |
||||
|
countFields = 0; |
||||
|
|
||||
|
// check diagonal right |
||||
|
for (int i = this.board.length - 1, j = 0; i >= 0; i--, j++) { |
||||
|
if (this.board[i][j] == player) |
||||
|
countFields++; |
||||
|
} |
||||
|
if (countFields == this.board.length) |
||||
|
return won = true; |
||||
|
|
||||
|
return won; |
||||
|
} |
||||
|
|
||||
|
public boolean checkEndOfGame() { |
||||
|
return this.checkForWin(PLAYER_1) || this.checkForWin(PLAYER_2); |
||||
|
} |
||||
|
|
||||
|
public char getCurrentPlayer() { |
||||
|
return this.currentPlayer; |
||||
|
} |
||||
|
|
||||
|
public void switchPlayer() { |
||||
|
this.currentPlayer = this.currentPlayer == PLAYER_1 ? PLAYER_2 : PLAYER_1; |
||||
|
} |
||||
|
|
||||
|
public void resetBoard() { |
||||
|
for (int i = 0; i < this.board.length; i++) { |
||||
|
for (int j = 0; j < this.board.length; j++) { |
||||
|
this.board[i][j] = EMPTY_FIELD; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public JPanel generateGUI() { |
||||
|
this.fields = new JButton[this.board.length][this.board.length]; |
||||
|
this.contentPanel = new JPanel(); |
||||
|
this.contentPanel.setLayout(new GridLayout(this.board.length, this.board.length)); |
||||
|
|
||||
|
for (int i = 0; i < this.fields.length; i++) { |
||||
|
for (int j = 0; j < this.fields.length; j++) { |
||||
|
this.fields[i][j] = new JButton(); |
||||
|
this.fields[i][j].addActionListener(this); |
||||
|
this.contentPanel.add(this.fields[i][j]); |
||||
|
} |
||||
|
} |
||||
|
this.gui = true; |
||||
|
return this.contentPanel; |
||||
|
} |
||||
|
|
||||
|
public JButton getGUIField(int column, int row) { |
||||
|
return this.fields[column][row]; |
||||
|
} |
||||
|
|
||||
|
private void updateGUI() { |
||||
|
if (this.checkEndOfGame()) { |
||||
|
for (int i = 0; i < this.fields.length; i++) { |
||||
|
for (int j = 0; j < this.fields.length; j++) { |
||||
|
this.fields[i][j].setEnabled(false); |
||||
|
} |
||||
|
} |
||||
|
JOptionPane.showMessageDialog(contentPanel, "Spieler " + this.currentPlayer + " hat gewonnen."); |
||||
|
this.resetGUI(); |
||||
|
} |
||||
|
this.switchPlayer(); |
||||
|
} |
||||
|
|
||||
|
private void resetGUI() { |
||||
|
for (int i = 0; i < this.fields.length; i++) { |
||||
|
for (int j = 0; j < this.fields.length; j++) { |
||||
|
this.resetBoard(); |
||||
|
this.fields[i][j].setText(""); |
||||
|
this.fields[i][j].setEnabled(true); |
||||
|
} |
||||
|
} |
||||
|
this.switchPlayer(); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void actionPerformed(ActionEvent e) { |
||||
|
for (int i = 0; i < this.fields.length; i++) { |
||||
|
for (int j = 0; j < this.fields[0].length; j++) { |
||||
|
if (e.getSource() == this.fields[i][j]) { |
||||
|
this.setField(i, j, currentPlayer); |
||||
|
this.fields[i][j].getText(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
} |
@ -0,0 +1,22 @@ |
|||||
|
package de.tims.tictactoe; |
||||
|
|
||||
|
import javax.swing.JFrame; |
||||
|
|
||||
|
public class ShowGUI { |
||||
|
|
||||
|
private JFrame frame; |
||||
|
|
||||
|
public ShowGUI() { |
||||
|
this.frame = new JFrame("TicTacToe"); |
||||
|
this.frame.setSize(600, 600); |
||||
|
|
||||
|
GameLogic game = new GameLogic(3); |
||||
|
this.frame.add(game.generateGUI()); |
||||
|
this.frame.setVisible(true); |
||||
|
} |
||||
|
|
||||
|
public static void main(String[] args) { |
||||
|
new ShowGUI(); |
||||
|
} |
||||
|
|
||||
|
} |
@ -0,0 +1,34 @@ |
|||||
|
package de.tims.tictactoe.ai; |
||||
|
|
||||
|
import de.tims.tictactoe.GameLogic; |
||||
|
|
||||
|
import java.util.Random; |
||||
|
|
||||
|
public class AIEasy implements TicTacToeAI { |
||||
|
private static final char AI_CHAR = 'o'; |
||||
|
private static final char EMPTY_CHAR = '-'; |
||||
|
|
||||
|
private Random rand; |
||||
|
private GameLogic gl; |
||||
|
private int boardSize; |
||||
|
|
||||
|
public AIEasy(GameLogic gl) { |
||||
|
this.gl = gl; |
||||
|
boardSize = gl.getBoard().length; |
||||
|
rand = new Random(); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void calculateNextMove() { |
||||
|
char[][] board = gl.getBoard(); |
||||
|
int row; |
||||
|
int col; |
||||
|
do { |
||||
|
row = rand.nextInt(boardSize); |
||||
|
col = rand.nextInt(boardSize); |
||||
|
} while (board[row][col] != EMPTY_CHAR); |
||||
|
|
||||
|
gl.setField(row, col, AI_CHAR); |
||||
|
} |
||||
|
|
||||
|
} |
@ -0,0 +1,190 @@ |
|||||
|
package de.tims.tictactoe.ai; |
||||
|
|
||||
|
import de.tims.tictactoe.GameLogic; |
||||
|
|
||||
|
public class AIHard implements TicTacToeAI { |
||||
|
private static final char AI_CHAR = 'o'; |
||||
|
private static final char EMPTY_CHAR = '-'; |
||||
|
private static final char PLAYER_CHAR = 'x'; |
||||
|
private static final int BOARD_SIZE = 3; |
||||
|
|
||||
|
private GameLogic gl; |
||||
|
|
||||
|
public AIHard(GameLogic gl) throws IllegalArgumentException { |
||||
|
if (gl.getBoard().length != BOARD_SIZE) { |
||||
|
throw new IllegalArgumentException("Hard AI only supports 3x3 boards!"); |
||||
|
} |
||||
|
this.gl = gl; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void calculateNextMove() { |
||||
|
char[][] board = gl.getBoard(); |
||||
|
int row; |
||||
|
int col; |
||||
|
|
||||
|
int charsInRow = 0; |
||||
|
int charsInCol = 0; |
||||
|
int charsInDiag = 0; |
||||
|
char actualChar; |
||||
|
|
||||
|
for (int i = 0; i < 2; i++) { |
||||
|
actualChar = (i == 0) ? AI_CHAR : PLAYER_CHAR; |
||||
|
|
||||
|
for (int j = 0; j < BOARD_SIZE; j++) { |
||||
|
charsInRow = countCharsInRow(j, actualChar); |
||||
|
charsInCol = countCharsInCol(j, actualChar); |
||||
|
|
||||
|
if (j < 2) { |
||||
|
charsInDiag = countCharsInDiag(j, actualChar); |
||||
|
if (charsInDiag == BOARD_SIZE - 1) { |
||||
|
for (int k = 0; k < BOARD_SIZE; k++) { |
||||
|
row = k; |
||||
|
col = (j == 0) ? k : BOARD_SIZE - 1 - k; |
||||
|
|
||||
|
if (board[row][col] == EMPTY_CHAR) { |
||||
|
gl.setField(row, col, AI_CHAR); |
||||
|
return; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (charsInRow == BOARD_SIZE - 1 || charsInCol == BOARD_SIZE - 1) { |
||||
|
for (int k = 0; k < BOARD_SIZE; k++) { |
||||
|
if (charsInRow == BOARD_SIZE - 1) { |
||||
|
row = j; |
||||
|
col = k; |
||||
|
|
||||
|
if (board[row][col] == EMPTY_CHAR) { |
||||
|
gl.setField(row, col, AI_CHAR); |
||||
|
return; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (charsInCol == BOARD_SIZE - 1) { |
||||
|
row = k; |
||||
|
col = j; |
||||
|
|
||||
|
if (board[row][col] == EMPTY_CHAR) { |
||||
|
gl.setField(row, col, AI_CHAR); |
||||
|
return; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
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++) { |
||||
|
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; |
||||
|
row = -1; |
||||
|
col = -1; |
||||
|
int prioRow = -1; |
||||
|
int prioCol = -1; |
||||
|
|
||||
|
for (int i = 0; i < BOARD_SIZE; i = i + BOARD_SIZE - 1) { |
||||
|
for (int j = 0; j < BOARD_SIZE; j = j + BOARD_SIZE - 1) { |
||||
|
if (board[i][j] == EMPTY_CHAR) { |
||||
|
row = (row == -1) ? i : row; |
||||
|
col = (col == -1) ? j : col; |
||||
|
|
||||
|
if (countCharsInRow(i, PLAYER_CHAR) != 0 || countCharsInCol(j, PLAYER_CHAR) != 0) { |
||||
|
prioRow = i; |
||||
|
prioCol = j; |
||||
|
emptyEdgeFound = true; |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (emptyEdgeFound) { |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
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; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public int countCharsInRow(int index, char charToCount) { |
||||
|
int count = 0; |
||||
|
char[][] board = gl.getBoard(); |
||||
|
|
||||
|
for (int i = 0; i < BOARD_SIZE; i++) { |
||||
|
count += (board[index][i] == charToCount) ? 1 : 0; |
||||
|
} |
||||
|
|
||||
|
return count; |
||||
|
} |
||||
|
|
||||
|
public int countCharsInCol(int index, char charToCount) { |
||||
|
int count = 0; |
||||
|
char[][] board = gl.getBoard(); |
||||
|
|
||||
|
for (int i = 0; i < BOARD_SIZE; i++) { |
||||
|
count += (board[i][index] == charToCount) ? 1 : 0; |
||||
|
} |
||||
|
|
||||
|
return count; |
||||
|
} |
||||
|
|
||||
|
public int countCharsInDiag(int index, char charToCount) throws IndexOutOfBoundsException { |
||||
|
if (index < 0 || index > 1) { |
||||
|
throw new IndexOutOfBoundsException("Only 0 and 1 are allowed values for index!"); |
||||
|
} |
||||
|
|
||||
|
int count = 0; |
||||
|
char[][] board = gl.getBoard(); |
||||
|
|
||||
|
for (int i = 0; i < BOARD_SIZE; i++) { |
||||
|
count += (board[i][(index == 0) ? i : BOARD_SIZE - 1 - i] == charToCount) ? 1 : 0; |
||||
|
} |
||||
|
|
||||
|
return count; |
||||
|
} |
||||
|
} |
@ -0,0 +1,5 @@ |
|||||
|
package de.tims.tictactoe.ai; |
||||
|
|
||||
|
public interface TicTacToeAI { |
||||
|
void calculateNextMove(); |
||||
|
} |
@ -0,0 +1,337 @@ |
|||||
|
package de.tims.tictactoe; |
||||
|
|
||||
|
import static org.junit.jupiter.api.Assertions.assertArrayEquals; |
||||
|
import static org.junit.jupiter.api.Assertions.assertEquals; |
||||
|
|
||||
|
import java.awt.Component; |
||||
|
import java.util.stream.Stream; |
||||
|
|
||||
|
import javax.swing.JButton; |
||||
|
import javax.swing.JPanel; |
||||
|
|
||||
|
import org.junit.jupiter.api.BeforeAll; |
||||
|
import org.junit.jupiter.api.Test; |
||||
|
import org.junit.jupiter.api.TestInstance; |
||||
|
import org.junit.jupiter.api.TestInstance.Lifecycle; |
||||
|
import org.junit.jupiter.params.ParameterizedTest; |
||||
|
import org.junit.jupiter.params.provider.Arguments; |
||||
|
import org.junit.jupiter.params.provider.MethodSource; |
||||
|
|
||||
|
@TestInstance(Lifecycle.PER_CLASS) |
||||
|
class GameLogicTest { |
||||
|
|
||||
|
private final int SIZE = 3; |
||||
|
private GameLogic game; |
||||
|
|
||||
|
@BeforeAll |
||||
|
void setUpBeforeClass() throws Exception { |
||||
|
this.game = new GameLogic(SIZE); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
void createGameLogicTest() { |
||||
|
GameLogic expectedResult = this.game; |
||||
|
GameLogic realResult = new GameLogic(SIZE); |
||||
|
|
||||
|
assertEquals(expectedResult.getClass(), realResult.getClass()); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
void getBoardTest() { |
||||
|
// @formatter:off |
||||
|
char[][] expectedResult = new char[][]{{'-', '-', '-'}, |
||||
|
{'-', '-', '-'}, |
||||
|
{'-', '-', '-'}}; |
||||
|
// @formatter:on |
||||
|
char[][] realResult = this.game.getBoard(); |
||||
|
|
||||
|
assertArrayEquals(expectedResult, realResult); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
void createGameLogicWithGivenBoardTest() { |
||||
|
// @formatter:off |
||||
|
char[][] expectedResult = new char[][]{{'x', '-', '-'}, |
||||
|
{'-', 'o', '-'}, |
||||
|
{'x', '-', '-'}}; |
||||
|
// @formatter:on |
||||
|
char[][] givenBoard = expectedResult; |
||||
|
char[][] realResult = new GameLogic(givenBoard).getBoard(); |
||||
|
|
||||
|
assertArrayEquals(expectedResult, realResult); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
void generateGUITest() { |
||||
|
JPanel expectedResult = new JPanel(); |
||||
|
JPanel realResult = this.game.generateGUI(); |
||||
|
|
||||
|
assertEquals(expectedResult.getClass(), realResult.getClass()); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
void numberOfGUIFieldsTest() { |
||||
|
int expectedResult = (int) Math.pow(SIZE, 2); |
||||
|
int realResult = 0; |
||||
|
|
||||
|
JPanel gui = this.game.generateGUI(); |
||||
|
Component[] components = gui.getComponents(); |
||||
|
|
||||
|
for (Component component : components) { |
||||
|
if (component instanceof JButton) |
||||
|
realResult++; |
||||
|
} |
||||
|
|
||||
|
assertEquals(expectedResult, realResult); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
void getCurrentPlayerTest() { |
||||
|
GameLogic game = new GameLogic(SIZE); |
||||
|
char expectedResult = 'x'; |
||||
|
char realResult = game.getCurrentPlayer(); |
||||
|
|
||||
|
assertEquals(expectedResult, realResult); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
void switchPlayerTest() { |
||||
|
GameLogic game = new GameLogic(SIZE); |
||||
|
game.switchPlayer(); |
||||
|
|
||||
|
char expectedResult = 'o'; |
||||
|
char realResult = game.getCurrentPlayer(); |
||||
|
|
||||
|
assertEquals(expectedResult, realResult); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
void resetBoardTest() { |
||||
|
GameLogic game = new GameLogic(SIZE); |
||||
|
game.setField(1, 2, 'x'); |
||||
|
// @formatter:off |
||||
|
char[][] expectedResult = new char[][]{{'-', '-', '-'}, |
||||
|
{'-', '-', '-'}, |
||||
|
{'-', '-', '-'}}; |
||||
|
// @formatter:on |
||||
|
game.resetBoard(); |
||||
|
char[][] realResult = game.getBoard(); |
||||
|
|
||||
|
assertArrayEquals(expectedResult, realResult); |
||||
|
} |
||||
|
|
||||
|
@ParameterizedTest(name = "[{index}] {0} -> {2} fields") |
||||
|
@MethodSource("testCasesForCountPlayfields") |
||||
|
void fieldCountTest(String testName, int size, int expectedResult) { |
||||
|
GameLogic game = new GameLogic(size); |
||||
|
int realResult = game.countFields(); |
||||
|
|
||||
|
assertEquals(expectedResult, realResult); |
||||
|
} |
||||
|
|
||||
|
@ParameterizedTest(name = "[{index}] {0}") |
||||
|
@MethodSource("testCasesForSetField") |
||||
|
void setFieldTest(String testName, int column, int row, char player, char[][] expectedResult) { |
||||
|
this.game.setField(column, row, player); |
||||
|
char[][] realResult = this.game.getBoard(); |
||||
|
|
||||
|
assertArrayEquals(expectedResult, realResult); |
||||
|
} |
||||
|
|
||||
|
@ParameterizedTest(name = "[{index}] {0}") |
||||
|
@MethodSource("testCasesForCheckEmptyField") |
||||
|
void fieldIsEmptyTest(String testName, int columnToCheck, int rowToCheck, boolean expectedResult, char[][] board) { |
||||
|
GameLogic game = new GameLogic(board); |
||||
|
boolean realResult = game.fieldIsEmpty(columnToCheck, rowToCheck); |
||||
|
|
||||
|
assertEquals(expectedResult, realResult); |
||||
|
} |
||||
|
|
||||
|
@ParameterizedTest(name = "[{index}] {0}: should be {2}") |
||||
|
@MethodSource("testCasesForCheckForWin") |
||||
|
void checkForWinTest(String testName, char player, boolean expectedResult, char[][] boardToCheck) { |
||||
|
boolean realResult = new GameLogic(boardToCheck).checkForWin(player); |
||||
|
|
||||
|
assertEquals(expectedResult, realResult); |
||||
|
} |
||||
|
|
||||
|
@ParameterizedTest(name = "[{index}] {0}: should be {1}") |
||||
|
@MethodSource("testCasesForCheckEndOfGame") |
||||
|
void checkEndOfGameTest(String testName, boolean expectedResult, char[][] boardToCheck) { |
||||
|
boolean realResult = new GameLogic(boardToCheck).checkEndOfGame(); |
||||
|
|
||||
|
assertEquals(expectedResult, realResult); |
||||
|
} |
||||
|
|
||||
|
@ParameterizedTest(name = "[{index}] {0}: should be {1}") |
||||
|
@MethodSource("testCasesForCheckButtonState") |
||||
|
void buttonStateTest(String testName, boolean expectedResult, boolean doClick, int column, int row) throws InterruptedException { |
||||
|
GameLogic game = new GameLogic(SIZE); |
||||
|
game.generateGUI(); |
||||
|
JButton currentField = game.getGUIField(0, 0); |
||||
|
|
||||
|
if (doClick) |
||||
|
currentField.doClick(); |
||||
|
boolean realResult = !currentField.getText().isEmpty(); |
||||
|
|
||||
|
assertEquals(expectedResult, realResult); |
||||
|
} |
||||
|
|
||||
|
// @formatter:off |
||||
|
private static Stream<Arguments> testCasesForCountPlayfields() { |
||||
|
return Stream.of(Arguments.of("1x1 board with too few fields", 1, 9), |
||||
|
Arguments.of("2x2 board with too few fields", 2, 9), |
||||
|
Arguments.of("3x3 board with 9 playfields", 3, 9), |
||||
|
Arguments.of("4x4 board with 16 playfields", 4, 16), |
||||
|
Arguments.of("5x5 board with 25 playfields", 5, 25)); |
||||
|
} |
||||
|
|
||||
|
private static Stream<Arguments> testCasesForSetField() { |
||||
|
return Stream.of( |
||||
|
Arguments.of("set field [0][0] for player 1", 0, 0, 'x', new char[][] |
||||
|
{{'x', '-', '-'}, |
||||
|
{'-', '-', '-'}, |
||||
|
{'-', '-', '-'}}), |
||||
|
Arguments.of("set field [1][0] for player 2", 1, 0, 'o', new char[][] |
||||
|
{{'x', '-', '-'}, |
||||
|
{'o', '-', '-'}, |
||||
|
{'-', '-', '-'}}), |
||||
|
Arguments.of("try to set occupied field [1][0] for player 1", 1, 0, 'x', new char[][] |
||||
|
{{'x', '-', '-'}, |
||||
|
{'o', '-', '-'}, |
||||
|
{'-', '-', '-'}}) |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
private static Stream<Arguments> testCasesForCheckEmptyField() { |
||||
|
return Stream.of( |
||||
|
Arguments.of("check an empty field", 0, 0, true, new char[][] |
||||
|
{{'-', '-', '-'}, |
||||
|
{'-', '-', '-'}, |
||||
|
{'-', '-', '-'}}), |
||||
|
Arguments.of("check a field set by player 1", 0, 0, false, new char[][] |
||||
|
{{'x', '-', '-'}, |
||||
|
{'-', '-', '-'}, |
||||
|
{'-', '-', '-'}}), |
||||
|
Arguments.of("check a field set by player 2", 0, 0, false, new char[][] |
||||
|
{{'o', '-', '-'}, |
||||
|
{'-', '-', '-'}, |
||||
|
{'-', '-', '-'}}) |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
private static Stream<Arguments> testCasesForCheckForWin() { |
||||
|
return Stream.of( |
||||
|
Arguments.of("check win in column 0 for player 1", 'x', true, new char[][] |
||||
|
{{'x', '-', '-'}, |
||||
|
{'x', '-', '-'}, |
||||
|
{'x', '-', '-'}}), |
||||
|
Arguments.of("check win in column 1 for player 1", 'x', true, new char[][] |
||||
|
{{'-', 'x', '-'}, |
||||
|
{'-', 'x', '-'}, |
||||
|
{'-', 'x', '-'}}), |
||||
|
Arguments.of("check win in column 2 for player 1", 'x', true, new char[][] |
||||
|
{{'-', '-', 'x'}, |
||||
|
{'-', '-', 'x'}, |
||||
|
{'-', '-', 'x'}}), |
||||
|
Arguments.of("check win in column 0 for player 2", 'o', true, new char[][] |
||||
|
{{'o', '-', '-'}, |
||||
|
{'o', '-', '-'}, |
||||
|
{'o', '-', '-'}}), |
||||
|
Arguments.of("check win in row 0 for player 1", 'x', true, new char[][] |
||||
|
{{'x', 'x', 'x'}, |
||||
|
{'-', '-', '-'}, |
||||
|
{'-', '-', '-'}}), |
||||
|
Arguments.of("check win in row 0 for player 2", 'o', true, new char[][] |
||||
|
{{'o', 'o', 'o'}, |
||||
|
{'-', '-', '-'}, |
||||
|
{'-', '-', '-'}}), |
||||
|
Arguments.of("check win in row 1 for player 2", 'o', true, new char[][] |
||||
|
{{'-', '-', '-'}, |
||||
|
{'o', 'o', 'o'}, |
||||
|
{'-', '-', '-'}}), |
||||
|
Arguments.of("check win in row 2 for player 2", 'o', true, new char[][] |
||||
|
{{'-', '-', '-'}, |
||||
|
{'-', '-', '-'}, |
||||
|
{'o', 'o', 'o'}}), |
||||
|
Arguments.of("check win in column 0 for player 1 with full board", 'x', true, new char[][] |
||||
|
{{'x', 'o', 'o'}, |
||||
|
{'x', 'o', 'x'}, |
||||
|
{'x', 'x', 'o'}}), |
||||
|
Arguments.of("check win in column 1 for player 2 with full board", 'o', true, new char[][] |
||||
|
{{'x', 'o', 'o'}, |
||||
|
{'x', 'o', 'x'}, |
||||
|
{'o', 'o', 'x'}}), |
||||
|
Arguments.of("check win in column 2 for player 2 with full board", 'o', true, new char[][] |
||||
|
{{'x', 'o', 'o'}, |
||||
|
{'x', 'x', 'o'}, |
||||
|
{'o', 'o', 'o'}}), |
||||
|
Arguments.of("check win in row 0 for player 1 with full board", 'x', true, new char[][] |
||||
|
{{'x', 'x', 'x'}, |
||||
|
{'x', 'o', 'o'}, |
||||
|
{'o', 'o', 'x'}}), |
||||
|
Arguments.of("check win in row 1 for player 1 with full board", 'x', true, new char[][] |
||||
|
{{'x', 'x', 'o'}, |
||||
|
{'x', 'x', 'x'}, |
||||
|
{'o', 'o', 'x'}}), |
||||
|
Arguments.of("check win in row 2 for player 2 with full board", 'o', true, new char[][] |
||||
|
{{'x', 'x', 'o'}, |
||||
|
{'o', 'x', 'x'}, |
||||
|
{'o', 'o', 'o'}}), |
||||
|
Arguments.of("check win in column 0 for player 2", 'o', false, new char[][] |
||||
|
{{'o', '-', '-'}, |
||||
|
{'o', '-', '-'}, |
||||
|
{'-', '-', '-'}}), |
||||
|
Arguments.of("check a draw for player 2", 'o', false, new char[][] |
||||
|
{{'o', 'o', 'x'}, |
||||
|
{'o', 'x', 'x'}, |
||||
|
{'x', 'x', 'o'}}), |
||||
|
Arguments.of("check a draw for player 1", 'x', false, new char[][] |
||||
|
{{'o', 'o', 'x'}, |
||||
|
{'o', 'o', 'x'}, |
||||
|
{'x', 'x', 'o'}}), |
||||
|
Arguments.of("check diagonal left win for player 1", 'x', true, new char[][] |
||||
|
{{'x', 'o', 'x'}, |
||||
|
{'x', 'x', 'o'}, |
||||
|
{'o', 'o', 'x'}}), |
||||
|
Arguments.of("check diagonal right win for player 2", 'o', true, new char[][] |
||||
|
{{'x', 'x', 'o'}, |
||||
|
{'x', 'o', 'o'}, |
||||
|
{'o', 'x', 'x'}}) |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
private static Stream<Arguments> testCasesForCheckEndOfGame() { |
||||
|
return Stream.of( |
||||
|
Arguments.of("check empty board", false, new char[][] |
||||
|
{{'-', '-', '-'}, |
||||
|
{'-', '-', '-'}, |
||||
|
{'-', '-', '-'}}), |
||||
|
Arguments.of("end of game with win for player 1", true, new char[][] |
||||
|
{{'x', 'o', 'x'}, |
||||
|
{'x', 'x', 'o'}, |
||||
|
{'x', 'o', 'o'}}), |
||||
|
Arguments.of("end of game with win for player 2", true, new char[][] |
||||
|
{{'x', 'x', 'o'}, |
||||
|
{'o', 'o', 'o'}, |
||||
|
{'x', 'o', 'x'}}), |
||||
|
Arguments.of("check tied game", true, new char[][] |
||||
|
{{'x', 'x', 'o'}, |
||||
|
{'o', 'x', 'o'}, |
||||
|
{'x', 'o', 'x'}}), |
||||
|
Arguments.of("check not yet finished game", false, new char[][] |
||||
|
{{'x', 'x', '-'}, |
||||
|
{'o', 'o', '-'}, |
||||
|
{'x', 'o', 'x'}}) |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
private static Stream<Arguments> testCasesForCheckButtonState() { |
||||
|
return Stream.of( |
||||
|
Arguments.of("trigger gui field [0][0]", true, true, 0, 0), |
||||
|
Arguments.of("dont't trigger gui field [1][1]", false, false, 1, 1) |
||||
|
); |
||||
|
} |
||||
|
// @formatter:on |
||||
|
|
||||
|
} |
@ -0,0 +1,62 @@ |
|||||
|
package de.tims.tictactoe.ai; |
||||
|
|
||||
|
import static org.mockito.Mockito.*; |
||||
|
|
||||
|
import org.junit.jupiter.api.Test; |
||||
|
import org.junit.jupiter.api.extension.ExtendWith; |
||||
|
import org.mockito.ArgumentMatcher; |
||||
|
import org.mockito.Mock; |
||||
|
import org.mockito.junit.jupiter.MockitoExtension; |
||||
|
|
||||
|
import de.tims.tictactoe.GameLogic; |
||||
|
|
||||
|
@ExtendWith(MockitoExtension.class) |
||||
|
class AIEasyTest { |
||||
|
static int size = 3; |
||||
|
|
||||
|
@Mock |
||||
|
private GameLogic gl; |
||||
|
|
||||
|
@Test |
||||
|
void emptyBoardChooseRandomField() { |
||||
|
char realChar = 'o'; |
||||
|
doReturn(new char[][] { {'-', '-', '-'}, {'-', '-', '-'}, {'-', '-', '-'} }).when(gl).getBoard(); |
||||
|
|
||||
|
TicTacToeAI ai = new AIEasy(gl); |
||||
|
|
||||
|
//run method 100 times, because of random generator |
||||
|
for (int i = 0; i < 100; i++) { |
||||
|
ai.calculateNextMove(); |
||||
|
} |
||||
|
|
||||
|
verify(gl, times(100)).setField(intThat(new ChooseRandomFieldMatcher()), intThat(new ChooseRandomFieldMatcher()), eq(realChar)); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
void notEmptyBoardChooseRandomFreeField() { |
||||
|
char realChar = 'o'; |
||||
|
doReturn(new char[][] { {'x', '-', 'o'}, {'-', 'o', '-'}, {'-', 'x', 'x'} }).when(gl).getBoard(); |
||||
|
|
||||
|
TicTacToeAI ai = new AIEasy(gl); |
||||
|
|
||||
|
//run method 100 times, because of random generator |
||||
|
for (int i = 0; i < 100; i++) { |
||||
|
ai.calculateNextMove(); |
||||
|
} |
||||
|
|
||||
|
verify(gl, times(100)).setField(intThat(new ChooseRandomFieldMatcher()), intThat(new ChooseRandomFieldMatcher()), eq(realChar)); |
||||
|
//verify that the method is never called with a field which was already set |
||||
|
verify(gl, never()).setField(0, 0, realChar); |
||||
|
verify(gl, never()).setField(0, 2, realChar); |
||||
|
verify(gl, never()).setField(1, 1, realChar); |
||||
|
verify(gl, never()).setField(2, 1, realChar); |
||||
|
verify(gl, never()).setField(2, 2, realChar); |
||||
|
} |
||||
|
|
||||
|
private static class ChooseRandomFieldMatcher implements ArgumentMatcher<Integer> { |
||||
|
@Override |
||||
|
public boolean matches(Integer argument) { |
||||
|
return argument.intValue() >= 0 && argument.intValue() < size; |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,262 @@ |
|||||
|
package de.tims.tictactoe.ai; |
||||
|
|
||||
|
import static org.assertj.core.api.Assertions.*; |
||||
|
import static org.mockito.Mockito.*; |
||||
|
|
||||
|
import java.util.stream.Stream; |
||||
|
|
||||
|
import org.junit.jupiter.api.Test; |
||||
|
import org.junit.jupiter.api.extension.ExtendWith; |
||||
|
import org.junit.jupiter.params.ParameterizedTest; |
||||
|
import org.junit.jupiter.params.provider.Arguments; |
||||
|
import org.junit.jupiter.params.provider.MethodSource; |
||||
|
import org.mockito.Mock; |
||||
|
import org.mockito.junit.jupiter.MockitoExtension; |
||||
|
|
||||
|
import de.tims.tictactoe.GameLogic; |
||||
|
|
||||
|
@ExtendWith(MockitoExtension.class) |
||||
|
class AIHardTest { |
||||
|
static int size = 3; |
||||
|
|
||||
|
@Mock |
||||
|
private GameLogic gl; |
||||
|
|
||||
|
@Test |
||||
|
void emptyBoardChooseMiddleField() { |
||||
|
char realChar = 'o'; |
||||
|
doReturn(new char[][] { {'-', '-', '-'}, {'-', '-', '-'}, {'-', '-', '-'} }).when(gl).getBoard(); |
||||
|
|
||||
|
TicTacToeAI ai = new AIHard(gl); |
||||
|
ai.calculateNextMove(); |
||||
|
|
||||
|
verify(gl, times(1)).setField(1, 1, realChar); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
void middleFieldAlreadySetChooseEdgeField() { |
||||
|
char realChar = 'o'; |
||||
|
doReturn(new char[][] { {'-', '-', '-'}, {'-', 'x', '-'}, {'-', '-', '-'} }).when(gl).getBoard(); |
||||
|
|
||||
|
TicTacToeAI ai = new AIHard(gl); |
||||
|
ai.calculateNextMove(); |
||||
|
|
||||
|
verify(gl, times(1)).setField(0, 0, realChar); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
void opponentDidntChooseMiddleFieldSoAIDoes() { |
||||
|
char realChar = 'o'; |
||||
|
doReturn(new char[][] { {'-', '-', 'x'}, {'-', '-', '-'}, {'-', '-', '-'} }).when(gl).getBoard(); |
||||
|
|
||||
|
TicTacToeAI ai = new AIHard(gl); |
||||
|
ai.calculateNextMove(); |
||||
|
|
||||
|
verify(gl, times(1)).setField(1, 1, realChar); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
void setEdgeFieldInSecondMove() { |
||||
|
char realChar = 'o'; |
||||
|
doReturn(new char[][] { {'x', '-', '-'}, {'-', 'o', '-'}, {'-', '-', '-'} }).when(gl).getBoard(); |
||||
|
|
||||
|
TicTacToeAI ai = new AIHard(gl); |
||||
|
ai.calculateNextMove(); |
||||
|
|
||||
|
verify(gl, times(1)).setField(0, 2, realChar); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
void preventOpponentsWinInRow() { |
||||
|
char realChar = 'o'; |
||||
|
doReturn(new char[][] { {'o', '-', '-'}, {'x', 'x', '-'}, {'-', '-', '-'} }).when(gl).getBoard(); |
||||
|
|
||||
|
TicTacToeAI ai = new AIHard(gl); |
||||
|
ai.calculateNextMove(); |
||||
|
|
||||
|
verify(gl, times(1)).setField(1, 2, realChar); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
void preventOpponentsWinInCol() { |
||||
|
char realChar = 'o'; |
||||
|
doReturn(new char[][] { {'o', 'x', '-'}, {'-', 'x', '-'}, {'-', '-', '-'} }).when(gl).getBoard(); |
||||
|
|
||||
|
TicTacToeAI ai = new AIHard(gl); |
||||
|
ai.calculateNextMove(); |
||||
|
|
||||
|
verify(gl, times(1)).setField(2, 1, realChar); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
void preventOpponentsWinInDiag() { |
||||
|
char realChar = 'o'; |
||||
|
doReturn(new char[][] { {'x', '-', 'o'}, {'-', 'x', '-'}, {'-', '-', '-'} }).when(gl).getBoard(); |
||||
|
|
||||
|
TicTacToeAI ai = new AIHard(gl); |
||||
|
ai.calculateNextMove(); |
||||
|
|
||||
|
verify(gl, times(1)).setField(2, 2, realChar); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
void when2InRowSetThird() { |
||||
|
char realChar = 'o'; |
||||
|
doReturn(new char[][] { {'o', '-', 'o'}, {'-', 'x', '-'}, {'x', 'x', '-'} }).when(gl).getBoard(); |
||||
|
|
||||
|
TicTacToeAI ai = new AIHard(gl); |
||||
|
ai.calculateNextMove(); |
||||
|
|
||||
|
verify(gl, times(1)).setField(0, 1, realChar); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
void when2InColSetThird() { |
||||
|
char realChar = 'o'; |
||||
|
doReturn(new char[][] { {'o', '-', 'x'}, {'-', 'x', '-'}, {'o', '-', 'x'} }).when(gl).getBoard(); |
||||
|
|
||||
|
TicTacToeAI ai = new AIHard(gl); |
||||
|
ai.calculateNextMove(); |
||||
|
|
||||
|
verify(gl, times(1)).setField(1, 0, realChar); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
void when2inDiagSetThird() { |
||||
|
char realChar = 'o'; |
||||
|
doReturn(new char[][] { {'o', '-', 'x'}, {'-', 'o', 'x'}, {'-', '-', '-'} }).when(gl).getBoard(); |
||||
|
|
||||
|
TicTacToeAI ai = new AIHard(gl); |
||||
|
ai.calculateNextMove(); |
||||
|
|
||||
|
verify(gl, times(1)).setField(2, 2, realChar); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
void opportunityToWinIsMoreImportantThanPreventingOpponentsWinInNextRound() { |
||||
|
char realChar = 'o'; |
||||
|
doReturn(new char[][] { {'x', '-', 'o'}, {'-', 'o', '-'}, {'x', 'x', 'o'} }).when(gl).getBoard(); |
||||
|
|
||||
|
TicTacToeAI ai = new AIHard(gl); |
||||
|
ai.calculateNextMove(); |
||||
|
|
||||
|
verify(gl, times(1)).setField(1, 2, realChar); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
void alwaysSetInTheNearestEdgeToOpponentsX() { |
||||
|
char realChar = 'o'; |
||||
|
doReturn(new char[][] { {'-', '-', '-'}, {'-', 'o', 'x'}, {'-', '-', '-'} }).when(gl).getBoard(); |
||||
|
|
||||
|
TicTacToeAI ai = new AIHard(gl); |
||||
|
ai.calculateNextMove(); |
||||
|
|
||||
|
verify(gl, times(1)).setField(0, 2, realChar); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
void chooseFieldWhichCreatesMostOpportunitiesToWin() { |
||||
|
char realChar = 'o'; |
||||
|
doReturn(new char[][] { {'-', 'x', 'o'}, {'-', 'o', '-'}, {'x', '-', '-'} }).when(gl).getBoard(); |
||||
|
|
||||
|
TicTacToeAI ai = new AIHard(gl); |
||||
|
ai.calculateNextMove(); |
||||
|
|
||||
|
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); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
void ifRowAndColWithSameNumberContainEachContainTwoCharsDontIgnoreCol() { |
||||
|
char realChar = 'o'; |
||||
|
doReturn(new char[][] { {'x', 'o', 'x'}, {'-', 'o', '-'}, {'o', 'x', 'x'} }).when(gl).getBoard(); |
||||
|
|
||||
|
TicTacToeAI ai = new AIHard(gl); |
||||
|
ai.calculateNextMove(); |
||||
|
|
||||
|
verify(gl, times(1)).setField(1, 2, realChar); |
||||
|
} |
||||
|
|
||||
|
@ParameterizedTest |
||||
|
@MethodSource("testCasesForCountCharsInRow") |
||||
|
void countCharsInRowTest(String testName, char[][] board, int rowNum, char charToCount, int expectedResult) { |
||||
|
doReturn(board).when(gl).getBoard(); |
||||
|
|
||||
|
AIHard ai = new AIHard(gl); |
||||
|
int realResult = ai.countCharsInRow(rowNum, charToCount); |
||||
|
|
||||
|
assertThat(realResult).describedAs(testName).isEqualTo(expectedResult); |
||||
|
} |
||||
|
|
||||
|
private static Stream<Arguments> testCasesForCountCharsInRow() { |
||||
|
return Stream.of(Arguments.of("EmptyFieldReturns0", |
||||
|
new char[][] { {'-', '-', '-'}, {'-', '-', '-'}, {'-', '-', '-'} }, |
||||
|
0, 'o', 0), |
||||
|
Arguments.of("TwoCharsInRowReturnsTwo", |
||||
|
new char[][] { {'-', '-', '-'}, {'o', 'o', '-'}, {'-', '-', '-'} }, |
||||
|
1, 'o', 2)); |
||||
|
} |
||||
|
|
||||
|
@ParameterizedTest |
||||
|
@MethodSource("testCasesForCountCharsInCol") |
||||
|
void countCharsInColTest(String testName, char[][] board, int colNum, char charToCount, int expectedResult) { |
||||
|
doReturn(board).when(gl).getBoard(); |
||||
|
|
||||
|
AIHard ai = new AIHard(gl); |
||||
|
int realResult = ai.countCharsInCol(colNum, charToCount); |
||||
|
|
||||
|
assertThat(realResult).describedAs(testName).isEqualTo(expectedResult); |
||||
|
} |
||||
|
|
||||
|
private static Stream<Arguments> testCasesForCountCharsInCol() { |
||||
|
return Stream.of(Arguments.of("EmptyFieldReturns0", |
||||
|
new char[][] { {'-', '-', '-'}, {'-', '-', '-'}, {'-', '-', '-'} }, |
||||
|
0, 'o', 0), |
||||
|
Arguments.of("TwoCharsInRowReturnsTwo", |
||||
|
new char[][] { {'-', '-', '-'}, {'o', 'o', '-'}, {'-', '-', '-'} }, |
||||
|
1, 'o', 1)); |
||||
|
} |
||||
|
|
||||
|
@ParameterizedTest |
||||
|
@MethodSource("testCasesForCountCharsInDiag") |
||||
|
void countCharsInDiagTest(String testName, char[][] board, int diagNum, char charToCount, int expectedResult) { |
||||
|
doReturn(board).when(gl).getBoard(); |
||||
|
|
||||
|
AIHard ai = new AIHard(gl); |
||||
|
int realResult = ai.countCharsInDiag(diagNum, charToCount); |
||||
|
|
||||
|
assertThat(realResult).describedAs(testName).isEqualTo(expectedResult); |
||||
|
} |
||||
|
|
||||
|
private static Stream<Arguments> testCasesForCountCharsInDiag() { |
||||
|
return Stream.of(Arguments.of("EmptyFieldReturns0", |
||||
|
new char[][] { {'-', '-', '-'}, {'-', '-', '-'}, {'-', '-', '-'} }, |
||||
|
0, 'o', 0), |
||||
|
Arguments.of("TwoCharsInRowReturnsTwo", |
||||
|
new char[][] { {'-', '-', 'o'}, {'o', 'o', '-'}, {'-', '-', '-'} }, |
||||
|
1, 'o', 2)); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
void invalidIndexCausesIndexOutOfBoundsException() { |
||||
|
int index = 2; |
||||
|
char charToCount = 'o'; |
||||
|
String msg = "Only 0 and 1 are allowed values for index!"; |
||||
|
doReturn(new char[][] { {'-', '-', '-'}, {'-', '-', '-'}, {'-', '-', '-'} }).when(gl).getBoard(); |
||||
|
|
||||
|
AIHard ai = new AIHard(gl); |
||||
|
|
||||
|
assertThatThrownBy(() -> {ai.countCharsInDiag(index, charToCount);}).isInstanceOf(IndexOutOfBoundsException.class).hasMessage(msg); |
||||
|
} |
||||
|
|
||||
|
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue