Skip to contentMethod: getSearchDirection()
      1: package de.fhdw.gaming.ipspiel23.c4.domain.impl.evaluation;
2: 
3: import java.util.Set;
4: 
5: import de.fhdw.gaming.ipspiel23.c4.domain.C4Direction;
6: import de.fhdw.gaming.ipspiel23.c4.domain.IC4SolutionSlim;
7: import de.fhdw.gaming.ipspiel23.c4.domain.impl.C4BoardSlim;
8: import de.fhdw.gaming.ipspiel23.c4.domain.impl.C4SolutionSlim;
9: 
10: import static de.fhdw.gaming.ipspiel23.c4.domain.impl.C4BoardSlim.EMPTY_TOKEN;
11: 
12: /**
13:  * Represents a solution analyzer that scans the board in a specific direction.
14:  * <p>
15:  * Note: This is an internal API that may be subject to incompatible changes in future releases.
16:  * </p>
17:  */
18: public abstract class C4SolutionAnalyzer {
19:     
20:     /**
21:      * The direction in which the board is scanned.
22:      */
23:     private final C4Direction searchDirection;
24:     
25:     /**
26:      * The board that is scanned.
27:      */
28:     private final C4BoardSlim boardField;
29: 
30:     /**
31:      * A local copy of the board's row count for quicker access.
32:      */
33:     private final int rowMaxField;
34: 
35:     /**
36:      * A local copy of the board's column count for quicker access.
37:      */
38:     private final int colMaxField;
39: 
40:     /**
41:      * The internal number of tokens that need to be matched to form a solution.
42:      */
43:     private final int targetCountField;
44:     
45:     /**
46:      * Creates a new instance of {@link C4SolutionAnalyzer}.
47:      * @param board The board that is scanned.
48:      * @param searchDirection The direction in which the board is scanned.
49:      */
50:     protected C4SolutionAnalyzer(final C4BoardSlim board, final C4Direction searchDirection) {
51:         this.searchDirection = searchDirection;
52:         this.boardField = board;
53:         this.rowMaxField = board.getRowCount();
54:         this.colMaxField = board.getColumnCount();
55:         this.targetCountField = board.getMinimumSolutionSize() - 1;
56:     }
57:     
58:     /**
59:      * Lazily searches for the first solution on the board if the current solution is null.
60:      * @param currentSolution The current solution.
61:      * @param updateCache Whether to update the solution cache, preventing the same line to be checked again.
62:      * @return The first solution on the board or null if no solution was found.
63:      */
64:     public abstract IC4SolutionSlim tryFindFirstSolution(IC4SolutionSlim currentSolution, boolean updateCache);
65:     
66:     /**
67:      * Eagerly searches for all solutions on the board and adds them to the result set.
68:      * @param resultSet The result set to which the solutions are added, if any.
69:      * @param updateCache Whether to update the solution cache, preventing the same line to be checked again.
70:      */
71:     public abstract void findAllSolutions(Set<IC4SolutionSlim> resultSet, boolean updateCache);
72: 
73:     /**
74:      * The direction in which the board is scanned.
75:      */
76:     public C4Direction getSearchDirection() {
77:         return this.searchDirection;
78:     }
79:     
80:     /**
81:      * The board that is scanned.
82:      */
83:     protected C4BoardSlim board() {
84:         return this.boardField;
85:     }
86: 
87:     /**
88:      * Resets the internal cache.
89:      */
90:     public abstract void resetCache();
91:     
92:     /**
93:      * A local copy of the board's row count for quicker access.
94:      */
95:     protected int rowMax() {
96:         return this.rowMaxField;
97:     }
98:     
99:     /**
100:      * A local copy of the board's column count for quicker access.
101:      */
102:     protected int colMax() {
103:         return this.colMaxField;
104:     }
105:     
106:     /**
107:      * The internal number of tokens that need to be matched to form a solution.
108:      */
109:     protected int targetCount() {
110:         return this.targetCountField;
111:     }
112:     
113:     /**
114:      * Creates a new solution instance.
115:      * @param token The token that forms the solution.
116:      * @param matchEndRow The row index of the last token in the solution.
117:      * @param matchEndCol The column index of the last token in the solution.
118:      * @param size The number of tokens in the solution.
119:      * @return A new solution instance.
120:      */
121:     protected C4SolutionSlim solutionOf(final int token, final int matchEndRow, 
122:             final int matchEndCol, final int size) {
123:         return new C4SolutionSlim(boardField, token, matchEndRow, matchEndCol, this.searchDirection, size);
124:     }
125:     
126:     /**
127:      * Scans any additional tokens that belong to an already identified solution.
128:      * @param token The token that forms the solution.
129:      * @param row The row index of the last scanned token in the solution.
130:      * @param col The column index of the last scanned token in the solution.
131:      * @return The solution containing any additional tokens.
132:      */
133:     protected abstract C4SolutionSlim scanRemaining(int token, int row, int col);
134:     
135:     /**
136:      * Calculates the number of consecutive tokens with the same value without branching (prevent CPU pipeline stalls)
137:      * Returns count + 1 if the current token has the same value as the previous one AND 
138:      * the previous token was not 0, otherwise returns 0.
139:      * @param count The current number of consecutive tokens with the same value.
140:      * @param token The value of the current token.
141:      * @param previousToken The value of the previous token.
142:      * @return count + 1 if token == previousToken and previousToken != EMPTY_TOKEN else 0
143:      */
144:     protected static int countConsecutivesBranchless(final int count, final int token, final int previousToken) {
145:         // I hope this gets inlined by the JIT :)
146:         // this implements: return 0 + (token == previousToken && previousToken != EMPTY_TOKEN ? count + 1 : 0);
147:         // return 0 by default
148:         return 0
149:             // ... and add count + 1 ...
150:             + ((count + 1)
151:             // ... IFF the previous token has a value other than EMPTY_TOKEN ...
152:             & areNotEqualMask(previousToken, EMPTY_TOKEN)
153:             // ... AND the tokens are equal.
154:             & areEqualMask(token, previousToken));
155:     }
156: 
157:     /**
158:      * without branching, checks if {@code a} and {@code b} are not equal, returning the corresponding bit mask.
159:      * @param value1 the first value
160:      * @param value2 the other value
161:      * @return {@code -1 (TRUE)} iff a and b are not equal, {@code 0 (FALSE)} otherwise
162:      */
163:     protected static int areNotEqualMask(final int value1, final int value2) {
164:         return (-(value1 ^ value2)) >> 31;
165:     }
166: 
167:     /**
168:      * without branching, checks if {@code a} and {@code b} are equal, returning the corresponding bit mask.
169:      * @param value1 the first value
170:      * @param value2 the other value
171:      * @return {@code -1 (TRUE)} iff a and b are equal, {@code 0 (FALSE)} otherwise
172:      */
173:     protected static int areEqualMask(final int value1, final int value2) {
174:         return ~areNotEqualMask(value1, value2);
175:     }
176: }