[Home]Kazuhiko/Game-Chess

ec2-44-220-44-148.compute-1.amazonaws.com | ToothyWiki | Kazuhiko | RecentChanges | Login | Webcomic

[Click to play]

The game of chess implemented in ToothyGDL.

This is now fully functional (i.e. any game of chess that could be played according to the standard rules can be played in this version) including en passant, castling and pawn promotion.  Ranged movement (Queen, Rook, Bishop) is achieved by picking a direction and then clicking until the desired distance has been reached.

Note that limits on movement imposed by a check/checkmate situation are not imposed so make sure your move is legal before making it.  Currently, there is no 'undo' function.

By default, unicode characters are used to give 'graphical' representations of the pieces.  If you just see QuestionMarks for the pieces it means that the font you are using to view the page does not have these characters available.  Clicking on the "Switch to ascii pieces" button at any time will replace these with simple letters to represent the pieces.



To Do:



ToothyGDL: [Recompile] - [View compiler errors]

logic
{
  // ~~~~~~~  Initial board layout
  // 0 = empty space
  // 1 = white pawn
  // 3 = white knight
  // 5 = white bishop
  // 7 = white rook
  // 9 = white queen
  // 11 = white king
  // 2 = black pawn
  // 4 = black knight
  // 6 = black bishop
  // 8 = black rook
  // 10 = black queen
  // 12 = black king
  
  array board
    {
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
      -1, -1,  8,  4,  6, 10, 12,  6,  4,  8, -1, -1,
      -1, -1,  2,  2,  2,  2,  2,  2,  2,  2, -1, -1,
      -1, -1,  0,  0,  0,  0,  0,  0,  0,  0, -1, -1,
      -1, -1,  0,  0,  0,  0,  0,  0,  0,  0, -1, -1,
      -1, -1,  0,  0,  0,  0,  0,  0,  0,  0, -1, -1,
      -1, -1,  0,  0,  0,  0,  0,  0,  0,  0, -1, -1,
      -1, -1,  1,  1,  1,  1,  1,  1,  1,  1, -1, -1,
      -1, -1,  7,  3,  5,  9, 11,  5,  3,  7, -1, -1,
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
    }
  local int boardwidth { 12 }
  local int boardheight { 12 }
  
  // Display rotation - A given x,y is represented by dRotA+dRotX*x+dRotY*y
  local array dRotA {  0, 132, 143, 11 }
  local array dRotX {  1, -12,  -1, 12 }
  local array dRotY { 12,   1, -12, -1 }
  local int dRot { 0 }
  local int dRotAuto { 0 }
  
  // Denote the currently selected piece and who's turn it is
  int selected { -1 }
  int turn { 1 }
  
  // Are we using unicode or simple characters?
  local int charset { 0 }
  
  // Special move variables
  int enpassant { -1 } // Keep track of possible en-passant moves
  int castlingWKmoved { 0 }  // Has white king moved yet?
  int castlingWRRmoved { 0 } // Has white rook on right moved yet?
  int castlingWRLmoved { 0 } // Has white rook on left moved yet?
  int castlingBKmoved { 0 }  // Has black king moved yet?
  int castlingBRRmoved { 0 } // Has black rook on right moved yet?
  int castlingBRLmoved { 0 } // Has black rook on left moved yet?
  

  // Previous move direction - Used by pieces that can move any number of
  //  spaces in a given direction
  int prevmove { 0 }
  
  // Piece to 'promote'
  int promotePiece { -1 }
  
  // Knight move list relative to current position
  /*
  local array knightmove
    {
      -2*boardwidth-1, -2*boardwidth+1, -boardwidth-2, -boardwidth+2,
       2*boardwidth-1,  2*boardwidth+1,  boardwidth-2,  boardwidth+2
    }
  */
  // For boardwidth 12 this becomes...
  local array knightmove {-25, -23, -14, -10, 23, 25, 10, 14}
  
  // King move list, arranged so all the even positions are diagonal (rotate clockwise)
  local array kingmove {-13, -12, -11, 1, 13, 12, 11, -1}

  // Scratch variable for test calculations
  local int test {0}
  
  // Allow charset to be changed whenever
  when { 1 }
  allow { charset = (charset+1)%2; }
  label {"switch charset"}
  
  // Allow board to be rotated and auto-rotate toggled whenever
  when { 1 }
  allow { dRot = (dRot+1)%4; }
  label {"rotate clockwise"}
  when { 1 }
  allow { dRot = (dRot+3)%4; }
  label {"rotate anticlockwise"}
  when { 1 }
  allow { dRotAuto = 1-dRotAuto; }
  label {"toggle autorotate"}
  
  // If nothing selected, allow the player to pick any of her pieces
  // (we don't check if a piece could be moved, that would be too complicated)
  for i (0 .. ((boardheight*boardwidth)-1))
  {
    when
    {
      (selected == -1) and (promotePiece == -1) and
      (board[i] > 0) and (board[i] % 2 == turn)
    }
    allow {selected = i;}
    label {"select " i}
  }
  
  // If piece selected, allow all possible moves for that piece to be selected
  // *** Pawn
  // Normal move
  when
  {
    (selected != -1) and ((board[selected] + 1) / 2 == 1) and
    (board[selected-(((turn*2) - 1)*boardwidth)] == 0)
  }
  allow
  {
    // Store the place the pawn will be moving to
    test = selected-(((turn*2) - 1)*boardwidth);
    board[test] = board[selected];
    board[selected] = 0;
    // Does this trigger promotion?
    if ((test/boardwidth == 2) or (test/boardwidth == 9))
    {
      selected = -1;
      promotePiece = test;
    }
    // else
    if (!((test/boardwidth == 2) or (test/boardwidth == 9)))
    {
      // Standard "end turn"
      selected = -1;
      enpassant = -1;
      if (dRotAuto == 1) {dRot = turn*2;}
      turn = (turn + 1) % 2;
    }
  }
  label {"move " (selected-(((turn*2) - 1)*boardwidth))}
  
  // Start move
  when
  {
    (selected != -1) and ((board[selected] + 1) / 2 == 1) and
    ((selected/boardwidth == 3) or (selected/boardwidth == 8)) and
    (board[selected-(((turn*2) - 1)*boardwidth)] == 0) and
    (board[selected-(((turn*2) - 1)*boardwidth*2)] == 0)
  }
  allow
  {
    board[selected-(((turn*2) - 1)*boardwidth*2)] = board[selected];
    // Store the en passant variable
    enpassant = selected-(((turn*2) - 1)*boardwidth);
    board[selected] = 0;
    // Standard "end turn"
    selected = -1;
    //enpassant = -1;
    if (dRotAuto == 1) {dRot = turn*2;}
    turn = (turn + 1) % 2;
  }
  label {"move " (selected-(((turn*2) - 1)*boardwidth*2))}
  
  // Take to left
  when
  {
    (selected != -1) and ((board[selected] + 1) / 2 == 1) and
    (board[(selected-(((turn*2) - 1)*boardwidth))-1] > 0) and
    (board[(selected-(((turn*2) - 1)*boardwidth))-1] % 2 == (turn+1)%2)
  }
  allow
  {
    // Store the place the pawn will be moving to
    test = (selected-(((turn*2) - 1)*boardwidth))-1;
    board[test] = board[selected];
    board[selected] = 0;
    // Does this trigger promotion?
    if ((test/boardwidth == 2) or (test/boardwidth == 9))
    {
      selected = -1;
      promotePiece = test;
    }
    // else
    if (!((test/boardwidth == 2) or (test/boardwidth == 9)))
    {
      // Standard "end turn"
      selected = -1;
      enpassant = -1;
      if (dRotAuto == 1) {dRot = turn*2;}
      turn = (turn + 1) % 2;
    }
  }
  label {"move " ((selected-(((turn*2) - 1)*boardwidth))-1)}
  
  // Take to right
  when
  {
    (selected != -1) and ((board[selected] + 1) / 2 == 1) and
    (board[(selected-(((turn*2) - 1)*boardwidth))+1] > 0) and
    (board[(selected-(((turn*2) - 1)*boardwidth))+1] % 2 == (turn+1)%2)
  }
  allow
  {
    // Store the place the pawn will be moving to
    test = (selected-(((turn*2) - 1)*boardwidth))+1;
    board[test] = board[selected];
    board[selected] = 0;
    // Does this trigger promotion?
    if ((test/boardwidth == 2) or (test/boardwidth == 9))
    {
      selected = -1;
      promotePiece = test;
    }
    // else
    if (!((test/boardwidth == 2) or (test/boardwidth == 9)))
    {
      // Standard "end turn"
      selected = -1;
      enpassant = -1;
      if (dRotAuto == 1) {dRot = turn*2;}
      turn = (turn + 1) % 2;
    }
  }
  label {"move " ((selected-(((turn*2) - 1)*boardwidth))+1)}
  
  // En-passant take
  when
  {
    (selected != -1) and ((board[selected] + 1) / 2 == 1)
    and
    (
      ((selected-(((turn*2) - 1)*boardwidth))-1 == enpassant) or
      ((selected-(((turn*2) - 1)*boardwidth))+1 == enpassant)
    )
  }
  allow
  {
    board[enpassant] = board[selected];
    board[enpassant+(((turn*2) - 1)*boardwidth)] = 0;
    board[selected] = 0;
    // Standard "end turn"
    selected = -1;
    enpassant = -1;
    if (dRotAuto == 1) {dRot = turn*2;}
    turn = (turn + 1) % 2;
  }
  label {"move " enpassant}
  
  // Promotion
  when {promotePiece > -1}
  allow
  {
    board[promotePiece] += 2;
    promotePiece = -1;
    // Standard "end turn"
    selected = -1;
    enpassant = -1;
    if (dRotAuto == 1) {dRot = turn*2;}
    turn = (turn + 1) % 2;
  }
  label {"promote to knight"}
  
  when {promotePiece > -1}
  allow
  {
    board[promotePiece] += 4;
    promotePiece = -1;
    // Standard "end turn"
    selected = -1;
    enpassant = -1;
    if (dRotAuto == 1) {dRot = turn*2;}
    turn = (turn + 1) % 2;
  }
  label {"promote to bishop"}
  
  when {promotePiece > -1}
  allow
  {
    board[promotePiece] += 6;
    promotePiece = -1;
    // Standard "end turn"
    selected = -1;
    enpassant = -1;
    if (dRotAuto == 1) {dRot = turn*2;}
    turn = (turn + 1) % 2;
  }
  label {"promote to rook"}
  
  when {promotePiece > -1}
  allow
  {
    board[promotePiece] += 8;
    promotePiece = -1;
    // Standard "end turn"
    selected = -1;
    enpassant = -1;
    if (dRotAuto == 1) {dRot = turn*2;}
    turn = (turn + 1) % 2;
  }
  label {"promote to queen"}
  
  // *** Knight
  for i ( 0 .. 7 )
  {
    when
    {
      (selected != -1) and ((board[selected] + 1) / 2 == 2) and
      (
        (board[selected+knightmove[i]] == 0) or
        (
          (board[selected+knightmove[i]] > 0) and
          (board[selected+knightmove[i]]%2 == (turn+1)%2)
        )
      )
    }
    allow
    {
      board[selected+knightmove[i]] = board[selected];
      board[selected] = 0;
      // Standard "end turn"    
      selected = -1;
      enpassant = -1;
      if (dRotAuto == 1) {dRot = turn*2;}
      turn = (turn + 1) % 2;
    }
    label {"move " (selected+knightmove[i])}
  }
  
  // *** Bishop and Queen diagonal movement
  for i ( 0 .. 3 )
  {
    when
    {
      (selected != -1) and (prevmove == 0) and
      (((board[selected] + 1) / 2 == 3) or ((board[selected] + 1) / 2 == 5)) and
      (
        (board[selected+kingmove[i*2]] == 0) or
        (
          (board[selected+kingmove[i*2]] > 0) and
          (board[selected+kingmove[i*2]]%2 == (turn+1)%2)
        )
      )
    }
    allow
    {
      // Store the content of the square we are moving to
      test = board[selected+kingmove[i*2]];
      
      board[selected+kingmove[i*2]] = board[selected];
      board[selected] = 0;
      
      if (test == 0)
      {
        // Turn does NOT end: Ranged movement allowed
        selected = selected+kingmove[i*2];
        prevmove = kingmove[i*2];
      }
      if (test != 0)
      {
        // Standard "end turn"    
        selected = -1;
        enpassant = -1;
        if (dRotAuto == 1) {dRot = turn*2;}
        turn = (turn + 1) % 2;
      }
    }
    label {"move " (selected+kingmove[i*2])}
  }
  
  // *** Rook and Queen straight movement
  for i ( 0 .. 3 )
  {
    when
    {
      (selected != -1) and (prevmove == 0) and
      (((board[selected] + 1) / 2 == 4) or ((board[selected] + 1) / 2 == 5)) and
      (
        (board[selected+kingmove[i*2+1]] == 0) or
        (
          (board[selected+kingmove[i*2+1]] > 0) and
          (board[selected+kingmove[i*2+1]]%2 == (turn+1)%2)
        )
      )
    }
    allow
    {
      // First mark that this movement has disallowed castling for this piece
      // (Values hardcoded for 12*12 board)
      if (selected==26) {castlingBRLmoved = 1;}
      if (selected==33) {castlingBRRmoved = 1;}
      if (selected==110) {castlingWRLmoved = 1;}
      if (selected==117) {castlingWRRmoved = 1;}
      
      // Now do actual move
      // Store the content of the square we are moving to
      test = board[selected+kingmove[i*2+1]];
      
      board[selected+kingmove[i*2+1]] = board[selected];
      board[selected] = 0;
      
      if (test == 0)
      {
        // Turn does NOT end: Ranged movement allowed
        selected = selected+kingmove[i*2+1];
        prevmove = kingmove[i*2+1];
      }
      if (test != 0)
      {
        // Standard "end turn"    
        selected = -1;
        enpassant = -1;
        if (dRotAuto == 1) {dRot = turn*2;}
        turn = (turn + 1) % 2;
      }
    }
    label {"move " (selected+kingmove[i*2+1])}
  }
  
  // *** King movement
  // Normal movement
  for i ( 0 .. 7 )
  {
    when
    {
      (selected != -1) and ((board[selected] + 1) / 2 == 6) and
      (
        (board[selected+kingmove[i]] == 0) or
        (
          (board[selected+kingmove[i]] > 0) and
          (board[selected+kingmove[i]]%2 == (turn+1)%2)
        )
      )
    }
    allow
    {
      // First mark that this movement has disallowed castling
      if (turn%2==0) {castlingBKmoved = 1;}
      if (turn%2==1) {castlingWKmoved = 1;}
      
      // Now do the actual movement
      board[selected+kingmove[i]] = board[selected];
      board[selected] = 0;
      // Standard "end turn"    
      selected = -1;
      enpassant = -1;
      if (dRotAuto == 1) {dRot = turn*2;}
      turn = (turn + 1) % 2;
    }
    label {"move " (selected+kingmove[i])}
  }
  
  // Castling kingside
  when
  {
    (selected != -1) and ((board[selected] + 1) / 2 == 6) and
    (
      ((turn%2==0) and (castlingBKmoved == 0) and (castlingBRRmoved == 0)) or
      ((turn%2==1) and (castlingWKmoved == 0) and (castlingWRRmoved == 0))
    ) and
    (board[selected+1] == 0) and (board[selected+2] == 0)
  }
  allow
  {
    // Disallow further castling
    if (turn%2==0) {castlingBKmoved = 1;}
    if (turn%2==1) {castlingWKmoved = 1;}
    // Do the movement
    board[selected+2] = board[selected];
    board[selected] = 0;
    board[selected+1] = board[selected+3];
    board[selected+3] = 0;
    // Standard "end turn"    
    selected = -1;
    enpassant = -1;
    if (dRotAuto == 1) {dRot = turn*2;}
    turn = (turn + 1) % 2;
  }
  label {"move " (selected+2)}
  
  // Castling queenside

  when
  {
    (selected != -1) and ((board[selected] + 1) / 2 == 6) and
    (
      ((turn%2==0) and (castlingBKmoved == 0) and (castlingBRLmoved == 0)) or
      ((turn%2==1) and (castlingWKmoved == 0) and (castlingWRLmoved == 0))
    ) and
    (board[selected-1] == 0) and (board[selected-2] == 0) and (board[selected-3] == 0)
  }
  allow
  {
    // Disallow further castling
    if (turn%2==0) {castlingBKmoved = 1;}
    if (turn%2==1) {castlingWKmoved = 1;}
    // Do the movement
    board[selected-2] = board[selected];
    board[selected] = 0;
    board[selected-1] = board[selected-4];
    board[selected-4] = 0;
    // Standard "end turn"    
    selected = -1;
    enpassant = -1;
    if (dRotAuto == 1) {dRot = turn*2;}
    turn = (turn + 1) % 2;
  }
  label {"move " (selected-2)}
  
  // *** Continuation of any of the ranged movements
  when
  {
    (selected != -1) and (prevmove != 0) and
    (
      (board[selected+prevmove] == 0) or
      (
        (board[selected+prevmove] > 0) and
        (board[selected+prevmove]%2 == (turn+1)%2)
      )
    )
  }
  allow
  {
    // Store the content of the square we are moving to
    test = board[selected+prevmove];
    
    board[selected+prevmove] = board[selected];
    board[selected] = 0;
    
    if (test == 0)
    {
      // Ranged movement allowed to continue
      selected = selected+prevmove;
    }
    if (test != 0)
    {
      prevmove = 0;
      // Standard "end turn"    
      selected = -1;
      enpassant = -1;
      if (dRotAuto == 1) {dRot = turn*2;}
      turn = (turn + 1) % 2;
    }
  }
  label {"move " (selected+prevmove)}
  
  // *** Allow de-selection of the piece
  // For a normal piece
  when {(selected != -1) and (prevmove == 0)}
  allow {selected = -1;}
  label {"unselect " selected}
  
  // To end a ranged move
  when {(selected != -1) and (prevmove != 0)}
  allow
  {
    prevmove = 0;
    // Standard "end turn"    
    selected = -1;
    enpassant = -1;
    if (dRotAuto == 1) {dRot = turn*2;}
    turn = (turn + 1) % 2;
  }
  label {"unselect " selected}
}

