萌新的三子棋记录

你好哦,这里是云切斩月(Echo_Fish),记录一下新学的东西,写的不好,佬们轻点喷!如果有错误的话还请犇和佬们不吝赐教😗。

首先需要知道一个完整的三子棋程序流程分为几步

  1. 使用二维数组存放代表正方与反方的棋子,该数组初始化时将其全部初始化为空格,以便打印的时候进行观察。

  1. 打印棋盘。

  1. 定义一个接口用于编写正方(玩家)落子逻辑。

  1. 定义一个接口用于编写反方(电脑)落子逻辑。

  1. 定义一个接口用于判断哪方胜利,或者平局,或者游戏胜利。

明晰了程序框架之后,写程序就方便许多了。在写程序的时候一定要注意程序的复用性和维护性,也就是说——一个函数只有一个功能。将一个大集合拆成多个小部分,日后在需要相同功能的函数的时候直接复制粘贴即可。

首先创建一个.h后缀的表头文件,存放我们之后会用到的头文件的引用,函数的声明与宏定义。

代码如下:

#pragma once
#define _CRT_SECURE_NO_WARNINGS 1
#pragma warning(disable:4996)
#include<stdio.h>
#include<time.h>
#include<stdlib.h>
//符号定义
#define ROW 3
#define COL 3

//函数声明

