sc2ai 0.1.0 → 0.3.0

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.
Files changed (75) hide show
  1. checksums.yaml +4 -4
  2. data/data/sc2ai/protocol/common.proto +6 -6
  3. data/data/sc2ai/protocol/data.proto +23 -20
  4. data/data/sc2ai/protocol/debug.proto +25 -21
  5. data/data/sc2ai/protocol/error.proto +217 -215
  6. data/data/sc2ai/protocol/query.proto +1 -1
  7. data/data/sc2ai/protocol/raw.proto +16 -14
  8. data/data/sc2ai/protocol/sc2api.proto +108 -94
  9. data/data/sc2ai/protocol/score.proto +4 -3
  10. data/data/sc2ai/protocol/spatial.proto +6 -5
  11. data/data/sc2ai/protocol/ui.proto +17 -14
  12. data/exe/sc2ai +0 -3
  13. data/lib/docker_build/Dockerfile.ruby +4 -2
  14. data/lib/sc2ai/api/ability_id.rb +6 -1
  15. data/lib/sc2ai/api/data.rb +18 -3
  16. data/lib/sc2ai/api/tech_tree.rb +1 -1
  17. data/lib/sc2ai/api/tech_tree_data.rb +54 -3
  18. data/lib/sc2ai/connection/connection_listener.rb +3 -3
  19. data/lib/sc2ai/connection/requests.rb +31 -35
  20. data/lib/sc2ai/connection/status_listener.rb +1 -1
  21. data/lib/sc2ai/connection.rb +1 -2
  22. data/lib/sc2ai/local_play/client.rb +2 -2
  23. data/lib/sc2ai/local_play/match.rb +7 -2
  24. data/lib/sc2ai/overrides/async/process/child.rb +1 -1
  25. data/lib/sc2ai/paths.rb +12 -2
  26. data/lib/sc2ai/player/actions.rb +54 -35
  27. data/lib/sc2ai/player/debug.rb +54 -20
  28. data/lib/sc2ai/player/game_state.rb +11 -18
  29. data/lib/sc2ai/player/geo.rb +56 -66
  30. data/lib/sc2ai/player/units.rb +41 -17
  31. data/lib/sc2ai/player.rb +104 -47
  32. data/lib/sc2ai/ports.rb +1 -2
  33. data/lib/sc2ai/protocol/_meta_documentation.rb +270 -25
  34. data/lib/sc2ai/protocol/common_pb.rb +3862 -33
  35. data/lib/sc2ai/protocol/data_pb.rb +9106 -36
  36. data/lib/sc2ai/protocol/debug_pb.rb +10434 -45
  37. data/lib/sc2ai/protocol/error_pb.rb +1084 -29
  38. data/lib/sc2ai/protocol/extensions/ability_remapable.rb +9 -9
  39. data/lib/sc2ai/protocol/extensions/action.rb +60 -0
  40. data/lib/sc2ai/protocol/extensions/point_2_d.rb +9 -0
  41. data/lib/sc2ai/protocol/extensions/position.rb +11 -36
  42. data/lib/sc2ai/protocol/extensions/power_source.rb +3 -0
  43. data/lib/sc2ai/protocol/extensions/unit.rb +61 -36
  44. data/lib/sc2ai/protocol/extensions/unit_type_data.rb +8 -0
  45. data/lib/sc2ai/protocol/query_pb.rb +5022 -36
  46. data/lib/sc2ai/protocol/raw_pb.rb +18347 -46
  47. data/lib/sc2ai/protocol/sc2api_pb.rb +48424 -126
  48. data/lib/sc2ai/protocol/score_pb.rb +5965 -30
  49. data/lib/sc2ai/protocol/spatial_pb.rb +11941 -37
  50. data/lib/sc2ai/protocol/ui_pb.rb +12924 -46
  51. data/lib/sc2ai/unit_group/action_ext.rb +0 -2
  52. data/lib/sc2ai/unit_group/filter_ext.rb +24 -8
  53. data/lib/sc2ai/unit_group/geo_ext.rb +0 -2
  54. data/lib/sc2ai/unit_group.rb +1 -1
  55. data/lib/sc2ai/version.rb +2 -3
  56. data/lib/sc2ai.rb +10 -11
  57. data/lib/templates/ladderzip/bin/ladder.tt +0 -3
  58. data/lib/templates/new/.ladderignore +15 -5
  59. data/lib/templates/new/api/common.proto +6 -6
  60. data/lib/templates/new/api/data.proto +23 -20
  61. data/lib/templates/new/api/debug.proto +25 -21
  62. data/lib/templates/new/api/error.proto +217 -215
  63. data/lib/templates/new/api/query.proto +1 -1
  64. data/lib/templates/new/api/raw.proto +16 -14
  65. data/lib/templates/new/api/sc2api.proto +108 -94
  66. data/lib/templates/new/api/score.proto +4 -3
  67. data/lib/templates/new/api/spatial.proto +6 -5
  68. data/lib/templates/new/api/ui.proto +17 -14
  69. data/lib/templates/new/boot.rb.tt +1 -1
  70. data/lib/templates/new/my_bot.rb.tt +2 -2
  71. data/lib/templates/new/run_example_match.rb.tt +2 -2
  72. data/sig/sc2ai.rbs +11072 -1651
  73. metadata +31 -26
  74. data/lib/sc2ai/overrides/kernel.rb +0 -33
  75. data/lib/sc2ai/protocol/extensions/unit_type.rb +0 -9
