KOTH: TNT Run Challenge

25

Esto fue inspirado por un minijuego de Minecraft. Las reglas son bastante simples: corres y saltas, y cada bloque que pisas desaparece una vez que lo pisas. El objetivo es ser el último que quede.

Tu bot debe ser un programa completo. Debe aceptar la entrada como un argumento de línea de comando. La entrada será un mapa del "mundo"; Aquí hay un ejemplo:

xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxx xxxxxxxxxxxxxxxxxxxxxxxxxxx
xxx xxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxx x xxxxxxxxxxxxx@xxxxxxxxxxx
xxxxxx1xxxxxxxxxxxxx xxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxx xxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxx xxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxx xxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxx xxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxx xxxxxxxxxxx
xxxxxxxxxx           xxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxx xxxxxxxxxxxx
xxxxxxxxxxxxxxxxx x x xxxxxxxxxx
xxxxxxxxxxxxxxxxxxxx xxxxxxxxxxx
xxxxxxxxxxxxxx xxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxx xxx xx3xxxxxxxxxx
xxxxxxxxxxxxxxxxxxx xxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxx  x
xxxxxxxxxxxxxxxxxxxxxxxxxxx   xx
xxxxxxxxxxxxxxxxxxxxxxxxx      2
xxxxxxxxxxxxxxxxxxxxxxx         

La leyenda es la siguiente:

x: solid block

 : empty air

@: your bot

1,2,3,4,5,6,7,8,9,0: other bots

Su bot debería generar su movimiento como un par de enteros. Ejemplo: -1, 2se moverá 1 bloque hacia la izquierda y 2 bloques hacia abajo (el origen de las coordenadas está en la esquina superior izquierda).

Puede moverse hasta cuatro cuadras, distancia de Manhattan, desde su ubicación actual. Si intenta avanzar más allá de eso, el movimiento no es válido. Cualquier movimiento que lo lleve más allá del borde lo colocará en el borde. Los movimientos inválidos serán ignorados.

Una vez que aterrizas en un bloque, se elimina; Si permaneces en el mismo bloque el próximo turno, caerás. Dos bots pueden aterrizar en el mismo bloque en el mismo turno y ambos sobreviven; Si esto sucede, ambos bots solo se verán a sí mismos y no al otro bot.

Si necesita almacenar archivos para persistencia, hágalo en una carpeta con el nombre de su bot. No puede leer los datos persistentes de otros bots si existe alguno.

El controlador de partido está disponible en https://paste.ee/p/Xf65d .

Utilice idiomas que se puedan ejecutar en una instalación estándar de Linux u OSX.

Resultados actuales (100 rondas):

JumpBot                   31
LookBot                   27
ShyBot                    26
Slow Bot                  15
KnightBot                 2
Moat Builder              0
UpBot                     0
Random Bot                0
Skyler
fuente
Similar, aunque una diferencia clave aquí es que puedes "saltar" varios bloques, por lo que no puedes simplemente bloquear a alguien si ve lo que estás haciendo.
Skyler
no se puede cerrar como un tonto en la caja de arena y no creo que sea una sola
Azul
1
¿Los movimientos son simultáneos o secuenciales? ¿Es realmente la entrada una cadena que contiene nueva línea como argumento de línea de comando?
Feersum
1
Yo sugeriría llamando al robot una vez sin un mundo de inicialización (no se sabe si su estado guarda como archivo es de última ronda o desde esta ronda)
bauen1
Los movimientos de @feersum son simultáneos; la entrada es de hecho un argumento de línea de comando que contiene nueva línea. Si lo necesita como stdin, avíseme y probablemente podría modificar el controlador para permitirlo.
Skyler

Respuestas:

9

Bot lento (Python)

Se mueve en un patrón de línea y comprueba sus movimientos antes de hacerlos (también se suicida cuando es el último vivo para evitar largos tiempos de ejecución) Ganó 195/200 Battels en mi torneo de prueba.

import sys
import re


