When there is HTML/Java project, clicking on 'Develop' should open the project wizard
2 * The MIT License (MIT)
4 * Copyright (C) 2013 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24 package org.apidesign.demo.minesweeper;
26 import java.awt.event.ActionEvent;
27 import java.net.MalformedURLException;
29 import java.util.ArrayList;
30 import java.util.List;
31 import java.util.Random;
32 import java.util.logging.Level;
33 import java.util.logging.Logger;
34 import javax.swing.Action;
35 import net.java.html.json.ComputedProperty;
36 import net.java.html.json.Function;
37 import net.java.html.json.Model;
38 import net.java.html.json.ModelOperation;
39 import net.java.html.json.Property;
40 import net.java.html.sound.AudioClip;
41 import org.openide.awt.HtmlBrowser;
42 import org.openide.filesystems.FileObject;
43 import org.openide.filesystems.FileUtil;
44 import org.openide.util.ContextAwareAction;
45 import org.openide.util.Exceptions;
46 import org.openide.util.Lookup;
47 import org.openide.util.Utilities;
49 /** Model of the mine field.
51 @Model(className = "Mines", properties = {
52 @Property(name = "state", type = MinesModel.GameState.class),
53 @Property(name = "rows", type = Row.class, array = true),
55 public final class MinesModel {
57 IN_PROGRESS, MARKING_MINE, WON, LOST;
60 @ComputedProperty static String gameStyle(GameState state) {
61 return state == GameState.MARKING_MINE ? "MARKING" : "PLAYING";
64 @Model(className = "Row", properties = {
65 @Property(name = "columns", type = Square.class, array = true)
67 static class RowModel {
70 @Model(className = "Square", properties = {
71 @Property(name = "state", type = SquareType.class),
72 @Property(name = "mine", type = boolean.class)
74 static class SquareModel {
75 @ComputedProperty static String style(SquareType state) {
76 return state == null ? null : state.toString();
81 N_0, N_1, N_2, N_3, N_4, N_5, N_6, N_7, N_8,
82 UNKNOWN, EXPLOSION, DISCOVERED, MARKED;
84 final boolean isVisible() {
85 return name().startsWith("N_");
88 final SquareType moreBombsAround() {
96 return values()[ordinal() + 1];
100 @ComputedProperty static boolean fieldShowing(GameState state) {
101 return state != null;
104 @ComputedProperty static boolean gameInProgress(GameState state) {
105 return state == GameState.IN_PROGRESS;
108 @Function static void showHelp(Mines model) {
109 model.setState(null);
112 @Function static void smallGame(Mines model) {
115 @Function static void normalGame(Mines model) {
116 model.init(10, 10, 10);
119 @Function static void giveUp(Mines model) {
120 showAllBombs(model, SquareType.EXPLOSION);
121 model.setState(GameState.LOST);
124 @Function static void markMine(Mines model) {
125 if (model.getState() == GameState.IN_PROGRESS) {
126 model.setState(GameState.MARKING_MINE);
130 private static final Logger LOG = Logger.getLogger(MinesModel.class.getName());
131 @Function static void develop(Mines model) {
134 ClassLoader l = Lookup.getDefault().lookup(ClassLoader.class);
136 l = Thread.currentThread().getContextClassLoader();
139 l = MinesModel.class.getClassLoader();
141 Class<?> newPrj = Class.forName("org.netbeans.spi.project.ui.support.CommonProjectActions", true, l); // NOI18N
142 a = (Action) newPrj.getMethod("newProjectAction").invoke(null); // NOI18N
143 } catch (Exception ex) {
144 LOG.log(Level.FINE, "Cannot find New project action!", ex);
147 FileObject fo = FileUtil.getConfigFile("Templates/Project/ClientSide"); // NOI18N
149 a.putValue("PRESELECT_CATEGORY", "ClientSide"); // NOI18N
150 for (FileObject template : fo.getChildren()) {
151 if (template.getName().contains("htmljava")) { // NOI18N
152 a.putValue("PRESELECT_TEMPLATE", template.getName()); // NOI18N
153 a.actionPerformed(new ActionEvent(model, 0, null));
160 HtmlBrowser.URLDisplayer.getDefault().showURL(new URL("http://wiki.apidesign.org/wiki/DukeScriptInNetBeans")); // NO18N
161 } catch (MalformedURLException ex) {
162 Exceptions.printStackTrace(ex);
166 @ModelOperation static void init(Mines model, int width, int height, int mines) {
167 List<Row> rows = model.getRows();
168 if (rows.size() != height || rows.get(0).getColumns().size() != width) {
169 rows = new ArrayList<Row>(height);
170 for (int y = 0; y < height; y++) {
171 Square[] columns = new Square[width];
172 for (int x = 0; x < width; x++) {
173 columns[x] = new Square(SquareType.UNKNOWN, false);
175 rows.add(new Row(columns));
178 for (Row row : rows) {
179 for (Square sq : row.getColumns()) {
180 sq.setState(SquareType.UNKNOWN);
186 Random r = new Random();
188 int x = r.nextInt(width);
189 int y = r.nextInt(height);
190 final Square s = rows.get(y).getColumns().get(x);
198 model.setState(GameState.IN_PROGRESS);
199 if (rows != model.getRows()) {
200 model.getRows().clear();
201 model.getRows().addAll(rows);
205 @ModelOperation static void computeMines(Mines model) {
206 List<Integer> xBombs = new ArrayList<Integer>();
207 List<Integer> yBombs = new ArrayList<Integer>();
208 final List<Row> rows = model.getRows();
209 boolean emptyHidden = false;
210 SquareType[][] arr = new SquareType[rows.size()][];
211 for (int y = 0; y < rows.size(); y++) {
212 final List<Square> columns = rows.get(y).getColumns();
213 arr[y] = new SquareType[columns.size()];
214 for (int x = 0; x < columns.size(); x++) {
215 Square sq = columns.get(x);
220 if (sq.getState().isVisible()) {
221 arr[y][x] = SquareType.N_0;
229 for (int i = 0; i < xBombs.size(); i++) {
230 int x = xBombs.get(i);
231 int y = yBombs.get(i);
233 incrementAround(arr, x, y);
235 for (int y = 0; y < rows.size(); y++) {
236 final List<Square> columns = rows.get(y).getColumns();
237 for (int x = 0; x < columns.size(); x++) {
238 Square sq = columns.get(x);
239 final SquareType newState = arr[y][x];
240 if (newState != null && newState != sq.getState()) {
241 sq.setState(newState);
247 model.setState(GameState.WON);
248 showAllBombs(model, SquareType.DISCOVERED);
249 AudioClip applause = AudioClip.create("applause.mp3");
254 private static void incrementAround(SquareType[][] arr, int x, int y) {
255 incrementAt(arr, x - 1, y - 1);
256 incrementAt(arr, x - 1, y);
257 incrementAt(arr, x - 1, y + 1);
259 incrementAt(arr, x + 1, y - 1);
260 incrementAt(arr, x + 1, y);
261 incrementAt(arr, x + 1, y + 1);
263 incrementAt(arr, x, y - 1);
264 incrementAt(arr, x, y + 1);
267 private static void incrementAt(SquareType[][] arr, int x, int y) {
268 if (y >= 0 && y < arr.length) {
269 SquareType[] r = arr[y];
270 if (x >= 0 && x < r.length) {
271 SquareType sq = r[x];
273 r[x] = sq.moreBombsAround();
279 static void showAllBombs(Mines model, SquareType state) {
280 for (Row row : model.getRows()) {
281 for (Square square : row.getColumns()) {
282 if (square.isMine()) {
283 square.setState(state);
289 @Function static void click(Mines model, Square data) {
290 if (model.getState() == GameState.MARKING_MINE) {
291 if (data.getState() == SquareType.UNKNOWN) {
292 data.setState(SquareType.MARKED);
293 if (allMarked(model)) {
294 model.setState(GameState.WON);
298 model.setState(GameState.IN_PROGRESS);
301 if (model.getState() != GameState.IN_PROGRESS) {
304 if (data.getState() == SquareType.MARKED) {
305 data.setState(SquareType.UNKNOWN);
306 if (allMarked(model)) {
307 model.setState(GameState.WON);
311 if (data.getState() != SquareType.UNKNOWN) {
315 Square fair = atLeastOnePlaceWhereBombCantBe(model);
317 if (placeBombElseWhere(model, data)) {
318 cleanedUp(model, data);
324 Square takeFrom = tryStealBomb(model, data);
325 if (takeFrom != null) {
326 final Square fair = atLeastOnePlaceWhereBombCantBe(model);
328 takeFrom.setMine(false);
334 cleanedUp(model, data);
338 private static void cleanedUp(Mines model, Square data) {
339 AudioClip touch = AudioClip.create("move.mp3");
341 expandKnown(model, data);
342 model.computeMines();
345 private static void explosion(Mines model) {
346 showAllBombs(model, SquareType.EXPLOSION);
347 model.setState(GameState.LOST);
348 AudioClip oops = AudioClip.create("oops.mp3");
352 private static Square tryStealBomb(Mines model, Square data) {
354 final List<Row> rows = model.getRows();
355 for (int y = 0; y < rows.size(); y++) {
356 final List<Square> columns = rows.get(y).getColumns();
357 for (int x = 0; x < columns.size(); x++) {
358 Square sq = columns.get(x);
364 final boolean ok = isConsistent(model);
377 private static Square atLeastOnePlaceWhereBombCantBe(Mines model) {
378 final List<Row> rows = model.getRows();
379 Square cantBe = null;
381 for (int y = 0; y < rows.size(); y++) {
382 final List<Square> columns = rows.get(y).getColumns();
383 for (int x = 0; x < columns.size(); x++) {
384 Square sq = columns.get(x);
385 if (sq.getState() == SquareType.UNKNOWN) {
387 if (tryStealBomb(model, sq) == null) {
397 if (discovered > 5) {
404 private static boolean placeBombElseWhere(Mines model, Square moveBomb) {
405 List<Square> ok = new ArrayList<Square>();
406 moveBomb.setMine(false);
407 final List<Row> rows = model.getRows();
408 for (int y = 0; y < rows.size(); y++) {
409 final List<Square> columns = rows.get(y).getColumns();
410 for (int x = 0; x < columns.size(); x++) {
411 Square sq = columns.get(x);
412 if (sq == moveBomb || sq.isMine() || sq.getState().isVisible()) {
416 if (isConsistent(model)) {
423 moveBomb.setMine(true);
426 int r = new Random().nextInt(ok.size());
427 ok.get(r).setMine(true);
432 private static void expandKnown(Mines model, Square data) {
433 final List<Row> rows = model.getRows();
434 for (int y = 0; y < rows.size(); y++) {
435 final List<Square> columns = rows.get(y).getColumns();
436 for (int x = 0; x < columns.size(); x++) {
437 Square sq = columns.get(x);
439 expandKnown(model, x, y);
445 private static void expandKnown(Mines model, int x , int y) {
446 if (y < 0 || y >= model.getRows().size()) {
449 final List<Square> columns = model.getRows().get(y).getColumns();
450 if (x < 0 || x >= columns.size()) {
453 final Square sq = columns.get(x);
454 if (sq.getState() == SquareType.UNKNOWN) {
455 int around = around(model, x, y);
456 final SquareType t = SquareType.valueOf("N_" + around);
458 if (t == SquareType.N_0) {
459 expandKnown(model, x - 1, y - 1);
460 expandKnown(model, x - 1, y);
461 expandKnown(model, x - 1, y + 1);
462 expandKnown(model, x , y - 1);
463 expandKnown(model, x, y + 1);
464 expandKnown(model, x + 1, y - 1);
465 expandKnown(model, x + 1, y);
466 expandKnown(model, x + 1, y + 1);
471 private static int around(Mines model, int x, int y) {
473 minesAt(model, x - 1, y - 1) +
474 minesAt(model, x - 1, y) +
475 minesAt(model, x - 1, y + 1) +
476 minesAt(model, x , y - 1) +
477 minesAt(model, x, y + 1) +
478 minesAt(model, x + 1, y - 1) +
479 minesAt(model, x + 1, y) +
480 minesAt(model, x + 1, y + 1);
483 private static int minesAt(Mines model, int x, int y) {
484 if (y < 0 || y >= model.getRows().size()) {
487 final List<Square> columns = model.getRows().get(y).getColumns();
488 if (x < 0 || x >= columns.size()) {
491 Square sq = columns.get(x);
492 return sq.isMine() ? 1 : 0;
495 private static boolean isConsistent(Mines m) {
496 for (int row = 0; row < m.getRows().size(); row++) {
497 Row r = m.getRows().get(row);
498 for (int col = 0; col < r.getColumns().size(); col++) {
499 Square sq = r.getColumns().get(col);
500 if (sq.getState().isVisible()) {
501 int around = around(m, col, row);
502 if (around != sq.getState().ordinal()) {
511 private static boolean allMarked(Mines m) {
512 for (Row r : m.getRows()) {
513 for (Square sq : r.getColumns()) {
514 if (sq.isMine() == (sq.getState() != SquareType.MARKED)) {
519 for (Row r : m.getRows()) {
520 for (Square sq : r.getColumns()) {
522 sq.setState(SquareType.DISCOVERED);
524 sq.setState(SquareType.N_0);
533 * Called when page is ready
535 public static void main(String... args) throws Exception {
536 Mines m = new Mines();