控制台的五子连珠游戏

3 篇文章 0 订阅
1 篇文章 0 订阅

前言

我们如何使用一些对游戏引擎不友好的、或者没有专业游戏支持库的语言,或者不使用游戏库来制作小游戏。
在这里 我们一起制作一个基于c++的“五子连珠”小游戏

游戏需求

用C++编写控制台程序,模拟完成一个单人益智小游戏“五子连珠”。棋盘大小是9X9,一共会出现6种颜色的珠子。初始状态棋盘上7个随机位置分布着7个随机颜色的珠子。通过移动珠子将同色的珠子连在一起来消除后得分。当无珠子可以移动时程序结束。游戏记录所有游戏者得分并排序。

游戏规则如下:
1,一次只允许移动一个珠子。
2,每移动一颗珠子以后,如果不满足同色的5颗珠子相连,将会出现3个随机颜色珠子分布到棋盘任意空置的位置上。如果同色的珠子能有5颗连在一起排成横向、纵向或者斜向时,这5颗珠子从棋盘上消失,不产生3颗珠子。同时游戏者可以得10分。
3,当同色的珠子有6颗连在一起排成横向、纵向或者斜向时,游戏者可以得12分。同时6颗珠子从棋盘上消失。(即:在同一方向上连在一起的珠子每增加一颗,游戏者多得2分。依此类推。)
4, 如果移动一个珠子之后,有两个方向都可以同时消除(即:任何单一方向上的同色珠子数至少为5颗),则两个方向的所有珠子都消除。按每个珠子2分获得分值。
5,两个方向同时消除的规则,同样适用于三个或四个方向。
6, 如果系统随机产生的珠子正好能凑成了同色的5颗及以上一起排成横向、纵向或者斜向,则这几颗同向的珠子自行消除,游戏者得分。
7,当棋盘被珠子占满时游戏结束。

实现说明:
1,用课程所讲的类的方式(编程思想)来实现项目。
2,源代码里必须写足够的注释,提高程序可读性。
3,最基本的要求是单纯使用键盘作为输入设备在命令行控制,珠子的移动通过输入起始坐标和终止坐标来模拟完成。当然也可以自学C++的鼠标控制,来替代键盘完成的功能,提高用户体验感。原游戏中不同色的珠子,也可以用不同的符号来代替。

框架

总体框架

0.确定游戏中所用的类
1.初始化
2.显示游戏帧
3.获取输入
4.更新游戏帧

main代码

int main() {
	srand(time(NULL));
	init();
	while (1) {
		print();
		input();
		if(update()==NO_PLACE){
			gameover();
		}
	}
	return 0;
}

补充和完善

确定游戏中的类

在此处 我们只需要定义棋盘类就OK
并且主要功能在棋盘类中体现

class MAP {
		char **maparr;//存储棋盘数据
		map<pair<int, int>, int> isempty;//记录空位置
		int score;//分数
	public:
		MAP() {//构造
			score = 0;
			maparr = new char*[maxsize];
			for (int i = 0; i < maxsize; i++) {
				maparr[i] = new char[maxsize];
			}
			for (int i = 0; i < maxsize; i++) {
				for (int j = 0; j < maxsize; j++) {
					maparr[i][j] = typearr[NONE];
					if (i > 0 && i < maxsize && j > 0 && j < maxsize) {
						isempty[ {i, j}] = 1;
					}
				}
			}
		}
		
		void print() {//输出棋盘
			cout << "score:" << score << endl;
			cout << '\t';
			for (int i = 1; i < maxsize; i++) {
				cout << i << '\t';
			}
			cout << endl;

			for (int i = 1; i < maxsize; i++) {
				cout << i << '\t';
				for (int j = 1; j < maxsize; j++) {
					cout << maparr[i][j] << '\t' ;
				}
				cout << endl;
			}
		}
		
		int addpoint(int addnum) {//加入点
			if ((int)isempty.size() < addnum) {
				return NO_PLACE;
			}
			for (int i = 0; i < addnum; i++) {
				int randnum = rand() % isempty.size();
				int x, y, t = 0;
				auto n = isempty.begin();
				for (; t < randnum; n++, t++);
				x = n->first.first;
				y = n->first.second;
				isempty.erase(n);
				maparr[x][y] = typearr[rand() % 5 + 1];
			}
			return OK;
		}
		