class vec2(object):
    def __init__(self, x=0, y=0):
        self.x = x
        self.y = y

    def __add__(self, other):
        return vec2(self.x + other.x, self.y + other.y)

    def __sub__(self, other):
        return vec2(self.x - other.x, self.y - other.y)

    def __iadd__(self, other):
        return self + other

    def __isub__(self, other):
        return self - other

    def __neg__(self):
        return vec2(-self.x, -self.y)


def xy_to_i(vec=vec2(0, 0)):
    vec -= vec2(1, 1)
    vec.y += (vec.x - vec.x % 32) / 32
    return vec.x + vec.y * 33


def i_to_xy(i=0):
    vec = vec2(0, 0)
    vec.x = i % 33
    vec.y = (i - vec.x) / 32 + 1
    vec.x += 1
    return vec


class World(object):
    def __init__(self, map=''):
        self.map = map

    def getPlayerPosition(self):
        return i_to_xy(re.search('@', self.map).start())

    def getNumOtherBots(self):
        return len(re.findall('([0123456789])', ' ' + self.map + ' '))

    def get_tile(self, vec=vec2(0, 0)):
        i = xy_to_i(vec)
        return self.map[i:i + 1]


world = World(sys.argv[1])
pos = world.getPlayerPosition()


def check_moveV(vecd=vec2(0, 0)):
    try:
        vecn = pos + vecd

        if vecn.x > 32 or vecn.x < 1 or vecn.y > 32 or vecn.y < 1 \
            or abs(vecd.x) + abs(vecd.y) > 4:
            return False

        # Note: this will also avoid positions other bots are on (will disappear in the next step).

        return world.get_tile(vecn) == 'x'
    except:
        raise
        return False


def check_move(x=0, y=0):
    return check_moveV(vec2(x, y))


def run():
    if world.getNumOtherBots() == 0:
        return '0 0'  # Suicide if we are the only one left.

    # this creates the "line" pattern

    if check_move(0, -1):
        return '0 -1'

    if check_move(0, 1):
        return '0 1'

    if check_move(1, 0):
        return '1 0'

    if check_move(1, -1):
        return '1 -1'

    # If we get here, we are desperate and need to find a safe place to jump.

    for dx in range(-2, 2):
        for dy in range(-2, 2):
            if check_move(dx, dy):
                return '%i %i' % (dx, dy)

    # If we can't find a place to jump in close range, try long range.

    for dx in range(-4, 4):
        for dy in range(-4, 4):
            if check_move(dx, dy):
                return '%i %i' % (dx, dy)

    # If we get here, we are dead no matter what; accept our fate.

    return '0 0'


print(run())

No soy un experto en python y probablemente hay 100 formas de hacerlo más corto / mejor

bauen1
fuente
1
Solo una cosa, si estás en el mismo espacio con otro robot y eres los dos últimos, el tuyo pensará que es el último y el suicidio.
Timtech
cuando implemente la persistencia, esperará 5 rondas hasta el suicidio
bauen1
Genial, eso es lo que iba a sugerir. Excelente respuesta, por cierto.
Timtech
6

JumpBot (C)

Intenta saltar al campo con la mayoría de los movimientos posibles en la próxima ronda.

#include <stdio.h>
#include <stdlib.h>

typedef struct map {
     char *raw_map;
     int size;
     int lines;
     char *pos;
} *MAP;

typedef struct cdata {
     int result;
     MAP m;
     int x;
     int y;
} *CDATA;

typedef struct mdata {
     int x;
     int y;
     int moves;
     int bx;
     int by;
     MAP m;
} *MDATA;

int numberOfMoves(MAP, int, int);
char getAt(MAP, int, int);

int abs(int x)
{
    return x < 0 ? x*-1 : x;
}

void count(void *data, int x, int y)
{
    CDATA d = (CDATA)data;
    char c = getAt(d->m, d->x + x, d->y + y);
    if(c != 'x') return;
    d->result++;
}