@@ -5,18 +5,18 @@ module Api
5
5
  # i.e. Api::AbilityId::ATTACK_BATTLECRUISER returns generic Api::AbilityId::ATTACK
6
6
  # @return [Integer]
7
7
  def ability_id
8
- @ability_id ||= Api::AbilityId.generic_id(send(:method_missing, :ability_id))
8
+ @_ability_id ||= Api::AbilityId.generic_id(@ability_id)
9
9
  end
10
10
  end
11
11
  end
12
12
 
13
13
  # AbilityData should not include, since it holds exact info and contains the remap id as attr
14
14
  # Similarly Request* methods only do requests and ew supply correct id's.
15
- Api::AvailableAbility.include Api::AbilityRemapable
16
- Api::UnitOrder.include Api::AbilityRemapable
17
- Api::ActionRawUnitCommand.include Api::AbilityRemapable
18
- Api::ActionRawToggleAutocast.include Api::AbilityRemapable
19
- Api::ActionError.include Api::AbilityRemapable
20
- Api::ActionSpatialUnitCommand.include Api::AbilityRemapable
21
- Api::BuildItem.include Api::AbilityRemapable
22
- Api::ActionToggleAutocast.include Api::AbilityRemapable
15
+ Api::AvailableAbility.prepend Api::AbilityRemapable
16
+ Api::UnitOrder.prepend Api::AbilityRemapable
17
+ Api::ActionRawUnitCommand.prepend Api::AbilityRemapable
18
+ Api::ActionRawToggleAutocast.prepend Api::AbilityRemapable
19
+ Api::ActionError.prepend Api::AbilityRemapable
20
+ Api::ActionSpatialUnitCommand.prepend Api::AbilityRemapable
21
+ Api::BuildItem.prepend Api::AbilityRemapable
22
+ Api::ActionToggleAutocast.prepend Api::AbilityRemapable
@@ -0,0 +1,60 @@
1
+ module Api
2
+ # Adds additional functionality and fixes quirks with color specifically pertaining to debug commands
3
+ module ActionExtension
4
+ # Checks the action type and returns corresponding class
5
+ # @return [Class] class of type, i.e. Api::ActionRaw
6
+ def action_type
7
+ @action_type ||= if has_action_raw?
8
+ Api::ActionRaw
9
+ elsif has_action_chat?
10
+ Api::ActionChat
11
+ elsif has_action_feature_layer?
12
+ Api::ActionSpatial
13
+ elsif has_action_ui?
14
+ Api::ActionUI
15
+ end
16
+ end
17
+
18
+ # Finds unit tags for action, if applicable
19
+ # @return [Array<Integer>] an array of unit tags
20
+ def unit_tags
21
+ tags = []
22
+ if action_type == Api::ActionRaw
23
+ case action_raw.action
24
+ when :unit_command # ActionRawUnitCommand
25
+ tags = action_raw.unit_command.unit_tags
26
+ when :toggle_autocast # ActionRawToggleAutocast
27
+ tags = action_raw.toggle_autocast.unit_tags
28
+ end
29
+ # when Api::ActionSpatial
30
+ # if action == :unit_command # ActionSpatialUnitCommand
31
+ # # For spatial unit commands, one _could_ assume the errors came from
32
+ # # the last selected allied units on the previous frame.
33
+ # # Since we don't have bot access here, leaving this empty.
34
+ # # tags = @bot.previous.all_units.owned.select(&:is_selected).tags
35
+ # end
36
+ end
37
+
38
+ tags
39
+ end
40
+
41
+ # Finds ability_id for the action, if applicable
42
+ # @return [Integer, nil] Api::AbilityID::* or nil if n/a
43
+ def ability_id
44
+ if action_type == Api::ActionRaw
45
+ case action_raw.action
46
+ when :unit_command # ActionRawUnitCommand
47
+ return action_raw.unit_command.ability_id
48
+ when :toggle_autocast # ActionRawToggleAutocast
49
+ return action_raw.toggle_autocast.ability_id
50
+ end
51
+ elsif action_type == Api::ActionSpatial && action_ui.action == :unit_command # ActionSpatialUnitCommand
52
+ return action_ui.unit_command.ability_id
53
+ end
54
+
55
+ nil
56
+ end
57
+ end
58
+ end
59
+
60
+ Api::Action.include Api::ActionExtension
@@ -2,15 +2,22 @@ module Api
2
2
  # Adds additional functionality to message object Api::Point2D
