制作的第一个小游戏:三子棋

178 阅读6分钟

这是我学习C语言来制作的第一个小游戏--三子棋,也叫井字棋。三子棋是我们小时候经常玩的一个游戏,上课的时候经常和同桌不听讲就撕张纸玩玩五子棋,到后来能撕的纸就没那么多了,就开始玩玩三子棋,两横两竖就把棋盘给画好了。

那么我现在就来说说三子棋的游戏规则,首先两个人猜拳决定谁先下棋,你下一次,然后我下一次,当谁的水平方向、竖直方向或者斜线方向连成了3个棋子,谁就赢了,如果棋盘中棋子已经放满了,但是还没有决出胜负,那么就是平局。

接着我们来捋一捋思路:

1.自定义模块

制作一个游戏,我们需要对游戏进行一个自定义模块,那么自然创建一个头文件(game.h)包含各种游戏步骤的命名,一个源文件(game.c)的游戏步骤定义,以及一个测试源文件(test.c); image.png

解决方案资源管理器

2.游戏菜单

首先得有一个游戏菜单(menu)让玩家进行是否游戏选择,玩一次当然不过瘾,所以我们在main函数中用do while循环进行一个游戏界面循环,游戏结束后再次选择是否进行游戏;

int main()
{
	int input = 0;
	srand((unsigned int)time(NULL));

	do
	{
		menu();
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			//选择1,游戏开始
			game();
			break;
		case 0:
			//退出
			printf("exit\n");
			break;
		default:
			//重新选择
			printf("please choose again!\n");
			break;
		}
	} while (input != 0);


	return 0;
}

test.c

3. 游戏的思路

接下来就是得把main函数中的(menu())和(game())给解决了,在屏幕上输出一个简易菜单不难吧;我们再来把游戏的内容进行一个编辑;这个游戏的思路:(1)先在屏幕上输出一个33的棋盘,那么棋盘里面要放入玩家和电脑下的棋,所以我们创建了一个二维字符数组board[ROW][COL]并初始化,这里的ROW和COL应在game.h中define,以便于以后需要修改不同的游戏类型;(2)接下来就是游戏的一个过程,这里我把玩家下的棋记录为‘’,电脑下的棋记录为‘#’,首先是玩家下棋,然后是电脑下棋;

4. 判断游戏的状态

并且每一次下完棋都要判断游戏的一个状态,那么我将游戏状态(ret)分为了4种返回值:(1)玩家胜‘*’;(2)电脑胜‘#’;(3)平局‘-’;(4)游戏继续‘C’;

//test.c

#define _CRT_SECURE_NO_WARNINGS 1

#include"game.h"

void menu()
{
	printf("______________________________________\n");
	printf("                 %d子棋                \n",price);
	printf("                 1.play                \n");
	printf("                 0.exit                \n");
	printf("______________________________________\n");
	printf("plaese choose:>");
}

void game()
{
	//创建棋盘(二维数组)
	char board[ROWS][COLS];                   //转定义
	//初始化棋盘
	InitBoard(board, ROWS, COLS,' ');
	//打印棋盘
	DisplayBoard(board, ROWS, COLS);

	//开始游戏并判断游戏进程
	char ret = 0;
	
	while (1)
	{
		//玩家下棋
		PlayerMove(board, ROWS, COLS, '*');
		DisplayBoard(board, ROWS, COLS);
		//判断输赢
		ret = Iswin(board, ROWS, COLS,'*','#');
		if (ret != 'c')
		{
			break;
		}


		//电脑下棋
		ComputerMove(board, ROWS, COLS, '#');
		DisplayBoard(board, ROWS, COLS);
		//判断输赢
		ret = Iswin(board, ROWS, COLS,'*','#');
		if (ret != 'c')
		{
			break;
		}
	}
	if (ret == '*')
	{
		printf("you win!\n");
	}
	else if (ret == '#')
	{
		printf("you lost!\n");
	}
	else if (ret == '-')
	{
		printf("---draw---\n");
	}
	DisplayBoard(board, ROWS, COLS);

}

游戏进行的每一步都尽量模块化 - test.c

步骤进行定义和算法

现在大致的游戏步骤已经讲完了,接下来是给各种步骤进行定义和算法;在test.c里我初始化棋盘(InitBoard)、打印棋盘(DisplayBoard)、玩家下棋(PlayerMove)、电脑下棋(ComputerMove)、判断输赢(IsWin)都需要我们来进行定义及解释,所以我们要在test.c里包含“game.h”头文件,并在“game.h”里放入刚刚命名的步骤及类型;

image.png

test.c

image.png

//game.h
#pragma once

#include<stdio.h>
#include<string.h>
#include<time.h>

#define ROWS 10
#define COLS 10
#define price 5

//初始化棋盘
char InitBoard(char board[ROWS][COLS], int rows, int cols,char set);

//打印棋盘
void DisplayBoard(char board[ROWS][COLS],int rows,int cols);

//玩家下棋
void PlayerMove(char board[ROWS][COLS], int rows, int cols,char set1);

//电脑下棋
void ComputerMove(char board[ROWS][COLS], int rows, int cols, char set2);

//判断输赢
char Iswin(char board[ROWS][COLS], int rows, int cols,char set1,char set2);

game.h

"game.h"头文件及自定义函数

  • 在“game.h”文件中包含所有要用到的头文件及自定义函数;接下来就是对自定义函数的自定义;

"game.c"函数实现

  • 对"game.h"的定义在“game.c”中实现,所以“game.c”中同样要包含头文件“game.h”

初始化

  • 初始化棋盘(IninBoard) - 这个就不多说了,把二维数组的每个元素都用' '代替;
