当前位置: 首页 > news >正文

C语言---井字棋(三子棋)

Tic-Tac-Toe

  • 1 游戏介绍和随机数
    • 1.1 游戏介绍
    • 1.2 随机数的生成
    • 1.3 棋盘大小和符号
  • 2 设计游戏
    • 2.1 初始化棋盘
    • 2.2 打印棋盘
    • 2.3 玩家下棋
    • 2.4 电脑下棋
    • 2.5 判断输赢
    • 2.6 game()函数
    • 2.7 main()函数
  • 3 完整三子棋代码
    • 3.1 Tic_Tac_Toe.h
    • 3.2 Tic_Tac_Toe.c
    • 3.3 Test.c
  • 4 游戏代码的缺陷


1 游戏介绍和随机数

1.1 游戏介绍

井字棋,英文名叫Tic-Tac-Toe,是一种在3*3格子上进行的连珠游戏,由于棋盘一般不画边框,格线排成井字故得名。游戏需要的工具仅为纸和笔,然后由分别代表O和X的两个游戏者轮流在格子里留下标记(一般来说先手者为X),任意三个标记形成一条直线,则为获胜。
点此进入井字棋链接

我和电脑打成平局
自己跟自己下棋,我赢了

1.2 随机数的生成

  • 1 rand()函数
  • int rand(void)

生成随机数
返回0到RAND_MAX之间的伪随机整数。(RAND_MAX是32767,二进制原码最大为0111111111111111=215-1=32767)
这个数字是由一个算法生成的,该算法每次被调用时都会返回一个明显不相关的数字序列。该算法使用种子来生成序列,该序列应使用函数srand初始化为某个独特的值。
RAND_MAX是中定义的常数。
使用rand在确定的范围内生成平凡伪随机数的典型方法是使用返回值乘以范围跨度的模,并添加范围的初始值:

v1 = rand() % 100;         // v1 范围 0 to 99
v2 = rand() % 100 + 1;     // v2 范围 1 to 100
v3 = rand() % 30 + 1985;   // v3 范围 1985-2014

请注意,这种取模运算并不会在跨度中生成均匀分布的随机数。

  • 2 sand函数是用来初始化随机数生成器的
  • void srand(unsigned int seed);
  • 在调用rand前调用srand,修改seed参数生成随机数种子
int main()
{void srand(unsigned int seed);return 0;
}
  • 3 time()
    time函数可以获得时间,需要引用头文件time.h
time_t time(time_t* timer);
  • 生成真的随机数
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<time.h>
#include<stdlib.h>
int main()
{srand((unsigned int)time(NULL));//srand 的参数是unsigned int 类型,强制转换,//time(NULL)返回一段时间戳printf("%d\n", rand());printf("%d\n", rand());printf("%d\n", rand());printf("%d\n", rand());printf("%d\n", rand());return 0;
}

在这里插入图片描述

1.3 棋盘大小和符号

Tic-Tac-Toe.h中定义棋盘行列大小,玩家和电脑的符号,方便后续修改

#define ROW 3
#define COL 3#define Player_Choice 'X'
#define Computer_Choice 'O'
//#define Player_Choice '√'
//#define Computer_Choice '×'

2 设计游戏

2.1 初始化棋盘

将二维数组的所有元素定义为“空”,等待后续放入Player_ChoiceComputer_Choice

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] = ' ';}}
}

2.2 打印棋盘

我们朝向这个%c | %c | %c \n---|---|---\n打印,设计棋盘形状。
后续改变棋盘大小可以通过更改ROWCOL快速更改。
我们以%c |为一组,在最后一组是没有|的,---|也是同理。需要单独判断

//2 打印棋盘
void DispalyBoard(char board[ROW][COL], int row, int col)
{int i = 0;for (i = 0; i < row; i++){//打印数据int j = 0;for (j = 0; j < col; j++){printf(" %c ", board[i][j]);if (j < col - 1)printf("|");}printf("\n");//打印分割信息if (i < row - 1){int j = 0;for (j = 0; j < col; j++){printf("---");if (j < col - 1)printf("|");}printf("\n");}}
}

在这里插入图片描述
在这里插入图片描述

2.3 玩家下棋

有以下3点要注意
1 输入的坐标格式
2 输入的坐标是否被占用
3 输入的坐标范围是否正确

void PlayerMove(char board[ROW][COL], int row, int col)
{int x = 0;int y = 0;printf("玩家下棋:\n");while (1){printf("请输入坐标:>");scanf("%d %d", &x, &y);//坐标范围合法的判断if (x >= 1 && x <= row && y >= 1 && y <= col){if (board[x - 1][y - 1] == ' '){board[x - 1][y - 1] = Player_Choice;break;}else{printf("坐标被占用,不能下棋,请选择其他位置\n");}}else{printf("坐标非法,请重新输入\n");}}
}

在这里插入图片描述

2.4 电脑下棋

为了让电脑生产真正的随机数,我们前面已经讲了如何做。

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;if (board[x][y] == ' '){board[x][y] = Computer_Choice;break;}}
}

