ATUALIZAÇÃO: adicionada uma estrutura Python para começar.
A estação espacial foi ultrapassada por bots trituradores. Você deve direcionar tantos dos nossos caras e frágeis robôs tecnológicos chamados "coelhos" para um teleportador de saída antes que a estação se autodestrua, mas os robôs trituradores estão patrulhando os corredores.
Seu programa recebe um mapa ASCII, e a cada turno é informado onde estão os bots do triturador e seus coelhos atuais. Seu programa deve então mover seus coelhos em direção ao teleportador de saída, evitando os bots do triturador.
Execução
Execute o controlador Python 2 com:
python controller.py <mapfile> <turns> <seed> <runs> <prog>...
<prog> can be <interpreter> <yourprog> or similar.
A semente é um pequeno número inteiro usado para o britador e seu programa PRNG, para que as execuções sejam repetíveis. Seu programa deve ter um desempenho consistente, independentemente da semente real usada. Se a semente for zero, o controlador usará uma semente aleatória para cada execução.
O controlador executará seu programa com o nome do arquivo de texto do mapa e será propagado como argumentos. Por exemplo:
perl wandomwabbits.pl large.map 322
Se o seu programa usa um PRNG, você deve inicializá-lo com a semente especificada. O controlador envia as atualizações do seu programa através do STDIN e lê os movimentos do seu coelho através do STDOUT.
Cada vez que o controlador produz 3 linhas:
turnsleft <INT>
crusher <x,y> <movesto|crushes> <x,y>; ...
rabbits <x,y> <x,y> ...
então aguarda o programa gerar uma linha:
move <x,y> to <x,y>; ...
ATUALIZAÇÃO: Seu programa terá 2 segundos para inicializar antes que as primeiras linhas sejam enviadas pelo controlador.
Se o seu programa demorar mais de 0,5 segundos para responder aos movimentos após a entrada da localização do coelho do controlador, o controlador sairá.
Se não houver coelhos na grade, a linha de coelhos não terá valores, e seu programa deve gerar uma linha de seqüência de caracteres "move" simples.
Lembre-se de liberar o fluxo de saída do programa a cada turno ou o controlador pode travar.
Exemplo
entrada 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
saída prog:
move 14,3 to 14,4; 14,2 to 14,3; 6,4 to 7,4
Lógica do controlador
A lógica para cada turno:
- se virar à esquerda é zero, imprima a pontuação e saia.
- para cada célula inicial vazia, adicione um coelho se não houver triturador à vista.
- para cada triturador, decida a direção do movimento (veja abaixo).
- para cada triturador, mova se possível.
- se o triturador estiver em um local de coelho, remova o coelho.
- virada de saída, ações do triturador e locais dos coelhos para programar.
- leia solicitações de movimentação de coelhos do programa.
- se um coelho não existir ou não for possível, pule.
- traçar cada nova localização de coelhos.
- se o coelho atinge um triturador, o coelho é destruído.
- se o coelho estiver no teleporter de saída, o coelho será removido e a pontuação aumentada.
- se os coelhos colidem, ambos são destruídos.
Cada triturador sempre tem uma direção de direção (uma das NSEW). Um triturador segue essa lógica de navegação a cada turno:
- Se um ou mais coelhos estiverem visíveis em qualquer uma das 4 direções ortogonais, mude a direção para um dos coelhos mais próximos. Observe que os trituradores não podem ver além de outro triturador.
- caso contrário, escolha aleatoriamente entre as opções de avanço aberto, esquerda e direita, se possível.
- caso contrário, se obstáculos (parede ou outro triturador) na frente, esquerda e direita, definir a direção para trás.
Então, para cada triturador:
- Se não houver obstáculos na nova direção do britador, mova-se (e possivelmente esmague).
Os símbolos do mapa
O mapa é uma grade retangular de caracteres ASCII. O mapa é composto de paredes
#
, espaços de corredor , posições iniciais de coelho
s
, teleportadores de saída e
e locais de partida do triturador c
. O canto superior esquerdo é a localização (0,0).
Mapa pequeno
###################
# c #
# # ######## # # ##
# ###s # ####e#
# # # # ## ## #
### # ### # ## # #
# ## #
###################
Mapa de teste
#################################################################
#s ############################ s#
## ## ### ############ # ####### ##### ####### ###
## ## ### # # ####### ########## # # #### ###### ###
## ## ### # ############ ####### ########## ##### ####### ###
## ## ## # ####### ########## # # ##### #### #
## ### #### #### ######## ########## ##### #### ## ###
######### #### ######## ################ ####### #### ###
######### ################# ################ c ####### ###
######### ################## ####### ####### ###########
######### ################## ######## ####### ###########
##### ### c ###### ###################
# #### ### # # # # # # # # # # ###### ############## #
# ####### #### ### #### ##### ## #
# #### ### # # # # # # # # # # ### # ### ######### #
##### ### #### ### ##### ### # ######## ####
############## ### # # # # # # # # # # ####### ## ####### ####
#### #### #### ### ### # # ### ###### ####
## ### # # # # # # # # # # ### ### # ### ##### ####
##### ######## ### # # # ##### # # # # ### ### # ##### #### ####
##### ##### ###### c # ### ### ###### ### ####
## c ######################### ### ##### ####### ### ####
##### # ### ####### ######## ### ##### c ## ## ####
##### # ####### ########## ## ######## # ######## ## ####
######### # ####### ## # ## # # # ##### # ####
### ##### # ### # ############## # ### # ### ## # ####
# ## # ### ### # ############## # ### ##### ##### ## ####
### ## ## # ### # ######## #
#s ## ###################################################e#
#################################################################
Exemplo de execução de mapa grande
Ponto
Para avaliar seu programa, execute o controlador com o mapa de teste, 500 voltas, 5 execuções e semente de 0. Sua pontuação é o número total de coelhos teleportados com sucesso da estação para a segurança. Em caso de empate, a resposta com mais votos vencerá.
Sua resposta deve incluir um título com o nome da entrada, o idioma usado e a pontuação. No corpo da resposta, inclua a saída da pontuação do controlador completa com números de sementes, para que outros possam repetir suas execuções. Por exemplo:
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
Você pode usar bibliotecas padrão e disponíveis gratuitamente, mas as brechas padrão são proibidas. Você não deve otimizar seu programa para uma determinada semente, contagem de turnos, conjunto de recursos do mapa ou outros parâmetros. Reservo-me o direito de alterar o mapa, contagem de turnos e propagação se suspeitar de uma violação desta regra.
Código do controlador
#!/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
O controlador faz um log de texto das execuções run.log
e uma série de imagens no anim
diretório. Se sua instalação do Python não conseguir encontrar a biblioteca de imagens PIL (faça o download como Pillow), nenhuma imagem será gerada. Venho animando a série de imagens com o ImageMagick. Por exemplo:
convert -delay 100 -loop 0 anim/run01* run1anim.gif
Você pode publicar animações ou imagens interessantes com sua resposta.
Você pode desativar esses recursos e acelerar o controlador configurando GRIDLOG
= False
e / ou MKIMAGE = False
nas primeiras linhas do programa do controlador.
Estrutura Python sugerida
Para ajudar a começar, aqui está uma estrutura em Python. O primeiro passo é ler o arquivo de mapa e encontrar caminhos para as saídas. Em cada turno, deve haver algum código para armazenar onde estão os trituradores e um código que decide para onde mover nossos coelhos. A estratégia mais simples para começar é mover os coelhos em direção a uma saída, ignorando os trituradores - alguns coelhos podem passar.
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()
fonte
Respostas:
Louco, Python 45
Eu fiz 25 corridas com semente aleatória, meu computador não é rápido o suficiente para ir para 1000 (se alguém quiser corrigir a pontuação) Primeiro programa em python, foi uma dor para depurar para mim. Também não sei se usei bem.
Ele usa um algoritmo de amplitude inicial desde a saída, um levando em consideração os trituradores e o outro sem. Eu tive muito tempo limite, então não optei por algo mais complexo (remoção de um britador, etc.). Os coelhos também ficam loucos se houver um triturador próximo, na esperança de fazê-lo seguir um caminho errado.
fonte
walkables.add((b,a))
linhas.Coelho, Java, 26.385
Eu calculei a média das pontuações das corridas 1 a 1000 e multipliquei por 5 para obter minha pontuação. Tenho certeza de que isso é equivalente à pontuação média em todos os jogos possíveis com as opções padrão.
Pontuações
A melhor execução testada possui a semente 1000. Aqui está um GIF:
fonte