Added simple (yet surprisingly effective) AI player

+ AIPlayer class
+ Evaluation method in Board
+ AI vs AI button in GameModeDialog (produces a rendering delay)
This commit is contained in:
Kai S. K. Engelbart 2019-07-07 14:37:33 +02:00
parent 716c84bf49
commit 9ab00fe674
4 changed files with 102 additions and 3 deletions

View File

@ -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);
}

View File

@ -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: <strong>Chess</strong><br>
* File: <strong>AIPlayer.java</strong><br>
* Created: <strong>06.07.2019</strong><br>
* Author: <strong>Kai S. K. Engelbart</strong>
*/
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;
}
}

View File

@ -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: <strong>Chess</strong><br>
@ -16,10 +17,12 @@ public class Game {
private Map<Color, Player> players;
private Board board;
private BoardPanel boardPanel;
public Game(Map<Color, Player> players, Board board) {
public Game(Map<Color, Player> 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());

View File

@ -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<Color, Player> 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<Color, Player> 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<Color, Player> 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);
}
}