Go Ebiten随机迷宫生成示例

引言
迷宫生成是计算机科学中一个经典的问题,常用于算法教学和游戏开发。本文将介绍如何使用 Go 语言和 Ebiten 游戏引擎实现一个基于深度优先搜索(DFS)的随机迷宫生成算法,并通过可视化的方式展示迷宫的生成过程。
技术栈
- Go 语言:一种高效、简洁的编程语言,适合实现算法和并发任务。
- Ebiten:一个轻量级的 2D 游戏引擎,适合快速开发简单的图形应用程序。
- 深度优先搜索(DFS):一种经典的图遍历算法,用于生成随机迷宫。
算法原理
深度优先搜索(DFS)
DFS 是一种用于遍历或搜索树或图的算法。在迷宫生成中,我们可以将迷宫看作一个图,每个块是一个节点,墙是节点之间的边。DFS 通过随机选择邻居节点并打破墙来生成迷宫。
栈回溯
为了确保迷宫生成的完整性,我们使用栈来记录访问路径。当当前块没有未访问的邻居时,通过栈回溯到上一个未完全探索的块,继续生成迷宫。
代码实现
package mainimport ("image/color""math/rand""os""time""github.com/hajimehoshi/ebiten/v2""github.com/hajimehoshi/ebiten/v2/vector"
)const (N int = 20 // 迷宫的行数和列数BlockSize int = 50 // 每个迷宫块的大小(像素)WindowWidth int = N*BlockSize + 2*int(WW) // 窗口宽度WindowHeight int = N*BlockSize + 2*int(WW) // 窗口高度WW float32 = 2 // 墙的宽度(像素)BS float32 = float32(BlockSize) // 块大小的浮点数表示
)// Pos 结构体表示一个二维坐标
type Pos struct {X, Y int
}// Dirs 数组表示四个可能的移动方向(上、左、右、下)
var Dirs [4]Pos = [4]Pos{Pos{0, -1}, Pos{-1, 0}, Pos{1, 0}, Pos{0, 1}}// Game 结构体表示游戏的状态
type Game struct {T int // 当前已访问的块数P Pos // 当前的位置Walls [N][N][4]bool // 记录每个块的四面墙是否存在IsVis [N][N]bool // 记录每个块是否被访问过Stack []Pos // 栈,用于记录访问路径
}// Update 是 Ebiten 游戏循环中的更新函数,每一帧调用一次
func (g *Game) Update() error {SystemFunction() // 处理系统功能(如退出)if g.T < N*N { // 如果还有未访问的块g.Next() // 生成下一个块}return nil
}// Draw 是 Ebiten 游戏循环中的绘制函数,每一帧调用一次
func (g *Game) Draw(screen *ebiten.Image) {g.DrawWalls(screen) // 绘制迷宫的墙
}// Layout 设置游戏窗口的布局
func (g *Game) Layout(outsideWidth, outsideHeight int) (int, int) {return outsideWidth, outsideHeight
}func main() {ebiten.SetWindowTitle("maze") // 设置窗口标题ebiten.SetWindowSize(WindowWidth, WindowHeight) // 设置窗口大小game := &Game{P: Pos{rand.Intn(N), rand.Intn(N)}, // 随机选择一个起始位置Stack: make([]Pos, 0), // 初始化栈}if err := ebiten.RunGame(game); err != nil { // 运行游戏panic(err)}
}// SystemFunction 处理系统功能,如退出游戏
func SystemFunction() {if ebiten.IsKeyPressed(ebiten.KeyEscape) { // 如果按下 ESC 键os.Exit(0) // 退出程序}
}// DrawWalls 绘制迷宫的墙
func (g *Game) DrawWalls(screen *ebiten.Image) {if g.T < N*N { // 如果还有未访问的块// 绘制当前块的位置(红色方块)vector.DrawFilledRect(screen, float32(g.P.X)*BS, float32(g.P.Y)*BS, BS, BS, color.RGBA{255, 0, 0, 255}, true)}// 遍历所有块,绘制墙for i := 0; i < N; i++ {for j := 0; j < N; j++ {if !g.Walls[i][j][0] { // 如果上墙存在vector.DrawFilledRect(screen, float32(i)*BS, float32(j)*BS, BS, WW, color.White, true)}if !g.Walls[i][j][1] { // 如果左墙存在vector.DrawFilledRect(screen, float32(i)*BS, float32(j)*BS, WW, BS, color.White, true)}if !g.Walls[i][j][2] { // 如果右墙存在vector.DrawFilledRect(screen, float32(i+1)*BS, float32(j)*BS, WW, BS, color.White, true)}if !g.Walls[i][j][3] { // 如果下墙存在vector.DrawFilledRect(screen, float32(i)*BS, float32(j+1)*BS, BS, WW, color.White, true)}}}
}// Next 生成迷宫的下一个块
func (g *Game) Next() {// check 函数检查给定的坐标是否在迷宫范围内且未被访问过check := func(x, y int) bool {return x >= 0 && x < N && y >= 0 && y < N && !g.IsVis[x][y]}// 标记当前块为已访问if !g.IsVis[g.P.X][g.P.Y] {g.IsVis[g.P.X][g.P.Y] = trueg.T++}// 查找当前块的所有未访问邻居var neighbors []intfor i := 0; i < 4; i++ {if check(g.P.X+Dirs[i].X, g.P.Y+Dirs[i].Y) {neighbors = append(neighbors, i)}}if len(neighbors) > 0 {// 如果有未访问的邻居,随机选择一个方向d := neighbors[rand.Intn(len(neighbors))]g.Stack = append(g.Stack, g.P) // 将当前位置压入栈g.Walls[g.P.X][g.P.Y][d] = true // 打破当前块的墙g.P.X += Dirs[d].X // 移动到邻居块g.P.Y += Dirs[d].Yg.Walls[g.P.X][g.P.Y][3-d] = true // 打破邻居块的对应墙} else if len(g.Stack) > 0 {// 如果没有未访问的邻居,回溯到上一个块g.P = g.Stack[len(g.Stack)-1] // 弹出栈顶元素g.Stack = g.Stack[:len(g.Stack)-1]}
}
相关文章:
Go Ebiten随机迷宫生成示例
引言 迷宫生成是计算机科学中一个经典的问题,常用于算法教学和游戏开发。本文将介绍如何使用 Go 语言和 Ebiten 游戏引擎实现一个基于深度优先搜索(DFS)的随机迷宫生成算法,并通过可视化的方式展示迷宫的生成过程。 技术栈 Go …...
前端学习DAY31(子元素溢出父元素)
.box1{width: 200px;height: 200px;background-color: chocolate;} 子元素是在父元素的内容区中排列的,如果子元素的大小超过了父元素,则子元素会从 父元素中溢出,使用overflow属性设置父元素如何处理溢出的子元素 可选值:visible…...
『SQLite』表的创建、修改和删除
本节摘要:主要讲述SQLite中创建、删除、修改表等操作。 创建表 CREATE TABLE 语句来创建表。 修改表 ALTER TABLE 语句来修改表名称、已有表字段,或者新增字段。 删除表 DROP TABLE 语句用来删除表. 注意: 上述内容详细讲解见文章&#…...
可持久化数据结构-线段树(主席树)
可持久化数据结构-线段树(主席树) (与可持久化字典树差不多) 概念:可持久化线段树是基本线段树的一个简单拓展, 是使用函数式编程思想的线段树; 作用: 可以存下来数据结构的所有历史版本 特点: 拓扑结构…...
如何利用PHP爬虫按关键字搜索淘宝商品
在当今的电商时代,获取淘宝商品信息对于市场研究、价格监控和竞争分析等方面具有重要意义。手动搜索和整理大量商品信息不仅耗时耗力,而且容易出错。幸运的是,PHP爬虫技术为我们提供了一种高效、自动化的方式来按关键字搜索淘宝商品。本文将详…...
GitHub - riscv-software-src/riscv-isa-sim: Spike, a RISC-V ISA Simulator
GitHub - riscv-software-src/riscv-isa-sim: Spike, a RISC-V ISA Simulator 操作手册 $ apt-get install device-tree-compiler libboost-regex-dev libboost-system-dev $ mkdir build $ cd build $ ../configure --prefix$RISCV $ make $ [sudo] make install 具体安装 …...
ubuntu开机启动服务
需求背景: 需要监控日志,每次都是手动启动 nohup ./prometheus >/dev/null & nohub ./node_exporter >/dev/null & 需求目标: 重启后系统自动启动服务...
电子电气架构 --- 设计车载充电机的关键考虑因素
我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 简单,单纯,喜欢独处,独来独往,不易合同频过着接地气的生活,除了生存温饱问题之外,没有什么过多的欲望,表面看起来很高冷,内心热情,如果你身…...
2025_0105_生活记录
3号去内蒙看了流星雨。还记得上次看流星的时间是2018年,也是冬天,大家在雁栖湖校区的操场上仰望星空。那个时候幸运的看到了一颗流星,便迅速地在心里许愿。这次看到了三颗流星,我也许了愿,希望实现。 24年走过了十多个…...
电池管理系统(BMS)架构详细解析:原理与器件选型指南
BMS(电池管理系统)架构详细讲解 从你提供的BMS(Battery Management System)架构图来看,主要涉及到电池监控模块、通信模块、功率控制模块等部分。下面我将详细讲解该架构的各个功能模块及其工作原理。 1. 电池管理核…...
用JAVA编写一个简单的小游戏
用Java语言编写一个简单的小游戏。这里是一个非常基础的猜数字小游戏的代码示例。在这个游戏中,程序会随机选择一个1到100之间的整数,玩家需要猜测这个数字是什么。每次猜测后,程序会告诉玩家他们猜的数字是太高了、太低了还是正确。 impor…...
【SpringSecurity】二、自定义页面前后端分离
文章目录 1、用户认证流程AuthenticationSuccessHandler AuthenticationFailureHandlerSecurityFilterChain配置用户认证信息 2、会话并发处理2.1、实现处理器接口2.2、SecurityFilterChain配置 1、用户认证流程 AuthenticationSuccessHandler AuthenticationFailureHandler …...
小兔鲜儿:头部区域的logo,导航,搜索,购物车
头部:logo ,导航,搜索,购物车 头部总体布局: 设置好上下外边距以及总体高度, flex布局让总体一行排列 logo: logo考虑搜索引擎优化,所以要使用 h1中包裹 a 标签,a 里边写内容(到时候…...
什么是VLAN?
VLAN(Virtual Local Area Network,虚拟局域网)是一种将物理局域网划分成多个逻辑上独立的虚拟网络的技术。VLAN不依赖于设备的物理位置,而是通过逻辑划分,将局域网内的设备虚拟地组织到同一组。这种技术允许网络管理员…...
WPS计算机二级•数据查找分析
听说这里是目录哦 通配符🌌问号(?)星号(*)波形符(~) 排序🌠数字按大小排序以当前选定区域排序以扩展选定区域排序 文字按首字母排序 快速筛选分类数据☄️文字筛选数字筛选颜色筛选…...
计算机网络 (28)虚拟专用网VPN
前言 虚拟专用网络(VPN)是一种在公共网络上建立私有网络连接的技术,它允许远程用户通过加密通道访问内部网络资源,实现远程办公和安全通信。 一、基本概念 定义:VPN是一种通过公共网络(如互联网)…...
【Python学习(七)——序列、列表、元组、range、字符串、字典、集合、可变类型不可变类型】
Python学习(七)——序列、列表、元组、range、字符串、字典、集合、可变类型&不可变类型 本文介绍了序列、列表、元组、range、字符串、字典、集合、可变类型&不可变类型,仅作为本人学习时记录,感兴趣的初学者可以一起看…...
MATLAB常用建模方法——常用非参数检验
常用非参数检验 在用样本数据对正态总体参数作出统计判断(例如参数估计和假设检验)时,要求样本数据应服从正态分布,这种数据分布类型已知的总体参数的假设检验称为参数假设检验。 与参数假设检验相对应的还有非参数假设检验&#…...
【多线程初阶篇 ²】创建线程的方式
目录 二、多线程代码 1.继承Thread类 2.实现Runnable接口 3.匿名内部类 3.1 创建Thread⼦类对象 3.2 创建Runnable⼦类对象 4.lambda表达式(推荐) 小结: 🔥面试题:Java中创建线程都有哪些写法 二、多线程代码 …...
纵览!报表控件 Stimulsoft Reports、Dashboards 和 Forms 2025.1 新版本发布!
Stimulsoft 2025.1 新版发布,旨在增强您创建报告、仪表板和 PDF 表单的体验!此最新版本为您带来了许多改进和新功能,使数据处理更加高效和用户友好。亮点包括对 .NET 9 的支持、Microsoft Analysis Services 的新数据适配器、发布向导中适用于…...
python打卡day49
知识点回顾: 通道注意力模块复习空间注意力模块CBAM的定义 作业:尝试对今天的模型检查参数数目,并用tensorboard查看训练过程 import torch import torch.nn as nn# 定义通道注意力 class ChannelAttention(nn.Module):def __init__(self,…...
蓝牙 BLE 扫描面试题大全(2):进阶面试题与实战演练
前文覆盖了 BLE 扫描的基础概念与经典问题蓝牙 BLE 扫描面试题大全(1):从基础到实战的深度解析-CSDN博客,但实际面试中,企业更关注候选人对复杂场景的应对能力(如多设备并发扫描、低功耗与高发现率的平衡)和前沿技术的…...
el-switch文字内置
el-switch文字内置 效果 vue <div style"color:#ffffff;font-size:14px;float:left;margin-bottom:5px;margin-right:5px;">自动加载</div> <el-switch v-model"value" active-color"#3E99FB" inactive-color"#DCDFE6"…...
【单片机期末】单片机系统设计
主要内容:系统状态机,系统时基,系统需求分析,系统构建,系统状态流图 一、题目要求 二、绘制系统状态流图 题目:根据上述描述绘制系统状态流图,注明状态转移条件及方向。 三、利用定时器产生时…...
论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一)
宇树机器人多姿态起立控制强化学习框架论文解析 论文解读:交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一) 论文解读:交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化…...
全志A40i android7.1 调试信息打印串口由uart0改为uart3
一,概述 1. 目的 将调试信息打印串口由uart0改为uart3。 2. 版本信息 Uboot版本:2014.07; Kernel版本:Linux-3.10; 二,Uboot 1. sys_config.fex改动 使能uart3(TX:PH00 RX:PH01),并让boo…...
关于uniapp展示PDF的解决方案
在 UniApp 的 H5 环境中使用 pdf-vue3 组件可以实现完整的 PDF 预览功能。以下是详细实现步骤和注意事项: 一、安装依赖 安装 pdf-vue3 和 PDF.js 核心库: npm install pdf-vue3 pdfjs-dist二、基本使用示例 <template><view class"con…...
SpringAI实战:ChatModel智能对话全解
一、引言:Spring AI 与 Chat Model 的核心价值 🚀 在 Java 生态中集成大模型能力,Spring AI 提供了高效的解决方案 🤖。其中 Chat Model 作为核心交互组件,通过标准化接口简化了与大语言模型(LLM࿰…...
热门Chrome扩展程序存在明文传输风险,用户隐私安全受威胁
赛门铁克威胁猎手团队最新报告披露,数款拥有数百万活跃用户的Chrome扩展程序正在通过未加密的HTTP连接静默泄露用户敏感数据,严重威胁用户隐私安全。 知名扩展程序存在明文传输风险 尽管宣称提供安全浏览、数据分析或便捷界面等功能,但SEMR…...
js 设置3秒后执行
如何在JavaScript中延迟3秒执行操作 在JavaScript中,要设置一个操作在指定延迟后(例如3秒)执行,可以使用 setTimeout 函数。setTimeout 是JavaScript的核心计时器方法,它接受两个参数: 要执行的函数&…...
