Welcome to Dream.In.Code
Getting C++ Help is Easy!

Join 136,417 C++ Programmers for FREE! Get instant access to thousands of C++ experts, tutorials, code snippets, and more! There are 2,357 people online right now. Registration is fast and FREE... Join Now!




tic-tac-toe

 
Reply to this topicStart new topic

tic-tac-toe

avinandanaot
15 Oct, 2008 - 01:28 AM
Post #1

New D.I.C Head
*

Joined: 14 Oct, 2008
Posts: 13


My Contributions
cpp
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>

#include <assert.h>

/* A noughts-and-crosses board can be thought of as a bitmap,
* with each square representing one bit. We can define the
* values of the squares, then, as successive powers of 2,
* with an empty square having a value of 0.
*/

#define SPACE 0
#define TOP_LEFT 1
#define TOP_CENTRE 2
#define TOP_RIGHT 4
#define CENTRE_LEFT 8
#define CENTRE_CENTRE 16
#define CENTRE_RIGHT 32
#define BOTTOM_LEFT 64
#define BOTTOM_CENTRE 128
#define BOTTOM_RIGHT 256

/* To analyse a position, we simply add (or OR) together the
* values of all the squares occupied by a particular
* player. Now, there are eight winning lines, which can
* be represented by the following OR operations.
*/

#define TOP_ROW (TOP_LEFT | TOP_CENTRE | TOP_RIGHT)
#define CENTRE_ROW (CENTRE_LEFT | CENTRE_CENTRE | CENTRE_RIGHT)
#define BOTTOM_ROW (BOTTOM_LEFT | BOTTOM_CENTRE | BOTTOM_RIGHT)

#define LEFT_COL (TOP_LEFT | CENTRE_LEFT | BOTTOM_LEFT)
#define CENTRE_COL (TOP_CENTRE | CENTRE_CENTRE | BOTTOM_CENTRE)
#define RIGHT_COL (TOP_RIGHT | CENTRE_RIGHT | BOTTOM_RIGHT)

#define TLBR_DIAG (TOP_LEFT | CENTRE_CENTRE | BOTTOM_RIGHT)
#define BLTR_DIAG (BOTTOM_LEFT | CENTRE_CENTRE | TOP_RIGHT)

/* To determine whether a player has a win, we can just
* AND his score with each of these winning lines in turn.
* If any of them is equal to the value of the winning
* line after ANDing, then that player has a win. Clearly
* we must do this in strict move-history order! Otherwise,
* we could end up with two winners in one game, which
* wouldn't do at all.
*/


/* Let's start off with a banner */
void ShowBanner(void)
{
char *msg[] =
{
"",
" ***********************",
" * *",
" * NOUGHTS AND CROSSES *",
" * *",
" * a.k.a. Tic-Tac-Toe *",
" * *",
" ***********************",
"",
""
};
size_t n = sizeof msg / sizeof msg[0];
size_t i = 0;
while(i < n)
{
printf("%s\n", msg[i++]);
}
}

/* YesNo() asks a question that can be answered yes or no, and
* returns 0 if the first character of the answer is y or Y. If any
* other character is used, the function returns 1. In the event of
* the user bombing out by signalling end-of-input, *stop is set to 1.
*/
int YesNo(const char *prompt, int *stop)
{
int rc = 1; /* 0 = yes, 1 = no */
char answer[8] = {0};
int dummy = printf("%s\n", prompt);
char *result = fgets(answer, sizeof answer, stdin);
if(result != NULL)
{
if(toupper((unsigned char)answer[0]) == 'Y')
{
rc = 0;
}
}
else
{
*stop = 1;
}
return rc;
}