void choose(void *data, int x, int y)
{
    MDATA m = (MDATA)data;
    char c = getAt(m->m, m->x + x, m->y + y);
    if(c != 'x') return;
    int moves = numberOfMoves(m->m, m->x+x, m->y+y);
    if(moves > m->moves || (!m->bx && !m->by)) {
        m->moves = moves;
        m->bx = x;
        m->by = y;
    }
}

MAP parse_input(char *input)
{
    MAP m = malloc(sizeof *m);
    if(!m) {
        fprintf(stderr, "failed to alloc map\n");
        return NULL;
    }

    m->size=0;
    m->lines=1;
    m->pos=0;

    char *temp;
    for(temp = input;*temp;temp++) {
        switch(*temp) {
            case '\n': m->lines++; break;
            default: break;
        }
    }
    m->size = (temp + 1) - (input + m->lines);
    m->raw_map = malloc(m->size);
    if(!m->raw_map) {
        fprintf(stderr, "failed to alloc raw_map\n");
        return NULL;
    }

    int index = 0;
    for(temp = input; *temp; temp++) {
        if(*temp == '@') m->pos = m->raw_map + index;
        if(*temp != '\n') m->raw_map[index++] = *temp;
    }

    return m;
}

char getAt(MAP m, int x, int y)
{
    return m->raw_map[x + y*(m->size / m->lines)];
}

void posToXY(MAP m, int *x, int *y)
{
    int index = m->pos - m->raw_map;
    int length = m->size / m->lines;
    *x = index % length;
    *y = index / length;
}

typedef void (*DOFUNC)(void *, int, int);
void processMoves(MAP m, int x, int y, DOFUNC proc, void *data)
{
    int length = m->size / m->lines;    
    int left = x>=4 ? 4 : x;
    int right = x + 4 <= length ? 4 : length - (x + 1);
    int up = y >= 4 ? 4 : y;
    int down = y + 4 <= m->lines ? 4 : m->lines - (y + 1);

    for(int i=-left; i<=right; i++) {
        for(int j=-up; j<=down; j++) {
            if((abs(i) + abs(j) <= 4) && (i || j)) (*proc)(data, i, j);
        }
    }
}

int numberOfMoves(MAP m, int x, int y)
{
    struct cdata d;
    d.result = 0;
    d.x = x;
    d.y = y;
    d.m = m;
    processMoves(m, x, y, &count, &d);
    return d.result;
}

void getMove(MAP m, int *x, int *y)
{
    struct mdata d;
    posToXY(m, &d.x, &d.y);
    d.moves = 0;
    d.bx = 0;
    d.by = 0;
    d.m = m;
    processMoves(m, d.x, d.y, &choose, &d);
    *x = d.bx;
    *y = d.by;
}

int main(int argc, char *argv[])
{
    if(argc != 2) {
        fprintf(stderr, "bad number of arguments %d\n", argc);
        return -1;
    }

    MAP m = parse_input(argv[1]);
    int x=0, y=0;
    getMove(m, &x, &y);
    printf("%d %d\n", x, y);
    return 0;
}
Optokopper
fuente
5

LookBot (C)

Bot simple que es similar en rendimiento a Slow Bot, excepto que este realiza movimientos aleatorios posibles. Planee mejorar esto para PredictBot.

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <assert.h>
#include <sys/time.h>

#define WORLDSZ (32)
#define WORLDSZ_2 (WORLDSZ*WORLDSZ)

int max(int a,int b){return a>b?a:b;}
int min(int a,int b){return a<b?a:b;}

struct Position{
    int x,y;
};
typedef struct Position Position;

struct World{
    Position me;
    double enemymap[WORLDSZ][WORLDSZ]; //chance of enemy present
    bool open[WORLDSZ][WORLDSZ];
};
typedef struct World World;

