# HG changeset patch # User Jaroslav Tulach # Date 1380055037 -7200 # Node ID 3f1866fdb2a1b19450bd5450d27bf3f7616b152e # Parent 49011b4a268949b91aa0bdf7b4a66319c83e63a9 Moving to correct packages diff -r 49011b4a2689 -r 3f1866fdb2a1 chess/pom.xml --- a/chess/pom.xml Tue Sep 24 22:28:31 2013 +0200 +++ b/chess/pom.xml Tue Sep 24 22:37:17 2013 +0200 @@ -7,8 +7,8 @@ 1.0-SNAPSHOT - com.oracle.chess - ChessHTMLJavaClient + org.apidesign.html.demo + chessdemo 1.0-SNAPSHOT jar @@ -58,7 +58,7 @@ - com.oracle.chess.client.htmljava.Main + org.apidesign.html.demo.chess.Main true lib/ @@ -76,7 +76,7 @@ ${basedir}/src/main/webapp/ - com.oracle.chess.client.htmljava.Main + org.apidesign.html.demo.chess.Main diff -r 49011b4a2689 -r 3f1866fdb2a1 chess/src/main/java/com/oracle/chess/client/htmljava/BoardModel.java --- a/chess/src/main/java/com/oracle/chess/client/htmljava/BoardModel.java Tue Sep 24 22:28:31 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,451 +0,0 @@ -/** - * The MIT License (MIT) - * - * Copyright (C) 2013 Jaroslav Tulach - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package com.oracle.chess.client.htmljava; - -import com.oracle.chess.client.htmljava.Communication.AlertType; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.Timer; -import java.util.TimerTask; -import net.java.html.json.ComputedProperty; -import net.java.html.json.Function; -import net.java.html.json.Model; -import net.java.html.json.ModelOperation; -import net.java.html.json.OnPropertyChange; -import net.java.html.json.Property; -import net.java.html.sound.AudioClip; - -@Model(className="Board", properties={ - @Property(name = "player", type = String.class), - @Property(name = "gameId", type = String.class), - @Property(name = "whitePlayer", type = String.class), - @Property(name = "blackPlayer", type = String.class), - @Property(name = "alert", type = Alert.class), - @Property(name = "alertMessage", type = String.class), - @Property(name = "status", type = String.class), - @Property(name = "rows", type = Row.class, array = true), - @Property(name = "turn", type = Color.class), - @Property(name = "moves", type = Move.class, array = true), - @Property(name = "pendingMove", type = Move.class), - @Property(name = "active", type = boolean.class), -}) -public class BoardModel { - @ComputedProperty static String title(String status, String gameId) { - String t = status != null ? status : gameId; - if (t != null && t.length() > 10) { - return t.substring(0, 10); - } - return t; - } - - @ModelOperation static void updateSummary(Board b, String summary) { - if (summary != null) { - b.setStatus(summary); - } - } - - @ComputedProperty static boolean myTurn(String player, String whitePlayer, String blackPlayer, Color turn) { - if (turn != null && player != null) switch (turn) { - case B: return player.equals(blackPlayer); - case W: return player.equals(whitePlayer); - } - return false; - } - - @ComputedProperty static boolean justObserving(String player) { - return player == null; - } - - private static final AudioClip MOVE = AudioClip.create("sounds/move.mp3"); - @OnPropertyChange("moves") static void playMove() { - MOVE.play(); - } - - private static final AudioClip CHECK = AudioClip.create("sounds/check.mp3"); - private static final AudioClip CHECKMATE = AudioClip.create("sounds/checkmate.mp3"); - @OnPropertyChange("alert") static void warnCheckAndMate(Board b) { - if (b.getAlert() == null) { - return; - } - if (b.getAlert().getType() == AlertType.CHECK) { - CHECK.play(); - } - if (b.getAlert().getType() == AlertType.CHECKMATE) { - CHECKMATE.play(); - } - } - - @Function static void selected(Board b, Square data) { - if (!b.isMyTurn()) { - b.setAlertMessage("Not your turn!"); - return; - } - - Square previoslySelected = findSelectedSquare(b); - if (previoslySelected == null) { - if (data.getPiece() != null && data.getPieceColor() == b.getTurn()) { - data.setSelected(true); - Rules.computeAccessible(b, data); - } - } else { - if (previoslySelected == data) { - data.setSelected(false); - Rules.computeAccessible(b, null); - return; - } - if (data.getPiece() != null && data.getPieceColor() == previoslySelected.getPieceColor()) { - previoslySelected.setSelected(false); - data.setSelected(true); - Rules.computeAccessible(b, data); - return; - } - if (data.isAccessible()) { - previoslySelected.setSelected(false); - - Move newMove = new Move(); - newMove.setFrom(previoslySelected.getPosition()); - newMove.setTo(data.getPosition()); - newMove.setRound(b.getMoves().size() / 2 + 1); - newMove.setPiece(previoslySelected.getPiece()); - newMove.setTurn(previoslySelected.getPieceColor()); - newMove.setTakes(data.getPiece() != null); - b.getMoves().add(newMove); - b.setPendingMove(newMove); - - data.setPending(true); - data.setPieceColor(previoslySelected.getPieceColor()); - data.setPiece(previoslySelected.getPiece()); - b.setTurn(null); - previoslySelected.setPiece(null); - previoslySelected.setPieceColor(null); - Rules.computeAccessible(b, null); - - Request sm = new Request(); - sm.setMsg(MsgType.SendMove); - sm.setGameId(b.getGameId()); - sm.setFrom(newMove.getFrom().getLocation()); - sm.setTo(newMove.getTo().getLocation()); - sm.setColor(newMove.getTurn()); - final UI ui = UIModel.findUI(b); - if (ui != null) { - ui.sendMsg(sm); - } - } - } - } - - static class NextMove extends TimerTask { - private static final Timer T = new Timer("Animate moves"); - private final Board b; - private final Move m; - - public NextMove(Board b, Move m) { - this.b = b; - this.m = m; - T.schedule(this, 1000); - } - - - @Override - public void run() { - b.showPosition(m); - } - } - - @ModelOperation @Function static void showPosition(Board b, Move data) { - Rules.initBoard(b); - boolean found = false; - for (Move m : b.getMoves()) { - if (found) { - b.setTurn(null); - new NextMove(b, m); - return; - } - Square from = findSquare(b, (char)m.getFrom().getX(), m.getFrom().getY()); - Square to = findSquare(b, (char)m.getTo().getX(), m.getTo().getY()); - to.setPiece(from.getPiece()); - to.setPieceColor(from.getPieceColor()); - from.setPiece(null); - from.setPieceColor(null); - if (m == data) { - found = true; - } - } - b.setTurn(b.getMoves().size() % 2 == 0 ? Color.W : Color.B); - } - - - - @Function static void rotateBoard(Board b) { - Collections.reverse(b.getRows()); - for (Row r : b.getRows()) { - Collections.reverse(r.getColumns()); - } - Square sq = findSelectedSquare(b); - if (sq != null) { - sq.setSelected(false); - Rules.computeAccessible(b, null); - } - } - - @ComputedProperty static boolean whiteTurn(Color turn) { - return turn == Color.W; - } - - @ComputedProperty static boolean blackTurn(Color turn) { - return turn == Color.B; - } - - @ComputedProperty static List columnNames(List rows) { - boolean whiteDown = rows.isEmpty() || rows.get(0).getY() == 8; - String[] arr = new String[8]; - for (int i = 0; i < 8; i++) { - String s; - if (whiteDown) { - s = "" + (char)('A' + i); - } else { - s = "" + (char)('H' - i); - } - arr[i] = s; - } - return Arrays.asList(arr); - } - - static Square findSquare(Board b, char column, int row) { - for (Row r : b.getRows()) { - for (Square square : r.getColumns()) { - if (square.getPosition().getX() == column && square.getPosition().getY() == row) { - return square; - } - } - } - return null; - } - - private static Square findSquare(Board b, Position to) { - return findSquare(b, (char)to.getX(), to.getY()); - } - - static Square findSelectedSquare(Board b) { - for (Row row : b.getRows()) { - for (Square square : row.getColumns()) { - if (square.isSelected()) { - return square; - } - } - } - return null; - } - - static void moveResponse(final Board b, final String errMsg, final List whites, final List blacks, final Color turn, - Alert alert - ) { - if (errMsg != null) { - b.getMoves().remove(b.getPendingMove()); - b.setPendingMove(null); - b.setAlertMessage(errMsg); - } else { - b.setTurn(turn); - b.setAlertMessage(null); - } - b.setAlert(alert); - Rules.initBoard(b, whites, blacks, turn); - } - - static void moveUpdate(final Board b, final Move move, final List whites, final List blacks, final Color turn, Alert alert) { - final Square from = BoardModel.findSquare(b, move.getFrom()); - final Square to = BoardModel.findSquare(b, move.getTo()); - move.setPiece(from.getPiece()); - move.setTurn(from.getPieceColor()); - if (to.getPiece() != null) { - move.setTakes(true); - } - b.setAlert(alert); - b.setAlertMessage(alert == null ? null : alert.getMessage()); - move.setRound(b.getMoves().size() / 2 + 1); - b.getMoves().add(move); - Rules.initBoard(b, whites, blacks, turn); - } - - @Model(className="Row", properties = { - @Property(name = "columns", type = Square.class, array = true) - }) - static class RowsImpl { - @ComputedProperty static int y(List columns) { - return columns.isEmpty() ? 0 : columns.get(0).getY(); - } - } - - enum PieceType { - PAWN(5), ROCK(2), KNIGHT(4), BISHOP(3), QUEEN(1), KING(0); - - final int entityIndex; - - PieceType(int ei) { - this.entityIndex = ei; - } - - static PieceType fromNotation(char notation) { - switch (notation) { - case 'R': return ROCK; - case 'N': return KNIGHT; - case 'B': return BISHOP; - case 'Q': return QUEEN; - case 'K': return KING; - case 'P': return PAWN; - } - throw new IllegalStateException("Unexpected: " + notation); - } - - String computeEntity(Color color) { - if (color == null) { - color = Color.W; - } - int base; - switch (color) { - case W: base = 12; break; - case B: base = 18; break; - default: - throw new AssertionError(); - } - return "b" + String.valueOf(base + entityIndex) + ";"; - } - } - - @Model(className="Position", properties = { - @Property(name = "x", type = char.class), - @Property(name = "y", type = int.class), - }) - static class PositionImpl { - @ComputedProperty static String location(int x, int y) { - return "" + (char)(x - 'A' + 'a') + y; - } - } - - @Model(className="Square", properties = { - @Property(name = "position", type = Position.class), - @Property(name = "color", type = Color.class), - @Property(name = "piece", type = PieceType.class), - @Property(name = "pieceColor", type = Color.class), - @Property(name = "selected", type = boolean.class), - @Property(name = "accessible", type = boolean.class), - @Property(name = "pending", type = boolean.class), - }) - static class SquareModel { - @ComputedProperty static String pieceEntity( - PieceType piece, Color pieceColor - ) { - if (piece == null) { - return ""; - } - return piece.computeEntity(pieceColor); - } - - @ComputedProperty static String squareColor( - Color color, boolean selected, boolean accessible, boolean pending - ) { - if (selected) { - return "selected"; - } - if (accessible) { - return "accessible"; - } - if (pending) { - return "pending"; - } - - if (color == null) { - return ""; - } else { - if (color == Color.W) { - return "white"; - } else { - return "black"; - } - } - } - - @ComputedProperty static char x(Position position) { - return position == null ? 'A' : position.getX(); - } - - @ComputedProperty static int y(Position position) { - return position == null ? 1 : position.getY(); - } - } - - @Model(className = "Move", properties = { - @Property(name = "round", type = int.class), - @Property(name = "turn", type = Color.class), - @Property(name = "piece", type = PieceType.class), - @Property(name = "from", type = Position.class), - @Property(name = "to", type = Position.class), - @Property(name = "promoted", type = PieceType.class), - @Property(name = "takes", type = boolean.class), - @Property(name = "check", type = boolean.class), - }) - static class MoveImpl { - @ComputedProperty static boolean whiteMove(Color turn) { - return turn == Color.W; - } - - @ComputedProperty static String html( - Position from, Position to, boolean takes, PieceType piece, Color turn - ) { - if (from == null || to == null) { - return ""; - } - StringBuilder sb = new StringBuilder(); - if (piece != null && piece != PieceType.PAWN) { - sb.append(piece.computeEntity(turn)); - } - - sb.append(from.getLocation()); - if (takes) { - sb.append("x"); - } - sb.append(to.getLocation()); - return sb.toString(); - } - - static Move valueOf(String move) { - move = move.toUpperCase(); - Move m = new Move(); - { - Position p = new Position(); - p.setX(move.charAt(0)); - p.setY(move.charAt(1) - '0'); - m.setFrom(p); - } - { - Position p = new Position(); - p.setX(move.charAt(2)); - p.setY(move.charAt(3) - '0'); - m.setTo(p); - } - return m; - } - } -} diff -r 49011b4a2689 -r 3f1866fdb2a1 chess/src/main/java/com/oracle/chess/client/htmljava/Color.java --- a/chess/src/main/java/com/oracle/chess/client/htmljava/Color.java Tue Sep 24 22:28:31 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,32 +0,0 @@ -/** - * The MIT License (MIT) - * - * Copyright (C) 2013 Jaroslav Tulach - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package com.oracle.chess.client.htmljava; - -/** - * - * @author Jaroslav Tulach - */ -public enum Color { - W, B; -} diff -r 49011b4a2689 -r 3f1866fdb2a1 chess/src/main/java/com/oracle/chess/client/htmljava/Communication.java --- a/chess/src/main/java/com/oracle/chess/client/htmljava/Communication.java Tue Sep 24 22:28:31 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,110 +0,0 @@ -/** - * The MIT License (MIT) - * - * Copyright (C) 2013 Jaroslav Tulach - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package com.oracle.chess.client.htmljava; - -import net.java.html.json.ComputedProperty; -import net.java.html.json.Model; -import net.java.html.json.Property; - - -/** Communication protocols to talk to the server. - * - * @author Jaroslav Tulach - */ -final class Communication { - @Model(className = "Request", properties = { - @Property(name = "msg", type = MsgType.class), - @Property(name = "username", type = String.class), - @Property(name = "password", type = String.class), - @Property(name = "gameId", type = String.class), - @Property(name = "color", type = Color.class), - @Property(name = "from", type = String.class), - @Property(name = "to", type = String.class), - @Property(name = "observer", type = boolean.class), - }) - static class RequestModel { - } - - @Model(className = "BoardRep", properties = { - @Property(name = "whites", type = String.class, array = true), - @Property(name = "blacks", type = String.class, array = true), - }) - static class BoardRepModel { - } - - @Model(className = "Response", properties = { - @Property(name = "msg", type = String.class), - @Property(name = "games", array = true, type = Game.class), - @Property(name = "board", type = BoardRep.class), - @Property(name = "turn", type = Color.class), - @Property(name = "color", type = Color.class), - @Property(name = "gameId", type = String.class), - @Property(name = "error", type = Err.class), - @Property(name = "alert", type = Alert.class), - @Property(name = "status", type = String.class), - @Property(name = "summary", type = String.class), - @Property(name = "moves", type = String.class, array = true), - @Property(name = "from", type = String.class), - @Property(name = "to", type = String.class), - @Property(name = "check", type = CheckResult.class), - @Property(name = "whitePlayer", type = String.class), - @Property(name = "blackPlayer", type = String.class), - }) - static class ResponseModel { - @ComputedProperty static Color nextTurn(Color turn, Alert alert) { - if (alert == null || !alert.getType().isTerminal()) { - return turn; - } else { - return null; - } - } - } - - static enum CheckResult { - VALID, INVALID, NOT_REGISTERED - }; - - @Model(className = "Err", properties = { - @Property(name = "code", type = int.class), - @Property(name = "message", type = String.class), - }) - static class ErrorModel { - } - - @Model(className = "Alert", properties = { - @Property(name = "type", type = AlertType.class), - @Property(name = "message", type = String.class), - }) - static class AlertModel { - } - - static enum AlertType { - CHECKMATE, CHECK, DRAW; - - public boolean isTerminal() { - return this == CHECKMATE || this == DRAW; - } - }; - -} diff -r 49011b4a2689 -r 3f1866fdb2a1 chess/src/main/java/com/oracle/chess/client/htmljava/GamesModel.java --- a/chess/src/main/java/com/oracle/chess/client/htmljava/GamesModel.java Tue Sep 24 22:28:31 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,86 +0,0 @@ -/** - * The MIT License (MIT) - * - * Copyright (C) 2013 Jaroslav Tulach - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package com.oracle.chess.client.htmljava; - -import java.util.List; -import net.java.html.json.ComputedProperty; -import net.java.html.json.Model; -import net.java.html.json.ModelOperation; -import net.java.html.json.Property; - -/** - * - * @author Jaroslav Tulach - */ -@Model(className = "Games", properties = { - @Property(name = "own", array = true, type = Game.class), - @Property(name = "other", array = true, type = Game.class), - @Property(name = "selectedColor", type = String.class), -}) -class GamesModel { - @Model(className = "Game", properties = { - @Property(name = "gameId", type=String.class), - @Property(name = "summary", type=String.class), - @Property(name = "open", type=boolean.class), - @Property(name = "whitePlayer", type=String.class), - @Property(name = "blackPlayer", type=String.class) - }) - static class GameImpl { - @ComputedProperty static String description(String gameId, String summary) { - return summary != null ? summary : gameId; - } - - @ComputedProperty static boolean pendingPlayer(String whitePlayer, String blackPlayer) { - return whitePlayer == null || blackPlayer == null; - } - } - - - @ComputedProperty static boolean anyOwnGame(List own) { - return !own.isEmpty(); - } - - @ComputedProperty static boolean anyOtherGame(List other) { - return !other.isEmpty(); - } - - @ModelOperation static void listGames(Games g, List games, String username) { - g.getOwn().clear(); - g.getOther().clear(); - int cnt = 0; - final int maxGames = 30; - for (Game game : games) { - if ( - username.equals(game.getWhitePlayer()) || - username.equals(game.getBlackPlayer()) - ) { - g.getOwn().add(game); - } else { - if (cnt++ < maxGames) { - g.getOther().add(game); - } - } - } - } -} diff -r 49011b4a2689 -r 3f1866fdb2a1 chess/src/main/java/com/oracle/chess/client/htmljava/LoadMain.java --- a/chess/src/main/java/com/oracle/chess/client/htmljava/LoadMain.java Tue Sep 24 22:28:31 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,41 +0,0 @@ -/** - * The MIT License (MIT) - * - * Copyright (C) 2013 Jaroslav Tulach - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package com.oracle.chess.client.htmljava; - - -public final class LoadMain { - private LoadMain() { - } - - /** - * Called when the page is ready. - */ - static { - try { - UIModel.initialize(); - } catch (Throwable ex) { - ex.printStackTrace(); - } - } -} diff -r 49011b4a2689 -r 3f1866fdb2a1 chess/src/main/java/com/oracle/chess/client/htmljava/Main.java --- a/chess/src/main/java/com/oracle/chess/client/htmljava/Main.java Tue Sep 24 22:28:31 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,42 +0,0 @@ -/** - * The MIT License (MIT) - * - * Copyright (C) 2013 Jaroslav Tulach - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package com.oracle.chess.client.htmljava; - -import net.java.html.boot.BrowserBuilder; -import org.apidesign.bck2brwsr.core.JavaScriptOnly; - -public final class Main { - private Main() { - } - - @JavaScriptOnly(name = "main", value = "null") - public static void main(String... args) throws Exception { - BrowserBuilder.newBrowser(). - loadPage("pages/index.html"). - loadClass(UIModel.class). - invoke("initialize", args). - showAndWait(); - System.exit(0); - } -} diff -r 49011b4a2689 -r 3f1866fdb2a1 chess/src/main/java/com/oracle/chess/client/htmljava/MsgType.java --- a/chess/src/main/java/com/oracle/chess/client/htmljava/MsgType.java Tue Sep 24 22:28:31 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,40 +0,0 @@ -/** - * The MIT License (MIT) - * - * Copyright (C) 2013 Jaroslav Tulach - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package com.oracle.chess.client.htmljava; - -/** - * - * @author Jaroslav Tulach - */ - enum MsgType { - CreateGame, QueryGames, SendMove, JoinGame, UpdateGame, CheckCredentials; - - static MsgType forResponse(String msg) { - if (msg.endsWith("Rsp")) { - msg = msg.substring(0, msg.length() - 3); - } - return MsgType.valueOf(msg); - } - -} diff -r 49011b4a2689 -r 3f1866fdb2a1 chess/src/main/java/com/oracle/chess/client/htmljava/Rules.java --- a/chess/src/main/java/com/oracle/chess/client/htmljava/Rules.java Tue Sep 24 22:28:31 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,314 +0,0 @@ -/** - * The MIT License (MIT) - * - * Copyright (C) 2013 Jaroslav Tulach - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package com.oracle.chess.client.htmljava; - -import java.util.ArrayList; -import java.util.List; - -/** Common chess rules. - * - * @author Jaroslav Tulach - */ -class Rules { - static void computeAccessible(Board b, Square s) { - for (Row r : b.getRows()) { - for (Square ts : r.getColumns()) { - ts.setAccessible(false); - } - } - if (s == null) { - return; - } - - switch (s.getPiece()) { - case BISHOP: - moveBishop(b, s); - break; - case KING: - computeAccessible(b, s, 1, 1, 1); - computeAccessible(b, s, 1, -1, 1); - computeAccessible(b, s, -1, -1, 1); - computeAccessible(b, s, -1, 1, 1); - computeAccessible(b, s, 1, 0, 1); - computeAccessible(b, s, 0, -1, 1); - computeAccessible(b, s, 0, 1, 1); - computeAccessible(b, s, -1, 0, 1); - checkRochade(b, s); - break; - case ROCK: - moveRock(b, s); - break; - case QUEEN: - moveRock(b, s); - moveBishop(b, s); - break; - case KNIGHT: - computeAccessible(b, s, 2, 1, 1); - computeAccessible(b, s, 2, -1, 1); - computeAccessible(b, s, -2, -1, 1); - computeAccessible(b, s, -2, 1, 1); - computeAccessible(b, s, 1, 2, 1); - computeAccessible(b, s, -1, 2, 1); - computeAccessible(b, s, -1, -2, 1); - computeAccessible(b, s, 1, -2, 1); - break; - case PAWN: - pawns(b, s); - break; - } - } - - private static void moveRock(Board b, Square s) { - computeAccessible(b, s, 1, 0, 8); - computeAccessible(b, s, 0, -1, 8); - computeAccessible(b, s, -1, 0, 8); - computeAccessible(b, s, 0, 1, 8); - } - - private static void moveBishop(Board b, Square s) { - computeAccessible(b, s, 1, 1, 8); - computeAccessible(b, s, 1, -1, 8); - computeAccessible(b, s, -1, -1, 8); - computeAccessible(b, s, -1, 1, 8); - } - - private static void computeAccessible( - Board b, Square s, int dx, int dy, - int limit - ) { - int x = s.getX(); - int y = s.getY(); - - while (limit-- > 0) { - x += dx; - y += dy; - Square next = BoardModel.findSquare(b, (char)x, y); - if (next == null) { - break; - } - if (next.getPieceColor() == s.getPieceColor()) { - break; - } - next.setAccessible(true); - if (next.getPieceColor() != null) { - break; - } - } - } - - private static void pawns(Board b, Square s) { - final boolean white = s.getPieceColor() == Color.W; - int dy = white ? 1 : -1; - Square step = BoardModel.findSquare(b, (char)s.getX(), s.getY() + dy); - if (step != null && step.getPiece() == null) { - step.setAccessible(true); - if ((s.getY() == 2 && white) || (s.getY() == 7 && !white)) { - Square nextSTep = BoardModel.findSquare(b, (char)s.getX(), step.getY() + dy); - if (nextSTep != null && step.getPiece() == null && nextSTep.getPiece() == null) { - nextSTep.setAccessible(true); - } - } - } - Color opposite = white ? Color.B : Color.W; - Square takeLeft = BoardModel.findSquare(b, (char)(s.getX() - 1), s.getY() + dy); - if (takeLeft != null && takeLeft.getPieceColor() == opposite) { - takeLeft.setAccessible(true); - } - Square takeRight = BoardModel.findSquare(b, (char)(s.getX() + 1), s.getY() + dy); - if (takeRight != null && takeRight.getPieceColor() == opposite) { - takeRight.setAccessible(true); - } - if ((white && s.getY() == 5) || (!white && s.getY() == 4)) { - int enPassantFrom = white ? 7 : 2; - int enPassantTo = white ? 5 : 4; - if (!b.getMoves().isEmpty()) { - Move last = b.getMoves().get(b.getMoves().size() - 1); - if ( - last.getPiece() == BoardModel.PieceType.PAWN && - last.getFrom().getY() == enPassantFrom && - last.getTo().getY() == enPassantTo - ) { - if (takeLeft != null && last.getFrom().getX() == s.getX() - 1) { - takeLeft.setAccessible(true); - } - if (takeRight != null && last.getFrom().getX() == s.getX() + 1) { - takeRight.setAccessible(true); - } - } - } - } - } - - static Board createBoard() { - Board b = new Board(); - initBoardField(b); - initBoard(b); - return b; - } - - private static void initBoardField(Board b) { - List addRows = new ArrayList<>(8); - for (int i = 8; i > 0; i--) { - Square[] arr = new Square[8]; - for (char j = 'A'; j <= 'H'; j++) { - arr[j - 'A'] = new Square( - new Position(j, i), - (i + j) % 2 == 1 ? Color.W : Color.B, - null, null, // figure - false, false, false - ); - } - addRows.add(new Row(arr)); - } - b.getRows().addAll(addRows); - } - - static void initBoard(Board b) { - initBoard(b, true); - } - private static void initBoard(Board b, boolean init) { - if (init) { - b.setTurn(Color.W); - } - if (b.getRows().isEmpty()) { - for (int i = 8; i > 0; i--) { - Row r = b.getRows().get(8 - i); - for (char j = 'A'; j <= 'H'; j++) { - Square s = r.getColumns().get(j - 'A'); - s.setAccessible(false); - s.setPending(false); - r.getColumns().set(j - 'A', s); - initialPosition(s, init); - } - } - } else { - for (Row r : b.getRows()) { - for (Square square : r.getColumns()) { - square.setAccessible(false); - square.setPending(false); - square.setPiece(null); - square.setPieceColor(null); - square.setSelected(false); - initialPosition(square, init); - } - } - } - b.setPendingMove(null); - } - - private static void initialPosition(Square s, boolean init) { - int row = s.getPosition().getY(); - char column = s.getPosition().getX(); - s.setPiece(null); - s.setPieceColor(null); - if (init) { - if (row == 2) { - s.setPiece(BoardModel.PieceType.PAWN); - s.setPieceColor(Color.W); - } else if (row == 7) { - s.setPiece(BoardModel.PieceType.PAWN); - s.setPieceColor(Color.B); - } else if (row == 8 || row == 1) { - s.setPieceColor(row == 1 ? Color.W : Color.B); - BoardModel.PieceType t; - switch (column) { - case 'A': - case 'H': - t = BoardModel.PieceType.ROCK; - break; - case 'B': - case 'G': - t = BoardModel.PieceType.KNIGHT; - break; - case 'C': - case 'F': - t = BoardModel.PieceType.BISHOP; - break; - case 'D': - t = BoardModel.PieceType.QUEEN; - break; - default: - t = BoardModel.PieceType.KING; - break; - } - s.setPiece(t); - } - } - } - - static void initBoard( - Board board, List whites, List blacks, Color turn - ) { - initBoard(board, false); - for (String w : whites) { - assert w.length() == 3 : "Expecting three letter string: '" + w + "'"; - w = w.toUpperCase(); - char column = w.charAt(1); - int row = (w.charAt(2) - '0'); - - Square s = BoardModel.findSquare(board, column, row); - s.setPieceColor(Color.W); - s.setPiece(BoardModel.PieceType.fromNotation(w.charAt(0))); - } - for (String w : blacks) { - assert w.length() == 3 : "Expecting three letter string: '" + w + "'"; - w = w.toUpperCase(); - char column = w.charAt(1); - int row = (w.charAt(2) - '0'); - - Square s = BoardModel.findSquare(board, column, row); - s.setPieceColor(Color.B); - s.setPiece(BoardModel.PieceType.fromNotation(w.charAt(0))); - } - board.setTurn(turn); - } - - private static void checkRochade(Board b, Square s) { - if (s.getPosition().getX() == 'E') { - int y = s.getPosition().getY(); - final Square gRow = BoardModel.findSquare(b, 'G', y); - if ( - BoardModel.findSquare(b, 'H', y).getPiece() == BoardModel.PieceType.ROCK - && - BoardModel.findSquare(b, 'F', y).getPiece() == null - && - gRow.getPiece() == null - ) { - gRow.setAccessible(true); - } - final Square cRow = BoardModel.findSquare(b, 'C', y); - if ( - BoardModel.findSquare(b, 'A', y).getPiece() == BoardModel.PieceType.ROCK - && - BoardModel.findSquare(b, 'B', y).getPiece() == null - && - BoardModel.findSquare(b, 'D', y).getPiece() == null - && - cRow.getPiece() == null - ) { - cRow.setAccessible(true); - } - } - } -} diff -r 49011b4a2689 -r 3f1866fdb2a1 chess/src/main/java/com/oracle/chess/client/htmljava/SettingsModel.java --- a/chess/src/main/java/com/oracle/chess/client/htmljava/SettingsModel.java Tue Sep 24 22:28:31 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,82 +0,0 @@ -/** - * The MIT License (MIT) - * - * Copyright (C) 2013 Jaroslav Tulach - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package com.oracle.chess.client.htmljava; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.util.Properties; -import net.java.html.json.ComputedProperty; -import net.java.html.json.Model; -import net.java.html.json.ModelOperation; -import net.java.html.json.Property; - -/** - * - * @author Jaroslav Tulach - */ -@Model(className = "Settings", properties = { - @Property(name = "username", type = String.class), - @Property(name = "password", type = String.class), - @Property(name = "url", type = String.class), -}) -class SettingsModel { - private static final String SETTINGS = ".j1chess/html.properties"; - private static final String DEFAULT_URL = "ws://localhost:8080/chess/chessserver"; - - @ComputedProperty static boolean validCredentials(String username, String password) { - return username != null && password != null && !username.isEmpty() && !password.isEmpty(); - } - - @ModelOperation static void read(Settings s) { - File prefs = new File(System.getProperty("user.home"), SETTINGS); - try (FileInputStream is = new FileInputStream(prefs)) { - Properties p = new Properties(); - p.load(is); - - s.setUsername(p.getProperty("username", "")); - s.setPassword(p.getProperty("password", "")); - s.setUrl(p.getProperty("url", DEFAULT_URL)); - } catch (Throwable ex) { - s.setUsername(""); - s.setPassword(""); - s.setUrl(DEFAULT_URL); - } - } - - @ModelOperation static void write(Settings s, UI ui) { - File prefs = new File(System.getProperty("user.home"), SETTINGS); - prefs.getParentFile().mkdirs(); - try (FileOutputStream os = new FileOutputStream(prefs)) { - Properties p = new Properties(); - p.setProperty("username", s.getUsername()); - p.setProperty("password", s.getPassword()); - p.setProperty("url", s.getUrl()); - p.store(os, "Java One Chess Preferences for HTML/Java Client"); - } catch (Throwable ex) { - ui.setStatus("Cannot save preferences: " + ex.getMessage()); - } - } -} diff -r 49011b4a2689 -r 3f1866fdb2a1 chess/src/main/java/com/oracle/chess/client/htmljava/UIModel.java --- a/chess/src/main/java/com/oracle/chess/client/htmljava/UIModel.java Tue Sep 24 22:28:31 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,315 +0,0 @@ -/** - * The MIT License (MIT) - * - * Copyright (C) 2013 Jaroslav Tulach - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package com.oracle.chess.client.htmljava; - -import java.io.IOException; -import java.net.URISyntaxException; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.logging.Level; -import java.util.logging.Logger; -import net.java.html.json.ComputedProperty; -import net.java.html.json.Function; -import net.java.html.json.Model; -import net.java.html.json.ModelOperation; -import net.java.html.json.OnPropertyChange; -import net.java.html.json.OnReceive; -import net.java.html.json.Property; - -/** - * - * @author Jaroslav Tulach - */ -@Model(className = "UI", properties = { - @Property(name = "status", type = String.class), - @Property(name = "selectedGameId", type = String.class), - @Property(name = "boards", type = Board.class, array = true), - @Property(name = "viewGames", type = Games.class), - @Property(name = "settings", type = Settings.class), - @Property(name = "connected", type = boolean.class), - @Property(name = "disconnected", type = boolean.class), -}) -public class UIModel { - private static final Logger LOG = Logger.getLogger(UIModel.class.getName()); - - @ComputedProperty static boolean settingsActive(String selectedGameId) { - return selectedGameId == null; - } - - @ComputedProperty static boolean viewGamesActive(String selectedGameId, boolean connected) { - return selectedGameId == null && connected; - } - - @ComputedProperty static Board selectedBoard(String selectedGameId, List boards) { - Board active = null; - for (Board b : boards) { - b.setActive(false); - if (selectedGameId != null && selectedGameId.equals(b.getGameId())) { - b.setActive(true); - active = b; - } - } - return active; - } - - @Function static void activateGame(UI m, Board data) { - m.setSelectedGameId(data.getGameId()); - } - - @Function static void activateSettings(UI m) { - m.setSelectedGameId(null); - refreshGames(m); - } - - @Function static void createGame(UI u) { - u.setStatus("Creating new game..."); - Request r = new Request(); - r.setMsg(MsgType.CreateGame); - r.setColor(Color.valueOf(u.getViewGames().getSelectedColor())); - u.sendMsg(r); - } - - @ModelOperation @Function static void refreshGames(UI u) { - u.setStatus("Refreshing games..."); - Request r = new Request(); - r.setMsg(MsgType.QueryGames); - u.sendMsg(r); - } - - @ModelOperation static void sendMsg(UI ui, Request r) { - final Settings sttngs = ui.getSettings(); - - String url = sttngs.getUrl(); - r.setUsername(sttngs.getUsername()); - r.setPassword(sttngs.getPassword()); - - LOG.log(Level.INFO, "Sending {0} to {1}", new Object[]{r, url}); - ui.queryServer(url, r); - } - - - @OnReceive(data = Request.class, url = "{url}", method = "WebSocket", onError = "wasAnError") - static void queryServer(UI ui, Response r) { - LOG.log(Level.INFO, "Received {0}", r); - if (r == null) { - ui.setDisconnected(false); - verifyLogin(ui); - return; - } - SWITCH: switch (MsgType.forResponse(r.getMsg())) { - case CheckCredentials: { - switch (r.getCheck()) { - case NOT_REGISTERED: - case VALID: - ui.setConnected(true); - ui.refreshGames(); - ui.setStatus("Connected."); - return; - case INVALID: - ui.setStatus("Password is invalid"); - break; - } - disconnect(ui); - } - - case QueryGames: - ui.getViewGames().listGames(r.getGames(), ui.getSettings().getUsername()); - ui.setStatus(""); - break; - case CreateGame: { - ui.setStatus("Game " + r + " created"); - final Board b = Rules.createBoard(); - Rules.initBoard( - b, r.getBoard().getWhites(), - r.getBoard().getBlacks(), - r.getTurn() - ); - b.setGameId(r.getGameId()); - b.setWhitePlayer(r.getWhitePlayer()); - b.setBlackPlayer(r.getBlackPlayer()); - b.setPlayer(ui.getSettings().getUsername()); - b.updateSummary(r.getSummary()); - ui.getBoards().add(b); - ui.setSelectedGameId(r.getGameId()); - UIModel.refreshGames(ui); - break; - } - case SendMove: { - if (r.getBoard() == null) { - throw new NullPointerException("No board in " + r); - } - final Board b = findBoard(ui, r.getGameId()); - if (b == null) { - break; - } - b.updateSummary(r.getSummary()); - final String errMsg = r.getError() == null ? null : r.getError().getMessage(); - final List whites = r.getBoard().getWhites(); - final List blacks = r.getBoard().getBlacks(); - final Color turn = r.getNextTurn(); - BoardModel.moveResponse(b, errMsg, whites, blacks, turn, r.getAlert()); - break; - } - case JoinGame: { - Board b; - if ((b = findBoard(ui, r.getGameId())) != null) { - b.setWhitePlayer(r.getWhitePlayer()); - b.setBlackPlayer(r.getBlackPlayer()); - b.setPlayer(ui.getSettings().getUsername()); - b.updateSummary(r.getSummary()); - ui.setSelectedGameId(r.getGameId()); - break SWITCH; - } - ui.setStatus("Joining " + r.getGameId() + " as " + r.getColor()); - b = Rules.createBoard(); - Rules.initBoard( - b, r.getBoard().getWhites(), - r.getBoard().getBlacks(), - r.getTurn() - ); - b.setGameId(r.getGameId()); - b.setWhitePlayer(r.getWhitePlayer()); - b.setBlackPlayer(r.getBlackPlayer()); - b.setPlayer(ui.getSettings().getUsername()); - b.updateSummary(r.getSummary()); - if (r.getColor() == Color.B) { - BoardModel.rotateBoard(b); - } - if (r.getMoves() != null) { - for (String move : r.getMoves()) { - Move m = BoardModel.MoveImpl.valueOf(move); - b.getMoves().add(m); - } - } else { - b.getMoves().clear(); - } - ui.setSelectedGameId(b.getGameId()); - ui.getBoards().add(b); - break; - } - case UpdateGame: { - final Board b = findBoard(ui, r.getGameId()); - if (b == null) { - break; - } - b.updateSummary(r.getSummary()); - final Move move = BoardModel.MoveImpl.valueOf(r.getFrom() + r.getTo()); - final List whites = r.getBoard().getWhites(); - final List blacks = r.getBoard().getBlacks(); - final Color turn = r.getNextTurn(); - BoardModel.moveUpdate(b, move, whites, blacks, turn, null); - } - } - } - - - private static Board findBoard(UI ui, String gameId) { - for (Board tmp : ui.getBoards()) { - if (tmp.getGameId().equals(gameId)) { - return tmp; - } - } - return null; - } - - - static void wasAnError(UI ui, Exception t) { - if (t == null) { - ui.setConnected(false); - ui.setDisconnected(true); - ui.setStatus("Disconnected."); - } else { - ui.setStatus("Error: " + t.getLocalizedMessage()); - } - } - - @Function static void joinGame(UI u, Game data) { - Request r = new Request(); - r.setMsg(MsgType.JoinGame); - r.setObserver(false); - r.setGameId(data.getGameId()); - u.sendMsg(r); - } - - @Function static void observeGame(UI u, Game data) { - Request r = new Request(); - r.setMsg(MsgType.JoinGame); - r.setObserver(true); - r.setGameId(data.getGameId()); - u.sendMsg(r); - } - - private static final Set ACTIVE = new HashSet<>(); - @OnPropertyChange("connected") static void clearGames(UI m) { - if (m.isDisconnected()) { - m.getBoards().clear(); - ACTIVE.remove(m); - } else { - ACTIVE.add(m); - } - } - - static UI findUI(Board b) { - for (UI ui : ACTIVE) { - if (ui.getBoards().contains(b)) { - return ui; - } - } - return null; - } - - @Function static void leave(UI ui, Board data) { - ui.getBoards().remove(data); - ui.setSelectedGameId(null); - } - - @Function static void disconnect(UI ui) { - ui.queryServer(ui.getSettings().getUrl(), null); - ui.setStatus("Disconnecting..."); - } - - private static void verifyLogin(UI ui) { - ui.setStatus("Verifying user credentials..."); - Request r = new Request(); - r.setMsg(MsgType.CheckCredentials); - ui.sendMsg(r); - } - - @Function static void reconnect(UI ui) { - final Settings s = ui.getSettings(); - ui.setStatus("Connecting to the server..."); - ui.queryServer(s.getUrl(), null); - s.write(ui); - } - - public static void initialize(String... args) throws URISyntaxException, IOException { - UI ui = new UI(); - ui.getSettings().read(); - ui.setStatus("Ready."); - ui.setDisconnected(true); - ui.applyBindings(); - } - -} diff -r 49011b4a2689 -r 3f1866fdb2a1 chess/src/main/java/org/apidesign/html/demo/chess/BoardModel.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/chess/src/main/java/org/apidesign/html/demo/chess/BoardModel.java Tue Sep 24 22:37:17 2013 +0200 @@ -0,0 +1,451 @@ +/** + * The MIT License (MIT) + * + * Copyright (C) 2013 Jaroslav Tulach + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.apidesign.html.demo.chess; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Timer; +import java.util.TimerTask; +import net.java.html.json.ComputedProperty; +import net.java.html.json.Function; +import net.java.html.json.Model; +import net.java.html.json.ModelOperation; +import net.java.html.json.OnPropertyChange; +import net.java.html.json.Property; +import net.java.html.sound.AudioClip; +import org.apidesign.html.demo.chess.Communication.AlertType; + +@Model(className="Board", properties={ + @Property(name = "player", type = String.class), + @Property(name = "gameId", type = String.class), + @Property(name = "whitePlayer", type = String.class), + @Property(name = "blackPlayer", type = String.class), + @Property(name = "alert", type = Alert.class), + @Property(name = "alertMessage", type = String.class), + @Property(name = "status", type = String.class), + @Property(name = "rows", type = Row.class, array = true), + @Property(name = "turn", type = Color.class), + @Property(name = "moves", type = Move.class, array = true), + @Property(name = "pendingMove", type = Move.class), + @Property(name = "active", type = boolean.class), +}) +public class BoardModel { + @ComputedProperty static String title(String status, String gameId) { + String t = status != null ? status : gameId; + if (t != null && t.length() > 10) { + return t.substring(0, 10); + } + return t; + } + + @ModelOperation static void updateSummary(Board b, String summary) { + if (summary != null) { + b.setStatus(summary); + } + } + + @ComputedProperty static boolean myTurn(String player, String whitePlayer, String blackPlayer, Color turn) { + if (turn != null && player != null) switch (turn) { + case B: return player.equals(blackPlayer); + case W: return player.equals(whitePlayer); + } + return false; + } + + @ComputedProperty static boolean justObserving(String player) { + return player == null; + } + + private static final AudioClip MOVE = AudioClip.create("sounds/move.mp3"); + @OnPropertyChange("moves") static void playMove() { + MOVE.play(); + } + + private static final AudioClip CHECK = AudioClip.create("sounds/check.mp3"); + private static final AudioClip CHECKMATE = AudioClip.create("sounds/checkmate.mp3"); + @OnPropertyChange("alert") static void warnCheckAndMate(Board b) { + if (b.getAlert() == null) { + return; + } + if (b.getAlert().getType() == AlertType.CHECK) { + CHECK.play(); + } + if (b.getAlert().getType() == AlertType.CHECKMATE) { + CHECKMATE.play(); + } + } + + @Function static void selected(Board b, Square data) { + if (!b.isMyTurn()) { + b.setAlertMessage("Not your turn!"); + return; + } + + Square previoslySelected = findSelectedSquare(b); + if (previoslySelected == null) { + if (data.getPiece() != null && data.getPieceColor() == b.getTurn()) { + data.setSelected(true); + Rules.computeAccessible(b, data); + } + } else { + if (previoslySelected == data) { + data.setSelected(false); + Rules.computeAccessible(b, null); + return; + } + if (data.getPiece() != null && data.getPieceColor() == previoslySelected.getPieceColor()) { + previoslySelected.setSelected(false); + data.setSelected(true); + Rules.computeAccessible(b, data); + return; + } + if (data.isAccessible()) { + previoslySelected.setSelected(false); + + Move newMove = new Move(); + newMove.setFrom(previoslySelected.getPosition()); + newMove.setTo(data.getPosition()); + newMove.setRound(b.getMoves().size() / 2 + 1); + newMove.setPiece(previoslySelected.getPiece()); + newMove.setTurn(previoslySelected.getPieceColor()); + newMove.setTakes(data.getPiece() != null); + b.getMoves().add(newMove); + b.setPendingMove(newMove); + + data.setPending(true); + data.setPieceColor(previoslySelected.getPieceColor()); + data.setPiece(previoslySelected.getPiece()); + b.setTurn(null); + previoslySelected.setPiece(null); + previoslySelected.setPieceColor(null); + Rules.computeAccessible(b, null); + + Request sm = new Request(); + sm.setMsg(MsgType.SendMove); + sm.setGameId(b.getGameId()); + sm.setFrom(newMove.getFrom().getLocation()); + sm.setTo(newMove.getTo().getLocation()); + sm.setColor(newMove.getTurn()); + final UI ui = UIModel.findUI(b); + if (ui != null) { + ui.sendMsg(sm); + } + } + } + } + + static class NextMove extends TimerTask { + private static final Timer T = new Timer("Animate moves"); + private final Board b; + private final Move m; + + public NextMove(Board b, Move m) { + this.b = b; + this.m = m; + T.schedule(this, 1000); + } + + + @Override + public void run() { + b.showPosition(m); + } + } + + @ModelOperation @Function static void showPosition(Board b, Move data) { + Rules.initBoard(b); + boolean found = false; + for (Move m : b.getMoves()) { + if (found) { + b.setTurn(null); + new NextMove(b, m); + return; + } + Square from = findSquare(b, (char)m.getFrom().getX(), m.getFrom().getY()); + Square to = findSquare(b, (char)m.getTo().getX(), m.getTo().getY()); + to.setPiece(from.getPiece()); + to.setPieceColor(from.getPieceColor()); + from.setPiece(null); + from.setPieceColor(null); + if (m == data) { + found = true; + } + } + b.setTurn(b.getMoves().size() % 2 == 0 ? Color.W : Color.B); + } + + + + @Function static void rotateBoard(Board b) { + Collections.reverse(b.getRows()); + for (Row r : b.getRows()) { + Collections.reverse(r.getColumns()); + } + Square sq = findSelectedSquare(b); + if (sq != null) { + sq.setSelected(false); + Rules.computeAccessible(b, null); + } + } + + @ComputedProperty static boolean whiteTurn(Color turn) { + return turn == Color.W; + } + + @ComputedProperty static boolean blackTurn(Color turn) { + return turn == Color.B; + } + + @ComputedProperty static List columnNames(List rows) { + boolean whiteDown = rows.isEmpty() || rows.get(0).getY() == 8; + String[] arr = new String[8]; + for (int i = 0; i < 8; i++) { + String s; + if (whiteDown) { + s = "" + (char)('A' + i); + } else { + s = "" + (char)('H' - i); + } + arr[i] = s; + } + return Arrays.asList(arr); + } + + static Square findSquare(Board b, char column, int row) { + for (Row r : b.getRows()) { + for (Square square : r.getColumns()) { + if (square.getPosition().getX() == column && square.getPosition().getY() == row) { + return square; + } + } + } + return null; + } + + private static Square findSquare(Board b, Position to) { + return findSquare(b, (char)to.getX(), to.getY()); + } + + static Square findSelectedSquare(Board b) { + for (Row row : b.getRows()) { + for (Square square : row.getColumns()) { + if (square.isSelected()) { + return square; + } + } + } + return null; + } + + static void moveResponse(final Board b, final String errMsg, final List whites, final List blacks, final Color turn, + Alert alert + ) { + if (errMsg != null) { + b.getMoves().remove(b.getPendingMove()); + b.setPendingMove(null); + b.setAlertMessage(errMsg); + } else { + b.setTurn(turn); + b.setAlertMessage(null); + } + b.setAlert(alert); + Rules.initBoard(b, whites, blacks, turn); + } + + static void moveUpdate(final Board b, final Move move, final List whites, final List blacks, final Color turn, Alert alert) { + final Square from = BoardModel.findSquare(b, move.getFrom()); + final Square to = BoardModel.findSquare(b, move.getTo()); + move.setPiece(from.getPiece()); + move.setTurn(from.getPieceColor()); + if (to.getPiece() != null) { + move.setTakes(true); + } + b.setAlert(alert); + b.setAlertMessage(alert == null ? null : alert.getMessage()); + move.setRound(b.getMoves().size() / 2 + 1); + b.getMoves().add(move); + Rules.initBoard(b, whites, blacks, turn); + } + + @Model(className="Row", properties = { + @Property(name = "columns", type = Square.class, array = true) + }) + static class RowsImpl { + @ComputedProperty static int y(List columns) { + return columns.isEmpty() ? 0 : columns.get(0).getY(); + } + } + + enum PieceType { + PAWN(5), ROCK(2), KNIGHT(4), BISHOP(3), QUEEN(1), KING(0); + + final int entityIndex; + + PieceType(int ei) { + this.entityIndex = ei; + } + + static PieceType fromNotation(char notation) { + switch (notation) { + case 'R': return ROCK; + case 'N': return KNIGHT; + case 'B': return BISHOP; + case 'Q': return QUEEN; + case 'K': return KING; + case 'P': return PAWN; + } + throw new IllegalStateException("Unexpected: " + notation); + } + + String computeEntity(Color color) { + if (color == null) { + color = Color.W; + } + int base; + switch (color) { + case W: base = 12; break; + case B: base = 18; break; + default: + throw new AssertionError(); + } + return "b" + String.valueOf(base + entityIndex) + ";"; + } + } + + @Model(className="Position", properties = { + @Property(name = "x", type = char.class), + @Property(name = "y", type = int.class), + }) + static class PositionImpl { + @ComputedProperty static String location(int x, int y) { + return "" + (char)(x - 'A' + 'a') + y; + } + } + + @Model(className="Square", properties = { + @Property(name = "position", type = Position.class), + @Property(name = "color", type = Color.class), + @Property(name = "piece", type = PieceType.class), + @Property(name = "pieceColor", type = Color.class), + @Property(name = "selected", type = boolean.class), + @Property(name = "accessible", type = boolean.class), + @Property(name = "pending", type = boolean.class), + }) + static class SquareModel { + @ComputedProperty static String pieceEntity( + PieceType piece, Color pieceColor + ) { + if (piece == null) { + return ""; + } + return piece.computeEntity(pieceColor); + } + + @ComputedProperty static String squareColor( + Color color, boolean selected, boolean accessible, boolean pending + ) { + if (selected) { + return "selected"; + } + if (accessible) { + return "accessible"; + } + if (pending) { + return "pending"; + } + + if (color == null) { + return ""; + } else { + if (color == Color.W) { + return "white"; + } else { + return "black"; + } + } + } + + @ComputedProperty static char x(Position position) { + return position == null ? 'A' : position.getX(); + } + + @ComputedProperty static int y(Position position) { + return position == null ? 1 : position.getY(); + } + } + + @Model(className = "Move", properties = { + @Property(name = "round", type = int.class), + @Property(name = "turn", type = Color.class), + @Property(name = "piece", type = PieceType.class), + @Property(name = "from", type = Position.class), + @Property(name = "to", type = Position.class), + @Property(name = "promoted", type = PieceType.class), + @Property(name = "takes", type = boolean.class), + @Property(name = "check", type = boolean.class), + }) + static class MoveImpl { + @ComputedProperty static boolean whiteMove(Color turn) { + return turn == Color.W; + } + + @ComputedProperty static String html( + Position from, Position to, boolean takes, PieceType piece, Color turn + ) { + if (from == null || to == null) { + return ""; + } + StringBuilder sb = new StringBuilder(); + if (piece != null && piece != PieceType.PAWN) { + sb.append(piece.computeEntity(turn)); + } + + sb.append(from.getLocation()); + if (takes) { + sb.append("x"); + } + sb.append(to.getLocation()); + return sb.toString(); + } + + static Move valueOf(String move) { + move = move.toUpperCase(); + Move m = new Move(); + { + Position p = new Position(); + p.setX(move.charAt(0)); + p.setY(move.charAt(1) - '0'); + m.setFrom(p); + } + { + Position p = new Position(); + p.setX(move.charAt(2)); + p.setY(move.charAt(3) - '0'); + m.setTo(p); + } + return m; + } + } +} diff -r 49011b4a2689 -r 3f1866fdb2a1 chess/src/main/java/org/apidesign/html/demo/chess/Color.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/chess/src/main/java/org/apidesign/html/demo/chess/Color.java Tue Sep 24 22:37:17 2013 +0200 @@ -0,0 +1,32 @@ +/** + * The MIT License (MIT) + * + * Copyright (C) 2013 Jaroslav Tulach + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.apidesign.html.demo.chess; + +/** + * + * @author Jaroslav Tulach + */ +public enum Color { + W, B; +} diff -r 49011b4a2689 -r 3f1866fdb2a1 chess/src/main/java/org/apidesign/html/demo/chess/Communication.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/chess/src/main/java/org/apidesign/html/demo/chess/Communication.java Tue Sep 24 22:37:17 2013 +0200 @@ -0,0 +1,110 @@ +/** + * The MIT License (MIT) + * + * Copyright (C) 2013 Jaroslav Tulach + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.apidesign.html.demo.chess; + +import net.java.html.json.ComputedProperty; +import net.java.html.json.Model; +import net.java.html.json.Property; + + +/** Communication protocols to talk to the server. + * + * @author Jaroslav Tulach + */ +final class Communication { + @Model(className = "Request", properties = { + @Property(name = "msg", type = MsgType.class), + @Property(name = "username", type = String.class), + @Property(name = "password", type = String.class), + @Property(name = "gameId", type = String.class), + @Property(name = "color", type = Color.class), + @Property(name = "from", type = String.class), + @Property(name = "to", type = String.class), + @Property(name = "observer", type = boolean.class), + }) + static class RequestModel { + } + + @Model(className = "BoardRep", properties = { + @Property(name = "whites", type = String.class, array = true), + @Property(name = "blacks", type = String.class, array = true), + }) + static class BoardRepModel { + } + + @Model(className = "Response", properties = { + @Property(name = "msg", type = String.class), + @Property(name = "games", array = true, type = Game.class), + @Property(name = "board", type = BoardRep.class), + @Property(name = "turn", type = Color.class), + @Property(name = "color", type = Color.class), + @Property(name = "gameId", type = String.class), + @Property(name = "error", type = Err.class), + @Property(name = "alert", type = Alert.class), + @Property(name = "status", type = String.class), + @Property(name = "summary", type = String.class), + @Property(name = "moves", type = String.class, array = true), + @Property(name = "from", type = String.class), + @Property(name = "to", type = String.class), + @Property(name = "check", type = CheckResult.class), + @Property(name = "whitePlayer", type = String.class), + @Property(name = "blackPlayer", type = String.class), + }) + static class ResponseModel { + @ComputedProperty static Color nextTurn(Color turn, Alert alert) { + if (alert == null || !alert.getType().isTerminal()) { + return turn; + } else { + return null; + } + } + } + + static enum CheckResult { + VALID, INVALID, NOT_REGISTERED + }; + + @Model(className = "Err", properties = { + @Property(name = "code", type = int.class), + @Property(name = "message", type = String.class), + }) + static class ErrorModel { + } + + @Model(className = "Alert", properties = { + @Property(name = "type", type = AlertType.class), + @Property(name = "message", type = String.class), + }) + static class AlertModel { + } + + static enum AlertType { + CHECKMATE, CHECK, DRAW; + + public boolean isTerminal() { + return this == CHECKMATE || this == DRAW; + } + }; + +} diff -r 49011b4a2689 -r 3f1866fdb2a1 chess/src/main/java/org/apidesign/html/demo/chess/GamesModel.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/chess/src/main/java/org/apidesign/html/demo/chess/GamesModel.java Tue Sep 24 22:37:17 2013 +0200 @@ -0,0 +1,86 @@ +/** + * The MIT License (MIT) + * + * Copyright (C) 2013 Jaroslav Tulach + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.apidesign.html.demo.chess; + +import java.util.List; +import net.java.html.json.ComputedProperty; +import net.java.html.json.Model; +import net.java.html.json.ModelOperation; +import net.java.html.json.Property; + +/** + * + * @author Jaroslav Tulach + */ +@Model(className = "Games", properties = { + @Property(name = "own", array = true, type = Game.class), + @Property(name = "other", array = true, type = Game.class), + @Property(name = "selectedColor", type = String.class), +}) +class GamesModel { + @Model(className = "Game", properties = { + @Property(name = "gameId", type=String.class), + @Property(name = "summary", type=String.class), + @Property(name = "open", type=boolean.class), + @Property(name = "whitePlayer", type=String.class), + @Property(name = "blackPlayer", type=String.class) + }) + static class GameImpl { + @ComputedProperty static String description(String gameId, String summary) { + return summary != null ? summary : gameId; + } + + @ComputedProperty static boolean pendingPlayer(String whitePlayer, String blackPlayer) { + return whitePlayer == null || blackPlayer == null; + } + } + + + @ComputedProperty static boolean anyOwnGame(List own) { + return !own.isEmpty(); + } + + @ComputedProperty static boolean anyOtherGame(List other) { + return !other.isEmpty(); + } + + @ModelOperation static void listGames(Games g, List games, String username) { + g.getOwn().clear(); + g.getOther().clear(); + int cnt = 0; + final int maxGames = 30; + for (Game game : games) { + if ( + username.equals(game.getWhitePlayer()) || + username.equals(game.getBlackPlayer()) + ) { + g.getOwn().add(game); + } else { + if (cnt++ < maxGames) { + g.getOther().add(game); + } + } + } + } +} diff -r 49011b4a2689 -r 3f1866fdb2a1 chess/src/main/java/org/apidesign/html/demo/chess/LoadMain.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/chess/src/main/java/org/apidesign/html/demo/chess/LoadMain.java Tue Sep 24 22:37:17 2013 +0200 @@ -0,0 +1,41 @@ +/** + * The MIT License (MIT) + * + * Copyright (C) 2013 Jaroslav Tulach + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.apidesign.html.demo.chess; + + +public final class LoadMain { + private LoadMain() { + } + + /** + * Called when the page is ready. + */ + static { + try { + UIModel.initialize(); + } catch (Throwable ex) { + ex.printStackTrace(); + } + } +} diff -r 49011b4a2689 -r 3f1866fdb2a1 chess/src/main/java/org/apidesign/html/demo/chess/Main.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/chess/src/main/java/org/apidesign/html/demo/chess/Main.java Tue Sep 24 22:37:17 2013 +0200 @@ -0,0 +1,42 @@ +/** + * The MIT License (MIT) + * + * Copyright (C) 2013 Jaroslav Tulach + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.apidesign.html.demo.chess; + +import net.java.html.boot.BrowserBuilder; +import org.apidesign.bck2brwsr.core.JavaScriptOnly; + +public final class Main { + private Main() { + } + + @JavaScriptOnly(name = "main", value = "null") + public static void main(String... args) throws Exception { + BrowserBuilder.newBrowser(). + loadPage("pages/index.html"). + loadClass(UIModel.class). + invoke("initialize", args). + showAndWait(); + System.exit(0); + } +} diff -r 49011b4a2689 -r 3f1866fdb2a1 chess/src/main/java/org/apidesign/html/demo/chess/MsgType.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/chess/src/main/java/org/apidesign/html/demo/chess/MsgType.java Tue Sep 24 22:37:17 2013 +0200 @@ -0,0 +1,40 @@ +/** + * The MIT License (MIT) + * + * Copyright (C) 2013 Jaroslav Tulach + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.apidesign.html.demo.chess; + +/** + * + * @author Jaroslav Tulach + */ + enum MsgType { + CreateGame, QueryGames, SendMove, JoinGame, UpdateGame, CheckCredentials; + + static MsgType forResponse(String msg) { + if (msg.endsWith("Rsp")) { + msg = msg.substring(0, msg.length() - 3); + } + return MsgType.valueOf(msg); + } + +} diff -r 49011b4a2689 -r 3f1866fdb2a1 chess/src/main/java/org/apidesign/html/demo/chess/Rules.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/chess/src/main/java/org/apidesign/html/demo/chess/Rules.java Tue Sep 24 22:37:17 2013 +0200 @@ -0,0 +1,314 @@ +/** + * The MIT License (MIT) + * + * Copyright (C) 2013 Jaroslav Tulach + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.apidesign.html.demo.chess; + +import java.util.ArrayList; +import java.util.List; + +/** Common chess rules. + * + * @author Jaroslav Tulach + */ +class Rules { + static void computeAccessible(Board b, Square s) { + for (Row r : b.getRows()) { + for (Square ts : r.getColumns()) { + ts.setAccessible(false); + } + } + if (s == null) { + return; + } + + switch (s.getPiece()) { + case BISHOP: + moveBishop(b, s); + break; + case KING: + computeAccessible(b, s, 1, 1, 1); + computeAccessible(b, s, 1, -1, 1); + computeAccessible(b, s, -1, -1, 1); + computeAccessible(b, s, -1, 1, 1); + computeAccessible(b, s, 1, 0, 1); + computeAccessible(b, s, 0, -1, 1); + computeAccessible(b, s, 0, 1, 1); + computeAccessible(b, s, -1, 0, 1); + checkRochade(b, s); + break; + case ROCK: + moveRock(b, s); + break; + case QUEEN: + moveRock(b, s); + moveBishop(b, s); + break; + case KNIGHT: + computeAccessible(b, s, 2, 1, 1); + computeAccessible(b, s, 2, -1, 1); + computeAccessible(b, s, -2, -1, 1); + computeAccessible(b, s, -2, 1, 1); + computeAccessible(b, s, 1, 2, 1); + computeAccessible(b, s, -1, 2, 1); + computeAccessible(b, s, -1, -2, 1); + computeAccessible(b, s, 1, -2, 1); + break; + case PAWN: + pawns(b, s); + break; + } + } + + private static void moveRock(Board b, Square s) { + computeAccessible(b, s, 1, 0, 8); + computeAccessible(b, s, 0, -1, 8); + computeAccessible(b, s, -1, 0, 8); + computeAccessible(b, s, 0, 1, 8); + } + + private static void moveBishop(Board b, Square s) { + computeAccessible(b, s, 1, 1, 8); + computeAccessible(b, s, 1, -1, 8); + computeAccessible(b, s, -1, -1, 8); + computeAccessible(b, s, -1, 1, 8); + } + + private static void computeAccessible( + Board b, Square s, int dx, int dy, + int limit + ) { + int x = s.getX(); + int y = s.getY(); + + while (limit-- > 0) { + x += dx; + y += dy; + Square next = BoardModel.findSquare(b, (char)x, y); + if (next == null) { + break; + } + if (next.getPieceColor() == s.getPieceColor()) { + break; + } + next.setAccessible(true); + if (next.getPieceColor() != null) { + break; + } + } + } + + private static void pawns(Board b, Square s) { + final boolean white = s.getPieceColor() == Color.W; + int dy = white ? 1 : -1; + Square step = BoardModel.findSquare(b, (char)s.getX(), s.getY() + dy); + if (step != null && step.getPiece() == null) { + step.setAccessible(true); + if ((s.getY() == 2 && white) || (s.getY() == 7 && !white)) { + Square nextSTep = BoardModel.findSquare(b, (char)s.getX(), step.getY() + dy); + if (nextSTep != null && step.getPiece() == null && nextSTep.getPiece() == null) { + nextSTep.setAccessible(true); + } + } + } + Color opposite = white ? Color.B : Color.W; + Square takeLeft = BoardModel.findSquare(b, (char)(s.getX() - 1), s.getY() + dy); + if (takeLeft != null && takeLeft.getPieceColor() == opposite) { + takeLeft.setAccessible(true); + } + Square takeRight = BoardModel.findSquare(b, (char)(s.getX() + 1), s.getY() + dy); + if (takeRight != null && takeRight.getPieceColor() == opposite) { + takeRight.setAccessible(true); + } + if ((white && s.getY() == 5) || (!white && s.getY() == 4)) { + int enPassantFrom = white ? 7 : 2; + int enPassantTo = white ? 5 : 4; + if (!b.getMoves().isEmpty()) { + Move last = b.getMoves().get(b.getMoves().size() - 1); + if ( + last.getPiece() == BoardModel.PieceType.PAWN && + last.getFrom().getY() == enPassantFrom && + last.getTo().getY() == enPassantTo + ) { + if (takeLeft != null && last.getFrom().getX() == s.getX() - 1) { + takeLeft.setAccessible(true); + } + if (takeRight != null && last.getFrom().getX() == s.getX() + 1) { + takeRight.setAccessible(true); + } + } + } + } + } + + static Board createBoard() { + Board b = new Board(); + initBoardField(b); + initBoard(b); + return b; + } + + private static void initBoardField(Board b) { + List addRows = new ArrayList<>(8); + for (int i = 8; i > 0; i--) { + Square[] arr = new Square[8]; + for (char j = 'A'; j <= 'H'; j++) { + arr[j - 'A'] = new Square( + new Position(j, i), + (i + j) % 2 == 1 ? Color.W : Color.B, + null, null, // figure + false, false, false + ); + } + addRows.add(new Row(arr)); + } + b.getRows().addAll(addRows); + } + + static void initBoard(Board b) { + initBoard(b, true); + } + private static void initBoard(Board b, boolean init) { + if (init) { + b.setTurn(Color.W); + } + if (b.getRows().isEmpty()) { + for (int i = 8; i > 0; i--) { + Row r = b.getRows().get(8 - i); + for (char j = 'A'; j <= 'H'; j++) { + Square s = r.getColumns().get(j - 'A'); + s.setAccessible(false); + s.setPending(false); + r.getColumns().set(j - 'A', s); + initialPosition(s, init); + } + } + } else { + for (Row r : b.getRows()) { + for (Square square : r.getColumns()) { + square.setAccessible(false); + square.setPending(false); + square.setPiece(null); + square.setPieceColor(null); + square.setSelected(false); + initialPosition(square, init); + } + } + } + b.setPendingMove(null); + } + + private static void initialPosition(Square s, boolean init) { + int row = s.getPosition().getY(); + char column = s.getPosition().getX(); + s.setPiece(null); + s.setPieceColor(null); + if (init) { + if (row == 2) { + s.setPiece(BoardModel.PieceType.PAWN); + s.setPieceColor(Color.W); + } else if (row == 7) { + s.setPiece(BoardModel.PieceType.PAWN); + s.setPieceColor(Color.B); + } else if (row == 8 || row == 1) { + s.setPieceColor(row == 1 ? Color.W : Color.B); + BoardModel.PieceType t; + switch (column) { + case 'A': + case 'H': + t = BoardModel.PieceType.ROCK; + break; + case 'B': + case 'G': + t = BoardModel.PieceType.KNIGHT; + break; + case 'C': + case 'F': + t = BoardModel.PieceType.BISHOP; + break; + case 'D': + t = BoardModel.PieceType.QUEEN; + break; + default: + t = BoardModel.PieceType.KING; + break; + } + s.setPiece(t); + } + } + } + + static void initBoard( + Board board, List whites, List blacks, Color turn + ) { + initBoard(board, false); + for (String w : whites) { + assert w.length() == 3 : "Expecting three letter string: '" + w + "'"; + w = w.toUpperCase(); + char column = w.charAt(1); + int row = (w.charAt(2) - '0'); + + Square s = BoardModel.findSquare(board, column, row); + s.setPieceColor(Color.W); + s.setPiece(BoardModel.PieceType.fromNotation(w.charAt(0))); + } + for (String w : blacks) { + assert w.length() == 3 : "Expecting three letter string: '" + w + "'"; + w = w.toUpperCase(); + char column = w.charAt(1); + int row = (w.charAt(2) - '0'); + + Square s = BoardModel.findSquare(board, column, row); + s.setPieceColor(Color.B); + s.setPiece(BoardModel.PieceType.fromNotation(w.charAt(0))); + } + board.setTurn(turn); + } + + private static void checkRochade(Board b, Square s) { + if (s.getPosition().getX() == 'E') { + int y = s.getPosition().getY(); + final Square gRow = BoardModel.findSquare(b, 'G', y); + if ( + BoardModel.findSquare(b, 'H', y).getPiece() == BoardModel.PieceType.ROCK + && + BoardModel.findSquare(b, 'F', y).getPiece() == null + && + gRow.getPiece() == null + ) { + gRow.setAccessible(true); + } + final Square cRow = BoardModel.findSquare(b, 'C', y); + if ( + BoardModel.findSquare(b, 'A', y).getPiece() == BoardModel.PieceType.ROCK + && + BoardModel.findSquare(b, 'B', y).getPiece() == null + && + BoardModel.findSquare(b, 'D', y).getPiece() == null + && + cRow.getPiece() == null + ) { + cRow.setAccessible(true); + } + } + } +} diff -r 49011b4a2689 -r 3f1866fdb2a1 chess/src/main/java/org/apidesign/html/demo/chess/SettingsModel.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/chess/src/main/java/org/apidesign/html/demo/chess/SettingsModel.java Tue Sep 24 22:37:17 2013 +0200 @@ -0,0 +1,82 @@ +/** + * The MIT License (MIT) + * + * Copyright (C) 2013 Jaroslav Tulach + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.apidesign.html.demo.chess; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.Properties; +import net.java.html.json.ComputedProperty; +import net.java.html.json.Model; +import net.java.html.json.ModelOperation; +import net.java.html.json.Property; + +/** + * + * @author Jaroslav Tulach + */ +@Model(className = "Settings", properties = { + @Property(name = "username", type = String.class), + @Property(name = "password", type = String.class), + @Property(name = "url", type = String.class), +}) +class SettingsModel { + private static final String SETTINGS = ".j1chess/html.properties"; + private static final String DEFAULT_URL = "ws://localhost:8080/chess/chessserver"; + + @ComputedProperty static boolean validCredentials(String username, String password) { + return username != null && password != null && !username.isEmpty() && !password.isEmpty(); + } + + @ModelOperation static void read(Settings s) { + File prefs = new File(System.getProperty("user.home"), SETTINGS); + try (FileInputStream is = new FileInputStream(prefs)) { + Properties p = new Properties(); + p.load(is); + + s.setUsername(p.getProperty("username", "")); + s.setPassword(p.getProperty("password", "")); + s.setUrl(p.getProperty("url", DEFAULT_URL)); + } catch (Throwable ex) { + s.setUsername(""); + s.setPassword(""); + s.setUrl(DEFAULT_URL); + } + } + + @ModelOperation static void write(Settings s, UI ui) { + File prefs = new File(System.getProperty("user.home"), SETTINGS); + prefs.getParentFile().mkdirs(); + try (FileOutputStream os = new FileOutputStream(prefs)) { + Properties p = new Properties(); + p.setProperty("username", s.getUsername()); + p.setProperty("password", s.getPassword()); + p.setProperty("url", s.getUrl()); + p.store(os, "Java One Chess Preferences for HTML/Java Client"); + } catch (Throwable ex) { + ui.setStatus("Cannot save preferences: " + ex.getMessage()); + } + } +} diff -r 49011b4a2689 -r 3f1866fdb2a1 chess/src/main/java/org/apidesign/html/demo/chess/UIModel.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/chess/src/main/java/org/apidesign/html/demo/chess/UIModel.java Tue Sep 24 22:37:17 2013 +0200 @@ -0,0 +1,315 @@ +/** + * The MIT License (MIT) + * + * Copyright (C) 2013 Jaroslav Tulach + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.apidesign.html.demo.chess; + +import java.io.IOException; +import java.net.URISyntaxException; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; +import net.java.html.json.ComputedProperty; +import net.java.html.json.Function; +import net.java.html.json.Model; +import net.java.html.json.ModelOperation; +import net.java.html.json.OnPropertyChange; +import net.java.html.json.OnReceive; +import net.java.html.json.Property; + +/** + * + * @author Jaroslav Tulach + */ +@Model(className = "UI", properties = { + @Property(name = "status", type = String.class), + @Property(name = "selectedGameId", type = String.class), + @Property(name = "boards", type = Board.class, array = true), + @Property(name = "viewGames", type = Games.class), + @Property(name = "settings", type = Settings.class), + @Property(name = "connected", type = boolean.class), + @Property(name = "disconnected", type = boolean.class), +}) +public class UIModel { + private static final Logger LOG = Logger.getLogger(UIModel.class.getName()); + + @ComputedProperty static boolean settingsActive(String selectedGameId) { + return selectedGameId == null; + } + + @ComputedProperty static boolean viewGamesActive(String selectedGameId, boolean connected) { + return selectedGameId == null && connected; + } + + @ComputedProperty static Board selectedBoard(String selectedGameId, List boards) { + Board active = null; + for (Board b : boards) { + b.setActive(false); + if (selectedGameId != null && selectedGameId.equals(b.getGameId())) { + b.setActive(true); + active = b; + } + } + return active; + } + + @Function static void activateGame(UI m, Board data) { + m.setSelectedGameId(data.getGameId()); + } + + @Function static void activateSettings(UI m) { + m.setSelectedGameId(null); + refreshGames(m); + } + + @Function static void createGame(UI u) { + u.setStatus("Creating new game..."); + Request r = new Request(); + r.setMsg(MsgType.CreateGame); + r.setColor(Color.valueOf(u.getViewGames().getSelectedColor())); + u.sendMsg(r); + } + + @ModelOperation @Function static void refreshGames(UI u) { + u.setStatus("Refreshing games..."); + Request r = new Request(); + r.setMsg(MsgType.QueryGames); + u.sendMsg(r); + } + + @ModelOperation static void sendMsg(UI ui, Request r) { + final Settings sttngs = ui.getSettings(); + + String url = sttngs.getUrl(); + r.setUsername(sttngs.getUsername()); + r.setPassword(sttngs.getPassword()); + + LOG.log(Level.INFO, "Sending {0} to {1}", new Object[]{r, url}); + ui.queryServer(url, r); + } + + + @OnReceive(data = Request.class, url = "{url}", method = "WebSocket", onError = "wasAnError") + static void queryServer(UI ui, Response r) { + LOG.log(Level.INFO, "Received {0}", r); + if (r == null) { + ui.setDisconnected(false); + verifyLogin(ui); + return; + } + SWITCH: switch (MsgType.forResponse(r.getMsg())) { + case CheckCredentials: { + switch (r.getCheck()) { + case NOT_REGISTERED: + case VALID: + ui.setConnected(true); + ui.refreshGames(); + ui.setStatus("Connected."); + return; + case INVALID: + ui.setStatus("Password is invalid"); + break; + } + disconnect(ui); + } + + case QueryGames: + ui.getViewGames().listGames(r.getGames(), ui.getSettings().getUsername()); + ui.setStatus(""); + break; + case CreateGame: { + ui.setStatus("Game " + r + " created"); + final Board b = Rules.createBoard(); + Rules.initBoard( + b, r.getBoard().getWhites(), + r.getBoard().getBlacks(), + r.getTurn() + ); + b.setGameId(r.getGameId()); + b.setWhitePlayer(r.getWhitePlayer()); + b.setBlackPlayer(r.getBlackPlayer()); + b.setPlayer(ui.getSettings().getUsername()); + b.updateSummary(r.getSummary()); + ui.getBoards().add(b); + ui.setSelectedGameId(r.getGameId()); + UIModel.refreshGames(ui); + break; + } + case SendMove: { + if (r.getBoard() == null) { + throw new NullPointerException("No board in " + r); + } + final Board b = findBoard(ui, r.getGameId()); + if (b == null) { + break; + } + b.updateSummary(r.getSummary()); + final String errMsg = r.getError() == null ? null : r.getError().getMessage(); + final List whites = r.getBoard().getWhites(); + final List blacks = r.getBoard().getBlacks(); + final Color turn = r.getNextTurn(); + BoardModel.moveResponse(b, errMsg, whites, blacks, turn, r.getAlert()); + break; + } + case JoinGame: { + Board b; + if ((b = findBoard(ui, r.getGameId())) != null) { + b.setWhitePlayer(r.getWhitePlayer()); + b.setBlackPlayer(r.getBlackPlayer()); + b.setPlayer(ui.getSettings().getUsername()); + b.updateSummary(r.getSummary()); + ui.setSelectedGameId(r.getGameId()); + break SWITCH; + } + ui.setStatus("Joining " + r.getGameId() + " as " + r.getColor()); + b = Rules.createBoard(); + Rules.initBoard( + b, r.getBoard().getWhites(), + r.getBoard().getBlacks(), + r.getTurn() + ); + b.setGameId(r.getGameId()); + b.setWhitePlayer(r.getWhitePlayer()); + b.setBlackPlayer(r.getBlackPlayer()); + b.setPlayer(ui.getSettings().getUsername()); + b.updateSummary(r.getSummary()); + if (r.getColor() == Color.B) { + BoardModel.rotateBoard(b); + } + if (r.getMoves() != null) { + for (String move : r.getMoves()) { + Move m = BoardModel.MoveImpl.valueOf(move); + b.getMoves().add(m); + } + } else { + b.getMoves().clear(); + } + ui.setSelectedGameId(b.getGameId()); + ui.getBoards().add(b); + break; + } + case UpdateGame: { + final Board b = findBoard(ui, r.getGameId()); + if (b == null) { + break; + } + b.updateSummary(r.getSummary()); + final Move move = BoardModel.MoveImpl.valueOf(r.getFrom() + r.getTo()); + final List whites = r.getBoard().getWhites(); + final List blacks = r.getBoard().getBlacks(); + final Color turn = r.getNextTurn(); + BoardModel.moveUpdate(b, move, whites, blacks, turn, null); + } + } + } + + + private static Board findBoard(UI ui, String gameId) { + for (Board tmp : ui.getBoards()) { + if (tmp.getGameId().equals(gameId)) { + return tmp; + } + } + return null; + } + + + static void wasAnError(UI ui, Exception t) { + if (t == null) { + ui.setConnected(false); + ui.setDisconnected(true); + ui.setStatus("Disconnected."); + } else { + ui.setStatus("Error: " + t.getLocalizedMessage()); + } + } + + @Function static void joinGame(UI u, Game data) { + Request r = new Request(); + r.setMsg(MsgType.JoinGame); + r.setObserver(false); + r.setGameId(data.getGameId()); + u.sendMsg(r); + } + + @Function static void observeGame(UI u, Game data) { + Request r = new Request(); + r.setMsg(MsgType.JoinGame); + r.setObserver(true); + r.setGameId(data.getGameId()); + u.sendMsg(r); + } + + private static final Set ACTIVE = new HashSet<>(); + @OnPropertyChange("connected") static void clearGames(UI m) { + if (m.isDisconnected()) { + m.getBoards().clear(); + ACTIVE.remove(m); + } else { + ACTIVE.add(m); + } + } + + static UI findUI(Board b) { + for (UI ui : ACTIVE) { + if (ui.getBoards().contains(b)) { + return ui; + } + } + return null; + } + + @Function static void leave(UI ui, Board data) { + ui.getBoards().remove(data); + ui.setSelectedGameId(null); + } + + @Function static void disconnect(UI ui) { + ui.queryServer(ui.getSettings().getUrl(), null); + ui.setStatus("Disconnecting..."); + } + + private static void verifyLogin(UI ui) { + ui.setStatus("Verifying user credentials..."); + Request r = new Request(); + r.setMsg(MsgType.CheckCredentials); + ui.sendMsg(r); + } + + @Function static void reconnect(UI ui) { + final Settings s = ui.getSettings(); + ui.setStatus("Connecting to the server..."); + ui.queryServer(s.getUrl(), null); + s.write(ui); + } + + public static void initialize(String... args) throws URISyntaxException, IOException { + UI ui = new UI(); + ui.getSettings().read(); + ui.setStatus("Ready."); + ui.setDisconnected(true); + ui.applyBindings(); + } + +} diff -r 49011b4a2689 -r 3f1866fdb2a1 chess/src/test/java/com/oracle/chess/client/htmljava/BoardModelTest.java --- a/chess/src/test/java/com/oracle/chess/client/htmljava/BoardModelTest.java Tue Sep 24 22:28:31 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,537 +0,0 @@ -/** - * The MIT License (MIT) - * - * Copyright (C) 2013 Jaroslav Tulach - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package com.oracle.chess.client.htmljava; - -import com.oracle.chess.client.htmljava.BoardModel.PieceType; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import static org.testng.Assert.*; -import org.testng.annotations.Test; - -/** - * - * @author Jaroslav Tulach - */ -public class BoardModelTest { - - public BoardModelTest() { - } - - @Test public void whiteRockLeftBottom() { - Board b = Rules.createBoard(); - Row bottom = b.getRows().get(7); - Square left = bottom.getColumns().get(0); - assertEquals(left.getPiece(), PieceType.ROCK, "Rock is on bottom left square"); - assertEquals(left.getPieceColor(), Color.W, "It is white"); - assertEquals(left.getColor(), Color.B, "Square is black"); - } - - @Test public void e2e4e7e6() { - Board b = Rules.createBoard(); - b.setGameId("myId"); - setPlayer(b, Color.W); - assertEquals(b.getTurn(), Color.W); - - Square e2 = BoardModel.findSquare(b, 'E', 2); - assertNotNull(e2); - BoardModel.selected(b, e2); - assertEquals(BoardModel.findSelectedSquare(b), e2, "E2 is selected"); - - Square e4 = BoardModel.findSquare(b, 'E', 4); - assertNotNull(e4); - - BoardModel.selected(b, e4); - - assertTrue(e4.isPending(), "e4 marked as pending move"); - assertNull(e2.getPiece(), "No pawn at e2"); - - // ignore all other figures than the two pawns - BoardModel.moveResponse(b, null, Collections.singletonList("Pe4"), - Collections.singletonList("Pe7"), Color.B, null - ); - - assertFalse(e4.isPending(), "e4 now the move is real"); - assertNull(e2.getPiece(), "No pawn at e2"); - assertEquals(e4.getPiece(), BoardModel.PieceType.PAWN, "Pawn moved successfully"); - assertNull(BoardModel.findSelectedSquare(b), "No square selected"); - - BoardModel.selected(b, e4); - assertNull(BoardModel.findSelectedSquare(b), "No square selected, it is blacks turn"); - - List mvs1 = b.getMoves(); - assertEquals(1, mvs1.size(), "First move was made: " + mvs1); - Move firstMove = mvs1.get(0); - assertEquals(firstMove.getPiece(), PieceType.PAWN, "Moving with pawn"); - assertEquals(firstMove.getRound(), 1, "First round of moves"); - assertEquals(firstMove.getTurn(), Color.W, "Moved by white"); - assertTrue(firstMove.isWhiteMove(), "Really moved by white"); - - assertTrue(b.isBlackTurn(), "black's turn"); - - - Square e7 = BoardModel.findSquare(b, 'E', 7); - BoardModel.selected(b, e7); - assertNull(BoardModel.findSelectedSquare(b), "Can't select anything when I am white player"); - - Move blackMv = BoardModel.MoveImpl.valueOf("E7E6"); - BoardModel.moveUpdate(b, blackMv, - Collections.singletonList("PE4"), - Collections.singletonList("PE6"), - Color.W, null - ); - - assertNull(e7.getPiece(), "Piece has been moved"); - - List mvs = b.getMoves(); - assertEquals(2, mvs.size(), "There are two moves now: " + mvs); - Move secondMove = mvs.get(1); - assertEquals(secondMove.getPiece(), PieceType.PAWN, "Moving with pawn"); - assertEquals(secondMove.getRound(), 1, "Still 1st round of moves"); - assertEquals(secondMove.getTurn(), Color.B, "Moved by black"); - assertFalse(secondMove.isWhiteMove(), "Really moved by black"); - } - - @Test public void unselect() { - Board b = Rules.createBoard(); - b.setGameId("myId"); - setPlayer(b, Color.W); - assertEquals(b.getTurn(), Color.W); - - Square e2 = BoardModel.findSquare(b, 'E', 2); - assertNotNull(e2); - BoardModel.selected(b, e2); - assertEquals(BoardModel.findSelectedSquare(b), e2, "E2 is selected"); - - BoardModel.selected(b, e2); - assertNull(BoardModel.findSelectedSquare(b), "E2 is unselected"); - - for (Row row : b.getRows()) { - for (Square s : row.getColumns()) { - assertFalse(s.isAccessible(), "No square is accessible anymore: " + s); - } - } - } - - @Test public void discaredMove() { - Board b = Rules.createBoard(); - b.setGameId("myId"); - setPlayer(b, Color.W); - assertEquals(b.getTurn(), Color.W); - - Square e2 = BoardModel.findSquare(b, 'E', 2); - assertNotNull(e2); - BoardModel.selected(b, e2); - assertEquals(BoardModel.findSelectedSquare(b), e2, "E2 is selected"); - - Square e4 = BoardModel.findSquare(b, 'E', 4); - assertNotNull(e4); - - BoardModel.selected(b, e4); - - assertTrue(e4.isPending(), "e4 marked as pending move"); - assertNull(e2.getPiece(), "No pawn at e2"); - - assertEquals(b.getMoves().size(), 1, "One move recorded"); - assertEquals(b.getMoves().get(0), b.getPendingMove(), "Pending move is waiting"); - - // ignore all other figures than the two pawns - BoardModel.moveResponse(b, "No way, can't play like this", Collections.singletonList("PE2"), - Collections.singletonList("Pe7"), Color.W, null - ); - - assertEquals(b.getAlertMessage(), "No way, can't play like this"); - assertNull(e4.getPiece(), "No piece on e4"); - assertEquals(e2.getPiece(), PieceType.PAWN, "Pawn is back"); - - assertEquals(b.getMoves().size(), 0, "Move was discarded"); - assertNull(b.getPendingMove(), "No pending moves"); - } - - @Test public void cantSelectEmptySquare() { - Board b = Rules.createBoard(); - Square e3 = BoardModel.findSquare(b, 'E', 3); - assertNotNull(e3); - BoardModel.selected(b, e3); - assertNull(BoardModel.findSelectedSquare(b), "No square is selected"); - } - - @Test public void cantTakeOwnPiece() { - Board b = Rules.createBoard(); - setPlayer(b, Color.W); - Square e1 = BoardModel.findSquare(b, 'E', 1); - assertNotNull(e1); - BoardModel.selected(b, e1); - assertEquals(BoardModel.findSelectedSquare(b), e1, "E1 is selected"); - - Square e2 = BoardModel.findSquare(b, 'E', 2); - assertNotNull(e2); - - BoardModel.selected(b, e2); - - assertNotNull(e1.getPiece(), "King remains at e1"); - assertEquals(e2.getPiece(), BoardModel.PieceType.PAWN, "Pawn remains"); - assertEquals(BoardModel.findSelectedSquare(b), e2, "e2 now selected"); - - } - - @Test public void knightMustMoveToF3() { - Board b = Rules.createBoard(); - setPlayer(b, Color.W); - Square g1 = BoardModel.findSquare(b, 'G', 1); - BoardModel.selected(b, g1); - - Square f3 = BoardModel.findSquare(b, 'F', 3); - assertTrue(f3.isAccessible(), "This is a field where knight can move"); - - Square g3 = BoardModel.findSquare(b, 'G', 3); - assertFalse(g3.isAccessible(), "Not a place for knight"); - - Square e2 = BoardModel.findSquare(b, 'E', 2); - assertFalse(e2.isAccessible(), "Not a place either, occupied"); - - BoardModel.selected(b, g3); - - assertNull(g3.getPiece(), "No figure was moved"); - assertTrue(g1.isSelected(), "Original square still selected"); - - BoardModel.selected(b, f3); - - assertEquals(f3.getPiece(), PieceType.KNIGHT, "Moved"); - assertFalse(g3.isSelected(), "No longer selected"); - } - - @Test public void pawnCanTakeToSide() { - Board b = Rules.createBoard(); - Square e2 = BoardModel.findSquare(b, 'E', 2); - Square e4 = BoardModel.findSquare(b, 'E', 4); - Square d7 = BoardModel.findSquare(b, 'D', 7); - Square d5 = BoardModel.findSquare(b, 'D', 5); - - BoardModel.selected(b, e2); - BoardModel.selected(b, e4); - - // ignore all other figures than the two pawns - BoardModel.moveResponse(b, null, Collections.singletonList("PE4"), - Collections.singletonList("PD7"), Color.B, null - ); - - setPlayer(b, Color.B); - - BoardModel.selected(b, d7); - BoardModel.selected(b, d5); - - // ignore all other figures than the two pawns - BoardModel.moveResponse(b, null, Collections.singletonList("PE4"), - Collections.singletonList("PD5"), Color.W, null - ); - - setPlayer(b, Color.W); - - BoardModel.selected(b, e4); - assertTrue(d5.isAccessible(), "Can take on d5"); - BoardModel.selected(b, d5); - assertNull(e4.getPiece(), "No pawn on e4"); - assertEquals(d5.getPieceColor(), Color.W, "White Pawn on d5"); - } - - @Test public void pawnCanJumpOnAnother() { - Board b = Rules.createBoard(); - Square e2 = BoardModel.findSquare(b, 'E', 2); - Square e4 = BoardModel.findSquare(b, 'E', 4); - Square d7 = BoardModel.findSquare(b, 'D', 7); - Square d5 = BoardModel.findSquare(b, 'D', 5); - Square e7 = BoardModel.findSquare(b, 'E', 7); - Square e6 = BoardModel.findSquare(b, 'E', 6); - Square e5 = BoardModel.findSquare(b, 'E', 5); - - BoardModel.selected(b, e2); - BoardModel.selected(b, e4); - - // ignore all other figures than the three pawns - BoardModel.moveResponse(b, null, Collections.singletonList("PE4"), - Arrays.asList("PD7", "PE7"), Color.B, null - ); - - setPlayer(b, Color.B); - - BoardModel.selected(b, d7); - BoardModel.selected(b, d5); - - // ignore all other figures than the three pawns - BoardModel.moveResponse(b, null, Collections.singletonList("PE4"), - Arrays.asList("PD5", "PE7"), Color.W, null - ); - - setPlayer(b, Color.W); - - BoardModel.selected(b, e4); - BoardModel.selected(b, e5); - - // ignore all other figures than the three pawns - BoardModel.moveResponse(b, null, Collections.singletonList("PE5"), - Arrays.asList("PD5", "PE7"), Color.B, null - ); - - setPlayer(b, Color.B); - - BoardModel.selected(b, e7); - assertTrue(e6.isAccessible(), "Can move by one piece"); - assertFalse(e5.isAccessible(), "Can't move where other pawn is"); - } - - @Test public void showFirstMove() { - Board b = Rules.createBoard(); - setPlayer(b, Color.W); - Square e2 = BoardModel.findSquare(b, 'E', 2); - Square e4 = BoardModel.findSquare(b, 'E', 4); - Square d7 = BoardModel.findSquare(b, 'D', 7); - Square d5 = BoardModel.findSquare(b, 'D', 5); - - BoardModel.selected(b, e2); - BoardModel.selected(b, e4); - - // ignore all other figures than the two pawns - BoardModel.moveResponse(b, null, Collections.singletonList("Pe4"), - Collections.singletonList("PD7"), Color.B, null - ); - - Move blackMv = BoardModel.MoveImpl.valueOf("D7D5"); - BoardModel.moveUpdate(b, blackMv, - Collections.singletonList("PE4"), - Collections.singletonList("PD5"), - Color.W, null - ); - - - assertEquals(b.getMoves().size(), 2, "Two moves"); - BoardModel.showPosition(b, b.getMoves().get(0)); - - assertEquals(b.getRows().size(), 8, "Still eight rows"); - assertEquals(b.getRows().get(0).getColumns().size(), 8, "Eight columns"); - - assertNull(e2.getPiece(), "e2 is empty"); - assertEquals(e4.getPiece(), PieceType.PAWN, "e4 has pawn"); - assertEquals(d7.getPiece(), PieceType.PAWN, "d7 has pawn"); - assertNull(d5.getPiece(), "Second move is not made"); - assertNull(b.getTurn(), "Nobody to make a move"); - } - - @Test public void initialPositionReadFromAServer() { - List blacks = Arrays.asList( - "Pa7", - "Pb7", - "Pc7", - "Pd7", - "Pe7", - "Pf7", - "Pg7", - "Ph7", - "Ra8", - "Nb8", - "Bc8", - "Qd8", - "Ke8", - "Bf8", - "Ng8", - "Rh8" - ); - List whites = Arrays.asList( - "Ra1", - "Nb1", - "Bc1", - "Qd1", - "Ke1", - "Bf1", - "Ng1", - "Rh1", - "Pa2", - "Pb2", - "Pc2", - "Pd2", - "Pe2", - "Pf2", - "Pg2", - "Ph2" - ); - - Board b = Rules.createBoard(); - Rules.initBoard(b, whites, blacks, Color.W); - - Board b2 = Rules.createBoard(); - Rules.initBoard(b2); - - for (int i = 0; i < 7; i++) { - for (int j = 0; j < 7; j++) { - Square s = b.getRows().get(i).getColumns().get(j); - Square s2 = b2.getRows().get(i).getColumns().get(j); - - assertEquals(s.getPiece(), s2.getPiece(), "Same piece at " + i + "/" + j); - assertEquals(s.getPieceColor(), s2.getPieceColor(), "Same piece color at " + i + "/" + j); - } - } - } - - @Test public void allowSmallRochade() { - Board b = Rules.createBoard(); - setPlayer(b, Color.W); - Square f1 = BoardModel.findSquare(b, 'F', 1); - f1.setPiece(null); - f1.setPieceColor(null); - Square g1 = BoardModel.findSquare(b, 'G', 1); - g1.setPiece(null); - g1.setPieceColor(null); - Square e1 = BoardModel.findSquare(b, 'E', 1); - - BoardModel.selected(b, e1); - assertTrue(g1.isAccessible(), "Can do 0-0"); - } - - @Test public void allowSmallRochadeForBlack() { - Board b = Rules.createBoard(); - setPlayer(b, Color.B); - b.setTurn(Color.B); - Square f8 = BoardModel.findSquare(b, 'F', 8); - f8.setPiece(null); - f8.setPieceColor(null); - Square g8 = BoardModel.findSquare(b, 'G', 8); - g8.setPiece(null); - g8.setPieceColor(null); - Square e8 = BoardModel.findSquare(b, 'E', 8); - - BoardModel.selected(b, e8); - assertTrue(g8.isAccessible(), "Can do 0-0"); - } - - @Test public void disallowSmallRochadeWhenNoRock() { - Board b = Rules.createBoard(); - setPlayer(b, Color.W); - Square f1 = BoardModel.findSquare(b, 'F', 1); - f1.setPiece(null); - f1.setPieceColor(null); - Square g1 = BoardModel.findSquare(b, 'G', 1); - g1.setPiece(null); - g1.setPieceColor(null); - Square h1 = BoardModel.findSquare(b, 'H', 1); - h1.setPiece(null); - h1.setPieceColor(null); - Square e1 = BoardModel.findSquare(b, 'E', 1); - - BoardModel.selected(b, e1); - assertFalse(g1.isAccessible(), "Cannot do 0-0 when there is no rock"); - } - - @Test public void disallowSmallRochadeWhenBishop() { - Board b = Rules.createBoard(); - setPlayer(b, Color.W); - Square g1 = BoardModel.findSquare(b, 'G', 1); - g1.setPiece(null); - Square e1 = BoardModel.findSquare(b, 'E', 1); - - BoardModel.selected(b, e1); - assertFalse(g1.isAccessible(), "Cannot do 0-0 when there is bishop"); - } - - @Test public void disallowSmallRochadeWhenKnight() { - Board b = Rules.createBoard(); - setPlayer(b, Color.W); - Square f1 = BoardModel.findSquare(b, 'F', 1); - f1.setPiece(null); - Square e1 = BoardModel.findSquare(b, 'E', 1); - - BoardModel.selected(b, e1); - assertFalse(f1.isAccessible(), "Cannot do 0-0 when there is knight"); - } - - @Test public void allowBigRochadeForBlack() { - Board b = Rules.createBoard(); - setPlayer(b, Color.B); - b.setTurn(Color.B); - Square b8 = BoardModel.findSquare(b, 'B', 8); - b8.setPiece(null); - b8.setPieceColor(null); - Square c8 = BoardModel.findSquare(b, 'C', 8); - c8.setPiece(null); - c8.setPieceColor(null); - Square d8 = BoardModel.findSquare(b, 'D', 8); - d8.setPiece(null); - d8.setPieceColor(null); - Square e8 = BoardModel.findSquare(b, 'E', 8); - - BoardModel.selected(b, e8); - assertTrue(c8.isAccessible(), "Can do 0-0-0"); - } - - @Test public void enPassant() { - Board b = Rules.createBoard(); - setPlayer(b, Color.B); - b.setTurn(Color.B); - - Square e7 = BoardModel.findSquare(b, 'E', 7); - Square e4 = BoardModel.findSquare(b, 'E', 4); - movePiece(e7, e4); - - Move move = new Move(); - move.setFrom(position('D', 2)); - move.setTo(position('D', 4)); - move.setPiece(PieceType.PAWN); - b.getMoves().add(move); - - Square d2 = BoardModel.findSquare(b, 'D', 2); - Square d4 = BoardModel.findSquare(b, 'D', 4); - movePiece(d2, d4); - - BoardModel.selected(b, e4); - - Square e3 = BoardModel.findSquare(b, 'E', 3); - assertTrue(e3.isAccessible(), "Obviously can move to e3"); - - Square d3 = BoardModel.findSquare(b, 'D', 3); - assertTrue(d3.isAccessible(), "Can also take on d3"); - } - - private void movePiece(Square from, Square to) { - to.setPiece(from.getPiece()); - to.setPieceColor(from.getPieceColor()); - from.setPiece(null); - from.setPieceColor(null); - } - - private Position position(char c, int i) { - Position p = new Position(); - p.setX(c); - p.setY(i); - return p; - } - - private static void setPlayer(Board b, Color c) { - b.setWhitePlayer("x"); - b.setBlackPlayer("y"); - if (c == Color.B) { - b.setPlayer("y"); - } else { - b.setPlayer("x"); - } - } -} diff -r 49011b4a2689 -r 3f1866fdb2a1 chess/src/test/java/org/apidesign/html/demo/chess/BoardModelTest.java --- a/chess/src/test/java/org/apidesign/html/demo/chess/BoardModelTest.java Tue Sep 24 22:28:31 2013 +0200 +++ b/chess/src/test/java/org/apidesign/html/demo/chess/BoardModelTest.java Tue Sep 24 22:37:17 2013 +0200 @@ -23,8 +23,13 @@ */ package org.apidesign.html.demo.chess; -import org.apidesign.html.demo.chess.BoardModel.ColorType; +import org.apidesign.html.demo.chess.Color; +import org.apidesign.html.demo.chess.Rules; +import org.apidesign.html.demo.chess.BoardModel; import org.apidesign.html.demo.chess.BoardModel.PieceType; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; import static org.testng.Assert.*; import org.testng.annotations.Test; @@ -36,11 +41,21 @@ public BoardModelTest() { } - + + @Test public void whiteRockLeftBottom() { + Board b = Rules.createBoard(); + Row bottom = b.getRows().get(7); + Square left = bottom.getColumns().get(0); + assertEquals(left.getPiece(), PieceType.ROCK, "Rock is on bottom left square"); + assertEquals(left.getPieceColor(), Color.W, "It is white"); + assertEquals(left.getColor(), Color.B, "Square is black"); + } @Test public void e2e4e7e6() { - Board b = BoardModel.createBoard(); - assertEquals(b.getTurn(), ColorType.WHITE); + Board b = Rules.createBoard(); + b.setGameId("myId"); + setPlayer(b, Color.W); + assertEquals(b.getTurn(), Color.W); Square e2 = BoardModel.findSquare(b, 'E', 2); assertNotNull(e2); @@ -52,26 +67,113 @@ BoardModel.selected(b, e4); + assertTrue(e4.isPending(), "e4 marked as pending move"); + assertNull(e2.getPiece(), "No pawn at e2"); + + // ignore all other figures than the two pawns + BoardModel.moveResponse(b, null, Collections.singletonList("Pe4"), + Collections.singletonList("Pe7"), Color.B, null + ); + + assertFalse(e4.isPending(), "e4 now the move is real"); assertNull(e2.getPiece(), "No pawn at e2"); assertEquals(e4.getPiece(), BoardModel.PieceType.PAWN, "Pawn moved successfully"); assertNull(BoardModel.findSelectedSquare(b), "No square selected"); BoardModel.selected(b, e4); assertNull(BoardModel.findSelectedSquare(b), "No square selected, it is blacks turn"); + + List mvs1 = b.getMoves(); + assertEquals(1, mvs1.size(), "First move was made: " + mvs1); + Move firstMove = mvs1.get(0); + assertEquals(firstMove.getPiece(), PieceType.PAWN, "Moving with pawn"); + assertEquals(firstMove.getRound(), 1, "First round of moves"); + assertEquals(firstMove.getTurn(), Color.W, "Moved by white"); + assertTrue(firstMove.isWhiteMove(), "Really moved by white"); assertTrue(b.isBlackTurn(), "black's turn"); Square e7 = BoardModel.findSquare(b, 'E', 7); BoardModel.selected(b, e7); - assertEquals(BoardModel.findSelectedSquare(b), e7); + assertNull(BoardModel.findSelectedSquare(b), "Can't select anything when I am white player"); - BoardModel.selected(b, BoardModel.findSquare(b, 'E', 6)); + Move blackMv = BoardModel.MoveImpl.valueOf("E7E6"); + BoardModel.moveUpdate(b, blackMv, + Collections.singletonList("PE4"), + Collections.singletonList("PE6"), + Color.W, null + ); + assertNull(e7.getPiece(), "Piece has been moved"); + + List mvs = b.getMoves(); + assertEquals(2, mvs.size(), "There are two moves now: " + mvs); + Move secondMove = mvs.get(1); + assertEquals(secondMove.getPiece(), PieceType.PAWN, "Moving with pawn"); + assertEquals(secondMove.getRound(), 1, "Still 1st round of moves"); + assertEquals(secondMove.getTurn(), Color.B, "Moved by black"); + assertFalse(secondMove.isWhiteMove(), "Really moved by black"); + } + + @Test public void unselect() { + Board b = Rules.createBoard(); + b.setGameId("myId"); + setPlayer(b, Color.W); + assertEquals(b.getTurn(), Color.W); + + Square e2 = BoardModel.findSquare(b, 'E', 2); + assertNotNull(e2); + BoardModel.selected(b, e2); + assertEquals(BoardModel.findSelectedSquare(b), e2, "E2 is selected"); + + BoardModel.selected(b, e2); + assertNull(BoardModel.findSelectedSquare(b), "E2 is unselected"); + + for (Row row : b.getRows()) { + for (Square s : row.getColumns()) { + assertFalse(s.isAccessible(), "No square is accessible anymore: " + s); + } + } + } + + @Test public void discaredMove() { + Board b = Rules.createBoard(); + b.setGameId("myId"); + setPlayer(b, Color.W); + assertEquals(b.getTurn(), Color.W); + + Square e2 = BoardModel.findSquare(b, 'E', 2); + assertNotNull(e2); + BoardModel.selected(b, e2); + assertEquals(BoardModel.findSelectedSquare(b), e2, "E2 is selected"); + + Square e4 = BoardModel.findSquare(b, 'E', 4); + assertNotNull(e4); + + BoardModel.selected(b, e4); + + assertTrue(e4.isPending(), "e4 marked as pending move"); + assertNull(e2.getPiece(), "No pawn at e2"); + + assertEquals(b.getMoves().size(), 1, "One move recorded"); + assertEquals(b.getMoves().get(0), b.getPendingMove(), "Pending move is waiting"); + + // ignore all other figures than the two pawns + BoardModel.moveResponse(b, "No way, can't play like this", Collections.singletonList("PE2"), + Collections.singletonList("Pe7"), Color.W, null + ); + + assertEquals(b.getAlertMessage(), "No way, can't play like this"); + assertNull(e4.getPiece(), "No piece on e4"); + assertEquals(e2.getPiece(), PieceType.PAWN, "Pawn is back"); + + assertEquals(b.getMoves().size(), 0, "Move was discarded"); + assertNull(b.getPendingMove(), "No pending moves"); } @Test public void cantSelectEmptySquare() { - Board b = BoardModel.createBoard(); + Board b = Rules.createBoard(); Square e3 = BoardModel.findSquare(b, 'E', 3); assertNotNull(e3); BoardModel.selected(b, e3); @@ -79,7 +181,8 @@ } @Test public void cantTakeOwnPiece() { - Board b = BoardModel.createBoard(); + Board b = Rules.createBoard(); + setPlayer(b, Color.W); Square e1 = BoardModel.findSquare(b, 'E', 1); assertNotNull(e1); BoardModel.selected(b, e1); @@ -97,7 +200,8 @@ } @Test public void knightMustMoveToF3() { - Board b = BoardModel.createBoard(); + Board b = Rules.createBoard(); + setPlayer(b, Color.W); Square g1 = BoardModel.findSquare(b, 'G', 1); BoardModel.selected(b, g1); @@ -122,7 +226,7 @@ } @Test public void pawnCanTakeToSide() { - Board b = BoardModel.createBoard(); + Board b = Rules.createBoard(); Square e2 = BoardModel.findSquare(b, 'E', 2); Square e4 = BoardModel.findSquare(b, 'E', 4); Square d7 = BoardModel.findSquare(b, 'D', 7); @@ -130,13 +234,307 @@ BoardModel.selected(b, e2); BoardModel.selected(b, e4); + + // ignore all other figures than the two pawns + BoardModel.moveResponse(b, null, Collections.singletonList("PE4"), + Collections.singletonList("PD7"), Color.B, null + ); + + setPlayer(b, Color.B); + BoardModel.selected(b, d7); BoardModel.selected(b, d5); + // ignore all other figures than the two pawns + BoardModel.moveResponse(b, null, Collections.singletonList("PE4"), + Collections.singletonList("PD5"), Color.W, null + ); + + setPlayer(b, Color.W); + BoardModel.selected(b, e4); assertTrue(d5.isAccessible(), "Can take on d5"); BoardModel.selected(b, d5); assertNull(e4.getPiece(), "No pawn on e4"); - assertEquals(d5.getPieceColor(), ColorType.WHITE, "White Pawn on d5"); + assertEquals(d5.getPieceColor(), Color.W, "White Pawn on d5"); + } + + @Test public void pawnCanJumpOnAnother() { + Board b = Rules.createBoard(); + Square e2 = BoardModel.findSquare(b, 'E', 2); + Square e4 = BoardModel.findSquare(b, 'E', 4); + Square d7 = BoardModel.findSquare(b, 'D', 7); + Square d5 = BoardModel.findSquare(b, 'D', 5); + Square e7 = BoardModel.findSquare(b, 'E', 7); + Square e6 = BoardModel.findSquare(b, 'E', 6); + Square e5 = BoardModel.findSquare(b, 'E', 5); + + BoardModel.selected(b, e2); + BoardModel.selected(b, e4); + + // ignore all other figures than the three pawns + BoardModel.moveResponse(b, null, Collections.singletonList("PE4"), + Arrays.asList("PD7", "PE7"), Color.B, null + ); + + setPlayer(b, Color.B); + + BoardModel.selected(b, d7); + BoardModel.selected(b, d5); + + // ignore all other figures than the three pawns + BoardModel.moveResponse(b, null, Collections.singletonList("PE4"), + Arrays.asList("PD5", "PE7"), Color.W, null + ); + + setPlayer(b, Color.W); + + BoardModel.selected(b, e4); + BoardModel.selected(b, e5); + + // ignore all other figures than the three pawns + BoardModel.moveResponse(b, null, Collections.singletonList("PE5"), + Arrays.asList("PD5", "PE7"), Color.B, null + ); + + setPlayer(b, Color.B); + + BoardModel.selected(b, e7); + assertTrue(e6.isAccessible(), "Can move by one piece"); + assertFalse(e5.isAccessible(), "Can't move where other pawn is"); + } + + @Test public void showFirstMove() { + Board b = Rules.createBoard(); + setPlayer(b, Color.W); + Square e2 = BoardModel.findSquare(b, 'E', 2); + Square e4 = BoardModel.findSquare(b, 'E', 4); + Square d7 = BoardModel.findSquare(b, 'D', 7); + Square d5 = BoardModel.findSquare(b, 'D', 5); + + BoardModel.selected(b, e2); + BoardModel.selected(b, e4); + + // ignore all other figures than the two pawns + BoardModel.moveResponse(b, null, Collections.singletonList("Pe4"), + Collections.singletonList("PD7"), Color.B, null + ); + + Move blackMv = BoardModel.MoveImpl.valueOf("D7D5"); + BoardModel.moveUpdate(b, blackMv, + Collections.singletonList("PE4"), + Collections.singletonList("PD5"), + Color.W, null + ); + + + assertEquals(b.getMoves().size(), 2, "Two moves"); + BoardModel.showPosition(b, b.getMoves().get(0)); + + assertEquals(b.getRows().size(), 8, "Still eight rows"); + assertEquals(b.getRows().get(0).getColumns().size(), 8, "Eight columns"); + + assertNull(e2.getPiece(), "e2 is empty"); + assertEquals(e4.getPiece(), PieceType.PAWN, "e4 has pawn"); + assertEquals(d7.getPiece(), PieceType.PAWN, "d7 has pawn"); + assertNull(d5.getPiece(), "Second move is not made"); + assertNull(b.getTurn(), "Nobody to make a move"); + } + + @Test public void initialPositionReadFromAServer() { + List blacks = Arrays.asList( + "Pa7", + "Pb7", + "Pc7", + "Pd7", + "Pe7", + "Pf7", + "Pg7", + "Ph7", + "Ra8", + "Nb8", + "Bc8", + "Qd8", + "Ke8", + "Bf8", + "Ng8", + "Rh8" + ); + List whites = Arrays.asList( + "Ra1", + "Nb1", + "Bc1", + "Qd1", + "Ke1", + "Bf1", + "Ng1", + "Rh1", + "Pa2", + "Pb2", + "Pc2", + "Pd2", + "Pe2", + "Pf2", + "Pg2", + "Ph2" + ); + + Board b = Rules.createBoard(); + Rules.initBoard(b, whites, blacks, Color.W); + + Board b2 = Rules.createBoard(); + Rules.initBoard(b2); + + for (int i = 0; i < 7; i++) { + for (int j = 0; j < 7; j++) { + Square s = b.getRows().get(i).getColumns().get(j); + Square s2 = b2.getRows().get(i).getColumns().get(j); + + assertEquals(s.getPiece(), s2.getPiece(), "Same piece at " + i + "/" + j); + assertEquals(s.getPieceColor(), s2.getPieceColor(), "Same piece color at " + i + "/" + j); + } + } + } + + @Test public void allowSmallRochade() { + Board b = Rules.createBoard(); + setPlayer(b, Color.W); + Square f1 = BoardModel.findSquare(b, 'F', 1); + f1.setPiece(null); + f1.setPieceColor(null); + Square g1 = BoardModel.findSquare(b, 'G', 1); + g1.setPiece(null); + g1.setPieceColor(null); + Square e1 = BoardModel.findSquare(b, 'E', 1); + + BoardModel.selected(b, e1); + assertTrue(g1.isAccessible(), "Can do 0-0"); + } + + @Test public void allowSmallRochadeForBlack() { + Board b = Rules.createBoard(); + setPlayer(b, Color.B); + b.setTurn(Color.B); + Square f8 = BoardModel.findSquare(b, 'F', 8); + f8.setPiece(null); + f8.setPieceColor(null); + Square g8 = BoardModel.findSquare(b, 'G', 8); + g8.setPiece(null); + g8.setPieceColor(null); + Square e8 = BoardModel.findSquare(b, 'E', 8); + + BoardModel.selected(b, e8); + assertTrue(g8.isAccessible(), "Can do 0-0"); + } + + @Test public void disallowSmallRochadeWhenNoRock() { + Board b = Rules.createBoard(); + setPlayer(b, Color.W); + Square f1 = BoardModel.findSquare(b, 'F', 1); + f1.setPiece(null); + f1.setPieceColor(null); + Square g1 = BoardModel.findSquare(b, 'G', 1); + g1.setPiece(null); + g1.setPieceColor(null); + Square h1 = BoardModel.findSquare(b, 'H', 1); + h1.setPiece(null); + h1.setPieceColor(null); + Square e1 = BoardModel.findSquare(b, 'E', 1); + + BoardModel.selected(b, e1); + assertFalse(g1.isAccessible(), "Cannot do 0-0 when there is no rock"); + } + + @Test public void disallowSmallRochadeWhenBishop() { + Board b = Rules.createBoard(); + setPlayer(b, Color.W); + Square g1 = BoardModel.findSquare(b, 'G', 1); + g1.setPiece(null); + Square e1 = BoardModel.findSquare(b, 'E', 1); + + BoardModel.selected(b, e1); + assertFalse(g1.isAccessible(), "Cannot do 0-0 when there is bishop"); + } + + @Test public void disallowSmallRochadeWhenKnight() { + Board b = Rules.createBoard(); + setPlayer(b, Color.W); + Square f1 = BoardModel.findSquare(b, 'F', 1); + f1.setPiece(null); + Square e1 = BoardModel.findSquare(b, 'E', 1); + + BoardModel.selected(b, e1); + assertFalse(f1.isAccessible(), "Cannot do 0-0 when there is knight"); + } + + @Test public void allowBigRochadeForBlack() { + Board b = Rules.createBoard(); + setPlayer(b, Color.B); + b.setTurn(Color.B); + Square b8 = BoardModel.findSquare(b, 'B', 8); + b8.setPiece(null); + b8.setPieceColor(null); + Square c8 = BoardModel.findSquare(b, 'C', 8); + c8.setPiece(null); + c8.setPieceColor(null); + Square d8 = BoardModel.findSquare(b, 'D', 8); + d8.setPiece(null); + d8.setPieceColor(null); + Square e8 = BoardModel.findSquare(b, 'E', 8); + + BoardModel.selected(b, e8); + assertTrue(c8.isAccessible(), "Can do 0-0-0"); + } + + @Test public void enPassant() { + Board b = Rules.createBoard(); + setPlayer(b, Color.B); + b.setTurn(Color.B); + + Square e7 = BoardModel.findSquare(b, 'E', 7); + Square e4 = BoardModel.findSquare(b, 'E', 4); + movePiece(e7, e4); + + Move move = new Move(); + move.setFrom(position('D', 2)); + move.setTo(position('D', 4)); + move.setPiece(PieceType.PAWN); + b.getMoves().add(move); + + Square d2 = BoardModel.findSquare(b, 'D', 2); + Square d4 = BoardModel.findSquare(b, 'D', 4); + movePiece(d2, d4); + + BoardModel.selected(b, e4); + + Square e3 = BoardModel.findSquare(b, 'E', 3); + assertTrue(e3.isAccessible(), "Obviously can move to e3"); + + Square d3 = BoardModel.findSquare(b, 'D', 3); + assertTrue(d3.isAccessible(), "Can also take on d3"); + } + + private void movePiece(Square from, Square to) { + to.setPiece(from.getPiece()); + to.setPieceColor(from.getPieceColor()); + from.setPiece(null); + from.setPieceColor(null); + } + + private Position position(char c, int i) { + Position p = new Position(); + p.setX(c); + p.setY(i); + return p; + } + + private static void setPlayer(Board b, Color c) { + b.setWhitePlayer("x"); + b.setBlackPlayer("y"); + if (c == Color.B) { + b.setPlayer("y"); + } else { + b.setPlayer("x"); + } } }