/* ScoreGame() returns 0 if the board does not
* currently contain a win. Otherwise, it returns
* the turn number on which the win occurred.
* A result of 4 or 6 or 8 therefore indicates a
* win for the first player, whereas a result
* of 5 or 7 indicates a win for the second player.
*/
int ScoreGame(int *MoveHistory)
{
int result = 0;
int i = 0;
unsigned long FirstPlayer = 0;
unsigned long SecondPlayer = 0;

for(i = 0; 0 == result && i < 9; i++)
{
if(i % 2 == 0)
{
FirstPlayer |= MoveHistory[i];
if(((FirstPlayer & TOP_ROW) == TOP_ROW) ||
((FirstPlayer & CENTRE_ROW) == CENTRE_ROW) ||
((FirstPlayer & BOTTOM_ROW) == BOTTOM_ROW) ||
((FirstPlayer & LEFT_COL) == LEFT_COL) ||
((FirstPlayer & CENTRE_COL) == CENTRE_COL) ||
((FirstPlayer & RIGHT_COL) == RIGHT_COL) ||
((FirstPlayer & TLBR_DIAG) == TLBR_DIAG) ||
((FirstPlayer & BLTR_DIAG) == BLTR_DIAG))
{
result = i;
}
}
else
{
SecondPlayer |= MoveHistory[i];
if(((SecondPlayer & TOP_ROW) == TOP_ROW) ||
((SecondPlayer & CENTRE_ROW) == CENTRE_ROW) ||
((SecondPlayer & BOTTOM_ROW) == BOTTOM_ROW) ||
((SecondPlayer & LEFT_COL) == LEFT_COL) ||
((SecondPlayer & CENTRE_COL) == CENTRE_COL) ||
((SecondPlayer & RIGHT_COL) == RIGHT_COL) ||
((SecondPlayer & TLBR_DIAG) == TLBR_DIAG) ||
((SecondPlayer & BLTR_DIAG) == BLTR_DIAG))
{
result = i;
}
}
}

return result;
}


/* A legal move is one which has not yet been made. */
int GetLegalMoves(int *LegalMoves, int *MoveHistory)
{
int i = 0;
int j = 0;

unsigned long MovesAlreadyMade = 0;
unsigned long ThisMove = 0;

for(i = 0; i < 9; i++)
{
MovesAlreadyMade |= MoveHistory[i];
LegalMoves[i] = 0;
}

for(i = 0, j = 0; i < 9; i++)
{
ThisMove = 1 << i;
if((ThisMove & MovesAlreadyMade) != ThisMove)
{
LegalMoves[j] = ThisMove;
++j;
}
}

return j; /* number of legal moves */
}

int GetPlayerMove(int *MoveHistory, int *stop)
{
int LegalMoveArray[9] = {0};
int LegalMoves = GetLegalMoves(LegalMoveArray, MoveHistory);
int Move = 0;
int done = 0;

char Answer[8] = {0};
char *result = NULL;

char LegalResponses[10] = {0};

int i = 0;

while(i < LegalMoves)
{
switch(LegalMoveArray[i])
{
case 0:
break;
case TOP_LEFT:
LegalResponses[i] = 'A';
break;
case TOP_CENTRE:
LegalResponses[i] = 'B';
break;
case TOP_RIGHT:
LegalResponses[i] = 'C';
break;
case CENTRE_LEFT:
LegalResponses[i] = 'D';
break;
case CENTRE_CENTRE:
LegalResponses[i] = 'E';
break;
case CENTRE_RIGHT:
LegalResponses[i] = 'F';
break;
case BOTTOM_LEFT:
LegalResponses[i] = 'G';
break;
case BOTTOM_CENTRE:
LegalResponses[i] = 'H';
break;
case BOTTOM_RIGHT:
LegalResponses[i] = 'I';
break;
default:
printf("Illegal legal move! :-%%\n");
assert(0);
break;
}

++i;
}

printf("Where do you want to move?\n");

do
{
result = fgets(Answer, sizeof Answer, stdin);
if(result != NULL)
{
int firstchar = toupper((unsigned char)Answer[0]);
char *legit = strchr(LegalResponses, firstchar);

if(legit != NULL)
{
Move = legit - LegalResponses;
done = 1;
}
else
{
printf("Sorry, you must choose from %s\n", LegalResponses);
printf("Please re-try.\n");
}
}
else
{
*stop = 1;
}
} while(!*stop && !done);

return LegalMoveArray[Move];
}

/* The move history is in terms of bitmaps, but sometimes
* we need to know the board position pertaining to a given
* bitmap. Here's a function to give us that information.
*/
int BitmapToPosition(int bitmap)
{
int pos = 0;

while(bitmap > 1)
{
bitmap >>= 1;
++pos;
}

return pos;
}

/* Such intelligence as the game has is rooted in this function, which
* is a fairly straight recursive trawl through the remaining lines of
* play. Regrettably, this doesn't always give us the best strategy!
*/
void SearchForBestMove(int *Moves,
size_t n,
size_t unchanged,
long *Score,
size_t HowFarIntoGame,
int WhichPlayerAmI)
{
size_t outer = 0;
size_t inner = 0;
int temp = 0;

if(unchanged < n)
{
for(outer = unchanged; outer < n; outer++)
{
temp = Moves[outer];
for(inner = outer; inner > unchanged; inner--)
{
Moves[inner] = Moves[inner - 1];
}
Moves[unchanged] = temp;
SearchForBestMove(Moves,
n,
unchanged + 1,
Score,
HowFarIntoGame,
WhichPlayerAmI);

for(inner = unchanged; inner < outer; inner++)
{
Moves[inner] = Moves[inner + 1];
}
Moves[outer] = temp;
}
}
else
{
int Result = ScoreGame(Moves);
if(0 == Result)
{
/* draw - do nothing */
}
else if((Result % 2) == WhichPlayerAmI)
{
Score[BitmapToPosition(Moves[HowFarIntoGame])] += 9 - Result;
}
else
{
Score[BitmapToPosition(Moves[HowFarIntoGame])] -= 9 - Result;
}
}
}

