3

I'm writing a basic Java chess game and have written the following classes: Game, Player, Board, Square, Piece (superclass of each of the specific pieces), and each specific piece class (e.g. Pawn, Knight, Bishop, etc.)

One of the trickier methods is figuring out whether a move is valid based on whether it results in the moving player being in check. My solution is as follows:

  1. clone current Board
  2. make move
  3. see if moving player is in check
  4. if so, disallow move; else, allow move

I'm taking my cues on how to clone from here: http://www.jusfortechies.com/java/core-java/cloning.php

Now, the board object consists of a 2d array of Square objects. Each square object has a piece field which is either null (no piece on it) or refers to a piece object (has a piece on it). The board object also has a whiteKingSquare and a blackKingSquare (both are Square objects) to make locating the white king or black king faster / easier.

I've written the following method within my Board class:

public Object clone() throws CloneNotSupportedException {
    Board clonedBoard = (Board) super.clone();
    for (int i = 0; i < HEIGHT; i++) {
        for (int j = 0; j < WIDTH; j++) {
            clonedBoard.myBoard[i][j] = new Square(this, i, j); 
            clonedBoard.whiteKingSquare = myBoard[7][4];
            clonedBoard.blackKingSquare = myBoard[0][4];
        }
    }
    return clonedBoard; 
}

However, because Board refers to an 8 x 8 array of Square objects, I have to clone each of these. I've written this method within the Square class:

public Object clone() throws CloneNotSupportedException {
    return (Square) super.clone();
}

Finally, I've written this method in the Piece class:

public Object clone() throws CloneNotSupportedException {
    return (Piece) super.clone();
}

Onto the questions:

  1. Does this look roughly right?
  2. My Square objects also have a Board field which basically lets me reference the board they belong to. Will this mess with my attempt to clone by having each of my 64 squares clone the board separately?
Tom
  • 16,842
  • 17
  • 45
  • 54
anon_swe
  • 8,791
  • 24
  • 85
  • 145
  • 1
    See [this incredibly simple solution](http://stackoverflow.com/questions/869033/how-do-i-copy-an-object-in-java) to cloning objects. The answer to your second question: yes, it will mess with your attempt, since the squares will still have the original reference. – pietv8x Jul 26 '15 at 22:13
  • @pietv8x Thanks; will I need to provide copy constructors for each object referenced within a Board object? – anon_swe Jul 26 '15 at 22:15
  • 1
    either that, or stick with the `clone()` after all. For example, in my 2048 game I followed a similar method to check validity of moves for AI. See [this](https://github.com/Unia/2048/blob/master/src/main/java/nl/tudelft/ti2206/gameobjects/Grid.java#L480) method. It recreates all the tiles so effectively it is a deep copy. – pietv8x Jul 26 '15 at 22:18
  • 1
    Why dont you just remember the previous position in some variable, and if the move doesnt check out, just place the piece back into the previous position? I dont know your code, but this seems to me the simplest solution of all. Atleast I would have done that as this is a one move at a time game. – Samrat Dutta Jul 26 '15 at 22:24
  • 1
    I don't think you should use clone though. I made a _Chinese_ chess program before. It works like this, if the user selects a piece and then the on click listener gets executed. It then calls a method called getValidLocations. It returns an array of xy coordinates and buttons are put onto those cords. You see what I mean? you don't need to check whether the move is valid. – Sweeper Jul 27 '15 at 00:18
  • @Sweeper you still need getValidLocations to check whether each possible location is valid. There are more rules than just "I can move one square in any direction" or "I can move horizontally". E.g. You're not allowed to make a move if that move would cause your king to be threatened (which is what OP is talking about). – Blorgbeard Jul 27 '15 at 01:27
  • @Blorgbeard Then you just call the getValidLocations again to check if the King is a valid location. If it is then show a message box saying Checked! or something – Sweeper Jul 27 '15 at 01:40

2 Answers2

7

I have an alternative solution, see if you like this or not.

There is no need to use clone! I wrote a Chinese chess program a while ago. Let me explain what I did: (This is only an outline, you have to do the details yourself)

I had a 2D array of Pieces and another 2D array of buttons. When the user clicks on a Piece, an abstract method getValidLocations in the Piece class is called and this method returns a bunch of coordinates to indicate where can the Piece go. When the user clicks on the button, the piece is moved to the button's position.

The bad news is, I don't know how to play chess. I only know Chinese chess so I can't tell you how to write the algorithm of getValidLocations, sorry!

Sweeper
  • 213,210
  • 22
  • 193
  • 313
  • @bcclayman I agree that this may be a cleaner method, as surely there will be other comparisons you will need to perform to determine whether a move is valid. Wouldn't you want to do all of those comparisons together? And keep in mind that just checking for post-move position will not actually confirm that a move was legal (ie: a King cannot castle "through" check, even if it is safe when it arrives on the final square). I'd want something like: User attempts move // Check for general range limits // Check for own piece being there // Check for moving through a piece (if not knight) // ... – Grade 'Eh' Bacon Jul 28 '15 at 13:48
  • ... Check for whether it is a move of a king which enters check // if it is castling, does it castle through check [I assume each square class has a property indicating whether it is currently being attacked by each side] // Check for whether it is a non-King move, which makes the King's square now indicate that it is currently being attacked. – Grade 'Eh' Bacon Jul 28 '15 at 13:50
  • Final thought - on consideration, you could check for whether a 'pinned' piece would cause the King to be in check if it moved, by simply checking direction between the player's King and the player's piece. ie: Player's piece is on the same rank / file as the king, so follow that rank / file until you hit the first piece - is it an enemy piece which attacks along that direction? If it's not the same rank / file, is it an equal distance of rows/columns away (diagonally protecting the king)? If so, check the next piece in that direction - is it an enemy piece which attacks along that direction? – Grade 'Eh' Bacon Jul 28 '15 at 14:00
  • @Grade'Eh'Bacon I really don't know chess as I said before so I can only give a kind of general guideline. Sorry! – Sweeper Jul 28 '15 at 23:50
4

Don't use clone(). It's broken instead make a complete deep copy of your board. Here is a discussion with Josh Bloch on copy constructor vs cloning

By making a deep copy, you will have different objects for each piece so moving them won't mess up the state of the corresponding piece in a different Board representing an alternate move.

It might make sense to make a copy() method on all of your pieces to allow them to make valid copies of all their internal state.

Example copy method:

Let's pretend your Pawn class has a variable for if it has moved before or not. This determines if it can move 2 spaces or only 1.

class Pawn{ 
       private boolean hasMovedAlready=false;
       ...
       public Pawn copy(){
          return new Pawn(hasMovedAlready, ...);
}
dkatzel
  • 31,188
  • 3
  • 63
  • 67