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 +4 -4
- data/lib/sc2ai/player/actions.rb +4 -3
- data/lib/sc2ai/player/debug.rb +50 -4
- data/lib/sc2ai/player/geometry.rb +74 -5
- data/lib/sc2ai/player/units.rb +2 -2
- data/lib/sc2ai/player.rb +7 -7
- data/lib/sc2ai/protocol/extensions/point.rb +13 -1
- data/lib/sc2ai/protocol/extensions/unit.rb +54 -7
- data/lib/sc2ai/unit_group/action_ext.rb +22 -2
- data/lib/sc2ai/unit_group.rb +22 -10
- data/lib/sc2ai/version.rb +1 -1
- data/lib/templates/new/my_bot.rb.tt +2 -3
- data/sig/sc2ai.rbs +10183 -0
- metadata +37 -78
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fcc96e2f18e9cc95b0fd8b9427d3c76866551031b69811251b547eeb13284cba
|
4
|
+
data.tar.gz: 88c3dce17dba1e6b4378c306de8d754e1f33dcd092a9e44ac7477e50940ca911
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 39997d407d6fbf2ffc936c387f703d4b12d6aff1f016bc1905148a6590f156c4f39acee982074be28d33ea9451cbbf483f985c62a902693c65da450465d915e2
|
7
|
+
data.tar.gz: af9ed623bf2ee6ce905127fb89828a45c95456b4996e1aa4f6c3d477d31b383ed54d425de80abcd28f2df9cf08bfa127fbd36c89f822e226804b116bf20e73c1
|
data/lib/sc2ai/player/actions.rb
CHANGED
@@ -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]
|
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]
|
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]
|
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(
|
data/lib/sc2ai/player/debug.rb
CHANGED
@@ -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
|
-
#
|
67
|
-
#
|
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.
|
78
|
-
max: Api::Point.new(x: point.x + radius, y: point.y + radius, z: point.z + (radius * 2) + 0.
|
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
|
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
|
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
|
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
|
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 ||= {}
|
data/lib/sc2ai/player/units.rb
CHANGED
@@ -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
|
-
#
|
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 =
|
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
|
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
|
-
|
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
|
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 +
|
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.
|
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
|
-
|
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
|
-
|
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
|
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
|
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
|
83
|
+
action(ability_id: Api::AbilityId::ATTACK, target:, queue_command:)
|
64
84
|
end
|
65
85
|
|
66
86
|
# Issues repair command on target
|
data/lib/sc2ai/unit_group.rb
CHANGED
@@ -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
|
-
|
100
|
-
|
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.
|
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
|
-
|
112
|
-
|
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
@@ -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
|
-
@
|
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
|
-
|
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
|