Skip to content

Commit

Permalink
Added basic parsing functionality.
Browse files Browse the repository at this point in the history
  • Loading branch information
apetresc committed Mar 21, 2010
1 parent e788275 commit f87a21c
Show file tree
Hide file tree
Showing 8 changed files with 336 additions and 9 deletions.
7 changes: 7 additions & 0 deletions build.xml
Expand Up @@ -14,6 +14,7 @@
<property name="dir.build" location="${basedir}/build" />
<property name="dir.lib" location="${basedir}/lib" />
<property name="dir.img" location="${basedir}/img" />
<property name="dir.pgn" location="${basedir}/pgn" />
<property name="dir.build.classes" location="${dir.build}/classes" />
<property name="file.output" location="${dir.build}/${kindlet.title}.azw2" />

Expand Down Expand Up @@ -107,6 +108,12 @@ Download the 'Personal Basis Profile Reference Implementation'. Be sure to sele
<include name="**/*.png" />
</fileset>
</copy>

<copy todir="${dir.build.classes}/pgn">
<fileset dir="${dir.pgn}">
<include name="**/*.pgn" />
</fileset>
</copy>
</target>

<target name="build" depends="compile" description="Alias for compile target"/>
Expand Down
16 changes: 16 additions & 0 deletions pgn/test.pgn
@@ -0,0 +1,16 @@
[Event "F/S Return Match"]
[Site "Belgrade, Serbia Yugoslavia|JUG"]
[Date "1992.11.04"]
[Round "29"]
[White "Fischer, Robert J."]
[Black "Spassky, Boris V."]
[Result "1/2-1/2"]

1. e4 e5 2. Nf3 Nc6 3. Bb5 a6
4. Ba4 Nf6 5. O-O Be7 6. Re1 b5 7. Bb3 d6 8. c3 O-O 9. h3 Nb8 10. d4 Nbd7
11. c4 c6 12. cxb5 axb5 13. Nc3 Bb7 14. Bg5 b4 15. Nb1 h6 16. Bh4 c5 17. dxe5
Nxe4 18. Bxe7 Qxe7 19. exd6 Qf6 20. Nbd2 Nxd6 21. Nc4 Nxc4 22. Bxc4 Nb6
23. Ne5 Rae8 24. Bxf7+ Rxf7 25. Nxf7 Rxe1+ 26. Qxe1 Kxf7 27. Qe3 Qg5 28. Qxg5
hxg5 29. b3 Ke6 30. a3 Kd6 31. axb4 cxb4 32. Ra5 Nd5 33. f3 Bc8 34. Kf2 Bf5
35. Ra7 g6 36. Ra6+ Kc5 37. Ke1 Nf4 38. g3 Nxh3 39. Kd2 Kb5 40. Rd6 Kc5 41. Ra6
Nf2 42. g4 Bd3 43. Re6 1/2-1/2
53 changes: 53 additions & 0 deletions src/com/amazon/kindle/app/chess/BoardController.java
@@ -0,0 +1,53 @@
package com.amazon.kindle.app.chess;

