试图在C中制作TIC-TAC-TOE游戏

发布于 2025-01-24 22:50:47 字数 1150 浏览 4 评论 0 原文

因此,我试图在C中制作一款TIC-TAC-TOE游戏,并且我已经写出了大部分代码,没有错误消息,但是我想知道要添加什么代码以使计算机也可以玩游戏。我只是不知道在这里还要添加​​什么,以确保游戏可以进行交互 - 不仅仅是用户输入一个字母并完成的用户。如果有人可以提供帮助,那将不胜感激!代码如下:

#include <time.h>
#include <stdlib.h>
#include <string.h>

int main(void) {
  system("clear");
  
  const char ROWS = 3;
  const char COLS = 3;

  int rows, cols, tile = 1, choice;

  for (rows = 0; rows < ROWS; rows++)
    {
      for (cols = 0; cols < COLS; cols++)
        {
          printf("[%d]", tile++);
        }
      printf("\n");
    }

  printf("\nWhere would you like to put your X: ");
  scanf("%d", &choice);
  tile = 1;

  for (rows = 0; rows < ROWS; rows++)
    {
        for (cols = 0; cols < COLS; cols++)
          {
            if (tile == 1 || tile == 2 || tile == 3 || tile == 4 || tile == 5 || tile == 6 || tile== 7 || tile == 8 || tile == 9)
            {
            printf("[%d]", tile++);
            }

            if (choice == tile)
            {
              tile -= tile;
              printf("[X]");
              choice += 1000;
            }
          }

      printf("\n");
    }
  
  return 0;
} 

So I am trying to make a tic-tac-toe game in C and I've gotten most of the code written out, no error messages, but I want to know what code to add to get the computer to play the game as well. I just don't know what else to add here to make sure the game is interact-able and not just the user typing in one letter and being done. If anyone could help, that'd be appreciated! The code is below:

#include <time.h>
#include <stdlib.h>
#include <string.h>

int main(void) {
  system("clear");
  
  const char ROWS = 3;
  const char COLS = 3;

  int rows, cols, tile = 1, choice;

  for (rows = 0; rows < ROWS; rows++)
    {
      for (cols = 0; cols < COLS; cols++)
        {
          printf("[%d]", tile++);
        }
      printf("\n");
    }

  printf("\nWhere would you like to put your X: ");
  scanf("%d", &choice);
  tile = 1;

  for (rows = 0; rows < ROWS; rows++)
    {
        for (cols = 0; cols < COLS; cols++)
          {
            if (tile == 1 || tile == 2 || tile == 3 || tile == 4 || tile == 5 || tile == 6 || tile== 7 || tile == 8 || tile == 9)
            {
            printf("[%d]", tile++);
            }

            if (choice == tile)
            {
              tile -= tile;
              printf("[X]");
              choice += 1000;
            }
          }

      printf("\n");
    }
  
  return 0;
} 

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

只是一片海 2025-01-31 22:50:47

我认为您不必担心实现计算机播放器,而是您的第一步是为第二个人提供支持。

首先,您将需要一些数组来表示游戏板的状态。类型 char 的多维数组似乎合适,其中每个 char 可以具有以下三个值之一:

  1. 'e'' for empty
  2. 'x'对于第一个播放器
  3. 'o'对于第二播放器,

您可以定义这样的数组:

char board[3][3];

另外,您还需要一个额外的变量来跟踪当前的转弯。类型 bool 的变量 first_players_turn 可能是合适的。当它是第一个播放器时,该变量应将其设置为 true ,否则为 false

我还建议您创建自己的功能 print_board 将游戏板打印到屏幕上。

您还需要一个函数 estuatue_game_state 才能确定游戏的状态,这可能是以下内容之一:

  1. 游戏仍然不确定。
  2. 游戏是抽奖。
  3. 球员1赢了。
  4. 玩家2赢了。

要表示这4个可能的游戏状态,我建议您使用 代码> ,像这样:

enum game_state
{
    GAMESTATE_UNDECIDED,
    GAMESTATE_DRAW,
    GAMESTATE_WINNER_PLAYER1,
    GAMESTATE_WINNER_PLAYER2
};

