sc2ai 0.0.3 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f4d88f09b0eaa016360346fe4b150664feb3072792ded12302267a2cf64d2854
4
- data.tar.gz: 50f38a9008d26a62fbe9fae1f05076dee04544ea73b8e72e8f9447371501f54b
3
+ metadata.gz: fcc96e2f18e9cc95b0fd8b9427d3c76866551031b69811251b547eeb13284cba
4
+ data.tar.gz: 88c3dce17dba1e6b4378c306de8d754e1f33dcd092a9e44ac7477e50940ca911
5
5
  SHA512:
6
- metadata.gz: e3f980a5519d31a49cb890cb529d213b34819dd3a4888301ea7dfde1b2edf9fda7685fb62d28c4187809b5b9a646e1bed2e4af6b86f037c5207c8d515306d88a
7
- data.tar.gz: 579de21d38e0f596cdc7fc29a43d878caa4e72b4a9a995e22c26edc0b3bfda4c79b266703a8eaea0e60b513e88a6bcaa5de196d8e8d90e353344925666bb7ecb
6
+ metadata.gz: 39997d407d6fbf2ffc936c387f703d4b12d6aff1f016bc1905148a6590f156c4f39acee982074be28d33ea9451cbbf483f985c62a902693c65da450465d915e2
7
+ data.tar.gz: af9ed623bf2ee6ce905127fb89828a45c95456b4996e1aa4f6c3d477d31b383ed54d425de80abcd28f2df9cf08bfa127fbd36c89f822e226804b116bf20e73c1
@@ -17,7 +17,7 @@ module Sc2
17
17
  # Queues a Api::ActionRaw. Perform ability on unit_tags optionally on target_world_space_pos/target_unit_tag
18
18
  # @param unit_tags [Array<Integer>]
19
19
  # @param ability_id [Integer]
20
- # @param queue_command [Boolean] Shift+Click, default: false
20
+ # @param queue_command [Boolean] shift+command
21
21
  # @param target_world_space_pos [Api::Point2D]
22
22
  # @param target_unit_tag [Integer]
23
23
  def action_raw_unit_command(unit_tags:, ability_id:, queue_command: false, target_world_space_pos: nil, target_unit_tag: nil)
@@ -39,7 +39,7 @@ module Sc2
39
39
  # @param units [Array<Integer>,Integer,Api::Unit] can be an Api::Unit, array of Api::Unit#tag or single tag
40
40
  # @param ability_id [Integer]
41
41
  # @param target [Api::Unit, Integer, Api::Point2D] is a unit, unit tag or a Api::Point2D
42
- # @param queue_command [Boolean] Shift+Click, default: false
42
+ # @param queue_command [Boolean] shift+command
43
43
  def action(units:, ability_id:, target: nil, queue_command: false)
44
44
  unit_tags = unit_tags_from_source(units)
45
45
 
@@ -79,6 +79,7 @@ module Sc2
79
79
  # Warps in unit type at target (location or pylon) with optional source units (warp gates)
80
80
  # When not specifying the specific warp gate(s), all warpgates will be used
81
81
  # @param unit_type_id [Integer] Api::UnitTypeId the unit type which will do the creation
82
+ # @param queue_command [Boolean] shift+command
82
83
  # @param target [Api::Point2D, Integer] is a unit tag or a Api::Point2D
83
84
  def warp(unit_type_id:, target:, queue_command:, units: nil)
