Thread: Tic-tac-toe implementation problems

  1. #1
    Registered User
    Join Date
    Mar 2007
    Posts
    43

    Tic-tac-toe implementation problems

    Here is my problem I'm supposed to write a tic-tac-toe game.By the way this is the biggest program I've ever attempted to write. When I started writing the game I thought that it wouldn't be too difficult to do-boy was I wrong. The specs for the game are that a human plays the computer (random number generator). The human player chooses to be X or O,X moves first. Right now all the game does is prompt the user to choose X or O. After that is done I can play as X or O only. I can fill the game board with X's or O's (run the code thus far to see what I'm talking about). This is what I need to add to the game.
    • A way to store a previous move choice and not allow a move to be over written by a subsequent move
    • A call to the random number generator to get the computer's move


    As for Item one I think that I need to use two arrays to solve this problem:
    One is the board[] array that is the actual game board and I think that the other array I need is a copy of the board[] array to store move choices. Lets say I name this array- full[]. The fulll[] array holds the actual move choices. I think that I need to continually search the full array (using a while loop) to find move choices and to prompt the user that they can't pick that spot. For example if the human player chooses number 5 and puts an X in there the computer can not put an O in the same place and the human player cant put another X in space 5. As for problem two I just need to write a function to call a random number generator. Someone point me in the right direction. I don't want anyone to do it for me,but I do need some help. Here is my code to this point:

    Code:
    #include<stdio.h>
    
    int getXorO(void);
    void drawBoard(void);
    int human(int x,int board[]);
    int computer(int o,int board[]);
    int play(int x,char board[]);
    
    
    
    int main(void){
        
        int initialChoice;
        char board[9]= {'1','2','3','4','5','6','7','8','9'};
        int humanMove;
        int computerMove;
        
        initialChoice = getXorO();
        drawBoard();
        play(initialChoice,board);
        
        
        
        return 0;
    }
    int getXorO(void){//player decides to be X or O here and returns the initialChoice to main
        
        
        int initialChoice;
        
        
        printf("Let's play TIC-TAC-TOE!\n\n");
        printf("Do you want to be X or O (X moves first)? ");
        printf("\nEnter 1 for X and 2 for O: ");
       // scanf("%d",&initialChoice);
        
        if(scanf("%i",&initialChoice)!=1){
                printf("\nInvalid input,exiting game \n");
                exit(1);
            }
        
        while(initialChoice!=1 && initialChoice!=2){
            
             printf("Do you want to be X or O (X moves first)? \n");
             printf("Enter 1 for X and 2 for O:");
             if(scanf("%i",&initialChoice)!=1){
                 printf("\nInvalid input,exiting game\n");
                 exit(1);
             }
              }
        return initialChoice;
        
    }//getMove returns initialChoice to be X or O
    
    void drawBoard(void){
        char board[9]= {'1','2','3','4','5','6','7','8','9'};
        
        printf("\n");
        printf("\t %c | %c | %c \n",board[0],board[1],board[2]);
        printf("\t---|---|--- \n");
        printf("\t %c | %c | %c \n",board[3],board[4],board[5]);
        printf("\t---|---|--- \n");
        printf("\t %c | %c | %c \n\n",board[6],board[7],board[8]);
        
        return;
    }
    int play(int initialChoice,char board[]){
        int i;
        int humanMove;
        int computerMove;
        char X;
        int choice;
        char full[9] = {'1','2','3','4','5','6','7','8','9'};
        
        if(initialChoice==1)
            initialChoice = 88;//ascii for X/set player as X
        else
            initialChoice = 79;//ascii for O/set player as O
            
        printf("Enter the number of an available space you are,%c: ",initialChoice);
                                                 
        scanf("%d",&choice);
        
        for(i=0;i<9;i++){
           
            board[choice-1]= 88;
            
            while(board[choice]==88)
                printf("\nError");
        
        
        printf("\n");
        printf("\t %c | %c | %c \n",board[0],board[1],board[2]);
        printf("\t---|---|--- \n");
        printf("\t %c | %c | %c \n",board[3],board[4],board[5]);
        printf("\t---|---|--- \n");
        printf("\t %c | %c | %c \n\n",board[6],board[7],board[8]);
        
        printf("Enter the number of an available space you are,%c: "
                                                      "",initialChoice);
        //should call the computer here
        scanf("%d",&choice);
        }
        
        
        return 0;
    }
    Last edited by mesmer; 10-25-2008 at 10:39 AM.

  2. #2
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    You can either "remember where you put your move", or basically make a copy of the current board and make the play on this temporary copy.

    For the computer to have a decent chance of winning, you probably want to make it a bit clever, and not base it's moves on randomness (because humans will beat it every time in that case).

    I wrote a computer strategy not so long ago that was essentially:
    1. Try all moves that can be made, and see if it's a win.
    2. Block any move that the other player may win from.
    3. Take the middle position.
    4. Take a corner position (here I do use random numbers to allow the play to be less predictable).
    5. Otherwise take a random free point.

    I had to actually make it "player friendly" so that it's not always taking a corner in the 4th rule, otherwise I'd never win (either computer wins or it's a draw). I used a formula to use statistics of the win/loose/draw proportions to determine how likely it should be that teh computer would take a corner or not. If the random number was less than the proportion of computer wins + draws, then it would not take a corner. That way, depending on how good the opponent is, it will adjust the difficulty.

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  3. #3
    Registered User
    Join Date
    Mar 2007
    Posts
    43
    Quote Originally Posted by matsp View Post
    You can either "remember where you put your move", or basically make a copy of the current board and make the play on this temporary copy.

    For the computer to have a decent chance of winning, you probably want to make it a bit clever, and not base it's moves on randomness (because humans will beat it every time in that case).

    I wrote a computer strategy not so long ago that was essentially:
    1. Try all moves that can be made, and see if it's a win.
    2. Block any move that the other player may win from.
    3. Take the middle position.
    4. Take a corner position (here I do use random numbers to allow the play to be less predictable).
    5. Otherwise take a random free point.

    I had to actually make it "player friendly" so that it's not always taking a corner in the 4th rule, otherwise I'd never win (either computer wins or it's a draw). I used a formula to use statistics of the win/loose/draw proportions to determine how likely it should be that teh computer would take a corner or not. If the random number was less than the proportion of computer wins + draws, then it would not take a corner. That way, depending on how good the opponent is, it will adjust the difficulty.

    --
    Mats
    Thanks for the reply,but you are waaaayyy ahead of me. Right now I just want to get a working game. Any pointers in that direction? Thanks m8.

  4. #4
    Hacker MeTh0Dz's Avatar
    Join Date
    Oct 2008
    Posts
    111
    In order to 'remember' what moves have been made or the like. Just make an array of the nine places on the board. And if the position hasn't been taken yet put a 0 in it, if it has been taken by an X put a 1 in it, and if it's been taken by a 0 put a 2 in it.

    This will allow you to check if a spot is filled or if someone has won.

  5. #5
    Registered User
    Join Date
    Jan 2008
    Posts
    225
    For tic tac toe you can write many if conditions to determine the computer's best move and solve the problem. But i would advise you to go for minimax algorithm which can be very useful even if you plan to make some other game like othello etc in which you can see the opponent's move. Refer the below given link

    http://ce.sharif.edu/~ahmadinejad/gametree/

  6. #6
    spurious conceit MK27's Avatar
    Join Date
    Jul 2008
    Location
    segmentation fault
    Posts
    8,300
    Some thoughts:

    You should make the "drawBoard" function reusable and either have it affect a global array or pass it an array representing the current state of the board -- "full". You do not need to bother with putting the square numbers in this array, since they are always equal to the element number plus one anyway. Instead, occupy it with three possibilities -- one for free, one for X, and one for O.

    If you are sticking to the random move strategy until you get everything working, use to the contents of the "board" or "full" array to form an array of valid choices for computer.

    Code:
    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    
    int main() {	      //123456789	
    	char board[10]="  x o  x ", full[10];
    	int i, n=0, move;
    	for (i=0;i<9;i++) {
    		if (board[i]==' ') {
    			full[n]=i+1;
    			n++;
    		}
    	}
    	full[n]='\0';
            printf("Possible choices: ");
    	for (i=0;i<strlen(full);i++) printf("&#37;d ",full[i]);
    	move=rand() % strlen(full);
    	printf("move: %d\n", full[move]);	
    }
    Unfortunately because of the way rand() works this will always make the same choice, but hopefully the logic is clear.

    [edit] sorry, maybe I shouldn't have called the array "full" as it serves a different purpose than yours.
    Last edited by MK27; 10-25-2008 at 12:14 PM.
    C programming resources:
    GNU C Function and Macro Index -- glibc reference manual
    The C Book -- nice online learner guide
    Current ISO draft standard
    CCAN -- new CPAN like open source library repository
    3 (different) GNU debugger tutorials: #1 -- #2 -- #3
    cpwiki -- our wiki on sourceforge

  7. #7
    Registered User
    Join Date
    Sep 2006
    Posts
    8,868
    I haven't run your program, but this is usually how TTT works:

    Code:
    Initialize() all variables.
    
    1) draw the board
    
    Before a move is made, check if the game is already over from the last move. 
    IsWin(), IsLose(), or a Cat's game. (total moves from both players can't be greater 
    than 9 before the game is over.
    
    If the game is over, announce the winner and give a "Do you want to play again?"
    prompt. 
       If so, 
           Initialize() a new game board and reset the variables.
       else
          Goodby() messages
    
    2) If the game is not over, 
    
            if it's the computers turn to play, 
                find an empty sqr in the board and have the computer make a move 
                (random or otherwise)
    
            else
                 if it's the human's turn to play, give him a prompt to input his move.
    
    3) Check if the move made IsLegal() and IsGood(). 
    
    If it's legal, AND if it's good (it doesn't allow the opponent to win with his 
    next move), 
       then let the move, stay put. 
    
    else, 
       undo the move, and return to #2. 
       (note that no board has been drawn yet, so the move was not "really madel", yet.)
    
    Return to #1
    You may very well want to use a copy of the game board to undo a move. You do not need to have an array of all possible moves.

    You should have logic in IsLegal() saying that
    if a sqr is not empty,
    no move can be made to that sqr.

    For random number generation, just google it: C random number.
    Last edited by Adak; 10-25-2008 at 08:35 PM.

  8. #8
    Registered User
    Join Date
    Mar 2007
    Posts
    43
    Thanks for the help guys. I'm trying to solve the problem of stopping the user form choosing an already taken space by using swith(choice -1). The problem I'm having is that after the first choice the case statements are being executed when they should not. Here is my updated code:Note, I'm just trying to get the board working for a user choosing to be X. what is wrong with my switch statements, why are they running when they should not?
    Code:
    #include<stdio.h>
    
    int getXorO(void);
    void drawBoard(void);
    int human(int x,int board[]);
    int computer(int o,int board[]);
    int play(int x,char board[]);
    
    
    
    int main(void){
        
        int initialChoice;
        char board[9]= {'1','2','3','4','5','6','7','8','9'};
        int humanMove;
        int computerMove;
        
        initialChoice = getXorO();
        drawBoard();
        play(initialChoice,board);
        
        
        
        return 0;
    }
    int getXorO(void){//player decides to be X or O here and returns the initialChoice to main
        
        
        int initialChoice;
        
        
        printf("Let's play TIC-TAC-TOE!\n\n");
        printf("Do you want to be X or O (X moves first)? ");
        printf("\nEnter 1 for X and 2 for O: ");
       // scanf("%d",&initialChoice);
        
        if(scanf("%i",&initialChoice)!=1){
                printf("\nInvalid input,exiting game \n");
                exit(1);
            }
        
        while(initialChoice!=1 && initialChoice!=2){
            
             printf("Do you want to be X or O (X moves first)? \n");
             printf("Enter 1 for X and 2 for O:");
             if(scanf("%i",&initialChoice)!=1){
                 printf("\nInvalid input,exiting game\n");
                 exit(1);
             }
              }
        return initialChoice;
        
    }//getMove returns initialChoice to be X or O
    
    void drawBoard(void){
        char board[9]= {'1','2','3','4','5','6','7','8','9'};
        
        printf("\n");
        printf("\t %c | %c | %c \n",board[0],board[1],board[2]);
        printf("\t---|---|--- \n");
        printf("\t %c | %c | %c \n",board[3],board[4],board[5]);
        printf("\t---|---|--- \n");
        printf("\t %c | %c | %c \n\n",board[6],board[7],board[8]);
        
        return;
    }
    int play(int initialChoice,char board[]){
        int i;
        int humanMove;
        int computerMove;
        int choice;
        char full[9] ={0};
        char X,O;
        
        if(initialChoice==1)
            initialChoice = 88;//ascii for X/set player as X
        else
            initialChoice = 79;//ascii for O/set player as O
            
        printf("Enter the number of an available space you are,%c: ",initialChoice);
                                                 
        scanf("%d",&choice);
        
        for(i=0;i<9;i++){
            
            board[choice-1]= 'X';
        
        
        printf("\n");
        printf("\t %c | %c | %c \n",board[0],board[1],board[2]);
        printf("\t---|---|--- \n");
        printf("\t %c | %c | %c \n",board[3],board[4],board[5]);
        printf("\t---|---|--- \n");
        printf("\t %c | %c | %c \n\n",board[6],board[7],board[8]);
        
        printf("Enter the number of an available space you are,%c: "
                                                      "",initialChoice);
        //should call the computer here
        scanf("%d",&choice);
        
        switch(choice-1){
            case 8:if(board[8]=='X');
            printf("That space is taken,choose anohter");
            break;
            case 7: if(board[7]=='X');
            printf("That space is taken,choose another");
            break;
            case 6:if(board[6]=='X');
            printf("That space is taken,choose another");
            break;
            case 5:if(board[6]=='X');
            printf("That space is taken,choose another");
            break;
            case 4:if(board[6]=='X');
            printf("That space is taken,choose another");
            break;
            case 3:if(board[6]=='X');
            printf("That space is taken,choose another");
            break;
            case 2:if(board[6]=='X');
            printf("That space is taken,choose another");
            break;
            case 1:if(board[6]=='X');
            printf("That space is taken,choose another");
            break;
            case 0:if(board[6]=='X');
            printf("That space is taken,choose another");
            break;
                    
        }
        
            
        }
        
        return 0;
    }

  9. #9
    and the Hat of Guessing tabstop's Avatar
    Join Date
    Nov 2007
    Posts
    14,336
    Obviously you got a little carried away with the cut and paste there, as they should check the different spots. (That should have been your hint that all you needed to do was
    Code:
    if (board[choice-1]=='X')
    . Also, why don't you check for 'O'?)

  10. #10
    Registered User
    Join Date
    Mar 2007
    Posts
    43
    Quote Originally Posted by tabstop View Post
    Obviously you got a little carried away with the cut and paste there, as they should check the different spots. (That should have been your hint that all you needed to do was
    Code:
    if (board[choice-1]=='X')
    . Also, why don't you check for 'O'?)
    I just noticed that and went back and changed the cases. I did get carried away with the cut and paste,but It still does not work properly
    Code:
    #include<stdio.h>
    
    int getXorO(void);
    void drawBoard(void);
    int human(int x,int board[]);
    int computer(int o,int board[]);
    int play(int x,char board[]);
    
    
    
    int main(void){
        
        int initialChoice;
        char board[9]= {'1','2','3','4','5','6','7','8','9'};
        int humanMove;
        int computerMove;
        
        initialChoice = getXorO();
        drawBoard();
        play(initialChoice,board);
        
        
        
        return 0;
    }
    int getXorO(void){//player decides to be X or O here and returns the initialChoice to main
        
        
        int initialChoice;
        
        
        printf("Let's play TIC-TAC-TOE!\n\n");
        printf("Do you want to be X or O (X moves first)? ");
        printf("\nEnter 1 for X and 2 for O: ");
       // scanf("&#37;d",&initialChoice);
        
        if(scanf("%i",&initialChoice)!=1){
                printf("\nInvalid input,exiting game \n");
                exit(1);
            }
        
        while(initialChoice!=1 && initialChoice!=2){
            
             printf("Do you want to be X or O (X moves first)? \n");
             printf("Enter 1 for X and 2 for O:");
             if(scanf("%i",&initialChoice)!=1){
                 printf("\nInvalid input,exiting game\n");
                 exit(1);
             }
              }
        return initialChoice;
        
    }//getMove returns initialChoice to be X or O
    
    void drawBoard(void){
        char board[9]= {'1','2','3','4','5','6','7','8','9'};
        
        printf("\n");
        printf("\t %c | %c | %c \n",board[0],board[1],board[2]);
        printf("\t---|---|--- \n");
        printf("\t %c | %c | %c \n",board[3],board[4],board[5]);
        printf("\t---|---|--- \n");
        printf("\t %c | %c | %c \n\n",board[6],board[7],board[8]);
        
        return;
    }
    int play(int initialChoice,char board[]){
        int i;
        int humanMove;
        int computerMove;
        int choice;
        char full[9] ={0};
        char X,O;
        
        if(initialChoice==1)
            initialChoice = 88;//ascii for X/set player as X
        else
            initialChoice = 79;//ascii for O/set player as O
            
        printf("Enter the number of an available space you are,%c: ",initialChoice);
                                                 
        scanf("%d",&choice);
        
        for(i=0;i<9;i++){
            
            board[choice-1]= 'X';
        
        
        printf("\n");
        printf("\t %c | %c | %c \n",board[0],board[1],board[2]);
        printf("\t---|---|--- \n");
        printf("\t %c | %c | %c \n",board[3],board[4],board[5]);
        printf("\t---|---|--- \n");
        printf("\t %c | %c | %c \n\n",board[6],board[7],board[8]);
        
        printf("Enter the number of an available space you are,%c: "
                                                      "",initialChoice);
        //should call the computer here
        scanf("%d",&choice);
        
        switch(choice-1){
            case 8:if(board[8]=='X');
            printf("That space is taken,choose anohter");
            break;
            case 7: if(board[7]=='X');
            printf("That space is taken,choose another");
            break;
            case 6:if(board[6]=='X');
            printf("That space is taken,choose another");
            break;
            case 5:if(board[5]=='X');
            printf("That space is taken,choose another");
            break;
            case 4:if(board[4]=='X');
            printf("That space is taken,choose another");
            break;
            case 3:if(board[3]=='X');
            printf("That space is taken,choose another");
            break;
            case 2:if(board[2]=='X');
            printf("That space is taken,choose another");
            break;
            case 1:if(board[1]=='X');
            printf("That space is taken,choose another");
            break;
            case 0:if(board[0]=='X');
            printf("That space is taken,choose another");
            break;
                    
        }
        
            
        }
        
        return 0;
    }
    Last edited by mesmer; 10-25-2008 at 09:55 PM.

  11. #11
    and the Hat of Guessing tabstop's Avatar
    Join Date
    Nov 2007
    Posts
    14,336
    Since you're doing two prompts and two inputs in the for loop, it will not check move numbers 1, 3, 5, 7, 9, 11, 13, 15, and 17, but will check move numbers 2, 4, 6, 8, 10, 12, 14, 16, and 18. You should have just one prompt and input inside the for loop (and of course, it should be a checked move).

  12. #12
    Registered User
    Join Date
    Mar 2007
    Posts
    43
    Quote Originally Posted by tabstop View Post
    Since you're doing two prompts and two inputs in the for loop, it will not check move numbers 1, 3, 5, 7, 9, 11, 13, 15, and 17, but will check move numbers 2, 4, 6, 8, 10, 12, 14, 16, and 18. You should have just one prompt and input inside the for loop (and of course, it should be a checked move).
    I only have one prompt and one input in the for loop. Am I missing something?

  13. #13
    and the Hat of Guessing tabstop's Avatar
    Join Date
    Nov 2007
    Posts
    14,336
    No, sorry, I missed that, I mis-indented your code. This time I compiled it:
    Code:
    temp.c:102: warning: empty body in an if-statement
    temp.c:105: warning: empty body in an if-statement
    temp.c:108: warning: empty body in an if-statement
    temp.c:111: warning: empty body in an if-statement
    temp.c:114: warning: empty body in an if-statement
    temp.c:117: warning: empty body in an if-statement
    temp.c:120: warning: empty body in an if-statement
    temp.c:123: warning: empty body in an if-statement
    temp.c:126: warning: empty body in an if-statement
    Maybe you have too many semi-colons after your if conditions.

  14. #14
    Registered User
    Join Date
    Mar 2007
    Posts
    43
    Quote Originally Posted by tabstop View Post
    No, sorry, I missed that, I mis-indented your code. This time I compiled it:
    Code:
    temp.c:102: warning: empty body in an if-statement
    temp.c:105: warning: empty body in an if-statement
    temp.c:108: warning: empty body in an if-statement
    temp.c:111: warning: empty body in an if-statement
    temp.c:114: warning: empty body in an if-statement
    temp.c:117: warning: empty body in an if-statement
    temp.c:120: warning: empty body in an if-statement
    temp.c:123: warning: empty body in an if-statement
    temp.c:126: warning: empty body in an if-statement
    Maybe you have too many semi-colons after your if conditions.
    Wow, that was it too many semi-colons. My compiler was not giving me the warning. I'm suprised that it even compiled. By the way what IDE/compiler are you using.
    I'm using netbeans/gcc.

  15. #15
    and the Hat of Guessing tabstop's Avatar
    Join Date
    Nov 2007
    Posts
    14,336
    gcc. Apparently you need to turn on warnings. -Wall is the flag (I don't know enough about netbeans to know if it has a handy little checkbox to do so, or if you have to type it in). Even without -Wall, I still get warnings about implicit declaration of exit() because you're missing <stdlib.h>.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. tic tac toe crashes :(
    By stien in forum Game Programming
    Replies: 4
    Last Post: 05-13-2007, 06:25 PM
  2. Tic Tac Toe... so close...
    By SlayerBlade in forum C Programming
    Replies: 14
    Last Post: 10-10-2005, 08:58 PM
  3. tic tac toe
    By holden in forum A Brief History of Cprogramming.com
    Replies: 8
    Last Post: 05-09-2004, 09:59 AM
  4. Tic tac toe game
    By Seth in forum C Programming
    Replies: 9
    Last Post: 05-04-2003, 09:21 AM
  5. problems with my tic tac toe game
    By Leeman_s in forum C++ Programming
    Replies: 4
    Last Post: 04-12-2002, 08:59 PM