[C语言]三字棋和五字棋动态实现

三字棋或五字棋的代码与逻辑

先看效果图
在这里插入图片描述

大体思路及逻辑

编写三(五)棋我们需要创建三个文件 test.c (游戏运行实现) game.c (游戏功能实现文件) game.hgame.c文件函数头文件声明)。
我们在编写程序前,我们需要定义每个源文件的功能
test.c

  • 游戏开始界面
  • 定义棋盘数组及玩家游玩功能
  • 玩家重复游戏功能

game.c

  • 棋盘的初始化
  • 棋盘打印
  • 玩家下棋功能

定义两个变量作为二维数组的下标,经过下标放入 ‘O’ 作为玩家的棋子

  • 电脑下棋功能

这里需要引入time.h、stdlib.h头文件,使用rand()函数来随机产生数组下标,并通过下标放入‘X’做为电脑的棋棋子

  • 游戏输赢判断

game.h :主要声明 game.c 的函数及定义棋盘规格。

代码编写

游戏开始及头文件定义

我们首先需要创建一个头文件 game.h 用来引入需要的头文件和定义棋盘规格以及游戏类型
代码如下

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

#define ROW 3
#define COL 3
#define TYPE 3

这样就初步完成了game.h头文件的编写,接下来编写test.c的开始界面。
创建一个函数

#include "game.h"//引入头文件

void printPlay(){
    printf("************\n");
    printf("***1.进入***\n");
    printf("***0.退出***\n");
    printf("************\n");
}

接下来我们需要在main函数中使用while循环加switch分支语句来构建玩家循环进行游戏。

int main(int argc, char** argv)
{
    int i = 1;
    while (i)
    {
        printPlay();
        printf("请输入:>");
        scanf("%d", &i);
        switch (i)
        {
        case 1:
            game();
            break;
        case 0:
            break;
        default:
        	printf("输入错误请重新输入!");
            break;
        }
    }
    return 0;
}

效果如下;
在这里插入图片描述
我们在switchcase 1分支中构建一个game函数用于实现游戏进行逻辑。

棋盘的初始化和打印

我们需要在里面创建用于打印的棋盘数组。

void game(){
	char bored[ROW][COL];
}

接下来我们给该数组进行初始化,放入空格符用作占位符然后打印。
game.h 中声明四个函数一个用于遍历,一个用于打印、电脑和玩家下棋操作及游戏状态判断的功能。

void initBored(char bored[ROW][COL], int col, int row);//初始化数组
void printBored(char bored[ROW][COL]);//打印数组
void playBored(char bored[ROW][COL], int row, int col);//玩家下棋功能
void botBored(char bored[ROW][COL]);//电脑下棋功能
char stateBored(char bored[ROW][COL], char ch);//输赢判断

initBored函数中我们使用两个循环进行遍历放入占位符

void initBored(char bored[ROW][COL], int col, int row){
    for (size_t i = 0; i < row; i++)
    {
        for (size_t j = 0; j < col; j++)
        {
            bored[i][j] = ' ';
        }
    }
}

然后因为后期玩家或电脑每进行下棋操作便需要打印一次,所以独立创建一个printBored函数
若只打印数组则显得太过单调,我们则添加些许边框。

void printBored(char bored[ROW][COL]){
    for (size_t i = 0; i < ROW; i++)
    {
        for (size_t j = 0; j < COL; j++)
        {
            printf(" %c ", bored[i][j]);
            if (j < COL - 1)
            {
                printf("|");
            }
        }
        printf("\n");
        if (i == ROW - 1)
            continue;
        for (size_t j = 0; j < COL; j++)
        {
            printf("---");
            if (j < COL - 1)
            {
                printf("|");
            }
        }
        printf("\n");
    }
}

打印函数完成后,遍需要进行编写玩家下棋操作。

玩家下棋操作

在编写这个函数中需要解决玩家输入的坐标是否 ”有主“,当有主并重复循环让玩家重新输入。

void playBored(char bored[ROW][COL], int row, int col){
    int x, y;
    while (1)
    {
        printf("请输入坐标空格隔开:>");
        scanf("%d %d", &x, &y);
        if (bored[x-1][y-1] == ' ')
        {
            bored[x-1][y-1] = 'O';
            return;
        }else{
            printf("该坐标已被占用,请重新选择:");
        }
    }
}

电脑下棋操作

电脑下棋就比玩家下棋多了一个使用rand获取下标,当该下标存在棋子时,重新获取下标。
在使用rand()函数前我需要在test文件中的game 使用tmie当做种子初始化rand函数。

void game(){
	srand((unsigned int)time(NULL));
	char bored[ROW][COL];
}

bot下棋使用循环进行赋值操作,当遇到“空”位置时,跳出循环即可。

