ช่วยให้หุ่นยนต์ของเราไปถึงเครื่องเคลื่อนย้ายมวลสาร


17

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()

อนุญาตให้จำลองคอนโทรลเลอร์ด้วย RNG เดียวกันหรือไม่ สิ่งนี้จะทำให้เครื่องบีบอัดถูกกำหนดได้อย่างมีประสิทธิภาพช่วยให้คุณทำนายพฤติกรรมของพวกเขาและหลีกเลี่ยงพวกมันได้ นรกคุณอาจจะสร้าง 'เหยื่อกระต่าย' หนึ่งหรือสองสามตัวเพื่อให้พวก
หัวขโมย

ไม่คุณไม่สามารถจำลอง RNG (หรือบันทึกเป็นเมล็ดพันธุ์เฉพาะ) เครื่องบีบอัดถูกออกแบบมาให้ไม่สามารถกำหนดค่าได้ดังนั้นนี่เป็นความท้าทายด้านรหัสเพื่อสร้างกลยุทธ์ที่น่าจะหลีกเลี่ยงได้ ความคิด 'เหยื่อกระต่าย' ก็โอเคอย่างแน่นอน ฉันคาดว่าจะมีกลยุทธ์บางอย่างที่เกี่ยวข้องกับกระต่ายบูชายัญ
Logic Knight

หากเมล็ดเป็น 0 แต่ละการรันจะไม่ใช้เมล็ดสุ่ม
TheNumberOne

ใช่. หากเมล็ดควบคุมเป็นศูนย์มันจะใช้ (และออกโปรแกรมทดสอบ) เมล็ดสุ่มสำหรับแต่ละการรัน เมล็ดนี้ถูกรายงานด้วยคะแนนการรัน การป้อนเมล็ดพันธุ์นี้กลับเข้าไปในคอนโทรลเลอร์ควรทำให้การตรวจสอบการทำงาน (และคะแนน) นั้นถูกต้อง มันซับซ้อนเล็กน้อย แต่เป็นวิธีที่ดีที่สุดที่ฉันสามารถคิดเพื่อเปิดใช้งานการจำลองแบบ (กำหนดค่า) และอนุญาตให้มีการสุ่มในตัวควบคุมและทดสอบโปรแกรมพฤติกรรม
Logic Knight

คำตอบ:


2

Crazy, Python 45

ฉันทำงาน 25 ครั้งด้วยการสุ่มเมล็ดคอมพิวเตอร์ของฉันไม่เร็วพอที่จะไป 1,000 (ถ้ามีคนต้องการแก้ไขคะแนน) โปรแกรมแรกในหลามมันเป็นความเจ็บปวดที่จะแก้ปัญหาให้ฉัน ฉันก็ไม่รู้เหมือนกันว่าฉันใช้มันได้ดี

มันใช้อัลกอริทึมที่กว้างจากทางออกหนึ่งโดยคำนึงถึงเครื่องบีบอัดที่เหลือโดยไม่ต้อง ฉันหมดเวลามากดังนั้นฉันจึงไม่ได้ไปหาอะไรที่ซับซ้อนกว่านี้อีกแล้ว กระต่ายก็บ้าไปเหมือนกันถ้ามีผู้บดขยี้อยู่ด้วยหวังว่าจะทำให้มันไปในทางที่ผิด

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')]
width = len(grid[0])
height = len(grid)

starts = set([])
end = ()
walkables = set([])
crushers = set([])
#
# Process grid to find teleporters and paths to get there
#
for a in range(height):
    for b in range(width):
        if grid[a][b] == 'e':
            end = (b,a)
            walkables.add((b,a))
        elif grid[a][b] == 's':
            starts.add((b,a))
            walkables.add((b,a))
        elif grid[a][b] == 'c':
            crushers.add((b,a))
            walkables.add((b,a))
        elif grid[a][b] == ' ':
            walkables.add((b,a))

toSearch = [ (end, 0) ]
inSearch = [ end ]
visited = set([])
gradient = [[0]*height for x in range(width)]
while len(toSearch) > 0 :
    current = toSearch.pop(0)
    (row, col) = current[0]
    length = current[1]
    visited.add(current[0])
    neighbors = {(row+1,col),(row-1,col),(row,col+1),(row,col-1)}
    neighborSpaces = walkables & neighbors
    unvisited = neighborSpaces - visited
    for node in unvisited:
        if not node in inSearch:
            gradient[node[0]][node[1]]=[current[0][0],current[0][1]]
            inSearch.append(node)
            toSearch.append((node, length+1))
    toSearch.sort(key=lambda node: node[1])

while 1:
    msg = sys.stdin.readline()

    if msg.startswith('turnsleft'):
        turnsleft = int(msg.split()[1])

    elif msg.startswith('crusher'):
        # Update crushers
        actions = re.findall(r'(\d+),(\d+) (movesto|crushes) (\d+),(\d+)', msg)
        for one_action in actions:
            crushers.discard((int(one_action[0]),int(one_action[1])))
            crushers.add((int(one_action[3]),int(one_action[4])))

    elif msg.startswith('rabbits'):
        toSearch = [ (end, 0) ]
        inSearch = [ end ]
        visited = set([])
        gradient2 = [[0]*height for x in range(width)]
        while len(toSearch) > 0 :
            current = toSearch.pop(0)
            (row, col) = current[0]
            length = current[1]
            visited.add(current[0])
            neighbors = {(row+1,col),(row-1,col),(row,col+1),(row,col-1)}
            neighborSpaces = (walkables - crushers) & neighbors
            unvisited = neighborSpaces - visited
            for node in unvisited:
                if not node in inSearch:
                    gradient2[node[0]][node[1]]=[current[0][0],current[0][1]]
                    inSearch.append(node)
                    toSearch.append((node, length+1))
            toSearch.sort(key=lambda node: node[1])
        moves = []
        places = re.findall(r'(\d+),(\d+)', msg)
        for rabbit in [map(int, xy) for xy in places]:
            # If any crushers insight, go crazy to lose him
            directions = [(1,0),(-1,0),(0,1),(0,-1)]
            crazy = False
            newpos = 0
            for direction in directions:
                (row, col) = rabbit
                sight = 0
                while len({(row,col)} & walkables)>0 and sight<5 and crazy == False:
                    sight+=1
                    if (row,col) in crushers:
                        directions.remove(direction)
                        crazy = True
                    (row,col) = (row+direction[0],col+direction[1])
            for direction in directions:
                if not (rabbit[0]+direction[0],rabbit[1]+direction[1]) in walkables:
                    directions.remove(direction)
            if len(directions)==0:
                directions = [(1,0),(-1,0),(0,1),(0,-1)]
            direction = choice(directions)
            newpos = [rabbit[0]+direction[0],rabbit[1]+direction[1]]
            # Else use gradients
            if crazy == False:
                newpos = gradient2[rabbit[0]][rabbit[1]]
                if newpos == 0:
                    newpos = gradient[rabbit[0]][rabbit[1]]
            moves.append('%u,%u to %u,%u' % tuple(rabbit + newpos))
        print 'move ' + '; '.join(moves)
        sys.stdout.flush()

แอนิเมชันสำหรับการวิ่งเฉลี่ย


ฉันคิดว่าคุณอาจมีข้อผิดพลาดในการเยื้องของคุณ ช่องว่างนำหน้ามีความสำคัญใน Python เช่น: walkables.add((b,a))บรรทัด
Logic Knight

ควรทำงานได้ดีในขณะนี้
กด

1

Bunny, Java, 26.385

ฉันเฉลี่ยคะแนนของการวิ่ง 1 ถึง 1,000 และคูณด้วย 5 เพื่อรับคะแนนของฉัน ฉันค่อนข้างมั่นใจว่านี่เทียบเท่ากับคะแนนเฉลี่ยของเกมที่เป็นไปได้ทั้งหมดที่มีตัวเลือกมาตรฐาน

คะแนน

import java.awt.Point;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.util.*;
import java.util.stream.Collectors;

public class Main {

    private static final char WALL = '#';
    private static final char CRUSHER = 'c';
    private static final char ESCAPE = 'e';
    private static final char HUTCH = 's';

    private int height;
    private int width;

    private char[][] map;
    private List<Point> escapes = new ArrayList<>();
    private List<Point> crushers = new ArrayList<>();
    private List<Point> rabbits = new ArrayList<>();
    private List<Point> hutches = new ArrayList<>();

    private BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
    private PrintStream out = System.out;
    private int[][] distances;

    public Main(String[] args) throws Exception {
        loadMap(args[0]);
    }

