Initial version of statistics and ELO rating. Donated by Martin Rexa statistics-and-elo
authorJaroslav Tulach <jaroslav.tulach@apidesign.org>
Thu, 07 Jan 2010 22:34:17 +0100
branchstatistics-and-elo
changeset 1784b78d4f028b3
parent 177 2f8672ac9f1a
child 179 c5fbddc4c590
Initial version of statistics and ELO rating. Donated by Martin Rexa
freemarkerdor/pom.xml
freemarkerdor/src/main/java/cz/xelfi/quoridor/freemarkerdor/UI.java
freemarkerdor/src/main/java/cz/xelfi/quoridor/freemarkerdor/W3CDocumentReader.java
freemarkerdor/src/main/resources/cz/xelfi/quoridor/freemarkerdor/UI/Bundle.properties
freemarkerdor/src/main/resources/cz/xelfi/quoridor/freemarkerdor/UI/Bundle_cs.properties
freemarkerdor/src/main/resources/cz/xelfi/quoridor/freemarkerdor/UI/elo.fmt
freemarkerdor/src/main/resources/cz/xelfi/quoridor/freemarkerdor/UI/game.fmt
freemarkerdor/src/main/resources/cz/xelfi/quoridor/freemarkerdor/UI/index.fmt
freemarkerdor/src/main/resources/cz/xelfi/quoridor/freemarkerdor/UI/opening_games.fmt
freemarkerdor/src/main/resources/cz/xelfi/quoridor/freemarkerdor/UI/openings.fmt
pom.xml
quoridor/src/main/java/cz/xelfi/quoridor/Board.java
quoridor/src/main/java/cz/xelfi/quoridor/Move.java
quoridor/src/test/java/cz/xelfi/quoridor/BoardCase.java
quoridor/src/test/java/cz/xelfi/quoridor/SerializeTest.java
statistics/all-zip.xml
statistics/pom.xml
statistics/src/main/java/cz/xelfi/quoridor/statistics/EloEntry.java
statistics/src/main/java/cz/xelfi/quoridor/statistics/EloList.java
statistics/src/main/java/cz/xelfi/quoridor/statistics/JAXBContextResolver.java
statistics/src/main/java/cz/xelfi/quoridor/statistics/OpeningNodeView.java
statistics/src/main/java/cz/xelfi/quoridor/statistics/OpeningNodeViewEntry.java
statistics/src/main/java/cz/xelfi/quoridor/statistics/OpeningTree.java
statistics/src/main/java/cz/xelfi/quoridor/statistics/OpeningTreeNode.java
statistics/src/main/java/cz/xelfi/quoridor/statistics/resources/Elo.java
statistics/src/main/java/cz/xelfi/quoridor/statistics/resources/Openings.java
statistics/src/main/java/cz/xelfi/quoridor/statistics/resources/Statistics.java
statistics/src/test/java/cz/xelfi/quoridor/statistics/EloTest.java
statistics/src/test/java/cz/xelfi/quoridor/statistics/OpeningsTest.java
statistics/src/test/java/cz/xelfi/quoridor/statistics/resources/StatisticsTest.java
visidor/src/main/java/cz/xelfi/quoridor/visidor/Viewer.java
webidor/pom.xml
webidor/src/main/java/cz/xelfi/quoridor/webidor/Game.java
webidor/src/main/java/cz/xelfi/quoridor/webidor/User.java
webidor/src/test/java/cz/xelfi/quoridor/webidor/FinishedGameTest.java
webidor/src/test/java/cz/xelfi/quoridor/webidor/QuoridorTest.java
     1.1 --- a/freemarkerdor/pom.xml	Fri Jan 01 20:53:17 2010 +0100
     1.2 +++ b/freemarkerdor/pom.xml	Thu Jan 07 22:34:17 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 22:34:17 2010 +0100
     2.3 @@ -87,6 +87,7 @@
     2.4          version = p.getProperty("version", "unknown"); // NOI18N
     2.5      }
     2.6      private static WebResource base;
     2.7 +    private static WebResource stat;
     2.8  
     2.9      @Context
    2.10      private HttpHeaders headers;
    2.11 @@ -163,6 +164,7 @@
    2.12          }
    2.13          String txt = wr.accept(MediaType.TEXT_PLAIN).get(String.class);
    2.14          Board b = Board.valueOf(txt);
    2.15 +//        Board b = new Board(txt);
    2.16          ResponseBuilder resp = Response.ok();
    2.17          CacheControl cc = new CacheControl();
    2.18          cc.setNoCache(true);
    2.19 @@ -220,13 +222,25 @@
    2.20  
    2.21          Document doc = url.accept(MediaType.TEXT_XML).get(Document.class);
    2.22          Board b;
    2.23 +        String t = doc.getElementsByTagName("board").item(0).getTextContent();
    2.24          try {
    2.25              b = Board.valueOf(doc.getElementsByTagName("board").item(0).getTextContent());
    2.26 -        } catch (IllegalPositionException ex) {
    2.27 +        } catch (Exception ex) {
    2.28 +//            b = new Board(t);
    2.29 +//        } catch (IllegalStateException ex) {
    2.30              Exceptions.printStackTrace(ex);
    2.31              b = Board.empty();
    2.32          }
    2.33 -        v = viewable("game.fmt", doc, "message", msg, "format", format, "board", b);
    2.34 +        String bCode = null;
    2.35 +        try{
    2.36 +            bCode = stat.path("openings").path(Board.board2HashCode(b)+".check").queryParam("loginID", user.getId()).accept(MediaType.TEXT_PLAIN).get(String.class);
    2.37 +        }catch(Exception e){
    2.38 +            bCode = null;
    2.39 +        }
    2.40 +        if(bCode == null || "".equals(bCode))
    2.41 +            v = viewable("game.fmt", doc, "message", msg, "format", format, "board", b,"textPicture",b.boardToPicture());
    2.42 +        else
    2.43 +            v = viewable("game.fmt", doc, "message", msg, "format", format, "board", b,"textPicture",b.boardToPicture(),"bCode", bCode);
    2.44          return resp.entity(v).build();
    2.45      }
    2.46  
    2.47 @@ -346,7 +360,72 @@
    2.48  
    2.49          return welcome(10);
    2.50      }
    2.51 +
    2.52 +    @GET
    2.53 +    @Path("elo")
    2.54 +    @Produces(MediaType.TEXT_HTML)
    2.55 +    public Response getEloList(){
    2.56 +        Viewable v = checkLogin();
    2.57 +        if (v != null) {
    2.58 +            return Response.status(Response.Status.FORBIDDEN).entity(v).build();
    2.59 +        }
    2.60 +        final Document got = stat.path("elo").path("list").accept(MediaType.TEXT_XML).get(Document.class);
    2.61 +        return Response.ok(viewable("elo.fmt", got)).build();
    2.62 +    }
    2.63      
    2.64 +    @GET
    2.65 +    @Path("openings")
    2.66 +    @Produces(MediaType.TEXT_HTML)
    2.67 +    public Response getOpeningRoot(){
    2.68 +        return getOpeningNode("ROOT");
    2.69 +    }
    2.70 +
    2.71 +    @GET
    2.72 +    @Path("openings/{code}")
    2.73 +    @Produces(MediaType.TEXT_HTML)
    2.74 +    public Response getOpeningNode(@PathParam("code") String code){
    2.75 +        Viewable v = checkLogin();
    2.76 +        if (v != null) {
    2.77 +            return Response.status(Response.Status.FORBIDDEN).entity(v).build();
    2.78 +        }
    2.79 +        final Document got = stat.path("openings").path(code).queryParam("loginID", user.getId()).accept(MediaType.TEXT_XML).get(Document.class);
    2.80 +        Board b;
    2.81 +        try {
    2.82 +            b = Board.valueOf(got.getElementsByTagName("nodeCode").item(0).getTextContent());
    2.83 +        } catch (Exception ex) {
    2.84 +            Exceptions.printStackTrace(ex);
    2.85 +            b = Board.empty();
    2.86 +        }
    2.87 +        return Response.ok(viewable("openings.fmt", got, "whitefences",b.getPlayers().get(0).getFences(),"blackfences",b.getPlayers().get(1).getFences())).build();
    2.88 +    }
    2.89 +
    2.90 +    @GET
    2.91 +    @Path("openings/{code}/{status}")
    2.92 +    @Produces(MediaType.TEXT_HTML)
    2.93 +    public Response getOpeningNodeGames(@PathParam("code") String code, @PathParam("status") String status){
    2.94 +        Viewable v = checkLogin();
    2.95 +        if (v != null) {
    2.96 +            return Response.status(Response.Status.FORBIDDEN).entity(v).build();
    2.97 +        }
    2.98 +        final Document got = stat.path("openings").path(code).path(status).queryParam("loginID", user.getId()).accept(MediaType.TEXT_XML).get(Document.class);
    2.99 +        return Response.ok(viewable("opening_games.fmt", got,"code",code,"color",status)).build();
   2.100 +    }
   2.101 +
   2.102 +    @GET
   2.103 +    @Path("openings/{code}.png")
   2.104 +    @Produces("image/png")
   2.105 +    public Response getOpeningBoardImage(
   2.106 +        @PathParam("code") String code,
   2.107 +        @QueryParam("fieldSize") @DefaultValue("40") int fieldSize
   2.108 +    ) throws IllegalPositionException {
   2.109 +        Board b = new Board(code);
   2.110 +        ResponseBuilder resp = Response.ok();
   2.111 +        CacheControl cc = new CacheControl();
   2.112 +        cc.setNoCache(true);
   2.113 +        resp.cacheControl(cc);
   2.114 +        return resp.entity(BoardImage.draw(b, fieldSize)).build();
   2.115 +    }
   2.116 +
   2.117      //
   2.118      // start the server
   2.119      //
   2.120 @@ -376,12 +455,14 @@
   2.121  
   2.122      static Callable<Void> startServers(int port, String remoteAPI) throws Exception {
   2.123          Client client = new Client();
   2.124 +        Client client1 = new Client();
   2.125  
   2.126          final HttpServer apiServer;
   2.127          if (remoteAPI == null) {
   2.128              throw new IllegalArgumentException("Provide URL to API server"); // NOI18N
   2.129          } else {
   2.130              base = client.resource(new URI(remoteAPI));
   2.131 +            stat = client1.resource(new URI("http://localhost:9444"));
   2.132              apiServer = null;
   2.133          }
   2.134  
     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 22:34:17 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 22:34:17 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 22:34:17 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 22:34:17 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 22:34:17 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 22:34:17 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 22:34:17 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 22:34:17 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/pom.xml	Fri Jan 01 20:53:17 2010 +0100
    11.2 +++ b/pom.xml	Thu Jan 07 22:34:17 2010 +0100
    11.3 @@ -37,10 +37,11 @@
    11.4      <module>quoridor</module>
    11.5      <module>visidor</module>
    11.6      <module>webidor</module>
    11.7 +    <module>statistics</module>
    11.8      <module>freemarkerdor</module>
    11.9      <module>emailer</module>
   11.10    </modules>
   11.11      <name>Quoridor related projects</name>
   11.12      <description>Master project that agregates all quoridor related functionality.
   11.13      </description>
   11.14 -</project>
   11.15 \ No newline at end of file
   11.16 +</project>
    12.1 --- a/quoridor/src/main/java/cz/xelfi/quoridor/Board.java	Fri Jan 01 20:53:17 2010 +0100
    12.2 +++ b/quoridor/src/main/java/cz/xelfi/quoridor/Board.java	Thu Jan 07 22:34:17 2010 +0100
    12.3 @@ -42,6 +42,7 @@
    12.4  import java.util.HashSet;
    12.5  import java.util.List;
    12.6  import java.util.Set;
    12.7 +import java.util.TreeSet;
    12.8  import java.util.regex.Matcher;
    12.9  import java.util.regex.Pattern;
   12.10  
   12.11 @@ -352,7 +353,11 @@
   12.12       * @return board object, if the string can be read
   12.13       * @throws IllegalPositionException if the string does not represent the board
   12.14       */
   12.15 -    public static Board valueOf(String board) throws IllegalPositionException {
   12.16 +    public static Board valueOf(String board) {
   12.17 +        return new Board(board);
   12.18 +    }
   12.19 +
   12.20 +    public static Board picture2board(String board) throws IllegalPositionException {
   12.21          try {
   12.22              return read(new StringReader(board));
   12.23          } catch (IOException ex) {
   12.24 @@ -697,6 +702,10 @@
   12.25       */
   12.26      @Override
   12.27      public String toString() {
   12.28 +        return Board.board2HashCode(this);
   12.29 +    }
   12.30 +
   12.31 +    public String boardToPicture() {
   12.32          StringWriter w = new StringWriter();
   12.33          try {
   12.34              write(w);
   12.35 @@ -863,4 +872,91 @@
   12.36          return 17 * y + x;
   12.37      }
   12.38  
   12.39 +    public Board(String hashCode) throws IllegalStateException{
   12.40 +        this.fences = new HashSet<Fence>();
   12.41 +        if((hashCode != null) && (hashCode.length() > 6)){
   12.42 +            char[]c = hashCode.toCharArray();
   12.43 +            this.players = Collections.unmodifiableList (Arrays.asList (new Player[] {
   12.44 +                new Player ((c[0]-'A')*2, (c[1]-'0')*2, c[2]-'a', Player.Direction.NORTH),
   12.45 +                new Player ((c[3]-'A')*2, (c[4]-'0')*2, c[5]-'a', Player.Direction.SOUTH),
   12.46 +            }));
   12.47 +            if(c[6]=='w'){
   12.48 +                this.turn = 0;
   12.49 +                this.winner = null;
   12.50 +            }else if(c[6]=='b'){
   12.51 +                this.turn = 1;
   12.52 +                this.winner = null;
   12.53 +            }else if(c[6]=='W'){
   12.54 +                this.turn = 0;
   12.55 +                this.winner = this.players.get(0);
   12.56 +            }else if(c[6]=='B'){
   12.57 +                this.turn = 1;
   12.58 +                this.winner = this.players.get(1);
   12.59 +            }else{
   12.60 +                this.turn = 0;
   12.61 +                this.winner = null;
   12.62 +            }
   12.63 +            for(int i=7; i<c.length;i+=2){
   12.64 +                int f = Integer.parseInt(hashCode.substring(i, i+2),16);
   12.65 +                Fence.Orientation o = Fence.Orientation.HORIZONTAL;
   12.66 +                if(f > 64){
   12.67 +                    o = Fence.Orientation.VERTICAL;
   12.68 +                    f -= 64;
   12.69 +                }
   12.70 +                fences.add(new Fence((f/8)*2+1, (f%8)*2+1,o));
   12.71 +            }
   12.72 +        }else{
   12.73 +            this.players = Collections.unmodifiableList (Arrays.asList (new Player[] {
   12.74 +                new Player (8,0,10,Player.Direction.NORTH),
   12.75 +                new Player (8,16,10,Player.Direction.SOUTH),
   12.76 +            }));
   12.77 +            this.winner = null;
   12.78 +            this.turn = 0;
   12.79 +        }
   12.80 +        try {
   12.81 +            this.occupied = computeOccupied (players, fences);
   12.82 +        } catch (IllegalPositionException ex) {
   12.83 +            throw new IllegalStateException (ex.getMessage ());
   12.84 +        }
   12.85 +    }
   12.86 +
   12.87 +    public static String board2HashCode(Board b){
   12.88 +        StringBuilder sb = new StringBuilder();
   12.89 +        for(Player p: b.getPlayers()){
   12.90 +            sb.append((char)(p.getColumn() + 'A'));
   12.91 +            sb.append((char)(p.getRow() + '0'));
   12.92 +            sb.append((char)(p.getFences() + 'a'));
   12.93 +        }
   12.94 +        Player winner = b.getWinner();
   12.95 +        if(winner == null){
   12.96 +            if(b.players.indexOf(b.getCurrentPlayer())==0)
   12.97 +                sb.append('w');
   12.98 +            else if(b.players.indexOf(b.getCurrentPlayer())==1)
   12.99 +                sb.append('b');
  12.100 +            else
  12.101 +                sb.append('n');
  12.102 +        }else{
  12.103 +            if(b.players.indexOf(winner)==0)
  12.104 +                sb.append('W');
  12.105 +            else if(b.players.indexOf(winner)==1)
  12.106 +                sb.append('B');
  12.107 +            else
  12.108 +                sb.append('N');
  12.109 +        }
  12.110 +
  12.111 +        TreeSet<Integer> fences = new TreeSet<Integer>();
  12.112 +        for(Fence f: b.getFences()){
  12.113 +            int a = (f.getColumn() - 'A')*8 + (f.getRow()-1);
  12.114 +            if(f.getOrientation().equals(Fence.Orientation.VERTICAL))
  12.115 +                a+=64;
  12.116 +            fences.add(a);
  12.117 +        }
  12.118 +        for(int f: fences){
  12.119 +            if(f<16)
  12.120 +                sb.append('0');
  12.121 +            sb.append(Integer.toHexString(f));
  12.122 +        }
  12.123 +        return sb.toString();
  12.124 +    }
  12.125 +
  12.126  }
    13.1 --- a/quoridor/src/main/java/cz/xelfi/quoridor/Move.java	Fri Jan 01 20:53:17 2010 +0100
    13.2 +++ b/quoridor/src/main/java/cz/xelfi/quoridor/Move.java	Thu Jan 07 22:34:17 2010 +0100
    13.3 @@ -199,5 +199,26 @@
    13.4          return hash;
    13.5      }
    13.6  
    13.7 +    public Move getMirrorMove(){
    13.8 +        if(fence != null){
    13.9 +            return new Move(new Fence(16-fence.getX(), fence.getY(), fence.getOrientation()));
   13.10 +        }
   13.11 +        if(direction == null)
   13.12 +            return new Move();
   13.13 +        int dirSize = direction.length;
   13.14 +        Direction[] mirrorDirection = new Direction[dirSize];
   13.15 +        for(int i = 0; i < dirSize; i++){
   13.16 +            if(direction[i].equals(Direction.NORTH))
   13.17 +                mirrorDirection[i] = Direction.NORTH;
   13.18 +            else if(direction[i].equals(Direction.SOUTH))
   13.19 +                mirrorDirection[i] = Direction.SOUTH;
   13.20 +            else if(direction[i].equals(Direction.EAST))
   13.21 +                mirrorDirection[i] = Direction.WEST;
   13.22 +            else if(direction[i].equals(Direction.WEST))
   13.23 +                mirrorDirection[i] = Direction.EAST;
   13.24 +        }
   13.25 +        return new Move(mirrorDirection);
   13.26 +    }
   13.27 +
   13.28  
   13.29  }
    14.1 --- a/quoridor/src/test/java/cz/xelfi/quoridor/BoardCase.java	Fri Jan 01 20:53:17 2010 +0100
    14.2 +++ b/quoridor/src/test/java/cz/xelfi/quoridor/BoardCase.java	Thu Jan 07 22:34:17 2010 +0100
    14.3 @@ -200,6 +200,7 @@
    14.4  "\n" +
    14.5  "                         [S]                 \n";
    14.6  
    14.7 +        b = Board.picture2board(b).toString();
    14.8          Board begin = Board.valueOf(b);
    14.9  
   14.10          try {
    15.1 --- a/quoridor/src/test/java/cz/xelfi/quoridor/SerializeTest.java	Fri Jan 01 20:53:17 2010 +0100
    15.2 +++ b/quoridor/src/test/java/cz/xelfi/quoridor/SerializeTest.java	Thu Jan 07 22:34:17 2010 +0100
    15.3 @@ -72,7 +72,8 @@
    15.4          StringWriter w = new StringWriter();
    15.5          b.write(w);
    15.6          w.close();
    15.7 -        return Board.valueOf(w.toString());
    15.8 +        return Board.picture2board(w.toString());
    15.9 +        //return Board.valueOf(w.toString());
   15.10      }
   15.11  
   15.12  }
    16.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    16.2 +++ b/statistics/all-zip.xml	Thu Jan 07 22:34:17 2010 +0100
    16.3 @@ -0,0 +1,19 @@
    16.4 +<?xml version="1.0" encoding="UTF-8"?>
    16.5 +<assembly>
    16.6 + <id>all</id>
    16.7 +  <formats>
    16.8 +    <format>zip</format>
    16.9 +  </formats>
   16.10 +  <dependencySets>
   16.11 +    <dependencySet>
   16.12 +        <useProjectArtifact>false</useProjectArtifact>
   16.13 +        <outputDirectory>lib</outputDirectory>
   16.14 +    </dependencySet>
   16.15 +  </dependencySets>
   16.16 +  <files>
   16.17 +    <file>
   16.18 +      <source>target/statistics-${version}.jar</source>
   16.19 +      <outputDirectory>/</outputDirectory>
   16.20 +    </file>
   16.21 +  </files>
   16.22 +</assembly>
    17.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    17.2 +++ b/statistics/pom.xml	Thu Jan 07 22:34:17 2010 +0100
    17.3 @@ -0,0 +1,141 @@
    17.4 +<?xml version="1.0" encoding="UTF-8"?>
    17.5 +<project xmlns="http://maven.apache.org/POM/4.0.0" 
    17.6 +         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    17.7 +         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    17.8 +  <modelVersion>4.0.0</modelVersion>
    17.9 +  <parent>
   17.10 +        <artifactId>all-quoridor</artifactId>
   17.11 +        <groupId>org.apidesign</groupId>
   17.12 +        <version>1.0</version>
   17.13 +  </parent>
   17.14 +  <groupId>org.apidesign</groupId>
   17.15 +  <artifactId>statistics</artifactId>
   17.16 +  <packaging>jar</packaging>
   17.17 +  <version>1.0</version>
   17.18 +  <name>Game Statistics</name>
   17.19 +  <url>http://maven.apache.org</url>
   17.20 +  <repositories>
   17.21 +    <repository>
   17.22 +        <id>maven2-repository.dev.java.net</id>
   17.23 +        <name>Java.net Repository for Maven</name>
   17.24 +        <url>http://download.java.net/maven/2/</url>
   17.25 +        <layout>default</layout>
   17.26 +    </repository>
   17.27 +    <repository>
   17.28 +        <id>maven-repository.dev.java.net</id>
   17.29 +        <name>Java.net Maven 1 Repository (legacy)</name>
   17.30 +        <url>http://download.java.net/maven/1</url>
   17.31 +        <layout>legacy</layout>
   17.32 +    </repository>
   17.33 +  </repositories>
   17.34 +  <dependencies>
   17.35 +
   17.36 +    <dependency>
   17.37 +      <groupId>junit</groupId>
   17.38 +      <artifactId>junit</artifactId>
   17.39 +      <version>4.5</version>
   17.40 +      <scope>test</scope>
   17.41 +    </dependency>
   17.42 +    <dependency>
   17.43 +      <groupId>com.sun.jersey</groupId>
   17.44 +      <artifactId>jersey-core</artifactId>
   17.45 +      <version>1.1.0-ea</version>
   17.46 +    </dependency>
   17.47 +    <dependency>
   17.48 +      <groupId>com.sun.jersey</groupId>
   17.49 +      <artifactId>jersey-server</artifactId>
   17.50 +      <version>1.1.0-ea</version>
   17.51 +    </dependency>
   17.52 +    <dependency>
   17.53 +      <groupId>com.sun.jersey</groupId>
   17.54 +      <artifactId>jersey-json</artifactId>
   17.55 +      <version>1.1.0-ea</version>
   17.56 +      <type>jar</type>
   17.57 +      <exclusions>
   17.58 +        <exclusion>
   17.59 +          <artifactId>jaxb-api</artifactId>
   17.60 +          <groupId>javax.xml.bind</groupId>
   17.61 +        </exclusion>
   17.62 +        <exclusion>
   17.63 +          <artifactId>stax-api</artifactId>
   17.64 +          <groupId>stax</groupId>
   17.65 +        </exclusion>
   17.66 +      </exclusions>
   17.67 +    </dependency>
   17.68 +    <dependency>
   17.69 +      <groupId>com.sun.jersey.test.framework</groupId>
   17.70 +      <artifactId>jersey-test-framework</artifactId>
   17.71 +      <version>1.1.0-ea</version>
   17.72 +      <scope>test</scope>
   17.73 +    </dependency>
   17.74 +    <dependency>
   17.75 +      <groupId>org.apidesign</groupId>
   17.76 +      <artifactId>quoridor</artifactId>
   17.77 +      <version>1.0</version>
   17.78 +      <type>jar</type>
   17.79 +    </dependency>
   17.80 +    <dependency>
   17.81 +      <groupId>${project.groupId}</groupId>
   17.82 +      <artifactId>webidor</artifactId>
   17.83 +      <version>1.11</version>
   17.84 +    </dependency>
   17.85 +  </dependencies>
   17.86 +  <build>
   17.87 +    <plugins>
   17.88 +      <plugin>
   17.89 +        <groupId>org.apache.maven.plugins</groupId>
   17.90 +        <artifactId>maven-compiler-plugin</artifactId>
   17.91 +        <version>2.0.2</version>
   17.92 +        <configuration>
   17.93 +          <source>1.6</source>
   17.94 +          <target>1.6</target>
   17.95 +        </configuration>
   17.96 +      </plugin>
   17.97 +      <plugin>
   17.98 +        <groupId>org.codehaus.mojo</groupId>
   17.99 +        <artifactId>exec-maven-plugin</artifactId>
  17.100 +        <configuration>
  17.101 +            <mainClass>cz.xelfi.quoridor.statistics.resources.Statistics</mainClass>
  17.102 +        </configuration>
  17.103 +      </plugin>
  17.104 +      <plugin>
  17.105 +        <artifactId>maven-assembly-plugin</artifactId>
  17.106 +        <version>2.2-beta-2</version>
  17.107 +        <executions>
  17.108 +          <execution>
  17.109 +            <id>create-executable-jar</id>
  17.110 +            <phase>package</phase>
  17.111 +            <goals>
  17.112 +              <goal>single</goal>
  17.113 +            </goals>
  17.114 +            <configuration>
  17.115 +              <descriptors>
  17.116 +                <descriptor>all-zip.xml</descriptor>
  17.117 +              </descriptors>
  17.118 +              <finalName>statistics-${version}</finalName>
  17.119 +            </configuration>
  17.120 +          </execution>
  17.121 +        </executions>
  17.122 +      </plugin>
  17.123 +      <plugin>
  17.124 +        <groupId>org.apache.maven.plugins</groupId>
  17.125 +        <artifactId>maven-jar-plugin</artifactId>
  17.126 +        <configuration>
  17.127 +            <archive>
  17.128 +                <manifest>
  17.129 +                    <addClasspath>true</addClasspath>
  17.130 +                    <classpathPrefix>lib/</classpathPrefix>
  17.131 +                    <mainClass>cz.xelfi.quoridor.statistics.resources.Statistics</mainClass>
  17.132 +                </manifest>
  17.133 +            </archive>
  17.134 +        </configuration>
  17.135 +      </plugin>
  17.136 +    </plugins>
  17.137 +  </build>
  17.138 +    <description>Server with API for games statistics</description>
  17.139 +</project>
  17.140 +
  17.141 +
  17.142 +
  17.143 +
  17.144 +
    18.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    18.2 +++ b/statistics/src/main/java/cz/xelfi/quoridor/statistics/EloEntry.java	Thu Jan 07 22:34:17 2010 +0100
    18.3 @@ -0,0 +1,82 @@
    18.4 +/*
    18.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
    18.6 + *
    18.7 + * The contents of this file are subject to the terms of either the GNU
    18.8 + * General Public License Version 2 only ("GPL") or the Common
    18.9 + * Development and Distribution License("CDDL") (collectively, the
   18.10 + * "License"). You may not use this file except in compliance with the
   18.11 + * License. You can obtain a copy of the License at
   18.12 + * http://www.netbeans.org/cddl-gplv2.html
   18.13 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
   18.14 + * specific language governing permissions and limitations under the
   18.15 + * License.  When distributing the software, include this License Header
   18.16 + * Notice in each file and include the License file at
   18.17 + * nbbuild/licenses/CDDL-GPL-2-CP.  Sun designates this
   18.18 + * particular file as subject to the "Classpath" exception as provided
   18.19 + * by Sun in the GPL Version 2 section of the License file that
   18.20 + * accompanied this code. If applicable, add the following below the
   18.21 + * License Header, with the fields enclosed by brackets [] replaced by
   18.22 + * your own identifying information:
   18.23 + * "Portions Copyrighted [year] [name of copyright owner]"
   18.24 + *
   18.25 + * Contributor(s):
   18.26 + *
   18.27 + * Portions Copyrighted 2010 Martin Rexa
   18.28 + */
   18.29 +
   18.30 +package cz.xelfi.quoridor.statistics;
   18.31 +
   18.32 +import javax.xml.bind.annotation.XmlAttribute;
   18.33 +import java.util.Comparator;
   18.34 +/**
   18.35 + *
   18.36 + * @author Martin Rexa
   18.37 + */
   18.38 +public class EloEntry {
   18.39 +    String player;
   18.40 +    double elo;
   18.41 +    int games;
   18.42 +
   18.43 +    public static final Comparator<EloEntry> BEST_FIRST = new BestFirst();
   18.44 +
   18.45 +    EloEntry(){
   18.46 +        super();
   18.47 +    }
   18.48 +
   18.49 +    EloEntry(String player, double elo, int games){
   18.50 +        super();
   18.51 +        this.player = player;
   18.52 +        this.elo = elo;
   18.53 +        this.games = games;
   18.54 +    }
   18.55 +
   18.56 +    @XmlAttribute
   18.57 +    public String getPlayer(){
   18.58 +        return player;
   18.59 +    }
   18.60 +
   18.61 +    @XmlAttribute
   18.62 +    public Double getElo(){
   18.63 +        return elo;
   18.64 +    }
   18.65 +
   18.66 +    @XmlAttribute
   18.67 +    public Integer getGames(){
   18.68 +        return games;
   18.69 +    }
   18.70 +
   18.71 +    public String toString(){
   18.72 +        return "Player: " + player + ", ELO: " + elo + ", Games: " + games;
   18.73 +    }
   18.74 +
   18.75 +    private static final class BestFirst implements Comparator<EloEntry> {
   18.76 +        public int compare(EloEntry e1, EloEntry e2) {
   18.77 +            if(e1.elo > e2.elo)
   18.78 +                return -1;
   18.79 +            else if(e1.elo < e2.elo)
   18.80 +                return 1;
   18.81 +            else return e1.player.compareTo(e2.player);
   18.82 +        }
   18.83 +    }
   18.84 +
   18.85 +}
    19.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    19.2 +++ b/statistics/src/main/java/cz/xelfi/quoridor/statistics/EloList.java	Thu Jan 07 22:34:17 2010 +0100
    19.3 @@ -0,0 +1,104 @@
    19.4 +/*
    19.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
    19.6 + *
    19.7 + * The contents of this file are subject to the terms of either the GNU
    19.8 + * General Public License Version 2 only ("GPL") or the Common
    19.9 + * Development and Distribution License("CDDL") (collectively, the
   19.10 + * "License"). You may not use this file except in compliance with the
   19.11 + * License. You can obtain a copy of the License at
   19.12 + * http://www.netbeans.org/cddl-gplv2.html
   19.13 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
   19.14 + * specific language governing permissions and limitations under the
   19.15 + * License.  When distributing the software, include this License Header
   19.16 + * Notice in each file and include the License file at
   19.17 + * nbbuild/licenses/CDDL-GPL-2-CP.  Sun designates this
   19.18 + * particular file as subject to the "Classpath" exception as provided
   19.19 + * by Sun in the GPL Version 2 section of the License file that
   19.20 + * accompanied this code. If applicable, add the following below the
   19.21 + * License Header, with the fields enclosed by brackets [] replaced by
   19.22 + * your own identifying information:
   19.23 + * "Portions Copyrighted [year] [name of copyright owner]"
   19.24 + *
   19.25 + * Contributor(s):
   19.26 + *
   19.27 + * Portions Copyrighted 2010 Martin Rexa
   19.28 + */
   19.29 +
   19.30 +package cz.xelfi.quoridor.statistics;
   19.31 +
   19.32 +import java.util.Map;
   19.33 +import java.util.HashMap;
   19.34 +import java.util.List;
   19.35 +import java.util.Collections;
   19.36 +import java.util.ArrayList;
   19.37 +import javax.xml.bind.annotation.XmlElement;
   19.38 +import javax.xml.bind.annotation.XmlElementWrapper;
   19.39 +import javax.xml.bind.annotation.XmlRootElement;
   19.40 +import javax.xml.bind.annotation.XmlAccessType;
   19.41 +import javax.xml.bind.annotation.XmlAccessorType;
   19.42 +/**
   19.43 + *
   19.44 + * @author Martin Rexa
   19.45 + */
   19.46 +@XmlRootElement
   19.47 +@XmlAccessorType(XmlAccessType.FIELD)
   19.48 +public class EloList {
   19.49 +    static int INITIAL_ELO = 1250;
   19.50 +    static int MAX_ELO_DIFF = 400;
   19.51 +    static int KVAL_START = 25;
   19.52 +    static int KVAL_FOLLOW = 10;
   19.53 +    static int KVAL_LIMIT = 30;
   19.54 +
   19.55 +    private Map<String, Double> players;
   19.56 +    private Map<String, Integer> playerGames;
   19.57 +
   19.58 +    public EloList () {
   19.59 +        players = new HashMap<String, Double>();
   19.60 +        playerGames = new HashMap<String, Integer>();
   19.61 +    }
   19.62 +
   19.63 +    @XmlElementWrapper(name="elolist")
   19.64 +    @XmlElement(name="item")
   19.65 +    public List<EloEntry> getFinalList(){
   19.66 +        List<EloEntry> finalList = new ArrayList<EloEntry>();
   19.67 +        for (Map.Entry<String, Double> player : players.entrySet()){
   19.68 +            finalList.add(new EloEntry(player.getKey(), player.getValue(), playerGames.get(player.getKey())));
   19.69 +        }
   19.70 +        Collections.sort(finalList, EloEntry.BEST_FIRST);
   19.71 +        return finalList;
   19.72 +    }
   19.73 +
   19.74 +    public EloList putResult(String winner, String looser){
   19.75 +        double wElo = getElo(winner);
   19.76 +        double lElo = getElo(looser);
   19.77 +
   19.78 +        double eloDiff = wElo - lElo;
   19.79 +        if(eloDiff < - MAX_ELO_DIFF)
   19.80 +            eloDiff = - MAX_ELO_DIFF;
   19.81 +        if(eloDiff > MAX_ELO_DIFF)
   19.82 +            eloDiff = MAX_ELO_DIFF;
   19.83 +
   19.84 +        double wDiff = Math.round(100 * getKVal(winner) * (1 - (1 / (1 + Math.pow(10, -eloDiff / MAX_ELO_DIFF)))));
   19.85 +        wDiff /= 100.0;
   19.86 +        double lDiff = Math.round(100 * getKVal(looser) * (0 - (1 / (1 + Math.pow(10, eloDiff / MAX_ELO_DIFF)))));
   19.87 +        lDiff /= 100.0;
   19.88 +
   19.89 +        players.put(winner, wElo + wDiff);
   19.90 +        players.put(looser, lElo + lDiff);
   19.91 +        return this;
   19.92 +    }
   19.93 +    
   19.94 +    public double getElo(String player){
   19.95 +        Double elo = players.get(player);
   19.96 +        return (elo == null) ? INITIAL_ELO : elo;
   19.97 +    }
   19.98 +    
   19.99 +    private int getKVal(String player){
  19.100 +        Integer games = playerGames.get(player);
  19.101 +        int g = (games == null) ? 0 : games;
  19.102 +        // Side effect, method should not be called more times for the same game/player
  19.103 +        playerGames.put(player, g + 1);
  19.104 +        return g < KVAL_LIMIT ? KVAL_START : KVAL_FOLLOW;
  19.105 +    }
  19.106 +
  19.107 +}
    20.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    20.2 +++ b/statistics/src/main/java/cz/xelfi/quoridor/statistics/JAXBContextResolver.java	Thu Jan 07 22:34:17 2010 +0100
    20.3 @@ -0,0 +1,58 @@
    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 +package cz.xelfi.quoridor.statistics;
   20.30 +
   20.31 +import com.sun.jersey.api.json.JSONConfiguration;
   20.32 +import com.sun.jersey.api.json.JSONJAXBContext;
   20.33 +import java.util.Arrays;
   20.34 +import java.util.HashSet;
   20.35 +import java.util.Set;
   20.36 +import javax.ws.rs.ext.ContextResolver;
   20.37 +import javax.ws.rs.ext.Provider;
   20.38 +import javax.xml.bind.JAXBContext;
   20.39 +
   20.40 +/**
   20.41 + *
   20.42 + * @author Martin Rexa
   20.43 + */
   20.44 +@Provider
   20.45 +public final class JAXBContextResolver implements ContextResolver<JAXBContext> {
   20.46 +
   20.47 +    private final JAXBContext context;
   20.48 +
   20.49 +    private final Set<Class> types;
   20.50 +
   20.51 +    private final Class[] cTypes = {EloList.class, EloEntry.class, OpeningNodeView.class, OpeningNodeViewEntry.class};
   20.52 +
   20.53 +    public JAXBContextResolver() throws Exception {
   20.54 +        this.types = new HashSet(Arrays.asList(cTypes));
   20.55 +        this.context = new JSONJAXBContext(JSONConfiguration.natural().build(), cTypes);
   20.56 +    }
   20.57 +
   20.58 +    public JAXBContext getContext(Class<?> objectType) {
   20.59 +        return (types.contains(objectType)) ? context : null;
   20.60 +    }
   20.61 +}
   20.62 \ No newline at end of file
    21.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    21.2 +++ b/statistics/src/main/java/cz/xelfi/quoridor/statistics/OpeningNodeView.java	Thu Jan 07 22:34:17 2010 +0100
    21.3 @@ -0,0 +1,129 @@
    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 cz.xelfi.quoridor.webidor.GameId;
   21.33 +import cz.xelfi.quoridor.webidor.GameStatus;
   21.34 +import cz.xelfi.quoridor.Move;
   21.35 +import java.util.Set;
   21.36 +import java.util.HashSet;
   21.37 +import java.util.Map;
   21.38 +import java.util.HashMap;
   21.39 +import java.util.List;
   21.40 +import java.util.ArrayList;
   21.41 +import javax.xml.bind.annotation.XmlElement;
   21.42 +import javax.xml.bind.annotation.XmlElementWrapper;
   21.43 +import javax.xml.bind.annotation.XmlRootElement;
   21.44 +//import javax.xml.bind.annotation.XmlAccessType;
   21.45 +//import javax.xml.bind.annotation.XmlAccessorType;
   21.46 +import javax.xml.bind.annotation.XmlAttribute;
   21.47 +
   21.48 +/**
   21.49 + *
   21.50 + * @author Martin Rexa
   21.51 + */
   21.52 +
   21.53 +@XmlRootElement
   21.54 +//@XmlAccessorType(XmlAccessType.FIELD)
   21.55 +public class OpeningNodeView {
   21.56 +    @XmlAttribute
   21.57 +    String code;
   21.58 +    @XmlElementWrapper(name="whiteGames")
   21.59 +    @XmlElement(name="gameId")
   21.60 +    List<GameId> whiteGames;
   21.61 +    @XmlElementWrapper(name="blackGames")
   21.62 +    @XmlElement(name="gameId")
   21.63 +    List<GameId> blackGames;
   21.64 +    Map<Move, OpeningNodeViewEntry> entries;
   21.65 +    boolean empty;
   21.66 +
   21.67 +    public OpeningNodeView(){
   21.68 +    }
   21.69 +
   21.70 +    public OpeningNodeView(String code){
   21.71 +        this.code = code;
   21.72 +        entries = new HashMap<Move, OpeningNodeViewEntry>();
   21.73 +        whiteGames = new ArrayList<GameId>();
   21.74 +        blackGames = new ArrayList<GameId>();
   21.75 +        empty = true;
   21.76 +    }
   21.77 +
   21.78 +    @XmlElement
   21.79 +    public String getNodeCode(){
   21.80 +        return code;
   21.81 +    }
   21.82 +
   21.83 +    public void processGame(Move m, String code, GameId gId){
   21.84 +        OpeningNodeViewEntry e = entries.get(m);
   21.85 +        if(e == null){
   21.86 +            e = new OpeningNodeViewEntry(m,code);
   21.87 +            entries.put(m, e);
   21.88 +        }
   21.89 +        e.processGame(gId);
   21.90 +        empty = false;
   21.91 +    }
   21.92 +
   21.93 +    @XmlElementWrapper(name="children")
   21.94 +    @XmlElement(name="item")
   21.95 +    public Set<OpeningNodeViewEntry> getChildren(){
   21.96 +        Set<OpeningNodeViewEntry> result = new HashSet<OpeningNodeViewEntry>();
   21.97 +        for(Map.Entry<Move, OpeningNodeViewEntry> e: entries.entrySet()){
   21.98 +            result.add(e.getValue());
   21.99 +        }
  21.100 +        return result;
  21.101 +    }
  21.102 +
  21.103 +
  21.104 +    public void addFinishedGame(GameId gId){
  21.105 +        if(gId.getStatus().equals(GameStatus.whiteWon))
  21.106 +            whiteGames.add(gId);
  21.107 +        if(gId.getStatus().equals(GameStatus.blackWon))
  21.108 +            blackGames.add(gId);
  21.109 +        empty = false;
  21.110 +    }
  21.111 +
  21.112 +    @XmlAttribute
  21.113 +    public int getWhiteCount(){
  21.114 +        return whiteGames.size();
  21.115 +    }
  21.116 +
  21.117 +    @XmlAttribute
  21.118 +    public int getBlackCount(){
  21.119 +        return blackGames.size();
  21.120 +    }
  21.121 +
  21.122 +
  21.123 +    @XmlAttribute
  21.124 +    public boolean getChildrenNotEmpty(){
  21.125 +        return !entries.isEmpty();
  21.126 +    }
  21.127 +
  21.128 +    public boolean isEmpty(){
  21.129 +        return this.empty;
  21.130 +    }
  21.131 +
  21.132 +}
    22.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    22.2 +++ b/statistics/src/main/java/cz/xelfi/quoridor/statistics/OpeningNodeViewEntry.java	Thu Jan 07 22:34:17 2010 +0100
    22.3 @@ -0,0 +1,86 @@
    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 +
   22.30 +package cz.xelfi.quoridor.statistics;
   22.31 +
   22.32 +import cz.xelfi.quoridor.Move;
   22.33 +import cz.xelfi.quoridor.webidor.GameId;
   22.34 +import cz.xelfi.quoridor.webidor.GameStatus;
   22.35 +import javax.xml.bind.annotation.XmlAccessType;
   22.36 +import javax.xml.bind.annotation.XmlAccessorType;
   22.37 +
   22.38 +/**
   22.39 + *
   22.40 + * @author Martin Rexa
   22.41 + */
   22.42 +@XmlAccessorType(XmlAccessType.FIELD)
   22.43 +public class OpeningNodeViewEntry extends Object{
   22.44 +    String move;
   22.45 +    String code;
   22.46 +    GameId gameId;
   22.47 +    GameId whiteGame;
   22.48 +    GameId blackGame;
   22.49 +    int whiteWon;
   22.50 +    int blackWon;
   22.51 +
   22.52 +    public OpeningNodeViewEntry(){
   22.53 +    }
   22.54 +
   22.55 +    public OpeningNodeViewEntry(Move m, String code){
   22.56 +        move = m.toString();
   22.57 +        this.code = code;
   22.58 +    }
   22.59 +
   22.60 +    public void processGame(GameId gId){
   22.61 +        gameId = gId;
   22.62 +        if(gId.getStatus().equals(GameStatus.whiteWon)){
   22.63 +            whiteWon++;
   22.64 +            whiteGame = gId;
   22.65 +        }
   22.66 +        if(gId.getStatus().equals(GameStatus.blackWon)){
   22.67 +            blackWon++;
   22.68 +            blackGame = gId;
   22.69 +        }
   22.70 +    }
   22.71 +
   22.72 +    @Override
   22.73 +    public boolean equals(Object obj){
   22.74 +        if (obj == null) {
   22.75 +            return false;
   22.76 +        }
   22.77 +        if (getClass() != obj.getClass()) {
   22.78 +            return false;
   22.79 +        }
   22.80 +        final OpeningNodeViewEntry other = (OpeningNodeViewEntry) obj;
   22.81 +        return this.move.equals(other.move);
   22.82 +    }
   22.83 +
   22.84 +    @Override
   22.85 +    public String toString(){
   22.86 +        return move+": "+whiteWon+"/"+blackWon;
   22.87 +    }
   22.88 +
   22.89 +}
    23.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    23.2 +++ b/statistics/src/main/java/cz/xelfi/quoridor/statistics/OpeningTree.java	Thu Jan 07 22:34:17 2010 +0100
    23.3 @@ -0,0 +1,107 @@
    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.Board;
   23.33 +import cz.xelfi.quoridor.Move;
   23.34 +import cz.xelfi.quoridor.webidor.Game;
   23.35 +import cz.xelfi.quoridor.webidor.CommentedMove;
   23.36 +import cz.xelfi.quoridor.IllegalPositionException;
   23.37 +import java.util.Map;
   23.38 +import java.util.HashMap;
   23.39 +
   23.40 +/**
   23.41 + *
   23.42 + * @author Martin Rexa
   23.43 + */
   23.44 +public class OpeningTree {
   23.45 +    Map<String, OpeningTreeNode> nodes = new HashMap<String, OpeningTreeNode>();
   23.46 +    OpeningTreeNode root;
   23.47 +
   23.48 +    public OpeningTree(){
   23.49 +        root = new OpeningTreeNode("ROOT");
   23.50 +        nodes.put("ROOT", root);
   23.51 +    }
   23.52 +
   23.53 +    public OpeningTreeNode getRoot(){
   23.54 +        return root;
   23.55 +    }
   23.56 +
   23.57 +    public OpeningTreeNode getNode(String hashCode){
   23.58 +        return nodes.get(hashCode);
   23.59 +    }
   23.60 +
   23.61 +    public void processGame(Game game) throws IllegalPositionException{
   23.62 +        OpeningTreeNode node = root;
   23.63 +        OpeningTreeNode parentNode;
   23.64 +        OpeningTreeNode mirrorNode = root;
   23.65 +        OpeningTreeNode mirrorParentNode;
   23.66 +        Board board = Board.empty();
   23.67 +        Board mirrorBoard = Board.empty();
   23.68 +        root.addGame(game);
   23.69 +        for(CommentedMove move: game.getMoves()){
   23.70 +            if(!move.getMove().equals(Move.RESIGN)){
   23.71 +                parentNode = node;
   23.72 +                mirrorParentNode = mirrorNode;
   23.73 +                board = board.apply(move.getMove());
   23.74 +                mirrorBoard = mirrorBoard.apply(move.getMove().getMirrorMove());
   23.75 +                String hashCode = Board.board2HashCode(board);
   23.76 +                String mirrorHashCode = Board.board2HashCode(mirrorBoard);
   23.77 +                node = nodes.get(hashCode);
   23.78 +                if(node == null){
   23.79 +                    node = new OpeningTreeNode(hashCode);
   23.80 +                    nodes.put(hashCode, node);
   23.81 +                }
   23.82 +                node.addGame(game);
   23.83 +                mirrorNode = nodes.get(mirrorHashCode);
   23.84 +                if(mirrorHashCode.equals(hashCode)){
   23.85 +                    if(mirrorNode == null){
   23.86 +                        mirrorNode = node;
   23.87 +                    }
   23.88 +                }else{
   23.89 +                    if(mirrorNode == null){
   23.90 +                        mirrorNode = new OpeningTreeNode(mirrorHashCode);
   23.91 +                        nodes.put(mirrorHashCode, mirrorNode);
   23.92 +                    }
   23.93 +                    mirrorNode.addGame(game);
   23.94 +                }
   23.95 +                if(parentNode != null)
   23.96 +                    parentNode.addChild(move.getMove(),node);
   23.97 +                if(mirrorParentNode != null){
   23.98 +                    if(parentNode.equals(mirrorParentNode) && node.equals(mirrorNode))
   23.99 +                        ;
  23.100 +                    else
  23.101 +                        mirrorParentNode.addChild(move.getMove().getMirrorMove(),mirrorNode);
  23.102 +                }
  23.103 +            }
  23.104 +        }
  23.105 +        node.addFinishedGame(game);
  23.106 +        if(!node.equals(mirrorNode))
  23.107 +            mirrorNode.addFinishedGame(game);
  23.108 +    }
  23.109 +
  23.110 +}
    24.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    24.2 +++ b/statistics/src/main/java/cz/xelfi/quoridor/statistics/OpeningTreeNode.java	Thu Jan 07 22:34:17 2010 +0100
    24.3 @@ -0,0 +1,138 @@
    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.webidor.Game;
   24.33 +import cz.xelfi.quoridor.webidor.GameId;
   24.34 +import cz.xelfi.quoridor.webidor.GameStatus;
   24.35 +import cz.xelfi.quoridor.webidor.User;
   24.36 +import cz.xelfi.quoridor.Move;
   24.37 +import java.util.HashMap;
   24.38 +import java.util.Map;
   24.39 +import java.util.List;
   24.40 +import java.util.ArrayList;
   24.41 +import javax.xml.bind.annotation.XmlAttribute;
   24.42 +
   24.43 +/**
   24.44 + *
   24.45 + * @author Martin Rexa
   24.46 + */
   24.47 +public class OpeningTreeNode {
   24.48 +    @XmlAttribute
   24.49 +    String hashCode;
   24.50 +    List<GameId> gameIds;
   24.51 +    List<GameId> finishedGames;
   24.52 +    Map<Move, OpeningTreeNode> children;
   24.53 +
   24.54 +    public OpeningTreeNode(){
   24.55 +    }
   24.56 +
   24.57 +    public OpeningTreeNode(String hashCode){
   24.58 +        this.hashCode = hashCode;
   24.59 +        gameIds = new ArrayList<GameId>();
   24.60 +        finishedGames = new ArrayList<GameId>();
   24.61 +        children = new HashMap<Move,OpeningTreeNode>();
   24.62 +    }
   24.63 +
   24.64 +    public OpeningTreeNode addGame(Game game){
   24.65 +        gameIds.add(game.getId());
   24.66 +        return this;
   24.67 +    }
   24.68 +
   24.69 +    public OpeningTreeNode addFinishedGame(Game game){
   24.70 +        finishedGames.add(game.getId());
   24.71 +        return this;
   24.72 +    }
   24.73 +
   24.74 +    public OpeningTreeNode addGameId(GameId gameId){
   24.75 +        gameIds.add(gameId);
   24.76 +        return this;
   24.77 +    }
   24.78 +
   24.79 +    public OpeningTreeNode addChild(Move move, OpeningTreeNode child){
   24.80 +        children.put(move, child);
   24.81 +        return this;
   24.82 +    }
   24.83 +
   24.84 +    public OpeningNodeView getView(User user){
   24.85 +        return getView(user.getId());
   24.86 +    }
   24.87 +
   24.88 +    public OpeningNodeView getView(String userId){
   24.89 +        OpeningNodeView view = new OpeningNodeView(hashCode);
   24.90 +        for(Map.Entry<Move, OpeningTreeNode> e: children.entrySet()){
   24.91 +            for(GameId gId: e.getValue().gameIds){
   24.92 +                if(User.canSee(gId, userId)){
   24.93 +                    view.processGame(e.getKey(), e.getValue().hashCode, gId);
   24.94 +                }
   24.95 +            }
   24.96 +        }
   24.97 +        for(GameId gId: finishedGames){
   24.98 +            view.addFinishedGame(gId);
   24.99 +        }
  24.100 +        return view;
  24.101 +    }
  24.102 +
  24.103 +    public List<GameId> filterGames(String userId, GameStatus status){
  24.104 +        List<GameId> result = new ArrayList<GameId>();
  24.105 +        for(GameId gId: gameIds){
  24.106 +            if(User.canSee(gId, userId))
  24.107 +                if(gId.getStatus().equals(status))
  24.108 +                    result.add(gId);
  24.109 +        }
  24.110 +        return result;
  24.111 +    }
  24.112 +
  24.113 +    public List<GameId> getGameIds(){
  24.114 +        return gameIds;
  24.115 +    }
  24.116 +
  24.117 +//    @XmlAttribute
  24.118 +//    @XmlElement
  24.119 +    public String getHashCode(){
  24.120 +        return this.hashCode;
  24.121 +    }
  24.122 +
  24.123 +
  24.124 +    @Override
  24.125 +    public boolean equals(Object obj) {
  24.126 +        if (obj == null) {
  24.127 +            return false;
  24.128 +        }
  24.129 +        if (getClass() != obj.getClass()) {
  24.130 +            return false;
  24.131 +        }
  24.132 +        OpeningTreeNode other = (OpeningTreeNode)obj;
  24.133 +        return hashCode.equals(other.hashCode);
  24.134 +    }
  24.135 +
  24.136 +    @Override
  24.137 +    public int hashCode() {
  24.138 +        return hashCode.hashCode();
  24.139 +    }
  24.140 +
  24.141 +}
    25.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    25.2 +++ b/statistics/src/main/java/cz/xelfi/quoridor/statistics/resources/Elo.java	Thu Jan 07 22:34:17 2010 +0100
    25.3 @@ -0,0 +1,75 @@
    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.resources;
   25.31 +
   25.32 +import cz.xelfi.quoridor.statistics.EloList;
   25.33 +import cz.xelfi.quoridor.webidor.Game;
   25.34 +import cz.xelfi.quoridor.webidor.GameId;
   25.35 +import cz.xelfi.quoridor.webidor.GameStatus;
   25.36 +import javax.ws.rs.GET;
   25.37 +import javax.ws.rs.Path;
   25.38 +import javax.ws.rs.PathParam;
   25.39 +import javax.ws.rs.Produces;
   25.40 +import javax.ws.rs.core.MediaType;
   25.41 +
   25.42 +/**
   25.43 + *
   25.44 + * @author Martin Rexa
   25.45 + */
   25.46 +public final class Elo {
   25.47 +    private EloList list;
   25.48 +
   25.49 +    public Elo(){
   25.50 +        list = new EloList();
   25.51 +    }
   25.52 +
   25.53 +    public void processGame(Game game){
   25.54 +        GameId gId = game.getId();
   25.55 +        GameStatus status = gId.getStatus();
   25.56 +        if(status.equals(GameStatus.whiteWon)){
   25.57 +            list.putResult(gId.getWhite(), gId.getBlack());
   25.58 +        }else if(status.equals(GameStatus.blackWon)){
   25.59 +            list.putResult(gId.getBlack(), gId.getWhite());
   25.60 +        }
   25.61 +    }
   25.62 +
   25.63 +    @GET
   25.64 +    @Path("list")
   25.65 +    @Produces({ MediaType.APPLICATION_JSON, MediaType.TEXT_XML })
   25.66 +    public EloList getList(){
   25.67 +        return list;
   25.68 +    }
   25.69 +
   25.70 +    @GET
   25.71 +    @Path("{username}")
   25.72 +    @Produces(MediaType.TEXT_PLAIN)
   25.73 +    public String getElo(@PathParam("username") String id){
   25.74 +        list.getElo(id);
   25.75 +        return Double.toString(list.getElo(id));
   25.76 +    }
   25.77 +
   25.78 +}
    26.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    26.2 +++ b/statistics/src/main/java/cz/xelfi/quoridor/statistics/resources/Openings.java	Thu Jan 07 22:34:17 2010 +0100
    26.3 @@ -0,0 +1,107 @@
    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.resources;
   26.31 +
   26.32 +import cz.xelfi.quoridor.IllegalPositionException;
   26.33 +import cz.xelfi.quoridor.webidor.Game;
   26.34 +import cz.xelfi.quoridor.webidor.GameId;
   26.35 +import cz.xelfi.quoridor.statistics.OpeningTree;
   26.36 +import cz.xelfi.quoridor.statistics.OpeningTreeNode;
   26.37 +import cz.xelfi.quoridor.statistics.OpeningNodeView;
   26.38 +import cz.xelfi.quoridor.webidor.GameStatus;
   26.39 +import java.util.List;
   26.40 +import javax.ws.rs.GET;
   26.41 +import javax.ws.rs.Path;
   26.42 +import javax.ws.rs.PathParam;
   26.43 +import javax.ws.rs.Produces;
   26.44 +import javax.ws.rs.QueryParam;
   26.45 +import javax.ws.rs.DefaultValue;
   26.46 +import javax.ws.rs.core.MediaType;
   26.47 +
   26.48 +/**
   26.49 + *
   26.50 + * @author Martin Rexa
   26.51 + */
   26.52 +public class Openings {
   26.53 +    OpeningTree tree;
   26.54 +
   26.55 +    public Openings(){
   26.56 +        tree = new OpeningTree();
   26.57 +    }
   26.58 +
   26.59 +    public void processGame(Game game) {
   26.60 +        try{
   26.61 +            tree.processGame(game);
   26.62 +        }catch (IllegalPositionException e){
   26.63 +            e.printStackTrace();
   26.64 +        }
   26.65 +    }
   26.66 +
   26.67 +    @GET
   26.68 +    @Path("{nodeId}")
   26.69 +    @Produces({ MediaType.APPLICATION_JSON, MediaType.TEXT_XML })
   26.70 +    public OpeningNodeView getNode(@QueryParam("loginID") @DefaultValue("") String loginId,
   26.71 +                         @PathParam("nodeId") String nodeId){
   26.72 +        OpeningTreeNode node = tree.getNode(nodeId);
   26.73 +        if(node == null)
   26.74 +            node = tree.getRoot();
   26.75 +        return node.getView(loginId);
   26.76 +    }
   26.77 +
   26.78 +    @GET
   26.79 +    @Path("{nodeId}.check")
   26.80 +    @Produces({ MediaType.TEXT_PLAIN })
   26.81 +    public String checkNode(@QueryParam("loginID") @DefaultValue("") String loginId,
   26.82 +                         @PathParam("nodeId") String nodeId){
   26.83 +        OpeningTreeNode node = tree.getNode(nodeId);
   26.84 +        if(node == null)
   26.85 +            return "";
   26.86 +        if(node.getView(loginId).isEmpty())
   26.87 +            return "";
   26.88 +        else
   26.89 +            return nodeId;
   26.90 +    }
   26.91 +
   26.92 +    @GET
   26.93 +    @Path("{nodeId}/{status}")
   26.94 +    @Produces({MediaType.APPLICATION_JSON, MediaType.TEXT_XML })
   26.95 +    public List<GameId> listGames(
   26.96 +        @DefaultValue("") @QueryParam("loginID") String loginId,
   26.97 +        @PathParam("nodeId") String nodeId,
   26.98 +        @DefaultValue("") @PathParam("status") String status
   26.99 +    ) {
  26.100 +        OpeningTreeNode node = tree.getNode(nodeId);
  26.101 +        if(node == null)
  26.102 +            return null;
  26.103 +        if("white".equals(status))
  26.104 +            return node.filterGames(loginId, GameStatus.whiteWon);
  26.105 +        else if("black".equals(status))
  26.106 +            return node.filterGames(loginId, GameStatus.blackWon);
  26.107 +        else
  26.108 +            return null;
  26.109 +    }
  26.110 +}
    27.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    27.2 +++ b/statistics/src/main/java/cz/xelfi/quoridor/statistics/resources/Statistics.java	Thu Jan 07 22:34:17 2010 +0100
    27.3 @@ -0,0 +1,156 @@
    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 com.sun.jersey.api.container.httpserver.HttpServerFactory;
   27.33 +import com.sun.jersey.api.core.PackagesResourceConfig;
   27.34 +import com.sun.jersey.api.core.ResourceConfig;
   27.35 +import com.sun.jersey.spi.resource.Singleton;
   27.36 +import com.sun.net.httpserver.HttpServer;
   27.37 +import java.io.File;
   27.38 +import java.io.IOException;
   27.39 +import java.util.Comparator;
   27.40 +import java.util.List;
   27.41 +import java.util.Collections;
   27.42 +import java.net.ServerSocket;
   27.43 +import javax.ws.rs.Path;
   27.44 +import cz.xelfi.quoridor.webidor.resources.Games;
   27.45 +import cz.xelfi.quoridor.webidor.Game;
   27.46 +
   27.47 +/**
   27.48 + *
   27.49 + * @author Martin Rexa
   27.50 + */
   27.51 +@Path("/")
   27.52 +@Singleton
   27.53 +public class Statistics {
   27.54 +    static final Comparator<Game> OLDEST_FIRST = new OldestFirst();
   27.55 +    static Elo elo;
   27.56 +    static Openings openings;
   27.57 +
   27.58 +    @Path("elo")
   27.59 +    public Elo getElo() {
   27.60 +        return elo;
   27.61 +    }
   27.62 +
   27.63 +    @Path("openings")
   27.64 +    public Openings getOpenings() {
   27.65 +        return openings;
   27.66 +    }
   27.67 +
   27.68 +    public static void main( String[] args ) throws IOException, InterruptedException
   27.69 +    {
   27.70 +        int port = 9444;
   27.71 +        // timeout between reprocessing games in miliseconds
   27.72 +//        long timeout = 1000 * 60 * 60;
   27.73 +        long timeout = 1000 * 10;
   27.74 +
   27.75 +        try {
   27.76 +            port = Integer.parseInt(args[0]);
   27.77 +        } catch (Exception ex) {
   27.78 +            // OK
   27.79 +        }
   27.80 +
   27.81 +        if (System.getProperty("quoridor.dir") == null) {
   27.82 +            File home = new File(System.getProperty("user.home"));
   27.83 +            File quoridor = new File(home, ".quoridor");
   27.84 +            System.setProperty("quoridor.dir", quoridor.getPath());
   27.85 +        }
   27.86 +
   27.87 +        processGames();
   27.88 +        HttpServer s = start(port);
   27.89 +        System.out.println("Statistics started at port " + port);
   27.90 +        Object monitor = new Object();
   27.91 +        for(;;){
   27.92 +            synchronized (monitor) {
   27.93 +                monitor.wait(timeout);
   27.94 +            }
   27.95 +            processGames();
   27.96 +        }
   27.97 +    }
   27.98 +
   27.99 +    public static HttpServer start(int port) throws IOException {
  27.100 +        if (port == -1) {
  27.101 +            ServerSocket ss = new ServerSocket(0);
  27.102 +            port =ss.getLocalPort();
  27.103 +            ss.close();
  27.104 +        }
  27.105 +        final String baseUri = "http://localhost:" + port + "/";
  27.106 +
  27.107 +        if (System.getProperty("quoridor.dir") == null) {
  27.108 +            File home = new File(System.getProperty("user.home"));
  27.109 +            File quoridor = new File(home, ".quoridor");
  27.110 +            System.setProperty("quoridor.dir", quoridor.getPath());
  27.111 +        }
  27.112 +
  27.113 +        ResourceConfig rc = new PackagesResourceConfig("cz.xelfi.quoridor.statistics");
  27.114 +        HttpServer server = HttpServerFactory.create(baseUri, rc);
  27.115 +        server.start();
  27.116 +        return server;
  27.117 +    }
  27.118 +
  27.119 +    public static void processGames(){
  27.120 +        Elo eloTmp = new Elo();
  27.121 +        Openings openingsTmp = new Openings();
  27.122 +
  27.123 +        final String prop = System.getProperty("quoridor.dir"); // NOI18N
  27.124 +        if (prop == null) {
  27.125 +            throw new IllegalStateException("quoridor.dir property must be specified"); // NOI18N
  27.126 +        }
  27.127 +        File path = new File(prop);
  27.128 +        path.mkdirs();
  27.129 +        File fGames = new File(path, "games");
  27.130 +        //fGames.mkdirs();
  27.131 +        Games games = new Games(fGames, null);
  27.132 +        List<Game> lGames = games.getGames();
  27.133 +        Collections.sort(lGames, OLDEST_FIRST);
  27.134 +
  27.135 +        // process games
  27.136 +        for(Game g: lGames){
  27.137 +            if(g.getId().isFinished()){
  27.138 +                eloTmp.processGame(g);
  27.139 +                openingsTmp.processGame(g);
  27.140 +            }
  27.141 +        }
  27.142 +
  27.143 +        elo = eloTmp;
  27.144 +        openings = openingsTmp;
  27.145 +    }
  27.146 +
  27.147 +    private static final class OldestFirst implements Comparator<Game> {
  27.148 +        public int compare(Game g1, Game g2) {
  27.149 +            if(g1.getId() == g2.getId())
  27.150 +                return 0;
  27.151 +            long diff = g2.getId().getModified() - g1.getId().getModified();
  27.152 +            if (diff != 0) {
  27.153 +                return diff < 0 ? -1 : 1;
  27.154 +            }
  27.155 +            return g1.getId().getId().compareTo(g2.getId().getId());
  27.156 +        }
  27.157 +    }
  27.158 +
  27.159 +}
    28.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    28.2 +++ b/statistics/src/test/java/cz/xelfi/quoridor/statistics/EloTest.java	Thu Jan 07 22:34:17 2010 +0100
    28.3 @@ -0,0 +1,53 @@
    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;
   28.31 +
   28.32 +import org.junit.Test;
   28.33 +import static org.junit.Assert.*;
   28.34 +
   28.35 +/**
   28.36 + *
   28.37 + * @author Martin Rexa
   28.38 + */
   28.39 +public class EloTest extends Object {
   28.40 +
   28.41 +    public EloTest() throws Exception {
   28.42 +    }
   28.43 +
   28.44 +    @Test public void testEloList() throws Exception {
   28.45 +        EloList eloList = new EloList();
   28.46 +        eloList.putResult("p1", "p2");
   28.47 +        assertFalse("empty list", eloList.getFinalList().isEmpty());
   28.48 +        eloList.putResult("p3", "p4");
   28.49 +        assertTrue("wrong list", eloList.getFinalList().get(0).getElo().equals(eloList.getFinalList().get(1).getElo()));
   28.50 +        eloList.putResult("p2", "p1");
   28.51 +        eloList.putResult("p2", "p1");
   28.52 +        eloList.putResult("p2", "p1");
   28.53 +        assertTrue("wrong list", eloList.getFinalList().get(0).player.equals("p2"));
   28.54 +    }
   28.55 +
   28.56 +}
    29.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    29.2 +++ b/statistics/src/test/java/cz/xelfi/quoridor/statistics/OpeningsTest.java	Thu Jan 07 22:34:17 2010 +0100
    29.3 @@ -0,0 +1,129 @@
    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;
   29.31 +
   29.32 +import cz.xelfi.quoridor.Fence.Orientation;
   29.33 +import org.junit.Test;
   29.34 +import static org.junit.Assert.*;
   29.35 +import cz.xelfi.quoridor.Move;
   29.36 +import cz.xelfi.quoridor.webidor.Game;
   29.37 +import java.util.Map;
   29.38 +
   29.39 +/**
   29.40 + *
   29.41 + * @author Martin Rexa
   29.42 + */
   29.43 +public class OpeningsTest extends Object {
   29.44 +    
   29.45 +    public OpeningsTest() throws Exception {
   29.46 +    }
   29.47 +
   29.48 +    @Test public void testMirrorMoves() throws Exception {
   29.49 +        assertEquals("bad symetric move", Move.NORTH.getMirrorMove(), Move.NORTH);
   29.50 +        assertEquals("bad symetric move", Move.SOUTH.getMirrorMove(), Move.SOUTH);
   29.51 +        assertEquals("bad symetric move", Move.WEST.getMirrorMove(), Move.EAST);
   29.52 +        assertEquals("bad symetric move", Move.EAST.getMirrorMove(), Move.WEST);
   29.53 +        assertEquals("bad symetric move", Move.fence('A', 1, Orientation.HORIZONTAL).getMirrorMove(), Move.fence('H', 1, Orientation.HORIZONTAL));
   29.54 +        assertEquals("bad symetric move", Move.fence('D', 3, Orientation.VERTICAL).getMirrorMove(), Move.fence('E', 3, Orientation.VERTICAL));
   29.55 +    }
   29.56 +
   29.57 +    @Test public void testMirrorJump() throws Exception {
   29.58 +        Game g = new Game("w","b");
   29.59 +        g.apply("w", Move.NORTH, new java.util.Date());
   29.60 +        g.apply("b", Move.SOUTH, new java.util.Date());
   29.61 +        g.apply("w", Move.NORTH, new java.util.Date());
   29.62 +        g.apply("b", Move.SOUTH, new java.util.Date());
   29.63 +        g.apply("w", Move.NORTH, new java.util.Date());
   29.64 +        g.apply("b", Move.SOUTH, new java.util.Date());
   29.65 +        g.apply("w", Move.NORTH, new java.util.Date());
   29.66 +        g.apply("b", Move.EAST, new java.util.Date());
   29.67 +        g.apply("w", Move.NORTH, new java.util.Date());
   29.68 +        g.apply("b", Move.valueOf("WW"), new java.util.Date());
   29.69 +        g.apply("w", Move.RESIGN, new java.util.Date());
   29.70 +        OpeningTree t = new OpeningTree();
   29.71 +        t.processGame(g);
   29.72 +        OpeningTreeNode n = t.root;
   29.73 +        n = n.children.get(Move.NORTH);
   29.74 +        n = n.children.get(Move.SOUTH);
   29.75 +        n = n.children.get(Move.NORTH);
   29.76 +        n = n.children.get(Move.SOUTH);
   29.77 +        n = n.children.get(Move.NORTH);
   29.78 +        n = n.children.get(Move.SOUTH);
   29.79 +        n = n.children.get(Move.NORTH);
   29.80 +        n = n.children.get(Move.WEST);
   29.81 +        n = n.children.get(Move.NORTH);
   29.82 +        n = n.children.get(Move.valueOf("EE"));
   29.83 +        assertEquals("bad number of children", n.children.size(), 0);
   29.84 +        for(Map.Entry e: n.children.entrySet()){
   29.85 +            System.out.println(e.getKey());
   29.86 +        }
   29.87 +    }
   29.88 +
   29.89 +    @Test public void testOpeningTreeNodes() throws Exception {
   29.90 +        OpeningTree t = new OpeningTree();
   29.91 +        assertEquals("bad number of nodes", t.nodes.size(), 1);
   29.92 +        Game g = new Game("w","b");
   29.93 +        g.apply("w", Move.NORTH, new java.util.Date());
   29.94 +        g.apply("b", Move.SOUTH, new java.util.Date());
   29.95 +        g.apply("w", Move.EAST, new java.util.Date());
   29.96 +        g.apply("b", Move.EAST, new java.util.Date());
   29.97 +        g.apply("w", Move.WEST, new java.util.Date());
   29.98 +        g.apply("b", Move.WEST, new java.util.Date());
   29.99 +        g.apply("w", Move.RESIGN, new java.util.Date());
  29.100 +        t.processGame(g);
  29.101 +        assertEquals("bad number of nodes", t.nodes.size(), 9);
  29.102 +        g = new Game("w","b");
  29.103 +        g.apply("w", Move.NORTH, new java.util.Date());
  29.104 +        g.apply("b", Move.SOUTH, new java.util.Date());
  29.105 +        g.apply("w", Move.fence('A', 1, Orientation.HORIZONTAL), new java.util.Date());
  29.106 +        g.apply("b", Move.fence('C', 1, Orientation.VERTICAL), new java.util.Date());
  29.107 +        g.apply("w", Move.RESIGN, new java.util.Date());
  29.108 +        t.processGame(g);
  29.109 +        assertEquals("bad number of nodes", t.nodes.size(), 13);
  29.110 +        g = new Game("w","b");
  29.111 +        g.apply("w", Move.NORTH, new java.util.Date());
  29.112 +        g.apply("b", Move.SOUTH, new java.util.Date());
  29.113 +        g.apply("w", Move.fence('C', 1, Orientation.VERTICAL), new java.util.Date());
  29.114 +        g.apply("b", Move.fence('A', 1, Orientation.HORIZONTAL), new java.util.Date());
  29.115 +        g.apply("w", Move.RESIGN, new java.util.Date());
  29.116 +        t.processGame(g);
  29.117 +        assertEquals("bad number of nodes", t.nodes.size(), 15);
  29.118 +        OpeningTreeNode n = t.root;
  29.119 +        n = n.children.get(Move.NORTH);
  29.120 +        n = n.children.get(Move.SOUTH);
  29.121 +        OpeningTreeNode n1 = n;
  29.122 +        n = n.children.get(Move.WEST);
  29.123 +        n = n.children.get(Move.WEST);
  29.124 +        n = n.children.get(Move.EAST);
  29.125 +        n = n.children.get(Move.EAST);
  29.126 +        assertEquals("different nodes", n.hashCode, n1.hashCode);
  29.127 +        n = n.children.get(Move.fence('F', 1, Orientation.VERTICAL));
  29.128 +        n = n.children.get(Move.fence('H', 1, Orientation.HORIZONTAL));
  29.129 +        assertEquals("bad number of children", n.children.size(), 0);
  29.130 +    }
  29.131 +
  29.132 +}
    30.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    30.2 +++ b/statistics/src/test/java/cz/xelfi/quoridor/statistics/resources/StatisticsTest.java	Thu Jan 07 22:34:17 2010 +0100
    30.3 @@ -0,0 +1,131 @@
    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.resources;
   30.31 +
   30.32 +import com.sun.jersey.api.client.Client;
   30.33 +import com.sun.jersey.api.client.WebResource;
   30.34 +import com.sun.jersey.core.header.MediaTypes;
   30.35 +import com.sun.net.httpserver.HttpServer;
   30.36 +import java.io.File;
   30.37 +import java.io.FileOutputStream;
   30.38 +import java.io.IOException;
   30.39 +import java.net.URI;
   30.40 +import java.util.Locale;
   30.41 +import javax.ws.rs.core.MediaType;
   30.42 +import org.junit.AfterClass;
   30.43 +import org.junit.Before;
   30.44 +import org.junit.BeforeClass;
   30.45 +import org.junit.Test;
   30.46 +import static org.junit.Assert.*;
   30.47 +import cz.xelfi.quoridor.statistics.EloList;
   30.48 +/**
   30.49 + *
   30.50 + * @author Martin Rexa
   30.51 + */
   30.52 +public class StatisticsTest {
   30.53 +    private static File dir;
   30.54 +    private static File fGames;
   30.55 +    private static HttpServer stopAPI;
   30.56 +    private WebResource apiResource;
   30.57 +
   30.58 +    public StatisticsTest() throws Exception {
   30.59 +    }
   30.60 +
   30.61 +    @BeforeClass
   30.62 +    public static void localeEnglish() throws Exception {
   30.63 +        Locale.setDefault(Locale.ENGLISH);
   30.64 +        dir = File.createTempFile("quoridor", ".dir");
   30.65 +        dir.delete();
   30.66 +        dir.mkdirs();
   30.67 +        fGames = new File(dir, "games");
   30.68 +        fGames.mkdirs();
   30.69 +
   30.70 +        System.setProperty("quoridor.dir", dir.getPath());
   30.71 +        stopAPI = Statistics.start(9990);
   30.72 +
   30.73 +        File passwd = new File(dir, "passwd");
   30.74 +        FileOutputStream os = new FileOutputStream(passwd);
   30.75 +        os.write("test=pes\nJarda=darda\n".getBytes("UTF-8"));
   30.76 +        os.close();
   30.77 +    }
   30.78 +
   30.79 +    @Before
   30.80 +    public void setUp() throws Exception {
   30.81 +        Client client = new Client();
   30.82 +        apiResource = client.resource(new URI("http://localhost:9990/"));
   30.83 +    }
   30.84 +
   30.85 +    @AfterClass
   30.86 +    public static void cleanUpAll() throws Exception {
   30.87 +        deleteRec(dir);
   30.88 +        if (stopAPI != null) {
   30.89 +            stopAPI.stop(0);
   30.90 +        }
   30.91 +    }
   30.92 +
   30.93 +    static void deleteRec(File dir) throws IOException {
   30.94 +        if (dir == null) {
   30.95 +            return;
   30.96 +        }
   30.97 +        File[] arr = dir.listFiles();
   30.98 +        if (arr != null) {
   30.99 +            for (File f : arr) {
  30.100 +                deleteRec(f);
  30.101 +            }
  30.102 +        }
  30.103 +        dir.delete();
  30.104 +    }
  30.105 +
  30.106 +    @Test public void testApplicationWadl() {
  30.107 +        String serviceWadl = apiResource.path("application.wadl").
  30.108 +                accept(MediaTypes.WADL).get(String.class);
  30.109 +        assertTrue(serviceWadl.length() > 0);
  30.110 +    }
  30.111 +
  30.112 +    @Test public void testEloList() throws Exception {
  30.113 +        createFile(fGames, "g1", "# white: W\n# black: B\n# status: IN_PROGRESS\nN S\n\n");
  30.114 +        createFile(fGames, "g2", "# white: W\n# black: B\n# status: blackWon\nRESIGN\n\n");
  30.115 +        createFile(fGames, "g3", "# white: W\n# black: B\n# status: blackWon\nRESIGN\n\n");
  30.116 +        createFile(fGames, "g4", "# white: W\n# black: B\n# status: blackWon\nRESIGN\n\n");
  30.117 +        Statistics.processGames();
  30.118 +
  30.119 +        EloList eloList = apiResource.path("elo").path("list").accept(MediaType.TEXT_XML).get(EloList.class);
  30.120 +//        assertFalse(eloList.getFinalList().isEmpty());
  30.121 +//        System.out.println("ELO LIST");
  30.122 +//        for (EloEntry entry : eloList.getFinalList()){
  30.123 +//            System.out.println(entry);
  30.124 +//        }
  30.125 +    }
  30.126 +
  30.127 +    private void createFile(File dir, String filename, String content) throws Exception{
  30.128 +        File f = new File(dir, filename);
  30.129 +        FileOutputStream os = new FileOutputStream(f);
  30.130 +        os.write(content.getBytes("UTF-8"));
  30.131 +        os.close();
  30.132 +    }
  30.133 +
  30.134 +}
    31.1 --- a/visidor/src/main/java/cz/xelfi/quoridor/visidor/Viewer.java	Fri Jan 01 20:53:17 2010 +0100
    31.2 +++ b/visidor/src/main/java/cz/xelfi/quoridor/visidor/Viewer.java	Thu Jan 07 22:34:17 2010 +0100
    31.3 @@ -1,8 +1,28 @@
    31.4  /*
    31.5 - * To change this template, choose Tools | Templates
    31.6 - * and open the template in the editor.
    31.7 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
    31.8 + *
    31.9 + * The contents of this file are subject to the terms of either the GNU
   31.10 + * General Public License Version 2 only ("GPL") or the Common
   31.11 + * Development and Distribution License("CDDL") (collectively, the
   31.12 + * "License"). You may not use this file except in compliance with the
   31.13 + * License. You can obtain a copy of the License at
   31.14 + * http://www.netbeans.org/cddl-gplv2.html
   31.15 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
   31.16 + * specific language governing permissions and limitations under the
   31.17 + * License.  When distributing the software, include this License Header
   31.18 + * Notice in each file and include the License file at
   31.19 + * nbbuild/licenses/CDDL-GPL-2-CP.  Sun designates this
   31.20 + * particular file as subject to the "Classpath" exception as provided
   31.21 + * by Sun in the GPL Version 2 section of the License file that
   31.22 + * accompanied this code. If applicable, add the following below the
   31.23 + * License Header, with the fields enclosed by brackets [] replaced by
   31.24 + * your own identifying information:
   31.25 + * "Portions Copyrighted [year] [name of copyright owner]"
   31.26 + *
   31.27 + * Contributor(s):
   31.28 + *
   31.29 + * Portions Copyrighted 2009 Jaroslav Tulach
   31.30   */
   31.31 -
   31.32  package cz.xelfi.quoridor.visidor;
   31.33  
   31.34  import cz.xelfi.quoridor.Board;
    32.1 --- a/webidor/pom.xml	Fri Jan 01 20:53:17 2010 +0100
    32.2 +++ b/webidor/pom.xml	Thu Jan 07 22:34:17 2010 +0100
    32.3 @@ -1,5 +1,7 @@
    32.4  <?xml version="1.0" encoding="UTF-8"?>
    32.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">
    32.6 +<project xmlns="http://maven.apache.org/POM/4.0.0" 
    32.7 +         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    32.8 +         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    32.9    <modelVersion>4.0.0</modelVersion>
   32.10    <parent>
   32.11          <artifactId>all-quoridor</artifactId>
    33.1 --- a/webidor/src/main/java/cz/xelfi/quoridor/webidor/Game.java	Fri Jan 01 20:53:17 2010 +0100
    33.2 +++ b/webidor/src/main/java/cz/xelfi/quoridor/webidor/Game.java	Thu Jan 07 22:34:17 2010 +0100
    33.3 @@ -82,10 +82,19 @@
    33.4  
    33.5      @XmlAttribute
    33.6      final String getCurrentPlayer() {
    33.7 -        if (board.getCurrentPlayer() == board.getPlayers().get(0)) {
    33.8 -            return id.getWhite();
    33.9 -        } else {
   33.10 -            return id.getBlack();
   33.11 +        Player w = board.getWinner();
   33.12 +        if(w==null){
   33.13 +            if (board.getCurrentPlayer() == board.getPlayers().get(0)) {
   33.14 +                return id.getWhite();
   33.15 +            } else {
   33.16 +                return id.getBlack();
   33.17 +            }
   33.18 +        }else{
   33.19 +            if (w == board.getPlayers().get(0)) {
   33.20 +                return id.getWhite();
   33.21 +            } else {
   33.22 +                return id.getBlack();
   33.23 +            }
   33.24          }
   33.25      }
   33.26  
   33.27 @@ -191,11 +200,13 @@
   33.28          @Override
   33.29          public Board unmarshal(String v) throws Exception {
   33.30              return v == null ? null : Board.valueOf(v);
   33.31 +//            return v == null ? null : new Board(v);
   33.32          }
   33.33  
   33.34          @Override
   33.35          public String marshal(Board v) throws Exception {
   33.36              return v == null ? null : v.toString();
   33.37 +//            return v == null ? null : Board.board2HashCode(v);
   33.38          }
   33.39  
   33.40      }
    34.1 --- a/webidor/src/main/java/cz/xelfi/quoridor/webidor/User.java	Fri Jan 01 20:53:17 2010 +0100
    34.2 +++ b/webidor/src/main/java/cz/xelfi/quoridor/webidor/User.java	Thu Jan 07 22:34:17 2010 +0100
    34.3 @@ -90,6 +90,23 @@
    34.4          return permissions.contains(permission);
    34.5      }
    34.6  
    34.7 +    public static boolean canSee(GameId gId, String userId) {
    34.8 +        if (!gId.isFinished()) {
    34.9 +            return true;
   34.10 +        }
   34.11 +        if (userId.equals(gId.getWhite())) {
   34.12 +            return true;
   34.13 +        }
   34.14 +        if (userId.equals(gId.getBlack())) {
   34.15 +            return true;
   34.16 +        }
   34.17 +        return false;
   34.18 +    }
   34.19 +
   34.20 +    public boolean canSee(GameId gId){
   34.21 +        return canSee(gId, id);
   34.22 +    }
   34.23 +
   34.24      public String getId() {
   34.25          return id;
   34.26      }
    35.1 --- a/webidor/src/test/java/cz/xelfi/quoridor/webidor/FinishedGameTest.java	Fri Jan 01 20:53:17 2010 +0100
    35.2 +++ b/webidor/src/test/java/cz/xelfi/quoridor/webidor/FinishedGameTest.java	Thu Jan 07 22:34:17 2010 +0100
    35.3 @@ -184,4 +184,45 @@
    35.4          assertFalse("is finished", end.getId().getStatus().isInProgress());
    35.5      }
    35.6  
    35.7 +    @Test public void testResignBGame() throws Exception {
    35.8 +        String logJarda = webResource.path("login").
    35.9 +            queryParam("name", "Jarda").
   35.10 +            queryParam("password", "heslo").
   35.11 +            accept(MediaType.TEXT_PLAIN).
   35.12 +            put(String.class);
   35.13 +        String logJirka = webResource.path("login").
   35.14 +            queryParam("name", "Jirka").
   35.15 +            queryParam("password", "pesko").
   35.16 +            accept(MediaType.TEXT_PLAIN).
   35.17 +            put(String.class);
   35.18 +
   35.19 +        GameId s = webResource.path("games").queryParam("white", "Jarda")
   35.20 +                .queryParam("loginID", logJarda)
   35.21 +                .queryParam("black", "Jirka").post(GameId.class);
   35.22 +
   35.23 +        assertTrue("In progress", s.getStatus().isInProgress());
   35.24 +
   35.25 +        webResource.path("games/" + s.getId()).
   35.26 +            queryParam("loginID", logJarda).
   35.27 +            queryParam("player", "Jarda").
   35.28 +            queryParam("move", "N").put(GameId.class);
   35.29 +        webResource.path("games/" + s.getId()).
   35.30 +            queryParam("loginID", logJirka).
   35.31 +            queryParam("player", "Jirka").
   35.32 +            queryParam("move", "RESIGN").put(GameId.class);
   35.33 +
   35.34 +
   35.35 +        try {
   35.36 +            Game end = webResource.path("games/" + s.getId()).accept(MediaType.TEXT_XML).get(Game.class);
   35.37 +            fail("Should not be able to get game when finished");
   35.38 +        } catch (UniformInterfaceException ex) {
   35.39 +            // OK
   35.40 +        }
   35.41 +        Game end = webResource.path("games/" + s.getId()).queryParam("loginID", logJarda).accept(MediaType.TEXT_XML).get(Game.class);
   35.42 +        assertEquals("WhiteWins", GameStatus.whiteWon, end.getId().getStatus());
   35.43 +        assertEquals("Jarda wins", "Jarda", end.getCurrentPlayer());
   35.44 +
   35.45 +        assertFalse("is finished", end.getId().getStatus().isInProgress());
   35.46 +    }
   35.47 +
   35.48  }
    36.1 --- a/webidor/src/test/java/cz/xelfi/quoridor/webidor/QuoridorTest.java	Fri Jan 01 20:53:17 2010 +0100
    36.2 +++ b/webidor/src/test/java/cz/xelfi/quoridor/webidor/QuoridorTest.java	Thu Jan 07 22:34:17 2010 +0100
    36.3 @@ -201,14 +201,17 @@
    36.4  
    36.5          class GMap extends GenericType<Map<String,Object>>{}
    36.6          String text = webResource.path("games").path(s.getId()).accept(MediaType.TEXT_PLAIN).get(String.class);
    36.7 +        text = (new Board(text)).boardToPicture();
    36.8          if (text.indexOf("-----") == -1) {
    36.9              fail("Expecting board:\n" + text);
   36.10          }
   36.11          Game readGame = webResource.path("games").path(s.getId()).accept(MediaType.TEXT_XML).get(Game.class);
   36.12          String sGame = webResource.path("games").path(s.getId()).accept(MediaType.TEXT_XML).get(String.class);
   36.13          assertNotNull("Game really returned", readGame);
   36.14 -        assertEquals("Same game as in text representation", readGame.getBoard(), Board.valueOf(text));
   36.15 -        assertEquals("It is same as text of our game", readGame.getBoard().toString(), text);
   36.16 +//        assertEquals("Same game as in text representation", readGame.getBoard(), Board.valueOf(text));
   36.17 +        assertEquals("Same game as in text representation", readGame.getBoard(), Board.picture2board(text));
   36.18 +//        assertEquals("It is same as text of our game", readGame.getBoard().toString(), text);
   36.19 +        assertEquals("It is same as text of our game", readGame.getBoard().boardToPicture(), text);
   36.20  
   36.21          assertEquals(Move.NORTH, readGame.getMoves().get(0).getMove());
   36.22          assertEquals(Move.SOUTH, readGame.getMoves().get(1).getMove());