Партія пошуку фільмів жахів


21

Сюжет : Джиммі відсутній; ми мусимо його знайти. Ми повинні розділитися.

Скрут сюжету : Джиммі вже мертвий.

Але наш акторський склад цього не знає, тому їм все одно потрібно шукати всю область. Існує N стовпців x M рядків (1 <= M, N <= 256) сітка комірок, або позначених як "S" для початкової точки, ". для відкритого простору або "#" для перешкоди. Це карта .

Є 0 <= p <= 26 costars , 0 <= q <= 26 додаткових та 1 зірка . Усі спочатку знаходяться у комірці із позначкою S.

Правила

Кожна людина має радіус зору, показаний нижче:

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

Зірку позначають «@», реставрацію великими літерами, починаючи з «A», а додаткові - малими літерами, починаючи з «а». Спочатку радіус прицілу, що оточує початкову точку, вже позначається як пошуковий. Якщо це становить весь відкритий простір карти, гра закінчується. Кожен виток у такому порядку :

  1. Кожна людина одночасно робить рухи короля (або стоячи на місці, або рухаючись до однієї з 8 сусідніх осередків).
  2. Усі клітини в радіусі зору навколо кожної людини зараховуються як обшукані.
  3. Якщо костар не може побачити нікого іншого, вона помирає. Якщо зайвий не може побачити ані костар, ані зірку, ані щонайменше 2 інших монет, він помирає. Вони трапляються одночасно - тобто не може бути ланцюгової реакції смертей за один раз; перераховані вище умови перевіряються, і кожен, хто збирається померти, гине відразу.
  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 - числа costars і extras відповідно.

Вихідні дані

Вихід повинен бути, для кожного ходу, рухами, які робляться, з напрямками, позначеними символом

789
456
123

Порядок рухів не має значення, оскільки вони виконуються одночасно. Якщо не вказати переїзд на людину, це нормально і рівнозначно переміщенню його в напрямку 5. Переміщення повинні бути перелічені у такому форматі:

@9 A2 a2 B7.

"." позначає кінець ваших кроків для повороту.

Після пошуку карти, кінцевий рядок виходу повинен становити три цілі числа, розділені пробілами: кількість витків, які вам знадобилися, щоб закінчити пошук дошки, кількість живих костар, а також кількість живих місць. Для першого прикладу введення

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

наступний дійсний вихід:

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

Заключна примітка: зірка не може загинути, і відкритий простір на карті гарантовано з'єднаний, тому пошук завжди буде успішним.

Оцінка балів

Ваш бал - це загальна кількість витків, виконаних за набір тестових еталонів; Ви можете подати власний тестовий випадок разом із Вашою відповіддю. Сума кількості живих costars над набором еталонів буде використовуватися в якості переривника, і якщо все-таки є нічия, буде використана сума кількості живих екстрасів.

Тестовий набір і контролер

Наразі 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

Контролер має вихід (для вашої програми стандартного введення ) виду

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

Це номер повороту (поворот 1 - перед тим, як ви переїхали), "". - список усіх акторів та їх x, y координат (верхній лівий символ (0,0)), представлення всього дошка та лінія з 40-х. Потім він очікує введення (від вашої програми на стандартний висновок ) виду

@9 A2 B7.

Це вихідний формат, зазначений вище. Контролер виводить "o" для відкритого простору, який шукали, "." для відкритого простору, який не шукали, і "#" для перешкод. Він включає лише живих людей у ​​свій список людей та їх координати та відстежує всі правила гри. Контролер вийде, якщо буде здійснена спроба незаконного переміщення. Якщо рухи на заданий поворот закінчують пошук, вихід не такий, як вище; натомість він має форму

Finished in 4 turns
4 1 0

"4 1 0" тут позначає 4 загальних повороти, 1 живу костар і 0 живих монет. Вам не потрібно використовувати контролер; сміливо використовуйте його або модифікуйте для власного вступу. Якщо ви вирішили використовувати його і зіткнетеся з проблемами, дайте мені знати.

Завдяки @githubphagocyte за те, що він допомагав мені писати контролер.

Редагувати: Для рандомізованого запису ви можете вибрати будь-який запуск на певній карті як ваш рахунок для цієї карти. Зауважте, що через вимоги до вибору балів завжди слід вибирати найменші повороти, потім найменше мертвих костар, а потім найменше мертвих додатків для кожної карти.


7
другий рядок повинен бути між тегами спойлерів!
Averroes

Відповіді:


8

Ruby, Safety First + BFS + Randomness, Оцінка ≤ 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).


чи можна сподіватися, що ваш запис завжди матиме доступ до файлу карти?
Спарр

Так; файл карти завжди є, і якщо ви використовуєте контролер, ви отримуєте оновлену копію кожного кроку.
Ерік Тресслер
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.