From f4b399ae43bdefba8e4f107212729636a5603e17 Mon Sep 17 00:00:00 2001 From: kske Date: Sun, 7 Jul 2019 14:37:33 +0200 Subject: [PATCH] Added simple (yet surprisingly effective) AI player + AIPlayer class + Evaluation method in Board + AI vs AI button in GameModeDialog (produces a rendering delay) --- src/dev/kske/chess/board/Board.java | 30 +++++++++++++++ src/dev/kske/chess/game/AIPlayer.java | 47 +++++++++++++++++++++++ src/dev/kske/chess/game/Game.java | 6 ++- src/dev/kske/chess/ui/GameModeDialog.java | 22 ++++++++++- 4 files changed, 102 insertions(+), 3 deletions(-) create mode 100644 src/dev/kske/chess/game/AIPlayer.java diff --git a/src/dev/kske/chess/board/Board.java b/src/dev/kske/chess/board/Board.java index 5f7347e..9c29145 100644 --- a/src/dev/kske/chess/board/Board.java +++ b/src/dev/kske/chess/board/Board.java @@ -131,6 +131,36 @@ public class Board { : getMoves(color).isEmpty() ? GameEventType.STALEMATE : GameEventType.NONE; } + /** + * Evaluated the board. + * + * @param color The color to evaluate for + * @return An positive number representing how good the position is + */ + public int evaluate(Color color) { + int score = 0; + for (int i = 0; i < 8; i++) + for (int j = 0; j < 8; j++) + if (boardArr[i][j] != null && boardArr[i][j].getColor() == color) switch (boardArr[i][j].getType()) { + case QUEEN: + score += 8; + break; + case ROOK: + score += 5; + break; + case KNIGHT: + score += 3; + break; + case BISHOP: + score += 3; + break; + case PAWN: + score += 1; + break; + } + return score; + } + public void registerGameEventListener(GameEventListener listener) { gameEventListeners.add(listener); } diff --git a/src/dev/kske/chess/game/AIPlayer.java b/src/dev/kske/chess/game/AIPlayer.java new file mode 100644 index 0000000..4dad40d --- /dev/null +++ b/src/dev/kske/chess/game/AIPlayer.java @@ -0,0 +1,47 @@ +package dev.kske.chess.game; + +import dev.kske.chess.board.Board; +import dev.kske.chess.board.Move; +import dev.kske.chess.board.Piece; +import dev.kske.chess.board.Piece.Color; + +/** + * Project: Chess
+ * File: AIPlayer.java
+ * Created: 06.07.2019
+ * Author: Kai S. K. Engelbart + */ +public class AIPlayer extends Player { + + private Move bestMove; + + public AIPlayer(Board board, Color color) { + super(board, color); + } + + @Override + public void requestMove() { + findBestMove(board, color, 0); + game.onMove(this, bestMove); + } + + private int findBestMove(Board board, Color color, int depth) { + int bestValue = Integer.MIN_VALUE; + for (Move move : board.getMoves(color)) { + Piece capturePiece = board.move(move); + int teamValue = board.evaluate(color); + int enemyValue = board.evaluate(color.opposite()); + int valueChange = teamValue - enemyValue; + + if (depth < 4) valueChange -= findBestMove(board, color.opposite(), depth + 1); + + if (valueChange > bestValue) { + bestValue = valueChange; + if (depth == 0) bestMove = move; + } + + board.revert(move, capturePiece); + } + return bestValue; + } +} diff --git a/src/dev/kske/chess/game/Game.java b/src/dev/kske/chess/game/Game.java index 80fad76..487a421 100644 --- a/src/dev/kske/chess/game/Game.java +++ b/src/dev/kske/chess/game/Game.java @@ -5,6 +5,7 @@ import java.util.Map; import dev.kske.chess.board.Board; import dev.kske.chess.board.Move; import dev.kske.chess.board.Piece.Color; +import dev.kske.chess.ui.BoardPanel; /** * Project: Chess
@@ -16,10 +17,12 @@ public class Game { private Map players; private Board board; + private BoardPanel boardPanel; - public Game(Map players, Board board) { + public Game(Map players, Board board, BoardPanel boardPanel) { this.players = players; this.board = board; + this.boardPanel = boardPanel; // Initialize the game variable in each player players.values().forEach(player -> player.setGame(this)); @@ -32,6 +35,7 @@ public class Game { public void onMove(Player player, Move move) { if (board.getPos(move).getColor() == player.color && board.attemptMove(move)) { System.out.printf("%s: %s%n", player.color, move); + boardPanel.repaint(); players.get(player.color.opposite()).requestMove(); } else { System.out.printf("%s: Illegal move!%n", player.getColor()); diff --git a/src/dev/kske/chess/ui/GameModeDialog.java b/src/dev/kske/chess/ui/GameModeDialog.java index 5461767..fbc8835 100644 --- a/src/dev/kske/chess/ui/GameModeDialog.java +++ b/src/dev/kske/chess/ui/GameModeDialog.java @@ -8,6 +8,7 @@ import javax.swing.JButton; import javax.swing.JDialog; import dev.kske.chess.board.Piece.Color; +import dev.kske.chess.game.AIPlayer; import dev.kske.chess.game.Game; import dev.kske.chess.game.NaturalPlayer; import dev.kske.chess.game.Player; @@ -29,7 +30,7 @@ public class GameModeDialog extends JDialog { super(); setModal(true); setTitle("Game Mode Selection"); - setBounds(100, 100, 231, 99); + setBounds(100, 100, 231, 133); setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); getContentPane().setLayout(new FlowLayout(FlowLayout.CENTER, 5, 5)); @@ -38,12 +39,29 @@ public class GameModeDialog extends JDialog { Map players = new HashMap<>(); players.put(Color.WHITE, new NaturalPlayer(boardPanel.getBoard(), Color.WHITE, boardPanel)); players.put(Color.BLACK, new NaturalPlayer(boardPanel.getBoard(), Color.BLACK, boardPanel)); - new Game(players, boardPanel.getBoard()).start(); + new Game(players, boardPanel.getBoard(), boardPanel).start(); dispose(); }); getContentPane().add(btnNatural); JButton btnAI = new JButton("Game against AI"); + btnAI.addActionListener((evt) -> { + Map players = new HashMap<>(); + players.put(Color.WHITE, new NaturalPlayer(boardPanel.getBoard(), Color.WHITE, boardPanel)); + players.put(Color.BLACK, new AIPlayer(boardPanel.getBoard(), Color.BLACK)); + new Game(players, boardPanel.getBoard(), boardPanel).start(); + dispose(); + }); getContentPane().add(btnAI); + + JButton btnAI2 = new JButton("AI against AI"); + btnAI2.addActionListener((evt) -> { + Map players = new HashMap<>(); + players.put(Color.WHITE, new AIPlayer(boardPanel.getBoard(), Color.WHITE)); + players.put(Color.BLACK, new AIPlayer(boardPanel.getBoard(), Color.BLACK)); + new Game(players, boardPanel.getBoard(), boardPanel).start(); + dispose(); + }); + getContentPane().add(btnAI2); } }