*^-^* 五子棋做出来了,来下棋吗? *^-^*

1. 问题与分析

问题:设计个15×15规格的五子棋游戏,自己跟电脑对局,要判断胜负与平局

  1. 需求分析:
    • 主函数:进入游戏界面-->选择是否进入游戏-->给个while循环可以多次玩(包含退出指令)
    • 头文件:常量符号定义;头文件包含;函数声明;
    • 游戏主要现实:初始化棋盘-->打印棋盘-->玩家下棋/电脑下棋-->判断胜利(需要用指针获取所下棋子的坐标);

2. 代码

2.1 头文件--game.h

#pragma once
//1. 符号定义:
#define ROW 15
#define COL 15
#define tag 5

//2. 头文件包含:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

//3. 函数声明:
//初始化棋盘:
void InitBoard(char board[ROW][COL], int row, int col);  
//打印棋盘:
void DisplayBoard(char board[ROW][COL], int row, int col); 
//玩家下棋:
void Playerdo(char board[ROW][COL], int row, int col, int*pa, int* pb);  
//电脑下棋:
void Computerdo(char board[ROW][COL], int row, int col, int* pa, int* pb);
//判断胜负,返回判断结果:
char Judge(char board[ROW][COL], int row, int col, int x, int y);
//返回  '*'--玩家胜利;
//返回  '#'--电脑胜利;
//返回  'Q'--打成平局;
//返回  'C'--继续游戏

2.2 主函数--main()

#define _CRT_SECURE_NO_WARNINGS 1
#pragma warning(disable:6031)
#include "game.h"

void menu()
{
	printf("************************\n");
	printf("***** 您已进入游戏 *****\n");
	printf("*****   1--play    *****\n");
	printf("*****   0--exit    *****\n");
	printf("************************\n");
}

void game()
{
	//下棋就得记录棋子,也就得存储数据,棋盘是二维的,所以用二维数组来存储;
	char board[ROW][COL];
	//初始化棋盘---把数组都变成空格;
	InitBoard(board, ROW, COL);
	//打印棋盘---把棋盘符号与数组对应起来,然后打印数组;
	DisplayBoard(board, ROW, COL);

	int flag = rand() % 2;	//生成随机数1/0,判断谁先下棋
	//printf("flag = %d\n", flag);
	char ret = 0; //接收游戏状态
	int a = 0;
	int b = 0;
	//给个初始化的坐标,用于接收落子的坐标;
	while (1)
	{
		if (flag == 1)
		{  //玩家下棋
			Playerdo(board, ROW, COL, &a, &b);
			DisplayBoard(board, ROW, COL);
			//判断输赢
			ret  = Judge(board, ROW, COL, a, b);
			if (ret != 'C')
				break;
			//电脑下棋
			Computerdo(board, ROW, COL, & a, &b);
			DisplayBoard(board, ROW, COL);
			ret = Judge(board, ROW, COL, a, b);
			if (ret != 'C')
				break;
			//判断输赢
		}
		else
		{    //电脑下棋
			Computerdo(board, ROW, COL, &a, &b);
			DisplayBoard(board, ROW, COL);
			//判断输赢
			ret = Judge(board, ROW, COL, a, b);
			if (ret != 'C')
				break;
			//玩家下棋
			Playerdo(board, ROW, COL, &a, &b);
			DisplayBoard(board, ROW, COL);
			//判断输赢
			ret = Judge(board, ROW, COL, a, b);
			if (ret != 'C')
				break;
		}
	}
	if (ret == '*')
	{
		printf("恭喜你赢得胜利!\n");
	}
	else if (ret == '#')
	{
		printf("电脑赢得胜利!\n");
	}
	else
	{
		printf("打成平局!\n");
	}
}

