Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

implemented zoc

  • Loading branch information...
commit 18a952c850da24bd56f881ea593a131bb94aa16d 1 parent 604f407
Mickael Faivre-Maçon authored
22 bots/chuckbot.rb
@@ -25,7 +25,8 @@ def take_turn
25 25
26 26 # Move units
27 27 # TODO: make 2 loops on units that didn't move before doing something else
28   - units.sort_by{|u| [u.attack_range[0], -u.defense_strength, -u.hp, -u.speed(1)]}.each do |unit|
  28 + units.sort_by{|u| [u.speed(1)]}.each do |unit|
  29 + # [u.attack_range[0], -u.defense_strength, -u.hp, -u.speed(1)]
29 30 #next if !unit # useless, but in case we implement the remove myself if dead after attack feature
30 31
31 32 # After a victorious attack, the enemy is still in the array and therefore
@@ -80,20 +81,12 @@ def take_turn
80 81 # TODO: the capturer to go to the base shall be the nearest,
81 82 # it is not the case as we loop through the capturers first (and then select the nearest base)
82 83 # and not the bases first
83   - dest = unit.nearest(@game.neutral_bases.find_all { |b| !going_to_bases.include?(b)}, enemies)
  84 + dest = unit.nearest((@game.neutral_bases + @game.enemy_bases).find_all { |b| !going_to_bases.include?(b)}, enemies)
84 85 if dest
85   - moved = unit.move_to(dest,{:exclusions=>enemies, :also_attack=>[]})
  86 + moved = unit.move_to(dest,{:exclusions=>enemies, :also_attack=>@game.enemy_on_bases})
86 87 if moved
87 88 going_to_bases << dest
88   - puts " gone to neutral base #{dest}"
89   - end
90   - end
91   - if !dest
92   - dest = unit.nearest(@game.enemy_bases.find_all { |b| !going_to_bases.include?(b)}, enemies)
93   - moved = unit.move_to(dest,{:exclusions=>enemies, :also_attack=>weakers}) if dest
94   - if moved
95   - going_to_bases << dest
96   - puts " gone to enemy base #{dest}"
  89 + puts " gone to base #{dest}"
97 90 end
98 91 end
99 92 if !dest
@@ -105,7 +98,6 @@ def take_turn
105 98 # unit.find_target_and_safe_place
106 99 end
107 100
108   -
109 101 # TODO: attack enemies capturing our base
110 102 # TODO: if we are trying to attack nearest enemy, first use my_targets, it is quicker
111 103 if !moved
@@ -124,14 +116,14 @@ def take_turn
124 116 else
125 117 # TODO: must verify that enemy is in range, otherwise the unit could go very far....
126 118 dest = unit.nearest(attacked, all_others-attacked)
127   - dest = nil if unit.dist_between(dest) > unit.speed(1)-1
  119 + dest = nil if dest and unit.dist_between(dest) > unit.speed(1)-1
128 120 puts(" found attacked: #{dest}") if dest
129 121 end
130 122 if dest
131 123 # FIXME: a :bers can not move to woods so it can not attack the unit there !!!!
132 124 attacked += [dest] if !attacked.include?(dest)
133 125 puts(" #{unit} trying to move to #{dest}")
134   - moved = unit.move_to(dest,{:exclusions=>all_others-enemies, :also_attack=>enemies})
  126 + moved = unit.move_to(dest,{:exclusions=>all_others, :also_attack=>enemies})
135 127 # FIXME: unit.best_place_to_attack(dest)
136 128 puts(" moved ? => #{moved}")
137 129 end
4 lib/game.rb
@@ -260,6 +260,10 @@ def my_free_bases
260 260 my_bases.find_all{|b| !b.finished? and !b.unit}
261 261 end
262 262
  263 + def enemy_on_bases
  264 + (enemy_bases+neutral_bases).find_all { |b| b.unit and enemy_units.include?(b.unit)}
  265 + end
  266 +
263 267 end
264 268
265 269 end
46 lib/pathfinding.rb
@@ -5,7 +5,7 @@ module Weewar
5 5 class Unit
6 6 # An Array of the Hex es which the given Unit can move to in the current turn.
7 7 # possible_moves = my_unit.destinations
8   - def destinations
  8 + def server_destinations
