五子棋(C语言实现)

news2024/11/27 2:34:40

目录

构思

1、主程序

2、初始化

3、游戏菜单

4、打印棋盘

6、玩家下棋

7、判断输赢

 8、功能整合

人机下棋

完整版: 

game.h

game.c

text.c

 测试功能代码


构思

五子棋不必多介绍了,大家小时候都玩过哈。

我们要通过程序实现这个小游戏,大体上的构思得有:

游戏流程:运行游戏>>打印棋盘>>下棋>>判断输赢

由此,我们声明如下函数、棋盘数组和回合数 (用于双人下棋时下不同棋子的判断)。

将这些都放在我创建的头文件game.h中方便其他文件使用: 

#include <stdio.h>
#include <Windows.h>

int map[19][19];//棋盘
int flag;//回合数

//初始化棋盘
void init();

//游戏菜单
void menuView();

//打印棋盘
void gameView_ShowMap();

//玩家下棋
int playerMove(int x, int y);

//判断输赢
int isWin(int x, int y);

//判断输赢
void winView();

1、主程序

先进行游戏下棋,后进行判断输赢,比较符合do—while的特点。 

int main()
{
    int input = 0;
    do
    {
        menuView();
        printf("请选择:");
        scanf("%d", &input);
        switch (input) {
        case 1:
            gameView();
            break;
        case 0:
            printf("退出游戏\n");
            break;
        default:
            printf("输入错误,请重新选择.\n");
            break;
        }
    } while (input);
    return 0;
}
  • 首先打印菜单,提示玩家进行选择开始游戏。
  • 通过输入变量input的值,判断是否进行游戏,
  • 值为1则调用gameView函数进行游戏,0则结束游戏。
  • gameView函数对各种功能函数进行整合。

在此之前先将各种功能函数进行实现,最后将它们放入gameView整合。 

2、初始化

void init() {
    for (int i = 0; i < 19; i++) {
        for (int j = 0; j < 19; j++) {
            map[i][j] = 0;
        }
    }
    flag = 0;
}
  • 将棋盘每个格子初始化为0。
  • 回合数初始化为0。

3、游戏菜单

void menuView() {
    printf("*************************\n");
    printf("*****   1. play    ******\n");
    printf("*****   0. exit    ******\n");
    printf("*************************\n");
}

4、打印棋盘

这里我选择设计了棋盘格和横纵坐标,我们可以根据需要自行设计。 

void gameView_ShowMap() {
    int i, j;
    printf("   ");
    for (i = 0; i < 19; i++) {//打印横坐标
        printf("%3d ",i);
    }
    printf("\n   ");
    for (i = 0; i < 19; i++) {
        printf("+---");
    }
    printf("+\n");

    for (i = 0; i < 19; i++) {
        
        printf("%2d |", i);//每行输出前都先打印纵坐标

        for (j = 0; j < 19; j++) {
            printf(" %d ", map[i][j]);//打印棋子
            if (j < 18)
                printf("|");
        }
        printf("|\n");

        if(i<18)
            printf("   |");
        if(i==18)
            printf("   +");
        for (j = 0; j < 19; j++) {
            printf("---");
            if (j < 18)
                printf("+");
        }
        printf("+\n");
    }
}

效果如下: 

6、玩家下棋

int playerMove(int x, int y) {
    if (x >= 0 && x < 19 && y >= 0 && y < 19) {
        if (map[x][y] == 0) {
            if (flag % 2 == 0)
                map[x][y] = 1;//下黑子
            else
                map[x][y] = 2;//下白子
            //落子成功
            return 1;
        }
        else {
            //该位置已有棋子
            return 0;
        }
    }
    else {
        //坐标不合法
        return -1;
    }
}
  • 首先判断下棋位置是否在棋盘0到18的横纵坐标内,如果不在函数返回值-1,
  • 如果在棋盘内,则进行下棋。
  • 棋盘每个格子初始值为0,判断当前格子的值为0才可以下棋,否则返回值为0。
  • 回合数flag初始值为0,当flag为偶数下黑子>>1,奇数下白子>>2,成功下棋返回值为1。

7、判断输赢

