Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

implemented zoc

  • Loading branch information...
commit 18a952c850da24bd56f881ea593a131bb94aa16d 1 parent 604f407
@micktaiwan authored
View
22 bots/chuckbot.rb
@@ -25,7 +25,8 @@ def take_turn
# Move units
# TODO: make 2 loops on units that didn't move before doing something else
- units.sort_by{|u| [u.attack_range[0], -u.defense_strength, -u.hp, -u.speed(1)]}.each do |unit|
+ units.sort_by{|u| [u.speed(1)]}.each do |unit|
+ # [u.attack_range[0], -u.defense_strength, -u.hp, -u.speed(1)]
#next if !unit # useless, but in case we implement the remove myself if dead after attack feature
# After a victorious attack, the enemy is still in the array and therefore
@@ -80,20 +81,12 @@ def take_turn
# TODO: the capturer to go to the base shall be the nearest,
# it is not the case as we loop through the capturers first (and then select the nearest base)
# and not the bases first
- dest = unit.nearest(@game.neutral_bases.find_all { |b| !going_to_bases.include?(b)}, enemies)
+ dest = unit.nearest((@game.neutral_bases + @game.enemy_bases).find_all { |b| !going_to_bases.include?(b)}, enemies)
if dest
- moved = unit.move_to(dest,{:exclusions=>enemies, :also_attack=>[]})
+ moved = unit.move_to(dest,{:exclusions=>enemies, :also_attack=>@game.enemy_on_bases})
if moved
going_to_bases << dest
- puts " gone to neutral base #{dest}"
- end
- end
- if !dest
- dest = unit.nearest(@game.enemy_bases.find_all { |b| !going_to_bases.include?(b)}, enemies)
- moved = unit.move_to(dest,{:exclusions=>enemies, :also_attack=>weakers}) if dest
- if moved
- going_to_bases << dest
- puts " gone to enemy base #{dest}"
+ puts " gone to base #{dest}"
end
end
if !dest
@@ -105,7 +98,6 @@ def take_turn
# unit.find_target_and_safe_place
end
-
# TODO: attack enemies capturing our base
# TODO: if we are trying to attack nearest enemy, first use my_targets, it is quicker
if !moved
@@ -124,14 +116,14 @@ def take_turn
else
# TODO: must verify that enemy is in range, otherwise the unit could go very far....
dest = unit.nearest(attacked, all_others-attacked)
- dest = nil if unit.dist_between(dest) > unit.speed(1)-1
+ dest = nil if dest and unit.dist_between(dest) > unit.speed(1)-1
puts(" found attacked: #{dest}") if dest
end
if dest
# FIXME: a :bers can not move to woods so it can not attack the unit there !!!!
attacked += [dest] if !attacked.include?(dest)
puts(" #{unit} trying to move to #{dest}")
- moved = unit.move_to(dest,{:exclusions=>all_others-enemies, :also_attack=>enemies})
+ moved = unit.move_to(dest,{:exclusions=>all_others, :also_attack=>enemies})
# FIXME: unit.best_place_to_attack(dest)
puts(" moved ? => #{moved}")
end
View
4 lib/game.rb
@@ -260,6 +260,10 @@ def my_free_bases
my_bases.find_all{|b| !b.finished? and !b.unit}
end
+ def enemy_on_bases
+ (enemy_bases+neutral_bases).find_all { |b| b.unit and enemy_units.include?(b.unit)}
+ end
+
end
end
View
46 lib/pathfinding.rb
@@ -5,7 +5,7 @@ module Weewar
class Unit
# An Array of the Hex es which the given Unit can move to in the current turn.
# possible_moves = my_unit.destinations
- def destinations
+ def server_destinations
xml = XmlSimple.xml_in(@game.send("<movementOptions x='#{x}' y='#{y}' type='#{TYPE_FOR_SYMBOL[@type]}'/>"))
coords = xml['coordinate']
if !coords
@@ -22,8 +22,10 @@ def my_destinations_and_backchains(exclusions = [], from=nil)
openset = [from] # The set of tentative nodes to be evaluated, initially containing the start node
came_from = Hash.new # The map of navigated nodes.
cost = Hash.new
+ zoc_hash = Unit.init_zoc_hash(@game)
possibles = Array.new
cost[from] = 0 # Cost from start along best known path.
+
mob = mobility(1)
while not openset.empty?
x = openset.sort_by{ |x| cost[x]}.first
@@ -31,7 +33,7 @@ def my_destinations_and_backchains(exclusions = [], from=nil)
closedset.push(x)
for y in x.neighbours
next if closedset.include?(y)
- ec = entrance_cost(y)
+ ec = entrance_cost(y, x, zoc_hash)
next if cost[x]+ec > mob
if not openset.include?(y)
openset.push(y)
@@ -45,13 +47,9 @@ def my_destinations_and_backchains(exclusions = [], from=nil)
end
def my_destinations(exclusions = [], from=nil)
- return destinations # FIXME: temporarily as I'm implementing Zone Of Control
- # http://weewar.wikispaces.com/Zone+of+Control
my_destinations_and_backchains(exclusions, from)[0]
end
-
-
#-- ----------------------------------------------
# Travel
#++
@@ -59,15 +57,27 @@ def my_destinations(exclusions = [], from=nil)
# 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.
- def entrance_cost(hex)
- raise "hex is nil" if hex.nil?
+ def entrance_cost(hex, from, zh)
+ raise "hex is nil" if hex.nil?
raise "hex.type is nil" if hex.type.nil?
specs_for_type = Hex.terrain_specs[hex.type]
- raise "** No spec for type '#{hex.type}' hex: #{hex}" if specs_for_type.nil?
- tag(specs_for_type[:movement][unit_class]) { |rv|
- raise "no movement spec for #{unit_class}" if !rv
- }
+ raise "** No spec at all for type '#{hex.type}' from hex: #{hex}" if specs_for_type.nil?
+ rv = specs_for_type[:movement][unit_class]
+ raise "** No movement spec for #{unit_class}" if !rv
+ rv + zoc_cost(hex, from, zh)
+ end
+
+ def self.init_zoc_hash(game)
+ zoc_hash = Hash.new(false)
+ game.enemy_units.each{|e| e.hex.neighbours.each { |n| zoc_hash[n] = true}}
+ zoc_hash
+ end
+
+ def zoc_cost(hex, from, zoc_hash)
+ # if was already in zoc, can not move to hex
+ return 99 if zoc_hash[from] and zoc_hash[hex]
+ return 0
end
# The cost in movement points for the unit to travel along the given path.
@@ -96,7 +106,6 @@ def travel_cost(dest)
#
# best_path = my_trooper.shortest_path(enemy_base)
def shortest_path(dest, exclusions = [])
- exclusions ||= []
reconstruct_path(dest, my_shortest_path(dest, exclusions)) # shortest_paths(exclusions)
end
@@ -117,12 +126,13 @@ def my_shortest_path(goal, exclusions = [])
closedset = exclusions.map{ |x| x} # The set of nodes already evaluated. Perform a copy of the array
openset = [@hex] # The set of tentative nodes to be evaluated, initially containing the start node
came_from = Hash.new # The map of navigated nodes.
+ zoc_hash = Unit.init_zoc_hash(@game)
g_score = Hash.new
h_score = Hash.new
f_score = Hash.new
g_score[@hex] = 0 # Cost from start along best known path.
- h_score[@hex] = heuristic_cost_estimate(@hex, goal.hex)
+ h_score[@hex] = heuristic_cost_estimate(@hex, goal, @hex, zoc_hash)
f_score[@hex] = g_score[@hex] + h_score[@hex] # Estimated total cost from start to goal through y.
while not openset.empty?
@@ -135,7 +145,7 @@ def my_shortest_path(goal, exclusions = [])
openset.delete(x)
closedset.push(x)
- #x.value = "X "
+ #x.value = ". "
#x.map.print_map
for y in x.neighbours
@@ -152,7 +162,7 @@ def my_shortest_path(goal, exclusions = [])
if tentative_is_better
came_from[y] = x
g_score[y] = tentative_g_score
- h_score[y] = heuristic_cost_estimate(y, goal)
+ h_score[y] = heuristic_cost_estimate(y, goal, x, zoc_hash)
f_score[y] = g_score[y] + h_score[y]
end
end
@@ -175,8 +185,8 @@ def dist_between(b)
# end
#end
- def heuristic_cost_estimate(x,y)
- x.dist_between(y) + entrance_cost(x)
+ def heuristic_cost_estimate(x, goal, from, zoc_hash)
+ x.dist_between(goal) + entrance_cost(x, from, zoc_hash)
end
# Calculate all shortest paths from the Unit's current Hex to every other
View
27 lib/unit.rb
@@ -168,9 +168,9 @@ def can_attack?(target)
# if my_unit.can_reach? the_hex
# my_unit.move_to the_hex
# end
- def can_reach?(hex)
- my_destinations.include? hex
- end
+ #def can_reach?(hex)
+ # my_destinations.include? hex
+ #end
# An Array of the Unit s of the Game which are on the same side as this Unit.
# friends = my_unit.allied_units
@@ -255,22 +255,25 @@ def move_to(destination, options = {})
raise "** options is not a Hash" if options.class.name != "Hash"
command = ""
options[:exclusions] ||= []
+ options[:exclusions] -= [destination]
#puts " destination is #{destination}, #{options[:exclusions].size} exclusions"
moved = false
attacked = false
captured = false
- new_hex = @hex
+ new_hex = @hex
- if destination != @hex #and !dfa_has_target_in_range(destination)
+ if destination != @hex and !dfa_has_target_in_range(destination)
# Travel
-
path = shortest_path(destination, options[:exclusions])
+ # if the destination is occupied, travel one less
+ path.pop if destination.hex.occupied?
+
#puts " path: #{path.join(', ')}"
if !path or path.empty?
$stderr.puts "* No path from #{self} to #{destination}"
else
- dests = my_destinations
+ dests = my_destinations(allied_units)
#puts " dests: #{dests.size}: #{dests.join(', ')}"
new_dest = path.pop
while new_dest and not dests.include?(new_dest)
@@ -567,10 +570,6 @@ def select_near_target()
# TODO: find a safer place anyway (intersection with a best place to attack and a safe place).
# If does not exists, well return best place to attack
def best_place_to_attack(target)
- # FIXME: my_destinations does not take into account the fact that
- # there are surrounding enemies
- # (the movement options are not the same in that case, but I don't know them)
-
# dfas-like can not attack after moving
return @hex if dfa_has_target_in_range(target)
@@ -614,11 +613,7 @@ def insure_paths_to_enemy_bases_not_blocked
end
def move_away_from(units)
- d = my_destinations
- #far_from_unit = farest(unit, @game.units)
- #far_destination = farest(far_from_unit, @game.units)
- #return move_to(far_destination, {:exclusions=>@game.units})
- # TODO: shortest_path takes a unit, bt we pass an hex
+ # TODO: shortest_path takes a unit, but we pass an hex
return move_to(farest(units, @game.units), {:exclusions=>@game.units})
end
View
180 specs/pathfinding_tests.rb
@@ -1,12 +1,22 @@
require File.dirname(__FILE__) + '/../lib/pathfinding'
+class Array
+ def to_hex
+
+ end
+end
+
module Weewar
# only for old shortest_path method
class Game
- attr_accessor :map
+ attr_accessor :map, :enemy_units
def initialize(map)
@map = map
+ @enemy_units = []
+ end
+ def clear_enemies
+ @enemy_units.clear
end
end
@@ -14,16 +24,17 @@ class Unit
require File.dirname(__FILE__) + '/../lib/unit_constants'
- attr_accessor :map, :value, :hex, :x, :y, :cost
+ attr_accessor :map, :value, :hex, :x, :y, :cost, :unit
- def initialize(map, x, y, value, cost)
- @x, @y = x, y
+ def initialize(game, map, x, y, value, cost)
+ @game = game
@map = map
+ @x, @y = x, y
#@map.set(x,y,value,cost)
@value = value
- @hex = self
- @game = Game.new(@map)
- @cost = cost
+ @cost = cost
+ @hex = self
+ @unit = self
end
def neighbours
@@ -39,9 +50,10 @@ def neighbours
rv
end
- def entrance_cost(hex)
+ def entrance_cost(hex, from, zoc_hash)
raise "hex is nil" if hex.nil?
- hex.cost
+ zc = zoc_cost(hex, from, zoc_hash)
+ hex.cost + zoc_cost(hex, from, zoc_hash)
end
def to_s
@@ -68,89 +80,90 @@ def occupied?
end
end
-end
-class Map
+ class Map
- def initialize
- @map = Array.new
- for i in (0..19)
- @map[i] = Array.new
- for j in (0..19)
- @map[i][j] = Weewar::Unit.new(self, i, j, "1 ", 1)
+ def initialize(game)
+ @game = game
+ @map = Array.new
+ for i in (0..19)
+ @map[i] = Array.new
+ for j in (0..19)
+ @map[i][j] = Weewar::Unit.new(@game, self, i, j, "1 ", 1)
+ end
end
end
- end
- def print_map
- for j in (0..19)
- for i in (0..19)
- print @map[i][j].value
+ def print_map
+ puts
+ for j in (0..19)
+ for i in (0..19)
+ print @map[i][j].value
+ end
+ puts
end
puts
end
- puts
- end
- def set(i,j, value, cost)
- @map[i][j].value = value+" "
- @map[i][j].cost = cost
- @map[i][j]
- end
+ def set(i,j, value, cost)
+ @map[i][j].value = value+" "
+ @map[i][j].cost = cost
+ @map[i][j]
+ end
- def get(i,j)
- @map[i][j]
- end
+ def get(i,j)
+ @map[i][j]
+ end
- # only for old shortest_path method
- def each
- for j in (0..19)
- for i in (0..19)
- raise "nil" if @map[i][j].nil?
- yield @map[i][j]
+ # only for old shortest_path method
+ def each
+ for j in (0..19)
+ for i in (0..19)
+ raise "nil" if @map[i][j].nil?
+ yield @map[i][j]
+ end
end
end
- end
- # only for old shortest_path method
- def hex_neighbours(unit)
- unit.neighbours
- end
+ # only for old shortest_path method
+ def hex_neighbours(unit)
+ unit.neighbours
+ end
- def clear(char=nil, cost=1)
- for j in (0..19)
- for i in (0..19)
- if !char
- @map[i][j].value = "1 "
- else
- @map[i][j].value = char+" "
+ def clear(char=nil, cost=1)
+ for j in (0..19)
+ for i in (0..19)
+ if !char
+ @map[i][j].value = "1 "
+ else
+ @map[i][j].value = char+" "
+ end
+ @map[i][j].cost = cost
end
- @map[i][j].cost = cost
end
end
- end
- def random
- for j in (0..19)
- for i in (0..19)
- a = ((Math.sin(i.to_f/4)*5).round - (Math.cos(j.to_f/4)*4).round)
- a = 0 if a < 0
- @map[i][j].value = a.to_s+" "
- @map[i][j].cost = a*4
+ def random
+ for j in (0..19)
+ for i in (0..19)
+ a = ((Math.sin(i.to_f/4)*5).round - (Math.cos(j.to_f/4)*4).round)
+ a = 0 if a < 0
+ @map[i][j].value = a.to_s+" "
+ @map[i][j].cost = a*4
+ end
end
end
- end
-
+ end
end
describe "Pathfinding" do
before(:all) do
- @map = Map.new
+ @game = Weewar::Game.new(@map)
+ @map = Weewar::Map.new(@game)
end
-
it "basic" do
@map.clear
@map.set(2,2,"5",5)
@@ -171,7 +184,7 @@ def random
#print "#{u.to_s}=>"
}
#puts "goal"
- #@map.print_map
+ @map.print_map
end
path.size.should eq(8)
end
@@ -187,7 +200,7 @@ def random
@map.set(u.x,u.y," ",0)
#print "#{u.to_s}=>"
}
- #@map.print_map
+ @map.print_map
end
path.size.should eq(26)
end
@@ -211,31 +224,44 @@ def random
@map.set(11,13,"+",1),
@map.set(12,13,"+",1)
]
- u = Weewar::Unit.new(@map,9,10,"O",1)
+ @game.clear_enemies
+ exclusions.each { |e|
+ @game.enemy_units << e
+ }
+ u = Weewar::Unit.new(@game, @map,9,10,"O",1)
@map.set(9,10,"O",1)
dests = @map.get(u.x,u.y).my_destinations(exclusions)
dests.each { |d|
@map.set(d.x,d.y,"X",1)
}
- #puts
- #@map.print_map
+ @map.print_map
dests.size.should eq(193)
end
it "near an enemy" do
@map.clear(" ",2)
- u = Weewar::Unit.new(@map,9,10,"O",1)
- @map.set(9,10,"O",1)
- dests = @map.get(u.x,u.y).my_destinations
- dests.each { |d|
- @map.set(d.x,d.y,"X",1)
- }
- puts
+ @map.set(9,10,"O",0)
+
+ u = @map.get(9,10)
+ goal = @map.get(13,8)
+ goal.value = "O "
+ @game.clear_enemies
+ @game.enemy_units << @map.set(11,8,"+",1)
+ @game.enemy_units << @map.set(11,9,"+",1)
+
+ #dests = @map.get(u.x,u.y).my_destinations
+ #dests.each { |d|
+ # @map.set(d.x,d.y,"X",1)
+ # }
+ zh = Weewar::Unit.init_zoc_hash(@game)
+ path = u.shortest_path(goal, @game.enemy_units)
+ #path.each { |d|
+ # @map.set(d.x,d.y,"X",1)
+ # }
@map.print_map
+ #puts path.join('=>')
#dests.size.should eq(193)
end
-
-
end

0 comments on commit 18a952c

Please sign in to comment.
Something went wrong with that request. Please try again.