Merging statistics-and-elo
authorJaroslav Tulach <jaroslav.tulach@apidesign.org>
Thu, 07 Jan 2010 23:20:33 +0100
changeset 1828004f8165677
parent 177 2f8672ac9f1a
parent 181 e3fb438103e0
child 183 c5b02e84ea84
Merging statistics-and-elo
     1.1 --- a/freemarkerdor/pom.xml	Fri Jan 01 20:53:17 2010 +0100
     1.2 +++ b/freemarkerdor/pom.xml	Thu Jan 07 23:20:33 2010 +0100
     1.3 @@ -71,6 +71,12 @@
     1.4        <artifactId>freemarker</artifactId>
     1.5        <version>2.3.8</version>
     1.6      </dependency>
     1.7 +    <dependency>
     1.8 +      <groupId>${project.groupId}</groupId>
     1.9 +      <artifactId>statistics</artifactId>
    1.10 +      <version>1.0</version>
    1.11 +      <scope>test</scope>
    1.12 +    </dependency>
    1.13    </dependencies>
    1.14    <build>
    1.15      <plugins>
    1.16 @@ -126,3 +132,4 @@
    1.17  
    1.18  
    1.19  
    1.20 +
     2.1 --- a/freemarkerdor/src/main/java/cz/xelfi/quoridor/freemarkerdor/UI.java	Fri Jan 01 20:53:17 2010 +0100
     2.2 +++ b/freemarkerdor/src/main/java/cz/xelfi/quoridor/freemarkerdor/UI.java	Thu Jan 07 23:20:33 2010 +0100
     2.3 @@ -38,6 +38,7 @@
     2.4  import cz.xelfi.quoridor.IllegalPositionException;
     2.5  import java.io.IOException;
     2.6  import java.io.InputStream;
     2.7 +import java.io.StringWriter;
     2.8  import java.net.URI;
     2.9  import java.util.Date;
    2.10  import java.util.HashMap;
    2.11 @@ -87,6 +88,7 @@
    2.12          version = p.getProperty("version", "unknown"); // NOI18N
    2.13      }
    2.14      private static WebResource base;
    2.15 +    private static WebResource stat;
    2.16  
    2.17      @Context
    2.18      private HttpHeaders headers;
    2.19 @@ -163,6 +165,7 @@
    2.20          }
    2.21          String txt = wr.accept(MediaType.TEXT_PLAIN).get(String.class);
    2.22          Board b = Board.valueOf(txt);
    2.23 +//        Board b = new Board(txt);
    2.24          ResponseBuilder resp = Response.ok();
    2.25          CacheControl cc = new CacheControl();
    2.26          cc.setNoCache(true);
    2.27 @@ -220,16 +223,38 @@
    2.28  
    2.29          Document doc = url.accept(MediaType.TEXT_XML).get(Document.class);
    2.30          Board b;
    2.31 +        String t = doc.getElementsByTagName("board").item(0).getTextContent();
    2.32          try {
    2.33              b = Board.valueOf(doc.getElementsByTagName("board").item(0).getTextContent());
    2.34 -        } catch (IllegalPositionException ex) {
    2.35 +        } catch (Exception ex) {
    2.36 +//            b = new Board(t);
    2.37 +//        } catch (IllegalStateException ex) {
    2.38              Exceptions.printStackTrace(ex);
    2.39              b = Board.empty();
    2.40          }
    2.41 -        v = viewable("game.fmt", doc, "message", msg, "format", format, "board", b);
    2.42 +        String bCode = null;
    2.43 +        try{
    2.44 +            bCode = stat.path("openings").path(b.getCode()+".check").queryParam("loginID", user.getId()).accept(MediaType.TEXT_PLAIN).get(String.class);
    2.45 +        }catch(Exception e){
    2.46 +            bCode = null;
    2.47 +        }
    2.48 +        if(bCode == null || "".equals(bCode))
    2.49 +            v = viewable("game.fmt", doc, "message", msg, "format", format, "board", b,"textPicture", boardToPicture(b));
    2.50 +        else
    2.51 +            v = viewable("game.fmt", doc, "message", msg, "format", format, "board", b,"textPicture", boardToPicture(b),"bCode", bCode);
    2.52          return resp.entity(v).build();
    2.53      }
    2.54  
    2.55 +    private static String boardToPicture(Board b) {
    2.56 +        StringWriter w = new StringWriter();
    2.57 +        try {
    2.58 +            b.write(w);
    2.59 +        } catch (IOException ex) {
    2.60 +            return ex.toString();
    2.61 +        }
    2.62 +        return w.toString();
    2.63 +    }
    2.64 +
    2.65      @GET
    2.66      @Path("games/{id}/move")
    2.67      @Produces(MediaType.TEXT_HTML)
    2.68 @@ -346,7 +371,72 @@
    2.69  
    2.70          return welcome(10);
    2.71      }
    2.72 +
    2.73 +    @GET
    2.74 +    @Path("elo")
    2.75 +    @Produces(MediaType.TEXT_HTML)
    2.76 +    public Response getEloList(){
    2.77 +        Viewable v = checkLogin();
    2.78 +        if (v != null) {
    2.79 +            return Response.status(Response.Status.FORBIDDEN).entity(v).build();
    2.80 +        }
    2.81 +        final Document got = stat.path("elo").path("list").accept(MediaType.TEXT_XML).get(Document.class);
    2.82 +        return Response.ok(viewable("elo.fmt", got)).build();
    2.83 +    }
    2.84      
    2.85 +    @GET
    2.86 +    @Path("openings")
    2.87 +    @Produces(MediaType.TEXT_HTML)
    2.88 +    public Response getOpeningRoot(){
    2.89 +        return getOpeningNode("ROOT");
    2.90 +    }
    2.91 +
    2.92 +    @GET
    2.93 +    @Path("openings/{code}")
    2.94 +    @Produces(MediaType.TEXT_HTML)
    2.95 +    public Response getOpeningNode(@PathParam("code") String code){
    2.96 +        Viewable v = checkLogin();
    2.97 +        if (v != null) {
    2.98 +            return Response.status(Response.Status.FORBIDDEN).entity(v).build();
    2.99 +        }
   2.100 +        final Document got = stat.path("openings").path(code).queryParam("loginID", user.getId()).accept(MediaType.TEXT_XML).get(Document.class);
   2.101 +        Board b;
   2.102 +        try {
   2.103 +            b = Board.valueOf(got.getElementsByTagName("nodeCode").item(0).getTextContent());
   2.104 +        } catch (Exception ex) {
   2.105 +            Exceptions.printStackTrace(ex);
   2.106 +            b = Board.empty();
   2.107 +        }
   2.108 +        return Response.ok(viewable("openings.fmt", got, "whitefences",b.getPlayers().get(0).getFences(),"blackfences",b.getPlayers().get(1).getFences())).build();
   2.109 +    }
   2.110 +
   2.111 +    @GET
   2.112 +    @Path("openings/{code}/{status}")
   2.113 +    @Produces(MediaType.TEXT_HTML)
   2.114 +    public Response getOpeningNodeGames(@PathParam("code") String code, @PathParam("status") String status){
   2.115 +        Viewable v = checkLogin();
   2.116 +        if (v != null) {
   2.117 +            return Response.status(Response.Status.FORBIDDEN).entity(v).build();
   2.118 +        }
   2.119 +        final Document got = stat.path("openings").path(code).path(status).queryParam("loginID", user.getId()).accept(MediaType.TEXT_XML).get(Document.class);
   2.120 +        return Response.ok(viewable("opening_games.fmt", got,"code",code,"color",status)).build();
   2.121 +    }
   2.122 +
   2.123 +    @GET
   2.124 +    @Path("openings/{code}.png")
   2.125 +    @Produces("image/png")
   2.126 +    public Response getOpeningBoardImage(
   2.127 +        @PathParam("code") String code,
   2.128 +        @QueryParam("fieldSize") @DefaultValue("40") int fieldSize
   2.129 +    ) throws IllegalPositionException {
   2.130 +        Board b = Board.valueOf(code);
   2.131 +        ResponseBuilder resp = Response.ok();
   2.132 +        CacheControl cc = new CacheControl();
   2.133 +        cc.setNoCache(true);
   2.134 +        resp.cacheControl(cc);
   2.135 +        return resp.entity(BoardImage.draw(b, fieldSize)).build();
   2.136 +    }
   2.137 +
   2.138      //
   2.139      // start the server
   2.140      //
   2.141 @@ -357,12 +447,13 @@
   2.142              port = Integer.parseInt(args[0]);
   2.143          }
   2.144          String remoteAPI = args.length >= 2 ? args[1] : null;
   2.145 +        String remoteStatistics = args.length >= 3 ? args[2] : null;
   2.146  
   2.147          Locale.setDefault(Locale.ROOT);
   2.148  
   2.149 -        Callable<Void> r = startServers(port, remoteAPI);
   2.150 +        Callable<Void> r = startServers(port, remoteAPI, remoteStatistics);
   2.151  
   2.152 -        if (args.length < 2 || !args[args.length - 1].equals("--kill")) {
   2.153 +        if (args.length < 3 || !args[args.length - 1].equals("--kill")) {
   2.154              System.out.println("Hit enter to stop it...");
   2.155              System.in.read();
   2.156          } else {
   2.157 @@ -374,8 +465,9 @@
   2.158          System.exit(0);
   2.159      }
   2.160  
   2.161 -    static Callable<Void> startServers(int port, String remoteAPI) throws Exception {
   2.162 +    static Callable<Void> startServers(int port, String remoteAPI, String remoteStatistics) throws Exception {
   2.163          Client client = new Client();
   2.164 +        Client client1 = new Client();
   2.165  
   2.166          final HttpServer apiServer;
   2.167          if (remoteAPI == null) {
   2.168 @@ -385,6 +477,12 @@
   2.169              apiServer = null;
   2.170          }
   2.171  
   2.172 +        if (remoteStatistics == null) {
   2.173 +            throw new IllegalArgumentException("Provide URL to API server"); // NOI18N
   2.174 +        } else {
   2.175 +            stat = client1.resource(new URI("http://localhost:9444"));
   2.176 +        }
   2.177 +
   2.178          ResourceConfig rc = new PackagesResourceConfig(
   2.179              "cz.xelfi.quoridor.freemarkerdor"
   2.180          );
     3.1 --- a/freemarkerdor/src/main/java/cz/xelfi/quoridor/freemarkerdor/W3CDocumentReader.java	Fri Jan 01 20:53:17 2010 +0100
     3.2 +++ b/freemarkerdor/src/main/java/cz/xelfi/quoridor/freemarkerdor/W3CDocumentReader.java	Thu Jan 07 23:20:33 2010 +0100
     3.3 @@ -1,6 +1,27 @@
     3.4  /*
     3.5 - * To change this template, choose Tools | Templates
     3.6 - * and open the template in the editor.
     3.7 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
     3.8 + *
     3.9 + * The contents of this file are subject to the terms of either the GNU
    3.10 + * General Public License Version 2 only ("GPL") or the Common
    3.11 + * Development and Distribution License("CDDL") (collectively, the
    3.12 + * "License"). You may not use this file except in compliance with the
    3.13 + * License. You can obtain a copy of the License at
    3.14 + * http://www.netbeans.org/cddl-gplv2.html
    3.15 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
    3.16 + * specific language governing permissions and limitations under the
    3.17 + * License.  When distributing the software, include this License Header
    3.18 + * Notice in each file and include the License file at
    3.19 + * nbbuild/licenses/CDDL-GPL-2-CP.  Sun designates this
    3.20 + * particular file as subject to the "Classpath" exception as provided
    3.21 + * by Sun in the GPL Version 2 section of the License file that
    3.22 + * accompanied this code. If applicable, add the following below the
    3.23 + * License Header, with the fields enclosed by brackets [] replaced by
    3.24 + * your own identifying information:
    3.25 + * "Portions Copyrighted [year] [name of copyright owner]"
    3.26 + *
    3.27 + * Contributor(s):
    3.28 + *
    3.29 + * Portions Copyrighted 2009 Jaroslav Tulach
    3.30   */
    3.31  
    3.32  package cz.xelfi.quoridor.freemarkerdor;
     4.1 --- a/freemarkerdor/src/main/resources/cz/xelfi/quoridor/freemarkerdor/UI/Bundle.properties	Fri Jan 01 20:53:17 2010 +0100
     4.2 +++ b/freemarkerdor/src/main/resources/cz/xelfi/quoridor/freemarkerdor/UI/Bundle.properties	Thu Jan 07 23:20:33 2010 +0100
     4.3 @@ -77,3 +77,10 @@
     4.4  cs=\u010Cesky
     4.5  LOCALE=en
     4.6  CHANGE_LANGUAGE=Change!
     4.7 +
     4.8 +ELO_LIST=Elo rating
     4.9 +ELO=Elo
    4.10 +GAMES=Games
    4.11 +OPENINGS=Openings
    4.12 +WHITE_WON=White won
    4.13 +BLACK_WON=Black won
     5.1 --- a/freemarkerdor/src/main/resources/cz/xelfi/quoridor/freemarkerdor/UI/Bundle_cs.properties	Fri Jan 01 20:53:17 2010 +0100
     5.2 +++ b/freemarkerdor/src/main/resources/cz/xelfi/quoridor/freemarkerdor/UI/Bundle_cs.properties	Thu Jan 07 23:20:33 2010 +0100
     5.3 @@ -89,3 +89,10 @@
     5.4  LANGUAGE=Jazyk:
     5.5  CHANGE_LANGUAGE=Zm\u011Bnit!
     5.6  LOCALE=cs
     5.7 +
     5.8 +ELO_LIST=Elo rating
     5.9 +ELO=Elo
    5.10 +GAMES=Hry
    5.11 +OPENINGS=Zah\u00E1jen\u00ED
    5.12 +WHITE_WON=B\u00EDl\u00FD vyhr\u00E1l
    5.13 +BLACK_WON=\u010Cern\u00FD vyhr\u00E1l
     6.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     6.2 +++ b/freemarkerdor/src/main/resources/cz/xelfi/quoridor/freemarkerdor/UI/elo.fmt	Thu Jan 07 23:20:33 2010 +0100
     6.3 @@ -0,0 +1,43 @@
     6.4 +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
     6.5 +<html>
     6.6 +  <head>
     6.7 +    <title>${bundle.ELO_LIST}</title>
     6.8 +    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
     6.9 +  </head>
    6.10 +  <body bgcolor="white">
    6.11 +      <h3><a href="/">${bundle.TITLE_PLAIN}</a></h3>
    6.12 +      <h4>${bundle.ELO_LIST}</h4>
    6.13 +      <table border="1">
    6.14 +          <thead>
    6.15 +              <tr>
    6.16 +                  <th>#</th>
    6.17 +                  <th>${bundle.NAME}</th>
    6.18 +                  <th>${bundle.ELO}</th>
    6.19 +                  <th>${bundle.GAMES}</th>
    6.20 +              </tr>
    6.21 +          </thead>
    6.22 +          <tbody>
    6.23 +              <#assign cElo = 10000>
    6.24 +              <#assign index = 0>
    6.25 +              <#assign cIndex = index>
    6.26 +              <#list doc.eloList.elolist.* as item>
    6.27 +
    6.28 +                <#assign index = index + 1>
    6.29 +                <#if cElo = item.@elo?number>
    6.30 +                <#else>
    6.31 +                  <#assign cIndex = index>
    6.32 +                </#if>
    6.33 +                <#assign cElo = item.@elo?number>
    6.34 +
    6.35 +                <tr>
    6.36 +                <td>${cIndex}</td>
    6.37 +                <td>${item.@player}</td>
    6.38 +                <td>${item.@elo?number}</td>
    6.39 +                <td>${item.@games}</td>
    6.40 +                </tr>
    6.41 +
    6.42 +              </#list>
    6.43 +          </tbody>
    6.44 +      </table>
    6.45 +  </body>
    6.46 +</html>
    6.47 \ No newline at end of file
     7.1 --- a/freemarkerdor/src/main/resources/cz/xelfi/quoridor/freemarkerdor/UI/game.fmt	Fri Jan 01 20:53:17 2010 +0100
     7.2 +++ b/freemarkerdor/src/main/resources/cz/xelfi/quoridor/freemarkerdor/UI/game.fmt	Thu Jan 07 23:20:33 2010 +0100
     7.3 @@ -50,6 +50,7 @@
     7.4              if (dir == 'W') r.x = -1;
     7.5              return r;
     7.6          }
     7.7 +        <#if board.currentPlayer??>
     7.8          function paintPlayer() {
     7.9              var fifth = fieldSize / 5;
    7.10              var m1 = deltas(document.getElementById("pdir1").value.charAt(0));
    7.11 @@ -72,7 +73,11 @@
    7.12           var playerPos = {
    7.13               x: ${board.currentPlayer.column}, 
    7.14               y: ${board.currentPlayer.row}
    7.15 -        };
    7.16 +         };
    7.17 +         <#else>
    7.18 +         function paintPlayer() {
    7.19 +         }
    7.20 +         </#if>
    7.21      </script>
    7.22    </head>
    7.23    <body bgcolor="white">
    7.24 @@ -177,7 +182,7 @@
    7.25        </#if>
    7.26        <p>
    7.27        <#if format?? && format = "text">
    7.28 -        <pre>${doc.game.board}</pre>
    7.29 +        <pre>${textPicture}</pre>
    7.30          ${bundle.BOARD_VIEW}
    7.31          <a href="/games/${doc.game.id.@id}?format=small">${bundle.BOARD_SMALL}</a>
    7.32          <a href="/games/${doc.game.id.@id}?format=image">${bundle.BOARD_IMAGE}</a>
    7.33 @@ -227,7 +232,9 @@
    7.34              <a href="/games/${doc.game.id.@id}?move=${item.@index}">${item.@move}</a>
    7.35          </#if>
    7.36        </#macro>
    7.37 -
    7.38 +      <#if (bCode??) >
    7.39 +        <h3><a href="/openings/${bCode}">${bundle.OPENINGS}</a></h3>
    7.40 +      </#if>
    7.41        <h3><a href="/games/${doc.game.id.@id}?move=0">${bundle.MOVES}</a></h3>
    7.42  
    7.43        <table border="0">
     8.1 --- a/freemarkerdor/src/main/resources/cz/xelfi/quoridor/freemarkerdor/UI/index.fmt	Fri Jan 01 20:53:17 2010 +0100
     8.2 +++ b/freemarkerdor/src/main/resources/cz/xelfi/quoridor/freemarkerdor/UI/index.fmt	Thu Jan 07 23:20:33 2010 +0100
     8.3 @@ -167,6 +167,8 @@
     8.4          </p>
     8.5        </#if>
     8.6  
     8.7 +      <h5><a href="/elo">${bundle.ELO_LIST}</a></h5>
     8.8 +      <h5><a href="/openings">${bundle.OPENINGS}</a></h5>
     8.9        <h5>${bundle.OPTIONS}</h5>
    8.10        
    8.11        <form action="/options">
     9.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     9.2 +++ b/freemarkerdor/src/main/resources/cz/xelfi/quoridor/freemarkerdor/UI/opening_games.fmt	Thu Jan 07 23:20:33 2010 +0100
     9.3 @@ -0,0 +1,36 @@
     9.4 +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
     9.5 +<html>
     9.6 +  <head>
     9.7 +    <title>${bundle.OPENINGS} - ${bundle.GAMES}</title>
     9.8 +    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
     9.9 +  </head>
    9.10 +  <body bgcolor="white">
    9.11 +      <h3><a href="/">${bundle.TITLE_PLAIN}</a></h3>
    9.12 +      <h4>${bundle.OPENINGS} - ${bundle.GAMES}</h4>
    9.13 +
    9.14 +      <#macro before t>
    9.15 +        <#assign seconds = (t / 1000)?long/>
    9.16 +        <#assign minutes= (seconds / 60)?long/>
    9.17 +        <#assign hours= (minutes / 60)?long/>
    9.18 +        <#assign days= (hours / 24)?long/>
    9.19 +        <!--millis: ${t}, seconds: ${seconds}, minutes: ${minutes} hours: ${hours}, days: ${days} -->
    9.20 +        ${bundle("LastMove", t?int, seconds?int, minutes?int, hours?int, days?int)}
    9.21 +      </#macro>
    9.22 +      <#macro game game>
    9.23 +          ${bundle("gameWhiteBlack", game.@white?string, game.@black?string)}
    9.24 +          <a href="/games/${game.@id}/"><@before (now - game.@modified?number)?long/></a>
    9.25 +          <#if game.@comments?number != 0>
    9.26 +            ${bundle("comments", game.@comments?number)}
    9.27 +          </#if>
    9.28 +      </#macro>
    9.29 +
    9.30 +      <ol>
    9.31 +      <#list doc.gameIds.* as g>
    9.32 +            <li>
    9.33 +                <@game g/>
    9.34 +            </li>
    9.35 +      </#list>
    9.36 +      </ol>
    9.37 +      <h4><a href="/openings/${code}">${bundle.OPENINGS}</a></h4>
    9.38 +  </body>
    9.39 +</html>
    9.40 \ No newline at end of file
    10.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    10.2 +++ b/freemarkerdor/src/main/resources/cz/xelfi/quoridor/freemarkerdor/UI/openings.fmt	Thu Jan 07 23:20:33 2010 +0100
    10.3 @@ -0,0 +1,97 @@
    10.4 +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
    10.5 +<html>
    10.6 +  <head>
    10.7 +    <title>${bundle.OPENINGS}</title>
    10.8 +    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    10.9 +  </head>
   10.10 +  <body bgcolor="white">
   10.11 +      <h3><a href="/">${bundle.TITLE_PLAIN}</a></h3>
   10.12 +      <h4><a href="/openings">${bundle.OPENINGS}</a></h4>
   10.13 +      <p>
   10.14 +          <b>${bundle.WHITE}:</b> ${bundle("FENCES_LEFT", whitefences?number)}<br>
   10.15 +          <b>${bundle.BLACK}:</b> ${bundle("FENCES_LEFT", blackfences?number)}<br>
   10.16 +      </p>
   10.17 +      <div style="position: relative; height:180px;">
   10.18 +           <img style="position: absolute; left: 0; right: 0;"
   10.19 +                width="180" height="180 "
   10.20 +                src="/openings/${doc.openingNodeView.@code}.png?fieldSize=20"
   10.21 +                alt="${bundle.BOARD_TEXT}"
   10.22 +           >
   10.23 +           <div id="fence" style="position: absolute; left: 0; right: 0;"></div>
   10.24 +           <div id="player" style="position: absolute; left: 0; right: 0;"></div>
   10.25 +      </div>
   10.26 +
   10.27 +      <#macro move item>
   10.28 +          <a href="/openings/${item.code}/">${item.move}</a>
   10.29 +      </#macro>
   10.30 +      <#macro games cnt gId status code>
   10.31 +          <#if (cnt?number = 0)>
   10.32 +              ${cnt?number}
   10.33 +          </#if>
   10.34 +          <#if (cnt?number = 1)>
   10.35 +              <a href="/games/${gId.@id}/">${cnt?number}</a>
   10.36 +          </#if>
   10.37 +          <#if (cnt?number > 1)>
   10.38 +              <a href="/openings/${code}/${status}">${cnt?number}</a>
   10.39 +          </#if>
   10.40 +      </#macro>
   10.41 +      <#if (doc.openingNodeView.children.*?size > 0)>
   10.42 +      <table border="1">
   10.43 +          <thead>
   10.44 +              <tr>
   10.45 +                  <th>${bundle.MOVENUMBER}</th>
   10.46 +                  <th>${bundle.WHITE_WON}</th>
   10.47 +                  <th>${bundle.BLACK_WON}</th>
   10.48 +              </tr>
   10.49 +          </thead>
   10.50 +          <tbody>
   10.51 +              <#list doc.openingNodeView.children.* as item>
   10.52 +                <tr>
   10.53 +                <td><@move item/></td>
   10.54 +                <td><@games item.whiteWon item.whiteGame 'white' item.code/></td>
   10.55 +                <td><@games item.blackWon item.blackGame 'black' item.code/></td>
   10.56 +                </tr>
   10.57 +              </#list>
   10.58 +          </tbody>
   10.59 +      </table>
   10.60 +      </#if>
   10.61 +
   10.62 +      <#macro before t>
   10.63 +        <#assign seconds = (t / 1000)?long/>
   10.64 +        <#assign minutes= (seconds / 60)?long/>
   10.65 +        <#assign hours= (minutes / 60)?long/>
   10.66 +        <#assign days= (hours / 24)?long/>
   10.67 +        <!--millis: ${t}, seconds: ${seconds}, minutes: ${minutes} hours: ${hours}, days: ${days} -->
   10.68 +        ${bundle("LastMove", t?int, seconds?int, minutes?int, hours?int, days?int)}
   10.69 +      </#macro>
   10.70 +      <#macro game game>
   10.71 +          ${bundle("gameWhiteBlack", game.@white?string, game.@black?string)}
   10.72 +          <a href="/games/${game.@id}/"><@before (now - game.@modified?number)?long/></a>
   10.73 +          <#if game.@status = "whiteWon">
   10.74 +            ${bundle.WHITE_WON}
   10.75 +          </#if>
   10.76 +          <#if game.@status = "blackWon">
   10.77 +            ${bundle.BLACK_WON}
   10.78 +          </#if>
   10.79 +          <#if game.@comments?number != 0>
   10.80 +            ${bundle("comments", game.@comments?number)}
   10.81 +          </#if>
   10.82 +      </#macro>
   10.83 +
   10.84 +      <#if (doc.openingNodeView.@whiteCount?number + doc.openingNodeView.@blackCount?number > 0)>
   10.85 +          <h3>${bundle.GAMES}</h3>
   10.86 +          <ol>
   10.87 +          <#list doc.openingNodeView.whiteGames.* as g>
   10.88 +                <li>
   10.89 +                    <@game g/>
   10.90 +                </li>
   10.91 +          </#list>
   10.92 +          <#list doc.openingNodeView.blackGames.* as g>
   10.93 +                <li>
   10.94 +                    <@game g/>
   10.95 +                </li>
   10.96 +          </#list>
   10.97 +          </ol>
   10.98 +      </#if>
   10.99 +  </body>
  10.100 +</html>
  10.101 \ No newline at end of file
    11.1 --- a/freemarkerdor/src/test/java/cz/xelfi/quoridor/freemarkerdor/UITest.java	Fri Jan 01 20:53:17 2010 +0100
    11.2 +++ b/freemarkerdor/src/test/java/cz/xelfi/quoridor/freemarkerdor/UITest.java	Thu Jan 07 23:20:33 2010 +0100
    11.3 @@ -33,6 +33,7 @@
    11.4  import com.sun.jersey.core.util.MultivaluedMapImpl;
    11.5  import com.sun.net.httpserver.HttpServer;
    11.6  import cz.xelfi.quoridor.webidor.resources.Quoridor;
    11.7 +import cz.xelfi.quoridor.statistics.resources.Statistics;
    11.8  import java.awt.Image;
    11.9  import java.io.File;
   11.10  import java.io.FileOutputStream;
   11.11 @@ -59,9 +60,11 @@
   11.12  public class UITest extends Object {
   11.13      private static File dir;
   11.14      private static HttpServer stopAPI;
   11.15 +    private static HttpServer stopStatistics;
   11.16      private static Callable<Void> stop;
   11.17      private WebResource webResource;
   11.18      private WebResource apiResource;
   11.19 +    private WebResource statResource;
   11.20  
   11.21      public UITest() throws Exception {
   11.22      }
   11.23 @@ -74,7 +77,8 @@
   11.24          dir.mkdirs();
   11.25          System.setProperty("quoridor.dir", dir.getPath());
   11.26          stopAPI = Quoridor.start(9990);
   11.27 -        stop = UI.startServers(9991, "http://localhost:9990");
   11.28 +        stopStatistics = Quoridor.start(9992);
   11.29 +        stop = UI.startServers(9991, "http://localhost:9990", "http://localhost:9992");
   11.30  
   11.31          File passwd = new File(dir, "passwd");
   11.32          FileOutputStream os = new FileOutputStream(passwd);
   11.33 @@ -88,6 +92,7 @@
   11.34          Client client = new Client();
   11.35          webResource = client.resource(new URI("http://localhost:9991/"));
   11.36          apiResource = client.resource(new URI("http://localhost:9990/"));
   11.37 +        statResource = client.resource(new URI("http://localhost:9992/"));
   11.38      }
   11.39  
   11.40      @AfterClass
   11.41 @@ -126,6 +131,12 @@
   11.42          assertTrue(serviceWadl.length() > 0);
   11.43      }
   11.44  
   11.45 +    @Test public void testStatWadl() {
   11.46 +        String serviceWadl = statResource.path("application.wadl").
   11.47 +                accept(MediaTypes.WADL).get(String.class);
   11.48 +        assertTrue(serviceWadl.length() > 0);
   11.49 +    }
   11.50 +
   11.51      @Test public void testGetIndexPage() throws Exception {
   11.52          String logJarda = apiResource.path("login").
   11.53              queryParam("name", "Jarda").
    12.1 --- a/pom.xml	Fri Jan 01 20:53:17 2010 +0100
    12.2 +++ b/pom.xml	Thu Jan 07 23:20:33 2010 +0100
    12.3 @@ -37,10 +37,11 @@
    12.4      <module>quoridor</module>
    12.5      <module>visidor</module>
    12.6      <module>webidor</module>
    12.7 +    <module>statistics</module>
    12.8      <module>freemarkerdor</module>
    12.9      <module>emailer</module>
   12.10    </modules>
   12.11      <name>Quoridor related projects</name>
   12.12      <description>Master project that agregates all quoridor related functionality.
   12.13      </description>
   12.14 -</project>
   12.15 \ No newline at end of file
   12.16 +</project>
    13.1 --- a/quoridor/src/main/java/cz/xelfi/quoridor/Board.java	Fri Jan 01 20:53:17 2010 +0100
    13.2 +++ b/quoridor/src/main/java/cz/xelfi/quoridor/Board.java	Thu Jan 07 23:20:33 2010 +0100
    13.3 @@ -32,7 +32,6 @@
    13.4  import java.io.IOException;
    13.5  import java.io.Reader;
    13.6  import java.io.StringReader;
    13.7 -import java.io.StringWriter;
    13.8  import java.io.Writer;
    13.9  import java.util.ArrayList;
   13.10  import java.util.Arrays;
   13.11 @@ -42,6 +41,7 @@
   13.12  import java.util.HashSet;
   13.13  import java.util.List;
   13.14  import java.util.Set;
   13.15 +import java.util.TreeSet;
   13.16  import java.util.regex.Matcher;
   13.17  import java.util.regex.Pattern;
   13.18  
   13.19 @@ -352,13 +352,8 @@
   13.20       * @return board object, if the string can be read
   13.21       * @throws IllegalPositionException if the string does not represent the board
   13.22       */
   13.23 -    public static Board valueOf(String board) throws IllegalPositionException {
   13.24 -        try {
   13.25 -            return read(new StringReader(board));
   13.26 -        } catch (IOException ex) {
   13.27 -            // shall not happen, StringReader does not throw IOException
   13.28 -            throw (IllegalPositionException)new IllegalPositionException(ex.getMessage()).initCause(ex);
   13.29 -        }
   13.30 +    public static Board valueOf(String board) {
   13.31 +        return new Board(board);
   13.32      }
   13.33  
   13.34      private static int assertChar(String s, int pos, char... ch) throws IOException {
   13.35 @@ -697,16 +692,9 @@
   13.36       */
   13.37      @Override
   13.38      public String toString() {
   13.39 -        StringWriter w = new StringWriter();
   13.40 -        try {
   13.41 -            write(w);
   13.42 -        } catch (IOException ex) {
   13.43 -            return ex.toString();
   13.44 -        }
   13.45 -        return w.toString();
   13.46 +        return getCode();
   13.47      }
   13.48  
   13.49 -    
   13.50      //
   13.51      // Validation methods
   13.52      //
   13.53 @@ -863,4 +851,92 @@
   13.54          return 17 * y + x;
   13.55      }
   13.56  
   13.57 +    Board(String hashCode) throws IllegalStateException{
   13.58 +        this.fences = new HashSet<Fence>();
   13.59 +        if((hashCode != null) && (hashCode.length() > 6)){
   13.60 +            char[]c = hashCode.toCharArray();
   13.61 +            this.players = Collections.unmodifiableList (Arrays.asList (new Player[] {
   13.62 +                new Player ((c[0]-'A')*2, (c[1]-'0')*2, c[2]-'a', Player.Direction.NORTH),
   13.63 +                new Player ((c[3]-'A')*2, (c[4]-'0')*2, c[5]-'a', Player.Direction.SOUTH),
   13.64 +            }));
   13.65 +            if(c[6]=='w'){
   13.66 +                this.turn = 0;
   13.67 +                this.winner = null;
   13.68 +            }else if(c[6]=='b'){
   13.69 +                this.turn = 1;
   13.70 +                this.winner = null;
   13.71 +            }else if(c[6]=='W'){
   13.72 +                this.turn = 0;
   13.73 +                this.winner = this.players.get(0);
   13.74 +            }else if(c[6]=='B'){
   13.75 +                this.turn = 1;
   13.76 +                this.winner = this.players.get(1);
   13.77 +            }else{
   13.78 +                this.turn = 0;
   13.79 +                this.winner = null;
   13.80 +            }
   13.81 +            for(int i=7; i<c.length;i+=2){
   13.82 +                int f = Integer.parseInt(hashCode.substring(i, i+2),16);
   13.83 +                Fence.Orientation o = Fence.Orientation.HORIZONTAL;
   13.84 +                if(f > 64){
   13.85 +                    o = Fence.Orientation.VERTICAL;
   13.86 +                    f -= 64;
   13.87 +                }
   13.88 +                fences.add(new Fence((f/8)*2+1, (f%8)*2+1,o));
   13.89 +            }
   13.90 +        }else{
   13.91 +            this.players = Collections.unmodifiableList (Arrays.asList (new Player[] {
   13.92 +                new Player (8,0,10,Player.Direction.NORTH),
   13.93 +                new Player (8,16,10,Player.Direction.SOUTH),
   13.94 +            }));
   13.95 +            this.winner = null;
   13.96 +            this.turn = 0;
   13.97 +        }
   13.98 +        try {
   13.99 +            this.occupied = computeOccupied (players, fences);
  13.100 +        } catch (IllegalPositionException ex) {
  13.101 +            throw new IllegalStateException (ex.getMessage ());
  13.102 +        }
  13.103 +    }
  13.104 +
  13.105 +    public final String getCode() {
  13.106 +        Board b = this;
  13.107 +        StringBuilder sb = new StringBuilder();
  13.108 +        for(Player p: b.getPlayers()){
  13.109 +            sb.append((char)(p.getColumn() + 'A'));
  13.110 +            sb.append((char)(p.getRow() + '0'));
  13.111 +            sb.append((char)(p.getFences() + 'a'));
  13.112 +        }
  13.113 +        Player winner = b.getWinner();
  13.114 +        if(winner == null){
  13.115 +            if(b.players.indexOf(b.getCurrentPlayer())==0)
  13.116 +                sb.append('w');
  13.117 +            else if(b.players.indexOf(b.getCurrentPlayer())==1)
  13.118 +                sb.append('b');
  13.119 +            else
  13.120 +                sb.append('n');
  13.121 +        }else{
  13.122 +            if(b.players.indexOf(winner)==0)
  13.123 +                sb.append('W');
  13.124 +            else if(b.players.indexOf(winner)==1)
  13.125 +                sb.append('B');
  13.126 +            else
  13.127 +                sb.append('N');
  13.128 +        }
  13.129 +
  13.130 +        TreeSet<Integer> fences = new TreeSet<Integer>();
  13.131 +        for(Fence f: b.getFences()){
  13.132 +            int a = (f.getColumn() - 'A')*8 + (f.getRow()-1);
  13.133 +            if(f.getOrientation().equals(Fence.Orientation.VERTICAL))
  13.134 +                a+=64;
  13.135 +            fences.add(a);
  13.136 +        }
  13.137 +        for(int f: fences){
  13.138 +            if(f<16)
  13.139 +                sb.append('0');
  13.140 +            sb.append(Integer.toHexString(f));
  13.141 +        }
  13.142 +        return sb.toString();
  13.143 +    }
  13.144 +
  13.145  }
    14.1 --- a/quoridor/src/main/java/cz/xelfi/quoridor/Move.java	Fri Jan 01 20:53:17 2010 +0100
    14.2 +++ b/quoridor/src/main/java/cz/xelfi/quoridor/Move.java	Thu Jan 07 23:20:33 2010 +0100
    14.3 @@ -199,5 +199,26 @@
    14.4          return hash;
    14.5      }
    14.6  
    14.7 +    public Move getMirrorMove(){
    14.8 +        if(fence != null){
    14.9 +            return new Move(new Fence(16-fence.getX(), fence.getY(), fence.getOrientation()));
   14.10 +        }
   14.11 +        if(direction == null)
   14.12 +            return new Move();
   14.13 +        int dirSize = direction.length;
   14.14 +        Direction[] mirrorDirection = new Direction[dirSize];
   14.15 +        for(int i = 0; i < dirSize; i++){
   14.16 +            if(direction[i].equals(Direction.NORTH))
   14.17 +                mirrorDirection[i] = Direction.NORTH;
   14.18 +            else if(direction[i].equals(Direction.SOUTH))
   14.19 +                mirrorDirection[i] = Direction.SOUTH;
   14.20 +            else if(direction[i].equals(Direction.EAST))
   14.21 +                mirrorDirection[i] = Direction.WEST;
   14.22 +            else if(direction[i].equals(Direction.WEST))
   14.23 +                mirrorDirection[i] = Direction.EAST;
   14.24 +        }
   14.25 +        return new Move(mirrorDirection);
   14.26 +    }
   14.27 +
   14.28  
   14.29  }
    15.1 --- a/quoridor/src/test/java/cz/xelfi/quoridor/BoardCase.java	Fri Jan 01 20:53:17 2010 +0100
    15.2 +++ b/quoridor/src/test/java/cz/xelfi/quoridor/BoardCase.java	Thu Jan 07 23:20:33 2010 +0100
    15.3 @@ -27,6 +27,8 @@
    15.4  
    15.5  import cz.xelfi.quoridor.Fence.Orientation;
    15.6  import cz.xelfi.quoridor.Player.Direction;
    15.7 +import java.io.IOException;
    15.8 +import java.io.StringReader;
    15.9  import java.util.List;
   15.10  import junit.framework.TestCase;
   15.11  
   15.12 @@ -200,6 +202,7 @@
   15.13  "\n" +
   15.14  "                         [S]                 \n";
   15.15  
   15.16 +        b = picture2board(b).toString();
   15.17          Board begin = Board.valueOf(b);
   15.18  
   15.19          try {
   15.20 @@ -497,4 +500,8 @@
   15.21          assertFalse(f4.equals(f5));
   15.22          assertFalse(f3.equals(f5));
   15.23      }
   15.24 +    static Board picture2board(String text) throws IOException, IllegalPositionException {
   15.25 +        StringReader sr = new StringReader(text);
   15.26 +        return Board.read(sr);
   15.27 +    }
   15.28  }
    16.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    16.2 +++ b/quoridor/src/test/java/cz/xelfi/quoridor/SerializeCodeTest.java	Thu Jan 07 23:20:33 2010 +0100
    16.3 @@ -0,0 +1,73 @@
    16.4 +/*
    16.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
    16.6 + *
    16.7 + * The contents of this file are subject to the terms of either the GNU
    16.8 + * General Public License Version 2 only ("GPL") or the Common
    16.9 + * Development and Distribution License("CDDL") (collectively, the
   16.10 + * "License"). You may not use this file except in compliance with the
   16.11 + * License. You can obtain a copy of the License at
   16.12 + * http://www.netbeans.org/cddl-gplv2.html
   16.13 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
   16.14 + * specific language governing permissions and limitations under the
   16.15 + * License.  When distributing the software, include this License Header
   16.16 + * Notice in each file and include the License file at
   16.17 + * nbbuild/licenses/CDDL-GPL-2-CP.  Sun designates this
   16.18 + * particular file as subject to the "Classpath" exception as provided
   16.19 + * by Sun in the GPL Version 2 section of the License file that
   16.20 + * accompanied this code. If applicable, add the following below the
   16.21 + * License Header, with the fields enclosed by brackets [] replaced by
   16.22 + * your own identifying information:
   16.23 + * "Portions Copyrighted [year] [name of copyright owner]"
   16.24 + *
   16.25 + * Contributor(s):
   16.26 + *
   16.27 + * Portions Copyrighted 2009 Jaroslav Tulach
   16.28 + */
   16.29 +package cz.xelfi.quoridor;
   16.30 +
   16.31 +import cz.xelfi.quoridor.Player.Direction;
   16.32 +import java.io.IOException;
   16.33 +
   16.34 +/** Basic tests in simple configuration.
   16.35 + *
   16.36 + * @author Jaroslav Tulach
   16.37 + */
   16.38 +public class SerializeCodeTest extends BoardCase {
   16.39 +    public SerializeCodeTest (String testName) {
   16.40 +        super (testName);
   16.41 +    }
   16.42 +
   16.43 +    @Override
   16.44 +    protected Board move(Board b, int player, Direction... where) throws IllegalPositionException {
   16.45 +        try {
   16.46 +            Board read = reread(b);
   16.47 +            return read.move(read.getPlayers().get(player), where);
   16.48 +        } catch (IOException ex) {
   16.49 +            throw new IllegalStateException(ex);
   16.50 +        }
   16.51 +    }
   16.52 +    protected Board fence(Board b, int player, char x, int y, Fence.Orientation orie)
   16.53 +    throws IllegalPositionException {
   16.54 +        try {
   16.55 +            Board read = reread(b);
   16.56 +            return read.fence(read.getPlayers().get(player), x, y, orie);
   16.57 +        } catch (IOException ex) {
   16.58 +            throw new IllegalStateException(ex);
   16.59 +        }
   16.60 +    }
   16.61 +
   16.62 +    @Override
   16.63 +    protected Board apply(Board b, Move move) throws IllegalPositionException {
   16.64 +        try {
   16.65 +            Board read = reread(b);
   16.66 +            return read.apply(move);
   16.67 +        } catch (IOException ex) {
   16.68 +            throw new IllegalStateException(ex);
   16.69 +        }
   16.70 +    }
   16.71 +
   16.72 +    private Board reread(Board b) throws IOException, IllegalPositionException {
   16.73 +        return Board.valueOf(b.getCode());
   16.74 +    }
   16.75 +
   16.76 +}
    17.1 --- a/quoridor/src/test/java/cz/xelfi/quoridor/SerializeTest.java	Fri Jan 01 20:53:17 2010 +0100
    17.2 +++ b/quoridor/src/test/java/cz/xelfi/quoridor/SerializeTest.java	Thu Jan 07 23:20:33 2010 +0100
    17.3 @@ -27,7 +27,6 @@
    17.4  
    17.5  import cz.xelfi.quoridor.Player.Direction;
    17.6  import java.io.IOException;
    17.7 -import java.io.StringReader;
    17.8  import java.io.StringWriter;
    17.9  
   17.10  /** Basic tests in simple configuration.
   17.11 @@ -72,7 +71,8 @@
   17.12          StringWriter w = new StringWriter();
   17.13          b.write(w);
   17.14          w.close();
   17.15 -        return Board.valueOf(w.toString());
   17.16 +        return picture2board(w.toString());
   17.17 +        //return Board.valueOf(w.toString());
   17.18      }
   17.19  
   17.20  }
    18.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    18.2 +++ b/statistics/all-zip.xml	Thu Jan 07 23:20:33 2010 +0100
    18.3 @@ -0,0 +1,19 @@
    18.4 +<?xml version="1.0" encoding="UTF-8"?>
    18.5 +<assembly>
    18.6 + <id>all</id>
    18.7 +  <formats>
    18.8 +    <format>zip</format>
    18.9 +  </formats>
   18.10 +  <dependencySets>
   18.11 +    <dependencySet>
   18.12 +        <useProjectArtifact>false</useProjectArtifact>
   18.13 +        <outputDirectory>lib</outputDirectory>
   18.14 +    </dependencySet>
   18.15 +  </dependencySets>
   18.16 +  <files>
   18.17 +    <file>
   18.18 +      <source>target/statistics-${version}.jar</source>
   18.19 +      <outputDirectory>/</outputDirectory>
   18.20 +    </file>
   18.21 +  </files>
   18.22 +</assembly>
    19.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    19.2 +++ b/statistics/pom.xml	Thu Jan 07 23:20:33 2010 +0100
    19.3 @@ -0,0 +1,141 @@
    19.4 +<?xml version="1.0" encoding="UTF-8"?>
    19.5 +<project xmlns="http://maven.apache.org/POM/4.0.0" 
    19.6 +         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    19.7 +         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    19.8 +  <modelVersion>4.0.0</modelVersion>
    19.9 +  <parent>
   19.10 +        <artifactId>all-quoridor</artifactId>
   19.11 +        <groupId>org.apidesign</groupId>
   19.12 +        <version>1.0</version>
   19.13 +  </parent>
   19.14 +  <groupId>org.apidesign</groupId>
   19.15 +  <artifactId>statistics</artifactId>
   19.16 +  <packaging>jar</packaging>
   19.17 +  <version>1.0</version>
   19.18 +  <name>Game Statistics</name>
   19.19 +  <url>http://maven.apache.org</url>
   19.20 +  <repositories>
   19.21 +    <repository>
   19.22 +        <id>maven2-repository.dev.java.net</id>
   19.23 +        <name>Java.net Repository for Maven</name>
   19.24 +        <url>http://download.java.net/maven/2/</url>
   19.25 +        <layout>default</layout>
   19.26 +    </repository>
   19.27 +    <repository>
   19.28 +        <id>maven-repository.dev.java.net</id>
   19.29 +        <name>Java.net Maven 1 Repository (legacy)</name>
   19.30 +        <url>http://download.java.net/maven/1</url>
   19.31 +        <layout>legacy</layout>
   19.32 +    </repository>
   19.33 +  </repositories>
   19.34 +  <dependencies>
   19.35 +
   19.36 +    <dependency>
   19.37 +      <groupId>junit</groupId>
   19.38 +      <artifactId>junit</artifactId>
   19.39 +      <version>4.5</version>
   19.40 +      <scope>test</scope>
   19.41 +    </dependency>
   19.42 +    <dependency>
   19.43 +      <groupId>com.sun.jersey</groupId>
   19.44 +      <artifactId>jersey-core</artifactId>
   19.45 +      <version>1.1.0-ea</version>
   19.46 +    </dependency>
   19.47 +    <dependency>
   19.48 +      <groupId>com.sun.jersey</groupId>
   19.49 +      <artifactId>jersey-server</artifactId>
   19.50 +      <version>1.1.0-ea</version>
   19.51 +    </dependency>
   19.52 +    <dependency>
   19.53 +      <groupId>com.sun.jersey</groupId>
   19.54 +      <artifactId>jersey-json</artifactId>
   19.55 +      <version>1.1.0-ea</version>
   19.56 +      <type>jar</type>
   19.57 +      <exclusions>
   19.58 +        <exclusion>
   19.59 +          <artifactId>jaxb-api</artifactId>
   19.60 +          <groupId>javax.xml.bind</groupId>
   19.61 +        </exclusion>
   19.62 +        <exclusion>
   19.63 +          <artifactId>stax-api</artifactId>
   19.64 +          <groupId>stax</groupId>
   19.65 +        </exclusion>
   19.66 +      </exclusions>
   19.67 +    </dependency>
   19.68 +    <dependency>
   19.69 +      <groupId>com.sun.jersey.test.framework</groupId>
   19.70 +      <artifactId>jersey-test-framework</artifactId>
   19.71 +      <version>1.1.0-ea</version>
   19.72 +      <scope>test</scope>
   19.73 +    </dependency>
   19.74 +    <dependency>
   19.75 +      <groupId>org.apidesign</groupId>
   19.76 +      <artifactId>quoridor</artifactId>
   19.77 +      <version>1.0</version>
   19.78 +      <type>jar</type>
   19.79 +    </dependency>
   19.80 +    <dependency>
   19.81 +      <groupId>${project.groupId}</groupId>
   19.82 +      <artifactId>webidor</artifactId>
   19.83 +      <version>1.11</version>
   19.84 +    </dependency>
   19.85 +  </dependencies>
   19.86 +  <build>
   19.87 +    <plugins>
   19.88 +      <plugin>
   19.89 +        <groupId>org.apache.maven.plugins</groupId>
   19.90 +        <artifactId>maven-compiler-plugin</artifactId>
   19.91 +        <version>2.0.2</version>
   19.92 +        <configuration>
   19.93 +          <source>1.6</source>
   19.94 +          <target>1.6</target>
   19.95 +        </configuration>
   19.96 +      </plugin>
   19.97 +      <plugin>
   19.98 +        <groupId>org.codehaus.mojo</groupId>
   19.99 +        <artifactId>exec-maven-plugin</artifactId>
  19.100 +        <configuration>
  19.101 +            <mainClass>cz.xelfi.quoridor.statistics.resources.Statistics</mainClass>
  19.102 +        </configuration>
  19.103 +      </plugin>
  19.104 +      <plugin>
  19.105 +        <artifactId>maven-assembly-plugin</artifactId>
  19.106 +        <version>2.2-beta-2</version>
  19.107 +        <executions>
  19.108 +          <execution>
  19.109 +            <id>create-executable-jar</id>
  19.110 +            <phase>package</phase>
  19.111 +            <goals>
  19.112 +              <goal>single</goal>
  19.113 +            </goals>
  19.114 +            <configuration>
  19.115 +              <descriptors>
  19.116 +                <descriptor>all-zip.xml</descriptor>
  19.117 +              </descriptors>
  19.118 +              <finalName>statistics-${version}</finalName>
  19.119 +            </configuration>
  19.120 +          </execution>
  19.121 +        </executions>
  19.122 +      </plugin>
  19.123 +      <plugin>
  19.124 +        <groupId>org.apache.maven.plugins</groupId>
  19.125 +        <artifactId>maven-jar-plugin</artifactId>
  19.126 +        <configuration>
  19.127 +            <archive>
  19.128 +                <manifest>
  19.129 +                    <addClasspath>true</addClasspath>
  19.130 +                    <classpathPrefix>lib/</classpathPrefix>
  19.131 +                    <mainClass>cz.xelfi.quoridor.statistics.resources.Statistics</mainClass>
  19.132 +                </manifest>
  19.133 +            </archive>
  19.134 +        </configuration>
  19.135 +      </plugin>
  19.136 +    </plugins>
  19.137 +  </build>
  19.138 +    <description>Server with API for games statistics</description>
  19.139 +</project>
  19.140 +
  19.141 +
  19.142 +
  19.143 +
  19.144 +
    20.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    20.2 +++ b/statistics/src/main/java/cz/xelfi/quoridor/statistics/EloEntry.java	Thu Jan 07 23:20:33 2010 +0100
    20.3 @@ -0,0 +1,82 @@
    20.4 +/*
    20.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
    20.6 + *
    20.7 + * The contents of this file are subject to the terms of either the GNU
    20.8 + * General Public License Version 2 only ("GPL") or the Common
    20.9 + * Development and Distribution License("CDDL") (collectively, the
   20.10 + * "License"). You may not use this file except in compliance with the
   20.11 + * License. You can obtain a copy of the License at
   20.12 + * http://www.netbeans.org/cddl-gplv2.html
   20.13 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
   20.14 + * specific language governing permissions and limitations under the
   20.15 + * License.  When distributing the software, include this License Header
   20.16 + * Notice in each file and include the License file at
   20.17 + * nbbuild/licenses/CDDL-GPL-2-CP.  Sun designates this
   20.18 + * particular file as subject to the "Classpath" exception as provided
   20.19 + * by Sun in the GPL Version 2 section of the License file that
   20.20 + * accompanied this code. If applicable, add the following below the
   20.21 + * License Header, with the fields enclosed by brackets [] replaced by
   20.22 + * your own identifying information:
   20.23 + * "Portions Copyrighted [year] [name of copyright owner]"
   20.24 + *
   20.25 + * Contributor(s):
   20.26 + *
   20.27 + * Portions Copyrighted 2010 Martin Rexa
   20.28 + */
   20.29 +
   20.30 +package cz.xelfi.quoridor.statistics;
   20.31 +
   20.32 +import javax.xml.bind.annotation.XmlAttribute;
   20.33 +import java.util.Comparator;
   20.34 +/**
   20.35 + *
   20.36 + * @author Martin Rexa
   20.37 + */
   20.38 +public class EloEntry {
   20.39 +    String player;
   20.40 +    double elo;
   20.41 +    int games;
   20.42 +
   20.43 +    public static final Comparator<EloEntry> BEST_FIRST = new BestFirst();
   20.44 +
   20.45 +    EloEntry(){
   20.46 +        super();
   20.47 +    }
   20.48 +
   20.49 +    EloEntry(String player, double elo, int games){
   20.50 +        super();
   20.51 +        this.player = player;
   20.52 +        this.elo = elo;
   20.53 +        this.games = games;
   20.54 +    }
   20.55 +
   20.56 +    @XmlAttribute
   20.57 +    public String getPlayer(){
   20.58 +        return player;
   20.59 +    }
   20.60 +
   20.61 +    @XmlAttribute
   20.62 +    public Double getElo(){
   20.63 +        return elo;
   20.64 +    }
   20.65 +
   20.66 +    @XmlAttribute
   20.67 +    public Integer getGames(){
   20.68 +        return games;
   20.69 +    }
   20.70 +
   20.71 +    public String toString(){
   20.72 +        return "Player: " + player + ", ELO: " + elo + ", Games: " + games;
   20.73 +    }
   20.74 +
   20.75 +    private static final class BestFirst implements Comparator<EloEntry> {
   20.76 +        public int compare(EloEntry e1, EloEntry e2) {
   20.77 +            if(e1.elo > e2.elo)
   20.78 +                return -1;
   20.79 +            else if(e1.elo < e2.elo)
   20.80 +                return 1;
   20.81 +            else return e1.player.compareTo(e2.player);
   20.82 +        }
   20.83 +    }
   20.84 +
   20.85 +}
    21.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    21.2 +++ b/statistics/src/main/java/cz/xelfi/quoridor/statistics/EloList.java	Thu Jan 07 23:20:33 2010 +0100
    21.3 @@ -0,0 +1,104 @@
    21.4 +/*
    21.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
    21.6 + *
    21.7 + * The contents of this file are subject to the terms of either the GNU
    21.8 + * General Public License Version 2 only ("GPL") or the Common
    21.9 + * Development and Distribution License("CDDL") (collectively, the
   21.10 + * "License"). You may not use this file except in compliance with the
   21.11 + * License. You can obtain a copy of the License at
   21.12 + * http://www.netbeans.org/cddl-gplv2.html
   21.13 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
   21.14 + * specific language governing permissions and limitations under the
   21.15 + * License.  When distributing the software, include this License Header
   21.16 + * Notice in each file and include the License file at
   21.17 + * nbbuild/licenses/CDDL-GPL-2-CP.  Sun designates this
   21.18 + * particular file as subject to the "Classpath" exception as provided
   21.19 + * by Sun in the GPL Version 2 section of the License file that
   21.20 + * accompanied this code. If applicable, add the following below the
   21.21 + * License Header, with the fields enclosed by brackets [] replaced by
   21.22 + * your own identifying information:
   21.23 + * "Portions Copyrighted [year] [name of copyright owner]"
   21.24 + *
   21.25 + * Contributor(s):
   21.26 + *
   21.27 + * Portions Copyrighted 2010 Martin Rexa
   21.28 + */
   21.29 +
   21.30 +package cz.xelfi.quoridor.statistics;
   21.31 +
   21.32 +import java.util.Map;
   21.33 +import java.util.HashMap;
   21.34 +import java.util.List;
   21.35 +import java.util.Collections;
   21.36 +import java.util.ArrayList;
   21.37 +import javax.xml.bind.annotation.XmlElement;
   21.38 +import javax.xml.bind.annotation.XmlElementWrapper;
   21.39 +import javax.xml.bind.annotation.XmlRootElement;
   21.40 +import javax.xml.bind.annotation.XmlAccessType;
   21.41 +import javax.xml.bind.annotation.XmlAccessorType;
   21.42 +/**
   21.43 + *
   21.44 + * @author Martin Rexa
   21.45 + */
   21.46 +@XmlRootElement
   21.47 +@XmlAccessorType(XmlAccessType.FIELD)
   21.48 +public class EloList {
   21.49 +    static int INITIAL_ELO = 1250;
   21.50 +    static int MAX_ELO_DIFF = 400;
   21.51 +    static int KVAL_START = 25;
   21.52 +    static int KVAL_FOLLOW = 10;
   21.53 +    static int KVAL_LIMIT = 30;
   21.54 +
   21.55 +    private Map<String, Double> players;
   21.56 +    private Map<String, Integer> playerGames;
   21.57 +
   21.58 +    public EloList () {
   21.59 +        players = new HashMap<String, Double>();
   21.60 +        playerGames = new HashMap<String, Integer>();
   21.61 +    }
   21.62 +
   21.63 +    @XmlElementWrapper(name="elolist")
   21.64 +    @XmlElement(name="item")
   21.65 +    public List<EloEntry> getFinalList(){
   21.66 +        List<EloEntry> finalList = new ArrayList<EloEntry>();
   21.67 +        for (Map.Entry<String, Double> player : players.entrySet()){
   21.68 +            finalList.add(new EloEntry(player.getKey(), player.getValue(), playerGames.get(player.getKey())));
   21.69 +        }
   21.70 +        Collections.sort(finalList, EloEntry.BEST_FIRST);
   21.71 +        return finalList;
   21.72 +    }
   21.73 +
   21.74 +    public EloList putResult(String winner, String looser){
   21.75 +        double wElo = getElo(winner);
   21.76 +        double lElo = getElo(looser);
   21.77 +
   21.78 +        double eloDiff = wElo - lElo;
   21.79 +        if(eloDiff < - MAX_ELO_DIFF)
   21.80 +            eloDiff = - MAX_ELO_DIFF;
   21.81 +        if(eloDiff > MAX_ELO_DIFF)
   21.82 +            eloDiff = MAX_ELO_DIFF;
   21.83 +
   21.84 +        double wDiff = Math.round(100 * getKVal(winner) * (1 - (1 / (1 + Math.pow(10, -eloDiff / MAX_ELO_DIFF)))));
   21.85 +        wDiff /= 100.0;
   21.86 +        double lDiff = Math.round(100 * getKVal(looser) * (0 - (1 / (1 + Math.pow(10, eloDiff / MAX_ELO_DIFF)))));
   21.87 +        lDiff /= 100.0;
   21.88 +
   21.89 +        players.put(winner, wElo + wDiff);
   21.90 +        players.put(looser, lElo + lDiff);
   21.91 +        return this;
   21.92 +    }
   21.93 +    
   21.94 +    public double getElo(String player){
   21.95 +        Double elo = players.get(player);
   21.96 +        return (elo == null) ? INITIAL_ELO : elo;
   21.97 +    }
   21.98 +    
   21.99 +    private int getKVal(String player){
  21.100 +        Integer games = playerGames.get(player);
  21.101 +        int g = (games == null) ? 0 : games;
  21.102 +        // Side effect, method should not be called more times for the same game/player
  21.103 +        playerGames.put(player, g + 1);
  21.104 +        return g < KVAL_LIMIT ? KVAL_START : KVAL_FOLLOW;
  21.105 +    }
  21.106 +
  21.107 +}
    22.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    22.2 +++ b/statistics/src/main/java/cz/xelfi/quoridor/statistics/JAXBContextResolver.java	Thu Jan 07 23:20:33 2010 +0100
    22.3 @@ -0,0 +1,58 @@
    22.4 +/*
    22.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
    22.6 + *
    22.7 + * The contents of this file are subject to the terms of either the GNU
    22.8 + * General Public License Version 2 only ("GPL") or the Common
    22.9 + * Development and Distribution License("CDDL") (collectively, the
   22.10 + * "License"). You may not use this file except in compliance with the
   22.11 + * License. You can obtain a copy of the License at
   22.12 + * http://www.netbeans.org/cddl-gplv2.html
   22.13 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
   22.14 + * specific language governing permissions and limitations under the
   22.15 + * License.  When distributing the software, include this License Header
   22.16 + * Notice in each file and include the License file at
   22.17 + * nbbuild/licenses/CDDL-GPL-2-CP.  Sun designates this
   22.18 + * particular file as subject to the "Classpath" exception as provided
   22.19 + * by Sun in the GPL Version 2 section of the License file that
   22.20 + * accompanied this code. If applicable, add the following below the
   22.21 + * License Header, with the fields enclosed by brackets [] replaced by
   22.22 + * your own identifying information:
   22.23 + * "Portions Copyrighted [year] [name of copyright owner]"
   22.24 + *
   22.25 + * Contributor(s):
   22.26 + *
   22.27 + * Portions Copyrighted 2010 Martin Rexa
   22.28 + */
   22.29 +package cz.xelfi.quoridor.statistics;
   22.30 +
   22.31 +import com.sun.jersey.api.json.JSONConfiguration;
   22.32 +import com.sun.jersey.api.json.JSONJAXBContext;
   22.33 +import java.util.Arrays;
   22.34 +import java.util.HashSet;
   22.35 +import java.util.Set;
   22.36 +import javax.ws.rs.ext.ContextResolver;
   22.37 +import javax.ws.rs.ext.Provider;
   22.38 +import javax.xml.bind.JAXBContext;
   22.39 +
   22.40 +/**
   22.41 + *
   22.42 + * @author Martin Rexa
   22.43 + */
   22.44 +@Provider
   22.45 +public final class JAXBContextResolver implements ContextResolver<JAXBContext> {
   22.46 +
   22.47 +    private final JAXBContext context;
   22.48 +
   22.49 +    private final Set<Class> types;
   22.50 +
   22.51 +    private final Class[] cTypes = {EloList.class, EloEntry.class, OpeningNodeView.class, OpeningNodeViewEntry.class};
   22.52 +
   22.53 +    public JAXBContextResolver() throws Exception {
   22.54 +        this.types = new HashSet(Arrays.asList(cTypes));
   22.55 +        this.context = new JSONJAXBContext(JSONConfiguration.natural().build(), cTypes);
   22.56 +    }
   22.57 +
   22.58 +    public JAXBContext getContext(Class<?> objectType) {
   22.59 +        return (types.contains(objectType)) ? context : null;
   22.60 +    }
   22.61 +}
   22.62 \ No newline at end of file
    23.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    23.2 +++ b/statistics/src/main/java/cz/xelfi/quoridor/statistics/OpeningNodeView.java	Thu Jan 07 23:20:33 2010 +0100
    23.3 @@ -0,0 +1,129 @@
    23.4 +/*
    23.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
    23.6 + *
    23.7 + * The contents of this file are subject to the terms of either the GNU
    23.8 + * General Public License Version 2 only ("GPL") or the Common
    23.9 + * Development and Distribution License("CDDL") (collectively, the
   23.10 + * "License"). You may not use this file except in compliance with the
   23.11 + * License. You can obtain a copy of the License at
   23.12 + * http://www.netbeans.org/cddl-gplv2.html
   23.13 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
   23.14 + * specific language governing permissions and limitations under the
   23.15 + * License.  When distributing the software, include this License Header
   23.16 + * Notice in each file and include the License file at
   23.17 + * nbbuild/licenses/CDDL-GPL-2-CP.  Sun designates this
   23.18 + * particular file as subject to the "Classpath" exception as provided
   23.19 + * by Sun in the GPL Version 2 section of the License file that
   23.20 + * accompanied this code. If applicable, add the following below the
   23.21 + * License Header, with the fields enclosed by brackets [] replaced by
   23.22 + * your own identifying information:
   23.23 + * "Portions Copyrighted [year] [name of copyright owner]"
   23.24 + *
   23.25 + * Contributor(s):
   23.26 + *
   23.27 + * Portions Copyrighted 2010 Martin Rexa
   23.28 + */
   23.29 +
   23.30 +package cz.xelfi.quoridor.statistics;
   23.31 +
   23.32 +import cz.xelfi.quoridor.webidor.GameId;
   23.33 +import cz.xelfi.quoridor.webidor.GameStatus;
   23.34 +import cz.xelfi.quoridor.Move;
   23.35 +import java.util.Set;
   23.36 +import java.util.HashSet;
   23.37 +import java.util.Map;
   23.38 +import java.util.HashMap;
   23.39 +import java.util.List;
   23.40 +import java.util.ArrayList;
   23.41 +import javax.xml.bind.annotation.XmlElement;
   23.42 +import javax.xml.bind.annotation.XmlElementWrapper;
   23.43 +import javax.xml.bind.annotation.XmlRootElement;
   23.44 +//import javax.xml.bind.annotation.XmlAccessType;
   23.45 +//import javax.xml.bind.annotation.XmlAccessorType;
   23.46 +import javax.xml.bind.annotation.XmlAttribute;
   23.47 +
   23.48 +/**
   23.49 + *
   23.50 + * @author Martin Rexa
   23.51 + */
   23.52 +
   23.53 +@XmlRootElement
   23.54 +//@XmlAccessorType(XmlAccessType.FIELD)
   23.55 +public class OpeningNodeView {
   23.56 +    @XmlAttribute
   23.57 +    String code;
   23.58 +    @XmlElementWrapper(name="whiteGames")
   23.59 +    @XmlElement(name="gameId")
   23.60 +    List<GameId> whiteGames;
   23.61 +    @XmlElementWrapper(name="blackGames")
   23.62 +    @XmlElement(name="gameId")
   23.63 +    List<GameId> blackGames;
   23.64 +    Map<Move, OpeningNodeViewEntry> entries;
   23.65 +    boolean empty;
   23.66 +
   23.67 +    public OpeningNodeView(){
   23.68 +    }
   23.69 +
   23.70 +    public OpeningNodeView(String code){
   23.71 +        this.code = code;
   23.72 +        entries = new HashMap<Move, OpeningNodeViewEntry>();
   23.73 +        whiteGames = new ArrayList<GameId>();
   23.74 +        blackGames = new ArrayList<GameId>();
   23.75 +        empty = true;
   23.76 +    }
   23.77 +
   23.78 +    @XmlElement
   23.79 +    public String getNodeCode(){
   23.80 +        return code;
   23.81 +    }
   23.82 +
   23.83 +    public void processGame(Move m, String code, GameId gId){
   23.84 +        OpeningNodeViewEntry e = entries.get(m);
   23.85 +        if(e == null){
   23.86 +            e = new OpeningNodeViewEntry(m,code);
   23.87 +            entries.put(m, e);
   23.88 +        }
   23.89 +        e.processGame(gId);
   23.90 +        empty = false;
   23.91 +    }
   23.92 +
   23.93 +    @XmlElementWrapper(name="children")
   23.94 +    @XmlElement(name="item")
   23.95 +    public Set<OpeningNodeViewEntry> getChildren(){
   23.96 +        Set<OpeningNodeViewEntry> result = new HashSet<OpeningNodeViewEntry>();
   23.97 +        for(Map.Entry<Move, OpeningNodeViewEntry> e: entries.entrySet()){
   23.98 +            result.add(e.getValue());
   23.99 +        }
  23.100 +        return result;
  23.101 +    }
  23.102 +
  23.103 +
  23.104 +    public void addFinishedGame(GameId gId){
  23.105 +        if(gId.getStatus().equals(GameStatus.whiteWon))
  23.106 +            whiteGames.add(gId);
  23.107 +        if(gId.getStatus().equals(GameStatus.blackWon))
  23.108 +            blackGames.add(gId);
  23.109 +        empty = false;
  23.110 +    }
  23.111 +
  23.112 +    @XmlAttribute
  23.113 +    public int getWhiteCount(){
  23.114 +        return whiteGames.size();
  23.115 +    }
  23.116 +
  23.117 +    @XmlAttribute
  23.118 +    public int getBlackCount(){
  23.119 +        return blackGames.size();
  23.120 +    }
  23.121 +
  23.122 +
  23.123 +    @XmlAttribute
  23.124 +    public boolean getChildrenNotEmpty(){
  23.125 +        return !entries.isEmpty();
  23.126 +    }
  23.127 +
  23.128 +    public boolean isEmpty(){
  23.129 +        return this.empty;
  23.130 +    }
  23.131 +
  23.132 +}
    24.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    24.2 +++ b/statistics/src/main/java/cz/xelfi/quoridor/statistics/OpeningNodeViewEntry.java	Thu Jan 07 23:20:33 2010 +0100
    24.3 @@ -0,0 +1,86 @@
    24.4 +/*
    24.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
    24.6 + *
    24.7 + * The contents of this file are subject to the terms of either the GNU
    24.8 + * General Public License Version 2 only ("GPL") or the Common
    24.9 + * Development and Distribution License("CDDL") (collectively, the
   24.10 + * "License"). You may not use this file except in compliance with the
   24.11 + * License. You can obtain a copy of the License at
   24.12 + * http://www.netbeans.org/cddl-gplv2.html
   24.13 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
   24.14 + * specific language governing permissions and limitations under the
   24.15 + * License.  When distributing the software, include this License Header
   24.16 + * Notice in each file and include the License file at
   24.17 + * nbbuild/licenses/CDDL-GPL-2-CP.  Sun designates this
   24.18 + * particular file as subject to the "Classpath" exception as provided
   24.19 + * by Sun in the GPL Version 2 section of the License file that
   24.20 + * accompanied this code. If applicable, add the following below the
   24.21 + * License Header, with the fields enclosed by brackets [] replaced by
   24.22 + * your own identifying information:
   24.23 + * "Portions Copyrighted [year] [name of copyright owner]"
   24.24 + *
   24.25 + * Contributor(s):
   24.26 + *
   24.27 + * Portions Copyrighted 2010 Martin Rexa
   24.28 + */
   24.29 +
   24.30 +package cz.xelfi.quoridor.statistics;
   24.31 +
   24.32 +import cz.xelfi.quoridor.Move;
   24.33 +import cz.xelfi.quoridor.webidor.GameId;
   24.34 +import cz.xelfi.quoridor.webidor.GameStatus;
   24.35 +import javax.xml.bind.annotation.XmlAccessType;
   24.36 +import javax.xml.bind.annotation.XmlAccessorType;
   24.37 +
   24.38 +/**
   24.39 + *
   24.40 + * @author Martin Rexa
   24.41 + */
   24.42 +@XmlAccessorType(XmlAccessType.FIELD)
   24.43 +public class OpeningNodeViewEntry extends Object{
   24.44 +    String move;
   24.45 +    String code;
   24.46 +    GameId gameId;
   24.47 +    GameId whiteGame;
   24.48 +    GameId blackGame;
   24.49 +    int whiteWon;
   24.50 +    int blackWon;
   24.51 +
   24.52 +    public OpeningNodeViewEntry(){
   24.53 +    }
   24.54 +
   24.55 +    public OpeningNodeViewEntry(Move m, String code){
   24.56 +        move = m.toString();
   24.57 +        this.code = code;
   24.58 +    }
   24.59 +
   24.60 +    public void processGame(GameId gId){
   24.61 +        gameId = gId;
   24.62 +        if(gId.getStatus().equals(GameStatus.whiteWon)){
   24.63 +            whiteWon++;
   24.64 +            whiteGame = gId;
   24.65 +        }
   24.66 +        if(gId.getStatus().equals(GameStatus.blackWon)){
   24.67 +            blackWon++;
   24.68 +            blackGame = gId;
   24.69 +        }
   24.70 +    }
   24.71 +
   24.72 +    @Override
   24.73 +    public boolean equals(Object obj){
   24.74 +        if (obj == null) {
   24.75 +            return false;
   24.76 +        }
   24.77 +        if (getClass() != obj.getClass()) {
   24.78 +            return false;
   24.79 +        }
   24.80 +        final OpeningNodeViewEntry other = (OpeningNodeViewEntry) obj;
   24.81 +        return this.move.equals(other.move);
   24.82 +    }
   24.83 +
   24.84 +    @Override
   24.85 +    public String toString(){
   24.86 +        return move+": "+whiteWon+"/"+blackWon;
   24.87 +    }
   24.88 +
   24.89 +}
    25.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    25.2 +++ b/statistics/src/main/java/cz/xelfi/quoridor/statistics/OpeningTree.java	Thu Jan 07 23:20:33 2010 +0100
    25.3 @@ -0,0 +1,107 @@
    25.4 +/*
    25.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
    25.6 + *
    25.7 + * The contents of this file are subject to the terms of either the GNU
    25.8 + * General Public License Version 2 only ("GPL") or the Common
    25.9 + * Development and Distribution License("CDDL") (collectively, the
   25.10 + * "License"). You may not use this file except in compliance with the
   25.11 + * License. You can obtain a copy of the License at
   25.12 + * http://www.netbeans.org/cddl-gplv2.html
   25.13 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
   25.14 + * specific language governing permissions and limitations under the
   25.15 + * License.  When distributing the software, include this License Header
   25.16 + * Notice in each file and include the License file at
   25.17 + * nbbuild/licenses/CDDL-GPL-2-CP.  Sun designates this
   25.18 + * particular file as subject to the "Classpath" exception as provided
   25.19 + * by Sun in the GPL Version 2 section of the License file that
   25.20 + * accompanied this code. If applicable, add the following below the
   25.21 + * License Header, with the fields enclosed by brackets [] replaced by
   25.22 + * your own identifying information:
   25.23 + * "Portions Copyrighted [year] [name of copyright owner]"
   25.24 + *
   25.25 + * Contributor(s):
   25.26 + *
   25.27 + * Portions Copyrighted 2010 Martin Rexa
   25.28 + */
   25.29 +
   25.30 +package cz.xelfi.quoridor.statistics;
   25.31 +
   25.32 +import cz.xelfi.quoridor.Board;
   25.33 +import cz.xelfi.quoridor.Move;
   25.34 +import cz.xelfi.quoridor.webidor.Game;
   25.35 +import cz.xelfi.quoridor.webidor.CommentedMove;
   25.36 +import cz.xelfi.quoridor.IllegalPositionException;
   25.37 +import java.util.Map;
   25.38 +import java.util.HashMap;
   25.39 +
   25.40 +/**
   25.41 + *
   25.42 + * @author Martin Rexa
   25.43 + */
   25.44 +public class OpeningTree {
   25.45 +    Map<String, OpeningTreeNode> nodes = new HashMap<String, OpeningTreeNode>();
   25.46 +    OpeningTreeNode root;
   25.47 +
   25.48 +    public OpeningTree(){
   25.49 +        root = new OpeningTreeNode("ROOT");
   25.50 +        nodes.put("ROOT", root);
   25.51 +    }
   25.52 +
   25.53 +    public OpeningTreeNode getRoot(){
   25.54 +        return root;
   25.55 +    }
   25.56 +
   25.57 +    public OpeningTreeNode getNode(String hashCode){
   25.58 +        return nodes.get(hashCode);
   25.59 +    }
   25.60 +
   25.61 +    public void processGame(Game game) throws IllegalPositionException{
   25.62 +        OpeningTreeNode node = root;
   25.63 +        OpeningTreeNode parentNode;
   25.64 +        OpeningTreeNode mirrorNode = root;
   25.65 +        OpeningTreeNode mirrorParentNode;
   25.66 +        Board board = Board.empty();
   25.67 +        Board mirrorBoard = Board.empty();
   25.68 +        root.addGame(game);
   25.69 +        for(CommentedMove move: game.getMoves()){
   25.70 +            if(!move.getMove().equals(Move.RESIGN)){
   25.71 +                parentNode = node;
   25.72 +                mirrorParentNode = mirrorNode;
   25.73 +                board = board.apply(move.getMove());
   25.74 +                mirrorBoard = mirrorBoard.apply(move.getMove().getMirrorMove());
   25.75 +                String hashCode = board.getCode();
   25.76 +                String mirrorHashCode = mirrorBoard.getCode();
   25.77 +                node = nodes.get(hashCode);
   25.78 +                if(node == null){
   25.79 +                    node = new OpeningTreeNode(hashCode);
   25.80 +                    nodes.put(hashCode, node);
   25.81 +                }
   25.82 +                node.addGame(game);
   25.83 +                mirrorNode = nodes.get(mirrorHashCode);
   25.84 +                if(mirrorHashCode.equals(hashCode)){
   25.85 +                    if(mirrorNode == null){
   25.86 +                        mirrorNode = node;
   25.87 +                    }
   25.88 +                }else{
   25.89 +                    if(mirrorNode == null){
   25.90 +                        mirrorNode = new OpeningTreeNode(mirrorHashCode);
   25.91 +                        nodes.put(mirrorHashCode, mirrorNode);
   25.92 +                    }
   25.93 +                    mirrorNode.addGame(game);
   25.94 +                }
   25.95 +                if(parentNode != null)
   25.96 +                    parentNode.addChild(move.getMove(),node);
   25.97 +                if(mirrorParentNode != null){
   25.98 +                    if(parentNode.equals(mirrorParentNode) && node.equals(mirrorNode))
   25.99 +                        ;
  25.100 +                    else
  25.101 +                        mirrorParentNode.addChild(move.getMove().getMirrorMove(),mirrorNode);
  25.102 +                }
  25.103 +            }
  25.104 +        }
  25.105 +        node.addFinishedGame(game);
  25.106 +        if(!node.equals(mirrorNode))
  25.107 +            mirrorNode.addFinishedGame(game);
  25.108 +    }
  25.109 +
  25.110 +}
    26.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    26.2 +++ b/statistics/src/main/java/cz/xelfi/quoridor/statistics/OpeningTreeNode.java	Thu Jan 07 23:20:33 2010 +0100
    26.3 @@ -0,0 +1,138 @@
    26.4 +/*
    26.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
    26.6 + *
    26.7 + * The contents of this file are subject to the terms of either the GNU
    26.8 + * General Public License Version 2 only ("GPL") or the Common
    26.9 + * Development and Distribution License("CDDL") (collectively, the
   26.10 + * "License"). You may not use this file except in compliance with the
   26.11 + * License. You can obtain a copy of the License at
   26.12 + * http://www.netbeans.org/cddl-gplv2.html
   26.13 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
   26.14 + * specific language governing permissions and limitations under the
   26.15 + * License.  When distributing the software, include this License Header
   26.16 + * Notice in each file and include the License file at
   26.17 + * nbbuild/licenses/CDDL-GPL-2-CP.  Sun designates this
   26.18 + * particular file as subject to the "Classpath" exception as provided
   26.19 + * by Sun in the GPL Version 2 section of the License file that
   26.20 + * accompanied this code. If applicable, add the following below the
   26.21 + * License Header, with the fields enclosed by brackets [] replaced by
   26.22 + * your own identifying information:
   26.23 + * "Portions Copyrighted [year] [name of copyright owner]"
   26.24 + *
   26.25 + * Contributor(s):
   26.26 + *
   26.27 + * Portions Copyrighted 2010 Martin Rexa
   26.28 + */
   26.29 +
   26.30 +package cz.xelfi.quoridor.statistics;
   26.31 +
   26.32 +import cz.xelfi.quoridor.webidor.Game;
   26.33 +import cz.xelfi.quoridor.webidor.GameId;
   26.34 +import cz.xelfi.quoridor.webidor.GameStatus;
   26.35 +import cz.xelfi.quoridor.webidor.User;
   26.36 +import cz.xelfi.quoridor.Move;
   26.37 +import java.util.HashMap;
   26.38 +import java.util.Map;
   26.39 +import java.util.List;
   26.40 +import java.util.ArrayList;
   26.41 +import javax.xml.bind.annotation.XmlAttribute;
   26.42 +
   26.43 +/**
   26.44 + *
   26.45 + * @author Martin Rexa
   26.46 + */
   26.47 +public class OpeningTreeNode {
   26.48 +    @XmlAttribute
   26.49 +    String hashCode;
   26.50 +    List<GameId> gameIds;
   26.51 +    List<GameId> finishedGames;
   26.52 +    Map<Move, OpeningTreeNode> children;
   26.53 +
   26.54 +    public OpeningTreeNode(){
   26.55 +    }
   26.56 +
   26.57 +    public OpeningTreeNode(String hashCode){
   26.58 +        this.hashCode = hashCode;
   26.59 +        gameIds = new ArrayList<GameId>();
   26.60 +        finishedGames = new ArrayList<GameId>();
   26.61 +        children = new HashMap<Move,OpeningTreeNode>();
   26.62 +    }
   26.63 +
   26.64 +    public OpeningTreeNode addGame(Game game){
   26.65 +        gameIds.add(game.getId());
   26.66 +        return this;
   26.67 +    }
   26.68 +
   26.69 +    public OpeningTreeNode addFinishedGame(Game game){
   26.70 +        finishedGames.add(game.getId());
   26.71 +        return this;
   26.72 +    }
   26.73 +
   26.74 +    public OpeningTreeNode addGameId(GameId gameId){
   26.75 +        gameIds.add(gameId);
   26.76 +        return this;
   26.77 +    }
   26.78 +
   26.79 +    public OpeningTreeNode addChild(Move move, OpeningTreeNode child){
   26.80 +        children.put(move, child);
   26.81 +        return this;
   26.82 +    }
   26.83 +
   26.84 +    public OpeningNodeView getView(User user){
   26.85 +        return getView(user.getId());
   26.86 +    }
   26.87 +
   26.88 +    public OpeningNodeView getView(String userId){
   26.89 +        OpeningNodeView view = new OpeningNodeView(hashCode);
   26.90 +        for(Map.Entry<Move, OpeningTreeNode> e: children.entrySet()){
   26.91 +            for(GameId gId: e.getValue().gameIds){
   26.92 +                if(User.canSee(gId, userId)){
   26.93 +                    view.processGame(e.getKey(), e.getValue().hashCode, gId);
   26.94 +                }
   26.95 +            }
   26.96 +        }
   26.97 +        for(GameId gId: finishedGames){
   26.98 +            view.addFinishedGame(gId);
   26.99 +        }
  26.100 +        return view;
  26.101 +    }
  26.102 +
  26.103 +    public List<GameId> filterGames(String userId, GameStatus status){
  26.104 +        List<GameId> result = new ArrayList<GameId>();
  26.105 +        for(GameId gId: gameIds){
  26.106 +            if(User.canSee(gId, userId))
  26.107 +                if(gId.getStatus().equals(status))
  26.108 +                    result.add(gId);
  26.109 +        }
  26.110 +        return result;
  26.111 +    }
  26.112 +
  26.113 +    public List<GameId> getGameIds(){
  26.114 +        return gameIds;
  26.115 +    }
  26.116 +
  26.117 +//    @XmlAttribute
  26.118 +//    @XmlElement
  26.119 +    public String getHashCode(){
  26.120 +        return this.hashCode;
  26.121 +    }
  26.122 +
  26.123 +
  26.124 +    @Override
  26.125 +    public boolean equals(Object obj) {
  26.126 +        if (obj == null) {
  26.127 +            return false;
  26.128 +        }
  26.129 +        if (getClass() != obj.getClass()) {
  26.130 +            return false;
  26.131 +        }
  26.132 +        OpeningTreeNode other = (OpeningTreeNode)obj;
  26.133 +        return hashCode.equals(other.hashCode);
  26.134 +    }
  26.135 +
  26.136 +    @Override
  26.137 +    public int hashCode() {
  26.138 +        return hashCode.hashCode();
  26.139 +    }
  26.140 +
  26.141 +}
    27.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    27.2 +++ b/statistics/src/main/java/cz/xelfi/quoridor/statistics/resources/Elo.java	Thu Jan 07 23:20:33 2010 +0100
    27.3 @@ -0,0 +1,75 @@
    27.4 +/*
    27.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
    27.6 + *
    27.7 + * The contents of this file are subject to the terms of either the GNU
    27.8 + * General Public License Version 2 only ("GPL") or the Common
    27.9 + * Development and Distribution License("CDDL") (collectively, the
   27.10 + * "License"). You may not use this file except in compliance with the
   27.11 + * License. You can obtain a copy of the License at
   27.12 + * http://www.netbeans.org/cddl-gplv2.html
   27.13 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
   27.14 + * specific language governing permissions and limitations under the
   27.15 + * License.  When distributing the software, include this License Header
   27.16 + * Notice in each file and include the License file at
   27.17 + * nbbuild/licenses/CDDL-GPL-2-CP.  Sun designates this
   27.18 + * particular file as subject to the "Classpath" exception as provided
   27.19 + * by Sun in the GPL Version 2 section of the License file that
   27.20 + * accompanied this code. If applicable, add the following below the
   27.21 + * License Header, with the fields enclosed by brackets [] replaced by
   27.22 + * your own identifying information:
   27.23 + * "Portions Copyrighted [year] [name of copyright owner]"
   27.24 + *
   27.25 + * Contributor(s):
   27.26 + *
   27.27 + * Portions Copyrighted 2010 Martin Rexa
   27.28 + */
   27.29 +
   27.30 +package cz.xelfi.quoridor.statistics.resources;
   27.31 +
   27.32 +import cz.xelfi.quoridor.statistics.EloList;
   27.33 +import cz.xelfi.quoridor.webidor.Game;
   27.34 +import cz.xelfi.quoridor.webidor.GameId;
   27.35 +import cz.xelfi.quoridor.webidor.GameStatus;
   27.36 +import javax.ws.rs.GET;
   27.37 +import javax.ws.rs.Path;
   27.38 +import javax.ws.rs.PathParam;
   27.39 +import javax.ws.rs.Produces;
   27.40 +import javax.ws.rs.core.MediaType;
   27.41 +
   27.42 +/**
   27.43 + *
   27.44 + * @author Martin Rexa
   27.45 + */
   27.46 +public final class Elo {
   27.47 +    private EloList list;
   27.48 +
   27.49 +    public Elo(){
   27.50 +        list = new EloList();
   27.51 +    }
   27.52 +
   27.53 +    public void processGame(Game game){
   27.54 +        GameId gId = game.getId();
   27.55 +        GameStatus status = gId.getStatus();
   27.56 +        if(status.equals(GameStatus.whiteWon)){
   27.57 +            list.putResult(gId.getWhite(), gId.getBlack());
   27.58 +        }else if(status.equals(GameStatus.blackWon)){
   27.59 +            list.putResult(gId.getBlack(), gId.getWhite());
   27.60 +        }
   27.61 +    }
   27.62 +
   27.63 +    @GET
   27.64 +    @Path("list")
   27.65 +    @Produces({ MediaType.APPLICATION_JSON, MediaType.TEXT_XML })
   27.66 +    public EloList getList(){
   27.67 +        return list;
   27.68 +    }
   27.69 +
   27.70 +    @GET
   27.71 +    @Path("{username}")
   27.72 +    @Produces(MediaType.TEXT_PLAIN)
   27.73 +    public String getElo(@PathParam("username") String id){
   27.74 +        list.getElo(id);
   27.75 +        return Double.toString(list.getElo(id));
   27.76 +    }
   27.77 +
   27.78 +}
    28.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    28.2 +++ b/statistics/src/main/java/cz/xelfi/quoridor/statistics/resources/Openings.java	Thu Jan 07 23:20:33 2010 +0100
    28.3 @@ -0,0 +1,107 @@
    28.4 +/*
    28.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
    28.6 + *
    28.7 + * The contents of this file are subject to the terms of either the GNU
    28.8 + * General Public License Version 2 only ("GPL") or the Common
    28.9 + * Development and Distribution License("CDDL") (collectively, the
   28.10 + * "License"). You may not use this file except in compliance with the
   28.11 + * License. You can obtain a copy of the License at
   28.12 + * http://www.netbeans.org/cddl-gplv2.html
   28.13 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
   28.14 + * specific language governing permissions and limitations under the
   28.15 + * License.  When distributing the software, include this License Header
   28.16 + * Notice in each file and include the License file at
   28.17 + * nbbuild/licenses/CDDL-GPL-2-CP.  Sun designates this
   28.18 + * particular file as subject to the "Classpath" exception as provided
   28.19 + * by Sun in the GPL Version 2 section of the License file that
   28.20 + * accompanied this code. If applicable, add the following below the
   28.21 + * License Header, with the fields enclosed by brackets [] replaced by
   28.22 + * your own identifying information:
   28.23 + * "Portions Copyrighted [year] [name of copyright owner]"
   28.24 + *
   28.25 + * Contributor(s):
   28.26 + *
   28.27 + * Portions Copyrighted 2010 Martin Rexa
   28.28 + */
   28.29 +
   28.30 +package cz.xelfi.quoridor.statistics.resources;
   28.31 +
   28.32 +import cz.xelfi.quoridor.IllegalPositionException;
   28.33 +import cz.xelfi.quoridor.webidor.Game;
   28.34 +import cz.xelfi.quoridor.webidor.GameId;
   28.35 +import cz.xelfi.quoridor.statistics.OpeningTree;
   28.36 +import cz.xelfi.quoridor.statistics.OpeningTreeNode;
   28.37 +import cz.xelfi.quoridor.statistics.OpeningNodeView;
   28.38 +import cz.xelfi.quoridor.webidor.GameStatus;
   28.39 +import java.util.List;
   28.40 +import javax.ws.rs.GET;
   28.41 +import javax.ws.rs.Path;
   28.42 +import javax.ws.rs.PathParam;
   28.43 +import javax.ws.rs.Produces;
   28.44 +import javax.ws.rs.QueryParam;
   28.45 +import javax.ws.rs.DefaultValue;
   28.46 +import javax.ws.rs.core.MediaType;
   28.47 +
   28.48 +/**
   28.49 + *
   28.50 + * @author Martin Rexa
   28.51 + */
   28.52 +public class Openings {
   28.53 +    OpeningTree tree;
   28.54 +
   28.55 +    public Openings(){
   28.56 +        tree = new OpeningTree();
   28.57 +    }
   28.58 +
   28.59 +    public void processGame(Game game) {
   28.60 +        try{
   28.61 +            tree.processGame(game);
   28.62 +        }catch (IllegalPositionException e){
   28.63 +            e.printStackTrace();
   28.64 +        }
   28.65 +    }
   28.66 +
   28.67 +    @GET
   28.68 +    @Path("{nodeId}")
   28.69 +    @Produces({ MediaType.APPLICATION_JSON, MediaType.TEXT_XML })
   28.70 +    public OpeningNodeView getNode(@QueryParam("loginID") @DefaultValue("") String loginId,
   28.71 +                         @PathParam("nodeId") String nodeId){
   28.72 +        OpeningTreeNode node = tree.getNode(nodeId);
   28.73 +        if(node == null)
   28.74 +            node = tree.getRoot();
   28.75 +        return node.getView(loginId);
   28.76 +    }
   28.77 +
   28.78 +    @GET
   28.79 +    @Path("{nodeId}.check")
   28.80 +    @Produces({ MediaType.TEXT_PLAIN })
   28.81 +    public String checkNode(@QueryParam("loginID") @DefaultValue("") String loginId,
   28.82 +                         @PathParam("nodeId") String nodeId){
   28.83 +        OpeningTreeNode node = tree.getNode(nodeId);
   28.84 +        if(node == null)
   28.85 +            return "";
   28.86 +        if(node.getView(loginId).isEmpty())
   28.87 +            return "";
   28.88 +        else
   28.89 +            return nodeId;
   28.90 +    }
   28.91 +
   28.92 +    @GET
   28.93 +    @Path("{nodeId}/{status}")
   28.94 +    @Produces({MediaType.APPLICATION_JSON, MediaType.TEXT_XML })
   28.95 +    public List<GameId> listGames(
   28.96 +        @DefaultValue("") @QueryParam("loginID") String loginId,
   28.97 +        @PathParam("nodeId") String nodeId,
   28.98 +        @DefaultValue("") @PathParam("status") String status
   28.99 +    ) {
  28.100 +        OpeningTreeNode node = tree.getNode(nodeId);
  28.101 +        if(node == null)
  28.102 +            return null;
  28.103 +        if("white".equals(status))
  28.104 +            return node.filterGames(loginId, GameStatus.whiteWon);
  28.105 +        else if("black".equals(status))
  28.106 +            return node.filterGames(loginId, GameStatus.blackWon);
  28.107 +        else
  28.108 +            return null;
  28.109 +    }
  28.110 +}
    29.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    29.2 +++ b/statistics/src/main/java/cz/xelfi/quoridor/statistics/resources/Statistics.java	Thu Jan 07 23:20:33 2010 +0100
    29.3 @@ -0,0 +1,156 @@
    29.4 +/*
    29.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
    29.6 + *
    29.7 + * The contents of this file are subject to the terms of either the GNU
    29.8 + * General Public License Version 2 only ("GPL") or the Common
    29.9 + * Development and Distribution License("CDDL") (collectively, the
   29.10 + * "License"). You may not use this file except in compliance with the
   29.11 + * License. You can obtain a copy of the License at
   29.12 + * http://www.netbeans.org/cddl-gplv2.html
   29.13 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
   29.14 + * specific language governing permissions and limitations under the
   29.15 + * License.  When distributing the software, include this License Header
   29.16 + * Notice in each file and include the License file at
   29.17 + * nbbuild/licenses/CDDL-GPL-2-CP.  Sun designates this
   29.18 + * particular file as subject to the "Classpath" exception as provided
   29.19 + * by Sun in the GPL Version 2 section of the License file that
   29.20 + * accompanied this code. If applicable, add the following below the
   29.21 + * License Header, with the fields enclosed by brackets [] replaced by
   29.22 + * your own identifying information:
   29.23 + * "Portions Copyrighted [year] [name of copyright owner]"
   29.24 + *
   29.25 + * Contributor(s):
   29.26 + *
   29.27 + * Portions Copyrighted 2010 Martin Rexa
   29.28 + */
   29.29 +
   29.30 +package cz.xelfi.quoridor.statistics.resources;
   29.31 +
   29.32 +import com.sun.jersey.api.container.httpserver.HttpServerFactory;
   29.33 +import com.sun.jersey.api.core.PackagesResourceConfig;
   29.34 +import com.sun.jersey.api.core.ResourceConfig;
   29.35 +import com.sun.jersey.spi.resource.Singleton;
   29.36 +import com.sun.net.httpserver.HttpServer;
   29.37 +import java.io.File;
   29.38 +import java.io.IOException;
   29.39 +import java.util.Comparator;
   29.40 +import java.util.List;
   29.41 +import java.util.Collections;
   29.42 +import java.net.ServerSocket;
   29.43 +import javax.ws.rs.Path;
   29.44 +import cz.xelfi.quoridor.webidor.resources.Games;
   29.45 +import cz.xelfi.quoridor.webidor.Game;
   29.46 +
   29.47 +/**
   29.48 + *
   29.49 + * @author Martin Rexa
   29.50 + */
   29.51 +@Path("/")
   29.52 +@Singleton
   29.53 +public class Statistics {
   29.54 +    static final Comparator<Game> OLDEST_FIRST = new OldestFirst();
   29.55 +    static Elo elo;
   29.56 +    static Openings openings;
   29.57 +
   29.58 +    @Path("elo")
   29.59 +    public Elo getElo() {
   29.60 +        return elo;
   29.61 +    }
   29.62 +
   29.63 +    @Path("openings")
   29.64 +    public Openings getOpenings() {
   29.65 +        return openings;
   29.66 +    }
   29.67 +
   29.68 +    public static void main( String[] args ) throws IOException, InterruptedException
   29.69 +    {
   29.70 +        int port = 9444;
   29.71 +        // timeout between reprocessing games in miliseconds
   29.72 +//        long timeout = 1000 * 60 * 60;
   29.73 +        long timeout = 1000 * 10;
   29.74 +
   29.75 +        try {
   29.76 +            port = Integer.parseInt(args[0]);
   29.77 +        } catch (Exception ex) {
   29.78 +            // OK
   29.79 +        }
   29.80 +
   29.81 +        if (System.getProperty("quoridor.dir") == null) {
   29.82 +            File home = new File(System.getProperty("user.home"));
   29.83 +            File quoridor = new File(home, ".quoridor");
   29.84 +            System.setProperty("quoridor.dir", quoridor.getPath());
   29.85 +        }
   29.86 +
   29.87 +        processGames();
   29.88 +        HttpServer s = start(port);
   29.89 +        System.out.println("Statistics started at port " + port);
   29.90 +        Object monitor = new Object();
   29.91 +        for(;;){
   29.92 +            synchronized (monitor) {
   29.93 +                monitor.wait(timeout);
   29.94 +            }
   29.95 +            processGames();
   29.96 +        }
   29.97 +    }
   29.98 +
   29.99 +    public static HttpServer start(int port) throws IOException {
  29.100 +        if (port == -1) {
  29.101 +            ServerSocket ss = new ServerSocket(0);
  29.102 +            port =ss.getLocalPort();
  29.103 +            ss.close();
  29.104 +        }
  29.105 +        final String baseUri = "http://localhost:" + port + "/";
  29.106 +
  29.107 +        if (System.getProperty("quoridor.dir") == null) {
  29.108 +            File home = new File(System.getProperty("user.home"));
  29.109 +            File quoridor = new File(home, ".quoridor");
  29.110 +            System.setProperty("quoridor.dir", quoridor.getPath());
  29.111 +        }
  29.112 +
  29.113 +        ResourceConfig rc = new PackagesResourceConfig("cz.xelfi.quoridor.statistics");
  29.114 +        HttpServer server = HttpServerFactory.create(baseUri, rc);
  29.115 +        server.start();
  29.116 +        return server;
  29.117 +    }
  29.118 +
  29.119 +    public static void processGames(){
  29.120 +        Elo eloTmp = new Elo();
  29.121 +        Openings openingsTmp = new Openings();
  29.122 +
  29.123 +        final String prop = System.getProperty("quoridor.dir"); // NOI18N
  29.124 +        if (prop == null) {
  29.125 +            throw new IllegalStateException("quoridor.dir property must be specified"); // NOI18N
  29.126 +        }
  29.127 +        File path = new File(prop);
  29.128 +        path.mkdirs();
  29.129 +        File fGames = new File(path, "games");
  29.130 +        //fGames.mkdirs();
  29.131 +        Games games = new Games(fGames, null);
  29.132 +        List<Game> lGames = games.getGames();
  29.133 +        Collections.sort(lGames, OLDEST_FIRST);
  29.134 +
  29.135 +        // process games
  29.136 +        for(Game g: lGames){
  29.137 +            if(g.getId().isFinished()){
  29.138 +                eloTmp.processGame(g);
  29.139 +                openingsTmp.processGame(g);
  29.140 +            }
  29.141 +        }
  29.142 +
  29.143 +        elo = eloTmp;
  29.144 +        openings = openingsTmp;
  29.145 +    }
  29.146 +
  29.147 +    private static final class OldestFirst implements Comparator<Game> {
  29.148 +        public int compare(Game g1, Game g2) {
  29.149 +            if(g1.getId() == g2.getId())
  29.150 +                return 0;
  29.151 +            long diff = g2.getId().getModified() - g1.getId().getModified();
  29.152 +            if (diff != 0) {
  29.153 +                return diff < 0 ? -1 : 1;
  29.154 +            }
  29.155 +            return g1.getId().getId().compareTo(g2.getId().getId());
  29.156 +        }
  29.157 +    }
  29.158 +
  29.159 +}
    30.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    30.2 +++ b/statistics/src/test/java/cz/xelfi/quoridor/statistics/EloTest.java	Thu Jan 07 23:20:33 2010 +0100
    30.3 @@ -0,0 +1,53 @@
    30.4 +/*
    30.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
    30.6 + *
    30.7 + * The contents of this file are subject to the terms of either the GNU
    30.8 + * General Public License Version 2 only ("GPL") or the Common
    30.9 + * Development and Distribution License("CDDL") (collectively, the
   30.10 + * "License"). You may not use this file except in compliance with the
   30.11 + * License. You can obtain a copy of the License at
   30.12 + * http://www.netbeans.org/cddl-gplv2.html
   30.13 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
   30.14 + * specific language governing permissions and limitations under the
   30.15 + * License.  When distributing the software, include this License Header
   30.16 + * Notice in each file and include the License file at
   30.17 + * nbbuild/licenses/CDDL-GPL-2-CP.  Sun designates this
   30.18 + * particular file as subject to the "Classpath" exception as provided
   30.19 + * by Sun in the GPL Version 2 section of the License file that
   30.20 + * accompanied this code. If applicable, add the following below the
   30.21 + * License Header, with the fields enclosed by brackets [] replaced by
   30.22 + * your own identifying information:
   30.23 + * "Portions Copyrighted [year] [name of copyright owner]"
   30.24 + *
   30.25 + * Contributor(s):
   30.26 + *
   30.27 + * Portions Copyrighted 2010 Martin Rexa
   30.28 + */
   30.29 +
   30.30 +package cz.xelfi.quoridor.statistics;
   30.31 +
   30.32 +import org.junit.Test;
   30.33 +import static org.junit.Assert.*;
   30.34 +
   30.35 +/**
   30.36 + *
   30.37 + * @author Martin Rexa
   30.38 + */
   30.39 +public class EloTest extends Object {
   30.40 +
   30.41 +    public EloTest() throws Exception {
   30.42 +    }
   30.43 +
   30.44 +    @Test public void testEloList() throws Exception {
   30.45 +        EloList eloList = new EloList();
   30.46 +        eloList.putResult("p1", "p2");
   30.47 +        assertFalse("empty list", eloList.getFinalList().isEmpty());
   30.48 +        eloList.putResult("p3", "p4");
   30.49 +        assertTrue("wrong list", eloList.getFinalList().get(0).getElo().equals(eloList.getFinalList().get(1).getElo()));
   30.50 +        eloList.putResult("p2", "p1");
   30.51 +        eloList.putResult("p2", "p1");
   30.52 +        eloList.putResult("p2", "p1");
   30.53 +        assertTrue("wrong list", eloList.getFinalList().get(0).player.equals("p2"));
   30.54 +    }
   30.55 +
   30.56 +}
    31.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    31.2 +++ b/statistics/src/test/java/cz/xelfi/quoridor/statistics/OpeningsTest.java	Thu Jan 07 23:20:33 2010 +0100
    31.3 @@ -0,0 +1,129 @@
    31.4 +/*
    31.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
    31.6 + *
    31.7 + * The contents of this file are subject to the terms of either the GNU
    31.8 + * General Public License Version 2 only ("GPL") or the Common
    31.9 + * Development and Distribution License("CDDL") (collectively, the
   31.10 + * "License"). You may not use this file except in compliance with the
   31.11 + * License. You can obtain a copy of the License at
   31.12 + * http://www.netbeans.org/cddl-gplv2.html
   31.13 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
   31.14 + * specific language governing permissions and limitations under the
   31.15 + * License.  When distributing the software, include this License Header
   31.16 + * Notice in each file and include the License file at
   31.17 + * nbbuild/licenses/CDDL-GPL-2-CP.  Sun designates this
   31.18 + * particular file as subject to the "Classpath" exception as provided
   31.19 + * by Sun in the GPL Version 2 section of the License file that
   31.20 + * accompanied this code. If applicable, add the following below the
   31.21 + * License Header, with the fields enclosed by brackets [] replaced by
   31.22 + * your own identifying information:
   31.23 + * "Portions Copyrighted [year] [name of copyright owner]"
   31.24 + *
   31.25 + * Contributor(s):
   31.26 + *
   31.27 + * Portions Copyrighted 2010 Martin Rexa
   31.28 + */
   31.29 +
   31.30 +package cz.xelfi.quoridor.statistics;
   31.31 +
   31.32 +import cz.xelfi.quoridor.Fence.Orientation;
   31.33 +import org.junit.Test;
   31.34 +import static org.junit.Assert.*;
   31.35 +import cz.xelfi.quoridor.Move;
   31.36 +import cz.xelfi.quoridor.webidor.Game;
   31.37 +import java.util.Map;
   31.38 +
   31.39 +/**
   31.40 + *
   31.41 + * @author Martin Rexa
   31.42 + */
   31.43 +public class OpeningsTest extends Object {
   31.44 +    
   31.45 +    public OpeningsTest() throws Exception {
   31.46 +    }
   31.47 +
   31.48 +    @Test public void testMirrorMoves() throws Exception {
   31.49 +        assertEquals("bad symetric move", Move.NORTH.getMirrorMove(), Move.NORTH);
   31.50 +        assertEquals("bad symetric move", Move.SOUTH.getMirrorMove(), Move.SOUTH);
   31.51 +        assertEquals("bad symetric move", Move.WEST.getMirrorMove(), Move.EAST);
   31.52 +        assertEquals("bad symetric move", Move.EAST.getMirrorMove(), Move.WEST);
   31.53 +        assertEquals("bad symetric move", Move.fence('A', 1, Orientation.HORIZONTAL).getMirrorMove(), Move.fence('H', 1, Orientation.HORIZONTAL));
   31.54 +        assertEquals("bad symetric move", Move.fence('D', 3, Orientation.VERTICAL).getMirrorMove(), Move.fence('E', 3, Orientation.VERTICAL));
   31.55 +    }
   31.56 +
   31.57 +    @Test public void testMirrorJump() throws Exception {
   31.58 +        Game g = new Game("w","b");
   31.59 +        g.apply("w", Move.NORTH, new java.util.Date());
   31.60 +        g.apply("b", Move.SOUTH, new java.util.Date());
   31.61 +        g.apply("w", Move.NORTH, new java.util.Date());
   31.62 +        g.apply("b", Move.SOUTH, new java.util.Date());
   31.63 +        g.apply("w", Move.NORTH, new java.util.Date());
   31.64 +        g.apply("b", Move.SOUTH, new java.util.Date());
   31.65 +        g.apply("w", Move.NORTH, new java.util.Date());
   31.66 +        g.apply("b", Move.EAST, new java.util.Date());
   31.67 +        g.apply("w", Move.NORTH, new java.util.Date());
   31.68 +        g.apply("b", Move.valueOf("WW"), new java.util.Date());
   31.69 +        g.apply("w", Move.RESIGN, new java.util.Date());
   31.70 +        OpeningTree t = new OpeningTree();
   31.71 +        t.processGame(g);
   31.72 +        OpeningTreeNode n = t.root;
   31.73 +        n = n.children.get(Move.NORTH);
   31.74 +        n = n.children.get(Move.SOUTH);
   31.75 +        n = n.children.get(Move.NORTH);
   31.76 +        n = n.children.get(Move.SOUTH);
   31.77 +        n = n.children.get(Move.NORTH);
   31.78 +        n = n.children.get(Move.SOUTH);
   31.79 +        n = n.children.get(Move.NORTH);
   31.80 +        n = n.children.get(Move.WEST);
   31.81 +        n = n.children.get(Move.NORTH);
   31.82 +        n = n.children.get(Move.valueOf("EE"));
   31.83 +        assertEquals("bad number of children", n.children.size(), 0);
   31.84 +        for(Map.Entry e: n.children.entrySet()){
   31.85 +            System.out.println(e.getKey());
   31.86 +        }
   31.87 +    }
   31.88 +
   31.89 +    @Test public void testOpeningTreeNodes() throws Exception {
   31.90 +        OpeningTree t = new OpeningTree();
   31.91 +        assertEquals("bad number of nodes", t.nodes.size(), 1);
   31.92 +        Game g = new Game("w","b");
   31.93 +        g.apply("w", Move.NORTH, new java.util.Date());
   31.94 +        g.apply("b", Move.SOUTH, new java.util.Date());
   31.95 +        g.apply("w", Move.EAST, new java.util.Date());
   31.96 +        g.apply("b", Move.EAST, new java.util.Date());
   31.97 +        g.apply("w", Move.WEST, new java.util.Date());
   31.98 +        g.apply("b", Move.WEST, new java.util.Date());
   31.99 +        g.apply("w", Move.RESIGN, new java.util.Date());
  31.100 +        t.processGame(g);
  31.101 +        assertEquals("bad number of nodes", t.nodes.size(), 9);
  31.102 +        g = new Game("w","b");
  31.103 +        g.apply("w", Move.NORTH, new java.util.Date());
  31.104 +        g.apply("b", Move.SOUTH, new java.util.Date());
  31.105 +        g.apply("w", Move.fence('A', 1, Orientation.HORIZONTAL), new java.util.Date());
  31.106 +        g.apply("b", Move.fence('C', 1, Orientation.VERTICAL), new java.util.Date());
  31.107 +        g.apply("w", Move.RESIGN, new java.util.Date());
  31.108 +        t.processGame(g);
  31.109 +        assertEquals("bad number of nodes", t.nodes.size(), 13);
  31.110 +        g = new Game("w","b");
  31.111 +        g.apply("w", Move.NORTH, new java.util.Date());
  31.112 +        g.apply("b", Move.SOUTH, new java.util.Date());
  31.113 +        g.apply("w", Move.fence('C', 1, Orientation.VERTICAL), new java.util.Date());
  31.114 +        g.apply("b", Move.fence('A', 1, Orientation.HORIZONTAL), new java.util.Date());
  31.115 +        g.apply("w", Move.RESIGN, new java.util.Date());
  31.116 +        t.processGame(g);
  31.117 +        assertEquals("bad number of nodes", t.nodes.size(), 15);
  31.118 +        OpeningTreeNode n = t.root;
  31.119 +        n = n.children.get(Move.NORTH);
  31.120 +        n = n.children.get(Move.SOUTH);
  31.121 +        OpeningTreeNode n1 = n;
  31.122 +        n = n.children.get(Move.WEST);
  31.123 +        n = n.children.get(Move.WEST);
  31.124 +        n = n.children.get(Move.EAST);
  31.125 +        n = n.children.get(Move.EAST);
  31.126 +        assertEquals("different nodes", n.hashCode, n1.hashCode);
  31.127 +        n = n.children.get(Move.fence('F', 1, Orientation.VERTICAL));
  31.128 +        n = n.children.get(Move.fence('H', 1, Orientation.HORIZONTAL));
  31.129 +        assertEquals("bad number of children", n.children.size(), 0);
  31.130 +    }
  31.131 +
  31.132 +}
    32.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    32.2 +++ b/statistics/src/test/java/cz/xelfi/quoridor/statistics/resources/StatisticsTest.java	Thu Jan 07 23:20:33 2010 +0100
    32.3 @@ -0,0 +1,131 @@
    32.4 +/*
    32.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
    32.6 + *
    32.7 + * The contents of this file are subject to the terms of either the GNU
    32.8 + * General Public License Version 2 only ("GPL") or the Common
    32.9 + * Development and Distribution License("CDDL") (collectively, the
   32.10 + * "License"). You may not use this file except in compliance with the
   32.11 + * License. You can obtain a copy of the License at
   32.12 + * http://www.netbeans.org/cddl-gplv2.html
   32.13 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
   32.14 + * specific language governing permissions and limitations under the
   32.15 + * License.  When distributing the software, include this License Header
   32.16 + * Notice in each file and include the License file at
   32.17 + * nbbuild/licenses/CDDL-GPL-2-CP.  Sun designates this
   32.18 + * particular file as subject to the "Classpath" exception as provided
   32.19 + * by Sun in the GPL Version 2 section of the License file that
   32.20 + * accompanied this code. If applicable, add the following below the
   32.21 + * License Header, with the fields enclosed by brackets [] replaced by
   32.22 + * your own identifying information:
   32.23 + * "Portions Copyrighted [year] [name of copyright owner]"
   32.24 + *
   32.25 + * Contributor(s):
   32.26 + *
   32.27 + * Portions Copyrighted 2010 Martin Rexa
   32.28 + */
   32.29 +
   32.30 +package cz.xelfi.quoridor.statistics.resources;
   32.31 +
   32.32 +import com.sun.jersey.api.client.Client;
   32.33 +import com.sun.jersey.api.client.WebResource;
   32.34 +import com.sun.jersey.core.header.MediaTypes;
   32.35 +import com.sun.net.httpserver.HttpServer;
   32.36 +import java.io.File;
   32.37 +import java.io.FileOutputStream;
   32.38 +import java.io.IOException;
   32.39 +import java.net.URI;
   32.40 +import java.util.Locale;
   32.41 +import javax.ws.rs.core.MediaType;
   32.42 +import org.junit.AfterClass;
   32.43 +import org.junit.Before;
   32.44 +import org.junit.BeforeClass;
   32.45 +import org.junit.Test;
   32.46 +import static org.junit.Assert.*;
   32.47 +import cz.xelfi.quoridor.statistics.EloList;
   32.48 +/**
   32.49 + *
   32.50 + * @author Martin Rexa
   32.51 + */
   32.52 +public class StatisticsTest {
   32.53 +    private static File dir;
   32.54 +    private static File fGames;
   32.55 +    private static HttpServer stopAPI;
   32.56 +    private WebResource apiResource;
   32.57 +
   32.58 +    public StatisticsTest() throws Exception {
   32.59 +    }
   32.60 +
   32.61 +    @BeforeClass
   32.62 +    public static void localeEnglish() throws Exception {
   32.63 +        Locale.setDefault(Locale.ENGLISH);
   32.64 +        dir = File.createTempFile("quoridor", ".dir");
   32.65 +        dir.delete();
   32.66 +        dir.mkdirs();
   32.67 +        fGames = new File(dir, "games");
   32.68 +        fGames.mkdirs();
   32.69 +
   32.70 +        System.setProperty("quoridor.dir", dir.getPath());
   32.71 +        stopAPI = Statistics.start(9990);
   32.72 +
   32.73 +        File passwd = new File(dir, "passwd");
   32.74 +        FileOutputStream os = new FileOutputStream(passwd);
   32.75 +        os.write("test=pes\nJarda=darda\n".getBytes("UTF-8"));
   32.76 +        os.close();
   32.77 +    }
   32.78 +
   32.79 +    @Before
   32.80 +    public void setUp() throws Exception {
   32.81 +        Client client = new Client();
   32.82 +        apiResource = client.resource(new URI("http://localhost:9990/"));
   32.83 +    }
   32.84 +
   32.85 +    @AfterClass
   32.86 +    public static void cleanUpAll() throws Exception {
   32.87 +        deleteRec(dir);
   32.88 +        if (stopAPI != null) {
   32.89 +            stopAPI.stop(0);
   32.90 +        }
   32.91 +    }
   32.92 +
   32.93 +    static void deleteRec(File dir) throws IOException {
   32.94 +        if (dir == null) {
   32.95 +            return;
   32.96 +        }
   32.97 +        File[] arr = dir.listFiles();
   32.98 +        if (arr != null) {
   32.99 +            for (File f : arr) {
  32.100 +                deleteRec(f);
  32.101 +            }
  32.102 +        }
  32.103 +        dir.delete();
  32.104 +    }
  32.105 +
  32.106 +    @Test public void testApplicationWadl() {
  32.107 +        String serviceWadl = apiResource.path("application.wadl").
  32.108 +                accept(MediaTypes.WADL).get(String.class);
  32.109 +        assertTrue(serviceWadl.length() > 0);
  32.110 +    }
  32.111 +
  32.112 +    @Test public void testEloList() throws Exception {
  32.113 +        createFile(fGames, "g1", "# white: W\n# black: B\n# status: IN_PROGRESS\nN S\n\n");
  32.114 +        createFile(fGames, "g2", "# white: W\n# black: B\n# status: blackWon\nRESIGN\n\n");
  32.115 +        createFile(fGames, "g3", "# white: W\n# black: B\n# status: blackWon\nRESIGN\n\n");
  32.116 +        createFile(fGames, "g4", "# white: W\n# black: B\n# status: blackWon\nRESIGN\n\n");
  32.117 +        Statistics.processGames();
  32.118 +
  32.119 +        EloList eloList = apiResource.path("elo").path("list").accept(MediaType.TEXT_XML).get(EloList.class);
  32.120 +//        assertFalse(eloList.getFinalList().isEmpty());
  32.121 +//        System.out.println("ELO LIST");
  32.122 +//        for (EloEntry entry : eloList.getFinalList()){
  32.123 +//            System.out.println(entry);
  32.124 +//        }
  32.125 +    }
  32.126 +
  32.127 +    private void createFile(File dir, String filename, String content) throws Exception{
  32.128 +        File f = new File(dir, filename);
  32.129 +        FileOutputStream os = new FileOutputStream(f);
  32.130 +        os.write(content.getBytes("UTF-8"));
  32.131 +        os.close();
  32.132 +    }
  32.133 +
  32.134 +}
    33.1 --- a/visidor/src/main/java/cz/xelfi/quoridor/visidor/Viewer.java	Fri Jan 01 20:53:17 2010 +0100
    33.2 +++ b/visidor/src/main/java/cz/xelfi/quoridor/visidor/Viewer.java	Thu Jan 07 23:20:33 2010 +0100
    33.3 @@ -1,8 +1,28 @@
    33.4  /*
    33.5 - * To change this template, choose Tools | Templates
    33.6 - * and open the template in the editor.
    33.7 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
    33.8 + *
    33.9 + * The contents of this file are subject to the terms of either the GNU
   33.10 + * General Public License Version 2 only ("GPL") or the Common
   33.11 + * Development and Distribution License("CDDL") (collectively, the
   33.12 + * "License"). You may not use this file except in compliance with the
   33.13 + * License. You can obtain a copy of the License at
   33.14 + * http://www.netbeans.org/cddl-gplv2.html
   33.15 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
   33.16 + * specific language governing permissions and limitations under the
   33.17 + * License.  When distributing the software, include this License Header
   33.18 + * Notice in each file and include the License file at
   33.19 + * nbbuild/licenses/CDDL-GPL-2-CP.  Sun designates this
   33.20 + * particular file as subject to the "Classpath" exception as provided
   33.21 + * by Sun in the GPL Version 2 section of the License file that
   33.22 + * accompanied this code. If applicable, add the following below the
   33.23 + * License Header, with the fields enclosed by brackets [] replaced by
   33.24 + * your own identifying information:
   33.25 + * "Portions Copyrighted [year] [name of copyright owner]"
   33.26 + *
   33.27 + * Contributor(s):
   33.28 + *
   33.29 + * Portions Copyrighted 2009 Jaroslav Tulach
   33.30   */
   33.31 -
   33.32  package cz.xelfi.quoridor.visidor;
   33.33  
   33.34  import cz.xelfi.quoridor.Board;
    34.1 --- a/webidor/pom.xml	Fri Jan 01 20:53:17 2010 +0100
    34.2 +++ b/webidor/pom.xml	Thu Jan 07 23:20:33 2010 +0100
    34.3 @@ -1,5 +1,7 @@
    34.4  <?xml version="1.0" encoding="UTF-8"?>
    34.5 -<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    34.6 +<project xmlns="http://maven.apache.org/POM/4.0.0" 
    34.7 +         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    34.8 +         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    34.9    <modelVersion>4.0.0</modelVersion>
   34.10    <parent>
   34.11          <artifactId>all-quoridor</artifactId>
    35.1 --- a/webidor/src/main/java/cz/xelfi/quoridor/webidor/Game.java	Fri Jan 01 20:53:17 2010 +0100
    35.2 +++ b/webidor/src/main/java/cz/xelfi/quoridor/webidor/Game.java	Thu Jan 07 23:20:33 2010 +0100
    35.3 @@ -82,10 +82,19 @@
    35.4  
    35.5      @XmlAttribute
    35.6      final String getCurrentPlayer() {
    35.7 -        if (board.getCurrentPlayer() == board.getPlayers().get(0)) {
    35.8 -            return id.getWhite();
    35.9 -        } else {
   35.10 -            return id.getBlack();
   35.11 +        Player w = board.getWinner();
   35.12 +        if(w==null){
   35.13 +            if (board.getCurrentPlayer() == board.getPlayers().get(0)) {
   35.14 +                return id.getWhite();
   35.15 +            } else {
   35.16 +                return id.getBlack();
   35.17 +            }
   35.18 +        }else{
   35.19 +            if (w == board.getPlayers().get(0)) {
   35.20 +                return id.getWhite();
   35.21 +            } else {
   35.22 +                return id.getBlack();
   35.23 +            }
   35.24          }
   35.25      }
   35.26  
   35.27 @@ -191,11 +200,13 @@
   35.28          @Override
   35.29          public Board unmarshal(String v) throws Exception {
   35.30              return v == null ? null : Board.valueOf(v);
   35.31 +//            return v == null ? null : new Board(v);
   35.32          }
   35.33  
   35.34          @Override
   35.35          public String marshal(Board v) throws Exception {
   35.36              return v == null ? null : v.toString();
   35.37 +//            return v == null ? null : Board.board2HashCode(v);
   35.38          }
   35.39  
   35.40      }
    36.1 --- a/webidor/src/main/java/cz/xelfi/quoridor/webidor/User.java	Fri Jan 01 20:53:17 2010 +0100
    36.2 +++ b/webidor/src/main/java/cz/xelfi/quoridor/webidor/User.java	Thu Jan 07 23:20:33 2010 +0100
    36.3 @@ -90,6 +90,23 @@
    36.4          return permissions.contains(permission);
    36.5      }
    36.6  
    36.7 +    public static boolean canSee(GameId gId, String userId) {
    36.8 +        if (!gId.isFinished()) {
    36.9 +            return true;
   36.10 +        }
   36.11 +        if (userId.equals(gId.getWhite())) {
   36.12 +            return true;
   36.13 +        }
   36.14 +        if (userId.equals(gId.getBlack())) {
   36.15 +            return true;
   36.16 +        }
   36.17 +        return false;
   36.18 +    }
   36.19 +
   36.20 +    public boolean canSee(GameId gId){
   36.21 +        return canSee(gId, id);
   36.22 +    }
   36.23 +
   36.24      public String getId() {
   36.25          return id;
   36.26      }
    37.1 --- a/webidor/src/test/java/cz/xelfi/quoridor/webidor/FinishedGameTest.java	Fri Jan 01 20:53:17 2010 +0100
    37.2 +++ b/webidor/src/test/java/cz/xelfi/quoridor/webidor/FinishedGameTest.java	Thu Jan 07 23:20:33 2010 +0100
    37.3 @@ -184,4 +184,45 @@
    37.4          assertFalse("is finished", end.getId().getStatus().isInProgress());
    37.5      }
    37.6  
    37.7 +    @Test public void testResignBGame() throws Exception {
    37.8 +        String logJarda = webResource.path("login").
    37.9 +            queryParam("name", "Jarda").
   37.10 +            queryParam("password", "heslo").
   37.11 +            accept(MediaType.TEXT_PLAIN).
   37.12 +            put(String.class);
   37.13 +        String logJirka = webResource.path("login").
   37.14 +            queryParam("name", "Jirka").
   37.15 +            queryParam("password", "pesko").
   37.16 +            accept(MediaType.TEXT_PLAIN).
   37.17 +            put(String.class);
   37.18 +
   37.19 +        GameId s = webResource.path("games").queryParam("white", "Jarda")
   37.20 +                .queryParam("loginID", logJarda)
   37.21 +                .queryParam("black", "Jirka").post(GameId.class);
   37.22 +
   37.23 +        assertTrue("In progress", s.getStatus().isInProgress());
   37.24 +
   37.25 +        webResource.path("games/" + s.getId()).
   37.26 +            queryParam("loginID", logJarda).
   37.27 +            queryParam("player", "Jarda").
   37.28 +            queryParam("move", "N").put(GameId.class);
   37.29 +        webResource.path("games/" + s.getId()).
   37.30 +            queryParam("loginID", logJirka).
   37.31 +            queryParam("player", "Jirka").
   37.32 +            queryParam("move", "RESIGN").put(GameId.class);
   37.33 +
   37.34 +
   37.35 +        try {
   37.36 +            Game end = webResource.path("games/" + s.getId()).accept(MediaType.TEXT_XML).get(Game.class);
   37.37 +            fail("Should not be able to get game when finished");
   37.38 +        } catch (UniformInterfaceException ex) {
   37.39 +            // OK
   37.40 +        }
   37.41 +        Game end = webResource.path("games/" + s.getId()).queryParam("loginID", logJarda).accept(MediaType.TEXT_XML).get(Game.class);
   37.42 +        assertEquals("WhiteWins", GameStatus.whiteWon, end.getId().getStatus());
   37.43 +        assertEquals("Jarda wins", "Jarda", end.getCurrentPlayer());
   37.44 +
   37.45 +        assertFalse("is finished", end.getId().getStatus().isInProgress());
   37.46 +    }
   37.47 +
   37.48  }
    38.1 --- a/webidor/src/test/java/cz/xelfi/quoridor/webidor/QuoridorTest.java	Fri Jan 01 20:53:17 2010 +0100
    38.2 +++ b/webidor/src/test/java/cz/xelfi/quoridor/webidor/QuoridorTest.java	Thu Jan 07 23:20:33 2010 +0100
    38.3 @@ -26,6 +26,8 @@
    38.4  
    38.5  package cz.xelfi.quoridor.webidor;
    38.6  
    38.7 +import cz.xelfi.quoridor.IllegalPositionException;
    38.8 +import java.io.StringWriter;
    38.9  import com.sun.jersey.api.client.GenericType;
   38.10  import com.sun.jersey.api.client.UniformInterfaceException;
   38.11  import com.sun.jersey.core.header.MediaTypes;
   38.12 @@ -38,6 +40,7 @@
   38.13  import java.io.FileOutputStream;
   38.14  import java.io.FileReader;
   38.15  import java.io.IOException;
   38.16 +import java.io.StringReader;
   38.17  import java.util.List;
   38.18  import java.util.Map;
   38.19  import javax.ws.rs.core.MediaType;
   38.20 @@ -201,17 +204,33 @@
   38.21  
   38.22          class GMap extends GenericType<Map<String,Object>>{}
   38.23          String text = webResource.path("games").path(s.getId()).accept(MediaType.TEXT_PLAIN).get(String.class);
   38.24 +        text = (boardToPicture(Board.valueOf(text)));
   38.25          if (text.indexOf("-----") == -1) {
   38.26              fail("Expecting board:\n" + text);
   38.27          }
   38.28          Game readGame = webResource.path("games").path(s.getId()).accept(MediaType.TEXT_XML).get(Game.class);
   38.29          String sGame = webResource.path("games").path(s.getId()).accept(MediaType.TEXT_XML).get(String.class);
   38.30          assertNotNull("Game really returned", readGame);
   38.31 -        assertEquals("Same game as in text representation", readGame.getBoard(), Board.valueOf(text));
   38.32 -        assertEquals("It is same as text of our game", readGame.getBoard().toString(), text);
   38.33 +//        assertEquals("Same game as in text representation", readGame.getBoard(), Board.valueOf(text));
   38.34 +        assertEquals("Same game as in text representation", readGame.getBoard(), picture2board(text));
   38.35 +//        assertEquals("It is same as text of our game", readGame.getBoard().toString(), text);
   38.36 +        assertEquals("It is same as text of our game", boardToPicture(readGame.getBoard()), text);
   38.37  
   38.38          assertEquals(Move.NORTH, readGame.getMoves().get(0).getMove());
   38.39          assertEquals(Move.SOUTH, readGame.getMoves().get(1).getMove());
   38.40      }
   38.41 +    private static String boardToPicture(Board b) {
   38.42 +        StringWriter w = new StringWriter();
   38.43 +        try {
   38.44 +            b.write(w);
   38.45 +        } catch (IOException ex) {
   38.46 +            return ex.toString();
   38.47 +        }
   38.48 +        return w.toString();
   38.49 +    }
   38.50  
   38.51 +    private static Board picture2board(String text) throws IOException, IllegalPositionException {
   38.52 +        StringReader sr = new StringReader(text);
   38.53 +        return Board.read(sr);
   38.54 +    }
   38.55  }