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
.