다이너마이트가있는 마우스


23

당신은 마우스입니다. 당신의 마우스 친구들은 모두 잡히고 의식이없고 입구 / 출구가 하나 밖에없는 미로에 갇혀 있습니다. 당신은 미로의 완벽한지도를 가지게되므로, 당신은 돌진하고 그들을 모두 안전하게 운반 할 수있는 솔루션을 계획 할 수 있습니다. 그러나 미로는 임계 값에 1000도달 하면 경고를 트리거하는 보안 시스템으로 보호되어 구조 임무에 실패하게됩니다.

이전 미로 조사에서, 당신이 밟는 각 사각형 (즉, 각 수평 또는 수직 움직임- 마우스는 대각선으로 움직일 수 없음 ) 1이 보안 시스템의 카운터에 추가 됩니다. 그러나 몸무게 (다이너마이트 블록 또는 무의식 마우스 친구)를 운반하는 2경우 추가 압력을 감지하므로 무게가 증가합니다. 출입구 광장에는이 보안 시스템이 없으므로 카운터에 추가되지 않습니다.

입구에 가져온 다이너마이트는 무제한으로 제공되므로 모두 날려 친구를 자유롭게 해줄 수 있습니다. 그러나 폭발 50할 때 마다 폭발적인 압력으로 인해 카운터에 추가되므로주의를 기울여야합니다. 또한 한 번에 하나의 마우스 또는 하나의 다이너마이트 블록 만 운반 할 수 있습니다. 다이너마이트의 각 블록은 하나의 벽 공간을 폭파 할 수 있기 때문에, 연속으로 여러 개의 벽이있는 경우 더 빈틈없이 입구로 돌아가 여행을해야합니다.

실습 예제

우리의 미로가 다음과 같다고 가정 해보십시오.

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

c카운터에 사용하겠습니다 . 우리는 Entrance 에서 시작하여 다이너마이트를 운반하면서 왼쪽으로 한 칸 이동 c=2합니다. 다이나마이트를 폭발시켜 벽을 폭발시켰다 c=52. 우리는 빈 손으로 왼쪽으로 두 정사각형을 이동하여를 얻습니다 c=54. 이제 마우스 사각형에 서 있습니다. 우리는 친구를 데리러 3 정사각형을 다시 Exit으로 옮기지 만 마지막 정사각형에는 센서가 없기 때문에 계산되지 않으므로 뒤쪽에 무언가가있는 2 정사각형입니다. 즉, 최종 마우스로 출구에 도달하면 c=58이보다 작 1000으므로 임무가 성공합니다.

도전

입력 미로가 주어지면 마우스 영웅이 위에서 설명한 제약 조건 내에서 갇힌 모든 마우스를 성공적으로 구출 할 수 있는지 또는 임무가 실패했는지 여부를 출력합니다.

입력

  • 허용되는 형식 (여러 줄 문자열, 문자열 배열 등) 의 2D 미로 .
  • 이 도전을 위해, 나는 #내벽과 외벽, M마우스 친구들과 E입구에 사용할 것입니다.
  • 입구는 내부 벽에 바로 인접하지 않습니다 (항상 자유롭게 이동할 수있는 공간이 하나 이상 있어야 함).
  • 원하는 인쇄 가능한 ASCII 문자 를 일관된 한 대체 할 수 있습니다 . 이것은 않습니다 그래서 당신이 예를 들어, 일관성 (사용하도록 선택하는 경우 유지, 당신은 외벽 대 내부 벽에 두 개의 서로 다른 기호를 사용할 수 있도록 @하는 대신 내부 벽을 위해, 그리고 휴가 #외부를 들어, 모든 내부 벽은해야 @하고 모든 외부 벽 #).
  • 미로는 항상 벽에 완전히 묶여 있지만 반드시 직사각형 일 필요는 없습니다. 원하는 경우 미로에 공백이 채워져 사각형 입력이 가능하다고 가정 할 수 있습니다 (선택 사항).
  • 미로에는 다이너마이트가 없으면 도달 할 수없는 섹션이있을 수 있습니다.
  • 미로의 외벽을 다이너마이트 할 수 없습니다.

산출

truthy / falsey의 값입니다. "예, 마우스는 다른 모든 마우스를 구할 수 있습니다"또는 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. 인터넷의 낯선 사람에게서 배워야해서 죄송합니다. ;-)
AdmBorkBork

2
나는 대부분의 사람들이 골프는 물론 일반 코드로 이것을 해결하는 데 어려움을 겪을 것이라고 생각합니다. 불행히도 현재 작업 할 시간이 없다는 것은 큰 도전입니다.
Moshe Katz

2
외벽에 대해 다른 문자를 갖는 것이 허용됩니까?
Tensibai

2
@Moshe 카츠는 , 확실히 당신은 시간이 없어. 당신은 단지 Mäuse를 저장하고 싶지 않습니다.
msh210

답변:


19

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

영웅은 벽 바로 앞에 서서 다이너마이트 ( q, s또는 y)를 들고 있다면 벽을 날려 버릴 수 있습니다 . 영웅은 자신의 다이너 마이트를 잃게됩니다 ( xor00000001)과 벽은 #통로로 변경됩니다 0(와 XOR 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/

보드가 해결되면이 상태를 기억하고 마지막에 인쇄하고 싶습니다. 나는에서 "해결"플래그를 가지고 있음을 들어 $\및 인쇄하지 않고 프로그램의 마지막에 그 인쇄 $_때문에,

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

압력 0에서 처리 될 상태 @0는 압력 1 @1등에서 유지됩니다 $%. 현재 압력은 유지됩니다 . 사용 $n또는 변수가 무엇인가에 초기화되어 있지 않은 경우 autovivification 그렇지 않으면 변경됩니다 때문에 같은 짧은 될하지만 코드가 작동하지 않는 $n일정한 압력에서 상태에 대한 배열 reference.Looping로하는 것은 사용하여 수행됩니다 for아니라 map인해를 를 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

3
@TimmyD Ok, 펄이 아닌 프로그래머라도 작동 방식에 대해 최소한의 인상을 얻을 수 있도록 충분한 설명이 추가 된 설명
Ton Hospel

1
매우 인상적!
Emigna

굉장한 골프, 그것은 정말 인상적입니다 ... 내가 Perl과의 골프에서 나쁘지 않다고 생각할 때, 나는 당신의 골프를 살펴보고 그 느낌은 꽤 빨리 사라집니다!
Dada

13

자바 스크립트, 863 834 785 781 바이트

ETH 프로덕션 덕분에 29 바이트
절약 요르단 덕분에 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

f한 줄짜리 라이너 를 만들어 무리를 구할 수 있다고 생각합니다 .f=(...)=>s in L|p>999?1e3:!/M/.test(s,L[s]=0)&/E/.test(s)?p:Math.min(...
ETHproductions

@Cyoce 나는 $118 번 사용 하고 .replace("@","$1")18 바이트입니다. 나는 그것을 뽑을 방법을 보지 못했다.
DanTheMan
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.