三子棋游戏教学(c语言)

我们所要实现的目标
在这里插入图片描述

步骤

我的设计思路是想想函数怎么使用,再来实现函数
提醒: 实现这个游戏,我们为了逻辑清晰一点,需要两个源文件和一个头文件.
test.c文件主要包含main()函数,menu()菜单,game()函数
game.c文件阐述了game()函数中所需要的一系列函数
game.h文件声明了被调用的函数,声明所要用的头文件,定义了两个宏:
ROW代表行,COL代表列

game.h

#define ROW 3
#define COL 3

输出一个简易的菜单

菜单不需要接收和返回任何值

test.c

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

首先,无论我们玩不玩游戏,菜单总是要打印的,这里我们使用do while函数.

并且在外面看到菜单时我们要选择play或者exit.而switch case语句很好地将各种情况涵盖到了.while的判断语句(input)的设计是非常巧妙的,如果是0,while循环结束.非0则循环继续.

test.c

int main() {
	int input = 0;
	do
	{
		menu();
		printf("请输入:>");
		scanf("%d", &input);
		switch (input) {
		case 1:
			game();
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default:
			printf("输入非法,请重新输入\n");
		}
	} while (input);
	return 0;
}

如何写game()函数

1.打印一个数组并初始化
2.打印出初始游戏界面
在这里插入图片描述
3.玩家下棋并打印下棋后的棋盘在这里插入图片描述
4.判断游戏是否结束 (4和6类似,故只解释一个)
5.电脑下棋并打印下棋后的棋盘
6.判断游戏是否结束
然后判断游戏是否结束.如果没有结束,上面的3-6步骤继续循环

test.c

void game()
{
	char ret = 0;
	char board[ROW][COL];
	//初始化棋盘
	Initboard(board, ROW, COL);
	Displayboard(board, ROW, COL);
	while (1) {
		Playermove(board, ROW, COL);//玩家下棋
		Displayboard(board, ROW, COL);
		ret = Iswin(board, ROW, COL);
		if (ret != 'C') {
			break;
		}
		Computermove(board, ROW, COL);//电脑下棋
		Displayboard(board, ROW, COL);
		ret = Iswin(board, ROW, COL);
		if (ret != 'C') {
			break;
		}
	}
	if (ret == '*') {
		printf("玩家赢\n");
	}
	else if (ret == '#') {
		printf("电脑赢\n");
	}
	else {
		printf("平局\n");
	}
}

在这里判断游戏是否结束是用ret接收Iswin函数的return 的东西
玩家赢 用 ‘*’ 表示
电脑赢 用 '#‘表示
平局 用 ‘Q’ 表示
继续 用’C’ 表示