void world_read(World *world,const char *arg){
    int x,y,i=0;
    for(y=0;y<WORLDSZ;y++,i++){
        for(x=0;x<WORLDSZ;x++,i++){
            if(arg[i]=='@'){world->me.x=x; world->me.y=y;}
            world->enemymap[y][x]=arg[i]>='0'&&arg[i]<='9';
            world->open[y][x]=arg[i]=='x';
        }
    }
}

//returns relative position
Position world_calcmove(World *world){
    const int mex=world->me.x,mey=world->me.y;
    int dx,dy;
    Position poss[40];
    int nposs=0;
    for(dy=max(-mey,-4);dy<=min(WORLDSZ-1-mey,4);dy++){
        const int absdy=abs(dy);
        for(dx=max(-mex,absdy-4);dx<=min(WORLDSZ-1-mex,4-absdy);dx++){
            if(!world->open[mey+dy][mex+dx])continue;
            poss[nposs].x=dx;
            poss[nposs++].y=dy;
        }
    }
    if(nposs==0){
        poss[0].x=poss[0].y=0;
        return poss[0];
    }
    return poss[rand()%nposs];
}

int main(int argc,char **argv){
    if(argc!=2){
        fprintf(stderr,"Call with world!\n");
        return 1;
    }
    struct timeval tv;
    gettimeofday(&tv,NULL);
    srand(tv.tv_sec*1000000ULL+tv.tv_usec);

    World world;
    world_read(&world,argv[1]);
    Position move=world_calcmove(&world);
    printf("%d %d\n",move.x,move.y);
}
tomsmeding
fuente
5

Constructor de fosos (Python)

Si cavo un foso a mi alrededor, nadie afuera puede fastidiarme.

... también conocido como "Píntate en un simulador de esquina 2016".

import numpy
import sys
import math
import os

if not os.path.exists('./moatbuilder'):
    os.mkdir('./moatbuilder')

raw_field = sys.argv[1]
field = numpy.array([numpy.array(list(i)) for i in raw_field.splitlines()])
field_size = len(field)
x, y = raw_field.replace('\n','').index('@')%field_size, int(raw_field.replace('\n','').index('@')/field_size)
# If there are no holes, it's the first round - reset persistence
if raw_field.count(' ')==0:
    open('./moatbuilder/persistent','w').write('')

def bigmove(target):
    if x < target[0]:
        return min(4, target[0] - x), 0
    elif x > target[0]:
        return max(-4, target[0] - x), 0
    elif y < target[1]:
        return 0, min(4, target[1] - y)
    else:
        return 0, max(-4, target[1] - y)

def smallmove(target):
        if x < target[0]:
        try:
            return min(max(1, list(field[y][x:x+4]).index('x')), target[0] - x), 0
        except:
            return 0, 0
        elif x > target[0]:
        try:
            return max(min(-1, 0-list(reversed(field[y][x-4:x])).index('x')), target[0] - x), 0
        except:
            return 0, 0
        elif y < target[1]:  
        try:
                    return 0, min(max(1, list(field[:,x][y:y+4]).index('x')), target[1] - y)
        except:
            return 0, 0
        else:
        try:
            return 0, max(min(-1, 0-list(reversed(field[:,x][y-4:y])).index('x')), target[1] - y)
        except:
            return 0, 0


try:
    mode = int(open('./moatbuilder/persistent').read())
except:
    mode = 1

# Modes:
# 1 - go to the center
# 2 - go to an outside edge
# 3 - dig moat
if mode==1:
    dx, dy = bigmove((int(field_size/2), int(field_size/2)))
    if dx==0 and dy==0:
        open('./moatbuilder/persistent', 'w').write('2')
        mode = 2
if mode==2:
    dx, dy = bigmove((int(field_size-1), int(field_size/2)))
    if dx==0 and dy==0:
        dy = 1
        open('./moatbuilder/persistent', 'w').write('3')
        mode = 3
elif mode==3:
    direction = max(field_size-x, field_size-y)%2
    if direction == 1:
        if x > y:
            dx, dy = smallmove((y, y))
        else:
            dx, dy = smallmove((x, field_size - 1))
        if dx==0 and dy==0:
            dx = 1
    else:
        if y > x:
            dx, dy = smallmove((x, x))
        else:
            dx, dy = smallmove((field_size - 1, y))
        if dx==0 and dy==0:
            dy = 1

print "%i %i" % (dx, dy)
taixzo
fuente
En realidad, esto funciona bastante bien, pero perderá contra bot que viven durante mucho tiempo (es por eso que elijo un patrón de línea por cierto)
bauen1
Es posible que desee corregir su sangría en smallmove () ... Mi python no se come esto :)
tomsmeding
5

Monte (pitón)

Lo siento, ese juego de palabras solo tenía que hacerse.

De todos modos, este bot funciona haciendo un Búsqueda de árbol de Monte Carlo en todos los conjuntos de movimientos posibles. Piense en JumpBot, solo que más en profundidad.

Para ejecutarse, necesita un argumento de línea de comando adicional (puede especificarse en el controlador). Controla cuánto tiempo debe buscar el bot (en ms); Usé 750-1500 en las pruebas.

Código:

import sys
import math
import copy
#from profilestats import profile
pmap = sys.argv[2].split("\n")
pmap = [list(r) for r in pmap]

#find a player
#@profile
def find(tmap,bot):
   r,c=-1,-1
   for row in range(len(tmap)):
      for col in range(len(tmap[row])):
         if tmap[row][col]==bot:
            r,c=row,col
   return r,c

mer,mec=find(pmap,'@')
bots=[(mer,mec)]

#find all the other players
for b in range(10):
   r,c=find(pmap,str(b))
   if r != -1:
      bots.append((r,c))

#getter function, treats oob as spaces
def get(tmap,r,c):
   if r<0 or r>=len(tmap) or c<0 or c>=len(tmap[r]):
      return ' '
   return tmap[r][c]

#returns manhattan distance between 2 positions  
def dist(r1,c1,r2,c2):
   return abs(r1-r2)+abs(c1-c2)

#gets all possible moves from a map
#@profile 
def moves(tmap,ther=-1,thec=-1):
   if ther==-1: ther,thec = find(tmap,'@')
   pos=[]
   for r in range(-4,5):
      for c in range(-4,5):
         if abs(r)+abs(c)<=4 and get(tmap,ther+r,thec+c)=='x':
            pos.append((r,c))
   return pos


ttlmoves = 40
#monte-carlo tree node
class MCNode:
   def __init__(self):
      self.wins=0
      self.simu=0
      self.chld=[]
      self.cmap=[[]]
      self.prnt=None
      self.r=-1
      self.c=-1
   def add(self, cnode):
      self.chld.append(cnode)
      cnode.prnt = self
   #used to balance exploitation and exploration
   #@profile
   def param(self,cin):
      return self.chld[cin].wins/self.chld[cin].simu\
             + 1.414 * math.sqrt( math.log(self.simu) / \
             self.chld[cin].simu )
   #finds the child with the highest param
   #@profile
   def best(self):
      vals = [self.param(x) for x in range(len(self.chld))]
      binx = 0
      bval = vals[0]
      for x in range(len(vals)):
         if vals[x]>bval:
            binx=x
            bval=vals[x]
      return self.chld[binx]


#update all the parents 
#@profile   
def backprog(leaf):
   par = leaf.prnt
   if not (par is None):
      par.wins+=leaf.wins
      par.simu+=leaf.simu
      backprog(par)

#expand all the moves from a position
#@profile
def expand(rootn):
   ther,thec = rootn.r,rootn.c
   for r,c in moves(rootn.cmap,rootn.r,rootn.c):
      nmap = copy.deepcopy(rootn.cmap)
      nmap[ther+r][thec+c] = '@'
      nmap[ther][thec]=' '
      nnode = MCNode()
      nm = moves(nmap,ther+r,ther+c)
      nnode.wins = len(nm)
      nnode.simu = ttlmoves
      nnode.r=ther+r
      nnode.c=thec+c
      nnode.cmap = nmap
      rootn.add(nnode)
      backprog(nnode)