9 9 xml = XmlSimple.xml_in(@game.send("<movementOptions x='#{x}' y='#{y}' type='#{TYPE_FOR_SYMBOL[@type]}'/>"))
10 10 coords = xml['coordinate']
11 11 if !coords
@@ -22,8 +22,10 @@ def my_destinations_and_backchains(exclusions = [], from=nil)
22 22 openset = [from] # The set of tentative nodes to be evaluated, initially containing the start node
23 23 came_from = Hash.new # The map of navigated nodes.
24 24 cost = Hash.new
  25 + zoc_hash = Unit.init_zoc_hash(@game)
25 26 possibles = Array.new
26 27 cost[from] = 0 # Cost from start along best known path.
  28 +
27 29 mob = mobility(1)
28 30 while not openset.empty?
29 31 x = openset.sort_by{ |x| cost[x]}.first
@@ -31,7 +33,7 @@ def my_destinations_and_backchains(exclusions = [], from=nil)
31 33 closedset.push(x)
32 34 for y in x.neighbours
33 35 next if closedset.include?(y)
34   - ec = entrance_cost(y)
  36 + ec = entrance_cost(y, x, zoc_hash)
35 37 next if cost[x]+ec > mob
36 38 if not openset.include?(y)
37 39 openset.push(y)
@@ -45,13 +47,9 @@ def my_destinations_and_backchains(exclusions = [], from=nil)
45 47 end
46 48
47 49 def my_destinations(exclusions = [], from=nil)
48   - return destinations # FIXME: temporarily as I'm implementing Zone Of Control
49   - # http://weewar.wikispaces.com/Zone+of+Control
50 50 my_destinations_and_backchains(exclusions, from)[0]
51 51 end
52 52
53   -
54   -
55 53 #-- ----------------------------------------------
56 54 # Travel
57 55 #++
@@ -59,15 +57,27 @@ def my_destinations(exclusions = [], from=nil)
59 57 # The cost in movement points for the unit to enter the given Hex. This
60 58 # is an internal method used for travel-related calculations; you should not
61 59 # normally need to use this yourself.
62   - def entrance_cost(hex)
63   - raise "hex is nil" if hex.nil?
  60 + def entrance_cost(hex, from, zh)
  61 + raise "hex is nil" if hex.nil?
64 62 raise "hex.type is nil" if hex.type.nil?
65 63
66 64 specs_for_type = Hex.terrain_specs[hex.type]
67   - raise "** No spec for type '#{hex.type}' hex: #{hex}" if specs_for_type.nil?
68   - tag(specs_for_type[:movement][unit_class]) { |rv|
69   - raise "no movement spec for #{unit_class}" if !rv
70   - }
  65 + raise "** No spec at all for type '#{hex.type}' from hex: #{hex}" if specs_for_type.nil?
  66 + rv = specs_for_type[:movement][unit_class]
  67 + raise "** No movement spec for #{unit_class}" if !rv
  68 + rv + zoc_cost(hex, from, zh)
  69 + end
  70 +
  71 + def self.init_zoc_hash(game)
  72 + zoc_hash = Hash.new(false)
  73 + game.enemy_units.each{|e| e.hex.neighbours.each { |n| zoc_hash[n] = true}}
  74 + zoc_hash
  75 + end
  76 +
  77 + def zoc_cost(hex, from, zoc_hash)
  78 + # if was already in zoc, can not move to hex
  79 + return 99 if zoc_hash[from] and zoc_hash[hex]
  80 + return 0
71 81 end
72 82
73 83 # The cost in movement points for the unit to travel along the given path.
@@ -96,7 +106,6 @@ def travel_cost(dest)
96 106 #
97 107 # best_path = my_trooper.shortest_path(enemy_base)
98 108 def shortest_path(dest, exclusions = [])
99   - exclusions ||= []
100 109 reconstruct_path(dest, my_shortest_path(dest, exclusions)) # shortest_paths(exclusions)
101 110 end
102 111
@@ -117,12 +126,13 @@ def my_shortest_path(goal, exclusions = [])
117 126 closedset = exclusions.map{ |x| x} # The set of nodes already evaluated. Perform a copy of the array
118 127 openset = [@hex] # The set of tentative nodes to be evaluated, initially containing the start node
119 128 came_from = Hash.new # The map of navigated nodes.
  129 + zoc_hash = Unit.init_zoc_hash(@game)
120 130 g_score = Hash.new
121 131 h_score = Hash.new
122 132 f_score = Hash.new
123 133
124 134 g_score[@hex] = 0 # Cost from start along best known path.
125   - h_score[@hex] = heuristic_cost_estimate(@hex, goal.hex)
  135 + h_score[@hex] = heuristic_cost_estimate(@hex, goal, @hex, zoc_hash)
