sc2ai 0.1.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
|