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,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