126 136 f_score[@hex] = g_score[@hex] + h_score[@hex] # Estimated total cost from start to goal through y.
127 137
128 138 while not openset.empty?
@@ -135,7 +145,7 @@ def my_shortest_path(goal, exclusions = [])
135 145
136 146 openset.delete(x)
137 147 closedset.push(x)
138   - #x.value = "X "
  148 + #x.value = ". "
139 149 #x.map.print_map
140 150
141 151 for y in x.neighbours
@@ -152,7 +162,7 @@ def my_shortest_path(goal, exclusions = [])
152 162 if tentative_is_better
153 163 came_from[y] = x
154 164 g_score[y] = tentative_g_score
155   - h_score[y] = heuristic_cost_estimate(y, goal)
  165 + h_score[y] = heuristic_cost_estimate(y, goal, x, zoc_hash)
156 166 f_score[y] = g_score[y] + h_score[y]
157 167 end
158 168 end
@@ -175,8 +185,8 @@ def dist_between(b)
175 185 # end
176 186 #end
177 187
178   - def heuristic_cost_estimate(x,y)
179   - x.dist_between(y) + entrance_cost(x)
  188 + def heuristic_cost_estimate(x, goal, from, zoc_hash)
  189 + x.dist_between(goal) + entrance_cost(x, from, zoc_hash)
180 190 end
181 191
182 192 # Calculate all shortest paths from the Unit's current Hex to every other
27 lib/unit.rb
@@ -168,9 +168,9 @@ def can_attack?(target)
168 168 # if my_unit.can_reach? the_hex
169 169 # my_unit.move_to the_hex
170 170 # end
171   - def can_reach?(hex)
172   - my_destinations.include? hex
173   - end
  171 + #def can_reach?(hex)
  172 + # my_destinations.include? hex
  173 + #end
174 174
175 175 # An Array of the Unit s of the Game which are on the same side as this Unit.
176 176 # friends = my_unit.allied_units
@@ -255,22 +255,25 @@ def move_to(destination, options = {})
255 255 raise "** options is not a Hash" if options.class.name != "Hash"
256 256 command = ""
257 257 options[:exclusions] ||= []
  258 + options[:exclusions] -= [destination]
258 259 #puts " destination is #{destination}, #{options[:exclusions].size} exclusions"
259 260
260 261 moved = false
261 262 attacked = false
262 263 captured = false
263   - new_hex = @hex
  264 + new_hex = @hex
264 265
265   - if destination != @hex #and !dfa_has_target_in_range(destination)
  266 + if destination != @hex and !dfa_has_target_in_range(destination)
266 267 # Travel
267   -
268 268 path = shortest_path(destination, options[:exclusions])
  269 + # if the destination is occupied, travel one less
  270 + path.pop if destination.hex.occupied?
  271 +
269 272 #puts " path: #{path.join(', ')}"
270 273 if !path or path.empty?
271 274 $stderr.puts "* No path from #{self} to #{destination}"
272 275 else
273   - dests = my_destinations
  276 + dests = my_destinations(allied_units)
274 277 #puts " dests: #{dests.size}: #{dests.join(', ')}"
275 278 new_dest = path.pop
276 279 while new_dest and not dests.include?(new_dest)
@@ -567,10 +570,6 @@ def select_near_target()
567 570 # TODO: find a safer place anyway (intersection with a best place to attack and a safe place).
568 571 # If does not exists, well return best place to attack
569 572 def best_place_to_attack(target)
570   - # FIXME: my_destinations does not take into account the fact that
571   - # there are surrounding enemies
572   - # (the movement options are not the same in that case, but I don't know them)
573   -
574 573 # dfas-like can not attack after moving
575 574 return @hex if dfa_has_target_in_range(target)
576 575
@@ -614,11 +613,7 @@ def insure_paths_to_enemy_bases_not_blocked
614 613 end
615 614
616 615 def move_away_from(units)
617   - d = my_destinations
618   - #far_from_unit = farest(unit, @game.units)
619   - #far_destination = farest(far_from_unit, @game.units)
620   - #return move_to(far_destination, {:exclusions=>@game.units})
621   - # TODO: shortest_path takes a unit, bt we pass an hex
  616 + # TODO: shortest_path takes a unit, but we pass an hex
