You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

262 lines
7.6 KiB

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);
}
}