在这里插入图片描述

2.5 判断输赢

判断输赢要从三方面,行,列,对角线。分三种情况判定。
先判断棋盘是否填满。
棋盘满了返回1,判断平局和胜负,
棋盘不满返回0,游戏继续进行,知道决出结局。

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;}}}return 1;
}

我们规定
玩家赢的时候,IsWin函数返回符号 '*'
电脑赢的时候,IsWin函数返回符号 '#'
平局的时候,IsWin函数返回符号 'Q'
没结果还要继续时候,IsWin函数返回符号 'C'
因此,规定IsWin 函数返回类型是char

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][1] != ' '){return board[i][1];}}//列int j = 0;for (j = 0; j < col; j++){if (board[0][j] == board[1][j] && board[1][j] == board[2][j] && board[1][j] != ' '){return board[1][j];}}//对角线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)){return 'Q';}//游戏继续return 'C';
}

在这里插入图片描述

2.6 game()函数

game()函数放在Test.c中。一旦选择了1 开始游戏,启动game()函数。
一定注意下棋的逻辑顺序。
初始化棋盘后,放置“空”元素进去。
开始下棋后,我们规定玩家先走,电脑后走。
玩家走一步,判断输赢,展示棋盘,电脑再走,再判断输赢。
第一回合是没必要输赢。但是不妨碍游戏逻辑。

玩家赢 - IsWin函数返回 - Player_Choice
电脑赢 - IsWin函数返回 - Computer_Choice
平局 - IsWin函数返回 - 'Q'

void game()
{char result = 0;char board[ROW][COL] = { 0 };//初始化棋盘的函数InitBoard(board, ROW, COL);DispalyBoard(board, ROW, COL);//下棋while (1){PlayerMove(board, ROW, COL);//判断输赢result = IsWin(board, ROW, COL);if (result != 'C'){break;}DispalyBoard(board, ROW, COL);ComputerMove(board, ROW, COL);//判断输赢result = IsWin(board, ROW, COL);if (result != 'C'){break;}DispalyBoard(board, ROW, COL);}if (result == Player_Choice){printf("你赢了\n");}else if (result == Computer_Choice){printf("电脑赢了\n");}else{printf("你和电脑打成平局\n");}DispalyBoard(board, ROW, COL);
}

2.7 main()函数

其实main()函数一开始就写了,笔者最后以是修改main()结尾的,就把它放最后了。
设置随机数的生成起点的。srand((unsigned int)time(NULL));我忘了这个逻辑。电脑生产伪随机数。
每次进行游戏时,电脑都是固定位置,没有意义。

int main()
{srand((unsigned int)time(NULL));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);return 0;
}

在这里插入图片描述

3 完整三子棋代码

主要利用二维数组实现井字棋,主要分以下三个模块。
Test.c负责测试游戏的逻辑
Tic_Tac_Toe.c 负责游戏代码的实现
Tic_Tac_Toe.h 负责游戏代码的声明

3.1 Tic_Tac_Toe.h

#pragma once
#define _CRT_SECURE_NO_WARNINGS 1#include <stdio.h>
#include <stdlib.h>
#include <time.h>#define ROW 3
#define COL 3#define Player_Choice 'X'
#define Computer_Choice 'O'//#define Player_Choice '√'
//#define Computer_Choice '×'//1 初始化棋盘
void InitBoard(char board[ROW][COL], int row, int col);//2 打印棋盘
void DispalyBoard(char board[ROW][COL], int row, int col);//3 玩家下棋
void PlayerMove(char board[ROW][COL], int row, int col);//4 电脑下棋
void ComputerMove(char board[ROW][COL], int row, int col);//5 判断输赢
char IsWin(char board[ROW][COL], int row, int col);

3.2 Tic_Tac_Toe.c

