Pistos-weewar-ai 2008.06.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/READTHAT +1 -0
- data/Rakefile +62 -0
- data/THAT +23 -0
- data/examples/basic.rb +71 -0
- data/lib/weewar-ai.rb +14 -0
- data/lib/weewar-ai/__dir__.rb +23 -0
- data/lib/weewar-ai/ai.rb +81 -0
- data/lib/weewar-ai/api.rb +100 -0
- data/lib/weewar-ai/faction.rb +57 -0
- data/lib/weewar-ai/game.rb +173 -0
- data/lib/weewar-ai/hex.rb +112 -0
- data/lib/weewar-ai/map.rb +135 -0
- data/lib/weewar-ai/player.rb +24 -0
- data/lib/weewar-ai/traits.rb +76 -0
- data/lib/weewar-ai/unit.rb +520 -0
- metadata +94 -0
data/READTHAT
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
Don't read ME; read THAT!
|
data/Rakefile
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
require 'rake/clean'
|
4
|
+
require 'rake/rdoctask'
|
5
|
+
|
6
|
+
$:.unshift File.join( File.dirname(__FILE__), "lib" )
|
7
|
+
|
8
|
+
root = File.expand_path( File.dirname(__FILE__) )
|
9
|
+
|
10
|
+
# ------------------
|
11
|
+
|
12
|
+
#task :default => ['spec']
|
13
|
+
#task :test => ['spec']
|
14
|
+
|
15
|
+
desc "generate rdoc"
|
16
|
+
Rake::RDocTask.new do |rdoc|
|
17
|
+
files = [
|
18
|
+
'lib/**/*.rb',
|
19
|
+
#'examples/*.rb',
|
20
|
+
'THAT',
|
21
|
+
'READTHAT'
|
22
|
+
#'spec/**/*.rb',
|
23
|
+
]
|
24
|
+
rdoc.rdoc_files.add( files )
|
25
|
+
rdoc.main = "WeewarAI::AI" # page to start on
|
26
|
+
rdoc.title = "Weewar AI Ruby library"
|
27
|
+
# rdoc.template = "/misc/pistos/unpack/allison-2.3/allison.rb"
|
28
|
+
rdoc.template = "jamis"
|
29
|
+
rdoc.rdoc_dir = '/var/www/localhost/htdocs/weewar/ai-doc' # rdoc output folder
|
30
|
+
rdoc.options << '--line-numbers' << '--inline-source'
|
31
|
+
end
|
32
|
+
|
33
|
+
#desc 'Run coverage examiner (rcov)'
|
34
|
+
#task 'rcov' do
|
35
|
+
# exec( "rcov -o /var/www/localhost/htdocs/m4dbi/rcov spec/*.rb" )
|
36
|
+
#end
|
37
|
+
|
38
|
+
#desc 'Run all specs'
|
39
|
+
#task 'spec' do
|
40
|
+
# exec "bacon #{root}/spec/*.rb"
|
41
|
+
#end
|
42
|
+
|
43
|
+
#desc 'Build nightly gem'
|
44
|
+
#task 'nightly' do
|
45
|
+
# output = `gem build #{root}/gemspecs/m4dbi-nightly.gemspec`
|
46
|
+
# version = Time.now.strftime( "%Y.%m.%d" )
|
47
|
+
# `mv m4dbi-#{version}.gem m4dbi-nightly.gem`
|
48
|
+
#end
|
49
|
+
|
50
|
+
desc 'Make release'
|
51
|
+
task 'release' do
|
52
|
+
output = `gem build weewar-ai.gemspec`
|
53
|
+
end
|
54
|
+
|
55
|
+
#desc 'Build examples from specs'
|
56
|
+
#task 'examples' do
|
57
|
+
# Dir[ 'spec/*.rb' ].each do |specfile|
|
58
|
+
# next if specfile =~ /helper\.rb/
|
59
|
+
# base = File.basename( specfile, ".rb" )
|
60
|
+
# `ruby -I /misc/svn/specs2examples/lib /misc/svn/specs2examples/bin/specs2examples #{specfile} > /var/www/localhost/htdocs/m4dbi/examples/#{base}.html`
|
61
|
+
# end
|
62
|
+
#end
|
data/THAT
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
== Dependencies
|
2
|
+
|
3
|
+
- XMLSimple
|
4
|
+
- Mechanize
|
5
|
+
- Hpricot
|
6
|
+
|
7
|
+
The dependencies should be pulled along with the main gem installation.
|
8
|
+
|
9
|
+
== Installation
|
10
|
+
|
11
|
+
gem install weewar-ai --source http://purepistos.net
|
12
|
+
|
13
|
+
== Usage
|
14
|
+
|
15
|
+
See examples/ dir. See rdoc at http://weewar.purepistos.net/ai-doc/ .
|
16
|
+
|
17
|
+
== Source Code
|
18
|
+
|
19
|
+
git clone git://github.com/Pistos/weewar-ai.git
|
20
|
+
cd /usr/lib/ruby/site_ruby/1.8
|
21
|
+
ln -s /path/to/cloned/weewar-ai/lib/weewar-ai.rb
|
22
|
+
ln -s /path/to/cloned/weewar-ai/lib/weewar-ai
|
23
|
+
|
data/examples/basic.rb
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'weewar-ai'
|
4
|
+
#$debug = true
|
5
|
+
|
6
|
+
# A simple, working example of using the WeewarAI library.
|
7
|
+
# This bot's strategy is simply to build infantry and move them onto any
|
8
|
+
# bases it does not own. It will attack any enemies it meets along the way.
|
9
|
+
|
10
|
+
class AIBasic < WeewarAI::AI
|
11
|
+
|
12
|
+
def initialize
|
13
|
+
super(
|
14
|
+
:server => 'test.weewar.com',
|
15
|
+
:username => 'aiBasic', # change this to your bot's username
|
16
|
+
:api_key => 'GMmTBxE2ztNbdABAW5vgVZVhH' # change this to your bot's API key
|
17
|
+
)
|
18
|
+
end
|
19
|
+
|
20
|
+
def run_once
|
21
|
+
one_accepted = accept_all_invitations
|
22
|
+
if one_accepted
|
23
|
+
puts "Accepted some invitations."
|
24
|
+
refresh
|
25
|
+
end
|
26
|
+
|
27
|
+
@needy_games.each do |g|
|
28
|
+
take_turn g
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def take_turn( game )
|
33
|
+
puts
|
34
|
+
puts "*" * 80
|
35
|
+
puts "Taking turn for game #{game.id}"
|
36
|
+
i = me = my = game.my_faction
|
37
|
+
|
38
|
+
# Find a place to go, things to shoot
|
39
|
+
destination = game.enemy_bases.first
|
40
|
+
enemies = game.enemy_units
|
41
|
+
|
42
|
+
# Move units
|
43
|
+
my.units.find_all { |u| not u.finished? }.each do |unit|
|
44
|
+
unit.move_to(
|
45
|
+
destination,
|
46
|
+
:also_attack => enemies
|
47
|
+
)
|
48
|
+
end
|
49
|
+
|
50
|
+
# Build
|
51
|
+
game.my_bases.each do |base|
|
52
|
+
next if base.occupied?
|
53
|
+
|
54
|
+
if i.can_afford?( :linf )
|
55
|
+
base.build :linf
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
puts "Ending turn for game #{game.id}"
|
60
|
+
game.finish_turn
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
begin
|
66
|
+
bot = AIBasic.new
|
67
|
+
bot.run_once
|
68
|
+
rescue Exception => e
|
69
|
+
$stderr.puts "#{e.class}: #{e.message}"
|
70
|
+
$stderr.puts e.backtrace.join( "\n\t" )
|
71
|
+
end
|
data/lib/weewar-ai.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'xmlsimple'
|
2
|
+
|
3
|
+
_DIR = File.expand_path( File.dirname( __FILE__ ) )
|
4
|
+
|
5
|
+
require "#{_DIR}/weewar-ai/__dir__"
|
6
|
+
require "#{__DIR__}/weewar-ai/traits"
|
7
|
+
require "#{__DIR__}/weewar-ai/player"
|
8
|
+
require "#{__DIR__}/weewar-ai/faction"
|
9
|
+
require "#{__DIR__}/weewar-ai/hex"
|
10
|
+
require "#{__DIR__}/weewar-ai/unit"
|
11
|
+
require "#{__DIR__}/weewar-ai/map"
|
12
|
+
require "#{__DIR__}/weewar-ai/game"
|
13
|
+
require "#{__DIR__}/weewar-ai/api"
|
14
|
+
require "#{__DIR__}/weewar-ai/ai"
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# From Ramaze ( http://ramaze.net )
|
2
|
+
# Copyright (c) 2008 Michael Fellinger m.fellinger@gmail.com
|
3
|
+
|
4
|
+
# Extensions for Kernel
|
5
|
+
|
6
|
+
module Kernel
|
7
|
+
unless defined?__DIR__
|
8
|
+
|
9
|
+
# This is similar to +__FILE__+ and +__LINE__+, and returns a String
|
10
|
+
# representing the directory of the current file is.
|
11
|
+
# Unlike +__FILE__+ the path returned is absolute.
|
12
|
+
#
|
13
|
+
# This method is convenience for the
|
14
|
+
# File.expand_path(File.dirname(__FILE__))
|
15
|
+
# idiom.
|
16
|
+
#
|
17
|
+
|
18
|
+
def __DIR__()
|
19
|
+
filename = caller[0][/^(.*):/, 1]
|
20
|
+
File.expand_path(File.dirname(filename))
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
data/lib/weewar-ai/ai.rb
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# All parts of the library reside in the WeewarAI module.
|
4
|
+
module WeewarAI
|
5
|
+
|
6
|
+
# Begin creating your bot by subclassing the WeewarAI::AI class.
|
7
|
+
#
|
8
|
+
# class MyBot < WeewarAI::AI
|
9
|
+
# end
|
10
|
+
#
|
11
|
+
# See examples/basic.rb for a simple working example.
|
12
|
+
class AI
|
13
|
+
# In your bot's initialize method, call the AI superclass's
|
14
|
+
# initialize method with these parameters (values are examples):
|
15
|
+
#
|
16
|
+
# super(
|
17
|
+
# :server => 'test.weewar.com',
|
18
|
+
# :username => 'aiMyBot',
|
19
|
+
# :api_key => 'r0goujPhKJEM6udL3RNfBtcS9',
|
20
|
+
# )
|
21
|
+
#
|
22
|
+
# Retrieve your API key from http://test.weewar.com/apiToken .
|
23
|
+
#
|
24
|
+
# Once constructed, your AI instance will have the following instance
|
25
|
+
# variables available:
|
26
|
+
#
|
27
|
+
# @games : An Array of the Game s your AI is in.
|
28
|
+
#
|
29
|
+
# @needy_games : The subset of Game s in which it is your AI's turn.
|
30
|
+
#
|
31
|
+
# @invitations : The ids of Game s to which your AI is invited.
|
32
|
+
def initialize( params )
|
33
|
+
@username = params[ :username ]
|
34
|
+
WeewarAI::API.init( params )
|
35
|
+
refresh
|
36
|
+
end
|
37
|
+
|
38
|
+
# Retrieves your AI's headquarters data from the game server,
|
39
|
+
# and updates the instance variables @games, @needy_games and
|
40
|
+
# @invitations.
|
41
|
+
# bot.refresh
|
42
|
+
def refresh
|
43
|
+
xml = XmlSimple.xml_in(
|
44
|
+
WeewarAI::API.get( "/headquarters" ),
|
45
|
+
{ 'ForceArray' => [ 'game' ], }
|
46
|
+
)
|
47
|
+
gxml = xml[ 'game' ]
|
48
|
+
@invitations = gxml.find_all { |g|
|
49
|
+
g[ 'link' ] =~ /join/
|
50
|
+
}.map { |g|
|
51
|
+
g[ 'id' ].to_i
|
52
|
+
}
|
53
|
+
gxml = gxml.reject { |g|
|
54
|
+
@invitations.include? g[ 'id' ].to_i
|
55
|
+
}
|
56
|
+
@games = gxml.map { |g|
|
57
|
+
WeewarAI::Game.new( g[ 'id' ] )
|
58
|
+
}
|
59
|
+
@needy_games = @games.find_all { |g|
|
60
|
+
g.current_player and g.current_player.name == @username
|
61
|
+
}
|
62
|
+
end
|
63
|
+
|
64
|
+
# Accepts the invitation to join the game identified by the game_id.
|
65
|
+
# bot.accept_invitation 165
|
66
|
+
def accept_invitation( game_id )
|
67
|
+
response = WeewarAI::API.accept_invitation( game_id )
|
68
|
+
%r{<ok/>} === response
|
69
|
+
end
|
70
|
+
|
71
|
+
# Accepts all game invitations.
|
72
|
+
# bot.accept_all_invitations
|
73
|
+
def accept_all_invitations
|
74
|
+
one_accepted = false
|
75
|
+
@invitations.each do |invitation|
|
76
|
+
one_accepted ||= accept_invitation( invitation )
|
77
|
+
end
|
78
|
+
one_accepted
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
require 'mechanize'
|
2
|
+
|
3
|
+
module WeewarAI
|
4
|
+
GOD_LOVES_YOU = true
|
5
|
+
VERSION = '2008.06.09'
|
6
|
+
|
7
|
+
# The API class contains some lower-level methods. You should not normally
|
8
|
+
# need to use them yourself. Instead, use the methods of your AI instance.
|
9
|
+
class API
|
10
|
+
# Initializes the connection to the weewar.com API. You do not need to
|
11
|
+
# call this yourself; it is called for you when you subclass AI.
|
12
|
+
def self.init( params )
|
13
|
+
[ :server, :username, :api_key ].each do |required_param|
|
14
|
+
if params[ required_param ].nil? or params[ required_param ].strip.empty?
|
15
|
+
raise "Missing #{required_param}."
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
trait[ :agent ] = agent = WWW::Mechanize.new
|
20
|
+
trait[ :username ], trait[ :api_key ] = params[ :username ], params[ :api_key ]
|
21
|
+
agent.basic_auth( params[ :username ], params[ :api_key ] )
|
22
|
+
trait[ :server ] = params[ :server ]
|
23
|
+
|
24
|
+
Hex.initialize_specs
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.username
|
28
|
+
trait[ :username ]
|
29
|
+
end
|
30
|
+
def self.agent
|
31
|
+
trait[ :agent ]
|
32
|
+
end
|
33
|
+
def self.server
|
34
|
+
trait[ :server ]
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.get( path )
|
38
|
+
result = nil
|
39
|
+
retries = 0
|
40
|
+
while GOD_LOVES_YOU
|
41
|
+
url = "http://#{server}/api1/#{path}"
|
42
|
+
begin
|
43
|
+
result = agent.get( url ).body
|
44
|
+
break
|
45
|
+
rescue EOFError, Errno::EPIPE => e
|
46
|
+
if retries < 10
|
47
|
+
$stderr.puts "Communications error fetching '#{url}'. Retrying (#{retries})..."
|
48
|
+
sleep retries + 3
|
49
|
+
retries += 1
|
50
|
+
else
|
51
|
+
break
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
if $debug
|
56
|
+
$stderr.puts "XML RECEIVE: #{result}"
|
57
|
+
end
|
58
|
+
result
|
59
|
+
end
|
60
|
+
|
61
|
+
def self.send( xml )
|
62
|
+
url = URI.parse( "http://#{server}/api1/eliza" )
|
63
|
+
req = Net::HTTP::Post.new( url.path )
|
64
|
+
req.basic_auth( trait[ :username ], trait[ :api_key ] )
|
65
|
+
req[ 'Content-Type' ] = 'application/xml'
|
66
|
+
result = Net::HTTP.new( url.host, url.port ).start { |http|
|
67
|
+
if $debug
|
68
|
+
$stderr.puts "XML SEND: #{xml}"
|
69
|
+
end
|
70
|
+
http.request( req, xml )
|
71
|
+
}.body
|
72
|
+
if $debug
|
73
|
+
$stderr.puts "XML RECEIVE: #{result}"
|
74
|
+
end
|
75
|
+
result
|
76
|
+
end
|
77
|
+
|
78
|
+
# Accepts an invitation to a game.
|
79
|
+
def self.accept_invitation( game_id )
|
80
|
+
send "<weewar game='#{game_id}'><acceptInvitation/></weewar>"
|
81
|
+
end
|
82
|
+
|
83
|
+
# Declines an invitation to a game.
|
84
|
+
def self.decline_invitation( game_id )
|
85
|
+
send "<weewar game='#{game_id}'><declineInvitation/></weewar>"
|
86
|
+
end
|
87
|
+
|
88
|
+
# Removes a game from your AI's headquarters.
|
89
|
+
def self.remove_game( game_id )
|
90
|
+
send "<weewar game='#{game_id}'><removeGame/></weewar>"
|
91
|
+
end
|
92
|
+
|
93
|
+
class << self
|
94
|
+
alias acceptInvitation accept_invitation
|
95
|
+
alias declineInvitation decline_invitation
|
96
|
+
alias removeGame remove_game
|
97
|
+
end
|
98
|
+
|
99
|
+
end
|
100
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module WeewarAI
|
2
|
+
|
3
|
+
# Instances of the Faction class correspond to factions in a Game.
|
4
|
+
# They provide some utility methods about factions, such as whether
|
5
|
+
# or not a faction is currently playing in a Game, or whether it can
|
6
|
+
# afford to buy a unit type.
|
7
|
+
class Faction
|
8
|
+
attr_reader :credits, :player_id, :player_name, :state
|
9
|
+
|
10
|
+
# You should not need to instantiate any Faction s on your own;
|
11
|
+
# they are created for you by Game instances.
|
12
|
+
def initialize( game, xml, ordinal )
|
13
|
+
@game, @ordinal = game, ordinal
|
14
|
+
@credits = xml[ 'credits' ].to_i
|
15
|
+
@current = ( xml[ 'current' ] == 'true' )
|
16
|
+
@player_id = xml[ 'playerId' ].to_i
|
17
|
+
@player_name = xml[ 'playerName' ]
|
18
|
+
@state = xml[ 'state' ]
|
19
|
+
rescue Exception => e
|
20
|
+
$stderr.puts "Input XML: " + xml.inspect
|
21
|
+
raise e
|
22
|
+
end
|
23
|
+
alias playerId player_id
|
24
|
+
alias playerName player_name
|
25
|
+
|
26
|
+
# Whether or not this Faction is the one whose turn it is in the Game.
|
27
|
+
# i = me = my = game.my_faction
|
28
|
+
# is_my_turn = i.current?
|
29
|
+
def current?
|
30
|
+
@current
|
31
|
+
end
|
32
|
+
|
33
|
+
def playing?
|
34
|
+
@state == 'playing'
|
35
|
+
end
|
36
|
+
|
37
|
+
# True iff the faction has enough credits to purchase a unit of the given type.
|
38
|
+
# i = me = my = game.my_faction
|
39
|
+
# if i.can_afford? :hart
|
40
|
+
# my_base.build :hart
|
41
|
+
# end
|
42
|
+
def can_afford?( type )
|
43
|
+
@credits >= WeewarAI::Unit::UNIT_COSTS[ type ]
|
44
|
+
end
|
45
|
+
|
46
|
+
# An Array of the Unit s in the Game belonging to this Faction.
|
47
|
+
# i = me = my = game.my_faction
|
48
|
+
# my_units = my.units
|
49
|
+
def units
|
50
|
+
@game.units.find_all { |u| u.faction == self }
|
51
|
+
end
|
52
|
+
|
53
|
+
def to_s
|
54
|
+
@player_name
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,173 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'pistos'
|
3
|
+
|
4
|
+
module WeewarAI
|
5
|
+
|
6
|
+
# The Game class is your interface to a game on the weewar server.
|
7
|
+
# Game instances are used to do such things as finish turns, surrender,
|
8
|
+
# and abandon. Also, you access game maps and units through a Game
|
9
|
+
# instance.
|
10
|
+
class Game
|
11
|
+
attr_reader :id, :name, :round, :state, :pending_invites, :pace, :type,
|
12
|
+
:url, :map, :map_url, :credits_per_base, :initial_credits, :playing_since,
|
13
|
+
:players, :units
|
14
|
+
attr_accessor :last_attacked
|
15
|
+
|
16
|
+
# Instantiate a new Game instance corresponding to the weewar game
|
17
|
+
# with the given id number.
|
18
|
+
# game = WeewarAI::Game.new( 132 )
|
19
|
+
def initialize( id )
|
20
|
+
@id = id.to_i
|
21
|
+
refresh
|
22
|
+
end
|
23
|
+
alias pendingInvites pending_invites
|
24
|
+
alias mapUrl map_url
|
25
|
+
alias creditsPerBase credits_per_base
|
26
|
+
alias initialCredits initial_credits
|
27
|
+
alias playingSince playing_since
|
28
|
+
|
29
|
+
# Hits the weewar server for all the game state data as it sees it.
|
30
|
+
# All internal variables are updated to match.
|
31
|
+
# my_game.refresh
|
32
|
+
def refresh
|
33
|
+
xml = XmlSimple.xml_in(
|
34
|
+
WeewarAI::API.get( "/gamestate/#{id}" ),
|
35
|
+
{ 'ForceArray' => [ 'faction', 'player', 'terrain', 'unit' ], }
|
36
|
+
)
|
37
|
+
#$stderr.puts xml.nice_inspect
|
38
|
+
@name = xml[ 'name' ]
|
39
|
+
@round = xml[ 'round' ].to_i
|
40
|
+
@state = xml[ 'state' ]
|
41
|
+
@pending_invites = ( xml[ 'pendingInvites' ] == 'true' )
|
42
|
+
@pace = xml[ 'pace' ].to_i
|
43
|
+
@type = xml[ 'type' ]
|
44
|
+
@url = xml[ 'url' ]
|
45
|
+
@players = xml[ 'players' ][ 'player' ].map { |p| WeewarAI::Player.new( p ) }
|
46
|
+
@map = WeewarAI::Map.new( self, xml[ 'map' ].to_i )
|
47
|
+
@map_url = xml[ 'mapUrl' ]
|
48
|
+
@credits_per_base = xml[ 'creditsPerBase' ]
|
49
|
+
@initial_credits = xml[ 'initialCredits' ]
|
50
|
+
@playing_since = Time.parse( xml[ 'playingSince' ] )
|
51
|
+
|
52
|
+
@units = Array.new
|
53
|
+
@factions = Array.new
|
54
|
+
xml[ 'factions' ][ 'faction' ].each_with_index do |faction_xml,ordinal|
|
55
|
+
faction = Faction.new( self, faction_xml, ordinal )
|
56
|
+
@factions << faction
|
57
|
+
|
58
|
+
faction_xml[ 'unit' ].each do |u|
|
59
|
+
hex = @map[ u[ 'x' ], u[ 'y' ] ]
|
60
|
+
unit = Unit.new(
|
61
|
+
self,
|
62
|
+
hex,
|
63
|
+
faction,
|
64
|
+
u[ 'type' ],
|
65
|
+
u[ 'quantity' ].to_i,
|
66
|
+
u[ 'finished' ] == 'true',
|
67
|
+
u[ 'capturing' ] == 'true'
|
68
|
+
)
|
69
|
+
@units << unit
|
70
|
+
hex.unit = unit
|
71
|
+
end
|
72
|
+
|
73
|
+
faction_xml[ 'terrain' ].each do |terrain|
|
74
|
+
hex = @map[ terrain[ 'x' ], terrain[ 'y' ] ]
|
75
|
+
if hex.type == :base
|
76
|
+
hex.faction = faction
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
# Sends some command XML for this game to the server. You should
|
83
|
+
# generally never need to call this method directly; it is used
|
84
|
+
# internally by the Game class.
|
85
|
+
def send( command_xml )
|
86
|
+
WeewarAI::API.send "<weewar game='#{@id}'>#{command_xml}</weewar>"
|
87
|
+
end
|
88
|
+
|
89
|
+
#-- -------------------------
|
90
|
+
# API Commands
|
91
|
+
#++
|
92
|
+
|
93
|
+
# End turn in this game.
|
94
|
+
# game.finish_turn
|
95
|
+
def finish_turn
|
96
|
+
send "<finishTurn/>"
|
97
|
+
end
|
98
|
+
alias finishTurn finish_turn
|
99
|
+
|
100
|
+
# Surrender in this game.
|
101
|
+
# game.surrender
|
102
|
+
def surrender
|
103
|
+
send "<surrender/>"
|
104
|
+
end
|
105
|
+
|
106
|
+
# Abandon this game.
|
107
|
+
# game.abandon
|
108
|
+
def abandon
|
109
|
+
send "<abandon/>"
|
110
|
+
end
|
111
|
+
|
112
|
+
#-- -------------------------
|
113
|
+
# Game state
|
114
|
+
#++
|
115
|
+
|
116
|
+
# The Player whose turn it is.
|
117
|
+
# turn_taker = game.current_player
|
118
|
+
def current_player
|
119
|
+
@players.find { |p| p.current? }
|
120
|
+
end
|
121
|
+
|
122
|
+
#-- --------------------------------------------------
|
123
|
+
# Utilities
|
124
|
+
#++
|
125
|
+
|
126
|
+
# The Faction of the given player.
|
127
|
+
# pistos_faction = game.faction_for_player 'Pistos'
|
128
|
+
def faction_for_player( player_name )
|
129
|
+
@factions.find { |f| f.player_name == player_name }
|
130
|
+
end
|
131
|
+
|
132
|
+
# Your AI's Faction in this game.
|
133
|
+
# me = my = i = game.my_faction
|
134
|
+
# puts "My name is #{my.player_name}."
|
135
|
+
# if i.can_afford? :htank
|
136
|
+
# my_base.build :htank
|
137
|
+
# end
|
138
|
+
def my_faction
|
139
|
+
faction_for_player WeewarAI::API.username
|
140
|
+
end
|
141
|
+
|
142
|
+
# An Array of the Units not belonging to your AI.
|
143
|
+
# bad_guys = game.enemy_units
|
144
|
+
def enemy_units
|
145
|
+
@units.find_all { |u| u.faction != my_faction }
|
146
|
+
end
|
147
|
+
|
148
|
+
# An Array of the base Hexes for this game.
|
149
|
+
# bases = game.bases
|
150
|
+
def bases
|
151
|
+
@map.bases
|
152
|
+
end
|
153
|
+
|
154
|
+
# An Array of the base Hexes owned by the given faction.
|
155
|
+
# their_bases = game.bases enemy_faction
|
156
|
+
def bases_of( faction )
|
157
|
+
@map.bases.find_all { |b| b.faction == faction }
|
158
|
+
end
|
159
|
+
|
160
|
+
# Your AI's bases in this game.
|
161
|
+
# good_bases = game.my_bases
|
162
|
+
def my_bases
|
163
|
+
bases_of my_faction
|
164
|
+
end
|
165
|
+
|
166
|
+
# An Array of bases not owned by your AI (including neutral bases).
|
167
|
+
# capturable_bases = game.enemy_bases
|
168
|
+
def enemy_bases
|
169
|
+
@map.bases.find_all { |b| b.faction != my_faction }
|
170
|
+
end
|
171
|
+
|
172
|
+
end
|
173
|
+
end
|