public class BoardController {

private ChessBoard board;

public BoardController(ChessBoard board) {
this.board = board;
}

public void applyMove(String move) throws IllegalMoveException {
int[][] moveCoords = null;
switch(move.length()) {
case 2:
moveCoords = pawnSimpleMove(move);
}

if (moveCoords != null) {
board.move(moveCoords[0][0], moveCoords[0][1], moveCoords[1][0], moveCoords[1][1]);
board.setColorToMove((board.getColorToMove() + 1) % 2);
}
}

private int[][] pawnSimpleMove(String move) throws IllegalMoveException {
int colorToMove = board.getColorToMove();
int[] moveCoords = ChessBoard.convertAlgebraicToCoordinate(move);
int x = moveCoords[0] ; int y = moveCoords[1];
switch (colorToMove) {
case ChessBoard.WHITE:
switch (y) {
case 3:
// Might be a double pawn move
if (board.getSquare(x, 1) == ChessBoard.WHITE_PAWN)
return new int[][] {new int[]{x, 1}, new int[]{x, y}};
default:
if (board.getSquare(x, y - 1) == ChessBoard.WHITE_PAWN)
return new int[][] {new int[]{x, y-1}, new int[]{x, y}};
}
break;
case ChessBoard.BLACK:
switch (y) {
case 4:
// Might be a double pawn move
if (board.getSquare(x, 6) == ChessBoard.BLACK_PAWN)
return new int[][] {new int[]{x, 6}, new int[]{x, y}};
default:
if (board.getSquare(x, y+1) == ChessBoard.BLACK_PAWN)
return new int[][] {new int[]{x, y+1}, new int[]{x, y}};
}
}
throw new IllegalMoveException(move, "There's no pawn to move there!");
}
}
23 changes: 22 additions & 1 deletion src/com/amazon/kindle/app/chess/ChessBoard.java
Expand Up @@ -11,6 +11,7 @@
public class ChessBoard {

private int [][] board;
private int colorToMove;

public static final int BLANK = 0;
public static final int WHITE_PAWN = 1;
Expand All @@ -29,6 +30,8 @@ public class ChessBoard {
public static final int WHITE = 0;
public static final int BLACK = 10;

public static final int OFF_BOARD = -1;

/** The size of the chess board being represented. */
public static final int SIZE = 8;

Expand Down Expand Up @@ -59,6 +62,17 @@ public void init() {
board[2][7] = board[5][7] = BLACK_BISHOP;
board[3][7] = BLACK_QUEEN;
board[4][7] = BLACK_KING;

colorToMove = WHITE;
}

public int getColorToMove() {
return colorToMove;
}

public void setColorToMove(int color) {
if (color == WHITE || color == BLACK)
colorToMove = color;
}

/**
Expand Down Expand Up @@ -115,7 +129,11 @@ public int getSquare(String coordinate) {
* occupying piece (for example, <code>WHITE_PAWN</code>).
*/
public int getSquare(int srcX, int srcY) {
return board[srcX][srcY];
try {
return board[srcX][srcY];
} catch (ArrayIndexOutOfBoundsException aioobe) {
return OFF_BOARD;
}
}

/**
Expand Down Expand Up @@ -172,6 +190,9 @@ public int getColor(String coordinate) {
* if the specified square is colored black.
*/
public int getColor(int srcX, int srcY) {
if (srcX < 0 || srcY < 0 || srcX >= SIZE || srcY >= SIZE) {
return OFF_BOARD;
}
return ((srcX + srcY) % 2) == 0 ? BLACK : WHITE;
}

Expand Down
169 changes: 169 additions & 0 deletions src/com/amazon/kindle/app/chess/ChessRecord.java
@@ -0,0 +1,169 @@
package com.amazon.kindle.app.chess;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.StringTokenizer;

import org.apache.log4j.Logger;

public class ChessRecord {

private static List resultList;

private Map tagPairs;
private List moveList;
private String result;

private final Logger log = Logger.getLogger(ChessBoard.class);

public ChessRecord() {
tagPairs = new HashMap();
moveList = new LinkedList();

resultList = new LinkedList();
resultList.add("1-0");
resultList.add("0-1");
resultList.add("1/2-1/2");
resultList.add("*");
}

public void parsePGN(InputStream inStream) throws IOException, PGNParseException {
BufferedReader in = new BufferedReader(new InputStreamReader(inStream));
String inLine;
while ((inLine = in.readLine()) != null && inLine.trim().startsWith("[") && inLine.trim().endsWith("]")) {
inLine = inLine.trim().substring(1, inLine.trim().length() - 1);
try {
tagPairs.put(inLine.substring(0, inLine.indexOf(" ")), inLine.substring(inLine.indexOf("\"") + 1, inLine.length() -1));
} catch (IndexOutOfBoundsException ioobe) {
throw new PGNParseException("Could not parse tag: " + inLine);
}
}

String moves = "";
while ((inLine = in.readLine()) != null && !resultList.contains(inLine.trim())) {
moves += inLine + " ";
}

StringTokenizer st = new StringTokenizer(moves);
int moveNum = 1;
while(st.hasMoreTokens()) {
if (!st.nextToken().startsWith(moveNum +".")) {
throw new PGNParseException("Could not parse past move " + moveNum);
}

try {
String whiteMove = st.nextToken();
if (resultList.contains(whiteMove)) {
result = whiteMove;
break;
}
if (validateMove(whiteMove)) {
moveList.add(whiteMove);
} else {
throw new PGNParseException("Could not parse White " + moveNum + ": " + whiteMove +".");
}

String blackMove = st.nextToken();
if (resultList.contains(blackMove)) {
result = blackMove;
break;
}
if (validateMove(blackMove)) {
moveList.add(blackMove);
} else {
throw new PGNParseException("Could not parse Black " + moveNum + ": " + blackMove + ".");
}
} catch (NoSuchElementException nsee) {
throw new PGNParseException("Could not parse: Game ended unexpectedly on move " + moveNum);
}
moveNum++;
}
}

public String getTag(String key) {
return (String) tagPairs.get(key);
}

public List getMoves() {
return moveList;
}

private boolean validateMove(String move) {
if (move.endsWith("!")) move = move.substring(0, move.length() - 1);
else if (move.endsWith("?")) move = move.substring(0, move.length() - 1);
else if (move.endsWith("?!")) move = move.substring(0, move.length() - 2);
else if (move.endsWith("!?")) move = move.substring(0, move.length() - 2);
if (move.endsWith("#")) move = move.substring(0, move.length() - 1);
else if (move.endsWith("+")) move = move.substring(0, move.length() - 1);

if (move.equals("O-O") || move.equals("O-O-O")) return true;

switch (move.length()) {
case 1: return false;
case 2: {
char l = move.charAt(0);
char n = move.charAt(1);
if (l >= 'a' && l <= 'h' && n >= '1' && n <= '8')
return true;
}
break;
case 3: {
char p = move.charAt(0);
char l = move.charAt(1);
char n = move.charAt(2);
if ("RNBQK".indexOf(p) >= 0 && l >= 'a' && l <= 'h' && n >= '1' && n <= '8')
return true;
}
break;
case 4: {
if (move.charAt(1) == 'x') {
// Capture
char p = move.charAt(0);
char l = move.charAt(2);
char n = move.charAt(3);
if (("RNBQK".indexOf(p) >= 0 || (p >= 'a' && p <= 'h')) && l >= 'a' && l <= 'h' && n >= '1' && n <= '8')
return true;
} else {
char p = move.charAt(0);
char l1 = move.charAt(1);
char l2 = move.charAt(2);
char n = move.charAt(3);
if ("RNBQK".indexOf(p) >= 0 && l1 >= 'a' && l1 <= 'h' && l2 >= 'a' && l2 <= 'h' && n >= '1' && n <= '8')
return true;
}
}
break;
default: break;
}
return false;
}

public String toString() {
String res = "";
Iterator it = tagPairs.keySet().iterator();
while (it.hasNext()) {
String key = (String) it.next();
res += "[" + key + " \"" + tagPairs.get(key) + "\"]\n";
}
res += "\n";
it = moveList.iterator();
int moveNum = 1;
while (it.hasNext()) {
res += moveNum + ". ";
res += it.next() + " ";
if (it.hasNext()) res += it.next() + "\n";
moveNum++;
}
res += result + "\n";

return res;
}
}
21 changes: 21 additions & 0 deletions src/com/amazon/kindle/app/chess/IllegalMoveException.java
@@ -0,0 +1,21 @@
package com.amazon.kindle.app.chess;

public class IllegalMoveException extends Exception {
private static final long serialVersionUID = -8275239031004025884L;
private String move;
private String reason;

/**
* Constructs a new instance of IllegalMoveException.
* @param move The illegal move.
* @param reason The reason why this move is not legal.
*/
public IllegalMoveException(String move, String reason) {
this.move = move;
this.reason = reason;
}

public String getMessage() {
return "Move " + move + " is illegal: " + reason;
}
}

0 comments on commit f87a21c

Please sign in to comment.