草庐IT

追寻童年回忆~三子棋【C语言】

小杨MiManchi 2023-04-14 原文

大家好呀!我是小杨。小杨今天利用C语言中前面学习的函数,数组等一系列知识自己动手制作了我们大家童年时代都比较热衷的小游戏–三子棋。虽然这个小游戏的游戏比较简单,但是却是我们童年美好的回忆,带给了我们无尽欢乐!今天小杨就带领小伙伴们一起制作这个我们童年时代的经典小游戏–三子棋(初阶版),让大家重新体验一下童年时代的快乐!


文章目录

1,游戏前期准备
2,游戏流程设计
3,游戏功能实现
  3.1,游戏菜单实现
  3.2,棋盘初始化
  3.3,棋盘打印
  3.4,玩家下棋
  3.5,电脑下棋
  3.6,判断输赢
4,游戏整体实现
  4.1,游戏逻辑主体~test.c
  4.2,游戏代码实现~game.c
  4.3,游戏代码声明~game.h
5,游戏运行效果


1,游戏前期准备

 当我们写一个项目时,为了实现代码美观和操作便捷,我们常常不直接在一个文件里进行书写代码,而是将这个项目分为多个不同的文件,在不同的文件里分别书写不同版块内容代码,从而避免出现因代码过长和代码逻辑顺序混乱而导致的阅读体验极差以及代码修改优化操作的难度加大等一系列问题。为了避免出现上述问题,小杨在设计这个游戏时,将该项目分为3个文件进行代码书写,分别为game.h,game.c,test.c。

各文件功能如下:

注:三子棋的实现需要多个模块的功能相互串联,这三个文件可以分别处理各自模块的功能,能更好处理各个模块之间的逻辑并且便于后期调试,也使得代码的可读性提高。


2,游戏流程设计

 游戏流程大致可以分为以下:

1,需要设计出游戏菜单,供玩家选择
2,玩家选择开始游戏后,棋盘界面出现
3,玩家根据棋盘界面输入棋盘坐标进行落子操作
4,玩家落子后进行是否获胜判断操作,并打印棋盘
5,电脑在玩家落子操作后,自动进行随机落子操作
6,电脑落子后进行是否获胜判断操作,并打印棋盘
7,玩家和电脑依次进行落子操作,直至一方获胜或者平局结束


3,游戏功能实现

  3.1,游戏菜单实现

//游戏菜单实现
void Menu()
{
	printf("***********************\n");
	printf("***** 1.开始游戏  *****\n");
	printf("***** 0.退出游戏  *****\n");
	printf("***********************\n");
}

说明:游戏菜单规定:选择1表示开始游戏,选择2表示退出游戏,玩家根据需要来进行选择。


  3.2,棋盘初始化

//初始化棋盘
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] = ' ';
		}
	}
}

说明:每次游戏的开始之前我们都要进行初始化棋盘操作,先建立空棋盘的雏形,在这里我们利用二维数组来替代棋盘上的位置,空格符号来代表此时棋盘上没有棋子落子。此时就是一个空棋盘,每次开始之前都要置空。


  3.3,棋盘打印

//打印棋盘
void DisplayBoard(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");
		}
	}
}

说明:棋盘游戏的开始如果只有棋子,整体会看起来比较单一 ,所以我们通过两横两竖来打印一个简单的棋盘,棋盘的参数可以用define(宏)来定义,修改参数很方便,如果要做更大的棋盘,只需要修改参数,棋盘打印的细节很少,需要注意的是每打印一行注意跳行,且打印的时候要把之前棋盘定义的“空格”打印上,这样之后才能落子。


  3.4,玩家下棋

//玩家下棋
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 > 0 && x <= row && y > 0 && y <= col)
		{
			if (board[x - 1][y - 1] == ' ')
			{
				board[x - 1][y - 1] = '*';
				break;
			}
			else
			{
				printf("坐标被占用,不能下棋,请选择其他位置\n");
			}
		}
		else
		{
			printf("该坐标不合法,请重新输入\n");
		}
	}
}

