Skip to content

Commit 685e59a

Browse files
committed
Basic version of ultimate tic tac toe is playable
1 parent 54ded11 commit 685e59a

File tree

5 files changed

+119
-77
lines changed

5 files changed

+119
-77
lines changed

Player.cpp

Lines changed: 14 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -73,16 +73,21 @@ vector<double> NeuralPlayer::getMove(const Matrix& input) const{
7373
}
7474

7575
//----------ManualPlayer--------------
76-
ManualPlayer::ManualPlayer(istream& is, ostream& os)
76+
ManualPlayer::ManualPlayer(istream& is, ostream& os,
77+
const int rows, const int cols)
7778
: Player()
7879
, m_is(is)
79-
, m_os(os){
80+
, m_os(os)
81+
, m_rows(rows)
82+
,m_cols(cols){
8083
}
8184

8285
ManualPlayer::ManualPlayer(const ManualPlayer& p)
8386
: Player(p)
8487
, m_is(p.m_is)
85-
, m_os(p.m_os){
88+
, m_os(p.m_os)
89+
, m_rows(p.m_rows)
90+
, m_cols(p.m_cols){
8691
}
8792

8893
ManualPlayer::~ManualPlayer(){
@@ -95,21 +100,13 @@ void ManualPlayer::operator= (const ManualPlayer& right){
95100

96101
vector<double> ManualPlayer::getMove(const Matrix& input) const{
97102
unsigned int row, col;
98-
do{
99-
char eater;
100-
m_os << "Your move, in the range 1-3, of the form \"row, col\": ";
101-
m_is >> row >> eater >> col;
102-
row --;
103-
col --;
104-
if(row > 2){
105-
row = 2;
106-
}
107-
if(col > 2){
108-
col = 2;
109-
}
110-
} while((int)input(0, 3 * row + col) != 0);
103+
char eater;
104+
m_os << "Your move, of the form \"row, col\": ";
105+
m_is >> row >> eater >> col;
106+
row --;
107+
col --;
111108

112-
Matrix temp(3,3);
109+
Matrix temp(m_rows, m_cols);
113110
temp.initialize(0);
114111
temp(row, col) = 1;
115112
return temp.toVector();

Player.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ class NeuralPlayer : public Player{
5555
//A player with a manual input brain
5656
class ManualPlayer : public Player{
5757
public:
58-
ManualPlayer(istream& is, ostream& os);
58+
ManualPlayer(istream& is, ostream& os, const int rows, const int cols);
5959
ManualPlayer(const ManualPlayer& p);
6060
virtual ~ManualPlayer();
6161

@@ -66,6 +66,8 @@ class ManualPlayer : public Player{
6666
private:
6767
istream& m_is;
6868
ostream& m_os;
69+
const int m_rows;
70+
const int m_cols;
6971
};
7072
/*
7173
//A player with a theoretically perfect brain

Population.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ void Population<Game>::loadBest(string path, string name){
119119
loadedPlayer.player.neural.printWeights();
120120

121121
//Set up a human-input player
122-
ManualPlayer tempHuman(cin, cout);
122+
ManualPlayer tempHuman(cin, cout, 3, 3);
123123
playerContainer<ManualPlayer> human(tempHuman);
124124

125125
//Play each other

UltimateTTT.h

Lines changed: 86 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,12 @@ class UltimateTTT {
3030
void printBoard() const;
3131

3232
const static int NUM_INPUTS = 9;
33-
const static int NUM_OUTPUTS = 81;
33+
const static int NUM_OUTPUTS = 90;
3434
private:
3535
bool takeTurn(const States state, const int turns);
3636
bool isEmpty() const;
3737
bool isFull() const;
38+
bool subBoardTied(const int subBoard) const;
3839

3940
Matrix toMatrix() const;
4041
Matrix toPlayerPerspective(const States state) const;
@@ -47,7 +48,7 @@ class UltimateTTT {
4748

4849
vector<unsigned int> bestMoves(const vector<double>& input) const;
4950
//void printBoard() const;
50-
bool hasWon() const;
51+
bool boardHasWon(const uint32_t board) const;
5152

5253
vector<uint32_t> m_board;
5354
uint32_t m_metaBoard;
@@ -69,17 +70,12 @@ template <class T1, class T2>
6970
UltimateTTT<T1, T2>::UltimateTTT(playerContainer<T1>& player1,
7071
playerContainer<T2>& player2, bool verbose)
7172
: m_board(9, (uint32_t)0)
72-
, activeBoard(0)
73+
, activeBoard(4)
7374
, m_player1(player1)
7475
, m_player2(player2)
7576
, dontRevisitSquares(true)
7677
, m_verbose(verbose){
7778
m_metaBoard = (uint32_t)0;
78-
setBoardAtPos(-1, 0, States::playerO);
79-
setBoardAtPos(-1, 5, States::playerX);
80-
setBoardAtPos(-1, 8, States::playerX);
81-
setBoardAtPos(1, 7, States::playerX);
82-
setBoardAtPos(6, 4, States::playerX);
8379
}
8480

8581
/* Plays until a player wins or the board is full.
@@ -115,7 +111,18 @@ bool UltimateTTT<T1, T2>::isEmpty() const{
115111
template <class T1, class T2>
116112
bool UltimateTTT<T1, T2>::isFull() const{
117113
for(int i = 0; i < 9; ++i){
118-
if(getBoardAtPos(-1, i) == States::empty){
114+
if(getBoardAtPos(-1, i) == States::empty
115+
&& !subBoardTied(i)){
116+
return false;
117+
}
118+
}
119+
return true;
120+
}
121+
122+
template <class T1, class T2>
123+
bool UltimateTTT<T1, T2>::subBoardTied(const int subBoard) const{
124+
for(int i = 0; i < 9; ++i){
125+
if(getBoardAtPos(subBoard, i) == States::empty){
119126
return false;
120127
}
121128
}
@@ -200,8 +207,12 @@ void UltimateTTT<T1, T2>::printBoard() const{
200207
} else if(i % 2 == 0 && (i % 8 != 0) && j % 4 == 3 &&
201208
(j % 16 != 15)){
202209
States curState = States::empty;
203-
if(metaOccupied && dontRevisitSquares && subPostion == 4){
204-
curState = getBoardAtPos(-1, subBoard);
210+
if(metaOccupied && dontRevisitSquares){
211+
if(subPostion == 4){
212+
curState = getBoardAtPos(-1, subBoard);
213+
} else{
214+
curState = States::empty;
215+
}
205216
} else{
206217
curState = getBoardAtPos(subBoard, subPostion);
207218
}
@@ -226,9 +237,9 @@ void UltimateTTT<T1, T2>::printBoard() const{
226237
*/
227238
template <class T1, class T2>
228239
Matrix UltimateTTT<T1, T2>::toMatrix() const{
229-
Matrix temp(1, NUM_OUTPUTS);
240+
Matrix temp(1, NUM_OUTPUTS - 9);
230241

231-
for(int i = 0; i < NUM_OUTPUTS; ++i){
242+
for(int i = 0; i < (NUM_OUTPUTS - 9); ++i){
232243
temp(0, i) = (float)getBoardAtPos(i);
233244
}
234245
return temp;
@@ -372,47 +383,36 @@ void UltimateTTT<T1, T2>::setBoardAtPos(const int i, const States state){
372383
int position = x % 3 + 3 * (y % 3);
373384

374385
uint32_t shiftAmount = (uint32_t)(8 - position) << 1;
386+
//Clear the 2-bit-wode field
387+
m_board[subBoard] &= ~((uint32_t)3 << shiftAmount);
375388

376-
if(subBoard < 0){
377-
//Clear the 2-bit-wode field
378-
m_metaBoard &= ~((uint32_t)3 << shiftAmount);
379-
380-
//Set the new state at the 2-bit-wide field
381-
uint32_t val = (uint32_t)state;
382-
m_metaBoard |= (val << shiftAmount);
383-
} else{
384-
//Clear the 2-bit-wode field
385-
m_board[subBoard] &= ~((uint32_t)3 << shiftAmount);
386-
387-
//Set the new state at the 2-bit-wide field
388-
uint32_t val = (uint32_t)state;
389-
m_board[subBoard] |= (val << shiftAmount);
390-
}
391-
389+
//Set the new state at the 2-bit-wide field
390+
uint32_t val = (uint32_t)state;
391+
m_board[subBoard] |= (val << shiftAmount);
392392
}
393393

394-
//Helper function to determine if a player has won
394+
395395
template <class T1, class T2>
396-
bool UltimateTTT<T1, T2>::hasWon() const{
396+
bool UltimateTTT<T1, T2>::boardHasWon(const uint32_t board) const{
397397
//PlayerX
398-
if((m_metaBoard & (uint32_t)65793 ) == (uint32_t)65793){ return true;}
399-
if((m_metaBoard & (uint32_t)4368 ) == (uint32_t)4368 ){ return true;}
400-
if((m_metaBoard & (uint32_t)86016 ) == (uint32_t)86016){ return true;}
401-
if((m_metaBoard & (uint32_t)1344 ) == (uint32_t)1344 ){ return true;}
402-
if((m_metaBoard & (uint32_t)21 ) == (uint32_t)21 ){ return true;}
403-
if((m_metaBoard & (uint32_t)66576 ) == (uint32_t)66576){ return true;}
404-
if((m_metaBoard & (uint32_t)16644 ) == (uint32_t)16644){ return true;}
405-
if((m_metaBoard & (uint32_t)4161 ) == (uint32_t)4161 ){ return true;}
398+
if((board & (uint32_t)65793 ) == (uint32_t)65793){ return true;}
399+
if((board & (uint32_t)4368 ) == (uint32_t)4368 ){ return true;}
400+
if((board & (uint32_t)86016 ) == (uint32_t)86016){ return true;}
401+
if((board & (uint32_t)1344 ) == (uint32_t)1344 ){ return true;}
402+
if((board & (uint32_t)21 ) == (uint32_t)21 ){ return true;}
403+
if((board & (uint32_t)66576 ) == (uint32_t)66576){ return true;}
404+
if((board & (uint32_t)16644 ) == (uint32_t)16644){ return true;}
405+
if((board & (uint32_t)4161 ) == (uint32_t)4161 ){ return true;}
406406

407407
//PlayerO
408-
if((m_metaBoard & (uint32_t)131586) == (uint32_t)131586){ return true;}
409-
if((m_metaBoard & (uint32_t)8736 ) == (uint32_t)8736 ){ return true;}
410-
if((m_metaBoard & (uint32_t)172032) == (uint32_t)172032){ return true;}
411-
if((m_metaBoard & (uint32_t)2688 ) == (uint32_t)2688 ){ return true;}
412-
if((m_metaBoard & (uint32_t)42 ) == (uint32_t)42 ){ return true;}
413-
if((m_metaBoard & (uint32_t)133152) == (uint32_t)133152){ return true;}
414-
if((m_metaBoard & (uint32_t)33288 ) == (uint32_t)33288 ){ return true;}
415-
if((m_metaBoard & (uint32_t)8322 ) == (uint32_t)8322 ){ return true;}
408+
if((board & (uint32_t)131586) == (uint32_t)131586){ return true;}
409+
if((board & (uint32_t)8736 ) == (uint32_t)8736 ){ return true;}
410+
if((board & (uint32_t)172032) == (uint32_t)172032){ return true;}
411+
if((board & (uint32_t)2688 ) == (uint32_t)2688 ){ return true;}
412+
if((board & (uint32_t)42 ) == (uint32_t)42 ){ return true;}
413+
if((board & (uint32_t)133152) == (uint32_t)133152){ return true;}
414+
if((board & (uint32_t)33288 ) == (uint32_t)33288 ){ return true;}
415+
if((board & (uint32_t)8322 ) == (uint32_t)8322 ){ return true;}
416416

417417
return false;
418418
}
@@ -437,16 +437,49 @@ bool UltimateTTT<T1, T2>::takeTurn(const States state, const int turn){
437437
}
438438

439439
//Make the best move from available squares
440+
bool foundOpenSpot = false;
440441
for(int i = 0; i < NUM_OUTPUTS; ++i){
441-
if(getBoardAtPos(moves[i]) == States::empty){
442-
setBoardAtPos(moves[i], state);
443-
break;
442+
if(moves[i] < 9){
443+
if(getBoardAtPos(activeBoard, moves[i]) == States::empty
444+
&& (getBoardAtPos(-1, activeBoard) == States::empty
445+
|| !dontRevisitSquares)){
446+
setBoardAtPos(activeBoard, moves[i], state);
447+
if(getBoardAtPos(-1, activeBoard) == States::empty
448+
&& boardHasWon(m_board[activeBoard])){
449+
setBoardAtPos(-1, activeBoard, state);
450+
}
451+
activeBoard = moves[i];
452+
foundOpenSpot = true;
453+
break;
454+
}
455+
}
456+
}
457+
if(!foundOpenSpot){
458+
459+
for(int i = 0; i < NUM_OUTPUTS; ++i){
460+
if(moves[i] < 81){
461+
int subBoard = (moves[i] % 9) / 3 +
462+
3 * (int)((int)(moves[i] / 9) / 3);
463+
if(getBoardAtPos(moves[i]) == States::empty
464+
&& (getBoardAtPos(-1, subBoard) == States::empty
465+
|| !dontRevisitSquares)){
466+
setBoardAtPos(moves[i], state);
467+
468+
if(getBoardAtPos(-1, subBoard) == States::empty
469+
&& boardHasWon(m_board[subBoard])){
470+
setBoardAtPos(-1, subBoard, state);
471+
}
472+
activeBoard = subBoard;
473+
break;
474+
}
475+
}
444476
}
477+
445478
}
446479

447480
//Check if the move played was a winning move
448-
if(turn >= 5){
449-
if(hasWon()){
481+
if(turn >= 17){
482+
if(boardHasWon(m_metaBoard)){
450483
if(state == States::playerX){
451484
m_player1.player.addToFitness(1.0 + (10.0 - turn) / 10.0);
452485
} else{
@@ -472,7 +505,7 @@ bool UltimateTTT<T1, T2>::takeTurn(const States state, const int turn){
472505

473506

474507
//Check if the board is now full
475-
if(turn == 9){
508+
if(turn == 81){
476509
if(isFull()){
477510
m_player1.player.addToFitness(1.0);
478511
m_player2.player.addToFitness(1.0);

main.cpp

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,29 @@
33
#include "main.h"
44
#include "Population.h"
55

6+
67
int main(){
8+
srand(time(NULL));
9+
710
//Where your player log files are stored
811
string path = "data/";
912
Population<TicTacToe> pop;
1013

1114

12-
ManualPlayer tempHuman1(cin, cout);
15+
ManualPlayer tempHuman1(cin, cout, 10, 9);
1316
playerContainer<ManualPlayer> human1(tempHuman1);
14-
ManualPlayer tempHuman2(cin, cout);
15-
playerContainer<ManualPlayer> human2(tempHuman2);
17+
18+
std::vector<unsigned int> layersizes;
19+
layersizes.push_back(81);
20+
layersizes.push_back(1);
21+
layersizes.push_back(90);
22+
NeuralPlayer NeuralPlayer1(layersizes);
23+
NeuralPlayer NeuralPlayer2(layersizes);
24+
playerContainer<NeuralPlayer> machine1(NeuralPlayer1);
25+
playerContainer<NeuralPlayer> machine2(NeuralPlayer1);
1626

17-
UltimateTTT<ManualPlayer, ManualPlayer> ttt(human1, human2, true);
18-
ttt.printBoard();
27+
UltimateTTT<NeuralPlayer, NeuralPlayer> ttt(machine1, machine2, true);
28+
ttt.playGame();
1929

2030

2131
char loadPlayer;

0 commit comments

Comments
 (0)