622 617 return move_to(farest(units, @game.units), {:exclusions=>@game.units})
623 618 end
624 619
180 specs/pathfinding_tests.rb
... ... @@ -1,12 +1,22 @@
1 1 require File.dirname(__FILE__) + '/../lib/pathfinding'
2 2
  3 +class Array
  4 + def to_hex
  5 +
  6 + end
  7 +end
  8 +
3 9 module Weewar
4 10
5 11 # only for old shortest_path method
6 12 class Game
7   - attr_accessor :map
  13 + attr_accessor :map, :enemy_units
8 14 def initialize(map)
9 15 @map = map
  16 + @enemy_units = []
  17 + end
  18 + def clear_enemies
  19 + @enemy_units.clear
10 20 end
11 21 end
12 22
@@ -14,16 +24,17 @@ class Unit
14 24
15 25 require File.dirname(__FILE__) + '/../lib/unit_constants'
16 26
17   - attr_accessor :map, :value, :hex, :x, :y, :cost
  27 + attr_accessor :map, :value, :hex, :x, :y, :cost, :unit
18 28
19   - def initialize(map, x, y, value, cost)
20   - @x, @y = x, y
  29 + def initialize(game, map, x, y, value, cost)
  30 + @game = game
21 31 @map = map
  32 + @x, @y = x, y
22 33 #@map.set(x,y,value,cost)
23 34 @value = value
24   - @hex = self
25   - @game = Game.new(@map)
26   - @cost = cost
  35 + @cost = cost
  36 + @hex = self
  37 + @unit = self
27 38 end
28 39
29 40 def neighbours
@@ -39,9 +50,10 @@ def neighbours
39 50 rv
40 51 end
41 52
42   - def entrance_cost(hex)
  53 + def entrance_cost(hex, from, zoc_hash)
43 54 raise "hex is nil" if hex.nil?
44   - hex.cost
  55 + zc = zoc_cost(hex, from, zoc_hash)
  56 + hex.cost + zoc_cost(hex, from, zoc_hash)
45 57 end
46 58
47 59 def to_s
@@ -68,89 +80,90 @@ def occupied?
68 80 end
69 81
70 82 end
71   -end
72 83
73   -class Map
  84 + class Map
74 85
75   - def initialize
76   - @map = Array.new
77   - for i in (0..19)
78   - @map[i] = Array.new
79   - for j in (0..19)
80   - @map[i][j] = Weewar::Unit.new(self, i, j, "1 ", 1)
  86 + def initialize(game)
  87 + @game = game
  88 + @map = Array.new
  89 + for i in (0..19)
  90 + @map[i] = Array.new
  91 + for j in (0..19)
  92 + @map[i][j] = Weewar::Unit.new(@game, self, i, j, "1 ", 1)
  93 + end
81 94 end
82 95 end
83   - end
84 96
85   - def print_map
86   - for j in (0..19)
87   - for i in (0..19)
88   - print @map[i][j].value
  97 + def print_map
  98 + puts
  99 + for j in (0..19)
  100 + for i in (0..19)
  101 + print @map[i][j].value
  102 + end
  103 + puts
89 104 end
90 105 puts
91 106 end
92   - puts
93   - end
94 107
95   - def set(i,j, value, cost)
96   - @map[i][j].value = value+" "
97   - @map[i][j].cost = cost
98   - @map[i][j]
99   - end
  108 + def set(i,j, value, cost)
  109 + @map[i][j].value = value+" "
  110 + @map[i][j].cost = cost
  111 + @map[i][j]
  112 + end
100 113
101   - def get(i,j)
102   - @map[i][j]
103   - end
  114 + def get(i,j)
  115 + @map[i][j]
  116 + end
104 117
105   - # only for old shortest_path method
106   - def each
107   - for j in (0..19)
108   - for i in (0..19)
109   - raise "nil" if @map[i][j].nil?
110   - yield @map[i][j]
  118 + # only for old shortest_path method
  119 + def each
  120 + for j in (0..19)
  121 + for i in (0..19)
  122 + raise "nil" if @map[i][j].nil?
  123 + yield @map[i][j]
  124 + end
111 125 end
112 126 end
113   - end
114 127
115   - # only for old shortest_path method
116   - def hex_neighbours(unit)
117   - unit.neighbours
118   - end
  128 + # only for old shortest_path method
  129 + def hex_neighbours(unit)
  130 + unit.neighbours
  131 + end
