공포 영화 검색 파티


21

줄거리 : 지미가 없습니다. 우리는 그를 찾아야합니다. 우리는 헤어져야합니다.

플롯 트위스트 : 지미는 이미 죽었습니다.

그러나 우리 캐스트는 그것을 알지 못하므로 어쨌든 전체 지역을 검색해야합니다. 셀의 N 열 x M 행 (1 <= M, N <= 256) 격자가 있으며, 시작점 "."에 대해 "S"로 표시됩니다. 열린 공간의 경우, 또는 장애물의 경우 "#"입니다. 이것은 지도 입니다.

0 <= p <= 26 costars , 0 <= q <= 26 extras , 1 star가 있습니다. 모든 사람은 처음에 S로 표시된 셀에 있습니다.

규칙

각 사람의 시력 반경은 다음과 같습니다.

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

별표는 "@", 대문자는 "A"로 시작하고, 엑스트라는 소문자로, "a"로 시작합니다. 처음에는 시작점을 둘러싼 시야 반경이 이미 검색된 것으로 표시되어 있습니다. 이것이 맵의 전체 열린 공간을 구성하면 게임이 종료됩니다. 각 순서는 다음 순서로 수행됩니다 .

  1. 각 사람은 동시에 왕을 움직이게합니다 (정지되어 있거나 8 개의 이웃 세포 중 하나로 이동).
  2. 각 사람 주위의 시력 반경에있는 모든 셀이 검색된 것으로 계산됩니다.
  3. costar가 다른 사람을 볼 수 없으면 그녀는 죽습니다. 엑스트라가 코스타, 스타 또는 적어도 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는 각각 costar와 extra의 수입니다.

산출

출력은 매 턴마다 지시 된 방향으로 움직여야합니다.

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...
##.....
###....
----------------------------------------

이것은 모든 액터의 x.y 좌표 (왼쪽 상단 문자는 (0,0) 임)의 '.'- 종료 목록 인 턴 번호 (1 턴은 이동하기 전)이며 전체를 나타냅니다. 보드와 40 '-'s 라인. 그런 다음 양식 의 입력을 기다립니다 (프로그램의 표준 출력에서 )

@9 A2 B7.

이것은 위에서 지정한 출력 형식입니다. 컨트롤러는 검색된 열린 공간에 대해 'o'를 출력합니다. '.' 검색되지 않은 열린 공간, '#'에 장애물이 있습니다. 여기에는 사람과 좌표 목록에 살아있는 사람 만 포함되며 게임의 모든 규칙을 추적합니다. 잘못된 이동을 시도하면 컨트롤러가 종료됩니다. 주어진 턴에 대한 이동이 검색을 마치면 출력은 위와 같지 않습니다. 대신 그것은 형태입니다

Finished in 4 turns
4 1 0

여기에서 "4 1 0"은 총 4 번의 턴, 1 개의 살아있는 코스타 및 0 개의 여분의 여분을 나타냅니다. 컨트롤러를 사용할 필요는 없습니다. 자유롭게 사용하거나 직접 입력하십시오. 사용하기로 결정하고 문제가 발생하면 알려주십시오.

컨트롤러 작성에 도움을 주신 @githubphagocyte에게 감사드립니다.

편집 : 무작위 항목의 경우 특정 맵에서 실행 한 것을 해당 맵의 점수로 선택할 수 있습니다. 스코어링 요구 사항으로 인해 항상 가장 적은 턴을 선택한 다음 각 죽은 데드 코스트를 가장 적게 선택한 다음 각 맵에 대해 가장 적은 데드 엑스트라를 선택해야합니다.


7
두 번째 줄은 스포일러 태그 사이에 있어야합니다!
Averroes

답변:


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를 사용하는 지점).


항목이 항상지도 파일에 액세스 할 것으로 기대하는 것이 안전합니까?
Sparr

예; 맵 파일은 항상 존재하며 컨트롤러를 사용하면 매 턴마다 업데이트 된 사본을 얻습니다.
Eric Tressler
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.