用c语言写一个简单的三子棋(井字棋)

猫和老鼠
emmmm…大致就是这个效果

目录

1.所需的知识

c语言基本语法,二维数组。

2.总体思路

将三子棋看作3x3的一个二维数组,由玩家和电脑分别填充这个数组。当行、列、叉能够达到三连时,就判断输赢。
默认玩家先行且棋子为 ‘ X ’,电脑后手棋子为 ‘ 0 ’。
下面是用到的一些函数。

//game.h
#ifndef __GAME_H__
#define __GAME_H__
#define ROW 3
#define CLO 3
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <memory.h>
void menu();                                           //打印菜单
void game(char arr[ROW][CLO], int row, int clo);       //执行菜单   
void play(char arr[ROW][CLO], int row, int clo);       //游戏主体
void InitBoard(char arr[ROW][CLO], int row, int clo);  //打印棋盘
void Pmove(char arr[ROW][CLO], int row, int clo);      //玩家下棋
void Cmove(char arr[ROW][CLO], int row, int clo);      //电脑下棋
int Isfull(char arr[ROW][CLO], int row, int clo);      //检测盘满
int Iswin(char arr[ROW][CLO], int row, int clo);       //判断输赢
#endif

下面是主函数

//test.c
#include"game.h"
int main()
{
    srand((unsigned int)time(NULL)); //产生随机数种子
    char arr[ROW][CLO];
    game(arr,ROW,CLO);
    system("pause");
    return 0;
}

3.详细实现

打印菜单
void menu()
{
    printf("*******************\n");
    printf("*******1.play *****\n");
    printf("*******2.exit *****\n");
    printf("*******************\n");
}

这个函数打印菜单选项。

执行菜单
void game(char arr[ROW][CLO], int row, int clo)
{
    int input = 0;
    do
    {
        menu();
        scanf_s("%d",&input);
        memset(arr, ' ', ROW*CLO * sizeof(arr[0][0]));
        switch (input)
        {
        case 1:
            play(arr, row, clo); break;
        case 2:
            exit(0); break;
        default:printf("你按错键了?\n");
        }
    } while (input);
}

这个函数是菜单执行部分。其中 memset 用来初始化二位字符数组为全‘ 空格 ’。
接着,用switch语句来判断玩家选项。

游戏主体
void play(char arr[ROW][CLO], int row, int clo)
{
    int flag = 0;
    InitBoard(arr, row, clo);
    while (1)
    {
        printf("人类走\n");
        Pmove(arr, row, clo);
        InitBoard(arr, row, clo);
        flag = Iswin(arr, row, clo);
        if (flag == 1)
        {
            printf("玩家赢\n");
            break;
        }
        flag = Isfull(arr, row, clo);
        if (flag == 4)
        {
            printf("棋盘满,游戏结束\n");
            break;
        }
        printf("电脑走\n");
        Cmove(arr, row, clo);
        InitBoard(arr, row, clo);
        flag = Iswin(arr, row, clo);
        if (flag == 1)
        {
            printf("电脑赢\n");
            break;
        }
    }
}

这个函数里面拥有一个 while 死循环,它的跳出条件是 玩家赢 、 电脑赢 、棋盘满。
flag == 1 表示电脑或者玩家获胜,flag == 4 表示棋盘满了。
这里将棋盘满的检测放在玩家行动后,因为只有先手才能使棋盘满。

棋盘打印
void InitBoard(char arr[ROW][CLO], int row, int clo)
{
    int i = 0;
    int j = 0;
    for (j = 0; j < row; j++)
    {
        for (i = 0; i < clo; i++)
        {
            printf(" %c ", arr[j][i]);
            if (i < (clo - 1))
            {
                printf("|");
            }
        }
        printf("\n");
        if (j < row - 1)
        {
            for (i = 0; i < clo; i++)
            {
                printf("---");
                if (i < (clo - 1))
                {
                    printf("|");
                }
            }
        }
        printf("\n");
    }
}

这个棋盘打印函数可以扩展。只要改变ROW 和 CLO 的值就可以改变棋盘大小。
例如: 7x7的棋盘
这里写图片描述

玩家下棋
void Pmove(char arr[ROW][CLO], int row, int clo)
{
    printf("请输入坐标(空格隔开)\n");
    int m = 0, n = 0;
    while (1)
    {
        scanf_s("%d %d", &m, &n);
        if (((m >= 1) && (m <= 3)) && ((n >= 1) && (n <= 3)))
        {
            if (arr[m - 1][n - 1] == ' ')
            {
                arr[m - 1][n - 1] = 'X';
                break;
            }
            else
            {
                printf("这里可能已经有棋子了\n");
            }
        }
        else
        {
            printf("可能你输错了\n");
        }
    }
}

这个是玩家输入函数。这里注意判断输入是否合法,输入范围1~3,并查看该点是否有棋子。考虑玩家因素,将获取的数字-1,使之成为数组变量。

判断输赢
int Iswin(char arr[ROW][CLO], int row, int clo)
{
    //行
    int i = 0;
    for (i = 0; i < row; i++)
    {
        if (((arr[i][0] == arr[i][1]) && (arr[i][1] == arr[i][2])) && ((arr[i][0] != ' ')))
        {
            return 1;
        }
    }
    //列
    for (i = 0; i < clo; i++)
    {
        if ((arr[0][i] == arr[1][i]) && (arr[1][i] == arr[2][i]) && ((arr[0][i] != ' ') ))
        {
            return 1;
        }
    }
    //叉
    if ((arr[0][0] == arr[1][1]) && (arr[1][1] == arr[2][2]) && ((arr[0][0] != ' ') ))
    {
        return 1;
    }
    if ((arr[0][2] == arr[1][1]) && (arr[1][1] == arr[2][0]) && ((arr[0][2] != ' ')))
    {
        return 1;
    }
    return 0;
}

这个函数判断输赢。由于3行3列,所以用for循环扫描3次。当连续三字相同且不为‘ 空格 ’,那么取得胜利。

电脑下棋(核心)
void Cmove(char arr[ROW][CLO], int row, int clo)
{
    int flag = 0;
    int m = 0, n = 0;
    for (m = 0; m < row; m++)
    {
        for (n = 0; n < row; n++)
        {
            if (arr[m][n] == ' ')
            {
                arr[m][n] = '0';
                flag = Iswin(arr, row, clo);
                if (flag == 0)
                {
                    arr[m][n] = ' ';
                }
                if (flag == 1)
                {
                    break;
                }
            }
        }
        if (flag == 1)
        {
            break;
        }
    }
    if (flag == 0)
    {
        for (m = 0; m < row; m++)
        {
            for (n = 0; n < row; n++)
            {
                if (arr[m][n] == ' ')
                {
                    arr[m][n] = 'X';
                    flag = Iswin(arr, row, clo);
                    if (flag == 0)
                    {
                        arr[m][n] = ' ';
                    }
                    if (flag == 1)
                    {
                        arr[m][n] = '0';
                        break;
                    }
                }
            }
            if (flag == 1)
            {
                break;
            }
        }

        if ((m == 3) && (n == 3))
        {
            while (1)
            {
                m = rand() % 3;
                n = rand() % 3;
                if (arr[m][n] == ' ')
                {
                    arr[m][n] = '0';
                    break;
                }
            }
        }
    }
}

这是个电脑下棋函数。这里动了点歪脑筋。
第一个双层for循环的作用是:依次检测空位。如果是空位,那么先让该空位被’ 0 ‘填充,然后判断能不能获胜。能获胜就跳出 且 flag == 1,不能获胜就把改位再次置成空位。

接上述不能获胜。如果扫描完后发现并不能获胜,那么采取防守策略。

第二个双层for循环也是检测空位。如果是空位,那么让该位被 ’ X ‘填充,(相当电脑伪装成玩家先走一步)。填充后判断输赢,如果能够获胜,那么电脑再将这个位置改为‘ 0 ’,达到阻止玩家取胜的目的。

最后如果既不能取得胜利,也不用阻止玩家获胜。那么,就让电脑随机找位置下棋。

判断盘满
int Isfull(char arr[ROW][CLO], int row, int clo)
{
    int i = 0, j = 0;
    int count = 0;
    for (i = 0; i < row; i++)
    {
        for (j = 0; j < clo; j++)
        {
            if (arr[i][j] != ' ')
            {
                count ++;
            }
        }
    }
    if (count == 9)
    {
        return 4;
    }
    return 3;
}