您现在可以声明函数 evaluate_game_state 像这样:

enum game_state evaluate_game_state( char board[3][3] );

只要函数 evarueatue_game_state returns gamestate_undecided ,您将必须提示用户在循环中输入。

这是我对上述所有内容的实现,我已经对其进行了一些测试并似乎有效,但是我尚未测试所有角色案例。我从 get_int_int_from_user >修改,以便它接受 variadic参数,以便我可以通过包含<<的字符串代码>%d 到该功能,并将其视为 printf 格式指定符。尽管您使用 printf scanf 也可以使用,但该解决方案的缺点是 scanf 不执行适当的输入验证,因此我使用了我的功能 get_int_from_user 而不是。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <limits.h>
#include <errno.h>
#include <stdarg.h>
#include <stdbool.h>

enum game_state
{
    GAMESTATE_UNDECIDED,
    GAMESTATE_DRAW,
    GAMESTATE_WINNER_PLAYER1,
    GAMESTATE_WINNER_PLAYER2
};

void init_board( char board[3][3] );
void print_board( char board[3][3] );
enum game_state evaluate_game_state( char board[3][3] );
int get_int_from_user( const char *prompt, ... );

int main( void )
{
    char board[3][3];
    bool first_players_turn = true;
    enum game_state gs;

    init_board( board );

    while ( ( gs = evaluate_game_state( board ) ) == GAMESTATE_UNDECIDED )
    {
        //prompt user for input until input is valid
        for (;;)
        {
            int input;

            print_board( board );

            input = get_int_from_user(
                "It is player %d's turn.\n"
                "Please select a move (1-9): ",
                first_players_turn ? 1 : 2
            );

            if ( 1 <= input && input <= 9 )
            {
                int x = ( input - 1 ) % 3;
                int y = ( input - 1 ) / 3;

                //make sure field is empty
                if ( board[y][x] != 'E' )
                {
                    printf( "That field is already occupied!\n\n" );
                    continue;
                }

                //perform the move
                board[y][x] = first_players_turn ? 'X' : 'O';
                first_players_turn = !first_players_turn;
                break;
            }

            printf( "Input must be between 1 and 9!\n\n" );
        }
    }

    //game is over

    print_board( board );

    switch ( gs )
    {
        case GAMESTATE_DRAW:
            printf( "The game is a draw.\n\n" );
            break;
        case GAMESTATE_WINNER_PLAYER1:
            printf( "Player 1 wins!\n\n" );
            break;
        case GAMESTATE_WINNER_PLAYER2:
            printf( "Player 2 wins!\n\n" );
            break;
    }
}

void init_board( char board[3][3] )
{
    for ( int i = 0; i < 3; i++ )
        for ( int j = 0; j < 3; j++ )
            board[i][j] = 'E';
}

void print_board( char board[3][3] )
{
    printf( "\n\n" );

    for ( int i = 0; i < 3; i++ )
    {
        for ( int j = 0; j < 3; j++ )
        {
            //add spacing if necessary
            if ( j != 0 )
                printf( " " );
            
            if ( board[i][j] == 'E' )
                //replace empty field with number of field
                printf( "%d", (i*3) + (j+1) );
            else
                printf( "%c", board[i][j] );
        }

        //end the line
        printf( "\n" );
    }

    printf( "\n" );
}

enum game_state evaluate_game_state( char board[3][3] )
{
    //check for horizontal wins
    for ( int i = 0; i < 3; i++ )
    {
        char possible_winner = board[i][0];

        if ( possible_winner != 'E' )
        {
            if ( possible_winner == board[i][1] && possible_winner == board[i][2] )
            {
                return possible_winner == 'X' ? GAMESTATE_WINNER_PLAYER1 : GAMESTATE_WINNER_PLAYER2;
            }
        }
    }

    //check for vertical wins
    for ( int i = 0; i < 3; i++ )
    {
        char possible_winner = board[0][i];

        if ( possible_winner != 'E' )
        {
            if ( possible_winner == board[1][i] && possible_winner == board[2][i] )
            {
                return possible_winner == 'X' ? GAMESTATE_WINNER_PLAYER1 : GAMESTATE_WINNER_PLAYER2;
            }
        }
    }

