C语言——简单三子棋小游戏

一、分模块

整体构思

二、具体实现

1、声明模块

这部分放在了game.h 文件里,主要是对用到的头文件、函数和 #define 常量进行声明,声明完之后其他文件只要包含这个头文件就可以使用这里出现的所有东西。下面把这部分代码作一个展示:

#define _CRT_SEECURE_NO_WARNINGS

//提前定义三子棋的行列值,这样想变成其他规模只要在这里修改就好
#define ROW 3
#define COL 3

#include<stdio.h>
#include<time.h>     //为了生成随机数,使用time()时间戳函数
#include<stdlib.h>    //为了使用随机数rand
#include<string.h>

void InitBoard(char arr[ROW][COL], int row, int col);
void PrintBoard(char arr[ROW][COL], int row, int col);
void Player(char arr[ROW][COL]);
void Computer(char arr[ROW][COL]);
char JudgeResult(char arr[ROW][COL]);
void game();

2、测试模块

这部分放在了test.c 文件里,主要实现的是玩家打开游戏之后,出现在屏幕上的逻辑构想。

首先,菜单的打印不需要实现什么逻辑,所以我就把它和主函数放在了一个源文件里面。然后对游戏的选择我使用了while语句嵌套switch语句的常见套路。代码如下:

#define _CRT_SECURE_NO_WARNINGS
#include "game.h"
void menu() {
	int r = ROW;
	printf("________________________________________\n");
	printf("******** 欢迎进入%d子棋游戏!***********\n",r);
	printf("************ 玩游戏请输 1 **************\n");
	printf("************ 想退出请输 0 **************\n");
	printf("————————————————————\n");
	return;
}

int main() {
	srand((unsigned int)time(NULL));    //采用时间戳作为生成随机数的指标,随时在变
	int input = 0;
	do {
		menu();
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			game();
			break;
		case 0:
			printf("谢谢使用,已退出!\n");
			break;
		default:
			printf("只能输入1/0,请重新输入!\n");
			printf("\n");
			break;
		}
	} while (input);
	return 0;
}

3、实现模块

这部分放到了 game.c 文件里,主要实现的是整个游戏需要的所有逻辑实现。下面具体说一下实现:

(1)主要框架

首先我把这部分作一个流程图:
主要流程实现这一流程的就是game函数,游戏的进行通过while循环实现,循环条件设置为永真,在循环体内部通过判断结果的函数返回值得到游戏结果之后,直接使用break跳出,再在下面使用 if 语句判断具体情况就好。下面看一下它的实现代码及逻辑:

void game() {
	char board[ROW][COL];    //定义棋盘数组
	char result = 0;
	InitBoard(board, ROW, COL);    //初始化棋盘,默认为空格
	PrintBoard(board, ROW, COL);    //将棋盘打印到屏幕上
	while (1) {
		Player(board);    //玩家走
		PrintBoard(board, ROW, COL);    //将棋盘打印到屏幕上
		result = JudgeResult(board);    //判断输赢
		if (result != 'N') {
			break;
		}
		Computer(board);    //电脑走
		PrintBoard(board, ROW, COL);    //将棋盘打印到屏幕上
		result = JudgeResult(board);    //判断输赢
		if (result != 'N') {
			break;
		}
		printf("继续,");
	}
	if (result == 'P') {
		printf("玩家获胜!\n");
	}
	else if (result == 'C') {
		printf("电脑获胜!\n");
	}
	else {
		printf("平局!\n");
	}
	return;
}

(2)棋盘初始化

一开始,没有下子的时候棋盘数组应该是空的,这样玩家看着空位再去下子。为了实现空白,初始化时候给每个数组元素赋值空格。

void InitBoard(char arr[ROW][COL], int row, int col) {
	int i = 0;
	int j = 0;
	for (i = 0; i < row; i++) {
		for (j = 0; j < col; j++) {
			arr[i][j] = ' ';
		}
	}
	return;
}

(3)棋盘打印

说实话,整个游戏最麻烦的地方就是这里。为了让一个整齐美观的棋盘出现在屏幕上,我真可谓是煞费苦心。
棋盘设计
这就是最后实现出来的样子。

  • 对于行来说
    每行分成两行,第一行是空格+棋子+空格+间隔线;第二行是横线+横线+横线+间隔线
  • 对于列来说
    每列分成两列,第一列是是空格+横线;第二列是间隔线

它让人头疼的点是,为了整体美观,需要去掉最后一行的横线+横线+横线+间隔线,去掉最后一列的间隔线。

所采用的办法就是表示行的循环嵌套条件语句区分最后一行和前面,然后在两种不同的行里再嵌套列的循环语句,里面再嵌套条件语句区分最后一列和前面。下面是代码实现:

void PrintBoard(char arr[ROW][COL], int row, int col) {
	int i = 0;
	int j = 0;
	for (i = 0; i < row; i++) {
		if (i < row - 1) {    //前两行
			for (j = 0; j < col; j++) {
				if (j < col - 1) {    //前两列
					printf(" %c ", arr[i][j]);
					printf("|");
				}
				else {    //前两行最后一列,不需要竖线分割
					printf(" %c ", arr[i][j]);
				}
			}
			printf("\n");
			for (j = 0; j < col; j++) {
				if (j < col - 1) {    //前两列
					printf("___");
					printf("|");
				}
				else {    //前两行最后一列,不需要竖线分割
					printf("___");
				}
			}
			printf("\n");
		}
		else {    //最后一行,不需要横线分割
			for (j = 0; j < col; j++) {
				if (j < col - 1) {    //前两列
					printf(" %c ", arr[i][j]);
					printf("|");
				}
				else {    //最后一列,不需要竖线分割
					printf(" %c ", arr[i][j]);
				}
			}
			printf("\n");
			for (j = 0; j < col; j++) {    
				if (j < col - 1) {    //前两列
					printf("   ");
					printf("|");
				}
				else {    //最后一列,不需要竖线分割
					printf("   ");
				}
			}
			printf("\n");
		}
    }
	printf("\n");
	return;
}

(4)玩家走子

这部分主要需要注意的就是,要做条件判断,当玩家输入的行列值在数组范围里,并且该行列值确定的棋格也未下子,再把子下进去;否则,就给出提示,要求玩家重新输入。具体实现看下面:

void Player(char arr[ROW][COL]) {
	int row = 0;
	int col = 0;
	while (1) {
		printf("请玩家下子:");
		scanf("%d,%d", &row, &col);
		printf("\n");
		if (row >= 1 && row <= ROW && col >= 1 && col <= COL && arr[row - 1][col - 1] == ' ') {    //输入合法且无值
			arr[row - 1][col - 1] = '*';    //玩家下子
			break;
		}
		else {
			printf("检查该位置是否已有子或者输入子超出棋盘范围!重新");
		}
	}  
	return;
}

(5)电脑走子

这部分要实现电脑走子,最重要的就是输入的值。

在这里我采用了rand函数,在主函数里,以时间戳为基准指标,加上一句 “ srand((unsigned int)time(NULL)); ” 保证值是随机产生,再取余行列值保证值都是合法且在棋盘数组范围内。

因为经过取余值一定合法,所以再条件判断一下值对应的盘格是否为空,为空,就可以下子;反之,让电脑重新输入。

具体实现如下:

void Computer(char arr[ROW][COL]) {
	printf("电脑下子:\n");
	printf("\n");
	do {
		int row = rand() % ROW;
		int col = rand() % COL;
		if (arr[row][col] == ' ') {
			arr[row][col] = '#';    //电脑下子
			break;
		}
	} 	while (1);
	return;
}

(6)判断结果

每次下完子之后,都会出现四个结果:玩家赢、电脑赢、棋盘下满没有输赢平局、棋盘没有下满继续下子。

所以我就以设返回值是字符型,以返回值的形式去表面这四种结果:

  • 返回值为’P’ ——player,玩家赢
  • 返回值为’C’—— computer,电脑赢
  • 返回值为’D’ ——draw,平局
  • 返回值为’N’ ——no,棋盘未满,继续

然后判断赢有四种可能,同行同列,主副对角线这四种,我就使用 if 语句去判断,如果都不是的话,那要么平局,要么没结果。

在没有输赢的情况下,就是判断盘格是否下满,考虑到可读性,我把这个单独拎出来写了一个函数实现这一功能。设立整型返回值,若还有位置返回 1 ,没有则返回 0 。因为只在game.c这个源文件里使用,所以我又用关键字 static 限制了它的使用范围。判断棋盘是否已满的代码实现如下:

static int JudgeFull(char arr[ROW][COL]) {   
	int i = ROW;
	int j = COL;
	for(i = 0; i < ROW; i++) {
		for (j = 0; j < COL; j++) {
			if (arr[i][j] == ' ') {
				return 1;
			}
		
		}
	}
	return 0;
}

然后判断盘格的函数实现了之后,再调用它到判断结果的函数中,代码实现如下:

char JudgeResult(char arr[ROW][COL]) {
	int i = 0;
	for (i = 0; i < ROW; i++) {
		if (arr[i][0] == arr[i][1] && arr[i][1] == arr[i][2] && arr[i][0] != ' ') {    //同行
			if (arr[i][0] == '*') {
				return 'P';
			}
			else {
				return 'C';
			}
		}
		if (arr[0][i] == arr[1][i] && arr[1][i] == arr[2][i] && arr[0][i] != ' ') {    //同列
			if (arr[i][0] == '*') {
				return 'P';
			}
			else {
				return 'C';
			}
		}
		if (arr[0][0] == arr[1][1] && arr[1][1] == arr[2][2] && arr[0][0] != ' ') {    //主对角线
			if (arr[i][0] == '*') {
				return 'P';
			}
			else {
				return 'C';
			}
		}
		if (arr[0][2] == arr[1][1] && arr[1][1] == arr[2][0] && arr[0][2] != ' ') {    //副主对角线
			if (arr[i][0] == '*') {
				return 'P';
			}
			else {
				return 'C';
			}
		}
	}
	int ret = JudgeFull(arr);
	if (ret) {    //棋盘有空,继续
		return 'N';
	}
	else {
		return 'D';    //棋盘输满,平局
	}
}

三、收获

哈哈哈,把自己的编程记录写的像实验报告一样也是没谁了。写完之后,自己就先玩了一把。感觉还真是不一样,再想一想现在同龄人玩的游戏,我的编程之路道阻且长呀!

然后呢,如果你也想尝试一下的话,欢迎来玩我的小游戏!!!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值