void botBored(char bored[ROW][COL]){
    while (1)
    {
        printf("Bot正在下棋!\n");
        int timeBot1 = rand() % ROW;
        int timeBot2 = rand() % COL;
        if (bored[timeBot1][timeBot2] == ' '){
            bored[timeBot1][timeBot2] = 'X';
            break;
        }
    }
    
}

当遍写完以上功能,我们便需要完成test文件中的game函数了

void game(){
    srand((unsigned int)time(NULL));
    char bored[ROW][COL];
    initBored(bored, COL, ROW);
    printBored(bored);
    while (1)
    {
        playBored(bored,ROW,COL);
        printBored(bored);
        botBored(bored);
        printBored(bored);
    }
}

游戏状态

行列判断

游戏的结束的条件是 玩家或电脑下的三个棋字 连在一起时才能结束对局。 当前game函是死循环的,我们还需要编写一个对于游戏状态判断的函数再搭配while循环构成循环下棋的操作,再由状态函数判断结束循环。

char stateBored(char bored[ROW][COL], char ch)
{
	for (size_t i = 0; i < ROW; i++)
    {
        int numRow = 0;
        int numCol = 0;
        for (size_t j = 0; j < COL; j++)
        {
            if (bored[i][j] == ch)
                numRow++;
            else
                numRow = 0;
            if (bored[j][i] == ch)
                numCol++;
            else
                numCol = 0;
        }
        if(numRow == TYPE || numCol == TYPE)
            return ch;
    }
}

游戏状态函数需要玩家或者电脑传入特征棋子变量,创建两个变量用于记录数组中特征棋子变量类型是否连贯,当出现中断时记录变量置为0;

主、副对角线

棋盘是个正方型 除了行、列还有主、副对角线等斜线。
每个方向需要遍历的线条为 n= (ROW + COL - 1) - (TYPE - 1)* 2 条,但主、副对对角线我们可以同时遍历,所以我们的while 条件则是 i < ROW - (TYPE - 1) 那么我们看下面关于斜线的逻辑。

主对角线及旁线

在这里插入图片描述

主对角线我们只需一个循环,条件小于行或列标即可遍历,同样需要一个变量记录值。
由图可清晰看出,主对角线右方每条斜线上的元素坐标,行标与列标成等差,差值为 1。
左放则是列标与行标成等差,差值为 1。
[0][1]、[1][2]、[2][3]、[3][4][1][0]、[2][1]、[3][2]、[4][3] 我们可以看出两组坐标行列互换即可,所以它两边遍历则可以在一个循环中完成。

副对角线及旁线

在这里插入图片描述

副对角线我们则可以使用一个变量 col == 0当作列行标位于副对角线循环外即可完成对副角线的判断。
副角线旁我们可以根据图片得出
[0][3]、[1][2]、[2][1]、[3][0][1][4]、[2][3]、[3][2]、[4][1] 这组,记 p1 差值 1
[0][2]、[1][1]、[2][0][2][4]、[3][3]、[4][2] 这组 记p2 差值 2
我们仔细看,这差值不就是外层循环的i值吗?所以也只需要一个循环即可完成对副角线旁的判断

由以上逻辑 对角线可在 i == 0 时完成,这时遍历了主、副对角线,i > 0时则遍历了主副对角线旁的线。
逻辑清晰,接下来看代码

    int i = 0;
    while (i < ROW - (TYPE - 1))
    {
        int num = 0;
        int numCol = 0;
        if (i == 0)
        {
            //主对角线
            for (int j = 0; j < ROW; j++)
            {
                if (bored[j][j] == ch)
                    num++;
                else
                    num = 0;
                if (num == TYPE)
                {
                    return ch;
                }
            }
            //副对角线
            num = 0;
            int col = 0;
            for (int j = ROW - 1; j >= 0; j--,col++)
            {
                if (bored[col][j] == ch)
                    num++;
                else
                    num = 0;
                if (num == TYPE)
                    return ch;
            }
        }else
        {
            //主对角线旁
            int tmp = 0;
            for (int j = i; j < ROW; j++)
            {
                if (bored[j][tmp] == ch)
                    num++;
                else
                    num = 0;
                if (bored[tmp++][j] == ch)
                    numCol++;
                else
                    numCol = 0;
                if (num == TYPE || numCol == TYPE)
                    return ch;
            }
            //副对角线旁
            num = 0;
            tmp = 0;
            for (int j = (ROW - i) - 1; j >= 0; j--)
            {
                if (bored[tmp][j] == ch)
                    num++;
                else
                    num = 0;
                if (bored[tmp + i][j + i] == ch)
                    numCol++;
                else
                    numCol = 0;
                if (num == TYPE || numCol == TYPE)
                    return ch;
                tmp++;
            }
        }
        i++;
    }