//初始化棋盘
char InitBoard(char board[ROWS][COLS], int rows, int cols,char set)
{
	int i = 0;
	int j = 0;
	for (i = 0; i < rows; i++)
	{
		for (j = 0; j < cols; j++)
		{
			board[i][j] = set;
		}
	}
}

game.c

打印棋盘

  • 打印棋盘(DisplayBoard) - 这里值得注意的是我们可以将棋盘分成两个大部分:打印一行数组,再打印一行分隔,再将这两个大部分细分一下就好做了;
//打印棋盘
void DisplayBoard(char board[ROWS][COLS], int rows, int cols)
{
	int i = 0;
	int j = 0;
	for (i = 0; i < rows; i++)
	{
		for (j = 0; j < cols; j++)
		{
			printf(" %c ", board[i][j]);
			if (j < cols - 1)
			{
				printf("|");
			}
		}
		printf("\n");

		if (i < rows - 1)
		{
			for (j = 0; j < cols; j++)
			{
				printf("---");
				if (j < cols - 1)
				{
					printf("|");
				}
			}
			printf("\n");
		}
	}
}

game.c

玩家下棋

  • 玩家下棋(PlayerMove) - 提示玩家下棋,玩家输入的是行和列的数字,首先要判断玩家输入的数字是否合法(在board[ROW][COL]范围内),如果合法 - 判断该位置是否被占用,没有占用就执行下棋然后该步骤结束,如果被占用了则进行提示和重新输入下棋位置并对该过程进行循环;如果不合法 - 提示和重新输入下棋位置并对该过程进行循环;代码实现如下:
//玩家下棋 -> '*'
void PlayerMove(char board[ROWS][COLS], int rows, int cols,char set1)
{
	//玩家输入需要下棋坐标
	int x, y;
	printf("请输入你要下的棋:>");
	while (1)
	{
		scanf("%d %d", &x, &y);
		//判断该坐标是否合法
		if (x >= 1 && x <= rows && y >= 1 && y <= cols && board[x-1][y-1] == ' ')
		{
			board[x-1][y-1] = set1;
			break;
		}
		else
		{
			printf("该坐标不合法或已被占用,请重新输入:>");
		}
	}

}

game.c

电脑下棋

  • 电脑下棋(ComputerMove) - 电脑下棋就没有那么复杂了,我们每次随机生成两个0 - 2的整数,main函数中设定随机值:srand((unsigned int)rand(time(NULL)));生成的数x,y放入board[x][y]判断该位置是否被占用,若是被占用则循环该过程,没被占用则下棋后跳出步骤;
//电脑下棋
void ComputerMove(char board[ROWS][COLS], int rows, int cols, char set2)
{
	printf("电脑下棋:\n");
	while (1)
	{
		int x = rand() % rows;
		int y = rand() % cols;
		if (board[x][y] == ' ')
		{
			board[x][y] = set2;
			break;
		}
	}
}

game.c

判定游戏输赢

最后就是判断游戏过程(IsWin) - 有横向,纵向和斜向三种判断,分别写出判断算法,值得注意的是玩家和电脑下棋后都要进行判定输赢或继续。

//判断输赢
char Iswin(char board[ROWS][COLS], int rows, int cols,char set1,char set2)
{
	//win条件:一排、一列、斜着连成一串

	int x = 0;
	int y = 0;
	int count1 = 0;   //玩家计数
	int count2 = 0;   //电脑计数
	//横向判断
	for (x = 0; x < rows; x++)
	{
		count1 = 0;   //玩家计数
		count2 = 0;   //电脑计数
		for (y = 0; y < cols; y++)
		{
			if (board[x][y] == set1)
			{
				count1++;
				if (count1 == price)
				{
					return set1;
				}
			}
			else if (board[x][y] == set2)
			{
				count2++;
				if (count2 == price)
				{
					return set2;
				}
			}
		}		
	}

	//纵向判断
	for (y = 0; y < cols; y++)
	{
		count1 = 0;   //玩家计数
		count2 = 0;   //电脑计数
		for (x = 0; x < rows; x++)
		{
			if (board[x][y] == set1)
			{
				count1++;
				if (count1 == price)
				{
					return set1;
				}
			}
			else if (board[x][y] == set2)
			{
				count2++;
				if (count2 == price)
				{
					return set2;
				}
			}
		}
	}
	
	count1 = 0;   //玩家计数
	count2 = 0;   //电脑计数
	//斜向1判断
	for (x = 0, y = 0;x < rows && y < cols; x++, y++)
	{
		if (board[x][y] == set1)
		{
			count1++;
			if (count1 == price)
			{
				return set1;
			}
		}
		else if (board[x][y] == set2)
		{
			count2++;
			if (count2 == price)
			{
				return set2;
			}
		}
		else
		{
			count1 = 0;
			count2 = 0;
		}
	}

	count1 = 0;   //玩家计数
	count2 = 0;   //电脑计数
	//斜向2判断
	for (x = 0, y = cols - 1; y >= 0; x++, y--)
	{

		if (board[x][y] == set1)
		{
			count1++;
			if (count1 == price)
			{
				return set1;
			}
		}
		else if (board[x][y] == set2)
		{
			count2++;
			if (count2 == price)
			{
				return set2;
			}
		}
		else
		{
			count1 = 0;
			count2 = 0;
		}
	}
	
	for (x = 0; x < rows; x++)
	{
		for (y = 0; y < cols; y++)
		{
			if (board[x][y] == ' ')
			{
				return 'c';
			}
		}
	}
	return '-';
}


那么在最后,我们是否还能让电脑下棋的时候能有一个很智能的下棋方法呢,因为每个随机下棋并不能给玩家带来好的游戏体验。能力有限,希望有大神指教

以上就是我制作三子棋的一个思路和操作过程,如有不正,请多指教!