Leaderboard
Popular Content
Showing content with the highest reputation on 12/07/2016 in all areas
-
spudw2k already pointed you in the right direction. The following crude example checks first if at least one commandline parameter is parsed; if so, it is interpreted as a number that is checked further. It then checks if a 2nd parameter is parsed, and if so, displays it. if $cmdline[0]>0 Then $number=$cmdline[1] Switch $number Case 1 to 50 msgbox(0,"number = " & $number,"Low number") Case 51 to 100 msgbox(0,"number = " & $number,"High number") case Else msgbox(0,"number = " & $number,"Invalid number") EndSwitch EndIf if $cmdline[0]>1 Then $param2=$cmdline[2] msgbox(0,"A 2nd parameter was parsed","The second parameter is" & $param2) EndIf Of course, if the order of the parsed parameters can be flexible you'd have to create more sophisticated checking, and maybe cycle through all parameters in a For-Next loop to identifiy which is which. I'll leave you to figure that one out for yourself (but search $cmdline or $cmdlineRaw on the forums to find plenty of examples).1 point
-
Sorry, little difficult to understand what you are asking...but if I understand what you are looking to accomplish, yes. Consider the following: From the help file link for running scripts myProg.exe param1 "This is a string parameter" 99 $CmdLine[0] ; This contains 3 parameters. $CmdLine[1] ; This contains param1. $CmdLine[2] ; This contains This is a string parameter. $CmdLine[3] ; This contains 99. Notice that each parameter has it's own index in the array. $CmdLine[0] >= 1 is a good first check as it means at least one parameter was provided. It will be up to you to verify the parameter(s) values. You could then switch the condition of the parameter you need. following the execution example above, let's switch the 3rd parameter and check for 99. If $CmdLine[0] >= 1 Then Switch $CmdLine[3] Case 99 ;Do Stuff EndSelect EndIf fuzzier, or clearer?1 point
-
Parsing Short Algebraic Notation This script is the main back end of a search engine. The search engine will attempt to match positions on the chessboard, after parsing an indeterminate number of moves, from a number of chess games. Although I have not yet written a front end, an important stage in development has been reached: i.e. parsing the moves. As the number of games being parsed is likely be quite high: speed and performance are a priority. Arguments are ordered such that the most frequently occuring scenarios are encountered (and dealt with) first. This demo walks you through three sample games, move by move, displaying each position as a two dimensional array. These games do not demonstrate the full flexibility of the script, however all the algorithms have been tested and appear to be working. Portable Game Notation is the most commonly used format for storing chess moves, either in a file or on a website. One reason that PGN is so popular is that it is easy to read and modify the contents. However chess players are not generally aware of correct PGN syntax, and there is also a degree of inconsistancy to be found in auto-generated PGN output. This method attempts to accomodate (at least the most frequent of) these inconsistancies. A few instances of files containing corrupt syntax may not be worth accomodating. Further research is required. This is perhaps the most complicated script I have produced to date. Although an in depth knowledge of chess is not required, an understanding of the basic rules of the game would make the code more accessible to the reader. Anyone is free to adapt this script to suit his or her own needs. For example: the code could be used to create a chess game viewer. I would be happy to answer any questions regarding the method I have used. Much of the code has been commented, to guide you through the jungle. I have also provided some links which you may find useful. It is hoped that at least some part of this script will be of value or interest to others besides myself. #include <Array.au3> #include <String.au3> Global $board[8][8] #comments-start ---------------------------------------------------------------- --------------------------------------- | h1 | g1 | f1 | e1 | d1 | c1 | b1 | a1 | |----|----|----|----|----|----|----|----| | h2 | g2 | f2 | e2 | d2 | c2 | b2 | a2 | |----|----|----|----|----|----|----|----| | h3 | g3 | f3 | e3 | d3 | c3 | b3 | a3 | |----|----|----|----|----|----|----|----| | h4 | g4 | f4 | e4 | d4 | c4 | b4 | a4 | |----|----|----|----|----|----|----|----| | h5 | g5 | f5 | e5 | d5 | c5 | b5 | a5 | |----|----|----|----|----|----|----|----| | h6 | g6 | f6 | e6 | d6 | c6 | b6 | a6 | |----|----|----|----|----|----|----|----| | h7 | g7 | f7 | e7 | d7 | c7 | b7 | a7 | |----|----|----|----|----|----|----|----| | h8 | g8 | f8 | e8 | d8 | c8 | b8 | a8 | --------------------------------------- The chessboard ($board) is viewed from black's perspective: This design choice sets white's back rank as the first row: this being the most intuitive orientation for the intended purpose. Each square is represented by two digit coordinates => h1 = 00, g2 = 11, f3 = 22 etc... A possible alternative would be to set a8 = 00 (white's perspective), however this would entail inverting every numerical coordinate. #comments-end ------------------------------------------------------------------ Global $nPath[9][8][9] #comments-start ---------------------------------------------------------------- A knight attacking a target square can have between two and eight possible locations. Calculating the coordinates for each possible location is unecessary. $nPath stores knight pathways in relation to 64 possible target squares on the chessboard. #comments-end ------------------------------------------------------------------ Local $scope, $pattern $scope = "2344443234666643468888644688886446888864468888643466664323444432" ; Number of pathways to 64 target squares. $pattern = "1221132220142321101524221116252312172624132725142615" & _ ; 336 Possible (two digit) coordinates for the knight's original location. "0222310323323004243331002005253432012106263533022207273634032337350424360525" & _ "12320141133302420040143403430141103015350444024211311636054503431232173706460444133307470545143406461535" & _ "22421151234312521050244413531151204025451454125221412646155513532242274716561454234317571555244416562545" & _ "32522161335322622060345423632161305035552464226231513656256523633252375726662464335327672565345426663555" & _ "42623171436332723070446433733171406045653474327241614666357533734262476736763474436337773575446436764565" & _ "5272415373424054744341507055754442517156764543527257774644537347455474465575" & _ "6251635250645351606554526166555362675654635755645665" ;=> 8th rank (target squares) $s = 1 For $i = 0 To 7 For $j = 0 To 7 $nPath[$i][$j][0] = StringMid($scope, $s, 1) ; Knight path variations: 2, 3, 4, 6 or 8 assigned to each square. $s += 1 Next Next $s = 1 For $i = 0 To 7 For $j = 0 To 7 For $k = 1 To $nPath[$i][$j][0] $nPath[$i][$j][$k] = StringMid($pattern, $s, 2) ; Populate the third dimension with knight pathway coordinates. $s += 2 Next Next Next Global $xPath[9][8][2][9] #comments-start ---------------------------------------------------------------- Any square (except the 4 corner squares) can be seen as the intersection point between two opposing diagonal paths. $xPath stores coordinate values for 26 diagonal paths in relation to each of the 64 squares on the chessboard. This array is used to determine which pieces (if any) are diagonally pinned. #comments-end ------------------------------------------------------------------ $scope = "8765432178765432678765435678765445678765345678762345678712345678" & _ ; Diagonal scope on 64 target squares. "1234567823456787345678764567876556787654678765437876543287654321" ; Second board: as above but with opposite diagonal inclination. $pattern = "001122334455667701122334455667021324354657031425364704152637051627061707" & _ ; Diagonal pathways originating in white's back rank. "102132435465760011223344556677011223344556670213243546570314253647041526370516270617" & _ "20314253647510213243546576001122334455667701122334455667021324354657031425364704152637051627" & _ "304152637420314253647510213243546576001122334455667701122334455667021324354657031425364704152637" & _ "405162733041526374203142536475102132435465760011223344556677011223344556670213243546570314253647" & _ "50617240516273304152637420314253647510213243546576001122334455667701122334455667021324354657" & _ "607150617240516273304152637420314253647510213243546576001122334455667701122334455667" & _ "706071506172405162733041526374203142536475102132435465760011223344556677" & _ ;=> 8th rank "000110021120031221300413223140051423324150061524334251600716253443526170" & _ ; Second board "011002112003122130041322314005142332415006152433425160071625344352617017263544536271" & _ "02112003122130041322314005142332415006152433425160071625344352617017263544536271273645546372" & _ "031221300413223140051423324150061524334251600716253443526170172635445362712736455463723746556473" & _ "041322314005142332415006152433425160071625344352617017263544536271273645546372374655647347566574" & _ "05142332415006152433425160071625344352617017263544536271273645546372374655647347566574576675" & _ "061524334251600716253443526170172635445362712736455463723746556473475665745766756776" & _ "071625344352617017263544536271273645546372374655647347566574576675677677" ;=> 8th rank $s = 1 For $k = 0 To 1 ; The second chessboard is placed behind the first. For $i = 0 To 7 For $j = 0 To 7 $xPath[$i][$j][$k][0] = StringMid($scope, $s, 1) $s += 1 Next Next Next $s = 1 For $k = 0 To 1 For $i = 0 To 7 For $j = 0 To 7 For $l = 1 To $xPath[$i][$j][$k][0] ; Populate the fourth dimension with diagonal pathways. $xPath[$i][$j][$k][$l] = StringMid($pattern, $s, 2) $s += 2 Next Next Next Next #comments-start ---------------------------------------------------------------- A PGN (portable game notation) file contains one or more chess games. Information such as players names, dates, venue etc are indicated by tags (inside square brackets). Commentry, variation lines and evaluation symbols sometimes appear as separate elements (normally inside brackets). Sometimes a special FEN (Forsyth Edwards Notation) tag is used to indicate a different starting position. In FEN: uppercase (KQBNRP) represents white's pieces, and lowercase (kqbnrp) represents black's pieces. => example on line 215 The same convention will be used to represent and identify the pieces on the chessboard. The moves of the game are written in SAN (Short Algebraic Notation). => See examples at EOF (lines 813 to 849) #comments-end ------------------------------------------------------------------ Local $PGN, $samplePGN, $rawPGN, $rawMoves, $games, $position _loadSamplePGN($samplePGN) ; Load the demonstration games. ; To circumvent inconsistancies in different PGN layouts, it may be necessary to tidy up a little before parsing. $rawPGN = $samplePGN ; Replace this line with => $rawPGN = FileRead("name_of_file.pgn") $rawPGN = StringRegExpReplace($rawPGN, "[\n][\h]*", @LF) ; Remove any horizontal white spaces after a line feed - align left. $rawPGN = StringRegExpReplace($rawPGN, '[%][^\r]+[\r]', @CR) ; Remove developer's inline comments. $rawPGN = StringRegExpReplace($rawPGN, "[\r\n]{3,}", @CRLF & @CRLF) ; Tidy the stack. $rawPGN = StringStripWS($rawPGN, 3) ; As above $rawPGN = StringReplace($rawPGN, @CRLF & @CRLF &'[', @CRLF & '|[') ; Create a game separator '|' for each game. $PGN = StringSplit($rawPGN, '|') ; All game data is stored for future access, as required. Local $position, $games[UBound($PGN)][2] ; To hold starting positions and the moves only. For $i = 1 To $PGN[0] ; Populate the first column with each starting position - FEN. If StringInStr($PGN[$i], "[FEN ", 0) Then ; The game does not start from the standard postion. $position = _StringBetween($PGN[$i], '[FEN "', '"]') $games[$i][0] = $position[0] Else $games[$i][0] = -1 ; Default starting position. EndIf Next #comments-start ---------------------------------------------------------------- Now we must tease out the information we wish to use. The starting position 'FEN' is obligatory. The information needed for a search engine includes whether a king can still castle or not, and whether a pawn can be captured using en passant. The 50 move rule (the number of moves since the last pawn move or capture of a piece) is only really relevant to certain endgames, and can generally be ignored. We want the search results to return all matching positions regardless of move order or sequence. Some attempt has been made to identify and remove non SAN elements (Further research is required). #comments-end ------------------------------------------------------------------ $rawPGN = StringRegExpReplace($rawPGN, '[;][^\r]+[\r]|[\x5b][^\x5d]+[\x5d]|[\x28][^\x29]+[\x29]|[\x7b][^\x7d]+[\x7d]|[0-9]+[.]{2,}|[$][0-9]+|(1-0)|(0-1)|(1/2-1/2)|[*]|[!?]+', '') #comments-start ---------------------------------------------------------------- Regular Expressions used (above) to identify and remove the non essential data include: 'Rest of line' comments: [;][^\r]+[\r] Tags in square brackets: [\x5b][^\x5d]+[\x5d] Alternative move variations in round brackets: [\x28][^\x29]+[\x29] Comments in curly brackets: [\x7b][^\x7d]+[\x7d] Black's move number plus trailing dots: [0-9]+[.]{2,} Nags (Evaluation remarks): [$][0-9]+ Game result: (1-0)|(0-1)|(1/2-1/2)|[*] Evaluation remarks (Non SAN): [!?]+ Move numbers: [0-9]+[.] #comments-end ------------------------------------------------------------------ $rawPGN = StringRegExpReplace($rawPGN, '[\s]+', ' ') ; Spaces separate white moves from black. $rawPGN = StringRegExpReplace($rawPGN, '[\x20]*[.][\x20]*|[\x20]*[0-9]+[.][\x20]*', '.') ; Dots act as move separators. $rawPGN = StringReplace($rawPGN, '++', '+') ; Replace double check symbol (non SAN) with check (SAN). $rawPGN = StringRegExpReplace($rawPGN, '[\s]*[\x7c][\s]*[.]*[\s]*', '|') ; Compession => forces the first and final moves flush with each game separator. $rawPGN = StringStripWS($rawPGN , 3) If StringLeft($rawPGN, 1) = "." Then $rawPGN = StringTrimLeft($rawPGN, 1) ; If the string starts with a dot, remove it. $rawMoves = StringSplit($rawPGN, '|') For $i = 1 To $PGN[0] $games[$i][1] = $rawMoves[$i] Next ; Clear memory. $rawPGN = 0 $rawMoves = 0 Local $FEN, $forsythEdwards, $edwardsForsyth, $fenBoard, $castlingOptions, $enPassantSquare Local $SAN, $notation, $piece, $target, $rank, $r, $y, $file, $f, $x, $partialCoordinate Local $moves, $m, $movesLimit, $floor, $partialElement, $player, $g Local $pieceType, $knights[1], $bishops[1], $rooks[1], $kings[2], $queens[1], $moreQueens[1] Local $attackers, $array, $lateralPath, $diagonal, $pins, $ref, $selected Local $errorReport, $player1, $player2, $status ; Standard starting position (FEN) Const $setUp = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1" ; => position - player - castling - enpassent target - 50 move rule - move number For $g = 1 To UBound($games) -1 ; Parsing begins. If $games[$g][0] = -1 Then $FEN = $setUp Else $FEN = $games[$g][0] EndIf $forsythEdwards = StringSplit($FEN, " ") $castlingOptions = $forsythEdwards[3] ; To be used as search criteria. $edwardsForsyth = StringReverse($forsythEdwards[1]) ; Sets up the pieces from black's POV. $fenBoard = StringSplit($edwardsForsyth, "/") ; Separates the eight ranks of the chessboard. For $i = 1 To 8 ; ranks 1 to 8 $y = $i - 1 ; vertical coordinate $x = 0 ; horizontal coordinate For $j = 1 To StringLen($fenBoard[$i]) If StringIsDigit(StringMid($fenBoard[$i], $j, 1)) Then ; Indicates one or more unoccupied squares. For $k = 1 To StringMid($fenBoard[$i], $j, 1) $board[$y][$x] = "-" $x += 1 Next Else $piece = StringMid($fenBoard[$i], $j, 1) $board[$y][$x] = $piece ; Places chess pieces on the chessboard. Select ; To keep tabs on king coordinates for later reference. Case $piece == "K" $kings[0] = $y & $x ; White king location Case $piece == "k" $kings[1] = $y & $x ; Black king location EndSelect $x += 1 EndIf Next Next $moves = StringSplit($games[$g][1], ".") ; Load the moves of each game. $movesLimit = $moves[0] ; Limiting the number of moves to parse is a useful search engine feature. Any limit set must not exceed the number of moves in the game. _displayInitialPosition() ; => For the purposes of this demonstration. If $forsythEdwards[2] == "w" Then ; Which player makes the first move? $floor = 1 ; White starts Else $player = 1 ; Black to move. $m = 1 ; Move Number $SAN = $moves[1] _parseMove() _displayCurrentPosition() ; => For demonstration purposes only. $floor = 2 EndIf If $moves[0] >= $floor Then For $m = $floor To $movesLimit $partialElement = StringSplit($moves[$m], " ") ; Split each element into two moves (one by white and one by black), or just one white move. $player = 0 ; White to move. For $s = 1 To $partialElement[0] $SAN = $partialElement[$s] _parseMove() _displayCurrentPosition() $player = 1 Next Next EndIf Next Func _parseMove() ; Read SAN and move the appropriate pieces on the chessboard. $notation = StringRegExpReplace($SAN, "[+|#]", "") ; Check (+) and Checkmate (#) symbols do not influence move coordinates, so we get rid of them. ; Locating the target square. If StringInStr($notation, "=") Then $target = StringMid($notation, StringInStr($notation, "=") -2, 2) ; A pawn promotes. Else $target = StringRight($notation, 2) ; With any other move type except castles. EndIf ; Determine the type of move to make. If StringRegExp(StringLeft($notation, 1), "(?-i)[K|Q|B|N|R]", 0) = 1 Then ; A piece moves (as opposed to a pawn move or castles). $notation = StringReplace($notation, "x", "") ; Remove the symbol 'x' => Captured pieces are automatically replaced when the board is updated. _squareGetCoordinates($y, $x) ; Convert the rank and file of the target square to numerical coordinates. Select ; Which type of piece to move? Case StringLeft($notation, 1) = "N" ; A knight move. _knightGetCoordinates($knights) If $knights[0] > 1 Then ; More than one knight attacks the target square. => Elimination process _eliminationProcess($knights) ; Seed out the insignificant attackers. Else ; The knight's location was discovered on the first attempt. $board[StringLeft($knights[1], 1)][StringRight($knights[1], 1)] = "-" ; Remove the knight. EndIf If $player = 0 Then $board[$y][$x] = "N" ; Place a white knight on the target square. Else $board[$y][$x] = "n" ; Place a black knight on the target square. EndIf Case StringLeft($notation, 1) = "B" ; A bishop move. $pieceType = "B" _bishopGetCoordinates($bishops) If $bishops[0] > 1 Then ; More than one bishop attacks the target square. _eliminationProcess($bishops) ; As above. Else ; The bishop's location was discovered on the first attempt. $board[StringLeft($bishops[1], 1)][StringRight($bishops[1], 1)] = "-" ; Remove the bishop. EndIf If $player = 0 Then $board[$y][$x] = "B" ; Place a white bishop on the target square. Else $board[$y][$x] = "b" ; Place a black bishop on the target square. EndIf Case StringLeft($notation, 1) = "Q" ; A queen move. $pieceType = "Q" _bishopGetCoordinates($queens) ; Queens move diagonally. _rookGetCoordinates($moreQueens) ; And along ranks and files. If $moreQueens[0] > 0 Then $queens[0] += $moreQueens[0] _ArrayDelete($moreQueens, 0) _ArrayConcatenate($queens, $moreQueens) EndIf If $queens[0] > 1 Then ; More than one queen attacks the target square. _eliminationProcess($queens) ; As above. Else ; The queen's location was discovered on the first attempt. $board[StringLeft($queens[1], 1)][StringRight($queens[1], 1)] = "-" ; Remove the queen. EndIf If $player = 0 Then $board[$y][$x] = "Q" ; Place a white queen on the target square. Else $board[$y][$x] = "q" ; Place a black queen on the target square. EndIf Case StringLeft($notation, 1) = "K" ; A king move. $rank = StringLeft($kings[$player],1) $file = StringRight($kings[$player],1) $board[$rank][$file] = "-" ; Remove the king from the original square. If $player = 0 Then $board[$y][$x] = "K" ; Place the white king on the target square. If $rank = 0 And $file = 3 Then _castlingSetState() ; Castling will no longer be an option for white. EndIf Else $board[$y][$x] = "k" ; Place the black king on the target square. If $rank = 7 And $file = 3 Then _castlingSetState(); Castling will no longer be an option for black. EndIf EndIf $kings[$player] = $y & $x ; Keep tabs on current king location. Case Else ; A Rook move. $pieceType = "R" _rookGetCoordinates($rooks) If $rooks[0] > 1 Then ; More than one rook attacks the target square. _eliminationProcess($rooks) ; As above. Else ; The rook's location was discovered on the first attempt. $board[StringLeft($rooks[1], 1)][StringRight($rooks[1], 1)] = "-" EndIf If $rooks[1] = "00" Or $rooks[1] = "07" Or $rooks[1] = "70" Or $rooks[1] = "77" Then _castlingSetState() ; Castling will no longer be possible on either the king's wing or the queen's wing. EndIf If $player = 0 Then $board[$y][$x] = "R" ; Place a white Rook on the target square. Else $board[$y][$x] = "r" ; Place a black Rook on the target square. EndIf EndSelect ElseIf StringRegExp(StringLeft($notation, 1), "(?-i)[a-h]", 0) = 1 Then ; A pawn move. _squareGetCoordinates($y, $x) ; Remove the pawn from the square it came from. If StringInStr($notation, "x") And $x > 104 - Asc(StringLeft($notation, 1)) Then ; A pawn captures a piece and moves closer to the a file. If $player = 0 Then $board[$y - 1][$x - 1] = "-" Else $board[$y + 1][$x - 1] = "-" EndIf _enPassant() ; Test if a pawn is being captured en passant and remove it accordingly. ElseIf StringInStr($notation, "x") And $x < 104 - Asc(StringLeft($notation, 1)) Then ; A pawn captures a piece and moves closer to the h file. If $player = 0 Then $board[$y - 1][$x + 1] = "-" Else $board[$y + 1][$x + 1] = "-" EndIf _enPassant() ; As above. ElseIf $player = 0 Then If $y = 3 And $board[2][$x] = "-" Then ; The pawn moves two steps forward. $board[$y - 2][$x] = "-" $z = $y - 1 $enPassantSquare = $z & $x ; Update en passant square coordinates. Else ; Otherwise pawns always move one step forward. $board[$y - 1][$x] = "-" EndIf Else If $y = 4 And $board[5][$x] = "-" Then ; As above. $board[$y + 2][$x] = "-" $z = $y + 1 $enPassantSquare = $z & $x ; NOTE: Pawns may only be captured en passant on the very next move by the opponent. Else ; The pawn moves one step forward. $board[$y + 1][$x] = "-" EndIf EndIf ; Place a pawn or promoted piece (queen, bishop, knight or rook) on the target square. If $player = 0 And $y < 7 Then $board[$y][$x] = "P" ; Place a white pawn on the target square. ElseIf $player = 0 And $y = 7 Then $board[$y][$x] = StringRight($notation, 1) ; Place a promoted white piece on the target square. ElseIf $player = 1 And $y > 0 Then $board[$y][$x] = "p" ; Place a black pawn on the target square. ElseIf $player = 1 And $y = 0 Then $board[$y][$x] = StringLower(StringRight($notation, 1)) ; Place a promoted black piece on the target square. EndIf ElseIf $notation = "o-o" Then ; Castles Kingside If $player = 0 Then ; White castles $board[0][0] = "-" $board[0][1] = "K" $board[0][2] = "R" $board[0][3] = "-" Else ; Black castles $board[7][0] = "-" $board[7][1] = "k" $board[7][2] = "r" $board[7][3] = "-" EndIf _castlingSetState() If $player = 0 Then $kings[$player] = "01" Else $kings[$player] = "71" EndIf ElseIf $notation = "o-o-o" Then ; Castles Queenside If $player = 0 Then ; White castles $board[0][7] = "-" $board[0][5] = "K" $board[0][4] = "R" $board[0][3] = "-" Else ; Black castles $board[7][7] = "-" $board[7][5] = "k" $board[7][4] = "r" $board[7][3] = "-" EndIf _castlingSetState() If $player = 0 Then $kings[$player] = "05" ; Keep tabs on current king location (white). Else $kings[$player] = "75" ; As above (for black). EndIf EndIf EndFunc Func _squareGetCoordinates(ByRef $r, ByRef $f) If $target = StringRegExp($target,"[a-h][1-8]?") = 1 Then $errorReport = "Parsing failed during game " & $g & " at move " & $m & "." & @CRLF & "Process terminated." _errorLog() MsgBox(0, "Unable to parse coordinates.", $errorReport) Exit ; Handling for this error => Pending. EndIf $r = StringRight($target, 1) -1 $f = 104 - Asc(StringLeft($target, 1)) EndFunc Func _knightGetCoordinates(ByRef $array) $selected = "" For $i = 1 To $nPath[$y][$x][0] $rank = StringLeft($nPath[$y][$x][$i], 1) $file = StringRight($nPath[$y][$x][$i], 1) If $player = 0 And $board[$rank][$file] == "N" Then $selected &= $nPath[$y][$x][$i] & "," ElseIf $player = 1 And $board[$rank][$file] == "n" Then $selected &= $nPath[$y][$x][$i] & "," EndIf Next $selected = StringTrimRight($selected, 1) $array = StringSplit($selected, ",") EndFunc ; Searches diagonally outwards from the target square in 4 directions, until a bishop (queen) or another obsticle is encountered. Func _bishopGetCoordinates(ByRef $array) For $i = UBound($array) - 1 To 1 Step -1 ; Clear all elements left over from any previous bishop (or queen) move. _ArrayDelete($array, $i) Next $rank = $y + 1 $file = $x + 1 While $rank < 8 And $file < 8 If $board[$rank][$file] == StringUpper($pieceType) And $player = 0 Then _ArrayAdd($array, $rank & $file) ExitLoop ElseIf $board[$rank][$file] == StringLower($pieceType) And $player = 1 Then _ArrayAdd($array, $rank & $file) ExitLoop ElseIf $board[$rank][$file] <> "-" Then ExitLoop EndIf $rank += 1 $file += 1 WEnd $rank = $y + 1 $file = $x - 1 While $rank < 8 And $file > -1 If $board[$rank][$file] == StringUpper($pieceType) And $player = 0 Then _ArrayAdd($array, $rank & $file) ExitLoop ElseIf $board[$rank][$file] == StringLower($pieceType) And $player = 1 Then _ArrayAdd($array, $rank & $file) ExitLoop ElseIf $board[$rank][$file] <> "-" Then ExitLoop EndIf $rank += 1 $file -= 1 WEnd $rank = $y - 1 $file = $x + 1 While $rank > -1 And $file < 8 If $board[$rank][$file] == StringUpper($pieceType) And $player = 0 Then _ArrayAdd($array, $rank & $file) ExitLoop ElseIf $board[$rank][$file] == StringLower($pieceType) And $player = 1 Then _ArrayAdd($array, $rank & $file) ExitLoop ElseIf $board[$rank][$file] <> "-" Then ExitLoop EndIf $rank -= 1 $file += 1 WEnd $rank = $y - 1 $file = $x - 1 While $rank > -1 And $file > -1 If $board[$rank][$file] == StringUpper($pieceType) And $player = 0 Then _ArrayAdd($array, $rank & $file) ExitLoop ElseIf $board[$rank][$file] == StringLower($pieceType) And $player = 1 Then _ArrayAdd($array, $rank & $file) ExitLoop ElseIf $board[$rank][$file] <> "-" Then ExitLoop EndIf $rank -= 1 $file -= 1 WEnd $array[0] = UBound($array) -1 EndFunc ; Searches outwards from the target square in 4 directions (along the rank and file), until a rook (queen) or another obsticle is encountered. Func _rookGetCoordinates(ByRef $array) For $i = UBound($array) - 1 To 1 Step -1 ; Clear all elements left over from any previous rook (or queen) move. _ArrayDelete($array, $i) Next $file = $x + 1 While $file < 8 If $board[$y][$file] == StringUpper($pieceType) And $player = 0 Then _ArrayAdd($array, $y & $file) ExitLoop ElseIf $board[$y][$file] == StringLower($pieceType) And $player = 1 Then _ArrayAdd($array, $y & $file) ExitLoop ElseIf $board[$y][$file] <> "-" Then ExitLoop EndIf $file += 1 WEnd $file = $x - 1 While $file > -1 If $board[$y][$file] == StringUpper($pieceType) And $player = 0 Then _ArrayAdd($array, $y & $file) ExitLoop ElseIf $board[$y][$file] == StringLower($pieceType) And $player = 1 Then _ArrayAdd($array, $y & $file) ExitLoop ElseIf $board[$y][$file] <> "-" Then ExitLoop EndIf $file -= 1 WEnd $rank = $y + 1 While $rank < 8 If $board[$rank][$x] == StringUpper($pieceType) And $player = 0 Then _ArrayAdd($array, $rank & $x) ExitLoop ElseIf $board[$rank][$x] == StringLower($pieceType) And $player = 1 Then _ArrayAdd($array, $rank & $x) ExitLoop ElseIf $board[$rank][$x] <> "-" Then ExitLoop EndIf $rank += 1 WEnd $rank = $y - 1 While $rank > -1 If $board[$rank][$x] == StringUpper($pieceType) And $player = 0 Then _ArrayAdd($array, $rank & $x) ExitLoop ElseIf $board[$rank][$x] == StringLower($pieceType) And $player = 1 Then _ArrayAdd($array, $rank & $x) ExitLoop ElseIf $board[$rank][$x] <> "-" Then ExitLoop EndIf $rank -= 1 WEnd $array[0] = UBound($array) -1 EndFunc Func _eliminationProcess(ByRef $attackers) If StringLen($notation) < 5 Then ; Location coordinates are either partially given, or not at all. If StringLen($notation) = 4 Then ; Partial location coordinates are given. _selectPartialCoordinates($attackers) ; Select the pieces where partial coordinates are given. EndIf If $attackers[0] > 1 Then ; At least one piece must be pinned. _eliminateDiagonalPins($attackers) ; Pins by an opposing queen or bishop. If $attackers[0] > 1 Then ; There must be at least one piece pinned laterally along a rank or file. _eliminateLateralPins($attackers) ; Pins by an opposing queen or a rook. If $attackers[0] = 1 Then $board[StringLeft($attackers[1], 1)][StringRight($attackers[1], 1)] = "-" Else ; Corrupt input, or possibly some fairy chess variant. $errorReport = "A problem was encountered during game " & $g & " at move " & $m & "." _errorLog() ; Testing and error analysis. EndIf Else ; The piece or pieces were pinned diagonally. $board[StringLeft($attackers[1], 1)][StringRight($attackers[1], 1)] = "-" EndIf Else $board[StringLeft($attackers[1], 1)][StringRight($attackers[1], 1)] = "-" EndIf ElseIf StringLen($notation) = 5 Then ; On rare occasions all coordinates are given. $target = StringMid($notation, 2, 2) _squareGetCoordinates($rank, $file) $board[$rank][$file] = "-" EndIf EndFunc Func _selectPartialCoordinates(ByRef $array) $selected = "" ; To store the coordinates of pieces found on the given rank or file. $partialCoordinate = StringMid($notation, 2, 1) If StringIsAlpha($partialCoordinate) Then ; File is given $ref = 104 - Asc($partialCoordinate) For $i = 1 To $array[0] If StringRight($array[$i], 1) = $ref Then $selected &= $array[$i] & "," EndIf Next Else ; Rank is given $ref = $partialCoordinate - 1 For $i = 1 To $array[0] If StringLeft($array[$i], 1) = $ref Then $selected &= $array[$i] & "," EndIf Next EndIf $selected = StringTrimRight($selected, 1) $array = StringSplit($selected, ",") EndFunc Func _eliminateDiagonalPins(ByRef $array) $pins = "" ; Used to store index numbers of each element found to contain the coordinates of a diagonally pinned piece. For $i = 1 To $array[0] ; For all pieces attacking the target's coordinates. $rank = StringLeft($array[$i], 1) $file = StringRight($array[$i], 1) For $j = 0 To 1 ; Along two diagonal inclinations. $diagonal = "" ; String to be concatenated with pieces found along each diagonal. For $k = 1 To $xPath[$rank][$file][$j][0] ; scope $target = $xPath[$rank][$file][$j][$k] If $target <> $array[$i] Then $diagonal &= $board[StringLeft($target, 1)][StringRight($target, 1)] Else $diagonal &= "-" EndIf Next If $player = 0 Then If StringRegExp($diagonal, '(?-i)[q|b][-]+[K]', 0) = 1 Or StringRegExp($diagonal, '(?-i)[K][-]+[q|b]', 0) = 1 Then ; An illegal self check has occured! $pins &= $i ; Concatenate the string of index numbers requiring deletion. EndIf Else If StringRegExp($diagonal, '(?-i)[Q|B][-]+[k]', 0) = 1 Or StringRegExp($diagonal, '(?-i)[k][-]+[Q|B]', 0) = 1 Then ; As above. $pins &= $i EndIf EndIf Next Next If $pins <> "" Then ; Eliminate pinned pieces from the array. For $j = StringLen($pins) To 1 Step -1 _ArrayDelete($array, StringMid($pins, $j, 1)) $array[0] -= 1 Next EndIf EndFunc Func _eliminateLateralPins(ByRef $array) $pins = "" ; Used to store index numbers of each element found to contain the coordinates of a piece pinned along a rank or file. $r = StringLeft($kings[$player], 1) $f = StringRight($kings[$player], 1) For $i = 1 To $array[0] ; For all pieces attacking the target square. $lateralPath = "" ; To represent pieces found along the rank or file on which the king (of the player who's turn it is) is located. If StringLeft($array[$i], 1) = $r Then For $j = 0 To 7 ; Build a string representing pieces found on the same rank as the king. If $r & $j <> $array[$i] Then $lateralPath &= $board[$r][$j] Else $lateralPath &= "-" EndIf Next ElseIf StringRight($array[$i], 1) = $f Then For $j = 0 To 7 ; Build a string representing pieces found on the same file as the king. If $j & $f <> $array[$i] Then $lateralPath &= $board[$j][$f] Else $lateralPath &= "-" EndIf Next EndIf If $player = 0 Then If StringRegExp($lateralPath, '[q|r][-]+[K]', 0) = 1 Or StringRegExp($lateralPath, '[K][-]+[q|r]', 0) = 1 Then ; An illegal self check has occured! $pins &= $i ; Concatenate the string of index numbers requiring deletion. EndIf Else If StringRegExp($lateralPath, '[Q|R][-]+[k]', 0) = 1 Or StringRegExp($lateralPath, '[k][-]+[Q|R]', 0) = 1 Then ; As above. $pins &= $i EndIf EndIf Next If $pins <> "" Then ; Eliminate each pinned piece's coordinates from the array. For $j = StringLen($pins) To 1 Step -1 ; Delete the elements in reverse order to avoid corrupting the index numbers during the process. _ArrayDelete($array, StringMid($pins, $j, 1)) $array[0] -= 1 Next EndIf EndFunc Func _castlingSetState() ; Update current available castling options for both players. If $castlingOptions <> "-" Then If $player = 0 Then If StringInStr($notation, 'R') = 0 And StringRegExp($castlingOptions, '(?-i)[K|Q]', 0) = 1 Then $castlingOptions = StringRegExpReplace($castlingOptions, '(?-i)[KQ|K|Q]', "") ; White can no longer castle on either wing. ElseIf StringInStr($notation, 'R', 1) = 1 Then If $rooks[1] = "00" Then $castlingOptions = StringReplace($castlingOptions, "K", "", 0, 1) ; White can no longer castle kingside. ElseIf $rooks[1] = "07" Then $castlingOptions = StringReplace($castlingOptions, "Q", "", 0, 1) ; White can no longer castle queenside. EndIf EndIf Else If StringInStr($notation, 'R') = 0 And StringRegExp($castlingOptions, '(?-i)[k|q]', 0) = 1 Then $castlingOptions = StringRegExpReplace($castlingOptions, '(?-i)[kq|k|q]', "") ; Black can no longer castle either on either wing. ElseIf StringInStr($notation, 'R', 1) = 1 Then If $rooks[1] = "70" Then $castlingOptions = StringReplace($castlingOptions, "k", "", 0, 1) ; Black can no longer castle kingside. ElseIf $rooks[1] = "77" Then $castlingOptions = StringReplace($castlingOptions, "q", "", 0, 1) ; Black can no longer castle queenside. EndIf EndIf EndIf If $castlingOptions == "" Then $castlingOptions = "-" EndIf EndIf EndFunc Func _enPassant() If $board[$y][$x] == "-" Then ; A pawn is captured en passant. If $player = 0 Then $board[$y - 1][$x] = "-" ; Remove the captured white pawn from the 4th rank. Else $board[$y + 1][$x] = "-" ; Remove the captured black pawn from the 5th rank. EndIf EndIf EndFunc Func _errorLog() ; For testing and error analysis. FileWriteLine("error.log", $errorReport) EndFunc Func _displayInitialPosition() ; For demonstration purposes only. $player1 = StringRegExpReplace($PGN[$g], '[\s\S]+White "[\s]*([^"]+)"[\s\S]+', '\1') $player2 = StringRegExpReplace($PGN[$g], '[\s\S]+Black "[\s]*([^"]+)"[\s\S]+', '\1') If $player1 <> "czardas" Then $status = "Game " &$g &" - " & $player1 & " v " & $player2 Else $status = "Game " &$g &" - Test Position" EndIf _ArrayDisplay($board, $status) ; Show the position on the board at the start of each game. EndFunc Func _displayCurrentPosition() ; For demonstration purposes only. If $player = 0 Then $status = "Game " & $g & " - Position after " & $m & ". " & $SAN Else $status = "Game " & $g & " - Position after " & $m & "... " & $SAN EndIf _ArrayDisplay($board, $status) ; Show the position on the board after parsing each move. EndFunc Func _loadSamplePGN(ByRef $sample) ; For demonstration purposes only. MsgBox(0, "Instructions", "Please read the title of each display." & @CRLF & "Press ESC to move on to the next screen.") $sample = '[Event ""]' & @CRLF & _ ; First Game '[Site "Vienna"]' & @CRLF & _ '[Date "1910"]' & @CRLF & _ '[Round ""]' & @CRLF & _ '[White "Reti"]' & @CRLF & _ '[Black "Tartakower"]' & @CRLF & _ '[Result "1-0"]' & @CRLF & @CRLF & _ '1.e4 c6 2.d4 d5 3.Nc3 dxe4 4.Nxe4 Nf6 5.Qd3 e5 6.dxe5 Qa5+ 7.Bd2 Qxe5' & @CRLF & _ '8.O-O-O Nxe4 9.Qd8+ Kxd8 10.Bg5+ (10.Bg5+ Kc7 11.Bd8#) 1-0' & @CRLF & @CRLF & _ '[Event "-"]' & @CRLF & _ ; Second Game '[Site "Italian Opera House - Paris"]' & @CRLF & _ '[Date "1858"]' & @CRLF & _ '[Round "?"]' & @CRLF & _ '[White "Paul Morphy"]' & @CRLF & _ '[Black "Duke Karl / Count Isouard"]' & @CRLF & _ '[Result "1-0"]' & @CRLF & _ '[ECO "C41"]' & @CRLF & @CRLF & _ '1.e4 e5 2.Nf3 d6 3.d4 Bg4 {This is a weak move' & @CRLF & _ 'already.--Fischer} 4.dxe5 Bxf3 5.Qxf3 dxe5 6.Bc4 Nf6 7.Qb3 Qe7' & @CRLF & _ '8.Nc3 c6 9.Bg5 {Black is in what is like a zugzwang position' & @CRLF & _ 'here. He can not develop the Queens knight because the pawn' & @CRLF & _ 'is hanging. The bishop is blocked because of the' & @CRLF & _ 'Queen.-- Fischer} b5 10.Nxb5 cxb5 11.Bxb5+ Nbd7 12.O-O-O Rd8' & @CRLF & _ '13.Rxd7 Rxd7 14.Rd1 Qe6 15.Bxd7+ Nxd7 16.Qb8+ Nxb8 17.Rd8# 1-0' & @CRLF & @CRLF & _ '[Event "London, England"]' & @CRLF & _ ; Third Game '[Site "London, England"]' & @CRLF & _ '[Date "1867.??.??"]' & @CRLF & _ '[EventDate "?"]' & @CRLF & _ '[Round "?"]' & @CRLF & _ '[Result "1-0"]' & @CRLF & _ '[White "Bird"]' & @CRLF & _ '[Black "Steinitz"]' & @CRLF & _ '[ECO "C60"]' & @CRLF & @CRLF & _ '1.e4 e5 2.Nf3 Nc6 3.Bb5 Nf6 4.d4 exd4 5.e5 Ne4 6.Nxd4 Be7' & @CRLF & _ '7.O-O Nxd4 8.Qxd4 Nc5 9.f4 b6 10.f5 Nb3 11.Qe4 Nxa1 12.f6 Bc5+' & @CRLF & _ '13.Kh1 Rb8 14.e6 Rg8 15.Qxh7 Rf8 16.exf7+ Rxf7 17.Re1+ Be7' & @CRLF & _ '18.Qg8+ Rf8 19.f7# 1-0' EndFunc Related Links: http://www.very-best.de/pgn-spec.htm http://homepage.mac.com/s_lott/books/python/html/p05/p05c06_chess.html1 point