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.
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 "&nbsp;";
jaroslav@585
   145
            switch (state) {
jaroslav@585
   146
                case EXPLOSION: return "&#x2717;";
jaroslav@585
   147
                case UNKNOWN: return "&nbsp;";
jaroslav@585
   148
                case DISCOVERED: return "&#x2714;";  
jaroslav@585
   149
                case N_0: return "&nbsp;";
jaroslav@585
   150
            }
jaroslav@585
   151
            return "&#x278" + (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
}