渇きで死ぬのを見たい


12

あなたは2つの町の間で砂漠を横断する旅行者です。停止せずに通り抜けるのに十分な水を運ぶことはできません。これは、古典的なパズルのバリエーションです。

ルール

砂漠は次のようになります。ほとんどが空のWxHグリッドです。マークされたスペースSは開始点、E終了点であり、数字Nでマークされた正方形にはN単位の水が入ります。.ゼロホールド水でマークされた正方形。

.....................................
........S............................
.....................................
.........7...........................
.....................................
.......................3.............
.....5...............................
................................2....
.....................................
.....................................
.....................................
...............................E.....
.....................................
....................7................
.....................................
.....................................

5単位の水でSから開始します。

最大5ユニットの水を運ぶことができます。

各ターンあなた

  1. 1つの正方形を上、下、左、または右に移動し、
  2. あなたが運んでいる水を1単位消費する、
  3. いくつかの単位の水を拾ったり、落としたりします

ターンは次のように表記されます:(direction)(+|-)(units of water)+あなたは水を拾うこと、-あなたがそれを落とすことを示します。

例は次のとおりです。

D+0        Move Down
R+0        Move Right
D+2        Move Down, pick up two units of water.
U-1        Move Up, drop one unit of water.

上記の例でSから始まるこれらの移動を実行すると、砂漠はその後このようになります。

.....................................
........S............................
.........1...........................
.........5...........................
.....................................
.......................3.............
.....5...............................
................................2....
.....................................
.....................................
.....................................
...............................E.....
.....................................
....................7................
.....................................
.....................................

あなたはすでにあなたの広場にあるよりも多くの水を拾うことはできません。水を拾うとき、タイルのカウントからそのユニット数を差し引きます。

最大5ユニットを保持するためにのみ水を拾うことができます。

Sが無限ユニットを保持する場合を除き、タイルは9ユニットを超えることはできません。

あなたが現在持っているだけの水を落とすことができます。

地面の水は、再び拾うまで変わりません。

Sに戻ると、水を使い果たすことなく、あらゆる量の水を拾うことができます。

Eに到達したら、勝ちます。Eで最後のユニットの水を消費すると、あなたはまだ勝ちます。

あなたのターンの後、あなたの水がゼロでEにいない場合あなたは死ぬ

入出力

プログラムはSTDIN、上記の形式のASCIIアートとして、任意のサイズの開始マップを受け取ります。あなたはそれが長方形であると仮定することができます。つまり、すべての行が同じ長さで、正確に1 Sつと1つのE正方形があり、すべての行がで終了し\n、STDIN全体がこの正規表現に準拠します。/^[SE1-9\.\n]+$/

プログラムは次の出力をSTDOUTに書き込みます。

  1. 動きのリスト、
  2. マップの最終状態。

移動のリストは、便利な形式で出力できます。

マップの最終状態は入力と同じ形式で印刷されます。ただし#、タイルが水を含まず、SまたはEでない場合、訪問したすべてのタイルをでマークして砂漠を通過したルートを追加で表示します。それはあります.)。

入力

.....S.
.......
.......
E......
....8..

入賞

D+0
D+0
D+0
D+0
L+5
L+0
L+0
L+0
L+0
U+0
.....S.
.....#.
.....#.
E....#.
####3#.

自明性

コードを投稿するときは、サンプルマップ入力を投稿します。コードは、次の非自明な条件を満たすソリューションを見つけます。

  • SとEは、少なくとも10回離れています。
  • 最初にN単位の水を含むすべての正方形は、すべての正方形が含まれるN幅の境界線で囲まれている必要があります.(水なし、SまたはEなし)

........2.
..........
..........
S.1..2....
..........
..........
........1.
..3.......
.........E

タイルの水の量を増やすと、上記は簡単になります。

必要条件

