带有炸药的鼠标


23

你是老鼠 您的鼠标朋友全部被捕获,失去知觉,被困在只有一个入口/出口的迷宫中。您恰好有一张完美的迷宫图,因此您可以制定一种解决方案,以赶紧进入并安全携带。但是,迷宫装有安全系统,1000一旦达到阈值,就会触发警报,导致您被抓获并无法执行救援任务。

根据您先前对迷宫的调查,您走过的每个正方形(即,每个水平或垂直移动- 鼠标都不能对角移动)会增加1安全系统的计数器。但是,如果您要负重(一块炸药或一个昏迷的老鼠朋友),它会增加重量,2因为它会检测到额外的压力。入口/出口广场没有此安全系统,因此也不会添加到柜台上。

您带到入口的炸药数量无限制,因此您只需炸毁所有墙壁即可释放朋友。但是您必须谨慎,因为每次爆炸都会增加50震荡压力。此外,您一次只能携带一件东西,无论是一只鼠标还是一块炸药。由于炸药块只能爆炸一个墙面空间,因此,如果连续有多堵墙面,则需要空手返回入口以获取更多空间。

通例

假设我们的迷宫如下所示:

######
#M# E#
######

我将c用于柜台。我们从E入口开始,一边携带炸药一边向左移动一格c=2。我们引爆炸药炸毁墙壁c=52。我们空手向左移动两个正方形以得到c=54,现在我们站在鼠标的正方形上。我们接起朋友,将3个正方形移回Exit,但是最后一个正方形不计数,因为它没有任何传感器,所以只有2个正方形,背面有东西。这意味着,当我们使用最后一个鼠标c=58(小于)到达出口时1000,因此任务成功。

挑战

在输入迷宫的情况下,无论您是鼠标英雄,还是可以在上述限制范围内成功营救所有被困鼠标,或者任务是否失败,都可以输出。

输入项

  • 任何可接受格式(多行字符串,字符串数组等)的2D迷宫。
  • 对于这一挑战,我将同时使用#内墙和外墙,M鼠标朋友和E入口。
  • 入口永远不会紧邻内墙(始终至少有一个空间可以自由移动)。
  • 您可以替换任何希望的可打印ASCII字符,只要它们是一致的即可。只要您保持一致(例如,如果您选择改用内墙,而选择外墙,则每个内墙都必须是,每个外墙必须),这确实允许您对内墙和外墙使用两个不同的符号)。@#@#
  • 迷宫将始终被墙壁完全包围,但不一定是矩形。如果需要,您可以假定迷宫用空格填充以进行矩形输入(可选)。
  • 迷宫中的部分可能没有炸药就无法到达。
  • 您无法炸开迷宫的外墙。

输出量

truthy / falsey值。对于“是,鼠标可以拯救其他所有鼠标”,为True;对于“否,警报系统将被触发”,为Falsey。

规则

  • 完整的程序或功能都是可以接受的。
  • 禁止出现标准漏洞
  • 这是因此所有常见的高​​尔夫规则都适用,并且最短的代码(以字节为单位)获胜。

例子

真实示例,用空白行分隔。

#####
#M E#
#####

######
#M# E#
######

########
#E  # M#
#   #  #
#   #  #
#      #
########

#############################
#    ##      #       #      #
#  M ## M    #       #      #
#    ##      #   M   #    E #
#M   ##      #       #      #
#############################

###############
#MMMMMMMMMMMMM#
#MMMMMMMMMMMMM#
#MMMMMMMMMMMMM#
#MMMMMMMMMM MM#
#MMMMMMMMMMMME#
###############

虚假示例,用空行分隔

#############################
#M   ##      ##      ##     #
#  M ## M    ##      ##     #
#    ##      ##  M   ##   E #
#M   ##      ##      ##     #
#############################
#############################
                     ########
                     ########
                     #   #  #
                     # M # M#
                     ########

              #####
              # M #
              #####
              #####
              #####
              #####
###################
# # # ##   ## # # #
#M#M#M## E ##M#M#M#
# # # ##   ## # # #
###################
#######
######
#####
####
# M#
####

###############
#MMMMMMMMMMMMM#
#MMMMMMMMMMMMM#
#MMMMMMMMMMMMM#
#MMMMMMMMMMMMM#
#MMMMMMMMMMMME#
###############

3
老鼠生气了(轻度破坏者)
Luis Mendo