		int check() {//检查是否有可以清除的
			vector<pair<int, int>> cancleararr;

			for (int x = 1; x < maxsize; x++) {
				for (int y = 1; y < maxsize; y++) {
					if (maparr[x][y] == typearr[NONE]) {
						continue;
					}
					canclear(x, y, cancleararr);
				}
			}
			for (auto i : cancleararr) {
				//cout << "i.first:"<<i.first << "i.second:" << i.second << endl;
				if(maparr[i.first][i.second]!=typearr[NONE]){
					score+=2;
				}
				maparr[i.first][i.second]=typearr[NONE];
			}
			return cancleararr.size() == 0 ? ADD : NO_ADD;
		}
		
		//对某个点判断是否可以清除
		bool canclear(int x, int y, vector<pair<int, int>> &cancleararr) {
			//cout << x << '\t' << y << endl;
			cancleararr.push_back({x,y});
			bool res = false;
			int count = 0;
			for (int i = 1; x + i < maxsize; i++) {

				if (maparr[x][y] == maparr[x + i][y]) {
					cancleararr.push_back({x + i, y});
					count++;
				} else {
					break;
				}
			}
			//cout << "x+i" << count << endl;
			if (count > clearnum) {
				//score += 2*count;
				res = true;
			} else {
				for (int i = 0; i < count; i++) {
					cancleararr.pop_back();
				}
			}

			count = 0;
			for (int i = 1; y + i < maxsize; i++) {
				if (maparr[x][y] == maparr[x][y + i]) {
					cancleararr.push_back({x, y + i});
					count++;
				} else {
					break;
				}
			}
			//cout << "y+i" << count << endl;
			if (count > clearnum) {
				//score += 2*count;
				res = true;
			} else {
				for (int i = 0; i < count; i++) {
					cancleararr.pop_back();
				}
			}

			count = 0;
			for (int i = 1; x + i < maxsize; i++) {
				if (maparr[x][y] == maparr[x + i][y + i]) {
					cancleararr.push_back({x + i, y + i});
					count++;
				} else {
					break;
				}
			}
			if (count > clearnum) {
				//score += 2*count;
				res = true;
			} else {
				for (int i = 0; i < count; i++) {
					cancleararr.pop_back();
				}
			}

			count = 0;
			for (int i = 1; x + i < maxsize; i++) {
				if (maparr[x][y] == maparr[x + i][y - i]) {
					cancleararr.push_back({x + i, y - i});
					count++;
				} else {
					break;
				}
			}
			if (count > clearnum) {
				//score += 2*count;
				res = true;
			} else {
				for (int i = 0; i < count; i++) {
					cancleararr.pop_back();
				}
			}
			if(!res){
				cancleararr.pop_back();
			}
			return res;
		}
		
		//交换两个点
		int swap(int y1, int x1, int y2, int x2) {
			if (maparr[x1][y1] == typearr[NONE] || maparr[x2][y2] == typearr[NONE]) {
				return SWAP_E;
			}
			char temp = maparr[x1][y1];
			maparr[x1][y1] = maparr[x2][y2];
			maparr[x2][y2] = temp;
			return OK;
		}
		
		//获取分数
		int get_score(){
			return score;
		}
};

常量的定义

#define maxsize 10
//棋盘大小
#define clearnum 4
//可以清除的最小数量

enum {
	NONE = 0,
	TYPE1,
	TYPE2,
	TYPE3,
	TYPE4,
	TYPE5,
	TYPE6,
	NO_PLACE,
	//没有位置
	OK,
	ADD,
	//可以添加
	NO_ADD,
	//不能添加
	SWAP_E
	//交换错误
};
char typearr[] = " ABCDEF";
//各类棋子的类型

初始化

void init() {
	m.addpoint(7);
	//加入7个随机点
	m.check();
}

显示游戏帧

void print() {
	system("cls");
	m.print();
}

获取输入

//获取输入
void input(string name, int &val) {
	cout << "请输入" << name << ":";
	cin >> val;
}

void input() {
	int x1, x2, y1, y2, sure;
	while (1) {
		input("要移动的第一个横坐标", x1);
		input("要移动的第一个纵坐标", y1);
		input("要移动的第二个横坐标", x2);
		input("要移动的第二个纵坐标", y2);
		cout << "要移动的两个点分别是 (" << x1 << "," << y1 << "),("
		     << x2 << "," << y2 << ")";
		input("是否确认(1/0)", sure);
		if (sure == 1 && m.swap(x1, y1, x2, y2) == OK) {
			return;
		}
	}
}