我们完成了行、列、主副对角线的判断,那么当没有数赢的时候,我们该判断是否是平局还是未完成将继续?

平局

未完成那么我们将继续游戏,我们只需要便利是否还有空位够供玩家或电脑进行下棋操作。
若无空位则平局

	for (size_t i = 0; i < ROW; i++)
    {
        for (size_t j = 0; j < COL; j++)
        {
            if(bored[i][j] == ' ')
                return 'P';
        }
    }
    return 'B';

到此该函数已经完成,我们来看完整的函数

char stateBored(char bored[ROW][COL], char ch)
{
    //行列
    for (size_t i = 0; i < ROW; i++)
    {
        int numRow = 0;
        int numCol = 0;
        for (size_t j = 0; j < COL; j++)
        {
            if (bored[i][j] == ch)
                numRow++;
            else
                numRow = 0;
            if (bored[j][i] == ch)
                numCol++;
            else
                numCol = 0;
            if (numRow == TYPE || numCol == TYPE)
                return ch;
        }
    }
    //斜线
    int i = 0;
    while (i < ROW - (TYPE - 1))
    {
        int num = 0;
        int numCol = 0;
        if (i == 0)
        {
            //主对角线
            for (int j = 0; j < ROW; j++)
            {
                if (bored[j][j] == ch)
                    num++;
                else
                    num = 0;
                if (num == TYPE)
                {
                    return ch;
                }
            }
            //副对角线
            num = 0;
            int col = 0;
            for (int j = ROW - 1; j >= 0; j--,col++)
            {
                if (bored[col][j] == ch)
                    num++;
                else
                    num = 0;
                if (num == TYPE)
                    return ch;
            }
        }else
        {
            //主对角线旁
            int tmp = 0;
            for (int j = i; j < ROW; j++)
            {
                if (bored[j][tmp] == ch)
                    num++;
                else
                    num = 0;
                if (bored[tmp++][j] == ch)
                    numCol++;
                else
                    numCol = 0;
                if (num == TYPE || numCol == TYPE)
                    return ch;
            }
            //副对角线旁
            num = 0;
            tmp = 0;
            for (int j = (ROW - i) - 1; j >= 0; j--)
            {
                if (bored[tmp][j] == ch)
                    num++;
                else
                    num = 0;
                if (bored[tmp + i][j + i] == ch)
                    numCol++;
                else
                    numCol = 0;
                if (num == TYPE || numCol == TYPE)
                    return ch;
                tmp++;
            }
        }
        i++;
    }
    //平局
    for (size_t i = 0; i < ROW; i++)
    {
        for (size_t j = 0; j < COL; j++)
        {
            if(bored[i][j] == ' ')
                return 'P';
        }
    }
    return 'B';
}

game函数

状态函数中当有赢家时返回变量ch,平局或继续时返回PB.
回到test.c文件game函数中,我们需要一个char类型的变量tmp用于接受game.c文件中状态函数的值,
只需判断tmpP则跳出循环,使用switch语句进行结束语句输出。

void game(){
    srand((unsigned int)time(NULL));
    char bored[ROW][COL];
    initBored(bored, COL, ROW);
    printBored(bored);
    char tmp = 0;
    while (1)
    {
        playBored(bored,ROW,COL);
        printBored(bored);
        tmp = stateBored(bored, 'O');
        if (tmp != 'P')
        {
            break;
        }
        botBored(bored);
        printBored(bored);
        tmp = stateBored(bored, 'X');
        if (tmp != 'P')
        {
            break;
        }
    }
    switch (tmp)
    {
    case 'O':
        printf("恭喜你你赢了!\n");
        break;
    case 'X' :
        printf("很遗憾你输了!\n");
        break;
    case 'B' :
        printf("平局!\n");
        break;
    }
    
}

效果

到此我们的编写完成,让我们来看效果图
在这里插入图片描述
到此各位看官可能会迷惑,怎么就只有三字棋?五字棋呢?
五字棋我们只需修改game.h文件 的TYPE COLROW 属性即可
在这里插入图片描述

效果如下:

在这里插入图片描述
若以上代码如有bug请在评论区指出。
新人码字不容易,请各位看官点个赞,支持一下。


附上述文件源码:

game.h:

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

#define ROW 10
#define COL 10
#define TYPE 5


void initBored(char bored[ROW][COL], int col, int row);
void printBored(char bored[ROW][COL]);
void playBored(char bored[ROW][COL], int row, int col);
void botBored(char bored[ROW][COL]);
char stateBored(char bored[ROW][COL], char ch);

game.c

#include "game.h"

void initBored(char bored[ROW][COL], int col, int row){
    for (size_t i = 0; i < row; i++)
    {
        for (size_t j = 0; j < col; j++)
        {
            bored[i][j] = ' ';
        }
    }
}

