C++ Darts Game: Loop does not exit second time round - c++

I am currently coding a simulator for a game (or multiple games) of 501 darts in C++ and have run into a problem in my code.
The purpose of this region is to simulate 10,000 matches of world championship final darts between two players. One match consists of two players playing the best of 13 sets (first to 7), a set being the best of 5 games (first to 3). In each game both players start at a score of 501 and try to reduce their score to 0 before the other player. I am simulating 10,000 of these matches in order to gain the frequency of each possible result between the two players.
My players are declared in a class with their skill attributes and score count etc, with a method throw_quick_set(); which simulates the player throwing a set of 3 darts for their turn before the other player steps up to do the same.
My problem is, after the first match is won (the program sees a player with 7 sets) - the second match goes on forever with a player winning an infinite amount of sets. I see this because of the output phrases I included when a player wins a game or set when I noticed something was going wrong.
Perhaps someone can help me spot the error in my code, I've been staring at it so long I probably wouldn't notice it even if it was staring me in the face.
Thanks for your time!
int results[14] = {0}; //Stores counters for each of 14 possible outcomes
for (int gameNum = 0; gameNum < 10000; gameNum++)
{
// Calculate results of 10,000 full world championship finals.
// Final is thirteen sets - each set is a best of 5 games.
int sidSetWins = 0, joeSetWins = 0;
int sidGWins = 0, joeGWins = 0;
do
{ // Play best of 13 sets
do
{ // Play best of 5 games
do
{ // Play single game
if (currTurn == sid)
{
Sid.throw_quick_set(); // Throws a set of three
currTurn = joe;
}
else if (currTurn == joe)
{
Joe.throw_quick_set(); // Throws a set of three
currTurn = sid;
}
}
while ((Sid.score != 0) && (Joe.score != 0));
if (Sid.score == 0)
{
sidGWins++;
cout << "Sid wins game" << endl;
Sid.score = 501;
Joe.score = 501;
}
else if (Joe.score == 0)
{
joeGWins++;
cout << "Joe wins game" << endl;
Sid.score = 501;
Joe.score = 501;
}
Sleep(1000);
}
while ((joeGWins < 3) && (sidGWins < 3));
if (joeGWins == 3)
{
joeSetWins++;
cout << "Joe wins set" << endl;
sidGWins = 0;
joeGWins = 0; // Reset for each set
}
else if (sidGWins == 3)
{
sidSetWins++;
cout << "Sid wins set" << endl;
sidGWins = 0;
joeGWins = 0; // Reset for each set
}
Sleep(1000);
}
while ((sidSetWins < 7) && (joeSetWins < 7));
if ((gameNum%1000) == 0)
{
cout << "SidSets " << sidSetWins << endl;
cout << "JoeSets " << joeSetWins << endl;
sidSetWins = 0;
joeSetWins = 0; // Reset for each match
}
EDIT: Here's the class method throw_live_set(); also, if that helps you.
int Darts_501::throw_quick_set()
{ // Used for quick games with no commentary - for average calculations
darts = 0;
int startScore = score;
quickGame = true; // Disable commentary
do
{
if (score>=100)
curr_state = fast;
else if (score > 50)
curr_state = slow;
else if ((score <= 50) && (score > 1))
curr_state = focus;
else if ((score == 1) || (score < 0))
curr_state = bust;
else
curr_state = out;
switch (curr_state)
{
case fast:
score -= throw_treble(20, trebPerc);
break;
case slow:
score -= throw_single(20);
break;
case focus:
if (score == 50)
score -= throw_bull(bullPerc);
else if ((score != 0) && (score%2 == 0))
score -= throw_double(score/2); // If score is even, look to hit the double of half that value
else if ((score != 0) && (score%2 != 0))
score -= throw_setup(score); // If odd, look to set-up a good double score (strategic)
break;
case bust:
score = startScore;
break;
case out: score = 0;;
}
darts++;
} while ((darts < 3) &&
(curr_state != out) &&
(curr_state != bust));
return 0;
}

Related

Texas holdem straight check values

I'm have a small issue with evaluating a straight in a seven card hand, where you want to get the highest 5 card straight.
I can get highest 5 card straight as long as there is not a pair in the hand so for example:
Hole cards: 2h,3d
community cards:4h, 5s, 6s, 8d, 9d,
Output: 2h,3d,4h,5s,6s
Because there is no pair within the straight it counts and outputs just fine.
The problem occurs when there is a pair within the straight
for example:
Hole cards: 2h, 3d,
community cards: 4h, 4c, 5s, 6s, 8d
Output: 2h,3d,4h,4c,5s
So it is counting the pair in the straight, I need a way to skip past one of the paired cards and select only one. This happens with 3 of a kind as well.
It makes complete sense to me why it is doing this, but for the life of me I can't think of a way to fix it.
Here is the function:
bool Player::checkForStraight()
{
//Loops through the ranks from greatest to least 0 is NONE is the enum class
for (int i = FACES; i >= 1; i--)
{
//Checks to see if a straight exists (_checkNumfaces is an array[13] to
//hold the amount of each card
if ((_checkNumFaces[i - 1] >= 1) && (_checkNumFaces[i - 2] >= 1) &&
(_checkNumFaces[i - 3] >= 1) && (_checkNumFaces[i - 4] >= 1) &&
(_checkNumFaces[i - 5] >= 1))
{
//If a straight exists loop through the cards(sorted in another
//function from lowest to highest
for (int j = 6; j >= 0; j--)
{
//if the face matches the enum value of i then we have a
//straight there down because I checked if the straight
//existed already
if ((*p_playerHand[j])->face == (Face)(i))
{
//PlayerHighCards out of the seven
p_playerHighFive[4] = p_playerHand[j];
p_playerHighFive[3] = p_playerHand[j - 1];
p_playerHighFive[2] = p_playerHand[j - 2];
p_playerHighFive[1] = p_playerHand[j - 3];
p_playerHighFive[0] = p_playerHand[j - 4];
return true;
}
}
}
}
return false;
}
Sorry about the shoddy formatting at the bottom but its only curly braces and they are fine in my code.
I know how to check for A,2,3,4,5 straight I just havnt coded yet, I am just really stumped on how to move past one of the pairs that I was on about. Its not just pairs, this occurs with 3 of a kind as well (so if there's 3 of a kind in the straight) or more than one pair for that matter so 2 pairs. Doesn't happen outside of this because there's not physically enough cards to be concerned about it.
Sorry if my code is not that elegant and if there's a better way then let me know. I havnt learned templates or the stl yet as I have only just started the language so a solution not using these would be great.
I tried to be as descriptive as possible but, I'm new to this so if you have any questions just ask:)
Here is the function for counting the different cards in the deck
void Player::countNumFaces(const int& size)
{
for (int i = 0; i < (size); i++)
{
switch((*p_playerHand[i])->face)
{
case Face::NONE: std::cout << "Joker" << std::endl;
case Face::TWO: _checkNumFaces[0] ++; break;
case Face::THREE: _checkNumFaces[1] ++; break;
case Face::FOUR: _checkNumFaces[2] ++; break;
case Face::FIVE: _checkNumFaces[3] ++; break;
case Face::SIX: _checkNumFaces[4] ++; break;
case Face::SEVEN: _checkNumFaces[5] ++; break;
case Face::EIGHT: _checkNumFaces[6] ++; break;
case Face::NINE: _checkNumFaces[7] ++; break;
case Face::TEN: _checkNumFaces[8] ++; break;
case Face::JACK: _checkNumFaces[9] ++; break;
case Face::QUEEN: _checkNumFaces[10] ++; break;
case Face::KING: _checkNumFaces[11] ++; break;
case Face::ACE: _checkNumFaces[12] ++; break;
}
}
std::cout << "| 2 || 3 || 4 || 5 || 6 || 7 || 8 || 9 | 10 || J || Q || K || A |" << std::endl; //Print for testing
for (int i = 0; i < FACES; i++) { std::cout << "| " << _checkNumFaces[i] << " |"; } //Print for testing
}
Instead of
p_playerHighFive[4] = p_playerHand[j];
p_playerHighFive[3] = p_playerHand[j - 1];
p_playerHighFive[2] = p_playerHand[j - 2];
p_playerHighFive[1] = p_playerHand[j - 3];
p_playerHighFive[0] = p_playerHand[j - 4];
return true;
try
int k = 4;
int l = 0;
p_playerHighFive[k] = p_playerHand[j-l];
k--;
l++;
while(k>=0)
{
if( isSameValue(p_playerHighFive[k+1], p_playerHand[j-l]) ) //check if card has same value
{
l++;
continue;
}
p_playerHighFive[k] = p_playerHand[j-l];
k--;
l++;
}

Why is my program crashing when I'm trying to fill up an array?

So with this program, basically, I ask for the names of three people, and store those strings in an array. That parts seems to work fine.
After that, I ask for their quarterly reports. So each person gets 4, and it's ordered through the array so that:
Index 0-3 goes to person A
Index 4-7 goes to person B
And index 8-11 goes to person C.
In this second for-loop that processes this second array, I have a list of if/else if statements that determine the name of the person in question that I will be asking for. In the separate function itself, readSales(), I have a similar thing set up to determine which quarter to ask for. This is designed to loop 12 times in order to get all 12 indexes filled.
For some reason, after I input the people's names, the program crashes. Any idea why?
Also, I know "using namespace std;" isn't very popular, but that's how my professor wants it so that's how I have it.
// In this program, I will
// (1) Ask for the names of three salespeople.
// (2) Accept sales for each quarter (for each person).
// (3) Display their name and total sales amount.
// I will use three functions:
// (1) main
// (2) readInfo
// (3) displayInfo
// No global variables, and I will pass data as parameters.
#include <iostream>
#include <string>
using namespace std;
// In order to pass an array through a void function,
// I must create it in the main function.
// So...
string readNames(int); // Function that gathers data from user.
double readSales(string, int);
void displayInfo(); // Function that displays final result.
int main() // Our main function
{
// Create my variables for arrays and final result.
string arrNames[3];
double arrSales[12], result;
// I must call my first function now.
for (int i = 0; i < 3; i++)
{
arrNames[i] = readNames(i); // Successfully gathers all 3 names and stores them in the array. Woo!
}
// Now I must gather the number data, using a double array.
string person = "uninitialized";
int x = 0;
for (x; x < 12; x++);
{
if (x < 4) // setup to ask question
person = arrNames[0];
if (x < 8)
person = arrNames[1];
if (x < 12)
person = arrNames[2];
arrSales[x] = readSales(person, x); // Successfully gathers all 12 quarters and stores them. Yay!
}
cout << arrNames[3];
} // end function main()
string readNames(int count)
{
for (count; count < 3;)
{
string i;
cout << "Please input salesperson " << count + 1 << "'s name: ";
cin >> i;
cout << endl;
return i;
}
return 0;
} // end function readNames()
double readSales(string person, int count) // reading the sales
{
double i; // variable I am returning at the end of function.
int quarter;
if (count == 0 || count == 4 || count == 8)
{
quarter = 1;
}
else if (count == 1 || count == 5 || count == 9)
{
quarter = 2;
}
else if (count == 2 || count == 6 || count == 10)
{
quarter = 3;
}
else if (count == 3 || count == 7 || count == 11)
{
quarter = 4;
}
else
return 0;
cout << "Please input salesperson " << person << "'s sales for Quarter " << quarter << " (Please round to the nearest cent): $" << endl;
cin >> i;
return i;
}
Please remove the semi-colon in the for (x; x < 12; x++);
You can make it like this,
int x = 0;
for (x; x < 12; x++)
{
if ((x >= 0) && ( x <= 3)) // setup to ask question
{
person = arrNames[0];
}
else if ((x >= 4) && (x <= 7))
{
person = arrNames[1];
}
else
{
person = arrNames[2];
}
arrSales[x] = readSales(person, x); // Successfully gathers all 12 quarters and stores them. Yay!
}

Nested loop acting weird

EDIT: Posting everything, because it gets really weird.
using namespace std;
int main()
{
int doors = -1;
int jumper = 1;
bool isOpen[100];
string tf;
for(int i = 0 ; i < 100; i++){
isOpen[i] = false;
}
while(jumper < 100){
while(doors < 100){
if(isOpen[doors + jumper] == true){
isOpen[doors + jumper] = false;
}
else{
isOpen[doors + jumper] = true;
}
doors += jumper;
cout << doors << endl;
}
doors = -1;
jumper+=1;
}
for(int i = 0; i < 100; i++){
if(isOpen[i]){
tf = "open";
}
else{
tf = "closed.";
}
cout << "Door " << i << " is " << tf << endl;
}
return 0;
}
So I'm having a very odd problem with this piece of code.
It's supposed to go through an array of 100 items. 0 - 99 by ones then tows then threes, etc. However, after a = 10, it shoots up to 266.
Can anyone tell me why?
Edit:
This problem only happens when the for loop is commented out. When it is left in the code, it does the same thing, but it doesn't happen until 19.
If I comment out the "string tf;" as well, it continues to loop at 99.
This is all based on the doors count.
I'm unsure why either of these should be a factor to the loop that neither are connected to.
According to your description this is what you should do:
for(int adv = 1, i = 0; adv < 100;)
{
// i is array index (your b) -> use it somehow:
doSomething(arr[i]);
i += adv;
if(i >= 100)
{
i = 0;
adv++;
}
}
The (probable) reason you got weird behavior (including the 266 value) is that your code overruns the buffer. When b will be high enough (say 99), you'd write to isOpen[b + a] which will be 100 or higher (100 if a is 1, and that's just the first iteration, later iterations will go much further). If the compiler allocates isOpen before the ints you'll be overwriting them.

Minimax with alpha-beta pruning problems

I'm making a C++ program for the game chopsticks.
It's a really simple game with only 625 total game states (and it's even lower if you account for symmetry and unreachable states). I have read up minimax and alpha-beta algorithms, mostly for tic tac toe, but the problem I was having was that in tic tac toe it's impossible to loop back to a previous state while that can easily happen in chopsticks. So when running the code it would end up with a stack overflow.
I fixed this by adding flags for previously visited states (I don't know if that's the right way to do it.) so that they can be avoided, but now the problem I have is that the output is not symmetric as expected.
For example in the start state of the game each player has one finger so it's all symmetric. The program tells me that the best move is to hit my right hand with my left but not the opposite.
My source code is -
#include <iostream>
#include <array>
#include <vector>
#include <limits>
std::array<int, 625> t; //Flags for visited states.
std::array<int, 625> f; //Flags for visited states.
int no = 0; //Unused. For debugging.
class gamestate
{
public:
gamestate(int x, bool t) : turn(t) //Constructor.
{
for (int i = 0; i < 2; i++)
for (int j = 0; j < 2; j++) {
val[i][j] = x % 5;
x /= 5;
}
init();
}
void print() //Unused. For debugging.
{
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 2; j++)
std::cout << val[i][j] << "\t";
std::cout << "\n";
}
std::cout << "\n";
}
std::array<int, 6> canmove = {{ 1, 1, 1, 1, 1, 1 }}; //List of available moves.
bool isover() //Is the game over.
{
return ended;
}
bool won() //Who won the game.
{
return winner;
}
bool isturn() //Whose turn it is.
{
return turn;
}
std::vector<int> choosemoves() //Choose the best possible moves in the current state.
{
std::vector<int> bestmoves;
if(ended)
return bestmoves;
std::array<int, 6> scores;
int bestscore;
if(turn)
bestscore = std::numeric_limits<int>::min();
else
bestscore = std::numeric_limits<int>::max();
scores.fill(bestscore);
for (int i = 0; i < 6; i++)
if (canmove[i]) {
t.fill(0);
f.fill(0);
gamestate *play = new gamestate(this->playmove(i),!turn);
scores[i] = minimax(play, 0, std::numeric_limits<int>::min(), std::numeric_limits<int>::max());
std::cout<<i<<": "<<scores[i]<<std::endl;
delete play;
if (turn) if (scores[i] > bestscore) bestscore = scores[i];
if (!turn) if (scores[i] < bestscore) bestscore = scores[i];
}
for (int i = 0; i < 6; i++)
if (scores[i] == bestscore)
bestmoves.push_back(i);
return bestmoves;
}
private:
std::array<std::array<int, 2>, 2 > val; //The values of the fingers.
bool turn; //Whose turn it is.
bool ended = false; //Has the game ended.
bool winner; //Who won the game.
void init() //Check if the game has ended and find the available moves.
{
if (!(val[turn][0]) && !(val[turn][1])) {
ended = true;
winner = !turn;
canmove.fill(0);
return;
}
if (!(val[!turn][0]) && !(val[!turn][1])) {
ended = true;
winner = turn;
canmove.fill(0);
return;
}
if (!val[turn][0]) {
canmove[0] = 0;
canmove[1] = 0;
canmove[2] = 0;
if (val[turn][1] % 2)
canmove[5] = 0;
}
if (!val[turn][1]) {
if (val[turn][0] % 2)
canmove[2] = 0;
canmove[3] = 0;
canmove[4] = 0;
canmove[5] = 0;
}
if (!val[!turn][0]) {
canmove[0] = 0;
canmove[3] = 0;
}
if (!val[!turn][1]) {
canmove[1] = 0;
canmove[4] = 0;
}
}
int playmove(int mov) //Play a move to get the next game state.
{
auto newval = val;
switch (mov) {
case 0:
newval[!turn][0] = (newval[turn][0] + newval[!turn][0]);
newval[!turn][0] = (5 > newval[!turn][0]) ? newval[!turn][0] : 0;
break;
case 1:
newval[!turn][1] = (newval[turn][0] + newval[!turn][1]);
newval[!turn][1] = (5 > newval[!turn][1]) ? newval[!turn][1] : 0;
break;
case 2:
if (newval[turn][1]) {
newval[turn][1] = (newval[turn][0] + newval[turn][1]);
newval[turn][1] = (5 > newval[turn][1]) ? newval[turn][1] : 0;
} else {
newval[turn][0] /= 2;
newval[turn][1] = newval[turn][0];
}
break;
case 3:
newval[!turn][0] = (newval[turn][1] + newval[!turn][0]);
newval[!turn][0] = (5 > newval[!turn][0]) ? newval[!turn][0] : 0;
break;
case 4:
newval[!turn][1] = (newval[turn][1] + newval[!turn][1]);
newval[!turn][1] = (5 > newval[!turn][1]) ? newval[!turn][1] : 0;
break;
case 5:
if (newval[turn][0]) {
newval[turn][0] = (newval[turn][1] + newval[turn][0]);
newval[turn][0] = (5 > newval[turn][0]) ? newval[turn][0] : 0;
} else {
newval[turn][1] /= 2;
newval[turn][0] = newval[turn][1];
}
break;
default:
std::cout << "\nInvalid move!\n";
}
int ret = 0;
for (int i = 1; i > -1; i--)
for (int j = 1; j > -1; j--) {
ret+=newval[i][j];
ret*=5;
}
ret/=5;
return ret;
}
static int minimax(gamestate *game, int depth, int alpha, int beta) //Minimax searching function with alpha beta pruning.
{
if (game->isover()) {
if (game->won())
return 1000 - depth;
else
return depth - 1000;
}
if (game->isturn()) {
for (int i = 0; i < 6; i++)
if (game->canmove[i]&&t[game->playmove(i)]!=-1) {
int score;
if(!t[game->playmove(i)]){
t[game->playmove(i)] = -1;
gamestate *play = new gamestate(game->playmove(i),!game->isturn());
score = minimax(play, depth + 1, alpha, beta);
delete play;
t[game->playmove(i)] = score;
}
else
score = t[game->playmove(i)];
if (score > alpha) alpha = score;
if (alpha >= beta) break;
}
return alpha;
} else {
for (int i = 0; i < 6; i++)
if (game->canmove[i]&&f[game->playmove(i)]!=-1) {
int score;
if(!f[game->playmove(i)]){
f[game->playmove(i)] = -1;
gamestate *play = new gamestate(game->playmove(i),!game->isturn());
score = minimax(play, depth + 1, alpha, beta);
delete play;
f[game->playmove(i)] = score;
}
else
score = f[game->playmove(i)];
if (score < beta) beta = score;
if (alpha >= beta) break;
}
return beta;
}
}
};
int main(void)
{
gamestate test(243, true);
auto movelist = test.choosemoves();
for(auto i: movelist)
std::cout<<i<<std::endl;
return 0;
}
I'm passing the moves in a sort of base-5 to decimal system as each hand can have values from 0 to 4.
In the code I have input the state -
3 3
4 1
The output says I should hit my right hand (1) to the opponent's right (3) but it does not say I should hit it to my opponent's left (also 3)
I think the problem is because of the way I handled infinite looping.
What would be the right way to do it? Or if that is the right way, then how do I fix the problem?
Also please let me know how I can improve my code.
Thanks a lot.
Edit:
I have changed my minimax function as follows to ensure that infinite loops are scored above losing but I'm still not getting symmetry. I also made a function to add depth to the score
static float minimax(gamestate *game, int depth, float alpha, float beta) //Minimax searching function with alpha beta pruning.
{
if (game->isover()) {
if (game->won())
return 1000 - std::atan(depth) * 2000 / std::acos(-1);
else
return std::atan(depth) * 2000 / std::acos(-1) - 1000;
}
if (game->isturn()) {
for (int i = 0; i < 6; i++)
if (game->canmove[i]) {
float score;
if(!t[game->playmove(i)]) {
t[game->playmove(i)] = -1001;
gamestate *play = new gamestate(game->playmove(i), !game->isturn());
score = minimax(play, depth + 1, alpha, beta);
delete play;
t[game->playmove(i)] = score;
} else if(t[game->playmove(i)] == -1001)
score = 0;
else
score = adddepth(t[game->playmove(i)], depth);
if (score > alpha) alpha = score;
if (alpha >= beta) break;
}
return alpha;
} else {
for (int i = 0; i < 6; i++)
if (game->canmove[i]) {
float score;
if(!f[game->playmove(i)]) {
f[game->playmove(i)] = -1001;
gamestate *play = new gamestate(game->playmove(i), !game->isturn());
score = minimax(play, depth + 1, alpha, beta);
delete play;
f[game->playmove(i)] = score;
} else if(f[game->playmove(i)] == -1001)
score = 0;
else
score = adddepth(f[game->playmove(i)], depth);
if (score < beta) beta = score;
if (alpha >= beta) break;
}
return beta;
}
}
This is the function to add depth -
float adddepth(float score, int depth) //Add depth to pre-calculated score.
{
int olddepth;
float newscore;
if(score > 0) {
olddepth = std::tan((1000 - score) * std::acos(-1) / 2000);
depth += olddepth;
newscore = 1000 - std::atan(depth) * 2000 / std::acos(-1);
} else {
olddepth = std::tan((1000 + score) * std::acos(-1) / 2000);
depth += olddepth;
newscore = std::atan(depth) * 2000 / std::acos(-1) - 1000;
}
return newscore;
}
Disclaimer: I don't know C++, and I frankly haven't bothered to read the game rules. I have now read the rules, and still stand by what I said...but I still don't know C++. Still, I can present some general knowledge of the algorithm which should set you off in the right direction.
Asymmetry is not in itself a bad thing. If two moves are exactly equivalent, it should choose one of them and not stand helpless like Buridan's ass. You should, in fact, be sure that any agent you write has some method of choosing arbitrarily between policies which it cannot distinguish.
You should think more carefully about the utility scheme implied by refusing to visit previous states. Pursuing an infinite loop is a valid policy, even if your current representation of it will crash the program; maybe the bug is the overflow, not the policy that caused it. If given the choice between losing the game, and refusing to let the game end, which do you want your agent to prefer?
Playing ad infinitum
If you want your agent to avoid losing at all costs -- that is, you want it to prefer indefinite play over loss -- then I would suggest treating any repeated state as a terminal state and assigning it a value somewhere between winning and losing. After all, in a sense it is terminal -- this is the loop the game will enter forever and ever and ever, and the definite result of it is that there is no winner. However, remember that if you are using simple minimax (one utility function, not two), then this implies that your opponent also regards eternal play as a middling result.
It may sound ridiculous, but maybe playing unto infinity is actually a reasonable policy. Remember that minimax assumes the worst case -- a perfectly rational foe whose interests are the exact opposite of yours. But if, for example, you're writing an agent to play against a human, then the human will either err logically, or will eventually decide they would rather end the game by losing -- so your agent will benefit from patiently staying in this Nash equilibrium loop!
Alright, let's end the game already
If you want your agent to prefer that the game end eventually, then I would suggest implementing a living penalty -- a modifier added to your utility which decreases as a function of time (be it asymptotic or without bound). Implemented carefully, this can guarantee that, eventually, any end is preferable to another turn. With this solution as well, you need to be careful about considering what preferences this implies for your opponent.
A third way
Another common solution is to depth-limit your search and implement an evaluation function. This takes the game state as its input and just spits out a utility value which is its best guess at the end result. Is this provably optimal? No, not unless your evaluation function is just completing the minimax, but it means your algorithm will finish within a reasonable time. By burying this rough estimate deep enough in the tree, you wind up with a pretty reasonable model. However, this produces an incomplete policy, which means that it is more useful for a replanning agent than for a standard planning agent. Minimax replanning is the usual approach for complex games (it is, if I'm not mistaken, the basic algorithm followed by Deep Blue), but since this is a very simple game you probably don't need to take this approach.
A side note on abstraction
Note that all of these solutions are conceptualized as either numeric changes to or estimations of the utility function. This is, in general, preferable to arbitrarily throwing away possible policies. After all, that's what your utility function is for -- any time you make a policy decision on the basis of anything except the numeric value of your utility, you are breaking your abstraction and making your code less robust.

minimax c++ implementation for tic tac toe

void generate_moves(int gameBoard[9], list<int> &moves)
{
for (int i = 0; i < 9; i++)
{
if (gameBoard[i] == 0){
moves.push_back(i);
}
}
}
int evaluate_position(int gameBoard[9], int playerTurn)
{
state currentGameState = checkWin(gameBoard);
if (currentGameState != PLAYING)
{
if ((playerTurn == 1 && currentGameState == XWIN) || (playerTurn == -1 && currentGameState == OWIN))
return +infinity;
else if ((playerTurn == -1 && currentGameState == XWIN) || (playerTurn == 1 && currentGameState == OWIN))
return -infinity;
else if (currentGameState == DRAW)
return 0;
}
return -1;
}
int MinMove(int gameBoard[9], int playerTurn)
{
//if (checkWin(gameBoard) != PLAYING) { return evaluate_board(gameBoard); };
int pos_val = evaluate_position(gameBoard, playerTurn);
if (pos_val != -1) return pos_val;
int bestScore = +infinity;
list<int> movesList;
generate_moves(gameBoard, movesList);
while (!movesList.empty())
{
gameBoard[movesList.front()] = playerTurn;
int score = MaxMove(gameBoard, playerTurn*-1);
if (score < bestScore)
{
bestScore = score;
}
gameBoard[movesList.front()] = 0;
movesList.pop_front();
}
return bestScore;
}
int MaxMove(int gameBoard[9], int playerTurn)
{
//if (checkWin(gameBoard) != PLAYING) { return evaluate_board(gameBoard); };
int pos_val = evaluate_position(gameBoard, playerTurn);
if (pos_val != -1) return pos_val;
int bestScore = -infinity;
list<int> movesList;
generate_moves(gameBoard, movesList);
while (!movesList.empty())
{
gameBoard[movesList.front()] = playerTurn;
int score = MinMove(gameBoard, playerTurn*-1);
if (score > bestScore)
{
bestScore = score;
}
gameBoard[movesList.front()] = 0;
movesList.pop_front();
}
return bestScore;
}
int MiniMax(int gameBoard[9], int playerTurn)
{
int bestScore = -infinity;
int index = 0;
list<int> movesList;
vector<int> bestMoves;
generate_moves(gameBoard, movesList);
while (!movesList.empty())
{
gameBoard[movesList.front()] = playerTurn;
int score = MinMove(gameBoard, playerTurn);
if (score > bestScore)
{
bestScore = score;
bestMoves.clear();
bestMoves.push_back(movesList.front());
}
else if (score == bestScore)
{
bestMoves.push_back(movesList.front());
}
gameBoard[movesList.front()] = 0;
movesList.pop_front();
}
index = bestMoves.size();
if (index > 0) {
time_t secs;
time(&secs);
srand((uint32_t)secs);
index = rand() % index;
}
return bestMoves[index];
}
I created a tic tac toe program in C++ and tried to implement a MiniMax algorithm with exhaustive search tree.
These are the functions I have written using wiki and with the help of some websites. But the AI just doesn't work right and at times doesn't play its turn at all.
Could someone have a look and please point out if there is anything wrong with the logic?
This is how I think it works:
Minimax : This function starts with very large -ve number as best score and goal is to maximize that number. It calls minMove function. If new score > best score, then best score = new score...
MinMove : This function evaluates game board. If game over then it returns -infinity or +infinity depending on who won. If game is going on this function starts with max +infinity value as best score and goal is to minimize it as much possible. It calls MaxMove with opponent player's turn. (since players alternate turns).
If score < best score then best score = score. ...
MaxMove : This function evaluates game board. If game over then it returns -infinity or +infinity depending on who won. If game is going on this function starts with least -infinity value as best score and goal is to maximize it as much possible. It calls MinMove with opponent player's turn. (since players alternate turns).
If score > best score then best score = score. ...
Minmove and MaxMove call each other mutually recursively, MaxMove maximizing the value and MinMove minimizing it. Finally it returns the best possible moves list.
If there are more than 1 best moves, then a random of them is picked as the computer's move.
In MiniMax, MinMove(gameBoard, playerTurn) should be MinMove(gameBoard, -playerTurn) as you do in MaxMove.
As you use MinMove and MaxMove, your evaluation function should be absolute. I mean +infinity for XWIN
and -infinity for OWIN. And so MinMove can only be use when player == -1 and MaxMove when player == 1, thus the parameter become useless. And so MiniMax can only be used by player == 1.
I have done some changes in your code and it works (https://ideone.com/Ihy1SR).