C语言--11.三子棋练习

我们的C语言已经了解了数组的基础知识,下面我们将进行两个小项目,三子棋与扫雷,来对我们的数组与前面所学的知识做一个小的综合复习,加深印象

三子棋小游戏

在我们编写小游戏之前,我们首先需要对这个小游戏进行一个整体的框架性认识;

首先:大家应该都玩过三子棋,在九宫格中先完成三子连珠的那一方取得胜利,而今天,我们对三子棋进行两个模式的完成,人机对战or双人对战,因为我们还未接触到鼠标windows图形编程方面的知识,所以我们使用键盘对于小游戏进行操作

三子棋小游戏设计流程(自顶而下的设计方式):

基础准备:在这个游戏中,我们利用多文件来将项目模块化,分别为game.h,game.c以及main.c文件,在game.h中,我们来完成对项目的基础宏的声明以及其他准备工作,可以想到的是,我们的棋盘大小一定是3*3的,所以这组长宽一定是一组宏,而后三子棋的黑白落子,也一定是一组宏,剩下的等我们到需要设计时再来定义

//game.h
#ifndef __GAME_H__
#define __GAME_H__
#include<stdio.h>
#include<windows.h>
#define ROW 3
#define COL 3
#define WHITE 'X'
#define BLACK 'O'
#pragma warning(disable:4996)

#endif

1.设计菜单

在我们正常的逻辑思维中,设计一款游戏映入眼帘的一定是菜单选项,所以我们需要先创建菜单,将其主架构放入菜单中,在我们这个三子棋游戏中,我们一共射击了两种模式,所以会对应有两个模式选项,以及一个退出选项,接下来我们便来完成菜单的设计

//main.C
#include"game.h"//
void Menu(){//建立菜单的主要面板
	printf("+----------------------+\n");
	printf("+1.Play-2.double-0.Exit+\n");
	printf("+----------------------+\n");
}
int main(){
	int select = 0;//用户输入
	int quit = 0;//死循环控制菜单显示
	while (!quit){
		Menu();
		printf("Please Select#");
		scanf("%d", &select);
		switch (select){
		case 1:
            Game1;
			break;
		case 2:
            Game2;
			break;
		case 0:
			quit = 1;//关闭死循环,停止菜单显示
			break;
		default:
			printf("Enter Error,Try Again!\n");
			break;
		}
	}

    printf("ByeBye!\n");//退出提示
	system("pause");
	return 0;
}

此时我们便已经完成了对于这个游戏的最外层设计,现在最主要的就是在我们的函数实现文件game.c中去设计我们的游戏逻辑Game1(Game2与Game1有着相似之处,我们在最后会将其引入)

2.设计游戏逻辑

现在,我们来思考一下游戏的步骤,首先,需要一个棋盘,这个棋盘中除了我们的O与X之外应该还要有空格,所以我们需要增加一个宏InitBoard来代表空格,接下来我们就可以来下棋了,玩家来先下创建一个PlayerMove函数,我们下棋一定是重复的多次落子,所以外层会有一个while循环,我们需要看到落子的变化,所以在落子之前之后等地方一定会有一个ShowBoard函数来显示棋盘,当我们显示出来后,我们还应该需要一个IsEnd函数来判断落子之后的结果,,这时候又有一个很关键的判断了,那就是判断IsEnd函数结束的方式,结束的方式有几种呢?根据我们的经验,会有玩家胜利,电脑胜利,平局,以及走下一步,所以除了黑白两子获胜外还需要定义两个宏DRAW ‘D’以及NEXT‘N’, 那么在判断完毕之后除NEXT之外就都结束直接break了,没有break的话便会轮到电脑再来落子,判断方式依旧和玩家判断方式相同,当跳出这个判断落子情况的循环情况时,便会得出游戏的其他三种结束方式之一,所以在跳出之后还有一个switch case语句去判断,那么我们用代码来实现上述思考

//game.h
#define ROW 3
#define COL 3
#define WHITE 'X'
#define BLACK 'O'
#define DRAW 'D'
#define NEXT 'N'
void Game(){
	char board[ROW][COL];//设置棋盘
	InitBoard(board, ROW, COL);//初始化棋盘
	char result = 0;//定义初始值接收落子结果
	while (1){
		ShowBoard(board, ROW, COL);//显示
		PlayerMove(board, ROW, COL);//玩家落子
		result = IsEnd(board, ROW, COL);//判断落子结果
		if (result != NEXT){
			break;
		}
		ShowBoard(board, ROW, COL);
		ComputerMove(board, ROW, COL);//电脑落子
		result = IsEnd(board, ROW, COL);
		if (result != NEXT){
			break;
		}
	}
	switch (result){//判断落子结果
	case WHITE://最后退出的是白子
		printf("You Win!\n");
		break;
	case BLACK://最后退出的是黑子
		printf("You lose!\n");
		break;
	case DRAW://平局
		printf("equals!\n");
		break;
	default:
		printf("BUG!\n");
		break;
	}
}

