package logic;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.util.List;
import java.util.Map;
import java.util.Scanner;
import java.util.StringTokenizer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import models.Board;
import models.Game;
import models.Square;
import utility.FileUtility;
import com.google.common.collect.Maps;
/**
* AlgebraicConverter.java Class to convert Move objects to Algebraic Chess
* Notation and vice versa.
*
* @author Drew Hannay & Alisa Maas CSCI 335, Wheaton College, Spring 2011 Phase
* 2 April 7, 2011
*/
public final class AlgebraicConverter
{
/**
* Static final String[] used for converting integers to letters for ACN
*/
private static final String columns = "%abcdefgh"; //$NON-NLS-1$
public static final char KNIGHT = 'N';
public static final char BISHOP = 'B';
public static final char KING = 'K';
public static final char QUEEN = 'Q';
public static final char ROOK = 'R';
/**
* HashMap to move from the char representation of a Class to the actual
* Class
*/
private static Map<Character, String> map = Maps.newHashMap();
static
{
map.put(BISHOP, Messages.getString("bishop")); //$NON-NLS-1$
map.put(KING, Messages.getString("king")); //$NON-NLS-1$
map.put(KNIGHT, Messages.getString("knight")); //$NON-NLS-1$
map.put(QUEEN, Messages.getString("queen")); //$NON-NLS-1$
map.put(ROOK, Messages.getString("rook")); //$NON-NLS-1$
}
/**
* Convert a single string of ACN to a Move object. Use the Pattern Matcher
* to decide what properties to give the Move and then return the created
* Move object
*
* @param s The String of ACN to convert to a Move.
* @param board The Board on which this Move is to be played
* @return The completed Move object
* @throws Exception Creating a new move requires an exception handler
*/
private static Move algToMove(String s, Board board) throws Exception
{
Move move = null;
Matcher result = null;
String pieceKlass = null;
String promo = null;
int origRow = 0;
int origCol = 0;
Square orig = null;
Square dest = null;
result = getPattern().matcher(s);
if (result.find())
{
if (result.group(1).equals("O-O-O") || result.group(1).equals("0-0-0")) //$NON-NLS-1$ //$NON-NLS-2$
{
move = new Move(board, Move.CASTLE_QUEEN_SIDE);
}
else if (result.group(1).equals("O-O") || result.group(1).equals("0-0")) //$NON-NLS-1$ //$NON-NLS-2$
{
move = new Move(board, Move.CASTLE_KING_SIDE);
}
else
{
if (result.group(2) != null)
{
pieceKlass = map.get(result.group(2).charAt(0));
}
if (result.group(3) != null)
{
origCol = columns.indexOf((result.group(3).charAt(0) + "").toLowerCase()); //$NON-NLS-1$
}
if (result.group(4) != null)
{
origRow = Integer.parseInt(result.group(4).charAt(0) + ""); //$NON-NLS-1$
}
dest = board.getSquare(Integer.parseInt(result.group(7).charAt(0) + ""), //$NON-NLS-1$
columns.indexOf((result.group(6).charAt(0) + "").toLowerCase())); //$NON-NLS-1$
if (origCol < 1 || origRow < 1)
{
if (pieceKlass == null)
{
pieceKlass = Messages.getString("pawn"); //$NON-NLS-1$
}
orig = board.getOriginSquare(pieceKlass, origCol, origRow, dest);
}
else
{
orig = board.getSquare(origRow, origCol);
}
if (result.group(8) != null)
{ // promotion
if (result.group(8).contains("=") || result.group(8).contains("(")) //$NON-NLS-1$ //$NON-NLS-2$
{
promo = map.get(result.group(8).charAt(1)); // 0 is '='
// or '('
}
else
{
promo = map.get(result.group(8).charAt(0));
}
}
if (promo == null)
{
move = new Move(board, orig, dest);
}
else
{
move = new Move(board, orig, dest, promo);
}
}
}
return move;
}
/**
* Convert a File of Algebraic Chess Notation to a Game object Read in the
* file, move by move, and convert each String to a Move object and add it
* to the history of the Game. Make sure as you're moving through the file
* that you haven't reached the end and gotten null Moves.
*
* @param game The Game in which to play the Moves
* @param f The File from which to read in ACN
* @return The Game object with it's history added
* @throws Exception when dealing with files, handle exceptions.
*/
public static Game convert(Game game, File f) throws Exception
{
// Since this is Classic chess, it'll always be Boards[0]
Board board = game.getBoards()[0];
game.setIsPlayback(true);
StringTokenizer st;
Scanner in = new Scanner(f);
while (in.hasNext())
{
st = new StringTokenizer(in.nextLine());
// Move past the turn number...we don't care about that
st.nextToken();
String whiteMove = st.nextToken();
if (!st.hasMoreTokens())
{
break;
}
String blackMove = st.nextToken();
Move temp = algToMove(whiteMove, board);
if (temp == null)
{
break;
}
temp.execute();
game.getHistory().add(temp);
game.setBlackMove(true);
temp = null;
temp = algToMove(blackMove, board);
if (temp == null)
{
break;
}
temp.execute();
game.getHistory().add(temp);
game.setBlackMove(false);
}
in.close();
return game;
}
/**
* q Convert a List of Moves to a text file of Algebraic Chess Notation Open
* the file to write to, then iterate through the list of Moves, printing
* each one in the proper format.
*
* @param moves The List of Moves to convert to ACN
* @param pathName The path to where to save the output File
*/
public static void convert(List<Move> moves, String pathName)
{
try
{
BufferedWriter out = new BufferedWriter(new FileWriter(FileUtility.getCompletedGamesFile(pathName)));
String toWrite = ""; //$NON-NLS-1$
for (int i = 0, j = 1; i < moves.size(); i++)
{
String turn = moves.get(i).toString();
if (moves.get(i).result != null)
{
turn = moves.get(i) + (i % 2 == 0 ? (" " + moves.get(i).result) : ("\n" + (j + 1) + " " + moves.get(i).result)); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
}
if (i % 2 != 0 || moves.get(i).result != null)
{
out.write(j + " " + toWrite + " " + turn + '\n'); //$NON-NLS-1$ //$NON-NLS-2$
toWrite = ""; //$NON-NLS-1$
j++;
}
else
{
toWrite += turn;
}
}
out.close();
}
catch (Exception e)
{
e.printStackTrace();
}
}
/**
* Create a Pattern object to match against the files read in Put together a
* String using regular expressions and then use Pattern.compile(pat) to
* return the Pattern
*
* @return The Pattern against which the Strings will be matched
*/
public static Pattern getPattern()
{
String pat = ""; //$NON-NLS-1$
pat += "([O0]-[O0]-[O0]|[O0]-[O0]";// Check for castling (Group 1) //$NON-NLS-1$
pat += "|^([KNQRB])?";// Check for the type of piece that's moving, null //$NON-NLS-1$
// if pawn
pat += "([A-Ha-h])?";// Check for the origin column of the moving piece //$NON-NLS-1$
pat += "([1-8])?";// Check for the origin row of the moving piece //$NON-NLS-1$
pat += "([x:])?";// Check if the move is a capture //$NON-NLS-1$
pat += "([A-Ha-h])([1-8])";// Get the destination column and row. //$NON-NLS-1$
pat += "(=[NBRQ]?|\\([NBRQ]?\\)|[NBRQ])?)";// Check for several //$NON-NLS-1$
// different styles of piece
// promotion
pat += "(e.p)?";// Check for enpassant (might not be marked) //$NON-NLS-1$
pat += "(\\+)?";// Check for check //$NON-NLS-1$
pat += "(\\+)?";// Check for double check //$NON-NLS-1$
pat += "(#)?";// Check if the game is over //$NON-NLS-1$
// ([O0]-[O0]-[O0]|[O0]-[O0]|^([KNQRB])?([A-Ha-h])?([1-8])?([x:])?([A-Ha-h])([1-8])(=[NBRQ]?|\([NBRQ]?\)|[NBRQ])?)(e.p)?(\+)?(\+)?(#)?
// Yay for regular expressions!
return Pattern.compile(pat);
}
}