Java tutorial
/* * Reversi.java * * Copyright (c) 2010, 2011 Roberto Corradini. All rights reserved. * * This file is part of the reversi program * http://github.com/rcrr/reversi * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 3, or (at your option) any * later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA * or visit the site <http://www.gnu.org/licenses/>. */ package rcrr.reversi; import java.util.List; import java.util.ListIterator; import java.util.ArrayList; import java.util.Map; import java.util.HashMap; import java.util.Set; import java.io.PrintStream; import org.joda.time.Period; import org.joda.time.Duration; import java.io.PrintWriter; import java.io.StringWriter; import rcrr.reversi.board.Player; /** * The reversi class {@code Reversi} is the main entry point for the program. * <p> * Documentation has to be completed. * Testing has to be completed. */ public final class Reversi { /** Error code 1. */ private static final int ERROR_CODE_1 = 1; /** Error code 2. */ private static final int ERROR_CODE_2 = 2; /** Error code 3. */ private static final int ERROR_CODE_3 = 3; /** Error code 4. */ private static final int ERROR_CODE_4 = 4; /** Error code 5. */ private static final int ERROR_CODE_5 = 5; /** Default game duration in minutes. */ private static final int DEFAULT_GAME_DURATION_IN_MINUTES = 90; /** Class constructor. Not used so far. */ private Reversi() { throw new UnsupportedOperationException(); } /** * Plays a game returning the score. * <p> * Documentation has to be completed. * Testing has to be completed. * It has to be fully refactored. * * @param blStrategy the black's strategy * @param whStrategy the white's strategy * @param ps the output print stream * @param gameDuration the game duration assigned to both players * * @return the game score */ public static int reversi(final Strategy blStrategy, final Strategy whStrategy, final PrintStream ps, final Duration gameDuration) { /** Must be revised!!!! */ final Actor black = Actor.valueOf("Black Actor", blStrategy); final Actor white = Actor.valueOf("White Actor", whStrategy); final Game game = Game.initialGame(black, white, gameDuration, ps); game.play(); return game.countDiscDifference(); } /** * Plays a series of 2*nPairs games, swapping sides. Returns a results map * having as key: * <ul> * <li>{@code scores}</li> * <li>{@code numberOfWins}</li> * <li>{@code totalOfPointDiff}</li> * <li>{@code pairsOfGames}</li> * </ul> * <p> * The {@code scores} key references a {@code List<Integer>} value that contains * the game scores of this series. * <p> * The {@code numberOfWins} key references a {@code double} value where each win * count 1, a loss count -1, and a tie is valued 0. * <p> * The {@code totalOfPointDiff} key references a {@code long} value that sum all * the game scores. * <p> * The {@code pairsOfGames} key references a {@code List<Map<String, Object>>} value * that contains a detail map for each pair of games. * <p> * All the results are calculated from the strategyOne's point of view. * * @param actorOne the first actor * @param actorTwo the second actor * @param nPairs the number of paired game to play * @param gameDuration the game duration assigned to both players * * @return a map hosting the scores, the number of wins, * the total of point difference */ public static Map<String, Object> reversiSeries(final Actor actorOne, final Actor actorTwo, final int nPairs, final Duration gameDuration) { final Map<String, Object> results = new HashMap<String, Object>(); final List<Integer> scores = new ArrayList<Integer>(); final List<Map<String, Object>> pairsOfGames = new ArrayList<Map<String, Object>>(); long totalOfPointDiff = 0; double numberOfWins = 0.; for (int i = 0; i < nPairs; i++) { Map<String, Object> details = new HashMap<String, Object>(); pairsOfGames.add(details); Game.Builder gb = randomInitialization(actorOne, actorTwo, gameDuration, NUMBER_OF_RANDOM_MOVES); Game gameOne = gb.build(); Game gameTwo = flipActors(gb).build(); int resOne = gameOne.play(); int resTwo = gameTwo.play(); scores.add(+resOne); scores.add(-resTwo); details.put("resOne", resOne); details.put("resTwo", resTwo); details.put("gameOne", gameOne); details.put("gameTwo", gameTwo); } for (int score : scores) { totalOfPointDiff += score; if (score > 0) { numberOfWins++; } else if (score == 0) { numberOfWins += 1. / 2; } } results.put("scores", scores); results.put("totalOfPointDiff", totalOfPointDiff); results.put("numberOfWins", numberOfWins); results.put("pairsOfGames", pairsOfGames); return results; } /** * !!! MUST BE COMPLETED!!! * Returns a map containing the otcames of the round robin matches. * <p> * The result map has these entries: * - actorsAsList * - nPairs * - matchResults * <p> * Parameter {@code actors} cannot be null. * Parameter {@code nPairs} cannot be null. * Parameter {@code gameDuration} cannot be null. * * @param actors a set of actors partecipating the round robin * @param nPairs the numder of pairs of matches * @param gameDuration the duration of each game * @return a result map */ public static Map<String, Object> roundRobin(final Set<Actor> actors, final int nPairs, final Duration gameDuration) { List<Actor> actorsAsList = new ArrayList<Actor>(actors); final Map<String, Object> results = new HashMap<String, Object>(); results.put("actorsAsList", actorsAsList); results.put("nPairs", nPairs); ListIterator<Actor> firstCursor = actorsAsList.listIterator(); while (firstCursor.hasNext()) { Actor defender = firstCursor.next(); ListIterator<Actor> secondCursor = actorsAsList.listIterator(firstCursor.nextIndex()); while (secondCursor.hasNext()) { Actor challenger = secondCursor.next(); results.put("matchResults" + firstCursor.nextIndex() + "-" + secondCursor.nextIndex(), reversiSeries(defender, challenger, nPairs, gameDuration)); } } return results; } /** * The method receives the {@code results} map and post process it formatting a report * returned as a string object. * <p> * * @param results the round robin result map * @return a string holding the formatted report */ public static String postProcessRoundRobinResults(final Map<String, Object> results) { StringBuilder report = new StringBuilder(); @SuppressWarnings("unchecked") List<Actor> actorsAsList = (List<Actor>) results.get("actorsAsList"); @SuppressWarnings("unchecked") int nPairs = (Integer) results.get("nPairs"); int maxActorStringLength = 0; for (Actor actor : actorsAsList) { int tmp = actor.print().length(); if (tmp > maxActorStringLength) { maxActorStringLength = tmp; } } String actorFormatter = "%" + maxActorStringLength + "s"; for (Actor defender : actorsAsList) { StringWriter swReportLine = new StringWriter(); PrintWriter pwReportLine = new PrintWriter(swReportLine); StringWriter swReportGrid = new StringWriter(); PrintWriter pwReportGrid = new PrintWriter(swReportGrid); pwReportLine.printf(actorFormatter, defender.print()); double totalNumberOfWins = 0.; for (Actor challenger : actorsAsList) { int idxDefender = actorsAsList.indexOf(defender) + 1; int idxChallenger = actorsAsList.indexOf(challenger) + 1; double numberOfWins = 0.; if (challenger == defender) { numberOfWins = 0.; pwReportGrid.printf(" ===== "); } else if (idxChallenger < idxDefender) { numberOfWins = (2 * nPairs) - numberOfWins(results, idxChallenger, idxDefender); pwReportGrid.printf(" %5.1f ", numberOfWins); } else { numberOfWins = numberOfWins(results, idxDefender, idxChallenger); pwReportGrid.printf(" %5.1f ", numberOfWins); } totalNumberOfWins += numberOfWins; } pwReportLine.printf(" ... %6.1f:", totalNumberOfWins); pwReportLine.printf("%s", swReportGrid.toString()); report.append(swReportLine.toString()).append("\n"); } return report.toString(); } /** * Returns the number of games won by the defender against the challenger. * The data is extracted by a result map. * <p> * Parameter {@code results} results cannot be null and must be structured consistently. * Parameters {@code idxDefender} and {@code idxChallenger} must be bounded * accordingly to the data contained by the {@code results} parameter. * * @param results the mat having the data to be extracted * @param idxDefender the index of the first actor * @param idxChallenger the index of the second actor * @return the number of wins extracted by the {@code result} map. */ private static double numberOfWins(final Map<String, Object> results, final int idxDefender, final int idxChallenger) { @SuppressWarnings("unchecked") Map<String, Object> matchResults = (Map<String, Object>) results .get("matchResults" + idxDefender + "-" + idxChallenger); @SuppressWarnings("unchecked") double numberOfWins = (Double) matchResults.get("numberOfWins"); return numberOfWins; } /** * The number of put disc moves played randomly in order to * initialize the board for a reversi series. */ private static final int NUMBER_OF_RANDOM_MOVES = 10; /** * Returns a game builder configured with the assigned actors, the provided * {@code gameDuration}, and a random game position generated by running the * assigned {@code numberOfRandomMoves}. * * @param actorOne the first actor * @param actorTwo the second actor * @param gameDuration the game duration assigned * @param numberOfRandomMoves the number of put disc moves to run * @return a new game builder configured as per the provided parameters */ private static Game.Builder randomInitialization(final Actor actorOne, final Actor actorTwo, final Duration gameDuration, final int numberOfRandomMoves) { return new Game.Builder() .withActors(new ActorsPair.Builder().withActor(Player.BLACK, actorOne) .withActor(Player.WHITE, actorTwo).build()) .withPrintStream(new NullPrintStream()) .withSequence(new GameSequence.Builder() .withSnapshots(new GameSnapshot.Builder().withClock(Clock.initialClock(gameDuration)) .withPosition(Game.randomGame(numberOfRandomMoves).position()) .withRegister(MoveRegister.empty(Player.NULL)).build()) .build()); } /** * Modify the provided {@code gb} parameter flipping the two actors, than return it. * * @param gb the game builder to update * @return the reference to the modified game builder {@code gb} parameter */ private static Game.Builder flipActors(final Game.Builder gb) { Actor one = gb.build().actors().get(Player.BLACK); Actor two = gb.build().actors().get(Player.WHITE); return gb.withActors( new ActorsPair.Builder().withActor(Player.BLACK, two).withActor(Player.WHITE, one).build()); } /** * The main entry point for the Reversi Program. * * @param args an array having two elements: [black's strategy, white's strategy] */ public static void main(final String[] args) { if (args == null || args.length != 2) { System.out.println("Argument list error: blackStrategy and whiteStrategy must be provided."); usage(); System.exit(ERROR_CODE_1); } Strategy[] s = new Strategy[] { null, null }; for (int i = 0; i < s.length; i++) { Object o = null; try { Class<?> c = Class.forName(args[i]); o = c.newInstance(); } catch (ClassNotFoundException e) { System.out.println("Exception e: " + e); usage(); System.exit(ERROR_CODE_2); } catch (InstantiationException e) { System.out.println("Exception e: " + e); usage(); System.exit(ERROR_CODE_3); } catch (IllegalAccessException e) { System.out.println("Exception e: " + e); usage(); System.exit(ERROR_CODE_4); } try { s[i] = (Strategy) o; } catch (ClassCastException e) { System.out.println("Exception e: " + e); usage(); System.exit(ERROR_CODE_5); } } reversi(s[0], s[1], System.out, Period.minutes(DEFAULT_GAME_DURATION_IN_MINUTES).toStandardDuration()); } /** * Print the usage message. */ private static void usage() { System.out.println("usage: java rcrr.reversi.Reversi blackStrategy whiteStrategy"); System.out.println("\t Where blackStrategy and whiteStrategy are two classes"); System.out.println("\t that implements the rcrr.reversi.Strategy interface."); } }