sc2ai 0.1.0 → 0.3.0
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/sc2ai/protocol/common.proto +6 -6
- data/data/sc2ai/protocol/data.proto +23 -20
- data/data/sc2ai/protocol/debug.proto +25 -21
- data/data/sc2ai/protocol/error.proto +217 -215
- data/data/sc2ai/protocol/query.proto +1 -1
- data/data/sc2ai/protocol/raw.proto +16 -14
- data/data/sc2ai/protocol/sc2api.proto +108 -94
- data/data/sc2ai/protocol/score.proto +4 -3
- data/data/sc2ai/protocol/spatial.proto +6 -5
- data/data/sc2ai/protocol/ui.proto +17 -14
- data/exe/sc2ai +0 -3
- data/lib/docker_build/Dockerfile.ruby +4 -2
- data/lib/sc2ai/api/ability_id.rb +6 -1
- data/lib/sc2ai/api/data.rb +18 -3
- data/lib/sc2ai/api/tech_tree.rb +1 -1
- data/lib/sc2ai/api/tech_tree_data.rb +54 -3
- data/lib/sc2ai/connection/connection_listener.rb +3 -3
- data/lib/sc2ai/connection/requests.rb +31 -35
- data/lib/sc2ai/connection/status_listener.rb +1 -1
- data/lib/sc2ai/connection.rb +1 -2
- data/lib/sc2ai/local_play/client.rb +2 -2
- data/lib/sc2ai/local_play/match.rb +7 -2
- data/lib/sc2ai/overrides/async/process/child.rb +1 -1
- data/lib/sc2ai/paths.rb +12 -2
- data/lib/sc2ai/player/actions.rb +54 -35
- data/lib/sc2ai/player/debug.rb +54 -20
- data/lib/sc2ai/player/game_state.rb +11 -18
- data/lib/sc2ai/player/geo.rb +56 -66
- data/lib/sc2ai/player/units.rb +41 -17
- data/lib/sc2ai/player.rb +104 -47
- data/lib/sc2ai/ports.rb +1 -2
- data/lib/sc2ai/protocol/_meta_documentation.rb +270 -25
- data/lib/sc2ai/protocol/common_pb.rb +3862 -33
- data/lib/sc2ai/protocol/data_pb.rb +9106 -36
- data/lib/sc2ai/protocol/debug_pb.rb +10434 -45
- data/lib/sc2ai/protocol/error_pb.rb +1084 -29
- data/lib/sc2ai/protocol/extensions/ability_remapable.rb +9 -9
- data/lib/sc2ai/protocol/extensions/action.rb +60 -0
- data/lib/sc2ai/protocol/extensions/point_2_d.rb +9 -0
- data/lib/sc2ai/protocol/extensions/position.rb +11 -36
- data/lib/sc2ai/protocol/extensions/power_source.rb +3 -0
- data/lib/sc2ai/protocol/extensions/unit.rb +61 -36
- data/lib/sc2ai/protocol/extensions/unit_type_data.rb +8 -0
- data/lib/sc2ai/protocol/query_pb.rb +5022 -36
- data/lib/sc2ai/protocol/raw_pb.rb +18347 -46
- data/lib/sc2ai/protocol/sc2api_pb.rb +48424 -126
- data/lib/sc2ai/protocol/score_pb.rb +5965 -30
- data/lib/sc2ai/protocol/spatial_pb.rb +11941 -37
- data/lib/sc2ai/protocol/ui_pb.rb +12924 -46
- data/lib/sc2ai/unit_group/action_ext.rb +0 -2
- data/lib/sc2ai/unit_group/filter_ext.rb +24 -8
- data/lib/sc2ai/unit_group/geo_ext.rb +0 -2
- data/lib/sc2ai/unit_group.rb +1 -1
- data/lib/sc2ai/version.rb +2 -3
- data/lib/sc2ai.rb +10 -11
- data/lib/templates/ladderzip/bin/ladder.tt +0 -3
- data/lib/templates/new/.ladderignore +15 -5
- data/lib/templates/new/api/common.proto +6 -6
- data/lib/templates/new/api/data.proto +23 -20
- data/lib/templates/new/api/debug.proto +25 -21
- data/lib/templates/new/api/error.proto +217 -215
- data/lib/templates/new/api/query.proto +1 -1
- data/lib/templates/new/api/raw.proto +16 -14
- data/lib/templates/new/api/sc2api.proto +108 -94
- data/lib/templates/new/api/score.proto +4 -3
- data/lib/templates/new/api/spatial.proto +6 -5
- data/lib/templates/new/api/ui.proto +17 -14
- data/lib/templates/new/boot.rb.tt +1 -1
- data/lib/templates/new/my_bot.rb.tt +2 -2
- data/lib/templates/new/run_example_match.rb.tt +2 -2
- data/sig/sc2ai.rbs +11072 -1651
- metadata +31 -26
- data/lib/sc2ai/overrides/kernel.rb +0 -33
- data/lib/sc2ai/protocol/extensions/unit_type.rb +0 -9
data/lib/sc2ai/player/debug.rb
CHANGED
@@ -22,7 +22,7 @@ module Sc2
|
|
22
22
|
|
23
23
|
# Prints debug text top left corner
|
24
24
|
# @param text [String] will respect newlines
|
25
|
-
# @param size [
|
25
|
+
# @param size [Integer] of font, default 14px
|
26
26
|
# @return [void]
|
27
27
|
def debug_print(text, size: 14)
|
28
28
|
queue_debug_command Api::DebugCommand.new(
|
@@ -43,7 +43,7 @@ module Sc2
|
|
43
43
|
# @param left_percent [Numeric] range 0..100. percent from left of screen
|
44
44
|
# @param top_percent [Numeric] range 0..100. percent from top of screen
|
45
45
|
# @param color [Api::Color] default white
|
46
|
-
# @param size [
|
46
|
+
# @param size [Integer] of font, default 14px
|
47
47
|
# @return [void]
|
48
48
|
def debug_text_screen(text, left_percent: 1.0, top_percent: 1.0, color: nil, size: 14)
|
49
49
|
queue_debug_command Api::DebugCommand.new(
|
@@ -67,7 +67,7 @@ module Sc2
|
|
67
67
|
# @param text [String] will respect newlines
|
68
68
|
# @param point [Api::Point] point in the world, i.e. unit.pos
|
69
69
|
# @param color [Api::Color] default white
|
70
|
-
# @param size [
|
70
|
+
# @param size [Integer] of font, default 14px
|
71
71
|
# @return [void]
|
72
72
|
def debug_text_world(text, point:, color: nil, size: 14)
|
73
73
|
queue_debug_command Api::DebugCommand.new(
|
@@ -121,8 +121,8 @@ module Sc2
|
|
121
121
|
draw: Api::DebugDraw.new(
|
122
122
|
boxes: [
|
123
123
|
Api::DebugBox.new(
|
124
|
-
min: Api::Point.new(x: point.x - radius, y: point.y - radius, z: point.z + 0.
|
125
|
-
max: Api::Point.new(x: point.x + radius, y: point.y + radius, z: point.z + (radius * 2) + 0.
|
124
|
+
min: Api::Point.new(x: point.x - radius, y: point.y - radius, z: point.z + 0.03),
|
125
|
+
max: Api::Point.new(x: point.x + radius, y: point.y + radius, z: point.z + (radius * 2) + 0.03),
|
126
126
|
color:
|
127
127
|
)
|
128
128
|
]
|
@@ -149,22 +149,56 @@ module Sc2
|
|
149
149
|
)
|
150
150
|
end
|
151
151
|
|
152
|
+
# Renders a block on the floor, drawn by 4 lines
|
153
|
+
# Pass in either a pos (Position/Unit) or exact x * y coordinates
|
154
|
+
# Optional indentation adds padding on borders inward
|
155
|
+
# @param pos [Api::Unit, Sc2::Position]
|
156
|
+
# @param x [Float, Integer]
|
157
|
+
# @param y [Float, Integer]
|
158
|
+
# @param color [Api::Color]
|
159
|
+
# @param indent [Float] default 0.05. should be lower than < 1.0
|
160
|
+
# @example
|
161
|
+
# debug_tile(x: 12.3, y: 4.56, color: Api::Color.new(r: 255, g: 0, b: 0))
|
162
|
+
# debug_tile(some_unit)
|
163
|
+
# debug_tile(some_unit.pos)
|
164
|
+
def debug_tile(pos = nil, x: nil, y: nil, color: nil, indent: 0.05)
|
165
|
+
if pos.is_a?(Api::Unit)
|
166
|
+
x = pos.pos.x.floor
|
167
|
+
y = pos.pos.y.floor
|
168
|
+
elsif pos.is_a?(Sc2::Position)
|
169
|
+
x = pos.x.floor
|
170
|
+
y = pos.y.floor
|
171
|
+
end
|
172
|
+
|
173
|
+
# Raise above floor to prevent texture clipping
|
174
|
+
z = geo.terrain_height(x:, y:).to_f + 0.1
|
175
|
+
tl = Api::Point[x + indent, y + 1.0 - indent, z]
|
176
|
+
bl = Api::Point[x + indent, y + indent, z]
|
177
|
+
br = Api::Point[x + 1.0 - indent, y + indent, z]
|
178
|
+
tr = Api::Point[x + 1.0 - indent, y + 1.0 - indent, z]
|
179
|
+
|
180
|
+
debug_draw_line(p0: tl, p1: bl, color:)
|
181
|
+
debug_draw_line(p0: bl, p1: br, color:)
|
182
|
+
debug_draw_line(p0: br, p1: tr, color:)
|
183
|
+
debug_draw_line(p0: tr, p1: tl, color:)
|
184
|
+
end
|
185
|
+
|
152
186
|
# Other Commands ---
|
153
187
|
|
154
188
|
# @param command [Integer] one of Api::DebugGameState::*
|
155
189
|
# Possible values:
|
156
|
-
# Api::DebugGameState::
|
157
|
-
# Api::DebugGameState::
|
158
|
-
# Api::DebugGameState::
|
159
|
-
# Api::DebugGameState::
|
160
|
-
# Api::DebugGameState::
|
161
|
-
# Api::DebugGameState::
|
162
|
-
# Api::DebugGameState::
|
163
|
-
# Api::DebugGameState::
|
164
|
-
# Api::DebugGameState::
|
165
|
-
# Api::DebugGameState::
|
166
|
-
# Api::DebugGameState::
|
167
|
-
# Api::DebugGameState::
|
190
|
+
# Api::DebugGameState::SHOW_MAP
|
191
|
+
# Api::DebugGameState::CONTROL_ENEMY
|
192
|
+
# Api::DebugGameState::FOOD
|
193
|
+
# Api::DebugGameState::FREE
|
194
|
+
# Api::DebugGameState::ALL_RESOURCES
|
195
|
+
# Api::DebugGameState::GOD
|
196
|
+
# Api::DebugGameState::MINERALS
|
197
|
+
# Api::DebugGameState::GAS
|
198
|
+
# Api::DebugGameState::COOLDOWN
|
199
|
+
# Api::DebugGameState::TECH_TREE
|
200
|
+
# Api::DebugGameState::UPGRADE
|
201
|
+
# Api::DebugGameState::FAST_BUILD
|
168
202
|
# @return [void]
|
169
203
|
def debug_game_state(command)
|
170
204
|
queue_debug_command Api::DebugCommand.new(
|
@@ -203,7 +237,7 @@ module Sc2
|
|
203
237
|
|
204
238
|
# @private
|
205
239
|
# Hangs, crashes and exits the Sc2 client. DO NOT USE.
|
206
|
-
# @param test [Integer] one of Api::DebugTestProcess::Test::
|
240
|
+
# @param test [Integer] one of Api::DebugTestProcess::Test::CRASH, Api::DebugTestProcess::Test::HANG, Api::DebugTestProcess::Test::EXIT
|
207
241
|
# @param delay_ms [Integer] default 0, how long this test is delayed
|
208
242
|
# @return [void]
|
209
243
|
def debug_test_process(test:, delay_ms: 0)
|
@@ -226,8 +260,8 @@ module Sc2
|
|
226
260
|
)
|
227
261
|
end
|
228
262
|
|
229
|
-
# Ends game with a specified result of either
|
230
|
-
# @param end_result [Integer] either 1/2. Api::DebugEndGame::EndResult::
|
263
|
+
# Ends game with a specified result of either :SURRENDER or :DECLARE_VICTORY
|
264
|
+
# @param end_result [Integer] either 1/2. Api::DebugEndGame::EndResult::SURRENDER or Api::DebugEndGame::EndResult::DECLARE_VICTORY
|
231
265
|
# @return [void]
|
232
266
|
def debug_end_game(end_result:)
|
233
267
|
queue_debug_command Api::DebugCommand.new(
|
@@ -5,7 +5,7 @@ module Sc2
|
|
5
5
|
# Holds game state
|
6
6
|
module GameState
|
7
7
|
# @!attribute status
|
8
|
-
# @return [:
|
8
|
+
# @return [:LAUNCHED, :IN_GAME, :IN_REPLAY, :ENDED, :QUIT, :UNKNOWN] status
|
9
9
|
attr_accessor :status
|
10
10
|
|
11
11
|
include Connection::StatusListener
|
@@ -27,10 +27,16 @@ module Sc2
|
|
27
27
|
# Access useful game information. Used in parsed pathing grid, terrain height, placement grid.
|
28
28
|
# Holds Api::ResponseGameInfo::#start_locations.
|
29
29
|
# @return [Api::ResponseGameInfo]
|
30
|
-
|
30
|
+
def game_info
|
31
|
+
if @game_info_task&.running?
|
32
|
+
@game_info_task&.wait
|
33
|
+
@game_info_task = nil
|
34
|
+
end
|
35
|
+
@game_info
|
36
|
+
end
|
31
37
|
|
32
38
|
def game_info=(new_info)
|
33
|
-
@game_info_loop = game_loop
|
39
|
+
@game_info_loop = game_loop
|
34
40
|
@game_info = new_info
|
35
41
|
end
|
36
42
|
|
@@ -40,19 +46,6 @@ module Sc2
|
|
40
46
|
# @return [Integer]
|
41
47
|
attr_accessor :game_info_loop
|
42
48
|
|
43
|
-
# Determines if your game_info will be refreshed at this moment
|
44
|
-
# Has a hard-capped refresh of only ever 4 steps
|
45
|
-
# In general game_info is only refreshed Player::Bot reads from pathing_grid or placement_grid
|
46
|
-
# @return [Boolean]
|
47
|
-
def game_info_stale?
|
48
|
-
return true if game_info_loop.nil? || game_info.nil?
|
49
|
-
return false if game_info_loop == game_loop
|
50
|
-
|
51
|
-
# Note: No minimum step count set anymore
|
52
|
-
# We can do something like, only updating every 2+ frames:
|
53
|
-
game_info_loop + 4 <= game_loop
|
54
|
-
end
|
55
|
-
|
56
49
|
# @!attribute data
|
57
50
|
# @return [Api::ResponseData]
|
58
51
|
attr_accessor :data
|
@@ -141,14 +134,14 @@ module Sc2
|
|
141
134
|
player_id: 0,
|
142
135
|
minerals: 50,
|
143
136
|
vespene: 0,
|
144
|
-
food_cap: ((race == Api::Race::
|
137
|
+
food_cap: ((race == Api::Race::ZERG) ? 14 : 15),
|
145
138
|
food_used: 12,
|
146
139
|
food_army: 0,
|
147
140
|
food_workers: 12,
|
148
141
|
idle_worker_count: 0,
|
149
142
|
army_count: 0,
|
150
143
|
warp_gate_count: 0,
|
151
|
-
larva_count: ((race == Api::Race::
|
144
|
+
larva_count: ((race == Api::Race::ZERG) ? 3 : 0)
|
152
145
|
)
|
153
146
|
end
|
154
147
|
|
data/lib/sc2ai/player/geo.rb
CHANGED
@@ -11,10 +11,30 @@ module Sc2
|
|
11
11
|
# @return [Sc2::Player] player with active connection
|
12
12
|
attr_accessor :bot
|
13
13
|
|
14
|
+
# @private
|
14
15
|
def initialize(bot)
|
15
16
|
@bot = bot
|
16
17
|
end
|
17
18
|
|
19
|
+
# @private
|
20
|
+
# Called once per update loop.
|
21
|
+
# It will clear memoization and caches where necessary
|
22
|
+
# @return [void]
|
23
|
+
def reset
|
24
|
+
# Only re-parse and cache-bust if strings don't match
|
25
|
+
if bot.game_info.start_raw.pathing_grid.data != bot.previous&.game_info&.start_raw&.pathing_grid&.data
|
26
|
+
@parsed_pathing_grid = nil
|
27
|
+
clear_placement_cache
|
28
|
+
end
|
29
|
+
if bot.observation.raw_data.map_state.creep.data != bot.previous.observation.raw_data&.map_state&.creep&.data
|
30
|
+
@parsed_creep = nil
|
31
|
+
clear_placement_cache
|
32
|
+
end
|
33
|
+
if bot.observation.raw_data.map_state.visibility.data != bot.previous.observation.raw_data&.map_state&.visibility&.data
|
34
|
+
@parsed_visibility_grid = nil
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
18
38
|
# Gets the map tile width. Range is 1-255.
|
19
39
|
# Effected by crop_to_playable_area
|
20
40
|
# @return [Integer]
|
@@ -78,13 +98,13 @@ module Sc2
|
|
78
98
|
# Each value in [row][column] holds a boolean value represented as an integer
|
79
99
|
# It does not say whether a position is occupied by another building.
|
80
100
|
# One pixel covers one whole block. Rounds fractionated positions down.
|
81
|
-
# @return [Numo::Bit]
|
101
|
+
# @return [::Numo::Bit]
|
82
102
|
def parsed_placement_grid
|
83
103
|
if @parsed_placement_grid.nil?
|
84
104
|
image_data = bot.game_info.start_raw.placement_grid
|
85
105
|
# Fix endian for Numo bit parser
|
86
106
|
data = image_data.data.unpack("b*").pack("B*")
|
87
|
-
@parsed_placement_grid =
|
107
|
+
@parsed_placement_grid = Numo::Bit.from_binary(data, [image_data.size.y, image_data.size.x])
|
88
108
|
end
|
89
109
|
@parsed_placement_grid
|
90
110
|
end
|
@@ -99,7 +119,7 @@ module Sc2
|
|
99
119
|
end
|
100
120
|
|
101
121
|
# Returns a grid where only the expo locations are marked
|
102
|
-
# @return [Numo::Bit]
|
122
|
+
# @return [::Numo::Bit]
|
103
123
|
def expo_placement_grid
|
104
124
|
if @expo_placement_grid.nil?
|
105
125
|
@expo_placement_grid = Numo::Bit.zeros(map_height, map_width)
|
@@ -108,13 +128,13 @@ module Sc2
|
|
108
128
|
y = point.y.floor
|
109
129
|
|
110
130
|
# For zerg, reserve a layer at the bottom for larva->egg
|
111
|
-
if bot.race == Api::Race::
|
131
|
+
if bot.race == Api::Race::ZERG
|
112
132
|
# Reserve one row lower, meaning (y-3) instead of (y-2)
|
113
133
|
@expo_placement_grid[(y - 3).clamp(map_tile_range_y)..(y + 2).clamp(map_tile_range_y),
|
114
|
-
(x - 2).clamp(
|
134
|
+
(x - 2).clamp(map_tile_range_x)..(x + 2).clamp(map_tile_range_x)] = 1
|
115
135
|
else
|
116
136
|
@expo_placement_grid[(y - 2).clamp(map_tile_range_y)..(y + 2).clamp(map_tile_range_y),
|
117
|
-
(x - 2).clamp(
|
137
|
+
(x - 2).clamp(map_tile_range_x)..(x + 2).clamp(map_tile_range_x)] = 1
|
118
138
|
end
|
119
139
|
end
|
120
140
|
end
|
@@ -123,7 +143,7 @@ module Sc2
|
|
123
143
|
|
124
144
|
# Returns a grid where only placement obstructions are marked
|
125
145
|
# includes Tumors and lowered Supply Depots
|
126
|
-
# @return [Numo::Bit]
|
146
|
+
# @return [::Numo::Bit]
|
127
147
|
private def placement_obstruction_grid
|
128
148
|
# Get obstructing structures
|
129
149
|
obstructure_types = [Api::UnitTypeId::SUPPLYDEPOTLOWERED, Api::UnitTypeId::CREEPTUMORQUEEN, Api::UnitTypeId::CREEPTUMOR, Api::UnitTypeId::CREEPTUMORBURROWED]
|
@@ -158,7 +178,7 @@ module Sc2
|
|
158
178
|
end
|
159
179
|
|
160
180
|
# Returns a grid where powered locations are marked true
|
161
|
-
# @return [Numo::Bit]
|
181
|
+
# @return [::Numo::Bit]
|
162
182
|
def parsed_power_grid
|
163
183
|
# Cache for based on power unit tags
|
164
184
|
cache_key = bot.power_sources.map(&:tag).sort.hash
|
@@ -188,7 +208,7 @@ module Sc2
|
|
188
208
|
# 00001111110000
|
189
209
|
# perf: Saving pre-created shape for speed (0.5ms saved) by using hardcode from .to_binary.unpack("C*")
|
190
210
|
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*")
|
191
|
-
blueprint_pylon =
|
211
|
+
blueprint_pylon = Numo::Bit.from_binary(blueprint_data, [14, 14])
|
192
212
|
|
193
213
|
# Warp Prism
|
194
214
|
# 00011000
|
@@ -200,7 +220,7 @@ module Sc2
|
|
200
220
|
# 01111110
|
201
221
|
# 00011000
|
202
222
|
blueprint_data = [24, 126, 126, 255, 255, 126, 126, 24].pack("C*")
|
203
|
-
blueprint_prism =
|
223
|
+
blueprint_prism = Numo::Bit.from_binary(blueprint_data, [8, 8])
|
204
224
|
|
205
225
|
# Print each power-source on map using shape above
|
206
226
|
bot.power_sources.each do |ps|
|
@@ -273,20 +293,13 @@ module Sc2
|
|
273
293
|
# parsed_pathing_grid[0,0] # reads bottom left corner
|
274
294
|
# # use helper function #pathable
|
275
295
|
# pathable?(x: 0, y: 0) # reads bottom left corner
|
276
|
-
# @return [Numo::Bit] Numo array
|
296
|
+
# @return [::Numo::Bit] Numo array
|
277
297
|
def parsed_pathing_grid
|
278
|
-
if bot.game_info_stale?
|
279
|
-
previous_data = bot.game_info.start_raw.pathing_grid.data
|
280
|
-
bot.refresh_game_info
|
281
|
-
# Only re-parse if binary strings don't match
|
282
|
-
clear_cached_pathing_grid if previous_data != bot.game_info.start_raw.pathing_grid.data
|
283
|
-
end
|
284
|
-
|
285
298
|
if @parsed_pathing_grid.nil?
|
286
299
|
image_data = bot.game_info.start_raw.pathing_grid
|
287
300
|
# Fix endian for Numo bit parser
|
288
301
|
data = image_data.data.unpack("b*").pack("B*")
|
289
|
-
@parsed_pathing_grid =
|
302
|
+
@parsed_pathing_grid = Numo::Bit.from_binary(data, [image_data.size.y, image_data.size.x])
|
290
303
|
end
|
291
304
|
@parsed_pathing_grid
|
292
305
|
end
|
@@ -294,8 +307,7 @@ module Sc2
|
|
294
307
|
# Clears pathing-grid dependent objects like placements.
|
295
308
|
# Called when pathing grid gets updated
|
296
309
|
#
|
297
|
-
private def
|
298
|
-
@parsed_pathing_grid = nil
|
310
|
+
private def clear_placement_cache
|
299
311
|
@_build_coordinates = {}
|
300
312
|
@_build_coordinate_tree = {}
|
301
313
|
end
|
@@ -318,13 +330,13 @@ module Sc2
|
|
318
330
|
|
319
331
|
# Returns a parsed terrain_height from bot.game_info.start_raw.
|
320
332
|
# Each value in [row][column] holds a float value which is the z height
|
321
|
-
# @return [Numo::SFloat] Numo array
|
333
|
+
# @return [::Numo::SFloat] Numo array
|
322
334
|
def parsed_terrain_height
|
323
335
|
if @parsed_terrain_height.nil?
|
324
336
|
|
325
337
|
image_data = bot.game_info.start_raw.terrain_height
|
326
|
-
@parsed_terrain_height =
|
327
|
-
[image_data.size.y, image_data.size.x])
|
338
|
+
@parsed_terrain_height = Numo::UInt8
|
339
|
+
.from_binary(image_data.data, [image_data.size.y, image_data.size.x])
|
328
340
|
.cast_to(Numo::SFloat)
|
329
341
|
|
330
342
|
# Values are between -16 and +16. The api values is a float height compressed to rgb range (0-255) in that range of 32.
|
@@ -339,7 +351,7 @@ module Sc2
|
|
339
351
|
# Returns one of three Integer visibility indicators at tile for x & y
|
340
352
|
# @param x [Float, Integer]
|
341
353
|
# @param y [Float, Integer]
|
342
|
-
# @return [Integer] 0=
|
354
|
+
# @return [Integer] 0=HIDDEN,1=SNAPSHOT,2=VISIBLE
|
343
355
|
def visibility(x:, y:)
|
344
356
|
parsed_visibility_grid[y.to_i, x.to_i]
|
345
357
|
end
|
@@ -371,12 +383,13 @@ module Sc2
|
|
371
383
|
# Returns a parsed map_state.visibility from bot.observation.raw_data.
|
372
384
|
# Each value in [row][column] holds one of three integers (0,1,2) to flag a vision type
|
373
385
|
# @see #visibility for reading from this value
|
374
|
-
# @return [Numo::SFloat] Numo array
|
386
|
+
# @return [::Numo::SFloat] Numo array
|
375
387
|
def parsed_visibility_grid
|
376
388
|
if @parsed_visibility_grid.nil?
|
377
389
|
image_data = bot.observation.raw_data.map_state.visibility
|
378
|
-
|
379
|
-
|
390
|
+
# Fix endian for Numo bit parser
|
391
|
+
data = image_data.data.unpack("b*").pack("B*")
|
392
|
+
@parsed_visibility_grid = Numo::UInt8.from_binary(data, [image_data.size.y, image_data.size.x])
|
380
393
|
end
|
381
394
|
@parsed_visibility_grid
|
382
395
|
end
|
@@ -391,17 +404,16 @@ module Sc2
|
|
391
404
|
end
|
392
405
|
|
393
406
|
# Provides parsed minimap representation of creep spread
|
394
|
-
# Caches for
|
395
|
-
# @return [Numo::Bit] Numo array
|
407
|
+
# Caches for this frame
|
408
|
+
# @return [::Numo::Bit] Numo array
|
396
409
|
def parsed_creep
|
397
|
-
if @parsed_creep.nil? ||
|
410
|
+
if @parsed_creep.nil? # || image_data != previous_data
|
398
411
|
image_data = bot.observation.raw_data.map_state.creep
|
399
412
|
# Fix endian for Numo bit parser
|
400
413
|
data = image_data.data.unpack("b*").pack("B*")
|
401
|
-
|
402
|
-
@parsed_creep = [result, bot.game_loop]
|
414
|
+
@parsed_creep = Numo::Bit.from_binary(data, [image_data.size.y, image_data.size.x])
|
403
415
|
end
|
404
|
-
@parsed_creep
|
416
|
+
@parsed_creep
|
405
417
|
end
|
406
418
|
|
407
419
|
# TODO: Removing. Better name or more features for this? Maybe check nearest units.
|
@@ -416,7 +428,7 @@ module Sc2
|
|
416
428
|
# TODO: Remove this method if it has no use. Build points uses this code directly for optimization.
|
417
429
|
# Reduce the dimensions of a grid by merging cells using length x length squares.
|
418
430
|
# Merged cell keeps it's 1 value only if all merged cells are equal to 1, else 0
|
419
|
-
# @param input_grid [Numo::Bit] Bit grid like parsed_pathing_grid or parsed_placement_grid
|
431
|
+
# @param input_grid [::Numo::Bit] Bit grid like parsed_pathing_grid or parsed_placement_grid
|
420
432
|
# @param length [Integer] how many cells to merge, i.e. 3 for finding 3x3 placement
|
421
433
|
def divide_grid(input_grid, length)
|
422
434
|
height = input_grid.shape[0]
|
@@ -713,7 +725,7 @@ module Sc2
|
|
713
725
|
length = 1 if length < 1
|
714
726
|
@_build_coordinates ||= {}
|
715
727
|
cache_key = [length, on_creep, in_power].hash
|
716
|
-
return @_build_coordinates[cache_key]
|
728
|
+
return @_build_coordinates[cache_key] unless @_build_coordinates[cache_key].nil?
|
717
729
|
|
718
730
|
result = []
|
719
731
|
input_grid = parsed_pathing_grid & parsed_placement_grid & ~expo_placement_grid & ~placement_obstruction_grid
|
@@ -722,7 +734,6 @@ module Sc2
|
|
722
734
|
else
|
723
735
|
~parsed_creep & input_grid
|
724
736
|
end
|
725
|
-
|
726
737
|
input_grid = parsed_power_grid & input_grid if in_power
|
727
738
|
|
728
739
|
# Dimensions
|
@@ -736,35 +747,14 @@ module Sc2
|
|
736
747
|
# Build points are in center of square, i.e. 1.5 inwards for a 3x3 building
|
737
748
|
offset_to_inside = length / 2.0
|
738
749
|
|
739
|
-
|
740
|
-
|
741
|
-
|
742
|
-
|
743
|
-
|
744
|
-
|
745
|
-
# Check right- and upwards for any negatives and break both loops, as soon as we find one
|
746
|
-
valid_position = true
|
747
|
-
inner_y = 0
|
748
|
-
while inner_y < length
|
749
|
-
inner_x = 0
|
750
|
-
while inner_x < length
|
751
|
-
if (input_grid[y + inner_y, x + inner_x]).zero?
|
752
|
-
# break sub-cells check and don't save position
|
753
|
-
valid_position = false
|
754
|
-
inner_y = length
|
755
|
-
break
|
756
|
-
end
|
757
|
-
inner_x += 1
|
758
|
-
end
|
759
|
-
inner_y += 1
|
760
|
-
end
|
761
|
-
# End of checking sub-cells
|
762
|
-
|
763
|
-
result << [x + offset_to_inside, y + offset_to_inside] if valid_position
|
764
|
-
x += length
|
765
|
-
end
|
766
|
-
y += length
|
750
|
+
output_grid = input_grid[0...capped_height, 0...capped_width]
|
751
|
+
.reshape(capped_height / length, length, capped_width / length, length)
|
752
|
+
.all?(1, 3)
|
753
|
+
output_grid.where.each do |true_index|
|
754
|
+
y, x = true_index.divmod(capped_width / length)
|
755
|
+
result << [x * length + offset_to_inside, y * length + offset_to_inside]
|
767
756
|
end
|
757
|
+
|
768
758
|
@_build_coordinates[cache_key] = result
|
769
759
|
end
|
770
760
|
|
@@ -781,7 +771,7 @@ module Sc2
|
|
781
771
|
target = target.pos if target.is_a? Api::Unit
|
782
772
|
random = 1 if random.to_i.negative?
|
783
773
|
length = 1 if length < 1
|
784
|
-
on_creep = bot.race == Api::Race::
|
774
|
+
on_creep = bot.race == Api::Race::ZERG
|
785
775
|
|
786
776
|
coordinates = build_coordinates(length:, on_creep:, in_power:)
|
787
777
|
cache_key = coordinates.hash
|
data/lib/sc2ai/player/units.rb
CHANGED
@@ -23,7 +23,7 @@ module Sc2
|
|
23
23
|
# @return [Sc2::UnitGroup] a group of placeholder structures
|
24
24
|
attr_accessor :placeholders
|
25
25
|
|
26
|
-
# All units with alliance :
|
26
|
+
# All units with alliance :NEUTRAL
|
27
27
|
# @!attribute neutral
|
28
28
|
# @return [Sc2::UnitGroup] a group of neutral units
|
29
29
|
attr_accessor :neutral
|
@@ -91,6 +91,17 @@ module Sc2
|
|
91
91
|
# For this unit type, tells you how many are in progress by checking orders for all it's sources.
|
92
92
|
# @return [Integer]
|
93
93
|
def units_in_progress(unit_type_id)
|
94
|
+
if unit_type_id == Api::UnitTypeId::REACTOR
|
95
|
+
return units_in_progress(Api::UnitTypeId::BARRACKSREACTOR) +
|
96
|
+
units_in_progress(Api::UnitTypeId::FACTORYREACTOR) +
|
97
|
+
units_in_progress(Api::UnitTypeId::STARPORTREACTOR)
|
98
|
+
elsif unit_type_id == Api::UnitTypeId::TECHLAB
|
99
|
+
return units_in_progress(Api::UnitTypeId::BARRACKSTECHLAB) +
|
100
|
+
units_in_progress(Api::UnitTypeId::FACTORYTECHLAB) +
|
101
|
+
units_in_progress(Api::UnitTypeId::STARPORTTECHLAB)
|
102
|
+
end
|
103
|
+
|
104
|
+
# Get source unit
|
94
105
|
source_unit_types = Api::TechTree.unit_created_from(unit_type_id: unit_type_id)
|
95
106
|
|
96
107
|
# When building from LARVA, check the intermediate models
|
@@ -114,14 +125,27 @@ module Sc2
|
|
114
125
|
target: unit_type_id
|
115
126
|
)
|
116
127
|
|
117
|
-
origin = if unit_data(source_unit_types.first).attributes.include?(
|
128
|
+
origin = if unit_data(source_unit_types.first).attributes.include?(Api::Attribute::STRUCTURE)
|
118
129
|
structures
|
119
130
|
else
|
120
131
|
units
|
121
132
|
end
|
133
|
+
# For SCV, the structure might be completed but dangling order for a few frames.
|
134
|
+
source_is_scv = source_unit_types.include?(Api::UnitTypeId::SCV)
|
135
|
+
|
136
|
+
# Let's count orders matching the ability
|
122
137
|
total_in_progress = origin.select_type(source_unit_types).sum do |source|
|
123
138
|
source.orders.count do |order|
|
124
|
-
|
139
|
+
if order.ability_id == unit_create_ability
|
140
|
+
if source_is_scv
|
141
|
+
# If we are a SCV, do not count our order if the target is a completed structure pos
|
142
|
+
structures.select_type(unit_type_id).completed.none? do |structure|
|
143
|
+
structure.pos == order.target_world_space_pos
|
144
|
+
end
|
145
|
+
else
|
146
|
+
true
|
147
|
+
end
|
148
|
+
end
|
125
149
|
end
|
126
150
|
end
|
127
151
|
total_in_progress *= 2 if unit_type_id == Api::UnitTypeId::ZERGLING
|
@@ -191,12 +215,12 @@ module Sc2
|
|
191
215
|
|
192
216
|
# Checks unit data for an attribute value
|
193
217
|
# @param unit [Integer,Api::Unit] Api::UnitTypeId or Api::Unit
|
194
|
-
# @param attribute [Symbol] Api::Attribute, i.e. Api::Attribute::
|
218
|
+
# @param attribute [Symbol] Api::Attribute, i.e. Api::Attribute::MECHANICAL or :Mechanical
|
195
219
|
# @return [Boolean] whether unit has attribute
|
196
220
|
# @example
|
197
|
-
# unit_has_attribute?(Api::UnitTypeId::SCV, Api::Attribute::
|
198
|
-
# unit_has_attribute?(units.workers.first, :
|
199
|
-
# unit_has_attribute?(Api::UnitTypeId::SCV, :
|
221
|
+
# unit_has_attribute?(Api::UnitTypeId::SCV, Api::Attribute::MECHANICAL)
|
222
|
+
# unit_has_attribute?(units.workers.first, :MECHANICAL)
|
223
|
+
# unit_has_attribute?(Api::UnitTypeId::SCV, :MECHANICAL)
|
200
224
|
def unit_has_attribute?(unit, attribute)
|
201
225
|
unit_data(unit).attributes.include? attribute
|
202
226
|
end
|
@@ -331,7 +355,7 @@ module Sc2
|
|
331
355
|
# Categorize own units/structures, enemy units/structures, neutral
|
332
356
|
if unit.is_blip
|
333
357
|
@blips[tag] = unit
|
334
|
-
elsif unit.display_type == :
|
358
|
+
elsif unit.display_type == :PLACEHOLDER
|
335
359
|
@placeholders[tag] = unit
|
336
360
|
elsif unit.alliance == own_alliance || unit.alliance == enemy_alliance
|
337
361
|
if unit.alliance == own_alliance
|
@@ -344,7 +368,7 @@ module Sc2
|
|
344
368
|
end
|
345
369
|
|
346
370
|
unit_data = unit_data(unit.unit_type)
|
347
|
-
if unit_data.attributes.include?
|
371
|
+
if unit_data.attributes.include?(Api::Attribute::STRUCTURE)
|
348
372
|
structure_collection[tag] = unit
|
349
373
|
else
|
350
374
|
unit_collection[tag] = unit
|
@@ -355,8 +379,8 @@ module Sc2
|
|
355
379
|
|
356
380
|
# Dont parse callbacks on first loop or for neutral units
|
357
381
|
if !@previous.all_units.nil? &&
|
358
|
-
unit.alliance != :
|
359
|
-
unit.display_type != :
|
382
|
+
unit.alliance != :NEUTRAL &&
|
383
|
+
unit.display_type != :PLACEHOLDER &&
|
360
384
|
unit.is_blip == false
|
361
385
|
|
362
386
|
previous_unit = @previous.all_units[unit.tag]
|
@@ -378,23 +402,23 @@ module Sc2
|
|
378
402
|
|
379
403
|
# @private
|
380
404
|
# Returns alliance based on whether you are a player or an enemy
|
381
|
-
# @return [:Symbol] :
|
405
|
+
# @return [:Symbol] :SELF or :ENEMY from Api::Alliance
|
382
406
|
def own_alliance
|
383
407
|
if is_a? Sc2::Player::Enemy
|
384
|
-
Api::Alliance.lookup(Api::Alliance::
|
408
|
+
Api::Alliance.lookup(Api::Alliance::ENEMY)
|
385
409
|
else
|
386
|
-
Api::Alliance.lookup(Api::Alliance::
|
410
|
+
Api::Alliance.lookup(Api::Alliance::SELF)
|
387
411
|
end
|
388
412
|
end
|
389
413
|
|
390
414
|
# @private
|
391
415
|
# Returns enemy alliance based on whether you are a player or an enemy
|
392
|
-
# @return [:Symbol] :
|
416
|
+
# @return [:Symbol] :SELF or :ENEMY from Api::Alliance
|
393
417
|
def enemy_alliance
|
394
418
|
if is_a? Sc2::Player::Enemy
|
395
|
-
Api::Alliance.lookup(Api::Alliance::
|
419
|
+
Api::Alliance.lookup(Api::Alliance::SELF)
|
396
420
|
else
|
397
|
-
Api::Alliance.lookup(Api::Alliance::
|
421
|
+
Api::Alliance.lookup(Api::Alliance::ENEMY)
|
398
422
|
end
|
399
423
|
end
|
400
424
|
|