おそらく、あなたのプログラムは、もしあれば解決策を見つける前に、いくつかの失敗した試みに遭遇するでしょう。

  1. 最終的に、プログラムは解決可能な入力を解決する必要があります。
  2. 私はあなたが死ぬのを見たい -あなたのプログラムは、解決策を見つけるために失敗した試みごとに、死へのルートの動きと最終マップを出力します。
  3. 成功するソリューションに遭遇した場合は、そのための完全な出力を印刷して終了します。
  4. 解決策が見つかるまで実行しますが、同じ解決策を2回試行しないでください。すべての死亡は別個のルートによるものでなければなりません。
  5. これをテスト入力として使用します。

(何らかの中間点でウォーターキャッシュをドロップするには、少なくとも1回の移動が必要です)。

 S........
 .........
 .........
 ........E

解決する非自明なデモンストレーション入力とともに投稿された最短コード優先されます。


これを明確にして、プログラムが解決可能なマップを解決できる必要があるかどうか、または1つのマップだけで機能する必要があるかどうかを指定する必要があります。前者は間違いなく奨励します。1つのマップの場合、ソリューションを計算するよりもハードコードする方が簡単です。

明確にするために編集されました。はい、ゼロ以上の水がある場合は勝ちます。そして、あなたのプログラムはすべての解決可能な入力を解決しなければなりません。
spraff

A *などのアルゴリズムを使用し、5つの水のないタイルに足を踏み入れた場合、連続して移動し、最初に戻る各タイルに5単位のパスをドロップするのはどうしてですか?
ブルー

なし。どうぞ。
spraff

「Sからすべての水を運ぶ」戦略はうまくいくはずですが、それはひどく退屈です。S。、。、。、。、。e .... Eを考えてみましょう。ここで、とeは本当にドットです。コンマは道に沿って隠し場所を保持する場所であり、「e」はEを実行するために5つの水が必要な場所です。最初のコンマに1つの水を移動する4つのステップ(E + 0 E-1 W + 0 W + 4)。1つの水を2番目のコンマに移動する16ステップ。52から3番目、160から4番目、484でeに1水を落とし、S。1926ステップに戻ります。パスの2つのステップごとに、ソリューションの長さが効果的に3倍になります。
スパー

回答:


12

Perl、299 + 1 = 300254 + 1 = 255バイト

人々が私のアルゴリズムを見ると、これはほぼ確実にゴルフ言語の1つに打ち負かされるでしょう:-)

-n(1バイトのペナルティ)で実行します。

前のバージョンは、マップに余分な水を残し、マップの最終バージョンに表示しなかったため、仕様に完全には準拠していませんでした。その状況に対処するためにアルゴリズムを変更している間、私はその過程でそれを少し小さくすることができました。