3
3
  module Point2DExtension
4
4
  # @private
5
+ # Hashes on x and y
5
6
  def hash
6
7
  [x, y].hash
7
8
  end
8
9
 
10
+ # @private
9
11
  def eql?(other)
10
12
  self.class == other.class && hash == other.hash
11
13
  end
12
14
 
15
+ # Returns self
16
+ # @return [self]
17
+ def to_p2d = self
18
+
13
19
  # Create a new 3d Point, by adding a y axis.
20
+ # @param z [Float, Integer]
14
21
  # @return [Api::Point]
15
22
  def to_3d(z: 0)
16
23
  Api::Point[x, y, z]
@@ -21,6 +28,8 @@ module Api
21
28
  # Shorthand for creating an instance for [x, y]
22
29
  # @example
23
30
  # Api::Point2D[2,4] # Where x is 2.0 and y is 4.0
31
+ # @param x [Float, Integer]
32
+ # @param y [Float, Integer]
24
33
  # @return [Api::Point2D]
25
34
  def [](x, y)
26
35
  Api::Point2D.new(x: x, y: y)
@@ -10,6 +10,8 @@ module Sc2
10
10
  # Loose equality matches on floats x and y.
11
11
  # We never check z-axis, because the map is single-level.
12
12
  # TODO: We should almost certainly introduce TOLERANCE here, but verify it's cost first.
13
+ # @param [Sc2::Position] other
14
+ # @return [Boolean]
13
15
  def ==(other)
14
16
  if other.is_a? Position
15
17
  x == other.x && y == other.y
@@ -61,39 +63,6 @@ module Sc2
61
63
  # @see #divide
62
64
  alias_method :/, :divide
63
65
 
64
- # Returns x coordinate
65
- # @return [Float]
66
- def x
67
- # Perf: Memoizing attributes which are hit hard, show gain
68
- @x ||= send(:method_missing, :x)
69
- end
70
-
71
- # Sets x coordinate
72
- # @return [Float]
73
- def x=(x)
74
- send(:method_missing, :x=, x)
75
- @x = x
76
- end
77
-
78
- # Returns y coordinate
79
- # @return [Float]
80
- def y
81
- # Bug: Psych implements method 'y' on Kernel, but protobuf uses method_missing to read AbstractMethod
82
- # We send method missing ourselves when y to fix this chain.
83
- # This is correct, but an unnecessary conditional:
84
- # raise NoMethodError unless location == self
85
-
86
- # Perf: Memoizing attributes which are hit hard, show gain
87
- @y ||= send(:method_missing, :y)
88
- end
89
-
90
- # Sets y coordinate
91
- # @return [Float]
92
- def y=(y)
93
- send(:method_missing, :y=, y)
94
- @y = y
95
- end
96
-
97
66
  # Randomly adjusts both x and y by a range of: -offset..offset
