C语言小游戏——井字棋(数组实现)

2023-11-08

学c也学了有一些时间了,今天用c语言做了一个小游戏——井字棋。相信大家也玩过。我们这个游戏的思路呢,是玩家和电脑对弈。谁先把三颗棋子连成一条线,谁就赢了。如下图所示:

要想实现我们这个井字棋需要用到数组的知识,所以,老规矩我们先简单的把数组讲一下:

数组:数组是一组相同类型元素的集合。

数组的创建方式: type_t   arr_name  [const_n];

                              // type_t  是指数组的元素类型

                              //const_n   是一个常量表达式,用来指定数组的大小

数组的创建实例:

int arr1[10];
char arr2[10];
char arr3[10];

数组初始化:

#include<stdio.h>
#include<stdlib.h>
int main(){
    //初始化方法一:
    int arr1[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    //初始化方法二:
    int arr2[] = {1, 2, 3};
    //初始化方法三:
    char arr3[10];
    int i = 0;
    for(i = 0; i < 10; i++){  // 利用for循环将arr3数组中的所有值初始化为'a';
        arr3[i] = 'a';    
    }
    
    return 0;
}

数组的使用:如果想要使用数组中的元素可以通过访问数组的下标来获取数组中的某一个元素

#include<stdio.h>
int main(){
    
    int arr[10] = {0}; // 数组的不完全初始化
    // 计算数组的元素个数
    int sz = sizeof(arr) / sizeof(arr[0]);
    // 对数组内容赋值,数组是使用下标来访问的,下标从0开始。
    int i = 0; // 做下标
    for(i = 0; i < sz; i++){
        arr[i] = i;
    }

    // 输出数组的内容
    for(i = 0; i< sz; i++){
        printf("%d", arr[i]);
    }
    return 0;
}

二维数组的创建:

int arr[3][4];
char arr[3][5];
double arr[2][4];

二维数组的初始化:

int arr[3][4] = {1, 2, 3, 4};
int arr[3][4] = {{1,2},{4,5}};
int arr[][4] = {{2,3}, {4,5}};

二维数组的使用:

#include<stdio.h>
int main{

    int arr[3][4] = {0};
    int i = 0;
    for(i = 0; i < 3; i++){
        int j = 0;
        for(j = 0; j < 4; j++){
            arr[i][j] = i*4+j;
        }
    }
    for(i = 0; i < 3; i++){
        int j = 0;
        for(j = 0; j < 4; j++){
            printf("%d", arr[i][j]);
        }
    }
    return 0;

}

PS:数组创建,[ ]中要给一个常量才可以,不能使用变量

游戏开始

游戏开始我们得让玩家自己选择是玩游戏还是退出,所以我们得打印一个游戏头,让玩家知道有哪些选项以及怎么进入那些选项代码如下:

// 打印游戏头
void game_head() {
	printf("####################\n");
	printf("#      1.PLAY      #\n");
	printf("#      0.EXIT      #\n");
	printf("####################\n");
}

这段代码执行出来得效果是这样的:

 游戏头搞定了,这下玩家就知道了有哪些选项,这个时候我们就得让玩家来选择,就得让玩家输入1或者0来玩游戏或退出游戏,因为是多条件所以我们使用switch语句。此外,可能玩家在玩了一局之后还想玩第二局,所以我们得把这些代码写到一个循环中。代码如下:

#include<stdio.h>
int main(){

    int input = 0; //让玩选择1 play 或者 0 exit
    
    do{
        game_head();
        printf("请输入1开始游戏>>");
        scanf("%d", &input);
        switch(input){
            case 1:
                printf("开始玩游戏\n");
                break;
            case 0:
                printf("游戏结束\n");
                break;
            default:
                printf("没有这个选项,请重新输入\n");
                break;            
        }
    }while(input != 0);
}

 运行结果如下: 

 到这一步,这个游戏大的框架就成型了。接下来我们就来实现游戏的功能,首先我们要把井字棋的棋盘先打印出来。这就需要我们用到二维数组,我们先创建一个字符二维数组,并把它初始化为全是空格的数组。为什么要用字符数组呢?那是因为我们这个数组里面存放的是玩家下的棋子和电脑下的棋子。大概的意思如下图:

#define ROW 3 //宏定义的常量,此时ROW == 3
#define COL 3 //宏定义的常量,此时COL == 3

char arr[ROW][COL]; // 这里我是将数组定义为一个全局变量,就在程序的任何地方可以直接使用

//  初始化数组
void init(){
    int i = 0;
    int j = 0;
    for(i = 0; i < ROW; i++){
        for(j = 0; j < COL; j++){
            arr[i][j] = ' ';
        }
    }
}

初始化数组之后,就把棋盘打印出来。我们知道棋盘除了要体现玩家和电脑下棋的数据以外还有一些分割线。所以我们打印棋盘的思路就是,下棋的数据和分割线一起打印。在打印每一行前两个数据的时候在数据的后面加一个' | ',把每个数据分割开。再在前两行打印完后增加一行’ -----------‘这样的短线,把每一行数据分割开。具体代码实现如下:

void map() {
	system("CLS"); //这一行是清屏的代码
	int row = 0;
	int col = 0;
	for (row = 0; row < ROW; row++) {
		for (col = 0; col < COL; col++) {
			if (col < COL - 1) {
				printf(" %c |", arr[row][col]);
			}
			else {
				printf(" %c \n", arr[row][col]);
			}
		}
		if (row < ROW - 1) {
			for (col = 0; col < COL; col++) {
				printf("----");
			}
		}
		printf("\n");
	}
}

当我们把打印地图的函数写到我们的游戏主函数中去时,它的效果是这样的:

#include<stdio.h>
#include"game.h"
int main() {

    int input = 0; //让玩选择1 play 或者 0 exit

    do {
        game_head();
        printf("请输入1开始游戏>>");
        scanf("%d", &input);
        switch (input) {
        case 1:
            map(); // 调用打印棋盘的函数
            break;
        case 0:
            printf("游戏结束\n");
            break;
        default:
            printf("没有这个选项,请重新输入\n");
            break;
        }
    } while (input != 0);
}

解决了棋盘的事要怎么下棋呢?我们设计的函数是让用户输入要下的那个位置的坐标,然后就把数组中对应坐标的数据改成‘ X ’。要考虑几个特殊情况,就是用户输入的位置已经被下了,另一个情况就是用户输入的坐标根本不存在。函数的实现如下:

void play_game() {
	int x = 0;
	int y = 0;
	while (1) {
		printf("请输入坐标>");
		scanf("%d%d", &x, &y);
		if ((x >= 1 && x <= ROW) && (y >= 1 && y <= COL)) {
			if (arr[x - 1][y - 1] == ' ') {
				arr[x - 1][y - 1] = 'X';
				break;
			}
			else {
				printf("这个地方已经下过了,请重新输入\n");
			}
		}
		else {
			printf("输入不合法,请重新输入\n");
		}
	}
}

玩家落子之后,就该电脑下棋了,电脑我是用rand()函数来实现,rand函数的作用是生成随机数,在使用前要使用srand()函数来设置一下,要不然rand()函数是由规律的生成一些数,就不是随机的。电脑下棋的代码如下:

srand((unsigned int)time(NULL)); // 这段代码写在主函数中,这里是为了方便说明

void cmp_game() {
	int cmp_x = 0;
	int cmp_y = 0;
	while (1) {
		if (is_end() != -1) {
			break;
		}
		cmp_x = rand() * 10 % 3;
		cmp_y = rand() * 10 % 3;
		if (arr[cmp_x][cmp_y] == ' ') {
			arr[cmp_x][cmp_y] = 'O';
			break;
		}
	}
}

下棋的操作实现之后,就得看看谁输谁赢了,我们知道井字棋的输赢就是看哪一方先有三颗子连成一条线。这个数据我们可以在数组中得到,所以输赢的判断就是连数组中相邻的三个位置所存放的数据是否相等,然后返回那个数据,代码如下:

char is_win() {
	if ((arr[0][0] == arr[1][0] && arr[1][0] == arr[2][0] && arr[0][0] != ' ')||
		(arr[0][0] == arr[0][1] && arr[0][1] == arr[0][2] && arr[0][0] != ' ')) {
		return arr[0][0];
	}
	if ((arr[0][1] == arr[1][1] && arr[1][1] == arr[2][1] && arr[0][1] != ' ') ||
		(arr[1][0] == arr[1][1] && arr[1][1] == arr[1][2] && arr[1][0] != ' ') ||
		(arr[2][0] == arr[1][1] && arr[1][1] == arr[0][2] && arr[0][2] != ' ')||
		(arr[0][0] == arr[1][1] && arr[1][1] == arr[2][2] && arr[0][0] != ' ')) {
		return arr[1][1];
	}
	if ((arr[0][2] == arr[1][2] && arr[1][2] == arr[2][2] && arr[0][2] != ' ') ||
		(arr[2][0] == arr[2][1] && arr[2][1] == arr[2][2] && arr[2][0] != ' ')) {
		return arr[2][2];
	}
	return ' ';
}

最终将我们的主函数完善,因为下棋是一步一步下,所以我们就用一个循环将我们的下棋操作包起来:

#include "game.h"


int main() {

	int input = 0;
	int cmp_x = 0;
	int cmp_y = 0;
	srand((unsigned int)time(NULL));

	do {
		game_head();
		printf("输入1开始游戏>>");
		scanf("%d", &input);
		InitMap(arr, ROW, COL);
		switch (input) {
		case PLAY:
			map();
			while (is_end() == -1) {

				play_game();
				map();
				cmp_game();
				map();
			}
			if (is_win() == 'X') {
				printf("玩家获胜\n");
			}
			else if (is_win() == 'O') {
				printf("电脑获胜\n");
			}
			else {
				printf("平局\n");
			}
			
			break;
		case EXIT:
			printf("游戏结束!\n");
			break;
		default:
			printf("输入不合法,请重新输入\n");
		}
	} while (input != 0);
	return 0;
}

如果需要完整的代码可以访问我的gitee:

https://gitee.com/damiing/game

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C语言小游戏——井字棋(数组实现) 的相关文章

  • 蓝桥杯—单片机第十四届底层驱动函数修改

    本文总结了关于14届蓝桥杯单片机底层驱动文件相较13届的变化所需作出的改变 总体区别不大 欢迎批评指正 1 不再提供 h文件 故要牢记头文件书写格式 与引用格式 添加步骤 2 Onewire c文件 1 头文件添加 include reg5
  • 如何分解质因数

    如何分解质因数 质数 因数 合数 和倍数的知识 的结构图 在小学数学里 两个正整数相乘 那么这两个数都叫做积的因数 或称为约数 质数 是指只能被1和自己整除的自然数 其余的叫做合数 上小学的时候 我们就知道所有的 自然数可以分为质数 素数
  • MIPI CSI相关计算

    本文介绍MIPI CSI相关计算 MIPI CSI为目前Image Sensor和Host连接常用的接口 目前MIPI CSI最高传输速率为2 5Gbps lane 也有一些低版本的最高传输速率为1 5Gbps lane 我们在连接Imag
  • Ubuntu中安装Pytorch

    第一步 为PyTorch单独创建conda环境 需要先创建一个单独的conda环境 用于匹配对应的PyTorch版本 这一步不是必须的 但可以很方便的为PyTorch创建一个干净且独立的Python环境 这里使用比较稳定的3 6版本的Pyt
  • 利用python的matplotlib画图技巧

    目录 1 如何完全显示所有刻度 2 修改jupyter notebook输出图片大小 1 如何完全显示所有刻度 如果我们在没有设置坐标轴刻度的情况下 显示坐标情况及代码如下所示 import matplotlib pyplot as plt

随机推荐

  • sonarLint使用

    转载 sonarLint使用 mine wz的博客 CSDN博客
  • 深度学习(32)随机梯度下降十: 手写数字识别问题(层)

    深度学习 32 随机梯度下降十 手写数字识别问题 层 1 数据集 2 网络层 3 网络模型 4 网络训练 本节将利用前面介绍的多层全连接网络的梯度推导结果 直接利用Python循环计算每一层的梯度 并按着梯度下降算法手动更新 由于Tenso
  • Vue-router history模式下Nginx配置

    对于VUE的router mode history 模式 这里主要是为了去除链接上的 开发的时候 一般都不出问题 是因为开发时用的服务器为node Dev环境中已配置好了 nginx运行的时首页没有问题 链接也没有问题 但在点击刷新后 页面
  • 更改WPS云文档数据存储位置

    WPS的默认位置在C盘 当我们文档数据比较多 文件的体积也越来越大时 就会占用C盘的存储空间 下面为更改WPS云文档数据存储位置的具体步骤 双击 WPS网盘 进入WPS网盘界面 在界面右上方的 更多 中选择 同步与设置 选择 设置 在 缓存
  • 【计算机视觉笔记】图像检索学习 (Content Based Image Retrieval)

    论文跟踪 Awesome image retrieval papers https github com willard yuan awesome cbir papers 综述 SIFT Meets CNN A Decade Survey
  • 白嫖 GitHub Pages,轻松搭建个人博客

    Hexo 是一个快速 简单且功能强大的博客框架 使用 Markdown 解析文档 Hexo 能在几秒内生成带有自定义主题并集成各项功能的网站页面 本文采用 Github Pages Hexo 的方式 搭建个人博客 零 准备工作 1 使用个人
  • QT通过parentWidget()实现多界面跳转及关闭(切换界面和返回前一界面)

    QT通过parentWidget 实现多界面跳转及关闭 切换界面和返回前一界面 不需要跳转界面相互包含头文件 超简单的方法 先将第一级的窗口this指针传给次级的的窗口 子窗口通过parentWidget 返回第一级的this并用来调用sh
  • (五)复函数积分的定义与性质

    本文内容主要如下 1 复积分的概念 1 1 复积分的定义 1 2 复积分的存在性与计算 1 3 一个圆周上的重要积分公式 1 4 复积分的基本性质 1 复积分的概念 1 1 复积分的定义 定义 如图 C为平面上一条光滑的简单曲线 z z
  • k8s集群pod直接无法ping通

    简介 基于OpenStack云主机搭建的k8s集群 网络CNI选用的calico 使用的是BGP模式 pod直接无法ping通 宿主机也无法ping通pod 排除后发现不是安全组的原因 应该是OpenStack的网卡默认会丢掉非本网卡ip地
  • WSL2-解决无法ping通主机/配置使用主机代理

    WSL2无法ping通主机 省流 如果主机能ping通wsl从机 从机ping不通主机 大概率是防火墙拦截了 可以以管理员身份在powershell中运行下面的代码 或者手动在防火墙设置中添加相应的出入站规则 New NetFirewall
  • 音频-基于Core Audio技术采集音频(版本1)

    这个是第一次版本优化 优化是简单易懂 代码 WindowsAudioSession cpp 基本的利用WAS采集音频的demo include
  • extern关键字作用

    语法 extern放在变量和函数声明之前 表示该变量或者函数在别的文件中已经定义 提示编译器在编译时要从别的文件中寻找 除此之外 extern还可以用来进行链接指定 作用 声明外部变量 在声明全局变量时 不同的文件在编译器编译时是不透明的
  • 字节流与字符流

    流是个抽象的概念 是对输入输出设备的抽象 输入流可以看作一个输入通道 输出流可以看作一个输出通道 输入流是相对程序而言的 外部传入数据给程序需要借助输入流 输出流是相对程序而言的 程序把数据传输到外部需要借助输出流 字节流 传输过程中 传输
  • java基础系列 -- 类的三大特性:封装、继承、多态

    java类的特性 类有三大特性 封装 继承 多态 封装 封装就是将类的某些属性隐藏起来 限制在类的外部对类内部成员进行访问 通过接口对外开放 但是在外部不能直接进行查找属性 只通过公共接口来访问类的成员数据 为什么要设置隐藏 隐藏数据是为了
  • jpa.hibernate.ddl-auto属性说明

    jpa hibernate ddl auto 的几个常用属性值 none 默认值 什么都不做 每次启动项目 不会对数据库进行任何验证和操作 create 每次运行项目 没有表会新建表 如果表内有数据会被清空 create drop 每次程序
  • nginx_http_proxy,upstream,stream模块简析

    一 ngx http proxy module模块 模块功能 为后端httpd服务做反向代理 并且与Httpd 之间使用http进行通信 1 proxy pass URL Context location if in location li
  • 如何求C语言字符串长度(strlen函数和sizeof关键字)

    如何求C语言字符串长度 strlen函数和sizeof关键字 在程序里 一般会用 strlen 函数或 sizeof 来获取一个字符串的长度 但这2种方法获取的字符串的长度其实是不一样 我们用如下函数进行测试 void test6 char
  • Java8新特性-Lambda表达式

    Lambda表达式 也可称为闭包 它是推动Java8发布的最重要的特性 Lambda允许把函数作为一个方法的参数 函数作为参数传递进入方法中去 使用lambda表达式可以把代码变得更加简洁紧凑 语法 lambda表达式的语法格式如下 par
  • Ciclop开源3D扫描仪软件---Horus源码分析之point_cloud_roi.py

    联系方式 QQ 2468851091 call 18163325140 Email 2468851091 qq com coding utf 8
  • C语言小游戏——井字棋(数组实现)

    学c也学了有一些时间了 今天用c语言做了一个小游戏 井字棋 相信大家也玩过 我们这个游戏的思路呢 是玩家和电脑对弈 谁先把三颗棋子连成一条线 谁就赢了 如下图所示 要想实现我们这个井字棋需要用到数组的知识 所以 老规矩我们先简单的把数组讲一