    private void loadMap(String mapFileName) throws Exception {
        char[][] preMap = new BufferedReader(new FileReader(mapFileName))
                .lines()
                .map(String::toCharArray)
                .toArray(char[][]::new);

        width = preMap[0].length;
        height = preMap.length;

        map = new char[width][height];    //tranpose

        for (int x = 0; x < width; x++){
            for (int y = 0; y < height; y++){
                map[x][y] = preMap[y][x];
            }
        }

        processMap();

        distances = dijkstra();

    }

    private void processMap() {
        for (int x = 0; x < width; x++){
            for (int y = 0; y < height; y++){
                char c = map[x][y];
                Point p = new Point(x, y);
                if (c == CRUSHER){
                    crushers.add(p);
                }
                if (c == ESCAPE){
                    escapes.add(p);
                }
                if (c == HUTCH){
                    hutches.add(p);
                }
            }
        }
    }

    public static void main(String[] args) throws Exception {
        new Main(args).run();
    }

    private void run() throws Exception{
        while (true) {
            in.readLine();
            readCrushers();
            readRabbits();
            makeDecision();
        }
    }

    private void makeDecision() {
        Map<Point, Point> moves = new HashMap<>();

        for (Point rabbit : rabbits){
            Point bestDirection = null;
            for (Point p : pointsAroundInclusive(rabbit)){
                if (
                        (bestDirection == null ||
                                distances[p.x][p.y] < distances[bestDirection.x][bestDirection.y]
                        ) && !moves.entrySet().contains(p)){
                    bestDirection = p;
                }
            }
            if (bestDirection != null) {
                moves.put(rabbit, bestDirection);
            }
        }

        out.println("move" +
                moves.entrySet().stream().map(a -> {
                    Point l0 = a.getKey();
                    Point l1 = a.getValue();
                    return " " + l0.x + "," + l0.y + " to " + l1.x + "," + l1.y;
                }).collect(Collectors.joining(";")));
    }

    private List<Point> pointsAroundInclusive(Point point) {
        List<Point> result = pointsAroundExclusive(point);
        result.add(point);
        return result;
    }

    private int[][] dijkstra() {
        Queue<Point> queue = new LinkedList<>();
        Set<Point> scanned = new HashSet<>();
        queue.addAll(escapes);
        scanned.addAll(escapes);

        int[][] distances = new int[width][height];

        while (!queue.isEmpty()) {
            Point next = queue.remove();
            int distance = distances[next.x][next.y];

            pointsAroundExclusive(next).stream()
                    .filter(p -> !scanned.contains(p))
                    .forEach(p -> {
                        distances[p.x][p.y] = distance + 1;
                        scanned.add(p);
                        queue.add(p);
                    });
        }

        return distances;
    }

    private List<Point> pointsAroundExclusive(Point p) {
        Point[] around = new Point[]{
                new Point(p.x - 1, p.y),
                new Point(p.x + 1, p.y),
                new Point(p.x, p.y - 1),
                new Point(p.x, p.y + 1)
        };

        List<Point> result = new ArrayList<>(Arrays.asList(around));
        result.removeIf(a -> {
            if (a.x < 0 || a.x >= width){
                return true;
            }
            if (a.y < 0 || a.y >= height){
                return true;
            }
            char c = map[a.x][a.y];
            return c == WALL;
        });

        return result;
    }

    private void readRabbits() throws Exception {
        String[] locations = in.readLine().substring("rabbits".length()).trim().split("\\s");
        rabbits.clear();

        for (String location : locations){

            if (location.equals("")){
                continue;
            }

            String[] decomposed = location.split(",");

            int x = Integer.parseInt(decomposed[0]);
            int y = Integer.parseInt(decomposed[1]);

            rabbits.add(new Point(x, y));
        }

    }

    private void readCrushers() throws Exception {
        String[] locations = in.readLine().substring("crusher".length()).trim().split("(; )?\\d+,\\d+ (movesto|crushes) ");
        crushers.clear();

        for (String location : locations){

            if (location.equals("")){
                continue;
            }

            String[] decomposed = location.split(",");

            int x = Integer.parseInt(decomposed[0]);
            int y = Integer.parseInt(decomposed[1]);

            crushers.add(new Point(x, y));
        }
    }

}

การทดสอบที่ดีที่สุดมีเมล็ด 1,000 นี่คือ GIF ของมัน:

ป้อนคำอธิบายรูปภาพที่นี่

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.