sc2ai 0.6.2 → 0.7.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.
@@ -520,11 +520,11 @@ module Sc2
520
520
  point_search_offsets = (-7..7).to_a.product((-7..7).to_a)
521
521
  point_search_offsets.select! do |x, y|
522
522
  dist = Math.hypot(x, y)
523
- dist > 4 && dist <= 8
523
+ dist > 4.0 && dist <= 8.0
524
524
  end
525
525
 
526
526
  # Split resources by Z axis
527
- resources = bot.neutral.minerals.reject_type(Api::UnitTypeId::MINERALFIELD450) + bot.neutral.geysers
527
+ resources = bot.neutral.minerals - mineral_walls + bot.neutral.geysers
528
528
  resource_group_z = resources.group_by do |resource|
529
529
  resource.pos.z.round # 32 units of Y, most maps will have use 3. round to nearest.
530
530
  end
@@ -591,11 +591,86 @@ module Sc2
591
591
  # Choose best fitting point
592
592
  best_point = possible_points.keys[possible_points.values.find_index(possible_points.values.min)]
593
593
  @expansions[best_point.to_p2d] = UnitGroup.new(clustered_resources)
594
+
595
+ # Check if this might be a mirrored base.
596
+ best_mirror_point = nil
597
+ geysers = clustered_resources.select { |res| Sc2::UnitGroup::TYPE_GEYSER.include?(res.unit_type) }
598
+ if geysers.size == 2
599
+ if geysers[0].pos.y == geysers[1].pos.y || geysers[0].pos.x == geysers[1].pos.x
600
+ # Mirrored vertical, potentially
601
+ best_mirror_point = [
602
+ best_point[0],
603
+ best_point[1] - (best_point[1] - (geysers[0].pos.y + geysers[1].pos.y) / 2.0) * 2.0
604
+ ]
605
+ if best_mirror_point != best_point && possible_points.has_key?(best_mirror_point)
606
+ @expansions[best_mirror_point.to_p2d] = UnitGroup.new(clustered_resources)
607
+ else
608
+ # Wasn't mirrored the one way. How about the other?...
609
+ # Mirrored horizontal, potentially
610
+ best_mirror_point = [
611
+ best_point[0] - (best_point[0] - (geysers[0].pos.x + geysers[1].pos.x) / 2.0) * 2.0,
612
+ best_point[1]
613
+ ]
614
+ if best_mirror_point != best_point && possible_points.has_key?(best_mirror_point)
615
+ @expansions[best_mirror_point.to_p2d] = UnitGroup.new(clustered_resources)
616
+ end
617
+
618
+ end
619
+ end
620
+ end
594
621
  end
595
622
  end
596
623
  @expansions
597
624
  end
598
625
 
626
+ # @private
627
+ # Mineral walls. Defined once upon start of game, mostly based on layout.
628
+ # @return [Sc2::UnitGroup]
629
+ private def mineral_walls
630
+ return @mineral_walls unless @mineral_walls.nil?
631
+
632
+ # Find mineral walls.
633
+ @mineral_walls = []
634
+ minerals_by_pos = {}
635
+ bot.neutral.minerals.reject_type(Api::UnitTypeId::MINERALFIELD450)
636
+ .each do |mineral|
637
+ minerals_by_pos[mineral.pos.to_axy] = mineral
638
+ end
639
+ minerals_by_pos.each do |xy, mineral|
640
+ x, y = xy
641
+ # Test X
642
+ if (side1 = minerals_by_pos[[x - 2, y]]) && (side2 = minerals_by_pos[[x + 2, y]])
643
+ @mineral_walls << side1
644
+ @mineral_walls << mineral
645
+ @mineral_walls << side2
646
+ end
647
+
648
+ # # Test Y
649
+ if (side1 = minerals_by_pos[[x, y - 1]]) && (side2 = minerals_by_pos[[x, y + 1]])
650
+ @mineral_walls << side1
651
+ @mineral_walls << mineral
652
+ @mineral_walls << side2
653
+ end
654
+
655
+ # Test \
656
+ if (side1 = minerals_by_pos[[x - 2, y + 1]]) && (side2 = minerals_by_pos[[x + 2, y - 1]])
657
+ @mineral_walls << side1
658
+ @mineral_walls << mineral
659
+ @mineral_walls << side2
660
+ end
661
+
662
+ # Test /
663
+ if (side1 = minerals_by_pos[[x - 2, y - 1]]) && (side2 = minerals_by_pos[[x + 2, y + 1]])
664
+ @mineral_walls << side1
665
+ @mineral_walls << mineral
666
+ @mineral_walls << side2
667
+ end
668
+ end
669
+ @mineral_walls.uniq!
670
+
671
+ @mineral_walls = UnitGroup.new(@mineral_walls) + bot.neutral.minerals.select_type(Api::UnitTypeId::MINERALFIELD450)
672
+ end
673
+
599
674
  # Returns a list of 2d points for expansion build locations
