[foo] 生命游戏C++简单实现

本文介绍了一个使用C++实现的生命游戏模拟程序。该程序利用Windows控制台特性,通过随机初始化部分异常状态(标记为'o'),并在后续迭代中遵循特定规则进行演化。游戏展示了细胞自动机的概念,其中单元格的状态(正常'x'或异常'o')根据其周围邻居的数量变化。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

看了知乎上的一个回答写了下面的代码。

#include <iostream>
#include <time.h>
#include <vector>
#include <windows.h>
using namespace std;

const int base = 100;
const int unnormal_seeds = 10;
const char normal = 'x';
const char unnormal = 'o';
int search[8] = {0,1,2,3,5,6,7,8};

vector<vector<char>> elements(base);

void init_elements(){
	for(int i = 0; i < base; i++){
		for(int j = 0; j < base; j++)
			elements[i].push_back(normal);
	}
}

void cout_elements(){
	for(int i = 0; i < base; i++){
		for(int j = 0; j < base; j++){
			if(elements[i][j] == unnormal)
				SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_GREEN);
			cout << elements[i][j] << ' ';
			SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY);
		}
		cout << endl;
	}
	cout << endl;
}

void set_unnormal(int seeds){
	srand((unsigned)time(NULL));
	int x, y;
	for(int i = 0; i < seeds; i++){
		x = rand() % 100;
		y = rand() % 100;
		elements[x][y] = unnormal;
		if(x > 0)elements[x-1][y] = unnormal;
		if(x < base-1)elements[x+1][y] = unnormal;
		if(y > 0)elements[x][y-1] = unnormal;
		//if(y < base-1)elements[x][y+1] = unnormal;
	}
}

void unnormal_rule(){
	vector<vector<char>> temp = elements;
	int count;
	for(int i = 1; i < base-1; i++){
		for(int j = 1; j < base-1; j++){
			count = 0;
			for(int k = 0; k < 8; k++){
				count += temp[i-1+search[k]/3][j-1+search[k]%3] == unnormal ? 1 : 0;
			}
			if(count == 3) elements[i][j] = unnormal;
			else if(count < 2) elements[i][j] = normal;
			else if(count > 3) elements[i][j] = normal;
		}
	}
}

int main()
{
	//cout << "...hello secret...\n";
	SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY);

	CONSOLE_CURSOR_INFO console_cursor_info;
    console_cursor_info.bVisible = false;
    console_cursor_info.dwSize = 100;

    HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
	SetConsoleCursorInfo(hConsole, &console_cursor_info);
	COORD coordScreen = {0, 0};
	
	init_elements();
	set_unnormal(unnormal_seeds);
	cout_elements();
	int n = 500;

	while(n--){
		unnormal_rule();
		SetConsoleCursorPosition(hConsole, coordScreen);
		cout_elements();
		cout << "Left times: " << n << endl;
		srand((unsigned)time(NULL));
		int p = rand() % 5;
		if(p == 0) set_unnormal(1);
	}
	
	
	return 0;
}

life_game效果图

