PEMBARUAN: Menambahkan kerangka kerja Python untuk memulai.
Stasiun luar angkasa telah diambil alih oleh bot crusher. Anda harus mengarahkan banyak bot teknologi kami yang mahal dan rapuh yang disebut "kelinci" ke teleporter keluar sebelum stasiun hancur sendiri, tetapi bot crusher sedang berpatroli di koridor.
Program Anda diberi peta ASCII, dan setiap belokan diberi tahu di mana bot penghancur berada dan kelinci Anda saat ini. Program Anda kemudian harus memindahkan kelinci Anda ke arah teleporter keluar sambil menghindari bot crusher.
Eksekusi
Jalankan pengontrol Python 2 dengan:
python controller.py <mapfile> <turns> <seed> <runs> <prog>...
<prog> can be <interpreter> <yourprog> or similar.
Benih adalah bilangan bulat kecil yang digunakan untuk crusher dan program Anda PRNG sehingga menjalankan berulang. Program Anda harus melakukan secara konsisten terlepas dari benih yang sebenarnya digunakan. Jika seed bernilai nol, controller akan menggunakan seed acak untuk setiap run.
Pengontrol akan menjalankan program Anda dengan nama file teks peta dan seed sebagai argumen. Misalnya:
perl wandomwabbits.pl large.map 322
Jika program Anda menggunakan PRNG, Anda harus menginisialisasi dengan seed yang diberikan. Pengontrol kemudian mengirimkan pembaruan program Anda melalui STDIN dan membaca gerakan kelinci Anda melalui STDOUT.
Setiap belokan controller akan menghasilkan 3 baris:
turnsleft <INT>
crusher <x,y> <movesto|crushes> <x,y>; ...
rabbits <x,y> <x,y> ...
kemudian tunggu program mengeluarkan satu baris:
move <x,y> to <x,y>; ...
UPDATE: Program Anda akan memiliki 2 detik untuk menginisialisasi sebelum baris pertama dikirim oleh controller.
Jika program Anda membutuhkan waktu lebih dari 0,5 detik untuk merespons dengan gerakan setelah input lokasi kelinci controller, controller akan keluar.
Jika tidak ada kelinci di kisi, garis kelinci tidak akan memiliki nilai, dan program Anda harus menampilkan garis string "pindahkan" yang telanjang.
Ingatlah untuk menyiram aliran output program Anda setiap belokan atau pengontrol mungkin menggantung.
Contoh
input 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
keluaran prog:
move 14,3 to 14,4; 14,2 to 14,3; 6,4 to 7,4
Logika pengontrol
Logika untuk setiap belokan:
- jika belok kiri adalah nol, cetak skor dan keluar.
- untuk setiap sel awal yang kosong, tambahkan kelinci jika tidak ada penghancur yang terlihat.
- untuk setiap crusher, tentukan arah gerakan (lihat di bawah).
- untuk setiap crusher, bergerak jika memungkinkan.
- Jika penghancur berada di lokasi kelinci, lepaskan kelinci.
- Output putaran, tindakan crusher, dan lokasi kelinci ke program.
- baca permintaan pindah kelinci dari program.
- jika kelinci tidak ada atau bergerak tidak mungkin, lewati.
- plot setiap lokasi baru kelinci.
- jika kelinci mengenai crusher, kelinci dihancurkan.
- jika kelinci keluar dari teleporter, kelinci dilepas dan nilainya meningkat.
- jika kelinci bertabrakan, mereka berdua dihancurkan.
Setiap crusher selalu memiliki arah tajuk (salah satu NSEW). Crusher mengikuti logika navigasi ini setiap belokan:
- Jika satu atau lebih kelinci terlihat di salah satu dari 4 arah ortogonal, ubah arah ke salah satu kelinci terdekat. Perhatikan bahwa crusher tidak dapat melihat melewati crusher lain.
- lain pilih secara acak antara opsi buka ke depan, kiri, kanan jika memungkinkan.
- lain jika rintangan (dinding atau penghancur lainnya) di depan, kiri, dan kanan, tetapkan arah ke belakang.
Kemudian untuk setiap crusher:
- Jika tidak ada penghalang dalam arah penghancur baru, bergerak (dan mungkin hancurkan).
Simbol peta
Peta adalah kisi-kisi persegi panjang karakter ASCII. Peta tersebut terdiri dari dinding
#
, ruang koridor , posisi awal kelinci
s
, keluar dari teleporter e
, dan lokasi mulai penghancur c
. Pojok kiri atas adalah lokasi (0,0).
Peta kecil
###################
# c #
# # ######## # # ##
# ###s # ####e#
# # # # ## ## #
### # ### # ## # #
# ## #
###################
Tes peta
#################################################################
#s ############################ s#
## ## ### ############ # ####### ##### ####### ###
## ## ### # # ####### ########## # # #### ###### ###
## ## ### # ############ ####### ########## ##### ####### ###
## ## ## # ####### ########## # # ##### #### #
## ### #### #### ######## ########## ##### #### ## ###
######### #### ######## ################ ####### #### ###
######### ################# ################ c ####### ###
######### ################## ####### ####### ###########
######### ################## ######## ####### ###########
##### ### c ###### ###################
# #### ### # # # # # # # # # # ###### ############## #
# ####### #### ### #### ##### ## #
# #### ### # # # # # # # # # # ### # ### ######### #
##### ### #### ### ##### ### # ######## ####
############## ### # # # # # # # # # # ####### ## ####### ####
#### #### #### ### ### # # ### ###### ####
## ### # # # # # # # # # # ### ### # ### ##### ####
##### ######## ### # # # ##### # # # # ### ### # ##### #### ####
##### ##### ###### c # ### ### ###### ### ####
## c ######################### ### ##### ####### ### ####
##### # ### ####### ######## ### ##### c ## ## ####
##### # ####### ########## ## ######## # ######## ## ####
######### # ####### ## # ## # # # ##### # ####
### ##### # ### # ############## # ### # ### ## # ####
# ## # ### ### # ############## # ### ##### ##### ## ####
### ## ## # ### # ######## #
#s ## ###################################################e#
#################################################################
Contoh menjalankan peta besar
Skor
Untuk mengevaluasi program Anda, jalankan pengontrol dengan peta uji, 500 putaran, 5 putaran, dan seed of 0. Skor Anda adalah jumlah total kelinci yang berhasil dipindahkan dari stasiun ke tempat yang aman. Dalam hal seri, jawaban dengan suara terbanyak akan menang.
Jawaban Anda harus mencakup judul dengan nama entri, bahasa yang digunakan, dan skor. Di badan jawaban, harap sertakan output skor pengontrol lengkap dengan nomor seed sehingga orang lain dapat mengulangi menjalankan Anda. Sebagai contoh:
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
Anda dapat menggunakan perpustakaan standar dan tersedia secara bebas tetapi celah standar dilarang. Anda tidak boleh mengoptimalkan program Anda untuk seed yang diberikan, hitung giliran, set fitur peta, atau parameter lainnya. Saya berhak mengubah peta, mengubah hitungan, dan mengunggah jika saya menduga ada pelanggaran terhadap aturan ini.
Kode pengontrol
#!/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
Pengontrol membuat log teks dari berjalan di run.log
dan serangkaian gambar di anim
direktori. Jika instalasi Python Anda tidak dapat menemukan pustaka gambar PIL (unduh sebagai Bantal), tidak akan ada gambar yang dihasilkan. Saya telah menjiwai seri gambar dengan ImageMagick. Misalnya:
convert -delay 100 -loop 0 anim/run01* run1anim.gif
Anda dipersilakan untuk mengirim animasi atau gambar yang menarik dengan jawaban Anda.
Anda dapat mematikan fitur-fitur ini dan mempercepat pengontrol dengan mengatur GRIDLOG
= False
dan / atauMKIMAGE = False
di beberapa baris pertama dari program pengontrol.
Kerangka Python yang disarankan
Untuk membantu memulai, berikut adalah kerangka kerja dalam Python. Langkah pertama adalah membaca di file peta dan menemukan jalur ke pintu keluar. Di setiap belokan harus ada beberapa kode untuk menyimpan di mana penghancur berada, dan kode yang memutuskan ke mana harus memindahkan kelinci kita. Strategi paling sederhana untuk memulainya adalah menggerakkan kelinci menuju jalan keluar tanpa menghiraukan penghancur - beberapa kelinci mungkin bisa melewatinya.
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()