600
675
  # Does not contain mineral info, but the value can be checked against geo.expansions
601
676
  #
@@ -304,6 +304,25 @@ module Sc2
304
304
  true
305
305
  end
306
306
 
307
+ # Checks whether you have met the tech requirements for building a specific unit type
308
+ # @param [Integer] unit_type_id
309
+ # @return [Boolean]
310
+ def tech_requirement_met?(unit_type_id)
311
+ created_from_unit_type = Api::TechTree.unit_created_from(unit_type_id: unit_type_id)
312
+ required_building = Api::TechTree.unit_type_creation_abilities(
313
+ source: created_from_unit_type.first,
314
+ target: unit_type_id
315
+ )[:required_building]
316
+
317
+ # Ensure we have required building, if there's such a requirement
318
+ if required_building
319
+ return false unless all_units.owned.select_type(required_building).completed.size > 0
320
+ end
321
+
322
+ # Ensure we have a completed source which it's created from
323
+ all_units.owned.select_type(created_from_unit_type).completed.size > 0
324
+ end
325
+
307
326
  # Micro/Unit-Specific ------
308
327
 
309
328
  # Returns whether Query Available Ability is true for unit and tag
@@ -58,75 +58,74 @@ module Api
58
58
  # @return [Boolean] whether unit has attribute
59
59
  # @example
60
60
  # unit.has_attribute?(Api::Attribute::MECHANICAL)
61
- # unit.has_attribute?(:MECHANICAL)
62
61
  def has_attribute?(attribute)
63
62
  attributes.include? attribute
64
63
  end
65
64
 
66
65
  # Checks if unit is light
67
- # @return [Boolean] whether unit has attribute :Light
66
+ # @return [Boolean] whether unit has attribute :LIGHT
68
67
  def is_light?
69
- has_attribute?(:LIGHT)
68
+ has_attribute?(Api::Attribute::LIGHT)
70
69
  end
71
70
 
72
71
  # Checks if unit is armored
73
- # @return [Boolean] whether unit has attribute :Armored
72
+ # @return [Boolean] whether unit has attribute :ARMORED
74
73
  def is_armored?
75
- has_attribute?(:ARMORED)
74
+ has_attribute?(Api::Attribute::ARMORED)
76
75
  end
77
76
 
78
77
  # Checks if unit is biological
79
- # @return [Boolean] whether unit has attribute :Biological
78
+ # @return [Boolean] whether unit has attribute :BIOLOGICAL
80
79
  def is_biological?
81
- has_attribute?(:BIOLOGICAL)
80
+ has_attribute?(Api::Attribute::BIOLOGICAL)
82
81
  end
83
82
 
84
83
  # Checks if unit is mechanical
85
- # @return [Boolean] whether unit has attribute :Mechanical
84
+ # @return [Boolean] whether unit has attribute :MECHANICAL
86
85
  def is_mechanical?
87
- has_attribute?(:MECHANICAL)
86
+ has_attribute?(Api::Attribute::MECHANICAL)
88
87
  end
89
88
 
90
89
  # Checks if unit is robotic
91
- # @return [Boolean] whether unit has attribute :Robotic
90
+ # @return [Boolean] whether unit has attribute :ROBOTIC
92
91
  def is_robotic?
93
- has_attribute?(:ROBOTIC)
92
+ has_attribute?(Api::Attribute::ROBOTIC)
94
93
  end
95
94
 
96
95
  # Checks if unit is psionic
97
- # @return [Boolean] whether unit has attribute :Psionic
96
+ # @return [Boolean] whether unit has attribute :PSIONIC
98
97
  def is_psionic?
99
- has_attribute?(:PSIONIC)
98
+ has_attribute?(Api::Attribute::PSIONIC)
100
99
  end
101
100
 