int isWin(int x, int y) {
    int i, j;
    for (i = 0; i < 19; i++) {
        for (j = 0; j < 19; j++) {
            if (map[i][j] == 0) {
                continue;
            }
                
            //横着连成五子
            if (j < 15)
                if (map[i][j] == map[i][j + 1] && map[i][j] == map[i][j + 2]
                    && map[i][j] == map[i][j + 3] && map[i][j] == map[i][j + 4])
                    return map[i][j];

            //竖着连成五子
            if (i < 15)
                if (map[i][j] == map[i + 1][j] && map[i][j] == map[i + 2][j]
                    && map[i][j] == map[i + 3][j] && map[i][j] == map[i + 4][j])
                    return map[i][j];

            //左斜着连成五子-> " \ "
            if (i < 15 && j < 15)
                if (map[i][j] == map[i + 1][j + 1] && map[i][j] == map[i + 2][j + 2]
                    && map[i][j] == map[i + 3][j + 3] && map[i][j] == map[i + 4][j + 4])
                    return map[i][j];

            //右斜着连成五子-> " / "
            if (i < 15 && j > 4)
                if (map[i][j] == map[i + 1][j - 1] && map[i][j] == map[i + 2][j - 2]
                    && map[i][j] == map[i + 3][j - 3] && map[i][j] == map[i + 4][j - 4])
                    return map[i][j];

            
        }
    }
    return 0;
}
  • i < 15 用于确保在检查垂直和左斜方向的连续五子时,起始位置 (i, j) 之后至少还有4个位置。因为五子连成一线需要五个连续的位置,所以确保从当前位置开始往下检查的时候不会超出数组的边界。

  • j < 15 用于确保在检查水平和左斜方向的连续五子时,起始位置 (i, j) 之后至少还有4个位置。同样,这是为了确保从当前位置开始往右检查的时候不会超出数组的边界。

  • j > 4 用于确保在检查右斜方向的连续五子时,起始位置 (i, j) 之前至少还有4个位置。这是为了确保从当前位置开始往左检查的时候不会超出数组的边界。

 8、功能整合

void gameView()
{
    init();
    int x = 0, y = 0;
    int ret = 0;//辅助判断坐标是否合法
    while (1) {
        gameView_ShowMap();

        printf("请输入要下棋的坐标:");
        scanf("%d %d", &x, &y);

        if (playerMove(x, y) == 0) {
            printf("\n!!!该坐标已被占用!!!\n");
            Sleep(2000);
            continue;
        }
        else if (playerMove(x, y) == -1) {
            printf("\n!!!请输入合法坐标!!!\n");
            Sleep(2000);
            continue;
        }
        else {
            flag++;//切换回合
        }

        if (isWin(x, y) == 0) {
            continue;
        }
           
        else if (isWin(x, y) == 1) {
            printf("黑子获胜\n");
            break;
        }

        else if (isWin(x, y) == 2) {
            printf("白子获胜\n");
            break;
        }
    }

}
  •  这里的Sleep函数需要头文件#include <Windows.h>,使用该函数暂停两秒,防止continue后下次循环打印的棋盘将提示信息挡住。
  • 下棋坐标不合法打印提示信息后,进入下次循环重新输入。
  • 成功下棋则flag自增,切换回合。
  • 每次下棋后都要判断输赢,有人赢了则停止循环,否则继续下棋。
  • (其实应该从下棋次数第五次开始判断输赢更合理,读者可以自行添加判断)

人机下棋

想要实现人机下棋可以看看我这篇文章《三子棋》 ,里面实现了人机下棋,读者可以自行模仿改进,实现其功能的重要函数可以在这篇文章中学习 rand&srand函数 。 

完整版: 

game.h

#include <stdio.h>
#include <Windows.h>
int map[19][19];//棋盘
int flag;//回合数

//初始化
void init();

//游戏菜单
void menuView();

//打印棋盘
void gameView_ShowMap();

//玩家下棋
int playerMove(int x, int y);

//判断输赢
int isWin(int x, int y);

game.c

#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"


void init() {
    for (int i = 0; i < 19; i++) {
        for (int j = 0; j < 19; j++) {
            map[i][j] = 0;
        }
    }
    flag = 1;
}


