#/usr/bin/perl -w
# ai_elbot.pl: Launched from the command line, either by cron or procmail
# presumably then goes to get the user's summary, and for each game
# where it's his turn, takes the turn.
#
# Usage: (Well, eventually)
# ai_elbot.pl [-v] [-u username] [-p api_key] [game_num [game_num]]
#
# -v Print more verbose debug output.
# -u username Log in as user username instead of the default
# -p api_key Explicitly use the given api_key rather than the
# one read from the file username.api
# game_num Only play the game(s) explicitly specified
use strict;
use LWP::UserAgent;
use XML::Parser;
use Data::Dumper;
use Time::Piece;
my $USERNAME = 'ai_elbot';
my $USERID = 45651;
my $PASSKEY = 'rCd5uDYK38whu8WnHb2VSCC4U';
my $SERVER = '';
my $DIR = $ENV{'HOME'} . "/Play/Perl/Weewar";
my $parser = new XML::Parser( Style => 'Tree' );
sub coredump ($$$)
{
my $t = localtime;
print STDERR $_[0];
open( LOGFILE, ">>${DIR}/${USERNAME}.log" );
print LOGFILE "\n\n", $t->strftime(), " $_[0] \n\n", $_[1];
close( LOGFILE );
unless( $_[2] ) { exit $_[2]; }
}
sub initialize_data
{
# Read the api_key from the file, and return a login string
my $agent = LWP::UserAgent->new;
$agent->credentials("weewar.com:80", "Eliza API", $USERNAME, $PASSKEY);
return $agent;
}
sub get_waiting_games_list ($)
{
# I'm starting simple here. Let's just get the list of games, and
# print out the ones with waiting turns.
my $agent = $_[0];
my $request = HTTP::Request->new(
GET => 'http://weewar.com/api1/headquarters' );
$request->authorization_basic( $USERNAME, $PASSKEY );
my $request_data = $agent->request( $request );
my $master_list = $parser->parse( $request_data->content );
unless( ref( $master_list ) eq 'ARRAY' ) {
coredump( 'Unexpected server response.', $request_data->content, 0 );
}
unless( $master_list->[0] eq 'games' ) {
coredump( 'Unexpected server content.', $request_data->content, 0 );
}
my $game_index = 0;
while( $game_index < $#{$master_list->[1]} ) {
unless( $master_list->[1][$game_index] eq 'game' ) {
$game_index++;
next;
}
$game_index++;
my $subgame_index = 1;
my $flag = '';
my $title = '';
my $game_id = 0;
if( defined( $master_list->[1][$game_index][0]{'inNeedOfAttention'} )) {
$flag = $master_list->[1][$game_index][0]{'inNeedOfAttention'};
}
while( $subgame_index < $#{$master_list->[1][$game_index]} ) {
if( $master_list->[1][$game_index][$subgame_index] eq 'id' ) {
$subgame_index++;
$game_id = $master_list->[1][$game_index][$subgame_index][2];
unless( $game_id > 0 ) {
coredump( 'Unexpected game id.',
$request_data->content, 0 );
}
$subgame_index++;
next;
}
if( $master_list->[1][$game_index][$subgame_index] eq 'name' ) {
$subgame_index++;
$title = $master_list->[1][$game_index][$subgame_index][2];
$subgame_index++;
next;
}
if( $master_list->[1][$game_index][$subgame_index] eq 'link' ) {
$subgame_index++;
if( $master_list->[1][$game_index][$subgame_index][2] =~ /join$/ ) {
$flag .= 'j';
}
$subgame_index++;
next;
}
if( $master_list->[1][$game_index][$subgame_index] eq 'rated' ) {
$subgame_index++;
if( $master_list->[1][$game_index][$subgame_index][2] eq 'true' ) {
$flag .= '1';
} else {
$flag .= '0';
}
$subgame_index++;
next;
}
$subgame_index++;
}
if( $flag =~ /^true/ ) {
print ' * ';
} else {
print ' ';
}
printf( '%8d', $game_id );
print " $title\n";
if( $flag =~ /^true/ ) {
if( $flag =~ /j([01])$/ ) {
my $rated = $1;
join_game( $agent, $game_id, $rated );
}
if( $flag =~ /([01])j$/ ) {
my $rated = $1;
join_game( $agent, $game_id, $rated );
}
take_turn( $agent, $game_id );
}
$game_index++;
}
}
sub take_turn ($$)
{
# Again, simple. Just end turn. Nothing more.
my $agent = $_[0];
my $game_id = $_[1] + 0;
my @req_headers = ( 'Content-Type', 'application/xml' );
my( $req, $req_data, $xml_data );
unless( $game_id > 0 ) {
coredump( 'Illegal game id.', $_[0], 0 );
}
my $finished = 0;
unless( $finished ) {
# Get game information
$req = HTTP::Request->new( GET => "http://weewar.com/api1/gameState.jsp?game=$game_id" );
$req->authorization_basic( $USERNAME, $PASSKEY );
$req_data = $agent->request( $req );
$xml_data = $parser->parse( $req_data->content );
# Cylce through the xml, looking for my stuff.
my $gidx = 0;
while( $gidx < $#{$xml_data} ) {
unless( $xml_data->[$gidx] eq 'game' ) {
$gidx++;
next;
}
last if $xml_data->[$gidx] eq 'game';
$gidx++;
}
$gidx++;
my $fidx = 1;
while( $fidx < $#{$xml_data->[$gidx]} ) {
if( ref( $xml_data->[$gidx][$fidx] ) ne '' ) {
$fidx++;
next;
}
last if $xml_data->[$gidx][$fidx] eq 'factions';
$fidx++;
}
$fidx++;
my $pidx = 1;
while( $pidx < $#{$xml_data->[$gidx][$fidx]} ) {
if( ref( $xml_data->[$gidx][$fidx][$pidx] ) ne '' ) {
$pidx++;
next;
}
unless( $xml_data->[$gidx][$fidx][$pidx] eq 'faction' ) {
$pidx++;
next;
}
unless( $xml_data->[$gidx][$fidx][$pidx+1][0]{'playerId'} == $USERID ) {
$pidx++;
next;
}
last;
}
$pidx++;
my $uidx = 1;
while( $uidx < $#{$xml_data->[$gidx][$fidx][$pidx]} ) {
if( ref( $xml_data->[$gidx][$fidx][$pidx][$uidx] ) ne '' ) {
$uidx++;
next;
}
unless( $xml_data->[$gidx][$fidx][$pidx][$uidx] eq 'unit' ) {
$uidx++;
next;
}
# Skip, if the unit has already moved
unless( defined( $xml_data->[$gidx][$fidx][$pidx][$uidx+1][0]{'finished'} ) && ( $xml_data->[$gidx][$fidx][$pidx][$uidx+1][0]{'finished'} eq 'false' )) {
$uidx++;
next;
}
# Guh. That struct is getting too long. Let's just look at the
# arrayref at the end
my $unit = $xml_data->[$gidx][$fidx][$pidx][$uidx+1][0];
# Otherwise, request a list of possible moves
$req = HTTP::Request->new( 'POST', 'http://weewar.com/api1/eliza', \@req_headers, "{'x'} . "\" y=\"" . $unit->{'y'} . "\" type=\"" . $unit->{'type'} . "\" />" );
$req->authorization_basic( $USERNAME, $PASSKEY );
$req_data = $agent->request( $req );
# Parse the list, picking a random move
my $unit_data = $parser->parse( $req_data->content );
my @moves = ( [$unit->{'x'}, $unit->{'y'}] );
my $umidx = 1;
while( $umidx < $#{$unit_data->[1]} ) {
if( ref( $unit_data->[1][$umidx] ) ne '' ) {
$umidx++;
next;
}
unless( $unit_data->[1][$umidx] eq 'coordinate' ) {
$umidx++;
next;
}
$umidx++;
push @moves, [$unit_data->[1][$umidx][0]{'x'}, $unit_data->[1][$umidx][0]{'y'}];
$umidx++;
}
my $move = int(rand($#moves + 1));
# Check if the unit ended up on a non-owned base
# Request a list of possible attacks from the ending position
$req = HTTP::Request->new( 'POST', 'http://weewar.com/api1/eliza', \@req_headers, "{'type'} . "\" />" );
$req->authorization_basic( $USERNAME, $PASSKEY );
$req_data = $agent->request( $req );
# Parse the list, picking a random attack
my $att_data = $parser->parse( $req_data->content );
my @attacks = ();
my $atidx = 1;
while( $atidx < $#{$att_data->[1]} ) {
if( ref( $att_data->[1][$atidx] ) ne '' ) {
$atidx++;
next;
}
unless( $att_data->[1][$atidx] eq 'coordinate' ) {
$atidx++;
next;
}
$atidx++;
push @attacks, [$att_data->[1][$atidx][0]{'x'}, $att_data->[1][$atidx][0]{'y'}];
$atidx++;
}
# Submit order.
my $order = "";
if( $move > 0 ) {
$order .= "";
}
if( @attacks ) {
my $att = int(rand($#attacks +1));
$order .= "";
}
$order .= "";
$req = HTTP::Request->new( 'POST', 'http://weewar.com/api1/eliza', \@req_headers, $order );
$req->authorization_basic( $USERNAME, $PASSKEY );
$req_data = $agent->request( $req );
coredump( "Moved unit: $game_id", "$order\n" . $req_data->content, 1 );
$uidx++;
}
# If I have free bases, build infantry.
$uidx = 1;
while( $uidx < $#{$xml_data->[$gidx][$fidx][$pidx]} ) {
if( ref( $xml_data->[$gidx][$fidx][$pidx][$uidx] ) ne '' ) {
$uidx++;
next;
}
unless( $xml_data->[$gidx][$fidx][$pidx][$uidx] eq 'terrain' ) {
$uidx++;
next;
}
unless( $xml_data->[$gidx][$fidx][$pidx][$uidx+1][0]{'type'} eq 'Base' ) {
$uidx++;
next;
}
my $base = $xml_data->[$gidx][$fidx][$pidx][$uidx+1][0];
my $order = "";
$req = HTTP::Request->new( 'POST', 'http://weewar.com/api1/eliza', \@req_headers, $order );
$req->authorization_basic( $USERNAME, $PASSKEY );
$req_data = $agent->request( $req );
coredump( "Built unit: $game_id", "$order\n" . $req_data->content, 1 );
$uidx++;
}
$finished = 1;
}
# End turn
$req = HTTP::Request->new( 'POST', 'http://weewar.com/api1/eliza',
\@req_headers,
"" );
$req->authorization_basic( $USERNAME, $PASSKEY );
$req_data = $agent->request( $req );
coredump( "Ended turn on game: $game_id", $req_data->content, 1 );
}
sub join_game ($$$)
{
# Only join unrated games, otherwise, decline.
my $agent = $_[0];
my $game_id = $_[1] + 0;
my $rated = $_[2];
my $invite = $rated ? 'decline' : 'accept';
my @req_headers = ( 'Content-Type', 'application/xml' );
my( $req, $req_data, $xml_data );
unless( $game_id > 0 ) {
coredump( 'Illegal game id.', $_[0], 0 );
}
$req = HTTP::Request->new( 'POST', 'http://weewar.com/api1/eliza',
\@req_headers,
"<${invite}Invitation/>" );
$req->authorization_basic( $USERNAME, $PASSKEY );
$req_data = $agent->request( $req );
coredump( "Are we done?", $req_data->content, 1 );
}
# main
{
my $agent = initialize_data();
take_turn( $agent, $ARGV[0] ) if( $#ARGV >= 0 && $ARGV[0] > 0);
my @games_list = get_waiting_games_list( $agent );
}