C语言实现三子棋

一、前言

今天我来演示下如何用C语言实现三子棋。

先认识下三子棋:双方依次在9宫格棋盘上摆放棋子,率先将自己的三个棋子走成一条线就视为胜利。

游戏规则很简单,在这篇文章中我会先讲述游戏代码实现的流程,然后给出代码并在注释中对其讲解。

二、流程

1.构建代码的大致框架

我们的代码分为两个框架:测试模块和游戏模块,因此可以创建test.c来测试游戏逻辑,创建game.c和game.h,分别来实现实现函数和进行函数的声明以及头文件的包含。

2.菜单

首先,做一个菜单,这个菜单可以指导玩家如何进入和退出游戏,因为我们是在c语言上操作,玩家通过输入字符来进行操作的,他们可能输入菜单上以外的字符,所以我们也要考虑的这种情况。

3.游戏的实现

在现实生活中我们玩三子棋,感觉这游戏再简单不过,可是要到代码上实现它,难免摸不着头脑:格子怎么用代码表示?落子怎么用代码表示?又要用怎么判断胜负?我为游戏的实现分装了五个功能,初始化棋盘,显示棋盘,玩家下棋,电脑下棋,判断输赢。

(1)初始化棋盘

构建个二维数组,将其元素全部初始化为空格,来表示最开始一个子都没下的棋盘。

(2)显示棋盘

三子棋的格子如同井字,想要用代码表示这个井,我们可以将井字拆解成三个循环,

| | 和---|---|---,在最后一次循环中控制条件省略掉---|---|---,就可以构建出我们的格子了,注意看会发现---|---|---也是这个道理,---|循环,在最后一次循环中省略|,等会代码演示更清晰明白。

(3)下棋

先说玩家下棋,下棋有三种可能,一、该位置空着,二、该位置已被下过,三、玩家输入了非法坐标,我们要考虑的这三种情况。然后电脑下棋,因为我们用c语言操作,电脑没办法那么智能,只能用随机的方式让它下棋了,我们考虑用rand和srand函数,来替电脑随机决定坐标。

(4)判断输赢

在每次玩家下棋和电脑下棋后我们都要判断下当前的局势。局势有四种可能,电脑赢,玩家赢,平局,还未结束。

三.代码

1.game.h

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#define COL 3
#define ROW 3//这里要用宏是方便我们随心打印任意大小的棋盘,如果要修改大小直接在这修改就好,但是如果想要修改大小的话,对应的判断胜负的方式也要变
void InitBoard(char board[ROW][COL], int row, int col);
void DisplayBoard(char board[ROW][COL], int row, int col);
void PlayerMove(char board[ROW][COL], int row, int col);
void ComputerMove(char board[ROW][COL], int row, int col);
char IsWin(char board[ROW][COL], int row, int col);
//因为在其他文件中都会包含game.h所以我们在game.h包含我们所需要的所有头文件,声明,和宏的定义

2.game.c

#define _CRT_SECURE_NO_WARNINGS
#include "game.h"
void InitBoard(char board[ROW][COL], int row, int col)//想要初始化棋盘需要二维数组的地址,和行列大小
{
    int i = 0;
    int j = 0;
    for (i = 0; i < row; i++)
    {
        for (j = 0; j < col; j++)
        {
            board[i][j] = ' ';
        }
    }
}//利用两个变量和两个循环将棋盘中的元素全部初始化为空格,也可以用memset函数实现,如下memset(&board[0][0],' ',col*row*sizeof(board[0][0]);
void DisplayBoard(char board[ROW][COL], int row, int col)//想要显示棋盘需要二维数组的地址,和行列大小
{
    int i = 0;
    int j = 0;
    for (i = 0; i < ROW; i++)
    {
        for (j = 0; j < COL; j++)
        {
            printf(" %c ", board[i][j]);
            if (j != COL - 1)
                 printf("|");
        }
        printf("\n");
        if (i != ROW - 1)
        {
            for (j = 0; j < COL; j++)
            {
                printf("---");
                if (j != COL - 1)
                    printf("|");
            }
            printf("\n");
        }
    }
}//这里的显示棋盘的关键就是如何将格子打印出来,思路上面已经说过了,大家可以好好品品代码。
void PlayerMove(char board[ROW][COL], int row, int col)//想要玩家下棋需要二维数组的地址,和行列大小
{
{
    int x = 0;
    int y = 0;
    printf("玩家下棋\n");
    while (1)//一直循环直到玩家成功落子
    {
        printf("请输入下棋坐标\n");
        scanf("%d%d", &x, &y);
        if (x >= 1 && x <= 3 && y >= 1 && y <= 3)//只有输入正确坐标才能下棋,因为玩家一般认为行列从1开始,所以这里范围是1到3
        {
            if (board[x - 1][y - 1] == ' ')//正确坐标上不能被占用,并且玩家输入的坐标对应到我们代码上x和y都应该减去1
            {
                board[x - 1][y - 1] = '*';//用*作为玩家的棋子
                break;
            }
            else//位置被占用提示玩家
                printf("坐标已被占用,请重新输入\n");
        }
        else//输入非法坐标提示玩家
            printf("坐标非法,请重新输入\n");
    }
}
void ComputerMove(char board[ROW][COL], int row, int col)//想要电脑下棋需要二维数组的地址,和行列大小
{
    printf("电脑下棋\n");
    int x = 0;
    int y = 0;
    while (1)//一直循环直到电脑成功落子
    {
        x = rand() % row;
        y = rand() % col;//%3是为了使生成的随机数的范围再0到2之间
        if (board[x][y] == ' ')
        {
            board[x][y] = '#';//用#作为电脑的棋子
            break;
        }
    }
}
int IsFull(char board[ROW][COL], int row, int col)//判断棋盘是否满了需要二维数组的地址,和行列大小
{
    int i = 0;
    int j = 0;
    for (i = 0; i < row; i++)
    {
        for (j = 0; j < col; j++)
        {
            if (board[i][j] == ' ')
                return 0;//遍历整个数组如果有空格说明没满返回0,反之满了,返回1
        }
    }
    return 1;
}
char IsWin(char board[ROW][COL], int row, int col)//判断输赢需要二维数组的地址,和行列大小
{
    int i = 0;
    for (i = 0; i < row; i++)
    {
        if (board[i][0] == board[i][1] && board[i][1] == board[i][2] && board[i][0] != ' ')
            return board[i][0];//判断是否在行上胜出,若有则返回该棋子
    }
    for (i = 0; i < col; i++)
    {
        if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[0][i] != ' ')
            return board[0][i];//判断是否在列上胜出,若有则返回该棋子
    }
    if (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[1][1] != ' ')
        return board[1][1];//判断是否在斜线上胜出,若有则返回该棋子
    if (board[0][2] == board[1][1] && board[1][1] == board[2][0] && board[1][1] != ' ')
        return board[1][1];//判断是否在斜线上胜出,若有则返回该棋子
    if (IsFull(board, row, col))//判断是否平局,若平局则返回Q
        return 'Q';
    return 'C';//若前面的if都不成立,说明游戏未结束,则返回C
}

3.test.c

#define _CRT_SECURE_NO_WARNINGS
#include "game.h"
void menu()
{
    printf("******************************\n");
    printf("******************************\n");
    printf("************1.play************\n");
    printf("************0.exit************\n");
    printf("******************************\n");
    printf("******************************\n");
}//简单的不能再简单的菜单
void game()
{
    char board[ROW][COL] = { 0 };//创建二维数组作为棋盘
    char ret = 0;
    InitBoard(board, ROW, COL);//初始化棋盘
    DisplayBoard(board, ROW, COL);//显示棋盘
    while (1)//一直循环,直到一方胜利或者平局再跳出循环
    {
        PlayerMove(board, ROW, COL);//玩家移动
        DisplayBoard(board, ROW,COL);//显示棋盘
         ret=IsWin(board,ROW,COL);//判断输赢
        if (ret != 'C')//如果返回不是继续的标志,说明游戏结束,跳出循环
            break;
        ComputerMove(board, ROW, COL);//电脑移动
        DisplayBoard(board, ROW, COL);//显示棋盘
        ret=IsWin(board,ROW,COL);//如果不是继续的标志跳出循环
        if (ret != 'C')
            break;
    }
    if (ret == '*')//如果返回的是玩家的棋子,则玩家胜利
        printf("游戏结束,玩家胜利\n");
    if (ret == '#')//如果返回的是电脑的棋子,则电脑胜利
        printf("游戏结束,电脑胜利\n");
    if (ret == 'Q')//如果返回的是平局的标志,则平局
        printf("游戏结束,平局\n");
}
int main()
{
    srand((unsigned int)time(NULL));//srand设置产生伪随机数发生器的起始点,而time是时间戳可以让起始点不断变化,产生类似随机的效果
    int input = 0;//玩家的输入
    do
    {
        menu();
        printf("请选择:->");
        scanf("%d", &input);
        switch (input)
        {
        case 1:
            game();
            break;
        case 0:
            printf("退出游戏\n");
            break;
        default:
            printf("输入错误,请重新输入\n");
            break;
        }
    } while (input);//巧妙地将do while和switch结构结合起来,在输入0的时候因为0为假便退出了循环,而输入1时候的时候先进行游戏,然后1为真再进入循环,再次打印菜单询问玩家是否要进行游戏,这样可以让玩家想玩多就玩多久,而不是玩一次就退出游戏,而输入1和0以外的数字,就会提醒玩家错误,并且再次打印菜单让玩家输入,直到正确。菜单虽小,应有尽有。
    return 0;
}

四、结束语

想要讲好一个代码只用文字比较难,因为代码的构建是动态的,是一步一步来的,所以代码只看不写是学不好的,若我的文章对你有所启发的,快快打起代码吧,这样才能真正的学到知识,谢谢观看!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值