更新游戏帧

void update() {
	if(m.check()==ADD){
		m.addpoint(3);
		//如果可以加入 就加三个点
	}
	m.check();
	//加完后检查
}

游戏结束

void gameover(){
	cout << "游戏结束 你的得分为:"<< m.get_score() <<endl;
}

游戏截图

主界面截图

最终代码(和上面有些不同 如果想cv请看这)

head.h

#include <iostream>
#include <vector>
#include <map>
using namespace std;

#define maxsize 10	//棋盘大小-1(也就是9*9的棋盘)
#define clearnum 4	//清除数量-1(也就是5个清除)

//常量定义
enum {
	NONE = 0,
	TYPE1,
	TYPE2,
	TYPE3,
	TYPE4,
	TYPE5,
	TYPE6,
	NO_PLACE,
	OK,
	ADD,
	NO_ADD,
	SWAP_E
};

//每种类型用什么表示
char typearr[] = " ABCDEF";

class MAP {
		char **maparr;//棋盘数组
		map<pair<int, int>, int> isempty;//记录空点
		int score;//记录分数
		//对某个点检查此点是否可以消除
		//因为这个函数应该只在check中调用 所以不让他为public
		bool canclear(int x, int y, vector<pair<int, int>> &cancleararr) {
			//cout << x << '\t' << y << endl;
			cancleararr.push_back({x,y});
			bool res = false;
			int count = 0;
			for (int i = 1; x + i < maxsize; i++) {

				if (maparr[x][y] == maparr[x + i][y]) {
					cancleararr.push_back({x + i, y});
					count++;
				} else {
					break;
				}
			}
			//cout << "x+i" << count << endl;
			if (count > clearnum) {
				//score += 2*count;
				res = true;
			} else {
				for (int i = 0; i < count; i++) {
					cancleararr.pop_back();
				}
			}

			count = 0;
			for (int i = 1; y + i < maxsize; i++) {
				if (maparr[x][y] == maparr[x][y + i]) {
					cancleararr.push_back({x, y + i});
					count++;
				} else {
					break;
				}
			}
			//cout << "y+i" << count << endl;
			if (count > clearnum) {
				//score += 2*count;
				res = true;
			} else {
				for (int i = 0; i < count; i++) {
					cancleararr.pop_back();
				}
			}

			count = 0;
			for (int i = 1; x + i < maxsize; i++) {
				if (maparr[x][y] == maparr[x + i][y + i]) {
					cancleararr.push_back({x + i, y + i});
					count++;
				} else {
					break;
				}
			}
			if (count > clearnum) {
				//score += 2*count;
				res = true;
			} else {
				for (int i = 0; i < count; i++) {
					cancleararr.pop_back();
				}
			}

			count = 0;
			for (int i = 1; x + i < maxsize; i++) {
				if (maparr[x][y] == maparr[x + i][y - i]) {
					cancleararr.push_back({x + i, y - i});
					count++;
				} else {
					break;
				}
			}
			if (count > clearnum) {
				//score += 2*count;
				res = true;
			} else {
				for (int i = 0; i < count; i++) {
					cancleararr.pop_back();
				}
			}
			if(!res){
				cancleararr.pop_back();
			}
			return res;
		}
	public:
		//构造
		MAP() {
			score = 0;
			maparr = new char*[maxsize];
			for (int i = 0; i < maxsize; i++) {
				maparr[i] = new char[maxsize];
			}
			for (int i = 0; i < maxsize; i++) {
				for (int j = 0; j < maxsize; j++) {
					maparr[i][j] = typearr[NONE];
					if (i > 0 && i < maxsize && j > 0 && j < maxsize) {
						isempty[{i, j}] = 1;
					}
				}
			}
		}
		
		//更新空点集合
		void update_empty(){
			isempty.clear();
			for(int i=1;i<maxsize;i++){
				for(int j=1;j<maxsize;j++){
					if(maparr[i][j] == typearr[NONE]){
						isempty[{i, j}] = 1;
					}
				}
			}
		}
		
