2D迷宫减1D


27

这个挑战是关于将2D迷宫转换为1D迷宫。

总览

+-+-+-+-+-+-+   +-+-+-+-+-+-+                    graph {
| |   |     |   |A|   |    B|   A         B        A -- D
+ + + + +-+-+   + + + + +-+-+    \        |        C -- D
|   | |     |   |   | |     |     \       |        D -- E
+-+-+ +-+-+ +   +-+-+ +-+-+ +      \      |        E -- F
|           |   |C   D E   F|   C---D-E---F        E -- G
+-+-+-+ +-+ +   +-+-+-+ +-+ +         |   |        B -- F
|         | |   |      G  | |     .---G   |        F -- J
+ +-+-+-+ + +   + +-+-+-+ + +   .'   /    |        G -- H
| |       | |   |H|I      |J|   H I-'     J        G -- I
+-+-+-+-+-+-+   +-+-+-+-+-+-+     (ascii)        } // (graphviz dot)       
   Figure 1       Figure 2                 Figure 3

出于此挑战的目的,传统的2D迷宫是由晶格点形成的矩形迷宫,其中所有以下条件均成立:

  • 它是封闭的(外缘由墙壁连接)。
  • 所有晶格点都连接到墙壁
  • 它已连接(每两个空间X和Y之间都有一条路径)
  • 它是非循环的(从任何空间X到X的路径都没有回溯而没有回溯)

图1显示了传统的2D迷宫。这些迷宫具有三个有趣的领域:

  • 死胡同 -只有一个可用路径的地方
  • 走廊 -有两个可用路径的地方
  • 决策点 -具有三个或四个可用路径的地方

对于每个这样的迷宫,可以创建一个图形,其中死角和决策点为节点,并且每两个节点之间都有一条沿走廊的路径连接的边。图2显示了带有此类节点标记的同一迷宫,图3显示了迷宫的图形(以ASCII和Graphviz点表示法表示)。

一维迷宫

一维迷宫包含成对出现的翘曲点,并使用字母标识(无论哪种情况)。图4显示了示例1D迷宫。否则,这与高度为1的2D迷宫相同,如图5所示。请特别注意,在图5中,用标记的晶格点位置+从左到右交替;在1D迷宫中,从最左边的墙开始的所有其他字符也是一个晶格点。

                                 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|  D|  D E|G E F|  F  |  G  |    |  D|  D E|G E F|  F  |  G  |
                                 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+
            Figure 4                         Figure 5

导航此迷宫的规则如下。每一步都可以表示为前进(>)或后退(<)。默认情况下,此处的前进和后退与我们直观的空间意识具有相同的含义;前进到紧靠右边的位置,前进到紧靠左边的位置。

变形点表示与邻居非对称交换连接的位置。如果您是从邻居到翘曲点,则两个翘曲点的位置会互换;如果您是从翘曲点到邻居,则不会交换它们。例如,在图6中,从1向后移动会将您带到2(因为1是G的邻居,而我们正在从邻居那里移动,因此点2和@被交换了)。从2(翘曲点G)向前移动可将您带到3(此处,我们从翘曲点开始,因此没有交换)。同样,从3向后移动会将您带到@。

        54 2367    89^   @1
|  D|  D E|G E F|  F  |  G  |
                     Y     X
          Figure 6

图6还显示了使用move序列从X到Y的示例导航<<>><>>>>>。这些动作将您带到按123456789^顺序分别标记的点。随时使用下一部分中的代码片段自行进行探索。

将2D转换为1D

给定一维迷宫,可以创建一个图形,其中每个节点都是死角或翘曲点对,并且沿走廊连接的任何两个节点之间都存在边。此图使我们可以比较1D和2D迷宫。

例如,图4中的1D迷宫与图1中的迷宫相同。要了解原因,图7将标签添加到死角。使用这些标签来构建图形,图7的图形再次只是图3。图8显示了构建此图的突破。

|  D|  D E|G E F|  F  |  G  |
 A   C           B   J H   I 
          Figure 7

