pgn', '/\\{\\$FEN\\}/e', 'FENBoard()'); ## The (:chessboard:) markup by default generates a table, since ## some PHP installations don't have GD support compiled in. Markup('chessboard', '>{$FEN}', '/\\(:chessboard(\\s.*?)?:\\)/ei', "ChessTable(\$pagename, ParseArgs(PSS('$1')))"); ## $PGNMoves is an array of all moves (in PGN notation). The ## PGNMove($num, $white, $black) function adds moves for white and ## black into the array, removing any later moves that might be ## already present. $PGNMoves = array(); function PGNMove($num, $white, $black) { global $PGNMoves; if ($white{0} != '.') { $PGNMoves[$num*2-2] = $white; array_splice($PGNMoves, $num*2-1); } if ($black) { $PGNMoves[$num*2-1] = $black; array_splice($PGNMoves, $num*2); } } ## FENBoard() plays out the moves given in $PGNMoves and returns ## the resulting position as a FEN record. Internally $board ## is maintained as a 10x10 character string, with $board{11} ## corresponding to a8 and $board{88} corresponding to h1. ## Since PGN generally gives us only the name of the piece being ## moved and the square it moved to, we have to figure out where ## that piece came from. Castling is handled by directly manipulating ## the $board to the result of the castle. The from position ## for pawn moves is computed directly by inspection, while all ## other pieces use the $legalmoves array to determine the relative ## square offsets where pieces could've come from. Once we ## figure out where the piece came from, we put the piece in its ## new position ($to) and change the old position ($from) to an ## empty square. function FENBoard() { global $PGNMoves; $num = count($PGNMoves) * 2; # initialize the board and our allowed moves $board = "-----------rnbqkbnr--pppppppp--........--........-" . "-........--........--PPPPPPPP--RNBQKBNR-----------"; $legalmoves = array( 'K' => array(-11, -10, -9, -1, 1, 9, 10, 11), 'Q' => array(-11, -10, -9, -1, 1, 9, 10, 11), 'B' => array(-11, -9, 9, 11), 'R' => array(-10, -1, 1, 10), 'N' => array(-21, -19, -12, -8, 8, 12, 19, 21)); # now, walk through each move and update the board accordingly for($i=0; $i<$num; $i++) { $move = @$PGNMoves[$i]; # odd numbered $isblack = $i % 2; # moves are black if ($move == 'O-O') { # kingside castling $board = ($isblack) ? substr_replace($board, '.rk.', 15, 4) : substr_replace($board, '.RK.', 85, 4); continue; } if ($move == 'O-O-O') { # queenside castling $board = ($isblack) ? substr_replace($board, '..kr.', 11, 5) : substr_replace($board, '..KR.', 81, 5); continue; } if (preg_match( # all other moves "/^([KQRBNP]?)([a-h]?)([1-8]?)(x?)([a-h])([1-8])(=[KQRBNP])?/", $move, $match)) { @list($m, $piece, $ff, $fr, $cap, $tf, $tr, $promotion) = $match; $tf = strpos("abcdefgh", $tf)+1; $ff = ($ff) ? strpos("abcdefgh", $ff)+1 : 0; $to = (9-$tr)*10 + $tf; if (!$piece) $piece = "P"; $pp = ($isblack) ? strtolower($piece) : $piece; if ($pp == 'P') { # white's pawn move if ($cap) { # capture $from = (9-$tr)*10+10+$ff; if ($board{$to}=='.') $board{$to+10}='.'; # en passant } elseif ($board{$to+10}==$pp) $from=$to+10; # move elseif ($tr==4 && $board{$to+20}==$pp) $from=$to+20; # first move } elseif ($pp == 'p') { # black's pawn if ($cap) { # capture $from = (9-$tr)*10-10+$ff; if ($board{$to}=='.') $board{$to-10}='.'; # en passant } elseif ($board{$to-10}==$pp) $from=$to-10; # move elseif ($tr==5 && $board{$to-20}==$pp) $from=$to-20; # first move } else { # Here we look at squares along the lines for the piece # being moved. $n contains an offset for each square along # a valid line for the current piece. foreach($legalmoves[$piece] as $n) { for($from=$to+$n; $from>10 && $from<89; $from+=$n) { # if we find the piece we're looking for, we're done if ($board{$from} == $pp && (!$ff || ($from%10==$ff)) && (!$fr || ((int)($from/10)==(9-$fr)))) break 2; # if we find anything but an empty square, try another line if ($board{$from} != '.') continue 2; # kings and knights don't repeat offsets if ($piece == 'K' || $piece == 'N') continue 2; } } } # pawn promotions if ($promotion) $pp = ($isblack) ? strtolower($promotion{1}) : $promotion{1}; # move the piece $board{$to} = $pp; $board{$from} = '.'; } } # now, convert the board to a FEN record $board = preg_replace(array('/-+/', '/\\.+/e'), array('/', "strlen('$0')"), substr($board, 11, 78)); # and return it return $board; } ## The ChessTable function takes a FEN string (or the current ## game position as returned by FENBoard() above) and creates an ## HTML table representing the current game position. $ChessPieces = array( 'k' => 'kd-60.png', 'q' => 'qd-60.png', 'r' => 'rd-60.png', 'b' => 'bd-60.png', 'n' => 'nd-60.png', 'p' => 'pd-60.png', 'K' => 'kl-60.png', 'Q' => 'ql-60.png', 'R' => 'rl-60.png', 'B' => 'bl-60.png', 'N' => 'nl-60.png', 'P' => 'pl-60.png', '.' => 'blank-60.png'); SDV($HTMLStylesFmt['chess'], " table.chesstable td.square1 { background-color: #cccccc; } table.chesstable { border:1px solid #666666; } "); function ChessTable($pagename, $args) { global $ChessPieces, $ChessPubDirUrlFmt, $FmtV; SDV($ChessSquareFmt, "\$PieceName"); SDV($ChessDefaults, array( 'width' => 240, 'class' => 'chesstable', 'cellspacing' => 0, 'cellpadding' => 0)); $args = array_merge($ChessDefaults, $args); $fen = (@$args['']) ? $args[''][0] : FENBoard(); $width = $args['width']; $height = (@$args['height'] > 0) ? $args['height'] : $width; $tableargs = ""; foreach(array('class', 'cellspacing', 'cellpadding', 'align', 'style') as $a) if (isset($args[$a])) $tableargs .= " $a='".str_replace("'", "'", $args[$a])."'"; ## build the 8x8 board from the FEN record $board = str_repeat('.', 64); $file = 0; $rank = 0; $len = strlen($fen); for($i=0; ($i<$len) && ($rank+$file<64); $i++) { $k = $fen{$i}; if ($k > 0) { $file += $k; continue; } if ($k == '/') { $rank += 8; $file = 0; continue; } if ($ChessPieces[$k]) { $board{$rank+$file} = $k; $file++; } } ## Now generate the table from the 8x8 board $FmtV['$SquareWidth'] = $width / 8; $FmtV['$SquareHeight'] = $height / 8; for($i=0; $i<64; $i++) { if ($i%8 == 0) $out[] = ""; $FmtV['$PieceImg'] = $ChessPieces[$board{$i}]; $FmtV['$PieceName'] = $board{$i}; $FmtV['$SquareColor'] = ($i + (int)($i/8)) % 2; $out[] = FmtPageName($ChessSquareFmt, $pagename); if ($i%8 == 7) $out[] = ""; } return "".Keep(implode('', $out))."
"; }