X

Geertjan's Blog

  • September 26, 2005

Playing Chess with NetBeans (Part 4)

Geertjan Wielenga
Product Manager
I've been making progress on the chess game (click to enlarge):


The only remaining problem since the last instalment of this pseudo-series (click here for details) is the generation of the FEN string. A FEN string describes the position of all the pieces and non-pieces (i.e., empty squares) on the board. A FEN string looks like this:

rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1

The above line is the starting position -- the lower-case letters are for the 'black' pieces and the upper-case letters are for the 'white' pieces. So, the first 8 characters show that the back row (row 8) contains the rook, knight, bishop, queen, king, bishop, knight, rook. The next 8 characters show that all the black pawns are on the next row (row 7). Row 6, 5, 4, and 3 are all empty -- each empty square gets one point, so each of these rows have 8 points. Then, in row 2 all the white pawns (upper-case P) and all the white pieces (upper-case RNBQKBNR) are all present. Next, you can see a 'w', which means it's white's move (after that you see 'KQkq-0' which is about castling and pawn promotion, both of which I'm not even going to think about yet) and the '1' at the end means that we're in move 1.

The FEN string needs to be generated automatically whenenever I make a new move in the game, because the web service that I'm playing against only understands FEN strings. (Since the web service is stateless, it requires me to re-describe the board each time I communicate with the web service.) So, this means that if I send the above FEN string to the web service, the web service is able to process it and return its own move. Currently, I'm able to generate the above string at the start of the game, i.e., prior to any moves being made. Obviously that's not very useful, since whenever a move is made, the FEN string should be recreated. However, it's a start, and I'm quite proud of it. Here's the code that makes it all happen:

private void createFenButtonActionPerformed(java.awt.event.ActionEvent evt) {
StringBuilder sb = new StringBuilder(80);
int emptySqNo = 0;
for (int sqIndex = 0; sqIndex < 64; sqIndex++) {
JPanel square = (JPanel)chessBoard.getComponent(sqIndex);
JLabel squareLabel = (JLabel)square.getComponent(0);
String squareIcon = (String)squareLabel.getIcon().toString();
//System.out.println("Icon on square " + sqIndex + " is: " + squareIcon);
if (sqIndex % 8 == 0 && sqIndex > 0) {
if (emptySqNo != 0) {
sb.append(emptySqNo);
emptySqNo = 0;
}
sb.append('/');
}
if (squareIcon.startsWith("javax")) {
emptySqNo++;
}
else {
if (squareIcon == "BlackRook.gif" ) {
symbol = "r";
} else if (squareIcon == "BlackKnight.gif" ) {
symbol = "n";
} else if (squareIcon == "BlackBishop.gif" ) {
symbol = "b";
} else if (squareIcon == "BlackQueen.gif" ) {
symbol = "q";
} else if (squareIcon == "BlackKing.gif" ) {
symbol = "k";
} else if (squareIcon == "BlackPawn.gif" ) {
symbol = "p";
} else if (squareIcon == "WhiteRook.gif" ) {
symbol = "R";
} else if (squareIcon == "WhiteKnight.gif" ) {
symbol = "N";
} else if (squareIcon == "WhiteBishop.gif" ) {
symbol = "B";
} else if (squareIcon == "WhiteQueen.gif" ) {
symbol = "Q";
} else if (squareIcon == "WhiteKing.gif" ) {
symbol = "K";
} else if (squareIcon == "WhitePawn.gif" ) {
symbol = "P";
}
sb.append(symbol);
}
}
sb.append(" " + opponent_color + " ");
sb.append("KQkq - 0 ");
sb.append(completed_move_no);
String s = sb.toString();
mySouthField.setText(s);
System.out.println("FEN string: " + sb);
}

What's kind of cool is the StringBuilder. I didn't know about it until Valentin Iliescu, who created the chess web service for which I'm trying to create a client, told me about it. He also provided the basis of the code above. (The search depth, move number, and the opponent's color are set elsewhere in the code, when you click the buttons at the top of the application.) Now, when I click 'Create FEN' (which is only there for test purposes, because eventually this will all happen under the cover) in my application, I see the following in the Output window:

The next thing I need to work on is changing the square's icon when making a move. And also check (again) whether I understand the FEN string correctly -- because there's quite a few little weird things going on in there. I can see that this code is going to contain a lot of if statements...

Join the discussion

Comments ( 8 )
  • Kovica Monday, September 26, 2005
    Maybe you can change some stuff to Enum classes and then use switch in your code.
    Example:
    If you have an icon named Icon.jpg then create Enum class: public enum Icons { ICON1 ("Icon.jpg"); private final String value;
    Icons(String value) {
    this.value = value;
    }
    public String getValue() {
    return value;
    }
    }
    then just use
    switch (icon)
    {
    case Icons.ICON1: {... break;}
    }
    And the code will become a lot easier to read :)
  • Russell Crook Monday, September 26, 2005
    You \*do\* realize that the diagram (not the FEN
    strings) is of an impossible/illegal position?
    (If it's supposed to be the starting position,
    the kings and queens of both sides are on the
    wrong squares. The FEN strings, however,
    have them in the correct positions.)
    Russell
  • Geertjan Monday, September 26, 2005
    Kovica -- thanks for the tip. I'm not the most advanced programmer around, clearly, and I'm at least partly working on this chess game so that I can learn some practical things about Java programming. So, I'll definitely look into Enum classes and see how I can use them in this context.

    Russell, you mean that the icon representing the king is on the queen's square, and vice versa, for both sides? I always get those confused. To me, the icon on queen's square has a very ornate crown, hence it's a queen, while the icon on the king's square has a simpler crown, hence it's a king. "Uneasy lies the head that wears a crown," as a famous man once said...

  • Geertjan Monday, September 26, 2005
    By the way, I've seen two other satirical versions of "Uneasy lies the head that wears a crown":
    • Uneasy lies the tooth that needs a crown.
    • Uneasy lie the lies that aren't thought through.

    PS: The second one is by me, from a poem.

  • Proton Tuesday, September 27, 2005
    Halo, If you use StringBuilder (StringBuffer) for creating String you shouldn't use this expression:
    sb.append(" " + opponent_color + " ");

    becouse concatening strings create new StringBuilder and append all string and return one String object. Right expression for this example is
    sb.append(" ").append(opponent_color).append(" ");

    or better
    sb.append(' ').append(opponent_color).append(' ');

    because strings with length 1 are chars and appending char is faster then appending string.
  • Geertjan Tuesday, September 27, 2005
    Very cool, thanks fir the clarification, Proton. Reading the Javadoc, I found that there's also a <tt>sb.insert</tt>, which seems pretty cool too: "For example, if z refers to a string builder object whose current contents are 'start', then the method call z.append('le') would cause the string builder to contain 'startle', whereas z.insert(4, 'le') would alter the string builder to contain 'starlet'."
  • Koos de Wit Wednesday, September 28, 2005
    Geertjan,
    Comparing strings should be done by the equals() method of String, so
    if (squareIcon.equals("BlackRook.gif"))

    instead of
    if (squareIcon == "BlackRook.gif")

    because in the latter example you are comparing object references. See also section "3.10.5 String Literals" in http://java.sun.com/docs/books/jls/second_edition/html/lexical.doc.html.
  • Geertjan Wednesday, September 28, 2005
    Thanks Koos! I'ts amazing how much I've learnt, just from posting my code here -- I guess the moral is that if you're not afraid of people finding out about your bad coding practises, you'll learn a surprising amount from making your code public.
Please enter your name.Please provide a valid email address.Please enter a comment.CAPTCHA challenge response provided was incorrect. Please try again.