Question

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.

Was it helpful?

Solution 2

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:

  1. Class.newInstance() can only invoke the zero-argument constructor, while Constructor.newInstance() may invoke any constructor, regardless of the number of parameters.
  2. 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.
  3. 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.

OTHER TIPS

Use a factory class.

interface ChessPieceFactory {
  ChessPiece create(String pieceName);
}

class ChessPieceFactoryImpl {
  public ChessPiece create(String pieceName) {
    if("pawn".equals(pieceName))
      return new Pawn(pieceName);
    throw new IllegalArgumentException(pieceName);
  }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top