如何为动态的,大小不受限制的“迷宫”构建数据结构?


43

我实际上不确定“迷宫”是否是正确的术语。基本上,用户从一个Room具有4个门(N,S,E和W)的单个门开始。它们可以向任何方向移动,每个后续房间都包含另一个房间,该房间的其他任何地方都具有1至4个门口。

“迷宫”的大小应该是无限的,并且随着房间的移动而增长。Rooms可用数量有限,但是可用数量是动态的并且可以更改。

我的问题是,我不确定这种模式的最佳数据结构

我最初想到的只是使用[X] [X] Room对象数组,但我真的宁愿避免这样做,因为该对象应该沿任何方向生长,并且只应构建“已访问”的房间。

另一个想法是让每个Room类包含RoomN,S,E和W的4个链接属性,并且仅链接到前一个Room,但是问题是我不知道如何识别用户是否进入了一个有一个相邻的房间已经“建造”

例如,

-------------
| | | |
   开始5 4
| | | |
----
------------- --- ---
| | | | | |
| 1 2 3
| | | | | |
---------------

如果用户从“开始”>“ 1”>“ 2”>“ 3”>“ 4”>“ 5”移动,则Room#5需要知道W包含起始房间,S是房间#2,在这种情况下不可用,N可以是新的Room或一堵墙(一无所有)。

也许我需要混合使用阵列和链接的房间,或者也许我只是看错了方向。

是否有更好的方法为这种“迷宫”构建数据结构?还是我在目前的思维过程中步入正轨,而只是缺少一些信息?

(如果您有兴趣,该项目是一个与Munchkin Quest非常相似的游戏)


我认为任何类型的数组都不会起作用,因为房间会向任何方向扩展...那么,如果您从[0,0]开始并向左移动?它将尝试[-1,0]。
保罗

@Paul追加一行/列,移走所有阵列数据,然后移开所有玩家位置以匹配新的地图阵列。速度慢,可能很难,具体取决于必须移动多少。尽管如此,Bubblewrap的答案要好得多。
伊兹方(Izkata)2012年

我可能是错的,但是在GameDev.SE上会不会更好?
2012年

1
@Dynamic这是一个数据结构问题,因此在这里很合适。
伊兹方(Izkata)2012年

Answers:


44

给出每个房间的坐标(开始将是(0,0)),然后将每个生成的房间按坐标存储在字典/哈希图中。

确定每个房间的相邻坐标很简单,您可以使用它来检查一个房间是否已经存在。您可以插入空值来表示已经确定不存在房间的位置。(或者,如果不可能,我不确定atm,这是用于不包含Room的坐标的单独字典/ hasmap)


2
使每个Room对象都包含其他Room对象的东西向西南信息,因此您既可以使用字典/哈希图,也可以使用Room基本方向来导航迷宫(有时您希望使用绝对坐标和有时您会想知道Room对象X的北部。
尼尔2012年

2
@Paul我认为这个想法是创建一个房间字典,当建立一个新的Room房间时,在字典中寻找相邻的房间并将它们的链接添加到Room对象,然后再将新房间添加到其余的边上。因此,只有在创建新Room对象时才发生字典查找,而不是在两次之间旅行时发生Rooms
Rachel

7
完全没有理由将链接存储到Room对象内部的其他房间。如果您在房间里(x,y)并想向北旅行,您可以(x,y+1)在字典中查找房间,如果不存在则创建它。字典查找非常快O(1)
·比勒费尔特

3
@KarlBielefeldt:房间@ (x,y+1)可能存在,但不能(x,y)向北航行。也就是说,边缘可能不会直接从(x,y)移到(x,y+1)
史蒂文·埃弗斯

2
你们让这变得复杂了。这与数组基本相同。除了要在字典中查找而不是在二维数组中查找之外。您遇到的与阵列有关的任何问题也将存在...但是他还是要使用阵列。
user606723

15

在大多数情况下,您所描述的内容听起来像一张图表。考虑到您的一些需求(不断增长的方面),我可能会选择一个邻接表作为实现(另一个常见的选择是邻接矩阵)。

我不确定您使用的是哪种语言,但是此处提供了一个很好的教程/说明,其中包含通过.NET中的邻接表实现的图形的实现细节。


1
由于N / E / S / W并不是图概念的真正组成部分,因此不确定图是否足以描述这一点。只有连接并且可能进/出。
爱德华·斯特朗奇

3
@CrazyEddie:请记住,/ a数据结构并非旨在直接映射到任何特定域(实际上,这是它们的好处)。在此特定示例中,图形将是有方向的,并且边缘可以用枚举简单地注释。
史蒂文·埃弗斯

该注释甚至可以平息东西方,南北之间的关系。这将消除由于连接不良而导致的问题,即从房间A向西走到房间B,然后从房间B向东走到房间C(而不是房间A)。
彼得·史密斯

3
假设我们要实现一个由向导填充的房间,该向导通过在随机方向上将玩家传送十个方块来保护自己。在基于图的数据结构中找到该点非常昂贵,尤其是如果该房间不存在并且您必须生成将该房间链接到图中的另一个房间的所有房间(由于该结构不允许有多个房间,地牢的相互断开的部分)。
Mark Booth

2
@MarkBooth,但随后我们更改了要求,不是吗?
约书亚·德雷克

9

关于实现有一些想法(我想到了卡尔卡松,它在构建矩阵时还有许多其他挑战性方面,甚至是我曾经被问到的面试问题)。

有关此结构,存在一些问题:

  1. X,Y有房间吗?
  2. 空位置X,Y是否有房间[N / S / E / W]?

简单列表和图形的问题

简单列表的困难在于,必须走动结构才能测试是否可以在给定位置放置某些东西。系统需要一种访问任意位置O(1)的方式。

考虑:

1 2 3 ? 4
5 . . * 6
7 8 9 A B

为了找到可能的位置“?”的信息,当位置为3时,必须一直走到4。在链接的答案中包含有关它跳了多少个节点的额外信息(因此3将链接到4)。跳'1'的额外信息)在从6或A中查找'*'的邻接时仍需要步行。