说明:玩家下棋操作里,要站在玩家的角度上考虑,为了让玩家知道轮到自己该下棋了,游戏界面应提示玩家进行下棋操作并输入下棋坐标,同时也要对玩家输入的坐标进行判断是否合法,若不合法则提示重新输入。因二维数组的下标是以0开始的,玩家对此并不知情,为了玩家游戏顺利进行和游戏体验,游戏系统内部要对玩家输入的x,y坐标进行-1操作,从而规避了此类问题。


  3.5,电脑下棋

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

说明:电脑下棋相对就没有那么多的限制,当玩家落子操作后,电脑自动在棋盘上未下棋子的地方进行随机落子操作,这里的处理时通过取余的方式来保证落在规定的行列里面。因该随机性导致电脑比较迟钝,这时的电脑可以被理解为”人工智障“,要想提高该游戏获胜的难度,主要通过改变电脑下棋的操作来解决,从而让电脑变得更聪明,知道”堵人“,哪怕无法获胜也平局,大大提高获胜的概率,实现从人工智障到人工智能的一大转变。


  3.6,判断输赢

//判断棋盘是否满了,若满了,返回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][0] != ' ')
		{
			return board[i][0];
		}
	}

	//列判断
	int j = 0;
	for (j = 0; j < col; j++)
	{
		if (board[0][j] == board[1][j] && board[2][j] == board[0][j] && board[0][j] != ' ')
		{
			return board[0][j];
		}
	}
	
	//对角线判断
	if (board[0][0] == board[1][1] && board[2][2] == board[1][1] && board[0][0] != ' ')
	{
		return board[1][1];
	}
	if (board[0][2] == board[1][1] && board[2][0] == board[1][1] && board[2][0] != ' ')
	{
		return board[1][1];
	}
	
	//平局判断
	if (IsFull(board, row, col))
	{
		return 'Q';
	}
	return 'C';
}

说明:判断游戏输赢是该游戏中最重要的一步操作,该游戏程序要结束,就得有输有赢,或者平局。对于三子棋这个游戏,我们要考虑到横三竖三斜三,此时在书写判断条件需要注意,条件判断不能连等,否则是错误的,并且我们知道这只是一个函数,不能直接判断输赢,需要通过他传递的信息来判断,所以它是一个有返回值的函数,当有三个相同棋子相连,则返回次棋子的字符,而游戏平局和游戏继续的标志在这里分别对应Q和C。游戏平局的标志是棋盘满了无法进行下棋操作但胜负未分。


4,游戏整体实现

  4.1,游戏逻辑主体~test.c

#define _CRT_SECURE_NO_WARNINGS
#include"game.h"
//游戏菜单打印
void Menu()
{
	printf("***********************\n");
	printf("***** 1.开始游戏  *****\n");
	printf("***** 0.退出游戏  *****\n");
	printf("***********************\n");
}
//游戏逻辑主体
void game()
{
	char ret = 0;
	char board[ROW][COL] = { 0 };
	//初始化棋盘
	InitBoard(board, ROW, COL);
	//打印棋盘
	DisplayBoard(board, ROW, COL);
	//下棋操作
	while(1){
		//玩家下棋
		PlayerMove(board, ROW, COL);
		//判断输赢
		ret = IsWin(board, ROW, COL);
		if (ret != 'C')
		{
			break;
		}
		DisplayBoard(board, ROW, COL);
		//电脑下棋
		ComputerMove(board, ROW, COL);
		//判断输赢
		ret = IsWin(board, ROW, COL);
		if (ret != 'C')
		{
			break;
		}
		DisplayBoard(board, ROW, COL);
	}
	if (ret == '*')
	{
		printf("玩家获胜\n");
	}
	else if (ret == '#')
	{
		printf("电脑获胜\n");
	}
	else
	{
		printf("游戏平局\n");
	}
}
//游戏主函数
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.2,游戏代码实现~game.c

#define _CRT_SECURE_NO_WARNINGS

#include "game.h"

//初始化棋盘
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 DisplayBoard(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");
		}
	}
}

//玩家下棋
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 > 0 && x <= row && y > 0 && y <= col)
		{
			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");
	while (1)
	{
		int x = 0;
		int y = 0;
		x = rand() % 3;//0~2
		y = rand() % 3;//0~2
		if (board[x][y] == ' ')
		{
			board[x][y] = '#';
			break;
		}
	}	
}

