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.
@@ -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
- 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
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
@@ -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 (input_grid[y + inner_y, x + inner_x]).zero?
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
@@ -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:, quantity: 1)
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
- all_unit_size = observation.raw_data.units.size
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
- # Dont parse callbacks on first loop or for neutral units
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
- CALLBACK_METHODS.include?(callback)
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
- @game_info_task = Async do
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
@@ -207,7 +207,7 @@ module Sc2
207
207
  # Conversion ---
208
208
 
209
209
  # Returns [x,y] array tuple
210
- # @return [Array[Float,Float]]
210
+ # @return [Array<Float, Float>]
211
211
  def to_axy
212
212
  [x, y]
213
213
  end
@@ -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 [Api::UnitTypeData] for a unit
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 hover
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 [Api::Color] default first weapon, see UnitTypeData.weapons
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
- # Defaults to basic weapon. Pass in ability to override
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] defaults to 0 which is it's basic weapon for it's current form
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: 0, ability_id: nil)
424
- if ability_id.nil?
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
- # ability
430
- ability = @bot.ability_data(ability_id)
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
- # Protoss ---
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
- return UnitGroup.new if !amount.nil? && amount.to_i <= 0
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
@@ -1,5 +1,5 @@
1
1
  module Sc2
2
2
  # gem version
3
3
  # @return [String]
4
- VERSION = "0.5.0"
4
+ VERSION = "0.6.2"
5
5
  end
@@ -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
- # Conservative heap sizes are based on 2x a tested medium-sized game
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
@@ -0,0 +1,11 @@
1
+ {
2
+ "Bots": {
3
+ "<%= botname %>": {
4
+ "Race": "<%= Api::Race.lookup($bot.race).to_s.capitalize %>",
5
+ "Type": "BinaryCpp",
6
+ "RootPath": "./",
7
+ "FileName": "<%= botname %>",
8
+ "Debug": false
9
+ }
10
+ }
11
+ }
@@ -21,7 +21,7 @@ test/
21
21
  tmp/
22
22
  .byebug_history
23
23
  .bundle/
24
- vendor/bundle
24
+ vendor/bundle/
25
25
  lib/bundler/man/
26
26
 
27
27
  # Other
@@ -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 [packed=false]; // The morphed variant of this unit.
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.
@@ -191,7 +191,7 @@ message ActionRawUnitCommand {
191
191
  uint64 target_unit_tag = 3;
192
192
  }
193
193
  repeated uint64 unit_tags = 4 [packed=false];
194
- optional bool queue_command = 5 [packed=false];
194
+ optional bool queue_command = 5;
195
195
  }
196
196
 
197
197
  message ActionRawCameraMove {
@@ -1,5 +1,5 @@
1
1
  # Use the ladder's boot.rb file and configure there
2
- require "./boot.rb"
2
+ require_relative "boot"
3
3
 
4
4
  # Or simply, create by hand
5
5
  #my_bot = MyBot.new(name: "Botname", race: Api::Race::RANDOM)