display
{
  // White
  // Pawn - ♙   Knight - ♘   Bishop - ♗
  // Rook - ♖   Queen - ♕    King - ♔
  // Black
  // Pawn - ♟   Knight - ♞   Bishop - ♝
  // Rook - ♜   Queen - ♛    King - ♚
  stringtable counters
  {
    " ",
    "♙", "♟", "♘", "♞", "♗", "♝",
    "♖", "♜", "♕", "♛", "♔", "♚",
    " ",
    "p", "P", "n", "N", "b", "B",
    "r", "R", "q", "Q", "k", "K"
  }
  
  box {content {"ToothyGDL Chess v0.10"}}
  
  newline
  
  // Draw the board, assigning all the possible actions as we go
  // Only render the actual border, not the border spaces
  for y ( 2 .. (boardheight-3) )
  {
    for x ( 2 .. (boardwidth-3) )
    {
      box
      {
        width { 1em }
        height { 1em }
        // (dRotA[dRot]+dRotX[dRot]*x+dRotY[dRot]*y)
        if (board[(dRotA[dRot]+dRotX[dRot]*x+dRotY[dRot]*y)]>=0)
          {content {counters[ board[(dRotA[dRot]+dRotX[dRot]*x+dRotY[dRot]*y)] + charset*13 ]}}
        bind_action { "select " (dRotA[dRot]+dRotX[dRot]*x+dRotY[dRot]*y) }
        bind_action { "unselect " (dRotA[dRot]+dRotX[dRot]*x+dRotY[dRot]*y) }
        bind_action { "move " (dRotA[dRot]+dRotX[dRot]*x+dRotY[dRot]*y) }
        
        // The last border encountered is used
        border { 2px solid #fff }
        if (((x+y)%2 == 1) and (board[(dRotA[dRot]+dRotX[dRot]*x+dRotY[dRot]*y)] >= 0))
          { border { 2px solid #999 } }
        while_active { border { 2px solid #00f } }
        
        // The last background encountered is used
        background { #fff }
        if (((x+y)%2 == 1) and (board[(dRotA[dRot]+dRotX[dRot]*x+dRotY[dRot]*y)] >= 0))
          { background { #999 } }
      }
    }
    newline
  }
  newline
  box {height{3px}}
  newline
  
  // Draw the promotion selections if required
  box {if (promotePiece > -1) {content{"Promote to:  "}}}
  box
  {
    if (promotePiece > -1)
    {
      width { 1em }
      height { 1em }
      border { 2px solid #00f }
      background { #999 }
      content {counters[3 + (1-turn) + charset*13]}
      bind_action { "promote to knight" }
    }
  }
  box {if (promotePiece > -1) {content{" "}}}
  box
  {
    if (promotePiece > -1)
    {
      width { 1em }
      height { 1em }
      border { 2px solid #00f }
      background { #999 }
      content {counters[5 + (1-turn) + charset*13]}
      bind_action { "promote to bishop" }
    }
  }
  box {if (promotePiece > -1) {content{" "}}}
  box
  {
    if (promotePiece > -1)
    {
      width { 1em }
      height { 1em }
      border { 2px solid #00f }
      background { #999 }
      content {counters[7 + (1-turn) + charset*13]}
      bind_action { "promote to rook" }
    }
  }
  box {if (promotePiece > -1) {content{" "}}}
  box
  {
    if (promotePiece > -1)
    {
      width { 1em }
      height { 1em }
      border { 2px solid #00f }
      background { #999 }
      content {counters[9 + (1-turn) + charset*13]}
      bind_action { "promote to queen" }
    }
  }

  newline
  box {height{3px}}
  newline
  
  box
  {
    content { "      Clockwise      " }
    bind_action { "rotate clockwise" }
    border { 1px solid #000 }
  }
  box
  {
    content { "  Auto-Rotate On  " }
    if (dRotAuto == 0) { content { "  Auto-Rotate Off  " } }
    bind_action { "toggle autorotate" }
    border { 1px solid #000 }
  }
  box
  {
    content { " Anti-clockwise " }
    bind_action { "rotate anticlockwise" }
    border { 1px solid #000 }
  }
  
  newline
  box {height{3px}}
  newline
  
  stringtable charset_message
  {
    "  Switch to ascii pieces  ",
    " Switch to unicode pieces "
  }
  box
  {
    content { charset_message[charset] }
    bind_action { "switch charset" }
    border { 1px solid #000 }
  }
  
  newline
  box {content {" "}}
}


ec2-44-220-44-148.compute-1.amazonaws.com | ToothyWiki | Kazuhiko | RecentChanges | Login | Webcomic
Edit this page | View other revisions | Recently used referrers
Last edited January 25, 2006 6:34 pm (viewing revision 25, which is the newest) (diff)
Search: