sc2ai 0.0.6 → 0.0.8
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/data/stableid.json +424 -2562
- data/data/versions.json +8 -0
- data/lib/docker_build/Dockerfile.ruby +1 -1
- data/lib/sc2ai/api/ability_id.rb +12 -314
- data/lib/sc2ai/api/buff_id.rb +9 -16
- data/lib/sc2ai/api/data.rb +1 -1
- data/lib/sc2ai/api/tech_tree.rb +35 -0
- data/lib/sc2ai/api/tech_tree_data.rb +14 -9
- data/lib/sc2ai/api/unit_type_id.rb +6 -58
- data/lib/sc2ai/api/upgrade_id.rb +9 -9
- data/lib/sc2ai/connection/requests.rb +10 -7
- data/lib/sc2ai/data.rb +101 -0
- data/lib/sc2ai/local_play/match.rb +1 -3
- data/lib/sc2ai/player/actions.rb +8 -4
- data/lib/sc2ai/player/debug.rb +2 -3
- data/lib/sc2ai/player/game_state.rb +28 -0
- data/lib/sc2ai/player/geometry.rb +44 -39
- data/lib/sc2ai/player/previous_state.rb +1 -0
- data/lib/sc2ai/player/units.rb +25 -37
- data/lib/sc2ai/player.rb +7 -7
- data/lib/sc2ai/protocol/_meta_documentation.rb +18 -0
- data/lib/sc2ai/protocol/extensions/point_2_d.rb +1 -1
- data/lib/sc2ai/protocol/extensions/point_distance.rb +11 -0
- data/lib/sc2ai/protocol/extensions/position.rb +8 -10
- data/lib/sc2ai/protocol/extensions/unit.rb +29 -3
- data/lib/sc2ai/protocol/extensions/unit_type.rb +9 -0
- data/lib/sc2ai/unit_group/action_ext.rb +3 -3
- data/lib/sc2ai/unit_group/filter_ext.rb +22 -7
- data/lib/sc2ai/version.rb +1 -1
- data/lib/templates/new/run_example_match.rb.tt +1 -1
- data/sig/sc2ai.rbs +387 -530
- metadata +39 -21
data/lib/sc2ai/api/upgrade_id.rb
CHANGED
@@ -299,14 +299,14 @@ module Api
|
|
299
299
|
DIGGINGCLAWS = 293
|
300
300
|
CARRIERCARRIERCAPACITY = 294
|
301
301
|
CARRIERLEASHRANGEUPGRADE = 295
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
302
|
+
HURRICANETHRUSTERS = 296
|
303
|
+
TEMPESTGROUNDATTACKUPGRADE = 297
|
304
|
+
MICROBIALSHROUD = 298
|
305
|
+
INTERFERENCEMATRIX = 299
|
306
|
+
SUNDERINGIMPACT = 300
|
307
|
+
AMPLIFIEDSHIELDING = 301
|
308
|
+
PSIONICAMPLIFIERS = 302
|
309
|
+
SECRETEDCOATING = 303
|
310
|
+
ENHANCEDSHOCKWAVES = 304
|
311
311
|
end
|
312
312
|
end
|
@@ -279,10 +279,11 @@ module Sc2
|
|
279
279
|
# Advances the game simulation by step_count. Not used in realtime mode.
|
280
280
|
# Only constant step size supported - subsequent requests use cache.
|
281
281
|
def step(step_count = 1)
|
282
|
-
@_cached_request_step ||=
|
282
|
+
@_cached_request_step ||= {}
|
283
|
+
@_cached_request_step[step_count] ||= Api::Request.new(
|
283
284
|
step: Api::RequestStep.new(count: step_count)
|
284
285
|
).to_proto
|
285
|
-
send_request_and_ignore(@_cached_request_step)
|
286
|
+
send_request_and_ignore(@_cached_request_step[step_count])
|
286
287
|
end
|
287
288
|
|
288
289
|
# Additional methods for inspecting game state. Synchronous and must wait on response
|
@@ -291,7 +292,7 @@ module Sc2
|
|
291
292
|
# @param placements [Array<Api::RequestQueryBuildingPlacement>]
|
292
293
|
# @param ignore_resource_requirements [Boolean] Ignores requirements like food, minerals and so on.
|
293
294
|
# @return [Api::ResponseQuery]
|
294
|
-
def query(pathing: nil, abilities: nil, placements: nil, ignore_resource_requirements:
|
295
|
+
def query(pathing: nil, abilities: nil, placements: nil, ignore_resource_requirements: false)
|
295
296
|
send_request_for query: Api::RequestQuery.new(
|
296
297
|
pathing:,
|
297
298
|
abilities:,
|
@@ -316,7 +317,7 @@ module Sc2
|
|
316
317
|
# @param queries [Array<Api::RequestQueryAvailableAbilities>] one or more pathing queries
|
317
318
|
# @param ignore_resource_requirements [Boolean] Ignores requirements like food, minerals and so on.
|
318
319
|
# @return [Array<Api::ResponseQueryAvailableAbilities>] one or more results depending on input size
|
319
|
-
def query_abilities(queries, ignore_resource_requirements:
|
320
|
+
def query_abilities(queries, ignore_resource_requirements: false)
|
320
321
|
arr_queries = queries.is_a?(Array) ? queries : [queries]
|
321
322
|
|
322
323
|
response = send_request_for query: Api::RequestQuery.new(
|
@@ -330,7 +331,8 @@ module Sc2
|
|
330
331
|
# @param unit_tags [Array<Integer>] an array of unit tags or a single tag
|
331
332
|
# @param ignore_resource_requirements [Boolean] Ignores requirements like food, minerals and so on.
|
332
333
|
# @return [Array<Api::ResponseQueryAvailableAbilities>] one or more results depending on input size
|
333
|
-
def query_abilities_for_unit_tags(unit_tags, ignore_resource_requirements:
|
334
|
+
def query_abilities_for_unit_tags(unit_tags, ignore_resource_requirements: false)
|
335
|
+
return [] if unit_tags.nil?
|
334
336
|
queries = []
|
335
337
|
unit_tags = [unit_tags] unless unit_tags.is_a? Array
|
336
338
|
unit_tags.each do |unit_tag|
|
@@ -345,13 +347,14 @@ module Sc2
|
|
345
347
|
# and can just return an array of ability ids.
|
346
348
|
# Note: Querying single units are expensive and should be batched with #query_abilities_for_unit_tags
|
347
349
|
# @param unit [Api::Unit, Integer] a unit or a tag.
|
348
|
-
|
350
|
+
# @return [Array<Integer>] array of ability ids
|
351
|
+
def query_ability_ids_for_unit(unit, ignore_resource_requirements: false)
|
349
352
|
tag = unit.is_a?(Api::Unit) ? unit.tag : unit
|
350
353
|
result = query_abilities_for_unit_tags([tag], ignore_resource_requirements:)
|
351
354
|
if result.nil?
|
352
355
|
[]
|
353
356
|
else
|
354
|
-
result.first.abilities
|
357
|
+
result.first.abilities.map(&:ability_id)
|
355
358
|
end
|
356
359
|
end
|
357
360
|
|
data/lib/sc2ai/data.rb
ADDED
@@ -0,0 +1,101 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "api/ability_id"
|
4
|
+
require_relative "api/unit_type_id"
|
5
|
+
require_relative "api/upgrade_id"
|
6
|
+
require_relative "api/buff_id"
|
7
|
+
require_relative "api/effect_id"
|
8
|
+
require_relative "api/tech_tree"
|
9
|
+
|
10
|
+
module Sc2
|
11
|
+
# Holds game data from tech tree and Api::ResponseData
|
12
|
+
# Called once on game start
|
13
|
+
class Data
|
14
|
+
# @!attribute abilities
|
15
|
+
# @return [Hash<Integer, Api::AbilityData]>] AbilitId => AbilityData
|
16
|
+
attr_accessor :abilities
|
17
|
+
# @!attribute units
|
18
|
+
# @return [Hash<Integer, Api::UnitTypeData>] UnitId => UnitData
|
19
|
+
attr_accessor :units
|
20
|
+
# @!attribute upgrades
|
21
|
+
# @return [Hash<Integer, Api::UpgradeData>] UpgradeId => UpgradeData
|
22
|
+
attr_accessor :upgrades
|
23
|
+
# @!attribute buffs
|
24
|
+
# Not particularly useful data. Just use BuffId directly
|
25
|
+
# @return [Hash<Integer, Api::UpgradeData>] BuffId => BuffData
|
26
|
+
attr_accessor :buffs
|
27
|
+
# @!attribute effects
|
28
|
+
# Not particularly useful data. Just use EffectId directly
|
29
|
+
# @return [Hash<Integer, Api::UpgradeData>] EffectId => EffectData
|
30
|
+
attr_accessor :effects
|
31
|
+
|
32
|
+
# @param data [Api::ResponseData]
|
33
|
+
def initialize(data)
|
34
|
+
return unless data
|
35
|
+
|
36
|
+
@abilities = abilities_from_proto(data.abilities)
|
37
|
+
@units = units_from_proto(data.units)
|
38
|
+
@upgrades = upgrades_from_proto(data.upgrades)
|
39
|
+
@buffs = buffs_from_proto(data.buffs)
|
40
|
+
@effects = effects_from_proto(data.effects)
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
# Indexes ability data by ability id
|
46
|
+
# @param abilities [Array<Api::AbilityData>]
|
47
|
+
# @return [Hash<Integer, Api::AbilityData] indexed data
|
48
|
+
def abilities_from_proto(abilities)
|
49
|
+
result = {}
|
50
|
+
|
51
|
+
ability_ids = Api::AbilityId.constants.map { |c| Api::AbilityId.const_get(c) }
|
52
|
+
abilities.each do |a|
|
53
|
+
next if a.ability_id.zero?
|
54
|
+
next if ability_ids.delete(a.ability_id).nil?
|
55
|
+
|
56
|
+
result[a.ability_id] = a
|
57
|
+
end
|
58
|
+
result
|
59
|
+
end
|
60
|
+
|
61
|
+
# Indexes unit data by id
|
62
|
+
# @param units [Array<Api::UnitTypeData>]
|
63
|
+
# @return [Hash<Integer, Api::UnitTypeData>] indexed data
|
64
|
+
def units_from_proto(units)
|
65
|
+
result = {}
|
66
|
+
units.each do |u|
|
67
|
+
next unless u.available
|
68
|
+
|
69
|
+
result[u.unit_id] = u
|
70
|
+
end
|
71
|
+
result
|
72
|
+
end
|
73
|
+
|
74
|
+
# Indexes upgrades data by id
|
75
|
+
# @param upgrades [Array<Api::UpgradeData>]
|
76
|
+
# @return [Hash<Integer, Api::UpgradeData] indexed data
|
77
|
+
def upgrades_from_proto(upgrades)
|
78
|
+
result = {}
|
79
|
+
upgrades.each do |u|
|
80
|
+
result[u.upgrade_id] = u
|
81
|
+
end
|
82
|
+
result
|
83
|
+
end
|
84
|
+
|
85
|
+
def effects_from_proto(effects)
|
86
|
+
result = {}
|
87
|
+
effects.each do |e|
|
88
|
+
result[e.effect_id] = e
|
89
|
+
end
|
90
|
+
result
|
91
|
+
end
|
92
|
+
|
93
|
+
def buffs_from_proto(buffs)
|
94
|
+
result = {}
|
95
|
+
buffs.each do |b|
|
96
|
+
result[b.buff_id] = b
|
97
|
+
end
|
98
|
+
result
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
@@ -23,8 +23,6 @@ module Sc2
|
|
23
23
|
# end
|
24
24
|
end
|
25
25
|
|
26
|
-
# TODO: DEFINE REALTIME AS A PARAM
|
27
|
-
|
28
26
|
# @!attribute players Sets the Player(s) for the match
|
29
27
|
# @return [Array<Sc2::Player>] an array of assigned players (ai,bots,humans,observers)
|
30
28
|
attr_accessor :players
|
@@ -66,7 +64,7 @@ module Sc2
|
|
66
64
|
connect_players
|
67
65
|
setup_player_hooks
|
68
66
|
|
69
|
-
player_host.create_game(map:, players: @players)
|
67
|
+
player_host.create_game(map:, players: @players, realtime: player_host.realtime)
|
70
68
|
|
71
69
|
api_players.each_with_index do |player, player_index|
|
72
70
|
run_task.async do
|
data/lib/sc2ai/player/actions.rb
CHANGED
@@ -45,9 +45,12 @@ module Sc2
|
|
45
45
|
|
46
46
|
target_pos = nil
|
47
47
|
target_unit_tag = nil
|
48
|
-
|
48
|
+
case target
|
49
|
+
when Api::Point2D
|
49
50
|
target_pos = target
|
50
|
-
|
51
|
+
when Api::Point
|
52
|
+
target_pos = target.to_p2d
|
53
|
+
when Api::Unit
|
51
54
|
target_unit_tag = target.tag
|
52
55
|
else
|
53
56
|
target_unit_tag = target
|
@@ -65,7 +68,7 @@ module Sc2
|
|
65
68
|
|
66
69
|
# Builds target unit type using units as source at optional target
|
67
70
|
# @param units [Array<Integer>,Integer,Api::Unit] can be an Api::Unit, array of Api::Unit#tag or single tag
|
68
|
-
# @param unit_type_id [Integer] Api::UnitTypeId the unit type
|
71
|
+
# @param unit_type_id [Integer] Api::UnitTypeId the unit type you wish to build
|
69
72
|
# @param target [Api::Point2D, Integer, nil] is a unit tag or a Api::Point2D. Nil for addons/orbital
|
70
73
|
# @param queue_command [Boolean] shift+command
|
71
74
|
def build(units:, unit_type_id:, target: nil, queue_command: false)
|
@@ -79,9 +82,10 @@ module Sc2
|
|
79
82
|
|
80
83
|
# Warps in unit type at target (location or pylon) with optional source units (warp gates)
|
81
84
|
# When not specifying the specific warp gate(s), all warpgates will be used
|
82
|
-
# @param unit_type_id [Integer] Api::UnitTypeId the unit type
|
85
|
+
# @param unit_type_id [Integer] Api::UnitTypeId the unit type you wish to build
|
83
86
|
# @param queue_command [Boolean] shift+command
|
84
87
|
# @param target [Api::Point2D, Integer] is a unit tag or a Api::Point2D
|
88
|
+
# @param units [Array<Integer>, Integer, Api::Unit]
|
85
89
|
def warp(unit_type_id:, target:, queue_command:, units: nil)
|
86
90
|
warp_ability = Api::TechTree.unit_type_creation_abilities(
|
87
91
|
source: Api::UnitTypeId::WARPGATE,
|
data/lib/sc2ai/player/debug.rb
CHANGED
@@ -133,7 +133,7 @@ module Sc2
|
|
133
133
|
# Debug draws a sphere at position with a radius in color
|
134
134
|
# @param point [Api::Point]
|
135
135
|
# @param radius [Float] default one tile wide, 1.0
|
136
|
-
# @param color [Api::Color] default white
|
136
|
+
# @param color [Api::Color] default white
|
137
137
|
# @return [void]
|
138
138
|
def debug_draw_sphere(point:, radius: 1.0, color: nil)
|
139
139
|
queue_debug_command Api::DebugCommand.new(
|
@@ -151,14 +151,13 @@ module Sc2
|
|
151
151
|
|
152
152
|
# Other Commands ---
|
153
153
|
|
154
|
-
# Toggles cheat commands on/off (send only once to enable)
|
155
154
|
# @param command [Integer] one of Api::DebugGameState::*
|
156
155
|
# Possible values:
|
157
156
|
# Api::DebugGameState::Show_map
|
158
157
|
# Api::DebugGameState::Control_enemy
|
159
158
|
# Api::DebugGameState::Food
|
160
159
|
# Api::DebugGameState::Free
|
161
|
-
# Api::DebugGameState::
|
160
|
+
# Api::DebugGameState::All_resources
|
162
161
|
# Api::DebugGameState::God
|
163
162
|
# Api::DebugGameState::Minerals
|
164
163
|
# Api::DebugGameState::Gas
|
@@ -106,6 +106,34 @@ module Sc2
|
|
106
106
|
@chats_received || []
|
107
107
|
end
|
108
108
|
|
109
|
+
# @private
|
110
|
+
# @!attribute available_abilities_loop
|
111
|
+
# This is the last loop at which available_abilities was queried.
|
112
|
+
# Used to determine staleness.
|
113
|
+
# @return [Integer]
|
114
|
+
attr_accessor :available_abilities_loop
|
115
|
+
private :available_abilities_loop
|
116
|
+
|
117
|
+
# A Hash by unit tag, holding an array of available ability ids
|
118
|
+
# Synchronously calls RequestQueryAvailableAbilities and caches for this game loop.
|
119
|
+
# @return [Hash<Integer, Array<Integer>>] { unit_tag => [ability_id, ...], ... }
|
120
|
+
def available_abilities
|
121
|
+
# Save/check when last we refreshed abilities
|
122
|
+
if @available_abilities_loop != game_loop
|
123
|
+
|
124
|
+
# Query abilities for all our units + structure tags combined
|
125
|
+
abilities = api.query_abilities_for_unit_tags(units.tags + structures.tags, ignore_resource_requirements: false)
|
126
|
+
# Build the hash by unit tag
|
127
|
+
fresh_available_abilities = {}
|
128
|
+
abilities.each do |row|
|
129
|
+
fresh_available_abilities[row.unit_tag] = row.abilities.map(&:ability_id)
|
130
|
+
end
|
131
|
+
@available_abilities = fresh_available_abilities
|
132
|
+
@available_abilities_loop = game_loop
|
133
|
+
end
|
134
|
+
@available_abilities
|
135
|
+
end
|
136
|
+
|
109
137
|
# An alias for observation.player_common to allow easier access to i.e. common.minerals
|
110
138
|
# @return [Api::PlayerCommon] common info such as minerals, vespene, supply
|
111
139
|
def common
|
@@ -31,6 +31,12 @@ module Sc2
|
|
31
31
|
@map_height ||= bot.game_info.start_raw.map_size.y
|
32
32
|
end
|
33
33
|
|
34
|
+
# Center of the map
|
35
|
+
# @return [Api::Point2D]
|
36
|
+
def map_center
|
37
|
+
@map_center ||= Api::Point2D[map_width / 2, map_height / 2]
|
38
|
+
end
|
39
|
+
|
34
40
|
# Returns zero to map_width as range
|
35
41
|
# @return [Range] 0 to map_width
|
36
42
|
def map_range_x
|
@@ -100,8 +106,16 @@ module Sc2
|
|
100
106
|
expansion_points.each do |point|
|
101
107
|
x = point.x.floor
|
102
108
|
y = point.y.floor
|
103
|
-
|
104
|
-
|
109
|
+
|
110
|
+
# For zerg, reserve a layer at the bottom for larva->egg
|
111
|
+
if bot.race == Api::Race::Zerg
|
112
|
+
# Reserve one row lower, meaning (y-3) instead of (y-2)
|
113
|
+
@expo_placement_grid[(y - 3).clamp(map_tile_range_y)..(y + 2).clamp(map_tile_range_y),
|
114
|
+
(x - 2).clamp(map_tile_range_y)..(x + 2).clamp(map_tile_range_y)] = 1
|
115
|
+
else
|
116
|
+
@expo_placement_grid[(y - 2).clamp(map_tile_range_y)..(y + 2).clamp(map_tile_range_y),
|
117
|
+
(x - 2).clamp(map_tile_range_y)..(x + 2).clamp(map_tile_range_y)] = 1
|
118
|
+
end
|
105
119
|
end
|
106
120
|
end
|
107
121
|
@expo_placement_grid
|
@@ -121,40 +135,11 @@ module Sc2
|
|
121
135
|
return result
|
122
136
|
end
|
123
137
|
|
124
|
-
radius = power_source.radius
|
125
|
-
radius_tile = radius.ceil
|
126
|
-
|
127
|
-
# Keep this code-block, should we need to make power sources dynamic again:
|
128
|
-
# START: Dynamic blueprint
|
129
|
-
# # Build a blueprint and mark it everywhere we need to
|
130
|
-
# # Lets mark everything as powered with 1 and then disable non-powered with a 0
|
131
|
-
# blueprint = Numo::Bit.ones(radius.ceil * 2, radius.ceil * 2)
|
132
|
-
# #blueprint[radius_tile, radius_tile] = 0
|
133
|
-
# blueprint[(radius_tile - 1)..radius_tile, (radius_tile - 1)..radius_tile] = 0
|
134
|
-
# # Loop over top-right quadrant of a circle, so we don't have to +/- for distance calcs.
|
135
|
-
# # Additionally, we only measure if in the upper triangle, since the inner is all inside the circle.
|
136
|
-
# # Then apply to all four quadrants.
|
137
|
-
# quadrant_size = radius_tile - 1
|
138
|
-
# point_search_offsets = (0..quadrant_size).to_a.product((0..quadrant_size).to_a)
|
139
|
-
# point_search_offsets.each do |y, x|
|
140
|
-
# next if x < quadrant_size - y # Only upper Triangle
|
141
|
-
#
|
142
|
-
# dist = Math.hypot(x, y)
|
143
|
-
# if dist >= radius
|
144
|
-
# # Mark as outside x4
|
145
|
-
# blueprint[radius_tile + y, radius_tile + x] = 0
|
146
|
-
# blueprint[radius_tile + y, radius_tile - 1 - x] = 0
|
147
|
-
# blueprint[radius_tile - 1 - y, radius_tile + x] = 0
|
148
|
-
# blueprint[radius_tile - 1 - y, radius_tile - 1 - x] = 0
|
149
|
-
# end
|
150
|
-
# end
|
151
|
-
# END: Dynamic blueprint ---
|
152
|
-
|
153
138
|
# Hard-coding this shape for pylon power
|
154
139
|
# 00001111110000
|
140
|
+
# 00011111111000
|
155
141
|
# 00111111111100
|
156
142
|
# 01111111111110
|
157
|
-
# 01111111111110
|
158
143
|
# 11111111111111
|
159
144
|
# 11111111111111
|
160
145
|
# 11111100111111
|
@@ -162,15 +147,35 @@ module Sc2
|
|
162
147
|
# 11111111111111
|
163
148
|
# 11111111111111
|
164
149
|
# 01111111111110
|
165
|
-
# 01111111111110
|
166
150
|
# 00111111111100
|
151
|
+
# 00011111111000
|
167
152
|
# 00001111110000
|
168
|
-
|
169
153
|
# perf: Saving pre-created shape for speed (0.5ms saved) by using hardcode from .to_binary.unpack("C*")
|
170
|
-
blueprint_data = [
|
171
|
-
|
172
|
-
|
154
|
+
blueprint_data = [0, 0, 254, 193, 255, 248, 127, 254, 159, 255, 231, 243, 249, 124, 254, 159, 255, 231, 255, 241, 63, 248, 7, 0, 0].pack("C*")
|
155
|
+
blueprint_pylon = ::Numo::Bit.from_binary(blueprint_data, [14, 14])
|
156
|
+
|
157
|
+
# Warp Prism
|
158
|
+
# 00011000
|
159
|
+
# 01111110
|
160
|
+
# 01111110
|
161
|
+
# 11111111
|
162
|
+
# 11111111
|
163
|
+
# 01111110
|
164
|
+
# 01111110
|
165
|
+
# 00011000
|
166
|
+
blueprint_data = [24, 126, 126, 255, 255, 126, 126, 24].pack("C*")
|
167
|
+
blueprint_prism = ::Numo::Bit.from_binary(blueprint_data, [8, 8])
|
168
|
+
|
169
|
+
# Print each power-source on map using shape above
|
173
170
|
bot.power_sources.each do |ps|
|
171
|
+
radius_tile = ps.radius.ceil
|
172
|
+
# Select blueprint for 7-tile radius (Pylon) or 4-tile radius (Prism)
|
173
|
+
blueprint = if radius_tile == 4
|
174
|
+
blueprint_prism
|
175
|
+
else
|
176
|
+
blueprint_pylon
|
177
|
+
end
|
178
|
+
|
174
179
|
x_tile = ps.pos.x.floor
|
175
180
|
y_tile = ps.pos.y.floor
|
176
181
|
replace_start_x = (x_tile - radius_tile)
|
@@ -424,7 +429,7 @@ module Sc2
|
|
424
429
|
# This differs from position of first base structure
|
425
430
|
# @return [Api::Point2D]
|
426
431
|
def start_position
|
427
|
-
@start_position ||= bot.observation.raw_data.player.camera
|
432
|
+
@start_position ||= bot.observation.raw_data.player.camera.to_p2d
|
428
433
|
end
|
429
434
|
|
430
435
|
# Returns the enemy 2d start position
|
@@ -751,7 +756,7 @@ module Sc2
|
|
751
756
|
)
|
752
757
|
end
|
753
758
|
nearest = @_build_coordinate_tree[cache_key].nearestk(target.x, target.y, random)
|
754
|
-
return nil if nearest.nil?
|
759
|
+
return nil if nearest.nil? || nearest.empty?
|
755
760
|
|
756
761
|
coordinates[nearest.sample].to_p2d
|
757
762
|
end
|
data/lib/sc2ai/player/units.rb
CHANGED
@@ -15,9 +15,14 @@ module Sc2
|
|
15
15
|
|
16
16
|
# A full list of all your structures (non-units)
|
17
17
|
# @!attribute units
|
18
|
-
# @return [Sc2::UnitGroup] a group of
|
18
|
+
# @return [Sc2::UnitGroup] a group of structures
|
19
19
|
attr_accessor :structures
|
20
20
|
|
21
|
+
# A list of structures which haven't started
|
22
|
+
# @!attribute units
|
23
|
+
# @return [Sc2::UnitGroup] a group of placeholder structures
|
24
|
+
attr_accessor :placeholders
|
25
|
+
|
21
26
|
# All units with alliance :Neutral
|
22
27
|
# @!attribute neutral
|
23
28
|
# @return [Sc2::UnitGroup] a group of neutral units
|
@@ -125,41 +130,17 @@ module Sc2
|
|
125
130
|
|
126
131
|
# An array of Sensor tower rings as per minimap. It has a `pos` and a `radius`
|
127
132
|
# @!attribute power_sources
|
128
|
-
# @return [Array<Api::RadarRing>] an array of
|
133
|
+
# @return [Array<Api::RadarRing>] an array of radar rings sources
|
129
134
|
attr_accessor :radar_rings # not a unit but has a tag
|
130
135
|
|
131
136
|
# @private
|
132
137
|
# @!attribute all_seen_unit_tags
|
133
138
|
# Privately keep track of all seen Unit tags (excl structures) in order to detect new created units
|
134
139
|
attr_accessor :_all_seen_unit_tags
|
140
|
+
private :_all_seen_unit_tags
|
135
141
|
|
136
142
|
# Event-driven unit groups ---
|
137
143
|
|
138
|
-
# @!attribute event_units_created
|
139
|
-
# Units created since last frame (visible only, units not structures)
|
140
|
-
# Read this on_step. Alternative to callback on_unit_created
|
141
|
-
# @note Morphed units should watch #event_units_type_changed
|
142
|
-
# @return [Sc2::UnitGroup] group of created units
|
143
|
-
attr_accessor :event_units_created
|
144
|
-
|
145
|
-
# Units which had their type changed since last frame
|
146
|
-
# Read this on_step. Alternative to callback on_unit_type_changed
|
147
|
-
# @!attribute event_units_type_changed
|
148
|
-
# @return [Sc2::UnitGroup] group effected
|
149
|
-
attr_accessor :event_units_type_changed
|
150
|
-
|
151
|
-
# Structures seen since last frame with building not completed (< 1.0)
|
152
|
-
# Read this on_step. Alternative to callback on_structure_started
|
153
|
-
# @!attribute event_structures_started
|
154
|
-
# @return [Sc2::UnitGroup] a group of structures started
|
155
|
-
attr_accessor :event_structures_started
|
156
|
-
|
157
|
-
# Structures which had their building completed (==1.0) since last frame
|
158
|
-
# Read this on_step. Alternative to callback on_structure_completed
|
159
|
-
# @!attribute event_structures_completed
|
160
|
-
# @return [Sc2::UnitGroup] a group of structures started
|
161
|
-
attr_accessor :event_structures_completed
|
162
|
-
|
163
144
|
# Units and Structures which had their health/shields reduced since last frame
|
164
145
|
# Read this on_step. Alternative to callback on_unit_damaged
|
165
146
|
# @!attribute event_units_damaged
|
@@ -288,6 +269,17 @@ module Sc2
|
|
288
269
|
true
|
289
270
|
end
|
290
271
|
|
272
|
+
# Micro/Unit-Specific ------
|
273
|
+
|
274
|
+
# Returns whether Query Available Ability is true for unit and tag
|
275
|
+
# Queries API if necessary. Uses batching in the background.
|
276
|
+
# @param [Integer] unit_tag
|
277
|
+
# @param [Integer] ability_id
|
278
|
+
# @return [Boolean]
|
279
|
+
def unit_ability_available?(unit_tag:, ability_id:)
|
280
|
+
!!available_abilities[unit_tag]&.include?(ability_id)
|
281
|
+
end
|
282
|
+
|
291
283
|
private
|
292
284
|
|
293
285
|
# @private
|
@@ -299,8 +291,10 @@ module Sc2
|
|
299
291
|
# Clear previous units and prep for categorization
|
300
292
|
@units = UnitGroup.new
|
301
293
|
@structures = UnitGroup.new
|
294
|
+
@placeholders = UnitGroup.new
|
302
295
|
@enemy.units = UnitGroup.new
|
303
296
|
@enemy.structures = UnitGroup.new
|
297
|
+
@enemy.all_units = UnitGroup.new
|
304
298
|
@neutral = UnitGroup.new
|
305
299
|
@effects = observation.raw_data.effects # not a unit
|
306
300
|
@power_sources = observation.raw_data.player.power_sources # not a unit
|
@@ -311,10 +305,6 @@ module Sc2
|
|
311
305
|
@_all_seen_unit_tags ||= Set.new(@units.tags)
|
312
306
|
|
313
307
|
# Event-driven unit groups as callback alternatives
|
314
|
-
@event_units_created = UnitGroup.new
|
315
|
-
@event_structures_started = UnitGroup.new
|
316
|
-
@event_structures_completed = UnitGroup.new
|
317
|
-
@event_units_type_changed = UnitGroup.new
|
318
308
|
@event_units_damaged = UnitGroup.new
|
319
309
|
# @event_units_buffed = UnitGroup.new
|
320
310
|
|
@@ -335,6 +325,8 @@ module Sc2
|
|
335
325
|
# Categorize own units/structures, enemy units/structures, neutral
|
336
326
|
if unit.is_blip
|
337
327
|
@blips[tag] = unit
|
328
|
+
elsif unit.display_type == :Placeholder
|
329
|
+
@placeholders[tag] = unit
|
338
330
|
elsif unit.alliance == own_alliance || unit.alliance == enemy_alliance
|
339
331
|
if unit.alliance == own_alliance
|
340
332
|
structure_collection = @structures
|
@@ -342,6 +334,7 @@ module Sc2
|
|
342
334
|
else
|
343
335
|
structure_collection = @enemy.structures
|
344
336
|
unit_collection = @enemy.units
|
337
|
+
@enemy.all_units[tag] = unit
|
345
338
|
end
|
346
339
|
|
347
340
|
unit_data = unit_data(unit.unit_type)
|
@@ -355,7 +348,7 @@ module Sc2
|
|
355
348
|
end
|
356
349
|
|
357
350
|
# Dont parse callbacks on first loop or for neutral units
|
358
|
-
if
|
351
|
+
if !@previous.all_units.nil? &&
|
359
352
|
unit.alliance != :Neutral &&
|
360
353
|
unit.display_type != :Placeholder &&
|
361
354
|
unit.is_blip == false
|
@@ -406,14 +399,11 @@ module Sc2
|
|
406
399
|
|
407
400
|
if unit.is_structure?
|
408
401
|
if unit.build_progress < 1
|
409
|
-
@event_structures_started.add(unit)
|
410
402
|
on_structure_started(unit)
|
411
403
|
else
|
412
|
-
@event_structures_completed.add(unit)
|
413
404
|
on_structure_completed(unit)
|
414
405
|
end
|
415
406
|
else
|
416
|
-
@event_units_created.add(unit)
|
417
407
|
on_unit_created(unit)
|
418
408
|
end
|
419
409
|
@_all_seen_unit_tags.add(unit.tag)
|
@@ -424,7 +414,6 @@ module Sc2
|
|
424
414
|
def issue_existing_unit_callbacks(unit, previous_unit)
|
425
415
|
# Check if a unit type has changed
|
426
416
|
if unit.unit_type != previous_unit.unit_type
|
427
|
-
@event_units_type_changed.add(unit)
|
428
417
|
on_unit_type_changed(unit, previous_unit.unit_type)
|
429
418
|
end
|
430
419
|
|
@@ -437,7 +426,6 @@ module Sc2
|
|
437
426
|
|
438
427
|
if unit.is_structure?
|
439
428
|
if previous_unit.build_progress < 1 && unit.build_progress == 1
|
440
|
-
@event_structures_completed.add(unit)
|
441
429
|
on_structure_completed(unit)
|
442
430
|
end
|
443
431
|
end
|
data/lib/sc2ai/player.rb
CHANGED
@@ -233,7 +233,7 @@ module Sc2
|
|
233
233
|
perform_actions
|
234
234
|
perform_debug_commands # TODO: Detect IS_LADDER? -> unless IS_LADDER?
|
235
235
|
step_forward
|
236
|
-
print "\e[2K#{@
|
236
|
+
print "\e[2K#{game_loop - @previous.game_loop} Steps Took (ms): #{(::Process.clock_gettime(::Process::CLOCK_MONOTONIC) - r) * 1000}\n\e[1A\r"
|
237
237
|
return @result unless @result.nil?
|
238
238
|
break if @status != :in_game
|
239
239
|
end
|
@@ -329,7 +329,7 @@ module Sc2
|
|
329
329
|
|
330
330
|
# Callback for unit destroyed. Tags might be found in `previous.all_units`
|
331
331
|
# This excludes unknown objects, like projectiles and only shows things the API has "seen" as a unit
|
332
|
-
# Override to use in your bot class or use Player.
|
332
|
+
# Override to use in your bot class or use Player.
|
333
333
|
# @param unit [Api::Unit]
|
334
334
|
# @see Sc2::Player::Units#units_destroyed
|
335
335
|
def on_unit_destroyed(unit)
|
@@ -343,7 +343,7 @@ module Sc2
|
|
343
343
|
|
344
344
|
# Callback for unit type changing.
|
345
345
|
# To detect certain unit creations, you should use this method to watch morphs.
|
346
|
-
# Override to use in your bot class or use Player.
|
346
|
+
# Override to use in your bot class or use Player.
|
347
347
|
# @param unit [Api::Unit]
|
348
348
|
# @param previous_unit_type_id [Integer] Api::UnitTypeId::*
|
349
349
|
def on_unit_type_changed(unit, previous_unit_type_id)
|
@@ -356,13 +356,13 @@ module Sc2
|
|
356
356
|
end
|
357
357
|
|
358
358
|
# Callback for structure building is completed
|
359
|
-
# Override to use in your bot class or use Player.
|
359
|
+
# Override to use in your bot class or use Player.
|
360
360
|
# @param unit [Api::Unit]
|
361
361
|
def on_structure_completed(unit)
|
362
362
|
end
|
363
363
|
|
364
364
|
# Callback for unit (Unit/Structure) taking damage
|
365
|
-
# Override to use in your bot class or use Player.
|
365
|
+
# Override to use in your bot class or use Player.
|
366
366
|
# @param unit [Api::Unit]
|
367
367
|
# @param amount [Integer] of damage
|
368
368
|
def on_unit_damaged(unit, amount)
|
@@ -549,7 +549,7 @@ module Sc2
|
|
549
549
|
# Having loaded all the necessities for the current state...
|
550
550
|
# If we're on the first frame of the game, say previous state and current are the same
|
551
551
|
# This is better than having a bunch of random zero and nil values
|
552
|
-
@previous.reset(self) if
|
552
|
+
@previous.reset(self) if @previous.all_units.nil?
|
553
553
|
|
554
554
|
# TODO: remove @events attributes if we don't use them for performance gains
|
555
555
|
# Actions performed and errors (only if implemented)
|
@@ -586,7 +586,7 @@ module Sc2
|
|
586
586
|
self.game_info = @api.game_info
|
587
587
|
end
|
588
588
|
|
589
|
-
#
|
589
|
+
# Enemy -----------------------
|
590
590
|
|
591
591
|
# If you're random, best to set #race to match after launched
|
592
592
|
def set_race_for_random
|