//判断棋盘是否满了,若满了,返回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][0] != ' ')
		{
			return board[i][0];
		}
	}
	
	//列判断
	int j = 0;
	for (j = 0; j < col; j++)
	{
		if (board[0][j] == board[1][j] && board[2][j] == board[0][j] && board[0][j] != ' ')
		{
			return board[0][j];
		}
	}
	
	//对角线判断
	if (board[0][0] == board[1][1] && board[2][2] == board[1][1] && board[0][0] != ' ')
	{
		return board[1][1];
	}
	if (board[0][2] == board[1][1] && board[2][0] == board[1][1] && board[2][0] != ' ')
	{
		return board[1][1];
	}

	//平局判断
	if (IsFull(board, row, col))
	{
		return 'Q';
	}
	return 'C';
}

  4.3,游戏代码声明~game.h

#pragma once

#include<stdio.h>
#include<stdlib.h>
#include<time.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);

//输赢约定:
//玩家赢--'*'  电脑赢--'#'
//游戏平局--'Q'    游戏继续--'C'

char IsWin(char board[ROW][COL], int row, int col);

5,游戏运行效果


结语

小伙伴们,小杨今天利用C语言数组,函数相关知识制作的三子棋小游戏就介绍到这里就结束,你们应该学会到了挺多东西以及对数组,函数这些知识的掌握更加深刻了吧!🥳🥳🥳今天带大家一起制作的三子棋小游戏是个初阶版,因电脑下棋是在棋盘空的地方随机落子,所以导致电脑比较迟钝,游戏难度小。后续小杨有时间会对该游戏进行升级,让电脑变得更聪明,知道”堵人“,哪怕无法获胜也平局,从而实现从人工智障到人工智能的一大转变。


