// Compare.java package net.sf.gogui.tools.twogtp; import java.io.File; import java.io.FileInputStream; import java.util.ArrayList; import java.util.Collections; import java.util.Map; import java.util.TreeMap; import net.sf.gogui.game.ConstNode; import net.sf.gogui.game.GameTree; import net.sf.gogui.go.Board; import net.sf.gogui.go.BoardUtil; import net.sf.gogui.go.ConstBoard; import net.sf.gogui.go.GoColor; import static net.sf.gogui.go.GoColor.BLACK_WHITE_EMPTY; import net.sf.gogui.go.GoPoint; import net.sf.gogui.go.Move; import net.sf.gogui.go.PointList; import net.sf.gogui.sgf.SgfReader; /** Find duplicates in games. */ public final class Compare { public static final class Placement { public boolean m_isSetup; public GoColor m_color; public GoPoint m_point; public Placement(boolean isSetup, GoColor color, GoPoint point) { m_isSetup = isSetup; m_color = color; m_point = point; } public Placement(Move move) { this(false, move.getColor(), move.getPoint()); } } /** Check if game already exists in game collection. All games must have the same board size. Also finds rotated duplicates. @param board Board with the correct size (only used for Board.rotate). @param moves Moves of game to check. @param games Games in collection. The key is the game number, the value is the sequence of moves. @param useAlternate If true, assume that players are exchanged every second game. Only check games where player played the same color. @param isAlternated If useAlternate, indicate if game to check had players exchanged. @return String containing number of first identical game in collection or "-" if no duplicate was found. If a nearly identical game is found (<= 20% identical moves comparing moves by number), the game number is returned with a question mark appended. */ public static String checkDuplicate(ConstBoard board, ArrayList<Placement> moves, Map<Integer, ArrayList<Placement>> games, boolean useAlternate, boolean isAlternated) { String result = "-"; int size = board.getSize(); for (Map.Entry<Integer, ArrayList<Placement>> entry : games.entrySet()) { int numberGame = entry.getKey(); if (useAlternate && ((numberGame % 2 != 0) != isAlternated)) continue; ArrayList<Placement> gameMoves = entry.getValue(); for (int rot = 0; rot < BoardUtil.NUMBER_ROTATIONS; ++rot) { int numberDifferent = 0; int moveNumber = moves.size(); int maxDifferent = moveNumber / 5; if (gameMoves.size() != moveNumber) { numberDifferent = Math.abs(gameMoves.size() - moveNumber); moveNumber = Math.min(gameMoves.size(), moveNumber); } for (int i = 0; numberDifferent <= maxDifferent && i < moveNumber; ++i) { Placement move = moves.get(i); Placement gameMove = gameMoves.get(i); GoPoint gameRotatedPoint = BoardUtil.rotate(rot, gameMove.m_point, size); if (move.m_isSetup != gameMove.m_isSetup || ! move.m_color.equals(gameMove.m_color) || ! GoPoint.equals(move.m_point, gameRotatedPoint)) ++numberDifferent; } if (numberDifferent == 0) return Integer.toString(numberGame); else if (numberDifferent < maxDifferent) result = Integer.toString(numberGame) + "?"; } } return result; } /** Compare a set of SGF files. Prints the results to standard output, one line per game with the filename and the duplicate information as returned by Compare.checkDuplicate. @param filenames List of filenames @throws Exception If reading one of the files fails. */ public static void compare(ArrayList<String> filenames) throws Exception { Board board = null; Map<Integer, ArrayList<Placement>> games = new TreeMap<Integer, ArrayList<Placement>>(); for (int gameNumber = 0; gameNumber < filenames.size(); ++gameNumber) { String filename = filenames.get(gameNumber); File file = new File(filename); FileInputStream fileStream = new FileInputStream(file); SgfReader reader = new SgfReader(fileStream, file, null, 0); GameTree tree = reader.getTree(); int size = tree.getBoardSize(); if (board == null) board = new Board(size); else if (size != board.getSize()) throw new Exception("Board size in " + filename + " does not match other games"); ArrayList<Placement> moves = getPlacements(tree.getRoot()); String duplicate = checkDuplicate(board, moves, games, false, false); System.out.println(Integer.toString(gameNumber) + " " + filename + " " + duplicate); games.put(gameNumber, moves); } } public static ArrayList<Placement> getPlacements(ConstNode node) { ArrayList<Placement> result = new ArrayList<Placement>(512); while (node != null) { for (GoColor c : BLACK_WHITE_EMPTY) { PointList list = new PointList(node.getSetup(c)); Collections.sort(list); for (GoPoint p : list) result.add(new Placement(true, c, p)); } Move move = node.getMove(); if (move != null) result.add(new Placement(move)); node = node.getChildConst(); } return result; } /** Make constructor unavailable; class is for namespace only. */ private Compare() { } }