简单的大型数组

客房数量有限

如果这不是一个很大的数字,则只需创建一个2N x 2N的数组并将其保留在那里。100 x 100数组是“仅” 10,000个指针和50个对象。直接索引到数组中。

如果超出范围的活动将数组移动到始终在约束范围内,则可以将其减少为NxN。例如,如果要添加一个将使数组下溢的空间,则使网格将每个元素移到一个位置,以使不再有下溢。此时,50个房间的世界大小将是2500个指针和50个对象。

稀疏数组和矩阵

为了更好地创建它,请查看一个稀疏数组,其中大多数元素为0或null。对于二维,这称为稀疏矩阵。许多编程语言都有使用这种结构的各种库。如果库限制为整数,则可以将该整数用作固定房间阵列的索引。

我的首选方法是将房间作为结构(指向相邻房间的指针和坐标),并使房间也成为来自哈希表的指针,该哈希表是在坐标上进行哈希处理的。在这种情况下,要从空房间询问什么房间[N / S / E / W],将查询哈希表。顺便说一句,这是用于存储稀疏矩阵的“ DOK”格式。


1
正如Bubblewrap所建议的那样,答案很好,以位置元组为键的字典是用于实现稀疏矩阵的合理结构。
Mark Booth

2

这个怎么样..

给每个单元格一个指向每个邻居的链接。给每个单元格指定某种名称(即“ 0/0”,“-10/0”(假设您从0,0开始))。添加一个包含所有名称的HashSet。当您尝试移动到另一个单元格时,只需检查HashSet中是否不存在该邻居即可。

另外,如果您创建另一个房间的开口,是否意味着这些房间存在?因此,您将创建一个指向空房间的链接,而没有指向任何房间的链接。您可能还想检查新房间的邻居。如果存在,并且将打开您的新房间,请在该房间中添加一扇门。

   Empty   
   (0,1)        

---    ---            ----------
|        |            |        |
    0,0       1,0        2,0       Empty
|        |            |        |   (3,0)
---    --- ---------- ---    ---
|        | |        | |        |
|   0,-1      1,-1       2,-1      Empty
|        | |        | |        |   (3,-1)
---    --- ---    --- ----------

   Empty     Empty   
  (0,-2)    (1,-2)

HashSet = {0 | 0,1 | 0,2 | 0,3 | 0,0 | -1,1 | -1 ....} 1,0:W = 0,0 / Door; 1,0:N = 1,1 / Empty; E = 2,0 /门 S = 1,-1 /墙

您还必须确保给每个新房间至少一个不相邻(到另一个房间)的门,以便迷宫可以朝那个方向生长。


1

您正在设计的内容听起来很像图表。

迷宫图

这些通常被表示为一组状态Q,初始状态q 0Q,和一些转换Δ。因此,我建议您像这样存储它。

  • 集合Q{s,1,2,3,5,6}
  • 初始状态q 0:s
  • 过渡关系Δ的映射:{s→1,s→5,1→2,2→3,3→4,4→5}

最合理的语言既有地图又有集合。


0

您可以考虑4向链表...

我最初考虑只使用[X] [X]个Room对象数组,但是我真的宁愿避免这种情况,因为该对象应该沿任何方向生长,并且只应构建“已访问”的房间。

您仍然可以使用[] []。动态增长可能会很慢,尤其是在一开始添加时,但可能并不那么糟糕。您应该配置文件进行检查。实际上,尝试操纵某些链接列表或映射可能会更糟,并且从恒定的角度而不是偶然的角度进行操作。

您可以通过实施懒惰评估来仅构建访问过的房间。可以在一个对象中包含一个房间,并且只有在该对象上调用该对象后,该对象才能建立room()。然后,它每次之后都返回相同的内容。不难做。


1
您能否扩展一下“ 4向链接列表”的含义?我唯一能找到的是CodeProject文章,归结为邻接列表。
史蒂文·埃弗斯

0

我通过《在计算机上创建冒险游戏》一书来学习如何做到这一点。如果您愿意研究BASIC代码(这本书很旧),请在这里阅读:

http://www.atariarchives.org/adventure/chapter1.php

对于快捷方式,他们要做的是拥有一个房间数组,每个数组中的一个元素是一个指向您可以进入的另一个房间的指针,用0(这几天我将使用-1)表示您不能往那边走。例如,您要制作一个房间结构(或教室或您拥有什么)

room {
 int north_dir;
 int south_dir;
 int west_dir;
 int east_dir;
 // All other variables and code you care about being attached to the rooms here
}

然后,您可能拥有一个数组或链表结构,或者您还想管理自己的房间。对于数组样式(或C ++向量),我将使用该代码,并且这些方向将保留它们链接到的房间的索引。对于链接列表,可以使用指向下一个房间的指针。

正如其他人所说,如果您需要在人们进入房间时动态生成新房间,您也可以这样做。


0

不要试图用一种结构来解决所有问题。

定义房间对象以存储有关房间的内容,而不是其与其他房间的关系。然后,一维数组,列表等可以根据需要增长。

一个单独的结构可以容纳“可达性”,即从我所在的房间可以访问哪些房间。然后确定是否需要快速计算传递可到达性。您可能需要蛮力计算和缓存。

避免过早优化。使用真正简单的结构并使用愚蠢的易于理解的算法,然后优化所使用的算法。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.