BattleBots: el torneo

69

Los resultados están en, el concurso ha terminado.
El ganador es EvilBot de arshajii con 14 victorias por delante de Neo-Bot con 13 victorias y CentreBot y LastStand con 11 victorias cada una.

Puntajes de la carrera final

Results:
java Rifter:                 9  match wins (45 total bout wins)
java EvadeBot:               10 match wins (44 total bout wins)
java EvilBot:                14 match wins (59 total bout wins)
java LastStand:              11 match wins (43 total bout wins)
java UltraBot:               9  match wins (40 total bout wins)
python ReadyAimShoot.py:     8  match wins (36 total bout wins)
./SpiralBot:                 0  match wins (1 total bout wins)
python DodgingTurret.py:     8  match wins (43 total bout wins)
ruby1.9 TroubleAndStrafe.rb: 8  match wins (41 total bout wins)
./RandomBot:                 1  match wins (6 total bout wins)
python StraightShooter.py:   8  match wins (41 total bout wins)
python mineminemine.py:      3  match wins (14 total bout wins)
./CamperBot:                 5  match wins (20 total bout wins)
python3.3 CunningPlanBot.py: 3  match wins (15 total bout wins)
node CentreBot.js:           11 match wins (44 total bout wins)
node Neo-Bot.js:             13 match wins (59 total bout wins)
python NinjaPy.py:           3  match wins (19 total bout wins)

Este es un desafío del . El objetivo es escribir un bot que supere a más bots que cualquier otro.

El juego

Todos los bots se enfrentarán entre sí 2 a la vez en una arena de 10x10 con la tarea de reducir la energía del oponente de 10 a 0 antes de que su propia energía se reduzca a 0.

Cada partido constará de 5 combates. El ganador del partido es el ganador de la mayoría de los combates. El programa de control almacenará el número total de victorias y combates y se utilizará para determinar el ganador general del concurso. El ganador recibe la gran marca verde y la adulación de las masas.

Cada combate continuará en una serie de rondas. Al comienzo de cada ronda, el estado actual de la arena se le dará a cada bot y el bot responderá con un comando para determinar qué quiere hacer a continuación. Una vez que el programa de control ha recibido ambos comandos, ambos comandos se ejecutan al mismo tiempo y los niveles de energía de arena y bot se actualizan para reflejar el nuevo estado. Si ambos bots aún tienen suficiente energía para continuar, el juego pasa a la siguiente ronda. Habrá un límite de 1000 rondas por combate para garantizar que ningún combate continúe para siempre, y en caso de que se alcance este límite, el ganador será el bot con más energía. Si ambos bots tienen la misma energía, el combate es un empate y ninguno de los dos obtendrá un punto por la victoria (sería como si ambos hubieran perdido).

Las armas

Cada bot tendrá a su disposición una serie de armas:

  • Balas perforantes. Estos viajan 3 casillas a la vez y causan 1 punto de daño de energía.
  • Misiles Estos viajan 2 casillas a la vez y causan 3 puntos de daño de energía en el punto de impacto, y 1 punto de daño en todas las casillas que rodean inmediatamente.
  • Minas terrestres. Estos se dejan caer en una de las casillas que rodean al bot y causan 2 puntos de daño de energía cuando se pisa, y 1 punto de daño de energía a cualquier cosa que se encuentre en una de las casillas que rodean inmediatamente.
  • Pulso electromagnetico. Hace que los circuitos de movimiento de ambos robots funcionen mal durante 2 turnos, lo que significa que no pueden moverse. Sin embargo, aún pueden desplegar armas (sí, sé que eso no es realista, pero es un juego. Se supone que no es la vida real). Editar: cada implementación de EMP costará un punto de energía para el bot que lo usa.

Las balas / misiles solo pueden impactar con bots o paredes. Golpearán cualquier bot que esté en cualquiera de los cuadrados por los que viajan. Desaparecen una vez que golpean algo.

En todos los casos, immediately surrounding squaressignifica los 8 cuadrados a los que el bot podría moverse en su próximo movimiento: el vecindario de Moore.

Los comandos

  • 0 hacer nada.
  • N, NE, E, SE, S, SW, W, NWSon todos los comandos de dirección y mover el bot un cuadrado en la dirección dada. Si el bot no puede moverse en esa dirección porque hay una pared u otro bot en la casilla, el bot permanece donde está. Moverse a un cuadrado que ya contiene una bala o misil es seguro ya que se considerará que la bala / misil ya está saliendo de ese cuadrado.
  • B seguido de un espacio y luego uno de los comandos de dirección dispara una bala perforadora de armadura en esa dirección.
  • M seguido de un espacio y luego uno de los comandos de dirección dispara un misil en esa dirección.
  • Lseguido de un espacio y luego uno de los comandos de dirección deja caer una mina terrestre en esa casilla al lado del bot. Si el cuadrado ya está ocupado por una pared o un bot, el comando se ignora. Si una mina terrestre se deja caer sobre otra mina terrestre, la detona. Esto dañará al bot que esté cayendo, y a cualquier otro bot dentro del alcance de la mina terrestre original.
  • P dispara el EMP.

Dado que solo se puede dar un comando por ronda, un bot solo puede mover o disparar / desplegar un arma, no hacer ambas cosas al mismo tiempo.

Orden de los comandos
El movimiento de cualquiera de los bots siempre vendrá primero, y todos los movimientos se intentarán dos veces para dar cuenta de que otro bot está en el camino pero se está apartando del camino.

Ejemplo

  • Bot1 intenta moverse Epero Bot2 ya está en esa casilla
  • El programa de control pasa a Bot2.
  • Bot2 intenta moverse Sy tiene éxito porque no hay nada en el camino.
  • Bot1 consigue un segundo intento de hacer su movimiento. Esta vez tiene éxito y Bot1 se mueve E.

Una vez que los bots hayan hecho cualquier movimiento que quieran hacer, se dispararán las armas y todos los proyectiles (nuevos y disparados previamente) moverán su número predefinido de casillas.

La arena

Al comienzo de cada ronda, el bot recibirá el estado actual de juego como el único argumento de línea de comando del programa:

X.....LLL.
..........
..........
..........
M.........
..........
..........
..........
..........
...B.....Y
Y 10
X 7
B 3 9 W
M 0 4 S
L 6 0
B 3 9 S
L 7 0
L 8 0

La arena viene primero que consiste en 10 líneas de 10 personajes. Está rodeado de paredes que no se muestran. Los significados de los personajes son los siguientes:

  • . representa un cuadrado vacío
  • Y representa a tu bot.
  • X representa al oponente bot.
  • L representa una mina terrestre.
  • B representa una bala en vuelo.
  • M representa un misil en vuelo.

Esto es seguido por la energía restante de los bots, un bot por línea. Solo un espacio separará el identificador de bot de su nivel de energía. Como en la arena, Yrepresenta a tu bot y Xrepresenta a tu oponente. Finalmente viene una lista de los proyectiles y minas terrestres, sus posiciones y (si corresponde) encabezados, nuevamente uno por línea.

El programa de control

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

#define NUMBOTS 2
#define BOUTSPERMATCH 5
#define ROUNDSPERBOUT 1000
#define MAXFILENAMESIZE 100
#define MAXWEAPONS 100
#define DISPLAYBOUTS true

typedef struct
{
  int x, y, energy;
  char cmd[5];
} Bot;

int getxmove(char cmd[5]);
int getymove(char cmd[5]);
int newposinbounds(int oldx, int oldy, int dx, int dy);
int directhit(Bot bot, int landmine[2]);
int landminecollision(int landmine1[2], int landmine2[2]);
int inshrapnelrange(Bot bot, int landmine[2]);
int directiontoint(char direction[5], char directions[8][3]);
void deployweapons(Bot *bot, Bot *enemy, int bullets[MAXWEAPONS][3], int missiles[MAXWEAPONS][3], int landmines[MAXWEAPONS][2], char directions[8][3]);
void cleararena(char arena[10][11]);

int main()
{
  FILE *fp;
  Bot b1, b2;
  int bot1, bot2, bot1bouts, bot2bouts;
  int bout, round, loop, totalprojectiles, dx, dy;
  char bots[NUMBOTS][MAXFILENAMESIZE]=
  {
    "./donowt              ",
    "php -f huggybot.php   "
  };
  char directions[8][3]={"N", "NE", "E", "SE", "S", "SW", "W", "NW"};
  char openstring[5000], argumentstring[4000], bot1string[6], bot2string[6];
  int matcheswon[NUMBOTS],boutswon[NUMBOTS];
  int missiles[MAXWEAPONS][3];
  int bullets[MAXWEAPONS][3];
  int landmines[MAXWEAPONS][2];
  int paralyzedturnsremaining=0;
  bool bot1moved;
  char arena[10][11];
  char projectiles[300][10];

  for(loop=0;loop<NUMBOTS;loop++)
  {
    matcheswon[loop]=0;
    boutswon[loop]=0;
  }

  srand(time(NULL));

  for(bot1=0;bot1<NUMBOTS-1;bot1++)
  {
    for(bot2=bot1+1;bot2<NUMBOTS;bot2++)
    {
      bot1bouts=bot2bouts=0;
      printf("%s vs %s ",bots[bot1],bots[bot2]);
      for(bout=0;bout<BOUTSPERMATCH;bout++)
      {
        printf("%d ",bout);
        //setup the arena for the bout
        b1.x=1;b1.y=1;
        b2.x=9;
        //b1.y=rand()%10;
        b2.y=rand()%10;
        b1.energy=b2.energy=10;
        //clear the previous stuff
        memset(missiles, -1, sizeof(missiles));
        memset(bullets, -1, sizeof(bullets));
        memset(landmines, -1, sizeof(landmines));
        for(round=0;round<ROUNDSPERBOUT;round++)
        {
          //draw the arena based on current state
          cleararena(arena);
          totalprojectiles=0;
          for(loop=0;loop<MAXWEAPONS;loop++)
          {
            if(bullets[loop][0]!= -1)
            {
              arena[bullets[loop][1]][bullets[loop][0]]='B';
              sprintf(projectiles[totalprojectiles], "%c %d %d %s\n", 'B', bullets[loop][0], bullets[loop][1], directions[bullets[loop][2]]);
              totalprojectiles+=1;
            }
            if(missiles[loop][0]!= -1)
            {
              arena[missiles[loop][1]][missiles[loop][0]]='M';
              sprintf(projectiles[totalprojectiles], "%c %d %d %s\n", 'M', missiles[loop][0], missiles[loop][1], directions[missiles[loop][2]]);
              totalprojectiles+=1;
            }
            if(landmines[loop][0]!= -1)
            {
              arena[landmines[loop][1]][landmines[loop][0]]='L';
              sprintf(projectiles[totalprojectiles], "%c %d %d\n", 'L', landmines[loop][0], landmines[loop][1]);
              totalprojectiles+=1;
            }
          }

          //send the arena to both bots to get the commands
          // create bot1's input
          arena[b1.y][b1.x]='Y';
          arena[b2.y][b2.x]='X';
          sprintf(bot1string, "Y %d\n", b1.energy);
          sprintf(bot2string, "X %d\n", b2.energy);
          strcpy(argumentstring, "'");
          strncat(argumentstring, *arena, 10*11);
          strcat(argumentstring, bot1string);
          strcat(argumentstring, bot2string);
          for(loop=0;loop<totalprojectiles;loop++)
          {
            strcat(argumentstring, projectiles[loop]);
          }
          strcat(argumentstring, "'");
          sprintf(openstring, "%s %s", bots[bot1], argumentstring);
          // send it and get the command back
          fp=popen(openstring, "r");
          fgets(b1.cmd, 5, fp);
          fflush(NULL);
          pclose(fp);

          // create bot2's input
          arena[b2.y][b2.x]='Y';
          arena[b1.y][b1.x]='X';
          sprintf(bot2string, "Y %d\n", b2.energy);
          sprintf(bot1string, "X %d\n", b1.energy);
          strcpy(argumentstring, "'");
          strncat(argumentstring, *arena, 10*11);
          strcat(argumentstring, bot2string);
          strcat(argumentstring, bot1string);
          for(loop=0;loop<totalprojectiles;loop++)
          {
            strcat(argumentstring, projectiles[loop]);
          }
          strcat(argumentstring, "'");
          sprintf(openstring, "%s %s", bots[bot2], argumentstring);
          // send it and get the command back
          fp=popen(openstring, "r");
          fgets(b2.cmd, 5, fp);
          fflush(NULL);
          pclose(fp);

          if(DISPLAYBOUTS)
          {
            arena[b1.y][b1.x]='A';
            arena[b2.y][b2.x]='B';
            printf("\033c");
            printf("Round: %d\n", round);
            printf("%s", arena);
            sprintf(bot1string, "A %d\n", b1.energy);
            sprintf(bot2string, "B %d\n", b2.energy);
            printf("%s%s", bot1string, bot2string);
          }

          //do bot movement phase
          if(paralyzedturnsremaining==0)
          {
            // move bot 1 first
            bot1moved=false;
            dx=dy=0;
            dx=getxmove(b1.cmd);
            dy=getymove(b1.cmd);
            if(newposinbounds(b1.x, b1.y, dx, dy))
            {
              if(!(b1.x+dx==b2.x) || !(b1.y+dy==b2.y))
              {
                bot1moved=true;
                b1.x=b1.x+dx;
                b1.y=b1.y+dy;
              }
            }
            // move bot 2 next
            dx=dy=0;
            dx=getxmove(b2.cmd);
            dy=getymove(b2.cmd);
            if(newposinbounds(b2.x, b2.y, dx, dy))
            {
              if(!(b2.x+dx==b1.x) || !(b2.y+dy==b1.y))
              {
                b2.x=b2.x+dx;
                b2.y=b2.y+dy;
              }
            }
            if(!bot1moved) // if bot2 was in the way first time, try again
            {
              dx=dy=0;
              dx=getxmove(b1.cmd);
              dy=getymove(b1.cmd);
              if(newposinbounds(b1.x, b1.y, dx, dy))
              {
                if(!(b1.x+dx==b2.x) || !(b1.y+dy==b2.y))
                {
                  b1.x=b1.x+dx;
                  b1.y=b1.y+dy;
                }
              }
            }
            //check for landmine hits
            for(loop=0;loop<MAXWEAPONS;loop++)
            {
              if(landmines[loop][0]!= -1)
              {
                if(directhit(b1, landmines[loop]))
                {
                  b1.energy-=2;
                  if(inshrapnelrange(b2, landmines[loop]))
                  {
                    b2.energy-=1;
                  }
                  landmines[loop][0]= -1;
                  landmines[loop][1]= -1;
                }
                if(directhit(b2, landmines[loop]))
                {
                  b2.energy-=2;
                  if(inshrapnelrange(b1, landmines[loop]))
                  {
                    b1.energy-=1;
                  }
                  landmines[loop][0]= -1;
                  landmines[loop][1]= -1;
                }
              }
            }
          }
          else
          {
            paralyzedturnsremaining-=1;
          }
          //do weapons firing phase
          if(strcmp(b1.cmd, "P")==0)
          {
            paralyzedturnsremaining=2;
            b1.energy--;
          }
          else if(strcmp(b2.cmd, "P")==0)
          {
            paralyzedturnsremaining=2;
            b2.energy--;
          }
          deployweapons(&b1, &b2, bullets, missiles, landmines, directions);
          deployweapons(&b2, &b1, bullets, missiles, landmines, directions);
          //do weapons movement phase
          int moves;
          for(loop=0;loop<MAXWEAPONS;loop++)
          {
            dx=dy=0;
            if(bullets[loop][0]!= -1)
            {
              dx=getxmove(directions[bullets[loop][2]]);
              dy=getymove(directions[bullets[loop][2]]);
              for(moves=0;moves<3;moves++)
              {
                if(newposinbounds(bullets[loop][0], bullets[loop][1], dx, dy))
                {
                  bullets[loop][0]+=dx;
                  bullets[loop][1]+=dy;
                  if(directhit(b1, bullets[loop]))
                  {
                    b1.energy-=1;
                    bullets[loop][0]= -1;
                    bullets[loop][1]= -1;
                    bullets[loop][2]= -1;
                  }
                  if(directhit(b2, bullets[loop]))
                  {
                    b2.energy-=1;
                    bullets[loop][0]= -1;
                    bullets[loop][1]= -1;
                    bullets[loop][2]= -1;
                  }
                }
                else
                {
                  bullets[loop][0]= -1;
                  bullets[loop][1]= -1;
                  bullets[loop][2]= -1;
                  dx=dy=0;
                }
              }
            }
          };
          for(loop=0;loop<MAXWEAPONS;loop++)
          {
            dx=dy=0;
            if(missiles[loop][0]!= -1)
            {
              dx=getxmove(directions[missiles[loop][2]]);
              dy=getymove(directions[missiles[loop][2]]);
              for(moves=0;moves<2;moves++)
              {
                if(newposinbounds(missiles[loop][0], missiles[loop][1], dx, dy))
                {
                  missiles[loop][0]+=dx;
                  missiles[loop][1]+=dy;
                  if(directhit(b1, missiles[loop]))
                  {
                    b1.energy-=3;
                    if(inshrapnelrange(b2, missiles[loop]))
                    {
                      b2.energy-=1;
                    }
                    missiles[loop][0]= -1;
                    missiles[loop][1]= -1;
                    missiles[loop][2]= -1;
                  }
                  if(directhit(b2, missiles[loop]))
                  {
                    b2.energy-=3;
                    if(inshrapnelrange(b1, missiles[loop]))
                    {
                      b1.energy-=1;
                    }
                    missiles[loop][0]= -1;
                    missiles[loop][1]= -1;
                    missiles[loop][2]= -1;
                  }
                }
                else
                {
                  if(inshrapnelrange(b1, missiles[loop]))
                  {
                    b1.energy-=1;
                  }
                  if(inshrapnelrange(b2, missiles[loop]))
                  {
                    b2.energy-=1;
                  }
                  missiles[loop][0]= -1;
                  missiles[loop][1]= -1;
                  missiles[loop][2]= -1;
                  dx=dy=0;
                }
              }
            }
          }
          //check if there's a winner
          if(b1.energy<1 || b2.energy<1)
          {
            round=ROUNDSPERBOUT;
          }
        }
        // who has won the bout
        if(b1.energy<b2.energy)
        {
          bot2bouts+=1;
          boutswon[bot2]+=1;
        }
        else if(b2.energy<b1.energy)
        {
          bot1bouts+=1;
          boutswon[bot1]+=1;
        }
      }
      if(bot1bouts>bot2bouts)
      {
        matcheswon[bot1]+=1;
      }
      else if(bot2bouts>bot1bouts)
      {
        matcheswon[bot2]+=1;
      }
      printf("\n");
    }
  }
  // output final scores
  printf("\nResults:\n");
  printf("Bot\t\t\tMatches\tBouts\n");
  for(loop=0;loop<NUMBOTS;loop++)
  {
    printf("%s\t%d\t%d\n", bots[loop], matcheswon[loop], boutswon[loop]);
  }
}

int getxmove(char cmd[5])
{
  int dx=0;
  if(strcmp(cmd, "NE")==0)
    dx= 1;
  else if(strcmp(cmd, "E")==0)
    dx= 1;
  else if(strcmp(cmd, "SE")==0)
    dx= 1;
  else if(strcmp(cmd, "SW")==0)
    dx= -1;
  else if(strcmp(cmd, "W")==0)
    dx= -1;
  else if(strcmp(cmd, "NW")==0)
    dx= -1;

  return dx;
}
int getymove(char cmd[5])
{
  int dy=0;
  if(strcmp(cmd, "N")==0)
    dy= -1;
  else if(strcmp(cmd, "NE")==0)
    dy= -1;
  else if(strcmp(cmd, "SE")==0)
    dy= 1;
  else if(strcmp(cmd, "S")==0)
    dy= 1;
  else if(strcmp(cmd, "SW")==0)
    dy= 1;
  else if(strcmp(cmd, "NW")==0)
    dy= -1;

  return dy;
}
int newposinbounds(int oldx, int oldy, int dx, int dy)
{
  return (oldx+dx>=0 && oldx+dx<10 && oldy+dy>=0 && oldy+dy<10);
}
int directhit(Bot bot, int landmine[2])
{
  return (bot.x==landmine[0] && bot.y==landmine[1]);
}
int landminecollision(int landmine1[2], int landmine2[2])
{
  return ((landmine1[1]==landmine2[1]) && abs(landmine1[0]==landmine2[0]));
}
int inshrapnelrange(Bot bot, int landmine[2])
{
  return (abs(bot.x-landmine[0])<2 && abs(bot.y-landmine[1])<2);
}
int directiontoint(char direction[5], char directions[8][3])
{
  int loop,returnval=8;
  for(loop=0;loop<8;loop++)
  {
    if(strcmp(directions[loop], direction)==0)
      returnval=loop;
  }
  return returnval;
}
void deployweapons(Bot *bot, Bot *enemy, int bullets[MAXWEAPONS][3], int missiles[MAXWEAPONS][3], int landmines[MAXWEAPONS][2], char directions[8][3])
{
  int loop;
  if(strlen(bot->cmd)>2)
  {
    if(bot->cmd[0]=='B')
    {
      int weaponslot=0;
      while(bullets[weaponslot][0]!= -1)
        weaponslot+=1;
      bullets[weaponslot][0]=bot->x;
      bullets[weaponslot][1]=bot->y;
      bullets[weaponslot][2]=directiontoint(bot->cmd+2, directions);
      if(bullets[weaponslot][2]>7)
      {
        // direction wasn't recognized so clear the weapon
        bullets[weaponslot][0]= -1;
        bullets[weaponslot][1]= -1;
        bullets[weaponslot][2]= -1;
      }
    }
    if(bot->cmd[0]=='M')
    {
      int weaponslot=0;
      while(missiles[weaponslot][0]!= -1)
        weaponslot+=1;
      missiles[weaponslot][0]=bot->x;
      missiles[weaponslot][1]=bot->y;
      missiles[weaponslot][2]=directiontoint(bot->cmd+2, directions);
      if(missiles[weaponslot][2]>7)
      {
        // direction wasn't recognized so clear the weapon
        missiles[weaponslot][0]= -1;
        missiles[weaponslot][1]= -1;
        missiles[weaponslot][2]= -1;
      }
    }
    if(bot->cmd[0]=='L')
    {
      int weaponslot=0;
      while(landmines[weaponslot][0]!= -1)
        weaponslot+=1;
      if(newposinbounds(bot->x, bot->y, getxmove(bot->cmd+2), getymove(bot->cmd+2)))
      {
        landmines[weaponslot][0]=bot->x+getxmove(bot->cmd+2);
        landmines[weaponslot][1]=bot->y+getymove(bot->cmd+2);

        //check for landmine hits
        for(loop=0;loop<MAXWEAPONS;loop++)
        {
          if(landmines[loop][0]!= -1)
          {
            if(landminecollision(landmines[weaponslot], landmines[loop]) && weaponslot!=loop)
            {
              if(inshrapnelrange(*bot, landmines[loop]))
              {
                bot->energy-=1;
              }
              if(inshrapnelrange(*enemy, landmines[loop]))
              {
                enemy->energy-=1;
              }
              landmines[loop][0]= -1;
              landmines[loop][1]= -1;
              landmines[weaponslot][0]= -1;
              landmines[weaponslot][1]= -1;
            }
          }
        }
      }
    }
  }
}
void cleararena(char arena[10][11])
{
  int loop;
  memset(arena, '.', 110);
  for(loop=0;loop<10;loop++)
  {
    arena[loop][10]='\n';
  }
}

El programa de control llamará a su bot desde la línea de comandos. Por esta razón, los programas que no se pueden llamar desde la línea de comando se considerarán inválidos . Pido disculpas a aquellos cuyo lenguaje de elección no funciona de esa manera, pero hacer cada partido manualmente no sería práctico.

intx13 ha escrito amablemente una versión más robusta del programa de control con algunas correcciones de errores que puede encontrar aquí .

Las sugerencias de mejoras o correcciones de errores en el programa de control son bienvenidas.

Prueba de bots

Ninguno de los bots de prueba se incluirá en las carreras de puntuación. Son solo para fines de prueba.

Dudley DoNowt (C)

int main(int argc, char *argv)
{
  printf("0");
}

No hace nada independientemente de la situación. No se espera ganar mucho.

HuggyBot (PHP)

<?php
$arena=$argv[1];
list($meX, $meY)=findMe($arena);
list($oppX, $oppY)=findOpp($arena);
if($meY<$oppY)
{
  if($meX<$oppX)
    echo "SE";
  elseif($meX==$oppX)
    echo "S";
  else
    echo "SW";
}
elseif($meY==$oppY)
{
  if($meX<$oppX)
    echo "E";
  else
    echo "W";
}
else
{
  if($meX<$oppX)
    echo "NE";
  elseif($meX==$oppX)
    echo "N";
  else
    echo "NW";
}

function findMe($arena)
{
  return find("Y", explode("\n", $arena));
}

function findOpp($arena)
{
  return find("X", explode("\n", $arena));
}

function find($char, $array)
{
  $x=0;
  $y=0;
  for($loop=0;$loop<10;$loop++)
  {
    if(strpos($array[$loop], $char)!==FALSE)
    {
      $x=strpos($array[$loop], $char);
      $y=$loop;
    }
  }
  return array($x, $y);
}
?>

Trata de estar justo al lado del oponente. Vulnerable a las minas terrestres ya que no las busca. Hace que disparar misiles sea una táctica menos efectiva para el oponente cuando logra su objetivo.

Los resultados

La carrera de puntuación final se realizará después de las 23:59 del 24 de marzo de 2014 . Realizaré pruebas de forma regular para que los participantes puedan ver cómo sus bots se están comparando con la oposición actual.

Entradas

Las entradas deben incluir la fuente de su bot y el argumento de la línea de comando que necesitaré para ejecutarlo. Puede publicar tantas entradas diferentes como desee, pero cada respuesta debe contener solo un bot.

Importante

Parece que algunas entradas desean escribir en el disco para conservar cierto estado entre ejecuciones. Estas son nuevas reglas con respecto a la escritura en el disco.

  • Puede modificar la fuente de su propio bot. Modificar cualquier otro bot es hacer trampa y resultará en la descalificación del bot infractor.
  • Puede escribir en un archivo creado con el propósito de almacenar el estado. Este archivo debe almacenarse en un subdirectorio del directorio donde se encuentra su bot. El subdirectorio será nombrado state. No se permite escribir en ninguna otra parte del sistema de archivos (que no sea su propia fuente).
Gareth
fuente
1
Lo siento, no capté esto en el Sandbox: ¿el suministro de todas las armas es infinito?
Jonathan Van Matre
2
@ intx13 Quizás el bot correspondiente al bot 1 y el correspondiente al bot 2 no deberían ser aleatorios en cada combate, sino elegidos aleatoriamente solo en el primer combate e intercambiados al comienzo de cada combate siguiente.
arshajii
2
Creo que encontré un error en el código de @ intx13. Cuando ambos bot disparan el EMP al mismo tiempo, creo que ambos deberían perder energía. No he ejecutado su código, pero mirándolo, este no parece ser el caso. Ver líneas 295-304 github.com/gazrogers/CodegolfBattlebotsScorer/blob/master/…
Thomas Eding
2
Otro error potencial. Parece que la energía del robot puede ir por debajo de cero. Esto está bien, pero si Bot1 tiene -1 energía y Bot2 tiene 0 energía, ninguno debería anotar una victoria.
Thomas Eding
2
Gracias por este torneo, @Gareth. Fue un buen desafío y me inspiró a explorar nuevas áreas de programación. Desafiará a algunos de mis amigos, creo. :) Agradece a todos los participantes, ¡buen juego!
Corwin

Respuestas:

14

EvilBot

un bot que intenta ser lo más malvado posible

Bueno, esto es lo que tengo: un robot de Java que intenta acercarse lo más posible al oponente a una franja circular de radio 2.5 alrededor del centro de la arena y luego hacer el mayor daño posible cuando pueda. Su patrón de movimiento se basa en asignar un valor de "peligro" a cada una de sus casillas vecinas, y en decidir moverse según estos valores y en una tendencia a estar tan cerca de una región circular de radio 2.5 alrededor del centro de la arena. Utilicé algunos de los detalles de la respuesta de @ Geobits (por ejemplo, tener un resumenBattleBotclase y la técnica de análisis), ¡así que gracias! Probablemente voy a modificar / expandir lo que tengo hasta ahora, aunque le va bastante bien, al igual que con los otros bots publicados hasta ahora. El código está abajo. (si alguien más está usando Java, siéntase libre de usar mis clases abstractas / auxiliares).

( EvilBot.java)

import java.io.File; // debugging
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Scanner; // debugging

class Point {

    private int x, y;

    public Point(int x, int y) {
        this.x = x;
        this.y = y;
    }

    public int getX() {
        return x;
    }

    public int getY() {
        return y;
    }

    public int distTo(Point other) {
        return Math.max(Math.abs(x - other.x), Math.abs(y - other.y));
    }

    public double conventionalDistTo(Point other) {
        return Math.hypot(x - other.x, y - other.y);
    }

    @Override
    public boolean equals(Object other) {
        if (!(other instanceof Point))
            return false;

        Point otherPoint = (Point) other;

        return x == otherPoint.x && y == otherPoint.y;
    }

    @Override
    public int hashCode() {
        return x * (1 << Arena.ARENA_SIZE) + y;
    }

    @Override
    public String toString() {
        return "(" + x + "," + y + ")";
    }
}

interface ArenaElement {
    char getSymbol();
}

enum Projectile implements ArenaElement {

    BULLET('B', 3, 1) {

    },

    MISSILE('M', 2, 3) {

    },

    LANDMINE('L', 0, 2) {
        @Override
        public int timeUntilImpact(Point current, Point target, Direction dir) {
            return current.equals(target) ? 0 : -1;
        }
    };

    private final char symbol;
    private final int speed;
    private final int damage;

    private Projectile(char symbol, int speed, int damage) {
        this.symbol = symbol;
        this.speed = speed;
        this.damage = damage;
    }

    @Override
    public char getSymbol() {
        return symbol;
    }

    public int getSpeed() {
        return speed;
    }

    public int getDamage() {
        return damage;
    }

    public static Projectile fromSymbol(char symbol) {
        for (Projectile p : values()) {
            if (p.getSymbol() == symbol)
                return p;
        }

        return null;
    }

    public int timeUntilImpact(Point current, Point target, Direction dir) {

        final int dx = target.getX() - current.getX();
        final int dy = target.getY() - current.getY();

        if (!(dx == 0 || dy == 0 || dx == dy || dx == -dy))
            return -1;

        if (dx == 0) {
            if (dy > 0 && dir != Direction.N)
                return -1;

            if (dy < 0 && dir != Direction.S)
                return -1;
        }
        if (dy == 0) {
            if (dx > 0 && dir != Direction.E)
                return -1;

            if (dx < 0 && dir != Direction.W)
                return -1;
        }
        if (dx == dy) {
            if (dx > 0 && dir != Direction.NE)
                return -1;

            if (dx < 0 && dir != Direction.SW)
                return -1;
        }
        if (dx == -dy) {
            if (dx > 0 && dir != Direction.SE)
                return -1;

            if (dx < 0 && dir != Direction.NW)
                return -1;
        }

        int dist = target.distTo(current);

        return (dist / speed) + (dist % speed == 0 ? 0 : 1);
    }
}

enum BotType implements ArenaElement {

    ME('Y'), ENEMY('X');

    private final char symbol;

    private BotType(char symbol) {
        this.symbol = symbol;
    }

    @Override
    public char getSymbol() {
        return symbol;
    }

    public static BotType fromSymbol(char symbol) {
        for (BotType bt : values()) {
            if (bt.getSymbol() == symbol)
                return bt;
        }

        return null;
    }
}

enum EmptySpot implements ArenaElement {

    EMPTY;

    @Override
    public char getSymbol() {
        return '.';
    }

    public static EmptySpot fromSymbol(char symbol) {
        for (EmptySpot es : values()) {
            if (es.getSymbol() == symbol)
                return es;
        }

        return null;
    }
}

enum Direction {
    N, NE, E, SE, S, SW, W, NW
}

class Arena {

    public static final int ARENA_SIZE = 10;
    public static final Point center = new Point(ARENA_SIZE / 2, ARENA_SIZE / 2);

    private ArenaElement[][] arena;

    private Arena(boolean fill) {
        arena = new ArenaElement[ARENA_SIZE][ARENA_SIZE];

        if (!fill)
            return;

        for (int i = 0; i < ARENA_SIZE; i++) {
            for (int j = 0; j < ARENA_SIZE; j++) {
                arena[i][j] = EmptySpot.EMPTY;
            }
        }
    }

    public boolean inBounds(int x, int y) {
        return x >= 0 && x < ARENA_SIZE && y >= 0 && y < ARENA_SIZE;
    }

    public boolean inBounds(Point p) {
        final int x = p.getX(), y = p.getY();
        return inBounds(x, y);
    }

    public ArenaElement get(int x, int y) {
        if (!inBounds(x, y)) {
            return null; // be cautious of this
        }

        return arena[ARENA_SIZE - 1 - y][x];
    }

    public ArenaElement get(Point p) {
        return get(p.getX(), p.getY());
    }

    // note: a point is considered its own neighbor
    public List<Point> neighbors(Point p) {
        List<Point> neighbors = new ArrayList<Point>(9);

        for (int i = -1; i <= 1; i++) {
            for (int j = -1; j <= 1; j++) {
                Point p1 = new Point(p.getX() + i, p.getY() + j);

                if (get(p1) != null)
                    neighbors.add(p1);
            }
        }

        return neighbors;
    }

    public Point findMe() {
        for (int i = 0; i < ARENA_SIZE; i++) {
            for (int j = 0; j < ARENA_SIZE; j++) {
                if (get(i, j) == BotType.ME)
                    return new Point(i, j);
            }
        }

        return null;
    }

    public Point findEnemy() {
        for (int i = 0; i < ARENA_SIZE; i++) {
            for (int j = 0; j < ARENA_SIZE; j++) {
                if (get(i, j) == BotType.ENEMY)
                    return new Point(i, j);
            }
        }

        return null;
    }

    public Point impactOfRayFromPointInDirection(Point p, Direction dir) {
        int x = p.getX(), y = p.getY();

        switch (dir) {
        case N:
            y += (Arena.ARENA_SIZE - 1 - y);
            break;
        case NE: {
            int dx = (Arena.ARENA_SIZE - 1 - x);
            int dy = (Arena.ARENA_SIZE - 1 - y);

            int off = Math.max(dx, dy);

            x += off;
            y += off;
            break;
        }
        case E:
            x += (Arena.ARENA_SIZE - 1 - x);
            break;
        case SE: {
            int dx = (Arena.ARENA_SIZE - 1 - x);
            int dy = y;

            int off = Math.max(dx, dy);

            x += off;
            y -= off;
            break;
        }
        case S:
            y = 0;
            break;
        case SW: {
            int dx = x;
            int dy = y;

            int off = Math.max(dx, dy);

            x -= off;
            y -= off;
            break;
        }
        case W:
            x = 0;
            break;
        case NW: {
            int dx = x;
            int dy = (Arena.ARENA_SIZE - 1 - y);

            int off = Math.max(dx, dy);

            x -= off;
            y += off;
            break;
        }
        }

        return new Point(x, y);
    }

    private static ArenaElement fromSymbol(char symbol) {
        ArenaElement e = EmptySpot.fromSymbol(symbol);

        if (e != null)
            return e;

        e = Projectile.fromSymbol(symbol);

        if (e != null)
            return e;

        return BotType.fromSymbol(symbol);
    }

    public static Arena parse(String[] input) {
        Arena arena = new Arena(false);

        for (int i = 0; i < ARENA_SIZE; i++) {
            for (int j = 0; j < ARENA_SIZE; j++) {
                char symbol = input[i].charAt(j);

                arena.arena[i][j] = fromSymbol(symbol);
            }
        }

        return arena;
    }
}

abstract class BaseBot {

    protected static class ProjectileInfo {
        Projectile projectile;
        Point position;
        Direction direction;

        @Override
        public String toString() {
            return projectile.toString() + " " + position + " " + direction;
        }
    }

    protected Arena arena;

    protected Point myPos;
    protected int energy;

    protected Point enemyPos;
    protected int enemyEnergy;

    public List<ProjectileInfo> projectiles;

    public BaseBot(String[] args) {
        if (args.length < 1)
            return;

        String[] lines = args[0].split("\r?\n");

        projectiles = new ArrayList<ProjectileInfo>(lines.length
                - Arena.ARENA_SIZE - 2);

        arena = Arena.parse(lines);
        myPos = arena.findMe();
        enemyPos = arena.findEnemy();

        for (int i = Arena.ARENA_SIZE; i < lines.length; i++) {
            parseInputLine(lines[i]);
        }
    }

    private void parseInputLine(String line) {
        String[] split = line.split(" ");

        char c0 = line.charAt(0);
        if (c0 == 'Y') {
            energy = Integer.parseInt(split[1]);
        } else if (c0 == 'X') {
            enemyEnergy = Integer.parseInt(split[1]);
        } else {
            ProjectileInfo pinfo = new ProjectileInfo();
            pinfo.projectile = Projectile.fromSymbol(split[0].charAt(0));
            pinfo.position = new Point(Integer.parseInt(split[1]),
                    Arena.ARENA_SIZE - 1 - Integer.parseInt(split[2]));

            if (split.length > 3)
                pinfo.direction = Direction.valueOf(split[3]);

            projectiles.add(pinfo);
        }
    }

    abstract String getMove();
}

public class EvilBot extends BaseBot {

    public static final boolean DEBUG = false;

    public static void main(String... args) throws Exception {
        if (DEBUG) {
            StringBuffer input = new StringBuffer();
            Scanner scan = new Scanner(new File("a.txt"));

            while (scan.hasNextLine()) {
                input.append(scan.nextLine());
                input.append('\n');
            }

            scan.close();

            args = new String[] { input.toString() };
        }

        System.out.print(new EvilBot(args).getMove());
    }

    public EvilBot(String[] args) {
        super(args);
    }

    /*
     * Direction to p if perfectly aligned, null otherwise
     */
    private Direction getDirTo(Point p) {

        final int dx = p.getX() - myPos.getX();
        final int dy = p.getY() - myPos.getY();

        if (dx == 0) {
            return (dy > 0) ? Direction.N : Direction.S;
        }
        if (dy == 0) {
            return (dx > 0) ? Direction.E : Direction.W;
        }
        if (dx == dy) {
            return (dy > 0) ? Direction.NE : Direction.SW;
        }
        if (dx == -dy) {
            return (dy > 0) ? Direction.NW : Direction.SE;
        }

        return null;
    }

    /*
     * Direction towards p (best approximation)
     */
    private Direction getDirTowards(Point p) {
        Direction minDir = null;
        double minDist = 0;

        for (Direction dir : Direction.values()) {
            double dist = arena.impactOfRayFromPointInDirection(myPos, dir)
                    .conventionalDistTo(p);

            if (minDir == null || dist < minDist) {
                minDir = dir;
                minDist = dist;
            }
        }

        return minDir;
    }

    private boolean isEnemyCloseToWall() {
        return (enemyPos.getX() < 2 || enemyPos.getY() < 2
                || enemyPos.getX() > Arena.ARENA_SIZE - 3 || enemyPos.getY() > Arena.ARENA_SIZE - 3);
    }

    private String missileAttack() {
        return "M " + getDirTowards(enemyPos);
    }

    @Override
    public String getMove() {
        List<Point> neighbors = arena.neighbors(myPos);

        Map<Point, Double> dangerFactors = new HashMap<Point, Double>();

        for (Point neighbor : neighbors) {

            double dangerFactor = 0;

            if (arena.get(neighbor) == Projectile.LANDMINE) {
                dangerFactor += 2;
            }

            for (ProjectileInfo pi : projectiles) {

                int time = pi.projectile.timeUntilImpact(pi.position, neighbor,
                        pi.direction);

                if (time > 0) {
                    dangerFactor += ((double) pi.projectile.getDamage()) / time;
                }
            }

            dangerFactors.put(neighbor, dangerFactor);
        }

        if (dangerFactors.get(myPos) == 0) {
            // we are safe for now...

            Direction dir = getDirTo(enemyPos);
            boolean closeToWall = isEnemyCloseToWall();

            if (dir != null) {
                int dist = myPos.distTo(enemyPos);

                if (dist < Projectile.MISSILE.getSpeed() * 2) {
                    return "M " + dir;
                } else {
                    return "B " + dir;
                }
            } else if (closeToWall) {

                if (Math.random() > 0.5) // so we don't get caught in loops
                    return missileAttack();
            }
        }

        // move!
        double leastDanger = Double.POSITIVE_INFINITY;

        for (Entry<Point, Double> entry : dangerFactors.entrySet()) {
            if (entry.getValue() < leastDanger)
                leastDanger = entry.getValue();
        }

        Point moveTo = null;

        for (Entry<Point, Double> entry : dangerFactors.entrySet()) {
            if (entry.getKey().equals(myPos))
                continue;

            if (entry.getValue() == leastDanger) {

                double d1 = entry.getKey().conventionalDistTo(Arena.center);
                double d2 = moveTo == null ? 0 : moveTo
                        .conventionalDistTo(Arena.center);

                if (moveTo == null || Math.abs(d1 - 2.5) < Math.abs(d2 - 2.5)) {

                    moveTo = entry.getKey();
                }
            }
        }

        if (moveTo == null) {
            return missileAttack();
        }

        return getDirTo(moveTo).toString();
    }
}

Uso:

javac EvilBot.java
java EvilBot <input>

Notas:

  • Actualmente, las minas terrestres no se utilizan, solo se esquivan. Probablemente no voy a cambiar esto, ya que el uso de minas terrestres parece hacer más daño que bien (al menos para EvilBot) a juzgar por algunas pruebas que realicé.

  • Actualmente, EMP no se está utilizando. Intenté la estrategia de alinearme con el oponente y disparar el EMP seguido de misiles, pero hay algunas contraestrategias que ganarían casi el 100% del tiempo, así que decidí abandonar esa ruta. Podría explorar el uso del EMP de diferentes maneras más adelante.

arshajii
fuente
En promedio, EvilBot supera a Straight Shooter 5-0 y Dodging Turret 2-0. Se vincula mucho con Dodging Turret.
intx13
@ intx13 Sí, me di cuenta de eso también. Tanto EvilBot como Dodging Turret también empatan con King's Last Stand; El juego se reduce a un bucle infinito.
arshajii
2
@arshajii ¡Felicidades, ganaste!
Gareth
17

Rifter

Este bot realiza diferentes acciones en función de qué bot está luchando. Para determinar al oponente, cambia su propio estado y lo alimenta a los otros bots para ver qué harían, y lo compara con lo que realmente hacen. Una vez que alcanzan un umbral de movimientos "correctos", deja de probar a los demás.

Una vez que sabe a qué bot está luchando, generalmente sabe dónde estará en el próximo turno, por lo que puede disparar allí en lugar de su posición actual.

Por supuesto, hay algunos inconvenientes. Una es que los bots que tienen actividad "aleatoria" no se detectan tan bien. Esto se equilibra mediante el uso de la lógica King's Last Stand cuando no se conoce al oponente.

Sin embargo, si un bot es puramente determinista, no tiene problemas para descubrir quién es. Luego se puede adaptar fácilmente a la situación agregando más casos en su lógica para cada oponente. Por ejemplo, al luchar contra Last Stand, lo arrinconará, se mantendrá 2x1 de distancia para que no pueda moverse o disparar directamente, y disparará misiles contra la pared detrás, matándolo con daños por salpicadura.

Como mis otros, extiende BattleBot.java:

import java.awt.Point;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class Rifter extends BattleBot{

    String output="0";
    String state;
    String oldState = null;
    List<Rift> rifts;
    Rift chosen;
    List<Point> safe;
    Point probable;
    int round;

    final int testCount = 100;

    Rifter(String[] args) {
        super(args.length>0?args:testState);
        state = args.length>0?args[0]:testState[0];
        round = 0;
    }

    public static void main(String[] args) {
        debug = false;
        System.out.print(new Rifter(args).execute());
    }

    @Override
    String execute() {
        if(!valid)
            return "0";
        init();
        probable = getLikelyPosition();
        if(!safe.contains(yPosition) && evade())
            return output;
        if(riftShift())
            return output;
        return fallback();
    }

    boolean riftShift(){
        if(chosen==null)
            return false;
        if("P".equals(chosen.nextAction))
            return fireAt(xPosition, true);
        switch(getChosenIndex()){
        case 1:
            output = fightStand();
            break;
        case 2:
            output = fightEvil();
            break;
        default:
            output = fallback();
        }
        return output.equals("0")?false:true;
    }

    int getChosenIndex(){
        for(int i=0;i<baseBots.length;i++)
            if(chosen.bot.equals(baseBots[i]))
                return i;
        return -1;
    }

    int distanceToWall(Point pos){
        int min = Math.min(pos.x,  pos.y);
        min = Math.min(min, (arenaSize - 1) - pos.x);
        return Math.min(min, (arenaSize - 1) - pos.y);
    }

    String fightStand(){
        int wall = distanceToWall(xPosition);
        if(wall > 0 || distance(yPosition, probable) > 2){
            if(moveToward(probable, NONE))
                return output;
            if(fireAt(probable, false))
                return output;
        }

        if(probable.x==0 && probable.y==0)
            return "M NW";
        if(probable.x==arenaSize-1 && probable.y==0)
            return "M NE";
        if(probable.x==arenaSize-1 && probable.y == arenaSize-1)
            return "M SE";
        if(probable.x==0 && probable.y == arenaSize-1)
            return "M SW";
        if(probable.x==0)
            return "M W";
        if(probable.x==arenaSize-1)
            return "M E";
        if(probable.y==0)
            return "M N";
        if(probable.y==arenaSize-1)
            return "M S";

        return "M " + headings[headingToPoint(probable)];
    }

    String fightEvil(){
        if(areAligned(yPosition,xPosition)){
            if(distance(yPosition,xPosition)>3)
                if(moveToward(probable,UNALIGN))
                    return output;
            if(fireAt(probable, false))
                return output;
        }
        if(fireAt(probable, false))
            return output;
        if(moveToward(center, ALIGN))
            return output;
        return "0";
    }

    String fallback(){
        output = getOutputFrom(fallbackBots[rand.nextInt(fallbackBots.length)]);
        if(output==null)
            output="0";
        return output;
    }

    int NONE = 0;
    int ALIGN = 1;
    int UNALIGN = 2;

    boolean moveToward(Point target, int align){
        Point closest = new Point(-99,-99);
        for(Point pos : safe){
            if(pos.equals(yPosition))
                continue;
            if(distance(pos,target) < distance(closest,target)){
                if(areAligned(pos,target) && align == UNALIGN)
                    continue;
                if(!areAligned(pos,target) && align == ALIGN)
                    continue;
                closest = pos;
            }
        }

        if(isOutside(closest))
            for(Point pos : safe)
                    if(distance(pos,target) < distance(closest,target))
                        closest = pos;      
        if(distance(closest,target) > distance(yPosition,target))
            return false;
        output = headings[headingToPoint(closest)];
        return true;
    }

    boolean fireAt(Point target, boolean override){
        if(!override && !areAligned(yPosition, target))
            return false;
        int dist = distance(yPosition, target);
        if(!override && dist>3)
            return false;
        int heading = headingToPoint(target);
        output = "M ";
        if(dist > 3 || dist == 1)
            output = "B ";
        output += headings[heading];
        return true;
    }

    String getOutputFrom(String bot){
        return new Rift(bot,0).foretell(state);
    }

    boolean evade(){
        if(safe.isEmpty())
            return false;
        Point dest = null;
        for(Point pos : safe)
            if(areAligned(pos,probable))
                dest = pos;
        if(dest==null){
            output = getOutputFrom("java LastStand");
            return true;
        }
        output = headings[headingToPoint(dest)];
        return true;
    }

    Point getLikelyPosition(){
        if(chosen!=null)
            return chosen.getNextPosition(null);
        if(round > testCount)
            return xPosition;

        int[] arena = new int[arenaSize*arenaSize];
        for(Rift rift : rifts){
            Point next = rift.getNextPosition(null);
            if(!isOutside(next))
                arena[next.y*arenaSize+next.x]++;
        }
        int max = 0, index = -1;
        for(int i=0;i<arena.length;i++){
            if(arena[i] > max){
                max = arena[i];
                index = i;
            }
        }
        Point dest = new Point(index%arenaSize, index/arenaSize);
        return isOutside(dest)?xPosition:dest;
    }

    boolean areAligned(Point a, Point b){
        int x = Math.abs(a.x - b.x);
        int y = Math.abs(a.y - b.y);
        if(x==0 || y==0 || x==y)
            return true;
        return false;
    }

    void init(){
        safe = new ArrayList<Point>();
        if(spotCollision(yPosition)==null)
            safe.add(yPosition);

        for(int heading=0;heading<8;heading++){
            Point pos = nextPosition(heading, yPosition);
            if(isOutside(pos))
                continue;
            if(spotCollision(pos)==null)
                safe.add(pos);
        }

        loadBots(readState());
        updateRifts();
        writeState();
    }

    void updateRifts(){
        if(chosen == null && round < testCount)
            for(Rift rift : rifts)
                if(rift.validate(oldState))
                    rift.correct++;
    }

    Rift chooseBot(){
        double avg = 0.0;
        int highest = 0;
        Rift choice = null;

        for(Rift rift : rifts){
            avg += rift.correct;
            if(rift.correct >= highest){
                highest = rift.correct;
                choice = rift;
            }
        }
        avg /= rifts.size();
        if(choice!= null && (choice.correct > 8) && choice.correct > avg*2)
            return choice;
        else
            return null;
    }

    boolean writeState(){
        File dir = new File("state");
        dir.mkdirs();
        File file = new File("state/rifter.state");
        try {
            BufferedWriter writer = new BufferedWriter(new FileWriter(file));
            writer.write(">" + round + "\n");
            for(Rift rift : rifts)
                writer.write(":" + rift.correct + "|" + rift.bot + "\n");
            writer.write(state);
            writer.flush();
            writer.close();
        } catch (IOException e) {
            log(e.getMessage());
            return false;
        }
        return true;
    }

    List<String> readState(){
        List<String> bots = new ArrayList<String>();
        File file = new File("state/rifter.state");
        if(file.exists()){
            try {
                BufferedReader reader = new BufferedReader(new FileReader(file));
                String line;
                String oldState = "";
                line = reader.readLine();
                if(line != null && line.startsWith(">"))
                    round = Integer.valueOf(line.substring(1)) + 1;
                while((line = reader.readLine()) != null){
                    if(line.startsWith(":"))
                        bots.add(line.substring(1));
                    else 
                        oldState += line + "\n";                                            
                }
                reader.close();
                BattleBot bot = new Rifter(new String[]{oldState});
                if(isStateInvalid(bot)){
                    bots.clear();
                    oldState = "";
                    round = 0;
                }
                this.oldState = oldState;
            } catch(Exception e){
                log(e.getMessage());
                bots.clear();
                this.oldState = "";
            }
        }
        return bots.isEmpty()?Arrays.asList(baseBots):bots;
    }

    boolean isStateInvalid(BattleBot bot){
        if(!bot.valid)
            return true;
        if(distance(bot.xPosition, xPosition) > 1)
            return true;
        if(distance(bot.yPosition, yPosition) > 1)
            return true;
        if(xEnergy > bot.xEnergy || yEnergy > bot.yEnergy)
            return true;
        return false;
    }

    List<Rift> loadBots(List<String> bots){
        rifts = new ArrayList<Rift>();
        String flipped = flipState(state);
        for(String bot : bots){
            String[] tokens = bot.split("\\|");
            Rift rift;
            if(tokens.length < 2)
                rift = new Rift(bot, 0);
            else
                rift = new Rift(tokens[1], Integer.valueOf(tokens[0]));         
            rifts.add(rift);
        }
        if((chosen = chooseBot()) == null)
            if(round < testCount)
                for(Rift rift : rifts)
                    rift.nextAction = rift.foretell(flipped);
        else
            chosen.nextAction = chosen.foretell(flipped);

        return rifts;
    }

    String flipState(String in){
        String tmp = in.replaceAll("X", "Q");
        tmp = tmp.replaceAll("Y", "X");
        tmp = tmp.replaceAll("Q", "Y");
        String[] lines = tmp.split("\\r?\\n");
        tmp = lines[arenaSize];
        lines[arenaSize] = lines[arenaSize+1];
        lines[arenaSize+1] = tmp;
        String out = "";
        for(int i=0;i<lines.length;i++)
            out += lines[i] + "\n";
        return out.trim();
    }

    class Rift{
        String bot;
        String nextAction;
        String state;
        String nextState;
        int correct;

        Rift(String name, int count){
            bot = name;
            correct = count;
        }

        Point getNextPosition(String action){
            if(action==null)
                action = nextAction;
            if(action==null || action.length()<1)
                return xPosition;
            int heading = getHeading(action.split(" ")[0]);
            return nextPosition(heading, xPosition);
        }

        boolean validate(String oldState){
            boolean valid = true;
            if(oldState == null)
                return valid;
            if(oldState.split("\\r?\\n").length < 12)
                return valid;
            String action = foretell(flipState(oldState));
            if(action==null || action.length() < 1){
                log(this.bot + " : " + "invalid action");
                return valid;
            }
            BattleBot bot = new Rifter(new String[]{oldState});
            switch(action.charAt(0)){
            case 'B':
            case 'M':
            case 'L':
                valid = testShot(action, bot);
                break;
            case 'P':
            case '0':
                valid = testNothing(bot);
                break;
            default:
                valid = testMovement(action, bot);
                break;
            }
            log(this.bot + " : " + action + " : " + valid); 

            return valid;
        }

        boolean testNothing(BattleBot bot){
            if(!xPosition.equals(bot.xPosition))
                return false;
            for(Weapon weapon : weapons){
                int dist = weapon.type==LANDMINE?1:weapon.speed;
                log(dist);
                if(distance(weapon.position, bot.xPosition) != dist)
                    continue;
                int dir = weapon.heading;
                if(isHeadingExact(dir,bot.xPosition,weapon.position))
                    return false;
            }
            return true;
        }

        boolean testShot(String act, BattleBot bot){
            if(!xPosition.equals(bot.xPosition))
                return false;
            if(weapons == null)
                return false;
            String[] tokens = act.split(" ");
            char which = tokens[0].charAt(0);
            int type = which=='B'?BULLET:
                   which=='M'?MISSILE:
                              LANDMINE;

            for(Weapon weapon : weapons){
                if(weapon.type != type)
                    continue;
                int dist = weapon.type==LANDMINE?1:weapon.speed;
                log(dist);
                if(distance(weapon.position, bot.xPosition) != dist)
                    continue;
                int dir;
                if(act==null)
                    dir = weapon.heading;
                else if(tokens.length < 2)
                    return false;
                else
                    dir = getHeading(tokens[1]);
                if(isHeadingExact(dir,bot.xPosition,weapon.position))
                    return true;
            }
            return false;

        }

        boolean testMovement(String act, BattleBot bot){
            return xPosition.equals(nextPosition(getHeading(act), bot.xPosition));
        }

        String foretell(String state){
            this.state = state;
            String[] cmdRaw = bot.split(" ");
            String[] cmd = new String[cmdRaw.length+1];
            for(int i=0;i<cmdRaw.length;i++)
                cmd[i] = cmdRaw[i];
            cmd[cmd.length-1]=state;

            String out = null;
            try {
                Process p = Runtime.getRuntime().exec(cmd);
                p.waitFor();
                BufferedReader err = new BufferedReader(new InputStreamReader(p.getErrorStream()));
                String line;
                while((line = err.readLine()) != null){
                    out = line;
                }
                err.close();
                BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream()));
                while((line = reader.readLine()) != null){
                    out = line;
                }
                reader.close();
            } catch (Exception e) {
                log(e.getMessage());
            }
            return out!=null&&out.length()<6&&out.length()>0?out:null;
        }
    }   

    String fallbackBots[] = {"node Neo-Bot.js"};

    String[] baseBots =     {
                             "java EvadeBot", 
                             "java LastStand",
                             "java EvilBot",
                             "python ReadyAimShoot.py",
                             "python DodgingTurret.py",
                             "python mineminemine.py",
                             "python StraightShooter.py",
                             "./RandomBot",
                             "./SpiralBot",
                             "ruby1.9 TroubleAndStrafe.rb",
                             "python3 CunningPlanBot.py",
                             "./CamperBot",
                             "node CentreBot.js",
                             "node Neo-Bot.js",
                             "java UltraBot",
                             "python NinjaPy.py"
    };

    static String[] testState = {".X....LLL.\n..........\n.M........\n..........\nM.........\n..........\n..........\n..........\n.Y........\n...B......\nY 10\nX 7\nM 1 2 S"};
}
Geobits
fuente
Wow ... ¡Tenía curiosidad por saber cuánto tiempo tomaría conseguir bots que reaccionaran a bots particulares! ¿Cómo irá si hay algunas entradas muy tarde?
Lochok
Bueno, planeo agregar más bots a medida que entren. Si hay un par que es demasiado tarde para personalizar, el valor predeterminado sigue funcionando bastante bien.
Geobits
1
¡Ja, también estoy trabajando en un bot que intenta identificar contra qué tipo de bot está luchando! ¡Ahora puedo actualizarlo más y hacer que identifique un bot que identifique otros bots! ¡O quizás te engañe fingiendo algunos movimientos! Muahaha!
Tom Verelst
1
@TomVerelst Ya he pensado algunas formas de frustrarlo fácilmente, así que no me sorprenderá demasiado ver que un nuevo bot lo derriba. Dicho esto, funciona muy bien contra los bots actualmente en el campo, que es lo que estaba buscando. Ganó 8/8 partidos en mi última prueba local. A veces vincula a DodgingTurret, pero no puedo encontrar un patrón de pelea para vencerlo constantemente , por lo que es más fácil dejarlo empatar para negar la victoria.
Geobits
¿Qué pasa si se juega solo?
PyRulez
10

ReadyAimShoot

a R Bot

input <- strsplit(commandArgs(TRUE),split="\\\\n")[[1]]
arena <- do.call(rbind,strsplit(input[1:10],"")) #Parse arena
life <- as.integer(strsplit(input[11:12]," ")[[1]][2]) #Parse stats
stuff <- strsplit(input[13:length(input)]," ") #Parse elements
if(length(input)>12){ #What are they
    stuff <- strsplit(input[13:length(input)]," ")
    whatstuff <- sapply(stuff,`[`,1)
    }else{whatstuff<-""}
if(sum(whatstuff=="L")>1){ #Where are the mines
    mines <- t(apply(do.call(rbind,stuff[whatstuff=="L"])[,3:2],1,as.integer))+1
    }else if(sum(whatstuff=="L")==1){
        mines <- as.integer(stuff[whatstuff=="L"][[1]][3:2])+1
    }else{mines <- c()}
me <- which(arena=="Y",arr.ind=T) #Where am I
other <- which(arena=="X",arr.ind=T) #Where is the target
direction <- other-me #Direction of the other bot in term of indices
if(length(mines)>2){ #Direction of mines in term of indices
    dirmines <- mines-matrix(rep(me,nrow(mines)),nc=2,byrow=T)
    }else if(length(mines)==1){
        dirmines <- mines-me
        }else{dirmines<-c()}
file <- normalizePath(gsub("^--file=","",grep("^--file=",commandArgs(FALSE),v=TRUE))) #Path to this very file
f1 <- readLines(file) #Read-in this source file
where <- function(D){ #Computes direction of something in term of NSWE
    d <- ""
    if(D[2]<0) d <- paste(d,"W",sep="")
    if(D[2]>0) d <- paste(d,"E",sep="")
    if(D[1]<0) d <- paste(d,"N",sep="")
    if(D[1]>0) d <- paste(d,"S",sep="")
    d
    }
d <- where(direction) #Direction of the other bot in term of NSWE
M <- dirmines[dirmines[,1]%in%(-1:1) & dirmines[,2]%in%(-1:1),] #Which mines are next to me
if(length(M)>2){m<-apply(M,1,where)}else if(length(M)==1){m<-where(M)}else{m<-""} #Direction of close-by mines in term of NSWE
if(any(direction==0) & life >1 & !grepl("#p_fired", tail(f1,1))){
    # If aligned with target, if life is more than one 
    # and if this source file doesn't end with a comment saying the EMP was already fired
    # Fire the EMP, and leave comment on this file saying so
    action <- "P"
    f2 <- c(f1,"#p_fired2")
    cat(f2, file=file, sep="\n")
    }else if(tail(f1,1)=="#p_fired2"){
    # If EMP have been fired last turn
    # Send missile in direction of target
    # Change comment on file.
    action <- paste("M", d)
    f2 <- c(f1[-length(f1)], "#p_fired1")
    cat(f2, file=file, sep="\n")
    }else if(tail(f1,1)=="#p_fired1"){
    # If EMP was fired two turns ago
    # Send bullet and erase comment line.
    action <- paste("B", d)
    f2 <- f1[-length(f1)]
    cat(f2, file=file, sep="\n")
    }
if (any(direction==0) & life<2){
    # If aligned but life is 1 don't fire the EMP, but send missile instead
    action <- paste("M",d)
    }
if (!any(direction==0)){
    # If not aligned, try to align using shortest, landmine-free direction
    if(direction[2]<direction[1]){
        if(grepl('W',d) & !'W'%in%m){action <- 'W'}
        if(grepl('E',d) & !'E'%in%m){action <- 'E'}
        }else if(direction[2]>=direction[1]){
            if(grepl('N',d) & !'N'%in%m){action <- 'N'}
            if(grepl('S',d) & !'S'%in%m){action <- 'S'}
            }else{ #If no landmine-free direction, don't move
                action <- 0
                }
    }
cat(action,"\n")

Este bot intenta colocarse en la misma fila o columna que el objetivo, cuando está alineado con el objetivo dispara el EMP, luego, en el siguiente turno, dispara un misil hacia el objetivo y luego una bala. También debe ser consciente de la mina circundante y evitarlos, pero es completamente ajeno a las balas y los misiles. Si la vida ya está en 1, se salta el EMP.
Para realizar un seguimiento de cuándo activa el EMP, modifica su código fuente agregando un comentario al final del archivo ( #p_fired2al principio, luego lo modifica #p_fired1y luego lo borra). Espero que hacer un seguimiento de cuándo desencadena el EMP de esta manera no sea demasiado límite.

La línea de comando debe ser Rscript ReadyAimShoot.R, seguida del argumento como en el ejemplo, al menos en sistemas UNIX pero probablemente también en Windows (lo comprobaré cuando lo pruebe en realidad con los otros bots).

Editar : Dado que la versión R parece tener problemas para analizar la entrada, aquí hay una versión de Python del mismo bot con, espero, funciona. Si cualquier otro programador de R ve la publicación y ve qué le pasa a este bot, ¡no dude en depurar!

import sys, os

def Position(arena, element):
    y = [i for i,j in enumerate(arena) if element in arena[i]][0]
    x = arena[y].index(element)
    return (x,y)

def Direction(coord1, coord2):
    d0 = coord1[0]-coord2[0]
    d1 = coord1[1]-coord2[1]
    if d1!=0:
        a = ['N','S'][d1<0]
    else: a = ""
    if d0!=0:
        b = ['W','E'][d0<0]
    else: b = ""
    return a+b

def Shortest(coord1,coord2):
    d = abs(coord1[0]-coord2[0])-abs(coord1[1]-coord2[1])
    if d>0: a = 'EW'
    if d<=0: a = 'NS'
    return a

input = sys.argv[1].splitlines()
arena = input[0:10]
life = input[10].split(" ")
stuff = input[12:]
path = os.path.dirname(__file__)
f1 = os.path.join(path,'state','RAS')
try:
    with open(f1, 'r') as f:
        fired = int(f.read())
except:
    fired = 0

me = Position(arena, "Y")
other = Position(arena, "X")
target = Direction(me,other)
m = []
if len(stuff):
    s = [i.split(" ") for i in stuff]
    for i in s:
        if i[0]=='L': m += [(int(i[1]),int(i[2]))]


near = [(me[0]+i,me[1]) for i in range(-1,2,2)]+[(me[0],me[1]+i) for i in range(-1,2,2)]+[(5+me[0],5+me[1]) for i in range(-1,2,2)]
closeMines = [i for i in m if i in near]
dirmines = []
for j in closeMines:
    dirmines += Direction(me, j)


if target in ['N','S','E','W']:
    if int(life[1])>1 and fired==0:
        action = "P"
        with open(f1,'w') as f:
            f.write('2')
    else:
        if fired==2:
            action = "M "+target
            with open(f1,'w') as f:
                f.write('1')
        if fired==1:
            action = "B "+target
            with open(f1,'w') as f:
                f.write('0')
        if int(life[1])==1:
            action = "M "+target
else:
    s = Shortest(me,other)
    d1 = Direction((me[0],other[1]), other)
    d2 = Direction((other[0],me[1]), other)
    if s=='EW' and d1 not in dirmines:
        action = d1
    if s=='NS' and d2 not in dirmines:
        action = d2
    else:
        if d2 not in dirmines: action = d2
        if d1 not in dirmines: action = d1
        else: action = 0


sys.stdout.write(action)
plannapus
fuente
Ah, se te ocurrió la misma técnica que pensé (y mencionaste a Einacio en los comentarios). Creo que este será el favorito en este momento. :-) Voy a probar cuando llegue a casa del trabajo.
Gareth
1
Agregué una sección al final de la pregunta sobre cómo escribir en el disco. También hice mi primera prueba: los resultados están cerca del final de la pregunta. Intentaré hacer esto con bastante regularidad ahora.
Gareth
Entonces, ¿supongo que eso significa que este bot funciona como lo está ahora con su anotador? ¡Excelente!
plannapus
Parece. Venció fácilmente a mineminemine y RandomBot, pero recibió una paliza de EvadeBot y su DodingTurret.
Gareth
No estoy tan sorprendido: porque toma su tiempo apuntar (no tratando de estar muy cerca del adversario) pensé que perdería contra los evasores. ¡Todavía es un buen resultado!
plannapus
8

El último puesto del rey

Una extensión para mi BattleBot, esto está diseñado para combatir EMP-blasters. La única forma sensata (IMO) de usar EMP es disparándolo mientras estás en el mismo eje que el oponente, luego disparando misiles / armas hacia el oponente atascado. Entonces, me mantengo fuera del eje :)

Si alguna vez has tenido un juego de ajedrez contra un rey contra un rey + reina, sabes que una reina sola no puede hacer jaque mate , debes involucrar al rey. Si no lo hace, la estrategia del rey solitario es fácil: trate de mantenerse fuera del eje y hacia el centro para maximizar la movilidad. Si te quedas atascado, ve a un punto muerto.

Por supuesto, no hay una gran manera de forzar un punto muerto aquí, así que eventualmente te quedas atascado en un lado o esquina si la reina está jugando en cualquier nivel de competencia. Si este bot alguna vez está en esa situación, dispara. Suponiendo que el oponente vaya a EMP, esto le da una ventaja de daño de un turno, por lo que la última posición del rey debería salir bien a menos que ya tenga poca vida.

Ah, y si ya está fuera del eje y a salvo de proyectiles, solo tomará un disparo en la dirección general del enemigo.

LastStand.java

import java.awt.Point;
import java.util.ArrayList;

public class LastStand extends BattleBot{

    String output = "0";
    ArrayList<Point> safeFromEnemy;
    ArrayList<Point> safeFromWeapons;
    ArrayList<Point> safeFromBoth;

    public static void main(String[] args){
        System.out.print(new LastStand(args).execute());
    }

    LastStand(String[] args){
        super(args);
        debug = false;
    }

    @Override
    String execute() {
        findSafeSpots();
        if(attack())
            return output;
        if(evade(safeFromBoth))
            return output;
        if(evade(safeFromEnemy))
            return output;

        return output;
    }

    boolean evade(ArrayList<Point> points){
        Point dest = closestToCenter(points);
        if(dest==null)
            return false;
        int heading = headingToPoint(dest);
        output = headings[heading];
        return true;
    }

    boolean attack(){
        if(safeFromEnemy.isEmpty() || safeFromBoth.contains(yPosition))
            return fire();
        return false;
    }

    Point closestToCenter(ArrayList<Point> points){
        Point closest = null;
        int dist = 15;
        for(Point pos : points){
            if(distance(center, pos) < dist){
                closest = pos;
                dist = distance(center, pos);
            }
        }
        return closest;
    }

    boolean isOnEnemyAxis(Point pos){
        int x = Math.abs(pos.x - xPosition.x);
        int y = Math.abs(pos.y - xPosition.y);
        if(x==0 || y==0 || x==y)
            return true;
        return false;
    }

    void findSafeSpots(){
        safeFromEnemy = new ArrayList<Point>();
        safeFromWeapons = new ArrayList<Point>();
        safeFromBoth = new ArrayList<Point>();

        if(!isOnEnemyAxis(yPosition))
            safeFromEnemy.add(yPosition);
        if(spotCollision(yPosition)==null)
            safeFromWeapons.add(yPosition);

        for(int heading=0;heading<8;heading++){
            Point pos = nextPosition(heading, yPosition);
            if(isOutside(pos))
                continue;
            if(!isOnEnemyAxis(pos))
                safeFromEnemy.add(pos);
            if(spotCollision(pos)==null)
                safeFromWeapons.add(pos);
        }
        for(Point pos : safeFromEnemy){
            if(safeFromWeapons.contains(pos))
                safeFromBoth.add(pos);
        }
    }

    boolean fire(){
        int heading = headingToPoint(xPosition);
        int dist = distance(xPosition, yPosition);
        if(dist>1 || yEnergy>4)
            output = "M " + headings[heading];
        else
            output = "B " + headings[heading];
        return true;
    }   
}

Para compilar run, colóquelo en una carpeta con BattleBot.javay ejecute:

javac LastStand.java
java LastStand <arena-argument>
Geobits
fuente
8

EvadeBot

Este bot prioriza mantenerse con vida. Si detecta colisiones entrantes, se trata de moverse a un lugar seguro comprobando que la mancha de colisiones. Si no hay lugares "seguros" circundantes, se queda donde está y pasa al siguiente paso.

Si no hubo colisiones (o puntos seguros en caso de colisión), se realiza una comprobación de ataque. Si el oponente está alineado en 8 ejes, dispara el 80% del tiempo. Si no está alineado, se dispara el 50% del tiempo en el encabezado más cercano. Elige un arma basada en la distancia. Si está cerca, una mina terrestre o una bala (dependiendo de la distancia exacta y la salud relativa), misiles desde lejos.

Si decidió no disparar, realiza una caminata aleatoria (nuevamente buscando lugares seguros).

Si nada de lo anterior funcionó, simplemente se queda allí hasta el próximo turno.

No usa EMP, y tengo un mal presentimiento acerca de cómo enfrentarse ReadyAimShoot, pero veremos cómo funciona.


El código está en dos piezas. Como puedo hacer más de un bot, creé una BattleBotclase abstracta . Incluye funciones auxiliares como leer la arena, verificación de colisiones, gestión de encabezados, etc. También hay una función de registro para ayudar a rastrear lo que está sucediendo durante la depuración. Si debug==false, solo imprimirá la salida real. Si alguien quiere usarlo / extenderlo, siéntase libre. No es un código bonito , pero supera la escritura repetitiva.

BattleBot.java

import java.awt.Point;
import java.util.Random;

abstract class BattleBot {
    static boolean debug;

    Random rand;
    final String[] headings = {"N","NE","E","SE","S","SW","W","NW"};
    final int           BULLET      = 0,
                        MISSILE     = 1,
                        LANDMINE    = 2;

    final int arenaSize = 10;
    final Point center  = new Point(arenaSize/2, arenaSize/2);

    boolean valid = false;
    Weapon[] weapons;
    Point xPosition, yPosition; 
    int xEnergy, yEnergy;

    abstract String execute();

    Point nextPosition(int heading, Point from){
        if(from == null)
            from = yPosition;
        Point next = new Point(from);
        if(heading<0||heading>7)
            return next; 
        if(heading<2 || heading>6)
            next.y--;
        if(heading<6 && heading>2)
            next.y++;
        if(heading>4)
            next.x--;
        if(heading<4 && heading>0)
            next.x++;
        return next;        
    }

    boolean isHeadingExact(int heading, Point from, Point to){
        Point next = new Point(from);
        while(!isOutside(next)){
            next = nextPosition(heading, next);
            if(next.equals(to))
                return true;
        }
        return false;
    }

    int headingToPoint(Point to){
        int x = yPosition.x - to.x;
        int y = yPosition.y - to.y;
        if(x<0){
            if(y<0) return 3;
            if(y>0) return 1;
            return 2;
        }else if(x>0){
            if(y<0) return 5;
            if(y>0) return 7;
            return 6;
        }else{
            if(y<0) return 4;
            return 0;
        }
    }

    BattleBot(String[] args){
        rand = new Random();
        if(args.length < 1 || args[0].length() < arenaSize*arenaSize)
            return;
        String[] lines = args[0].split("\\r?\\n");
        if(lines.length<12)
            return;
        weapons = new Weapon[lines.length - 12];
        int wIndex = 0;
        for(int i=0;i<lines.length;i++){
            String line = lines[i];
            if(i<arenaSize){
                if(line.contains("X"))
                    xPosition = new Point(line.indexOf("X"),i);
                if(line.contains("Y"))
                    yPosition = new Point(line.indexOf("Y"),i);
            } else {
                String[] tokens = line.split(" ");
                switch(tokens[0].charAt(0)){
                case 'X':
                    xEnergy = Integer.parseInt(tokens[1]);
                    break;
                case 'Y':
                    yEnergy = Integer.parseInt(tokens[1]);
                    break;
                case 'B':
                case 'M':
                case 'L':
                    weapons[wIndex++] = new Weapon(tokens);
                    break;
                }
            }
        }
        valid = true;
    }

    int distance(Point a, Point b){
        return Math.max(Math.abs(a.x-b.x), Math.abs(a.y-b.y));
    }

    Point spotCollision(Point pos){
        for(int i=0;i<weapons.length;i++){
            Point collision = weapons[i].collisionPoint(pos);
            if(collision != null){
                log("Collision at " + collision.x + "," + collision.y + " with weapon type " + weapons[i].type);
                if(collision.equals(pos))
                    return collision;
                else if(weapons[i].type==MISSILE && distance(collision,pos) < 2)
                    return collision;
                log("Collision disregarded");
            }
        }
        return null;
    }

    boolean isOutside(Point pos){
        if(pos.x<0||pos.y<0||pos.x>=arenaSize||pos.y>=arenaSize)
            return true;
        return false;
    }

    static <T> void log(T msg){
        if(debug) System.out.println(msg);
    }

    int getHeading(String in){
        for(int i=0;i<headings.length;i++){
            if(in.equalsIgnoreCase(headings[i]))
                return i;
        }
        return -1;
    }

    class Weapon{

        final int[] speeds = {3,2,0};   
        Point position;
        int type;
        int heading;
        int speed;

        Weapon(String[] tokens){
            char which = tokens[0].charAt(0);
            type = which=='B'?BULLET:
                   which=='M'?MISSILE:
                              LANDMINE;

            speed = speeds[type];

            position = new Point(Integer.parseInt(tokens[1]), Integer.parseInt(tokens[2]));

            if(type==BULLET || type == MISSILE)
                heading = getHeading(tokens[3]);
            else
                heading = -1;
        }

        Point collisionPoint(Point pos){
            Point next = new Point(position);
            if(type==LANDMINE)
                return next;
            for(int i=0;i<speed;i++){
                next = nextPosition(heading, next);
                if(isOutside(next))
                    return next;
                if(next.equals(xPosition) || next.equals(yPosition))
                    return next;
                if(next.equals(pos))
                    return next;
            }
            return null;            
        }
    }   
}

Este bot en particular es EvadeBot. Para compilar / ejecutar, póngalo en una carpeta con BattleBot.javay ejecute:

javac EvadeBot.java
java EvadeBot <arena-argument>

Si omite el argumento o no puede analizarlo correctamente, el valor predeterminado es la "0"salida.

EvadeBot.java

import java.awt.Point;

public class EvadeBot extends BattleBot{

    String output = "0";

    public static void main(String[] args){
        System.out.print(new EvadeBot(args).execute());
    }

    EvadeBot(String[] args) {
        super(args);
        debug = false;
    }

    @Override
    String execute() {
        if(!valid)
            return output;
        if(evade())
            return output;
        if(attack())
            return output;
        if(walk())
            return output;
        return output;
    }

    boolean evade(){
        Point collision = spotCollision(yPosition);
        if(collision!=null){
            log("Incoming! " + collision.x + "," + collision.y);
            return moveAwayFrom(collision);
        }
        return false;
    }

    boolean attack(){
        int dist = distance(yPosition, xPosition);
        int heading = headingToPoint(xPosition);
        int odds = rand.nextInt(100);

        if(isHeadingExact(heading, yPosition, xPosition)){
            if(odds<20)
                return false;
        } else {
            if(odds<50)
                return false;
        }
        log("Odds of firing " + headings[heading] + " to " + xPosition.x + "," + xPosition.y + " checked, preparing to attack.");
        if(dist==2){
            if(yEnergy > 3 || (xEnergy < 2 && yEnergy > 1)){
                output = "L " + headings[heading]; 
                return true;
            }
        }else if(dist<4){
            output = "B " + headings[heading];
            return true;
        }else{
            output = "M " + headings[heading];
            return true;
        }
        return false;
    }

    boolean walk(){
        log("Trying to random walk...");
        int heading = rand.nextInt(8);
        for(int i=0;i<8;i++,heading=(heading+1)%8){
            Point next = nextPosition(heading, yPosition);
            if(!isOutside(next) && spotCollision(next)==null){
                output = headings[heading];
                return true;
            }
        }
        return false;
    }

    boolean moveAwayFrom(Point from){
        int heading;
        if(from.equals(yPosition))
            heading = rand.nextInt(8);
        else
            heading = (headingToPoint(from) + (rand.nextBoolean()?2:6)) % 8;
        Point next = nextPosition(heading, yPosition);
        for(int i=0;i<8;i++){
            log("Checking move " + headings[heading] + " to " + next.x + "," + next.y);
            if(!isOutside(next) && spotCollision(next)==null){
                output = headings[heading];
                return true;
            }
            heading = (heading + 1) % 8;
            next = nextPosition(heading, yPosition);
        }
        return false;
    }
}
Geobits
fuente
1
Agradable. Vence a MineMineMine y RandomBot 5-0.
Gareth
@Keba No hay problema. Lo iba a hacer de todos modos; Me imagino que si ayuda a alguien, genial. Sin embargo , podría ser mucho mejor. Es bastante básico, pero tiene lo básico, supongo.
Geobits
@Gareth arreglé un error BattleBots.java. ¿Puedes recompilar mis bots antes de la próxima ejecución?
Geobits
@Geobits Ok, lo haremos.
Gareth
8

Espiral Bot Literate Haskell

En haskell alfabetizado, los comentarios son predeterminados, por lo que esta publicación completa es el programa. Este bot disparará misiles en espirales a su alrededor, ignorando la entrada. Almacena el estado en un archivo (que es de esperar que el competidor no lo incluya).

> import System.Directory (doesFileExist, createDirectoryIfMissing, setCurrentDirectory)
> import Control.Monad (unless)

Primero enumeramos las acciones de misiles.

> missiles = map ("M "++) $ cycle ["N", "NE", "E", "SE", "S", "SW", "W", "NW"]

A continuación, vamos directamente a la mónada IO. Si "spiral.txt" no existe, le escribimos "0". También verificamos el directorio.

> main = do
>   createDirectoryIfMissing True "state"
>   setCurrentDirectory "state"
>   exists <- doesFileExist "spiral.txt"
>   unless exists $ writeFile "spiral.txt" "0"

Luego lo leemos e imprimimos la acción.

>   actPos <- fmap read $ readFile "spiral.txt" :: IO Int
>   putStr $ missiles !! actPos

Y finalmente escribimos en el archivo la posición actual.

>   writeFile "spiral.txt" (show $ actPos + 1)
PyRulez
fuente
1
@Geobits He agregado una sección al final de la pregunta sobre cómo escribir en el disco. También hice mi primera prueba: los resultados están cerca del final de la pregunta. Intentaré hacer esto con bastante regularidad ahora.
Gareth
¿Puedo pedirle que lea las reglas sobre la escritura en archivos? Solo necesito que su archivo escrito esté en un subdirectorio llamado statepara evitar conflictos accidentales con cualquier otro archivo no estatal.
Gareth
Además, no incluí tu bot en la primera prueba solo porque todavía no tengo Haskell instalado en la máquina de prueba. Tan pronto como esté instalado, haré otra prueba con tu bot incluido.
Gareth
Creo que a tu nueva versión le falta una o dos importaciones. Estoy obteniendo LiterateHaskell.lhs:13:5: Not in scope: 'createDirectoryIfMissing'y LiterateHaskell.lhs:14:5: Not in scope: configurandoCurrentDirectory '' cuando intento compilar.
Gareth
1
Irónico, mi bot está en el último punto pero tiene la mayoría de los votos. Programación literaria para la victoria!
PyRulez
7

Torreta esquivada

un robot de Python

Aquí hay otro intento. Dado que ReadyAimShoot está en el taller de reparación por un tiempo :) pensé que mientras tanto intentaré otra cosa, esta vez usando Python.

import sys

def Position(arena, element):
    y = [i for i,j in enumerate(arena) if element in arena[i]][0]
    x = arena[y].index(element)
    return (x,y)

def Direction(coord1, coord2):
    d0 = coord1[0]-coord2[0]
    d1 = coord1[1]-coord2[1]
    if d1!=0:
        a = ['N','S'][d1<0]
    else: a = ""
    if d0!=0:
        b = ['W','E'][d0<0]
    else: b = ""
    return a+b

def GetPath(coord, direction):
    if direction=='N': path = [(coord[0],coord[1]-i) for i in xrange(3)]
    if direction=='S': path = [(coord[0],coord[1]+i) for i in xrange(3)]
    if direction=='E': path = [(coord[0]+i,coord[1]) for i in xrange(3)]
    if direction=='W': path = [(coord[0]-i,coord[1]) for i in xrange(3)]
    if direction=='NE': path = [(coord[0]+i,coord[1]-i) for i in xrange(3)]
    if direction=='NW': path = [(coord[0]-i,coord[1]-i) for i in xrange(3)]
    if direction=='SE': path = [(coord[0]+i,coord[1]+i) for i in xrange(3)]
    if direction=='SW': path = [(coord[0]-i,coord[1]+i) for i in xrange(3)]
    return path

def Danger(coord, stuff):
    if len(stuff):
        s = [i.split(" ") for i in stuff]
        for i in s:
            if i[0] in ['M','B']:
                path = GetPath((int(i[1]),int(i[2])),i[3])
                if coord in path:
                    return ['unsafe',path]
        return ['safe',()]
    else:
        return ['safe',()]

input = sys.argv[1].splitlines()
arena = input[0:10]
stuff = input[12:]
me = Position(arena, "Y")
center = Direction(me, (5,5))
if center != "":
    action = center
else:
    d = Danger(me,stuff)
    if d[0]=='safe':
        other = Position(arena,"X")
        target = Direction(me, other)
        action = 'M '+target
    if d[0]=='unsafe':
        escape = [(me[0]+i,me[1]) for i in range(-1,2,2)]+[(me[0],me[1]+i) for i in range(-1,2,2)]+[(5+me[0],5+me[1]) for i in range(-1,2,2)]
        esc_choice = [i for i in escape if i not in d[1]][0]
        action = Direction(me,esc_choice)

sys.stdout.write(action)

sys.argv[1].splitlines()Desvergonzadamente robé la línea de @Gareth, pero al menos esta vez eso significa que no tendré problemas para analizar la entrada.

Este bot corre en el centro al comienzo del combate, luego se queda allí y dispara misiles en la dirección del oponente. También intenta esquivar balas y misiles cercanos si está en su camino, pero luego regresa al centro antes de comenzar a disparar nuevamente.

plannapus
fuente
2
No me gustan los nombres de funciones en mayúsculas aquí.
Keba
Esto supera a mi "Straight Shooter" alrededor de 3-2 en promedio.
intx13
7

Tirador recto

Este es otro bot simple que puedes usar para probar. Si tiene una línea de visión directa con el oponente al que dispara, de lo contrario, da un paso al azar.

import sys
try:
  map = sys.argv[1][0:110].split()
except:
  sys.exit(1)

# Locate us and the opponent.
#
for y in range(0,10):
  for x in range(0, 10):
    if 'Y' == map[y][x]:
      me_y = y
      me_x = x
    elif 'X' == map[y][x]:
      him_y = y
      him_x = x

# If we're on a direct line with the opponent, fire a missile.
#
if me_y == him_y or me_x == him_x or abs(me_y - him_y) == abs(me_x - him_x):
  if   him_y < me_y and him_x < me_x:
    sys.stdout.write('M NW')
  elif him_y < me_y and him_x == me_x:
    sys.stdout.write('M N')
  elif him_y < me_y and him_x > me_x:
    sys.stdout.write('M NE')
  elif him_y == me_y and him_x < me_x:
    sys.stdout.write('M W')
  elif him_y == me_y and him_x > me_x:
    sys.stdout.write('M E')
  elif him_y > me_y and him_x < me_x:
    sys.stdout.write('M SW')
  elif him_y > me_y and him_x == me_x:
    sys.stdout.write('M S')
  elif him_y > me_y and him_x > me_x:
    sys.stdout.write('M SE')

# Otherwise, move randomly.
#
else:
  import random
  sys.stdout.write(random.choice(['N', 'NE', 'E', 'SE', 'S', 'SW', 'W', 'NW']))
intx13
fuente
7

neo-bot

coffeescript

Otro bot de JavaScript para agregar a la mezcla. Éste apunta a Node.js y está escrito en CoffeeScript. La arquitectura se sigue de la multitud de Java con una clase base que maneja la bottinidad general y otro archivo con especialización para el bot en cuestión.

La estrategia principal de este bot es no ser golpeado por tus proyectiles. Si no eres una amenaza inmediata, neo-bot simplemente comenzará a disparar.

El archivo base shared.coffee

# entry point

deserializeBoard = (board) ->
  me = no
  you = no
  rows = board.split '\n'
  all = for i in [0...rows.length]
    row = rows[i]
    me = row: i, col: row.indexOf 'Y' if /Y/.test row
    you = row: i, col: row.indexOf 'X' if /X/.test row
    row.split ''
  throw new Error "missing player" unless me and you
  all.me = me
  all.you = you
  all

deserializeState = (state) ->
  board = deserializeBoard state[0...110]
  rest = state[110...]
    .split '\n'
    .filter (d) -> d
  if rest[0][0] is 'Y'
    board.me.health = +rest[0][2...]
    board.you.health = +rest[1][2...]
  else
    board.you.health = +rest[0][2...]
    board.me.health = +rest[1][2...]
  board.mines = []
  board.projectiles = []
  for weapon in rest[2...]
    parts = weapon[2...].split ' '
    if weapon[0] is 'L'
      board.mines.push
        row: +parts[1]
        col: +parts[0]
    else
      board.projectiles.push
        type: weapon[0]
        row: +parts[1]
        col: +parts[0]
        dir: parts[2]
  board

module.exports = bot = (handle) ->

  state = process.argv[-1...][0]
  board = deserializeState state

  move = handle board
  process.stdout.write move

Y neo-bot.coffee, el código bot.

# i know kung fu

bot = require "./shared"

board_rows = [0...10]
board_cols = [0...10]

directions = [
  'NW', 'N', 'NE'
   'W',       'E'
  'SW', 'S', 'SE'
]

direction = (a, b) ->
  if a.row < b.row
    if a.col < b.col
      "SE"
    else if a.col is b.col
      "S"
    else
      "SW"
  else if a.row is b.row
    if a.col < b.col
      "E"
    else
      "W"
  else
    if a.col < b.col
      "NE"
    else if a.col is b.col
      "N"
    else
      "NW"

move = (me, dir) ->
  row = me.row
  col = me.col
  if /N/.test dir
    row--
  if /S/.test dir
    row++
  if /W/.test dir
    col--
  if /E/.test dir
    col++
  {row, col}

clamp = (v) ->
  Math.max 0, Math.min 9, v

legal = (pos) ->
  clamp(pos.row) is pos.row and clamp(pos.col) is pos.col

randOf = (choices) ->
  i = Math.floor Math.rand * choices.length
  choices[i]

moves =
  B: 3
  M: 2

damage =
  B: 1
  M: 3

danger = (board) ->
  n = ((0 for i in [0...10]) for j in [0...10])
  for projectile in board.projectiles
    next = projectile
    for i in [0...moves[projectile.type]]
      next = move next, projectile.dir
      if projectile.type is 'M' and not legal next
        for d in directions
          schrapnel = move next, d
          if legal schrapnel
            n[schrapnel.row][schrapnel.col] += 1
      continue unless legal next
      n[next.row][next.col] += damage[projectile.type]
  for mine in board.mines
    n[mine.row][mine.col] += 2
  n

warning = (board) ->
  n = ((0 for i in [0...10]) for j in [0...10])
  for dir in directions
    p = board.you
    p = move p, dir
    continue unless legal p
    n[p.row][p.col] = damage.M - 1 # relative damage
    p = move p, dir
    continue unless legal p
    n[p.row][p.col] = damage.M
    p = move p, dir
    continue unless legal p
    n[p.row][p.col] = damage.B
  for mine in board.mines
    for dir in directions
      p = move mine, dir
      continue unless legal p
      n[p.row][p.col] += 1
  n

board_map = (map) ->
  (a) ->
    ((map a[i][j] for j in board_cols) for i in board_rows)

board_pair = (join) ->
  (a, b) ->
    ((join a[i][j], b[i][j] for j in board_cols) for i in board_rows)

boards =
  sum: board_pair (a, b) -> a + b
  scale: (n) -> board_map (a) -> a * n

chooseSafeDir = ({me, you}, lava) ->
  dirs = []
  min = +Infinity
  for dir in directions
    guess = move me, dir
    continue unless legal guess
    guess.dir = dir
    guess.damage = lava[guess.row][guess.col]
    min = guess.damage if guess.damage < min
    dirs.push guess
  dirs.sort (a, b) ->
    if a.damage < b.damage
      -1
    else if b.damage < a.damage
      1
    else
      0
  choice = randOf dirs.filter (d) ->
    d.damage < min + 1
  choice = choice or dirs[0]
  choice.dir

neo = (WARNING_FACTOR, MISSILE_FACTOR, MOVE_FACTOR) ->
  WARNING_FACTOR ?= 0.8
  MISSILE_FACTOR ?= 0.2
  MOVE_FACTOR ?= 0.1

  combine = (d, w) ->
    boards.sum d, boards.scale(WARNING_FACTOR)(w)

  shoot = ({me, you}) ->
    weapon = if Math.random() < MISSILE_FACTOR then 'M' else 'B'
    dir = direction me, you
    "#{weapon} #{dir}"

  (board) ->
    lava = combine danger(board), warning(board)

    if lava[board.me.row][board.me.col] or Math.random() < MOVE_FACTOR
      chooseSafeDir board, lava
    else
      shoot board

bot neo()

Recomiendo compilar los archivos de café a javascript antes de ejecutarlos; Es un poco más rápido. Básicamente quieres hacer esto:

> coffee -c *.coffee
> ./bb "java EvilBot" "node ./neo-bot.js"
couchand
fuente
7

CamperBot

Este bot solo se queda donde está y dispara. Solo implementé balas, ya que las otras armas dañarían al bot. Por favor, perdóname mis terribles habilidades C;)

#include <stdio.h>
#include <time.h>

int main(int argc, char *argv[])
{
    int direction = 0;
    char directions[][3] = {"N", "NE", "E", "SE", "S", "SW", "W", "NW"};
    srand(time(NULL));

    direction = rand() % 8;
    printf("B %s", directions[direction]);
    return 0;
}

Realmente no esperaba ganar mucho.

CommonGuy
fuente
1
Bienvenido al sitio!
Jonathan Van Matre
1
Se corrigió un pequeño error ... ¿podría recompilarlo para la ejecución final? Gracias :)
CommonGuy
5

Como todavía no hay entradas, pondré una para que tengas algo que ganar. Te doy a ti:

¡Mía! ¡Mía! ¡Mía!

import sys
import random
from itertools import product

def getMyPos(arena):
    x=0
    y=0
    for idx, line in enumerate(arena):
        if(line.find('Y')!= -1):
            x=line.find('Y')
            y=idx
    return [x, y]

def isNearMine(pos, badstuff):
    returnval=False
    for badthing in badstuff:
        thinglist=badthing.split(" ")
        if(thinglist[0]=='L'):
            returnval=returnval or isNear(pos, map(int, thinglist[1:3]))
    return returnval

def isNear(pos1, pos2):
    return ((abs(pos1[0]-pos2[0])<2) and (abs(pos1[1]-pos2[1])<2))

def newpos(mypos, move):
    return [mypos[0]+move[0], mypos[1]+move[1]]

def inBounds(pos):
    return pos[0]<10 and pos[0]>=0 and pos[1]<10 and pos[1]>=0

def randomSafeMove(arena, badstuff):
    mypos=getMyPos(arena)
    badsquares=[mypos] #don't want to stay still
    for badthing in badstuff:
        thinglist=badthing.split(" ")
        if(thinglist[0]=='L'):
            badsquares.append(map(int, thinglist[1:3]))
    possiblemoves=list(product(range(-1, 2), repeat=2))
    possiblemoves=[list(x) for x in possiblemoves]
    safemoves=[x for x in possiblemoves if newpos(mypos, x) not in badsquares]
    safemoves=[x for x in safemoves if inBounds(newpos(mypos, x))]
    move=random.choice(safemoves)
    return (("N S"[move[1]+1])+("W E"[move[0]+1])).strip()

def randomDropMine(arena):
    mypos=getMyPos(arena)
    badsquares=[mypos] #don't want to drop a mine under myself
    possiblemoves=list(product(range(-1, 2), repeat=2))
    possiblemoves=[list(x) for x in possiblemoves]
    possiblemoves=[x for x in possiblemoves if newpos(mypos, x) not in badsquares]
    possiblemoves=[x for x in possiblemoves if inBounds(newpos(mypos, x))]
    move=random.choice(possiblemoves)
    return "L "+(("N S"[move[1]+1])+("W E"[move[0]+1])).strip()

input=sys.argv[1].splitlines()
arena=input[0:10]
energy=input[10:12]
badstuff=input[12:]

if(isNearMine(getMyPos(arena), badstuff)):
    sys.stdout.write(randomSafeMove(arena, badstuff))
else:
    sys.stdout.write(randomDropMine(arena))

No hace nada particularmente inteligente. Deja caer una mina si no hay ninguno en ninguno de los cuadrados circundantes; de lo contrario, se mueve a uno de los cuadrados seguros circundantes. Apenas puede vencer al HuggyBot.

Por favor, disculpe la codificación Python naff.

Gareth
fuente
5

Bot aleatorio

Este bot solo realiza una acción aleatoria en cada movimiento. No dispara el EMP y no mira el mapa en absoluto. ¡La mitad del tiempo solo está disparando contra la pared!

#include <stdio.h>
#include <sys/time.h>

void main(int argc, char **argv)
{
  char dirs[][3] = {"N", "NE", "E", "SE", "S", "SW", "W", "NW"};

  struct timeval tv;
  gettimeofday(&tv, NULL);
  srand(tv.tv_usec);

  int action = rand()%11;
  int dir = rand()%7;

  switch(action)
  {
    case 8:
      printf("B %s", dirs[dir]);
      break;

    case 9:
      printf("M %s", dirs[dir]);
      break;

    case 10:
      printf("L %s", dirs[dir]);
      break;

    default:
      printf(dirs[action]);
      break;
  }
}

Pruébelo (contra sí mismo) como se muestra a continuación.

$ gcc random.c -o random
$ ./bb random
intx13
fuente
Eso debería estar int mainbien?
arshajii
gcc establecerá el código de retorno en 0 si define main como void
intx13
Tonto gcc. void maines BS.
tomsmeding
5

Problemas y Strafe

Alguna representación de Ruby en la lucha. Mueve hacia arriba y hacia abajo los misiles de disparo de pared asignados al azar en la pared opuesta. Ligeramente irregular en la parte superior e inferior.

def getInput()
    inputlines=ARGV[0].split(/\n/)
    return [inputlines[0, 10], inputlines[10, 2], inputlines[12..-1]]
end

def getMyPos(arena)
    pos=[]
    arena.each_with_index{|str, index| pos=[str.index('Y'), index] if(!str.index('Y').nil?)}
    return pos
end

def parseProjectiles(projectiles)
    projectiles.map!{|prj| prj.split(' ')}
    missiles=projectiles.select{|prj| prj[0]=='M'}
    bullets=projectiles.select{|prj| prj[0]=='B'}
    landmines=projectiles.select{|prj| prj[0]=='L'}
    return [missiles, bullets, landmines]
end

def haveFired?(ypos, direction, projectiles)
    return projectiles.select{|prj| prj[2]==ypos.to_s && prj[3]==direction}.size>0
end

arena, botenergy, projectiles=getInput()
missiles, bullets, landmines=parseProjectiles(projectiles)

myposX=getMyPos(arena)[0]
myposY=getMyPos(arena)[1]

direction="WE"[myposX!=0 ? 0 : 1]

if haveFired?(myposY, direction, missiles)
    if myposY==0
        print "S"
    elsif myposY==9
        print "N"
    else
        if haveFired?(myposY-1, direction, missiles)
            print "S"
        elsif haveFired?(myposY+1, direction, missiles)
            print "N"
        else
            if(Random.rand(2)==0)
                print "N"
            else
                print "S"
            end
        end
    end
else
    print "M "+direction
end
Gareth
fuente
5

Un núcleo de JavaScript

Pensé que sería amable y te daría mi núcleo JS bot. Tiene todas las funciones necesarias para hacer un bot, todo lo que necesita es hacer algunas acciones en función de los datos que le proporciona. Todavía no ha terminado, ya que realmente no puedo probarlo (no puedo obtener el código de arena para compilar).

Siéntase libre de usar esto, estoy ansioso por ver algunos bots JS en la mezcla.

Que hacer:

  • Agregue funciones para calcular ubicaciones de armas

    var stdi = WScript.StdIn;
    var stdo = WScript.StdOut;
    
    function botLog(toLog){
        var fso  = new ActiveXObject("Scripting.FileSystemObject");
        var fh = fso.CreateTextFile("./botLog.txt", 8, true);
        fh.WriteLine(toLog); 
        fh.Close(); 
    }
    
    var directions = ['N', 'NE', 'E', 'SE', 'S', 'SW', 'W', 'NW'];
    
    // READ ARGUMENTS AND CREATE THE ARENA
    var arena = {};
    
    arena.map = WScript.Arguments.Item(0); // Get the arena from arguments
    arena.rows = arena.map.split('\\n');
    
    
    arena.find = function(toFind){ //Find a character in the arena.
        for(var i = 0; i < 10; i++){
            if(arena.rows[i].indexOf(toFind) !== -1){
                return [arena.rows[i].search(toFind), i];
            }
        }
    };
    arena.findAtPos = function(x, y){
        return arena.rows[y].charAt(x);
    };
    
    me = {};
        me.pos = arena.find('Y');
        me.x = me.pos[0];
        me.y = me.pos[1];
        me.energy = parseInt(arena.rows[10].replace("Y ", ""));
        me.nearby = {
            N : arena.findAtPos(me.x, me.y - 1),
            NE : arena.findAtPos(me.x + 1, me.y - 1),
            E : arena.findAtPos(me.x + 1, me.y),
            SE : arena.findAtPos(me.x + 1, me.y + 1),
            S : arena.findAtPos(me.x, me.y + 1),
            SW : arena.findAtPos(me.x - 1, me.y + 1),
            W : arena.findAtPos(me.x - 1, me.y),
            NW : arena.findAtPos(me.x -1, me.y - 1),
    
            contains : function(checkFor){
                for(var j = 0; j < 8; j++){
                    if(me.nearby[j] === checkFor){
                        return true;
                    }
                }
            }
        }
    
    foe = {};
        foe.pos = arena.find('X');
        foe.x = foe.pos[0];
        foe.y = foe.pos[1];
        foe.energy = parseInt(arena.rows[11].replace("X ", ""));
    

Tenga en cuenta que algunas cosas aquí pueden tener que modificarse para otro sistema operativo (esto solo funciona en Windows). Versión de Rhino aquí: http://pastebin.com/FHvmHCB8

Corwin
fuente
¿Voto negativo y sin comentarios? ¿Quién podría haber rechazado esto, por favor, dame una razón? ¿Hay algún error en mi código?
Corwin
Sí, el votante negativo necesita explicar su objeción aquí.
Gareth
4

Center-Bot

Un bot de JavaScript

Este bot tiene como objetivo llegar al medio de la arena, antes de disparar balas o misiles a su objetivo cada turno, dependiendo de qué tan cerca esté. Si el enemigo está en el medio, seguirá disparando balas en la dirección vaga.

No espero que le vaya muy bien, pero es más una prueba, y estoy interesado en ver qué tan bien lo hace realmente.

    var arena = {};
var sys = require("sys");
var fs = require("fs");

arena.map = process.argv[2];
arena.rows = arena.map.split('\n');


arena.find = function(toFind){
    for(var i = 0; i < 10; i++){
            if(arena.rows[i].indexOf(toFind) !== -1){
                return [arena.rows[i].search(toFind), i];
            }
    }
};
arena.findAtPos = function(x, y){
    return arena.rows[y].charAt(x);
};

me = {};
    me.pos = arena.find('Y');
    me.x = me.pos[0];
    me.y = me.pos[1];
    me.energy = parseInt(arena.rows[10].replace("Y ", ""));

foe = {};
    foe.pos = arena.find('X');
    foe.x = foe.pos[0];
    foe.y = foe.pos[1];
    foe.energy = parseInt(arena.rows[11].replace("X ", ""));
function findFoe(){ 
    if(me.x < foe.x){
        if(me.y < foe.y){
            foe.direction = 'SE';
        }
        else if(me. y  === foe.y){
            foe.direction  = 'E';
        }
        else{
            foe.direction = 'NE';
        }
    }
    if(me.x === foe.x){
        if(me.y < foe.y){
            foe.direction = 'S';
        }
        else{
            foe.direction = 'N';
        }
    }
    if(me.x > foe.x){
        if(me.y < foe.y){
            foe.direction = 'SW';
        }
        else if(me. y  === foe.y){
            foe.direction  = 'W';
        }
        else{
            foe.direction = 'NW'
        }
    }
}

function findCentre(){
    if(me.x < 5){
        if(me.y < 5){
            centreDirection = 'SE';
        }
        else if(me.y  === 5){
            centreDirection  = 'E';
        }
        else{
            centreDirection = 'NE'
        }
    }
    if(me.x === 5){
        if(me.y < 5){
            centreDirection = 'S';
        }
        else{
            centreDirection = 'N'
        }
    }
    if(me.x > 5){
        if(me.y < 5){
            centreDirection = 'SW';
        }
        else if(me. y  === 5){
            centreDirection  = 'W';
        }
        else{
            centreDirection = 'NW'
        }
    }
}
findCentre();
findFoe();
if(me.x !== 5 && me.y !== 5){
    process.stdout.write(centreDirection);
}else{
    if(foe.x >= me.x + 2 || foe.x <= me.x - 2  || foe.y >= me.y + 2 || foe.y <= me.y - 2){
        process.stdout.write('M ' + foe.direction);
    }else process.stdout.write('B ' + foe.direction);
}

guardar como archivo .js y ejecutar con node centrebot.js. Esto funcionará con Node.js, pero es posible que deba modificarlo para otro programa, ¡lo siento!

En mis pruebas:

  • Thrashed ReadyAimShoot sin un rasguño.
  • MUCHO gana contra DodgingTurret
  • Ganó todo con algunos rasguños de las afortunadas minas terrestres de Randombot
  • Beat Straight Shooter 9 de 9 veces, pero cada combate estuvo cerca, a pesar de que gané todos.

No he probado ninguno de los mejores robots de Java, y tampoco estoy muy seguro ...

Corwin
fuente
He instalado SpiderMonkey en la máquina de prueba, por lo que estoy usando putstr(...)en lugar de tu stdo.writeLine(...)y la entrada proviene scriptArgs[0]. Habiendo hecho de que tenía que cambiar el \\nde \ndividir el mapa en las líneas. Cuando lo ejecuto me sale un error porque FindFoe()y findCentre()se definen pero no se llaman.
Gareth
¡Uy! acabo de ver un error! ¡Tengo las funciones pero en realidad no las ejecuto! Mi mal, lo cambiaré. ¡Gracias!
Corwin
He detectado otro error: todas las instrucciones están al frente. Donde sea que tenga un E, debe tener un Wy donde sea que tenga un S, debe tener un N. Si usa la entrada de ejemplo de la pregunta, puede ver que la salida del programa SEno es una dirección posible desde la esquina inferior derecha. Lo he corregido para la próxima prueba.
Gareth
Bien, gracias @Gareth, escribí esto a toda prisa, así que realmente no hice muchas pruebas de error ... Lo solucionaré ahora.
Corwin
3

CunningPlanBot (Python 3.3)

Esto no se ha probado completamente en la interfaz real ... ¡Funciona correctamente con los mapas al menos!

Está escrito para Python 3.3

Que hace:

Si está en la Fase 1 - Si en la pared y la dirección se mueve hacia la pared o hacia una mina terrestre, cambie aleatoriamente la dirección a una dirección que no sea de pared o mina - Muévase en la dirección actual - Vaya a la Fase 2

Si está en la Fase 2 - Dispara una bala en la dirección más cercana al enemigo - Pasa a la Fase 3

Si está en la Fase 3 - Si no hay mina terrestre, deje caer la mina terrestre - Pase a la fase 1

Todavía necesita averiguar si disparar un misil. Además, no tengo ni idea de si las cosas para evitar las minas terrestres funcionan. Necesita más pruebas mañana por la noche.

#!/usr/bin/python
import sys
import os.path
import random
import math

def iround(x):
    return int(round(x) - .5) + (x > 0)   

currentphase = 0
currentdir = 0

#
#     4  
#   5   3  
# 6  DIR  2
#   7   1
#     0

if os.path.isfile('state/cpb'):
  statein = open('state/cpb', 'r')  
  currentdir = int(statein.read(1))
  currentphase = int(statein.read(1))
  statein.close()

Landmines = []    

#Loads the map bit. The bits we care about anyway.
line=sys.argv[1].splitlines()
for y in range(0, 10):
  for x in range(0, 10):
    if line[x][y] == "X":
      hisloc = (x, y)
    elif line[x][y] == "Y":    
      myloc = (x, y)
    elif line[x][y] == "L":
      Landmines.append((x,y))

#print(myloc[0])
#print(myloc[1])

newdir = False
if (currentphase == 0):
  if (currentdir == 7) or (currentdir == 0) or (currentdir == 1) and (myloc[1] == 9):
    newdir = True
  if (currentdir == 5) or (currentdir == 4) or (currentdir == 3) and (myloc[1] == 0):
    newdir = True
  if (currentdir == 3) or (currentdir == 2) or (currentdir == 1) and (myloc[0] == 9):
    newdir = True
  if (currentdir == 5) or (currentdir == 6) or (currentdir == 7) and (myloc[0] == 0):
    newdir = True    
  if newdir:
    newdirs = []
    #Test 0
    if (myloc[1] < 9) and not (myloc[0], myloc[1] + 1) in Landmines:
      newdirs.append(0)
    #Test 1
    if (myloc[0] < 9) and (myloc[1] < 9) and not (myloc[0] + 1, myloc[1] + 1) in Landmines:
      newdirs.append(1)
    #Test 2
    if (myloc[0] < 9) and not (myloc[0] + 1, myloc[1]) in Landmines:
      newdirs.append(2)
    #Test 3
    if (myloc[0] < 9) and (myloc[1] > 0) and not (myloc[0] + 1, myloc[1] - 1) in Landmines:
      newdirs.append(3)      
    #Test 4
    if (myloc[1] > 0) and not (myloc[0], myloc[1] - 1) in Landmines:
      newdirs.append(4)
    #Test 5
    if (myloc[0] > 0) and (myloc[1] > 0) and not (myloc[0] - 1, myloc[1] - 1) in Landmines:
      newdirs.append(5)    
    #Test 6
    if (myloc[0] > 0) and not (myloc[0] - 1, myloc[1] ) in Landmines:
      newdirs.append(6)      
    #Test 7
    if (myloc[0] > 0) and (myloc[1] > 9) and not (myloc[0] - 1, myloc[1] + 1) in Landmines:
      newdirs.append(7)     
    if len(newdirs) == 0:
      if currendir == 0: currentdir = 4
      elif currendir == 1: currentdir = 5
      elif currendir == 2: currentdir = 6
      elif currendir == 3: currentdir = 7
      elif currendir == 4: currentdir = 0
      elif currendir == 5: currentdir = 1
      elif currendir == 6: currentdir = 2
      elif currendir == 7: currentdir = 3
    else:
      currentdir = random.SystemRandom().choice(newdirs)
  if currentdir == 0: print ("S", end="")
  elif currentdir == 1: print ("SE", end="")
  elif currentdir == 2: print ("E", end="")
  elif currentdir == 3: print ("NE", end="")
  elif currentdir == 4: print ("N", end="")
  elif currentdir == 5: print ("NW", end="")
  elif currentdir == 6: print ("W", end="")
  elif currentdir == 7: print ("SW", end="")

elif (currentphase == 1):
  dx = (myloc[0] - hisloc[0])
  dy = (myloc[1] - hisloc[1])
  distance = math.pow(dx*dx+dy*dy, 0.5)
  angle = int(iround(math.degrees(math.atan2(dx, -dy)) / 45) ) % 8
  if angle == 5: print ("B S", end="")
  elif angle == 1: print ("B SE", end="")
  elif angle == 2: print ("B E", end="")
  elif angle == 3: print ("B NE", end="")
  elif angle == 4: print ("B N", end="")
  elif angle == 5: print ("B NW", end="")
  elif angle == 6: print ("B W", end="")
  elif angle == 7: print ("B SW", end="") 

elif (currentphase == 2):
  if not (myloc in Landmines): print ("L", end="")

currentphase = (currentphase + 1) % 3    

stateout = open ('state/cpb', 'w')
stateout.write(str(currentdir))
stateout.write(str(currentphase))
stateout.close()
lochok
fuente
2
Un par de cosas que tuve que hacer para que funcionara con el programa de puntuación: solía sys.argv[1].splitlines()tomar la entrada de la línea de comandos y luego la usaba line[x][y]en el siguiente bloque; agregado end=""a los comandos de impresión para deshacerse de la nueva línea que confunde al anotador; cambió el estado para escribir en un archivo dentro del statedirectorio en lugar de en statesí mismo.
Gareth
Eek! Mis disculpas. Era más tarde en la noche, entonces probablemente debería haberlo enviado. ¡Lo cumpliré con las especificaciones lo antes posible!
Lochok
No hay problema, usaré mis soluciones para cualquier ejecución de puntuación de prueba hasta que lo eche un vistazo.
Gareth
He aplicado las mismas correcciones pero obtengo 'Py_Initialise: no se pueden inicializar las secuencias estándar del sistema'. ¿Hay alguna posibilidad de que pueda obtener su versión de la fuente para ver si hace lo mismo?
lochok
1
He agregado mi versión de la fuente como una edición a su publicación (parecía la forma más fácil). Simplemente regréselo cuando haya agarrado la fuente.
Gareth
3

UltraBot

Un robot de Java que calcula el peligro para cada campo circundante. Si un campo circundante es menos peligroso que el actual, el bot se mueve allí (u otro campo igualmente peligroso). Si no hay un campo menos peligroso, el bot dispara (misiles si el bot enemigo está lejos, balas si el bot enemigo está cerca). Tomé un código del BattleBot (¡gracias!).

import java.awt.Point;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Random;

public class UltraBot {
    private static final int arenaSize = 10;
    private static ArrayList<Weapon> weapons = new ArrayList<Weapon>();
    private static Bot me;
    private static Bot enemy;

    public static void main(String args[]) {
        Direction suggestedMove;
        readInput(args[0]);
        suggestedMove = suggestedMove();

        if (suggestedMove != Direction.STAY) {
            System.out.print(suggestedMove.name());
            return;
        }

        System.out.print(shootCmd());
    }

    public static void readInput(String args) {
        String[] lines = args.split("\\r?\\n");

        for(int i=0;i<lines.length;i++){
            String line = lines[i];
            if(i<arenaSize){
                if(line.contains("X"))
                    enemy = new Bot(new Field(line.indexOf("X"),i));
                if(line.contains("Y"))
                    me = new Bot(new Field(line.indexOf("Y"),i));
            } else {
                String[] tokens = line.split(" ");
                switch(tokens[0].charAt(0)){
                case 'X':
                    enemy.setLife(Integer.parseInt(tokens[1]));
                    break;
                case 'Y':
                    me.setLife(Integer.parseInt(tokens[1]));
                    break;
                default:
                    weapons.add(new Weapon(tokens));
                    break;
                }
            }
        }
    }

    public static Direction suggestedMove() {
        Map<Direction, Integer> surrFields = new HashMap<Direction, Integer>();
        Random rand = new Random();

        //calculate danger for all surrounding fields
        for(Direction direction : Direction.values()) {
            Field currField = me.getPos().incPos(direction, 1);
            surrFields.put(direction, currField.calcDanger(weapons, enemy));
        }

        int currDanger = surrFields.get(Direction.STAY);
        Direction currDirection = Direction.STAY;

        for (Entry<Direction, Integer> e : surrFields.entrySet()) {
            //always move if better field found
            if (e.getValue() < currDanger) {
                currDanger = e.getValue();
                currDirection = e.getKey();
            }
            //move sometimes if equal danger field found
            else if(e.getValue() == currDanger && rand.nextInt(3) == 1) {
                if (currDanger != 0 || rand.nextInt(15) == 1) {
                    currDanger = e.getValue();
                    currDirection = e.getKey();
                }
            }
        }
        return currDirection;
    }

    public static String shootCmd() {
        WeaponType type = WeaponType.M;

        if(me.getPos().isNear(enemy.getPos(), 3)) {
            type = WeaponType.B;
        }

        return type.name() + " " + me.shootDirection(enemy);
    }
}

class Bot {
    private Field pos;
    private int life;

    public Bot(Field pos) {
        this.pos = pos;
    }

    public void setLife(int life) {
        this.life = life;
    }

    public Field getPos() {
        return pos;
    }

    public int getLife() {
        return life;
    }

    public String shootDirection(Bot other) {
        Random rand = new Random();
        Direction direction = Direction.S;
        if (getPos().getX() >= other.getPos().getX() && getPos().getY() >= other.getPos().getY()) {
            switch(rand.nextInt(5)) {
                case 0: direction =  Direction.N; break;
                case 1: direction = Direction.W; break;
                default: direction = Direction.NW; break;
            }
        }
        else if (getPos().getX() <= other.getPos().getX() && getPos().getY() >= other.getPos().getY()) {
            switch(rand.nextInt(3)) {
                case 0: direction = Direction.N; break;
                case 1: direction = Direction.E; break;
                default: direction = Direction.NE; break;
            }
        }
        if (getPos().getX() >= other.getPos().getX() && getPos().getY() <= other.getPos().getY()) {
            switch(rand.nextInt(3)) {
                case 0: direction = Direction.S; break;
                case 1: direction = Direction.W;break;
                default: direction = Direction.SW;break;
            }
        }
        if (getPos().getX() <= other.getPos().getX() && getPos().y <= other.getPos().y) {
            switch(rand.nextInt(3)) {
                case 0: direction = Direction.S; break;
                case 1: direction = Direction.E; break;
                default: direction = Direction.SE; break;
            }
        }
        return direction.name();
    }
}

enum Direction {
    N(0, -1), NE(1, -1), E(1, 0), SE(1, 1), S(0, 1), SW(-1, 1), W(-1, 0), NW(-1,-1), STAY(0,0);

    public final int offsetX;
    public final int offsetY;

    Direction(int offsetX, int offsetY) {
        this.offsetX = offsetX;
        this.offsetY = offsetY;
    }
}

enum WeaponType {
    B(1, 3), M(3, 2), L(2, 0);

    public final int dmg;
    public final int speed;

    WeaponType(int dmg, int speed) {
        this.dmg = dmg;
        this.speed = speed;
    }
}

class Weapon {
    private WeaponType type;
    private Direction direction;
    private Field pos;

    public Weapon(String[] tokens) {
        this.type = WeaponType.valueOf(tokens[0]);
        this.pos = new Field(Integer.parseInt(tokens[1]), Integer.parseInt(tokens[2]));
        if(type != WeaponType.L) {
            this.direction = Direction.valueOf(tokens[3]);
        }
    }

    public int getDanger(Field dest) {

        if (dest.isOutside()) {
            return 99;
        }

        if (type == WeaponType.L) {
            return dest.equals(pos) ? type.dmg * 3 : 0; // stepped on landmine
        }

        for (int i = 1; i <= type.speed; i++) {
            Field newPos = pos.incPos(direction, i);

            if (dest.equals(newPos)) {
                return type.dmg * 3; // direct hit with missile or bullet
            }
        }

        return 0;
    }
}

class Field extends Point{

    public Field(int x, int y) {
        super(x,y);
    }

    // as it tries to stay off walls and enemy, it doesn't need to calc splash dmg

    public int calcDanger(ArrayList<Weapon> weapons, Bot enemy) {
        int danger = 0;

        // is near wall
        if (this.getX() == 0 || this.getX() == 9)
            danger++;
        if (this.getY() == 0 || this.getY() == 9)
            danger++;

        for (Weapon weapon : weapons) {
            danger += weapon.getDanger(this);
        }

        // near bot
        if (this.isNear(enemy.getPos(), 2)) {
            danger++;
        }

        return danger;
    }

    public Boolean isOutside() {
        if (this.getX() > 9 || this.getY() > 9 || this.getX() < 0 || this.getY() < 0) {
            return true;
        }
        return false;
    }

    public Boolean isNear(Field dest, int distance) {
        int dx = (int)Math.abs(dest.getX() - this.getX());
        int dy = (int)Math.abs(dest.getY() - this.getY());

        if (dx <= distance || dy <= distance) {
            return true;
        }
        return false;
    }

    public Field incPos(Direction direction, int step) {
        return new Field((int)this.getX() + (direction.offsetX * step), 
                (int)this.getY() + (direction.offsetY * step));
    }
}

Este bot es extremadamente difícil de golpear, pero no es muy bueno para disparar al enemigo ... Todavía espero que sea mejor que mi CamperBot anterior.

CommonGuy
fuente
Solo traté de compilarlo y arrojé un montón de errores. Parece que le faltan algunos imports?
Gareth
Muchos errores sobre el acceso privado también:UltraBot.java:...: x has private access in Point
Gareth
Ups, se olvidó de las importaciones ... También arreglé el acceso x / y, a pesar de que funciona en mi máquina ...
CommonGuy
1
Bien gracias. Está funcionando en mi máquina ahora.
Gareth
2

NinjaPy

Un envío de última hora en python (no probado pero con suerte funcionará). La idea es que avance hacia el oponente mientras permanece en su punto ciego. Cuando está lo suficientemente cerca (a 3 celdas de distancia) se coloca en la diagonal del oponente y dispara un misil.

import sys

def position(arena, element):
    y = [i for i,j in enumerate(arena) if element in arena[i]][0]
    x = arena[y].index(element)
    return (x,y)

def distance(other):
    dM = [[0 for x in range(10)] for y in range(10)]
    for i in range(len(dM)):
        for j in range(len(dM[0])):
            dM[i][j] = max([abs(other[0]-i),abs(other[1]-j)])
    return dM

def direction(coord1, coord2):
    d0 = coord1[0]-coord2[0]
    d1 = coord1[1]-coord2[1]
    if d1!=0:
        a = ['N','S'][d1<0]
    else: a = ""
    if d0!=0:
        b = ['W','E'][d0<0]
    else: b = ""
    return a+b

def getPath(coord, aim, speed):
    d = {'N': (0,-1), 'S':(0,1), 'E':(1,0), 'W':(-1,0), 'NW':(-1,-1), 'NE':(1,-1), 'SW':(-1,1), 'SE':(1,1)}
    D = d[aim]
    path = [(coord[0]+D[0]*i, coord[1]+D[1]*i) for i in range(speed+1)]
    return path

def dangerMap(stuff,other):
    dM = [[0 for x in range(10)] for y in range(10)]
    surroundings = [(other[0]+i,other[1]+j) for i in range(-2,3) for j in range(-2,3)]
    for i in range(len(dM)):
        for j in range(len(dM[0])):
            if i == other[0] : dM[i][j] = 1
            if j == other[1] : dM[i][j] = 1
            if (i,j) in [(other[0]+k, other[1]+k) for k in range(-10,11)]: dM[i][j] = 1
            if (i,j) in [(other[0]-k, other[1]+k) for k in range(-10,11)]: dM[i][j] = 1
    for j in surroundings:
        dM[j[0]][j[1]] = 2
    if len(stuff):
        s = [i.split(" ") for i in stuff]
        for i in s:
            if i[0]=='L':
                g = [(int(i[1]),int(i[2]))]
            if i[0]=='M':
                g = getPath((int(i[1]),int(i[2])),i[3],2)
            if i[0]=='B':
                g = getPath((int(i[1]),int(i[2])),i[3],3)
            for j in g:
                dM[j[0]][j[1]] = 2
    return dM

input = sys.argv[1].splitlines()
arena = input[0:10]
stuff = input[12:]
me = position(arena, "Y")
other = position(arena,"X")
distOther = distance(other)
distMe = distance(me)
dangM = dangerMap(stuff,other)
if distOther[me[0]][me[1]] > 3:
    surroundings = [(i,j) for i in range(10) for j in range(10) if distMe[i][j]==1]
    choice = [k for k in surroundings if dangM[k[0]][k[1]] == 0]
    if len(choice)==0: choice = [k for k in surroundings if dangM[k[0]][k[1]] == 1]
    if len(choice)>1:
        K = []
        for i in choice: K += [distOther[i[0]][i[1]]]
        choice = [choice[k] for k in range(len(choice)) if K[k] == min(K)]
    action = direction(me,choice[0])
else:
    diag = [(other[0]+i, other[1]+i) for i in [-2,2]]+[(other[0]-i, other[1]+i) for i in [-2,2]]
    if me in diag:
        action = 'M '+direction(me,other)
    else:
        distDiag = []
        for i in diag:
            distDiag += [distMe[i[0]][i[1]]]
        choice = [diag[k] for k in range(len(diag)) if distDiag[k] == min(distDiag)]
        action = direction(me,choice[0])

sys.stdout.write(action)
plannapus
fuente
Perdón por la entrada tardía: estuve en una reunión durante la mayor parte de la semana pasada.
plannapus