root = MCNode()
m = moves(pmap,mer,mec)
root.wins = len(m)
root.simu = ttlmoves
root.cmap=copy.deepcopy(pmap)
root.r=mer
root.c=mec
expand(root)

#simulate a bunch of outcomes
import time
curt  = lambda: int(round(time.time() * 1000))
strt = curt()
ttme = int(sys.argv[1])
while curt()-strt < ttme:
   tnode=root
   while tnode.chld:
      tnode=tnode.best()
   expand(tnode)

#choose the most explored one
bnode = max(root.chld,key=lambda n:n.simu)

#output
print("{} {}".format((bnode.c-mec),(bnode.r-mer)))

Ensayos

25 rondas:

MonteBot            14
JumpBot             6
ShyBot              5
LookBot             1
KnightBot           0
SlowBot             0

100 rondas:

JumpBot             38
MonteBot            36
ShyBot              15
LookBot             14
SlowBot             2
KnightBot           0

200 rondas:

MonteBot            87
JumpBot             64
LookBot             33
ShyBot              21
SlowBot             5
KnightBot           0

Todas las simulaciones anteriores utilizaron el tiempo de búsqueda 750. Este robot probablemente sería aún mejor con un tiempo de búsqueda más largo (no sé cuál es el máximo permitido).

Mejoras

Este bot aún necesita mejoras en:

  1. Rendimiento: necesita todo el tiempo para buscar.
  2. Predicción: no tendrá en cuenta los movimientos de otros bots.
  3. Balance: no estoy seguro de si la fórmula UCT que estoy usando para calcular qué nodo debería explorar es óptima.
Azul
fuente
4

ShyBot (Python)

A este bot realmente no le gustan otros bots e intentará mantenerse alejado si es posible. ShyBot también es muy cuidadoso sobre dónde pisa; ni siquiera pisará otros bots. Sin embargo, ShyBot todavía pierde a menudo, lo que lo hace inseguro.

import sys
map = sys.argv[1]
map = map.split("\n")
map = [list(r) for r in map]

def find(map,bot):
   r,c=-1,-1
   for row in range(len(map)):
      for col in range(len(map[row])):
         if map[row][col]==bot:
            r,c=row,col
   return r,c


mer,mec=find(map,'@')
bots=[(mer,mec)]

for b in range(10):
   r,c=find(map,str(b))
   if r != -1:
      bots.append((r,c))

avg=[0,0]

for b in bots:
   avg[0]+=b[0]
   avg[1]+=b[1]

avg[0] = avg[0]/len(bots)
avg[1] = avg[1]/len(bots)

def get(map,r,c):
   if r<0 or r>=len(map) or c<0 or c>=len(map[r]):
      return ' '
   return map[r][c]

def dist(r1,c1,r2,c2):
   return abs(r1-r2)+abs(c1-c2)

pos=[]
for r in range(-4,5):
   for c in range(-4,5):
      if abs(r)+abs(c)<=4 and get(map,mer+r,mec+c)=='x':
         pos.append((r,c))

if len(pos)==0:
   bestr,bestc=0,0
else:
   bestr,bestc=pos[0]

for r,c in pos:
   if dist(mer+r,mec+c,avg[0],avg[1])>dist(mer+bestr,mec+bestc,avg[0],avg[1]):
      bestr,bestc=r,c

print(str(bestc)+" "+str(bestr))
Azul
fuente
4

KnightBot (Java)

Funciona como el ajedrez, y se llama como Twitch ...

...

.........

............................lo siento...

public class KnightBot{
   private static String[] map;
   private static int myx;
   private static int myy;
   public static void main(String[] args){
      map=args[0].split("\n");
      for(int y=0;y<map.length;y++){
         if(map[y].indexOf("@")!=-1){
            myy = y;
            myx = map[y].indexOf("@");
            break;
         }
      }
      System.out.println(move((int)(Math.random()*4),4));
   }
   public static String move(int dir,int tries){
      if(tries==0)return "0 0";
      int x=dir<2?1:-1;
      int y=dir%2==0?2:-2;
      if((myx+x<0||myx+x>=map[0].length()||myy+y<0||myy+y>=map.length)||map[y+myy].charAt(myx+x)!='x'){
         x=dir<2?2:-2;
         y=dir%2==0?1:-1;
      }
      if((myx+x<0||myx+x>=map[0].length()||myy+y<0||myy+y>=map.length)||map[y+myy].charAt(myx+x)!='x')
         return move(++dir>3?0:dir,tries-1);
      return x+" "+y;
   }
}

SwirlyBot (Java)

Claramente, estas no son las soluciones óptimas, pero espero que sean útiles para las pruebas de nivel medio.

public class SwirlyBot{
   private static String[] map;
   private static int myx;
   private static int myy;
   public static void main(String[] args){
      map=args[0].split("\n");
      for(int y=0;y<map.length;y++){
         if(map[y].indexOf("@")!=-1){
            myy = y;
            myx = map[y].indexOf("@");
            break;
         }
      }
      System.out.println(move(0));
   }
   public static String move(int dir){
      switch(dir){
         case 0:
            if(!safe(0,1)){
               if(safe(1,1)){
                  return "1 1";//Down-Right
               }else{
                  if(safe(1,0)){
                     return "1 0";//Right
                  }
               }
            }
            break;
         case 1:
            if(!safe(1,0)){
               if(safe(1,-1)){
                  return "1 -1";//Up-Right
               }else{
                  if(safe(0,-1)){
                     return "0 -1";//Up
                  }
               }
            }
            break;
         case 2:
            if(!safe(0,-1)){
               if(safe(-1,-1)){
                  return "-1 -1";//Up-Left
               }else{
                  if(safe(-1,0)){
                     return "-1 0";//Left
                  }
               }
            }
            break;
         case 3:
            if(!safe(-1,0)){
               if(safe(-1,1)){
                  return "-1 1";//Down-Left
               }else{
                  if(safe(0,1)){
                     return "0 1";//Down
                  }
               }
            }
            break;
         case 4:
            if(safe(0,-1))return "0 -1";
            break;
         case 5:
            if(!safe(0,2)){
               if(safe(1,2)){
                  return "1 2";//Down-Right
               }else{
                  if(safe(2,2)){
                     return "2 2";
                  }else{
                     if(safe(2,1)){
                        return "2 1";
                     }else{
                        if(safe(2,0)){
                           return "2 0";//Right
                        }
                     }
                  }
               }
            }
            break;
         case 6:
            if(!safe(2,0)){
               if(safe(2,-1)){
                  return "2 -1";//Up-Right
               }else{
                  if(safe(2,-2)){
                     return "2 -2";
                  }else{
                     if(safe(1,-2)){
                        return "1 -2";
                     }else{
                        if(safe(0,-2)){
                           return "0 -2";//Up
                        }
                     }
                  }
               }
            }
            break;
         case 7:
            if(!safe(0,-2)){
               if(safe(-1,-2)){
                  return "-1 -2";//Up-Left
               }else{
                  if(safe(-2,-2)){
                     return "-2 -2";
                  }else{
                     if(safe(-2,-1)){
                        return "-2 -1";
                     }else{
                        if(safe(-2,0)){
                           return "-2 0";//Left
                        }
                     }
                  }
               }
            }
            break;
         case 8:
            if(!safe(-2,0)){
               if(safe(-2,1)){
                  return "-2 1";//Down-Left
               }else{
                  if(safe(-2,2)){
                     return "-2 2";
                  }else{
                     if(safe(-1,2)){
                        return "-1 2";
                     }else{
                        if(safe(0,2)){
                           return "0 2";//Down
                        }
                     }
                  }
               }
            }
            break;
      }
      if(dir<8)return move(dir+1);
      return "0 -1";
   }
   public static boolean safe(int x, int y){
      return !((myx+x<0||myx+x>=map[0].length()||myy+y<0||myy+y>=map.length)||map[y+myy].charAt(myx+x)!='x');
   }
}
Cheeseitup
fuente
Hola y bienvenidos a PPCG! ¡Gran respuesta!
NoOneIsHere
2

