/
player.rb
211 lines (183 loc) · 6.11 KB
/
player.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
require './map'
class Player
# When we have less than this % health, rest (unless we're not in a safe
# space)
ATTACK_POWER = 5
MAX_HEALTH = 20
MINIMUM_PERCENT_HEALTH = 50
def set_variables!(warrior)
@warrior = warrior
@previous_health = current_health
# If true, then rest until at max health before continuing
@recuperating = false
@just_killed_an_enemy = false
@map = Map.new(self, warrior)
@already_set_variables = true
end
attr_accessor :direction
def play_turn(warrior)
# warrior.action returns [:latest_action, :direction_it_was_performed]
set_variables!(warrior) unless @already_set_variables
@warrior = warrior # yes, you have to set this each time
@map.update!(warrior)
perform_action!(warrior)
# Set variables for next turn
previous_health = current_health
@map.previous_location = current_location
end
# Performs a bang-action, e.g. walk!
def perform_action!(warrior)
# available methods for a Space (returned by warrior.feel)
# TESTERS
# stairs?
# empty?
# enemy?
# captive?
# wall?
# ticking? (OMINOUS!)
# TYPE GETTERS
# unit (e.g. "Thick Sludge", may be nil)
# location - An [X-coord, Y-coord] pair
# - A coordinate may be < 0, e.g. if you're at [0, 0],
# then warrior.feel(:backward).location = [-1, 0]
# character (e.g. "S" for a thick sludge, may be nil)
# Note that the stairs space is empty (unless a unit is on it), i.e.
# space.empty? returns true for the stairs space, so explicitly check
# for stairs.
# We can get away with only feeling in one direction, for now.
space = warrior.feel(direction)
# The stairs space may not be empty, e.g. if an enemy or captive is on
# it
if space.stairs? and space.empty?
warrior.walk!(direction)
elsif current_location == [1, 0] and warrior.feel(:backward).empty?
# Nothing more to do here
@direction = :forward
warrior.walk!(direction)
elsif should_rest?
#@just_killed_an_enemy = false
if in_safe_space?
# Regain some health
@map.mark_safe_location!(current_location)
warrior.rest!
else
puts "Should rest, but not in safe space. Moving towards it."
@recuperating = true
reverse_direction!
@direction = @map.towards_safe_space
warrior.walk!(direction)
end
elsif space.captive?
warrior.rescue!(direction)
elsif space.enemy?
#attack_enemy!
@map.register_current_enemy!
@map.decrement_current_enemys_health_by(ATTACK_POWER)
warrior.attack!(direction)
elsif space.empty?
# May have just moved away from an enemy, so check if we should
# re-engage.
if @map.location_of_current_enemy.nil?
# No enemies, blithely continue
warrior.walk!(direction)
else
@direction = @map.towards_current_enemy
warrior.walk!(direction)
end
elsif space.wall?
# Hit a wall, switch direction and retry
reverse_direction!
perform_action!(warrior)
else
puts "!!! Weird space: #{space.inspect}"
warrior.walk!(direction)
end
end
# STATE CHANGERS
def reverse_direction!
@direction = @map.opposite_direction_of(direction)
end
# A wrapper around warrior.attack!(direction) so we can track an enemy's
# health.
def attack_enemy!
location = @warrior.feel(direction).location
@map.register_current_enemy!
@map.decrement_current_enemys_health_by(ATTACK_POWER)
@warrior.attack!(direction)
end
## TESTERS
# Is this space safe to rest in?
# Note that it doesn't take into account whether the warrior is taking
# damage.
def in_safe_space?
not taking_damage? and not next_to_enemy?
end
# Should the warrior rest? Note that this doesn't take into account
# whether it's safe for the warrior to rest, it just recommends that he
# should.
def should_rest?
# No need to rest when we're about to clear the level
return false if @warrior.feel(direction).stairs?
return false if @warrior.health == MAX_HEALTH
if @recuperating
# Stop recuperating if at max health
@recuperating = @warrior.health < MAX_HEALTH
return @recuperating
elsif @map.current_enemy
not healthy_enough_to_beat_enemy_at?(@map.get_current_enemy_info[:location])
else
low_on_health?
end
end
# Is an enemy in an adjacent space?
def next_to_enemy?
Map::DIRECTIONS.any? { |dir| @warrior.feel(dir).enemy? }
end
def taking_damage?
current_health < @previous_health
end
def taking_damage_from_afar?
taking_damage? and not next_to_enemy?
end
# Did we just start taking damage this turn?
def just_started_taking_damage?
taking_damage? and @map.is_safe_location?(@previous_location)
end
def low_on_health?
percent_health = (current_health.to_f / MAX_HEALTH) * 100
percent_health < MINIMUM_PERCENT_HEALTH
end
# ACCESSORS
def current_health
@warrior.health
end
# Get direction warrior is walking in. Defaults to :backward.
def direction
@direction ||= :backward
end
# UTILITY
# Pass in an enemy location to determine how many turns it
# will take to kill it once you're next to it.
def turns_required_to_beat_enemy_at(enemy_location)
info = @map.get_info_about_enemy_at(enemy_location)
turns = (info[:health].to_f / ATTACK_POWER).ceil
turns
end
def current_location
backward = @warrior.feel(:backward).location
x_coord = backward[0] + 1
y_coord = backward[1]
[x_coord, y_coord]
end
# Do we have enough health to engage in battle?
def healthy_enough_to_beat_enemy_at?(enemy_location)
turns = turns_required_to_beat_enemy_at(enemy_location)
enemy_info = @map.get_info_about_enemy_at(enemy_location)
# Archers attack from a distance of 2 squares, but if we're right next
# to them then we need fewer turns
turns += [2, @map.get_distance_away(enemy_info[:location])].min if enemy_info[:character] == :a
puts "turns reqd: #{turns}"
predicted_damage_taken = turns * enemy_info[:damage]
predicted_damage_taken < current_health
end
end