# HG changeset patch # User Jaroslav Tulach # Date 1380054024 -7200 # Node ID 945fbfff28f3dc4c7f947f7149c3f6cfff8a4e61 # Parent 4dbbfc8310b09d54f59fc937b9f753c6396e7a56 Advanced version of the chess game diff -r 4dbbfc8310b0 -r 945fbfff28f3 chess/nbactions-bck2brwsr.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/chess/nbactions-bck2brwsr.xml Tue Sep 24 22:20:24 2013 +0200 @@ -0,0 +1,39 @@ + + + + + run + + package + bck2brwsr:brwsr + + + true + NONE + + + diff -r 4dbbfc8310b0 -r 945fbfff28f3 chess/nbactions-iOSDeploy.xml --- a/chess/nbactions-iOSDeploy.xml Tue Sep 24 21:08:15 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,51 +0,0 @@ - - - - - run - - package - ios-maven-plugin:0.2:deploy - - - true - - - - debug - - package - org.netbeans.ios:ios-maven-plugin:0.2:deploy - - - true - true - ${jpda.address} - Debug - - - \ No newline at end of file diff -r 4dbbfc8310b0 -r 945fbfff28f3 chess/nbactions.xml --- a/chess/nbactions.xml Tue Sep 24 21:08:15 2013 +0200 +++ b/chess/nbactions.xml Tue Sep 24 22:20:24 2013 +0200 @@ -47,7 +47,7 @@ Build IPA package - org.netbeans.ios:ios-maven-plugin:0.2-SNAPSHOT:build-IPA + ios:build-IPA true @@ -60,8 +60,8 @@ Debug IPA package - org.netbeans.ios:ios-maven-plugin:0.2-SNAPSHOT:ipaddress - org.netbeans.ios:ios-maven-plugin:0.2-SNAPSHOT:build-IPA + ios:ipaddress + ios:build-IPA true diff -r 4dbbfc8310b0 -r 945fbfff28f3 chess/pom.xml --- a/chess/pom.xml Tue Sep 24 21:08:15 2013 +0200 +++ b/chess/pom.xml Tue Sep 24 22:20:24 2013 +0200 @@ -1,28 +1,21 @@ 4.0.0 - + demo org.apidesign.html - 1.0-SNAPSHOT - + 1.0-SNAPSHOT + - org.apidesign.html.demo - chess + com.oracle.chess + ChessHTMLJavaClient 1.0-SNAPSHOT jar - ChessGame + Chess_HTML_Java_Client - java.net - Java.net - https://maven.java.net/content/repositories/releases/ - - - - netbeans NetBeans http://bits.netbeans.org/maven2/ @@ -30,13 +23,6 @@ - java.net - Java.net - https://maven.java.net/content/repositories/releases/ - - - - ios NetBeans iOS Maven Plugin http://beetle.cz.oracle.com/~jtulach/maven/ @@ -47,8 +33,12 @@ UTF-8 - 0.4 none + 0.6 + 0.8 + 0.8 + MINIMAL + pages/index.html @@ -68,7 +58,7 @@ - org.apidesign.html.demo.chess.Main + com.oracle.chess.client.htmljava.Main true lib/ @@ -86,7 +76,7 @@ ${basedir}/src/main/webapp/ - org.apidesign.html.demo.chess.Main + com.oracle.chess.client.htmljava.Main @@ -106,7 +96,21 @@ - + + + ios-maven-plugin + org.netbeans.ios + 0.4 + + 1 + 6.1 + JavaOneChess + + UIInterfaceOrientationLandscapeLeft,UIInterfaceOrientationPortrait,UIInterfaceOrientationLandscapeRight,UIInterfaceOrientationPortraitUpsideDown + false + UIInterfaceOrientationLandscapeLeft,UIInterfaceOrientationPortrait,UIInterfaceOrientationLandscapeRight,UIInterfaceOrientationPortraitUpsideDown + + @@ -121,66 +125,132 @@ ${net.java.html.version} - org.apidesign.html - ko-fx - ${net.java.html.version} - runtime - - - org.apidesign.html - boot-fx - ${net.java.html.version} - runtime - - org.testng testng 6.7 test + + org.apidesign.html + net.java.html.sound + ${net.java.html.version} + jar + + + org.apidesign.bck2brwsr + core + ${bck2brwsr.version} + provided + jar + - iOSDeploy + fxbrwsr - - Mac OS X - + true - - - - org.netbeans.ios - ios-maven-plugin - 0.2 - - ${project.build.directory}/${project.build.finalName}.jar - ${project.name} - org.apidesign.html.demo.chess.Main - src/main/webapp/ - - - - org.apache.maven.plugins - maven-jar-plugin - 2.4 - - - - org.apidesign.html.demo.chess.Main - true - lib/ - - - - - - org.apidesign.html ko-fx ${net.java.html.version} + runtime + + + org.apidesign.html + boot-fx + ${net.java.html.version} + runtime + + + org.apidesign.html + ko-ws-tyrus + runtime + ${net.java.html.version} + + + + + bck2brwsr + + + brwsr + bck2brwsr + + + + + + org.apidesign.bck2brwsr + bck2brwsr-maven-plugin + ${bck2brwsr.launcher.version} + + + + brwsr + + + + + ${basedir}/src/main/webapp/ + ${brwsr.startpage} + ${brwsr} + ${project.build.directory}/bck2brwsr.js + ${bck2brwsr.obfuscationlevel} + + + + org.apache.maven.plugins + maven-compiler-plugin + + + + + + + + maven-assembly-plugin + 2.4 + + + distro-assembly + package + + single + + + + src/main/assembly/bck2brwsr.xml + + + + + + + + + + org.apidesign.bck2brwsr + emul + ${bck2brwsr.version} + rt + + + org.apidesign.bck2brwsr + ko-bck2brwsr + ${bck2brwsr.version} + runtime + + + org.apidesign.bck2brwsr + vm4brwsr + js + zip + ${bck2brwsr.version} + provided diff -r 4dbbfc8310b0 -r 945fbfff28f3 chess/src/main/assembly/bck2brwsr.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/chess/src/main/assembly/bck2brwsr.xml Tue Sep 24 22:20:24 2013 +0200 @@ -0,0 +1,79 @@ + + + + + bck2brwsr + + zip + + public_html + + + false + runtime + lib + + *:jar + *:rt + + + + false + provided + + *:js + + true + / + + + + + src/main/webapp/pages + / + + *.png + + true + + + src/main/webapp/pages + / + + *.png + + false + + + + + ${project.build.directory}/${project.build.finalName}.jar + / + + + \ No newline at end of file diff -r 4dbbfc8310b0 -r 945fbfff28f3 chess/src/main/java/com/oracle/chess/client/htmljava/BoardModel.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/chess/src/main/java/com/oracle/chess/client/htmljava/BoardModel.java Tue Sep 24 22:20:24 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 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 4dbbfc8310b0 -r 945fbfff28f3 chess/src/main/java/com/oracle/chess/client/htmljava/Color.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/chess/src/main/java/com/oracle/chess/client/htmljava/Color.java Tue Sep 24 22:20:24 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 com.oracle.chess.client.htmljava; + +/** + * + * @author Jaroslav Tulach + */ +public enum Color { + W, B; +} diff -r 4dbbfc8310b0 -r 945fbfff28f3 chess/src/main/java/com/oracle/chess/client/htmljava/Communication.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/chess/src/main/java/com/oracle/chess/client/htmljava/Communication.java Tue Sep 24 22:20:24 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 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 4dbbfc8310b0 -r 945fbfff28f3 chess/src/main/java/com/oracle/chess/client/htmljava/GamesModel.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/chess/src/main/java/com/oracle/chess/client/htmljava/GamesModel.java Tue Sep 24 22:20:24 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 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 4dbbfc8310b0 -r 945fbfff28f3 chess/src/main/java/com/oracle/chess/client/htmljava/LoadMain.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/chess/src/main/java/com/oracle/chess/client/htmljava/LoadMain.java Tue Sep 24 22:20:24 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 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 4dbbfc8310b0 -r 945fbfff28f3 chess/src/main/java/com/oracle/chess/client/htmljava/Main.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/chess/src/main/java/com/oracle/chess/client/htmljava/Main.java Tue Sep 24 22:20:24 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 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 4dbbfc8310b0 -r 945fbfff28f3 chess/src/main/java/com/oracle/chess/client/htmljava/MsgType.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/chess/src/main/java/com/oracle/chess/client/htmljava/MsgType.java Tue Sep 24 22:20:24 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 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 4dbbfc8310b0 -r 945fbfff28f3 chess/src/main/java/com/oracle/chess/client/htmljava/Rules.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/chess/src/main/java/com/oracle/chess/client/htmljava/Rules.java Tue Sep 24 22:20:24 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 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 4dbbfc8310b0 -r 945fbfff28f3 chess/src/main/java/com/oracle/chess/client/htmljava/SettingsModel.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/chess/src/main/java/com/oracle/chess/client/htmljava/SettingsModel.java Tue Sep 24 22:20:24 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 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 4dbbfc8310b0 -r 945fbfff28f3 chess/src/main/java/com/oracle/chess/client/htmljava/UIModel.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/chess/src/main/java/com/oracle/chess/client/htmljava/UIModel.java Tue Sep 24 22:20:24 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 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 4dbbfc8310b0 -r 945fbfff28f3 chess/src/main/webapp/pages/DukeHTML.png Binary file chess/src/main/webapp/pages/DukeHTML.png has changed diff -r 4dbbfc8310b0 -r 945fbfff28f3 chess/src/main/webapp/pages/css/bootstrap.css --- a/chess/src/main/webapp/pages/css/bootstrap.css Tue Sep 24 21:08:15 2013 +0200 +++ b/chess/src/main/webapp/pages/css/bootstrap.css Tue Sep 24 22:20:24 2013 +0200 @@ -1119,7 +1119,7 @@ input, textarea, .uneditable-input { - width: 206px; + width: 300px; } textarea { @@ -1205,7 +1205,7 @@ *margin-top: 4px; /* For IE7, add top margin to align select with labels */ - line-height: 30px; + vertical-align: middle; } select { @@ -1775,6 +1775,7 @@ -webkit-border-radius: 4px 0 0 4px; -moz-border-radius: 4px 0 0 4px; border-radius: 4px 0 0 4px; + color: black; } .input-append input + .btn-group .btn:last-child, @@ -3218,7 +3219,7 @@ line-height: 20px; color: #333333; text-align: center; - text-shadow: 0 1px 1px rgba(255, 255, 255, 0.75); + text-shadow: none; vertical-align: middle; cursor: pointer; background-color: #f5f5f5; @@ -3966,7 +3967,7 @@ } .nav > li > a { - display: block; + display: inline-block; } .nav > li > a:hover, @@ -4625,7 +4626,7 @@ .navbar .nav > li > a { float: none; - padding: 10px 15px 10px; + padding: 10px 23px 10px; color: #777777; text-decoration: none; text-shadow: 0 1px 0 #ffffff; @@ -5646,7 +5647,7 @@ display: inline-block; padding: 2px 4px; font-size: 11.844px; - font-weight: bold; + font-weight: normal; line-height: 14px; color: #ffffff; text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); diff -r 4dbbfc8310b0 -r 945fbfff28f3 chess/src/main/webapp/pages/css/chess-phone-large.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/chess/src/main/webapp/pages/css/chess-phone-large.css Tue Sep 24 22:20:24 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. + */ +table.board td { + padding: 4px; + width: 28px; + height: 28px; + font-size: 2em; +} +h1 { + font-size: 15px; +} \ No newline at end of file diff -r 4dbbfc8310b0 -r 945fbfff28f3 chess/src/main/webapp/pages/css/chess-phone-small.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/chess/src/main/webapp/pages/css/chess-phone-small.css Tue Sep 24 22:20:24 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. + */ +table.board td { + padding: 4px; + width: 24px; + height: 24px; + font-size: 1.6em; +} +h1 { + font-size: 12px; +} \ No newline at end of file diff -r 4dbbfc8310b0 -r 945fbfff28f3 chess/src/main/webapp/pages/css/chess-tablet-large.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/chess/src/main/webapp/pages/css/chess-tablet-large.css Tue Sep 24 22:20:24 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. + */ +table.board td { + padding: 8px; + width: 42px; + height: 42px; + font-size: 3em; +} +h1 { + font-size: 22px; +} \ No newline at end of file diff -r 4dbbfc8310b0 -r 945fbfff28f3 chess/src/main/webapp/pages/css/chess-tablet-small.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/chess/src/main/webapp/pages/css/chess-tablet-small.css Tue Sep 24 22:20:24 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. + */ +table.board td { + padding: 6px; + width: 34px; + height: 34px; + font-size: 2.4em; +} +h1 { + font-size: 18px; +} \ No newline at end of file diff -r 4dbbfc8310b0 -r 945fbfff28f3 chess/src/main/webapp/pages/css/chess.css --- a/chess/src/main/webapp/pages/css/chess.css Tue Sep 24 21:08:15 2013 +0200 +++ b/chess/src/main/webapp/pages/css/chess.css Tue Sep 24 22:20:24 2013 +0200 @@ -22,42 +22,103 @@ * THE SOFTWARE. */ /** main chess style **/ +#apptitle { + font-size: 20px; +} .log { - overflow-x: hidden; - overflow-y: scroll; - max-height: 450px; - border:1px solid #eeeeee; - background-color: white; + margin-bottom: 10px; +} +table.board td { + border: 1px solid black; + padding:10px; + width: 56px; + height: 56px; + vertical-align: middle; + text-align: center; + font-size:4em; +} +table.board td.row-label, table.board td.col-label { + font-size: 2em; + border:none; + width:20px; +} +table.board td.col-label { + height:20px; } .container { - margin:10px; + margin:10px; } -stable.board { - width: 100%; -} -table.board { - font-size:4em; -} -table.board td { - border: 1px solid black; - padding:10px; - width:55px; - height:55px; - vertical-align: middle; - text-align: center; -} + table.board td.white { - background-color: white; + background-color: #D0D0D0; } table.board td.black { - background-color: #B0B0B0; + background-color: #A0A0A0; } table.board td.selected { - background-color: #B000B0; + background-color: #bce8f1; } table.board td.accessible { - background-color: #00B0B0; + background-color: #a9dba9; +} +table.board td.pending { + background-color: #dbc59e; } .figure { - cursor: pointer; -} \ No newline at end of file + cursor: pointer; +} +.but-orientation { + margin-top:20px; +} +h3.log-h { + border-top:1px solid silver; +} +.badge-status { + display:inline-block; + margin-left:20px; + padding:5px; + font-size:1em; + text-shadow: none; +} +.badge-white-turn { + font-size: 1em; + padding:4px; + background-color: white; + color:black; + text-shadow: none; + border:1px solid black; + font-weight: normal; + display: inline-block; + margin-left: 38px; +} +.badge-black-turn { + font-size: 1em; + padding:4px; + background-color: black; + color:white; + text-shadow: none; + border:1px solid black; + font-weight: normal; + display: inline-block; + margin-left: 38px; +} +.btn {text-shadow: none;} +.select {line-height: inherit;} +.uneditable-input { + margin-left: 0; + color: black; +} +span.myturn { + display: inline-block; + position:relative; + left:23px; + padding:3px; +} +.whitePiece { + text-shadow: -1px -1px 0 #000, 1px -1px 0 #000, -1px 1px 0 #000, 1px 1px 0 #000; + color: white; +} +h1 { + font-size: 29px; + line-height: initial; +} diff -r 4dbbfc8310b0 -r 945fbfff28f3 chess/src/main/webapp/pages/css/spinner.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/chess/src/main/webapp/pages/css/spinner.css Tue Sep 24 22:20:24 2013 +0200 @@ -0,0 +1,114 @@ +/** + * 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. + */ +/** chess cube style **/ + +#chesscube { + margin: 1em auto; + -webkit-perspective: 1200px; + perspective: 1200px; + position: fixed; + z-index: -100; + bottom:100px; + right: 100px; + width: 160px; + height: 160px; + transition: opacity 1s; +} + +#spinner div { + position: fixed; + width: 160px; + height: 160px; + border: 1px solid #ccc; + background: rgba(255,255,155,0.8); + text-align: center; + line-height: 160px; + font-size: 120px; + -webkit-box-shadow: inset 0 0 40px rgba(0,0,0,0.4); + box-shadow: inset 0 0 40px rgba(0,0,0,0.4); +} + +#spinner div:nth-child(1) { + -webkit-transform: translateZ(80px); + transform: translateZ(80px); +} +#spinner div:nth-child(2) { + -webkit-transform: rotateY(90deg) translateZ(80px); + transform: rotateY(90deg) translateZ(80px); +} +#spinner div:nth-child(3) { + -webkit-transform: rotateY(90deg) rotateX(90deg) translateZ(80px); + transform: rotateY(90deg) rotateX(90deg) translateZ(80px); +} +#spinner div:nth-child(4) { + -webkit-transform: rotateY(180deg) rotateZ(90deg) translateZ(80px); + transform: rotateY(180deg) rotateZ(90deg) translateZ(80px); +} +#spinner div:nth-child(5) { + -webkit-transform: rotateY(-90deg) rotateZ(90deg) translateZ(80px); + transform: rotateY(-90deg) rotateZ(90deg) translateZ(80px); +} +#spinner div:nth-child(6) { + -webkit-transform: rotateX(-90deg) translateZ(80px); + transform: rotateX(-90deg) translateZ(80px); +} + +@-webkit-keyframes spincube { + from,to { } + 16% { -webkit-transform: rotateY(-90deg); } + 33% { -webkit-transform: rotateY(-90deg) rotateZ(90deg); } + 50% { -webkit-transform: rotateY(180deg) rotateZ(90deg); } + 66% { -webkit-transform: rotateY(90deg) rotateX(90deg); } + 83% { -webkit-transform: rotateX(90deg); } +} + +@keyframes spincube { + from,to { } + 16% { transform: rotateY(-90deg); } + 33% { transform: rotateY(-90deg) rotateZ(90deg); } + 50% { transform: rotateY(180deg) rotateZ(90deg); } + 66% { transform: rotateY(90deg) rotateX(90deg); } + 83% { transform: rotateX(90deg); } +} + +#spinner { + -webkit-animation-name: spincube; + -webkit-animation-timing-function: ease-in-out; + -webkit-animation-iteration-count: infinite; + -webkit-animation-duration: 12s; + -webkit-transform-style: preserve-3d; + -webkit-transform-origin: 80px 80px 0; + animation-name: spincube; + animation-timing-function: ease-in-out; + animation-iteration-count: infinite; + animation-duration: 12s; + transform-style: preserve-3d; + transform-origin: 80px 80px 0; +} + +@media (max-width: 767px) { + #chesscube { + opacity: 0; + } +} diff -r 4dbbfc8310b0 -r 945fbfff28f3 chess/src/main/webapp/pages/index.html --- a/chess/src/main/webapp/pages/index.html Tue Sep 24 21:08:15 2013 +0200 +++ b/chess/src/main/webapp/pages/index.html Tue Sep 24 22:20:24 2013 +0200 @@ -25,107 +25,186 @@ --> - - - Chess Client - - - + + + Chess Client + + + - - - - - - - -
-
-
-