    //check for diagonal-down win
    {
        char possible_winner = board[0][0];

        if ( possible_winner != 'E' )
        {
            if ( possible_winner == board[1][1] && possible_winner == board[2][2] )
            {
                return possible_winner == 'X' ? GAMESTATE_WINNER_PLAYER1 : GAMESTATE_WINNER_PLAYER2;
            }
        }
    }

    //check for diagonal-up win
    {
        char possible_winner = board[2][0];

        if ( possible_winner != 'E' )
        {
            if ( possible_winner == board[1][1] && possible_winner == board[0][2] )
            {
                return possible_winner == 'X' ? GAMESTATE_WINNER_PLAYER1 : GAMESTATE_WINNER_PLAYER2;
            }
        }
    }

    //check if board is full
    for ( int i = 0; i < 3; i++ )
        for ( int j = 0; j < 3; j++ )
            if ( board[i][j] == 'E' )
                return GAMESTATE_UNDECIDED;

    return GAMESTATE_DRAW;
}

int get_int_from_user( const char *prompt, ... )
{
    for (;;) //loop forever until user enters a valid number
    {
        va_list args;
        char buffer[1024], *p;
        long l;

        //prompt user for input
        va_start( args, prompt );
        vprintf( prompt, args );
        va_end( args );

        //get one line of input from input stream
        if ( fgets( buffer, sizeof buffer, stdin ) == NULL )
        {
            fprintf( stderr, "unrecoverable error reading from input\n" );
            exit( EXIT_FAILURE );
        }

        //make sure that entire line was read in (i.e. that
        //the buffer was not too small)
        if ( strchr( buffer, '\n' ) == NULL && !feof( stdin ) )
        {
            int c;

            printf( "line input was too long!\n" );

            //discard remainder of line
            do
            {
                c = getchar();

                if ( c == EOF )
                {
                    fprintf( stderr, "unrecoverable error reading from input\n" );
                    exit( EXIT_FAILURE );
                }

            } while ( c != '\n' );

            continue;
        }

        //attempt to convert string to number
        errno = 0;
        l = strtol( buffer, &p, 10 );
        if ( p == buffer )
        {
            printf( "error converting string to number\n" );
            continue;
        }

        //make sure that number is representable as an "int"
        if ( errno == ERANGE || l < INT_MIN || l > INT_MAX )
        {
            printf( "number out of range error\n" );
            continue;
        }

        //make sure that remainder of line contains only whitespace,
        //so that input such as "6sdfh4q" gets rejected
        for ( ; *p != '\0'; p++ )
        {
            if ( !isspace( (unsigned char)*p ) )
            {
                printf( "unexpected input encountered!\n" );

                //cannot use `continue` here, because that would go to
                //the next iteration of the innermost loop, but we
                //want to go to the next iteration of the outer loop
                goto continue_outer_loop;
            }
        }

        return l;

    continue_outer_loop:
        continue;
    }
}

该程序具有以下行为:

1 2 3
4 5 6
7 8 9

It is player 1's turn.
Please select a move (1-9): 5


1 2 3
4 X 6
7 8 9

It is player 2's turn.
Please select a move (1-9): 4


1 2 3
O X 6
7 8 9

It is player 1's turn.
Please select a move (1-9): 1


X 2 3
O X 6
7 8 9

It is player 2's turn.
Please select a move (1-9): 6


X 2 3
O X O
7 8 9

It is player 1's turn.
Please select a move (1-9): 9


X 2 3
O X O
7 8 X

Player 1 wins!

如果您想制作一个好的计算机对手,那么我建议您看看 Wikipedia关于minimax的文章

Instead of worrying about implementing a computer player, I think your first step shoud be to offer support for a second human player.

First, you will need some array to represent the state of the game board. A multi-dimensional array of type char seems appropriate, in which each char can have one of three values:

  1. 'E' for empty
  2. 'X' for first player
  3. 'O' for second player

You could define the array like this:

char board[3][3];

Also, you will need an additional variable to keep track of whose turn it currently is. A variable of type bool with the name first_players_turn would probably be appropriate. When it is the first player's turn, this variable should be set to true, otherwise to false.

