sc2ai 0.1.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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