Skip to contentMethod: getColumnCount()
      1: package de.fhdw.gaming.ipspiel23.c4.domain.impl.validation;
2: 
3: import de.fhdw.gaming.core.ui.InputProviderException;
4: 
5: /**
6:  * Represents the absolute limits, and currently set dimensions of a Connect-N board.
7:  * User-provided values are stored in this class, and validated against the limits.
8:  */
9: public class C4BoardLimits {
10: 
11:     /**
12:      * The minimum number of rows a board must have.
13:      */
14:     // for the incredible "Connect Two" experience where the first player wins by default after the second move :)
15:     public static final int MIN_ROW_COUNT = 2;
16: 
17:     /**
18:      * The minimum number of columns a board must have.
19:      */
20:     // for the incredible "Connect Two" experience where the first player wins by default after the second move :)
21:     public static final int MIN_COLUMN_COUNT = 2;
22: 
23:     /**
24:      * The maximum number of rows a board can have.
25:      */
26:     // these are theoretical maximums. In the end, actualColumnCount * actualRowCount must be <= MAX_FIELD_COUNT
27:     public static final int MAX_ROW_COUNT = Integer.MAX_VALUE / MIN_COLUMN_COUNT;
28: 
29:     /**
30:      * The maximum number of columns a board can have.
31:      */
32:     // these are theoretical maximums. In the end, actualColumnCount * actualRowCount must be <= MAX_FIELD_COUNT
33:     public static final int MAX_COLUMN_COUNT = Integer.MAX_VALUE / MIN_ROW_COUNT;
34: 
35:     /**
36:      * The maximum number of fields a board can have.
37:      */
38:     // we use 32 bit indices for our internal token-state array, so if more than 4 * 2^31 - 1 = 8 GiB of RAM are 
39:     // available we can use the whole 31 bit range for indexing (also: why doesn't java have unsigned integers???).
40:     // fun fact:
41:     // at 1 second per move a draw could actually happen within our life time at
42:     // just over 68 Years of 24/7 exciting Connect-N gameplay :)
43:     public static final int MAX_FIELD_COUNT = Integer.MAX_VALUE;
44: 
45:     /**
46:      * The minimum allowed number that can be set as the number of tokens that need to be matched to form a solution.
47:      */
48:     // "Connect One" wouldn't be too exciting TBH. Also I don't think our board-state-validation algorithm would 
49:     // support it.
50:     public static final int MIN_SOLUTION_SIZE = 2;
51: 
52:     /**
53:      * The maximum allowed number that can be set as the number of tokens that need to be matched to form a solution.
54:      */
55:     // Its probably a good idea to keep the solution size below the maximum theoretical board boundaries
56:     public static final int MAX_SOLUTION_SIZE = Math.max(MAX_ROW_COUNT, MAX_COLUMN_COUNT);
57: 
58:     /**
59:      * The minimum number of players that can play on a board.
60:      */
61:     // to me it's questionable how exciting a Connect Four game with a single player would be,
62:     // but who am I to judge what gets other people thrilled ¯\_(ツ)_/¯
63:     public static final int MIN_NUMBER_OF_PLAYERS = 1;
64: 
65:     /**
66:      * The maximum number of players that can play on a board.
67:      */
68:     // let's assume we actually want each player to have at least the theoretical possibility to win
69:     // fun fact:
70:     // at the theoretical maximum we would still would support 33.5 times more players than RGB24 has colors available
71:     // to differentiate between the pieces :P
72:     // so the GUI team would have to start playing around with the alpha channel in RGB32 to give every player a unique
73:     // color.
74:     // also: assuming 16:9 aspect ratio you'd have to own at least a 30897x17379 px monitor to display the whole board
75:     // at once, so a 32k display would *just* not suffice at "only" 30720x17280 px :)
76:     public static final int MAX_NUMBER_OF_PLAYERS = C4BoardLimits.MAX_FIELD_COUNT / C4BoardLimits.MIN_SOLUTION_SIZE;
77: 
78:     /**
79:      * The currently specified board dimensions.
80:      */
81:     private final C4BoardDimensions boardDimensions;
82: 
83:     /**
84:      * The currently specified number of tokens that need to be matched to form a solution.
85:      */
86:     private int solutionSize;
87: 
88:     /**
89:      * The total number of players that will play on the board.
90:      */
91:     private final int playerCount;
92: 
93:     /**
94:      * Creates a new instance of {@link C4BoardLimits}.
95:      * @param playerCount the actual number of players that will play on the board
96:      * @param initialRowCount the initial number of rows the board will have
97:      * @param initialColumnCount the initial number of columns the board will have
98:      * @param initialSolutionSize the initial number of tokens that need to be matched to form a solution
99:      */
100:     public C4BoardLimits(final int playerCount, final int initialRowCount,
101:             final int initialColumnCount, final int initialSolutionSize) {
102:         this.playerCount = playerCount;
103:         this.boardDimensions = new C4BoardDimensions(initialRowCount, initialColumnCount);
104:         this.solutionSize = initialSolutionSize;
105:     }
106: 
107:     /**
108:      * Returns the number of players that will play on the board.
109:      */
110:     public int getPlayerCount() {
111:         return playerCount;
112:     }
113: 
114:     /**
115:      * Returns the number of rows the board will have.
116:      */
117:     public int getRowCount() {
118:         return this.boardDimensions.getRowCount();
119:     }
120: 
121:     /**
122:      * Updates the number of rows the board will have to the specified value.
123:      * @param rowCount the new number of rows the board will have
124:      */
125:     public void setRowCount(final int rowCount) {
126:         this.boardDimensions.setRowCount(rowCount);
127:     }
128: 
129:     /**
130:      * Returns the number of columns the board will have.
131:      */
132:     public int getColumnCount() {
133:         return this.boardDimensions.getColumnCount();
134:     }
135: 
136:     /**
137:      * Updates the number of columns the board will have to the specified value.
138:      * @param columnCount the new number of columns the board will have
139:      */
140:     public void setColumnCount(final int columnCount) {
141:         this.boardDimensions.setColumnCount(columnCount);
142:     }
143: 
144:     /**
145:      * Returns the number of tokens that need to be matched to form a solution.
146:      */
147:     public int getSolutionSize() {
148:         return solutionSize;
149:     }
150: 
151:     /**
152:      * Updates the number of tokens that need to be matched to form a solution to the specified value.
153:      * @param solutionSize the new number of tokens that need to be matched to form a solution
154:      */
155:     public void setSolutionSize(final int solutionSize) {
156:         this.solutionSize = solutionSize;
157:     }
158: 
159:     /**
160:      * Checks if the current board settings comply with the board limits.
161:      * @throws InputProviderException if the current board limits are invalid
162:      */
163:     public void assertIsValid() throws InputProviderException {
164:         // validate board size
165:         this.assertStaticLimits();
166:         this.boardDimensions.assertStaticLimits();
167: 
168:         final int fieldCount = this.boardDimensions.getRowCount() * this.boardDimensions.getColumnCount();
169:         // check for fieldCount > MAX_FIELD_COUNT
170:         if (fieldCount < 0) { // 32 bit signed integer overflow
171:             throw new InputProviderException("The board is too large: the total number of fields exceeds "
172:                 + "the maximum allowed value (" + MAX_FIELD_COUNT + ").");
173:         }
174: 
175:         // validate solution size
176:         // does the solution fit on the board?
177:         if (this.solutionSize > Math.max(this.boardDimensions.getRowCount(), this.boardDimensions.getColumnCount())) {
178:             throw new InputProviderException("The required solution size is too large: the solution size exceeds "
179:                 + "the number of rows and columns on the board. This is dumb.");
180:         }
181:         // has every player the possibility to win?
182:         if (fieldCount < this.playerCount * this.solutionSize) {
183:             throw new InputProviderException("The current board settings do not allow every player to win: "
184:                 + "the number of fields is too small to allow every player to form a solution, if players "
185:                 + "take turns placing tokens.");
186:         }
187:     }
188:     
189:     /**
190:      * Checks if the current board settings comply with the static board limits.
191:      * @throws InputProviderException if they don't comply...
192:      */
193:     private void assertStaticLimits() throws InputProviderException {
194:         if (this.solutionSize < MIN_SOLUTION_SIZE) {
195:             throw new InputProviderException("The specified solution size is too small: the solution size "
196:                 + "must be at least " + MIN_SOLUTION_SIZE + ".");
197:         }
198:         if (this.solutionSize > MAX_SOLUTION_SIZE) {
199:             throw new InputProviderException("The specified solution size is too large: the solution size "
200:                 + "must not exceed " + MAX_SOLUTION_SIZE + ".");
201:         }
202:     }
203: 
204:     @Override
205:     public boolean equals(final Object object) {
206:         if (object == null) {
207:             return false;
208:         }
209:         if (object == this) {
210:             return true;
211:         }
212:         if (!(object instanceof C4BoardLimits)) {
213:             return false;
214:         }
215:         final C4BoardLimits other = (C4BoardLimits) object;
216:         return this.boardDimensions.equals(other.boardDimensions)
217:             && this.solutionSize == other.solutionSize
218:             && this.playerCount == other.playerCount;
219:     }
220: 
221:     @Override
222:     public int hashCode() {
223:         int hash = 7;
224:         hash = hash * 31 + this.boardDimensions.hashCode();
225:         hash = hash * 31 + this.solutionSize;
226:         hash = hash * 31 + this.playerCount;
227:         return hash;
228:     }
229: }