UG_RRobots 1.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (165) hide show
  1. data/bin/rrobots +202 -0
  2. data/bin/tournament +413 -0
  3. data/config/rrobots.yml +15 -0
  4. data/contribs/allbots.rb +0 -0
  5. data/doc/manual.rdoc +126 -0
  6. data/doc/manual_fr.rdoc +129 -0
  7. data/images/explosion00.gif +0 -0
  8. data/images/explosion01.gif +0 -0
  9. data/images/explosion02.gif +0 -0
  10. data/images/explosion03.gif +0 -0
  11. data/images/explosion04.gif +0 -0
  12. data/images/explosion05.gif +0 -0
  13. data/images/explosion06.gif +0 -0
  14. data/images/explosion07.gif +0 -0
  15. data/images/explosion08.gif +0 -0
  16. data/images/explosion09.gif +0 -0
  17. data/images/explosion10.gif +0 -0
  18. data/images/explosion11.gif +0 -0
  19. data/images/explosion12.gif +0 -0
  20. data/images/explosion13.gif +0 -0
  21. data/images/explosion14.gif +0 -0
  22. data/images/red_body000.gif +0 -0
  23. data/images/red_body010.gif +0 -0
  24. data/images/red_body020.gif +0 -0
  25. data/images/red_body030.gif +0 -0
  26. data/images/red_body040.gif +0 -0
  27. data/images/red_body050.gif +0 -0
  28. data/images/red_body060.gif +0 -0
  29. data/images/red_body070.gif +0 -0
  30. data/images/red_body080.gif +0 -0
  31. data/images/red_body090.gif +0 -0
  32. data/images/red_body100.gif +0 -0
  33. data/images/red_body110.gif +0 -0
  34. data/images/red_body120.gif +0 -0
  35. data/images/red_body130.gif +0 -0
  36. data/images/red_body140.gif +0 -0
  37. data/images/red_body150.gif +0 -0
  38. data/images/red_body160.gif +0 -0
  39. data/images/red_body170.gif +0 -0
  40. data/images/red_body180.gif +0 -0
  41. data/images/red_body190.gif +0 -0
  42. data/images/red_body200.gif +0 -0
  43. data/images/red_body210.gif +0 -0
  44. data/images/red_body220.gif +0 -0
  45. data/images/red_body230.gif +0 -0
  46. data/images/red_body240.gif +0 -0
  47. data/images/red_body250.gif +0 -0
  48. data/images/red_body260.gif +0 -0
  49. data/images/red_body270.gif +0 -0
  50. data/images/red_body280.gif +0 -0
  51. data/images/red_body290.gif +0 -0
  52. data/images/red_body300.gif +0 -0
  53. data/images/red_body310.gif +0 -0
  54. data/images/red_body320.gif +0 -0
  55. data/images/red_body330.gif +0 -0
  56. data/images/red_body340.gif +0 -0
  57. data/images/red_body350.gif +0 -0
  58. data/images/red_radar000.gif +0 -0
  59. data/images/red_radar010.gif +0 -0
  60. data/images/red_radar020.gif +0 -0
  61. data/images/red_radar030.gif +0 -0
  62. data/images/red_radar040.gif +0 -0
  63. data/images/red_radar050.gif +0 -0
  64. data/images/red_radar060.gif +0 -0
  65. data/images/red_radar070.gif +0 -0
  66. data/images/red_radar080.gif +0 -0
  67. data/images/red_radar090.gif +0 -0
  68. data/images/red_radar100.gif +0 -0
  69. data/images/red_radar110.gif +0 -0
  70. data/images/red_radar120.gif +0 -0
  71. data/images/red_radar130.gif +0 -0
  72. data/images/red_radar140.gif +0 -0
  73. data/images/red_radar150.gif +0 -0
  74. data/images/red_radar160.gif +0 -0
  75. data/images/red_radar170.gif +0 -0
  76. data/images/red_radar180.gif +0 -0
  77. data/images/red_radar190.gif +0 -0
  78. data/images/red_radar200.gif +0 -0
  79. data/images/red_radar210.gif +0 -0
  80. data/images/red_radar220.gif +0 -0
  81. data/images/red_radar230.gif +0 -0
  82. data/images/red_radar240.gif +0 -0
  83. data/images/red_radar250.gif +0 -0
  84. data/images/red_radar260.gif +0 -0
  85. data/images/red_radar270.gif +0 -0
  86. data/images/red_radar280.gif +0 -0
  87. data/images/red_radar290.gif +0 -0
  88. data/images/red_radar300.gif +0 -0
  89. data/images/red_radar310.gif +0 -0
  90. data/images/red_radar320.gif +0 -0
  91. data/images/red_radar330.gif +0 -0
  92. data/images/red_radar340.gif +0 -0
  93. data/images/red_radar350.gif +0 -0
  94. data/images/red_turret000.gif +0 -0
  95. data/images/red_turret010.gif +0 -0
  96. data/images/red_turret020.gif +0 -0
  97. data/images/red_turret030.gif +0 -0
  98. data/images/red_turret040.gif +0 -0
  99. data/images/red_turret050.gif +0 -0
  100. data/images/red_turret060.gif +0 -0
  101. data/images/red_turret070.gif +0 -0
  102. data/images/red_turret080.gif +0 -0
  103. data/images/red_turret090.gif +0 -0
  104. data/images/red_turret100.gif +0 -0
  105. data/images/red_turret110.gif +0 -0
  106. data/images/red_turret120.gif +0 -0
  107. data/images/red_turret130.gif +0 -0
  108. data/images/red_turret140.gif +0 -0
  109. data/images/red_turret150.gif +0 -0
  110. data/images/red_turret160.gif +0 -0
  111. data/images/red_turret170.gif +0 -0
  112. data/images/red_turret180.gif +0 -0
  113. data/images/red_turret190.gif +0 -0
  114. data/images/red_turret200.gif +0 -0
  115. data/images/red_turret210.gif +0 -0
  116. data/images/red_turret220.gif +0 -0
  117. data/images/red_turret230.gif +0 -0
  118. data/images/red_turret240.gif +0 -0
  119. data/images/red_turret250.gif +0 -0
  120. data/images/red_turret260.gif +0 -0
  121. data/images/red_turret270.gif +0 -0
  122. data/images/red_turret280.gif +0 -0
  123. data/images/red_turret290.gif +0 -0
  124. data/images/red_turret300.gif +0 -0
  125. data/images/red_turret310.gif +0 -0
  126. data/images/red_turret320.gif +0 -0
  127. data/images/red_turret330.gif +0 -0
  128. data/images/red_turret340.gif +0 -0
  129. data/images/red_turret350.gif +0 -0
  130. data/images/toolbox.gif +0 -0
  131. data/lib/battlefield.rb +102 -0
  132. data/lib/bullets.rb +39 -0
  133. data/lib/configuration.rb +26 -0
  134. data/lib/explosions.rb +20 -0
  135. data/lib/overloads.rb +10 -0
  136. data/lib/robot.rb +122 -0
  137. data/lib/robotrunner.rb +260 -0
  138. data/lib/tkarena.rb +197 -0
  139. data/lib/toolboxes.rb +28 -0
  140. data/robots/BillDuck.rb +92 -0
  141. data/robots/BotOne.rb +39 -0
  142. data/robots/DuckBill.rb +384 -0
  143. data/robots/DuckBill04.rb +330 -0
  144. data/robots/DuckToEndAllDucks.rb +140 -0
  145. data/robots/EdgeBot.rb +203 -0
  146. data/robots/HuntingDuck.rb +74 -0
  147. data/robots/HyperactiveDuck.rb +15 -0
  148. data/robots/Killer.rb +58 -0
  149. data/robots/Kite.rb +193 -0
  150. data/robots/KoDuck.rb +57 -0
  151. data/robots/LinearShooter.rb +279 -0
  152. data/robots/LuckyDuck.rb +83 -0
  153. data/robots/MoxonoM.rb +85 -0
  154. data/robots/MsgBot.rb +13 -0
  155. data/robots/NervousDuck.rb +13 -0
  156. data/robots/Polisher.rb +15 -0
  157. data/robots/RomBot.rb +514 -0
  158. data/robots/RoomPainter.rb +205 -0
  159. data/robots/Rrrkele.rb +48 -0
  160. data/robots/Seeker.rb +57 -0
  161. data/robots/ShootingStation.rb +15 -0
  162. data/robots/SittingDuck.rb +18 -0
  163. data/robots/SniperDuck.rb +277 -0
  164. data/robots/WallPainter.rb +224 -0
  165. metadata +220 -0
