#/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 ); }