-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathProject2.cpp
3802 lines (3354 loc) · 129 KB
/
Project2.cpp
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
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#include <iostream>
#include <iomanip>
#include <string>
#include <vector>
#include <limits>
#include <stdexcept>
// used by strcmp method
#include <string.h>
#ifndef PIECES_H
#define PIECES_H
/**
* The Color enumerator is used in place of values 1 and -1
* in order to make the code easier to read. Note the values
* of 1 and -1. This allows for alternating play by multiplying the current
* by 1.
*/
enum class Color
{
RED = 1,
BLACK = -1
};
/**
* The Pieces class represents all pieces for a particular player. Each object
* of the class contains a single instance of pieces. In addition to this,
* ANSII code strings were included here in order to make the output
* of the code easier to read. These codes serve no functional purpose
* aside from changing text color.
*/
class Pieces
{
public:
Pieces();
Pieces(Color color);
// ANSII codes for colored text, to improve UI and readability
static std::string ANSII_BLUE_START;
static std::string ANSII_RED_START;
static std::string ANSII_RED_HIGH;
static std::string ANSII_END;
static std::string ANSII_GREEN_START;
static std::string ANSII_BLUE_COUT;
static std::string ANSII_RED_COUT;
static std::string ANSII_GREEN_COUT;
static std::string ANSII_YELLOW_COUT;
// Debug reporting level 3 == display all debug/status lines 2== important, 1 == basic, 0 == none
static int ouputDebugData;
bool isKing(int position) const;
void setKing(int poisition, bool toKing);
// This contains 64 bits to represent the entire piece set and king status for one side.
long long pieces;
};
#endif // !PIECES_H
#ifndef BOARD_H
#define BOARD_H
/**
* Class board is used to represent the entire board and its current state.
* It is based on an 8 x 8 grid, with 32 possible spaces. Two piece data
* types are the primary memory consumers of this class. This class also includes
* a static member that acts as a move guide. This move guide determines the possible
* moves for a square on the board, not for a piece.
*/
class Board
{
public:
/**
* Struct Nove is used to track moves made on the board. It contains
* a single starting location, a vector of steps to a move,
* as well as all of the pieces to remove when this move is made.
*/
struct Move
{
int startSquare;
std::vector<int> destinationSquare;
std::vector<int> removalSquare;
};
/**
* Struct BoardMoveTable is a static data type that is used to speed
* up searching for moves for each position. It is shared among all
* boards.
*/
struct BoardMoveTable
{
std::vector<int> jumps;
std::vector<int> removals;
std::vector<int> moves;
};
Board();
~Board();
static void InitializeMoveTable();
std::vector<Move> moveGen(Color color);
void printBoard() const;
Board updateBoard(Move move, Color color);
int getNumRegularPieces(Color color);
int getNumKingPieces(Color color);
int getNumPlayerTotalPieces(Color color);
int getPieceInSquare(int square, Color color);
std::vector<Move> getJumpsForPiece(Color color, int square, Pieces *playerPieces, Pieces *opponentPieces);
std::vector<Move> getMovesForPiece(Color color, int square, Pieces *playerPieces, Pieces *opponentPieces);
Pieces getPlayerPieces(Color color);
Pieces getOpponentPieces(Color color);
static BoardMoveTable boardMoveTable[33];
private:
Pieces blackPieces;
Pieces redPieces;
void getJumpsForPieceRec(Color color, Board::Move move, std::vector<Board::Move> &totalMoves, Board board, bool wasKingPriorMove);
};
#endif
#ifndef PLAYER_H
#define PLAYER_H
/**
* Header definition for class Player.
*
* The Player class defines a virtual player, in the case of this program an AI.
* No human players are used in this game - meaning no manual input is sought from the user.
* Project 2, Requirement #7: "Your program should play with a computer to a computer."
*
*/
class Player
{
private:
Color color; // represents player's color in the game, either RED or BLACK
int numPieces; // how many pieces does Player have left
int numPiecesTaken; // Player's current score based on captured enemy pieces
int numTurnsTaken; // counter for Player's turns taken
bool didPlayerMove; // EndGame Condition
int depth, evalVersion;
public:
Player(); // constructor
~Player(); // destructor
bool isMinimax; // if false use AB Prune, if true use Minimax. Allows control over alg player uses
Player(bool minMax, Color color, int depth, int evalVersion); // overloaded constructor to set player color, which is IMMUTABLE
int takeTurn(Board &state);
int getNumPieces();
int getNumPiecesTaken();
int getNumTurns();
bool getDidPlayerMove();
Color getColor();
void decreaseNumPieces(int numPiecesToDecreaseCount);
void increaseNumPiecesTaken(int numPiecesToIncreaseScore);
static void printMove(Board::Move, Color color, bool willPrintAlways);
int minimaxExpandedNodes; // how many nodes we expand
int minimaxLeafNodes; // how many nodes we expand
int absearchExpandedNodes; // how many nodes we expand
int absearchLeafNodes; // how many nodes we expand
int getMinimaxTotalNodes()
{
return minimaxExpandedNodes + minimaxLeafNodes;
}
int getAbSearchTotalNodes()
{
return absearchExpandedNodes + absearchLeafNodes;
}
};
#endif // !PLAYER_H
#ifndef GAME_H
#define GAME_H
/**
* Header definition for class Game.
*
* The Game class represents a checkers Game.
*
* A game consists of a board, two players (red and black), and
* 12 checkers pieces for each player.
*
* A game ends when either player loses all their pieces or is blocked and
* has no further moves available.
*
* The startGame() function will trigger the process and no input from the user is required.
* Red and Black players will be created. Each player will be given 12 checker pieces.
* The pieces will be appropriately placed on the board.
* Each player will execute their strategy.
*
*/
class Game
{
private:
Board state;
Player redPlayer;
Player blackPlayer;
const int MAX_ALLOWED_TURNS = 80;
void printNodes(Player player, std::string colorText);
public:
Game(); // constructor
~Game(); // destructor
Game(bool, int, bool, int, int); // player1 algo, eval version, player2 algo, eval version, depth
enum class GameOver
{
BLACK_WINS = 0,
RED_WINS = 1,
DRAW = 2,
NOT_DONE = 3
};
// The only initialization function needed, as the game will
// be played automatically by 2 AI players (MIN and MAX).
// while gameOver == NOT_DONE keep playing
GameOver startGame();
// simple function to invert the enum value, thus determine who's turn is it next.
// E.g., if currentPlayer is RED (1), function returns BLACK (-1)
Color changePlayer(Color currentPlayer);
GameOver gameOver(); // Have end game conditions been met?
bool doesRedWin();
bool doesBlackWin();
bool isItADraw();
};
#endif // !GAME_H
#ifndef ALGORITHM_H
#define ALGORITHM_H
/**
* Header definition for class Algorithm.
* @author multiple - David, Boris, and Randy
*
* This class will encapsulate the algorithmic approach the AI uses to play Checkers.
*
* There are only two major algorithms supported.
* 1) Minimax - a depth first, depth limited search procedure. From the Richard and Knight book.
* The minimax function has a heuristic value for leaf nodes (end nodes and nodes at the maximum permitted depth).
* Intermediate nodes get their value from a child/successor leaf node.
* 2) Alpha-Beta Pruning - a search algorithm that decreases the number of nodes evaluated by minimax in it's search tree.
* We stop evaluating a possible move when at least one option is found to be worse than a previously examined move.
* NOTE: It should return the SAME result as minimax, just "prunes" branches that will not affect the final outcome,
* thus improving performance.
*
* Three evaluation functions will be used in conjunction with the two search algorithms.
*
*/
class Algorithm
{
public:
Algorithm(); // constructor
~Algorithm(); // destructor
Algorithm(int evalVersion, int maxDepth, Player callingPlayer);
struct Result
{
int value;
Board::Move bestMove;
};
int minimaxExpandedNodes; // how many nodes we expand
int minimaxLeafNodes; // how many nodes we expand
int absearchExpandedNodes; // how many nodes we expand
int absearchLeafNodes; // how many nodes we expand
// minimax algorithm returns the position of the best move
Result minimax_a_b(Board board, int depth, Color color, int useThresh, int passThresh);
// AB Prune algorithm
Result alphaBetaSearch(Board state);
void setEvalVersion(int evalVersion);
void setMaxDepth(int maxDepth);
private:
int numNodesGenerated;
int evalVersion;
int currentDepth, maxDepth;
Player callingPlayer;
// plausible move generator, returns a list of positions that can be made by player
std::vector<Board::Move> movegen(Board board, Color color);
/* static evaluation functions return a number representing the
* goodness of Position from the standpoint of Player
* A helper function staticEval is used to determine which evalFunction to use
*/
int evalFunctOne(Board state, Color color);
int evalFunctTwo(Board state, Color color);
int evalFunctThree(Board state, Color color);
// wrapper function that will decide which of the actual three eval functions to call
int staticEval(Board state, Color color, int evalVersion);
// if true, return the structure
bool deepEnough(int currentDepth);
bool terminalTest(Board state, int depth); // terminal test for alpha-beta-search
Result maxValue(Board state, int depth, int alpha, int beta, Color color);
Result minValue(Board state, int depth, int alpha, int beta, Color color);
int utility(Board state);
std::vector<Board::Move> actions(Board state, Color color);
Color switchPlayerColor(Color color);
int passSign(int passthresh);
};
#endif // !ALGORITHM_H
#ifndef SIMULATION_H
#define SIMULATION_H
/**
* Header definition for class Simulation.
* @author Borislav Sabotinov
*
* This class is responsible for managing the series of Games that AI players will play for Project Two.
* It persists during the execution of the program and keeps track of the number of games played.
* It also allows to aggregate and print simulation analysis details.
*
* There are THREE (3) evaluation functions, one for each team member; two algorithms (minimax and Alpha-Beta).
* The simulation will execute each in turn.
*
* Fifteen runs with depth 2:
* 1. MinMax-A-B with Evl. Function #1 Verses Alpha-Beta with Evl. Function #1
* 2. MinMax-A-B with Evl. Function #1 Verses Alpha-Beta with Evl. Function #2
* 3. MinMax-A-B with Evl. Function #1 Verses Alpha-Beta with Evl. Function #3
*
* 4. MinMax-A-B with Evl. Function #2 Verses Alpha-Beta with Evl. Function #1
* 5. MinMax-A-B with Evl. Function #2 Verses Alpha-Beta with Evl. Function #2
* 6. MinMax-A-B with Evl. Function #2 Verses Alpha-Beta with Evl. Function #3
*
* 7. MinMax-A-B with Evl. Function #3 Verses Alpha-Beta with Evl. Function #1
* 8. MinMax-A-B with Evl. Function #3 Verses Alpha-Beta with Evl. Function #2
* 9. MinMax-A-B with Evl. Function #3 Verses Alpha-Beta with Evl. Function #3
*
* 10. MinMax-A-B with Evl. Function #1 Verses MinMax-A-B with Evl. Function #2
* 11. MinMax-A-B with Evl. Function #1 Verses MinMax-A-B with Evl. Function #3
* 12. MinMax-A-B with Evl. Function #2 Verses MinMax-A-B with Evl. Function #3
*
* 13. Alpha-Beta with Evl. Function #1 Verses Alpha-Beta with Evl. Function #2
* 14. Alpha-Beta with Evl. Function #1 Verses Alpha-Beta with Evl. Function #3
* 15. Alpha-Beta with Evl. Function #2 Verses Alpha-Beta with Evl. Function #3
*
* Fifteen runs with depth 4:
* 1. MinMax-A-B with Evl. Function #1 Verses Alpha-Beta with Evl. Function #1
* 2. MinMax-A-B with Evl. Function #1 Verses Alpha-Beta with Evl. Function #2
* 3. MinMax-A-B with Evl. Function #1 Verses Alpha-Beta with Evl. Function #3
*
* 4. MinMax-A-B with Evl. Function #2 Verses Alpha-Beta with Evl. Function #1
* 5. MinMax-A-B with Evl. Function #2 Verses Alpha-Beta with Evl. Function #2
* 6. MinMax-A-B with Evl. Function #2 Verses Alpha-Beta with Evl. Function #3
*
* 7. MinMax-A-B with Evl. Function #3 Verses Alpha-Beta with Evl. Function #1
* 8. MinMax-A-B with Evl. Function #3 Verses Alpha-Beta with Evl. Function #2
* 9. MinMax-A-B with Evl. Function #3 Verses Alpha-Beta with Evl. Function #3
*
* 10. MinMax-A-B with Evl. Function #1 Verses MinMax-A-B with Evl. Function #2
* 11. MinMax-A-B with Evl. Function #1 Verses MinMax-A-B with Evl. Function #3
* 12. MinMax-A-B with Evl. Function #2 Verses MinMax-A-B with Evl. Function #3
*
* 13. Alpha-Beta with Evl. Function #1 Verses Alpha-Beta with Evl. Function #2
* 14. Alpha-Beta with Evl. Function #1 Verses Alpha-Beta with Evl. Function #3
* 15. Alpha-Beta with Evl. Function #2 Verses Alpha-Beta with Evl. Function #3
*
* TOTAL 30 RUNS/GAMES WILL BE SIMULATED.
*/
class Simulation
{
private:
int numGamesPlayed;
// runs only games using Minimax algorithm
void runMinimaxOnly();
// runs only games using AB Prune algorithm
void runABPruneOnly();
public:
Simulation(); // constructor
~Simulation(); // destructor
// runs all games runs as delineated above
void runFullSimulation();
// public method for specific simulations
void runSpecificSimulation(int playerOneAlg, int playerOneEvalFunct, int playerTwoAlg, int PlayerTwoEvalFunct, int depth);
// public method for player vs AI simulation
void runPlayerVsAISimulation(int playerAlg, int playerEvalFunct, int depth);
// helper function to determine winner and break out of game loop
static bool didSomeoneWin(Board board);
// returns a count of the number of games played in a simulation
// each of the 3 run functions.
int getNumGamesPlayed();
// creates a table with results for analysis.
// how many nodes were created, etc.
void generateAnalysisResults();
void printGameConfig(int redPlayerAlg, int redPlayerEvalFunct, int blackPlayerAlg, int blackPlayerEvalFunct, int depth);
void printGameResults(Game::GameOver endGameStatus);
// helper print methods
static void printBlackWins();
static void printRedWins();
static void printDraw();
};
#endif // !SIMULATION_H
// CLASS PIECES
// ANSII codes for colored text, to improve UI and readability
std::string Pieces::ANSII_BLUE_START = "\033[0;30;46m";
std::string Pieces::ANSII_RED_START = "\033[0;31m";
std::string Pieces::ANSII_RED_HIGH = "\033[9;37;41m";
std::string Pieces::ANSII_END = "\033[0m";
std::string Pieces::ANSII_GREEN_START = "\033[0;32m";
std::string Pieces::ANSII_BLUE_COUT = "\033[0;30;46m";
std::string Pieces::ANSII_RED_COUT = "\033[41;1m";
std::string Pieces::ANSII_GREEN_COUT = "\033[0;30;42m";
std::string Pieces::ANSII_YELLOW_COUT = "\033[30;48;5;3m";
int Pieces::ouputDebugData = 3;
/**
* Constructor | Pieces | Pieces
*
* Summary : Unused. Constructor with
* a parameter is always required.
*
* @author : David Torrente
*
*/
Pieces::Pieces()
{
}
/**
* Constructor | Pieces | Pieces
*
* Summary : Sets up the player pieces depending on
* the color passed in.
*
* @author : David Torrente
*
* @param Color color : The color to assign the pieces to.
*
*/
Pieces::Pieces(Color color)
{
// The following are bit fields to be used for the board. Each value
// turns on or off a bit. For example, 4095 looks like:
// 1111 1111 1111, which will position a piece in squares 1 - 12
// top squares. The middle is blank, so 0000 0000, which covers
// squares 13 - 20. Lastly,
// black is placed in the lowest bits similar to white's pattern,
// but in spaces 21 - 32, hence, the large number. Neither start with
// kings, so bits 33 - 64 are all zeros on both sides.
// Note that the commented out values are primarily for debugging
// special move cases or interesting situations. They are retained here as
// pairs. If used, each pair must be uncommented.
if (color == Color::RED) // red
//pieces = 1;
//pieces = 19455;
//pieces = 1152921504875282432;
pieces = 4095; // Initial board state
else // black
//pieces = 4291952640;
//pieces = 128;
//pieces = 16974848;
pieces = 4293918720; // Initial board state
}
/**
* Member Function | Pieces | isKing
*
* Summary : Determines if the piece in a given position is
* a king or not. Prior to calling this, the position
* must be checked to see if the position is occupied
* by a player.
*
* @author : David Torrente
*
* @param int position : The position on the board to
check to see if it is a king.
*
* @return bool : Returns true if it is a king piece,
* false otherwise.
*
*/
bool Pieces::isKing(int position) const
{
// NOTE: it is 31, since the offset is 0 - 31
// Could have written +32 -1, but better to just combine.
// Checks the high bit (33 - 64) which is aligned with
// the position bit (1 - 32).
int kingBit = position + 31;
bool isKing = false;
if (((pieces >> kingBit) & 1) == 1)
{
isKing = true;
}
return isKing;
}
/**
* Member Function | Pieces | setKing
*
* Summary : Sets the king bit for a given position to either
* be on or off.
*
* @author : David Torrente
*
* @param int position : The position on the board to
* toggle to a king or not king.
*
* @param bool toKing : True if the piece is to become
* a king, false if it is to be reduced
* to not a king. Reducing the piece type
* is primarily done to keep the board clean.
*
*/
void Pieces::setKing(int position, bool toKing)
{
int kingBit = position + 31;
if (toKing == true)
{
pieces = pieces | (1ULL << kingBit);
}
else
{
pieces = pieces & ~(1ULL << kingBit);
}
}
// CLASS BOARD
// Forward declare the static data member.
Board::BoardMoveTable Board::boardMoveTable[33];
/**
* Constructor | Board | Board
*
* Summary : Creates both the red player and black player
* piece list. If not already done, it also
* instantiates the static move table.
*
* @author : David Torrente
*
*/
Board::Board()
{
// Guarded internally to prevent from having moves added to it.
InitializeMoveTable();
// Assign the proper pieces to the player, either red or black.
redPieces = Pieces(Color::RED);
blackPieces = Pieces(Color::BLACK);
}
/**
* Destructor | Board | ~Board
*
* Summary : Class destructor. No special code required.
*
* @author : David Torrente
*
*/
Board::~Board()
{
}
/**
* Member Function | Board | getPlayerPieces
*
* Summary : Gets the pieces that belong to a particular player.
*
* @author : David Torrente
*
* @param Color color : The player color of pieces to get.
*
* @return Pieces : The pieces belonging to the player.
*
*/
Pieces Board::getPlayerPieces(Color color)
{
if (color == Color::RED)
{
return redPieces;
}
else
{
return blackPieces;
}
}
/**
* Member Function | Board | getOpponentPieces
*
* Summary : Gets the pieces that belong to the
* opposing player.
*
* @author : David Torrente
*
* @param Color color : The player color of pieces to
* get the opponent of.
*
* @return Pieces : The pieces belonging to the
* opposing player.
*
*/
Pieces Board::getOpponentPieces(Color color)
{
if (color == Color::RED)
{
return blackPieces;
}
else
{
return redPieces;
}
}
/**
* Member Function | Board | getNumRegularPieces
*
* Summary : Gets the count of man pieces that belong
* to the player.
*
* @author : David Torrente
* @param Color color : The player color of pieces to
* get the count for.
*
* @return int : A value 0 to 12 which is the count
* of man pieces.
*
*/
int Board::getNumRegularPieces(Color color)
{
return getNumPlayerTotalPieces(color) - getNumKingPieces(color);
}
/**
* Member Function | Board | getNumKingPieces
*
* Summary : Gets the count of king pieces that belong
* to the player.
*
* @author : David Torrente
*
* @param Color color : The player color of pieces to
* get the count for.
*
* @return int : A value 0 to 12 which is the count
* of king pieces.
*
*/
int Board::getNumKingPieces(Color color)
{
{
int kingPieceCount = 0;
long long *playerPieces;
if (color == Color::RED)
{
playerPieces = &redPieces.pieces;
}
else
{
playerPieces = &blackPieces.pieces;
}
for (int iter = 32; iter < 64; iter++)
{
if (((*playerPieces >> iter) & 1) == 1)
{
kingPieceCount++;
}
}
return kingPieceCount;
}
}
/**
* Member Function | Board | getNumPlayerTotalPieces
*
* Summary : Gets the count of total pieces that belong
* to the player.
*
* @author : David Torrente
*
* @param Color color : The player color of pieces to
* get the count for.
*
* @return int : A value 0 to 12 which is the count
* of total pieces.
*
*/
int Board::getNumPlayerTotalPieces(Color color)
{
int totalPieceCount = 0;
long long *playerPieces;
if (color == Color::RED)
{
playerPieces = &redPieces.pieces;
}
else
{
playerPieces = &blackPieces.pieces;
}
for (int iter = 0; iter < 32; iter++)
{
if (((*playerPieces >> iter) & 1) == 1)
{
totalPieceCount++;
}
}
return totalPieceCount;
}
/**
* Member Function | Board | moveGen
*
* Summary : Gets all of the possible moves for the player
* based on the color parameter. Note that this
* is bound by the mandatory jump rule, meaning
* that a player who can jump will not be given
* the option to move to a adjacent space. These
* adjacent space moves will not even be computed
* if there is a jump possible.
*
* @author : David Torrente
*
* @param Color color : The player color to get the moves
* for.
*
* @return vector<Board::Move> : A vector of possible moves
* for the player.
*
*/
std::vector<Board::Move> Board::moveGen(Color color)
{
std::vector<Move> totalMoves;
std::vector<Move> returnedMoves;
Pieces *playerPieces;
Pieces *opponentPieces;
int bitOffset = 0;
if (color == Color::RED)
{
playerPieces = &redPieces;
opponentPieces = &blackPieces;
}
else
{
playerPieces = &blackPieces;
opponentPieces = &redPieces;
}
// Go through all 32 squares and see if it is one
// of the appropriate pieces belonging to player.
for (int pieceIter = 1; pieceIter <= 32; pieceIter++)
{
// The offset is used to align to 0 - 31, but
// the board in checkers is 1 - 32. Use an offset
// here to align to the position bits properly.
bitOffset = pieceIter - 1;
if ((((playerPieces->pieces) >> bitOffset) & 1) == 1)
{
// Check for possible jump mpves first.
returnedMoves = getJumpsForPiece(color, pieceIter, playerPieces, opponentPieces);
totalMoves.insert(totalMoves.end(), returnedMoves.begin(), returnedMoves.end());
}
}
// Go through all 32 squares and see if it is one
// of the appropriate pieces belonging to player.
// Do this only if no jumps are possible. This is controlled
// by the if condition here. No need to get moves if there are
// jumps already found.
if (totalMoves.size() == 0)
{
for (int pieceIter = 1; pieceIter <= 32; pieceIter++)
{
// The offset is used to align to 0 - 31, but
// the board in checkers is 1 - 32. Use an offset
// here to align to the position bits properly.
bitOffset = pieceIter - 1;
// Check if player is in this space
if ((((playerPieces->pieces) >> bitOffset) & 1) == 1)
{
// Check for non jump moves here.
returnedMoves = getMovesForPiece(color, pieceIter, playerPieces, opponentPieces);
totalMoves.insert(totalMoves.end(), returnedMoves.begin(), returnedMoves.end());
}
}
}
return totalMoves;
}
/**
* Member Function | Board | getJumpsForPiece
*
* Summary : Gets all of the possible jumps for the specific
* piece based on the color parameter and location.
* This is only called after confirming that the player
* has a piece in the proper location.
*
* @author : David Torrente
*
* @param Color color : The player color of pieces to
* get the jumps for.
*
* @param int piece : The board location to get the jumps
* for.
*
* @param Pieces *playerPieces : A pointer to the player pieces.
* Needed since these can block a possible
* jump.
*
* @param Pieces *opponentPieces : A pointer to the opponent pieces.
* Needed since these can block a possible
* jump as well as to confirm that a jump
* can happen.
*
* @return vector<Board::Move> : A vector of possible jumps
* for the particular piece.
*
*/
std::vector<Board::Move> Board::getJumpsForPiece(Color color, int piece, Pieces *playerPieces, Pieces *opponentPieces)
{
// The returned vector of all possible jumps carried out completely
std::vector<Move> finalMoves;
Board board;
bool wasKingPriorMove = false;
if (color == Color::RED)
{
board.redPieces = *playerPieces;
board.blackPieces = *opponentPieces;
}
else
{
board.redPieces = *opponentPieces;
board.blackPieces = *playerPieces;
}
// In order to start the recursion, this is needed.
Move move;
move.startSquare = piece;
wasKingPriorMove = board.getPlayerPieces(color).isKing(move.startSquare);
getJumpsForPieceRec(color, move, finalMoves, board, wasKingPriorMove);
return finalMoves;
}
/**
* Member Function | Board | getJumpsForPieceRec
*
* Summary : Gets all of the possible jumps in a single jump attempt.
* Often called "double" jumping. This creates a full
* jump chain. a recursive call.
*
* @author : David Torrente
*
* @param Color color : The player color of pieces to
* get the jump chain for.
*
* @param Move move : A starter move. Contains a starting
* space, with an empty destination vector.
* when passed in to a recursive call,
* this destination vector may contain
* moves, signifying that it is in the middle
* of a jump chain.
*
* @param vector<Board> & totalMovesAccumulator : Accumulates all possible
* jumps at the end of each jump chain. Passed
* in by reference and returned to calling
* function.
*
* @param Board board : The current state of the board, updated for each jump.
*
* @param bool wasKingPriorMove : A value used to determine if the king was a king prior
* to this move. Becoming a king stops a jump chain.
*
*/
void Board::getJumpsForPieceRec(Color color, Board::Move move, std::vector<Board::Move> &totalMovesAccumulator, Board board, bool wasKingPriorMove)
{
int piece;
bool endOfJumpChain = true;
bool isKing = false;
int bitOffset = 0;
int squareJumped = 0;
// This determines if we are starting the jump sequence or in the middle of it.
if (move.destinationSquare.size() == 0)
{
piece = move.startSquare;
// use the current board as it is.
}
else
{
piece = move.destinationSquare.back();
board = updateBoard(move, color);
}
// First, determine if it is a king. This is needed to see which moves
// are valid for this piece/player. Move either up/down at first.
isKing = board.getPlayerPieces(color).isKing(piece);
if (isKing == wasKingPriorMove)
{
// Check if a jump position is open for this piece. This goes through all of the jumps.
for (int jumpIter = 0; jumpIter < Board::boardMoveTable[piece].jumps.size(); jumpIter++)
{
// Get the position of the jump, reduce it by one for an offset. Note
// that while it is one less than the position, the direction check still works
// since a jump will always be greater than the distance needed to determine
// direction.
bitOffset = (Board::boardMoveTable[piece].jumps.at(jumpIter) - 1);
// Check to see which direction it is going, and if it can go that direction.
// King goes both ways Red not a king goes "down" Black not a king goes "up"
if (isKing || ((piece - bitOffset) < 0 && color == Color::RED) || ((piece - bitOffset) > 0 && color == Color::BLACK))
{