sc2ai 0.5.0 → 0.6.2
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/data.proto +1 -1
- data/data/sc2ai/protocol/raw.proto +1 -1
- data/docker_build/Dockerfile.aiarenabot +12 -0
- data/docker_build/Dockerfile.ruby +2 -2
- data/docker_build/docker-compose-base-image.yml +0 -1
- data/docker_build/docker-compose-ladderzip.yml +0 -1
- data/docker_build/docker-compose-versus-bot.yml +15 -0
- data/lib/sc2ai/api/data.rb +59 -1
- data/lib/sc2ai/cli/cli.rb +7 -47
- data/lib/sc2ai/cli/ladderzip.rb +6 -1
- data/lib/sc2ai/cli/versus_bot.rb +305 -0
- data/lib/sc2ai/connection/requests.rb +4 -1
- data/lib/sc2ai/local_play/client_manager.rb +3 -1
- data/lib/sc2ai/local_play/match.rb +2 -2
- data/lib/sc2ai/player/game_state.rb +2 -14
- data/lib/sc2ai/player/geo.rb +19 -2
- data/lib/sc2ai/player/units.rb +12 -8
- data/lib/sc2ai/player.rb +29 -7
- data/lib/sc2ai/protocol/extensions/player_common.rb +25 -0
- data/lib/sc2ai/protocol/extensions/position.rb +1 -1
- data/lib/sc2ai/protocol/extensions/unit.rb +59 -14
- data/lib/sc2ai/protocol/extensions/unit_type_data.rb +30 -0
- data/lib/sc2ai/unit_group/filter_ext.rb +23 -2
- data/lib/sc2ai/version.rb +1 -1
- data/lib/templates/ladderzip/bin/ladder.tt +1 -7
- data/lib/templates/ladderzip/ladderbots.json.tt +11 -0
- data/lib/templates/new/.ladderignore +1 -1
- data/lib/templates/new/api/data.proto +1 -1
- data/lib/templates/new/api/raw.proto +1 -1
- data/lib/templates/new/run_example_match.rb.tt +1 -1
- data/sig/sc2ai.rbs +152 -86
- metadata +17 -16
@@ -27,25 +27,13 @@ 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
|
-
|
31
|
-
if @game_info_task&.running?
|
32
|
-
@game_info_task&.wait
|
33
|
-
@game_info_task = nil
|
34
|
-
end
|
35
|
-
@game_info
|
36
|
-
end
|
30
|
+
attr_reader :game_info
|
37
31
|
|
38
32
|
def game_info=(new_info)
|
39
|
-
@game_info_loop = game_loop
|
33
|
+
@game_info_loop = game_loop || 0
|
40
34
|
@game_info = new_info
|
41
35
|
end
|
42
36
|
|
43
|
-
# @!attribute game_info_loop
|
44
|
-
# This is the last loop at which game_info was set.
|
45
|
-
# Used to determine staleness.
|
46
|
-
# @return [Integer]
|
47
|
-
attr_accessor :game_info_loop
|
48
|
-
|
49
37
|
# @!attribute data
|
50
38
|
# @return [Api::ResponseData]
|
51
39
|
attr_accessor :data
|
data/lib/sc2ai/player/geo.rb
CHANGED
@@ -81,6 +81,16 @@ module Sc2
|
|
81
81
|
0..(map_height - 1)
|
82
82
|
end
|
83
83
|
|
84
|
+
# Ensures a Sc2::Position's x/y stays in map tile range
|
85
|
+
# Prevents out of bound exceptions when working with minimap
|
86
|
+
# @param position [Sc2::Position, Api::Point2D]
|
87
|
+
# @return [Sc2::Position, Api::Point2D]
|
88
|
+
def clamp_to_grid(position)
|
89
|
+
position.y = position.y.clamp(map_tile_range_y)
|
90
|
+
position.x = position.x.clamp(map_tile_range_x)
|
91
|
+
position
|
92
|
+
end
|
93
|
+
|
84
94
|
# Map Parsing functions -----
|
85
95
|
|
86
96
|
# Returns whether a x/y (integer) is placeable as per minimap image data.
|
@@ -300,6 +310,13 @@ module Sc2
|
|
300
310
|
# Fix endian for Numo bit parser
|
301
311
|
data = image_data.data.unpack("b*").pack("B*")
|
302
312
|
@parsed_pathing_grid = Numo::Bit.from_binary(data, [image_data.size.y, image_data.size.x])
|
313
|
+
|
314
|
+
# Remove erroneous mineral blockers as pathable
|
315
|
+
bot.neutral.select_type(Api::UnitTypeId::MINERALFIELD450).each do |mineral|
|
316
|
+
@parsed_pathing_grid[mineral.pos.y.to_i, mineral.pos.x] = 0
|
317
|
+
@parsed_pathing_grid[mineral.pos.y.to_i, mineral.pos.x - 1] = 0
|
318
|
+
end
|
319
|
+
|
303
320
|
end
|
304
321
|
@parsed_pathing_grid
|
305
322
|
end
|
@@ -455,7 +472,7 @@ module Sc2
|
|
455
472
|
while inner_y < length
|
456
473
|
inner_x = 0
|
457
474
|
while inner_x < length
|
458
|
-
if
|
475
|
+
if input_grid[y + inner_y, x + inner_x].zero?
|
459
476
|
output_grid[y / length, x / length] = 0
|
460
477
|
inner_y = length
|
461
478
|
break
|
@@ -507,7 +524,7 @@ module Sc2
|
|
507
524
|
end
|
508
525
|
|
509
526
|
# Split resources by Z axis
|
510
|
-
resources = bot.neutral.minerals + bot.neutral.geysers
|
527
|
+
resources = bot.neutral.minerals.reject_type(Api::UnitTypeId::MINERALFIELD450) + bot.neutral.geysers
|
511
528
|
resource_group_z = resources.group_by do |resource|
|
512
529
|
resource.pos.z.round # 32 units of Y, most maps will have use 3. round to nearest.
|
513
530
|
end
|
data/lib/sc2ai/player/units.rb
CHANGED
@@ -35,6 +35,11 @@ module Sc2
|
|
35
35
|
# @return [Sc2::UnitGroup] a group of neutral units
|
36
36
|
attr_accessor :effects # not a unit
|
37
37
|
|
38
|
+
# An array of Units which are (red) blips on radar
|
39
|
+
# @!attribute blips
|
40
|
+
# @return [Sc2::UnitGroup] a group of blip units
|
41
|
+
attr_accessor :blips
|
42
|
+
|
38
43
|
# Returns the upgrade ids you have acquired such as weapon upgrade and armor upgrade ids.
|
39
44
|
# Shorthand for observation.raw_data.player.upgrade_ids
|
40
45
|
# @!attribute [r] upgrades_completed
|
@@ -254,7 +259,7 @@ module Sc2
|
|
254
259
|
|
255
260
|
# Checks whether you have the resources to construct quantity of unit type
|
256
261
|
# @return [Boolean]
|
257
|
-
def can_afford?(unit_type_id
|
262
|
+
def can_afford?(unit_type_id, quantity: 1)
|
258
263
|
unit_type_data = unit_data(unit_type_id)
|
259
264
|
return false if unit_type_data.nil?
|
260
265
|
|
@@ -343,10 +348,7 @@ module Sc2
|
|
343
348
|
enemy_alliance = self.enemy_alliance
|
344
349
|
|
345
350
|
# To prevent several loops over all units per frame, use this single loop for all checks
|
346
|
-
|
347
|
-
i = 0
|
348
|
-
while i < all_unit_size
|
349
|
-
unit = observation.raw_data.units[i]
|
351
|
+
observation.raw_data.units.each do |unit|
|
350
352
|
tag = unit.tag
|
351
353
|
tag = unit.tag = unit.hash if tag.zero?
|
352
354
|
# Reluctantly assigning player to unit
|
@@ -377,7 +379,7 @@ module Sc2
|
|
377
379
|
@neutral[tag] = unit
|
378
380
|
end
|
379
381
|
|
380
|
-
#
|
382
|
+
# Don't parse callbacks on first loop or for neutral units
|
381
383
|
if !@previous.all_units.nil? &&
|
382
384
|
unit.alliance != :NEUTRAL &&
|
383
385
|
unit.display_type != :PLACEHOLDER &&
|
@@ -392,11 +394,13 @@ module Sc2
|
|
392
394
|
issue_existing_unit_callbacks(unit, previous_unit)
|
393
395
|
end
|
394
396
|
end
|
397
|
+
end
|
395
398
|
|
399
|
+
# Due to race condition with callbacks and Unit#bot not being set,
|
400
|
+
# rather do a second iteration just for the callbacks
|
401
|
+
observation.raw_data.units.each do |unit|
|
396
402
|
# Allow user to fiddle with unit
|
397
403
|
on_parse_observation_unit(unit)
|
398
|
-
|
399
|
-
i += 1
|
400
404
|
end
|
401
405
|
end
|
402
406
|
|
data/lib/sc2ai/player.rb
CHANGED
@@ -213,6 +213,7 @@ module Sc2
|
|
213
213
|
# enable_feature_layer = true
|
214
214
|
#
|
215
215
|
# end
|
216
|
+
# @return [void]
|
216
217
|
def configure
|
217
218
|
end
|
218
219
|
|
@@ -276,12 +277,14 @@ module Sc2
|
|
276
277
|
|
277
278
|
# Override to perform steps before first on_step gets called.
|
278
279
|
# Current game_loop is 0 and @api is available
|
280
|
+
# @return [void]
|
279
281
|
def on_start
|
280
282
|
# Sc2.logger.debug { "#{self.class} on_start" }
|
281
283
|
end
|
282
284
|
|
283
285
|
# Override to implement your own game logic.
|
284
286
|
# Gets called whenever the game moves forward.
|
287
|
+
# @return [void]
|
285
288
|
def on_step
|
286
289
|
return unless is_a? Bot
|
287
290
|
|
@@ -305,6 +308,7 @@ module Sc2
|
|
305
308
|
# puts "Lets try again!"
|
306
309
|
# end
|
307
310
|
# end
|
311
|
+
# @return [void]
|
308
312
|
def on_finish(result)
|
309
313
|
# Sc2.logger.debug { "#{self.class} on_finish" }
|
310
314
|
end
|
@@ -312,12 +316,14 @@ module Sc2
|
|
312
316
|
# Called when Random race is first detected.
|
313
317
|
# Override to handle race identification of random enemy.
|
314
318
|
# @param race [Integer] see {Api::Race}
|
319
|
+
# @return [void]
|
315
320
|
def on_random_race_detected(race)
|
316
321
|
end
|
317
322
|
|
318
323
|
# Called on step if errors are present. Equivalent of UI red text errors.
|
319
324
|
# Override to read action errors.
|
320
325
|
# @param errors [Array<Api::ActionError>]
|
326
|
+
# @return [void]
|
321
327
|
def on_action_errors(errors)
|
322
328
|
# Sc2.logger.debug errors
|
323
329
|
end
|
@@ -325,6 +331,7 @@ module Sc2
|
|
325
331
|
# Actions this player performed since the last Observation.
|
326
332
|
# Override to read actions successfully performed
|
327
333
|
# @param actions [Array<Api::Action>] a list of actions which were performed
|
334
|
+
# @return [void]
|
328
335
|
def on_actions_performed(actions)
|
329
336
|
# Sc2.logger.debug actions
|
330
337
|
end
|
@@ -342,11 +349,13 @@ module Sc2
|
|
342
349
|
# end
|
343
350
|
# end
|
344
351
|
# @param alerts [Array<Integer>] array of Api::Alert::*
|
352
|
+
# @return [void]
|
345
353
|
def on_alerts(alerts)
|
346
354
|
end
|
347
355
|
|
348
356
|
# Callback when upgrades are completed, multiple might finish on the same observation.
|
349
357
|
# @param upgrade_ids [Array<Integer>] Api::UpgradeId::*
|
358
|
+
# @return [void]
|
350
359
|
def on_upgrades_completed(upgrade_ids)
|
351
360
|
end
|
352
361
|
|
@@ -359,6 +368,7 @@ module Sc2
|
|
359
368
|
# Callback, on observation parse when iterating over every unit
|
360
369
|
# Can be useful for decorating additional properties on a unit before on_step
|
361
370
|
# A Sc2::Player should override this to decorate additional properties
|
371
|
+
# @return [void]
|
362
372
|
def on_parse_observation_unit(unit)
|
363
373
|
end
|
364
374
|
|
@@ -367,12 +377,14 @@ module Sc2
|
|
367
377
|
# Override to use in your bot class or use Player.
|
368
378
|
# @param unit [Api::Unit]
|
369
379
|
# @see Sc2::Player::Units#units_destroyed
|
380
|
+
# @return [void]
|
370
381
|
def on_unit_destroyed(unit)
|
371
382
|
end
|
372
383
|
|
373
384
|
# Callback for unit created.
|
374
385
|
# Override to use in your bot class.
|
375
386
|
# @param unit [Api::Unit]
|
387
|
+
# @return [void]
|
376
388
|
def on_unit_created(unit)
|
377
389
|
end
|
378
390
|
|
@@ -381,18 +393,21 @@ module Sc2
|
|
381
393
|
# Override to use in your bot class or use Player.
|
382
394
|
# @param unit [Api::Unit]
|
383
395
|
# @param previous_unit_type_id [Integer] Api::UnitTypeId::*
|
396
|
+
# @return [void]
|
384
397
|
def on_unit_type_changed(unit, previous_unit_type_id)
|
385
398
|
end
|
386
399
|
|
387
400
|
# Callback for structure building began
|
388
401
|
# Override to use in your bot class.
|
389
402
|
# @param unit [Api::Unit]
|
403
|
+
# @return [void]
|
390
404
|
def on_structure_started(unit)
|
391
405
|
end
|
392
406
|
|
393
407
|
# Callback for structure building is completed
|
394
408
|
# Override to use in your bot class or use Player.
|
395
409
|
# @param unit [Api::Unit]
|
410
|
+
# @return [void]
|
396
411
|
def on_structure_completed(unit)
|
397
412
|
end
|
398
413
|
|
@@ -400,6 +415,7 @@ module Sc2
|
|
400
415
|
# Override to use in your bot class or use Player.
|
401
416
|
# @param unit [Api::Unit]
|
402
417
|
# @param amount [Integer] of damage
|
418
|
+
# @return [void]
|
403
419
|
def on_unit_damaged(unit, amount)
|
404
420
|
end
|
405
421
|
|
@@ -513,8 +529,6 @@ module Sc2
|
|
513
529
|
!IDENTIFIED_RACES.include?(race)
|
514
530
|
end
|
515
531
|
|
516
|
-
private
|
517
|
-
|
518
532
|
# @private
|
519
533
|
CALLBACK_METHODS = %i[on_finish
|
520
534
|
on_random_race_detected
|
@@ -529,19 +543,29 @@ module Sc2
|
|
529
543
|
on_structure_started
|
530
544
|
on_structure_completed
|
531
545
|
on_unit_damaged]
|
546
|
+
private_constant :CALLBACK_METHODS
|
547
|
+
|
548
|
+
# @private
|
549
|
+
# @return [Array<Symbol>] callbacks implemented on player class
|
550
|
+
attr_accessor :callbacks_defined
|
532
551
|
|
533
552
|
# @private
|
534
553
|
# Checks if callback method is defined on our bot
|
535
554
|
# Used to skip processing on unused callbacks
|
536
555
|
# @param callback [Symbol]
|
537
556
|
def callback_defined?(callback)
|
538
|
-
|
557
|
+
if @callbacks_defined.nil?
|
558
|
+
# Cache the intersection check, assuming nobody defines a callback method while actually running
|
559
|
+
@callbacks_defined = CALLBACK_METHODS.intersection(self.class.instance_methods(false))
|
560
|
+
end
|
561
|
+
@callbacks_defined.include?(callback)
|
539
562
|
end
|
540
563
|
|
541
564
|
# Initialize data on step 0 before stepping and before on_start is called
|
542
565
|
def prepare_start
|
543
566
|
@data = Sc2::Data.new(@api.data)
|
544
567
|
clear_action_queue
|
568
|
+
clear_action_errors
|
545
569
|
clear_debug_command_queue
|
546
570
|
end
|
547
571
|
|
@@ -574,6 +598,7 @@ module Sc2
|
|
574
598
|
# Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
575
599
|
step_to_loop = @realtime ? game_loop + @step_count : nil
|
576
600
|
response_observation = @api.observation(game_loop: step_to_loop)
|
601
|
+
return if response_observation.nil?
|
577
602
|
|
578
603
|
# Check if match has a result and callback
|
579
604
|
on_player_result(response_observation.player_result) unless response_observation.player_result.empty?
|
@@ -646,10 +671,7 @@ module Sc2
|
|
646
671
|
# Refreshes bot#game_info ignoring all caches
|
647
672
|
# @return [void]
|
648
673
|
public def refresh_game_info
|
649
|
-
|
650
|
-
self.game_info = @api.game_info
|
651
|
-
@game_info_task = nil
|
652
|
-
end
|
674
|
+
self.game_info = @api.game_info
|
653
675
|
end
|
654
676
|
|
655
677
|
# Enemy -----------------------
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Api
|
2
|
+
# Adds additional functionality to message object Api::Point2D
|
3
|
+
module PlayerCommonExt
|
4
|
+
# @!attribute supply_cap
|
5
|
+
# Maximum supply. alias of #food_cap
|
6
|
+
# @return [Integer]
|
7
|
+
def supply_cap = food_cap
|
8
|
+
|
9
|
+
# @!attribute supply_used
|
10
|
+
# Supply used. alias of #food_used
|
11
|
+
# @return [Integer]
|
12
|
+
def supply_used = food_used
|
13
|
+
|
14
|
+
# @!attribute supply_army
|
15
|
+
# Supply used for army. alias of #food_army
|
16
|
+
# @return [Integer]
|
17
|
+
def supply_army = food_army
|
18
|
+
|
19
|
+
# @!attribute supply_workers
|
20
|
+
# Supply used in workers. alias of #food_workers
|
21
|
+
# @return [Integer]
|
22
|
+
def supply_workers = food_workers
|
23
|
+
end
|
24
|
+
end
|
25
|
+
Api::PlayerCommon.prepend Api::PlayerCommonExt
|
@@ -23,7 +23,7 @@ module Api
|
|
23
23
|
# Get the unit as from the previous frame. Good for comparison.
|
24
24
|
# @return [Api::Unit, nil] this unit from the previous frame or nil if it wasn't present
|
25
25
|
def previous
|
26
|
-
@bot.previous.all_units[tag
|
26
|
+
@bot.previous.all_units&.[](tag)
|
27
27
|
end
|
28
28
|
|
29
29
|
# Returns whether a unit is alive or not
|
@@ -48,7 +48,7 @@ module Api
|
|
48
48
|
|
49
49
|
# Attributes ---
|
50
50
|
|
51
|
-
# Returns static
|
51
|
+
# Returns static an array of attributes for a unit
|
52
52
|
# @return [Array<Api::Attribute>]
|
53
53
|
def attributes
|
54
54
|
unit_data.attributes
|
@@ -111,7 +111,7 @@ module Api
|
|
111
111
|
has_attribute?(:STRUCTURE)
|
112
112
|
end
|
113
113
|
|
114
|
-
# Checks if unit is
|
114
|
+
# Checks if unit is hovering
|
115
115
|
# @return [Boolean] whether unit has attribute :Hover
|
116
116
|
def is_hover?
|
117
117
|
has_attribute?(:HOVER)
|
@@ -133,13 +133,31 @@ module Api
|
|
133
133
|
|
134
134
|
# Helpers for unit properties
|
135
135
|
|
136
|
-
def width = radius * 2
|
136
|
+
def width = radius * 2.0
|
137
137
|
# @!parse
|
138
138
|
# # @!attribute width
|
139
139
|
# # width = radius * 2
|
140
140
|
# # @return [Float]
|
141
141
|
# attr_reader :width
|
142
142
|
|
143
|
+
# @!attribute [r] full_health?
|
144
|
+
# @return [Boolean] health is at maximum
|
145
|
+
def full_health?
|
146
|
+
health == health_max
|
147
|
+
end
|
148
|
+
|
149
|
+
# @!attribute [r] full_shields?
|
150
|
+
# @return [Boolean] shields are at maximum
|
151
|
+
def full_shields?
|
152
|
+
shield == shield_max
|
153
|
+
end
|
154
|
+
|
155
|
+
# @!attribute [r] full_energy?
|
156
|
+
# @return [Boolean] energy is at maximum
|
157
|
+
def full_energy?
|
158
|
+
energy == energy_max
|
159
|
+
end
|
160
|
+
|
143
161
|
# Some overrides to allow question mark references to boolean properties
|
144
162
|
|
145
163
|
# @!attribute [r] is_flying?
|
@@ -179,6 +197,14 @@ module Api
|
|
179
197
|
# @return [Boolean]
|
180
198
|
def is_ground? = !is_flying?
|
181
199
|
|
200
|
+
# @!attribute [r] is_cloaked??
|
201
|
+
# Returns whether the unit is cloaked. Revealed cloak units also return true.
|
202
|
+
# For further distinction, use Unit#cloaked, which uses enum CloakedState
|
203
|
+
# @return [Boolean]
|
204
|
+
def is_cloaked?
|
205
|
+
cloak != :NOT_CLOAKED && cloak != :CLOAKED_UNKNOWN
|
206
|
+
end
|
207
|
+
|
182
208
|
# @!endgroup Virtual properties
|
183
209
|
|
184
210
|
# Whether unit is effected by buff_id
|
@@ -315,14 +341,18 @@ module Api
|
|
315
341
|
)
|
316
342
|
end
|
317
343
|
|
318
|
-
# Draws a sphere around the unit's attack range
|
319
|
-
# @param weapon_index [
|
344
|
+
# Draws a sphere around the unit's attack range (weapon range + radius)
|
345
|
+
# @param weapon_index [Integer] default first weapon, see UnitTypeData#weapons
|
320
346
|
# @param color [Api::Color] optional api color, default red
|
321
347
|
def debug_fire_range(weapon_index = 0, color = nil)
|
348
|
+
attack_range = unit_data.weapons[weapon_index]&.range
|
349
|
+
return if attack_range.nil?
|
350
|
+
attack_range += radius
|
351
|
+
|
322
352
|
color = Api::Color.new(r: 255, b: 0, g: 0) if color.nil?
|
323
|
-
attack_range = unit_data.weapons[weapon_index].range
|
324
353
|
raised_position = pos.dup
|
325
354
|
raised_position.z += 0.01
|
355
|
+
|
326
356
|
@bot.debug_draw_sphere(point: raised_position, radius: attack_range, color:)
|
327
357
|
end
|
328
358
|
|
@@ -413,22 +443,30 @@ module Api
|
|
413
443
|
end
|
414
444
|
|
415
445
|
# Checks whether enemy is within range of weapon or ability and can target ground/air.
|
416
|
-
#
|
446
|
+
# By default, checks all weapons.
|
447
|
+
# Pass weapon_index or ability_id to target a specific source of damage.
|
417
448
|
# @param unit [Api::Unit] enemy
|
418
|
-
# @param weapon_index [Integer]
|
449
|
+
# @param weapon_index [Integer] passing this will select a specific Weapon
|
419
450
|
# @param ability_id [Integer] passing this will override weapon Api::AbilityId::*
|
420
451
|
# @return [Boolean]
|
421
452
|
# @example
|
453
|
+
# queen.can_attack?(enemy, weapon_index: 0) # Air attack
|
454
|
+
# queen.can_attack?(enemy, weapon_index: 1) # Ground attack
|
455
|
+
# queen.can_attack?(enemy) # Auto detect (scans all weapons)
|
422
456
|
# ghost.can_attack?(enemy, weapon_index: 0, ability_id: Api::AbilityId::SNIPE)
|
423
|
-
def can_attack?(unit:, weapon_index:
|
424
|
-
if
|
457
|
+
def can_attack?(unit:, weapon_index: nil, ability_id: nil)
|
458
|
+
return false if unit.nil?
|
459
|
+
if ability_id
|
460
|
+
# ability
|
461
|
+
ability = @bot.ability_data(ability_id)
|
462
|
+
can_ability_target_unit?(unit:, ability:)
|
463
|
+
elsif weapon_index
|
425
464
|
# weapon
|
426
465
|
source_weapon = weapon(weapon_index)
|
427
466
|
can_weapon_target_unit?(unit:, weapon: source_weapon)
|
428
467
|
else
|
429
|
-
#
|
430
|
-
|
431
|
-
can_ability_target_unit?(unit:, ability:)
|
468
|
+
# auto-detect weapon
|
469
|
+
unit_data.weapons.any? { |weapon| can_weapon_target_unit?(unit:, weapon:) }
|
432
470
|
end
|
433
471
|
end
|
434
472
|
|
@@ -445,6 +483,7 @@ module Api
|
|
445
483
|
# @param weapon [Api::Weapon]
|
446
484
|
# @return [Boolean]
|
447
485
|
def can_weapon_target_unit?(unit:, weapon:)
|
486
|
+
return false if unit.nil? || weapon.nil?
|
448
487
|
# false if enemy is air and we can only shoot ground
|
449
488
|
return false if unit.is_flying && weapon.type == :GROUND # Api::Weapon::TargetType::GROUND
|
450
489
|
|
@@ -455,7 +494,12 @@ module Api
|
|
455
494
|
in_attack_range?(unit:, range: weapon.range)
|
456
495
|
end
|
457
496
|
|
497
|
+
# Checks whether an ability can target a unit
|
498
|
+
# @param unit [Api::Unit]
|
499
|
+
# @param ability [Api::AbilityData]
|
500
|
+
# @return [Boolean]
|
458
501
|
def can_ability_target_unit?(unit:, ability:)
|
502
|
+
return false if unit.nil? || ability.nil?
|
459
503
|
# false if enemy is air and we can only shoot ground
|
460
504
|
return false if ability.target == Api::AbilityData::Target::NONE
|
461
505
|
|
@@ -495,6 +539,7 @@ module Api
|
|
495
539
|
# This value should be correct for building placement math (unit.radius is not good for this)
|
496
540
|
# @return [Float] placement radius
|
497
541
|
def footprint_radius
|
542
|
+
return 0.0 if unit_data.ability_id == 0
|
498
543
|
@bot.data.abilities[unit_data.ability_id].footprint_radius
|
499
544
|
end
|
500
545
|
|
@@ -23,6 +23,36 @@ module Api
|
|
23
23
|
# i.e. 2 for Supply Depot (2x2)
|
24
24
|
# @return [Integer] side-length for placement
|
25
25
|
attr_accessor :placement_length
|
26
|
+
|
27
|
+
# @!attribute ground_range
|
28
|
+
# Ground weapon range or 0.0 when it can't shoot ground
|
29
|
+
# @return [Float]
|
30
|
+
attr_accessor :ground_range
|
31
|
+
|
32
|
+
# @!attribute air_range
|
33
|
+
# Air weapon range or 0.0 when it can't shoot air
|
34
|
+
# @return [Float]
|
35
|
+
attr_accessor :air_range
|
36
|
+
|
37
|
+
# @!attribute ground_damage
|
38
|
+
# Ground weapon damage or 0.0
|
39
|
+
# @return [Float]
|
40
|
+
attr_accessor :ground_damage
|
41
|
+
|
42
|
+
# @!attribute air_damage
|
43
|
+
# Air weapon damage or 0.0
|
44
|
+
# @return [Float]
|
45
|
+
attr_accessor :air_damage
|
46
|
+
|
47
|
+
# @!attribute ground_dps
|
48
|
+
# Ground weapon damage per second or 0.0
|
49
|
+
# @return [Float]
|
50
|
+
attr_accessor :ground_dps
|
51
|
+
|
52
|
+
# @!attribute air_dps
|
53
|
+
# Air weapon damage per second or 0.0
|
54
|
+
# @return [Float]
|
55
|
+
attr_accessor :air_dps
|
26
56
|
end
|
27
57
|
end
|
28
58
|
Api::UnitTypeData.prepend Api::UnitTypeDataExtension
|
@@ -74,6 +74,18 @@ module Sc2
|
|
74
74
|
Api::UnitTypeId::HATCHERY, Api::UnitTypeId::HIVE, Api::UnitTypeId::LAIR,
|
75
75
|
Api::UnitTypeId::NEXUS
|
76
76
|
].freeze
|
77
|
+
|
78
|
+
# An array of passive detector unit types.
|
79
|
+
# Does not include units with detection abilities, such as Oracle/Ghost
|
80
|
+
# @return [Array<Integer>]
|
81
|
+
TYPE_DETECTORS = [
|
82
|
+
Api::UnitTypeId::RAVEN, Api::UnitTypeId::MISSILETURRET,
|
83
|
+
Api::UnitTypeId::OBSERVER, Api::UnitTypeId::OBSERVERSIEGEMODE,
|
84
|
+
Api::UnitTypeId::PHOTONCANNON,
|
85
|
+
Api::UnitTypeId::OVERSEER, Api::UnitTypeId::OVERSEERSIEGEMODE,
|
86
|
+
Api::UnitTypeId::SPORECRAWLER
|
87
|
+
].freeze
|
88
|
+
|
77
89
|
# Returns a new UnitGroup containing all units matching unit type id(s)
|
78
90
|
# Multiple values work as an "OR" filter
|
79
91
|
# @example
|
@@ -254,6 +266,12 @@ module Sc2
|
|
254
266
|
alias_method :extractors, :gas
|
255
267
|
alias_method :assimilators, :gas
|
256
268
|
|
269
|
+
# Selects units passively have Detection
|
270
|
+
# @return [UnitGroup] gas structures
|
271
|
+
def detectors
|
272
|
+
select_type(TYPE_DETECTORS)
|
273
|
+
end
|
274
|
+
|
257
275
|
# Selects only units which have finished constructing, i.o.w. build_progress == 1.0
|
258
276
|
# @return [UnitGroup] complete unit group
|
259
277
|
def completed
|
@@ -374,7 +392,7 @@ module Sc2
|
|
374
392
|
select_type(Api::UnitTypeId::CREEPTUMORBURROWED)
|
375
393
|
end
|
376
394
|
|
377
|
-
#
|
395
|
+
# PROTOSS ---
|
378
396
|
|
379
397
|
# Selects pylons
|
380
398
|
# @return [Sc2::UnitGroup] pylons
|
@@ -416,7 +434,10 @@ module Sc2
|
|
416
434
|
# @param pos [Sc2::Position] unit.pos or a point of any kind
|
417
435
|
# @return [Sc2::UnitGroup, Api::Unit, nil] return group or single unit if amount is not supplied
|
418
436
|
def nearest_to(pos:, amount: nil)
|
419
|
-
|
437
|
+
unless amount.nil?
|
438
|
+
return UnitGroup.new if amount <= 0
|
439
|
+
return self if amount > @units.size
|
440
|
+
end
|
420
441
|
|
421
442
|
if use_kdtree
|
422
443
|
if amount.nil?
|
data/lib/sc2ai/version.rb
CHANGED
@@ -8,13 +8,7 @@ ruby_dir=$base_dir/.ruby
|
|
8
8
|
export PATH=$ruby_dir/bin:$PATH
|
9
9
|
export LD_LIBRARY_PATH=$(ruby -e 'puts RbConfig::CONFIG["libdir"]')
|
10
10
|
|
11
|
-
|
12
|
-
export RUBY_GC_HEAP_INIT_SIZE_40_SLOTS=340000
|
13
|
-
export RUBY_GC_HEAP_INIT_SIZE_80_SLOTS=50000
|
14
|
-
export RUBY_GC_HEAP_INIT_SIZE_160_SLOTS=18000
|
15
|
-
export RUBY_GC_HEAP_INIT_SIZE_320_SLOTS=2000
|
16
|
-
export RUBY_GC_HEAP_INIT_SIZE_640_SLOTS=1000
|
17
|
-
|
11
|
+
export RUBY_YJIT_ENABLE=1
|
18
12
|
export AIARENA=true
|
19
13
|
sc2ai ladderconfig
|
20
14
|
exec sc2ai laddermatch "$@" 1>&2
|
@@ -89,7 +89,7 @@ message UnitTypeData {
|
|
89
89
|
optional float sight_range = 25; // Range unit reveals vision.
|
90
90
|
|
91
91
|
repeated uint32 tech_alias = 21 [packed=false]; // Other units that satisfy the same tech requirement.
|
92
|
-
optional uint32 unit_alias = 22
|
92
|
+
optional uint32 unit_alias = 22; // The morphed variant of this unit.
|
93
93
|
|
94
94
|
optional uint32 tech_requirement = 23; // Structure required to build this unit. (Or any with the same tech_alias)
|
95
95
|
optional bool require_attached = 24; // Whether tech_requirement is an add-on.
|