Partai Pencarian Film Horor

21

Plot : Jimmy hilang; kita harus menemukannya. Kita harus berpisah.

Plot twist : Jimmy sudah mati.

Tapi, pemeran kami tidak tahu itu, jadi mereka perlu mencari di seluruh area. Ada kolom N kolom x M baris (1 <= M, N <= 256) sel, baik ditandai sebagai "S" untuk titik awal, "." untuk ruang terbuka, atau "#" untuk hambatan. Ini peta .

Ada 0 <= p <= 26 costars , 0 <= q <= 26 ekstra , dan 1 bintang . Setiap orang awalnya di sel bertanda S.

Aturan

Setiap orang memiliki radius penglihatan yang ditunjukkan di bawah ini:

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

Bintang dilambangkan dengan "@", costars dengan huruf kapital, dimulai dengan "A", dan ekstra dengan huruf kecil, dimulai dengan "a". Awalnya, radius penglihatan yang mengelilingi titik awal sudah ditandai sebagai dicari. Jika ini merupakan seluruh ruang terbuka peta, permainan berakhir. Setiap belokan, dengan urutan sebagai berikut :

  1. Setiap orang secara bersamaan membuat raja bergerak (baik berdiri diam, atau pindah ke salah satu dari 8 sel tetangga).
  2. Semua sel dalam radius penglihatan di sekitar setiap orang dihitung sebagai yang dicari.
  3. Jika seorang costar tidak bisa melihat orang lain, dia mati. Jika ekstra tidak dapat melihat costar, bintang, atau setidaknya 2 tambahan lainnya, dia mati. Ini terjadi secara bersamaan - yaitu, tidak ada reaksi berantai kematian pada satu putaran; kondisi di atas diperiksa, dan semua orang yang akan mati akan mati sekaligus.
  4. Jika semua ruang terbuka di peta telah dicari, pencarian sudah selesai.

Catatan

Banyak orang dapat berada di alun-alun yang sama pada titik mana pun, dan orang-orang ini dapat saling bertemu.

Rintangan tidak pernah menghalangi penglihatan, hanya gerakan; orang bisa melihat satu sama lain di seberang, eh ... lava?

Ruang terbuka di peta dijamin terhubung oleh gerakan raja.

Awal "S" juga dianggap sebagai ruang terbuka, bukan hambatan.

Setiap raja yang mendarat di ruang terbuka valid. Misalnya, langkah berikut ini legal:

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

Memasukkan

Masukan akan dalam format

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

Input sampel:

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

dan

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

p dan q adalah jumlah costars dan ekstra, masing-masing.

Keluaran

Output harus, untuk setiap belokan, gerakan yang dibuat, dengan arah yang ditunjukkan oleh

789
456
123

Urutan gerakan tidak masalah, karena semuanya diberlakukan secara bersamaan. Tidak mencantumkan langkah untuk seseorang baik-baik saja, dan sama dengan memindahkannya ke arah 5. Gerakan harus terdaftar dalam format berikut:

@9 A2 a2 B7.

"." menunjukkan akhir gerakan Anda untuk giliran.

Setelah peta dicari, garis akhir output haruslah tiga bilangan bulat, dipisahkan oleh spasi: jumlah belokan yang Anda perlukan untuk menyelesaikan pencarian papan, jumlah costar hidup, dan jumlah ekstra hidup. Untuk input contoh pertama

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

berikut ini adalah output yang valid:

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

Catatan terakhir: bintang tidak bisa mati dan ruang terbuka di peta dijamin akan terhubung, sehingga pencarian akan selalu berhasil pada akhirnya.

Mencetak gol

Skor Anda adalah jumlah total belokan yang diambil atas serangkaian tes benchmark; Anda dipersilakan untuk menyerahkan test case Anda sendiri beserta jawaban Anda. Jumlah dari jumlah costar hidup selama set benchmark akan digunakan sebagai tie-breaker, dan jika masih ada tie, jumlah dari jumlah ekstra living akan digunakan.

Perangkat Uji dan Pengontrol

Saat ini, 5 peta online di https://github.com/Tudwell/HorrorMovieSearchParty/ . Siapa pun yang mengirimkan jawaban juga dapat mengajukan test case, yang saya berhak tolak karena alasan apa pun (jika saya menolak peta Anda karena suatu alasan, Anda dapat mengirimkan yang lain). Ini akan ditambahkan ke set tes sesuai kebijaksanaan saya.