119 132
120   - def clear(char=nil, cost=1)
121   - for j in (0..19)
122   - for i in (0..19)
123   - if !char
124   - @map[i][j].value = "1 "
125   - else
126   - @map[i][j].value = char+" "
  133 + def clear(char=nil, cost=1)
  134 + for j in (0..19)
  135 + for i in (0..19)
  136 + if !char
  137 + @map[i][j].value = "1 "
  138 + else
  139 + @map[i][j].value = char+" "
  140 + end
  141 + @map[i][j].cost = cost
127 142 end
128   - @map[i][j].cost = cost
129 143 end
130 144 end
131   - end
132 145
133   - def random
134   - for j in (0..19)
135   - for i in (0..19)
136   - a = ((Math.sin(i.to_f/4)*5).round - (Math.cos(j.to_f/4)*4).round)
137   - a = 0 if a < 0
138   - @map[i][j].value = a.to_s+" "
139   - @map[i][j].cost = a*4
  146 + def random
  147 + for j in (0..19)
  148 + for i in (0..19)
  149 + a = ((Math.sin(i.to_f/4)*5).round - (Math.cos(j.to_f/4)*4).round)
  150 + a = 0 if a < 0
  151 + @map[i][j].value = a.to_s+" "
  152 + @map[i][j].cost = a*4
  153 + end
140 154 end
141 155 end
142   - end
143   -
144 156
  157 + end
145 158 end
146 159
147 160
148 161 describe "Pathfinding" do
149 162
150 163 before(:all) do
151   - @map = Map.new
  164 + @game = Weewar::Game.new(@map)
  165 + @map = Weewar::Map.new(@game)
152 166 end
153   -
154 167 it "basic" do
155 168 @map.clear
156 169 @map.set(2,2,"5",5)
@@ -171,7 +184,7 @@ def random
171 184 #print "#{u.to_s}=>"
172 185 }
173 186 #puts "goal"
174   - #@map.print_map
  187 + @map.print_map
175 188 end
176 189 path.size.should eq(8)
177 190 end
@@ -187,7 +200,7 @@ def random
187 200 @map.set(u.x,u.y," ",0)
188 201 #print "#{u.to_s}=>"
189 202 }
190   - #@map.print_map
  203 + @map.print_map
191 204 end
192 205 path.size.should eq(26)
193 206 end
@@ -211,31 +224,44 @@ def random
211 224 @map.set(11,13,"+",1),
212 225 @map.set(12,13,"+",1)
213 226 ]
214   - u = Weewar::Unit.new(@map,9,10,"O",1)
  227 + @game.clear_enemies
  228 + exclusions.each { |e|
  229 + @game.enemy_units << e
  230 + }
  231 + u = Weewar::Unit.new(@game, @map,9,10,"O",1)
215 232 @map.set(9,10,"O",1)
216 233 dests = @map.get(u.x,u.y).my_destinations(exclusions)
217 234 dests.each { |d|
218 235 @map.set(d.x,d.y,"X",1)
219 236 }
220   - #puts
221   - #@map.print_map
  237 + @map.print_map
222 238 dests.size.should eq(193)
223 239 end
224 240
225 241 it "near an enemy" do
226 242 @map.clear(" ",2)
227   - u = Weewar::Unit.new(@map,9,10,"O",1)
228   - @map.set(9,10,"O",1)
229   - dests = @map.get(u.x,u.y).my_destinations
230   - dests.each { |d|
231   - @map.set(d.x,d.y,"X",1)
232   - }
233   - puts
  243 + @map.set(9,10,"O",0)
  244 +
  245 + u = @map.get(9,10)
  246 + goal = @map.get(13,8)
  247 + goal.value = "O "
  248 + @game.clear_enemies
  249 + @game.enemy_units << @map.set(11,8,"+",1)
  250 + @game.enemy_units << @map.set(11,9,"+",1)
  251 +
  252 + #dests = @map.get(u.x,u.y).my_destinations
  253 + #dests.each { |d|
  254 + # @map.set(d.x,d.y,"X",1)
  255 + # }
  256 + zh = Weewar::Unit.init_zoc_hash(@game)
  257 + path = u.shortest_path(goal, @game.enemy_units)
  258 + #path.each { |d|
  259 + # @map.set(d.x,d.y,"X",1)
  260 + # }
234 261 @map.print_map
  262 + #puts path.join('=>')
235 263 #dests.size.should eq(193)
236 264 end
237 265
238   -
239   -
240 266 end
241 267

0 comments on commit 18a952c

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