Пещерные рейнджеры - Изучение тьмы

9

Ваш приятель-геолог чуть не пнул дверь вашего кабинета, когда он ворвался, широко раскрыв глаза от волнения, и попросил вас пойти с ним на место, которое он только что обнаружил. По дороге он объясняет, что он думает, что он буквально просто ударил золото. Единственная проблема в том, что он похоронен глубоко под землей в пещере с очень неустойчивой крышей. Это слишком опасно, чтобы заплакать, поэтому он хочет, чтобы вы запрограммировали одного из его роботов, исследующих пещеру, собирать столько золота, сколько он может, прежде чем он соберет его обратно. Он также упоминает, что он исследовал пещеру и обнаружил дикую природу, которая может быть вредна для роботов, а также что он уронил там какое-то оборудование, которое еще можно было бы использовать. Каждый робот оснащен двумя руками и рядом датчиков. Когда вы прибываете на место происшествия, он говорит, что планирует набрать больше кодеров,

Теперь до мелочей. Датчики передают информацию в вашу программу в виде символов ASCII. Вот список того, что означает каждый персонаж, и описания всего, что бот может встретить в пещере:

Code    Name/Description

Y       Your bot
        You do things

@       Other bots
        They do other things

-       Ground
        This doesn't do things

C       Centipede
        These will bite you and leave a poison effect
        The bite will cost 1 health
        The poison effect will last for 3 turns, costing 2 health each turn

B       Bats
        If bats end up in the same space you are, your bot runs in a random direction during its turn rather than what you told it to do

L       Lion (because reasons)
        Lions deal heavy damage, 10 health, each time they attack

F       Food
        Eating this will give you 5 health
        Can only be used once

W       Water
        Drinking this will cure poison effects early
        Can only be used once

R       Revealer
        This will increase the range of your visibility to an 11x11 grid
        The extra range will only be 75% correct, but the original range won't be effected

K       Knife
        You do twice as much damage to other bots if you have a knife

G       Gold
        The whole reason you're doing this in the first place

N       Nurse Nina
        She mend you good
        Restores your health by 10 while you occupy the same space as her

}       Boulder
        You can't walk over boulders, and neither can anything else

P       Pit
        If you fall in a pit, you will be stuck for 3 turns

Размер пещеры увеличивается в зависимости от количества участвующих ботов. Он начинается с 30х30, и он получает дополнительные 10х10 за каждого бота. Таким образом, 2 бота будут исследовать пещеру 50x50.

Боты начинают с 20 здоровья, но у них нет максимального ограничения здоровья.

Входные данные:

Вы получите вход через STDIN в следующем формате:

20,5,10,1,0,True,False    <-health, number gold pieces, number of turns your bot has lasted, number of until the poison wears off, number of turns until you are no longer stuck in a pit, if you have a revealer, if you have a knife
-----
-G}--
--Y-L
-C---
---B-

Первая строка содержит информацию о вашем боте, а остальные - это сетка, которую может видеть ваш бот. Если ваш бот против одной из 4 стен пещеры, вы получите сетку, которая выглядит примерно так (в случае, если вы находитесь на западе):

---
}--
Y--
---
---

Пещера не оборачивается, как и ваше зрение. Стены пещеры не обозначены, единственный признак того, что ваш бот получает, что он приближается к стене, - это уменьшение его вида. С Revealer вы можете получить что-то вроде этого:

--------C--
LW--------B
---K-N-----
--------BR-
-F---------
--B--Y---@N
-W@---F----
------K-F--
----@-}----
R@---G}--}-
--------G-R

Вывод:

Вы получаете два хода за ход, которые выводите в следующем формате:

MNNANW    <- Moves are groups of 3 characters representing the action and the direction

Возможные действия:

M    Move - Move your bot in the specified direction
A    Attack - Attack the square in the specified direction
H    Hold - Do nothing

Возможные направления:

NN - North (up)
NE - Northeast (up-right)
EE - East (right)
SE - Southeast (down-right)
SS - South
SW - Southwest
WW - West
NW - Northwest

Ходы применяются слева направо.

Повороты:

Поворачивает прогресс следующим образом:

  1. Ядовитые эффекты применяются к любому игроку, который был отравлен

  2. Не-боты двигаются и атакуют

    2а. Львы, Многоножки и Летучие мыши двигаются случайно

    2b. Львы и Многоножки будут атаковать все, что непосредственно прилегает к нему (в том числе по диагонали)

    2с. Эффект летучей мыши будет применяться только к боту, если он находится в том же пространстве, что и летучая мышь

    2d. Медсестра Нина останется в локации на 3 хода, а затем прыгнет в случайную локацию.

  3. Боты движутся

    3a. Если ваш бот выдает неверный вывод, он не будет двигаться

    3b. Ваш бот будет пытаться подобраться как можно ближе к месту, обозначенному выводом (см. Примечание внизу для более подробной информации)

    3в. Одна атака на Многоножку, Льва или Летучую мышь убьет его

    3d. Атака другого бота без ножа нанесет 5 урона, а 10 ножом

Правила:

  1. Придерживайтесь общих языков, которые могут быть запущены в OS X или Linux.

  2. При желании вы можете записать в файл данные объемом до 1 КБ, но не более

Подсчет очков:

Боты будут только в пещере, пока не останется только один, или пока не пройдет 50 ходов, в зависимости от того, что наступит раньше. Ваш бот будет оцениваться по сумме количества золотых монет, которые он собрал, и сколько ходов он продержался.

Код контроллера можно загрузить здесь для тестирования (создайте папку с именем «боты» в том же каталоге, в который вы его загрузили, и поместите своего бота в «боты»). Вам потребуется NumPy для его запуска. Не стесняйтесь копаться в этом, но вам придется извинить беспорядок ...

Вот код для случайного бота:

#!/usr/bin/python
import random as r

a = ['M','A','H']
d = ['NN','NE','EE','SE','SS','SW','WW','NW']

print(a[r.randint(0,2)]+d[r.randint(0,7)]+a[r.randint(0,2)]+d[r.randint(0,7)])

**** Ваш бот всегда будет двигаться в общем направлении, указанном вашим выходом, но если ему препятствует камень или стена, точное направление зависит от обстоятельств. Например, если ваш бот против стены, вот так:

---
}--
Y--
---
---

и ваш вывод

MNWMSW

Ваш бот переместится на одну позицию вниз. Он не мог двигаться на север или запад, так что это движение не имело никакого эффекта. Он мог двигаться на юг (и сделал), но не мог двигаться на запад. Однако, если ваш бот попытается переместиться на северо-восток, он пойдет прямо в это пространство (диагональное движение - диагональное, а не процедурное)

Leaderboard

Это средние оценки 4 игр.

The bot of Survival:    54.75
Coward:                 52.25
Pufferfish:             50.00
Randombot:              50.00
Indiana Jones:          47.50
TheoremBot:             46.50
Бобовый стебель
источник
Сколько здоровья у каждого бота? А как выглядит край пещеры?
Конор О'Брайен,
Они начинаются с 20 и могут собирать столько, сколько хотят. Добавил эту информацию выше
The Beanstalk
Края пещеры не отмечены, ваша программа получит только те биты, по которым вы потенциально можете пройти.
бобовый стебель
Вы действительно не знаете свое здоровье?
pppery
Не могли бы вы указать длину и ширину зрения бота?
LegionMammal978

Ответы:

4

Индиана Джонс, Питон 2

Этот бот ничего не боится. Он попытается получить золото; и если он не может их найти, он попытается нанести удар противникам ножами.

#!/usr/bin/env python
import sys
import random
data = sys.stdin.readlines()
health, gold, turns, poison_remaining, pit_remaining, revealer, knife = eval(data[0])
lines = data[1:]

myloc = [-1, -1]

width, height = len(lines[0]), len(lines)

for y, line in enumerate(lines):
    if line.find('Y')>-1:
        myloc = [line.index('Y'), y]
if myloc[0]<width/2:
    padding = int(width/2-myloc[0])
    lines = ['-'*padding+line for line in lines]
    myloc[0]+=padding
elif myloc[0]>width/2+1:
    padding = int(myloc[0]-width/2-1)
    lines = [line+'-'*padding for line in lines]

if myloc[1]<height/2:
    padding = int(height/2-myloc[1])
    lines = ['-'*width]*padding + lines
    myloc[1]+=padding
elif myloc[1]>height/2+1:
    padding = int(myloc[1]-height/2-1)
    lines = lines + ['-'*width]*padding

uddirections = {1:'N',0:'',-1:'S'}
lrdirections = {1:'E',0:'',-1:'W'}

golds = {}
for y, line in enumerate(lines):
    if 'G' in line:
        x = line.index('G')
        direction = ((uddirections[max(min(myloc[1]-y,1),-1)]+lrdirections[max(min(x-myloc[0],1),-1)])*2)[:2]
        distance = max(abs(myloc[0]-x), abs(myloc[1]-y))
        golds[distance] = direction

bots = {}
for y, line in enumerate(lines):
    if '@' in line:
        x = line.index('@')
        direction = ((uddirections[max(min(myloc[1]-y,1),-1)]+lrdirections[max(min(x-myloc[0],1),-1)])*2)[:2]
        distance = max(abs(myloc[0]-x), abs(myloc[1]-y))
        bots[distance] = direction

foods = {}
for y, line in enumerate(lines):
    if 'F' in line:
        x = line.index('F')
        direction = ((uddirections[max(min(myloc[1]-y,1),-1)]+lrdirections[max(min(x-myloc[0],1),-1)])*2)[:2]
        distance = max(abs(myloc[0]-x), abs(myloc[1]-y))
        foods[distance] = direction

knives = {}
for y, line in enumerate(lines):
    if 'K' in line:
        x = line.index('K')
        direction = ((uddirections[max(min(myloc[1]-y,1),-1)]+lrdirections[max(min(x-myloc[0],1),-1)])*2)[:2]
        distance = max(abs(myloc[0]-x), abs(myloc[1]-y))
        knives[distance] = direction

if golds:
    direction = golds[min(golds.keys())]
elif not knife and knives:
    direction = knives[min(knives.keys())]
elif health<20 and foods:
    direction = foods[min(foods.keys())]
elif bots and knife:
    direction = bots[min(bots.keys())]
    if min(bots.keys())==1:
        print ('A'+direction)*2
        sys.exit(0)
    elif min(bots.keys())==2:
        print 'M'+direction+'A'+direction
        sys.exit(0)
else:
    print ('M'+random.choice('NS')+random.choice('NEWS'))*2
    sys.exit(0)
print ('M'+direction)*2
taixzo
источник
Просто нужно было изменить одну вещь, чтобы это работало - line.index('Y')выдаст ошибку, если «Y» не в строке, но line.find('Y')вернет -1, если «Y» не в строке. В противном случае это здорово!
Beanstalk
Вы можете выводить информацию MSNMSN, что неверно.
pppery
3

Трус, python3

Трус всегда убегает от потенциальных угроз.

Однако, если он чувствует себя суперсильным, он внезапно выйдет из себя и нанесет удар всему, что находится рядом с ним.

Проблема с текущей реализацией состоит в том, что команды перемещения выдаются без знания, является ли это первым или вторым перемещением.

#!/usr/bin/env python3.4

import sys, random


class Coward():
  """
  A coward always runs from potential threats.

  However, if he feels super strong, he will suddenly run amok 
  and stab everything near him.  
  """
  def __init__(self, hp, gold, turn, poison, pit, revealer, knife):
    self.hp=int(hp)
    self.gold=int(gold)
    if knife=="True": self.knife=True
    else: self.knife=False    
    self.pit=int(pit)
    self.poison=int(poison)

  def readGrid(self, grid):
    self.grid=grid.split("\n")
  @property
  def _confidence(self):
    return self.hp+5*self.knife-2*self.poison
  @property
  def _lineOfY(self):
    for i, line in enumerate(self.grid):
      if "Y" in line:
        return i
  @property
  def _colOfY(self):
    return self.grid[self._lineOfY].index("Y")
  @property
  def _maxX(self):
    return len(self.grid)-1
  @property
  def _maxY(self):
    return len(self.grid[0])-1
  def move(self, step):
    d = {'NN':(0,-1),'NE':(1,-1),'EE':(1,0),'SE':(1,1),'SS':(0,1),'SW':(-1,1),'WW':(-1,0),'NW':(-1,-1)}
    c2d={(0,-1):'NN',(1,-1):'NE',(1,0):"EE",(1,1):"SE",(0,1):"SS",(-1,1):"SW",(-1,0):"WW",(-1,-1):"NW"}
    #Don't move into wall/ boulder/ pit
    #print(d, file=sys.stderr)
    for k,v in list(d.items()):
      x=self._lineOfY+v[0]
      y=self._colOfY+v[1]
      #print (k, v ,x , y, file=sys.stderr)
      if x<0 or y<0 or x>self._maxX or y>self._maxY:
        #print ("Out of bounds: ", k, file=sys.stderr)
        del d[k]
      elif self.grid[x][y]=="}" or self.grid[x][y]=="P":
        del d[k]
    #Always avoid bats, and enemys
    for dx in range(-2,3):
      for dy in range(-2,3):
        x=self._lineOfY+dx
        y=self._colOfY+dy
        if x<0 or y<0 or x>self._maxX or y>self._maxY:
          continue;
        if self.grid[x][y] in ["B", "L", "C", "@"]:
          for k in self._toDirection(dx, dy):
            if k in d: del d[k] #Too many threats from all sides can paralyze the Coward: nowhere to go...
    #print(d, file=sys.stderr)
    tomove=[]
    #Neighboring fields
    for dx in [-1,1]:
      for dy in [-1,1]:
        x=self._lineOfY+dx
        y=self._colOfY+dy
        if x<0 or y<0 or x>self._maxX or y>self._maxY:
          continue
        if self.poison>0 and self.grid[x][y]=="W":
          for k,v in d.items():
            if v==(dx,dy):
              tomove.append(k)
        if self.grid[x][y]=="N": #Always go to nurse, even if dangerous
          tomove.append(c2d[(x,y)])
        if self.grid[x][y] in ["F","K","G"]: #Go to Food, Knife or Gold, if save
          for k,v in d.items():
            if v==(dx,dy):
              tomove.append(k)
    #Further away: Go towards food, knife and gold and Nina if save.
    for target in ["N", "F", "G", "K"]:
      for dx in [-2,2]:
        for dy in [-2,2]:
          x=self._lineOfY+dx
          y=self._colOfY+dy
          if x<0 or y<0 or x>self._maxX or y>self._maxY:
            continue
          if self.grid[x][y]==target:
            l=[ k for k in self._toDirection(dx,dy) if k in d]
            if l: tomove.append(random.choice(l))
    s=list(d.keys())
    random.shuffle(s)
    tomove+=s
    try:
      return "M"+tomove[step-1]
    except IndexError:
      return ""
  def attack(self, step):    
    c2d={(0,-1):'NN',(1,-1):'NE',(1,0):"EE",(1,1):"SE",(0,1):"SS",(-1,1):"SW",(-1,0):"WW",(-1,-1):"NW"}
    #If Bot next to you: always attack
    for k,v in c2d.items():
      x=self._lineOfY+k[0]
      y=self._colOfY+k[1]
      if x<0 or y<0 or x>self._maxX or y>self._maxY:
        continue
      if self.grid[x][y]=="@":
        return "A"+v
    #If Bot or monster could come closer: attack potential new position
    attDir={(-2,-2):["NW"], (-2,-1):["NW","WW"], (-2,0):["WW","NW","SW"], (-2,1):["WW","SW"], (-2,2):["SW"],(-1,-2):["NW","NN"], (-1,2):["SW","SS"], (0,2):["SW","SS","SE"],(0,-2):["NW","NN","NE"],(1,-2):["NN","NE"],(1,2):["SS","SE"],(2,-2):["NE"],(2,-1):["NE","EE"], (2,0):["NE","EE","SE"], (2,1):["EE","SE"], (2,2):["SE"]}
    for k,v in attDir.items():
      x=self._lineOfY+k[0]
      y=self._colOfY+k[1]
      if x<0 or y<0 or x>self._maxX or y>self._maxY:
        continue
      if self.grid[x][y] in ["@","L","C","B"]:
        return "A"+random.choice(v)
    return ""
  def _toDirection(self,dx,dy):
    if dx<0:
      if dy<0:
        return ["WW","NW","NN"]
      elif dy==0:
        return ["WW","NW","SW"]
      elif dy>0:
        return ["WW","SW","SS"]
    elif dx>0:
      if dy<0:
        return ["EE","NE","NN"]
      elif dy==0:
        return ["EE","NE","SE"]
      elif dy>0:
        return ["EE","SE","SS"]
    elif dx==0:
      if dy<0:
        return ["NN","NE","NW"]
      elif dy==0:
        return []
      elif dy>0:
        return ["SS","SE","SW"]
  def _nearBat(self):
    for dx in range(-2,3):
      for dy in range(-2,3):
        x=self._lineOfY+dx
        y=self._colOfY+dy
        if x<0 or y<0:
          continue;
        if self.grid[x][y]=="B":
          return True
    return False
  def makeTurn(self):
    try:
      command=""
      #If stuck, just attack
      if self.pit:
        command+=self.attack(1)+self.attack(2)
      #Always run from bats
      if self._nearBat:
        command+=self.move(1)+self.move(2)
      #If high-confidence: attack
      if self._confidence>30:
        command+=self.attack(1)+self.attack(2)
      #Else: Move somewhere
      command+=self.move(1)+self.move(2)
      #Just in case, two random attacks
      d = ['NN','NE','EE','SE','SS','SW','WW','NW']
      a=random.choice(d)
      b=random.choice([ x for x in d if x!=a])
      command+="A"+a+"A"+b
      return command[:6]
    except Exception as e:
      #print (e, file=sys.stderr)
      #Attacking is better than nothing
      d = ['NN','NE','EE','SE','SS','SW','WW','NW']
      a=random.choice(d)
      b=random.choice([ x for x in d if x!=a])
      return "A"+a+"A"+b


info=sys.stdin.readline()
grid=sys.stdin.read()
info=info.split(",")
bot=Coward(*info)
bot.readGrid(grid)
t=bot.makeTurn()
#print(t, file=sys.stderr)
print(t)
TheEspinosa
источник
3

Бот выживания - Питон 2

from __future__ import print_function
health,gold,survived,poison,pit,revealer,knife = input()
if pit:
    exit()
#Yes, this is python 2, despite the use of input()
lines = []
try:
    while True:
        lines.append(raw_input())
except EOFError:
    pass
CMOVES={"NW":(-1,-1),"NN":(-1,+0),"NE":(-1,+1),
        "WW":(+0,-1),             "EE":(-0,+1),
    "SW":(+1,-1),"SS":(+1,+0),"SE":(+1,+1),
}
MOVES={v:k for k,v in CMOVES.iteritems()}
import sys
def get_your_pos():
    for row,line in enumerate(lines):
        for col,square in enumerate(line):
            if square == "Y":
                return row,col
    raise ValueError("Your bot is not present.")
def isnearby(thing,p=None):
    your_pos = p or get_your_pos()
    for move in MOVES:
        yp = your_pos[0]+move[0],your_pos[1]+move[1]
        try:
            if yp[0] >= 0 and yp[1] >= 0 and lines[yp[0]][yp[1]] == thing:
                return move
        except IndexError:
            #Edge of cavern
            pass
