From 1dc97ba3de958dc3e611640a5c0596a88dec9b08 Mon Sep 17 00:00:00 2001 From: kske Date: Tue, 5 Nov 2019 05:41:26 +0100 Subject: [PATCH] Added pawn promotion selection * Letting a NaturalPlayer select the promotion piece with a combo box * Optimized reflection use in PawnPromotion * Changed toString method of Move to use LAN Closes #9 --- src/dev/kske/chess/board/Board.java | 7 +-- src/dev/kske/chess/board/Move.java | 6 +-- src/dev/kske/chess/board/Pawn.java | 4 +- src/dev/kske/chess/board/PawnPromotion.java | 40 +++++++++++--- src/dev/kske/chess/game/NaturalPlayer.java | 60 ++++++++++++++++----- 5 files changed, 84 insertions(+), 33 deletions(-) diff --git a/src/dev/kske/chess/board/Board.java b/src/dev/kske/chess/board/Board.java index 997326b..fbbf2bd 100644 --- a/src/dev/kske/chess/board/Board.java +++ b/src/dev/kske/chess/board/Board.java @@ -1,6 +1,5 @@ package dev.kske.chess.board; -import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; @@ -143,8 +142,7 @@ public class Board { if (m.group("promotedTo") != null) { try { move = new PawnPromotion(pos, dest, Piece.fromFirstChar(m.group("promotedTo").charAt(0))); - } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException - | InvocationTargetException | InstantiationException e) { + } catch (NoSuchMethodException | SecurityException | IllegalArgumentException e) { e.printStackTrace(); } } else move = new Move(pos, dest); @@ -162,8 +160,7 @@ public class Board { if (m.group("promotedTo") != null) { try { move = new PawnPromotion(pos, dest, Piece.fromFirstChar(m.group("promotedTo").charAt(0))); - } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException - | InvocationTargetException | InstantiationException e) { + } catch (NoSuchMethodException | SecurityException | IllegalArgumentException e) { e.printStackTrace(); } } else move = new Move(pos, dest); diff --git a/src/dev/kske/chess/board/Move.java b/src/dev/kske/chess/board/Move.java index e016246..a03a78c 100644 --- a/src/dev/kske/chess/board/Move.java +++ b/src/dev/kske/chess/board/Move.java @@ -1,6 +1,5 @@ package dev.kske.chess.board; -import java.lang.reflect.InvocationTargetException; import java.util.Objects; /** @@ -45,8 +44,7 @@ public class Move { if (move.length() == 5) { try { return new PawnPromotion(pos, dest, Piece.fromFirstChar(move.charAt(4))); - } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException - | InstantiationException e) { + } catch (NoSuchMethodException | SecurityException | IllegalArgumentException e) { e.printStackTrace(); return null; } @@ -62,7 +60,7 @@ public class Move { public boolean isDiagonal() { return getxDist() == getyDist(); } @Override - public String toString() { return String.format("%s -> %s", getPos(), getDest()); } + public String toString() { return toLAN(); } @Override public int hashCode() { return Objects.hash(getDest(), getPos(), getxDist(), getxSign(), getyDist(), getySign()); } diff --git a/src/dev/kske/chess/board/Pawn.java b/src/dev/kske/chess/board/Pawn.java index eb048ad..18e275e 100644 --- a/src/dev/kske/chess/board/Pawn.java +++ b/src/dev/kske/chess/board/Pawn.java @@ -1,6 +1,5 @@ package dev.kske.chess.board; -import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.List; @@ -75,8 +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 | IllegalAccessException | IllegalArgumentException | InvocationTargetException - | InstantiationException e) { + } catch (NoSuchMethodException | SecurityException | IllegalArgumentException e) { e.printStackTrace(); } } else moves.add(move); diff --git a/src/dev/kske/chess/board/PawnPromotion.java b/src/dev/kske/chess/board/PawnPromotion.java index e2c6896..91ddaac 100644 --- a/src/dev/kske/chess/board/PawnPromotion.java +++ b/src/dev/kske/chess/board/PawnPromotion.java @@ -2,6 +2,7 @@ package dev.kske.chess.board; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; +import java.util.Objects; import dev.kske.chess.board.Piece.Color; @@ -15,19 +16,16 @@ import dev.kske.chess.board.Piece.Color; */ public class PawnPromotion extends Move { + private final Class promotionPieceClass; private final Constructor promotionPieceConstructor; - private final char promotionPieceChar; - public PawnPromotion(Position pos, Position dest, Class promotionPiece) throws NoSuchMethodException, SecurityException, - IllegalAccessException, IllegalArgumentException, InvocationTargetException, InstantiationException { + public PawnPromotion(Position pos, Position dest, Class promotionPieceClass) throws NoSuchMethodException, SecurityException { super(pos, dest); + this.promotionPieceClass = promotionPieceClass; // Cache piece constructor - promotionPieceConstructor = promotionPiece.getDeclaredConstructor(Color.class, Board.class); + promotionPieceConstructor = promotionPieceClass.getDeclaredConstructor(Color.class, Board.class); promotionPieceConstructor.setAccessible(true); - - // Cache piece first char - promotionPieceChar = (char) promotionPiece.getMethod("firstChar").invoke(promotionPieceConstructor.newInstance(null, null)); } public PawnPromotion(int xPos, int yPos, int xDest, int yDest, Class promotionPiece) @@ -53,5 +51,31 @@ public class PawnPromotion extends Move { } @Override - public String toLAN() { return pos.toLAN() + dest.toLAN() + promotionPieceChar; } + 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; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = super.hashCode(); + result = prime * result + Objects.hash(promotionPieceClass); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (!super.equals(obj)) return false; + if (getClass() != obj.getClass()) return false; + PawnPromotion other = (PawnPromotion) obj; + return Objects.equals(promotionPieceClass, other.promotionPieceClass); + } } diff --git a/src/dev/kske/chess/game/NaturalPlayer.java b/src/dev/kske/chess/game/NaturalPlayer.java index 4e12c04..864c4e5 100644 --- a/src/dev/kske/chess/game/NaturalPlayer.java +++ b/src/dev/kske/chess/game/NaturalPlayer.java @@ -5,8 +5,11 @@ import java.awt.event.MouseListener; import java.util.List; import java.util.stream.Collectors; -import dev.kske.chess.board.Board; +import javax.swing.JComboBox; +import javax.swing.JOptionPane; + import dev.kske.chess.board.Move; +import dev.kske.chess.board.Piece; import dev.kske.chess.board.Piece.Color; import dev.kske.chess.board.Position; import dev.kske.chess.ui.OverlayComponent; @@ -24,7 +27,8 @@ public class NaturalPlayer extends Player implements MouseListener { private final OverlayComponent overlayComponent; private boolean moveRequested; - private Position pos; + private Piece selectedPiece; + private List possibleMoves; public NaturalPlayer(Color color, OverlayComponent overlayComponent) { super(color); @@ -47,22 +51,52 @@ public class NaturalPlayer extends Player implements MouseListener { @Override public void mousePressed(MouseEvent evt) { if (!moveRequested) return; - if (pos == null) { - pos = new Position(evt.getPoint().x / overlayComponent.getTileSize(), evt.getPoint().y / overlayComponent.getTileSize()); + if (selectedPiece == null) { - Board board = new Board(this.board); - if (board.get(pos) != null && board.get(pos).getColor() == color) { - List positions = board.getMoves(pos).stream().map(move -> move.getDest()).collect(Collectors.toList()); - overlayComponent.displayDots(positions); - } else pos = null; + // Get selected Piece + final Position pos = new Position(evt.getPoint().x / overlayComponent.getTileSize(), evt.getPoint().y / overlayComponent.getTileSize()); + selectedPiece = board.get(pos); + + // Check if a piece was selected + if (selectedPiece != null) { + + // Discard selection if the piece has the wrong color + if (selectedPiece.getColor() == color.opposite()) selectedPiece = null; + else { + + // Generate all moves possible with the selected piece and display their + // destinations + possibleMoves = selectedPiece.getMoves(pos); + overlayComponent.displayDots(possibleMoves.stream().map(move -> move.getDest()).collect(Collectors.toList())); + } + } } else { Position dest = new Position(evt.getPoint().x / overlayComponent.getTileSize(), evt.getPoint().y / overlayComponent.getTileSize()); + // Get all moves leading to the specified destination + List selectedMoves = possibleMoves.stream().filter(m -> m.getDest().equals(dest)).collect(Collectors.toList()); + if (!selectedMoves.isEmpty()) { + Move move; + + // Process pawn promotion if necessary + if (selectedMoves.size() > 1) { + + // Let the user select a promotion piece + JComboBox comboBox = new JComboBox(selectedMoves.toArray(new Move[0])); + JOptionPane.showMessageDialog(overlayComponent, comboBox, "Select a promotion", JOptionPane.QUESTION_MESSAGE); + + move = selectedMoves.get(comboBox.getSelectedIndex()); + } else move = selectedMoves.get(0); + + // Tell the game to execute the move + moveRequested = false; + game.onMove(NaturalPlayer.this, move); + } + + // Discard the selection overlayComponent.clearDots(); - moveRequested = false; - // TODO: Special moves - game.onMove(NaturalPlayer.this, new Move(pos, dest)); - pos = null; + selectedPiece = null; + possibleMoves = null; } }