98
67
  # @param offset [Float]
99
68
  # @return [Sc2::Position] new Position
@@ -105,18 +74,22 @@ module Sc2
105
74
  # @return [Sc2::Position] self
106
75
  def random_offset!(offset)
107
76
  offset = offset.to_f
108
- range = rand(-offset..offset)
77
+ range = -offset..offset
109
78
  offset!(rand(range), rand(range))
110
79
  self
111
80
  end
112
81
 
113
82
  # Creates a new point with x and y which is offset
83
+ # @param x [Float, Integer]
84
+ # @param y [Float, Integer]
114
85
  # @return [Sc2::Position] new Position
115
86
  def offset(x = 0, y = 0)
116
87
  dup.offset!(x, y)
117
88
  end
118
89
 
119
90
  # Changes this point's x and y by the supplied offset
91
+ # @param x [Float, Integer]
92
+ # @param y [Float, Integer]
120
93
  # @return [Sc2::Position] self
121
94
  def offset!(x = 0, y = 0)
122
95
  self.x += x
@@ -182,7 +155,7 @@ module Sc2
182
155
  end
183
156
 
184
157
  # The squared distance between this point and the other point.
185
- # @param other [Point2D] The other point to calculate the squared distance to.
158
+ # @param other [Api::Point2D] The other point to calculate the squared distance to.
186
159
  # @return [Float]
187
160
  def distance_squared_to(other)
188
161
  if other.nil? || other == self
@@ -192,13 +165,15 @@ module Sc2
192
165
  end
193
166
 
194
167
  # Distance between this point and coordinate of x and y
168
+ # @param x [Float, Integer]
169
+ # @param y [Float, Integer]
195
170
  # @return [Float]
196
171
  def distance_to_coordinate(x:, y:)
197
172
  Math.hypot(self.x - x, self.y - y)
198
173
  end
199
174
 
200
175
  # The distance from this point to the circle.
201
- # @param center [Point2D] The center of the circle.
176
+ # @param center [Api::Point2D] The center of the circle.
202
177
  # @param radius [Float] The radius of the circle.
203
178
  # @return [Float]
204
179
  def distance_to_circle(center, radius)
@@ -9,6 +9,9 @@ module Api
9
9
  # @example
10
10
  # Api::Point[1,2,3] # Where x is 1.0, y is 2.0 and z is 3.0
11
11
  # @return [Api::Point]
12
+ # @param x [Float, Integer]
13
+ # @param y [Float, Integer]
14
+ # @param z [Float, Integer]
12
15
  def [](x, y, z)
13
16
  Api::Point.new(x: x, y: y, z: z)
14
17
  end
@@ -9,22 +9,6 @@ module Api
9
9
  tag || super
10
10
  end
11
11
 
12
- # Returns an integer unique identifier
13
- # If the unit goes out of vision and is snapshot-able, they get a random id
14
- # - Such a unit gets the same unit tag when it re-enters vision
15
- # @return [Integer]
16
- def tag
17
- # Perf: This speeds up hash and therefore common UnitGroup operations. Sometimes 3x!
18
- @tag ||= send(:method_missing, :tag)
19
- end
20
-
21
- # Sets unit tag
22
- # @return [Integer]
23
- def tag=(tag)
24
- send(:method_missing, :tag=, tag)
25
- @tag = tag
26
- end
27
-
28
12
  # Every unit gets access back to the bot to allow api access.
29
13
  # For your own units, this allows API access.
30
14
  # @return [Sc2::Player] player with active connection
@@ -42,6 +26,26 @@ module Api
42
26
  @bot.previous.all_units[tag]
43
27
  end
44
28
 
29
+ # Returns whether a unit is alive or not
30
+ # Useful for cached Unit objects to see if they are still relevant.
31
+ # @return [Boolean] alive
32
+ def is_alive?
33
+ !@bot.all_units[tag].nil?
34
+ end
35
+
36
+ # Replaces protobuf values with latest from game
37
+ # @return [Boolean] true if refreshed or false unchanged
38
+ def refresh!
39
+ new_unit = @bot.all_units[tag]
40
+ return false if new_unit.nil? || new_unit == self
41
+
42
+ method(:initialize).parameters.each do |_, entry|
43
+ send(:"#{entry.name}=", new_unit.send(entry))
44
+ end
45
+
46
+ true
47
+ end
48
+
45
49
  # Attributes ---