My game

-
Black's turn
- - - - - - - - -
- -
-
White's turn
-
-
-

Game Log

-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
#Move
10Move
9Move
8Move
7Move
6Move
5Move
4Move
3Move
2Move
1Move
-
-
-
-
- - \ No newline at end of file + + + + + + + + + + + + + + +
+
+
+ + + + + + +
+ Loading... +

Initializing the virtual machine...

+
+ + + + diff -r 4dbbfc8310b0 -r 945fbfff28f3 chess/src/main/webapp/pages/sounds/check.mp3 Binary file chess/src/main/webapp/pages/sounds/check.mp3 has changed diff -r 4dbbfc8310b0 -r 945fbfff28f3 chess/src/main/webapp/pages/sounds/checkmate.mp3 Binary file chess/src/main/webapp/pages/sounds/checkmate.mp3 has changed diff -r 4dbbfc8310b0 -r 945fbfff28f3 chess/src/main/webapp/pages/sounds/move.mp3 Binary file chess/src/main/webapp/pages/sounds/move.mp3 has changed diff -r 4dbbfc8310b0 -r 945fbfff28f3 chess/src/test/java/com/oracle/chess/client/htmljava/BoardModelTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/chess/src/test/java/com/oracle/chess/client/htmljava/BoardModelTest.java Tue Sep 24 22:20:24 2013 +0200 @@ -0,0 +1,537 @@ +/** + * 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"); + } + } +}