I also suggest that you create your own function print_board for printing the game board to the screen.

You will also need a function evaluate_game_state in order to determine the state of the game, which can be one of the following:

  1. The game is still undecided.
  2. The game is a draw.
  3. Player 1 has won.
  4. Player 2 has won.

To represent these 4 possible game states, I recommend that you use an enum, like this:

enum game_state
{
    GAMESTATE_UNDECIDED,
    GAMESTATE_DRAW,
    GAMESTATE_WINNER_PLAYER1,
    GAMESTATE_WINNER_PLAYER2
};

You can now declare the function evaluate_game_state like this:

enum game_state evaluate_game_state( char board[3][3] );

As long as the function evaluate_game_state returns GAMESTATE_UNDECIDED, you will have to prompt the user for input in a loop.

Here is my implementation of everything stated above, which I have tested a bit and seems to work, but I have not tested all corner cases. I took over the function get_int_from_user from this answer of mine from another question, and made a slight modification, so that it accepts variadic arguments, so that I can pass a string containing %d to that function and it treats it as a printf format specifier. Although your implementation of using printf and scanf would also work, that solution has the disadvantage that scanf does not perform proper input validation, so I used my function get_int_from_user instead.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <limits.h>
#include <errno.h>
#include <stdarg.h>
#include <stdbool.h>

enum game_state
{
    GAMESTATE_UNDECIDED,
    GAMESTATE_DRAW,
    GAMESTATE_WINNER_PLAYER1,
    GAMESTATE_WINNER_PLAYER2
};

void init_board( char board[3][3] );
void print_board( char board[3][3] );
enum game_state evaluate_game_state( char board[3][3] );
int get_int_from_user( const char *prompt, ... );

int main( void )
{
    char board[3][3];
    bool first_players_turn = true;
    enum game_state gs;

    init_board( board );

    while ( ( gs = evaluate_game_state( board ) ) == GAMESTATE_UNDECIDED )
    {
        //prompt user for input until input is valid
        for (;;)
        {
            int input;

            print_board( board );

            input = get_int_from_user(
                "It is player %d's turn.\n"
                "Please select a move (1-9): ",
                first_players_turn ? 1 : 2
            );

            if ( 1 <= input && input <= 9 )
            {
                int x = ( input - 1 ) % 3;
                int y = ( input - 1 ) / 3;

                //make sure field is empty
                if ( board[y][x] != 'E' )
                {
                    printf( "That field is already occupied!\n\n" );
                    continue;
                }

                //perform the move
                board[y][x] = first_players_turn ? 'X' : 'O';
                first_players_turn = !first_players_turn;
                break;
            }

            printf( "Input must be between 1 and 9!\n\n" );
        }
    }

    //game is over

    print_board( board );

    switch ( gs )
    {
        case GAMESTATE_DRAW:
            printf( "The game is a draw.\n\n" );
            break;
        case GAMESTATE_WINNER_PLAYER1:
            printf( "Player 1 wins!\n\n" );
            break;
        case GAMESTATE_WINNER_PLAYER2:
            printf( "Player 2 wins!\n\n" );
            break;
    }
}

void init_board( char board[3][3] )
{
    for ( int i = 0; i < 3; i++ )
        for ( int j = 0; j < 3; j++ )
            board[i][j] = 'E';
}

void print_board( char board[3][3] )
{
    printf( "\n\n" );

    for ( int i = 0; i < 3; i++ )
    {
        for ( int j = 0; j < 3; j++ )
        {
            //add spacing if necessary
            if ( j != 0 )
                printf( " " );
            
            if ( board[i][j] == 'E' )
                //replace empty field with number of field
                printf( "%d", (i*3) + (j+1) );
            else
                printf( "%c", board[i][j] );
        }

        //end the line
        printf( "\n" );
    }

    printf( "\n" );
}

enum game_state evaluate_game_state( char board[3][3] )
{
    //check for horizontal wins
    for ( int i = 0; i < 3; i++ )
    {
        char possible_winner = board[i][0];

        if ( possible_winner != 'E' )
        {
            if ( possible_winner == board[i][1] && possible_winner == board[i][2] )
            {
                return possible_winner == 'X' ? GAMESTATE_WINNER_PLAYER1 : GAMESTATE_WINNER_PLAYER2;
            }
        }
    }