这个函数是判断棋盘是否满。使用count变量计数,扫描棋盘,如果不为‘ 空格 ’的位置有九个,那么就表示棋盘满了。

3.效果演示

会堵人的电脑
这里写图片描述
会优先自己胜利的电脑
这里写图片描述
棋盘满效果
这里写图片描述

PS:先手优势很大,不出意外的话是先手赢,或是平局。
依此类推,五子棋在15x15及其以上的棋盘,先手必胜。
但是电脑获胜算法非常复杂,反正我是写不出来。

  • 8
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
A: ```c #include <stdio.h> #include <stdlib.h> // 定义井字棋格子内容类型 enum GridContent { GridContentEmpty, GridContentCross, GridContentCircle }; // 构建井字棋棋盘 typedef struct { enum GridContent content[3][3]; } TicTacToeBoard; // 初始化井字棋棋盘 void init_board(TicTacToeBoard *board) { for (int i = 0; i < 3; ++i) { for (int j = 0; j < 3; ++j) { board->content[i][j] = GridContentEmpty; } } } // 输出井字棋棋盘 void print_board(TicTacToeBoard *board) { for (int i = 0; i < 3; ++i) { for (int j = 0; j < 3; ++j) { if (board->content[i][j] == GridContentEmpty) { printf(" "); } else if (board->content[i][j] == GridContentCross) { printf("X"); } else if (board->content[i][j] == GridContentCircle) { printf("O"); } if (j != 2) { printf("|"); } } printf("\n"); if (i != 2) { printf("-+-+-\n"); } } } // 判断是否有一方获胜 int check_win(TicTacToeBoard *board, enum GridContent content) { // 判断行 for (int i = 0; i < 3; ++i) { if (board->content[i][0] == content && board->content[i][1] == content && board->content[i][2] == content) { return 1; } } // 判断列 for (int i = 0; i < 3; ++i) { if (board->content[0][i] == content && board->content[1][i] == content && board->content[2][i] == content) { return 1; } } // 判断对角线 if (board->content[0][0] == content && board->content[1][1] == content && board->content[2][2] == content) { return 1; } if (board->content[0][2] == content && board->content[1][1] == content && board->content[2][0] == content) { return 1; } return 0; } // 执行游戏主循环 void run_game() { TicTacToeBoard board; init_board(&board); enum GridContent player1_content = GridContentCross; enum GridContent player2_content = GridContentCircle; enum GridContent cur_content = player1_content; while (1) { // 输出当前棋盘 printf("\n"); print_board(&board); // 让家输入下一步位置 int row, col; printf("\nPlayer %s's turn (row, col): ", (cur_content == player1_content ? "1" : "2")); scanf("%d %d", &row, &col); // 检查输入是否合法 if (row < 1 || row > 3 || col < 1 || col > 3) { printf("Invalid input! Please enter the position as row col, where row and col are integers between 1 and 3.\n"); continue; } if (board.content[row - 1][col - 1] != GridContentEmpty) { printf("The position has already been taken! Please choose another one.\n"); continue; } // 将指定位置标记为当前家的棋子 board.content[row - 1][col - 1] = cur_content; // 检查是否有一方获胜 if (check_win(&board, cur_content)) { printf("\nPlayer %s wins!\n", (cur_content == player1_content ? "1" : "2")); print_board(&board); break; } // 如果棋盘已满,宣布平局 int is_tie = 1; for (int i = 0; i < 3; ++i) { for (int j = 0; j < 3; ++j) { if (board.content[i][j] == GridContentEmpty) { is_tie = 0; break; } } if (!is_tie) { break; } } if (is_tie) { printf("\nThe game is a tie!\n"); print_board(&board); break; } // 切换到下一个家 cur_content = (cur_content == player1_content ? player2_content : player1_content); } } int main() { run_game(); return 0; } ``` 这个程序使用了结构体和枚举类型来构建、表示井字棋棋盘和格子内容。它包括了下列功能: - 初始化棋盘 - 输出棋盘 - 判断是否有一方获胜 - 让家输入下一步位置,并检查输入是否合法 - 更新棋盘状态,并在必要时宣布获胜或平局 - 切换到下一个家 运行程序即可体验井字棋游戏的法!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值