46
50
 
47
51
  # Returns static [Api::UnitTypeData] for a unit
@@ -53,8 +57,8 @@ module Api
53
57
  # Checks unit data for an attribute value
54
58
  # @return [Boolean] whether unit has attribute
55
59
  # @example
56
- # unit.has_attribute?(Api::Attribute::Mechanical)
57
- # unit.has_attribute?(:Mechanical)
60
+ # unit.has_attribute?(Api::Attribute::MECHANICAL)
61
+ # unit.has_attribute?(:MECHANICAL)
58
62
  def has_attribute?(attribute)
59
63
  attributes.include? attribute
60
64
  end
@@ -62,67 +66,67 @@ module Api
62
66
  # Checks if unit is light
63
67
  # @return [Boolean] whether unit has attribute :Light
64
68
  def is_light?
65
- has_attribute?(:Light)
69
+ has_attribute?(:LIGHT)
66
70
  end
67
71
 
68
72
  # Checks if unit is armored
69
73
  # @return [Boolean] whether unit has attribute :Armored
70
74
  def is_armored?
71
- has_attribute?(:Armored)
75
+ has_attribute?(:ARMORED)
72
76
  end
73
77
 
74
78
  # Checks if unit is biological
75
79
  # @return [Boolean] whether unit has attribute :Biological
76
80
  def is_biological?
77
- has_attribute?(:Biological)
81
+ has_attribute?(:BIOLOGICAL)
78
82
  end
79
83
 
80
84
  # Checks if unit is mechanical
81
85
  # @return [Boolean] whether unit has attribute :Mechanical
82
86
  def is_mechanical?
83
- has_attribute?(:Mechanical)
87
+ has_attribute?(:MECHANICAL)
84
88
  end
85
89
 
86
90
  # Checks if unit is robotic
87
91
  # @return [Boolean] whether unit has attribute :Robotic
88
92
  def is_robotic?
89
- has_attribute?(:Robotic)
93
+ has_attribute?(:ROBOTIC)
90
94
  end
91
95
 
92
96
  # Checks if unit is psionic
93
97
  # @return [Boolean] whether unit has attribute :Psionic
94
98
  def is_psionic?
95
- has_attribute?(:Psionic)
99
+ has_attribute?(:PSIONIC)
96
100
  end
97
101
 
98
102
  # Checks if unit is massive
99
103
  # @return [Boolean] whether unit has attribute :Massive
100
104
  def is_massive?
101
- has_attribute?(:Massive)
105
+ has_attribute?(:MASSIVE)
102
106
  end
103
107
 
104
108
  # Checks if unit is structure
105
109
  # @return [Boolean] whether unit has attribute :Structure
106
110
  def is_structure?
107
- has_attribute?(:Structure)
111
+ has_attribute?(:STRUCTURE)
108
112
  end
109
113
 
110
114
  # Checks if unit is hover
111
115
  # @return [Boolean] whether unit has attribute :Hover
112
116
  def is_hover?
113
- has_attribute?(:Hover)
117
+ has_attribute?(:HOVER)
114
118
  end
115
119
 
116
120
  # Checks if unit is heroic
117
121
  # @return [Boolean] whether unit has attribute :Heroic
118
122
  def is_heroic?
119
- has_attribute?(:Heroic)
123
+ has_attribute?(:HEROIC)
120
124
  end
121
125
 
122
126
  # Checks if unit is summoned
123
127
  # @return [Boolean] whether unit has attribute :Summoned
124
128
  def is_summoned?
125
- has_attribute?(:Summoned)
129
+ has_attribute?(:SUMMONED)
126
130
  end
127
131
 
128
132
  # @!group Virtual properties
@@ -375,7 +379,13 @@ module Api
375
379
  )
376
380
  end
377
381
 
378
- # Checks whether the unit has
382
+ # Checks whether a unit is gathering or returning gathered minerals/gas
383
+ # @return [Boolean] true if either gathering or returning, false otherwise
384
+ def is_harvesting?
385
+ is_performing_ability?([Api::AbilityId::HARVEST_GATHER, Api::AbilityId::HARVEST_RETURN])
386
+ end
387
+
388
+ # Checks whether a unit's first order for ability
379
389
  # @param ability_ids [Integer, Array<Integer>] accepts one or an array of Api::AbilityId
