Chess/src/Chess.java

324 lines
11 KiB
Java

import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.data.category.DefaultCategoryDataset;
import org.jfree.svg.SVGGraphics2D;
import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* Main program layout
*/
public class Chess {
static JFrame window;
static Chessboard chessboard;
static JMenuBar menuBar;
static final String DEFAULT_FEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
/**
* Create the main window and chessboard with pieces
* @param args command-line arguments
*/
public static void main(String[] args) {
setLookAndFeel();
window = new JFrame();
window.setTitle("Chess");
window.setSize(800, 600);
window.setMinimumSize(new Dimension(800, 600));
newGame(Chessboard.fromFEN(DEFAULT_FEN));
window.pack();
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setLocationRelativeTo(null);
window.setVisible(true);
}
public static void setLookAndFeel() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception e) {
System.out.println("Unable to load system theme!");
}
}
public static JMenuBar createMenuBar() {
menuBar = new JMenuBar();
menuBar.add(createMenuGame());
menuBar.add(createMenuTheme());
menuBar.add(createMenuCPU());
menuBar.add(createMenuExport());
return menuBar;
}
public static JMenu createMenuGame() {
JMenu menuGame = new JMenu("Game");
JMenuItem startNewGame = new JMenuItem("Start new game");
startNewGame.addActionListener(l -> {
int response = JOptionPane.showConfirmDialog(chessboard, "Do you really want to start a new game?");
if(response == 0) newGame(Chessboard.fromFEN(DEFAULT_FEN));
});
menuGame.add(startNewGame);
JMenuItem newGameFromFEN = new JMenuItem("Load from FEN");
newGameFromFEN.addActionListener(l -> {
String fen = JOptionPane.showInputDialog("Please input FEN: ");
if(fen == null) return;
Chessboard c = Chessboard.fromFEN(fen);
if(c != null) newGame(c);
else JOptionPane.showMessageDialog(window, "Invalid FEN!");
});
menuGame.add(newGameFromFEN);
JMenuItem getFEN = new JMenuItem("Get current FEN");
getFEN.addActionListener(l -> {
JOptionPane.showInputDialog("Here is current FEN: ", chessboard.toFEN());
});
menuGame.add(getFEN);
JMenuItem showStats = new JMenuItem("Statistics");
showStats.addActionListener(l -> {
showStatistics();
});
menuGame.add(showStats);
menuGame.add(new JPopupMenu.Separator());
JCheckBoxMenuItem toggleBlindMode = new JCheckBoxMenuItem("Blind mode");
toggleBlindMode.addActionListener(l -> {
chessboard.blindMode = !chessboard.blindMode;
toggleBlindMode.setState(chessboard.blindMode);
chessboard.repaint();
});
menuGame.add(toggleBlindMode);
return menuGame;
}
public static JMenu createMenuTheme() {
JMenu menuTheme = new JMenu("Theme");
Theme[] themes = new Theme[]{Theme.CLASSIC, Theme.GREEN, Theme.YELLOW, Theme.RED};
String[] themeNames = new String[]{"Classic", "Green", "Yellow", "Red"};
JRadioButtonMenuItem[] items = new JRadioButtonMenuItem[themes.length];
Runnable deselectRadios = () -> {
for (JRadioButtonMenuItem item : items) {
item.setSelected(false);
}
};
for (int i = 0; i < themes.length; i++) {
items[i] = new JRadioButtonMenuItem(themeNames[i]);
int themeIndex = i;
items[i].addActionListener(l -> {
Theme.setTheme(themes[themeIndex]);
deselectRadios.run();
items[themeIndex].setSelected(true);
chessboard.repaintRootPane();
});
menuTheme.add(items[i]);
if(themes[i] == Theme.activeTheme) items[i].setSelected(true);
}
return menuTheme;
}
public static JMenu createMenuCPU() {
JMenu menuCPU = new JMenu("CPU");
JCheckBoxMenuItem whiteDisabled = new JCheckBoxMenuItem("White / Disabled");
JCheckBoxMenuItem whiteAutomaticRandom = new JCheckBoxMenuItem("White / Automatic random");
JCheckBoxMenuItem whiteAutomaticSmart = new JCheckBoxMenuItem("White / Automatic smart");
JCheckBoxMenuItem blackDisabled = new JCheckBoxMenuItem("Black / Disabled");
JCheckBoxMenuItem blackAutomaticRandom = new JCheckBoxMenuItem("Black / Automatic random");
JCheckBoxMenuItem blackAutomaticSmart = new JCheckBoxMenuItem("Black / Automatic smart");
Runnable updateWhite = () -> {
String cpu = chessboard.getPlayer1().getCPU();
whiteDisabled.setState(cpu == null);
whiteAutomaticSmart.setState(cpu != null && cpu.equals("smart"));
whiteAutomaticRandom.setState(cpu != null && cpu.equals("random"));
};
Runnable updateBlack = () -> {
String cpu = chessboard.getPlayer2().getCPU();
blackDisabled.setState(cpu == null);
blackAutomaticSmart.setState(cpu != null && cpu.equals("smart"));
blackAutomaticRandom.setState(cpu != null && cpu.equals("random"));
};
whiteDisabled.addActionListener(l -> {
setCPU(chessboard.getPlayer1(), null, updateWhite);
});
menuCPU.add(whiteDisabled);
whiteAutomaticRandom.addActionListener(l -> {
setCPU(chessboard.getPlayer1(), "random", updateWhite);
});
menuCPU.add(whiteAutomaticRandom);
whiteAutomaticSmart.addActionListener(l -> {
setCPU(chessboard.getPlayer1(), "smart", updateWhite);
});
menuCPU.add(whiteAutomaticSmart);
menuCPU.add(new JPopupMenu.Separator());
blackDisabled.addActionListener(l -> {
setCPU(chessboard.getPlayer2(), null, updateBlack);
});
menuCPU.add(blackDisabled);
blackAutomaticRandom.addActionListener(l -> {
setCPU(chessboard.getPlayer2(), "random", updateBlack);
});
menuCPU.add(blackAutomaticRandom);
blackAutomaticSmart.addActionListener(l -> {
setCPU(chessboard.getPlayer2(), "smart", updateBlack);
});
menuCPU.add(blackAutomaticSmart);
updateWhite.run();
updateBlack.run();
return menuCPU;
}
public static JMenu createMenuExport() {
JMenu menuExport = new JMenu("Export");
JMenuItem exportAsSvg = new JMenuItem("Export as SVG");
exportAsSvg.addActionListener(l -> exportSVG());
menuExport.add(exportAsSvg);
JMenuItem exportAsPng = new JMenuItem("Export as PNG");
exportAsPng.addActionListener(l -> exportPNG());
menuExport.add(exportAsPng);
return menuExport;
}
public static void setCPU(Player player, String mode, Runnable update) {
if(mode != null && mode.equals("smart")) {
String binary = Stockfish.findBinary();
if(binary == null) {
JOptionPane.showMessageDialog(chessboard, "Stockfish engine not found! (stockfishchess.org)\nDownload 'stockfish' or 'stockfish.exe' to the program's folder or the system's path.");
return;
}
if(Stockfish.getInstance() == null) {
try {
Stockfish.create(binary);
} catch (IOException e) {
JOptionPane.showMessageDialog(chessboard, "Unable to start Stockfish engine!");
e.printStackTrace();
return;
}
}
}
player.setCPU(mode);
update.run();
}
public static void repaintWindow() {
// Hack to force repaint of the chessboard (repaint is not working)
int winWidth = window.getWidth();
int winHeight = window.getHeight();
window.setSize(winWidth+1, winHeight+1);
window.setSize(winWidth, winHeight);
}
public static void newGame(Chessboard newChessboard) {
if(chessboard != null) window.remove(chessboard);
chessboard = newChessboard;
Stockfish sf = Stockfish.getInstance();
if(sf != null) sf.stopEngine();
window.setJMenuBar(createMenuBar());
window.add(chessboard, BorderLayout.CENTER);
repaintWindow();
}
public static void exportSVG() {
SVGGraphics2D g2 = new SVGGraphics2D(1000, 1000);
chessboard.paint(g2, 1000, 1000);
String svg = g2.getSVGElement();
try {
FileWriter bw = new FileWriter("chess.svg");
bw.append(svg);
bw.close();
JOptionPane.showMessageDialog(chessboard, "Exported SVG file 'chess.svg' was saved to program's folder.");
} catch (IOException e) {
JOptionPane.showMessageDialog(chessboard, "Saving SVG file failed!");
}
}
public static void exportPNG() {
BufferedImage image = new BufferedImage(1000, 1000, BufferedImage.TYPE_INT_RGB);
Graphics2D g = image.createGraphics();
chessboard.paint(g, 1000, 1000);
g.dispose();
try {
ImageIO.write(image, "png", new File("chess.png"));
JOptionPane.showMessageDialog(chessboard, "Exported PNG file 'chess.png' was saved to program's folder.");
} catch (IOException e) {
JOptionPane.showMessageDialog(chessboard, "Saving PNG file failed!");
}
}
public static void showStatistics() {
JFrame window = new JFrame();
window.setTitle("Statistics");
DefaultCategoryDataset dataset = new DefaultCategoryDataset();
Player[] players = new Player[]{chessboard.getPlayer1(), chessboard.getPlayer2()};
String[] playerNames = new String[]{"White", "Black"};
for (int i = 0; i < players.length; i++) {
List<Integer> delays = players[i].getDelays();
for (int j = 0; j < delays.size(); j++) {
dataset.addValue(delays.get(j), playerNames[i], (j+1)+".");
}
}
JFreeChart chart = ChartFactory.createLineChart(
"Turn speed", "Turns", "Time (ms)",
dataset
);
ChartPanel panel = new ChartPanel(chart);
window.add(panel);
window.pack();
window.setMinimumSize(new Dimension(500, 300));
window.setVisible(true);
}
}