有关追寻童年回忆~三子棋【C语言】的更多相关文章

  1. ruby - 如何将脚本文件的末尾读取为数据文件(Perl 或任何其他语言) - 2

    我正在寻找执行以下操作的正确语法(在Perl、Shell或Ruby中):#variabletoaccessthedatalinesappendedasafileEND_OF_SCRIPT_MARKERrawdatastartshereanditcontinues. 最佳答案 Perl用__DATA__做这个:#!/usr/bin/perlusestrict;usewarnings;while(){print;}__DATA__Texttoprintgoeshere 关于ruby-如何将脚

  2. ruby - 寻找通过阅读代码确定编程语言的ruby gem? - 2

    几个月前,我读了一篇关于ruby​​gem的博客文章,它可以通过阅读代码本身来确定编程语言。对于我的生活,我不记得博客或gem的名称。谷歌搜索“ruby编程语言猜测”及其变体也无济于事。有人碰巧知道相关gem的名称吗? 最佳答案 是这个吗:http://github.com/chrislo/sourceclassifier/tree/master 关于ruby-寻找通过阅读代码确定编程语言的rubygem?,我们在StackOverflow上找到一个类似的问题:

  3. Unity 热更新技术 | (三) Lua语言基本介绍及下载安装 - 2

    ?博客主页:https://xiaoy.blog.csdn.net?本文由呆呆敲代码的小Y原创,首发于CSDN??学习专栏推荐:Unity系统学习专栏?游戏制作专栏推荐:游戏制作?Unity实战100例专栏推荐:Unity实战100例教程?欢迎点赞?收藏⭐留言?如有错误敬请指正!?未来很长,值得我们全力奔赴更美好的生活✨------------------❤️分割线❤️-------------------------

  4. 7个大一C语言必学的程序 / C语言经典代码大全 - 2

    嗨~大家好,这里是可莉!今天给大家带来的是7个C语言的经典基础代码~那一起往下看下去把【程序一】打印100到200之间的素数#includeintmain(){ inti; for(i=100;i 【程序二】输出乘法口诀表#includeintmain(){inti;for(i=1;i 【程序三】判断1000年---2000年之间的闰年#includeintmain(){intyear;for(year=1000;year 【程序四】给定两个整形变量的值,将两个值的内容进行交换。这里提供两种方法来进行交换,第一种为创建临时变量来进行交换,第二种是不创建临时变量而直接进行交换。1.创建临时变量来

  5. ruby - 如何保持我不常用的编程语言技能 - 2

    关闭。这个问题是off-topic.它目前不接受答案。想改进这个问题吗?Updatethequestion所以它是on-topic用于堆栈溢出。关闭11年前。Improvethisquestion我不经常使用ruby​​-通常它加起来相当于每两个月或更长时间编写一次脚本。我的大部分编程都是使用C++进行的,这与ruby​​有很大不同。由于我与ruby​​之间的差距如此之大,我总是忘记语言的基本方面(比如解析文本文件和其他简单的东西)。我想每天练习一些基本的东西,我想知道是否有一些我可以订阅的网站,并且会向我发送当天的Ruby问题或类似的东西。有人知道这样的站点/Internet服务吗?

  6. ruby-on-rails - 如果特定语言环境中缺少翻译,如何配置 i18n 以使用 en 语言环境? - 2

    如果特定语言环境中缺少翻译,如何配置i18n以使用en语言环境翻译?当前已插入翻译缺失消息。我正在使用RoR3.1。 最佳答案 找到相似的question这里是答案:#application.rb#railswillfallbacktoconfig.i18n.default_localetranslationconfig.i18n.fallbacks=true#railswillfallbacktoen,nomatterwhatissetasconfig.i18n.default_localeconfig.i18n.fallback

  7. ruby-on-rails - 如何通过 URL 更改语言环境? - 2

    在我的双语Rails4应用程序中,我有一个像这样的LocalesController:classLocalesController用户可以通过此表单更改其语言环境:deflocale_switcherform_tagurl_for(:controller=>'locales',:action=>'change_locale'),:method=>'get',:id=>'locale_switcher'doselect_tag'set_locale',options_for_select(LANGUAGES,I18n.locale.to_s)end这有效。但是,目前用户无法通过URL更改

  8. ruby - 一种语言如何被自身解释(如 Rubinius)? - 2

    我使用Ruby编程已经有一段时间了,现在只使用Ruby的标准MRI实现,但我一直对我经常听到的其他实现感到好奇。前几天我在读有关Rubinius的文章,这是一个用Ruby编写的Ruby解释器。我试着在不同的地方查找它,但我很难弄清楚这样的东西到底是如何工作的。我在编译器或语言编写方面从来没有太多经验,但我真的很想弄明白。一门语言究竟如何才能被自己解释?编译中是否有一个我不明白这有意义的基本步骤?有人可以像我是个白痴一样向我解释这个吗(因为无论如何这都不会太离谱) 最佳答案 它比你想象的要简单。Rubinius并非100%用Ruby编

  9. ruby-on-rails - ruby 真的是一种完全面向对象的语言吗? - 2

    Ruby是完全面向对象的语言。在ruby​​中,一切都是对象,因此属于某个类。例如5属于Objectclass1.9.3p194:001>5.class=>Fixnum1.9.3p194:002>5.class.superclass=>Integer1.9.3p194:003>5.class.superclass.superclass=>Numeric1.9.3p194:005>5.class.superclass.superclass.superclass=>Object1.9.3p194:006>5.class.superclass.superclass.superclass.su

  10. 玩以太坊链上项目的必备技能(初识智能合约语言-Solidity之旅一) - 2

    前面一篇关于智能合约翻译文讲到了,是一种计算机程序,既然是程序,那就可以使用程序语言去编写智能合约了。而若想玩区块链上的项目,大部分区块链项目都是开源的,能看得懂智能合约代码,或找出其中的漏洞,那么,学习Solidity这门高级的智能合约语言是有必要的,当然,这都得在公链``````以太坊上,毕竟国内的联盟链有些是不兼容Solidity。Solidity是一种面向对象的高级语言,用于实现智能合约。智能合约是管理以太坊状态下的账户行为的程序。Solidity是运行在以太坊(Ethereum)虚拟机(EVM)上,其语法受到了c++、python、javascript影响。Solidity是静态类型

随机推荐