sc2ai 0.0.6 → 0.0.7
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.
- 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 -3
- data/lib/sc2ai/player.rb +3 -3
- 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 +383 -509
- metadata +20 -17
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,13 +130,14 @@ 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
|
|
@@ -288,6 +294,17 @@ module Sc2
|
|
288
294
|
true
|
289
295
|
end
|
290
296
|
|
297
|
+
# Micro/Unit-Specific ------
|
298
|
+
|
299
|
+
# Returns whether Query Available Ability is true for unit and tag
|
300
|
+
# Queries API if necessary. Uses batching in the background.
|
301
|
+
# @param [Integer] unit_tag
|
302
|
+
# @param [Integer] ability_id
|
303
|
+
# @return [Boolean]
|
304
|
+
def unit_ability_available?(unit_tag:, ability_id:)
|
305
|
+
!!available_abilities[unit_tag]&.include?(ability_id)
|
306
|
+
end
|
307
|
+
|
291
308
|
private
|
292
309
|
|
293
310
|
# @private
|
@@ -299,8 +316,10 @@ module Sc2
|
|
299
316
|
# Clear previous units and prep for categorization
|
300
317
|
@units = UnitGroup.new
|
301
318
|
@structures = UnitGroup.new
|
319
|
+
@placeholders = UnitGroup.new
|
302
320
|
@enemy.units = UnitGroup.new
|
303
321
|
@enemy.structures = UnitGroup.new
|
322
|
+
@enemy.all_units = UnitGroup.new
|
304
323
|
@neutral = UnitGroup.new
|
305
324
|
@effects = observation.raw_data.effects # not a unit
|
306
325
|
@power_sources = observation.raw_data.player.power_sources # not a unit
|
@@ -335,6 +354,8 @@ module Sc2
|
|
335
354
|
# Categorize own units/structures, enemy units/structures, neutral
|
336
355
|
if unit.is_blip
|
337
356
|
@blips[tag] = unit
|
357
|
+
elsif unit.display_type == :Placeholder
|
358
|
+
@placeholders[tag] = unit
|
338
359
|
elsif unit.alliance == own_alliance || unit.alliance == enemy_alliance
|
339
360
|
if unit.alliance == own_alliance
|
340
361
|
structure_collection = @structures
|
@@ -342,6 +363,7 @@ module Sc2
|
|
342
363
|
else
|
343
364
|
structure_collection = @enemy.structures
|
344
365
|
unit_collection = @enemy.units
|
366
|
+
@enemy.all_units[tag] = unit
|
345
367
|
end
|
346
368
|
|
347
369
|
unit_data = unit_data(unit.unit_type)
|
@@ -355,7 +377,7 @@ module Sc2
|
|
355
377
|
end
|
356
378
|
|
357
379
|
# Dont parse callbacks on first loop or for neutral units
|
358
|
-
if
|
380
|
+
if !@previous.all_units.nil? &&
|
359
381
|
unit.alliance != :Neutral &&
|
360
382
|
unit.display_type != :Placeholder &&
|
361
383
|
unit.is_blip == false
|
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
|
@@ -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
|
@@ -23,8 +23,26 @@
|
|
23
23
|
# class Unit < Google::Protobuf::AbstractMessage; end;
|
24
24
|
# # Protobuf virtual class.
|
25
25
|
# class UnitTypeData < Google::Protobuf::AbstractMessage; end;
|
26
|
+
# # Protobuf virtual class.
|
27
|
+
# class AvailableAbility < Google::Protobuf::AbstractMessage; end;
|
28
|
+
# # Protobuf virtual class.
|
29
|
+
# class UnitOrder < Google::Protobuf::AbstractMessage; end;
|
30
|
+
# # Protobuf virtual class.
|
31
|
+
# class ActionRawUnitCommand < Google::Protobuf::AbstractMessage; end;
|
32
|
+
# # Protobuf virtual class.
|
33
|
+
# class ActionRawToggleAutocast < Google::Protobuf::AbstractMessage; end;
|
34
|
+
# # Protobuf virtual class.
|
35
|
+
# class ActionError < Google::Protobuf::AbstractMessage; end;
|
36
|
+
# # Protobuf virtual class.
|
37
|
+
# class ActionSpatialUnitCommand < Google::Protobuf::AbstractMessage; end;
|
38
|
+
# # Protobuf virtual class.
|
39
|
+
# class BuildItem < Google::Protobuf::AbstractMessage; end;
|
40
|
+
# # Protobuf virtual class.
|
41
|
+
# class ActionToggleAutocast < Google::Protobuf::AbstractMessage; end;
|
26
42
|
# end
|
27
43
|
|
44
|
+
|
45
|
+
|
28
46
|
# Protobuf enums ---
|
29
47
|
# Protobuf classes
|
30
48
|
# @!parse
|
@@ -0,0 +1,11 @@
|
|
1
|
+
# This class was partially generated with the help of AI.
|
2
|
+
|
3
|
+
module Api
|
4
|
+
# Adds additional functionality to message object Api::Unit
|
5
|
+
module PointDistanceExtension
|
6
|
+
end
|
7
|
+
end
|
8
|
+
Api::Point.include Api::PointDistanceExtension
|
9
|
+
Api::Point2D.include Api::PointDistanceExtension
|
10
|
+
Api::PointI.include Api::PointDistanceExtension
|
11
|
+
Api::Size2DI.include Api::PointDistanceExtension
|
@@ -84,7 +84,6 @@ module Sc2
|
|
84
84
|
# raise NoMethodError unless location == self
|
85
85
|
|
86
86
|
# Perf: Memoizing attributes which are hit hard, show gain
|
87
|
-
|
88
87
|
@y ||= send(:method_missing, :y)
|
89
88
|
end
|
90
89
|
|
@@ -97,9 +96,9 @@ module Sc2
|
|
97
96
|
|
98
97
|
# Randomly adjusts both x and y by a range of: -offset..offset
|
99
98
|
# @param offset [Float]
|
100
|
-
# @return [
|
99
|
+
# @return [Sc2::Position] new Position
|
101
100
|
def random_offset(offset)
|
102
|
-
|
101
|
+
dup.random_offset!(offset)
|
103
102
|
end
|
104
103
|
|
105
104
|
# Changes this point's x and y by the supplied offset
|
@@ -112,17 +111,16 @@ module Sc2
|
|
112
111
|
end
|
113
112
|
|
114
113
|
# Creates a new point with x and y which is offset
|
115
|
-
# @return [
|
116
|
-
def offset(x, y)
|
117
|
-
|
118
|
-
self
|
114
|
+
# @return [Sc2::Position] new Position
|
115
|
+
def offset(x = 0, y = 0)
|
116
|
+
dup.offset!(x, y)
|
119
117
|
end
|
120
118
|
|
121
119
|
# Changes this point's x and y by the supplied offset
|
122
120
|
# @return [Sc2::Position] self
|
123
|
-
def offset!(x, y)
|
124
|
-
self.x
|
125
|
-
self.y
|
121
|
+
def offset!(x = 0, y = 0)
|
122
|
+
self.x += x
|
123
|
+
self.y += y
|
126
124
|
self
|
127
125
|
end
|
128
126
|
|