int GetComputerMove(int *MoveHistory, int WhoAmI, int Nobbled)
{
int LegalMoveArray[9] = {0};
int LegalMoves = GetLegalMoves(LegalMoveArray, MoveHistory);
long Scores[9] = {0};

int BoardCopy[9] = {0};
size_t MovesSoFar = 0;
int i = 0;
int Move = 0;
int BestMove = 0;
int BestScore = 0;
int ScoresOffset = 0;

if(Nobbled)
{
Move = LegalMoveArray[rand() % LegalMoves];
}
else
{
/* Copy the move history into a safe place */
while(MoveHistory[MovesSoFar] != 0 && MovesSoFar < 9)
{
BoardCopy[MovesSoFar] = MoveHistory[MovesSoFar];
MovesSoFar++;
}

/* Append the remaining legal moves to the safe copy */
i = MovesSoFar;
while(i < 9)
{
BoardCopy[i] = LegalMoveArray[i - MovesSoFar];
i++;
}

SearchForBestMove(BoardCopy, 9, MovesSoFar, Scores, MovesSoFar, WhoAmI);

BestMove = 0;
BestScore = Scores[BitmapToPosition(LegalMoveArray[BestMove])];

for(i = 1; i < LegalMoves; i++)
{
ScoresOffset = BitmapToPosition(LegalMoveArray[i]);

if(Scores[ScoresOffset] > BestScore)
{
BestMove = i;
BestScore = Scores[BitmapToPosition(LegalMoveArray[BestMove])];
}
}

Move = LegalMoveArray[BestMove];
}

return Move;
}


void ClearBoard(int *MoveHistory)
{
int i = 0;
while(i < 9)
{
MoveHistory[i++] = 0;
}
}

/* DisplayBoard() displ... well! Anyway, SymbolSet
* must be a pointer to a string beginning with
* either "OX" or "XO". The first of the two symbols
* is used for Player 1.
*/
void DisplayBoard(int *MoveHistory, char* SymbolSet)
{
char Display[3][3] =
{
"ABC",
"DEF",
"GHI"
};
int i = 0;

for(i = 0; i < 9; i++)
{
switch(MoveHistory[i])
{
case 0:
break;
case TOP_LEFT:
Display[0][0] = SymbolSet[i % 2];
break;
case TOP_CENTRE:
Display[0][1] = SymbolSet[i % 2];
break;
case TOP_RIGHT:
Display[0][2] = SymbolSet[i % 2];
break;
case CENTRE_LEFT:
Display[1][0] = SymbolSet[i % 2];
break;
case CENTRE_CENTRE:
Display[1][1] = SymbolSet[i % 2];
break;
case CENTRE_RIGHT:
Display[1][2] = SymbolSet[i % 2];
break;
case BOTTOM_LEFT:
Display[2][0] = SymbolSet[i % 2];
break;
case BOTTOM_CENTRE:
Display[2][1] = SymbolSet[i % 2];
break;
case BOTTOM_RIGHT:
Display[2][2] = SymbolSet[i % 2];
break;
default:
printf("ERROR! Move %d is illegal.\n", MoveHistory[i]);
assert(0); /* program bug! */
break;
}
}

printf(" %c | %c | %c\n", Display[0][0], Display[0][1], Display[0][2]);
printf("---+---+---\n");
printf(" %c | %c | %c\n", Display[1][0], Display[1][1], Display[1][2]);
printf("---+---+---\n");
printf(" %c | %c | %c\n", Display[2][0], Display[2][1], Display[2][2]);

return;
}