#include<bits/stdc++.h> #include<windows.h> using namespace std; int choice; int a=0,b=0,c=0,d=0; int user_input; void S(int Color) { HANDLE hCon=GetStdHandle(STD_OUTPUT_HANDLE); SetConsoleTextAttribute(hCon,Color); } void wait() { Sleep(100); user_input=getchar(); } void cls() { Sleep(100); system("cls"); } int main(int argc, char *argv[]) { S(172); start:cls(); goto a1; gameover:printf("游戏结束。"); printf("按回车键继续"); user_input=getchar(); goto start; a1:printf("现在游戏开始。"); user_input=getchar(); printf("一天,你正在开飞机经过一片雪山区。"); wait(); printf("忽然你的操作系统失控了,飞机直直地坠入山中。"); wait(); printf("你朝两旁看了看,左边是雪地,右边是树林。而你正前方是悬崖,所以你不得不转弯。\n1-左转。\n2-右转。\n"); s1:wait(); switch(user_input) { case'1':goto a8; case'2':goto a13; default:goto s1; } a2:cls(); printf("你下了飞机,走向树林。"); wait(); printf("过了一会儿,你感到又饿又冷,就喝了一点威士忌,坐下休息。"); wait(); printf("但是,威士忌并没有让你觉得温暖。你只是觉得很冷、很冷。\n"); goto gameover; a3:cls(); printf("你吃了一些果实,还摘了一些放在口袋,想晚一点吃。"); wait(); printf("过了一会儿,你的肚子剧烈的痛起来。这时,你才意识到,果子有毒!\n"); goto gameover; a4:cls(); printf("你是否要继续往隧道里走?\n1-是。\n2-不了。\n"); s4:wait(); switch(user_input) { case'1':goto a18; case'2':goto a22; default:goto s4; } a5:cls(); printf("你能带三件东西下飞机。请选择:\n1-香蕉、打火机、外套。\n2-威士忌、外套、匕首。\n"); s5:wait(); switch(user_input) { case'1':goto a10; case'2':goto a2; default:goto s5; } a6:cls(); printf("你走到了隧道里,隧道里很黑,忽然你被一个东西绊倒了。\n"); wait(); printf("你爬起来,看了看地上的东西,原来是一条麻绳。\n1-捡起来。\n2-置之不理。\n"); s6:wait(); switch(user_input) { case'1':goto a11; case'2':goto a23; default:goto s6; } a7:cls(); printf("你呆在飞机里,感到越来越冷,你不得不离开了飞机。\n"); goto a5; a8:cls(); printf("随着“噗”地一声闷响,你的飞机安全降落在雪地上。"); wait(); printf("你要离开飞机去找东西吗?\n1-离开。\n2-待着。\n"); s8:wait(); switch(user_input) { case'1':goto a5; case'2':goto a7; default:goto s8; } a9:cls(); printf("你走出了隧道,继续走。\n"); wait(); printf("你来到了一个山谷前。"); wait(); printf("要走过去吗?\n1-走。\n2-不走。\n"); s9:wait(); switch(user_input) { case'1':goto a17; case'2':goto a12; default:goto s9; } a10:cls(); printf("你下了飞机,漫无目的地寻找食物。\n"); wait(); printf("你看到了一棵树,树上结着一种你从未见过的果子。\n1-吃一点。\n2-离开。\n"); s10:wait(); switch(user_input) { case'1':goto a3; case'2':goto a15; default:goto s10; } a11:cls(); printf("你捡起了绳子,发现它很长。"); a++; wait(); wait(); goto a22; a12:cls(); printf("你没有走进山谷,而是继续走。\n"); wait(); printf("很快,你就因饥饿死去了。\n"); wait(); goto gameover; a13:cls(); printf("“轰”,你的飞机撞上了树,爆炸了。"); wait(); goto gameover; a14:cls(); printf("你追着飞机跑。但是,飞机比你快太多了,你没有追上。"); wait(); printf("你很累很累,倒在了地上。"); wait(); printf("你死于虚脱。\n"); wait(); goto gameover; a15:cls(); printf("你没有吃果子,而是继续走。你走了很久,来到一个隧道口。\n1-进去。\n2-不进去。\n"); s15:wait(); switch(user_input) { case'1':goto a6; case'2':goto a21; default:goto s15; } a16:cls(); printf("你捞起了很多鱼,找了些树枝,烤了鱼来吃。"); wait(); printf("你觉得好多了。"); wait(); printf("这时,天空中传来嗡嗡的响声。你抬头一看,有飞机!\n1-追着飞机跑。\n2-点篝火。\n"); s16:wait(); switch(user_input) { case'1':goto a14; case'2':goto a20; default:goto s16; } a17:cls(); printf("你走了很久,有些体力不支,于是你把香蕉吃了。"); wait(); printf("你走了很久,来到了一个废弃矿坑边。\n1-探索一下。\n2-离开。\n"); s17:wait(); switch(user_input) { case'1':goto a24; case'2':goto a19; default:goto s17; } a18:cls(); printf("你继续往隧道里走,你感觉呼吸越来越难受。\n"); wait(); wait(); printf("你死于窒息。\n"); wait(); goto gameover; a19:cls(); printf("你来到了一个冰封的湖边。"); wait(); if(a==1) { if(b==1) { printf("你用麻绳织了个渔网,用锤子砸破冰面,试图捞上一些鱼。\n"); wait(); goto a16; } else { printf("你用麻绳织了个渔网,却无法打破冰面。你无奈地望着湖面,绝望地叹了口气。\n"); wait(); printf("你饿死了。\n"); wait(); goto gameover; } } else { if(b==1) { printf("你用锤子砸破了冰面,却没办法捞鱼。你无奈地望着湖面,绝望地叹了口气。\n"); wait(); printf("你饿死了。\n"); wait(); goto gameover; } else { printf("你很想捞鱼,却无法打破冰面,又没办法捞鱼。你无奈地望着湖面,绝望地叹了口气。\n"); wait(); printf("你饿死了。\n"); wait(); goto gameover; } } a20:cls(); printf("你找来许多木头,点燃了篝火。飞机上的人看见了浓烟,降落下来,把你救上了飞机。\n"); wait(); printf("事后,你被送去了医院治疗。\n"); printf("生存成功!\n"); wait(); goto gameover; a21:cls(); printf("你绕过了隧道,继续走。\n"); goto a9; a22:cls(); printf("你要继续走吗?\n1-是。\n2-不了。\n"); s22:wait(); switch(user_input) { case'1':goto a18; case'2':goto a9; default:goto s22; } a23:cls(); printf("你没有捡起绳子。"); wait(); wait(); goto a22; a24:cls(); printf("你找到了一把锤子。\n"); b++; wait(); wait(); goto a19; return 0; }
<think>我们设计一个回调容器,要求: 1. 能够存储不同类型的回调函数(即参数不同的回调) 2. 能够一次性执行所有回调 但是,由于回调函数类型不同(参数不同),我们无法直接在一个容器中存储不同类型的回调(因为std::function类型不同)。因此,我们需要一个类型擦除的容器。 解决方案: 使用基类+继承的方式,将不同类型的回调函数包装成一个统一的基类指针,存储在容器中。基类定义一个执行接口(纯虚函数),然后每个具体的回调类型(模板类)继承基类并实现执行接口。 步骤: 1. 定义一个基类CallbackBase,包含纯虚函数operator()(执行回调)和虚析构函数。 2. 定义模板类CallbackWrapper,继承自CallbackBase,它持有一个std::function对象,并在operator()中调用这个function。 3. 定义一个容器类CallbackContainer,内部使用std::vector存储CallbackBase指针(使用智能指针管理)。 4. 提供添加回调的函数模板add,可以接受任意可调用对象(函数指针、成员函数指针、lambda等),并将其包装成CallbackWrapper,然后存入容器。 5. 提供执行所有回调的函数executeAll,遍历容器并调用每个回调对象的operator()。 注意:由于回调函数参数不同,我们无法在基类中定义带参数的operator(),因为参数类型和数量是变化的。因此,我们需要在添加回调时,将回调函数绑定到具体的参数(即创建无参的可调用对象)。也就是说,我们要求所有回调函数在添加时就已经绑定了参数,这样执行时就不需要参数了。 另一种思路:如果希望回调执行时传入参数,那么我们必须要求所有回调函数具有相同的参数类型。但根据问题,我们需要存储多种类型的回调,所以参数类型不同。因此,我们只能让回调在添加时绑定参数,变成无参的。 因此,我们设计: - 回调容器存储的是无参的可调用对象(即std::function<void()>) - 在添加回调时,通过绑定参数(使用std::bind或lambda捕获)将原本需要参数的回调转换为无参回调。 但是,用户可能有不同参数的回调,我们如何让用户在添加时绑定参数?我们可以让用户提供一个已经绑定好参数的回调(即无参函数对象),或者我们在添加时要求用户提供参数(然后我们绑定)。这里我们采用第一种方式:容器存储无参回调。 所以,我们的容器将存储std::function<void()>,然后通过类型擦除基类来存储不同类型的function<void()>(实际上不需要,因为function<void()>类型是相同的)。等等,不对,function<void()>类型是相同的,我们不需要类型擦除!那么为什么还需要基类?其实不需要基类,我们可以直接存储std::function<void()>。 因此,更简单的方法:容器内部使用std::vector<std::function<void()>>存储回调。 但是,这样我们就不能直接存储带不同参数的回调,而是存储已经被绑定参数的回调(即无参函数)。所以,在添加回调时,用户必须提供一个无参的可调用对象。我们可以通过模板函数add来接受任意可调用对象,但要求它能够转换为std::function<void()>。 那么,我们如何支持带参数的回调?答案是在添加回调时,用户必须使用std::bind或lambda将带参数的回调包装成无参的。我们可以提供辅助函数来帮助用户绑定参数。 所以,我们设计一个CallbackContainer类,它有一个std::vector<std::function<void()>>成员,并提供以下方法: template <typename Func> void add(Func&& func) { m_callbacks.emplace_back(std::forward<Func>(func)); } void executeAll() { for (auto& cb : m_callbacks) { cb(); } } 但是,这样用户需要自己绑定参数。例如: void foo(int a, double b) { ... } // 添加时 container.add(std::bind(foo, 1, 3.14)); 或者使用lambda: container.add([](){ foo(1, 3.14); }); 这样,我们就能存储多个无参回调,然后一次性执行。 但是,用户希望存储多种类型(参数不同)的回调,并且这些回调在添加时已经绑定了不同的参数,所以执行时不需要参数。这样设计是可行的。 然而,用户可能希望同一个回调函数被多次添加(绑定不同的参数),这也是允许的。 另外,我们还可以扩展,支持在添加回调时捕获调试信息(如函数名、文件名、行号),并在执行时打印。但根据问题,我们只要求存储和执行。 因此,我们实现一个简单的版本: 代码实现:</think># C++11 实现通用回调容器框架 我将设计一个基于 C++11 的回调容器框架,能够存储多种类型的回调函数并一次性执行所有回调。这个实现将结合类型擦除技术和 `std::function` 的灵活性,同时提供调试信息追踪功能。 ## 设计思路 1. **类型擦除技术**:使用 `std::function<void()>` 统一存储所有回调类型 2. **参数绑定**:在添加回调时绑定具体参数,创建无参可调用对象 3. **调试信息**:记录回调函数名、文件名和行号 4. **生命周期管理**:使用智能指针确保对象安全 ## 完整实现代码 ```cpp #include <iostream> #include <vector> #include <functional> #include <memory> #include <string> #include <utility> // 回调调试信息结构体 struct CallbackDebugInfo { std::string func_name; // 函数名 std::string file_name; // 文件名 int line_num; // 行号 CallbackDebugInfo(std::string fn, std::string file, int line) : func_name(std::move(fn)), file_name(std::move(file)), line_num(line) {} }; // 回调容器类 class CallbackContainer { public: // 添加回调函数(无参版本) template <typename Func> void add(Func&& func, CallbackDebugInfo dbg_info) { // 创建无参可调用对象 auto wrapped = [func = std::forward<Func>(func), info = std::move(dbg_info)]() { std::cout << "[执行回调] " << info.func_name << " @ " << info.file_name << ":" << info.line_num << "\n"; func(); }; callbacks_.emplace_back(std::move(wrapped)); } // 添加带参数的回调函数 template <typename Func, typename... Args> void add(Func&& func, CallbackDebugInfo dbg_info, Args&&... args) { // 绑定参数并创建无参可调用对象 auto bound_func = std::bind(std::forward<Func>(func), std::forward<Args>(args)...); auto wrapped = [bound_func, info = std::move(dbg_info)]() { std::cout << "[执行回调] " << info.func_name << " @ " << info.file_name << ":" << info.line_num << "\n"; bound_func(); }; callbacks_.emplace_back(std::move(wrapped)); } // 执行所有回调 void executeAll() { std::cout << "=== 开始执行所有回调 (" << callbacks_.size() << "个) ===\n"; for (auto& cb : callbacks_) { cb(); } std::cout << "=== 回调执行完成 ===\n"; } // 清空所有回调 void clear() { callbacks_.clear(); } // 获取回调数量 size_t size() const { return callbacks_.size(); } private: std::vector<std::function<void()>> callbacks_; }; // 辅助宏:自动捕获调试信息 #define ADD_CALLBACK(container, func, ...) \ container.add(func, CallbackDebugInfo(#func, __FILE__, __LINE__), ##__VA_ARGS__) // 辅助宏:添加成员函数回调 #define ADD_MEMBER_CALLBACK(container, obj, method, ...) \ container.add( \ [=] { (obj->*method)(__VA_ARGS__); }, \ CallbackDebugInfo(#method, __FILE__, __LINE__) \ ) // 辅助宏:添加共享指针成员函数回调 #define ADD_SHARED_MEMBER_CALLBACK(container, obj, method, ...) \ container.add( \ [=] { (obj.get()->*method)(__VA_ARGS__); }, \ CallbackDebugInfo(#method, __FILE__, __LINE__) \ ) ``` ## 使用示例 ```cpp // 示例类 class EventHandler { public: void onEvent(int id, const std::string& msg) { std::cout << "事件处理: ID=" << id << ", 消息: " << msg << "\n"; } void processData(float value) const { std::cout << "数据处理: " << value << "\n"; } }; // 普通函数 void systemAlert(const std::string& msg, int severity) { std::cout << "系统告警[" << severity << "]: " << msg << "\n"; } int main() { CallbackContainer container; // 添加普通函数回调(带参数) ADD_CALLBACK(container, systemAlert, "磁盘空间不足", 3); // 添加无参lambda回调 container.add( [] { std::cout << "定时任务执行\n"; }, CallbackDebugInfo("定时任务", __FILE__, __LINE__) ); // 添加成员函数回调 EventHandler handler; ADD_MEMBER_CALLBACK(container, &handler, &EventHandler::onEvent, 1001, "用户登录"); // 添加const成员函数回调 ADD_MEMBER_CALLBACK(container, &handler, &EventHandler::processData, 3.14f); // 添加共享指针成员函数回调(安全生命周期管理) auto sharedHandler = std::make_shared<EventHandler>(); ADD_SHARED_MEMBER_CALLBACK(container, sharedHandler, &EventHandler::onEvent, 2002, "数据更新"); std::cout << "已添加 " << container.size() << " 个回调\n"; // 一次性执行所有回调 container.executeAll(); return 0; } ``` ## 输出示例 ```plaintext 已添加 5 个回调 === 开始执行所有回调 (5个) === [执行回调] systemAlert @ main.cpp:76:1 系统告警[3]: 磁盘空间不足 [执行回调] 定时任务 @ main.cpp:79:5 定时任务执行 [执行回调] onEvent @ main.cpp:83:1 事件处理: ID=1001, 消息: 用户登录 [执行回调] processData @ main.cpp:86:1 数据处理: 3.14 [执行回调] onEvent @ main.cpp:90:1 事件处理: ID=2002, 消息: 数据更新 === 回调执行完成 === ``` ## 关键技术解析 ### 1. 类型擦除技术 通过 `std::function<void()>` 统一存储所有回调类型,无论原始回调的参数类型如何: ```cpp std::vector<std::function<void()>> callbacks_; ``` 使用 lambda 表达式将带参数的回调转换为无参形式[^1]。 ### 2. 参数绑定 使用 `std::bind` 或 lambda 捕获在添加回调时绑定具体参数: ```cpp auto bound_func = std::bind(std::forward<Func>(func), std::forward<Args>(args)...); ``` ### 3. 完美转发 使用 `std::forward` 保持参数的值类别: ```cpp template <typename Func, typename... Args> void add(Func&& func, CallbackDebugInfo dbg_info, Args&&... args) { // ... } ``` ### 4. 生命周期管理 对于成员函数回调,使用共享指针确保对象安全: ```cpp ADD_SHARED_MEMBER_CALLBACK(container, sharedHandler, &EventHandler::onEvent, ...); ``` ### 5. 调试信息 通过预定义宏自动捕获位置信息: ```cpp CallbackDebugInfo(#func, __FILE__, __LINE__) ``` ## 性能优化 ### 减少拷贝开销 使用 `std::ref` 包装大型对象,避免不必要的拷贝[^5]: ```cpp BigData data; ADD_CALLBACK(container, processBigData, std::ref(data)); ``` ### 移动语义优化 在回调添加时使用移动语义: ```cpp callbacks_.emplace_back(std::move(wrapped)); ``` ## 应用场景 1. **事件处理系统**:统一管理多种事件处理器 2. **定时任务调度**:批量执行定时回调[^4] 3. **插件系统**:管理不同插件的回调函数 4. **游戏引擎**:处理物理碰撞、动画事件等 5. **GUI框架**:管理界面事件回调 ## 相关问题 1. 如何在多线程环境下安全使用回调容器? (需要添加互斥锁保护容器操作) 2. 如何实现回调的优先级和排序机制? (可以使用优先级队列代替vector) 3. 如何扩展支持带返回值的回调函数? (需要修改设计,可能需要使用 `std::future`) 4. C++17/20 的哪些特性可以优化此实现? (`std::invoke`、`std::apply` 等) 5. 如何实现回调的取消和移除机制? (需要为每个回调添加唯一标识符) > 引用说明: > 回调框架设计参考了 `std::function` 的类型安全特性[^2],成员函数绑定使用了仿函数技术[^3],参数绑定优化参考了引用包装技术[^5]。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值