void printBored(char bored[ROW][COL]){
    for (size_t i = 0; i < ROW; i++)
    {
        for (size_t j = 0; j < COL; j++)
        {
            printf(" %c ", bored[i][j]);
            if (j < COL - 1)
            {
                printf("|");
            }
        }
        printf("\n");
        if (i == ROW - 1)
            continue;
        for (size_t j = 0; j < COL; j++)
        {
            printf("---");
            if (j < COL - 1)
            {
                printf("|");
            }
        }
        printf("\n");
    }
}
//玩家
void playBored(char bored[ROW][COL], int row, int col){
    int x, y;
    while (1)
    {
        printf("请输入坐标空格隔开:>");
        scanf("%d %d", &x, &y);
        if (bored[x-1][y-1] == ' ')
        {
            bored[x-1][y-1] = 'O';
            return;
        }else{
            printf("该坐标已被占用,请重新选择:");
        }
    }
}
//Bot
void botBored(char bored[ROW][COL]){
    while (1)
    {
        printf("Bot正在下棋!\n");
        int timeBot1 = rand() % ROW;
        int timeBot2 = rand() % ROW;
        if (bored[timeBot1][timeBot2] == ' '){
            bored[timeBot1][timeBot2] = 'X';
            break;
        }
    }
    
}
// 状态
char stateBored(char bored[ROW][COL], char ch)
{
    //行列
    for (size_t i = 0; i < ROW; i++)
    {
        int numRow = 0;
        int numCol = 0;
        for (size_t j = 0; j < COL; j++)
        {
            if (bored[i][j] == ch)
                numRow++;
            else
                numRow = 0;
            if (bored[j][i] == ch)
                numCol++;
            else
                numCol = 0;
            if (numRow == TYPE || numCol == TYPE)
                return ch;
        }
    }
    //主对角线
    int i = 0;
    while (i < ROW - (TYPE - 1))
    {
        int num = 0;
        int numCol = 0;
        if (i == 0)
        {
            //主对角线
            for (int j = 0; j < ROW; j++)
            {
                if (bored[j][j] == ch)
                    num++;
                else
                    num = 0;
                if (num == TYPE)
                {
                    return ch;
                }
            }
            //副对角线
            num = 0;
            int col = 0;
            for (int j = ROW - 1; j >= 0; j--,col++)
            {
                if (bored[col][j] == ch)
                    num++;
                else
                    num = 0;
                if (num == TYPE)
                    return ch;
            }
        }else
        {
            //主对角线旁
            int tmp = 0;
            for (int j = i; j < ROW; j++)
            {
                if (bored[j][tmp] == ch)
                    num++;
                else
                    num = 0;
                if (bored[tmp++][j] == ch)
                    numCol++;
                else
                    numCol = 0;
                if (num == TYPE || numCol == TYPE)
                    return ch;
            }
            //副对角线旁
            num = 0;
            tmp = 0;
            for (int j = (ROW - i) - 1; j >= 0; j--)
            {
                if (bored[tmp][j] == ch)
                    num++;
                else
                    num = 0;
                if (bored[tmp + i][j + i] == ch)
                    numCol++;
                else
                    numCol = 0;
                if (num == TYPE || numCol == TYPE)
                    return ch;
                tmp++;
            }
        }
        i++;
    }
    //平局
    for (size_t i = 0; i < ROW; i++)
    {
        for (size_t j = 0; j < COL; j++)
        {
            if(bored[i][j] == ' ')
                return 'P';
        }
    }
    return 'B';
}

test.c

#include "game.h"

void printPlay(){
    printf("************\n");
    printf("***1.进入***\n");
    printf("***0.退出***\n");
    printf("************\n");
}
void game(){
    srand((unsigned int)time(NULL));
    char bored[ROW][COL];
    initBored(bored, COL, ROW);
    printBored(bored);
    char tmp = 0;
    while (1)
    {
        playBored(bored,ROW,COL);
        printBored(bored);
        tmp = stateBored(bored, 'O');
        if (tmp != 'P')
        {
            break;
        }
        botBored(bored);
        printBored(bored);
        tmp = stateBored(bored, 'X');
        if (tmp != 'P')
        {
            break;
        }
    }
    switch (tmp)
    {
    case 'O':
        printf("恭喜你你赢了!\n");
        break;
    case 'X' :
        printf("很遗憾你输了!\n");
        break;
    case 'B' :
        printf("平局!\n");
        break;
    }
    
}

int main(int argc, char** argv)
{
    int i = 1;
    while (i)
    {
        printPlay();
        printf("请输入:>");
        scanf("%d", &i);
        switch (i)
        {
        case 1:
            game();
            break;
        case 0:
            break;
        default:
            printf("输入错误,请重新输入!");
            break;
        }
    }
    return 0;
}

`

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值