Added stockfish + random move, position conversion & more
This commit is contained in:
parent
2af3448a51
commit
2bc9b79ad8
|
@ -50,14 +50,8 @@ public class Chess {
|
|||
public static void setLookAndFeel() {
|
||||
try {
|
||||
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new RuntimeException(e);
|
||||
} catch (InstantiationException e) {
|
||||
throw new RuntimeException(e);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException(e);
|
||||
} catch (UnsupportedLookAndFeelException e) {
|
||||
throw new RuntimeException(e);
|
||||
} catch (Exception e) {
|
||||
System.out.println("Unable to load system theme!");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -65,6 +59,7 @@ public class Chess {
|
|||
menuBar = new JMenuBar();
|
||||
|
||||
JMenu menuGame = new JMenu("Game");
|
||||
JMenu menuCPU = new JMenu("CPU");
|
||||
JMenu menuExport = new JMenu("Export");
|
||||
|
||||
JMenuItem startNewGame = new JMenuItem("Start new game");
|
||||
|
@ -88,6 +83,18 @@ public class Chess {
|
|||
});
|
||||
menuGame.add(toggleBlindMode);
|
||||
|
||||
JMenuItem toggleAutomaticRandomOpponent = new JMenuItem("Toggle automatic random opponent");
|
||||
toggleAutomaticRandomOpponent.addActionListener(l -> {
|
||||
chessboard.getOtherPlayer().toggleAutomaticRandom();
|
||||
});
|
||||
menuCPU.add(toggleAutomaticRandomOpponent);
|
||||
|
||||
JMenuItem toggleAutomaticSmartOpponent = new JMenuItem("Toggle automatic smart opponent");
|
||||
toggleAutomaticSmartOpponent.addActionListener(l -> {
|
||||
chessboard.getOtherPlayer().toggleAutomaticSmart();
|
||||
});
|
||||
menuCPU.add(toggleAutomaticSmartOpponent);
|
||||
|
||||
JMenuItem exportAsSvg = new JMenuItem("Export as SVG");
|
||||
exportAsSvg.addActionListener(l -> exportSVG());
|
||||
menuExport.add(exportAsSvg);
|
||||
|
@ -97,6 +104,7 @@ public class Chess {
|
|||
menuExport.add(exportAsPng);
|
||||
|
||||
menuBar.add(menuGame);
|
||||
menuBar.add(menuCPU);
|
||||
menuBar.add(menuExport);
|
||||
|
||||
return menuBar;
|
||||
|
@ -106,8 +114,8 @@ public class Chess {
|
|||
// Hack to force repaint of the chessboard (repaint is not working)
|
||||
int winWidth = window.getWidth();
|
||||
int winHeight = window.getHeight();
|
||||
window.resize(winWidth+1, winHeight+1);
|
||||
window.resize(winWidth, winHeight);
|
||||
window.setSize(winWidth+1, winHeight+1);
|
||||
window.setSize(winWidth, winHeight);
|
||||
}
|
||||
|
||||
static void newGame(Chessboard newChessboard) {
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
import org.jfree.svg.SVGGraphics2D;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
|
||||
/**
|
||||
* Chessboard class
|
||||
|
@ -59,7 +58,9 @@ public class Chessboard extends JPanel {
|
|||
|
||||
private Player activePlayer;
|
||||
|
||||
private Player otherPlayer;
|
||||
private Player player1;
|
||||
|
||||
private Player player2;
|
||||
|
||||
public boolean blindMode = false;
|
||||
|
||||
|
@ -113,12 +114,11 @@ public class Chessboard extends JPanel {
|
|||
y++;
|
||||
}
|
||||
|
||||
if(parts[1].equals("w")) {
|
||||
c.setPlayer1(white);
|
||||
c.setPlayer2(black);
|
||||
} else {
|
||||
c.setPlayer1(black);
|
||||
c.setPlayer2(white);
|
||||
c.setPlayer1(white);
|
||||
c.setPlayer2(black);
|
||||
|
||||
if(parts[1].equals("b")) {
|
||||
c.setActivePlayer(black);
|
||||
}
|
||||
|
||||
for (char item : parts[2].toCharArray()) {
|
||||
|
@ -132,10 +132,77 @@ public class Chessboard extends JPanel {
|
|||
}
|
||||
}
|
||||
|
||||
if(!parts[3].equals("-") && parts[3].length() == 2) {
|
||||
PiecePosition pos = PiecePosition.fromString(parts[3]);
|
||||
if(pos == null) return null;
|
||||
boolean[][] lastMove = new boolean[c.SQUARE_COUNT][c.SQUARE_COUNT];
|
||||
lastMove[pos.y-1][pos.x] = true;
|
||||
lastMove[pos.y+1][pos.x] = true;
|
||||
c.showLastMove(lastMove);
|
||||
}
|
||||
|
||||
return c;
|
||||
|
||||
}
|
||||
|
||||
public String toFEN() {
|
||||
|
||||
StringBuilder chessboardSB = new StringBuilder();
|
||||
for (int y = 0; y < pieces.length; y++) {
|
||||
int empty = 0;
|
||||
for (APiece piece : pieces[y]) {
|
||||
if (piece == null) {
|
||||
empty++;
|
||||
continue;
|
||||
}
|
||||
Character pieceLetter = null;
|
||||
if (piece instanceof Pawn) pieceLetter = 'p';
|
||||
if (piece instanceof Knight) pieceLetter = 'n';
|
||||
if (piece instanceof King) pieceLetter = 'k';
|
||||
if (piece instanceof Queen) pieceLetter = 'q';
|
||||
if (piece instanceof Bishop) pieceLetter = 'b';
|
||||
if (piece instanceof Rook) pieceLetter = 'r';
|
||||
if (empty != 0) chessboardSB.append(empty);
|
||||
chessboardSB.append(piece.getPlayer() == player1 ? Character.toUpperCase(pieceLetter) : pieceLetter);
|
||||
empty = 0;
|
||||
}
|
||||
if (empty != 0) chessboardSB.append(empty);
|
||||
if(y != 7) chessboardSB.append('/');
|
||||
}
|
||||
|
||||
StringBuilder castlingSB = new StringBuilder();
|
||||
for (Player player : new Player[]{player1, player2}) {
|
||||
if(player.getKing().getMoveCount() != 0) continue;
|
||||
StartPosition sp = player.getStartPosition();
|
||||
for (int x : new int[]{7, 0}) {
|
||||
APiece rook = getPiece(x, sp == StartPosition.TOP ? 0 : 7);
|
||||
if(rook instanceof Rook && rook.getMoveCount() == 0 && rook.getPlayer() == player) {
|
||||
char castlingType = x == 0 ? 'q' : 'k';
|
||||
castlingSB.append(sp == StartPosition.TOP ? castlingType : Character.toUpperCase(castlingType));
|
||||
}
|
||||
}
|
||||
}
|
||||
if(castlingSB.length() == 0) castlingSB.append('-');
|
||||
|
||||
char player = player1 == activePlayer ? 'w' : 'b';
|
||||
|
||||
String enPassantTargetSquare = "-";
|
||||
for (int y = 0; y < lastMove.length; y++) {
|
||||
for (int x = 0; x < lastMove[y].length; x++) {
|
||||
if(lastMove[y][x] && (y == 1 || y == 4) && lastMove[y+2][x]) {
|
||||
APiece pawn = pieces[y == 1 ? y+2 : y][x];
|
||||
if(pawn instanceof Pawn && pawn.getMoveCount() == 1) {
|
||||
enPassantTargetSquare = (new PiecePosition(x, y+2)).toString();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return chessboardSB + " " + player + " " + castlingSB + " " + enPassantTargetSquare + " 0 0";
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Add passed piece to the specified coordinates
|
||||
* @param piece piece
|
||||
|
@ -397,20 +464,29 @@ public class Chessboard extends JPanel {
|
|||
|
||||
public void setPlayer1(Player player) {
|
||||
activePlayer = player;
|
||||
player1 = player;
|
||||
}
|
||||
|
||||
public void setPlayer2(Player player) {
|
||||
otherPlayer = player;
|
||||
player2 = player;
|
||||
}
|
||||
|
||||
public Player getActivePlayer() {
|
||||
return activePlayer;
|
||||
}
|
||||
|
||||
public void setActivePlayer(Player player) {
|
||||
activePlayer = player;
|
||||
}
|
||||
|
||||
public Player getOtherPlayer() {
|
||||
return activePlayer == player1 ? player2 : player1;
|
||||
}
|
||||
|
||||
public void changeActivePlayer() {
|
||||
Player tempPlayer = activePlayer;
|
||||
activePlayer = otherPlayer;
|
||||
otherPlayer = tempPlayer;
|
||||
activePlayer = activePlayer == player1 ? player2 : player1;
|
||||
getRootPane().repaint();
|
||||
activePlayer.play();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -52,17 +52,19 @@ public class ChessboardMouseAdapter extends MouseAdapter {
|
|||
if (piece != null && piece.getPlayer() == c.getActivePlayer() && selectedPiece != piece) {
|
||||
c.setSelectedPiece(piece);
|
||||
c.showPossibleMoves(piece.getPossibleMoves());
|
||||
c.getRootPane().repaint();
|
||||
} else if(selectedPiece != null) {
|
||||
if(selectedPiece.getPossibleMoves()[pos.y][pos.x]) {
|
||||
selectedPiece.move(pos);
|
||||
if(c.getActivePlayer().inCheck()) System.out.println("Inactive player in check!");
|
||||
c.changeActivePlayer();
|
||||
if(c.getActivePlayer().inCheck()) System.out.println("Active player in check!");
|
||||
} else {
|
||||
c.getRootPane().repaint();
|
||||
}
|
||||
c.showPossibleMoves(null);
|
||||
c.setSelectedPiece(null);
|
||||
}
|
||||
c.getRootPane().repaint();
|
||||
}
|
||||
|
||||
public void onDragStart(MouseEvent me) {
|
||||
|
@ -98,12 +100,12 @@ public class ChessboardMouseAdapter extends MouseAdapter {
|
|||
if(c.getActivePlayer().inCheck()) System.out.println("Inactive player in check!");
|
||||
c.changeActivePlayer();
|
||||
if(c.getActivePlayer().inCheck()) System.out.println("Active player in check!");
|
||||
} else {
|
||||
c.getRootPane().repaint();
|
||||
}
|
||||
c.showPossibleMoves(null);
|
||||
c.setSelectedPiece(null);
|
||||
}
|
||||
c.showPossibleMoves(null);
|
||||
c.getRootPane().repaint();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -70,7 +70,7 @@ public class Pawn extends APiece {
|
|||
|
||||
super.move(pos, animate);
|
||||
|
||||
int changeY = color == PieceColor.WHITE ? 0 : chessboard.SQUARE_COUNT-1;
|
||||
int changeY = player.getStartPosition() == StartPosition.BOTTOM ? 0 : chessboard.SQUARE_COUNT-1;
|
||||
if(y == changeY) {
|
||||
Timer timer = new Timer();
|
||||
timer.schedule(new TimerTask() {
|
||||
|
|
|
@ -13,6 +13,20 @@ public class PiecePosition {
|
|||
*/
|
||||
public int y;
|
||||
|
||||
public String toString() {
|
||||
return String.valueOf((char) (x+97)) + (8-y);
|
||||
}
|
||||
|
||||
public static PiecePosition fromString(String position) {
|
||||
try {
|
||||
int x = position.charAt(0) - 97;
|
||||
int y = 8 - Integer.parseInt(String.valueOf(position.charAt(1)));
|
||||
return new PiecePosition(x, y);
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* PiecePosition constructor
|
||||
* @param x piece's X location
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
import java.util.ArrayList;
|
||||
import java.util.Random;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
|
||||
public class Player {
|
||||
|
||||
private Chessboard chessboard;
|
||||
|
@ -8,6 +13,12 @@ public class Player {
|
|||
|
||||
private King king;
|
||||
|
||||
private Stockfish stockfish;
|
||||
|
||||
private boolean automaticRandom = false;
|
||||
|
||||
private boolean automaticSmart = false;
|
||||
|
||||
public Player(Chessboard chessboard, StartPosition startPosition, PieceColor color) {
|
||||
this.chessboard = chessboard;
|
||||
this.color = color;
|
||||
|
@ -18,6 +29,62 @@ public class Player {
|
|||
return king.isEndangered();
|
||||
}
|
||||
|
||||
public void play() {
|
||||
Timer t = new Timer();
|
||||
t.schedule(new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
if(automaticRandom) randomMove();
|
||||
if(automaticSmart) smartMove();
|
||||
t.cancel();
|
||||
}
|
||||
}, 1000, 1000);
|
||||
}
|
||||
|
||||
public void randomMove() {
|
||||
ArrayList<APiece> myPieces = getPieces();
|
||||
while(myPieces.size() != 0) {
|
||||
Random r = new Random();
|
||||
APiece selectedPiece = myPieces.get(r.nextInt(myPieces.size()));
|
||||
ArrayList<PiecePosition> possibleMoves = new ArrayList<>();
|
||||
myPieces.remove(selectedPiece);
|
||||
|
||||
boolean[][] possibleMoveMap = selectedPiece.getPossibleMoves();
|
||||
for (int y = 0; y < possibleMoveMap.length; y++) {
|
||||
for (int x = 0; x < possibleMoveMap[y].length; x++) {
|
||||
if(possibleMoveMap[y][x]) {
|
||||
possibleMoves.add(new PiecePosition(x, y));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int moveCount = possibleMoves.size();
|
||||
if(moveCount == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
PiecePosition randomMove = possibleMoves.get(r.nextInt(moveCount));
|
||||
selectedPiece.move(randomMove, true);
|
||||
chessboard.changeActivePlayer();
|
||||
return;
|
||||
}
|
||||
System.out.println("No possible move!");
|
||||
}
|
||||
|
||||
public void smartMove() {
|
||||
if(stockfish == null) {
|
||||
stockfish = new Stockfish();
|
||||
stockfish.startEngine();
|
||||
}
|
||||
String fen = chessboard.toFEN();
|
||||
String bestMove = stockfish.getBestMove(fen, 500);
|
||||
PiecePosition fromPos = PiecePosition.fromString(bestMove.substring(0, 2));
|
||||
PiecePosition toPos = PiecePosition.fromString(bestMove.substring(2, 4));
|
||||
APiece piece = chessboard.getPiece(fromPos.x, fromPos.y);
|
||||
piece.move(toPos);
|
||||
chessboard.changeActivePlayer();
|
||||
}
|
||||
|
||||
public Chessboard getChessboard() {
|
||||
return chessboard;
|
||||
}
|
||||
|
@ -38,4 +105,24 @@ public class Player {
|
|||
this.king = king;
|
||||
}
|
||||
|
||||
public ArrayList<APiece> getPieces() {
|
||||
ArrayList<APiece> myPieces = new ArrayList<>();
|
||||
for (APiece[] pieces : chessboard.pieces) {
|
||||
for (APiece piece : pieces) {
|
||||
if(piece != null && piece.getPlayer() == this) {
|
||||
myPieces.add(piece);
|
||||
}
|
||||
}
|
||||
}
|
||||
return myPieces;
|
||||
}
|
||||
|
||||
public void toggleAutomaticRandom() {
|
||||
automaticRandom = !automaticRandom;
|
||||
}
|
||||
|
||||
public void toggleAutomaticSmart() {
|
||||
automaticSmart = !automaticSmart;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
74
src/Stockfish.java
Normal file
74
src/Stockfish.java
Normal file
|
@ -0,0 +1,74 @@
|
|||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStreamWriter;
|
||||
|
||||
public class Stockfish {
|
||||
|
||||
private Process engineProcess;
|
||||
private BufferedReader processReader;
|
||||
private OutputStreamWriter processWriter;
|
||||
|
||||
private static final String PATH = "./stockfish";
|
||||
|
||||
/**
|
||||
* Starts Stockfish engine as a process and initializes it
|
||||
*
|
||||
* @return True on success. False otherwise
|
||||
*/
|
||||
public boolean startEngine() {
|
||||
try {
|
||||
engineProcess = Runtime.getRuntime().exec(PATH);
|
||||
processReader = new BufferedReader(new InputStreamReader(
|
||||
engineProcess.getInputStream()));
|
||||
processWriter = new OutputStreamWriter(
|
||||
engineProcess.getOutputStream());
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void sendCommand(String command) {
|
||||
try {
|
||||
processWriter.write(command + "\n");
|
||||
processWriter.flush();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public String getOutput(int waitTime) {
|
||||
StringBuffer buffer = new StringBuffer();
|
||||
try {
|
||||
Thread.sleep(waitTime);
|
||||
sendCommand("isready");
|
||||
while (true) {
|
||||
String text = processReader.readLine();
|
||||
if (text.equals("readyok"))
|
||||
break;
|
||||
else
|
||||
buffer.append(text + "\n");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
public String getBestMove(String fen, int waitTime) {
|
||||
sendCommand("position fen " + fen);
|
||||
sendCommand("go movetime " + waitTime);
|
||||
return getOutput(waitTime + 100).split("bestmove ")[1].split(" ")[0];
|
||||
}
|
||||
|
||||
public void stopEngine() {
|
||||
try {
|
||||
sendCommand("quit");
|
||||
processReader.close();
|
||||
processWriter.close();
|
||||
} catch (IOException e) {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in a new issue