@@ -0,0 +1,203 @@
1
+ # EdgeBot.rb
2
+ require 'robot'
3
+
4
+ class EdgeBot
5
+ include Robot
6
+
7
+ @@east = 0
8
+ @@north = 90
9
+ @@west = 180
10
+ @@south = 270
11
+ @@none = -1
12
+
13
+ @@direction = {
14
+ :east => {
15
+ :angle => @@east,
16
+ :inward_angle => @@west,
17
+ :next_edge => [:north, :south]
18
+ },
19
+ :north => {
20
+ :angle => @@north,
21
+ :inward_angle => @@south,
22
+ :next_edge => [:west, :east]
23
+ },
24
+ :west => {
25
+ :angle => @@west,
26
+ :inward_angle => @@east,
27
+ :next_edge => [:south, :north]
28
+ },
29
+ :south => {
30
+ :angle => @@south,
31
+ :inward_angle => @@north,
32
+ :next_edge => [:east, :west]
33
+ }
34
+ }
35
+
36
+ @@max_robot_turn = 10
37
+ @@max_gun_turn = 30
38
+ @@max_radar_turn = 60
39
+ @@max_speed = 8
40
+
41
+ @@chicken_energy = 10
42
+
43
+ def tick( events )
44
+ if time == 0
45
+ @direction = 0
46
+ @target_angle = Hash.new
47
+ @next_edge = nearest_edge
48
+ @target_angle[:robot] = @@direction[@next_edge][:angle]
49
+ @target_angle[:gun] = @@direction[@next_edge][:angle]
50
+ @target_angle[:radar] = @@direction[@next_edge][:angle]
51
+ @mode = :seek_edge
52
+ @prev_radar_heading = radar_heading
53
+ @fire_power = 3
54
+ end
55
+
56
+ rotate!
57
+ fire( @fire_power )
58
+
59
+ case @mode
60
+ when :seek_edge
61
+ # puts "seek_edge"
62
+ @fire_power = 0.1
63
+ if @turn_complete
64
+ @mode = :move_to_edge
65
+ end
66
+ when :move_to_edge
67
+ # puts "move_to_edge"
68
+ fire( 3 ) unless events['robot_scanned'].empty?
69
+ unless at_edge? @next_edge
70
+ accelerate( 1 )
71
+ else
72
+ @current_edge = @next_edge
73
+ @next_edge = @@direction[@current_edge][:next_edge][@direction]
74
+ @target_angle[:robot] = @@direction[@next_edge][:angle]
75
+ @target_angle[:gun] = @@direction[@current_edge][:inward_angle]
76
+ @target_angle[:radar] = @@direction[@next_edge][:angle]
77
+ @mode = :edge_align
78
+ end
79
+ when :edge_align
80
+ # puts "edge_align"
81
+ @fire_power = 0.1
82
+ if speed != 0
83
+ stop
84
+ end
85
+ if @turn_complete && @prev_radar_heading == radar_heading
86
+ # Pause for a still radar shot before progressing
87
+ @mode = :fire
88
+ end
89
+ if edge_occupied?( @@direction[@next_edge][:angle], events )
90
+ @mode = :clear_edge_align
91
+ @target_angle[:gun] = @@direction[@next_edge][:angle]
92
+ @target_angle[:radar] = @@direction[@next_edge][:angle]
93
+ end
94
+ when :fire
95
+ # puts "fire"
96
+ @fire_power = 0.1
97
+ if velocity < @@max_speed
98
+ accelerate( 1 )
99
+ end
100
+ if at_edge?( @next_edge )
101
+ @current_edge = @next_edge
102
+ @next_edge = @@direction[@current_edge][:next_edge][@direction]
103
+ @target_angle[:robot] = @@direction[@next_edge][:angle]
104
+ @target_angle[:gun] = @@direction[@current_edge][:inward_angle]
105
+ @target_angle[:radar] = @@direction[@next_edge][:angle]
106
+ @mode = :edge_align
107
+ elsif edge_occupied?( @@direction[@next_edge][:angle], events )
108
+ @mode = :clear_edge_align
109
+ @target_angle[:gun] = @@direction[@next_edge][:angle]
110
+ @target_angle[:radar] = @@direction[@next_edge][:angle]
111
+ end
112
+ when :clear_edge_align
113
+ # puts "clear_edge_align"
114
+ @fire_power = 0.1
115
+ if speed != 0
116
+ stop
117
+ end
118
+ if @turn_complete
119
+ @mode = :clear_edge
120
+ end
121
+ when :clear_edge
122
+ @fire_power = 0.1
123
+ if not edge_occupied?( @@direction[@next_edge][:angle], events )
124
+ @target_angle[:gun] = @@direction[@current_edge][:inward_angle]
125
+ @target_angle[:radar] = @@direction[@next_edge][:angle]
126
+ @mode = :edge_align
127
+ elsif energy < @@chicken_energy
128
+ reverse_course!
129
+ end
130
+ end
131
+
132
+ @prev_radar_heading = radar_heading
133
+ end
134
+
135
+ def rotate!
136
+ @turn_complete = true
137
+ if @target_angle[:robot] != @@none && heading != @target_angle[:robot]
138
+ turn( angle_to( @target_angle[:robot], heading, @@max_robot_turn ) )
139
+ @turn_complete = false
140
+ elsif @target_angle[:gun] != @@none && gun_heading != @target_angle[:gun]
141
+ turn_gun( angle_to( @target_angle[:gun], gun_heading, @@max_gun_turn ) )
142
+ @turn_complete = false
143
+ elsif @target_angle[:radar] != @@none && radar_heading != @target_angle[:radar]
144
+ turn_radar( angle_to( @target_angle[:radar], radar_heading, @@max_radar_turn ) )
145
+ @turn_complete = false
146
+ end
147
+ end
148
+
149
+ def angle_to( target_heading, current_heading, max_turn )
150
+ normalised_heading = current_heading - target_heading
151
+ dir = (normalised_heading < 180) ? -1 : 1
152
+ if normalised_heading < max_turn
153
+ return dir * normalised_heading
154
+ else
155
+ return dir * max_turn
156
+ end
157
+ end
158
+
159
+ def at_edge?( edge )
160
+ case edge
161
+ when :east
162
+ return x >= (battlefield_width - size)
163
+ when :north
164
+ return y <= size
165
+ when :west
166
+ return x <= size
167
+ when :south
168
+ return y >= (battlefield_height - size)
169
+ end
170
+ end
171
+
172
+ def edge_occupied?( angle, events )
173
+ return (@prev_radar_heading == angle &&
174
+ radar_heading == angle &&
175
+ !events['robot_scanned'].empty?)
176
+ end
177
+
178
+ def nearest_edge
179
+ min = [:west, x]
180
+ if y < min[1]
181
+ min = [:north, y]
182
+ end
183
+ if battlefield_width - x < min[1]
184
+ min = [:east, battlefield_width - x]
185
+ end
186
+ if battlefield_height - y < min[1]
187
+ min = [:south, battlefield_height - y]
188
+ end
189
+ return min[0]
190
+ end
191
+
192
+ def reverse_course!
193
+ @direction = (@direction + 1) % 2
194
+ @next_edge = @@direction[@current_edge][:next_edge][@direction]
195
+ @target_angle[:robot] = @@direction[@next_edge][:angle]
196
+ @target_angle[:gun] = @@direction[@current_edge][:inward_angle]
197
+ @target_angle[:radar] = @@direction[@next_edge][:angle]
198
+ @mode = :edge_align
199
+ end
200
+
201
+ end
202
+
203
+
@@ -0,0 +1,74 @@
1
+ require 'robot'
2
+
3
+ class HuntingDuck
4
+ include Robot
5
+ def initialize *bf
6
+ if bf.size != 0
7
+ super(bf[0])
8
+ @tourney = false
9
+ else
10
+ super
11
+ @tourney = true
12
+ end
13
+ @time_since=10
14
+ @direction=1
15
+ @rob_distance=200
16
+ end
17
+ def rel_direction(from,to)
18
+ rel = to -from
19
+ if rel > 180
20
+ rel = -360 + rel
21
+ end
22
+ if rel < -180
23
+ rel = 360+rel
24
+ end
25
+ return rel
26
+ end
27
+ def rel_gun_heading
28
+ rel_direction(heading, gun_heading)
29
+ end
30
+ def tick events
31
+ accelerate 1
32
+ @rob_distance=events['robot_scanned'][0][0] unless events['robot_scanned'].empty?
33
+ if @rob_distance > 800
34
+ #make small turret adjustments when distance to enemy is great
35
+ turret_turn=3
36
+ else
37
+ turret_turn=6
38
+ end
39
+ #turn clockwise or counter clockwise?
40
+ @direction = -@direction if Kernel.rand < 0.02
41
+ if !events['got_hit'].empty?
42
+ #if we got hit we'd better shoot back and try to turn away
43
+ fire 2
44
+ turn -10*@direction
45
+ end
46
+ if !events['robot_scanned'].empty?
47
+ fire 3
48
+ @time_since=0
49
+ else
50
+ if @time_since < 15
51
+ #spotted the enemy, moving closer
52
+ #while moving the turret to spot him again
53
+ if Kernel.rand < 0.5 && rel_gun_heading < 30
54
+ turn_gun turret_turn
55
+ elsif rel_gun_heading > -30
56
+ turn_gun -turret_turn
57
+ else
58
+ turn_gun turret_turn
59
+ end
60
+ fire 0.5
61
+ elsif @time_since < 100
62
+ #try to spot the enemy
63
+ turn 10*@direction
64
+ else
65
+ #if we got stuck this should get us out
66
+ turn 5
67
+ end
68
+ end
69
+ @time_since += 1
70
+ end
71
+ end
72
+
73
+
74
+
@@ -0,0 +1,15 @@
1
+ require 'robot'
2
+
3
+ class HyperactiveDuck
4
+ include Robot
5
+
6
+ def tick events
7
+ turn_radar 1 if time == 0
8
+ accelerate 1
9
+ turn 2
10
+ if !events['robot_scanned'].empty? && gun_heat <= 0
11
+ fire 1
12
+ turn_gun -30
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,58 @@
1
+ require 'robot'
2
+
3
+ class Killer
4
+ include Robot
5
+
6
+ def min_max value, m
7
+ value-= 360 if value > 180
8
+ value+= 360 if value < -180
9
+ value = -m if value < -m
10
+ value = m if value > m
11
+ return value
12
+ end
13
+
14
+ def tick events
15
+ @dist = 1000 if @dist.nil?
16
+ @target_heading = 0 if @target_heading.nil?
17
+ @predicted_heading = 0 if @predicted_heading.nil?
18
+ @radar_range = 60 if @radar_range.nil?
19
+ @direction = 1 if @direction.nil?
20
+
21
+ if !events['robot_scanned'].empty?
22
+ @dist = events['robot_scanned'].first.first
23
+ @target_heading = radar_heading - @radar_range * 0.5
24
+ @predicted_heading = @target_heading + 16.0 * (rand(3)-1)
25
+ if (@dist * @radar_range.abs <= 500)
26
+ sx = x + Math::cos(@target_heading / 180 * Math::PI) * @dist
27
+ sy = y - Math::sin(@target_heading / 180 * Math::PI) * @dist
28
+ if @last_scan && (time - @last_scan[2]) == 1
29
+ px = sx + (sx - @last_scan[0]) * (@dist - 90) / 30
30
+ py = sy + (sy - @last_scan[1]) * (@dist - 90) / 30
31
+ @predicted_heading = Math.atan2(y - py, px - x) / Math::PI * 180 % 360
32
+ end
33
+ @last_scan = [sx, sy, time]
34
+ end
35
+ @radar_range = (@dist * @radar_range.abs > 250) ? -@radar_range * 0.5 : -@radar_range
36
+ else
37
+ @radar_range *= -2 if (@radar_range.abs < 60)
38
+ end
39
+
40
+ fire 3
41
+
42
+ if ((x < 100) || (x > 1500) || (y < 100) || (y > 1500)) && (@escape.nil? || @escape.zero?)
43
+ @escape = 30
44
+ @direction *= -1
45
+ end
46
+
47
+ @escape -= 1 if @escape && (@escape > 0)
48
+ accelerate(@direction)
49
+
50
+ turn_body = min_max(@target_heading - heading + 90, 10)
51
+ gun = min_max(@predicted_heading - gun_heading - turn_body, 30)
52
+ radar = min_max(@radar_range - gun - turn_body, 60)
53
+
54
+ turn(turn_body)
55
+ turn_gun(gun)
56
+ turn_radar(radar)
57
+ end
58
+ end
@@ -0,0 +1,193 @@
1
+ # Uses linear targetting and
2
+ # moves in a way that dodges linear targetting.
3
+ # Not very good for melee.
4
+ #
5
+ require 'robot'
6
+ require 'matrix'
7
+
8
+ class Numeric
9
+ def deg2rad
10
+ self * 0.0174532925199433
11
+ end
12
+ def rad2deg
13
+ self * 57.2957795130823
14
+ end
15
+ def sign
16
+ self / abs
17
+ end
18
+ end
19
+ class Array
20
+ def average
21
+ inject{|s,i|s+i} / size.to_f
22
+ end
23
+ def sd
24
+ avg = average
25
+ Math.sqrt( inject(0){|s,i| s+(i-avg)**2} / (size-1.0) )
26
+ end
27
+ end
28
+
29
+ class Kite
30
+ include Robot
31
+
32
+ def initialize *args, &block
33
+ super
34
+ @rt = @radar_scan = 15
35
+ @min_radar_scan = 1.5
36
+ @max_radar_scan = 60.0
37
+ @lock = false
38
+ @firing_threshold = 20.0
39
+ @wanted_turn = @wanted_gun_turn = @wanted_radar_turn = 0
40
+ @rturn_dir = 1
41
+ @racc_dir = 1
42
+ @target_positions = []
43
+ @sd_limit = 30
44
+ end
45
+
46
+ def tick events
47
+ @prev_health = energy if time == 0
48
+ if events['robot_scanned'].empty?
49
+ @radar_scan = [@radar_scan * 1.5, @max_radar_scan].min
50
+ else
51
+ @radar_scan = [@radar_scan * 0.5, @min_radar_scan].max
52
+ end
53
+ @rt = (time/2 % 2 < 1 ? -@radar_scan/2.0 : @radar_scan/2.0) if @radar_scan.abs < @max_radar_scan - 0.1
54
+ @wanted_radar_turn += @rt
55
+ firing_solution events
56
+ @hit = unless events['got_hit'].empty?
57
+ @racc_dir = @racc_dir / @racc_dir.abs
58
+ 20
59
+ else
60
+ (@hit||0) - 1
61
+ end
62
+ if @hit < 0
63
+ @racc_dir = (@min_distance and @min_distance < 450) ? @racc_dir.sign : Math.sin(time*0.1+rand*0.2)
64
+ end
65
+ accelerate(@racc_dir)
66
+ if approaching_wall?
67
+ @wanted_turn = 60 * @rturn_dir
68
+ elsif @target_heading and @wanted_turn <= 1
69
+ @wanted_turn = heading_distance(((@min_distance and @min_distance < 450) ? (90-@rturn_dir.sign*30)*-@racc_dir.sign : 0)+heading, 90+@target_heading)
70
+ elsif rand < 0.3
71
+ @wanted_turn += rand * 10 * @racc_dir.sign * @rturn_dir
72
+ elsif rand < 0.01
73
+ @rturn_dir *= -1
74
+ elsif rand < 0.01
75
+ @racc_dir *= -1
76
+ end
77
+ turn_hull
78
+ turn_turret
79
+ turn_radar_dish
80
+ @prev_health = energy
81
+ end
82
+
83
+ def firing_solution events
84
+ unless events['robot_scanned'].empty?
85
+ last = @target_positions.last || Vector[0,0]
86
+ position = events['robot_scanned'].map{|d|
87
+ tx = x + Math.cos((radar_heading - @radar_scan.abs / 2.0).deg2rad) * d[0]
88
+ ty = y - Math.sin((radar_heading - @radar_scan.abs / 2.0).deg2rad) * d[0]
89
+ Vector[tx,ty]
90
+ }.min{|a,b| (a - last).r <=> (b - last).r }
91
+ @target_positions.push position
92
+ @min_distance = events['robot_scanned'].flatten.min
93
+ end
94
+ @target_positions.shift if @target_positions.size > 10
95
+ @min_distance = nil if @target_positions.empty?
96
+ @target_heading = target_heading
97
+ gtd = heading_distance(gun_heading, @target_heading) if @target_heading
98
+ @firepower = [3*25 / (@vsd||1500)].max / 2.0
99
+ fire @firepower if @on_target
100
+ if gtd and gtd.abs < @firing_threshold
101
+ @wanted_gun_turn = gtd
102
+ @on_target = true
103
+ else
104
+ @wanted_gun_turn = gtd || (gun_radar_distance/3.0)
105
+ @on_target = false
106
+ end
107
+ end
108
+
109
+ def average arr
110
+ arr.inject{|s,i| s+i} * (1.0/arr.size)
111
+ end
112
+
113
+ def target_heading
114
+ return nil if @target_positions.size < 10
115
+ return radar_heading if @min_distance and @min_distance < 200
116
+ lps = (0...5).map{|i| @target_positions[i*2,2] }.map{|pta| average(pta) }
117
+ p4 = lps.last
118
+ vs = lps[0..-2].zip(lps[1..-1]).map{|a,b| (b-a) * 0.5 }
119
+ @vsd = Math.sqrt(vs.map{|v| v[0]}.sd**2 + vs.map{|v| v[1]}.sd**2)
120
+ return nil if @vsd > @sd_limit
121
+ v = average(vs)
122
+ return heading_for(average(lps)) if v.r < 4.0
123
+ p4 = p4 + (v*0.5)
124
+ distance = p4 - Vector[x,y]
125
+ shot_speed = (30/8.0)*v.r
126
+ a = distance[0]**2 + distance[1]**2
127
+ b = 2*distance[0]*v[0] + 2*distance[1]*v[1]
128
+ c = v[0]**2 + v[1]**2 - shot_speed**2
129
+ t = 2*a / (-b + Math.sqrt(b**2-4*a*c))
130
+ ep = p4 + v*t
131
+ estimated_position = Vector[
132
+ [size, [battlefield_width-size, ep[0]].min].max,
133
+ [size, [battlefield_height-size, ep[1]].min].max]
134
+ heading_for(estimated_position) + (rand-0.5)*@vsd*0.2
135
+ end
136
+
137
+ def heading_for(position)
138
+ distance = position - Vector[x,y]
139
+ heading = (Math.atan2(-distance[1], distance[0])).rad2deg
140
+ heading += 360 if heading < 0
141
+ heading
142
+ end
143
+
144
+ def heading_distance h1, h2
145
+ limit h2 - h1, 180
146
+ end
147
+
148
+ def limit value, m
149
+ value -= 360 if value > 180
150
+ value += 360 if value < -180
151
+ value = -m if value < -m
152
+ value = m if value > m
153
+ return value
154
+ end
155
+
156
+ def gun_radar_distance
157
+ heading_distance gun_heading, radar_heading
158
+ end
159
+
160
+ def turn_hull
161
+ turn_amt = [-10.0, [@wanted_turn, 10.0].min].max
162
+ turn turn_amt
163
+ @wanted_turn -= turn_amt
164
+ @wanted_gun_turn -= turn_amt
165
+ @wanted_radar_turn -= turn_amt
166
+ end
167
+
168
+ def turn_turret
169
+ turn_amt = [-30.0, [@wanted_gun_turn, 30.0].min].max
170
+ turn_gun turn_amt
171
+ @wanted_gun_turn -= turn_amt
172
+ @wanted_radar_turn -= turn_amt
173
+ end
174
+
175
+ def turn_radar_dish
176
+ turn_amt = [-60.0, [@wanted_radar_turn, 60.0].min].max
177
+ turn_radar turn_amt
178
+ @wanted_radar_turn -= turn_amt
179
+ end
180
+
181
+ def approaching_wall?
182
+ if not ( (velocity > 0) ^ heading.between?(0.0, 180.0) )
183
+ y < 100
184
+ else
185
+ y > battlefield_height - 100
186
+ end or if not ( (velocity > 0) ^ heading.between?(90.0, 270.0) )
187
+ x < 100
188
+ else
189
+ x > battlefield_width - 100
190
+ end
191
+ end
192
+
193
+ end