84
85
  warp_ability = Api::TechTree.unit_type_creation_abilities(
@@ -128,7 +129,7 @@ module Sc2
128
129
  # @param ability_id [Api::AbilityId]
129
130
  # @param target_screen_coord [Api::Point2I]
130
131
  # @param target_minimap_coord [Api::Point2I]
131
- # @param queue_command [Boolean] Shift+Click, default: false
132
+ # @param queue_command [Boolean] shift+command
132
133
  # @return [void]
133
134
  def action_spatial_unit_command(ability_id:, target_screen_coord: nil, target_minimap_coord: nil, queue_command: false)
134
135
  queue_action Api::Action.new(
@@ -37,6 +37,52 @@ module Sc2
37
37
  )
38
38
  end
39
39
 
40
+ # Prints text on screen from top and left
41
+ # @param text [String] will respect newlines
42
+ # @param left_percent [Numeric] range 0..100. percent from left of screen
43
+ # @param top_percent [Numeric] range 0..100. percent from top of screen
44
+ # @param color [Api::Color] default white
45
+ # @param size [Size] of font, default 14px
46
+ # @return [void]
47
+ def debug_text_screen(text, left_percent: 1.0, top_percent: 1.0, color: nil, size: 14)
48
+ queue_debug_command Api::DebugCommand.new(
49
+ draw: Api::DebugDraw.new(
50
+ text: [
51
+ Api::DebugText.new(
52
+ text:,
53
+ virtual_pos: Api::Point.new(
54
+ x: left_percent.to_f / 100,
55
+ y: top_percent.to_f / 100
56
+ ),
57
+ color:,
58
+ size:
59
+ )
60
+ ]
61
+ )
62
+ )
63
+ end
64
+
65
+ # Prints text on screen at 3d world position
66
+ # @param text [String] will respect newlines
67
+ # @param point [Api::Point] point in the world, i.e. unit.pos
68
+ # @param color [Api::Color] default white
69
+ # @param size [Size] of font, default 14px
70
+ # @return [void]
71
+ def debug_text_world(text, point:, color: nil, size: 14)
72
+ queue_debug_command Api::DebugCommand.new(
73
+ draw: Api::DebugDraw.new(
74
+ text: [
75
+ Api::DebugText.new(
76
+ text:,
77
+ world_pos: point,
78
+ color:,
79
+ size:
80
+ )
81
+ ]
82
+ )
83
+ )
84
+ end
85
+
40
86
  # Draws a line between two Api::Point's for color
41
87
  # @param p0 [Api::Point] the first point
42
88
  # @param p1 [Api::Point] the second point
@@ -63,8 +109,8 @@ module Sc2
63
109
  # # Draws a box on structure placement grid
64
110
  # debug_draw_box(point: unit.pos, radius: unit.footprint_radius)
65
111
  #
66
- # Note: Api::Color RGB is broken for this command. Will use min(r,b)
67
- # Note: Z index is elevated 0.01 so the line is visible and doesn't clip through terrain
112
+ # @note Api::Color RGB is broken for this command. Will use min(r,b)
113
+ # @note Z index is elevated 0.02 so the line is visible and doesn't clip through terrain
68
114
  # @param point [Api::Point]
69
115
  # @param radius [Float] default one tile wide, 1.0
70
116
  # @param color [Api::Color] default white. min(r,b) is used for both r&b
@@ -74,8 +120,8 @@ module Sc2
74
120
  draw: Api::DebugDraw.new(
75
121
  boxes: [
76
122
  Api::DebugBox.new(
77
- min: Api::Point.new(x: point.x - radius, y: point.y - radius, z: point.z + 0.01),
78
- max: Api::Point.new(x: point.x + radius, y: point.y + radius, z: point.z + (radius * 2) + 0.01),
123
+ min: Api::Point.new(x: point.x - radius, y: point.y - radius, z: point.z + 0.02),
124
+ max: Api::Point.new(x: point.x + radius, y: point.y + radius, z: point.z + (radius * 2) + 0.02),
79
125
  color:
80
126
  )
81
127
  ]
@@ -7,7 +7,7 @@ module Sc2
7
7
  class Player
8
8
  # Holds map and geography helper functions
9
9
  class Geometry
10
- # @!attribute Holds the parent bot object
10
+ # @!attribute bot
11
11
  # @return [Sc2::Player] player with active connection
12
12
  attr_accessor :bot
13
13
 
@@ -72,6 +72,7 @@ module Sc2
72
72
  # Each value in [row][column] holds a boolean value represented as an integer
73
73
  # It does not say whether a position is occupied by another building.
74
74
  # One pixel covers one whole block. Rounds fractionated positions down.
75
+ # @return [Numo::Bit]
75
76
  def parsed_placement_grid
76
77
  if @parsed_placement_grid.nil?
77
78
  image_data = bot.game_info.start_raw.placement_grid
@@ -83,6 +84,7 @@ module Sc2
83
84
  end
84
85
 
85
86
  # Returns a grid where ony the expo locations are marked
87
+ # @return [Numo::Bit]
86
88
  def expo_placement_grid
87
89
  if @expo_placement_grid.nil?
88
90
  @expo_placement_grid = Numo::Bit.zeros(map_height, map_width)
@@ -97,6 +99,7 @@ module Sc2
97
99
  end
98
100
 
99
101
  # Returns a grid where powered locations are marked true
102
+ # @return [Numo::Bit]
100
103
  def parsed_power_grid
101
104
  # Cache for based on power unit tags
102
105
  cache_key = bot.power_sources.map(&:tag).sort.hash
@@ -399,12 +402,25 @@ module Sc2
399
402
  output_grid
400
403
  end
401
404
 
405
+ # Returns own 2d start position as set by initial camera
406
+ # This differs from position of first base structure
407
+ # @return [Api::Point2D]
408
+ def start_position
409
+ @start_position ||= bot.observation.raw_data.player.camera
410
+ end
411
+
412
+ # Returns the enemy 2d start position
413
+ # @return [Api::Point2D]
414
+ def enemy_start_position
415
+ bot.game_info.start_raw.start_locations.first
416
+ end
417
+
402
418
  # Gets expos and surrounding minerals
403
419
  # The index is a build location for an expo and the value is a UnitGroup, which has minerals and geysers
404
420
  # @example
405
- # random_expo = expansions.keys.sample #=> Point2D
421
+ # random_expo = geo.expansions.keys.sample #=> Point2D
406
422
  # expo_resources = geo.expansions[random_expo] #=> UnitGroup
407
- # alive_minerals = expo_resources.minerals - neutral.minerals
423
+ # alive_minerals = expo_resources.minerals & neutral.minerals
408
424
  # geysers = expo_resources.geysers
409
425
  # @return [Hash<Api::Point2D, UnitGroup>] Location => UnitGroup of resources (minerals+geysers)
410
426
  def expansions
@@ -503,24 +519,77 @@ module Sc2
503
519
  end
504
520
 
505
521
  # Returns a slice of #expansions where a base hasn't been built yet
522
+ # The has index is a build position and the value is a UnitGroup of resources for the base
506
523
  # @example
507
524
  # # Lets find the nearest unoccupied expo
508
525
  # expo_pos = expansions_unoccupied.keys.min { |p2d| p2d.distance_to(structures.hq.first) }
509
526
  # # What minerals/geysers does it have?
510
527
  # puts expansions_unoccupied[expo_pos].minerals # or expansions[expo_pos]... => UnitGroup
511
528
  # puts expansions_unoccupied[expo_pos].geysers # or expansions[expo_pos]... => UnitGroup
512
- # @return [Hash<Api::Point2D], UnitGroup] Location => UnitGroup of resources (minerals+geysers)
529
+ # @return [Hash<Api::Point2D, UnitGroup>] Location => UnitGroup of resources (minerals+geysers)
513
530
  def expansions_unoccupied
514
531
  taken_bases = bot.structures.hq.map { |hq| hq.pos.to_p2d } + bot.enemy.structures.hq.map { |hq| hq.pos.to_p2d }
515
532
  remaining_points = expansion_points - taken_bases
516
533
  expansions.slice(*remaining_points)
517
534
  end
518
535
 
536
+ # Gets minerals for a base or base position
537
+ # @param base [Api::Unit, Sc2::Position] base Unit or Position
538
+ # @return [Sc2::UnitGroup] UnitGroup of minerals for the base
539
+ def minerals_for_base(base)
540
+ # resources_for_base contains what we need, but slice neutral.minerals,
541
+ # so that only active patches remain
542
+ bot.neutral.minerals.slice(*resources_for_base(base).minerals.tags)
543
+ end
544
+
545
+ # Gets geysers for a base or base position
546
+ # @param base [Api::Unit, Sc2::Position] base Unit or Position
547
+ # @return [Sc2::UnitGroup] UnitGroup of geysers for the base
548
+ def geysers_for_base(base)
549
+ resources_for_base(base).geysers
550
+ end
551
+
552
+ # @private
553
+ # @param base [Api::Unit, Sc2::Position] base Unit or Position
554
+ # @return [Sc2::UnitGroup] UnitGroup of resources (minerals+geysers)
555
+ private def resources_for_base(base)
556
+ pos = base.is_a?(Api::Unit) ? base.pos : base
557
+
558
+ # If we have a base setup for this exact position, use it
559
+ if expansions.has_key?(pos)
560
+ return expansions[pos]
561
+ end
562
+
563
+ # Tolerance for misplaced base: Find the nearest base to this position
564
+ pos = expansion_points.min_by { |p| p.distance_to(pos) }
565
+
566
+ expansions[pos]
567
+ end
568
+
569
+ # Gets gasses for a base or base position
570
+ # @param base [Api::Unit, Sc2::Position] base Unit or Position
571
+ # @return [Sc2::UnitGroup] UnitGroup of geysers for the base
572
+ def gas_for_base(base)
573
+ # No gas structures at all yet, return nothing
574
+ return UnitGroup.new if bot.structures.gas.size.zero?
575
+
576
+ geysers = geysers_for_base(base)
577
+
578
+ # Mineral-only base, return nothing
579
+ return UnitGroup.new if geysers.size == 0
580
+
581
+ # Loop and collect gasses places exactly on-top of geysers
582
+ bot.structures.gas.select do |gas|
583
+ geysers.any? { |geyser| geyser.pos.to_p2d.eql?(gas.pos.to_p2d) }
584
+ end
585
+ end
586
+
519
587
  # Gets buildable point grid for squares of size, i.e. 3 = 3x3 placements
520
588
  # Uses pathing grid internally, to ignore taken positions
521
589
  # Does not query the api and is generally fast.
522
590
  # @param length [Integer] length of the building, 2 for depot/pylon, 3 for rax/gate
523
- # @param on_creep [Boolean] whether this build locatin should be on creep
591
+ # @param on_creep [Boolean] whether this build location should be on creep
592
+ # @return [Array<Array<(Float, Float)>>] Array of [x,y] tuples
524
593
  def build_coordinates(length:, on_creep: false, in_power: false)
525
594
  length = 1 if length < 1
526
595
  @_build_coordinates ||= {}
@@ -47,10 +47,10 @@ module Sc2
47
47
 
48
48
  # Event-driven unit groups ---
49
49
 
50
+ # @!attribute event_units_created
50
51
  # Units created since last frame (visible only, units not structures)
51
52
  # Read this on_step. Alternative to callback on_unit_created
52
- # Note: Morphed units should watch #event_units_type_changed
53
- # @!attribute event_units_created
53
+ # @note Morphed units should watch #event_units_type_changed
54
54
  # @return [Sc2::UnitGroup] group of created units
55
55
  attr_accessor :event_units_created
56
56
 
data/lib/sc2ai/player.rb CHANGED
@@ -97,7 +97,7 @@ module Sc2
97
97
  @difficulty = difficulty
98
98
  @ai_build = ai_build
99
99
  @realtime = false
100
- @step_count = 1
100
+ @step_count = 2
101
101
 
102
102
  @enable_feature_layer = false
103
103
  @interface_options = {}
@@ -201,7 +201,7 @@ module Sc2
201
201
 
202
202
  # Override to customize initialization
203
203
  # Alias of before_join
204
- # You can enable_feature_layer=true, set step_size, define
204
+ # You can enable_feature_layer=true, set step_count, define
205
205
  # @example
206
206
  # def configure
207
207
  # step_count = 4 # Update less frequently
@@ -490,6 +490,8 @@ module Sc2
490
490
  def started
491
491
  # Calculate expansions
492
492
  geo.expansions
493
+ # Set our start position base on camera
494
+ geo.start_position
493
495
  end
494
496
 
495
497
  # Moves emulation ahead and calls back #on_step
@@ -498,9 +500,7 @@ module Sc2
498
500
  # Sc2.logger.debug "#{self.class} step_forward"
499
501
 
500
502
  unless @realtime
501
- # ##TODO: Numsteps as config
502
- num_steps = 1
503
- @api.step(num_steps)
503
+ @api.step(@step_count)
504
504
  end
505
505
 
506
506
  refresh_state
@@ -610,9 +610,9 @@ module Sc2
610
610
  # ##TODO: perfect loop implementation
611
611
  # observation has an optional param game_loop and will only return once that step is reached (blocking).
612
612
  # without it, it returns things as they are.
613
- # broadly, i think this is what it should be doing, with step_size being minimum of 1, so no zero-steps occur.
613
+ # broadly, i think this is what it should be doing, with step_count being minimum of 1, so no zero-steps occur.
614
614
  # @example
615
- # desired_game_loop = current_game_loop + step_size
615
+ # desired_game_loop = current_game_loop + step_count
616
616
  # response = client.observation(game_loop: desired_game_loop)
617
617
  #
618
618
  # if response.game_loop > desired_game_loop {
@@ -1,6 +1,15 @@
1
1
  module Api
2
2
  # Adds additional functionality to message object Api::Point
3
3
  module PointExtension
4
+ # @private
5
+ def hash
6
+ [x, y, z].hash
7
+ end
8
+
9
+ def eql?(other)
10
+ self.class == other.class && hash == other.hash
11
+ end
12
+
4
13
  # Creates a Point2D using x and y
5
14
  # @return [Api::Point2D]
6
15
  def to_p2d
@@ -13,6 +22,9 @@ module Api
13
22
  # @example
14
23
  # Api::Point[1,2,3] # Where x is 1.0, y is 2.0 and z is 3.0
15
24
  # @return [Api::Point]
25
+ # @param [Float] x
26
+ # @param [Float] y
27
+ # @param [Float] z
16
28
  def [](x, y, z)
17
29
  Api::Point.new(x: x, y: y, z: z)
18
30
  end
@@ -20,4 +32,4 @@ module Api
20
32
  end
21
33
  end
22
34
  Api::Point.include Api::PointExtension
23
- Api::Point.include Api::PointExtension::ClassMethods
35
+ Api::Point.extend Api::PointExtension::ClassMethods
@@ -179,6 +179,26 @@ module Api
179
179
  action(ability_id: Api::AbilityId::SMART, target:, queue_command:)
180
180
  end
181
181
 
182
+ # Shorthand for performing action MOVE
183
+ # @param target [Api::Unit, Integer, Api::Point2D] is a unit, unit tag or a Api::Point2D
184
+ # @param queue_command [Boolean] shift+command
185
+ def move(target:, queue_command: false)
186
+ action(ability_id: Api::AbilityId::MOVE, target:, queue_command:)
187
+ end
188
+
189
+ # Shorthand for performing action STOP
190
+ # @param queue_command [Boolean] shift+command
191
+ def stop(queue_command: false)
192
+ action(ability_id: Api::AbilityId::STOP, queue_command:)
193
+ end
194
+
195
+ # Shorthand for performing action HOLDPOSITION
196
+ # @param queue_command [Boolean] shift+command
197
+ def hold(queue_command: false)
198
+ action(ability_id: Api::AbilityId::HOLDPOSITION, queue_command:)
199
+ end
200
+ alias_method :hold_position, :hold
201
+
182
202
  # Shorthand for performing action ATTACK
183
203
  # @param target [Api::Unit, Integer, Api::Point2D] is a unit, unit tag or a Api::Point2D
184
204
  # @param queue_command [Boolean] shift+command
@@ -224,6 +244,7 @@ module Api
224
244
  # Draws a placement outline
225
245
  # @param color [Api::Color] optional api color, default white
226
246
  # @return [void]
247
+ # noinspection RubyArgCount
227
248
  def debug_draw_placement(color = nil)
228
249
  # Slightly elevate the Z position so that the line doesn't clip into the terrain at same Z level
229
250
  z_elevated = pos.z + 0.01
@@ -432,6 +453,12 @@ module Api
432
453
  build_progress == 1.0 # standard:disable Lint/FloatComparison
433
454
  end
434
455
 
456
+ # Returns true if build progress is < 100%
457
+ # @return [Boolean]
458
+ def in_progress?
459
+ !is_completed?
460
+ end
461
+
435
462
  # Convenience functions ---
436
463
 
437
464
  # TERRAN Convenience functions ---
@@ -444,7 +471,7 @@ module Api
444
471
 
445
472
  # Returns whether the structure has a reactor add-on
446
473
  # @return [Boolean] if the unit has a reactor attached
447
- def has_reactor
474
+ def has_reactor?
448
475
  Sc2::UnitGroup::TYPE_REACTOR.include?(add_on&.unit_type)
449
476
  end
450
477
 
@@ -455,26 +482,46 @@ module Api
455
482
  # # Get the actual tech-lab with #add_on
456
483
  # sp.add_on.research ...
457
484
  # @return [Boolean] if the unit has a tech lab attached
458
- def has_tech_lab
485
+ def has_tech_lab?
459
486
  Sc2::UnitGroup::TYPE_TECHLAB.include?(add_on&.unit_type)
460
487
  end
461
488
 
462
489
  # For Terran builds a tech lab add-on on the current structure
463
490
  # @return [void]
464
- def build_reactor
465
- build(unit_type_id: Api::UnitTypeId::REACTOR)
491
+ def build_reactor(queue_command: false)
492
+ unit_type_id = case unit_type
493
+ when Api::UnitTypeId::BARRACKS, Api::UnitTypeId::BARRACKSFLYING
494
+ Api::UnitTypeId::BARRACKSREACTOR
495
+ when Api::UnitTypeId::FACTORY, Api::UnitTypeId::FACTORYFLYING
496
+ Api::UnitTypeId::FACTORYREACTOR
497
+ when Api::UnitTypeId::STARPORT, Api::UnitTypeId::STARPORTFLYING
498
+ Api::UnitTypeId::STARPORTREACTOR
499
+ end
500
+ build(unit_type_id: unit_type_id, queue_command:)
466
501
  end
467
502
 
468
503
  # For Terran builds a tech lab add-on on the current structure
469
504
  # @return [void]
470
- def build_tech_lab
471
- build(unit_type_id: Api::UnitTypeId::TECHLAB)
505
+ def build_tech_lab(queue_command: false)
506
+ unit_type_id = case unit_type
507
+ when Api::UnitTypeId::BARRACKS, Api::UnitTypeId::BARRACKSFLYING
508
+ Api::UnitTypeId::BARRACKSTECHLAB
509
+ when Api::UnitTypeId::FACTORY, Api::UnitTypeId::FACTORYFLYING
510
+ Api::UnitTypeId::FACTORYTECHLAB
511
+ when Api::UnitTypeId::STARPORT, Api::UnitTypeId::STARPORTFLYING
512
+ Api::UnitTypeId::STARPORTTECHLAB
513
+ end
514
+ build(unit_type_id: unit_type_id, queue_command:)
472
515
  end
473
516
 
517
+ # GENERAL Convenience functions ---
518
+
519
+ # ...
520
+
474
521
  private
475
522
 
476
523
  # @private
477
- # Reduces repitition in the is_*action*?(target:) methods
524
+ # Reduces repetition in the is_*action*?(target:) methods
478
525
  def is_performing_ability_on_target?(abilities, target: nil)
479
526
  # Exit if not actioning the ability
480
527
  return false unless is_performing_ability?(abilities)
@@ -46,7 +46,7 @@ module Sc2
46
46
  # @param queue_command [Boolean] shift+command
47
47
  def warp(unit_type_id:, target: nil, queue_command: false)
48
48
  return if size.zero?
49
- bot&.warp(units: self, unit_type_id:, target: target, queue_command:)
49
+ bot&.warp(units: self, unit_type_id:, target:, queue_command:)
50
50
  end
51
51
 
52
52
  # Shorthand for performing action SMART (right-click)
@@ -56,11 +56,31 @@ module Sc2
56
56
  action(ability_id: Api::AbilityId::SMART, target:, queue_command:)
57
57
  end
58
58
 
59
+ # Shorthand for performing action MOVE
60
+ # @param target [Api::Unit, Integer, Api::Point2D] is a unit, unit tag or a Api::Point2D
61
+ # @param queue_command [Boolean] shift+command
62
+ def move(target:, queue_command: false)
63
+ action(ability_id: Api::AbilityId::MOVE, target:, queue_command:)
64
+ end
65
+
66
+ # Shorthand for performing action STOP
67
+ # @param queue_command [Boolean] shift+command
68
+ def stop(queue_command: false)
69
+ action(ability_id: Api::AbilityId::STOP, queue_command:)
70
+ end
71
+
72
+ # Shorthand for performing action HOLDPOSITION
73
+ # @param queue_command [Boolean] shift+command
74
+ def hold(queue_command: false)
75
+ action(ability_id: Api::AbilityId::HOLDPOSITION, queue_command:)
76
+ end
77
+ alias_method :hold_position, :hold
78
+
59
79
  # Shorthand for performing action ATTACK
60
80
  # @param target [Api::Unit, Integer, Api::Point2D] is a unit, unit tag or a Api::Point2D
61
81
  # @param queue_command [Boolean] shift+command
62
82
  def attack(target:, queue_command: false)
63
- action(ability_id: Api::AbilityId::ATTACK, target: target, queue_command: queue_command)
83
+ action(ability_id: Api::AbilityId::ATTACK, target:, queue_command:)
64
84
  end
65
85
 
66
86
  # Issues repair command on target
@@ -89,27 +89,32 @@ module Sc2
89
89
  @units[tags.at(index)]
90
90
  end
91
91
 
92
+ # Meta documentation methods
93
+ # @!method first
94
+ # @return [Api::Unit]
95
+ # @!method last
96
+ # @return [Api::Unit]
97
+
92
98
  # Calls the given block with each Api::Unit value
93
99
  # @example
94
- # unit_group.each {|unit| puts unit.tag } #=> 1234 ...
95
- #
96
100
  # unit_group.each do |unit|
97
101
  # puts unit.tag #=> 1234 ...
98
102
  # end
99
- def each(&block)
100
- @units.each_value(&block)
103
+ # @yieldparam unit [Api::Unit]
104
+ def each(&)
105
+ @units.each_value(&)
101
106
  end
102
107
 
103
108
  # Calls the given block with each key-value pair
104
109
  # @return [self] a new unit group with items merged
105
110
  # @example
106
- # unit_group.each {|tag, unit| puts "#{tag}: #{unit}"} #=> "12345: #<Api::Unit ...>"
107
- #
108
- # unit_group.each do |tag, unit|
111
+ # unit_group.each_with_tag do |tag, unit|
109
112
  # puts "#{tag}: #{unit}"} #=> "12345: #<Api::Unit ...>"
110
113
  # end
111
- def each_with_tag(&block)
112
- @units.each(&block)
114
+ # @yieldparam tag [Integer]
115
+ # @yieldparam unit [Api::Unit]
116
+ def each_with_tag(&)
117
+ @units.each(&)
113
118
  self
114
119
  end
115
120
 
@@ -229,12 +234,19 @@ module Sc2
229
234
  end
230
235
 
231
236
  # Selects a single random Unit without a parameter or an array of Units with a param, i.e. self.random(2)
232
- # @return []
237
+ # @return [Api::Unit]
233
238
  def sample(...)
234
239
  @units.values.sample(...)
235
240
  end
236
241
  alias_method :random, :sample
237
242
 
243
+ # Returns the first Unit for which the block returns a truthy value
244
+ # @return [Api::Unit]
245
+ def detect(...)
246
+ @units.values.find(...)
247
+ end
248
+ alias_method :find, :detect
249
+
238
250
  # def select_or(*procs)
239
251
  # result = UnitGroup.new
240
252
  # procs.each do |proc|
data/lib/sc2ai/version.rb CHANGED
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Sc2
4
4
  # gem version
5
- VERSION = "0.0.3"
5
+ VERSION = "0.0.4"
6
6
  end
@@ -4,14 +4,13 @@ class <%= @classname %> < Sc2::Player::Bot
4
4
 
5
5
  def configure
6
6
  @realtime = false # Step-mode vs Bot, Realtime vs Humans
7
- @step_size = 1 # Gives 22.4ms, typically compete at 2 (44.8ms) or 4 (179.2ms).
7
+ @step_count = 2 # 2s/22.4 = ~89.29ms step cycle time allowance
8
8
  @enable_feature_layer = false; # Enables ui_ and spatial_ actions. Advanced, and has performance cost.
9
9
  end
10
10
 
11
11
  def on_step
12
12
  if game_loop == 0
13
- enemy_start_pos = game_info.start_raw.start_locations.first
14
- units.workers.attack(target: enemy_start_pos)
13
+ units.workers.attack(target: geo.enemy_start_position)
15
14
  end
16
15
 
17
16
  # If your attack fails, "good game" and exit