#include "Tic_Tac_Toe.h"//1 初始化棋盘
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] = ' ';}}
}//第一个版本
//void DispalyBoard(char board[ROW][COL], int row, int col)
//{
//	int i = 0;
//	for (i = 0; i < row; i++)
//	{
//		//打印数据
//		printf(" %c | %c | %c \n", board[i][0], board[i][1], board[i][2]);
//		//打印分割信息
//		if(i<row-1)
//			printf("---|---|---\n");
//	}
//}//2 打印棋盘
void DispalyBoard(char board[ROW][COL], int row, int col)
{int i = 0;for (i = 0; i < row; i++){//打印数据//printf(" %c | %c | %c \n", board[i][0], board[i][1], board[i][2]);int j = 0;for (j = 0; j < col; j++){printf(" %c ", board[i][j]);if (j < col - 1)printf("|");}printf("\n");//打印分割信息//printf("---|---|---\n");if (i < row - 1){int j = 0;for (j = 0; j < col; j++){printf("---");if (j < col - 1)printf("|");}printf("\n");}}
}//3 玩家下棋
void PlayerMove(char board[ROW][COL], int row, int col)
{int x = 0;int y = 0;printf("玩家下棋:\n");while (1){printf("请输入坐标(中间以空格分开):");scanf("%d %d", &x, &y);//坐标范围合法的判断if (x >= 1 && x <= row && y >= 1 && y <= col){if (board[x - 1][y - 1] == ' '){board[x - 1][y - 1] = Player_Choice;break;}else{printf("坐标被占用,不能下棋,请选择其他位置\n");}}else{printf("坐标非法,请重新输入\n");}}
}//4 电脑下棋
void ComputerMove(char board[ROW][COL], int row, int col)
{printf("电脑下棋:\n");int x = 0;int y = 0;while (1){x = rand() % row;//0~2y = rand() % col;//0~2if (board[x][y] == ' '){board[x][y] = Computer_Choice;break;}}
}//5 判断输赢
//棋盘满了就返回1
//棋盘不满返回0
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;}}}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][1] != ' '){return board[i][1];}}//列int j = 0;for (j = 0; j < col; j++){if (board[0][j] == board[1][j] && board[1][j] == board[2][j] && board[1][j] != ' '){return board[1][j];}}//对角线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)){return 'Q';}//游戏继续return 'C';
}//返回  Player_Choice 玩家赢
//返回  Computer_Choice 玩家赢
//返回  Q 平局
//返回  C 继续

3.3 Test.c

#include "Tic_Tac_Toe.h"void menu()
{printf("*****************************\n");printf("***********1 开始游戏*********\n");printf("***********0 退出游戏*********\n");printf("*****************************\n");
}void game()
{char result = 0;char board[ROW][COL] = { 0 };//初始化棋盘的函数InitBoard(board, ROW, COL);DispalyBoard(board, ROW, COL);//下棋while (1){PlayerMove(board, ROW, COL);//判断输赢result = IsWin(board, ROW, COL);if (result != 'C'){break;}DispalyBoard(board, ROW, COL);ComputerMove(board, ROW, COL);//判断输赢result = IsWin(board, ROW, COL);if (result != 'C'){break;}DispalyBoard(board, ROW, COL);}if (result == Player_Choice){printf("----------你赢了----------\n");}else if (result == Computer_Choice){printf("----------电脑赢了----------\n");}else{printf("----------平局----------\n");}DispalyBoard(board, ROW, COL);
}int main()
{srand((unsigned int)time(NULL));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);return 0;
}

4 游戏代码的缺陷

这个电脑是随便下棋的,它没有动脑子,每次生产随机的坐标,不把我们放心上。不追求矢量直线。
你说如果棋盘再大一带你的,电脑也知道适量原则。人下棋的胜率会低多少呢?
不过想要和电脑下平局还是很难的。因为我总要电脑着我。

http://www.lryc.cn/news/264582.html

相关文章:

  • [Kubernetes]3. k8s集群Service详解
  • C++ 指定范围内递增初始化一个vector<int> | Python: list(range(31, 90))
  • 【Java之数据结构与算法】
  • 爬虫scrapy中间件的使用
  • 普冉(PUYA)单片机开发笔记 [完结篇]:使用体会
  • Elasticsearch:生成 AI 中的微调与 RAG
  • ip静态好还是dhcp好?
  • PolarDB-X、OceanBase、CockroachDB、TiDB二级索引写入性能测评
  • Convolutional Neural Network(CNN)——卷积神经网络
  • 鸿蒙开发基本概念
  • Open CV 图像处理基础:(二)从基本概念到实践操作
  • 【MAC】M2 安装docker 与 mysql
  • 轻量级web开发框架Flask本地部署及无公网ip远程访问界面
  • 用最通俗的语言讲解 TCP “三次握手,四次挥手”
  • 使用RedisCacheWriter#clean在线异步地批量扫描匹配删除缓存数据-spring-data-redis
  • 机器视觉:AI赋能缺陷检测,铸就芯片产品的大算力与高能效
  • (9)Linux Git的介绍以及缓冲区
  • 华为云之ECS云产品快速入门
  • tcp 的限制 (TCP_WRAPPERS)
  • 如何保证架构的质量
  • JavaWeb笔记之前端开发JavaScript
  • SCAU:18063 圈中的游戏
  • .NET Core中鉴权 Authentication Authorization
  • PyTorch深度学习实战(26)——卷积自编码器(Convolutional Autoencoder)
  • Milvus实战:构建QA系统及推荐系统
  • 使用Docker部署Nexus Maven私有仓库并结合Cpolar实现远程访问
  • GEE-Sentinel-2月度时间序列数据合成并导出
  • 【深度学习】语言模型与注意力机制以及Bert实战指引之二
  • 计算机网络 网络层下 | IPv6 路由选择协议,P多播,虚拟专用网络VPN,MPLS多协议标签
  • 【MATLAB第83期】基于MATLAB的LSTM代理模型的SOBOL全局敏感性运用