for turn in range(2):
    import random
    nprio = .5
    if health > 25:
        nprio -= .2
    elif health < 10:
        nprio += .3
    if poison:
        nprio += .18
    #heal
    motive = how = None
    if random.random() < nprio:
        nurse = isnearby("N")
        if nurse:
            motive = "M"
            how = MOVES[nurse]
        elif random.random() < nprio:
            food = isnearby("F")
            if food:
                motive = "M"
                how = MOVES[food]
    #cure poison
    if poison and not motive:
        water = isnearby("W")
        if water:
            motive = "M"
            how = MOVES[water]
    if not motive:
        #Kill lions, bats, and centipedes
        for animal in ("L","B","C"):
            animal = isnearby(animal)
            if animal:
                motive = "A"
                how = MOVES[animal]
                y = get_your_pos()
                y = y[0]+animal[0],y[1]+animal[1]
                lines = map(list,lines)
                lines[y[0]][y[1]] = "-"
                break
        else:
            #Pick up knives
            if not knife:
                knife = isnearby("K")
                if knife:
                    motive = "M"
                    how = MOVES[knife]
            #Attack other bots
            else:
                prey = isnearby("@")
                if prey:
                    motive = "A"
                    how = MOVES[prey]
    #Get gold
    gold = isnearby("G")
    if gold and not motive:
        motive = "M"
        how = MOVES[gold]
    def isvalidmove(c):
        c = CMOVES[c]
        y = get_your_pos()
        y=(y[0]+c[0],y[1]+c[1])
        if y[0] >= 0 and y[1] >= 0:
            try:
                lines[y[0]][y[1]]
            except LookupError:
                pass
            else:
                return True
    if turn and not motive:
        motive = "M"
        while not (how and how not in (isnearby("}"),isnearby("P"),isnearby("@"))\
              and isvalidmove(how)):
            how = random.choice(CMOVES.keys())
    if not motive:break
    if not turn and motive == "M":
        lines = map(list,lines)
        yp = get_your_pos()
        lines[yp[0]][yp[1]] = "-"
        yp=[yp[0]+CMOVES[how][0],yp[1]+CMOVES[how][1]]
        lines[yp[0]][yp[1]] = "Y"
    print(motive+how,end="")
else:
    exit()
#Nothing found on first move
def isvaguelynearby(thing):
    your_pos = get_your_pos()
    for move in MOVES:
        yp = your_pos[0]+move[0],your_pos[1]+move[1]
        try:
            if yp[0] >= 0 and yp[1] >= 0 and board[yp[0]][yp[1]] != "P":
                dest = isnearby(thing,yp)
                if dest:
                    return move,dest
        except IndexError:
            #Edge of cavern
            pass
if random.random() < nprio:
    dests = isvaguelynearby("N")
    if not dests and random.random() < nprio:
        dests = isvaguelynearby("F")
    if dests:
        m1,m2 = MOVES[dests[0]],MOVES[dests[1]]
        print("M" + m1 + "M" + m2)
        exit()
dests = (poison and isvaguelynearby("W")) or (not knife and isvaguelynearby("K"))\
    or isvaguelynearby("G")
prey = isvaguelynearby("L") or isvaguelynearby("B") or isvaguelynearby("C") or \
       (knife and isvaguelynearby("@"))
if dests:
    m1,m2 = MOVES[dests[0]],MOVES[dests[1]]
    print("M" + m1 + "M" + m2)
elif prey:
    m1,m2 = MOVES[prey[0]],MOVES[prey[1]]
    print("M" + m1 + "A" + m2)
else:
    how = None
    while not (how and how not in     (isnearby("}"),isnearby("P"),isnearby("@"))\
          and isvalidmove(how)):
        how = random.choice(CMOVES.keys())
    print("M"+how,end="")
    lines = map(list,lines)
    yp = get_your_pos()
    lines[yp[0]][yp[1]] = "-"
    yp=[yp[0]+CMOVES[how][0],yp[1]+CMOVES[how][1]]
    lines[yp[0]][yp[1]] = "Y"
    while not (how and how not in (isnearby("}"),isnearby("P"),isnearby("@"))\
          and isvalidmove(how)):
            how = random.choice(CMOVES.keys())
    print("M"+how)

Изменить: добавлено лучшее избегание ямы.

pppery
источник
2

Pufferfish, Python 3+

Я просто тот человек.

#!/usr/bin/env python3.4
import random
def select():
 return "A"+["NN","NE","EE","SE","SS","SW","WW","NW"][random.randint(0,7)]
print(select()+select())
Конор О'Брайен
источник
Этот бот предназначен для самообеспечения. Я мог бы добавить поиск гольфа позже, если у меня будет время.
Конор О'Брайен
Это сделает ваш код немного длиннее, но Pufferfish может атаковать дважды за один и тот же ход, если хочет быть более смертоносным
The Beanstalk
@TheBeanstalk Оооо, сладко!
Конор О'Брайен,
Вы понимаете, что этот бот никогда не получит никакого золота, и не все другие боты будут блуждать в его "ауре убийства"
pppery
@ppperry Я понимаю это; это было главным образом, чтобы заставить шар катиться; это не предназначено, чтобы победить, но сделать жизнь немного более трудной. Как и в Black Hat Man
Конор О'Брайен