-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathChess_Project.cpp
More file actions
339 lines (285 loc) · 11.4 KB
/
Chess_Project.cpp
File metadata and controls
339 lines (285 loc) · 11.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
/// Chess_Project.cpp
///
/// Will Lei
/// This is where int main lives, along with the chess engine
#include <iostream>
#include <cstdlib>
#include <cmath>
#include "legal_moves.h"
#define ISWHITEMOVE (moveNum%2 == 1)
#define ISBLACKMOVE (moveNum%2 == 0)
#define ISFORWHITE (isCompMove == compIsWhite)
#define ISFORBLACK (isCompMove != compIsWhite)
using namespace std;
// Declare functions
ply findBestMove (bitboard bBoard, int depth, bool compIsWhite);
int alphabeta (bitboard bBoard, int depth, int alpha, int beta, bool isCompMove, bool compIsWhite);
int calcBoardVal (bitboard bBoard, bool forWhite);
int calcLocVal (int square);
string enterUserMove (bitboard bBoard, svec sBoard, int moveNum);
int main()
{
// Allow user to play chess
//twoPlayerGame();
// Initialize the arrays, the bitboard, the vector, variables etc.
getDirections();
bitboard bBoard;
svec sBoard;
plyvec legalMoves;
string input;
int moveNum = 1, curr = 0, dest = 0;
bool compIsWhite = true;
// Initialise boards
initBoard(sBoard);
svecToBitboard(bBoard, sBoard);
// Loop continuously getting moves from the AI and the user
while (true)
{
// Let the AI generate the move
if (compIsWhite && ISWHITEMOVE)
{
// Calculate move
ply compMove = findBestMove(bBoard, 3, compIsWhite);
curr = compMove.curr;
dest = compMove.dest;
// Output what move the AI chose
//cout << curr << " " << dest << endl;
squareToMove(curr, dest);
}
// Allow user to enter move
else
{
input = enterUserMove(bBoard, sBoard, moveNum);
if (input == "0")
break;
stringToSquare(input, curr, dest);
}
// Update the bitboard, the string vector, and the number of moves
bBoard = updateBitboard(bBoard, curr, dest, false);
bitBoardToSVec(bBoard, sBoard);
moveNum++;
// Check for checkmate or stalemate
if (!areLegalMoves(bBoard, ISWHITEMOVE))
{
displayBoard(sBoard);
if ((ISWHITEMOVE && isInCheck(bBoard, getWKingLoc(bBoard))))
cout << "Checkmate! Black wins!" << endl;
else if ((ISBLACKMOVE && isInCheck(bBoard, getBKingLoc(bBoard))))
cout << "Checkmate! White wins!" << endl;
else
cout << "Draw from stalemate." << endl;
break;
}
}
// For exe files where the window automatically closes after mate
string s;
cout << "Enter anything to close: ";
cin >> s;
// Finish program
return 0;
}
// Function to call alpha-beta to find the best move for the computer
ply findBestMove (bitboard bBoard, int depth, bool compIsWhite)
{
// Get all the moves available for the computer
plyvec legalMoves = getLegalMoves(bBoard, compIsWhite);
int bestVal, bestMove = 0;
// Go through all the legal moves of the computer
for (unsigned int i = 0; i < legalMoves.size(); i++)
{
// Update the bitboard after a move
bitboard bb2 = updateBitboard(bBoard, legalMoves[i].curr, legalMoves[i].dest, true);
// Check for checkmate/stalemate
if (compIsWhite && !areLegalMoves(bb2, false) && isInCheck(bb2, getBKingLoc(bb2)))
return legalMoves[i];
if (!compIsWhite && !areLegalMoves(bb2, true) && isInCheck(bb2, getWKingLoc(bb2)))
return legalMoves[i];
// Call the alpha-beta algorithm to evaluate the position at hand
int alpha = -2000000000, beta = 2000000000;
int boardVal = alphabeta(bb2, depth-1, alpha, beta, false, compIsWhite);
// Update the best move and the best value
if (i == 0 || boardVal > bestVal)
{
bestVal = boardVal;
bestMove = i;
}
}
//cout << "Computer value is: " << bestVal << endl;
return legalMoves[bestMove];
}
// Function that uses the recursive alpha-beta algorithm to return the value of an updated bitboard
int alphabeta (bitboard bBoard, int depth, int alpha, int beta, bool isCompMove, bool compIsWhite)
{
// Get all the legal moves for whoever is supposed to move
plyvec legalMoves = getLegalMoves(bBoard, ISFORWHITE);
// Stop search if there are no more legal moves or if the search has reached the maximum depth
if (legalMoves.size() == 0 || depth == 0)
{
return calcBoardVal(bBoard, compIsWhite);
}
// Maximize the value if it is the computer's turn to move
if (isCompMove)
{
int bestVal = 0;
// Go through all the legal moves, searching for the move that is worst for the computer
for (unsigned int i = 0; i < legalMoves.size(); i++)
{
// Update bitboard and recursively call the alpha-beta algorithm
bitboard bBoard2 = updateBitboard(bBoard, legalMoves[i].curr, legalMoves[i].dest, true);
int boardVal = alphabeta(bBoard2, depth-1, alpha, beta, !isCompMove, compIsWhite);
// Update the best board value and alpha, the best position the computer is guaranteed of
if (i == 0 || boardVal > bestVal)
bestVal = boardVal;
if (bestVal > alpha)
alpha = bestVal;
// Stop if the move is worse than all the previous moves
if (beta <= alpha)
break;
}
return bestVal;
}
// Minimize the board's value if it is the opponent's turn to move
else
{
int bestVal = 0;
// Go through all the legal moves, searching for the move that is worst for the computer
for (unsigned int i = 0; i < legalMoves.size(); i++)
{
// Update bitboard and recursively call the alpha-beta algorithm
bitboard bBoard2 = updateBitboard(bBoard, legalMoves[i].curr, legalMoves[i].dest, true);
int boardVal = alphabeta(bBoard2, depth-1, alpha, beta, !isCompMove, compIsWhite);
// Update the best board value and beta, the best position the user is guaranteed of
if (i == 0 || boardVal < bestVal)
bestVal = boardVal;
if (bestVal < beta)
beta = bestVal;
// Stop if the move is worse than all the previous moves
if (beta <= alpha)
break;
}
return bestVal;
}
}
// Function to return the value of a board for a side
int calcBoardVal (bitboard bBoard, bool forWhite)
{
int wPositionVal = 0, bPositionVal = 0;
// Return a million points if checkmate is achieved
if (forWhite && !areLegalMoves(bBoard, true) && isInCheck(bBoard, getWKingLoc(bBoard)))
return -1000000;
if (forWhite && !areLegalMoves(bBoard, false) && isInCheck(bBoard, getBKingLoc(bBoard)))
return 1000000;
if (!forWhite && !areLegalMoves(bBoard, false) && isInCheck(bBoard, getBKingLoc(bBoard)))
return -1000000;
if (!forWhite && !areLegalMoves(bBoard, true) && isInCheck(bBoard, getWKingLoc(bBoard)))
return 1000000;
// During the opening, give points for a minor pieces and pawns closer to the centre of the board
if (bBoard.wMaterialVal + bBoard.bMaterialVal < 6000)
{
U64 wGoodPieces = bBoard.wPawns | bBoard.wKnights | bBoard.wBishops;
U64 bGoodPieces = bBoard.bPawns | bBoard.bKnights | bBoard.bBishops;
U64 wBadPieces = bBoard.wRooks | bBoard.wQueens;
U64 bBadPieces = bBoard.bRooks | bBoard.bQueens;
// Go through all the squares determining where all the pieces are
for (int i = 0; i < 64; i++)
{
if (wGoodPieces & sqrVal[i])
wPositionVal += 2*calcLocVal(i);
if (bGoodPieces & sqrVal[i])
bPositionVal += 2*calcLocVal(i);
if (wBadPieces & sqrVal[i])
wPositionVal -= 5*calcLocVal(i);
if (bBadPieces & sqrVal[i])
bPositionVal -= 5*calcLocVal(i);
}
}
// Otherwise just give points for any piece closer to the centre of the board
else
{
// Go through all the squares determining where all the pieces are
for (int i = 0; i < 64; i++)
{
if (bBoard.wPieces & sqrVal[i])
wPositionVal += calcLocVal(i);
if (bBoard.bPieces & sqrVal[i])
bPositionVal += calcLocVal(i);
}
}
// Return the board value and add material value
if (forWhite)
return (bBoard.wMaterialVal-bBoard.bMaterialVal) + (wPositionVal-bPositionVal);
else
return (bBoard.bMaterialVal-bBoard.wMaterialVal) + (bPositionVal-wPositionVal);
}
// Calculate the value of a piece located on a particular square (centre is better)
int calcLocVal (int square)
{
// The values are as follows:
//
// -10 -10 -10 -10 -10 -10 -10 -10
// -10 0 0 0 0 0 0 -10
// -10 0 10 10 10 10 0 -10
// -10 0 10 20 20 10 0 -10
// -10 0 10 20 20 10 0 -10
// -10 0 10 10 10 10 0 -10
// -10 0 0 0 0 0 0 -10
// -10 -10 -10 -10 -10 -10 -10 -10
//
// Determine which concentric box the square is a part of
if (square/8 == 0 || square/8 == 7 || square%8 == 0 || square%8 == 7)
return -10;
else if (square/8 == 1 || square/8 == 6 || square%8 == 1 || square%8 == 6)
return 0;
else if (square/8 == 2 || square/8 == 5 || square%8 == 2 || square%8 == 5)
return 10;
else
return 20;
}
// Function to allow the user to enter in a move
string enterUserMove (bitboard bBoard, svec sBoard, int moveNum)
{
string input;
int curr = 0, dest = 0;
// Loop until the user enters in a valid move
while (true)
{
// Display the board
displayBoard(sBoard);
// Get the next move from the user
cout << "Enter in the desired move: ";
cin >> input;
// Clear screen
system("CLS");
// Allow user to exit game
if (input == "0")
return input;
// Make sure the input is valid
if (!isValidInput(input))
{
cout << "Invalid input. Please try again." << endl;
continue;
}
// Store the current square and the destination square
stringToSquare(input, curr, dest);
// Make sure the user is moving the piece of the right colour
if (!isRightColour(bBoard, curr, moveNum))
{
cout << "Wrong colour." << endl;
continue;
}
// Check to see if the move is a regular legal move for white
if (isLegalMove(bBoard, curr, dest))
return input;
// What to do if an illegal move is made
else
{
cout << "Illegal move." << endl;
bitboard bb2 = updateBitboard(bBoard, curr, dest, true);;
// Check if it is because of check
if (isInCheck(bb2, 63-log2(bb2.wKings)))
cout << "White king is in check" << endl;
if (isInCheck(bb2, 63-log2(bb2.bKings)))
cout << "Black king is in check" << endl;
}
}
}