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 的新数据适配器、发布向导中适用于…...
XML Group端口详解
在XML数据映射过程中,经常需要对数据进行分组聚合操作。例如,当处理包含多个物料明细的XML文件时,可能需要将相同物料号的明细归为一组,或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码,增加了开…...
智慧医疗能源事业线深度画像分析(上)
引言 医疗行业作为现代社会的关键基础设施,其能源消耗与环境影响正日益受到关注。随着全球"双碳"目标的推进和可持续发展理念的深入,智慧医疗能源事业线应运而生,致力于通过创新技术与管理方案,重构医疗领域的能源使用模式。这一事业线融合了能源管理、可持续发…...
SpringBoot+uniapp 的 Champion 俱乐部微信小程序设计与实现,论文初版实现
摘要 本论文旨在设计并实现基于 SpringBoot 和 uniapp 的 Champion 俱乐部微信小程序,以满足俱乐部线上活动推广、会员管理、社交互动等需求。通过 SpringBoot 搭建后端服务,提供稳定高效的数据处理与业务逻辑支持;利用 uniapp 实现跨平台前…...
WEB3全栈开发——面试专业技能点P2智能合约开发(Solidity)
一、Solidity合约开发 下面是 Solidity 合约开发 的概念、代码示例及讲解,适合用作学习或写简历项目背景说明。 🧠 一、概念简介:Solidity 合约开发 Solidity 是一种专门为 以太坊(Ethereum)平台编写智能合约的高级编…...
零基础设计模式——行为型模式 - 责任链模式
第四部分:行为型模式 - 责任链模式 (Chain of Responsibility Pattern) 欢迎来到行为型模式的学习!行为型模式关注对象之间的职责分配、算法封装和对象间的交互。我们将学习的第一个行为型模式是责任链模式。 核心思想:使多个对象都有机会处…...
前端开发面试题总结-JavaScript篇(一)
文章目录 JavaScript高频问答一、作用域与闭包1.什么是闭包(Closure)?闭包有什么应用场景和潜在问题?2.解释 JavaScript 的作用域链(Scope Chain) 二、原型与继承3.原型链是什么?如何实现继承&a…...
ios苹果系统,js 滑动屏幕、锚定无效
现象:window.addEventListener监听touch无效,划不动屏幕,但是代码逻辑都有执行到。 scrollIntoView也无效。 原因:这是因为 iOS 的触摸事件处理机制和 touch-action: none 的设置有关。ios有太多得交互动作,从而会影响…...
深度学习习题2
1.如果增加神经网络的宽度,精确度会增加到一个特定阈值后,便开始降低。造成这一现象的可能原因是什么? A、即使增加卷积核的数量,只有少部分的核会被用作预测 B、当卷积核数量增加时,神经网络的预测能力会降低 C、当卷…...
以光量子为例,详解量子获取方式
光量子技术获取量子比特可在室温下进行。该方式有望通过与名为硅光子学(silicon photonics)的光波导(optical waveguide)芯片制造技术和光纤等光通信技术相结合来实现量子计算机。量子力学中,光既是波又是粒子。光子本…...
【生成模型】视频生成论文调研
工作清单 上游应用方向:控制、速度、时长、高动态、多主体驱动 类型工作基础模型WAN / WAN-VACE / HunyuanVideo控制条件轨迹控制ATI~镜头控制ReCamMaster~多主体驱动Phantom~音频驱动Let Them Talk: Audio-Driven Multi-Person Conversational Video Generation速…...