		//打印棋盘
		void print() {
			cout << "score:" << score << endl;
			cout << '\t';
			for (int i = 1; i < maxsize; i++) {
				cout << i << '\t';
			}
			cout << endl;

			for (int i = 1; i < maxsize; i++) {
				cout << i << '\t';
				for (int j = 1; j < maxsize; j++) {
					cout << maparr[i][j] << '\t' ;
				}
				cout << endl;
			}
		}
		
		//随机加点
		int addpoint(int addnum) {
			if ((int)isempty.size() < addnum) {
				return NO_PLACE;
			}
			for (int i = 0; i < addnum; i++) {
				int randnum = rand() % isempty.size();
				int x, y, t = 0;
				auto n = isempty.begin();
				for (; t < randnum; n++, t++);
				x = n->first.first;
				y = n->first.second;
				isempty.erase(n);
				maparr[x][y] = typearr[rand() % 5 + 1];
			}
			return OK;
		}
		
		//检查是否有清除的点
		int check() {
			vector<pair<int, int>> cancleararr;

			for (int x = 1; x < maxsize; x++) {
				for (int y = 1; y < maxsize; y++) {
					if (maparr[x][y] == typearr[NONE]) {
						continue;
					}
					canclear(x, y, cancleararr);
				}
			}
			for (auto i : cancleararr) {
				//cout << "i.first:"<<i.first << "i.second:" << i.second << endl;
				if(maparr[i.first][i.second]!=typearr[NONE]){
					score+=2;
				}
				maparr[i.first][i.second]=typearr[NONE];
			}
			return cancleararr.size() == 0 ? ADD : NO_ADD;
		}
		
		
		
		//交换
		int swap(int y1, int x1, int y2, int x2) {
			if ((maparr[x1][y1] == typearr[NONE] && maparr[x2][y2] != typearr[NONE]) || 
				(maparr[x1][y1] != typearr[NONE] && maparr[x2][y2] == typearr[NONE])) {
				
				char temp = maparr[x1][y1];
							maparr[x1][y1] = maparr[x2][y2];
							maparr[x2][y2] = temp;
							return OK;
			}
			return SWAP_E;
		}
		
		//获取分数
		int get_score(){
			return score;
		}
};

main.cpp

#include <iostream>
#include <map>
#include <time.h>
#include "head.h"

using namespace std;

//自定义输入函数
void input(string name, int &val) {
	cout << "请输入" << name << ":";
	cin >> val;
}
//定义个棋盘
MAP m;

//输出棋盘
void print() {
	system("cls");
	m.print();
}

//输入要移动的
void input() {
	int x1, x2, y1, y2, sure;
	while (1) {
		input("要移动的第一个横坐标", x1);
		input("要移动的第一个纵坐标", y1);
		input("要移动的第二个横坐标", x2);
		input("要移动的第二个纵坐标", y2);
		cout << "要移动的两个点分别是 (" << x1 << "," << y1 << "),("
		     << x2 << "," << y2 << ")";
		input("是否确认(1/0)", sure);
		if (sure == 1 && m.swap(x1, y1, x2, y2) == OK) {
			return;
		}else if(sure==1){
			cout << "请输入有效位置" << endl;
		}else{
			cout << "取消了移动" << endl; 
		}
	}
}

//初始化
void init() {
	m.addpoint(7);
	m.check();
}

//更新游戏帧
int update() {
	int t=OK;
	m.update_empty();
	if(m.check()==ADD){
		t = m.addpoint(3);
	}
	m.check();
	return t;
}

//游戏结束
void gameover(){
	cout << "游戏结束 你的得分为:"<< m.get_score() <<endl;
}

//main函数控制流程
int main() {
	srand(time(NULL));
	init();
	while (1) {
		print();
		input();
		if(update()==NO_PLACE){
			gameover();
			break;
		}
	}
	return 0;
}

注意与不足

避免在update方法中添加耗时或阻塞类逻辑

因为每次(帧)都要调用此函数,耗时或阻塞会严重影响流畅性 当然 必须有用户输入才能继续的不算

不应使用遍历判断是否有可消除块

当棋盘变大 时间会越来越长 应使用维护变化量(交换的点或者插入的点)来查看是否有可消除的块

可以使用建图方式来进行更方便的查找块操作

用户输入输出不友好

因为时间不足 直接摆烂写输入输出

还有 只是我暂时不知道

  • 8
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值