Bot aleatorio, UpBot

Dos bots iniciales para competir contra:

Bot aleatorio: un bot de ejemplo que se mueve al azar.

import random

x = random.randint(-4, 4)
y = random.randint(max(-4, -4 + abs(x)), min(4, 4 - abs(x)))
print x, y

UpBot: un ejemplo de bot que se mueve hacia arriba.

print '0 -1'
Skyler
fuente
Ejecuté 10 rondas de prueba para mi respuesta (ahora eliminada) Random Walker, y hilarantemente, UpBot está muy bien. Obtuvo 7 de 10 rondas.
user48538
Aquí están los resultados completos de la prueba , proporcionados como un archivo zip.
user48538
UpBot funciona bien porque solo se mueve un bloque a la vez, por lo que generalmente le toma más tiempo correr hacia una pared que Random Bot para caminar hacia un agujero.
Skyler
1
@ zyabin101: ya sabes, puedes ejecutarlo, presionar 'y' para jugar un torneo completo e ingresar 10 para las rondas.
Skyler
1

StalkerBot (Python)

Se acercará lo más posible al bot más cercano que vea. Apunta al suicidio automático de Slow Bot (sin sentido). (Si estoy en la misma casilla y no hay otros jugadores, no me verá y se suicidará).

#!/usr/bin/python3
from math import inf
from sys import argv

class Vector:
    def __init__(self, x=0, y=0):
        self.x = x
        self.y = y

    def __add__(self, other):
        return Vector(self.x + other.x, self.y + other.y)

    def __sub__(self, other):
        return Vector(self.x - other.x, self.y - other.y)

    def __neg__(self):
        return Vector(-self.x, -self.y)

    def __abs__(self):
        return self.x ** 2 + self.y ** 2  # Technically the square of the magnitude, but we only need it for comparison.

    def __iter__(self):
        yield self.x
        yield self.y

def get_location(grid, target='@'):
    for i, line in enumerate(grid):
        for j, char in enumerate(line):
            if char == target:
                return Vector(i, j)

def main(grid):
    my_location = get_location()

    min_distance = inf
    min_distance_direction = None

    for i in range(10):
        enemy_location = get_location(str(i))

        if enemy_location is not None:
            direction = enemy_location - my_location
            distance = abs(direction)

            if distance < current_min:
                min_distance = distance
                min_distance_direction = direction

            if distance == 1:
                break

    if min_distance_direction is not None:
        return min_distance_direction

    for d in range(1, 5):
        for x in range(-d, d):
            for y in (d - abs(x), abs(x) - d):
                if grid[x][y] == ' ':
                    return x, y

    return 0, 0

if __name__ == '__main__':
    print(*main(argv[1].splitlines()))
Solomon Ucko
fuente
1
Solo para su información, en general no aprobamos las ediciones que alteran el código (como la que hizo en otra respuesta a esta pregunta). Lo aprobé porque parecía que no tocaba la lógica ni nada, solo lo limpiaba, pero eso no va a volar en la mayoría de las respuestas. Sin embargo, ese definitivamente podría usarlo.
Rɪᴋᴇʀ
@Riker Entendido. Tiene sentido no cambiar la lógica, pero tenía problemas para leer ese código, así que decidí limpiar el formato.
Solomon Ucko
1
No hay problema, pero tenga en cuenta que es probable que las ediciones de golf y similares sean rechazadas en otras preguntas en el futuro. Sin embargo, estoy de acuerdo en que el código que editó fue un poco raro.
Rɪᴋᴇʀ
1
@Riker Básicamente, no realice modificaciones que puedan afectar la puntuación.
Solomon Ucko