Advertisement
Guest User

Chess Playing Program

a guest
Feb 16th, 2013
108
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
VB.NET 53.56 KB | None | 0 0
  1. Public Class Form1
  2.  
  3.     ' Extended Project Chess v1.9.
  4.     ' Description: This program plays chess against the player, using minimax with alpha-beta pruning to determine the best move for the program to make.
  5.     ' It can look ahead a ply of 4 in no more than a few seconds. Castling and en passant are not implemented. The player can undo moves.
  6.     ' Finished: 16 Februrary 2013.
  7.  
  8.     ' Pieces: [1, Pawn] [2, Rook] [3, Knight] [4, Bishop] [5, Queen] [6, King] [+, White] [-, Black] [0, Empty] [99, Invalid]
  9.     ' Board: Index of bottom left corner = 21. Index of bottom right corner = 28. Index of top left corner = 91. Index of bottom left corner = 98.
  10.  
  11.     ' Structures/classes.
  12.     Public Class ChessMove
  13.         Public startOfMove As Integer
  14.         Public endOfMove As Integer
  15.     End Class
  16.     Public Class ChessBoard
  17.         Public indexes(119) As Integer
  18.         Public materialValue As Integer
  19.         Public whiteIndexes As New List(Of Integer)
  20.         Public blackIndexes As New List(Of Integer)
  21.     End Class
  22.  
  23.     ' Formatting:
  24.     Dim squareSize As Integer = 64
  25.     Dim squareColor1 As Color = Color.Peru
  26.     Dim squareColor2 As Color = Color.NavajoWhite
  27.     Dim squareColor3 As Color = Color.Green
  28.     Dim squareColor4 As Color = Color.Gold
  29.  
  30.     ' Arrays and lists:
  31.     Dim visualBoard(7, 7) As Windows.Forms.PictureBox
  32.     Dim gameBoard As New ChessBoard
  33.  
  34.     ' Controls:
  35.     Dim lblInfo As New Label
  36.  
  37.     ' Other variables:
  38.     Dim ply As Integer = 4
  39.     Dim playersTurnNow As Boolean = True
  40.     Dim recordOfGameBoards As New List(Of ChessBoard)
  41.     Dim recordOfMoves As New List(Of ChessMove)
  42.     ' The highest values that a board can have, both with a king and without a king.
  43.     Dim highestLegalValue, infinity As Integer
  44.     Dim gameState As String = "In progress"
  45.     Dim pieceValue As New Dictionary(Of Integer, Integer)
  46.  
  47.     ' Variables that are declared here so that they can be used in multiple subroutines:
  48.     Dim gameBoardCopy As New ChessBoard
  49.     Dim moveNumber As Integer = 1
  50.     Dim pieceIsSelectedByPlayer As Boolean = False
  51.     Dim selectedIndexByPlayer As Integer = 0
  52.  
  53.     ' Functions involving the form and controls.
  54.     Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
  55.  
  56.         ' Determines what to do upon loading the form.
  57.  
  58.         formatForm()
  59.         formatOtherStuff()
  60.         createBoard()
  61.         resetGame()
  62.  
  63.     End Sub
  64.     Private Sub Form1_KeyDown(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyEventArgs) Handles Me.KeyDown
  65.  
  66.         ' Handles all keys pressed.
  67.  
  68.         Select Case e.KeyCode
  69.             Case Keys.Escape
  70.                 Me.Close()
  71.         End Select
  72.  
  73.     End Sub
  74.     Private Sub btnUndoMove_Click(sender As System.Object, e As System.EventArgs) Handles btnUndoMove.Click
  75.  
  76.         undoGameMove()
  77.         Me.ActiveControl = Nothing
  78.  
  79.     End Sub
  80.     Private Sub btnResetGame_Click(sender As System.Object, e As System.EventArgs) Handles btnResetGame.Click
  81.  
  82.         resetGame()
  83.         Me.ActiveControl = Nothing
  84.  
  85.     End Sub
  86.     Private Sub formatForm()
  87.  
  88.         ' Format the form (sizes, text, buttons, etc).
  89.  
  90.         ' Size the form (16 and 38 account for the extra size of the borders of the form).
  91.         Me.Size = New System.Drawing.Size(8 * squareSize + 16, 8 * squareSize + 38 + 40)
  92.         ' Add the title to the form.
  93.         Me.Text = "Chess v1.8"
  94.         ' Centre the form.
  95.         Me.Left = (Screen.PrimaryScreen.WorkingArea.Width - Me.Width) / 2
  96.         Me.Top = (Screen.PrimaryScreen.WorkingArea.Height - Me.Height) / 2
  97.         ' Make the border look nicer.
  98.         Me.FormBorderStyle = Windows.Forms.FormBorderStyle.FixedSingle
  99.         ' Don't give the user the option to mess up the sizes by maximizing the form.
  100.         Me.MaximizeBox = False
  101.         ' Format lblInfo.
  102.         lblInfo.Size = New Size(300, 14)
  103.         lblInfo.Location = New System.Drawing.Point(10, 8 * squareSize + 13)
  104.         lblInfo.Text = "Your move."
  105.         lblInfo.BringToFront()
  106.         Me.Controls.Add(lblInfo)
  107.         ' Format buttons.
  108.         btnResetGame.Location = New System.Drawing.Point(Me.Width - 90, 8 * squareSize + 8)
  109.         btnUndoMove.Location = New System.Drawing.Point(Me.Width - 170, 8 * squareSize + 8)
  110.  
  111.     End Sub
  112.     Private Sub formatOtherStuff()
  113.  
  114.         ' Format and set up everything about from the form.
  115.  
  116.         Randomize()
  117.         ' Set up the pieceValue dictionary.
  118.         pieceValue(0) = 0
  119.         pieceValue(1) = 100
  120.         pieceValue(2) = 500
  121.         pieceValue(3) = 300
  122.         pieceValue(4) = 325
  123.         pieceValue(5) = 975
  124.         pieceValue(6) = 100000
  125.         For i = 1 To 6
  126.             pieceValue(-i) = -pieceValue(i)
  127.         Next i
  128.         highestLegalValue = 11025
  129.         infinity = 400000
  130.  
  131.     End Sub
  132.  
  133.     ' Functions dealing with the flow of the game.
  134.     Private Sub createBoard()
  135.  
  136.         ' Create the board, both visually and as storage.
  137.  
  138.         ' Create the board visually.
  139.         For i = 0 To 7
  140.             For j = 0 To 7
  141.                 ' Create and format the new square.
  142.                 Dim square As New Windows.Forms.PictureBox
  143.                 square.Size = New System.Drawing.Point(squareSize, squareSize)
  144.                 square.Location = New System.Drawing.Point(i * squareSize, j * squareSize)
  145.                 square.BorderStyle = BorderStyle.FixedSingle
  146.                 square.SizeMode = PictureBoxSizeMode.CenterImage
  147.  
  148.                 ' Attach the corresponding board index as a tag to the square.
  149.                 ' The 7 is there because the squares are placed in reverse order to how they're stored in the board array. In the
  150.                 ' board array, 0 is at the bottom left, as they are placed, 0 is at the top left.
  151.                 square.Tag = translateTo1D(i, j)
  152.  
  153.                 ' Add the event handler for clicking.
  154.                 AddHandler square.MouseDown, AddressOf squareClicked
  155.  
  156.                 ' Color the square (adds the two digits of the index and colors depending on whether it is odd or even, this saves code).
  157.                 If (i + j) Mod 2 = 1 Then
  158.                     square.BackColor = squareColor1
  159.                 Else
  160.                     square.BackColor = squareColor2
  161.                 End If
  162.  
  163.                 ' Add square to visualBoard array.
  164.                 visualBoard(i, j) = square
  165.  
  166.             Next j
  167.         Next i
  168.  
  169.         ' Display the board.
  170.         For Each square In visualBoard
  171.             Me.Controls.Add(square)
  172.         Next square
  173.  
  174.         ' Create the board for storage (the 1 dimensional array). 0 for an empty square, 99 for an invalid square.
  175.         For i = 0 To 119
  176.             If (i <= 20) Or (i >= 99) Or (i Mod 10 = 0) Or (i Mod 10 = 9) Then
  177.                 gameBoard.indexes(i) = 99
  178.             Else
  179.                 gameBoard.indexes(i) = 0
  180.             End If
  181.         Next i
  182.  
  183.     End Sub
  184.     Private Sub resetGame()
  185.  
  186.         ' Sets the game to the initial position.
  187.  
  188.         setDefaultBoard()
  189.         recordOfGameBoards.Clear()
  190.         recordOfGameBoards.Add(copyOfChessBoard(gameBoard))
  191.         recordOfMoves.Clear()
  192.         moveNumber = 1
  193.         playersTurnNow = True
  194.         pieceIsSelectedByPlayer = False
  195.         gameState = "In progress"
  196.         updateGame()
  197.  
  198.     End Sub
  199.     Private Sub changeTurn()
  200.  
  201.         ' Changes the current turn.
  202.         playersTurnNow = Not playersTurnNow
  203.         pieceIsSelectedByPlayer = False
  204.         selectedIndexByPlayer = 0
  205.         If hasNoMoves(gameBoard, playersTurnNow) Then
  206.             If isInCheck(gameBoard, playersTurnNow) Then
  207.                 If playersTurnNow Then
  208.                     gameState = "Black win"
  209.                 Else
  210.                     gameState = "White win"
  211.                 End If
  212.             Else
  213.                 gameState = "Stalemate"
  214.             End If
  215.         Else
  216.             If Not playersTurnNow Then
  217.                 updateGame()
  218.                 calculateComputerMove()
  219.             End If
  220.         End If
  221.  
  222.     End Sub
  223.     Private Sub updateGame()
  224.  
  225.         ' Update the visualBoard, update lblInfo and deal with the game ending.
  226.  
  227.         Application.DoEvents()
  228.  
  229.         Dim pieceImage As Image
  230.         Dim currentPieceNumber As Integer
  231.         Dim moveList As List(Of ChessMove) = generateMoves(gameBoard, selectedIndexByPlayer, True, True)
  232.  
  233.         ' Set the images on the squares.
  234.         For i = 0 To 7
  235.             For j = 0 To 7
  236.                 currentPieceNumber = gameBoard.indexes(translateTo1D(i, j))
  237.                 Select Case currentPieceNumber
  238.                     ' Select the correct image for the piece number.
  239.                     Case 1 : pieceImage = My.Resources.imgWhitePawn
  240.                     Case 2 : pieceImage = My.Resources.imgWhiteRook
  241.                     Case 3 : pieceImage = My.Resources.imgWhiteKnight
  242.                     Case 4 : pieceImage = My.Resources.imgWhiteBishop
  243.                     Case 5 : pieceImage = My.Resources.imgWhiteQueen
  244.                     Case 6 : pieceImage = My.Resources.imgWhiteKing
  245.                     Case -1 : pieceImage = My.Resources.imgBlackPawn
  246.                     Case -2 : pieceImage = My.Resources.imgBlackRook
  247.                     Case -3 : pieceImage = My.Resources.imgBlackKnight
  248.                     Case -4 : pieceImage = My.Resources.imgBlackBishop
  249.                     Case -5 : pieceImage = My.Resources.imgBlackQueen
  250.                     Case -6 : pieceImage = My.Resources.imgBlackKing
  251.                     Case Else : pieceImage = Nothing
  252.                 End Select
  253.                 ' Physically change the image.
  254.                 visualBoard(i, j).Image = pieceImage
  255.                 ' Set the color of the square, assuming it won't be a possible move or a previous move.
  256.                 If (i + j) Mod 2 = 1 Then
  257.                     visualBoard(i, j).BackColor = squareColor1
  258.                 Else
  259.                     visualBoard(i, j).BackColor = squareColor2
  260.                 End If
  261.                 ' Set the color of the square if it is part of the computer's last move.
  262.                 If moveNumber > 2 Then
  263.                     If playersTurnNow Then
  264.                         If (recordOfMoves.Last.startOfMove = translateTo1D(i, j) Or recordOfMoves.Last.endOfMove = translateTo1D(i, j)) Then
  265.                             visualBoard(i, j).BackColor = squareColor4
  266.                         End If
  267.                     End If
  268.                 End If
  269.                 ' Set the color of the square if it is a move to be displayed as possible to the player.
  270.                 If pieceIsSelectedByPlayer Then
  271.                     For Each possibleMove In moveList
  272.                         If possibleMove.endOfMove = translateTo1D(i, j) Then
  273.                             visualBoard(i, j).BackColor = squareColor3
  274.                             Exit For
  275.                         End If
  276.                     Next possibleMove
  277.                 End If
  278.             Next j
  279.         Next i
  280.  
  281.         ' Update lblInfo.
  282.         If playersTurnNow Then
  283.             If isInCheck(gameBoard, True) Then
  284.                 lblInfo.Text = "Your turn. You are in check."
  285.             Else
  286.                 lblInfo.Text = "Your turn."
  287.             End If
  288.         Else
  289.             If isInCheck(gameBoard, False) Then
  290.                 lblInfo.Text = "AI's turn. AI is in check."
  291.             Else
  292.                 lblInfo.Text = "AI's turn."
  293.             End If
  294.         End If
  295.  
  296.         ' Check if the game is over.
  297.         If gameState <> "In progress" Then
  298.             If gameState = "White win" Then
  299.                 lblInfo.Text = "Checkmate. You win!"
  300.             ElseIf gameState = "Black win" Then
  301.                 lblInfo.Text = "Checkmate. AI wins."
  302.             ElseIf gameState = "Stalemate" Then
  303.                 lblInfo.Text = "Stalemate."
  304.             End If
  305.             Select Case MsgBox(lblInfo.Text & " Would you like to play again?", MsgBoxStyle.YesNo, "Game Over")
  306.                 Case MsgBoxResult.Yes
  307.                     resetGame()
  308.                 Case MsgBoxResult.No
  309.                     Me.Close()
  310.             End Select
  311.         End If
  312.  
  313.         Application.DoEvents()
  314.  
  315.     End Sub
  316.     Private Sub squareClicked(ByVal sender As System.Object, ByVal e As System.Windows.Forms.MouseEventArgs)
  317.  
  318.         ' Determines what is to be done when a board square has been clicked.
  319.  
  320.         If playersTurnNow Then
  321.             Dim clickedIndexByPlayer As Integer = sender.Tag
  322.             ' Determine if a piece is already selected by the player.
  323.             If pieceIsSelectedByPlayer Then
  324.                 ' Determine if the clicked piece is the previously selected one.
  325.                 If clickedIndexByPlayer = selectedIndexByPlayer Then
  326.                     ' Deselect the piece.
  327.                     pieceIsSelectedByPlayer = False
  328.                 Else
  329.                     ' Determine if the clicked square is an available move.
  330.                     Dim availableMove As Boolean = False
  331.                     For Each possibleMove In generateMoves(gameBoard, selectedIndexByPlayer, True, True)
  332.                         If possibleMove.endOfMove = clickedIndexByPlayer Then
  333.                             ' Make the move.
  334.                             availableMove = True
  335.                             Dim finalMove As New ChessMove
  336.                             finalMove.startOfMove = selectedIndexByPlayer
  337.                             finalMove.endOfMove = clickedIndexByPlayer
  338.                             makeFinalMove(finalMove)
  339.                             Exit For
  340.                         End If
  341.                     Next possibleMove
  342.                     ' If the clicked square was not an available move, select the clicked piece if possible.
  343.                     If Not availableMove Then
  344.                         If gameBoard.indexes(clickedIndexByPlayer) > 0 Then
  345.                             pieceIsSelectedByPlayer = True
  346.                             selectedIndexByPlayer = clickedIndexByPlayer
  347.                         End If
  348.                     End If
  349.                 End If
  350.             Else
  351.                 ' Select the clicked piece if possible.
  352.                 If gameBoard.indexes(clickedIndexByPlayer) > 0 Then
  353.                     pieceIsSelectedByPlayer = True
  354.                     selectedIndexByPlayer = clickedIndexByPlayer
  355.                 End If
  356.             End If
  357.         End If
  358.  
  359.         updateGame()
  360.  
  361.     End Sub
  362.  
  363.     ' Functions dealing with the board and moves.
  364.     Private Sub setDefaultBoard()
  365.  
  366.         ' Set up the default chess board.
  367.  
  368.         ' Remove all pieces and indexes.
  369.         removeAllPieces(gameBoard)
  370.         gameBoard.whiteIndexes.Clear()
  371.         gameBoard.blackIndexes.Clear()
  372.         ' Add white pawns.
  373.         For i = 31 To 38
  374.             addPiece(gameBoard, 1, i)
  375.             gameBoard.whiteIndexes.Add(i)
  376.         Next i
  377.         ' Add the other white pieces.
  378.         addPiece(gameBoard, 2, 21)
  379.         addPiece(gameBoard, 2, 28)
  380.         addPiece(gameBoard, 3, 22)
  381.         addPiece(gameBoard, 3, 27)
  382.         addPiece(gameBoard, 4, 23)
  383.         addPiece(gameBoard, 4, 26)
  384.         addPiece(gameBoard, 5, 24)
  385.         addPiece(gameBoard, 6, 25)
  386.         For i = 21 To 28
  387.             gameBoard.whiteIndexes.Add(i)
  388.         Next i
  389.         ' Add black pawns.
  390.         For i = 81 To 88
  391.             addPiece(gameBoard, -1, i)
  392.             gameBoard.blackIndexes.Add(i)
  393.         Next i
  394.         ' Add the other black pieces.
  395.         addPiece(gameBoard, -2, 91)
  396.         addPiece(gameBoard, -2, 98)
  397.         addPiece(gameBoard, -3, 92)
  398.         addPiece(gameBoard, -3, 97)
  399.         addPiece(gameBoard, -4, 93)
  400.         addPiece(gameBoard, -4, 96)
  401.         addPiece(gameBoard, -5, 94)
  402.         addPiece(gameBoard, -6, 95)
  403.         For i = 91 To 98
  404.             gameBoard.blackIndexes.Add(i)
  405.         Next i
  406.         ' Set the material value.
  407.         gameBoard.materialValue = 0
  408.  
  409.     End Sub
  410.     Private Sub addPiece(ByRef board As ChessBoard, pieceNumber As Integer, index As Integer)
  411.  
  412.         ' Inserts the given piece at the given index on the given board.
  413.  
  414.         board.indexes(index) = pieceNumber
  415.  
  416.     End Sub
  417.     Private Sub removePiece(ByRef board As ChessBoard, index As Integer)
  418.  
  419.         ' Replaces the piece at the given index with an empty space on the given board.
  420.  
  421.         board.indexes(index) = 0
  422.  
  423.     End Sub
  424.     Private Sub removeAllPieces(ByRef board As ChessBoard)
  425.  
  426.         ' Remove all pieces and indexes from the given board.
  427.         For x = 0 To 7
  428.             For y = 0 To 7
  429.                 removePiece(board, translateTo1D(x, y))
  430.             Next y
  431.         Next x
  432.         board.whiteIndexes.Clear()
  433.         board.blackIndexes.Clear()
  434.         board.materialValue = 0
  435.  
  436.     End Sub
  437.     Private Sub movePiece(ByRef board As ChessBoard, move As ChessMove)
  438.  
  439.         ' Moves the given piece on the given board.
  440.         ' This updates the material count and the list of piece indexes for each side, and checks for pawn promotion.
  441.  
  442.         Dim startOfMove As Integer = move.startOfMove
  443.         Dim endOfMove As Integer = move.endOfMove
  444.         Dim startPiece As Integer = board.indexes(startOfMove)
  445.         Dim endPiece As Integer = board.indexes(endOfMove)
  446.  
  447.         ' Update the index lists.
  448.         If startPiece > 0 Then
  449.             ' White/human move.
  450.             If endPiece <> 0 Then
  451.                 board.materialValue -= pieceValue(endPiece)
  452.                 ' Remove the black index if that piece is taken by this move.
  453.                 board.blackIndexes.Remove(endOfMove)
  454.             End If
  455.             ' Change the white index to the new index the piece has been moved to.
  456.             board.whiteIndexes(board.whiteIndexes.FindIndex(Function(index) index.Equals(startOfMove))) = endOfMove
  457.         ElseIf startPiece < 0 Then
  458.             ' Black/computer move.
  459.             If endPiece <> 0 Then
  460.                 board.materialValue -= pieceValue(endPiece)
  461.                 ' Remove the white index if that piece is taken by this move.
  462.                 board.whiteIndexes.Remove(endOfMove)
  463.             End If
  464.             ' Change the black index to the new index the piece has been moved to.
  465.             board.blackIndexes(board.blackIndexes.FindIndex(Function(index) index.Equals(startOfMove))) = endOfMove
  466.         End If
  467.  
  468.         ' Make the move.
  469.         addPiece(board, startPiece, endOfMove)
  470.         removePiece(board, startOfMove)
  471.  
  472.         ' Check for pawn promotion.
  473.         If startPiece = 1 Then
  474.             If endOfMove >= 91 Then
  475.                 addPiece(board, 5, endOfMove)
  476.                 board.whiteIndexes.Add(endOfMove)
  477.                 board.materialValue += 875
  478.                 Exit Sub
  479.             End If
  480.         ElseIf startPiece = -1 Then
  481.             If endOfMove <= 28 Then
  482.                 addPiece(board, -5, endOfMove)
  483.                 board.blackIndexes.Add(endOfMove)
  484.                 board.materialValue -= 875
  485.                 Exit Sub
  486.             End If
  487.         End If
  488.  
  489.     End Sub
  490.     Private Sub undoMove(ByRef board As ChessBoard, move As ChessMove, endPiece As Integer, pawnPromotion As Boolean)
  491.  
  492.         ' Undoes a chess move. The piece that may have been taken must be known.
  493.  
  494.         Dim startPiece As Integer = board.indexes(move.endOfMove)
  495.         If pawnPromotion Then
  496.             startPiece = startPiece / Math.Abs(startPiece)
  497.         End If
  498.         Dim startIndex As Integer = move.startOfMove
  499.         Dim endIndex As Integer = move.endOfMove
  500.  
  501.         ' The moved piece goes back to the square it moved from.
  502.         addPiece(board, startPiece, startIndex)
  503.         ' The moved piece's index changes.
  504.         If startPiece > 0 Then
  505.             board.whiteIndexes(board.whiteIndexes.FindIndex(Function(index) index.Equals(endIndex))) = startIndex
  506.         ElseIf startPiece < 0 Then
  507.             board.blackIndexes(board.blackIndexes.FindIndex(Function(index) index.Equals(endIndex))) = startIndex
  508.         End If
  509.  
  510.         ' The captured piece goes back to where it was taken from.
  511.         addPiece(board, endPiece, endIndex)
  512.         ' The captured piece's index is added.
  513.         If endPiece > 0 Then
  514.             board.whiteIndexes.Add(move.endOfMove)
  515.         ElseIf endPiece < 0 Then
  516.             board.blackIndexes.Add(move.endOfMove)
  517.         End If
  518.  
  519.         board.materialValue += pieceValue(endPiece)
  520.  
  521.     End Sub
  522.     Private Sub placeNewPiece(ByRef board As ChessBoard, pieceNumber As Integer, index As Integer)
  523.  
  524.         ' Place a new piece at an index, changing the whiteIndex or blackIndex list and material value as appropriate.
  525.  
  526.         addPiece(board, pieceNumber, index)
  527.         If pieceNumber > 0 Then
  528.             board.whiteIndexes.Add(index)
  529.         Else
  530.             board.blackIndexes.Add(index)
  531.         End If
  532.         board.materialValue += pieceValue(pieceNumber)
  533.  
  534.     End Sub
  535.     Private Function moveIsPawnPromotion(board As ChessBoard, move As ChessMove)
  536.  
  537.         ' Determines whether the given mode is a pawn promotion.
  538.  
  539.         Dim start As Integer = move.startOfMove
  540.         If start >= 81 Then
  541.             If board.indexes(move.startOfMove) = 1 Then
  542.                 Return True
  543.             End If
  544.         ElseIf start <= 38 Then
  545.             If board.indexes(move.startOfMove) = -1 Then
  546.                 Return True
  547.             End If
  548.         End If
  549.  
  550.         Return False
  551.  
  552.     End Function
  553.     Private Sub makeFinalMove(move As ChessMove)
  554.  
  555.         ' Make the given move on the game board.
  556.  
  557.         movePiece(gameBoard, move)
  558.         recordOfGameBoards.Add(copyOfChessBoard(gameBoard))
  559.         recordOfMoves.Add(move)
  560.         moveNumber += 1
  561.         changeTurn()
  562.  
  563.     End Sub
  564.     Private Sub undoGameMove()
  565.  
  566.         ' Takes the game back a move, until it's the player's move again.
  567.         If playersTurnNow And moveNumber <> 1 Then
  568.             recordOfGameBoards.RemoveRange(recordOfGameBoards.Count - 2, 2)
  569.             gameBoard = copyOfChessBoard(recordOfGameBoards.Last)
  570.             recordOfMoves.RemoveRange(recordOfMoves.Count - 2, 2)
  571.             moveNumber -= 2
  572.         ElseIf (Not playersTurnNow) Then
  573.             recordOfGameBoards.RemoveRange(recordOfGameBoards.Count - 1, 1)
  574.             gameBoard = copyOfChessBoard(recordOfGameBoards.Last)
  575.             recordOfMoves.RemoveRange(recordOfMoves.Count - 1, 1)
  576.             moveNumber -= 1
  577.         End If
  578.  
  579.         pieceIsSelectedByPlayer = False
  580.         playersTurnNow = True
  581.         updateGame()
  582.  
  583.     End Sub
  584.     Private Function generateMoves(board As ChessBoard, index As Integer, humanPlayer As Boolean, legalOnly As Boolean)
  585.  
  586.         ' Generate moves for a given piece on a given board. Returns a list of moves.
  587.  
  588.         Dim endOfMoveList As New List(Of Integer)
  589.         Dim piece As Integer = board.indexes(index)
  590.         Dim temp As Integer
  591.  
  592.         Select Case piece
  593.             Case 1
  594.                 ' White pawn
  595.                 ' Forward one if no piece is there.
  596.                 If board.indexes(index + 10) = 0 Then
  597.                     endOfMoveList.Add(index + 10)
  598.                     ' Forward two if the pawn is yet to move.
  599.                     If index >= 31 And index <= 38 Then
  600.                         If board.indexes(index + 20) = 0 Then
  601.                             endOfMoveList.Add(index + 20)
  602.                         End If
  603.                     End If
  604.                 End If
  605.                 ' Diagonally forward one if any opposite piece is there.
  606.                 If (board.indexes(index + 9)) * (piece) < 0 Then
  607.                     endOfMoveList.Add(index + 9)
  608.                 End If
  609.                 If (board.indexes(index + 11)) * (piece) < 0 Then
  610.                     endOfMoveList.Add(index + 11)
  611.                 End If
  612.             Case -1
  613.                 ' Black pawn
  614.                 ' Backward one if no piece is there.
  615.                 If board.indexes(index - 10) = 0 Then
  616.                     endOfMoveList.Add(index - 10)
  617.                     ' Forward two if the pawn is yet to move.
  618.                     If index >= 81 And index <= 88 Then
  619.                         If board.indexes(index - 20) = 0 Then
  620.                             endOfMoveList.Add(index - 20)
  621.                         End If
  622.                     End If
  623.                 End If
  624.                 ' Diagonally backward one if any opposite piece is there.
  625.                 temp = board.indexes(index - 9)
  626.                 If (temp * (piece) < 0) And (Not temp = 99) Then
  627.                     endOfMoveList.Add(index - 9)
  628.                 End If
  629.                 temp = board.indexes(index - 11)
  630.                 If (temp * (piece) < 0) And (Not temp = 99) Then
  631.                     endOfMoveList.Add(index - 11)
  632.                 End If
  633.             Case 3, -3
  634.                 ' Knight
  635.                 temp = board.indexes(index - 21)
  636.                 If Not (temp = 99) Then
  637.                     If temp * piece <= 0 Then endOfMoveList.Add(index - 21)
  638.                 End If
  639.                 temp = board.indexes(index - 19)
  640.                 If Not (temp = 99) Then
  641.                     If temp * piece <= 0 Then endOfMoveList.Add(index - 19)
  642.                 End If
  643.                 temp = board.indexes(index - 12)
  644.                 If Not (temp = 99) Then
  645.                     If temp * piece <= 0 Then endOfMoveList.Add(index - 12)
  646.                 End If
  647.                 temp = board.indexes(index - 8)
  648.                 If Not (temp = 99) Then
  649.                     If temp * piece <= 0 Then endOfMoveList.Add(index - 8)
  650.                 End If
  651.                 temp = board.indexes(index + 8)
  652.                 If Not (temp = 99) Then
  653.                     If temp * piece <= 0 Then endOfMoveList.Add(index + 8)
  654.                 End If
  655.                 temp = board.indexes(index + 12)
  656.                 If Not (temp = 99) Then
  657.                     If temp * piece <= 0 Then endOfMoveList.Add(index + 12)
  658.                 End If
  659.                 temp = board.indexes(index + 19)
  660.                 If Not (temp = 99) Then
  661.                     If temp * piece <= 0 Then endOfMoveList.Add(index + 19)
  662.                 End If
  663.                 temp = board.indexes(index + 21)
  664.                 If Not (temp = 99) Then
  665.                     If temp * piece <= 0 Then endOfMoveList.Add(index + 21)
  666.                 End If
  667.             Case 6, -6
  668.                 ' King
  669.                 temp = board.indexes(index - 11)
  670.                 If Not (temp = 99) Then
  671.                     If temp * piece <= 0 Then endOfMoveList.Add(index - 11)
  672.                 End If
  673.                 temp = board.indexes(index - 10)
  674.                 If Not (temp = 99) Then
  675.                     If temp * piece <= 0 Then endOfMoveList.Add(index - 10)
  676.                 End If
  677.                 temp = board.indexes(index - 9)
  678.                 If Not (temp = 99) Then
  679.                     If temp * piece <= 0 Then endOfMoveList.Add(index - 9)
  680.                 End If
  681.                 temp = board.indexes(index - 1)
  682.                 If Not (temp = 99) Then
  683.                     If temp * piece <= 0 Then endOfMoveList.Add(index - 1)
  684.                 End If
  685.                 temp = board.indexes(index + 1)
  686.                 If Not (temp = 99) Then
  687.                     If temp * piece <= 0 Then endOfMoveList.Add(index + 1)
  688.                 End If
  689.                 temp = board.indexes(index + 9)
  690.                 If Not (temp = 99) Then
  691.                     If temp * piece <= 0 Then endOfMoveList.Add(index + 9)
  692.                 End If
  693.                 temp = board.indexes(index + 10)
  694.                 If Not (temp = 99) Then
  695.                     If temp * piece <= 0 Then endOfMoveList.Add(index + 10)
  696.                 End If
  697.                 temp = board.indexes(index + 11)
  698.                 If Not (temp = 99) Then
  699.                     If temp * piece <= 0 Then endOfMoveList.Add(index + 11)
  700.                 End If
  701.             Case 2, -2
  702.                 'Rook
  703.                 ' Check to the left.
  704.                 For i = 1 To 7
  705.                     temp = board.indexes(index - i)
  706.                     If (temp = 99) Or (temp * piece > 0) Then
  707.                         Exit For
  708.                     ElseIf (temp * piece < 0) Then
  709.                         endOfMoveList.Add(index - i)
  710.                         Exit For
  711.                     Else
  712.                         endOfMoveList.Add(index - i)
  713.                     End If
  714.                 Next i
  715.                 ' Check to the right.
  716.                 For i = 1 To 7
  717.                     temp = board.indexes(index + i)
  718.                     If (temp = 99) Or (temp * piece > 0) Then
  719.                         Exit For
  720.                     ElseIf (temp * piece < 0) Then
  721.                         endOfMoveList.Add(index + i)
  722.                         Exit For
  723.                     Else
  724.                         endOfMoveList.Add(index + i)
  725.                     End If
  726.                 Next i
  727.                 ' Check down.
  728.                 For i = 1 To 7
  729.                     temp = board.indexes(index - (10 * i))
  730.                     If (temp = 99) Or (temp * piece > 0) Then
  731.                         Exit For
  732.                     ElseIf (temp * piece < 0) Then
  733.                         endOfMoveList.Add(index - (10 * i))
  734.                         Exit For
  735.                     Else
  736.                         endOfMoveList.Add(index - (10 * i))
  737.                     End If
  738.                 Next i
  739.                 ' Check up.
  740.                 For i = 1 To 7
  741.                     temp = board.indexes(index + (10 * i))
  742.                     If (temp = 99) Or (temp * piece > 0) Then
  743.                         Exit For
  744.                     ElseIf (temp * piece < 0) Then
  745.                         endOfMoveList.Add(index + (10 * i))
  746.                         Exit For
  747.                     Else
  748.                         endOfMoveList.Add(index + (10 * i))
  749.                     End If
  750.                 Next i
  751.             Case 4, -4
  752.                 ' Bishop.
  753.                 ' Check down/left.
  754.                 For i = 1 To 7
  755.                     temp = board.indexes(index - (11 * i))
  756.                     If (temp = 99) Or (temp * piece > 0) Then
  757.                         Exit For
  758.                     ElseIf (temp * piece < 0) Then
  759.                         endOfMoveList.Add(index - (11 * i))
  760.                         Exit For
  761.                     Else
  762.                         endOfMoveList.Add(index - (11 * i))
  763.                     End If
  764.                 Next i
  765.                 ' Check down/right
  766.                 For i = 1 To 7
  767.                     temp = board.indexes(index - (9 * i))
  768.                     If (temp = 99) Or (temp * piece > 0) Then
  769.                         Exit For
  770.                     ElseIf (temp * piece < 0) Then
  771.                         endOfMoveList.Add(index - (9 * i))
  772.                         Exit For
  773.                     Else
  774.                         endOfMoveList.Add(index - (9 * i))
  775.                     End If
  776.                 Next i
  777.                 ' Check up/left.
  778.                 For i = 1 To 7
  779.                     temp = board.indexes(index + (9 * i))
  780.                     If (temp = 99) Or (temp * piece > 0) Then
  781.                         Exit For
  782.                     ElseIf (temp * piece < 0) Then
  783.                         endOfMoveList.Add(index + (9 * i))
  784.                         Exit For
  785.                     Else
  786.                         endOfMoveList.Add(index + (9 * i))
  787.                     End If
  788.                 Next i
  789.                 ' Check up/right.
  790.                 For i = 1 To 7
  791.                     temp = board.indexes(index + (11 * i))
  792.                     If (temp = 99) Or (temp * piece > 0) Then
  793.                         Exit For
  794.                     ElseIf (temp * piece < 0) Then
  795.                         endOfMoveList.Add(index + (11 * i))
  796.                         Exit For
  797.                     Else
  798.                         endOfMoveList.Add(index + (11 * i))
  799.                     End If
  800.                 Next i
  801.             Case 5, -5
  802.                 ' Queen (the code for the rook and bishop is simply copied here).
  803.  
  804.                 ' Check to the left.
  805.                 For i = 1 To 7
  806.                     temp = board.indexes(index - i)
  807.                     If (temp = 99) Or (temp * piece > 0) Then
  808.                         Exit For
  809.                     ElseIf (temp * piece < 0) Then
  810.                         endOfMoveList.Add(index - i)
  811.                         Exit For
  812.                     Else
  813.                         endOfMoveList.Add(index - i)
  814.                     End If
  815.                 Next i
  816.                 ' Check to the right.
  817.                 For i = 1 To 7
  818.                     temp = board.indexes(index + i)
  819.                     If (temp = 99) Or (temp * piece > 0) Then
  820.                         Exit For
  821.                     ElseIf (temp * piece < 0) Then
  822.                         endOfMoveList.Add(index + i)
  823.                         Exit For
  824.                     Else
  825.                         endOfMoveList.Add(index + i)
  826.                     End If
  827.                 Next i
  828.                 ' Check down.
  829.                 For i = 1 To 7
  830.                     temp = board.indexes(index - (10 * i))
  831.                     If (temp = 99) Or (temp * piece > 0) Then
  832.                         Exit For
  833.                     ElseIf (temp * piece < 0) Then
  834.                         endOfMoveList.Add(index - (10 * i))
  835.                         Exit For
  836.                     Else
  837.                         endOfMoveList.Add(index - (10 * i))
  838.                     End If
  839.                 Next i
  840.                 ' Check up.
  841.                 For i = 1 To 7
  842.                     temp = board.indexes(index + (10 * i))
  843.                     If (temp = 99) Or (temp * piece > 0) Then
  844.                         Exit For
  845.                     ElseIf (temp * piece < 0) Then
  846.                         endOfMoveList.Add(index + (10 * i))
  847.                         Exit For
  848.                     Else
  849.                         endOfMoveList.Add(index + (10 * i))
  850.                     End If
  851.                 Next i
  852.                 ' Check down/left.
  853.                 For i = 1 To 7
  854.                     temp = board.indexes(index - (11 * i))
  855.                     If (temp = 99) Or (temp * piece > 0) Then
  856.                         Exit For
  857.                     ElseIf (temp * piece < 0) Then
  858.                         endOfMoveList.Add(index - (11 * i))
  859.                         Exit For
  860.                     Else
  861.                         endOfMoveList.Add(index - (11 * i))
  862.                     End If
  863.                 Next i
  864.                 ' Check down/right
  865.                 For i = 1 To 7
  866.                     temp = board.indexes(index - (9 * i))
  867.                     If (temp = 99) Or (temp * piece > 0) Then
  868.                         Exit For
  869.                     ElseIf (temp * piece < 0) Then
  870.                         endOfMoveList.Add(index - (9 * i))
  871.                         Exit For
  872.                     Else
  873.                         endOfMoveList.Add(index - (9 * i))
  874.                     End If
  875.                 Next i
  876.                 ' Check up/left.
  877.                 For i = 1 To 7
  878.                     temp = board.indexes(index + (9 * i))
  879.                     If (temp = 99) Or (temp * piece > 0) Then
  880.                         Exit For
  881.                     ElseIf (temp * piece < 0) Then
  882.                         endOfMoveList.Add(index + (9 * i))
  883.                         Exit For
  884.                     Else
  885.                         endOfMoveList.Add(index + (9 * i))
  886.                     End If
  887.                 Next i
  888.                 ' Check up/right.
  889.                 For i = 1 To 7
  890.                     temp = board.indexes(index + (11 * i))
  891.                     If (temp = 99) Or (temp * piece > 0) Then
  892.                         Exit For
  893.                     ElseIf (temp * piece < 0) Then
  894.                         endOfMoveList.Add(index + (11 * i))
  895.                         Exit For
  896.                     Else
  897.                         endOfMoveList.Add(index + (11 * i))
  898.                     End If
  899.                 Next i
  900.         End Select
  901.  
  902.         ' Convert to endOfMove's in to full moves to return.
  903.         Dim moveList As New List(Of ChessMove)
  904.         For Each endOfMove In endOfMoveList
  905.             Dim move As New ChessMove
  906.             move.startOfMove = index
  907.             move.endOfMove = endOfMove
  908.             moveList.Add(move)
  909.         Next endOfMove
  910.  
  911.         ' Remove any moves that will cause check if required.
  912.         If legalOnly Then
  913.             moveList.RemoveAll(Function(x) willCauseCheck(board, x, humanPlayer) = True)
  914.         End If
  915.  
  916.         Return moveList
  917.  
  918.     End Function
  919.     Private Function generateAllMoves(board As ChessBoard, humanPlayer As Boolean, legalOnly As Boolean)
  920.  
  921.         ' Returns a list of all possible legal moves for a certain player.
  922.         Dim moveList As New List(Of ChessMove)
  923.  
  924.         If humanPlayer Then
  925.             For Each index In board.whiteIndexes
  926.                 For Each possibleMove In generateMoves(board, index, True, legalOnly)
  927.                     moveList.Add(possibleMove)
  928.                 Next possibleMove
  929.             Next index
  930.         ElseIf Not humanPlayer Then
  931.             For Each index In board.blackIndexes
  932.                 For Each possibleMove In generateMoves(board, index, False, legalOnly)
  933.                     moveList.Add(possibleMove)
  934.                 Next possibleMove
  935.             Next index
  936.         End If
  937.  
  938.         Return moveList
  939.  
  940.     End Function
  941.     Private Function willCauseCheck(board As ChessBoard, move As ChessMove, humanPlayer As Boolean)
  942.  
  943.         ' Determines whether a given move will cause the given player to become in check.
  944.  
  945.         Dim copyBoard As ChessBoard = copyOfChessBoard(board)
  946.         movePiece(copyBoard, move)
  947.         If isInCheck(copyBoard, humanPlayer) Then
  948.             Return True
  949.         Else
  950.             Return False
  951.         End If
  952.  
  953.     End Function
  954.     Private Function isInCheck(board As ChessBoard, humanPlayer As Boolean)
  955.  
  956.         ' Checks if a given player is in check on a given board. This is done by tracing outward from the king.
  957.  
  958.         Dim temp, sign, kingIndex As Integer
  959.  
  960.         ' Set the appropriate sign depending on the player.
  961.         If humanPlayer Then
  962.             sign = 1
  963.         Else
  964.             sign = -1
  965.         End If
  966.         ' Find the appropriates player's king on the board.
  967.         If humanPlayer Then
  968.             For Each index In board.whiteIndexes
  969.                 If board.indexes(index) = 6 Then
  970.                     kingIndex = index
  971.                     Exit For
  972.                 End If
  973.             Next
  974.         ElseIf Not humanPlayer Then
  975.             For Each index In board.blackIndexes
  976.                 If board.indexes(index) = -6 Then
  977.                     kingIndex = index
  978.                     Exit For
  979.                 End If
  980.             Next
  981.         End If
  982.  
  983.         ' Check horizontally and vertically for rooks and queens.
  984.         ' Check down.
  985.         For i = 1 To 7
  986.             temp = board.indexes(kingIndex - (10 * i))
  987.             If (temp = 99) Or (temp * sign > 0) Then
  988.                 Exit For
  989.             ElseIf (temp * sign < 0) Then
  990.                 If (temp) * (-sign) = 2 Or (temp) * (-sign) = 5 Then
  991.                     Return True
  992.                 Else
  993.                     Exit For
  994.                 End If
  995.             End If
  996.         Next i
  997.         ' Check up.
  998.         For i = 1 To 7
  999.             temp = board.indexes(kingIndex + (10 * i))
  1000.             If (temp = 99) Or (temp * sign > 0) Then
  1001.                 Exit For
  1002.             ElseIf (temp * sign < 0) Then
  1003.                 If (temp) * (-sign) = 2 Or (temp) * (-sign) = 5 Then
  1004.                     Return True
  1005.                 Else
  1006.                     Exit For
  1007.                 End If
  1008.             End If
  1009.         Next i
  1010.         ' Check left.
  1011.         For i = 1 To 7
  1012.             temp = board.indexes(kingIndex - (i))
  1013.             If (temp = 99) Or (temp * sign > 0) Then
  1014.                 Exit For
  1015.             ElseIf (temp * sign < 0) Then
  1016.                 If (temp) * (-sign) = 2 Or (temp) * (-sign) = 5 Then
  1017.                     Return True
  1018.                 Else
  1019.                     Exit For
  1020.                 End If
  1021.             End If
  1022.         Next i
  1023.         ' Check right.
  1024.         For i = 1 To 7
  1025.             temp = board.indexes(kingIndex + (i))
  1026.             If (temp = 99) Or (temp * sign > 0) Then
  1027.                 Exit For
  1028.             ElseIf (temp * sign < 0) Then
  1029.                 If (temp) * (-sign) = 2 Or (temp) * (-sign) = 5 Then
  1030.                     Return True
  1031.                 Else
  1032.                     Exit For
  1033.                 End If
  1034.             End If
  1035.         Next i
  1036.  
  1037.         ' Check diagonally for bishops and queens.
  1038.         ' Check down/left.
  1039.         For i = 1 To 7
  1040.             temp = board.indexes(kingIndex - (11 * i))
  1041.             If (temp = 99) Or (temp * sign > 0) Then
  1042.                 Exit For
  1043.             ElseIf (temp * sign < 0) Then
  1044.                 If (temp) * (-sign) = 4 Or (temp) * (-sign) = 5 Then
  1045.                     Return True
  1046.                 Else
  1047.                     Exit For
  1048.                 End If
  1049.             End If
  1050.         Next i
  1051.         ' Check down/right
  1052.         For i = 1 To 7
  1053.             temp = board.indexes(kingIndex - (9 * i))
  1054.             If (temp = 99) Or (temp * sign > 0) Then
  1055.                 Exit For
  1056.             ElseIf (temp * sign < 0) Then
  1057.                 If (temp) * (-sign) = 4 Or (temp) * (-sign) = 5 Then
  1058.                     Return True
  1059.                 Else
  1060.                     Exit For
  1061.                 End If
  1062.             End If
  1063.         Next i
  1064.         ' Check up/left.
  1065.         For i = 1 To 7
  1066.             temp = board.indexes(kingIndex + (9 * i))
  1067.             If (temp = 99) Or (temp * sign > 0) Then
  1068.                 Exit For
  1069.             ElseIf (temp * sign < 0) Then
  1070.                 If (temp) * (-sign) = 4 Or (temp) * (-sign) = 5 Then
  1071.                     Return True
  1072.                 Else
  1073.                     Exit For
  1074.                 End If
  1075.             End If
  1076.         Next i
  1077.         ' Check up/right.
  1078.         For i = 1 To 7
  1079.             temp = board.indexes(kingIndex + (11 * i))
  1080.             If (temp = 99) Or (temp * sign > 0) Then
  1081.                 Exit For
  1082.             ElseIf (temp * sign < 0) Then
  1083.                 If (temp) * (-sign) = 4 Or (temp) * (-sign) = 5 Then
  1084.                     Return True
  1085.                 Else
  1086.                     Exit For
  1087.                 End If
  1088.             End If
  1089.         Next i
  1090.  
  1091.         ' Check immediately diagonally for pawns.
  1092.         temp = board.indexes(kingIndex + (sign * 9))
  1093.         If (temp) * (-sign) = 1 Then
  1094.             Return True
  1095.         End If
  1096.         temp = board.indexes(kingIndex + (sign * 11))
  1097.         If (temp) * (-sign) = 1 Then
  1098.             Return True
  1099.         End If
  1100.  
  1101.         ' Check adjacently for a king.
  1102.         temp = board.indexes(kingIndex - 11)
  1103.         If (temp) * (-sign) = 6 Then
  1104.             Return True
  1105.         End If
  1106.         temp = board.indexes(kingIndex - 10)
  1107.         If (temp) * (-sign) = 6 Then
  1108.             Return True
  1109.         End If
  1110.         temp = board.indexes(kingIndex - 9)
  1111.         If (temp) * (-sign) = 6 Then
  1112.             Return True
  1113.         End If
  1114.         temp = board.indexes(kingIndex - 1)
  1115.         If (temp) * (-sign) = 6 Then
  1116.             Return True
  1117.         End If
  1118.         temp = board.indexes(kingIndex + 1)
  1119.         If (temp) * (-sign) = 6 Then
  1120.             Return True
  1121.         End If
  1122.         temp = board.indexes(kingIndex + 9)
  1123.         If (temp) * (-sign) = 6 Then
  1124.             Return True
  1125.         End If
  1126.         temp = board.indexes(kingIndex + 10)
  1127.         If (temp) * (-sign) = 6 Then
  1128.             Return True
  1129.         End If
  1130.         temp = board.indexes(kingIndex + 11)
  1131.         If (temp) * (-sign) = 6 Then
  1132.             Return True
  1133.         End If
  1134.  
  1135.         ' Check knightly for knights.
  1136.         temp = board.indexes(kingIndex - 21)
  1137.         If (temp) * (-sign) = 3 Then
  1138.             Return True
  1139.         End If
  1140.         temp = board.indexes(kingIndex - 19)
  1141.         If (temp) * (-sign) = 3 Then
  1142.             Return True
  1143.         End If
  1144.         temp = board.indexes(kingIndex - 12)
  1145.         If (temp) * (-sign) = 3 Then
  1146.             Return True
  1147.         End If
  1148.         temp = board.indexes(kingIndex - 8)
  1149.         If (temp) * (-sign) = 3 Then
  1150.             Return True
  1151.         End If
  1152.         temp = board.indexes(kingIndex + 8)
  1153.         If (temp) * (-sign) = 3 Then
  1154.             Return True
  1155.         End If
  1156.         temp = board.indexes(kingIndex + 12)
  1157.         If (temp) * (-sign) = 3 Then
  1158.             Return True
  1159.         End If
  1160.         temp = board.indexes(kingIndex + 19)
  1161.         If (temp) * (-sign) = 3 Then
  1162.             Return True
  1163.         End If
  1164.         temp = board.indexes(kingIndex + 21)
  1165.         If (temp) * (-sign) = 3 Then
  1166.             Return True
  1167.         End If
  1168.  
  1169.         ' Return false if a check hasn't already been recognised.
  1170.         Return False
  1171.  
  1172.     End Function
  1173.     Private Function hasNoMoves(board As ChessBoard, humanPlayer As Boolean)
  1174.  
  1175.         ' Determines whether a given player has no moves available on a given board.
  1176.  
  1177.         Dim moveList As List(Of ChessMove) = generateAllMoves(board, humanPlayer, True)
  1178.         If moveList.Count = 0 Then
  1179.             Return True
  1180.         Else
  1181.             Return False
  1182.         End If
  1183.  
  1184.     End Function
  1185.  
  1186.     ' Functions dealing with calculating the computer's move.
  1187.     Private Sub calculateComputerMove()
  1188.  
  1189.         ' Calculates the move the computer will make.
  1190.  
  1191.         gameBoardCopy = copyOfChessBoard(gameBoard)
  1192.         Dim bestMoves As New List(Of ChessMove)
  1193.         Dim checkmateMoves As New List(Of ChessMove)
  1194.         Dim bestValue As Integer = infinity
  1195.  
  1196.         ' Go through every possible move to work out which one yields the minimum (best for the computer) value.
  1197.         For Each possibleMove In generateAllMoves(gameBoardCopy, False, True)
  1198.  
  1199.             ' Test whether the move is a pawn promotion, so that it can be undone if it is.
  1200.             Dim pawnPromotion As Boolean = False
  1201.             If moveIsPawnPromotion(gameBoardCopy, possibleMove) Then pawnPromotion = True
  1202.             Dim tempEndPiece As Integer = gameBoardCopy.indexes(possibleMove.endOfMove)
  1203.             movePiece(gameBoardCopy, possibleMove)
  1204.  
  1205.             ' Do the alpha-beta stuff.
  1206.             Dim value As Integer = alphaBeta(1, -infinity, bestValue + 1, True)
  1207.             If value < bestValue Then
  1208.                 bestMoves.Clear()
  1209.                 bestMoves.Add(possibleMove)
  1210.                 bestValue = value
  1211.             ElseIf value = bestValue Then
  1212.                 bestMoves.Add(possibleMove)
  1213.             End If
  1214.             If value < -highestLegalValue Then checkmateMoves.Add(possibleMove)
  1215.  
  1216.             ' Undo the move.
  1217.             undoMove(gameBoardCopy, possibleMove, tempEndPiece, pawnPromotion)
  1218.  
  1219.             Application.DoEvents()
  1220.             ' Stop calculating if the move has been undone, and it is now the player's move.
  1221.             If playersTurnNow Then Exit For
  1222.  
  1223.         Next
  1224.  
  1225.         ' Make the move.
  1226.         If Not playersTurnNow Then
  1227.             If checkmateMoves.Count > 0 Then
  1228.                 makeFinalMove(bestFinalMove(checkmateMoves))
  1229.             Else
  1230.                 makeFinalMove(bestFinalMove(bestMoves))
  1231.             End If
  1232.         End If
  1233.  
  1234.     End Sub
  1235.     Private Function bestFinalMove(moves As List(Of ChessMove))
  1236.  
  1237.         ' Determines the best move out of a list of moves by the criteria that a capture is better than just moving forward which is better than not moving forward. This
  1238.         ' will give the AI a bit more direction at the start of the game.
  1239.  
  1240.         Dim bestMoves As New List(Of ChessMove)
  1241.  
  1242.         ' Check if any moves lead to pawn promotion.
  1243.         For Each m In moves
  1244.             If moveIsPawnPromotion(gameBoard, m) Then
  1245.                 bestMoves.Add(m)
  1246.             End If
  1247.         Next
  1248.  
  1249.         If bestMoves.Count <> 0 Then
  1250.             Return bestMoves(Math.Round(Rnd() * (bestMoves.Count - 1)))
  1251.         End If
  1252.  
  1253.         ' Check if any moves lead to capture.
  1254.         For Each m In moves
  1255.             If gameBoard.indexes(m.endOfMove) <> 0 Then
  1256.                 bestMoves.Add(m)
  1257.             End If
  1258.         Next m
  1259.  
  1260.         If bestMoves.Count <> 0 Then
  1261.             Return bestMoves(Math.Round(Rnd() * (bestMoves.Count - 1)))
  1262.         End If
  1263.  
  1264.         ' Check if any moves go forward.
  1265.         For Each m In moves
  1266.             If Math.Floor(m.endOfMove / 10) < Math.Floor(m.startOfMove / 10) Then
  1267.                 bestMoves.Add(m)
  1268.             End If
  1269.         Next
  1270.  
  1271.         If bestMoves.Count <> 0 Then
  1272.             Return bestMoves(Math.Round(Rnd() * (bestMoves.Count - 1)))
  1273.         Else
  1274.             Return moves(Math.Round(Rnd() * (moves.Count - 1)))
  1275.         End If
  1276.  
  1277.     End Function
  1278.     Private Function alphaBeta(depth As Integer, alpha As Integer, beta As Integer, playersTurn As Boolean)
  1279.  
  1280.         ' Recursively executes a minimax evaluation of the game tree with alpha-beta pruning.
  1281.  
  1282.         Dim value As Integer
  1283.  
  1284.         ' If the ply has been reached, return the evaluation value.
  1285.         If (depth = ply) Then Return evaluateBoard(gameBoardCopy)
  1286.  
  1287.         ' Work out the possible moves.
  1288.         Dim possibleMoves As New List(Of ChessMove)
  1289.         If depth <= 1 Then
  1290.             possibleMoves = generateAllMoves(gameBoardCopy, playersTurn, True)
  1291.             ' If stalemate or checkmate.
  1292.             If possibleMoves.Count = 0 Then
  1293.                 If playersTurn Then
  1294.                     If isInCheck(gameBoardCopy, True) Then : Return -infinity
  1295.                     Else : Return 0
  1296.                     End If
  1297.                 Else
  1298.                     If isInCheck(gameBoardCopy, False) Then : Return infinity
  1299.                     Else : Return 0
  1300.                     End If
  1301.                 End If
  1302.             End If
  1303.         Else
  1304.             possibleMoves = generateAllMoves(gameBoardCopy, playersTurn, False)
  1305.         End If
  1306.  
  1307.         ' Alpha-beta evaluate stuff.
  1308.         If playersTurn Then
  1309.             For Each possibleMove In possibleMoves
  1310.                 ' Make move on board.
  1311.                 Dim pawnPromotion As Boolean = False
  1312.                 If moveIsPawnPromotion(gameBoardCopy, possibleMove) Then pawnPromotion = True
  1313.                 Dim tempEndPiece As Integer = gameBoardCopy.indexes(possibleMove.endOfMove)
  1314.                 movePiece(gameBoardCopy, possibleMove)
  1315.                 ' Evaluate deeper.
  1316.                 value = alphaBeta(depth + 1, alpha, beta, Not playersTurn)
  1317.                 If value > alpha Then alpha = value ' We have found a better best move.
  1318.                 If alpha > beta Then
  1319.                     undoMove(gameBoardCopy, possibleMove, tempEndPiece, pawnPromotion)
  1320.                     Return alpha ' Cut off.
  1321.                 End If
  1322.                 ' Undo move on board.
  1323.                 undoMove(gameBoardCopy, possibleMove, tempEndPiece, pawnPromotion)
  1324.             Next possibleMove
  1325.             Return alpha ' This is our best move.
  1326.         Else
  1327.             For Each possibleMove In possibleMoves
  1328.                 ' Make move on board.
  1329.                 Dim pawnPromotion As Boolean = False
  1330.                 If moveIsPawnPromotion(gameBoardCopy, possibleMove) Then pawnPromotion = True
  1331.                 Dim tempEndPiece As Integer = gameBoardCopy.indexes(possibleMove.endOfMove)
  1332.                 movePiece(gameBoardCopy, possibleMove)
  1333.                 ' Evaluate deeper.
  1334.                 value = alphaBeta(depth + 1, alpha, beta, Not playersTurn)
  1335.                 If value < beta Then beta = value ' Opponent has found a better worse move.
  1336.                 If alpha > beta Then
  1337.                     undoMove(gameBoardCopy, possibleMove, tempEndPiece, pawnPromotion)
  1338.                     Return beta ' Cut off.
  1339.                 End If
  1340.                 ' Undo move on board.
  1341.                 undoMove(gameBoardCopy, possibleMove, tempEndPiece, pawnPromotion)
  1342.             Next possibleMove
  1343.             Return beta ' This is the opponent's best move.
  1344.         End If
  1345.  
  1346.     End Function
  1347.     Private Function evaluateBoard(board As ChessBoard)
  1348.  
  1349.         ' Performs an evaluation of the current board, a negative score being good for black, and a positive score being good for white.
  1350.         ' At present, this function only counts the material of either side to determine a value.
  1351.  
  1352.         Return board.materialValue
  1353.  
  1354.     End Function
  1355.  
  1356.     ' Utilities.
  1357.     Private Function copyOfChessBoard(board As ChessBoard)
  1358.  
  1359.         ' Return a copy of the given board.
  1360.  
  1361.         Dim copyBoard As New ChessBoard
  1362.  
  1363.         For i = 0 To 20 : copyBoard.indexes(i) = 99 : Next
  1364.         For i = 21 To 98 : copyBoard.indexes(i) = board.indexes(i) : Next
  1365.         For i = 99 To 119 : copyBoard.indexes(i) = 99 : Next
  1366.  
  1367.         copyBoard.whiteIndexes.AddRange(board.whiteIndexes)
  1368.         copyBoard.blackIndexes.AddRange(board.blackIndexes)
  1369.         copyBoard.materialValue = board.materialValue
  1370.  
  1371.         Return copyBoard
  1372.  
  1373.     End Function
  1374.     Private Function translateTo1D(x As Integer, y As Integer)
  1375.  
  1376.         ' Translate the 2D coordinates to the 1D coordinate.
  1377.  
  1378.         Return x - 10 * y + 91
  1379.  
  1380.     End Function
  1381.  
  1382.     ' Functions that are useful for testing/debugging.
  1383.     Private Function boardToText(board As ChessBoard)
  1384.  
  1385.         ' Translates a ChessBoard in to a string.
  1386.  
  1387.         Dim msg As String = ""
  1388.         For y = 0 To 7
  1389.             For x = 0 To 7
  1390.                 Dim currentPiece As Integer = board.indexes(translateTo1D(x, y))
  1391.                 If currentPiece >= 0 Then
  1392.                     msg += "   " & currentPiece & "   "
  1393.                 Else
  1394.                     msg += "  " & currentPiece & "  "
  1395.                 End If
  1396.             Next
  1397.             msg += vbNewLine
  1398.         Next
  1399.         msg.Remove(msg.Count - 1)
  1400.  
  1401.         Return msg
  1402.  
  1403.     End Function
  1404.     Private Sub setCustomBoard()
  1405.  
  1406.         ' Set up a custom board for testing purposes.
  1407.  
  1408.         removeAllPieces(gameBoard)
  1409.  
  1410.         ' Whites:
  1411.         placeNewPiece(gameBoard, 6, 31)
  1412.         placeNewPiece(gameBoard, 1, 54)
  1413.         ' Blacks:
  1414.         placeNewPiece(gameBoard, -6, 98)
  1415.         placeNewPiece(gameBoard, -1, 34)
  1416.         placeNewPiece(gameBoard, -2, 65)
  1417.  
  1418.  
  1419.     End Sub
  1420.  
  1421. End Class
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement