結果が出て、コンテストは終了しました。
勝者はarshajiiの EvilBotで、14勝でNeo-Botが13勝、CentreBotとLastStandがそれぞれ11勝です。
最終ランからのスコア
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)
これは王様の挑戦です。目的は、他のどのボットよりも多くのボットを打つボットを作成することです。
ゲーム
ボットは、10x10アリーナで一度に2つずつ互いに対戦し、敵のエネルギーを10から0に減らしてから、自身のエネルギーを0に減らします。
各試合は5試合で構成されます。試合の勝者は、ほとんどの試合の勝者です。試合の勝ちと勝ちの合計数は、制御プログラムによって保存され、コンテストの全体的な勝者を決定するために使用されます。勝者は大きな緑のカチカチと大衆の賞賛を受け取ります。
各試合は、いくつかのラウンドで進行します。各ラウンドの開始時に、アリーナの現在の状態が各ボットに与えられ、ボットは次にコマンドで応答し、次に何をしたいかを決定します。制御プログラムが両方のコマンドを受信すると、両方のコマンドが同時に実行され、アリーナとボットのエネルギーレベルが更新されて新しい状態が反映されます。両方のボットにまだ十分なエネルギーがあれば、ゲームは次のラウンドに進みます。試合が永遠に続くことを確実にするために試合ごとに1000ラウンドの制限があり、この制限に達した場合、勝者は最もエネルギーのあるボットになります。両方のボットのエネルギーが等しい場合、試合は引き分けであり、どちらのボットも勝ちのポイントを獲得しません(両方が負けたようになります)。
武器
各ボットにはいくつかの武器があります:
- 装甲貫通弾。これらは一度に3マス移動し、1エネルギーポイントのダメージを与えます。
- ミサイル。これらは、一度に2マス移動し、衝撃点で3エネルギーポイントのダメージを引き起こし、直近のすべてのマスで1ポイントのダメージを与えます。
- 地雷。これらは、ボットのすぐ周囲の正方形の1つにドロップされ、踏んだときに2エネルギーポイントのダメージを引き起こし、すぐ周囲の正方形の1つに立つものに1エネルギーポイントのダメージを与えます。
- 電磁パルス。両方のボットの移動回路が2ターン誤作動するため、移動できません。しかし、彼らはまだ武器を展開できます(はい、それは現実的ではありませんが、それはゲームです。現実ではないはずです)。編集:各EMP展開には、それを使用するボットに1エネルギーポイントがかかります。
弾丸/ミサイルは、ボットまたは壁にのみ衝撃を与えることができます。彼らは、彼らが通過する正方形のいずれかにあるボットをヒットします。何かにぶつかると消えます。
すべての場合immediately surrounding squares
において、ボットが次の動き、つまりムーア周辺に移動できる8つの正方形を意味します。
コマンド
0
何もしない。N
、NE
、E
、SE
、S
、SW
、W
、NW
すべての方向コマンドであり、所定の方向にボット1マスを移動させます。正方形内に壁または別のボットがあるためにボットがその方向に移動できない場合、ボットは現在の場所に残ります。すでに弾丸またはミサイルが含まれている広場に移動するのは安全です。弾丸/ミサイルはすでにその広場から出て行くと考えられるからです。B
その後にスペースが続き、方向コマンドの1つがその方向に鎧を貫通する弾丸を発射します。M
スペースが続き、次に方向コマンドの1つがその方向にミサイルを発射します。L
スペースが続き、次に方向コマンドの1つがボットの隣のその広場に地雷を投下します。正方形がすでに壁またはボットで占められている場合、コマンドは無視されます。地雷が別の地雷に落とされた場合、それは爆発します。これは、ドロップを行っているボット、および元の地雷の範囲内にある他のボットに損傷を与えます。P
EMPを起動します。
ラウンドごとに1つのコマンドしか与えられないため、ボットは武器を移動または発射/展開することしかできず、同時に両方を行うことはできません。
コマンド
の順序いずれかのボットの移動は常に最初に行われ、他のボットが邪魔になっているが邪魔にならないようにするために、すべての移動が2回試行されます。
例
- Bot1は移動しようとしますが
E
、Bot2はすでにその正方形にいます - 制御プログラムはBot2に進みます。
- 邪魔に
S
ならないものがあるため、Bot2は移動しようとして成功します。 - Bot1は、2回目の移動を試みます。今回は成功し、Bot1が移動し
E
ます。
ボットが実行したい動きを行うと、武器が発射され、すべての発射物(新規および以前に発射された)が事前定義された数の正方形を移動します。
アリーナ
各ラウンドの開始時に、ボットはプログラムの唯一のコマンドライン引数として現在のプレイ状態を受け取ります。
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
アリーナは最初に10文字の10行で構成されます。表示されていない壁に囲まれています。文字の意味は次のとおりです。
.
空の正方形を表しますY
ボットを表します。X
相手のボットを表します。L
地雷を表します。B
飛行中の弾丸を表します。M
飛行中のミサイルを表します。
これに続いて、ボットの残りのエネルギー、1行につき1つのボットが続きます。ボット識別子とエネルギーレベルを区切るスペースは1つだけです。アリーナと同様に、Y
ボットをX
表し、対戦相手を表します。最後に、発射体と地雷、それらの位置と(適切な場合は)見出しのリストが、再び行ごとに表示されます。
制御プログラム
#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';
}
}
制御プログラムは、コマンドラインからボットを呼び出します。このため、コマンドラインから呼び出すことができないプログラムは無効と見なされます。選択した言語がそのように機能しない人には謝罪しますが、各マッチを手動で行うのは非現実的です。
intx13は、ここで見つけることができるいくつかのバグ修正を備えた制御プログラムのより堅牢なバージョンを親切に書いています。
制御プログラムの改善またはバグ修正の提案を歓迎します。
テストボット
テストボットはスコアリングの実行に含まれません。それらはテスト目的のためだけです。
ダドリー・ドノウ(C)
int main(int argc, char *argv)
{
printf("0");
}
状況に関係なく何もしません。多く勝つことは期待できません。
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);
}
?>
相手のすぐ隣に到達しようとします。地雷を探さないため、地雷の影響を受けやすい。敵が目標を達成したときに、ミサイルを発射することは相手にとってあまり効果的ではありません。
結果
最終的な採点は、2014年3月24日23時59分以降に行われます。定期的にテストランを実行して、参加者がボットが現在の敵に対してどのように積み重なっているかを確認できるようにします。
エントリー
エントリには、ボットのソースと、それを実行するために使用する必要があるコマンドライン引数を含める必要があります。好きなだけエントリを投稿できますが、各回答には1つのボットのみを含める必要があります。
重要
一部のエントリは、実行間で何らかの状態を保持するためにディスクに書き込みたいようです。これらは、ディスクへの書き込みに関する新しいルールです。
- 独自のボットのソースを変更できます。他のボットを変更すると不正行為となり、問題のボットが失格になります。
- 状態を保存する目的で作成されたファイルに書き込むことができます。このファイルは、ボットが置かれているディレクトリのサブディレクトリに保存する必要があります。サブディレクトリの名前は
state
。ファイルシステムの他の部分(自分のソース以外)への書き込みは許可されていません。