Class: WeewarAI::Unit
Overview
An instance of the Unit class corresponds to a single unit in a game.
The Unit class provides access to Unit attributes like coordinates (x, y), health (hp), and type (trooper, raider, etc.). Also available are tactical calculation data, such as enemy targets that can be attacked, and hexes that can be reached in the current turn.
Unit s can be ordered to move, attack or repair.
Read the full method listing to see everything you can do with a Unit.
Constant Summary collapse
- SYMBOL_FOR_UNIT =
{ 'Trooper' => :linf, 'Heavy Trooper' => :hinf, 'Raider' => :raider, 'Assault Artillery' => :aart, 'Tank' => :tank, 'Heavy Tank' => :htank, 'Berserker' => :bers, 'Light Artillery' => :lart, 'Heavy Artillery' => :hart, 'DFA' => :dfa, 'Hovercraft' => :hover, #'capturing' => :capturing, }
- TYPE_FOR_SYMBOL =
{ :linf => 'Trooper', :hinf => 'Heavy Trooper', :raider => 'Raider', :tank => 'Tank', :htank => 'Heavy Tank', :lart => 'Light Artillery', :hart => 'Heavy Artillery', # TODO: rest }
- UNIT_CLASSES =
{ :linf => :soft, :hinf => :soft, :raider => :hard, :aart => :hard, :tank => :hard, :htank => :hard, :bers => :hard, :lart => :hard, :hart => :hard, :dfa => :hard, :capturing => :soft, :hover => :amphibic, }
- UNIT_COSTS =
<Pistos> These need to be checked, I was just going by memory
{ :linf => 75, :hinf => 150, :raider => 200, :tank => 300, :hover => 300, :htank => 600, :lart => 200, :aart => 450, :hart => 600, :dfa => 1200, :bers => 900, :sboat => 200, :dest => 1100, :bship => 2000, :sub => 1200, :jet => 800, :heli => 600, :bomber => 900, :aa => 300, }
- REPAIR_RATE =
<Pistos> These need to be checked, I was just going by memory
{ :linf => 1, :hinf => 1, :raider => 2, :tank => 2, :hover => 2, :htank => 2, :lart => 1, :aart => 2, :hart => 1, :dfa => 1, :bers => 1, :sboat => 2, :dest => 1, :bship => 1, :sub => 1, :jet => 3, :heli => 3, :bomber => 3, :aa => 1, }
- INFINITY =
99999999
Instance Attribute Summary collapse
-
#faction ⇒ Object
readonly
Returns the value of attribute faction.
-
#hex ⇒ Object
readonly
Returns the value of attribute hex.
-
#hp ⇒ Object
Returns the value of attribute hp.
-
#type ⇒ Object
readonly
Returns the value of attribute type.
Instance Method Summary collapse
-
#==(other) ⇒ Object
Comparison for equality with another Unit.
-
#allied_units ⇒ Object
An Array of the Unit s of the Game which are on the same side as this Unit.
-
#allied_with?(unit) ⇒ Boolean
Whether or not the given unit is an ally of this Unit.
-
#attack(unit) ⇒ Object
Commands this Unit to attack another Unit.
-
#can_attack?(target) ⇒ Boolean
Whether or not the Unit can attack the given target.
-
#can_capture? ⇒ Boolean
Whether or not the Unit type can capture bases or not.
-
#can_reach?(hex) ⇒ Boolean
Whether or not the Unit can reach the given Hex in the current turn.
-
#capturing? ⇒ Boolean
Whether or not the unit is capturing a base at the moment.
-
#destinations ⇒ Object
(also: #movement_options, #movementOptions)
An Array of the Hex es which the given Unit can move to in the current turn.
-
#entrance_cost(hex) ⇒ Object
The cost in movement points for the unit to enter the given Hex.
-
#finished? ⇒ Boolean
Whether or not the unit can be ordered to do anything further.
-
#initialize(game, hex, faction, type, hp, finished, capturing = false) ⇒ Unit
constructor
Units are created by the Map class.
-
#move_to(destination, options = {}) ⇒ Object
(also: #move)
Moves the given Unit to the given destination if it is reachable in one turn, otherwise moves the Unit towards it using the optimal path.
-
#path_cost(path) ⇒ Object
The cost in movement points for the unit to travel along the given path.
-
#process_attack(xml_text) ⇒ Object
This is an internal method used to update the Unit attributes after a command is sent to the weewar server.
-
#repair ⇒ Object
Commands the Unit to undergo repairs.
-
#send(xml) ⇒ Object
Sends an XML command to the server regarding this Unit.
-
#shortest_path(dest, exclusions = []) ⇒ Object
The shortest path (as an Array of Hexes) from the Unit’s current location to the given destination.
-
#shortest_paths(exclusions = []) ⇒ Object
Calculate all shortest paths from the Unit’s current Hex to every other Hex, as per Dijkstra’s algorithm ( en.wikipedia.org/wiki/Dijkstra’s_algorithm ).
-
#targets(origin = @hex) ⇒ Object
(also: #attack_options, #attackOptions)
An Array of the Units which this Unit can attack in the current turn.
- #to_s ⇒ Object
-
#travel_cost(dest) ⇒ Object
The cost in movement points for this unit to travel to the given destination.
-
#unit_class ⇒ Object
The unit class of this unit.
-
#x ⇒ Object
The Unit’s current x coordinate (column).
-
#y ⇒ Object
The Unit’s current y coordinate (row).
Constructor Details
#initialize(game, hex, faction, type, hp, finished, capturing = false) ⇒ Unit
Units are created by the Map class. No need to instantiate any on your own.
107 108 109 110 111 112 113 114 115 |
# File 'lib/weewar-ai/unit.rb', line 107 def initialize( game, hex, faction, type, hp, finished, capturing = false ) sym = SYMBOL_FOR_UNIT[ type ] if sym.nil? raise "Unknown type: '#{type}'" end @game, @hex, @faction, @type, @hp, @finished, @capturing = game, hex, faction, sym, hp.to_i, finished, capturing end |
Instance Attribute Details
#faction ⇒ Object (readonly)
Returns the value of attribute faction.
14 15 16 |
# File 'lib/weewar-ai/unit.rb', line 14 def faction @faction end |
#hex ⇒ Object (readonly)
Returns the value of attribute hex.
14 15 16 |
# File 'lib/weewar-ai/unit.rb', line 14 def hex @hex end |
#hp ⇒ Object
Returns the value of attribute hp.
15 16 17 |
# File 'lib/weewar-ai/unit.rb', line 15 def hp @hp end |
#type ⇒ Object (readonly)
Returns the value of attribute type.
14 15 16 |
# File 'lib/weewar-ai/unit.rb', line 14 def type @type end |
Instance Method Details
#==(other) ⇒ Object
Comparison for equality with another Unit. A Unit equals another Unit if it is standing on the same Hex, is of the same Faction, and is the same type.
if new_unit == old_unit
end
162 163 164 165 166 |
# File 'lib/weewar-ai/unit.rb', line 162 def ==( other ) @hex == other.hex and @faction == other.faction and @type == other.type end |
#allied_units ⇒ Object
An Array of the Unit s of the Game which are on the same side as this Unit.
friends = my_unit.allied_units
231 232 233 |
# File 'lib/weewar-ai/unit.rb', line 231 def allied_units @game.units.find_all { |u| u.faction == @faction } end |
#allied_with?(unit) ⇒ Boolean
Whether or not the given unit is an ally of this Unit.
if not my_unit.allied_with?( other_unit )
my_unit.attack other_unit
end
239 240 241 |
# File 'lib/weewar-ai/unit.rb', line 239 def allied_with?( unit ) @faction == unit.faction end |
#attack(unit) ⇒ Object
Commands this Unit to attack another Unit. This Unit will not move anywhere in the attempt to attack. Provide either a Unit or a Hex to attack as a method argument.
my_unit.attack enemy_unit
503 504 505 506 507 508 509 510 511 |
# File 'lib/weewar-ai/unit.rb', line 503 def attack( unit ) x = unit.x y = unit.y result = send "<attack x='#{x}' y='#{y}'/>" process_attack result @game.last_attacked = @game.map[ x, y ].unit true end |
#can_attack?(target) ⇒ Boolean
Whether or not the Unit can attack the given target. Returns true iff the Unit can still take action in the current round, and the target is in range.
if my_unit.can_attack? enemy_unit
my_unit.attack enemy_unit
end
204 205 206 |
# File 'lib/weewar-ai/unit.rb', line 204 def can_attack?( target ) not @finished and targets.include?( target ) end |
#can_capture? ⇒ Boolean
Whether or not the Unit type can capture bases or not. Be aware that this can return true even if the Unit can no longer take action during the current turn.
if my_unit.can_capture?
my_unit.move_to enemy_base
end
174 175 176 |
# File 'lib/weewar-ai/unit.rb', line 174 def can_capture? [ :linf, :hinf, :hover ].include? @type end |
#can_reach?(hex) ⇒ Boolean
Whether or not the Unit can reach the given Hex in the current turn.
if my_unit.can_reach? the_hex
my_unit.move_to the_hex
end
225 226 227 |
# File 'lib/weewar-ai/unit.rb', line 225 def can_reach?( hex ) destinations.include? hex end |
#capturing? ⇒ Boolean
Whether or not the unit is capturing a base at the moment.
if not my_trooper.capturing?
# do stuff with my_trooper
end
145 146 147 |
# File 'lib/weewar-ai/unit.rb', line 145 def capturing? @capturing end |
#destinations ⇒ Object Also known as: movement_options, movementOptions
An Array of the Hex es which the given Unit can move to in the current turn.
possible_moves = my_unit.destinations
210 211 212 213 214 215 216 217 |
# File 'lib/weewar-ai/unit.rb', line 210 def destinations coords = XmlSimple.xml_in( @game.send( "<movementOptions x='#{x}' y='#{y}' type='#{TYPE_FOR_SYMBOL[@type]}'/>" ) )[ 'coordinate' ] coords.map { |c| @game.map[ c[ 'x' ], c[ 'y' ] ] } end |
#entrance_cost(hex) ⇒ Object
The cost in movement points for the unit to enter the given Hex. This is an internal method used for travel-related calculations; you should not normally need to use this yourself.
250 251 252 253 254 255 256 257 258 |
# File 'lib/weewar-ai/unit.rb', line 250 def entrance_cost( hex ) return nil if hex.nil? specs_for_type = Hex.terrain_specs[ hex.type ] if specs_for_type.nil? raise "No specs for type '#{hex.type.inspect}': #{Hex.terrain_specs.inspect}" end specs_for_type[ :movement ][ unit_class ] end |
#finished? ⇒ Boolean
Whether or not the unit can be ordered to do anything further.
if not my_unit.finished?
# do stuff with my_unit
end
137 138 139 |
# File 'lib/weewar-ai/unit.rb', line 137 def finished? @finished end |
#move_to(destination, options = {}) ⇒ Object Also known as: move
Moves the given Unit to the given destination if it is reachable in one turn, otherwise moves the Unit towards it using the optimal path.
this_unit.move_to some_hex
that_unit.move_to enemy_unit
If a Unit or an Array of Units is passed as the :also_attack option, those Units will be prioritized for attack after moving, with the Units assumed to be given from highest priority (index 0) to lowest.
another_unit.move_to(
enemy_unit,
:also_attack => [ enemy_unit ] + enemy_artillery )
)
If an Array of hexes is provided as the :exclusions option, the Unit will not pass through any of the exclusion Hex es on its way to the destination.
spy_unit.move_to(
enemy_base,
:exclusions => well_defended_choke_point_hexes
)
By default, moving onto a base with a capturing unit will attempt a capture. Set the :no_capture option to true to prevent this.
my_trooper.move_to( enemy_base, :no_capture => true )
navy_seal.move_to(
enemy_base,
:also_attack => hard_targets,
:exclusions => fortified_hexes,
:no_capture => true
)
393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 |
# File 'lib/weewar-ai/unit.rb', line 393 def move_to( destination, = {} ) command = "" [ :exclusions ] ||= [] new_hex = @hex if destination != @hex # Travel path = shortest_path( destination, [ :exclusions ] ) if path.empty? $stderr.puts "No path from #{self} to #{destination}" else dests = destinations new_dest = path.pop while new_dest and not dests.include?( new_dest ) new_dest = path.pop end end if new_dest.nil? $stderr.puts " Can't move #{self} to #{destination}" else o = new_dest.unit if o and allied_with?( o ) # Can't move through allied units [ :exclusions ] << new_dest return move_to( destination, ) else x = new_dest.x y = new_dest.y new_hex = new_dest command << "<move x='#{x}' y='#{y}'/>" end end end target = nil also_attack = [ :also_attack ] if also_attack enemies = targets( new_hex ) if not enemies.empty? case also_attack when Array preferred = also_attack & enemies else preferred = [ also_attack ] & enemies end target = preferred.first# || enemies.random if target command << "<attack x='#{target.x}' y='#{target.y}'/>" end end end if( not [ :no_capture ] and can_capture? and new_hex == destination and new_hex.capturable? ) puts "#{self} capturing #{new_hex}" command << "<capture/>" end if not command.empty? result = send( command ) puts "Moved #{self} to #{new_hex}" @hex.unit = nil new_hex.unit = self @hex = new_hex if target #<attack target='[3,4]' damageReceived='2' damageInflicted='7' remainingQuantity='8' /> process_attack result @game.last_attacked = target end # Success true end end |
#path_cost(path) ⇒ Object
The cost in movement points for the unit to travel along the given path. The path given should be an Array of Hexes. This is an internal method used for travel-related calculations; you should not normally need to use this yourself.
264 265 266 267 268 |
# File 'lib/weewar-ai/unit.rb', line 264 def path_cost( path ) path.inject( 0 ) { |sum,hex| sum + entrance_cost( hex ) } end |
#process_attack(xml_text) ⇒ Object
This is an internal method used to update the Unit attributes after a command is sent to the weewar server. You should not call this yourself.
479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 |
# File 'lib/weewar-ai/unit.rb', line 479 def process_attack( xml_text ) xml = XmlSimple.xml_in( xml_text, { 'ForceArray' => false } )[ 'attack' ] if xml[ 'target' ] =~ /\[(\d+),(\d+)\]/ x, y = $1, $2 enemy = @game.map[ x, y ].unit end if enemy.nil? raise "Server says enemy attacked was at (#{x},#{y}), but we have no record of an enemy there." end damage_inflicted = xml[ 'damageInflicted' ].to_i enemy.hp -= damage_inflicted damage_received = xml[ 'damageReceived' ].to_i @hp = xml[ 'remainingQuantity' ].to_i puts " #{self} (-#{damage_received}: #{@hp}) ATTACKED #{enemy} (-#{damage_inflicted}: #{enemy.hp})" end |
#repair ⇒ Object
Commands the Unit to undergo repairs.
my_hurt_unit.repair
515 516 517 518 |
# File 'lib/weewar-ai/unit.rb', line 515 def repair send "<repair/>" @hp += REPAIR_RATE[ @type ] end |
#send(xml) ⇒ Object
Sends an XML command to the server regarding this Unit. This is an internal method that you should normally not need to call yourself.
339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 |
# File 'lib/weewar-ai/unit.rb', line 339 def send( xml ) command = "<unit x='#{x}' y='#{y}'>#{xml}</unit>" response = @game.send command doc = Hpricot.XML( response ) @finished = !! doc.at( 'finished' ) if not @finished $stderr.puts " #{self} NOT FINISHED:\n\t#{response}" end if not doc.at( 'ok' ) error = doc.at 'error' if error = "ERROR from server: #{error.inner_html}" else = "RECEIVED:\n#{response}" end raise "Failed to execute:\n#{command}\n#{}" end response end |
#shortest_path(dest, exclusions = []) ⇒ Object
The shortest path (as an Array of Hexes) from the Unit’s current location to the given destination.
If the optional exclusion array is provided, the path will not pass through any Hex in the exclusion array.
best_path = my_trooper.shortest_path( enemy_base )
284 285 286 287 288 289 290 291 292 293 294 |
# File 'lib/weewar-ai/unit.rb', line 284 def shortest_path( dest, exclusions = [] ) exclusions ||= [] previous = shortest_paths( exclusions ) s = [] u = dest.hex while previous[ u ] s.unshift u u = previous[ u ] end s end |
#shortest_paths(exclusions = []) ⇒ Object
Calculate all shortest paths from the Unit’s current Hex to every other Hex, as per Dijkstra’s algorithm ( en.wikipedia.org/wiki/Dijkstra’s_algorithm ). Most AIs will only need to make use of the shortest_path method instead.
300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 |
# File 'lib/weewar-ai/unit.rb', line 300 def shortest_paths( exclusions = [] ) # Initialization exclusions ||= [] source = hex dist = Hash.new previous = Hash.new q = [] @game.map.each do |h| if not exclusions.include? h dist[ h ] = INFINITY q << h end end dist[ source ] = 0 # Work while not q.empty? u = q.inject { |best,h| dist[ h ] < dist[ best ] ? h : best } q.delete u @game.map.hex_neighbours( u ).each do |v| next if exclusions.include? v alt = dist[ u ] + entrance_cost( v ) if alt < dist[ v ] dist[ v ] = alt previous[ v ] = u end end end # Results previous end |
#targets(origin = @hex) ⇒ Object Also known as: attack_options, attackOptions
An Array of the Units which this Unit can attack in the current turn. If the optional origin Hex is provided, the target list is calculated as if the unit were on that Hex instead of its current Hex.
enemies_in_range = my_unit.targets
enemies_in_range_from_there = my_unit.targets possible_attack_position
183 184 185 186 187 188 189 190 191 192 193 194 |
# File 'lib/weewar-ai/unit.rb', line 183 def targets( origin = @hex ) coords = XmlSimple.xml_in( @game.send( "<attackOptions x='#{origin.x}' y='#{origin.y}' type='#{TYPE_FOR_SYMBOL[@type]}'/>" ) )[ 'coordinate' ] if coords coords.map { |c| @game.map[ c[ 'x' ], c[ 'y' ] ].unit }.compact else [] end end |
#to_s ⇒ Object
117 118 119 |
# File 'lib/weewar-ai/unit.rb', line 117 def to_s "#{@faction} #{@type} @ (#{@hex.x},#{@hex.y})" end |
#travel_cost(dest) ⇒ Object
The cost in movement points for this unit to travel to the given destination.
272 273 274 275 |
# File 'lib/weewar-ai/unit.rb', line 272 def travel_cost( dest ) sp = shortest_path( dest ) path_cost( sp ) end |
#unit_class ⇒ Object
The unit class of this unit. i.e. :soft, :hard, etc.
if my_unit.unit_class == :hard
# attack some troopers!
end
153 154 155 |
# File 'lib/weewar-ai/unit.rb', line 153 def unit_class UNIT_CLASSES[ @type ] end |
#x ⇒ Object
The Unit’s current x coordinate (column).
my_unit.x
123 124 125 |
# File 'lib/weewar-ai/unit.rb', line 123 def x @hex.x end |
#y ⇒ Object
The Unit’s current y coordinate (row).
my unit.y
129 130 131 |
# File 'lib/weewar-ai/unit.rb', line 129 def y @hex.y end |