3.设计函数

现在,我们主要的游戏框架便完成了,现在只需要解决这个框架中出现的函数就可以,因为我们不需要将我们游戏内部的函数对外部进行暴露,所以对游戏内部的函数都使用static关键字进行修饰,只需要对外部暴露一个Game就可以

static InitBoard(board, ROW, COL){}//初始化(棋盘)二维数组
static  ShowBoard(board, ROW, COL){}//显示棋盘
static  PlayerMove(board, ROW, COL){}//玩家落子
static IsEnd(board, ROW, COL){}//判断落子结果
static  ComputerMove(board, ROW, COL){}//电脑落子

现在我们对这些函数一一进行实现

InitBoard函数

这个函数作用是对二维数组进行初始化,初始化为空格

static void InitBoard(char board[][COL], int row, int col){
	for (int i = 0; i < row; i++){
		for (int j = 0; j < col; j++){
			board[i][j] = 'INIT';//使用INIT宏而不使用含义相同的‘ ’ 对棋盘进行初始化,方便后期拓展维护
		}
	}
}

PlayerMove函数

这个函数的作用是控制玩家落子,首先,玩家落子必须落正确的位置,而且肯定不止一次,循环尝试,所以外部会有while循环来控制合法输入,其次,落子一定是通过输入二维数组坐标的方式来完成的,当玩家落子完成,便开始对落子坐标进行合法性判断(是否在3*3棋盘中,是否落子在可以落子的地方),当用户落子坐标合法,可以进行落子时,便开始落子

static void PlayerMove(char board[][COL], int row, int col){
	while (1){//死循环重复判断
		int x = 0;
		int y = 0;
		printf("Please Enter Postion<x,y>#");
		scanf("%d %d",&x, &y);//玩家输入坐标
		if (x < 1 || y < 1 || x>3 || y>3){//判断是否在棋盘内
			printf("Enter Postion Error!\n");
			continue;
		}
		if (board[x - 1][y - 1] == INIT){//判断落子地点是否为空(是否已经落子)
			board[x - 1][y - 1] = WHITE;//玩家落白子
			break;
		}
		else{//已经落子,返回while重新输入
			printf("Enter Is Not Empty!\n");
		}
		
	}
}

IsEnd函数

我们的IsEnd函数用来判断落子后的情况判断,分为4种情况分别判断

static char IsEnd(char board[][COL], int row, int col)
{
	for (int i = 0; i < row; i++){
		if (board[i][0] == board[i][1] && \
			board[i][1] == board[i][2] && \
			board[i][0] != INIT){
			return board[i][0];
		}
	}

	for (int j = 0; j < COL; j++){
		if (board[0][j] == board[1][j] && \
			board[1][j] == board[2][j] && \
			board[0][j] != INIT){
			return board[0][j];
		}
	}
	if (board[0][0] == board[1][1] && \
		board[1][1] == board[2][2] && \
		board[1][1] != INIT){
		return board[1][1];
	}
	if (board[0][2] == board[1][1] && \
		board[1][1] == board[2][0] && \
		board[1][1] != INIT){
		return board[1][1];
	}
	for (int i = 0; i < row; i++){
		for (int j = 0; j < col; j++){
			if (board[i][j] == INIT){
				return NEXT;
			}
		}
	}
	return DRAW;
}

ComputerMove函数

ComputerMove和PlayerMove类似,不过电脑生成棋子是随机的,此时我们就需要利用随机数去生成坐标,并且再去判断位置是否为空,是否合法,若合法,则再去落子,下面我们完成电脑的落子

static void ComputerMovechar(char board[][COL], int row, int col){
	while(1){
    int x = rand() % row;//随机的x值模3后为0,1,2
	int y = rand() % col;//随机的y值模3后为0,1,2
	if (board[x][y] == INIT){//判断为空,合法
		board[x][y] = BLACK;//落黑子
	       break;
    }
  }
}

ShowBoard函数

注意,我们先在画图板上构思一下棋盘的样子,是这个样子的,对于空格进行一系列的优化处理之后,得出代码

