From 8ea0c7a6031c4e6676889f18e20fe8d0eb48be68 Mon Sep 17 00:00:00 2001 From: CyB3RC0nN0R Date: Tue, 16 Jul 2019 14:42:10 +0200 Subject: [PATCH] Added alpha-beta pruning threshold to the AI and a configuration dialog --- src/dev/kske/chess/game/ai/AIPlayer.java | 7 +- src/dev/kske/chess/game/ai/MoveProcessor.java | 8 +- src/dev/kske/chess/ui/AIConfigDialog.java | 82 +++++++++++++++++++ src/dev/kske/chess/ui/MenuBar.java | 15 ++-- 4 files changed, 102 insertions(+), 10 deletions(-) create mode 100644 src/dev/kske/chess/ui/AIConfigDialog.java diff --git a/src/dev/kske/chess/game/ai/AIPlayer.java b/src/dev/kske/chess/game/ai/AIPlayer.java index 003afd8..1373dbd 100644 --- a/src/dev/kske/chess/game/ai/AIPlayer.java +++ b/src/dev/kske/chess/game/ai/AIPlayer.java @@ -25,14 +25,16 @@ public class AIPlayer extends Player { private int availableProcessors; private int maxDepth; + private int alphaBetaThreshold; private volatile boolean exitRequested; private volatile ExecutorService executor; - public AIPlayer(Board board, Color color, int maxDepth) { + public AIPlayer(Board board, Color color, int maxDepth, int alphaBetaThreshold) { super(board, color); availableProcessors = Runtime.getRuntime().availableProcessors(); this.maxDepth = maxDepth; + this.alphaBetaThreshold = alphaBetaThreshold; exitRequested = false; } @@ -62,7 +64,8 @@ public class AIPlayer extends Player { if (rem-- > 0) ++endIndex; endIndex += step; processors.add( - new MoveProcessor((Board) board.clone(), moves.subList(beginIndex, endIndex), color, maxDepth)); + new MoveProcessor((Board) board.clone(), moves.subList(beginIndex, endIndex), color, maxDepth, + alphaBetaThreshold)); beginIndex = endIndex; } diff --git a/src/dev/kske/chess/game/ai/MoveProcessor.java b/src/dev/kske/chess/game/ai/MoveProcessor.java index f8f2c24..c32ca08 100644 --- a/src/dev/kske/chess/game/ai/MoveProcessor.java +++ b/src/dev/kske/chess/game/ai/MoveProcessor.java @@ -16,17 +16,19 @@ import dev.kske.chess.board.Piece.Color; public class MoveProcessor implements Callable { private final Board board; - private final List rootMoves;; + private final List rootMoves; private final Color color; private final int maxDepth; + private final int alphaBetaThreshold; private Move bestMove; - public MoveProcessor(Board board, List rootMoves, Color color, int maxDepth) { + public MoveProcessor(Board board, List rootMoves, Color color, int maxDepth, int alphaBetaThreshold) { this.board = board; this.rootMoves = rootMoves; this.color = color; this.maxDepth = maxDepth; + this.alphaBetaThreshold = alphaBetaThreshold; } @Override @@ -43,7 +45,7 @@ public class MoveProcessor implements Callable { int enemyValue = board.evaluate(color.opposite()); int valueChange = teamValue - enemyValue; - if (depth < maxDepth && valueChange >= 0) + if (depth < maxDepth && valueChange >= alphaBetaThreshold) valueChange -= miniMax(board, board.getMoves(color.opposite()), color.opposite(), depth + 1); if (valueChange > bestValue) { diff --git a/src/dev/kske/chess/ui/AIConfigDialog.java b/src/dev/kske/chess/ui/AIConfigDialog.java new file mode 100644 index 0000000..85424bf --- /dev/null +++ b/src/dev/kske/chess/ui/AIConfigDialog.java @@ -0,0 +1,82 @@ +package dev.kske.chess.ui; + +import java.awt.Dimension; + +import javax.swing.JButton; +import javax.swing.JDialog; +import javax.swing.JLabel; +import javax.swing.JSpinner; +import javax.swing.SpinnerNumberModel; + +/** + * Project: Chess
+ * File: AIConfigDialog.java
+ * Created: 16.07.2019
+ * Author: Kai S. K. Engelbart + */ +public class AIConfigDialog extends JDialog { + + private static final long serialVersionUID = -8047984368152479992L; + + private int maxDepth; + private int alphaBetaThreshold; + private boolean startGame = false; + + public AIConfigDialog() { + setSize(new Dimension(293, 212)); + setModal(true); + setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); + setTitle("AI Configuration"); + getContentPane().setLayout(null); + + JSpinner spAlphaBetaThreshold = new JSpinner(); + spAlphaBetaThreshold.setBounds(170, 0, 95, 28); + getContentPane().add(spAlphaBetaThreshold); + spAlphaBetaThreshold.setModel(new SpinnerNumberModel(-10, -100, 100, 5)); + + JSpinner spMaxDepth = new JSpinner(); + spMaxDepth.setBounds(170, 68, 95, 28); + getContentPane().add(spMaxDepth); + spMaxDepth.setModel(new SpinnerNumberModel(4, 1, 10, 1)); + + JLabel lblAlphabetaThreshold = new JLabel("Alpha-Beta Threshold:"); + lblAlphabetaThreshold.setBounds(16, 68, 119, 28); + getContentPane().add(lblAlphabetaThreshold); + + JButton btnOk = new JButton("OK"); + btnOk.setBounds(16, 137, 84, 28); + getContentPane().add(btnOk); + btnOk.addActionListener((evt) -> { + maxDepth = ((Integer) spMaxDepth.getValue()).intValue(); + alphaBetaThreshold = ((Integer) spAlphaBetaThreshold.getValue()).intValue(); + startGame = true; + dispose(); + }); + btnOk.setToolTipText("Start the game"); + + JButton btnCancel = new JButton("Cancel"); + btnCancel.setBounds(170, 137, 95, 28); + getContentPane().add(btnCancel); + btnCancel.addActionListener((evt) -> dispose()); + btnCancel.setToolTipText("Cancel the game start"); + + JLabel lblMaximalRecursionDepth = new JLabel("Maximal Recursion Depth:"); + lblMaximalRecursionDepth.setBounds(16, 6, 141, 16); + getContentPane().add(lblMaximalRecursionDepth); + + setLocationRelativeTo(null); + } + + public int getMaxDepth() { return maxDepth; } + + public void setMaxDepth(int maxDepth) { this.maxDepth = maxDepth; } + + public int getAlphaBetaThreshold() { return alphaBetaThreshold; } + + public void setAlphaBetaThreshold(int alphaBetaThreshold) { this.alphaBetaThreshold = alphaBetaThreshold; } + + public boolean isStartGame() { return startGame; } + + public void setStartGame(boolean startGame) { this.startGame = startGame; } + +} diff --git a/src/dev/kske/chess/ui/MenuBar.java b/src/dev/kske/chess/ui/MenuBar.java index 16f41b3..334d1fc 100644 --- a/src/dev/kske/chess/ui/MenuBar.java +++ b/src/dev/kske/chess/ui/MenuBar.java @@ -54,14 +54,19 @@ public class MenuBar extends JMenuBar { }); aiMenuItem.addActionListener((evt) -> { - players.put(Color.WHITE, new NaturalPlayer(board, Color.WHITE, overlayComponent)); - players.put(Color.BLACK, new AIPlayer(board, Color.BLACK, 5)); - startGame(); + AIConfigDialog dialog = new AIConfigDialog(); + dialog.setVisible(true); + if (dialog.isStartGame()) { + players.put(Color.WHITE, new NaturalPlayer(board, Color.WHITE, overlayComponent)); + players.put(Color.BLACK, + new AIPlayer(board, Color.BLACK, dialog.getMaxDepth(), dialog.getAlphaBetaThreshold())); + startGame(); + } }); aiVsAiMenuItem.addActionListener((evt) -> { - players.put(Color.WHITE, new AIPlayer(board, Color.WHITE, 5)); - players.put(Color.BLACK, new AIPlayer(board, Color.BLACK, 4)); + players.put(Color.WHITE, new AIPlayer(board, Color.WHITE, 4, -10)); + players.put(Color.BLACK, new AIPlayer(board, Color.BLACK, 3, 0)); startGame(); });