//初始化棋盘
void InitBoard(char board[][COL],int row, int col);
//打印棋盘
void showBoard(char board[][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);

在使用自定义文件的时候,其他源文件引用时需要修改为如下形式:

#include<stdio.h>  //这是标准头文件
#include "game1.h" //这是自定义头文件

回到正题,main函数编写为以下形式:

int main()
{
    int input = 0;
    srand((unsigned int)time(NULL));//随机数  需要#include<time.h>该头文件
    do
    {
        menu();
        printf("请选择:>");
        scanf("%d", &input);
        switch (input)
        {
        case 1:
            
            printf("玩三子棋游戏\n");
            game();                    //在该函数中嵌套调用各功能接口
            break;
        case 0:
            printf("退出游戏\n");
            break;
        default:
            printf("选择错误,重新选择\n");
            break;
        }
    } while (input);
    return 0;
}

这里switch 使用0来进入退出程序的分支是有意义的,因为0代表着假,while()中的input如果为0,就停止循环。可以退出整个程序。

注:菜单写不写其实无所谓。

接着开始进入主要功能的编写,新创建一个源文件单独用于存放实现功能的接口。

首先是初始化棋盘:只要将在 game() 接口中定义好的数组与上文使用的宏定义传入即可,将二维数组中所有元素全部初始化为空格。

//初始化棋盘
void InitBoard(char board[ROW][COL], int row, int col)
{
    int i = 0,
        j = 0;
    for (i = 0; i < row; i++)
    {
        for (j = 0; j < col; j++)
        {
            board[i][j] = ' ';
        }
    }
}

接着是打印棋盘功能:参数与初始化棋盘的函数相同,打印这点没什么难的,如果你想让棋盘好看一点也可以自己设计设计,代码如下:

void showBoard(char board[][COL], int row, int col)
{
    int i = 0,
        j = 0;
    for (i = 0; i < row; i++)
    {
        for (j = 0; j < col; j++)
        {
            printf("  %c", board[i][j]);
            if(j < col-1)                    //在最后一列时不打印'|'
            printf("|", board[i][j]);
        }
        printf("\n");  //开始第二行 划分棋盘
        if (i != 2)
        {
            for (j = 0; j < col; j++)
            {
                printf("---");
                if (j < col - 1)
                {
                    printf("|");            //在最后一列时不打印'|'
                }
            }
        }
        printf("\n");
    }
}

效果如下:

然后是正方落子逻辑,正方的子以‘*’表示。这里我们需要思考几点

  1. 玩家可能不是程序员,不了解第0行与第0列的概念,例如他想要在0 0行落子,实际上输入的是

1 1这两个数字,所以我们就需要对程序进行处理。

b.玩家可能会越界落子,同样需要进行限制,越界落子就需要通知他,并且在落子正确之前不断循环。

c.若原位置不为空格,同样不能落子,需要进行限制。

清楚这两点之后就可以写出如下代码:

void playerMove(char board[ROW][COL], int row, int col) //玩家落子
{
    
    printf("请输入坐标:>");
    while (1)
    {
        int x = 0,
            y = 0;
        scanf("%d %d", &x, &y);
        if (x <= ROW && x >= 1 && y <= COL && y >= 1)
        {
            if (board[x - 1][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,
        y = 0;
    while (1)
    {
        x = rand() % row,//0-2
        y = rand() % col;//0-2
        if (board[x][y] == ' ')
        {
            board[x][y] = '#';
            break;
        }
    }
}

到了这里,三子棋的编写已经进入了尾声,我们仅仅缺少判断赢、平局或者继续的功能,再之后是将之前编写的东西进行整合即可。

判断某方赢的条件:横竖三行,正反对角线为同一符号,正方为'*' 反方为'#'。如果未达成条件就继续游戏,如果正方胜出就返回'*',反方胜出就返回'#'。

判断平局的条件:数组不存在空格并且还未有一方胜出,返回E(equal)。

判断继续条件:未有一方胜出,未平局,返回C(continue)。

代码如下:

char isWin(char board[ROW][COL], int row, int col)
{
    int i;
    //玩家一行三个
    for (i = 0; i < row; i++)
    {
        if (board[i][0] == board[i][1] && board[i][1] == board[i][2] && board[i][1] == '*')
        {
            return '*';
        }
    }
    //玩家一列三个
        for (i = 0; i < col; i++)
        {
            if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[1][i] == '*')
            {
                return '*';
            }
        }
    //玩家对角线三个
            if (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[1][1] == '*')
            {
                return '*';
            }
            if (board[0][2] == board[1][1] && board[1][1] == board[2][0] && board[1][1] == '*')
            {
                return '*';
            }
    //电脑一行三个
            for (i = 0; i < row; i++)
            {
                if (board[i][0] == board[i][1] && board[i][1] == board[i][2] && board[i][1] == '#')
                {
                    return '#';
                }
            }
    //电脑一列三个
            for (i = 0; i < col; i++)
            {
                if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[1][i] == '#')
                {

                    return '#';
                }
            }
    //电脑对角线三个
            if (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[1][1] == '#')
            {
                return '#';
            }
            if (board[0][2] == board[1][1] && board[1][1] == board[2][0] && board[1][1] == '#')
            {
                return '#';
            }
            //平局
            int count = 0;
            for (int i = 0; i < row; i++)
            {
                for (int j = 0; j < row; j++)
                {
                    if (board[i][j] != ' ')
                    {
                        count++;
                    }
                }
            }
            if (count == row * col)
            {
                return 'E';
            }
            //继续
            else
            {
                return 'C';
            }
}

在判断平局条件时可以用一个计数器的方法,二维数组中非空格的元素就让计数器加一,直到count变量等于二维数组的总元素个数时,代表着棋盘满了。而走到这一步,程序还没有返回‘*’或者‘#’,代表着平局。

平局条件也可以引申出继续的条件,如果不是平局就继续。直到棋盘落满子或者有某一方胜出。

在IsWin函数返回时,定义一个变量接收即可。如果是‘*’就打印棋盘,并且宣布正方胜利。

如果是‘#’同样打印棋盘,同时宣布反方胜利。

如果是‘E’打印棋盘,宣布平局。

如果是‘C’打印棋盘,不结束游戏,继续下棋逻辑。(这代表着我们需要循环)

综上所述,在整合的时候进行如下处理。

void game()   //游戏相关逻辑
{
    //存储数据 - 二维数组
    char board[ROW][COL];
    //初始化棋盘
    InitBoard(board, ROW, COL);
    //打印棋盘
    showBoard(board, ROW, COL);
    char ret = '0';
    while (1)//死循环 直到有一方胜出或者平局再跳出
    {
        //玩家落子
        playerMove(board, ROW, COL);
        //落子后展示当前棋盘
        showBoard(board, ROW, COL);
        ret = isWin(board, COL, COL);
        if (ret != 'C')//继续
            break;
        //电脑落子
        computerMove(board, ROW, COL);
        //落子后展示当前棋盘
        showBoard(board, ROW, COL);
        ret = isWin(board, COL, COL);
        if (ret != 'C')//继续
            break;
    }
    //跳出循环后的判断
    if (ret == '*')
    {
        printf("玩家赢\n");
    }
    else if (ret == '#')
    {
        printf("电脑赢\n");
    }
    else
    {
        printf("平局\n");
    }
    //游戏结束后展示棋盘
    showBoard(board, ROW, COL);
}

以上,就是本次三子棋程序的全部内容😆

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值