jaroslav@585
|
1 |
/**
|
jaroslav@585
|
2 |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
|
jaroslav@585
|
3 |
*
|
jaroslav@585
|
4 |
* Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
|
jaroslav@585
|
5 |
*
|
jaroslav@585
|
6 |
* Oracle and Java are registered trademarks of Oracle and/or its affiliates.
|
jaroslav@585
|
7 |
* Other names may be trademarks of their respective owners.
|
jaroslav@585
|
8 |
*
|
jaroslav@585
|
9 |
* The contents of this file are subject to the terms of either the GNU
|
jaroslav@585
|
10 |
* General Public License Version 2 only ("GPL") or the Common
|
jaroslav@585
|
11 |
* Development and Distribution License("CDDL") (collectively, the
|
jaroslav@585
|
12 |
* "License"). You may not use this file except in compliance with the
|
jaroslav@585
|
13 |
* License. You can obtain a copy of the License at
|
jaroslav@585
|
14 |
* http://www.netbeans.org/cddl-gplv2.html
|
jaroslav@585
|
15 |
* or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
|
jaroslav@585
|
16 |
* specific language governing permissions and limitations under the
|
jaroslav@585
|
17 |
* License. When distributing the software, include this License Header
|
jaroslav@585
|
18 |
* Notice in each file and include the License file at
|
jaroslav@585
|
19 |
* nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
|
jaroslav@585
|
20 |
* particular file as subject to the "Classpath" exception as provided
|
jaroslav@585
|
21 |
* by Oracle in the GPL Version 2 section of the License file that
|
jaroslav@585
|
22 |
* accompanied this code. If applicable, add the following below the
|
jaroslav@585
|
23 |
* License Header, with the fields enclosed by brackets [] replaced by
|
jaroslav@585
|
24 |
* your own identifying information:
|
jaroslav@585
|
25 |
* "Portions Copyrighted [year] [name of copyright owner]"
|
jaroslav@585
|
26 |
*
|
jaroslav@585
|
27 |
* Contributor(s):
|
jaroslav@585
|
28 |
*
|
jaroslav@585
|
29 |
* The Original Software is NetBeans. The Initial Developer of the Original
|
jaroslav@585
|
30 |
* Software is Oracle. Portions Copyright 2013-2014 Oracle. All Rights Reserved.
|
jaroslav@585
|
31 |
*
|
jaroslav@585
|
32 |
* If you wish your version of this file to be governed by only the CDDL
|
jaroslav@585
|
33 |
* or only the GPL Version 2, indicate your decision by adding
|
jaroslav@585
|
34 |
* "[Contributor] elects to include this software in this distribution
|
jaroslav@585
|
35 |
* under the [CDDL or GPL Version 2] license." If you do not indicate a
|
jaroslav@585
|
36 |
* single choice of license, a recipient has the option to distribute
|
jaroslav@585
|
37 |
* your version of this file under either the CDDL, the GPL Version 2 or
|
jaroslav@585
|
38 |
* to extend the choice of license to its licensees as provided above.
|
jaroslav@585
|
39 |
* However, if you add GPL Version 2 code and therefore, elected the GPL
|
jaroslav@585
|
40 |
* Version 2 license, then the option applies only if the new code is
|
jaroslav@585
|
41 |
* made subject to such option by the copyright holder.
|
jaroslav@585
|
42 |
*/
|
jaroslav@585
|
43 |
|
jaroslav@585
|
44 |
package net.java.html.json.tests;
|
jaroslav@585
|
45 |
|
jaroslav@585
|
46 |
import java.util.ArrayList;
|
jaroslav@585
|
47 |
import java.util.List;
|
jaroslav@585
|
48 |
import java.util.Random;
|
jaroslav@585
|
49 |
import net.java.html.BrwsrCtx;
|
jaroslav@585
|
50 |
import net.java.html.json.ComputedProperty;
|
jaroslav@587
|
51 |
import net.java.html.json.Function;
|
jaroslav@585
|
52 |
import net.java.html.json.Model;
|
jaroslav@585
|
53 |
import net.java.html.json.ModelOperation;
|
jaroslav@585
|
54 |
import net.java.html.json.Models;
|
jaroslav@585
|
55 |
import net.java.html.json.Property;
|
jtulach@838
|
56 |
import org.netbeans.html.json.tck.KOTest;
|
jaroslav@585
|
57 |
|
jaroslav@585
|
58 |
/** Tests model of a mine field and its behavior in the browser.
|
jaroslav@585
|
59 |
*/
|
jaroslav@585
|
60 |
@Model(className = "Mines", properties = {
|
jaroslav@585
|
61 |
@Property(name = "state", type = MinesTest.GameState.class),
|
jaroslav@585
|
62 |
@Property(name = "rows", type = Row.class, array = true),
|
jaroslav@585
|
63 |
})
|
jaroslav@585
|
64 |
public final class MinesTest {
|
jaroslav@587
|
65 |
Mines m;
|
jaroslav@587
|
66 |
@KOTest public void paintTheGridOnClick() throws Throwable {
|
jaroslav@587
|
67 |
if (m == null) {
|
jaroslav@587
|
68 |
BrwsrCtx ctx = Utils.newContext(MinesTest.class);
|
jaroslav@587
|
69 |
Object exp = Utils.exposeHTML(MinesTest.class,
|
jaroslav@587
|
70 |
" <button id='init' data-bind='click: normalSize'></button>\n" +
|
jaroslav@587
|
71 |
" <table>\n" +
|
jaroslav@587
|
72 |
" <tbody id='table'>\n" +
|
jaroslav@587
|
73 |
" <!-- ko foreach: rows -->\n" +
|
jaroslav@587
|
74 |
" <tr>\n" +
|
jaroslav@587
|
75 |
" <!-- ko foreach: columns -->\n" +
|
jaroslav@587
|
76 |
" <td data-bind='css: style' >\n" +
|
jaroslav@587
|
77 |
" <div data-bind='text: html'></div>\n" +
|
jaroslav@587
|
78 |
" </td>\n" +
|
jaroslav@587
|
79 |
" <!-- /ko -->\n" +
|
jaroslav@587
|
80 |
" </tr>\n" +
|
jaroslav@587
|
81 |
" <!-- /ko -->\n" +
|
jaroslav@587
|
82 |
" </tbody>\n" +
|
jaroslav@587
|
83 |
" </table>\n" +
|
jaroslav@587
|
84 |
""
|
jaroslav@587
|
85 |
);
|
jaroslav@587
|
86 |
m = Models.bind(new Mines(), ctx);
|
jaroslav@587
|
87 |
m.applyBindings();
|
jtulach@681
|
88 |
int cnt = Utils.countChildren(MinesTest.class, "table");
|
jaroslav@587
|
89 |
assert cnt == 0 : "Table is empty: " + cnt;
|
jaroslav@587
|
90 |
scheduleClick("init", 100);
|
jaroslav@587
|
91 |
}
|
jaroslav@585
|
92 |
|
jaroslav@585
|
93 |
|
jtulach@681
|
94 |
int cnt = Utils.countChildren(MinesTest.class, "table");
|
jaroslav@587
|
95 |
if (cnt == 0) {
|
jaroslav@587
|
96 |
throw new InterruptedException();
|
jaroslav@585
|
97 |
}
|
jaroslav@587
|
98 |
assert cnt == 10 : "There is ten rows in the table now: " + cnt;
|
jaroslav@587
|
99 |
|
jaroslav@587
|
100 |
Utils.exposeHTML(MinesTest.class, "");
|
jaroslav@585
|
101 |
}
|
jaroslav@585
|
102 |
|
jtulach@670
|
103 |
@KOTest public void countAround() throws Exception {
|
jtulach@670
|
104 |
Mines mines = new Mines();
|
jtulach@670
|
105 |
mines.init(5, 5, 0);
|
jtulach@670
|
106 |
mines.getRows().get(0).getColumns().get(0).setMine(true);
|
jtulach@670
|
107 |
mines.getRows().get(1).getColumns().get(0).setMine(true);
|
jtulach@670
|
108 |
mines.getRows().get(0).getColumns().get(1).setMine(true);
|
jtulach@670
|
109 |
|
jtulach@670
|
110 |
int cnt = around(mines, 1, 1);
|
jtulach@670
|
111 |
assert cnt == 3 : "There are three mines around. Was: " + cnt;
|
jtulach@670
|
112 |
}
|
jtulach@670
|
113 |
|
jaroslav@587
|
114 |
private static void scheduleClick(String id, int delay) throws Exception {
|
jaroslav@587
|
115 |
String s = "var id = arguments[0]; var delay = arguments[1];"
|
jaroslav@587
|
116 |
+ "var e = window.document.getElementById(id);\n "
|
jaroslav@587
|
117 |
+ "var f = function() {;\n "
|
jaroslav@587
|
118 |
+ " var ev = window.document.createEvent('MouseEvents');\n "
|
jaroslav@587
|
119 |
+ " ev.initMouseEvent('click', true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);\n "
|
jaroslav@587
|
120 |
+ " e.dispatchEvent(ev);\n"
|
jaroslav@587
|
121 |
+ "};\n"
|
jaroslav@587
|
122 |
+ "window.setTimeout(f, delay);";
|
jaroslav@587
|
123 |
Utils.executeScript(
|
jaroslav@587
|
124 |
MinesTest.class,
|
jaroslav@587
|
125 |
s, id, delay);
|
jaroslav@587
|
126 |
}
|
jaroslav@585
|
127 |
|
jaroslav@585
|
128 |
enum GameState {
|
jaroslav@585
|
129 |
IN_PROGRESS, WON, LOST;
|
jaroslav@585
|
130 |
}
|
jaroslav@585
|
131 |
|
jaroslav@585
|
132 |
@Model(className = "Row", properties = {
|
jaroslav@585
|
133 |
@Property(name = "columns", type = Square.class, array = true)
|
jaroslav@585
|
134 |
})
|
jaroslav@585
|
135 |
static class RowModel {
|
jaroslav@585
|
136 |
}
|
jaroslav@585
|
137 |
|
jaroslav@585
|
138 |
@Model(className = "Square", properties = {
|
jaroslav@585
|
139 |
@Property(name = "state", type = SquareType.class),
|
jaroslav@585
|
140 |
@Property(name = "mine", type = boolean.class)
|
jaroslav@585
|
141 |
})
|
jaroslav@585
|
142 |
static class SquareModel {
|
jaroslav@585
|
143 |
@ComputedProperty static String html(SquareType state) {
|
jaroslav@585
|
144 |
if (state == null) return " ";
|
jaroslav@585
|
145 |
switch (state) {
|
jaroslav@585
|
146 |
case EXPLOSION: return "✗";
|
jaroslav@585
|
147 |
case UNKNOWN: return " ";
|
jaroslav@585
|
148 |
case DISCOVERED: return "✔";
|
jaroslav@585
|
149 |
case N_0: return " ";
|
jaroslav@585
|
150 |
}
|
jaroslav@585
|
151 |
return "ɸ" + (state.ordinal() - 1);
|
jaroslav@585
|
152 |
}
|
jaroslav@585
|
153 |
|
jaroslav@585
|
154 |
@ComputedProperty static String style(SquareType state) {
|
jaroslav@585
|
155 |
return state == null ? null : state.toString();
|
jaroslav@585
|
156 |
}
|
jaroslav@585
|
157 |
}
|
jaroslav@585
|
158 |
|
jaroslav@585
|
159 |
enum SquareType {
|
jaroslav@585
|
160 |
N_0, N_1, N_2, N_3, N_4, N_5, N_6, N_7, N_8,
|
jaroslav@585
|
161 |
UNKNOWN, EXPLOSION, DISCOVERED;
|
jaroslav@585
|
162 |
|
jaroslav@585
|
163 |
final boolean isVisible() {
|
jaroslav@585
|
164 |
return name().startsWith("N_");
|
jaroslav@585
|
165 |
}
|
jaroslav@585
|
166 |
|
jaroslav@585
|
167 |
final SquareType moreBombsAround() {
|
jaroslav@585
|
168 |
switch (this) {
|
jaroslav@585
|
169 |
case EXPLOSION:
|
jaroslav@585
|
170 |
case UNKNOWN:
|
jaroslav@585
|
171 |
case DISCOVERED:
|
jaroslav@585
|
172 |
case N_8:
|
jaroslav@585
|
173 |
return this;
|
jaroslav@585
|
174 |
}
|
jaroslav@585
|
175 |
return values()[ordinal() + 1];
|
jaroslav@585
|
176 |
}
|
jaroslav@585
|
177 |
}
|
jaroslav@585
|
178 |
|
jaroslav@585
|
179 |
@ComputedProperty static boolean fieldShowing(GameState state) {
|
jaroslav@585
|
180 |
return state != null;
|
jaroslav@585
|
181 |
}
|
jaroslav@585
|
182 |
|
jaroslav@587
|
183 |
@Function static void normalSize(Mines m) {
|
jaroslav@587
|
184 |
m.init(10, 10, 10);
|
jaroslav@587
|
185 |
}
|
jaroslav@585
|
186 |
|
jaroslav@585
|
187 |
@ModelOperation static void init(Mines model, int width, int height, int mines) {
|
jaroslav@585
|
188 |
List<Row> rows = new ArrayList<Row>(height);
|
jaroslav@585
|
189 |
for (int y = 0; y < height; y++) {
|
jaroslav@585
|
190 |
Square[] columns = new Square[width];
|
jaroslav@585
|
191 |
for (int x = 0; x < width; x++) {
|
jaroslav@585
|
192 |
columns[x] = new Square(SquareType.UNKNOWN, false);
|
jaroslav@585
|
193 |
}
|
jaroslav@585
|
194 |
rows.add(new Row(columns));
|
jaroslav@585
|
195 |
}
|
jaroslav@585
|
196 |
|
jaroslav@585
|
197 |
Random r = new Random();
|
jaroslav@585
|
198 |
while (mines > 0) {
|
jaroslav@585
|
199 |
int x = r.nextInt(width);
|
jaroslav@585
|
200 |
int y = r.nextInt(height);
|
jaroslav@585
|
201 |
final Square s = rows.get(y).getColumns().get(x);
|
jaroslav@585
|
202 |
if (s.isMine()) {
|
jaroslav@585
|
203 |
continue;
|
jaroslav@585
|
204 |
}
|
jaroslav@585
|
205 |
s.setMine(true);
|
jaroslav@585
|
206 |
mines--;
|
jaroslav@585
|
207 |
}
|
jaroslav@585
|
208 |
|
jaroslav@585
|
209 |
model.setState(GameState.IN_PROGRESS);
|
jaroslav@585
|
210 |
model.getRows().clear();
|
jaroslav@585
|
211 |
model.getRows().addAll(rows);
|
jaroslav@585
|
212 |
}
|
jaroslav@585
|
213 |
|
jaroslav@585
|
214 |
@ModelOperation static void computeMines(Mines model) {
|
jaroslav@585
|
215 |
List<Integer> xBombs = new ArrayList<Integer>();
|
jaroslav@585
|
216 |
List<Integer> yBombs = new ArrayList<Integer>();
|
jaroslav@585
|
217 |
final List<Row> rows = model.getRows();
|
jaroslav@585
|
218 |
boolean emptyHidden = false;
|
jaroslav@585
|
219 |
SquareType[][] arr = new SquareType[rows.size()][];
|
jaroslav@585
|
220 |
for (int y = 0; y < rows.size(); y++) {
|
jaroslav@585
|
221 |
final List<Square> columns = rows.get(y).getColumns();
|
jaroslav@585
|
222 |
arr[y] = new SquareType[columns.size()];
|
jaroslav@585
|
223 |
for (int x = 0; x < columns.size(); x++) {
|
jaroslav@585
|
224 |
Square sq = columns.get(x);
|
jaroslav@585
|
225 |
if (sq.isMine()) {
|
jaroslav@585
|
226 |
xBombs.add(x);
|
jaroslav@585
|
227 |
yBombs.add(y);
|
jaroslav@585
|
228 |
}
|
jaroslav@585
|
229 |
if (sq.getState().isVisible()) {
|
jaroslav@585
|
230 |
arr[y][x] = SquareType.N_0;
|
jaroslav@585
|
231 |
} else {
|
jaroslav@585
|
232 |
if (!sq.isMine()) {
|
jaroslav@585
|
233 |
emptyHidden = true;
|
jaroslav@585
|
234 |
}
|
jaroslav@585
|
235 |
}
|
jaroslav@585
|
236 |
}
|
jaroslav@585
|
237 |
}
|
jaroslav@585
|
238 |
for (int i = 0; i < xBombs.size(); i++) {
|
jaroslav@585
|
239 |
int x = xBombs.get(i);
|
jaroslav@585
|
240 |
int y = yBombs.get(i);
|
jaroslav@585
|
241 |
|
jaroslav@585
|
242 |
incrementAround(arr, x, y);
|
jaroslav@585
|
243 |
}
|
jaroslav@585
|
244 |
for (int y = 0; y < rows.size(); y++) {
|
jaroslav@585
|
245 |
final List<Square> columns = rows.get(y).getColumns();
|
jaroslav@585
|
246 |
for (int x = 0; x < columns.size(); x++) {
|
jaroslav@585
|
247 |
Square sq = columns.get(x);
|
jaroslav@585
|
248 |
final SquareType newState = arr[y][x];
|
jaroslav@585
|
249 |
if (newState != null && newState != sq.getState()) {
|
jaroslav@585
|
250 |
sq.setState(newState);
|
jaroslav@585
|
251 |
}
|
jaroslav@585
|
252 |
}
|
jaroslav@585
|
253 |
}
|
jaroslav@585
|
254 |
|
jaroslav@585
|
255 |
if (!emptyHidden) {
|
jaroslav@585
|
256 |
model.setState(GameState.WON);
|
jaroslav@585
|
257 |
showAllBombs(model, SquareType.DISCOVERED);
|
jaroslav@585
|
258 |
}
|
jaroslav@585
|
259 |
}
|
jaroslav@585
|
260 |
|
jaroslav@585
|
261 |
private static void incrementAround(SquareType[][] arr, int x, int y) {
|
jaroslav@585
|
262 |
incrementAt(arr, x - 1, y - 1);
|
jaroslav@585
|
263 |
incrementAt(arr, x - 1, y);
|
jaroslav@585
|
264 |
incrementAt(arr, x - 1, y + 1);
|
jaroslav@585
|
265 |
|
jaroslav@585
|
266 |
incrementAt(arr, x + 1, y - 1);
|
jaroslav@585
|
267 |
incrementAt(arr, x + 1, y);
|
jaroslav@585
|
268 |
incrementAt(arr, x + 1, y + 1);
|
jaroslav@585
|
269 |
|
jaroslav@585
|
270 |
incrementAt(arr, x, y - 1);
|
jaroslav@585
|
271 |
incrementAt(arr, x, y + 1);
|
jaroslav@585
|
272 |
}
|
jaroslav@585
|
273 |
|
jaroslav@585
|
274 |
private static void incrementAt(SquareType[][] arr, int x, int y) {
|
jaroslav@585
|
275 |
if (y >= 0 && y < arr.length) {
|
jaroslav@585
|
276 |
SquareType[] r = arr[y];
|
jaroslav@585
|
277 |
if (x >= 0 && x < r.length) {
|
jaroslav@585
|
278 |
SquareType sq = r[x];
|
jaroslav@585
|
279 |
if (sq != null) {
|
jaroslav@585
|
280 |
r[x] = sq.moreBombsAround();
|
jaroslav@585
|
281 |
}
|
jaroslav@585
|
282 |
}
|
jaroslav@585
|
283 |
}
|
jaroslav@585
|
284 |
}
|
jaroslav@585
|
285 |
|
jaroslav@585
|
286 |
static void showAllBombs(Mines model, SquareType state) {
|
jaroslav@585
|
287 |
for (Row row : model.getRows()) {
|
jaroslav@585
|
288 |
for (Square square : row.getColumns()) {
|
jaroslav@585
|
289 |
if (square.isMine()) {
|
jaroslav@585
|
290 |
square.setState(state);
|
jaroslav@585
|
291 |
}
|
jaroslav@585
|
292 |
}
|
jaroslav@585
|
293 |
}
|
jaroslav@585
|
294 |
}
|
jaroslav@585
|
295 |
|
jaroslav@585
|
296 |
private static void expandKnown(Mines model, Square data) {
|
jaroslav@585
|
297 |
final List<Row> rows = model.getRows();
|
jaroslav@585
|
298 |
for (int y = 0; y < rows.size(); y++) {
|
jaroslav@585
|
299 |
final List<Square> columns = rows.get(y).getColumns();
|
jaroslav@585
|
300 |
for (int x = 0; x < columns.size(); x++) {
|
jaroslav@585
|
301 |
Square sq = columns.get(x);
|
jaroslav@585
|
302 |
if (sq == data) {
|
jaroslav@585
|
303 |
expandKnown(model, x, y);
|
jaroslav@585
|
304 |
return;
|
jaroslav@585
|
305 |
}
|
jaroslav@585
|
306 |
}
|
jaroslav@585
|
307 |
}
|
jaroslav@585
|
308 |
}
|
jaroslav@585
|
309 |
private static void expandKnown(Mines model, int x , int y) {
|
jaroslav@585
|
310 |
if (y < 0 || y >= model.getRows().size()) {
|
jaroslav@585
|
311 |
return;
|
jaroslav@585
|
312 |
}
|
jaroslav@585
|
313 |
final List<Square> columns = model.getRows().get(y).getColumns();
|
jaroslav@585
|
314 |
if (x < 0 || x >= columns.size()) {
|
jaroslav@585
|
315 |
return;
|
jaroslav@585
|
316 |
}
|
jaroslav@585
|
317 |
final Square sq = columns.get(x);
|
jaroslav@585
|
318 |
if (sq.getState() == SquareType.UNKNOWN) {
|
jtulach@670
|
319 |
int around = around(model, x, y);
|
jtulach@670
|
320 |
final SquareType t = SquareType.valueOf("N_" + around);
|
jtulach@670
|
321 |
sq.setState(t);
|
jaroslav@585
|
322 |
if (sq.getState() == SquareType.N_0) {
|
jaroslav@585
|
323 |
expandKnown(model, x - 1, y - 1);
|
jaroslav@585
|
324 |
expandKnown(model, x - 1, y);
|
jaroslav@585
|
325 |
expandKnown(model, x - 1, y + 1);
|
jaroslav@585
|
326 |
expandKnown(model, x , y - 1);
|
jaroslav@585
|
327 |
expandKnown(model, x, y + 1);
|
jaroslav@585
|
328 |
expandKnown(model, x + 1, y - 1);
|
jaroslav@585
|
329 |
expandKnown(model, x + 1, y);
|
jaroslav@585
|
330 |
expandKnown(model, x + 1, y + 1);
|
jaroslav@585
|
331 |
}
|
jaroslav@585
|
332 |
}
|
jaroslav@585
|
333 |
}
|
jtulach@670
|
334 |
private static int around(Mines model, int x, int y) {
|
jtulach@670
|
335 |
return minesAt(model, x - 1, y - 1)
|
jtulach@670
|
336 |
+ minesAt(model, x - 1, y)
|
jtulach@670
|
337 |
+ minesAt(model, x - 1, y + 1)
|
jtulach@670
|
338 |
+ minesAt(model, x, y - 1)
|
jtulach@670
|
339 |
+ minesAt(model, x, y + 1)
|
jtulach@670
|
340 |
+ minesAt(model, x + 1, y - 1)
|
jtulach@670
|
341 |
+ minesAt(model, x + 1, y)
|
jtulach@670
|
342 |
+ minesAt(model, x + 1, y + 1);
|
jtulach@670
|
343 |
}
|
jtulach@670
|
344 |
|
jtulach@670
|
345 |
private static int minesAt(Mines model, int x, int y) {
|
jtulach@670
|
346 |
if (y < 0 || y >= model.getRows().size()) {
|
jtulach@670
|
347 |
return 0;
|
jtulach@670
|
348 |
}
|
jtulach@670
|
349 |
final List<Square> columns = model.getRows().get(y).getColumns();
|
jtulach@670
|
350 |
if (x < 0 || x >= columns.size()) {
|
jtulach@670
|
351 |
return 0;
|
jtulach@670
|
352 |
}
|
jtulach@670
|
353 |
Square sq = columns.get(x);
|
jtulach@670
|
354 |
return sq.isMine() ? 1 : 0;
|
jtulach@670
|
355 |
}
|
jaroslav@585
|
356 |
}
|