package de.fhdw.gaming.ipspiel22.vierGewinnt.strategy;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import de.fhdw.gaming.ipspiel22.vierGewinnt.moves.VGMove;
import de.fhdw.gaming.ipspiel22.vierGewinnt.moves.impl.AbstractVGMove;
import de.fhdw.gaming.ipspiel22.vierGewinnt.moves.impl.VG1ColumnMove;
import de.fhdw.gaming.ipspiel22.vierGewinnt.moves.impl.VG2ColumnMove;
import de.fhdw.gaming.ipspiel22.vierGewinnt.moves.impl.VG3ColumnMove;
import de.fhdw.gaming.ipspiel22.vierGewinnt.moves.impl.VG4ColumnMove;
import de.fhdw.gaming.ipspiel22.vierGewinnt.moves.impl.VG5ColumnMove;
import de.fhdw.gaming.ipspiel22.vierGewinnt.moves.impl.VG6ColumnMove;
import de.fhdw.gaming.ipspiel22.vierGewinnt.moves.impl.VG7ColumnMove;
import de.fhdw.gaming.core.domain.GameException;
import de.fhdw.gaming.ipspiel22.searchtree.domain.MinMaxGame;
import de.fhdw.gaming.ipspiel22.vierGewinnt.domain.VGAnswerEnum;
import de.fhdw.gaming.ipspiel22.vierGewinnt.domain.VGBoard;
import de.fhdw.gaming.ipspiel22.vierGewinnt.domain.VGField;
import de.fhdw.gaming.ipspiel22.vierGewinnt.domain.VGFieldState;
import de.fhdw.gaming.ipspiel22.vierGewinnt.domain.VGPlayer;
import de.fhdw.gaming.ipspiel22.vierGewinnt.domain.VGPosition;
import de.fhdw.gaming.ipspiel22.vierGewinnt.domain.VGState;
import de.fhdw.gaming.ipspiel22.vierGewinnt.domain.impl.VGBoardImpl;
/**
* MinMaxViergewinnt.
*/
public class MinMaxViergewinnt implements MinMaxGame<VGPlayer, VGState, VGMove> {
/**
* Current given state.
*/
private final VGState vgState;
/**
* Current given state.
*/
private final VGBoard inistialBoard;
/**
* Current given player.
*/
private final VGPlayer player;
/**
* Playercolour for algorithm.
*/
private Boolean playercolour;
/**
* Saved Move.
*/
private VGMove savedMove;
/**
* For creating MinMaxViergewinnt.
*
* @param state Current given state.
* @param player Current given player.
*/
public MinMaxViergewinnt(final VGState state, final VGPlayer player) {
this.vgState = state;
this.player = player;
this.playercolour = player.isUsingRedChips();
this.inistialBoard = new VGBoardImpl((VGBoardImpl) state.getBoard());
}
@Override
public double evaluateStateful() {
double stateful;
final VGFieldState state = playercolour ? VGFieldState.RED : VGFieldState.YELLOW;
final VGFieldState otherPlayerState = !playercolour ? VGFieldState.RED : VGFieldState.YELLOW;
final List<Double> checkedStates = new ArrayList<>();
if (this.playercolour == this.player.isUsingRedChips()) {
checkedStates.addAll(twoCombo(vgState.getBoard(), state, 6.0));
checkedStates.addAll(threeCombo(vgState.getBoard(), state, 12.0));
checkedStates.addAll(fourCombo(vgState.getBoard(), state, 24.0));
stateful = Collections.max(checkedStates);
stateful += checkMiddle(vgState.getBoard(), playercolour);
final List<Double> firstPossibleMoves = new ArrayList<>();
firstPossibleMoves.add(ifThreeWithMove(otherPlayerState,
!this.player.isUsingRedChips()));
firstPossibleMoves.add(ifThreeWithMove(state, this.player.isUsingRedChips()));
firstPossibleMoves.add(ifWonWithMove(otherPlayerState,
!this.player.isUsingRedChips()));
firstPossibleMoves.add(ifWonWithMove(state, this.player.isUsingRedChips()));
stateful += Collections.max(firstPossibleMoves);
} else {
checkedStates.addAll(threeCombo(vgState.getBoard(), state, 5.0));
checkedStates.addAll(fourCombo(vgState.getBoard(), state, 20.0));
stateful = Collections.max(checkedStates);
}
return stateful;
}
@Override
public boolean isGameOver() {
final List<VGField> allFields = new ArrayList<>();
vgState.getBoard().getFields().forEach(allFields::addAll);
if (allFields.stream().noneMatch(vgField -> vgField.getState().equals(VGFieldState.EMPTY))) {
return true;
} else {
return checkWinner(vgState.getBoard(), playercolour); // checkWinner(!playercolour);
}
}
@Override
public List<VGMove> getPossibleMoves() {
final List<VGMove> movesList = new ArrayList<>();
if (Objects.nonNull(vgState.getBoard().getNextFieldInColumn(VGAnswerEnum.FIRSTCOLUMN))) {
movesList.add(new VG1ColumnMove());
}
if (Objects.nonNull(vgState.getBoard().getNextFieldInColumn(VGAnswerEnum.SECONDCOLUMN))) {
movesList.add(new VG2ColumnMove());
}
if (Objects.nonNull(vgState.getBoard().getNextFieldInColumn(VGAnswerEnum.THIRDCOLUMN))) {
movesList.add(new VG3ColumnMove());
}
if (Objects.nonNull(vgState.getBoard().getNextFieldInColumn(VGAnswerEnum.FOURTHCOLUMN))) {
movesList.add(new VG4ColumnMove());
}
if (Objects.nonNull(vgState.getBoard().getNextFieldInColumn(VGAnswerEnum.FITFHCOLUMN))) {
movesList.add(new VG5ColumnMove());
}
if (Objects.nonNull(vgState.getBoard().getNextFieldInColumn(VGAnswerEnum.SIXTHCOLUMN))) {
movesList.add(new VG6ColumnMove());
}
if (Objects.nonNull(vgState.getBoard().getNextFieldInColumn(VGAnswerEnum.SEVENTHCOLUMN))) {
movesList.add(new VG7ColumnMove());
}
Collections.shuffle(movesList);
return movesList;
}
@Override
public void commitMove(final VGMove move) throws GameException {
final VGPlayer playerMove = this.playercolour ? vgState.getRedPlayer() : vgState.getYellowPlayer();
move.applyTo(this.vgState, playerMove);
this.playercolour = !this.playercolour;
}
@Override
public void rollbackMove(final VGMove move) {
if (move instanceof AbstractVGMove) {
vgState.getBoard().getFields().get(((AbstractVGMove) move).getColumnInt() - 1);
int count = 0;
VGField field = vgState.getBoard().getFields().get(((AbstractVGMove) move)
.getColumnInt() - 1).get(count);
while (!field.getState().equals(VGFieldState.EMPTY) && count < 5) {
count += 1;
field = vgState.getBoard().getFields().get(((AbstractVGMove) move)
.getColumnInt() - 1).get(count);
}
vgState.getBoard().getFields().get(((AbstractVGMove) move).getColumnInt() - 1)
.get(count == 0 ? count : count - 1)
.setState(VGFieldState.EMPTY);
}
this.playercolour = !this.playercolour;
}
@Override
public VGMove saveFirstMoves(final VGMove move) {
this.savedMove = move;
return move;
}
/**
* If possible to win with next move.
*
* @param state
* @param playercolor
* @return
*/
private double ifWonWithMove(final VGFieldState state, final Boolean playercolor) {
final double value = playercolor == player.isUsingRedChips() ? 100.0 : 50.0;
return letsWinIfPossible(state, getMoveAsAnswer(this.savedMove),
playercolor) ? value : 0;
}
/**
* converts Move to Answer.
*
* @param move
* @return
*/
private VGAnswerEnum getMoveAsAnswer(final VGMove move) {
if (move instanceof VG1ColumnMove) {
return VGAnswerEnum.FIRSTCOLUMN;
} else if (move instanceof VG2ColumnMove) {
return VGAnswerEnum.SECONDCOLUMN;
} else if (move instanceof VG3ColumnMove) {
return VGAnswerEnum.THIRDCOLUMN;
} else if (move instanceof VG4ColumnMove) {
return VGAnswerEnum.FOURTHCOLUMN;
} else if (move instanceof VG5ColumnMove) {
return VGAnswerEnum.FITFHCOLUMN;
} else if (move instanceof VG6ColumnMove) {
return VGAnswerEnum.SIXTHCOLUMN;
} else {
return VGAnswerEnum.SEVENTHCOLUMN;
}
}
/**
* If possible to have three chips in next move.
*
* @param state
* @param playercolor
* @return
*/
private double ifThreeWithMove(final VGFieldState state, final boolean playercolor) {
final double value = playercolor == player.isUsingRedChips() ? 20.0 : 10.0;
return putThreeIfPossible(state, getMoveAsAnswer(this.savedMove)) ? value : 0;
}
/**
* Win if possible.
*
* @param state
* @param column
* @param playercolor
* @return
*/
private Boolean letsWinIfPossible(final VGFieldState state, final VGAnswerEnum column,
final Boolean playercolor) {
final VGField field = inistialBoard.getNextFieldInColumn(column);
Boolean won = false;
if (Objects.nonNull(field)) {
field.setState(state);
won = checkWinner(inistialBoard, playercolor);
field.setState(VGFieldState.EMPTY);
}
return won;
}
/**
* Three if possible.
*
* @param state
* @param column
* @return
*/
private Boolean putThreeIfPossible(final VGFieldState state, final VGAnswerEnum column) {
final VGField field = inistialBoard.getNextFieldInColumn(column);
Boolean won = false;
if (Objects.nonNull(field)) {
field.setState(state);
won = Collections.max(threeCombo(inistialBoard, state, 20.0)) > 0;
field.setState(VGFieldState.EMPTY);
}
return won;
}
// -----------------------------------------------------------------------------------------------------
/**
* Returns boolean, if Player has won.
*
* @param board
* @param usingRedChips The current player.
*/
private boolean checkWinner(final VGBoard board, final Boolean usingRedChips) {
final VGFieldState state = usingRedChips ? VGFieldState.RED : VGFieldState.YELLOW;
return checkHorizontaleLeftToRight(board, state, state, state, state)
|| checkVertikaleDownUp(board, state, state, state, state)
|| checkDiagonaleDownLeftUpRight(board, state, state, state, state)
|| checkDiagonaleDownRightUpLeftght(board, state, state, state, state);
}
/**
* Checks condition, if true return rating else 0.0.
* @param condition
* @param rating
* @return
*/
private Double checkCondition(final Boolean condition, final Double rating) {
return condition ? rating : 0.0;
}
/**
* Checks if a two combo exists.
*
* @param board
* @param state
* @param rating
* @return
*/
public List<Double> twoCombo(final VGBoard board, final VGFieldState state,
final Double rating) {
final List<Double> checkedStates = new ArrayList<>();
checkedStates
.add(checkCondition(checkDiagonaleDownLeftUpRight(board, VGFieldState.EMPTY, state,
VGFieldState.EMPTY, state), rating));
checkedStates.add(
checkCondition(checkDiagonaleDownRightUpLeftght(board, VGFieldState.EMPTY, state,
VGFieldState.EMPTY, state), rating));
checkedStates
.add(checkCondition(checkHorizontaleLeftToRight(board, VGFieldState.EMPTY, state,
VGFieldState.EMPTY, state), rating));
checkedStates
.add(checkCondition(checkDiagonaleDownLeftUpRight(board, state, VGFieldState.EMPTY,
state, VGFieldState.EMPTY), rating));
checkedStates.add(
checkCondition(checkDiagonaleDownRightUpLeftght(board, state, VGFieldState.EMPTY,
state, VGFieldState.EMPTY), rating));
checkedStates
.add(checkCondition(checkHorizontaleLeftToRight(board, state, VGFieldState.EMPTY,
state, VGFieldState.EMPTY), rating));
checkedStates
.add(checkCondition(checkDiagonaleDownLeftUpRight(board, state, state, VGFieldState.EMPTY,
VGFieldState.EMPTY), rating));
checkedStates.add(
checkCondition(checkDiagonaleDownRightUpLeftght(board, state, state, VGFieldState.EMPTY,
VGFieldState.EMPTY), rating));
checkedStates
.add(checkCondition(checkHorizontaleLeftToRight(board, state, state, VGFieldState.EMPTY,
VGFieldState.EMPTY), rating));
checkedStates
.add(checkCondition(checkDiagonaleDownLeftUpRight(board, state, VGFieldState.EMPTY,
VGFieldState.EMPTY, state), rating));
checkedStates.add(
checkCondition(checkDiagonaleDownRightUpLeftght(board, state, VGFieldState.EMPTY,
VGFieldState.EMPTY, state), rating));
checkedStates
.add(checkCondition(checkHorizontaleLeftToRight(board, state, VGFieldState.EMPTY,
VGFieldState.EMPTY, state), rating));
checkedStates
.add(checkCondition(checkDiagonaleDownLeftUpRight(board, VGFieldState.EMPTY,
VGFieldState.EMPTY, state, state), rating));
checkedStates.add(
checkCondition(checkDiagonaleDownRightUpLeftght(board, VGFieldState.EMPTY,
VGFieldState.EMPTY, state, state), rating));
checkedStates
.add(checkCondition(checkHorizontaleLeftToRight(board, VGFieldState.EMPTY,
VGFieldState.EMPTY, state, state), rating));
checkedStates
.add(checkCondition(checkVertikaleDownUp(board, state, state, VGFieldState.EMPTY,
VGFieldState.EMPTY), rating));
return checkedStates;
}
/**
* Checks if three Combo exists.
*
* @param board
* @param state
* @param rating
* @return
*/
public List<Double> threeCombo(final VGBoard board, final VGFieldState state, final Double rating) {
// Drei gewinnt
final List<Double> checkedStates = new ArrayList<>();
checkedStates.add(checkCondition(checkDiagonaleDownLeftUpRight(board, state, state,
VGFieldState.EMPTY, state), rating));
checkedStates
.add(checkCondition(checkDiagonaleDownRightUpLeftght(board, state, state,
VGFieldState.EMPTY, state), rating));
checkedStates.add(checkCondition(checkHorizontaleLeftToRight(board, state, state,
VGFieldState.EMPTY, state), rating));
checkedStates.add(checkCondition(checkDiagonaleDownLeftUpRight(board, state,
VGFieldState.EMPTY, state, state), rating));
checkedStates
.add(checkCondition(checkDiagonaleDownRightUpLeftght(board, state,
VGFieldState.EMPTY, state, state), rating));
checkedStates.add(checkCondition(checkHorizontaleLeftToRight(board, state,
VGFieldState.EMPTY, state, state), rating));
checkedStates.add(checkCondition(checkDiagonaleDownLeftUpRight(board,
VGFieldState.EMPTY, state, state, state), rating));
checkedStates
.add(checkCondition(checkDiagonaleDownRightUpLeftght(board,
VGFieldState.EMPTY, state, state, state), rating));
checkedStates.add(checkCondition(checkHorizontaleLeftToRight(board,
VGFieldState.EMPTY, state, state, state), rating));
checkedStates.add(checkCondition(checkDiagonaleDownLeftUpRight(board, state,
state, state, VGFieldState.EMPTY), rating));
checkedStates.add(checkCondition(checkVertikaleDownUp(board, state, state,
state, VGFieldState.EMPTY), rating));
checkedStates
.add(checkCondition(checkDiagonaleDownRightUpLeftght(board, state,
state, state, VGFieldState.EMPTY), rating));
checkedStates.add(checkCondition(checkHorizontaleLeftToRight(board, state,
state, state, VGFieldState.EMPTY), rating));
return checkedStates;
}
/**
* If four combo exists.
*
* @param board
* @param state
* @param rating
* @return
*/
public List<Double> fourCombo(final VGBoard board, final VGFieldState state, final Double rating) {
// Vier gewinnt
final List<Double> checkedStates = new ArrayList<>();
checkedStates.add(checkDiagonaleDownLeftUpRight(board, state, state, state, state) ? rating : 0.0);
checkedStates.add(checkVertikaleDownUp(board, state, state, state, state) ? rating : 0.0);
checkedStates.add(checkDiagonaleDownRightUpLeftght(board, state, state, state, state) ? rating : 0.0);
checkedStates.add(checkHorizontaleLeftToRight(board, state, state, state, state) ? rating : 0.0);
return checkedStates;
}
/**
* Extra Points for chips in the middle.
*
* @param board
* @param usingRedChips
* @return result
*/
private double checkMiddle(final VGBoard board, final boolean usingRedChips) {
double result = 0.0;
final VGFieldState state = usingRedChips ? VGFieldState.RED : VGFieldState.YELLOW;
result += board.getFields().get(2).stream().filter(field -> field.getState().equals(state))
.mapToDouble(field -> {
return 2.0;
}).sum(); // Dritte Spalte
result += board.getFields().get(3).stream().filter(field -> field.getState().equals(state))
.mapToDouble(field -> {
return 5.0;
}).sum(); // Vierte Spalte
result += board.getFields().get(4).stream().filter(field -> field.getState().equals(state))
.mapToDouble(field -> {
return 2.0;
}).sum(); // Fünfte Spa 0;
return result;
}
/**
* Checks horizontal.
*
* @param board
* @param chip1
* @param chip2
* @param chip3
* @param chip4
* @return
*/
private Boolean checkHorizontaleLeftToRight(final VGBoard board, final VGFieldState chip1,
final VGFieldState chip2,
final VGFieldState chip3, final VGFieldState chip4) {
for (int i = 0; i < board.getRows(); i++) {
for (int j = 0; j < 4; j++) {
if (board.getFieldAt(VGPosition.of(j, i)).getState().equals(chip1)
&& board.getFieldAt(VGPosition.of(j + 1, i)).getState().equals(chip2)
&& board.getFieldAt(VGPosition.of(j + 2, i)).getState().equals(chip3)
&& board.getFieldAt(VGPosition.of(j + 3, i)).getState().equals(chip4)) {
return true;
}
}
}
return false;
}
/**
* checks vertical.
*
* @param board
* @param chip1
* @param chip2
* @param chip3
* @param chip4
* @return
*/
private Boolean checkVertikaleDownUp(final VGBoard board, final VGFieldState chip1, final VGFieldState chip2,
final VGFieldState chip3,
final VGFieldState chip4) {
for (int i = 0; i < board.getColumns(); i++) {
for (int j = 0; j < 3; j++) {
if (board.getFieldAt(VGPosition.of(i, j)).getState().equals(chip1)
&& board.getFieldAt(VGPosition.of(i, j + 1)).getState().equals(chip2)
&& board.getFieldAt(VGPosition.of(i, j + 2)).getState().equals(chip3)
&& board.getFieldAt(VGPosition.of(i, j + 3)).getState().equals(chip4)) {
return true;
}
}
}
return false;
}
/**
* checks diagonale down left to up right.
*
* @param board
* @param chip1
* @param chip2
* @param chip3
* @param chip4
* @return
*/
private Boolean checkDiagonaleDownLeftUpRight(final VGBoard board, final VGFieldState chip1,
final VGFieldState chip2,
final VGFieldState chip3, final VGFieldState chip4) {
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 4; j++) {
if (board.getFieldAt(VGPosition.of(j, i)).getState().equals(chip1)
&& board.getFieldAt(VGPosition.of(j + 1, i + 1)).getState()
.equals(chip2)
&& board.getFieldAt(VGPosition.of(j + 2, i + 2)).getState()
.equals(chip3)
&& board.getFieldAt(VGPosition.of(j + 3, i + 3)).getState()
.equals(chip4)) {
return true;
}
}
}
return false;
}
/**
* checks diagonle down right to up left.
*
* @param board
* @param chip1
* @param chip2
* @param chip3
* @param chip4
* @return
*/
private Boolean checkDiagonaleDownRightUpLeftght(final VGBoard board, final VGFieldState chip1,
final VGFieldState chip2, final VGFieldState chip3, final VGFieldState chip4) {
for (int i = 0; i < 3; i++) {
for (int j = 3; j < 7; j++) {
if (board.getFieldAt(VGPosition.of(j, i)).getState().equals(chip1)
&& board.getFieldAt(VGPosition.of(j - 1, i + 1)).getState()
.equals(chip2)
&& board.getFieldAt(VGPosition.of(j - 2, i + 2)).getState()
.equals(chip3)
&& board.getFieldAt(VGPosition.of(j - 3, i + 3)).getState()
.equals(chip4)) {
return true;
}
}
}
return false;
}
}
The class 'MinMaxViergewinnt' has a total cyclomatic complexity of 81 (highest 8).
The complexity of methods directly affects maintenance costs and readability. Concentrating too much decisional logic
in a single method makes its behaviour hard to read and change.
Cyclomatic complexity assesses the complexity of a method by counting the number of decision points in a method,
plus one for the method entry. Decision points are places where the control flow jumps to another place in the
program. As such, they include all control flow statements, such as `if`, `while`, `for`, and `case`. For more
details on the calculation, see the documentation {% jdoc java::lang.java.metrics.JavaMetrics#CYCLO %}.
Generally, numbers ranging from 1-4 denote low complexity, 5-7 denote moderate complexity, 8-10 denote
high complexity, and 11+ is very high complexity. By default, this rule reports methods with a complexity >= 10.
Additionally, classes with many methods of moderate complexity get reported as well once the total of their
methods' complexities reaches 80, even if none of the methods was directly reported.
Reported methods should be broken down into several smaller methods. Reported classes should probably be broken down
into subcomponents.
class Foo {
void baseCyclo() { // Cyclo = 1
highCyclo();
}
void highCyclo() { // Cyclo = 10: reported!
int x = 0, y = 2;
boolean a = false, b = true;
if (a && (y == 1 ? b : true)) { // +3
if (y == x) { // +1
while (true) { // +1
if (x++ < 20) { // +1
break; // +1
}
}
} else if (y == t && !d) { // +2
x = a ? y : x; // +1
} else {
x = 2;
}
}
}
}