102
101
  # Checks if unit is massive
103
- # @return [Boolean] whether unit has attribute :Massive
102
+ # @return [Boolean] whether unit has attribute :MASSIVE
104
103
  def is_massive?
105
- has_attribute?(:MASSIVE)
104
+ has_attribute?(Api::Attribute::MASSIVE)
106
105
  end
107
106
 
108
107
  # Checks if unit is structure
109
- # @return [Boolean] whether unit has attribute :Structure
108
+ # @return [Boolean] whether unit has attribute :STRUCTURE
110
109
  def is_structure?
111
- has_attribute?(:STRUCTURE)
110
+ has_attribute?(Api::Attribute::STRUCTURE)
112
111
  end
113
112
 
114
113
  # Checks if unit is hovering
115
- # @return [Boolean] whether unit has attribute :Hover
114
+ # @return [Boolean] whether unit has attribute :HOVER
116
115
  def is_hover?
117
- has_attribute?(:HOVER)
116
+ has_attribute?(Api::Attribute::HOVER)
118
117
  end
119
118
 
120
119
  # Checks if unit is heroic
121
- # @return [Boolean] whether unit has attribute :Heroic
120
+ # @return [Boolean] whether unit has attribute :HEROIC
122
121
  def is_heroic?
123
- has_attribute?(:HEROIC)
122
+ has_attribute?(Api::Attribute::HEROIC)
124
123
  end
125
124
 
126
125
  # Checks if unit is summoned
127
- # @return [Boolean] whether unit has attribute :Summoned
126
+ # @return [Boolean] whether unit has attribute :SUMMONED
128
127
  def is_summoned?
129
- has_attribute?(:SUMMONED)
128
+ has_attribute?(Api::Attribute::SUMMONED)
130
129
  end
131
130
 
132
131
  # @!group Virtual properties
@@ -86,6 +86,17 @@ module Sc2
86
86
  Api::UnitTypeId::SPORECRAWLER
87
87
  ].freeze
88
88
 
89
+ # Protoss: An array of unit types warped from Warp Gate.
90
+ # @return [Array<Integer>]
91
+ TYPE_WARPGATE_UNIT = [
92
+ Api::UnitTypeId::ZEALOT,
93
+ Api::UnitTypeId::STALKER,
94
+ Api::UnitTypeId::HIGHTEMPLAR,
95
+ Api::UnitTypeId::DARKTEMPLAR,
96
+ Api::UnitTypeId::SENTRY,
97
+ Api::UnitTypeId::ADEPT
98
+ ].freeze
99
+
89
100
  # Returns a new UnitGroup containing all units matching unit type id(s)
90
101
  # Multiple values work as an "OR" filter
91
102
  # @example
@@ -172,7 +183,7 @@ module Sc2
172
183
  def select_attribute(attributes)
173
184
  cached("#{__method__}:#{attributes.hash}") do
174
185
  attributes = [attributes] unless attributes.is_a? Array
175
- attributes = attributes.map { |a| a.is_a?(Symbol) ? a : Api::Attribute.lookup(a) }
186
+ attributes = attributes.map { |a| a.is_a?(Symbol) ? Api::Attribute.lookup(a) : a }
176
187
  select do |unit|
177
188
  attributes & unit.attributes == attributes
178
189
  end
@@ -192,7 +203,7 @@ module Sc2
192
203
  def reject_attribute(attributes)
193
204
  cached("#{__method__}:#{attributes.hash}") do
194
205
  attributes = [attributes] unless attributes.is_a? Array
195
- attributes = attributes.map { |a| a.is_a?(Symbol) ? a : Api::Attribute.lookup(a) }
206
+ attributes = attributes.map { |a| a.is_a?(Symbol) ? Api::Attribute.lookup(a) : a }
196
207
  reject do |unit|
197
208
  unit.attributes & attributes == attributes
198
209
  end
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.6.2"
4
+ VERSION = "0.7.0"
5
5
  end
@@ -10,5 +10,5 @@ Sc2::Match.new(
10
10
  $bot,
11
11
  Sc2::Player::Computer.new(race: Api::Race::RANDOM, difficulty: Api::Difficulty::VERY_EASY)
12
12
  ],
13
- map: "ThunderbirdAIE" # Or any of the downloaded map names
13
+ map: "PylonAIE" # Or any of the downloaded map names
14
14
  ).run