UPDATE: เพิ่มเฟรมเวิร์ก Python เพื่อเริ่มต้น
สถานีอวกาศถูกแซงโดยบ็อตคั้น คุณต้องนำบอทเทคราคาแพงและเปราะบางของเราจำนวนมากที่เรียกว่า "กระต่าย" ไปยังเทเลพอร์เตอร์ทางออกก่อนที่สถานีจะทำลายตัวเอง แต่บอทบ็อตกำลังลาดตระเว ณ ทางเดิน
โปรแกรมของคุณจะได้รับแผนที่ ASCII และทุกเทิร์นจะได้รับการบอกตำแหน่งของ crusher-bots และกระต่ายปัจจุบันของคุณ โปรแกรมของคุณควรย้ายกระต่ายของคุณไปที่เครื่องเคลื่อนย้ายมวลสารทางออกในขณะที่หลีกเลี่ยงการบดขยี้บ็อต
การกระทำ
เรียกใช้คอนโทรลเลอร์ Python 2 ด้วย:
python controller.py <mapfile> <turns> <seed> <runs> <prog>...
<prog> can be <interpreter> <yourprog> or similar.
seed เป็นจำนวนเต็มขนาดเล็กที่ใช้สำหรับเครื่องบดและโปรแกรม PRNG ของคุณเพื่อให้สามารถทำงานซ้ำได้ โปรแกรมของคุณควรทำงานอย่างสม่ำเสมอโดยไม่คำนึงถึงเมล็ดที่ใช้จริง หากเมล็ดเป็นศูนย์ตัวควบคุมจะใช้เมล็ดพันธุ์แบบสุ่มสำหรับการวิ่งแต่ละครั้ง
ตัวควบคุมจะเรียกใช้โปรแกรมของคุณด้วยชื่อของไฟล์ข้อความแผนที่และ seed เป็นอาร์กิวเมนต์ เช่น:
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>; ...
UPDATE: โปรแกรมของคุณจะมีเวลา 2 วินาทีในการเริ่มต้นก่อนที่บรรทัดแรกจะถูกส่งโดยผู้ควบคุม
หากโปรแกรมของคุณใช้เวลานานกว่า 0.5 วินาทีในการตอบสนองต่อการเคลื่อนไหวหลังจากป้อนตำแหน่งตำแหน่งคอนโทรลเลอร์คอนโทรลเลอร์แล้วคอนโทรลเลอร์จะออก
หากไม่มีกระต่ายอยู่ในกริดบรรทัดกระต่ายจะไม่มีค่าและโปรแกรมของคุณควรแสดงผลสตริง "ย้าย" ที่ว่างเปล่า
อย่าลืมล้างข้อมูลเอาต์พุตโปรแกรมของคุณในแต่ละเทิร์นไม่เช่นนั้นคอนโทรลเลอร์อาจหยุดทำงาน
ตัวอย่าง
อินพุต prog:
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
prog เอาท์พุท:
move 14,3 to 14,4; 14,2 to 14,3; 6,4 to 7,4
ตรรกะของตัวควบคุม
ตรรกะสำหรับการเปิดแต่ละครั้ง:
- หากเลี้ยวซ้ายเป็นศูนย์ให้พิมพ์คะแนนและออก
- สำหรับเซลล์เริ่มต้นที่ว่างเปล่าแต่ละตัวให้เพิ่มกระต่ายถ้าไม่มีสิ่งบดอยู่ในสายตา
- สำหรับแต่ละ crusher เลือกทิศทางการเคลื่อนที่ (ดูด้านล่าง)
- สำหรับแต่ละ crusher ย้ายถ้าเป็นไปได้
- หากเครื่องบดอยู่ที่ตำแหน่งกระต่ายให้ถอดกระต่ายออก
- เอาท์พุท turnleft, การกระทำบดและสถานที่กระต่ายในการเขียนโปรแกรม
- อ่านคำร้องขอย้ายกระต่ายจากโปรแกรม
- หากไม่มีกระต่ายหรือเคลื่อนไหวไม่ได้ให้ข้าม
- วางแผนแต่ละตำแหน่งใหม่ของกระต่าย
- ถ้ากระต่ายกระทบกับคั้นกระต่ายจะถูกทำลาย
- หากกระต่ายอยู่ในเครื่องเคลื่อนย้ายมวลสารออกกระต่ายจะถูกลบและเพิ่มคะแนน
- หากกระต่ายชนกันพวกมันจะถูกทำลายทั้งคู่
เครื่องบดแต่ละเครื่องจะมีทิศทางมุ่งหน้า (หนึ่งใน NSEW) ตัวบดตามตรรกะการนำทางแต่ละครั้ง:
- หากกระต่ายหนึ่งตัวหรือมากกว่านั้นปรากฏในทิศทางใดทิศทางหนึ่งของ 4 มุมฉากให้เปลี่ยนทิศทางไปเป็นกระต่ายตัวใดตัวหนึ่งที่อยู่ใกล้ที่สุด โปรดทราบว่าผู้บีบอัดไม่สามารถมองเห็นผ่านเครื่องบดอื่นได้
- มิฉะนั้นจะสุ่มเลือกระหว่างตัวเลือกเปิดไปข้างหน้าซ้ายและขวาถ้าเป็นไปได้
- ถ้ามีสิ่งกีดขวาง (ผนังหรือเครื่องบดอื่น ๆ ) ที่ด้านหน้าซ้ายและขวากำหนดทิศทางไปทางด้านหลัง
จากนั้นสำหรับแต่ละ crusher:
- หากไม่มีสิ่งกีดขวางในทิศทางการเคลื่อนที่ใหม่ให้เคลื่อนย้าย (และอาจเป็นไปได้)
สัญลักษณ์แผนที่
แผนที่เป็นตารางสี่เหลี่ยมของอักขระ 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 framework ที่แนะนำ
เพื่อช่วยในการเริ่มต้นนี่เป็นกรอบใน 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()