Not an answer, but a simulation to help
I added a new class: GamePanel and edited Game and Controller
It isn't very pretty...yet. Did you know Unicode had chess characters!?!? (totally awesome!)
By the way, you need UTF-8 for these characters to show. It worked for me, but not sure how it will work on other operating systems.
Fixed end of games bug (thanks to tim for pointing that out).
GamePanel:
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.ArrayList;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class GamePanel extends JPanel{
private static final long serialVersionUID = 1L;
JFrame container;
Board currentBoard;
Game currentGame;
ArrayList<Game> games;
public GamePanel(ArrayList<Game> Games){
games = Games;
currentGame = games.get(0);
currentBoard = currentGame.allBoards.get(0);
container = new JFrame();
container.setSize(new Dimension(500,500));
container.setMinimumSize(new Dimension(150,150));
this.setMinimumSize(new Dimension(150,150));
JButton skipButton = new JButton("skip game");
skipButton.addMouseListener(new MouseListener(){
@Override
public void mouseClicked(MouseEvent arg0) {
nextGame();
}
@Override
public void mouseEntered(MouseEvent arg0) {
// TODO Auto-generated method stub
}
@Override
public void mouseExited(MouseEvent arg0) {
// TODO Auto-generated method stub
}
@Override
public void mousePressed(MouseEvent arg0) {
// TODO Auto-generated method stub
}
@Override
public void mouseReleased(MouseEvent arg0) {
// TODO Auto-generated method stub
}
});
JButton undoButton = new JButton("go back");
undoButton.addMouseListener(new MouseListener(){
@Override
public void mouseClicked(MouseEvent e) {
unStep();
}
@Override
public void mouseEntered(MouseEvent e) {
// TODO Auto-generated method stub
}
@Override
public void mouseExited(MouseEvent e) {
// TODO Auto-generated method stub
}
@Override
public void mousePressed(MouseEvent e) {
// TODO Auto-generated method stub
}
@Override
public void mouseReleased(MouseEvent e) {
// TODO Auto-generated method stub
}
});
JButton continueButton = new JButton("continue");
undoButton.setSize(40,40);
skipButton.setSize(40,40);
continueButton.setSize(40,40);
continueButton.addMouseListener(new MouseListener(){
@Override
public void mouseClicked(MouseEvent arg0) {
step();
}
@Override
public void mouseEntered(MouseEvent arg0) {
}
@Override
public void mouseExited(MouseEvent arg0) {
}
@Override
public void mousePressed(MouseEvent arg0) {
}
@Override
public void mouseReleased(MouseEvent arg0) {
}
});
JPanel buttonPanel = new JPanel();
buttonPanel.setLayout(new FlowLayout());
buttonPanel.add(continueButton);
buttonPanel.add(undoButton);
buttonPanel.add(skipButton);
container.setLayout(new BorderLayout());
container.getContentPane().add(this,BorderLayout.CENTER);
container.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
container.getContentPane().add(buttonPanel,BorderLayout.NORTH);
container.setTitle("White: " + currentGame.players[0].getClass().getSimpleName()
+ " vs "+ "Black: " + currentGame.players[1].getClass().getSimpleName());
container.setVisible(true);
container.invalidate();
container.repaint();
}
private void unStep(){
if(currentBoard!=currentGame.allBoards.get(0)){
currentBoard=currentGame.allBoards.get(currentGame.allBoards.indexOf(currentBoard)-1);
}
container.invalidate();
container.repaint();
}
private void unGame(){
if(currentGame != games.get(0)){
currentGame = games.get(games.indexOf(currentGame)-1);
currentBoard = currentGame.allBoards.get(0);
}
container.invalidate();
container.repaint();
}
private void step(){
if(currentGame.allBoards.indexOf(currentBoard)==currentGame.allBoards.size()-1){
nextGame();
}
else{
currentBoard = currentGame.allBoards.get(currentGame.allBoards.indexOf(currentBoard)+1);
}
container.invalidate();
container.repaint();
}
private void nextGame(){
container.setTitle("White: " + currentGame.players[0].getClass().getSimpleName()
+ " vs "+ "Black: " + currentGame.players[1].getClass().getSimpleName());
if(currentGame != games.get(games.size()-1)){
currentGame = games.get(games.indexOf(currentGame)+1);
}
else{
//games complete
container.dispose();
}
currentBoard = currentGame.allBoards.get(0);
container.invalidate();
container.repaint();
}
@Override
public void paintComponent(Graphics g){
if(getWidth()>150 && getHeight() > 150){
int leftBounds,topBounds,width,height;
topBounds = 50;
leftBounds = 50;
if(getWidth() > getHeight()){
width = (int) (getHeight()-100);
height = (int) (getHeight()-100);
}
else{
width = (int) (getWidth()-100);
height = (int) (getWidth()-100);
}
//draw grid
java.awt.Color dark = new java.awt.Color(128, 78, 41);
java.awt.Color light = new java.awt.Color(250, 223, 173);
Field[][] feilds = currentBoard.getFields();
for(int x = leftBounds; x < leftBounds+width-width/8; x+=width/8){
for(int y = topBounds; y < topBounds+height-height/8; y+=height/8){
int xPos = (int)Math.round(((double)(x-leftBounds)/(double)width)*8);
int yPos = (int)Math.round(((double)(y-topBounds)/(double)height)*8);
String piece = "";
java.awt.Color stringColor = java.awt.Color.black;
if(feilds[xPos][yPos].hasPiece()){
piece = getPiece(feilds[xPos][yPos].getPiece());
if(feilds[xPos][yPos].getPiece().getTeam()==Color.WHITE){stringColor = java.awt.Color.WHITE;}
}
if(yPos % 2 == 1){
if(xPos % 2 == 1){
g.setColor(dark);
g.fillRect(x, y, width/8, height/8);
}
else{
g.setColor(light);
g.fillRect(x, y, width/8, height/8);
}
}
else{
if(xPos % 2 == 1){
g.setColor(light);
g.fillRect(x, y, width/8, height/8);
}
else{
g.setColor(dark);
g.fillRect(x, y, width/8, height/8);
}
}
g.setColor(java.awt.Color.black);
g.setFont(new Font(Font.SANS_SERIF, Font.BOLD, width/8));
g.drawString(piece, x, y+width/8);
}
}
}
}
public String getPiece(Piece p){
if(p.getTeam() == Color.WHITE){
if(p.getType() == PieceType.BISHOP){return "\u2657";}
if(p.getType() == PieceType.PAWN){return "\u2659";}
if(p.getType() == PieceType.KING){return "\u2654";}
if(p.getType() == PieceType.QUEEN){return "\u2655";}
if(p.getType() == PieceType.ROOK){return "\u2656";}
if(p.getType() == PieceType.KNIGHT){return "\u2658";}
}
else{
if(p.getType() == PieceType.BISHOP){return "\u265D";}
if(p.getType() == PieceType.PAWN){return "\u265F";}
if(p.getType() == PieceType.KING){return "\u265A";}
if(p.getType() == PieceType.QUEEN){return "\u265B";}
if(p.getType() == PieceType.ROOK){return "\u265C";}
if(p.getType() == PieceType.KNIGHT){return "\u265E";}
}
return p.toString();
}
}
Game:
import java.util.ArrayList;
public class Game {
private static final int MAX_TURNS_WITHOUT_CAPTURES = 100; //=50, counts for both teams
private static final int MAX_MILLISECONDS = 2000;
private Board board;
Player[] players = new Player[2];
private int turnsWithoutCaptures = 0;
private boolean draw = false;
ArrayList<Board> allBoards = new ArrayList<Board>();
public Game(Player player1, Player player2) {
board = new Board();
board.initialize();
players[0] = player1;
players[0].setTeam(Color.WHITE);
players[1] = player2;
players[1].setTeam(Color.BLACK);
allBoards.add(board.copy());
}
int run() {
int i = 0;
while (!gameOver()) {
if (!turnAvailable(players[i])) {
draw = true;
} else {
makeTurn(players[i], players[(i+1) % 2]);
i = (i + 1) % 2;
}
}
if (loses(players[0]) && !loses(players[1])) {
return Controller.LOSE_POINTS;
} else if (loses(players[1]) && !loses(players[0])) {
return Controller.WIN_POINTS;
} else {
return Controller.DRAW_POINTS;
}
}
private boolean loses(Player player) {
if (player.isDisqualified() || board.getKing(player) == null) {
return true;
}
return false;
}
// player can make a turn
private boolean turnAvailable(Player player) {
for (Piece piece : player.getPieces(board)) {
if (piece.getValidDestinationSet(board).size() > 0) {
return true;
}
}
return false;
}
private void makeTurn(Player player, Player enemy) {
player.setCheck(board.isCheck(player, enemy));
enemy.setCheck(board.isCheck(enemy, player));
try {
long start = System.currentTimeMillis();
Move move = player.getMove(board.copy(), enemy);
if ((System.currentTimeMillis() - start) > MAX_MILLISECONDS) {
player.setDisqualified();
}
if (move.isValid(board, player)) {
if (board.movePiece(move) || move.getPiece().getType() == PieceType.PAWN) {
turnsWithoutCaptures = 0;
} else {
turnsWithoutCaptures++;
}
} else {
player.setDisqualified(); //invalid move
}
} catch (Exception e) {
player.setDisqualified();
e.printStackTrace();
System.out.println("Exception while moving " + player);
}
allBoards.add(board.copy());
}
public boolean gameOver() {
for (Player player : players) {
if (player.isDisqualified() || board.getKing(player) == null
|| turnsWithoutCaptures >= MAX_TURNS_WITHOUT_CAPTURES || draw) {
return true;
}
}
return false;
}
}
Controller:
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import players.*;
public class Controller {
public static final int WIN_POINTS = 3;
public static final int DRAW_POINTS = 1;
public static final int LOSE_POINTS = 0;
private static final int GAMES_PER_PAIR = 10;
private final Class[] classes = {StretchPlayer.class,TestPlayer.class};
private final Map<Class<? extends Player>, Integer> scores = new HashMap<Class<? extends Player>, Integer>();
public static void main(String... args) {
new Controller().generateResult();
}
public Controller() {
for (Class player : classes) {
scores.put(player, 0);
}
}
ArrayList<Game> games = new ArrayList<Game>();
private void generateResult() {
for (int i = 0; i < classes.length - 1; i++) {
for (int j = i + 1; j < classes.length; j++) {
for (int k = 0; k < GAMES_PER_PAIR; k++) {
runGame(classes[i], classes[j], k>=GAMES_PER_PAIR);
}
}
}
GamePanel panel = new GamePanel(games);
printScores();
}
private void runGame(Class class1, Class class2, boolean switchSides) {
if (switchSides) { //switch sides
Class tempClass = class2;
class2 = class1;
class1 = tempClass;
}
try {
Player player1 = (Player) class1.newInstance();
Player player2 = (Player) class2.newInstance();
Game game = new Game(player1, player2);
games.add(game);
int result = game.run();
addResult(class1, result, false);
addResult(class2, result, true);
} catch (Exception e) {
System.out.println("Error in game between " + class1 + " and " + class2);
}
}
private void addResult(Class player, int result, boolean reverse) {
if (reverse) {
if (result == WIN_POINTS) {
result = LOSE_POINTS;
} else if (result == LOSE_POINTS) {
result = WIN_POINTS;
}
}
int newScore = scores.get(player) + result;
scores.put(player, newScore);
}
private void printScores() {
int bestScore = 0;
Class currPlayer = null;
int place = 1;
while (scores.size() > 0) {
bestScore = 0;
currPlayer = null;
for (Class player : scores.keySet()) {
int playerScore = scores.get(player);
if (scores.get(player) >= bestScore) {
bestScore = playerScore;
currPlayer = player;
}
}
System.out.println(String.format("%02d", place++) + ") " + currPlayer + ": " + bestScore);
scores.remove(currPlayer);
}
}
}
Some of the rule changes don't change the game, but only make the control program simpler. Is this intended? – proud haskeller – 2014-09-03T07:16:22.963
@proudhaskeller Yes. It makes the control program simpler and faster (doesn't have to check for checkmate each turn), while the bots don't get more difficult. – CommonGuy – 2014-09-03T07:22:29.323
2It also turns a stalemate into a loss :( – Geobits – 2014-09-03T12:50:16.833
2
you can profit from the methods provided by the controller
: This isn't all that true. Most of the useful methods are package private, so they cannot be used. Would you mind maybe adding asimulateMove
method to theBoard
class, which returns a deep copy of the board with the given move applied? That way, we don't have to write it ourselves (or copy-paste your entire code^^). – tim – 2014-09-03T15:55:49.8371@tim The ones which are package private could be used to cheat. I will try to change the code, so you get a deep copy of the
Board
and let you do with it whatever you want ;) – CommonGuy – 2014-09-03T16:18:27.5204The 2 second rule is fantastic. Rather than being a hard-on chess optimal bots, the challenge is like : Create the best not-so intelligent chess bot – Zaenille – 2014-09-04T05:45:14.187
@Manu, until when can we send a submission? Also, will you set a maximum number of submissions per user? There was a meta post discussing this where one could just spam entries and hope to win statistically. – Zaenille – 2014-09-04T05:58:57.703
2@Geobits I updated the controller. Most of the useful methods are
public
now ;) – CommonGuy – 2014-09-04T09:16:12.290@MarkGabriel I will offer a bounty in some days/weeks (depends on how many submissions will be posted) and accept the answer then. Maximum submissions per user is 5. – CommonGuy – 2014-09-04T09:19:01.587
@Manu Oh yeah... – Beta Decay – 2014-09-04T10:55:12.600
I think that there are two bugs in your controller code: 1.
setMoved
ofPawn
is never called, so pawns can always go two fields ahead. 2. this is possible:WHITE KNIGHT on x: 1, y: 0 moves to x: 0, y: 2
BLACK ROOK on x: 0, y: 7 moves to x: 0, y: 0
. Because of this line:if (!field.hasPiece() || field.getPiece().getTeam() == enemy) {
it seems possible to jump over enemies (I think that you should break after the move has been added in case that the dest belongs to the enemy). – tim – 2014-09-05T12:01:36.017@tim Thank you!
setMoved
is called now. Rooks, Queens and Bishops can no longer jump over enemy pieces. – CommonGuy – 2014-09-05T12:20:12.197Can we please alternate black/white for each game? As it is, you play all games against an opponent as the same side, and the last player on the class list will be black for every game in the tourney, while the first is white every time. – Geobits – 2014-09-05T16:32:18.243
@Geobits Players already play 5 games as white and 5 games as black against another player... Look at
– CommonGuy – 2014-09-05T16:44:11.010Controller#runGame
1
switchSides
is only true ifk>=GAMES_PER_PAIR
ingenerateResult()
, which it never is since the loop stops at that point. I didn't notice until I added aprintln
to output the results of each game, and saw that my bot(last on the list) was black every time. – Geobits – 2014-09-05T16:46:50.200@Geobits Oh damn, it should be
GAMES_PER_PAIR/2
– CommonGuy – 2014-09-05T16:48:25.4671Also, it may be too much work, but is there any way to get game display and/or notation? I have no idea why my bot loses to Stretch, just that it loses. – Geobits – 2014-09-05T16:49:39.067
1If "en passant" is void, then maybe also prohibit pawn's first double move? – Vi. – 2014-09-06T10:07:25.617
3I'm almost inclined to write a bot that throws a
Throwable
or evenError
since it will avoid losing. I'd call it the BoardTipper. – Ingo Bürk – 2014-09-06T14:14:09.0671I am working on a way to display to see the results. Should be ready by the end of the day. – Stretch Maniac – 2014-09-06T17:01:14.177
Simulation is now up in answers. I will prettify it later, but feel free to make it better yourself. – Stretch Maniac – 2014-09-06T18:26:35.747
1@IngoBürk, OOPS! An earthquake! I guess this game doesn't count. whistles guiltly – Zaenille – 2014-09-08T04:13:06.500
Is it allowed to use the opponents time (by forking a thread)? – Bob Genom – 2014-10-06T19:36:09.813
How many memory may I use (e.g. for transition tables)? – Bob Genom – 2014-10-06T19:37:51.023
@BobGenom No, you can't use the opponents time. Use as much memory as you like, as long the control program works. – CommonGuy – 2014-10-06T19:48:46.763