|  D|  D E|G E F|  F  |  G  |
+ + + + + + + + + + + + + + + <- lattice points
|A  |C    |     |B   J|H   I| <- dead ends
|A D|C D E|G E F|B F J|H G I| <- all nodes (dead ends+warp points); i.e.:
                                 "where each end is either a dead end
                                  or a warp point pair"; note that each
                                  pair of warp points is the same node.
|A-D|C-D-E|G-E-F|B-F-J|H-G-I| <- corridors; note each is a connection, since
  1   2 3   4 5   6 7   8 9      "edges exist between any two nodes
                                  connected along a corridor"
   graph {                 graph {                 
     A -- D  // 1 <---->     A -- D                
     C -- D  // 2 <---->     C -- D                
     D -- E  // 3 <---->     D -- E                
     G -- E  // 4 <---->     E -- G                
     E -- F  // 5 <---->     E -- F                
     B -- F  // 6 <---->     B -- F                
     F -- J  // 7 <---->     F -- J                
     H -- G  // 8 <---->     G -- H                
     G -- I  // 9 <---->     G -- I                
   }                ^      }
    Built from      |      From Figure 3
     1D maze         `-> isomorphic mappings
                Figure 8

(请注意,每个图的标签和布局都是人为选择的,以便于说明目的对齐;通常来说,这是一个图同构问题)。

提供以下代码段以帮助可视化1D迷宫的力学以及1D迷宫,等效图和2D迷宫之间的连接。

在此片段中导航1D迷宫时,突出显示您触摸的最后两个节点。相同节点在等效图和2D迷宫上以相同方式突出显示。


通常,对于任何传统的2D迷宫,都可以创建这种类型的等效1D迷宫。图9是一个稍微复杂的示例:

+-+-+-+-+-+-+   +-+-+-+-+-+-+                   graph {
| |   |   | |   |A|   |   |B|   A         B       A -- D
+ + + + + + +   + + + + + + +    \       /        C -- D
|   | | |   |   |   | | |   |     \     /         D -- E
+-+-+ + +-+-+   +-+-+ + +-+-+      \   /          B -- E
|           |   |C   D E    |   C---D-E           E -- F
+-+-+-+ +-+ +   +-+-+-+ +-+ +         |\          E -- I
|         | |   |      F  | |     .---F \         F -- G
+ +-+-+-+ + +   + +-+-+-+ + +   .'   /   \        G -- H
| |       | |   |G|H      |I|   G H-'     I       H -- I
+-+-+-+-+-+-+   +-+-+-+-+-+-+     (ascii)       } // (graphviz dot)
   Figure 9       Figure 10             Figure 11

|  D|  D E  |F E  |  F  |       |  D|  D E  |F E  |  F  |
                                 A   C     I     B G   H
      Figure 12                       Figure 13

该迷宫的节点具有四个路径(图10中的E)。图11显示了它的图表。图12是等效的一维迷宫。图13显示了相同的迷宫,其中带有死角标签,以便与图11进行比较。

挑战

给定2D迷宫作为输入,编写将2D迷宫转换为具有扭曲点的1D迷宫的函数或程序。在每种情况下,翘曲点可以使用52个字母中的任何一个。

输入保证(如果输入中未满足其中任何一项,则无需处理):

  • 输入迷宫已连接(也就是说,您始终可以从任何位置转到任何其他位置)。
  • 输入迷宫关闭。
  • 输入迷宫为矩形。
  • 所有晶格点都使用+
  • 同一行上晶格点之间的所有墙都使用 |
  • 同一列中晶格点之间的所有墙都使用-
  • 所有空间都是路径的一部分(并且都在迷宫内部)。
  • 路径是所有空间(这将始终是传统的,非扭曲的)
  • 路径正好是一个空间宽。
  • 通过连接晶格上的点来构建迷宫。
  • 迷宫的图中总共不超过52个节点(即死角和决策点)。

输出格式:

  1. 您的输出应该是显示一维迷宫的一行
  2. 您的输出不应包含前导/尾随空格;除了结尾的换行符是可以的。
  3. 第一个字符和之后的所有其他字符是晶格点。
  4. 所有的墙壁都应该在格子点上;以及它们之间的所有翘曲点。
  5. 一维迷宫的图形应与二维迷宫的图形等效。
  6. 您的一维迷宫必须紧凑。所有非晶格点必须是死角(即与墙相邻)或翘曲点。
  7. 只信你的输出应该是经点。每个翘曲点恰好在直线上出现两次。

例:

|  D|  D E|G E F|  F  |  G  | <- (1,2) The single line output
+ + + + + + + + + + + + + + + <- lattice point spacing... (3) 
                                 (4,6) lattice points are all walls or spaces
                                 (5) See Figure 8
                                 (7) D, E, F, G appear twice; no other labels

这是代码高尔夫球。获胜者是最少字节的正确无漏洞提交。

测试中

没有针对这种挑战的测试案例,因为对于任何平凡的迷宫都有大量正确的输出。

但是,我已经在C ++中构建了一个检查器(该检查器通过图形规范化来绘制两个解决方案的图形)。

此外,还有一些示例可帮助说明正确的格式:

例子1

+-+-+-+-+-+-+
| |   |     |
+ + + + +-+-+
|   | |     |
+-+-+ +-+-+ +
|           |
+-+-+-+ +-+ +
|         | |
+ +-+-+-+ + +
| |       | |
+-+-+-+-+-+-+
->
|  D|  D E|G E F|  F  |  G  |

例子2

+-+-+-+-+-+-+
| |   |   | |
+ + + + + + +
|   | | |   |
+-+-+ + +-+-+
|           |
+-+-+-+ +-+ +
|         | |
+ +-+-+-+ + +
| |       | |
+-+-+-+-+-+-+
->
|  D|  D E  |F E  |  F  |

在这里可以找到更多示例


1
我认为一维迷宫的解释根本不是很清楚...也许添加一个更小/更简单的示例会有所帮助。
mbomb007 '01

太酷了。是的,有帮助。
mbomb007'1

尽管您的交互式脚本有所帮助,但这仍然是一个难题。所以我就跳过它。我对此的理解充其量仍是微不足道的。
mbomb007'1

一维迷宫的描述是粗略的。我必须阅读图7才能了解一维迷宫中的竖线字符是无法越过的墙壁。
edc65

1
示例1,其中1
Sparr

Answers:


3

蟒2 的igraph492个 369字节

import igraph,string
def f(s):
 C=s.find('\n')/2;N=' ';g=igraph.Graph(0,[(i,i+j)for i in range(len(s)/(4*C+4)*C)for j in(1,C)if s[(i/C*2+1)*(2*C+2)+i%C*2+2*j+j/C*3]==N]);V=g.vs;g.d=g.degree;O='';V(_d=1)[N]=N;V(_d_gt=2)[N]=list(string.ascii_letters)
 while g.es:
    v=V(_d=1)[0];O+='|'+v[N]
    while g.d(v):w=v.neighbors()[0];g-=(v,w);v=w;O+=N+v[N]if v[N]else''
 print O+'|'

(第五和第六行分别以制表符开头,而不是StackExchange所示的四个空格。)

  • 保存六个字节,重新排列一些算术
  • 使用切片而不是zip节省了七个字节
  • 使用g+=tuple(v.neighbors())代替节省了三个字节g.add_edge(*v.neighbors())
  • 使用g-=g.es[g.incident(v)]代替节省了七个字节g.delete_edges(g.incident(v))
  • 保存的十一字节别名 g.d=g.degree
  • 节省了52个字节(!),从而消除了通过将2度顶点替换为相邻顶点之间的边而使所有走廊收缩的循环。相反,输出循环只会忽略这些顶点。
  • 保存了13个字节,注意到在分配名称时,igraph不在乎所提供的可迭代项是否太长
  • 通过不使用R行数变量来节省四个字节,将其计算移至唯一的使用点
  • 保存了两个字节,将第二级缩进更改为制表符而不是空格
  • 保存六个字节重新排列2*(i%C)i%C*22*(i/C)i/C*2,并(C-1)*jj*C-j
  • 保存了四个字节的命名 N='n'
  • 在仅出现有效字符的假设下,使用<'-'而不是节省了一个字节来确定字符是否为空格==' '
  • 然后意识到我可以命名vertex属性' '而不是'n',并N在源中的两个文字空间中重用,而==N不是<'-'节省另外五个字节

随后是一个略微偏离版本的版本。基本思想是首先在所有迷宫顶点(零索引时具有奇数行和列的点)上绘制图。如果以下字符为空格,则在同一行上存在从一个顶点到下一个顶点的边而不是|。如果下一行中的对应字符是一个空格,那么从顶点到其右下角之间会有一条边,而不是-

构建完此图之后,我们选择任何叶子,并依次跟随相邻的顶点,如果它们不是走廊,则写出它们的名称,并删除使用的边线,直到被卡住。然后我们再拿一片叶子,直到所有边缘消失。

import string
import igraph
def f(s):
  C = s.find('\n')/2 # number of maze vertices in each row
  R = len(s)/(4*C+4) # number of rows
  def strpos(r, c):
    """Index of the vertex at row r, col c in the newline-delimited string s"""
    return (2*r+1)*(2*C+2) + 2*c + 1
  def vertpos(i):
    """Index of the i-th vertex in s"""
    return strpos(i/C, i%C)
  g = igraph.Graph(edges=[(i, i+(C if j else 1))
                          for i in range(R*C)
                          for j in (0, 1)
                          if s[vertpos(i)+(2*C+2 if j else 1)] == ' '])
  V = g.vs # the graph's vertex sequence
  O = ''
  V(_degree=1)['n'] = ' ' # All leaves are named space
  W = V(_degree_gt=2) # All warp points...
  W['n'] = list(string.ascii_letters[:len(W)]) # ...are named successive letters
  while g.es: # while any edges remain...
    v = V(_degree=1)[0] # find a leaf
    O += '|'+v['n'] # start a new 'block'
    while v.degree():
      w = v.neighbors()[0] # pick a neighbor
      g -= (v, w) # delete that edge
      v = w
      if v['n']: # If it's a dead end or warp point...
        O += ' '+v['n'] # ...write out the new neighbor
  print O+'|'

您可以看到五个示例迷宫的结果。(不幸的是,igraph在“在线试用”上不可用;这些结果是从SageMathCloud导出的。)


4

哈斯克尔- 481个 405 387字节

import Data.List
s&t=elemIndices s t
l=last
c!(x:y:z)=l$(y:c)!(x:z):do{[x:p,q]<-mapM([id,reverse]<*>)[[x],[y]];x&[l q];[[]!((q++p):c++z)]}
c![x]=x:[]!c
c!z=z
main=interact(\m->let{g=' '&m;
u=(\\[k|k<-g,length(v>>=(k&))==2])<$>[]!v;
v=[[x,y]|x<-g,y<-g,elem(y-x-1)[0,head$'\n'&m]];
}in '|':(u>>=(++"|").init.(>>=(:" ").toEnum.((+)<*>(+65).(*32).(`div`26)).l.(-1:).(&(nub$u>>=init.tail)))))

这将创建一个迷宫中的空格列表,该字符串按字符串中的索引编号,并使用它来查找所有相邻空格对。然后,它基于匹配的第一个/最后一个元素将线对缝合在一起,形成更长的点序列,并移除走廊,因此每个序列都是一维迷宫中的一个房间。然后,通过将至少一个房间内部的点(翘曲点)替换为相应的字母,将其余部分替换为空格,将序列转换为字符串。

从STDIN读取2D迷宫,并将1D迷宫打印到STDOUT。

编辑:由62字节减少通过更换重新排列一堆东西和修改的算法的比特,并且另一14 chrtoEnum由Laikoni的建议。

编辑2:通过简化中的逻辑,又节省了13个字节,(!)使用列表模式match sugar 节省了3个字节,并通过使用>>=concat 节省了2个字节u


我认为您在模式防护之前不需要换行符和空格,例如也o(x:p)q|x==last q=[q++p]|1>0=[]应该工作。
Laikoni

toEnum应该代替chr,然后import Data.Char将其删除。
Laikoni

最后,当挑战要求一个程序或功能时,您可以main=interact(\m->...)用just 代替f m=...。如果这对您来说意味着什么,那么这足以击败python的答案。
Laikoni
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.