package com.christophdietze.jack.shared.board; import java.util.ArrayList; import java.util.Iterator; import java.util.List; public class SanWriter { private StringBuilder sb; private Position position; private Move move; private Piece fromSquare; private Piece toSquare; public String write(Position position, Move move) throws SanWritingException { this.sb = new StringBuilder(); this.position = position; this.move = move; fromSquare = position.getPiece(move.getFrom()); toSquare = position.getPiece(move.getTo()); boolean isCastle = writeIfCastle(); if (!isCastle) { writePieceSymbol(); writeFrom(); writeCaptureX(); writeTo(); writePromotion(); } writeMateSuffix(); String result = sb.toString(); sb = null; position = null; move = null; fromSquare = null; toSquare = null; return result; } private boolean writeIfCastle() { if (fromSquare.getPieceType() != PieceType.KING) { return false; } if (move.getFrom() == 4 && move.getTo() == 6) { sb.append("O-O"); return true; } if (move.getFrom() == 4 && move.getTo() == 2) { sb.append("O-O-O"); return true; } if (move.getFrom() == 60 && move.getTo() == 62) { sb.append("O-O"); return true; } if (move.getFrom() == 60 && move.getTo() == 58) { sb.append("O-O-O"); return true; } return false; } private void writePieceSymbol() { if (fromSquare.getPieceType() != PieceType.PAWN) { sb.append(fromSquare.getPieceType().getSymbol()); } } private void writeFrom() throws SanWritingException { List<Integer> fromIndices = position.findPieces(fromSquare); if (fromIndices.size() == 0) { throw new SanWritingException("No piece of matching type found"); } if (fromIndices.size() == 1) { return; } List<Move> moveCandidates = new ArrayList<Move>(); for (int fromIndex : fromIndices) { moveCandidates.add(new Move(fromIndex, move.getTo())); } // filter the moves that are pseudo illegal for (Iterator<Move> iterator = moveCandidates.iterator(); iterator.hasNext();) { Move moveCandidate = iterator.next(); if (!MoveChecker.isPseudoLegalMove(position, moveCandidate).isLegal()) { iterator.remove(); } } if (moveCandidates.size() == 0) { throw new SanWritingException("No piece can pseudo-legally do that move"); } if (moveCandidates.size() == 1) { return; } // filter the moves that are really illegal for (Iterator<Move> iterator = moveCandidates.iterator(); iterator.hasNext();) { Move moveCandidate = iterator.next(); if (!MoveChecker.isLegalMove(position, moveCandidate)) { iterator.remove(); } } if (moveCandidates.size() == 1) { return; } // more disambiguation is required - try the following: // 1. only by file // 2. only by rank // 3. by file and rank List<Integer> concurrentFromIndeces = new ArrayList<Integer>(); for (Move moveCandidate : moveCandidates) { if (moveCandidate.getFrom() == move.getFrom()) { continue; } concurrentFromIndeces.add(moveCandidate.getFrom()); } boolean onlyFileSuffices = true; int fromFile = ChessUtils.toFile(move.getFrom()); for (int concurrentIndex : concurrentFromIndeces) { if (ChessUtils.toFile(concurrentIndex) == fromFile) { onlyFileSuffices = false; break; } } if (onlyFileSuffices) { sb.append(ChessUtils.toFileChar(fromFile)); return; } boolean onlyRankSuffices = true; int fromRank = ChessUtils.toRank(move.getFrom()); for (int concurrentIndex : concurrentFromIndeces) { if (ChessUtils.toRank(concurrentIndex) == fromRank) { onlyRankSuffices = false; break; } } if (onlyRankSuffices) { sb.append(ChessUtils.toRankChar(fromRank)); return; } else { sb.append(ChessUtils.toFileChar(fromFile)); sb.append(ChessUtils.toRankChar(fromRank)); return; } } private void writeTo() { sb.append(ChessUtils.toAlgebraicSquare(move.getTo())); } private void writeCaptureX() { if (toSquare.getPieceType() != null) { sb.append("x"); } else { if (fromSquare.getPieceType() == PieceType.PAWN && (toSquare == Piece.WHITE_EN_PASSANT_PAWN || toSquare == Piece.BLACK_EN_PASSANT_PAWN)) { sb.append("x"); } } } private void writePromotion() { if (move.getPromotionPiece() != null) { sb.append("="); sb.append(move.getPromotionPiece().getSymbol()); } } /** * Appends + or # when appropriate */ private void writeMateSuffix() { Position position2 = PositionUtils.makeMove(position, move); position2 = new Position.Builder(position2).whiteToMove(!position2.isWhiteToMove()).build(); if (MoveChecker.canCaptureKing(position2)) { sb.append("+"); // TODO check if it is a mate and append # and set the game result, // if so } } }