업데이트 : 시작하기 위해 Python 프레임 워크가 추가되었습니다.
우주 정거장은 크러셔 로봇에 의해 추월되었습니다. 역이 스스로 파괴되기 전에 "토끼"라고 불리는 우리의 값 비싸고 부서지기 쉬운 기술 로봇을 출구 텔레 포터로 보내야하지만 크러셔 로봇은 복도를 순찰하고 있습니다.
귀하의 프로그램은 ASCII 맵을 제공받으며, 각 턴에는 크러셔 봇이 어디에 있는지 그리고 현재 토끼가 있는지 알려줍니다. 그런 다음 분쇄기 로봇을 피하면서 토끼를 출구 텔레 포터쪽으로 옮깁니다.
실행
다음을 사용하여 Python 2 컨트롤러를 실행하십시오.
python controller.py <mapfile> <turns> <seed> <runs> <prog>...
<prog> can be <interpreter> <yourprog> or similar.
시드는 크러셔 및 프로그램 PRNG에 사용되는 작은 정수이므로 실행을 반복 할 수 있습니다. 사용 된 실제 시드에 관계없이 프로그램이 일관되게 수행되어야합니다. 시드가 0이면 컨트롤러는 각 실행에 대해 임의의 시드를 사용합니다.
컨트롤러는 맵 텍스트 파일 이름과 시드를 인수로 사용하여 프로그램을 실행합니다. 예 :
perl wandomwabbits.pl large.map 322
프로그램이 PRNG를 사용하는 경우 주어진 시드로 초기화해야합니다. 그런 다음 컨트롤러는 STDIN을 통해 프로그램 업데이트를 보내고 STDOUT을 통해 토끼 움직임을 읽습니다.
컨트롤러는 매번 3 라인을 출력합니다 :
turnsleft <INT>
crusher <x,y> <movesto|crushes> <x,y>; ...
rabbits <x,y> <x,y> ...
그런 다음 프로그램이 한 줄을 출력하기를 기다립니다.
move <x,y> to <x,y>; ...
업데이트 : 컨트롤러가 첫 줄을 보내기 전에 프로그램을 초기화하는 데 2 초가 걸립니다.
컨트롤러 래빗 위치 입력 후 이동에 응답하는 데 프로그램이 0.5 초 이상 걸리면 컨트롤러가 종료됩니다.
격자에 토끼가 없으면 토끼 줄에 값이 없으며 프로그램에서 "이동"문자열 줄을 출력해야합니다.
매번 프로그램 출력 스트림을 플러시해야합니다. 그렇지 않으면 컨트롤러가 멈출 수 있습니다.
예
프로그램 입력 :
turnsleft 35
crusher 22,3 crushes 21,3; 45,5 movesto 45,4
rabbits 6,4 8,7 7,3 14,1 14,2 14,3
프로그램 출력 :
move 14,3 to 14,4; 14,2 to 14,3; 6,4 to 7,4
컨트롤러 로직
각 턴의 논리 :
- 좌회전이 0이면 점수를 인쇄하고 종료합니다.
- 빈 시작 셀마다 분쇄기가 보이지 않으면 토끼를 추가하십시오.
- 각 파쇄기마다 이동 방향을 결정하십시오 (아래 참조).
- 각 분쇄기마다 가능하면 움직입니다.
- 분쇄기가 토끼 위치에 있으면 토끼를 제거하십시오.
- 출력 좌회전, 파쇄기 동작 및 토끼 위치 프로그래밍.
- 프로그램에서 토끼 이동 요청을 읽습니다.
- 토끼가 없거나 움직일 수 없으면 건너 뛰십시오.
- 토끼의 각각의 새로운 위치를 계획하십시오.
- 토끼가 분쇄기에 부딪 치면 토끼가 파괴됩니다.
- 토끼가 텔레 포터 출구에 있으면 토끼를 제거하고 점수를 올립니다.
- 토끼가 충돌하면 모두 파괴됩니다.
각 분쇄기는 항상 방향 방향 (NSEW 중 하나)을 갖습니다. 크러셔는 매번이 탐색 논리를 따릅니다.
- 하나 이상의 토끼가 4 개의 직교 방향 중 하나에서 보이는 경우, 가장 가까운 토끼 중 하나로 방향을 변경하십시오. 크러셔는 다른 크러셔를 지나칠 수 없습니다.
- 그렇지 않으면 가능한 경우 열린 앞으로, 왼쪽, 오른쪽 옵션 중에서 무작위로 선택하십시오.
- 그렇지 않으면 전방, 좌측 및 우측에 장애물 (벽 또는 기타 분쇄기)이있는 경우 방향을 후방으로 설정하십시오.
그런 다음 각 분쇄기마다 :
- 새로운 파쇄기 방향으로 장애물이 없으면 움직여서 분쇄하십시오.
지도 기호
맵은 ASCII 문자의 사각형 격자입니다. 맵은 벽
#
, 복도 공간 , 토끼 시작 위치
s
, 출구 텔레 포터 e
및 크러셔 시작 위치로 구성 c
됩니다. 왼쪽 상단은 위치 (0,0)입니다.
작은지도
###################
# c #
# # ######## # # ##
# ###s # ####e#
# # # # ## ## #
### # ### # ## # #
# ## #
###################
테스트 맵
#################################################################
#s ############################ s#
## ## ### ############ # ####### ##### ####### ###
## ## ### # # ####### ########## # # #### ###### ###
## ## ### # ############ ####### ########## ##### ####### ###
## ## ## # ####### ########## # # ##### #### #
## ### #### #### ######## ########## ##### #### ## ###
######### #### ######## ################ ####### #### ###
######### ################# ################ c ####### ###
######### ################## ####### ####### ###########
######### ################## ######## ####### ###########
##### ### c ###### ###################
# #### ### # # # # # # # # # # ###### ############## #
# ####### #### ### #### ##### ## #
# #### ### # # # # # # # # # # ### # ### ######### #
##### ### #### ### ##### ### # ######## ####
############## ### # # # # # # # # # # ####### ## ####### ####
#### #### #### ### ### # # ### ###### ####
## ### # # # # # # # # # # ### ### # ### ##### ####
##### ######## ### # # # ##### # # # # ### ### # ##### #### ####
##### ##### ###### c # ### ### ###### ### ####
## c ######################### ### ##### ####### ### ####
##### # ### ####### ######## ### ##### c ## ## ####
##### # ####### ########## ## ######## # ######## ## ####
######### # ####### ## # ## # # # ##### # ####
### ##### # ### # ############## # ### # ### ## # ####
# ## # ### ### # ############## # ### ##### ##### ## ####
### ## ## # ### # ######## #
#s ## ###################################################e#
#################################################################
큰지도 실행 예
점수
프로그램을 평가하려면 테스트 맵, 500 턴, 5 런 및 시드 0으로 컨트롤러를 실행하십시오. 점수는 스테이션에서 안전으로 성공적으로 텔레포트 된 총 토끼 수입니다. 동점 일 경우 가장 많은 표를 얻은 답이 이길 것입니다.
답변에는 출품작 이름, 사용 언어 및 점수가 포함 된 제목이 포함되어야합니다. 답변 본문에 다른 사람들이 달리기를 반복 할 수 있도록 시드 번호가 포함 된 컨트롤러 점수 출력을 포함하십시오. 예를 들면 다음과 같습니다.
Running: controller.py small.map 100 0 5 python bunny.py
Run Seed Score
1 965 0
2 843 6
3 749 11
4 509 10
5 463 3
Total Score: 30
표준 라이브러리 및 자유롭게 사용 가능한 라이브러리를 사용할 수 있지만 표준 허점은 금지되어 있습니다. 주어진 시드, 회전 수, 맵 기능 세트 또는 기타 매개 변수에 대해 프로그램을 최적화해서는 안됩니다. 본인은이 규칙을 위반 한 것으로 의심 될 경우지도, 회전 횟수 및 시드를 변경할 권리가 있습니다.
컨트롤러 코드
#!/usr/bin/env python
# Control Program for the Rabbit Runner on PPCG.
# Usage: controller.py <mapfile> <turns> <seed> <runs> <prog>...
# Tested on Python 2.7 on Ubuntu Linux. May need edits for other platforms.
# v1.0 First release.
# v1.1 Fixed crusher reporting bug.
# v1.2 Control for animation image production.
# v1.3 Added time delay for program to initialise
import sys, subprocess, time, re, os
from random import *
# Suggest installing Pillow if you don't have PIL already
try:
from PIL import Image, ImageDraw
except:
Image, ImageDraw = None, None
GRIDLOG = True # copy grid to run.log each turn (off for speed)
MKIMAGE = False # animation image creation (much faster when off)
IMGWIDTH = 600 # animation image width estimate
INITTIME = 2 # Allow 2 seconds for the program to initialise
point = complex # use complex numbers as 2d integer points
ORTH = [1, -1, 1j, -1j] # all 4 orthogonal directions
def send(proc, msg):
proc.stdin.write((msg+'\n').encode('utf-8'))
proc.stdin.flush()
def read(proc):
return proc.stdout.readline().decode('utf-8')
def cansee(cell):
# return a dict of visible cells containing robots with distances
see = {} # see[cell] = dist
robots = rabbits | set(crushers)
if cell in robots:
see[cell] = 0
for direc in ORTH:
for dist in xrange(1,1000):
test = cell + direc*dist
if test in walls:
break
if test in robots:
see[test] = dist
if test in crushers:
break # can't see past them
return see
def bestdir(cr, direc):
# Decide in best direction for this crusher-bot
seen = cansee(cr)
prey = set(seen) & rabbits
if prey:
target = min(prey, key=seen.get) # Find closest
vector = target - cr
return vector / abs(vector)
obst = set(crushers) | walls
options = [d for d in ORTH if d != -direc and cr+d not in obst]
if options:
return choice(options)
return -direc
def features(fname):
# Extract the map features
walls, crusherstarts, rabbitstarts, exits = set(), set(), set(), set()
grid = [line.strip() for line in open(fname, 'rt')]
grid = [line for line in grid if line and line[0] != ';']
for y,line in enumerate(grid):
for x,ch in enumerate(line):
if ch == ' ': continue
cell = point(x,y)
if ch == '#': walls.add(cell)
elif ch == 's': rabbitstarts.add(cell)
elif ch == 'e': exits.add(cell)
elif ch == 'c': crusherstarts.add(cell)
return grid, walls, crusherstarts, rabbitstarts, exits
def drawrect(draw, cell, scale, color, size=1):
x, y = int(cell.real)*scale, int(cell.imag)*scale
edge = int((1-size)*scale/2.0 + 0.5)
draw.rectangle([x+edge, y+edge, x+scale-edge, y+scale-edge], fill=color)
def drawframe(runno, turn):
if Image == None:
return
scale = IMGWIDTH/len(grid[0])
W, H = scale*len(grid[0]), scale*len(grid)
img = Image.new('RGB', (W,H), (255,255,255))
draw = ImageDraw.Draw(img)
for cell in rabbitstarts:
drawrect(draw, cell, scale, (190,190,255))
for cell in exits:
drawrect(draw, cell, scale, (190,255,190))
for cell in walls:
drawrect(draw, cell, scale, (190,190,190))
for cell in crushers:
drawrect(draw, cell, scale, (255,0,0), 0.8)
for cell in rabbits:
drawrect(draw, cell, scale, (0,0,255), 0.4)
img.save('anim/run%02uframe%04u.gif' % (runno, turn))
def text2point(textpoint):
# convert text like "22,6" to point object
return point( *map(int, textpoint.split(',')) )
def point2text(cell):
return '%i,%i' % (int(cell.real), int(cell.imag))
def run(number, nseed):
score = 0
turnsleft = turns
turn = 0
seed(nseed)
calltext = program + [mapfile, str(nseed)]
process = subprocess.Popen(calltext,
stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=errorlog)
time.sleep(INITTIME)
rabbits.clear()
crushers.clear()
crushers.update( dict((cr, choice(ORTH)) for cr in crusherstarts) )
while turnsleft > 0:
# for each empty start cell, add a rabbit if no crusher in sight.
for cell in rabbitstarts:
if cell in rabbits or set(cansee(cell)) & set(crushers):
continue
rabbits.add(cell)
# write the grid to the runlog and create image frames
if GRIDLOG:
for y,line in enumerate(grid):
for x,ch in enumerate(line):
cell = point(x,y)
if cell in crushers: ch = 'X'
elif cell in rabbits: ch = 'o'
runlog.write(ch)
runlog.write('\n')
runlog.write('\n\n')
if MKIMAGE:
drawframe(number, turn)
# for each crusher, decide move direction.
for cr, direc in crushers.items():
crushers[cr] = bestdir(cr, direc)
# for each crusher, move if possible.
actions = []
for cr, direc in crushers.items():
newcr = cr + direc
if newcr in walls or newcr in crushers:
continue
crushers[newcr] = crushers.pop(cr)
action = ' movesto '
# if crusher is at a rabbit location, remove rabbit.
if newcr in rabbits:
rabbits.discard(newcr)
action = ' crushes '
actions.append(point2text(cr)+action+point2text(newcr))
# output turnsleft, crusher actions, and rabbit locations to program.
send(process, 'turnsleft %u' % turnsleft)
send(process, 'crusher ' + '; '.join(actions))
rabbitlocs = [point2text(r) for r in rabbits]
send(process, ' '.join(['rabbits'] + rabbitlocs))
# read rabbit move requests from program.
start = time.time()
inline = read(process)
if time.time() - start > 0.5:
print 'Move timeout'
break
# if a rabbit not exist or move not possible, no action.
# if rabbit hits a crusher, rabbit is destroyed.
# if rabbit is in exit teleporter, rabbit is removed and score increased.
# if two rabbits collide, they are both destroyed.
newrabbits = set()
for p1,p2 in re.findall(r'(\d+,\d+)\s+to\s+(\d+,\d+)', inline):
p1, p2 = map(text2point, [p1,p2])
if p1 in rabbits and p2 not in walls:
if p2-p1 in ORTH:
rabbits.discard(p1)
if p2 in crushers:
pass # wabbit squished
elif p2 in exits:
score += 1 # rabbit saved
elif p2 in newrabbits:
newrabbits.discard(p2) # moving rabbit collision
else:
newrabbits.add(p2)
# plot each new location of rabbits.
for rabbit in newrabbits:
if rabbit in rabbits:
rabbits.discard(rabbit) # still rabbit collision
else:
rabbits.add(rabbit)
turnsleft -= 1
turn += 1
process.terminate()
return score
mapfile = sys.argv[1]
turns = int(sys.argv[2])
argseed = int(sys.argv[3])
runs = int(sys.argv[4])
program = sys.argv[5:]
errorlog = open('error.log', 'wt')
runlog = open('run.log', 'wt')
grid, walls, crusherstarts, rabbitstarts, exits = features(mapfile)
rabbits = set()
crushers = dict()
if 'anim' not in os.listdir('.'):
os.mkdir('anim')
for fname in os.listdir('anim'):
os.remove(os.path.join('anim', fname))
total = 0
print 'Running:', ' '.join(sys.argv)
print >> runlog, 'Running:', ' '.join(sys.argv)
fmt = '%10s %20s %10s'
print fmt % ('Run', 'Seed', 'Score')
for n in range(runs):
nseed = argseed if argseed else randint(1,1000)
score = run(n, nseed)
total += score
print fmt % (n+1, nseed, score)
print 'Total Score:', total
print >> runlog, 'Total Score:', total
컨트롤러는 디렉토리에 런의 텍스트 로그 run.log
와 일련의 이미지를 anim
만듭니다. Python 설치에서 PIL 이미지 라이브러리를 찾을 수 없으면 (베개로 다운로드) 이미지가 생성되지 않습니다. ImageMagick으로 이미지 시리즈에 애니메이션을 적용했습니다. 예 :
convert -delay 100 -loop 0 anim/run01* run1anim.gif
답변이 포함 된 재미있는 애니메이션이나 이미지를 게시 할 수 있습니다.
컨트롤러 프로그램의 처음 몇 줄을 설정 GRIDLOG
= False
및 / 또는 설정하여 이러한 기능을 끄고 컨트롤러 속도를 높일 수 있습니다 MKIMAGE = False
.
제안 된 파이썬 프레임 워크
시작을 돕기 위해 다음은 Python의 프레임 워크입니다. 첫 번째 단계는 맵 파일을 읽고 엑시트의 경로를 찾는 것입니다. 매 차례마다 분쇄기의 위치를 저장하는 코드와 토끼를 움직일 위치를 결정하는 코드가 있어야합니다. 가장 간단한 전략은 분쇄기를 무시하고 출구로 토끼를 옮기는 것입니다. 일부 토끼는 통과 할 수 있습니다.
import sys, re
from random import *
mapfile = sys.argv[1]
argseed = int(sys.argv[2])
seed(argseed)
grid = [line.strip() for line in open(mapfile, 'rt')]
#
# Process grid to find teleporters and paths to get there
#
while 1:
msg = sys.stdin.readline()
if msg.startswith('turnsleft'):
turnsleft = int(msg.split()[1])
elif msg.startswith('crusher'):
actions = re.findall(r'(\d+),(\d+) (movesto|crushes) (\d+),(\d+)', msg)
#
# Store crusher locations and movement so we can avoid them
#
elif msg.startswith('rabbits'):
moves = []
places = re.findall(r'(\d+),(\d+)', msg)
for rabbit in [map(int, xy) for xy in places]:
#
# Compute the best move for this rabbit
newpos = nextmoveforrabbit(rabbit)
#
moves.append('%u,%u to %u,%u' % tuple(rabbit + newpos))
print 'move ' + '; '.join(moves)
sys.stdout.flush()