ホラー映画検索パーティー


21

プロット:ジミーがいない 私たちは彼を見つけなければなりません。分割する必要があります。

プロットのねじれ:ジミーはすでに死んでいます。

しかし、私たちのキャストはそれを知らないので、とにかくエリア全体を検索する必要があります。N列x M行(1 <= M、N <= 256)のセルグリッドがあり、開始点 "。"に対して "S"としてマークされています。オープンスペースの場合、または障害物の場合は「#」。これが地図です。

0 <= p <= 26 コスタル、0 <= q <= 26 エキストラ、1 つ星があります。最初は全員がSとマークされたセルにいます。

ルール

各人の視界半径は次のとおりです。

 ...
.....
..@..
.....
 ...

星は「@」で示され、共演者は「A」で始まる大文字で、余りは「a」で始まる小文字で示されます。最初に、開始点を囲む視界半径はすでに検索済みとしてマークされています。これがマップのオープンスペース全体を構成する場合、ゲームは終了します。次の順序で各ターン:

  1. 各人が同時にキングを動かします(静止するか、8つの隣接するセルのいずれかに移動します)。
  2. 各人の視界内のすべてのセルが検索対象としてカウントされます。
  3. 共演者が他の人を見ることができない場合、彼女は死にます。エキストラが共演者、スター、または少なくとも2つの他のエキストラを見ることができない場合、彼は死にます。これら同時に起こります。つまり、1ターンでの死の連鎖反応はあり得ません。上記の条件がチェックされ、死にかけようとしている人は全員死にます。
  4. マップ上のすべてのオープンスペースが検索された場合、検索は終了します。

ノート

複数の人がいつでも同じ広場にいることができ、これらの人はお互いを見ることができます。

障害物は視界を妨げることはなく、動きのみを妨げます。人々は、溶岩を越えてお互いを見ることができます...溶岩?

マップ上のオープンスペースは、キングムーブによって接続されることが保証されています。

また、最初の「S」は障害物ではなく、オープンスペースと見なされます。

オープンスペースに着地するキングムーブは有効です。たとえば、次の動きは合法です:

....      ....
.@#. ---> ..#.
.#..      .#@.
....      ....

入力

入力は次の形式になります

N M p q
[N cols x M rows grid with characters ".", "#", and "S"]

サンプル入力:

6 5 0 0
......
......
..S...
......
......

そして

9 9 1 1
S.......#
.......##
......##.
..#####..
...##....
...##....
...#.....
....#..#.
.........

pとqは、それぞれ共演者とエキストラの数です。

出力

出力は、各ターンごとに行われる動きであり、方向は

789
456
123

すべて同時に実行されるため、移動の順序は重要ではありません。人の移動をリストしないことは問題ありません。方向5に移動することと同じです。移動は次の形式でリストする必要があります。

@9 A2 a2 B7.

「。」ターンの移動の終了を示します。

マップが検索された後、出力の最終行は、スペースで区切られた3つの整数である必要があります。ボードの検索を完了するのにかかったターン数、生きている共演者の数、および生きているエキストラの数です。最初の入力例

6 5 0 0
......
......
..S...
......
......

有効な出力は次のとおりです。

@4.
@6.
@6.
@6.
4 0 0

最後の注意:星は死ぬことはできず、マップ上のオープンスペースは接続されていることが保証されているため、検索は常に最終的に成功します。

得点

スコアは、一連のベンチマークテストで取得したターン総数です。回答と一緒に独自のテストケースを提出してください。ベンチマークセットを超える生の共演者の数の合計は、タイブレーカーとして使用され、まだ同点がある場合は、生のエキストラの数の合計が使用されます。

テストセットとコントローラー

現在、5つのマップがhttps://github.com/Tudwell/HorrorMovieSearchParty/でオンラインになっています。回答を提出した人は誰でもテストケースを提出することもできます。テストケースは、何らかの理由で拒否する権利を留保します(何らかの理由でマップを拒否した場合、別のケースを送信できます)。これらは、私の裁量でテストセットに追加されます。

Python (2.7.5でテスト済み)コントローラーがgithubでcontroller.pyとして提供されてい ます。が第二のコントローラ、controller_disp.pyは、それが検索(pygameのライブラリを必要とする)中にグラフィック出力を示すことを除いて同一です。

グラフィカルコントローラー出力

使用法

python controller.py <map file> <your execution line>

すなわち:

python controller.py map1.txt python solver.py map1.txt

コントローラーには次の形式の出力が(プログラムのstdinに)あります

Turn 1
@:2,3 A:2,3 B:2,3.
##...##
#ooo..#
ooooo..
ooooo..
ooooo..
#ooo...
##.....
###....
----------------------------------------

これはターン番号(ターン1は移動する前)、「。」で終わるすべてのアクターとそのx、y座標のリスト(左上の文字は(0,0)です)、全体の表現ですボード、および40 '-'の行。次に、フォームの(プログラムのstdoutからの)入力を待機します。

@9 A2 B7.

これは上記で指定された出力形式です。コントローラーは、検索されたオープンスペースに対して「o」、「。」を出力します。検索されていないオープンスペースの場合、および障害物の場合は「#」。人々のリストとその座標に生きている人々のみが含まれ、ゲームのすべてのルールを追跡します。不正な移動が試行されると、コントローラーは終了します。特定のターンの動きが検索を終了した場合、出力は上記のようにはなりません。代わりに、それは形式です

Finished in 4 turns
4 1 0

ここでの「4 1 0」は、合計4ターン、1人の共演者、および0人のエキストラエクストラを示します。コントローラーを使用する必要はありません。自由に使用したり、独自のエントリに合わせて変更したりできます。使用して問題が発生した場合は、お知らせください。

