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