-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathuniversal-intermediate.rb
224 lines (198 loc) · 5.51 KB
/
universal-intermediate.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
# score: 930
# epic mode: 941
class Player
DIRECTIONS = [:forward, :right, :backward, :left]
def initialize
@health = 20
@last_move = nil
@last_bomb = false
@units = []
@warrior = nil
@view = {}
@enemy = nil
@detonate = false
end
def closer(s1, s2)
return s2 unless s1
return s1 if s1[:distance] <= s2[:distance]
s2
end
def check_detonate
@detonate = false
DIRECTIONS.each do |dir|
enemies = 0
next if @view[dir][0].empty?
@view[dir].each do |space|
enemies += 1 if space.enemy?
end
if enemies > 1
@detonate = dir
@units.each do |u|
next if u.character != 'C'
if @warrior.distance_of(u) <= 2
@enemy = {:distance => 1, :space => @warrior.feel(@detonate), :direction => @detonate}
@detonate = false
break
end
end if @warrior.respond_to?(:distance_of)
end
end
if (not @detonate) and @last_bomb and @enemies.size > 0
enemy = false
@view[@last_bomb][0,2].each {|s| enemy = true if s.enemy?}
@detonate = @last_bomb if enemy
end
end
def scan_view
@detonate = @enemy = @captive = @stairs = nil
DIRECTIONS.each do |dir|
space = @view[dir].first
s = {:distance => 1, :direction => dir, :space => space}
if space.character == 'C'
@captive = closer @captive, s
elsif space.enemy? or space.captive?
@enemy = (not @enemy) ? s : space.captive? ? @enemy : closer(@enemy, s)
elsif space.stairs?
@stairs = s
end
end
check_detonate
end
def look_around
DIRECTIONS.each do |dir|
if @warrior.respond_to? :look
@view[dir] = @warrior.look(dir)
elsif @warrior.respond_to? :feel
@view[dir] = [@warrior.feel(dir)]
else
@view[dir] = []
end
end
if @warrior.respond_to? :listen
@units = @warrior.listen.sort { |a,b|
res = (a.captive? and a.ticking?) ? -1 :
(b.captive? and b.ticking?) ? 1 : 0
if @warrior.respond_to?(:distance_of) and res == 0
da = @warrior.distance_of(a)
db = @warrior.distance_of(b)
res = (da > db) ? -1 : (db > da) ? 1 : 0
end
res
}
end
@enemies = @units.select{ |u| u.character != 'C' }
scan_view
end
def low_health?
return false unless @warrior.respond_to?(:health)
return false if @warrior.respond_to?(:listen) and @enemies.size == 0
if @enemy
c = @enemy[:space].character
lh = @health < 16
return true if c == 'S' and lh
end
@warrior.health < 16
end
def ticking?
return false if @units.empty?
@units[0].captive? and @units[0].ticking?
end
def surrounded?
enemies = 0
captives = 0
DIRECTIONS.each do |d|
space = @view[d].first
enemies += 1 if space and space.enemy?
captives += 1 if space and space.captive?
end
(enemies > 1) or (ticking? and enemies > 0 and captives > 0 and (@captive and @captive[:space].ticking?))
end
def oposite?(a,b)
case a
when :left
b == :right
when :right
b == :left
when :forward
b == :backward
when :backward
b == :forward
end
end
def move!(dir=:forward)
@last_bomb = false
@last_move = dir
@warrior.walk! dir
end
def no_way?
dir = nil
DIRECTIONS.each do |d|
next unless @warrior.feel(d).empty?
next if oposite?(@last_move, d)
dir = d
end
dir == nil
end
def play_turn(warrior)
@warrior = warrior
if @warrior.respond_to?(:health)
@health = @warrior.health
end
look_around
if surrounded?
dir = @enemy[:direction]
DIRECTIONS.each do |d|
s = @view[d][0]
next if s.empty? or s.wall? or s.captive?
next if d == @detonate
next if ticking? and @warrior.respond_to?(:direction_of) and (@warrior.direction_of(@units.first) == d)
dir = d
end
return @warrior.bind!(dir)
end
if low_health? and (not @stairs) and (not ticking?)
if (@warrior.respond_to?(:listen) and @enemies.size > 0) and (not @enemy or @enemy[:space].captive?) or
(not @enemy) or
(@enemy and @enemy[:space].captive?)
return @warrior.rest!
end
end
if @detonate
return @warrior.rest! if @warrior.health < 5
@last_bomb = @detonate
return @warrior.detonate!(@detonate)
end
if @enemy and ((not ticking?) or no_way?)
if ticking?
# no way, so bot has to fight
return @warrior.attack!(@warrior.direction_of @units.first)
end
return @warrior.attack!(@enemy[:direction]) if (@enemy[:distance] <= 1)
return move!(@enemy[:direction])
end
if low_health? and @last_bomb and @enemies.size>0
return @warrior.rest!
else
@last_bomb = false
end
if @captive and ((not ticking?) or (@captive[:space].ticking?))
return @warrior.rescue!(@captive[:direction]) if @captive[:distance] <= 1
return move!(@captive[:direction])
end
unless @units.empty?
dir = @warrior.direction_of(@units.first)
if @warrior.feel(dir).stairs? or not @warrior.feel(dir).empty?
pos = []
DIRECTIONS.each do |d|
space = @warrior.feel(d)
next if space.stairs?
next if not space.empty?
pos << d
end
dir = (oposite?(dir, pos.first) and pos.size > 1) ? pos.last : pos.first
end
return move! dir
end
move! @warrior.direction_of_stairs
end
end