int main()
{
	menu();  //游戏界面提示
	int input = 0;
	srand((unsigned int)time(NULL));
	do {
		printf("\n请输入游戏指令<1/0>:");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			printf("游戏已启动,祝您玩得开心!\n");
			game();
			break;
		case 0:
			printf("退出游戏!\n");
			break;
		default:
			printf("输入有误!\n");
			break;
		}
	} while (input);
	return 0;
}

2.3 游戏实现--game.c

#define _CRT_SECURE_NO_WARNINGS 1
#pragma warning(disable:6031)
#include "game.h"

void InitBoard(char board[ROW][COL], int row, int col)
{
	int i, j;
	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, j;
	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 Playerdo(char board[ROW][COL], int row, int col, int* pa, int* pb)
{
	printf("*^-^* 玩家下棋 *^-^* \n");
	int i, j;
	while (1)
	{
		printf("请输入棋子坐标>:");
		scanf("%d%d", &i, &j);
		if (i >= 1 && i <= row && j >= 1&& j <= col)
		{
			if (board[i-1][j-1] == ' ')
			{
				board[i-1][j-1] = '*';
				*pa = i - 1;
				*pb = j - 1;
				break;
			}
			else
			{
				printf("此处已有棋子,请重新下子!\n");
			}
		}
		else
		{
			printf("坐标非法,请重新输入!\n");
		}
	}
}

void Computerdo(char board[ROW][COL], int row, int col, int* pa, int* pb)
{
	printf("*^-^* 电脑下棋 *^-^* \n");
	while (1)
	{
		int x = rand() % row;
		int y = rand() % col;
		//千万别把 x, y 定义和初始化放在外面,否则下棋位置被占,就会停下来;
		if (board[x][y] == ' ')
		{
			board[x][y] = '#';
			*pa = x;
			*pb = y;
			break;
		}
	}
}

char ISfull(char board[ROW][COL], int row, int col)
{
	int i, j;
	for (i = 0; i < row; i++)
	{
		for (j = 0; j < col; j++)
		{
			if (board[i][j] == ' ')
				return 'K';
		}
	}
	return 'M';
}

char Column(char board[ROW][COL], int row, int col, int x, int y) {
	int count = 1;
	//判断纵向:(先上后下)
	for (int i = x - 1; i >= 0; i--) {
		if (board[i][y] == board[x][y]) {
			count++;
			if (count == tag) {
				return board[x][y];
			}
		}
		/*else {
			return 'C';
		}*/
	}
	for (int i = x + 1; i < row; i++) {
		if (board[i][y] == board[x][y]) {
			count++;
			if (count == tag) {
				return board[x][y];
			}
		}
		/*else {
			return 'C';
		}*/
	}
	return 'N';
}

char Transverse(char board[ROW][COL], int row, int col, int x, int y) {
	int count = 1;
	//判断横向,先左后右;
	for (int j = y - 1; j >= 0; j--) {
		if (board[x][j] == board[x][y]) {
			count++;
			if (count == tag) {
				return board[x][y];
			}
		}
		/*else {
			return 'C';
		}*/
	}
	for (int j = y + 1; j < col; j++) {
		if (board[x][j] == board[x][y]) {
			count++;
			if (count == tag) {
				return board[x][y];
			}
		}
		/*else {
			return 'C';
		}*/
	}
	return 'N';
}

char Diagonal1(char board[ROW][COL], int row, int col, int x, int y) {
	int count = 1;
	int sum = x + y;
	//对角线:左下右上(sum = x+y)
	for (int i = x + 1; i < row; i++) {
		if (board[i][sum - i] == board[x][y]) {
			count++;
			if (count == tag) {
				return board[x][y];
			}
		}
		/*else {
			return 'C';
		}*/
	}
	for (int i = x - 1; i >= 0; i--) {
		if (board[i][sum - i] == board[x][y]) {
			count++;
			if (count == tag) {
				return board[x][y];
			}
		}
		/*else {
			return 'C';
		}*/
	}
	return 'N';
}

char Diagonal2(char board[ROW][COL], int row, int col, int x, int y) {
	int count = 1;
	int suml = x + y;
	int sumr = suml;
	//对角线:左上右下(x=y)
	for (int i = x - 1; i >= 0; i--) {
		suml -= 2;
		if (board[i][suml - i] == board[x][y]) {
			count++;
			if (count == tag) {
				return board[x][y];
			}
		}
		/*else {
			return 'C';
		}*/
	}
	for (int i = x + 1; i < row; i++) {
		sumr += 2;
		if (board[i][sumr - i] == board[x][y]) {
			count++;
			if (count == tag) {
				return board[x][y];
			}
		}
		/*else {
			return 'C';
		}*/
	}
	return 'N';
}

char Judge(char board[ROW][COL], int row, int col, int x, int y) {
	char tmp = 0;
	int tip = 0;
	switch (tip) {
	case 0:{
		tmp = Column(board, row, col, x, y);
		if (tmp == '*') {
			return '*';
		}
		else if (tmp == '#') {
			return '#';
		}
		else if (tmp == 'C') {
			return 'C';
		}
		else if (tmp == 'N') {
			tip++;
		}
		else {
			break;
		}
	}
	case 1:{
		tmp = Transverse(board, row, col, x, y);
		if (tmp == '*') {
			return '*';
		}
		else if (tmp == '#') {
			return '#';
		}
		else if (tmp == 'C') {
			return 'C';
		}
		else if (tmp == 'N') {
			tip++;
		}
		else {
			break;
		}
	}
	case 2:{
		tmp = Diagonal1(board, row, col, x, y);
		if (tmp == '*') {
			return '*';
		}
		else if (tmp == '#') {
			return '#';
		}
		else if (tmp == 'C') {
			return 'C';
		}
		else if (tmp == 'N') {
			tip++;
		}
		else {
			break;
		}
	}
	case 3:{
		tmp = Diagonal2(board, row, col, x, y);
		if (tmp == '*') {
			return '*';
		}
		else if (tmp == '#') {
			return '#';
		}
		else if (tmp == 'C') {
			return 'C';
		}
		else if (tmp == 'N') {
			tip++;
		}
		else {
			break;
		}
	}
	default:{
		tmp = ISfull(board, row, col);
		if (tmp == 'M') {
			return 'Q';
		}
		else {
			break;
		}
	}
	}
	return 'C';
}

3. 总结与展望

  • 在学C课程时,老师做了个简单的三子棋,有点简单,而且在代码没有延展性(就是说,不能拓展成五子棋,而且判断条件也很死板),其实老师只是想抛砖引玉,然我们思考游戏实现的每一步,而且要让代码具有延展性(比如从三子棋到五子棋,不需要改代码主体,只需要在头文件把常量符号改一下就好)。基于此,熬熬夜,终于把游戏雏形搞出来了。你不知道,当按下Ctrl+F5的时候,没有报错和警告,游戏跑下来也没有bug的 时候,激动得饭都不用吃了,我太开心了。(现在有多开心,过程就有多艰苦 smile
  • 拓展:框架还是很粗糙,没有行列号提示,没有黑白子,棋盘也是黑框,还不能触屏……总之,还有很多需要实现和完善的,比如给电脑下棋整个算法,让它聪明一些。
125 views
Comments
登录后评论
Sign In
·

五子棋运行界面

·

+1

·

稍微看了一下,从你说的代码延展性方面上提出一点建议

三子棋和五子棋之间的界面上(比如棋盘的绘制,棋盘的存储等)的泛化做得还行,

但关键部分特化做得还不够,就是他们之间的差别,比如机器人下棋的策略上,你仅仅使用了一个坐标随机填充,这实际上是电脑下棋的核心部分,有点太简单了,五子棋电脑应该使用一个策略,三子棋使用另一个策略,以及选择不同难度时使用不同的策略,这都是需要好好考虑的

最后就是你有些地方的代码重复度很高,可以单独抽出来复用,这也是好”代码延展性“的一种表现

·

+1