`

1.定义一个二维数组并将它初始化为0

game.c

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

数组传参传的是数组首元素的地址,所以需要传数组的行和列
定义i和j,遍历让字符数组全部初始化为空格
为什么要初始化为空格呢?
我们需要一个字符来占领那个位置,却又不是太明显,只能是字符了.

2.打印初始游戏界面

目标
在这里插入图片描述
我们先来想想怎么打印初始游戏界面.
首先要进行数组元素的打印,再在每个数组元素之间用分割符隔开.显然竖分隔符的打印比列数少一
然后打印横的分隔符,横分割符同样比行数少一.在打印横分割符中要间插着打印竖分隔符.
记得打印完每一行元素要换行
game.c

void Displayboard(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++) {
			printf(" %c ", board[i][j]);
			if (j < col - 1)
				printf("|");
		}//打印数组中的元素
		printf("\n");
		if(i<row-1)
		{ 
			for (j = 0; j < col; j++) {
				printf("---");
				if (j < col - 1)
					printf("|");
			}//打印下面的分割符
			printf("\n");
		}
	}
}

3.把玩家下的棋进行记录(用 ‘*’ 表示)

game.c

void Playermove(char board[ROW][COL], int row, int col) 
{
	printf("玩家下棋\n");
	printf("请输入坐标,以空格分开:");
	int x = 0, y = 0;
	while (1)
	{
		scanf("%d %d", &x, &y);
		if (x >= 1 && x <= row && y >= 1 && y <= row) {
			if (board[x - 1][y - 1] == ' ')
			{
				board[x - 1][y - 1] = '*';
				break;
			}
			else
				printf("该坐标已被占用,请重新输入\n");
		}
		else
			printf("坐标非法,请重新输入\n");
	}
}

一样,玩家下棋这个函数不需要返回和接受什么.
将玩家下的坐标记录起来.但玩家可不是程序员.我们数组第一个元素的下标是0,而我们日常认为所用坐标是从1开始的.
所以需要x-1和y-1来将玩家所输入的坐标变成二维数组中的坐标.
注意
1.坐标是否越界
2.坐标是否已被占领
3.数组传参传的是数组首元素的地址,所以需要传数组的行和列

4.使用一系列条件判断游戏的输赢,平局or继续

game.c

//玩家赢 '*'
//电脑赢 '#'
//平局 'Q';
//继续 'C'
char Iswin(char board[ROW][COL], int row, int col) 
{
	int i = 0, j = 0;
	for (i = 0; i < row; i++) {
		if (board[i][0] == board[i][1] && board[i][1] == board[i][2] && board[i][0] != ' ') {
			return board[i][0];
		}
	}
	for (j = 0; j < col; j++) {
		if (board[0][j] == board[1][j] && board[1][j] == board[2][j] && board[0][j] != ' ') {
			return board[0][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';

}

如果游戏有人赢,那么就是在三横,三竖和交叉中有三个棋子相同且不为空格. 代码中采用
玩家赢 用 ‘*’ 表示
电脑赢 用 '#‘表示
平局 用 ‘Q’ 表示
继续 用’C’ 表示

在每一行,每一列进行遍历,找出有没有人赢
再判断是否平局,这里我们用Isfull返回的值来判断
如果不是平局,那么只剩下最后一种可能–游戏继续
game.c

static int Isfull(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++) {
			if (board[i][j] == ' ')
				return 0;
		}
	}
	return 1;
}

Isfull
在每一行,每一列寻找是否有位置还未被占领,如果有,返回0.
如果全部位置都满了,返回1使if判断语句成立,返回 ‘Q’ 到game()函数里面.
因为Isfull函数是为了判断数组是否已经被棋子填满的(不是很重要),所以不需要在头文件中被包含.故用static修饰这个函数,把它留在静态区.

5.把电脑下的棋进行记录 ‘#’

game.c

void Computermove(char board[ROW][COL], int row, int col)
{
	printf("电脑下棋\n");
	int x = 0, y = 0;
	while (1) {
		x = rand() % 3;
		y = rand() % 3;
		if (board[x][y] == ' ')
		{
			board[x][y] = '#';
			break;
		}
	}
}

和上面玩家下棋的步骤类似,但是在这里我们需要一个函数来随机确定x和y的值.用rand()函数可以.但是要记得在main函数加一句
test.c

srand((unsigned int)time(NULL));

记得引用头文件
这里的头文件包括rand和time(rand和srand是同一个)
game.h

#include<time.h>
#include<stdlib.h>

简单来说就是rand()函数的使用需要srand()来设置一个起点,而srand需要time来返回一个随时都在变化的东西-----时间.

总:将上面的函数有规律的组装起来

game.h文件

#pragma once
#include<stdio.h>
#include<time.h>
#include<stdlib.h>
#define ROW 3
#define COL 3

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

在game.h头文件中,
1.将之前使用过的三个头文件进行包含
2.再把行和列分别用宏定义
3.把所有需要调用的函数在头文件中进行声明

test.c文件

#define _CRT_SECURE_NO_WARNINGS
#include "game.h"
void game()
{
	char ret = 0;
	char board[ROW][COL];
	//初始化棋盘
	Initboard(board, ROW, COL);
	Displayboard(board, ROW, COL);
	while (1) {
		Playermove(board, ROW, COL);//玩家下棋
		Displayboard(board, ROW, COL);
		ret = Iswin(board, ROW, COL);
		if (ret != 'C') {
			break;
		}
		Computermove(board, ROW, COL);//电脑下棋
		Displayboard(board, ROW, COL);
		ret = Iswin(board, ROW, COL);
		if (ret != 'C') {
			break;
		}
	}
	if (ret == '*') {
		printf("玩家赢\n");
	}
	else if (ret == '#') {
		printf("电脑赢\n");
	}
	else {
		printf("平局\n");
	}
}
void menu() {
	printf("**********************\n");
	printf("*****  1. play  ******\n");
	printf("*****  0. exit  ******\n");
	printf("**********************\n");
}
int main() {
	int input = 0;
	srand((unsigned int)time(NULL));
	do
	{
		menu();
		printf("请输入:>");
		scanf("%d", &input);
		switch (input) {
		case 1:
			game();
			break;
		case 0 :
			printf("退出游戏\n");
			break;
		default:
			printf("输入非法,请重新输入\n");
		}
	} while (input);
	return 0;
}

要注意引用game.h头文件

game.c文件

#define _CRT_SECURE_NO_WARNINGS
#include "game.h"
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 Displayboard(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++) {
			printf(" %c ", board[i][j]);
			if (j < col - 1)
				printf("|");
		}
		printf("\n");
		if(i<row-1)
		{ 
			for (j = 0; j < col; j++) {
				printf("---");
				if (j < col - 1)
					printf("|");
			}
			printf("\n");
		}
	}
}
void Playermove(char board[ROW][COL], int row, int col) 
{
	printf("玩家下棋\n");
	printf("请输入坐标,以空格分开:");
	int x = 0, y = 0;
	while (1)
	{
		scanf("%d %d", &x, &y);
		if (x >= 1 && x <= row && y >= 1 && y <= row) {
			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() % 3;
		y = rand() % 3;
		if (board[x][y] == ' ')
		{
			board[x][y] = '#';
			break;
		}
	}
}
static int Isfull(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++) {
			if (board[i][j] == ' ')
				return 0;
		}
	}
	return 1;
}
//玩家赢 '*'
//电脑赢 '#'
//平局 'Q';
//继续 'C'
char Iswin(char board[ROW][COL], int row, int col) 
{
	int i = 0, j = 0;
	for (i = 0; i < row; i++) {
		if (board[i][0] == board[i][1] && board[i][1] == board[i][2] && board[i][0] != ' ') {
			return board[i][0];
		}
	}
	for (j = 0; j < col; j++) {
		if (board[0][j] == board[1][j] && board[1][j] == board[2][j] && board[0][j] != ' ') {
			return board[0][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';

}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

海的宇宙

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值