int main(void)
{
int done = 0; /* Player has had enough */
int stop = 0; /* EOF detected */
int PlayerTurn = 0;
int PlayerSymbol = 0;
int GameOver = 0;
int Turn = 0;
int Move = 0;
char Player1IsNoughts[] = "OX";
char Player2IsNoughts[] = "XO";
char *Symbols = NULL;
int Winner = 0;

/* Set Nobbled to 1 if you want to turn off what little intelligence
* the game has. If Nobbled is set, the game will play (pseudo)randomly.
*/
int Nobbled = 0;

int MoveHistory[9] = {0};

ShowBanner();

do
{
PlayerTurn = YesNo("Do you want to go first?", &stop);

if(!stop)
{
PlayerSymbol = YesNo("Do you want to be Noughts (Y/N)?", &stop);
}
if(!stop)
{
GameOver = 0;
Turn = 0;
ClearBoard(MoveHistory);

if(0 == PlayerTurn)
{
if(0 == PlayerSymbol)
{
Symbols = Player1IsNoughts;
}
else
{
Symbols = Player2IsNoughts;
}
}
else
{
if(0 == PlayerSymbol)
{
Symbols = Player2IsNoughts;
}
else
{
Symbols = Player1IsNoughts;
}
}

while(!GameOver)
{
printf("TURN %d\n", Turn);

DisplayBoard(MoveHistory, Symbols);

if(Turn % 2 == PlayerTurn)
{
Move = GetPlayerMove(MoveHistory, &stop);
}
else
{
Move = GetComputerMove(MoveHistory, !PlayerTurn, Nobbled);
}

if(!stop)
{
MoveHistory[Turn] = Move;
}

Winner = ScoreGame(MoveHistory);

if(stop || 8 == Turn || Winner != 0)
{
GameOver = 1;
}

++Turn;
}
DisplayBoard(MoveHistory, Symbols);
printf("\nGame Over:");
if(0 == Winner)
{
printf(" Draw.\n");
}
else if(Winner % 2 == PlayerTurn)
{
printf("Human player wins!\n");
}
else
{
printf("Computer wins.\n");
}
}

if(!stop)
{
done = YesNo("Do you want to play again? (Y/N)", &stop);
}
} while(!stop && !done);

if(stop)
{
fprintf(stderr, "Unexpected end of file. Actually, I did\n");
fprintf(stderr, "kind of half-expect it, didn't I? :-)\n");
}

return 0;
}


** Edit ** code.gif
User is offlineProfile CardPM
+Quote Post

no2pencil
RE: Tic-tac-toe
15 Oct, 2008 - 01:29 AM
Post #2

My fridge be runnin OH NOEZ!
Group Icon

Joined: 10 May, 2007
Posts: 6,462



Thanked: 66 times
Dream Kudos: 2425
Expert In: Goofing Off

My Contributions
...& you've posted this code because :
User is online!Profile CardPM
+Quote Post

AmitTheInfinity
RE: Tic-tac-toe
15 Oct, 2008 - 01:38 AM
Post #3

C Surfing ∞
Group Icon

Joined: 25 Jan, 2007
Posts: 1,025



Thanked: 35 times
Dream Kudos: 125
My Contributions
QUOTE(no2pencil @ 15 Oct, 2008 - 02:59 PM) *

...& you've posted this code because :


hmmm, Let me guess...

It's not working and OP doesn't know why.

But I can' understand, why OP doesn't know why code is not working? blink.gif
User is offlineProfile CardPM
+Quote Post

no2pencil
RE: Tic-tac-toe
15 Oct, 2008 - 01:40 AM
Post #4

My fridge be runnin OH NOEZ!
Group Icon

Joined: 10 May, 2007
Posts: 6,462



Thanked: 66 times
Dream Kudos: 2425
Expert In: Goofing Off

My Contributions
The world may never know.
User is online!Profile CardPM
+Quote Post

baavgai
RE: Tic-tac-toe
15 Oct, 2008 - 03:26 AM
Post #5

Dreaming Coder
Group Icon

Joined: 16 Oct, 2007
Posts: 2,024



Thanked: 105 times
Dream Kudos: 475
Expert In: C, C++, Java, C#, ASP.NET, PHP, Perl, Python, Oracle, SQL Server, MySql, HTML, JavaScript, Lua

My Contributions
Using bit masks to represent the entire board in an int is amusing. However, they don't really appear to be taking advantage of it, or perhaps understand it.

I suspect the bulk of the skeleton was provided, with the student expected to figure out the rest.

Kind of wonder what the question is now. tongue.gif

User is online!Profile CardPM
+Quote Post

Reply to this topicStart new topic
Time is now: 12/2/08 12:57PM

Live C++ Help!

C++ Tutorials

Reference Sheets

C++ Snippets

DIC Chatroom

Bye Bye Ads

Monthly Drawing

Thumb Drive

Top Contributors

Top 10 Kudos This Month