UG_RRobots 1.2

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.
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,205 @@
1
+ # RoomPainter
2
+ # written by Jannis Harder (jix) in 2005
3
+
4
+ require 'robot'
5
+
6
+ # avoid clashes with other state robots
7
+
8
+ module RoomPainterTools
9
+
10
+ # a state based robot
11
+ class StateRobot
12
+ include Robot
13
+
14
+ def initialize(*)
15
+ super
16
+ @mode = :idle
17
+ @sec_modes = []
18
+ init
19
+ end
20
+
21
+ # set mode for next tick
22
+ def next_mode new_mode
23
+ if has_mode? new_mode
24
+ @mode = new_mode
25
+ else
26
+ warn "Robot #{self.class} switched to unknown mode :#{new_mode}!"
27
+ @mode = :idle
28
+ end
29
+ end
30
+
31
+ # activate a secondary mode
32
+ def add_sec_mode new_mode
33
+ if has_mode? new_mode
34
+ @sec_modes << new_mode
35
+ else
36
+ warn "Robot #{self.class} switched to unknown secondary mode :#{new_mode}!"
37
+ end
38
+ end
39
+
40
+ # activate some secondary modes
41
+ def add_sec_modes *modes
42
+ modes.flatten.each do |new_mode|
43
+ add_sec_mode new_mode
44
+ end
45
+ end
46
+
47
+ # deactivate a secondary mode
48
+ def del_sec_mode old_mode
49
+ @sec_modes.delete old_mode
50
+ end
51
+
52
+ # deactivate some secondary modes
53
+ def del_sec_modes *modes
54
+ modes.flatten.each do |old_mode|
55
+ del_sec_mode old_mode
56
+ end
57
+ end
58
+
59
+ # test for a mode
60
+ def has_mode? new_mode
61
+ respond_to? new_mode.to_s<<'_tick'
62
+ end
63
+
64
+ # set new mode and tick it once
65
+ def mode new_mode
66
+ next_mode new_mode
67
+ tick @events
68
+ end
69
+
70
+ # tick a mode
71
+ def tick_mode temp_mode
72
+ send temp_mode.to_s<<'_tick'
73
+ end
74
+
75
+ # default tick
76
+ def idle_tick
77
+ end
78
+
79
+ # processing
80
+ def tick events
81
+ @events = events
82
+ tick_mode @mode
83
+ @sec_modes.each do |smode|
84
+ tick_mode smode
85
+ end
86
+ end
87
+
88
+
89
+ attr_reader :events
90
+
91
+ end
92
+
93
+ end
94
+
95
+ class RoomPainter < RoomPainterTools::StateRobot
96
+
97
+ def init
98
+ next_mode :move
99
+ add_sec_modes :aim, :scan, :fire
100
+ @scan_range = 2
101
+ @tracking = 0
102
+ @fire = [nil]*2
103
+ @turning = 0
104
+ @deg = 0
105
+ @acc = 1
106
+ end
107
+
108
+
109
+ #helpers
110
+
111
+ def offset(heading_a,heading_b)
112
+ my_offset = heading_a-heading_b % 360
113
+ my_offset = my_offset -360 if my_offset > 180
114
+ my_offset
115
+ end
116
+
117
+ def on_wall
118
+ x <= size or \
119
+ y <= size or \
120
+ x >= battlefield_width-size or \
121
+ y >= battlefield_height-size
122
+ end
123
+
124
+ def nth?(n)
125
+ time % n == 0
126
+ end
127
+
128
+ #ticks
129
+
130
+ def turn_upwards_tick
131
+ stop
132
+ if heading == 90
133
+ mode :paint
134
+
135
+ else
136
+ turn 90-heading
137
+ end
138
+ end
139
+
140
+ def scan_tick
141
+ @scan_range = [@scan_range,10].min
142
+
143
+ radar_offset = offset(radar_heading,gun_heading)
144
+ if nth? 2
145
+ turn_radar -@scan_range-radar_offset
146
+ else
147
+ turn_radar +@scan_range-radar_offset
148
+ end
149
+ end
150
+
151
+ def fire_tick
152
+ if x = @fire.pop
153
+ fire x
154
+ end
155
+ if scanned = events['robot_scanned'].first
156
+ away = scanned.first
157
+ str = [0.1,800.0/away].max
158
+ @fire << str
159
+ else
160
+ @fire << nil
161
+ end
162
+
163
+ end
164
+
165
+ def aim_tick
166
+
167
+ if scanned = events['robot_scanned'].first
168
+ away = scanned.first
169
+ @scan_range = 1.2
170
+
171
+ @last_offset = offset(radar_heading,gun_heading)/2.0
172
+ turn_gun(@last_offset)
173
+ @tracking = 10
174
+ elsif @tracking <= 0
175
+ @scan_range += 0.1
176
+ if @deg > 0
177
+ turn_gun(-30)
178
+ else
179
+ turn_gun(30)
180
+ end
181
+ else
182
+ @tracking-=1
183
+ @last_offset*=-2
184
+ turn_gun(@last_offset)
185
+ end
186
+
187
+ end
188
+
189
+ def move_tick
190
+ accelerate(@acc)
191
+ if @turning <= 0
192
+ @turning = rand(30)
193
+ @deg = (rand*4)*(1-rand(2)*2)
194
+ end
195
+ @turning-=1
196
+ if on_wall and (velocity < 0) == (@acc < 0) and velocity.abs > 4
197
+ @acc *= -1
198
+ else
199
+ turn(@deg)
200
+ end
201
+ end
202
+
203
+
204
+
205
+ end
@@ -0,0 +1,48 @@
1
+ require 'robot'
2
+
3
+ class Rrrkele
4
+ include Robot
5
+
6
+ def limit 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
+ if time == 0
16
+ @acc = 1
17
+ @target_distance = 250
18
+ @target_heading = rand * 360
19
+ @scanning_range = 60
20
+ @velocity = 0
21
+ end
22
+
23
+ unless events['robot_scanned'].empty?
24
+ @target_distance = events["robot_scanned"].first.first
25
+ @target_heading = @radar_heading
26
+ @scanning_range *= -0.5 if @scanning_range.abs > 0.5
27
+ else
28
+ @scanning_range *= -2 if @scanning_range.abs < 60
29
+ end
30
+
31
+ unless events['got_hit'].empty?
32
+ @acc *= -1
33
+ end
34
+
35
+ fire 0.1
36
+
37
+ body_turn_angle = limit(@heading - @target_heading + 90, 10)
38
+ gun_turn_angle = limit(@target_heading - @gun_heading - body_turn_angle, 30)
39
+ radar_turn_angle = limit(@scanning_range - gun_turn_angle - body_turn_angle, 60)
40
+
41
+ turn(body_turn_angle)
42
+ turn_gun(gun_turn_angle)
43
+ turn_radar(radar_turn_angle)
44
+
45
+ accelerate(@acc)
46
+ end
47
+ end
48
+
@@ -0,0 +1,57 @@
1
+ # follows and shoots
2
+ require 'robot'
3
+
4
+ class Seeker
5
+ include Robot
6
+
7
+ attr_accessor
8
+
9
+ def initialize *args, &block
10
+ @rt = @radar_scan = 10
11
+ @min_radar_scan = 10.0
12
+ @max_radar_scan = 30.0
13
+ @radar_turned = false
14
+ @min_distance = 0
15
+ @direction = 1
16
+ super
17
+ end
18
+
19
+ def tick events
20
+ @direction = @min_distance - 175
21
+ accelerate(@direction)
22
+ wturn = 180 - ((radar_heading-heading)%360) + @rt*0.5
23
+ wturn = [-10,[10,wturn].min].max
24
+ turn wturn
25
+ scan events
26
+ turn_radar @rt - wturn
27
+ fire @direction > 0 ? 0.5 : 3.0
28
+ end
29
+
30
+ def scan events
31
+ if events['robot_scanned'].empty?
32
+ increase_radar_scan
33
+ else
34
+ decrease_radar_scan
35
+ @min_distance = events['robot_scanned'].min.first
36
+ end
37
+ @rt = if @radar_turned
38
+ -@radar_scan
39
+ else
40
+ @radar_scan
41
+ end if @radar_scan.abs < @max_radar_scan - 0.1
42
+ @radar_turned = !@radar_turned
43
+ @rt
44
+ end
45
+
46
+ def increase_radar_scan
47
+ @radar_scan *= 1.5
48
+ @radar_scan = [@radar_scan, @max_radar_scan].min
49
+ end
50
+
51
+ def decrease_radar_scan
52
+ @radar_scan *= 0.5
53
+ @radar_scan = [@radar_scan, @min_radar_scan].max
54
+ end
55
+
56
+ end
57
+
@@ -0,0 +1,15 @@
1
+ require 'robot'
2
+ class ShootingStation
3
+ include Robot
4
+ def tick events
5
+ if rand < 0.2
6
+ turn(10)
7
+ turn_gun(30)
8
+ else
9
+ turn(-4)
10
+ turn_gun(-30)
11
+ end
12
+ fire(0.3)
13
+ accelerate(1)
14
+ end
15
+ end
@@ -0,0 +1,18 @@
1
+ require 'robot'
2
+
3
+ class SittingDuck
4
+ include Robot
5
+
6
+ def tick events
7
+ turn_radar 5 if time == 0
8
+ fire 3 unless events['robot_scanned'].empty?
9
+ turn_gun 10
10
+
11
+ @last_hit = time unless events['got_hit'].empty?
12
+ if @last_hit && time - @last_hit < 20
13
+ accelerate(1)
14
+ else
15
+ stop
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,277 @@
1
+ require 'robot'
2
+ require 'matrix'
3
+
4
+
5
+ class Numeric
6
+
7
+ def deg2rad
8
+ self * 0.0174532925199433
9
+ end
10
+
11
+ def rad2deg
12
+ self * 57.2957795130823
13
+ end
14
+
15
+ end
16
+
17
+ class Array
18
+ def average
19
+ inject{|s,i|s+i} / size.to_f
20
+ end
21
+ def sd
22
+ avg = average
23
+ Math.sqrt( inject(0){|s,i| s+(i-avg)**2} / (size-1.0) )
24
+ end
25
+ end
26
+
27
+ class Vector
28
+ include Enumerable
29
+
30
+ def each &block
31
+ @elements.each(&block)
32
+ end
33
+
34
+ end
35
+
36
+
37
+ class SniperDuck
38
+ include Robot
39
+
40
+ def initialize *args, &block
41
+ super
42
+ @rt = @radar_scan = 15
43
+ @min_radar_scan = 1.5
44
+ @max_radar_scan = 60.0
45
+ @radar_turned = false
46
+ @lock = false
47
+ @lock_threshold = 15.0
48
+ @firing_threshold = 20.0
49
+ @wanted_turn = @wanted_gun_turn = @wanted_radar_turn = 0
50
+ @rturn_dir = 1
51
+ @racc_dir = 1
52
+ @serial_hit_limit = 10
53
+ @serial_hit_counter = 0
54
+ @target_positions = []
55
+ @min_distance = 1500
56
+ @sd_limit = 30
57
+ end
58
+
59
+ def tick events
60
+ @prev_health = energy if time == 0
61
+ scan events
62
+ firing_solution events
63
+ navigate events
64
+ turn_hull
65
+ turn_turret
66
+ turn_radar_dish
67
+ @prev_health = energy
68
+ end
69
+
70
+ def scan events
71
+ if events['robot_scanned'].empty?
72
+ increase_radar_scan
73
+ @lock = false
74
+ else
75
+ decrease_radar_scan
76
+ @lock = @radar_scan.abs < @lock_threshold
77
+ end
78
+ @rt = if @radar_turned
79
+ -@radar_scan
80
+ else
81
+ @radar_scan
82
+ end if @radar_scan.abs < @max_radar_scan - 0.1
83
+ @wanted_radar_turn += @rt
84
+ @radar_turned = !@radar_turned
85
+ end
86
+
87
+ def firing_solution events
88
+ add_target_position(events['robot_scanned'])
89
+ gtd = gun_target_distance
90
+ @firepower = [1.5e5 / (@min_distance)**2, 3*25 / (@vsd||1500)].max
91
+ fire @firepower if @on_target
92
+ if @lock and gtd and gtd.abs < @firing_threshold
93
+ @wanted_gun_turn = gtd
94
+ @on_target = true
95
+ else
96
+ fire @firepower
97
+ @wanted_gun_turn = (gtd || (gun_radar_distance/3.0) + Math.sin(time*0.2)*(@vsd||50)/10)
98
+ @on_target = false
99
+ end
100
+ end
101
+
102
+ def add_target_position(distances)
103
+ unless distances.empty?
104
+ positions = distances.map{|d|
105
+ tx = x + Math.cos((radar_heading - @radar_scan.abs / 2.0).deg2rad) * d[0]
106
+ ty = y - Math.sin((radar_heading - @radar_scan.abs / 2.0).deg2rad) * d[0]
107
+ [d, Vector[tx,ty]]
108
+ }
109
+ last = @target_positions.last
110
+ position = if last
111
+ positions.min{|a,b| (a[1] - last).r <=> (b[1] - last).r }[1]
112
+ else
113
+ positions.min{|a,b| a[0] <=> b[0]}[1]
114
+ end
115
+ @min_distance = distances.min{|a,b| a[0]<=>b[0]}[0]
116
+ @target_positions.push position if position
117
+ end
118
+ @target_positions.shift if @target_positions.size > 200
119
+ end
120
+
121
+ def average_four_last_positions
122
+ c = 5
123
+ tps = @target_positions[-20,20]
124
+ ptas = (0...tps.size/c).map{|i| tps[i*c,c] }
125
+ ptas.map{|pta| average(pta) }
126
+ end
127
+
128
+ def average arr
129
+ arr.inject{|s,i| s+i} * (1.0/arr.size)
130
+ end
131
+
132
+ def sd arr
133
+ Math.sqrt(arr.map{|v| v[0]}.sd**2 + arr.map{|v| v[1]}.sd**2)
134
+ end
135
+
136
+ def gun_target_distance
137
+ return nil if @target_positions.size < 20
138
+ p1,p2,p3,p4 = average_four_last_positions
139
+ #p format([p1,p2,p3,p4].map{|i|i.to_a})
140
+ vs = [p1,p2,p3].zip([p2,p3,p4]).map{|a,b|b-a}
141
+ @vsd = sd vs
142
+ return nil if @vsd > @sd_limit
143
+ v = average(vs) * 0.2
144
+ return gun_radar_distance if v.r < 4.0
145
+ p4 = p4 + (v*3)
146
+ distance = p4 - Vector[x,y]
147
+ a = distance[0]**2 + distance[1]**2
148
+ b = 2*distance[0]*v[0] + 2*distance[1]*v[1]
149
+ c = v[0]**2 + v[1]**2
150
+ shot_speed = (30/8.0)*v.r
151
+ #p shot_speed
152
+ d = c - shot_speed**2
153
+ n = b**2 - 4*a*d
154
+ if n < 0
155
+ return nil
156
+ end
157
+ t = 2*a / (-b + Math.sqrt(n))
158
+ ep = p4 + v*t
159
+ estimated_position = Vector[[size, [battlefield_width-size, ep[0]].min].max, [size, [battlefield_height-size, ep[1]].min].max]
160
+ est_head = heading_for(estimated_position)
161
+ hd = heading_distance(gun_heading, est_head+rand*(@vsd-@vsd/2.0)/(distance.r/500))
162
+ #p format( [@target_positions.last, p4, v, [t], estimated_position, [est_head, gun_heading, hd]].map{|i|i.to_a} )
163
+ hd
164
+ end
165
+
166
+ def format(arr, precision = 1)
167
+ case arr
168
+ when Float
169
+ (arr * 10**precision).round * 10**-precision
170
+ when Array
171
+ arr.map{|i| format i, precision}
172
+ else
173
+ arr
174
+ end
175
+ end
176
+
177
+ def gun_vector
178
+ Vector[Math.cos(gun_heading.deg2rad), -Math.sin(gun_heading.deg2rad)]
179
+ end
180
+
181
+ def heading_for(position)
182
+ distance = position - Vector[x,y]
183
+ heading = (Math.atan2(-distance[1], distance[0])).rad2deg
184
+ heading += 360 if heading < 0
185
+ heading
186
+ end
187
+
188
+ def navigate events
189
+ if events['got_hit'].empty?
190
+ @serial_hit_counter -= 0.05 if @serial_hit_counter > 0
191
+ accelerate(@racc_dir) if velocity.abs < 7.9
192
+ else
193
+ @serial_hit_counter += @prev_health - events['got_hit'].last.last
194
+ @serial_hit_counter = [@serial_hit_counter, @serial_hit_limit + 0.04].max
195
+ if @serial_hit_counter > @serial_hit_limit
196
+ acc = velocity > 0 ? 1 : -1
197
+ accelerate(acc) if velocity.abs < 7.9
198
+ end
199
+ end
200
+ if @serial_hit_counter > @serial_hit_limit and not events['got_hit'].empty? and @wanted_turn <= 1
201
+ @wanted_turn = @rturn_dir * 30
202
+ @rturn_dir *= -1 if rand < 0.05
203
+ elsif approaching_wall? and @wanted_turn <= 1
204
+ @wanted_turn = 60 * @rturn_dir
205
+ @rturn_dir *= -1 if rand < 0.5
206
+ elsif rand < 0.15
207
+ @wanted_turn += rand * 10 * @rturn_dir
208
+ elsif rand < 0.01
209
+ @rturn_dir *= -1
210
+ elsif rand < 0.01
211
+ @racc_dir *= -1
212
+ end
213
+ end
214
+
215
+ def increase_radar_scan
216
+ @radar_scan *= 1.5
217
+ @radar_scan = [@radar_scan, @max_radar_scan].min
218
+ end
219
+
220
+ def decrease_radar_scan
221
+ @radar_scan *= 0.5
222
+ @radar_scan = [@radar_scan, @min_radar_scan].max
223
+ end
224
+
225
+ def heading_distance h1, h2
226
+ limit h2 - h1, 180
227
+ end
228
+
229
+ def limit value, m
230
+ value -= 360 if value > 180
231
+ value += 360 if value < -180
232
+ value = -m if value < -m
233
+ value = m if value > m
234
+ return value
235
+ end
236
+
237
+ def gun_radar_distance
238
+ heading_distance gun_heading, radar_heading
239
+ end
240
+
241
+ def turn_hull
242
+ turn_amt = [-10.0, [@wanted_turn, 10.0].min].max
243
+ #puts "Turning hull by: #{turn_amt}" if turn_amt.abs > 1
244
+ turn turn_amt
245
+ @wanted_turn -= turn_amt
246
+ @wanted_gun_turn -= turn_amt
247
+ @wanted_radar_turn -= turn_amt
248
+ end
249
+
250
+ def turn_turret
251
+ turn_amt = [-30.0, [@wanted_gun_turn, 30.0].min].max
252
+ #puts "Turning gun by: #{turn_amt}" if turn_amt.abs > 1
253
+ turn_gun turn_amt
254
+ @wanted_gun_turn -= turn_amt
255
+ @wanted_radar_turn -= turn_amt
256
+ end
257
+
258
+ def turn_radar_dish
259
+ turn_amt = [-60.0, [@wanted_radar_turn, 60.0].min].max
260
+ #puts "Turning radar by: #{turn_amt}" if turn_amt.abs > 1
261
+ turn_radar turn_amt
262
+ @wanted_radar_turn -= turn_amt
263
+ end
264
+
265
+ def approaching_wall?
266
+ if not ( (velocity > 0) ^ heading.between?(0.0, 180.0) )
267
+ y < 100
268
+ else
269
+ y > battlefield_height - 100
270
+ end or if not ( (velocity > 0) ^ heading.between?(90.0, 270.0) )
271
+ x < 100
272
+ else
273
+ x > battlefield_width - 100
274
+ end
275
+ end
276
+
277
+ end