3
@AlexA。抱歉,您必须在Internet上向陌生人学习。;-)
AdmBorkBork

2
我怀疑大多数人都很难用常规代码解决此问题,更不用说打高尔夫球了。不幸的是,我目前没有时间去做,这是一个巨大的挑战。
Moshe Katz

2
外壁使用其他字符(因为它们不可分解)是否可以接受?
Tensibai '16

2
@Moshe Katz确定您没有时间。您只是不想保存Mäuse。
msh210 '16

Answers:


19

Perl中,216个 215字节

包括+2 -0p

在STDIN上输入。使用%外墙,#内部墙壁,0空的空间,8用于小鼠和r为起始位置。整个木板必须填充,以形成一个矩形。您可以将示例转换并运行为:

cat dynamite.txt | perl -p0e 's/.+/$a^=$&/egr;s//sprintf"%-*s",length$a,$&/eg;1while/\n/,s/^ *\K#|#(?= *$)|^ *.{@{-}}\K#|\A[^\n]*\K#|#(?=[^\n]*\n\z)|#(?=.{@{-}} *$)/%/sm;y/ EM/0x2/' | dynamite.pl

dynamite.pl

#!/usr/bin/perl -0p
sub f{@a{@_}||=push@{$%+($&?$1?50:$&=~8?0:$&&"5"?2:1:0)},@_}f$_;for(@{$%}){f y/xr|/ytx/r;{f s/\pL\d/$&^(E&$&)x2/er}{f s/(q|s|y)#/$&^"\x01\x13"/er}my$o;{$\|=/x/>/2/;$o.="
"while s/.$/$o.=$&,""/meg}f$o}$%++>999|$\||redo}{

将换码\xhh符替换为要求的分数。

该程序实际上无法处理复杂的情况。特别是它不能处理任何故障情况。这是因为有太多不同的方法可以炸毁内壁或捡起鼠标,因此搜索变得太宽泛并且占用了太多内存,即使它至少足够聪明,也无法多次处理相同状态。为了可以100忍受一些运行时和内存使用,必须将压力限制降低到大约。

说明

我使用字符的位模式来表示字段的状态:

contains victim: 0000 0010
has hero:        0100 0000
carry dynamite   0000 0001
carry mouse      0000 0100
home             0000 1000
walkable         0001 0000 (not really needed but results in shorter regexes)
make printable   0010 0000
wall             0010 xxxx (bit patterns not very important,
permawall        0010 xxxx  just avoid letters and digits)

例如,01000000携带炸药(00000001)的英雄()必须在他可以走路(00010000)的地方,并且我们希望所有值都是可打印的ASCII(00100000)。以or所有这些位掩码的位为单位,得出的01110001是的ASCII码q。总共变成:

p: hero                     r hero on victim
q: hero carrying dynamite   s hero carrying dynamite on victim
t: hero carrying mouse      v hero carrying mouse on victim

x : hero at home
y : hero at home carrying dynamite
| : hero at home carrying mouse

0: empty  without hero
8: home   without hero
2: victim without hero

%: permanent wall
#: normal wall

该程序仅考虑英雄向右移动(稍后说明的旋转将照顾其他方向)。精心选择了位掩码,以使英雄始终以字母表示,并且他可以通过数字移动到一个位置(除了在家中携带受害者的英雄,但这是故意的,因此英雄唯一的举动就是掉落受害者)。因此,可以前进的英雄与匹配/\pL\d/。必须修改匹配的子字符串,以便将英雄及其携带的东西从第一个字符中删除,然后添加到第二个字符中,这可以通过xor对两个字符具有相同值的按位完成。xor值由英雄位(01000000),炸药位(00000001)和进位鼠标位(00000100)组成。他们一起or01000101这是ASCII E。因此,移动英雄变为:

s/\pL\d/$&^(E&$&)x2/e

如果他是站在它的右前方,并携带炸药(英雄可以炸毁墙壁qsy)。英雄将失去炸药(xor带有00000001),并且墙壁#将变为段落0(带有或00010011),因此

s/(q|s|y)#/$&^"\x01\x13"/e

要处理其他方向,请旋转整个电路板(旋转的电路板结尾为$o):

my$o;$o.="\n"while s/.$/$o.=$&,""/meg

除了移动英雄,他还可以选择其他多种选择:

When at home, pick up dynamite:                   x -> y
When on victim not carrying anything pick him up: r -> t
When at home carrying a victim, drop him off:     | -> x

这是通过

y/xr|/ytx/

如果英雄不带任何东西(x),并且没有更多的受害者可以救助(否2),则完成董事会。可以使用以下方法方便地进行测试

/x/>/2/

电路板解决后,我想记住该状态,最后打印出来。为此,我携带“ issolve”标志,$\并在程序结束时将其打印而不打印$_,因此

$\|=/x/>/2/  ...   }{

处于压力0时待处理的状态保持不变@0,处于压力1时处于待压状态@1等。当前压力保持在$%。使用$n或类似的东西会短一些,但代码不工作,如果该变量未初始化的东西,因为会自动激活,否则改变$n在一定的压力到一个数组reference.Looping在美国使用做for,而不是map因为使用a for可以在数组仍在循环时对其进行扩展,并拾取新元素。之所以需要这样做,是因为英雄的旋转和单场选择是在0次时间内发生的,并最终出现在相同的压力阵列中。因此,给定压力的循环是通过

for(@{$%}){...}

重复此过程,直到压力达到1000或找到解决方案为止:

$%++>999|$\||redo

剩下的就是将新发现的状态添加到它们各自的压力数组中。这将通过子例程来完成f。我们只想添加一个尚未看到的状态。以前见过的状态保持不变%a

sub f{@a{@_}||=push@$n, @_}

$n代表一个国家的新压力。我将从正则表达式变量仍然是由于英雄的动作导致此调用的结果而得出的状态得出:

if $1 is set it was from s/(q|s|y)#/$&^"\x01\x13"/e which blows a wall -> 50
else if $& is set it was from s/\pL\d/$&^(E&$&)x2/e, so the hero moves.
    if $& contains 8 the hero went home -> 0
    else if the hero has carry bits (5) -> 2
    else                                   1
else ($& was not set) it was from y/xr|/ytx/r -> 0

这导致以下公式$n

$%+($&?$1?50:$&=~8?0:$&&"5"?2:1:0)

所有替换都获得r修饰符,因此它们将返回更改后的状态,而将当前状态保留为$_单独状态。f然后在此更改后的状态上被调用,因此您获得如下代码

f y/xr|/ytx/r;

因为$n需要计算正则表达式变量,所以它们必须默认为未设置,以防替换没有任何改变(因为不满足触发条件)。我也不能从上一个循环中拾取任何正则表达式变量。因此,替换项包装在{}块中以保存和恢复正则表达式状态。那就是你得到这样的语句的方式

{f s/\pL\d/$&^(E&$&)x2/er}

特别是,旋转是如此包裹,因此它调用时f无需使用正则表达式变量,并且压力贡献为0。

唯一要做的就是@0在开始时以初始状态填充

f$_

这在主循环中,因此它也尝试添加$_到以后的压力数组中,但是由于初始状态已经存在,因此%a什么也不会发生。

所有这些基本上都使用Dijkstra算法找到了最短的解决方案。但是存在潜在的问题。如果以比第一次发现低的压力重新发现当前代码,则不会添加状态。我无法构建一个可以触发这种情况的董事会,但也无法证明这是不可能的。


3
噢,有趣。这比我期望的答案要短得多。您能补充一点解释吗?我不太喜欢Perl。
AdmBorkBork '16

3
@TimmyD好吧,解释中添加了足够的细节,这样即使是非perl程序员也应该至少对它的工作方式有印象
Ton Hospel

1
非常令人印象深刻!
Emigna

很棒的高尔夫,真是令人印象深刻...当我认为与Perl打高尔夫球并不那么糟糕时,我来看看您的高尔夫运动,那感觉很快就消失了!
达达

13

JavaScript中,863个 834 785 781字节

由于ETHproductions节省了29个字节感谢Jordan
节省了53个字节

L=[]
f=(S,r="",R="",p=0,s=S.replace(RegExp(r),R),l=`((.|\n){${s.split`
`[0].length}})`,q=p+1,o=p+2,n=p+50)=>s in L|p>999?1e3:!/M/.test(s,L[s]=0)&/E/.test(s)?p:Math.min(...[[/ E/,"me",q],[/ E/,"de",o],[/ME/,"ce",q],[/E /,"em",q],[/E /,"ed",o],[/EM/,"ec",q],[`E${l} `,"e$1m",q],[`E${l} `,"e$1d",o],[`E${l}M`,"e$1c",q],[` ${l}E`,"m$1e",q],[` ${l}E`,"d$1e",o],[`M${l}E`,"c$1e",q],[/ m/,"m ",q],[/m /," m",q],[`m${l} `," $1m",q],[` ${l}m`,"m$1 ",q],[/ (d|c)/,"$1 ",o],[/(d|c) /," $1",o],[`(d|c)${l} `," $2$1",o],[` ${l}(d|c)`,"$3$1 ",o],[/d#/,"m ",n],[/#d/," m",n],[`#${l}d`," $1m",n],[`d${l}#`,"m$1 ",n],[/mM/," c",q],[/Mm/,"c ",q],[`M${l}m`,"c$1 ",q],[`m${l}M`," $1c",q],[/[mc]e/," E",p],[/e[mc]/,"E ",p],[`e${l}[mc]`,"E$1 ",p],[`[mc]${l}e`," $1E",p]].map(a=>f(s,...a)))
F=s=>f(s)<1e3

将输入作为多行字符串。

这定义了一个匿名函数,该函数使用递归函数f来确定在检索所有鼠标之前是否触发警报。f返回1000如果压力为1000以上(以避免无限递归),则返回,如果没有更多的小鼠援救和在出口处的鼠标和返回的从当前状态,否则所有可能移动的最小压力的压力。它使用数组L来跟踪已访问的位置(L[pos]==0如果已访问),如果未访问,则未定义。这可能不是必需的,但是它可以防止鼠标进行无用的移动并至少避免抛出递归错误。(这确实意味着L如果要多次测试,则应该重新定义)

这使用问题中的格式,除了要求您为外墙使用其他字符之外。(除以外# MEmecd

更具可读性的版本:

stateList = []
f=(s,regex="",replacement="",pressure=0,state=s.replace(regexp(regex),replacement),line=`((.|\n){${state.split("\n")[0].length}})`)=>{
    if (state in stateList || pressure > 999) return 1e3
    if (!/M/.test(state) && /E/.test(state)) return pressure

    stateList[state] = 0

    return [
        [/ E/,"me",pressure+1],
        [/ E/,"de",pressure+2],
        [/ME/,"ce",pressure+1],
        [/E /,"em",pressure+1],
        [/E /,"ed",pressure+2],
        [/EM/,"ec",pressure+1],
        [`E${line} `,"e$1m",pressure+1],
        [`E${line} `,"e$1d",pressure+2],
        [`E${line}M`,"e$1c",pressure+1],
        [` ${line}E`,"m$1e",pressure+1],
        [` ${line}E`,"d$1e",pressure+2],
        [`M${line}E`,"c$1e",pressure+1],
        [/ m/,"m ",pressure+1],
        [/m /," m",pressure+1],
        [`m${line} `," $1m",pressure+1],
        [` ${line}m`,"m$1 ",pressure+1],
        [/ ([dc])/,"$1 ",pressure+2],
        [/([dc]) /," $1",pressure+2],
        [`([dc])${line} `," $2$1",pressure+2],
        [` ${line}([dc])`,"$3$1 ",pressure+2],
        [/d#/,"m ",pressure+50],
        [/#d/," m",pressure+50],
        [`#${line}d`," $1m",pressure+50],
        [`d${line}#`,"m$1 ",pressure+50],
        [/mM/," c",pressure+1],
        [/Mm/,"c ",pressure+1],
        [`M${line}m`,"c$1 ",pressure+1],
        [`m${line}M`," $1c",pressure+1],
        [/[mc]e/," E",pressure],
        [/e[mc]/,"E ",pressure],
        [`e${line}[mc]`,"E$1 ",pressure],
        [`[mc]${line}e`," $1E",pressure]
    ].map(a=>f(state,...a)).reduce((a,b)=>a-b<0?a:b) //reduce used for support in more browsers.
}
s=>f(s)>1e3

处的无用空格s in L|p > 999
Yytsi

@TuukkaX感谢您提醒我这一点,字节数用于没有空格的版本。
DanTheMan

看看是否可以通过将代码换行eval并替换为@以节省字节$1(不确定这是否行得通,但您会写$1很多东西)
Cyoce

我认为您可以通过ff=(...)=>s in L|p>999?1e3:!/M/.test(s,L[s]=0)&/E/.test(s)?p:Math.min(...
单线

@Cyoce我使用$118次,并且.replace("@","$1")是18个字节。我没有办法实现。
DanTheMan's
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.