380
390
  def is_performing_ability?(ability_ids)
381
391
  return false if orders.empty?
@@ -431,15 +441,15 @@ module Api
431
441
  end
432
442
 
433
443
  # Checks whether a weapon can target a unit
434
- # @param unit [Api::unit]
444
+ # @param unit [Api::Unit]
435
445
  # @param weapon [Api::Weapon]
436
446
  # @return [Boolean]
437
447
  def can_weapon_target_unit?(unit:, weapon:)
438
448
  # false if enemy is air and we can only shoot ground
439
- return false if unit.is_flying && weapon.type == :Ground # Api::Weapon::TargetType::Ground
449
+ return false if unit.is_flying && weapon.type == :GROUND # Api::Weapon::TargetType::GROUND
440
450
 
441
451
  # false if enemy is ground and we can only shoot air
442
- return false if unit.is_ground? && weapon.type == :Air # A pi::Weapon::TargetType::Air
452
+ return false if unit.is_ground? && weapon.type == :AIR # Api::Weapon::TargetType::AIR
443
453
 
444
454
  # Check if weapon and unit models are in range
445
455
  in_attack_range?(unit:, range: weapon.range)
@@ -447,7 +457,7 @@ module Api
447
457
 
448
458
  def can_ability_target_unit?(unit:, ability:)
449
459
  # false if enemy is air and we can only shoot ground
450
- return false if ability.target == Api::AbilityData::Target::None
460
+ return false if ability.target == Api::AbilityData::Target::NONE
451
461
 
452
462
  # Check if weapon and unit models are in range
453
463
  in_attack_range?(unit:, range: ability.cast_range)
@@ -538,7 +548,8 @@ module Api
538
548
  when Api::UnitTypeId::STARPORT, Api::UnitTypeId::STARPORTFLYING
539
549
  Api::UnitTypeId::STARPORTREACTOR
540
550
  end
541
- build(unit_type_id: unit_type_id, queue_command:)
551
+
552
+ build(unit_type_id: unit_type_id, target: target_for_addon_placement, queue_command:)
542
553
  end
543
554
 
544
555
  # For Terran builds a tech lab add-on on the current structure
@@ -552,7 +563,21 @@ module Api
552
563
  when Api::UnitTypeId::STARPORT, Api::UnitTypeId::STARPORTFLYING
553
564
  Api::UnitTypeId::STARPORTTECHLAB
554
565
  end
555
- build(unit_type_id: unit_type_id, queue_command:)
566
+ build(unit_type_id: unit_type_id, target: target_for_addon_placement, queue_command:)
567
+ end
568
+
569
+ private def target_for_addon_placement
570
+ # Attempts to auto-move left if not placeable
571
+ x = pos.x.floor
572
+ y = pos.y.floor
573
+ if !bot.geo.placeable?(x: x + 3, y: y - 1) ||
574
+ !bot.geo.placeable?(x: x + 3, y: y) ||
575
+ !bot.geo.placeable?(x: x + 2, y: y - 1) ||
576
+ !bot.geo.placeable?(x: x + 2, y: y)
577
+ return Api::Point2D[pos.x - 1, pos.y]
578
+ end
579
+
580
+ nil
556
581
  end
557
582
 
558
583
  # PROTOSS Convenience functions ---
@@ -15,6 +15,14 @@ module Api
15
15
  # i.e. 250G Broodlord = 100G Corruptor + 150G Morph
16
16
  # @return [Integer] sum of vespene gas costs
17
17
  attr_accessor :vespene_cost_sum
18
+
19
+ # @!attribute placement_length
20
+ # Length of tiles to build.
21
+ # i.e. 5 for any Base-type (5x5)
22
+ # i.e. 3 for Barracks (3x3)
23
+ # i.e. 2 for Supply Depot (2x2)
24
+ # @return [Integer] side-length for placement
25
+ attr_accessor :placement_length
18
26
  end
19
27
  end
20
28
  Api::UnitTypeData.include Api::UnitTypeDataExtension