0

I'm using an environment called Greenfoot, a simple Java based 2d game engine, however I do not think the issue is related to Greenfoot, but java in general.

In Greenfoot, there is a World class, in which all of the Actors (just objects) interact with each other.

Whenever you create the World (it is constructed each time you start the game) you are actually creating a MyWorld object, which extends world and you can write your own code into.

My specific game is Chess, and my problem is related to the enpassant rule.

All of my chess pieces are children of one super class Piece which in turn is a child of the Greenfoot class Actor. For every piece except the pawn, they use the method in Piece makeMove(x, y), shown here.

protected void makeMove(int x, int y)
{
    MyWorld w = (MyWorld)getWorld();
    if(canMove(x, y))
    {
        w.mainBoard.movePiece(getX(), getY(), x, y);
        w.makeMove();
        w.displayBoard();
        w.setEnPassant(-1);
    }
}

The first line gets the world the object is in, using the getWorld() method. Since getWorld() returns an object of type World, but my actors are in the object of type MyWorld, I just use casting.

There is no scenario in which getWorld() will be called and a MyWorld will not be returned. Nothing else here is super important, it just checks if it is possible to make the move fed to it, and if so, all of the code inside just moves the piece and updates the board, all of this works.

Also, setEnPassant sets a variable stored in the MyWorld object to -1, I use this variable to tell if enPassant is a possible move or not, and I reset the value in makeMove since makeMove will be called every turn.

The problem comes from the Pawn class, which extends Piece. To make enPassant work, I have to override the makeMove method inside of Pawn, see here:

protected void makeMove(int x, int y)
{
    int y1 = getY();
    super.makeMove(x, y);
    
    if(Math.abs(y1 - y) == 2)
    {
        MyWorld w = (MyWorld)getWorld();
        w.setEnPassant(getX());
    }
}

First, I store the current y value (the up and down coordinate). Then, I call super.makeMove(x, y), which moves the piece to its new spot.

Next, I have an if-statement which checks whether or not the pawn moved 2 squares. The only time this can happen is at the very beginning, and if the pawn does move two squares it attempts to set the enPassant value to whichever column it is in. This is essentially broadcasting "I am available to be en passanted," and if there is a pawn on the other side who is in the proper spot, it will see that there is an en passant it can make.

The problem I am having is only on the very last line. Defining MyWorld w works, but calling setEnPassant() throws a NullPointerException. In fact, any method I call here throws a NullPointerException. No matter what I do with the object w, a NullPointerException is thrown.

I've done a few things which patch the issue, like for some reason moving super.makeMove(x, y) to the end of the method causes no problems. However, I need it to be at the beginning so that it does not immediately reset the EnPassant value to -1.

Another very strange solution I found was by changing the return type of makeMove in both Piece and Pawn to MyWorld. In Piece, I return the world w, and in Pawn, I use that same world w to set the en passant value. So instead of saying

MyWorld w = (MyWorld)getWorld();

I write

MyWorld w = super.makeMove(x, y);

There may be some more elegant ways of handling en passant but I don't really want to hear about any of those. Really I would just like to know why there's an error here and not in other places, and why the strange fixes I found made it not throw the NullPointerException.

Frakcool
  • 10,915
  • 9
  • 50
  • 89
  • 1
    I appreciate the amount of effort you put into this question - however most devs can understand code *much faster* than reading about something which tries to help them understand the code. Try editing your question to include a [reprex] and just describe your issue. – sleepToken Jul 03 '20 at 21:40
  • You might also adding some print statements where you try to use an object. E.g. `w.makemove()`. print `w` – WJS Jul 03 '20 at 21:43
  • 1
    Can you add code for getWorld() also? – js_248 Jul 03 '20 at 22:18
  • The reason a NullPointerException is thrown is probably that w is null, because getWorld() is returning null. In Greenfoot, getWorld() returns null if the object has been removed from the world. So my guess is that one of these methods in the parent's makeMove is removing the pawn from the world. – Neil Brown Jul 04 '20 at 07:56

0 Answers0