int isWin(int x, int y) {
    int i, j;
    for (i = 0; i < 19; i++) {
        for (j = 0; j < 19; j++) {
            if (map[i][j] == 0) {
                continue;
            }
                
            //横着连成五子
            if (j < 15)
                if (map[i][j] == map[i][j + 1] && map[i][j] == map[i][j + 2]
                    && map[i][j] == map[i][j + 3] && map[i][j] == map[i][j + 4])
                    return map[i][j];

            //竖着连成五子
            if (i < 15)
                if (map[i][j] == map[i + 1][j] && map[i][j] == map[i + 2][j]
                    && map[i][j] == map[i + 3][j] && map[i][j] == map[i + 4][j])
                    return map[i][j];

            //左斜着连成五子-> " \ "
            if (i < 15 && j < 15)
                if (map[i][j] == map[i + 1][j + 1] && map[i][j] == map[i + 2][j + 2]
                    && map[i][j] == map[i + 3][j + 3] && map[i][j] == map[i + 4][j + 4])
                    return map[i][j];

            //右斜着连成五子-> " / "
            if (i < 15 && j > 4)
                if (map[i][j] == map[i + 1][j - 1] && map[i][j] == map[i + 2][j - 2]
                    && map[i][j] == map[i + 3][j - 3] && map[i][j] == map[i + 4][j - 4])
                    return map[i][j];

            
        }
    }
    return 0;
}


int playerMove(int x, int y) {
    if (x >= 0 && x < 19 && y >= 0 && y < 19) {
        if (map[x][y] == 0) {
            if (flag % 2 == 0)
                map[x][y] = 1;//下黑子
            else
                map[x][y] = 2;//下白子
            //落子成功
            return 1;
        }
        else {
            //该位置已有棋子
            return 0;
        }
    }
    else {
        //坐标不合法
        return -1;
    }
}


void menuView() {
    printf("*************************\n");
    printf("*****   1. play    ******\n");
    printf("*****   0. exit    ******\n");
    printf("*************************\n");
}


void gameView_ShowMap() {
    int i, j;
    printf("   ");
    for (i = 0; i < 19; i++) {//打印横坐标
        printf("%3d ",i);
    }
    printf("\n   ");
    for (i = 0; i < 19; i++) {
        printf("+---");
    }
    printf("+\n");

    for (i = 0; i < 19; i++) {
        
        printf("%2d |", i);//每行输出前先都打印纵坐标

        for (j = 0; j < 19; j++) {
            printf(" %d ", map[i][j]);
            if (j < 18)
                printf("|");
        }
        printf("|\n");

        if(i<18)
            printf("   |");
        if(i==18)
            printf("   +");
        for (j = 0; j < 19; j++) {
            printf("---");
            if (j < 18)
                printf("+");
        }
        printf("+\n");
    }
}

text.c

#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"

void gameView()
{
    init();
    int x = 0, y = 0;
    int ret = 0;//辅助判断坐标是否合法
    while (1) {
        gameView_ShowMap();

        printf("请输入要下棋的坐标:");
        scanf("%d %d", &x, &y);

        if (playerMove(x, y) == 0) {
            printf("\n!!!该坐标已被占用!!!\n");
            Sleep(2000);
            continue;
        }
        else if (playerMove(x, y) == -1) {
            printf("\n!!!请输入合法坐标!!!\n");
            Sleep(2000);
            continue;
        }
        else {
            flag++;//切换回合
        }

        if (isWin(x, y) == 0) {
            continue;
        }
           
        else if (isWin(x, y) == 1) {
            printf("黑子获胜\n");
            break;
        }

        else if (isWin(x, y) == 2) {
            printf("白子获胜\n");
            break;
        }
    }

}

int main()
{
    int input = 0;
    do
    {
        menuView();
        printf("请选择:");
        scanf("%d", &input);
        switch (input) {
        case 1:
            gameView();
            break;
        case 0:
            printf("退出游戏\n");
            break;
        default:
            printf("输入错误,请重新选择.\n");
            break;
        }
    } while (input);
    return 0;
}

 测试功能代码

此代码可以替换int main主函数,用于测试函数功能是否正确,可以减少自行下棋测试时间 

成功运行输出如下:

int main()
{
    int testflag = 0;
    //init测试代码
    
    init();
    if (flag != 0) {
        printf("init()错误");
        exit(0);
    }
    for (int i = 0; i < 19; i++) {
        for (int j = 0; j < 19; j++) {
            if (map[i][j]) {
                printf("init()错误");
                exit(0);
            }
        }
    }
    printf("init()测试成功\n");
    testflag++;
    

    //playerMove测试代码
    
    int result = 1;
    result &= playerMove(2, 2);
    result &= playerMove(2, 3);
    result &= playerMove(2, 4);
    result &= playerMove(2, 5);
    if (result != 1 || (map[2][2] && map[2][3] && map[2][4] && map[2][5]) != 1) {
        printf("playerMove()错误");
        exit(0);
    }
    flag = 1;
    result &= playerMove(2, 5);
    if (result != 0 || map[2][5] != 1) {
        printf("playerMove()错误");
        exit(0);
    }
    printf("playerMove()测试成功\n");
    testflag++;
    

    //isWin测试代码
    
    playerMove(2, 1);
    if (isWin(2, 1)) {
        printf("isWin()错误");
        exit(0);
    }
    playerMove(1, 0);
    playerMove(3, 2);
    playerMove(4, 3);
    playerMove(5, 4);
    if (isWin(1, 0) != 2) {
        printf("isWin()错误");
        exit(0);
    }
    printf("isWin()测试成功\n");
    testflag++;
    

    if (testflag == 3) {
        printf("service代码测试成功\n");
    }
    return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1092583.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

AI对网络安全的影响与挑战

近年来&#xff0c;随着人工智能&#xff08;AI&#xff09;技术的快速发展&#xff0c;网络安全领域也开始逐渐引入生成式AI应用。根据最新的数据研究&#xff0c;生成式AI对网络安全和合规的影响最大&#xff0c;同时也包括了IT和云的运维、硬件和软件支持领域。通过AI和自动…

Typora--博客必备神器

文章目录 一、下载typora二、创建文件前的操作1.显示扩展名2.typora设置和markdown语法2.1markdown语法2.2图片的管理2.3**高亮**2.4设置自动保存和调试 一、下载typora 进入typota.io网站&#xff0c;点击下载即可。 二、创建文件前的操作 1.显示扩展名 设置显示文件扩展名…

python+django学生选课管理系统_wxjjv

1&#xff09;前台&#xff1a;首页、课程信息、校园论坛、校园公告、个人中心、后台管理。 &#xff08;2&#xff09;管理员&#xff1a;首页、个人中心、学生管理、教师管理课、程信息管理、课程分类管理、选课信息管理、作业信息管理、提交作业管理、学生成绩管理、校园论…

android studio 移植工程

第一步&#xff1a; 第二步&#xff1a;创建 第三步&#xff1a; 第四步&#xff1a;复制文件至替代新工程中的文件 第五步&#xff1a;修改 第六步&#xff1a;编译OK

第 125 场周赛 三元组

可恶&#xff0c;被longlong的长度卡住了&#xff0c;要是longlong再大一点就好了&#xff08;bushi&#xff09;&#xff0c;其实是算法有问题&#xff0c;里面涉及1e9*1e9*1e9,longlong肯定存不下&#xff0c;一会儿去改改&#xff0c;先记录一下。 题目&#xff1a;竞赛 - …

树莓派玩转openwrt软路由:12.OpenWrt安装MySQL

1、安装MySQL #下载MySQL镜像 docker pull arm64v8/mysql:latest#运行MySQL镜像 docker run --name mysql --restartalways --privilegedtrue \ -v /usr/local/mysql/data:/var/lib/mysql \ -v /usr/local/mysql/conf.d:/etc/mysql/conf.d \ -e MYSQL_ROOT_PASSWORD123456 -p …

C++——C++入门

C 前言一、认识C二、C入门C关键字(C98)命名空间命名空间定义命名空间使用 C输入&输出缺省参数缺省参数概念缺省参数分类 函数重载函数重载概念C支持函数重载的原理--名字修饰(name Mangling) 总结 前言 C的学习开始啦&#xff01; 来吧~让我们拥抱更广阔的知识海洋&#x…

msvcp140.dll文件下载方法,找不到msvcp140.dll丢失的解决方法

在我日常的计算机维护和故障排除工作中&#xff0c;我遇到了许多由于丢失或损坏MSVCP140.dll文件而导致的程序无法正常运行的问题。这个DLL文件是Microsoft Visual C 2010 Redistributable Package的一部分&#xff0c;它是许多Windows应用程序&#xff08;尤其是使用C编写的程…

C++11中类与对象推出的新功能 [补充讲解final/override关键字]

文章目录 1.移动构造2.移动赋值对于移动构造/移动赋值的想法 3.类成员定义时初始化4.强制生成默认函数的关键字default5.禁止生成默认函数的关键字delete5.1介绍5.2应用场景1.法一:析构函数私有化2.法二: delete关键字思考 6.final关键字7.override关键字 1.移动构造 编译器自动…

Lambda表达式(JAVA)

注&#xff1a;如果没有学过匿名内部类和接口不推荐往下看。 Lambda表达式的语法&#xff1a; (parameters) -> expression 或 (parameters) ->{ statements; } parameters&#xff1a;表示参数列表&#xff1b;->&#xff1a;可理解为“被用于”的意思&#xff1b…

苹果修复了旧款iPhone上的iOS内核零日漏洞

导语 近日&#xff0c;苹果发布了针对旧款iPhone和iPad的安全更新&#xff0c;回溯了一周前发布的补丁&#xff0c;解决了两个被攻击利用的零日漏洞。这些漏洞可能导致攻击者在受影响的设备上提升权限或执行任意代码。本文将介绍这些漏洞的修复情况以及苹果在修复漏洞方面的持续…

自定义类型:结构体,枚举,联合 (2)

2. 位段 位段的出现就是为了节省空间。 2.1 什么是位段 位段的声明和结构是类似的&#xff0c;有两个不同&#xff1a; 1.位段的成员必须是 int、unsigned int 或signed int 。 2.位段的成员名后边有一个冒号和一个数字。 比如&#xff1a; struct A {int _a:2;int _b:5;int…

ArcGIS笔记5_生成栅格文件时保存报错怎么办

本文目录 前言Step 1 直接保存到指定文件夹会报错Step 2 先保存到默认位置再数据导出到指定文件夹 前言 有时生成栅格文件时&#xff0c;保存在自定义指定的文件夹内会提示出错&#xff0c;而保存到默认位置则没有问题。因此可以通过先保存到默认位置&#xff0c;再数据导出到…

Vite与Webpack谁更胜一筹,谁将引领下一代前端工具的发展

你知道Vite和Webpack吗&#xff1f;也许有不少“程序猿”对它们十分熟悉。 Webpack Webpack是一个JavaScript应用程序的静态模块打包工具&#xff0c;它会对整个应用程序进行依赖关系图构建。而这也会导致一个不可避免的情况&#xff0c;使用Webpack启动应用程序的服务器&…

GO-SLAM——论文简析

GO-SLAM 位姿估计效果很好&#xff0c;有高效的回环检测和 full BA&#xff08;每个关键帧&#xff09;&#xff0c;适用于单目、双目和 RGB-D。 一、简介 消费级深度传感器容易产生噪声&#xff0c;这就导致 RGB-D SLAM 会丢失一些几何细节&#xff0c;导致过度平滑。使用轻…

ASP.NET framework升级core .NET 6.0

C# ASP.NET framework 升级core .NET 6.0 .NET 7.0 .NET 8.0 或者以上 using System.Web.Http; using HttpPostAttribute Microsoft.AspNetCore.Mvc.HttpPostAttribute; using RouteAttribute Microsoft.AspNetCore.Mvc.RouteAttribute; using HttpGetAttribute Mic…

c++初阶--内存管理

目录 c/c 内存分布c内存管理方式new/delete操作内置类型new和delete操作自定义类型 operator new与operator delete函数new和delete的实现原理内置类型自定义类型 malloc/free和new/delete的区别内存泄露什么是内存泄漏&#xff0c;内存泄露的危害如何避免内存泄漏 在c语言中我…

数据结构 - 4(栈和队列6000字详解)

一&#xff1a;栈 1.1 栈的概念 栈&#xff1a;一种特殊的线性表&#xff0c;其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶&#xff0c;另一端称为栈底。栈中的数据元素遵守后进先出LIFO&#xff08;Last In First Out&#xff09;的原…

Go语言入门心法(三): 接口

Go语言入门心法(一) Go语言入门心法(二): 结构体 一&#xff1a;go语言接口认知 Go语言中接口认知升维:解决人生问题的自我引导法则: 复盘思维|结构化思维|金字塔思维|体系化思维|系统化思维 面向对象编程(oop)三大特性: 封装,继承,多态 Go语言中,可以认为接口是一种自定义的抽…

【Pytorch】深度学习之优化器

文章目录 Pytorch提供的优化器所有优化器的基类Optimizer 实际操作实验参考资料 优化器 根据网络反向传播的梯度信息来更新网络的参数&#xff0c;以起到降低loss函数计算值&#xff0c;使得模型输出更加接近真实标签的工具 学习目标 Pytorch提供的优化器 优化器的库torch.opt…