    //check for vertical wins
    for ( int i = 0; i < 3; i++ )
    {
        char possible_winner = board[0][i];

        if ( possible_winner != 'E' )
        {
            if ( possible_winner == board[1][i] && possible_winner == board[2][i] )
            {
                return possible_winner == 'X' ? GAMESTATE_WINNER_PLAYER1 : GAMESTATE_WINNER_PLAYER2;
            }
        }
    }

    //check for diagonal-down win
    {
        char possible_winner = board[0][0];

        if ( possible_winner != 'E' )
        {
            if ( possible_winner == board[1][1] && possible_winner == board[2][2] )
            {
                return possible_winner == 'X' ? GAMESTATE_WINNER_PLAYER1 : GAMESTATE_WINNER_PLAYER2;
            }
        }
    }

    //check for diagonal-up win
    {
        char possible_winner = board[2][0];

        if ( possible_winner != 'E' )
        {
            if ( possible_winner == board[1][1] && possible_winner == board[0][2] )
            {
                return possible_winner == 'X' ? GAMESTATE_WINNER_PLAYER1 : GAMESTATE_WINNER_PLAYER2;
            }
        }
    }

    //check if board is full
    for ( int i = 0; i < 3; i++ )
        for ( int j = 0; j < 3; j++ )
            if ( board[i][j] == 'E' )
                return GAMESTATE_UNDECIDED;

    return GAMESTATE_DRAW;
}

int get_int_from_user( const char *prompt, ... )
{
    for (;;) //loop forever until user enters a valid number
    {
        va_list args;
        char buffer[1024], *p;
        long l;

        //prompt user for input
        va_start( args, prompt );
        vprintf( prompt, args );
        va_end( args );

        //get one line of input from input stream
        if ( fgets( buffer, sizeof buffer, stdin ) == NULL )
        {
            fprintf( stderr, "unrecoverable error reading from input\n" );
            exit( EXIT_FAILURE );
        }

        //make sure that entire line was read in (i.e. that
        //the buffer was not too small)
        if ( strchr( buffer, '\n' ) == NULL && !feof( stdin ) )
        {
            int c;

            printf( "line input was too long!\n" );

            //discard remainder of line
            do
            {
                c = getchar();

                if ( c == EOF )
                {
                    fprintf( stderr, "unrecoverable error reading from input\n" );
                    exit( EXIT_FAILURE );
                }

            } while ( c != '\n' );

            continue;
        }

        //attempt to convert string to number
        errno = 0;
        l = strtol( buffer, &p, 10 );
        if ( p == buffer )
        {
            printf( "error converting string to number\n" );
            continue;
        }

        //make sure that number is representable as an "int"
        if ( errno == ERANGE || l < INT_MIN || l > INT_MAX )
        {
            printf( "number out of range error\n" );
            continue;
        }

        //make sure that remainder of line contains only whitespace,
        //so that input such as "6sdfh4q" gets rejected
        for ( ; *p != '\0'; p++ )
        {
            if ( !isspace( (unsigned char)*p ) )
            {
                printf( "unexpected input encountered!\n" );

                //cannot use `continue` here, because that would go to
                //the next iteration of the innermost loop, but we
                //want to go to the next iteration of the outer loop
                goto continue_outer_loop;
            }
        }

        return l;

    continue_outer_loop:
        continue;
    }
}

This program has the following behavior:

1 2 3
4 5 6
7 8 9

It is player 1's turn.
Please select a move (1-9): 5


1 2 3
4 X 6
7 8 9

It is player 2's turn.
Please select a move (1-9): 4


1 2 3
O X 6
7 8 9

It is player 1's turn.
Please select a move (1-9): 1


X 2 3
O X 6
7 8 9

It is player 2's turn.
Please select a move (1-9): 6


X 2 3
O X O
7 8 9

It is player 1's turn.
Please select a move (1-9): 9


X 2 3
O X O
7 8 X

Player 1 wins!

If you want to make a good computer opponent, then I suggest that you take a look at the Wikipedia article on Minimax.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文