Kontroler Python (diuji dalam 2.7.5) disediakan di github sebagai controller.py . Kontroler kedua di sana, controller_disp.py , identik kecuali menunjukkan output grafis selama pencarian (memerlukan pustaka Pygame).

Output pengontrol grafis

Penggunaan :

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

Yaitu:

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

Pengontrol memiliki output (ke stdin program Anda ) dari formulir

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

Ini adalah nomor belokan (belok 1 adalah sebelum Anda pindah), daftar '.'- yang diakhiri dari semua aktor dan koordinat x, y mereka (karakter kiri atas adalah (0,0)), representasi dari keseluruhan papan, dan garis dengan 40 '-'s. Kemudian menunggu input (dari stdout program Anda ) dari formulir

@9 A2 B7.

Ini adalah format output yang ditentukan di atas. Pengontrol menghasilkan 'o' untuk ruang terbuka yang telah dicari, '.' untuk ruang terbuka yang belum dicari, dan '#' untuk hambatan. Ini hanya mencakup orang yang hidup dalam daftar orang dan koordinatnya, dan melacak semua aturan permainan. Pengontrol akan keluar jika ada upaya ilegal. Jika gerakan untuk giliran tertentu menyelesaikan pencarian, hasilnya tidak seperti di atas; alih-alih itu dari formulir

Finished in 4 turns
4 1 0

"4 1 0" di sini menunjukkan 4 total belokan, 1 costar hidup, dan 0 ekstra hidup. Anda tidak perlu menggunakan pengontrol; merasa bebas untuk menggunakannya atau memodifikasinya untuk entri Anda sendiri. Jika Anda memutuskan untuk menggunakannya dan menemui masalah, beri tahu saya.

Terima kasih kepada @githubphagocyte karena membantu saya menulis controller.

Sunting: Untuk entri acak, Anda dapat memilih jalan di peta tertentu sebagai skor Anda untuk peta itu. Perhatikan bahwa karena persyaratan penilaian, Anda harus selalu memilih belokan paling sedikit, kemudian costar mati paling sedikit, lalu tambahan mati paling sedikit untuk setiap peta.

Eric Tressler
sumber
7
baris kedua harus di antara tag spoiler!
Averroes

Jawaban:

8

Ruby, Safety First + BFS + Randomness, Score ≤ 1458

Saya tidak yakin bagaimana Anda akan mencetak kiriman acak. Jika semua jawaban harus deterministik, beri tahu saya dan saya akan memilih satu benih atau menyingkirkan keacakan secara bersamaan.

Beberapa fitur dan kekurangan dari solusi ini:

  • Tidak ada yang pernah mati. Pada awalnya saya mengelompokkan semua aktor sehingga semua orang aman. Karakter di masing-masing grup ini bergerak serempak. Itu bagus untuk menjaga semua orang tetap hidup tetapi tidak efisien secara optimal.
  • Setiap grup mencari tempat yang belum dijelajahi terdekat di peta melalui pencarian pertama yang luas dan mengambil langkah pertama dari cabang pencarian tersebut. Jika ada ikatan antara beberapa gerakan optimal, dipilih secara acak. Ini untuk memastikan bahwa tidak semua kelompok selalu menuju ke arah yang sama.
  • Program ini tidak tahu tentang bidang pandang. Sebenarnya mencoba untuk pindah ke setiap sel yang belum dijelajahi. Mempertimbangkan hal ini dapat sangat meningkatkan kinerja, karena Anda juga dapat mengukur kualitas setiap gerakan dengan berapa banyak sel yang akan terungkap.
  • Program tidak melacak informasi antar belokan (kecuali grup aktor). Itu membuatnya sangat lambat pada kasus uji yang lebih besar. map5.txtmembutuhkan waktu antara 1 dan 13 menit untuk menyelesaikan.

Beberapa hasil

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

Sekarang inilah kodenya:

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)

Ada beberapa keluaran debug yang dikomentari di sana. Terutama if group['@']blok ini cukup menarik karena mencetak visualisasi data BFS.

Sunting: Peningkatan kecepatan yang signifikan, dengan menghentikan BFS jika langkah yang lebih baik telah ditemukan (yang agak merupakan titik menggunakan BFS di tempat pertama).

Martin Ender
sumber
apakah aman untuk mengharapkan bahwa entri Anda akan selalu memiliki akses ke file peta?
Sparr
Iya nih; file peta selalu ada, dan jika Anda menggunakan controller, Anda mendapatkan salinannya yang diperbarui setiap belokan.
Eric Tressler