Skip to content

Method: TicTacToeEvaluation()

1: package evaluation;
2:
3: import java.util.ArrayList;
4: import java.util.List;
5: import java.util.Set;
6:
7: import de.fhdw.gaming.ipspiel21.searchtrees.domain.Evaluation;
8: import de.fhdw.gaming.ipspiel21.tictactoe.core.domain.TicTacToeBoard;
9: import de.fhdw.gaming.ipspiel21.tictactoe.core.domain.TicTacToeField;
10: import de.fhdw.gaming.ipspiel21.tictactoe.core.domain.TicTacToeFieldState;
11: import de.fhdw.gaming.ipspiel21.tictactoe.core.domain.TicTacToePlayer;
12: import de.fhdw.gaming.ipspiel21.tictactoe.core.domain.TicTacToePosition;
13: import de.fhdw.gaming.ipspiel21.tictactoe.core.domain.TicTacToeRow;
14: import de.fhdw.gaming.ipspiel21.tictactoe.core.domain.TicTacToeState;
15:
16: /**
17: * Evaluation function for a Tic-Tac-Toe Game.
18: *
19: * @param <S>
20: * @param <P>
21: */
22: public class TicTacToeEvaluation<P extends TicTacToePlayer, S extends TicTacToeState>
23: implements Evaluation<TicTacToePlayer, TicTacToeState> {
24: /**
25: * Evaluates the board situation from the side of the current player.
26: *
27: * @param state The current game state.
28: * @return The evaluated score.
29: */
30:
31: @Override
32: public Double evaluate(final TicTacToePlayer player, final TicTacToeState state) {
33:
34: double crossPlayerSum = 0;
35: double noughtPlayerSum = 0;
36:
37: crossPlayerSum += this.evaluateUniformlyMarkedRows(state.getBoard(), TicTacToeFieldState.CROSS);
38: noughtPlayerSum += this.evaluateUniformlyMarkedRows(state.getBoard(), TicTacToeFieldState.NOUGHT);
39:
40: if (crossPlayerSum != 100 && noughtPlayerSum != 100) {
41:
42: crossPlayerSum += this.evaluateMiddle(state.getBoard(), TicTacToeFieldState.CROSS);
43: noughtPlayerSum += this.evaluateMiddle(state.getBoard(), TicTacToeFieldState.NOUGHT);
44:
45: crossPlayerSum += this.evaluateCorners(state.getBoard(), TicTacToeFieldState.CROSS);
46: noughtPlayerSum += this.evaluateCorners(state.getBoard(), TicTacToeFieldState.NOUGHT);
47:
48: crossPlayerSum += this.evaluateNextToMids(state.getBoard(), TicTacToeFieldState.CROSS);
49: noughtPlayerSum += this.evaluateNextToMids(state.getBoard(), TicTacToeFieldState.NOUGHT);
50:
51: crossPlayerSum += this.evaluateWinningChances(state.getBoard(), TicTacToeFieldState.CROSS);
52: noughtPlayerSum += this.evaluateWinningChances(state.getBoard(), TicTacToeFieldState.NOUGHT);
53:
54: }
55:
56: if (player.isUsingCrosses()) {
57: return crossPlayerSum - noughtPlayerSum;
58: } else {
59: return noughtPlayerSum - crossPlayerSum;
60:
61: }
62: }
63:
64: /**
65: * Evaluates the middle field of the board. The evaluated score is 4, when usedMark equals the state of the middle
66: * field. Otherwise it is 0;
67: *
68: * @param board The game board.
69: * @param usedMark The player mark to be evaluated. Either cross or nought.
70: * @return The evaluated score.
71: */
72: private double evaluateMiddle(final TicTacToeBoard board, final TicTacToeFieldState usedMark) {
73: if (board.getFieldAt(TicTacToePosition.of(1, 1)).getState().equals(usedMark)) {
74: return 4;
75: } else {
76: return 0;
77: }
78: }
79:
80: /**
81: * Evaluates the four corner fields of the board.
82: *
83: * @param board The game board.
84: * @param usedMark The player mark to be evaluated. Either cross or nought.
85: * @return The evaluated score.
86: */
87: private double evaluateCorners(final TicTacToeBoard board, final TicTacToeFieldState usedMark) {
88: double cornerValue = 0;
89:
90: cornerValue += this.evaluateCorner(board.getFieldAt(TicTacToePosition.of(0, 0)), usedMark);
91: cornerValue += this.evaluateCorner(board.getFieldAt(TicTacToePosition.of(0, 2)), usedMark);
92: cornerValue += this.evaluateCorner(board.getFieldAt(TicTacToePosition.of(2, 0)), usedMark);
93: cornerValue += this.evaluateCorner(board.getFieldAt(TicTacToePosition.of(2, 2)), usedMark);
94:
95: return cornerValue;
96: }
97:
98: /**
99: * Evaluates a corner field of the board. The evaluated score is 3, when usedMark equals the state of the corner
100: * field. Otherwise it is 0;
101: *
102: * @param field The field that is evaluated.
103: * @param usedMark The player mark to be evaluated. Either cross or nought.
104: * @return The evaluated score.
105: */
106: private double evaluateCorner(final TicTacToeField field, final TicTacToeFieldState usedMark) {
107: if (field.getState().equals(usedMark)) {
108: return 3;
109: } else {
110: return 0;
111: }
112: }
113:
114: /**
115: * Evaluates the four fields that are next to the middle field of the board.
116: *
117: * @param board The game board.
118: * @param usedMark The player mark to be evaluated. Either cross or nought.
119: * @return The evaluated score.
120: */
121: private double evaluateNextToMids(final TicTacToeBoard board, final TicTacToeFieldState usedMark) {
122: double nexttoMidValue = 0;
123:
124: nexttoMidValue += this.evaluateNexttoMid(board.getFieldAt(TicTacToePosition.of(0, 1)), usedMark);
125: nexttoMidValue += this.evaluateNexttoMid(board.getFieldAt(TicTacToePosition.of(1, 0)), usedMark);
126: nexttoMidValue += this.evaluateNexttoMid(board.getFieldAt(TicTacToePosition.of(1, 2)), usedMark);
127: nexttoMidValue += this.evaluateNexttoMid(board.getFieldAt(TicTacToePosition.of(2, 1)), usedMark);
128:
129: return nexttoMidValue;
130: }
131:
132: /**
133: * Evaluates a field that is next to the middle field of the board. The evaluated score is 2, when usedMark equals
134: * the state of the corner field. Otherwise it is 0;
135: *
136: * @param field The field that is evaluated.
137: * @param usedMark The player mark to be evaluated. Either cross or nought.
138: * @return The evaluated score.
139: */
140: private double evaluateNexttoMid(final TicTacToeField field, final TicTacToeFieldState usedMark) {
141: if (field.getState().equals(usedMark)) {
142: return 2;
143: } else {
144: return 0;
145: }
146: }
147:
148: /**
149: * Evaluates uniformly marked rows of the board. The evaluated score is 100, when there is a uniformly marked row
150: * and this row state equals the usedMark. Otherwise it is 0;
151: *
152: * @param board The game board.
153: * @param usedMark The player mark to be evaluated. Either cross or nought.
154: * @return The evaluated score.
155: */
156: private double evaluateUniformlyMarkedRows(final TicTacToeBoard board, final TicTacToeFieldState usedMark) {
157: final Set<TicTacToeRow> rows = board.getRowsUniformlyMarked();
158: if (!rows.isEmpty() && rows.iterator().next().getState().isPresent()
159: && rows.iterator().next().getState().get().equals(usedMark)) {
160: return 100;
161: } else {
162: return 0;
163: }
164: }
165:
166: /**
167: * Evaluates all rows, columns and diagonals by possible winning chances.
168: *
169: * @param board The game board.
170: * @param usedMark The player mark to be evaluated. Either cross or nought.
171: * @return The evaluated score.
172: */
173: private double evaluateWinningChances(final TicTacToeBoard board, final TicTacToeFieldState usedMark) {
174: double sum = 0;
175: final List<List<? extends TicTacToeField>> list = new ArrayList<>();
176: list.add(this.getRowOfBoard(board, 0));
177: list.add(this.getRowOfBoard(board, 1));
178: list.add(this.getRowOfBoard(board, 2));
179: list.add(this.getColumnOfBoard(board, 0));
180: list.add(this.getColumnOfBoard(board, 1));
181: list.add(this.getColumnOfBoard(board, 2));
182: list.add(this.getDiagonalOfBoard(board, true));
183: list.add(this.getDiagonalOfBoard(board, false));
184:
185: for (final List<? extends TicTacToeField> current : list) {
186: sum += this.evaluateWinningChance(current, usedMark);
187: }
188: return sum;
189: }
190:
191: /**
192: * Gets a row of the board by index.
193: *
194: * @param board The game board.
195: * @param index The index of the row.
196: * @return The List of fields, that are in the row.
197: */
198: private List<? extends TicTacToeField> getRowOfBoard(final TicTacToeBoard board, final int index) {
199: return board.getFields().get(index);
200: }
201:
202: /**
203: * Gets a column of the board by index.
204: *
205: * @param board The game board.
206: * @param index The index of the column.
207: * @return The List of fields, that are in the column.
208: */
209: private List<? extends TicTacToeField> getColumnOfBoard(final TicTacToeBoard board, final int index) {
210: final List<TicTacToeField> list = new ArrayList<>();
211: for (int i = 0; i < 3; i++) {
212: list.add(board.getFieldAt(TicTacToePosition.of(i, index)));
213: }
214: return list;
215: }
216:
217: /**
218: * Gets a diagonal of the board.
219: *
220: * @param board The game board.
221: * @param topLeftToBottomRight When true, it refers to the diagonal from the top left corner to the bottom right
222: * corner. When false, it refers to the other diagonal.
223: * @return The List of fields, that are in the referred diagonal.
224: */
225: private List<? extends TicTacToeField> getDiagonalOfBoard(final TicTacToeBoard board,
226: final boolean topLeftToBottomRight) {
227: final List<TicTacToeField> list = new ArrayList<>();
228: if (topLeftToBottomRight) {
229: for (int i = 0; i < 3; i++) {
230: list.add(board.getFieldAt(TicTacToePosition.of(i, i)));
231: }
232: } else {
233: list.add(board.getFieldAt(TicTacToePosition.of(2, 0)));
234: list.add(board.getFieldAt(TicTacToePosition.of(1, 1)));
235: list.add(board.getFieldAt(TicTacToePosition.of(0, 2)));
236: }
237: return list;
238: }
239:
240: /**
241: * Evaluates a winning chance. There is a possible winning chance, when two of the three field states equal the
242: * usedMark and the last field state equals empty. When there is a possible winning chance the evaluated score is
243: * 10. Otherwise it is 0;
244: *
245: * @param fields A list of the fields to evaluate the possible winning chance.
246: * @param usedMark The player mark to be evaluated. Either cross or nought.
247: * @return The evaluated score.
248: */
249: private double evaluateWinningChance(final List<? extends TicTacToeField> fields,
250: final TicTacToeFieldState usedMark) {
251: int usedMarks = 0;
252: int emptys = 0;
253: for (final TicTacToeField field : fields) {
254: if (field.getState().equals(usedMark)) {
255: usedMarks += 1;
256: } else if (field.getState().equals(TicTacToeFieldState.EMPTY)) {
257: emptys += 1;
258: }
259: }
260:
261: if (usedMarks == 2 && emptys == 1) {
262: return 10;
263: } else {
264: return 0;
265: }
266: }
267:
268: }