So, this may or may not be what you think, and i'm not entirely sure I've worded the question correctly, but here we go! I'm building a chess game and I'm trying to be dynamic with how I call my classes, build pieces, and set them on the board.
A run down on the code: I read a file which contains a piece such as "pla1", representing a piece, its color, its x coordinate, and its y coordinate. In this example, p = pawn, l = white, a = x coordinate on the board, 1 = y coordinate on the board. so:
"the white pawn is on a1"
Simple. Now Currently, I have a piece class that parses the piece from the file, and a specific class called "Pawn" so when I want to add a piece to the board, that piece has its own class that governs its moving ability and powers on the chess board and so on.
The problem: I can't figure out how to dynamically set my piece class. I could do this easily with a hardcoded version, as you'll see in the comments, or with an if statement. Is it possible to set my class with my string variable?
My code is below:
//reads from the file, puts each line into an arraylist
public void readFromFile(String fileName) throws IOException
{
String line;
BufferedReader br = new BufferedReader(new FileReader(fileName));
//adds line to arraylist
while((line = br.readLine()) != null)
fileLines.add(line.toLowerCase());
//send each line in the list to method
for(String item : fileLines)
buildPiecesFromFileAndGetLocation(item);
}
ChessPiece[][] pieces = new ChessPiece[9][9]; my chessboard
String spawnPiece;
String spawnColor;
String pieceRepresentation;
String originx;
int originX;
int originY;
public void buildPiecesFromFileAndGetLocation(String item)
{
//regex matcher // using this to match it: Pattern copyPiece = Pattern.compile("(?<piece>q|k|b|p|n|r+)(?<color>l|d)(?<x>\\w)(?<y>\\d)");
Matcher copyMatcher = copyPiece.matcher(item);
//hashmap for matching quicker; currently only has pieceMatches.put("p", "Pawn") within it
hashIdentities();
if (copyMatcher.find())
{
spawnPiece = pieceMatches.get(copyMatcher.group("piece")); //spawnPiece becomes "Pawn"
spawnColor = colorMatches.get(copyMatcher.group("color"));
pieceRepresentation = spawnPiece + spawnColor;
originx = copyMatcher.group("x"); //change letter to number
transferChars(); //changes letter "a" into integer "1"
originY = Integer.parseInt(copyMatcher.group("y")); //string to int
//PROBLEM:
pieces[originX][originY] = new spawnPiece(pieceRepresentation); //since spawnPiece is now "Pawn" i want it to be able to call a new instance of the class Pawn. this way doesn't work. Solution?
//logic:
//pieces[a][b] = new (WHATEVER PIECE WAS MATCHED)("position + color");
//hardcoded version:
//pieces[1][1] = new Pawn("PawnWhite");
}
}
I'm not sure if this is possible to do. Any help would be much appreciated, and if any elaboration of the code is needed I can provide.
Look at this article: http://docs.oracle.com/javase/tutorial/reflect/member/ctorInstance.html
Quoting from it directly:
There are two reflective methods for creating instances of classes: java.lang.reflect.Constructor.newInstance() and Class.newInstance(). The former is preferred and is thus used in these examples because:
- Class.newInstance() can only invoke the zero-argument constructor, while Constructor.newInstance() may invoke any constructor, regardless of the number of parameters.
- Class.newInstance() throws any exception thrown by the constructor, regardless of whether it is checked or unchecked. Constructor.newInstance() always wraps the thrown exception with an InvocationTargetException.
- Class.newInstance() requires that the constructor be visible; Constructor.newInstance() may invoke private constructors under certain circumstances.
Let me add some code for you. First of all, you need to get a reference to the class whose instance you want to create. You already have the name of the class, so do this:
Class pieceKlass = Class.forName(spawnPiece)
Then get its constructor which accepts a single string and create an instance of the class:
Constructor ctor = pieceKlass.getDeclaredConstructor(String.class);
ctor.setAccessible(true);
ChessPiece piece = (ChessPiece)ctor.newInstance(pieceRepresentation);
Given that this piece of code is pretty complex and needs some error handling as well, you can now extract it out in a factory class neatly:
class ChessPieceFactory {
public ChessPiece create(String pieceName, String pieceRepresentation) {
ChessPiece piece;
try {
Class pieceKlass = Class.forName(pieceName)
Constructor ctor = pieceKlass.getDeclaredConstructor(String.class);
ctor.setAccessible(true);
piece = (ChessPiece)ctor.newInstance(pieceRepresentation);
// production code should handle these exceptions more gracefully
} catch (ClassNotFoundException x) {
x.printStackTrace();
} catch (InstantiationException x) {
x.printStackTrace();
} catch (IllegalAccessException x) {
x.printStackTrace();
} catch (InvocationTargetException x) {
x.printStackTrace();
} catch (NoSuchMethodException x) {
x.printStackTrace();
}
return piece;
}
Since you have also tagged the question with the Chess tag, I should point out that you should use the standard Forsyth-Edwards Notation to populate the chess board. It uses upper case letters to denote white pieces and lower case letters to denote the black pieces.