ダイナマイト付きマウス


23

あなたはマウスです。あなたのマウスの友人はすべて捕らえられており、無意識であり、入口/出口が1つしかない迷路に閉じ込められています。あなたはたまたま迷路の完全な地図を持っているので、あなたは急いでそれらをすべて安全に運ぶために解決策を計画することができます。ただし、迷路はセキュリティシステムで保護されており、しきい値に1000達するとアラートをトリガーし、あなたを捕らえて救助ミッションに失敗させます。

迷路の以前の調査から、踏み込んだ各正方形(つまり、水平方向または垂直方向の移動- マウスは斜めに移動できない)が1セキュリティシステムのカウンターに追加されます。ただし、重量(ダイナマイトのブロックまたは無意識のマウスフレンド)を運んでいる2場合は、追加の圧力を検出するため、代わりに追加されます。入り口/出口の広場にはこのセキュリティシステムがないため、カウンターに追加されません。

入り口に持ってきたダイナマイトは無制限にあるので、壁爆破して友達を解放できます。しかし、あなたはそうすることに注意する必要があります。なぜなら、各爆発は50脳震盪の圧​​力からカウンターに加わるからです。さらに、一度に持ち運ぶことができるのは、1匹のマウスまたは1ブロックのダイナマイトです。ダイナマイトの各ブロックは1つの壁スペースのみを爆発させることができるため、複数の壁が連続して存在する場合は、手に手を入れて入り口まで戻ってさらに手に入れる必要があります。

実例

迷路が次のようになっているとします:

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

cカウンターに使用します。私たちは、から始まりE、N-トランスダイナマイトを運んでいる間1つの平方左に移動しますc=2。壁を爆発させるためにダイナマイトを爆発させc=52ます。手ぶらで2つの正方形を左に移動してを取得c=54し、マウスの正方形の上に立っています。私たちは友人を選び、3つの正方形をExitに戻しますが、最後の正方形はセンサーを持たないためカウントされません。そのため、背中に何かがある2つの正方形だけです。つまり、最後のマウスで出口に到達すると、c=58は未満で1000あり、ミッションは成功します。

チャレンジ

入力された迷路が与えられると、マウスヒーローであるあなたが、上記で概説した制約内ですべての閉じ込められたマウスを正常に救出できるかどうか、またはミッションが失敗であるかどうかを出力します。

入力

  • 受け入れ可能な形式(複数行の文字列、文字列の配列など)の2D迷路。
  • この挑戦のために、私は#内壁と外壁の両方M、ネズミの友達、そしてE入り口に使用します。
  • 入口が内壁にすぐ隣接することはありません(常に少なくとも1つのスペースが自由に移動できます)。
  • 一貫性がある限り、任意の印刷可能なASCII文字に置き換えることができます。これはない、あなたが使用することを選択した場合に限り、あなたが一貫性(例えばを維持するように、外壁対内壁のための2つの異なるシンボルを使用できるようにする@代わりに、内壁のために、そして休暇#外装のため、すべての内壁がなければなりません@し、すべての外壁#)。
  • 迷路は常に完全に壁に囲まれていますが、必ずしも長方形ではありません。必要に応じて、長方形の入力を作成するために迷路にスペースが埋め込まれていると想定できます(オプション)。
  • 迷路には、ダイナマイトなしでは到達できないセクションがある場合があります。
  • 迷路の外壁をダイナマイトすることはできません。

出力

truthy / 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
マウスは激怒しました(軽度のネタバレ)
ルイスメンドー

3
@AlexA。インターネットの見知らぬ人からそれを学ばなければならなかったことを残念に思う。;-)
AdmBorkBork

2
ゴルフをすることはもちろん、ほとんどの人はこれを通常のコードで解決するのに苦労すると思います。残念ながら、現時点で作業する時間がないのは大きな挑戦です。
モシェ・カッツ

2
外壁に異なる炭を使用することは受け入れられますか?
テンシバイ

2
@Moshe Katz、時間がないこと確認してください。Mäuseを保存したくないだけです。
msh210

回答:


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/ます。マッチした部分文字列は、ヒーローと彼が運んでいるものが最初のキャラクターから削除され、2番目のキャラクターに追加されるように修正する必要がありxorます。xor値は、ヒーロービット(01000000)、ダイナマイトビット(00000001)、およびキャリーマウスビット(00000100)で構成されます。一緒or01000101これはASCII Eです。そのため、ヒーローを動かすと:

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

主人公が壁の前に立って、ダイナマイト(qsまたはy)を運んでいる場合、壁を爆破できます。主人公はダイナマイト(xorwith 00000001)を失い、壁#は通路0(xor with 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一定の圧力で、状態上のARRAYの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何も起こりません。

これらすべてを合わせて、基本的にダイクストラのアルゴリズムを使用して最短のソリューションを見つけます。ただし、潜在的な問題があります。現在のコードは、最初の発見よりも低い圧力で再発見された場合、状態を追加しません。これをトリガーするボードを構築することはできませんでしたが、不可能であることを証明することもできませんでした。


3
おっと、興味深い。これは、私が予想していた答えよりもかなり短いものです。少し説明を追加できますか?私は実際にPerlを理解していません。
-AdmBorkBork

3
@TimmyD OK、Perl以外のプログラマーでさえ、少なくともそれがどのように機能するかの印象を得るために十分な詳細が追加された説明
Ton Hospel

1
非常に印象的!
エミグナ

素晴らしいゴルフ、それは本当に印象的です...私が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
イッツィー

@TuukkaXそのことを思い出してくれてありがとう、バイトカウントはすでにスペースのないバージョンのものだった。
DanTheMan

コードをラップしevalて置き換えることでバイトを節約できるかどうかを確認@します$1(これが機能するかどうかは$1
わかり

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.