コントローラーの作成を支援してくれた@githubphagocyteに感謝します。

編集: ランダム化されたエントリの場合、特定のマップでの実行をそのマップのスコアとして選択できます。スコアリングの要件により、各マップでは常に、最も少ないターン、次に最も少ないデッドコスター、そして最も少ないデッドエクストラを選択する必要があることに注意してください。


7
2行目はネタバレタグの間にあるべきです!
アベロス14

回答:


8

ルビー、セーフティファースト+ BFS +ランダムネス、スコア≤1458

ランダムな提出物をどのように採点するかわかりません。すべての回答が決定論的でなければならない場合はお知らせください。シードを選択するか、ランダム性を完全に取り除きます。

このソリューションのいくつかの機能と欠点:

  • 誰も死ぬことはありません。最初は、すべての俳優をグループ化して、全員が安全になるようにします。これらの各グループのキャラクターは一斉に動きます。これは、全員を生かし続けるのに適していますが、最適な効率ではありません。
  • 各グループは、幅優先検索を介してマップ上の最も近い未探索のスポットを検索し、検索のそのブランチの最初の動きを取ります。複数の最適な動きの間に同点がある場合、ランダムなものが選択されます。これは、すべてのグループが常に同じ方向に向かうわけではないことを保証するためです。
  • このプログラムは、視野については知りません。実際には、未探索のすべてのセルに移動しようとします。これを考慮すると、パフォーマンスが大幅に向上する可能性があります。その場合、各動きの品質を、それが明らかにするセルの数で定量化できるためです。
  • プログラムは、ターン間の情報を追跡しません(俳優グループを除く)。そのため、大きなテストケースでは非常に遅くなります。map5.txt完了するまでに1〜13分かかります。

いくつかの結果

Map     Min turns    Max turns
map1        46           86
map2        49          104
map3       332          417
map4       485          693
map5       546          887

コードは次のとおりです。

start = Time.now

map_file = ARGV.shift
w=h=p=q=0
File::open(map_file, 'r') do |file|
    w,h,p,q = file.gets.split.map(&:to_i)
end

costars = p > 0 ? (1..p).map {|i| (i+64).chr} : []
extras = q > 0 ? (1..q).map {|i| (i+96).chr} : []
groups = []

costars.zip(extras).each do |costar, extra|
    break unless extra
    groups << (costar + extra)
    costars.delete(costar)
    extras.delete(extra)
end

costars.each_slice(2) {|c1, c2| groups << (c1 + (c2 || '@'))} unless costars.empty?
extras.each_slice(3) {|c1, c2, c3| groups << (c1 + (c2 || '') + (c3 || '@'))} unless extras.empty?
groups << '@' unless groups.join['@']

#$stderr.puts groups.inspect


directions = {
    1 => [-1, 1],
    2 => [ 0, 1],
    3 => [ 1, 1],
    4 => [-1, 0],
    5 => [ 0, 0],
    6 => [ 1, 0],
    7 => [-1,-1],
    8 => [ 0,-1],
    9 => [ 1,-1]
}

loop do
    break unless gets # slurp turn number
    coords = {}
    input = gets
    input.chop.chop.split.each{|s| actor, c = s.split(':'); coords[actor] = c.split(',').map(&:to_i)}
    #$stderr.puts input
    #$stderr.puts coords.inspect
    map = []
    h.times { map << gets.chomp }

    gets # slurp separator
    moves = groups.map do |group|
        x, y = coords[group[0]]
        distances = {[x,y] => 0}
        first_moves = {[x,y] => nil}
        nearest_goal = Float::INFINITY
        best_move = []
        active = [[x,y]]
        while !active.empty?
            coord = active.shift
            dist = distances[coord]
            first_move = first_moves[coord]
            next if dist >= nearest_goal
            [1,2,3,4,6,7,8,9].each do |move|
                dx, dy = directions[move]
                x, y = coord
                x += dx
                y += dy
                next if x < 0 || x >= w || y < 0 || y >= h || map[y][x] == '#'
                new_coord = [x,y]
                if !distances[new_coord]
                    distances[new_coord] = dist + 1
                    first_moves[new_coord] = first_move || move
                    active << new_coord if map[y][x] == 'o'
                end

                if dist < distances[new_coord]
                    distances[new_coord] = dist + 1
                    first_moves[new_coord] = first_move || move
                end

                if map[y][x] == '.'
                    if dist + 1 < nearest_goal
                        nearest_goal = dist + 1
                        best_move = [first_moves[new_coord]]
                    elsif dist + 1 == nearest_goal
                        best_move << first_moves[new_coord]
                    end
                end
            end
        end

        #if group['@']
        #    distances.each{|k,v|x,y=k;map[y][x]=(v%36).to_s(36)}
        #    $stderr.puts map
        #end

        dir = best_move.sample
        group.chars.map {|actor| actor + dir.to_s}
    end * ' '
    #$stderr.puts moves
    puts moves
    $stdout.flush
end

#$stderr.puts(Time.now - start)

そこにはいくつかのコメントアウトされたデバッグ出力があります。特にif group['@']ブロックは、BFSデータの視覚化を印刷するため、非常に興味深いものです。

編集:より良い動きがすでに見つかった場合にBFSを停止することにより、速度が大幅に改善されました(そもそもBFSを使用するポイントでした)。


エントリが常にマップファイルにアクセスできることを期待するのは安全ですか?
スパー14

はい; マップファイルは常にそこにあり、コントローラーを使用する場合は、毎回更新されたコピーを取得します。
エリックトレスラー14
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.