push@a,[split//];($s,$t)=(length$`,$.)if/S/;($e,$f)=(length$`,$.)if/E/}{$_.=$s<$e?($s++,R):$s>$e?($s--,L):$t<$f?($t++,D):($t--,U);$\=reverse$";$".=y/LRUD/RLDU/r.Y.reverse.($"=~y/Y/X/r);$a[$t-1][$s]=~y/./#/;$\.=$s-$e||$t-$f?redo:$_;print map{join'',@$_}@a

例(スクロールする必要がなく、構造を表示するために、出力に改行を追加しました):

E .....
#.....
#.....
#.....
##### S
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLXRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLLXRRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLXRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLLUXDRRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLXRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLLXRRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLXRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLLUUXDDRRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLXRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLLXRRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLXRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLLUXDRRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLXRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLLXRRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLXRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLLUUUYDDDRRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLXRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLLXRRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLXRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLLUXDRRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLXRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLLXRRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLXRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLLUUYDDRRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLXRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLLXRRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLXRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLLUYDRRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLXRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLLYRRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLYRRRR
 LXR LLXRR LXR LLLYRRR LXR LLYRR LYR LLLLLUUUU

プログラムが使用する表記法では、移動が経由表されLRU、及びD左のために、上、右、下にそれぞれ。デフォルトでは、移動するたびに1単位の水を拾いますが、これはキャラクターを追加することで変更できます。

  • X:1単位を拾うのではなく、2単位の水を落とします
  • Y:1単位を拾うのではなく、1単位の水を落とす
  • (つまり、スペース):水を完全に補充します(に移動した後にのみ出力されSます;プログラムは先行スペースでも出力されます。

ご覧のとおり、余分なプールをまったく使用せずに、この(むしろ不毛の)マップを横切ることができます。実際、事前に配置された水を使用せずに、これらのルールの下で地図上で距離を取得することが可能です。そのため、このアルゴリズムは事前に配置された水を無視するだけなので、処理するためにバイトを浪費する必要はありません。これはまた、ボットが死ぬのを見ないことを意味します、ごめんなさい。生き残ることが保証されていることがわかっている範囲を超えて移動することはありません。

両方XY(そしてX、戦略の大部分でY最後にたまにあることを確実にするための少しの追加コード)が必要な理由は、仕様ではマップの最終バージョンを出力する必要があるためです。これを実装する最も簡単な方法は、特に9水で始まった正方形が最終的には10パス上にある場合に(ASCIIアートを壊して))そして私たちはX、したがって、地図上に余分な水を落とさないようにする解決策を見つける必要があります。ここでのアルゴリズムは、ルート上の各正方形に1単位の水を「自然に」ドロップします。このように、各正方形を訪れる最後から2番目の時間であるため、XではなくYを使用することで、ドロップされる水量を1減らします。したがって、最後の訪問では、正方形を排水せずに元の水量に戻します。開始時よりも少し湿ったままにしておきます。

O(2 ^ n)のパフォーマンスがあるため、大きなマップでこれを実行することはお勧めしません(ボットは渇望で死ぬことはありませんが、このような戦略を使用すると空腹で死ぬと考えるのが妥当です)。

読み取り可能なバージョン:

# implicit with -n: read a line of input into $_
push @a, [split //]; #/ split $_ into characters, store at the end of @a
($s,$t) = (length$`,$.) if /S/; # if we see S, store its coordinates
($e,$f) = (length$`,$.) if /E/  # if we see E, store its coordinates
}{ # Due to -n, loop back to start if there are more lines.

# From here onwards, $" stores the partial solution this iteration;
#                    $\ stores the partial solution last iteration;
#                    $_ stores the path from ($s,$t) to S.
# At the start of the program, $" is a space, $\ and $_ are empty.

$_ .=  # Work out the next step on the path:
  $s < $e ? ($s++,R) # if too far left, move right, record that in $_;
: $s > $e ? ($s--,L) # if too far right, move left, record that in $_;
: $t < $f ? ($t++,D) # if too far up,    move down, record that in $_;
: ($t--,U);          # in other cases we must be too far down.
$\ = reverse $";     # Store last iteration; $" is constructed backwards.
$" .=                # Extend $" by appending
  y/LRUD/RLDU/r .    # the path from ($s, $t) back to S;
  Y .                # a literal 'Y';
  reverse .          # $/ backwards (i.e. the path from S to ($s, $t);
  ($"=~y/Y/X/r);     # a copy of $" with all 'Y' changed to 'X'.
$a[$t-1][$s] =~      # At the current path coordinate,
  y/./#/;            # replace any '.' with '#'.
$\ .=                # Start appending to $\;
  $s-$e||$t-$f?redo  # if we're not at E, abort that and jump back to {{,
: $_;                # otherwise append $_ (the path from S to E).
print map            # For each element of some array
  {join'',@$_}       # output its concatenated elements
  @a                 # specifying that array as @a.
# Implicitly: print $\ (which specifies the sort of newline print uses).

らせん状に世界を埋める洪水は、出口を探す方向の条件ブロックよりもコードが少ないでしょうか?
スパー

1
私はそれがそうなるとは思いません(私が見ないだけの方法があるかもしれませんが、それは確かに検討する価値のあるアイデアです)。それでも4つの方向に対処する必要があり、マップのエッジにも対処する必要があります。これは、このバージョンのアルゴリズムでは問題になりません。

良い点、再エッジ。無限の地図でできると思います。
スパー
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.