static void ShowBoard(char board[][COL], int row, int col)
{
	system("cls");//清理屏幕(使得我们的三子棋在一张图上完成)
	printf(" ");
	for (int i = 0; i < col; i++){
		printf("%4d", i + 1);//预留四个空位
	}
	printf("\n--------------\n");
	for (int i = 0; i < row; i++){
		printf("%-2d", i + 1); //2
		for (int j = 0; j < col; j++){
			printf("| %c ", board[i][j]); //12
		}
		printf("\n--------------\n");
	}
}

此时便完成了我们的三子棋小游戏,

那么我们如果想加上双人模式呢,只需要将PlayerMove拷贝一份在修改名称与最后的落子名称,而后再拷贝Game在Game2中将ComeputerMove换成Player2Move,将这两段代码加到原先的代码中即可。

//game.c
static void Player2Move(char board[][COL], int row, int col){
	while (1){//死循环重复判断
		int x = 0;
		int y = 0;
		printf("Please Enter Postion<x,y>#");
		scanf("%d %d", &x, &y);//玩家输入坐标
		if (x < 1 || y < 1 || x>3 || y>3){//判断是否在棋盘内
			printf("Enter Postion Error!\n");
			continue;
		}
		if (board[x - 1][y - 1] == INIT){//判断落子地点是否为空(是否已经落子)
			board[x - 1][y - 1] = BLACK;//玩家落白子
			break;
		}
		else{
			printf("Enter Is Not Empty!\n");
		}

	}
}

void Game2(){
	char board[ROW][COL];//设置棋盘
	InitBoard(board, ROW, COL);//初始化棋盘
	//srand((unsigned long)time(NULL));
	char result = 0;//定义初始值接收落子结果
	while (1){
		ShowBoard(board, ROW, COL);//显示
		PlayerMove(board, ROW, COL);//玩家落子
		result = IsEnd(board, ROW, COL);//判断落子结果
		if (result != NEXT){
			break;
		}
		ShowBoard(board, ROW, COL);
		Player2Move(board, ROW, COL);//二号玩家落子
		result = IsEnd(board, ROW, COL);
		if (result != NEXT){
			break;
		}
	}
	ShowBoard(board, ROW, COL);
	switch (result){//判断落子结果
	case WHITE://最后退出的是白子
		printf("WHITE Win!\n");
		break;
	case BLACK://最后退出的是黑子
		printf("BLACK lose!\n");
		break;
	case DRAW://平局
		printf("equals!\n");
		break;
	default:
		printf("BUG!\n");
		break;
	}
}

下面我们演示完整的三子棋练习代码

//game.h
#ifndef __GAME_H__
#define __GAME_H__

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

#define ROW 3
#define COL 3

#define INIT ' '
#define WHITE 'X' //Player
#define BLACK 'O' //Computer


#define DRAW 'D'
#define NEXT 'N'

#pragma warning(disable:4996)

extern void Game();

#endif

