Working on SAN serialization of moves

+ SAN generation for castling moves
+ Appending promotion piece symbol in pawn promotion SAN

+ Asking the user to view a generated PGN file
This commit is contained in:
Kai S. K. Engelbart 2019-11-24 21:31:32 +01:00
parent 6cb0da80d7
commit 4cb6e0e08b
Signed by: kske
GPG Key ID: 8BEB13EC5DF7EF13
5 changed files with 62 additions and 28 deletions

View File

@ -4,7 +4,7 @@ package dev.kske.chess.board;
* Project: <strong>Chess</strong><br>
* File: <strong>Castling.java</strong><br>
* Created: <strong>2 Nov 2019</strong><br>
*
*
* @since Chess v0.5-alpha
* @author Kai S. K. Engelbart
*/
@ -32,4 +32,13 @@ public class Castling extends Move {
super.revert(board, capturedPiece);
rookMove.revert(board, null);
}
/**
* @return {@code O-O-O} for a queenside castling or {@code O-O} for a kingside
* castling
*/
@Override
public String toSAN(Board board) {
return rookMove.pos.x == 0 ? "O-O-O" : "O-O";
}
}

View File

@ -12,7 +12,7 @@ import dev.kske.chess.board.Piece.Color;
* Project: <strong>Chess</strong><br>
* File: <strong>Move.java</strong><br>
* Created: <strong>02.07.2019</strong><br>
*
*
* @since Chess v0.1-alpha
* @author Kai S. K. Engelbart
*/
@ -50,7 +50,7 @@ public class Move {
if (move.length() == 5) {
try {
return new PawnPromotion(pos, dest, Piece.fromFirstChar(move.charAt(4)));
} catch (NoSuchMethodException | SecurityException | IllegalArgumentException e) {
} catch (Exception e) {
e.printStackTrace();
return null;
}
@ -62,7 +62,7 @@ public class Move {
/**
* Converts a move string from standard algebraic notation to a {@link Move}
* object.
*
*
* @param sanMove the move string to convert from
* @param board the board on which the move has to be executed
* @return the converted {@link Move} object
@ -112,7 +112,7 @@ public class Move {
if (m.group("promotedTo") != null) {
try {
move = new PawnPromotion(pos, dest, Piece.fromFirstChar(m.group("promotedTo").charAt(0)));
} catch (NoSuchMethodException | SecurityException | IllegalArgumentException e) {
} catch (Exception e) {
e.printStackTrace();
}
} else move = new Move(pos, dest);
@ -130,7 +130,7 @@ public class Move {
if (m.group("promotedTo") != null) {
try {
move = new PawnPromotion(pos, dest, Piece.fromFirstChar(m.group("promotedTo").charAt(0)));
} catch (NoSuchMethodException | SecurityException | IllegalArgumentException e) {
} catch (Exception e) {
e.printStackTrace();
}
} else move = new Move(pos, dest);
@ -147,7 +147,27 @@ public class Move {
return null;
}
public String toSAN(Board board) { return null; }
public String toSAN(Board board) {
final Piece piece = board.get(pos);
StringBuilder sb = new StringBuilder(8);
// Piece symbol
if(!(piece instanceof Pawn))
sb.append(Character.toUpperCase(piece.firstChar()));
// Position
// TODO: Deconstruct position into optional file or rank
// TODO: Omit if the move is a pawn push
sb.append(pos.toLAN());
// Capture indicator
if (board.get(dest) != null) sb.append('x');
// Destination
sb.append(dest.toLAN());
return sb.toString();
}
public boolean isHorizontal() { return getyDist() == 0; }

View File

@ -7,7 +7,7 @@ import java.util.List;
* Project: <strong>Chess</strong><br>
* File: <strong>Pawn.java</strong><br>
* Created: <strong>01.07.2019</strong><br>
*
*
* @since Chess v0.1-alpha
* @author Kai S. K. Engelbart
*/
@ -74,7 +74,7 @@ public class Pawn extends Piece {
moves.add(new PawnPromotion(pos, dest, Rook.class));
moves.add(new PawnPromotion(pos, dest, Knight.class));
moves.add(new PawnPromotion(pos, dest, Bishop.class));
} catch (NoSuchMethodException | SecurityException | IllegalArgumentException e) {
} catch (Exception e) {
e.printStackTrace();
}
} else moves.add(move);

View File

@ -10,27 +10,29 @@ import dev.kske.chess.board.Piece.Color;
* Project: <strong>Chess</strong><br>
* File: <strong>PawnPromotion.java</strong><br>
* Created: <strong>2 Nov 2019</strong><br>
*
*
* @since Chess v0.5-alpha
* @author Kai S. K. Engelbart
*/
public class PawnPromotion extends Move {
private final Class<? extends Piece> promotionPieceClass;
private final Constructor<? extends Piece> promotionPieceConstructor;
private final char promotionPieceChar;
public PawnPromotion(Position pos, Position dest, Class<? extends Piece> promotionPieceClass) throws NoSuchMethodException, SecurityException {
public PawnPromotion(Position pos, Position dest, Class<? extends Piece> promotionPieceClass)
throws ReflectiveOperationException, RuntimeException {
super(pos, dest);
this.promotionPieceClass = promotionPieceClass;
// Cache piece constructor
promotionPieceConstructor = promotionPieceClass.getDeclaredConstructor(Color.class, Board.class);
promotionPieceConstructor.setAccessible(true);
// Get piece char
promotionPieceChar = (char) promotionPieceClass.getMethod("firstChar").invoke(promotionPieceConstructor.newInstance(null, null));
}
public PawnPromotion(int xPos, int yPos, int xDest, int yDest, Class<? extends Piece> promotionPiece)
throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException,
InstantiationException {
throws ReflectiveOperationException, RuntimeException {
this(new Position(xPos, yPos), new Position(xDest, yDest), promotionPiece);
}
@ -51,22 +53,19 @@ public class PawnPromotion extends Move {
}
@Override
public String toLAN() {
char promotionPieceChar = '-';
try {
promotionPieceChar = (char) promotionPieceClass.getMethod("firstChar").invoke(promotionPieceConstructor.newInstance(null, null));
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException | SecurityException
| InstantiationException e) {
e.printStackTrace();
}
return pos.toLAN() + dest.toLAN() + promotionPieceChar;
public String toLAN() { return pos.toLAN() + dest.toLAN() + promotionPieceChar; }
@Override
public String toSAN(Board board) {
String san = super.toSAN(board);
return san + Character.toUpperCase(promotionPieceChar);
}
@Override
public int hashCode() {
final int prime = 31;
int result = super.hashCode();
result = prime * result + Objects.hash(promotionPieceClass);
result = prime * result + Objects.hash(promotionPieceChar, promotionPieceConstructor);
return result;
}
@ -74,8 +73,8 @@ public class PawnPromotion extends Move {
public boolean equals(Object obj) {
if (this == obj) return true;
if (!super.equals(obj)) return false;
if (getClass() != obj.getClass()) return false;
if (!(obj instanceof PawnPromotion)) return false;
PawnPromotion other = (PawnPromotion) obj;
return Objects.equals(promotionPieceClass, other.promotionPieceClass);
return promotionPieceChar == other.promotionPieceChar && Objects.equals(promotionPieceConstructor, other.promotionPieceConstructor);
}
}

View File

@ -1,5 +1,6 @@
package dev.kske.chess.ui;
import java.awt.Desktop;
import java.awt.EventQueue;
import java.awt.Toolkit;
import java.awt.dnd.DropTarget;
@ -168,7 +169,6 @@ public class MainWindow extends JFrame {
public void saveFile(File file) {
final int dotIndex = file.getName().lastIndexOf('.');
final String name = file.getName().substring(0, dotIndex);
final String extension = file.getName().substring(dotIndex).toLowerCase();
if (extension.equals(".pgn")) try {
@ -178,6 +178,12 @@ public class MainWindow extends JFrame {
PGNDatabase pgnDB = new PGNDatabase();
pgnDB.getGames().add(pgnGame);
pgnDB.save(file);
if (JOptionPane.showConfirmDialog(this,
"Game export finished. Do you want to view the created file?",
"Game export finished",
JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION)
Desktop.getDesktop().open(file);
} catch (IOException e) {
e.printStackTrace();
JOptionPane.showMessageDialog(this,