//game.c
#include"game.h";
static void InitBoard(char board[][COL], int row, int col){
	for (int i = 0; i < row; i++){
		for (int j = 0; j < col; j++){
			board[i][j] = INIT;
		}
	}
}//初始化(棋盘)二维数组
static void ShowBoard(char board[][COL], int row, int col)
{
	system("cls");
	printf(" ");
	for (int i = 0; i < col; i++){
		printf("%4d", i + 1);
	}
	printf("\n--------------\n");
	for (int i = 0; i < row; i++){
		printf("%-2d", i + 1); //2
		for (int j = 0; j < col; j++){
			printf("| %c ", board[i][j]); //12
		}
		printf("\n--------------\n");
	}
}
static void PlayerMove(char board[][COL], int row, int col){
	while (1){//死循环重复判断
		int x = 0;
		int y = 0;
		printf("Please Enter Postion<x,y>#");
		scanf("%d %d",&x, &y);//玩家输入坐标
		if (x < 1 || y < 1 || x>3 || y>3){//判断是否在棋盘内
			printf("Enter Postion Error!\n");
			continue;
		}
		if (board[x - 1][y - 1] == INIT){//判断落子地点是否为空(是否已经落子)
			board[x - 1][y - 1] = WHITE;//玩家落白子
			break;
		}
		else{
			printf("Enter Is Not Empty!\n");
		}
		
	}
}//玩家落子
static void Player2Move(char board[][COL], int row, int col){
	while (1){//死循环重复判断
		int x = 0;
		int y = 0;
		printf("Please Enter Postion<x,y>#");
		scanf("%d %d", &x, &y);//玩家输入坐标
		if (x < 1 || y < 1 || x>3 || y>3){//判断是否在棋盘内
			printf("Enter Postion Error!\n");
			continue;
		}
		if (board[x - 1][y - 1] == INIT){//判断落子地点是否为空(是否已经落子)
			board[x - 1][y - 1] = BLACK;//玩家落白子
			break;
		}
		else{
			printf("Enter Is Not Empty!\n");
		}

	}
}
static char IsEnd(char board[][COL], int row, int col)
{
	for (int i = 0; i < row; i++){
		if (board[i][0] == board[i][1] && \
			board[i][1] == board[i][2] && \
			board[i][0] != INIT){
			return board[i][0];
		}
	}

	for (int j = 0; j < COL; j++){
		if (board[0][j] == board[1][j] && \
			board[1][j] == board[2][j] && \
			board[0][j] != INIT){
			return board[0][j];
		}
	}
	if (board[0][0] == board[1][1] && \
		board[1][1] == board[2][2] && \
		board[1][1] != INIT){
		return board[1][1];
	}
	if (board[0][2] == board[1][1] && \
		board[1][1] == board[2][0] && \
		board[1][1] != INIT){
		return board[1][1];
	}
	for (int i = 0; i < row; i++){
		for (int j = 0; j < col; j++){
			if (board[i][j] == INIT){
				return NEXT;
			}
		}
	}
	return DRAW;
}//判断落子结果
static void ComputerMove(char board[][COL], int row, int col){
	while (1){
		int x = rand() % row;//随机的x值模3后为0,1,2
		int y = rand() % col;//随机的y值模3后为0,1,2
		if (board[x][y] == INIT){//判断为空,合法
			board[x][y] = BLACK;//落黑子
			break;
		}
	}
}//电脑落子
void Game(){
	char board[ROW][COL];//设置棋盘
	InitBoard(board, ROW, COL);//初始化棋盘
	srand((unsigned long)time(NULL));
	char result = 0;//定义初始值接收落子结果
	while (1){
		ShowBoard(board, ROW, COL);//显示
		PlayerMove(board, ROW, COL);//玩家落子
		result = IsEnd(board, ROW, COL);//判断落子结果
		if (result != NEXT){
			break;
		}
		ShowBoard(board, ROW, COL);
		ComputerMove(board, ROW, COL);//电脑落子
		result = IsEnd(board, ROW, COL);
		if (result != NEXT){
			break;
		}
	}
	ShowBoard(board, ROW, COL);
	switch (result){//判断落子结果
	case WHITE://最后退出的是白子
		printf("You Win!\n");
		break;
	case BLACK://最后退出的是黑子
		printf("You lose!\n");
		break;
	case DRAW://平局
		printf("equals!\n");
		break;
	default:
		printf("BUG!\n");
		break;
	}
}
void Game2(){
	char board[ROW][COL];//设置棋盘
	InitBoard(board, ROW, COL);//初始化棋盘
	//srand((unsigned long)time(NULL));
	char result = 0;//定义初始值接收落子结果
	while (1){
		ShowBoard(board, ROW, COL);//显示
		PlayerMove(board, ROW, COL);//玩家落子
		result = IsEnd(board, ROW, COL);//判断落子结果
		if (result != NEXT){
			break;
		}
		ShowBoard(board, ROW, COL);
		Player2Move(board, ROW, COL);//二号玩家落子
		result = IsEnd(board, ROW, COL);
		if (result != NEXT){
			break;
		}
	}
	ShowBoard(board, ROW, COL);
	switch (result){//判断落子结果
	case WHITE://最后退出的是白子
		printf("WHITE Win!\n");
		break;
	case BLACK://最后退出的是黑子
		printf("BLACK lose!\n");
		break;
	case DRAW://平局
		printf("equals!\n");
		break;
	default:
		printf("BUG!\n");
		break;
	}
}


//main.c
#include"game.h"

void Menu(){//建立菜单的主要面板
	printf("+----------------------+\n");
	printf("+1.Play-2.double-0.Exit+\n");
	printf("+----------------------+\n");
}
int main(){
	int select = 0;//用户输入
	int quit = 0;//死循环控制菜单显示
	while (!quit){
		Menu();
		printf("Please Select#");
		scanf("%d", &select);
		switch (select){
		case 1:
			Game();
			break;
		case 2:
			Game2();
			break;
		case 0:
			quit = 1;//关闭死循环,停止菜单显示
			break;
		default:
			printf("Enter Error,Try Again!\n");
			break;
		}
	}

    printf("ByeBye!\n");//退出提示
	system("pause");
	return 0;
}

这便是我们三子棋小游戏所有的代码,下面我们对其进行思维导图逻辑总结

这便是整个三子棋小游戏的实现与总结,共同学习,共同进步

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值