sc2ai 0.0.4 → 0.0.5

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.
@@ -30,6 +30,53 @@ module Sc2
30
30
  # @return [Sc2::UnitGroup] a group of neutral units
31
31
  attr_accessor :effects # not a unit
32
32
 
33
+ # Returns the upgrade ids you have acquired such as weapon upgrade and armor upgrade ids.
34
+ # Shorthand for observation.raw_data.player.upgrade_ids
35
+ # @!attribute [r] upgrades_completed
36
+ # @return [Array<Integer>] a group of neutral units
37
+ def upgrades_completed = observation&.raw_data&.player&.upgrade_ids.to_a || [] # not a unit
38
+
39
+ # Returns the upgrade ids which are researching or queued
40
+ # Not set for enemy.
41
+ # @return [Array<Integer>]
42
+ def upgrades_in_progress
43
+ # We need to scan every structure which performs upgrades for any order with an upgrade ability
44
+
45
+ result = []
46
+ # Loop every upgrade structure
47
+ structures
48
+ .select_type(Api::TechTree.upgrade_structure_unit_type_ids)
49
+ .each do |structure|
50
+ next unless structure.is_active? # Skip idle
51
+
52
+ # Check if any order at a structure contains an upgrade ability
53
+ structure.orders.each do |order|
54
+ Api::TechTree.upgrade_ability_data(structure.unit_type).each do |upgrade_id, update_info|
55
+ if update_info[:ability] == order.ability_id
56
+ # Save the upgrade_id
57
+ result << upgrade_id
58
+ end
59
+ end
60
+ end
61
+ end
62
+
63
+ # If the API told use it's complete, but an order still lingers, trust the API
64
+ result - upgrades_completed
65
+ end
66
+
67
+ # Returns the upgrade ids which are researching or queued
68
+ # @return [Boolean]
69
+ def upgrade_in_progress?(upgrade_id)
70
+ structure_unit_type_id = Api::TechTree.upgrade_researched_from(upgrade_id: upgrade_id)
71
+ research_ability_id = Api::TechTree.upgrade_research_ability_id(upgrade_id: upgrade_id)
72
+ structures.select_type(structure_unit_type_id).each do |structure|
73
+ structure.orders.each do |order|
74
+ return true if order.ability_id == research_ability_id
75
+ end
76
+ end
77
+ false
78
+ end
79
+
33
80
  # An array of Protoss power sources, which have a point, radius and unit tag
34
81
  # @!attribute power_sources
35
82
  # @return [Array<Api::PowerSource>] an array of power sources
@@ -107,6 +154,13 @@ module Sc2
107
154
  data.abilities[ability_id]
108
155
  end
109
156
 
157
+ # Returns static [Api::UpgradeData] for an upgrade id
158
+ # @param upgrade_id [Integer] Api::UpgradeId::*
159
+ # @return [Api::UpgradeData]
160
+ def upgrade_data(upgrade_id)
161
+ data.upgrades[upgrade_id]
162
+ end
163
+
110
164
  # Checks unit data for an attribute value
111
165
  # @param unit [Integer,Api::Unit] Api::UnitTypeId or Api::Unit
112
166
  # @param attribute [Symbol] Api::Attribute, i.e. Api::Attribute::Mechanical or :Mechanical
@@ -141,17 +195,13 @@ module Sc2
141
195
  def subtract_cost(unit_type_id)
142
196
  unit_type_data = unit_data(unit_type_id)
143
197
 
144
- # food_required is a float. ensure half units are counted as full
145
- # TODO: Extend UnitTypeData message. def food_required = unit_id == Api::UnitTypeId::ZERGLING ? 1 : send("method_missing", :food_required)
146
- supply_cost = unit_type_data.food_required
147
- supply_cost = 1 if unit_type_id == Api::UnitTypeId::ZERGLING
148
-
149
198
  @spent_minerals += unit_type_data.mineral_cost
150
199
  @spent_vespene += unit_type_data.vespene_cost
151
- @spent_supply += supply_cost
200
+ @spent_supply += unit_type_data.food_required
152
201
  end
153
202
 
154
203
  # Checks whether you have the resources to construct quantity of unit type
204
+ # @return [Boolean]
155
205
  def can_afford?(unit_type_id:, quantity: 1)
156
206
  unit_type_data = unit_data(unit_type_id)
157
207
  return false if unit_type_data.nil?
@@ -178,6 +228,25 @@ module Sc2
178
228
  true
179
229
  end
180
230
 
231
+ # Checks whether you have the resources to
232
+ # @return [Boolean]
233
+ def can_afford_upgrade?(upgrade_id)
234
+ unit_type_data = upgrade_data(upgrade_id)
235
+ return false if unit_type_data.nil?
236
+
237
+ mineral_cost = unit_type_data.mineral_cost
238
+ if common.minerals - spent_minerals < mineral_cost
239
+ return false # not enough minerals
240
+ end
241
+
242
+ vespene_cost = unit_type_data.vespene_cost
243
+ if common.vespene - spent_vespene < vespene_cost
244
+ return false # you require more vespene gas
245
+ end
246
+
247
+ true
248
+ end
249
+
181
250
  private
182
251
 
183
252
  # @private
@@ -21,6 +21,8 @@
21
21
  # class Point < Google::Protobuf::AbstractMessage; end;
22
22
  # # Protobuf virtual class.
23
23
  # class Unit < Google::Protobuf::AbstractMessage; end;
24
+ # # Protobuf virtual class.
25
+ # class UnitTypeData < Google::Protobuf::AbstractMessage; end;
24
26
  # end
25
27
 
26
28
  # Protobuf enums ---
@@ -10,6 +10,12 @@ module Api
10
10
  self.class == other.class && hash == other.hash
11
11
  end
12
12
 
13
+ # Create a new 3d Point, by adding a y axis.
14
+ # @return [Api::Point]
15
+ def to_3d(z:)
16
+ Api::Point[x, y, z]
17
+ end
18
+
13
19
  # Adds additional functionality to message class Api::Point2D
14
20
  module ClassMethods
15
21
  # Shorthand for creating an instance for [x, y]
@@ -37,9 +37,8 @@ module Api
37
37
  # Checks unit data for an attribute value
38
38
  # @return [Boolean] whether unit has attribute
39
39
  # @example
40
- # has_attribute?(Api::UnitTypeId::SCV, Api::Attribute::Mechanical)
41
- # has_attribute?(units.workers.first, :Mechanical)
42
- # has_attribute?(Api::UnitTypeId::SCV, :Mechanical)
40
+ # unit.has_attribute?(Api::Attribute::Mechanical)
41
+ # unit.has_attribute?(:Mechanical)
43
42
  def has_attribute?(attribute)
44
43
  attributes.include? attribute
45
44
  end
@@ -233,10 +232,18 @@ module Api
233
232
 
234
233
  # Issues repair command on target
235
234
  # @param target [Api::Unit, Integer] is a unit or unit tag
235
+ # @param queue_command [Boolean] shift+command
236
236
  def repair(target:, queue_command: false)
237
237
  action(ability_id: Api::AbilityId::EFFECT_REPAIR, target:, queue_command:)
238
238
  end
239
239
 
240
+ # Research a specific upgrade
241
+ # @param upgrade_id [Integer] Api::UnitTypeId the unit type which will do the creation
242
+ # @param queue_command [Boolean] shift+command
243
+ def research(upgrade_id:, queue_command: false)
244
+ @bot.research(units: self, upgrade_id:, queue_command:)
245
+ end
246
+
240
247
  # @!endgroup Actions
241
248
  #
242
249
  # Debug ----
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Api
4
+ # Adds additional functionality to message object Api::UnitTypeData
5
+ module UnitTypeDataExtension
6
+ # @!attribute mineral_cost_sum
7
+ # Sum of all morphs mineral cost
8
+ # i.e. 550M Orbital command = 400M CC + 150M Upgrade
9
+ # i.e. 350M Hatchery = 50M Drone + 300M Build
10
+ # @return [Integer] sum of mineral costs
11
+ attr_accessor :mineral_cost_sum
12
+
13
+ # @!attribute vespene_cost_sum
14
+ # Sum of all morphs vespene gas cost
15
+ # i.e. 250G Broodlord = 100G Corruptor + 150G Morph
16
+ # @return [Integer] sum of vespene gas costs
17
+ attr_accessor :vespene_cost_sum
18
+ end
19
+ end
20
+ Api::UnitTypeData.include Api::UnitTypeDataExtension
@@ -49,6 +49,15 @@ module Sc2
49
49
  bot&.warp(units: self, unit_type_id:, target:, queue_command:)
50
50
  end
51
51
 
52
+ # Research a specific upgrade at one of these structures
53
+ # @param upgrade_id [Integer] Api::UpgradeId to research
54
+ # @param queue_command [Boolean] shift+command
55
+ def research(upgrade_id:, queue_command: false)
56
+ return if size.zero?
57
+
58
+ bot&.research(units: self, upgrade_id:, queue_command:)
59
+ end
60
+
52
61
  # Shorthand for performing action SMART (right-click)
53
62
  # @param target [Api::Unit, Integer, Api::Point2D] is a unit, unit tag or a Api::Point2D
54
63
  # @param queue_command [Boolean] shift+command
@@ -229,6 +229,12 @@ module Sc2
229
229
  alias_method :extractors, :gas
230
230
  alias_method :assimilators, :gas
231
231
 
232
+ # Selects only units which have finished constructing, i.o.w. build_progress == 1.0
233
+ # @return [UnitGroup] gas structures
234
+ def completed
235
+ select(&:is_completed?)
236
+ end
237
+
232
238
  # NEUTRAL ------------------------------------------
233
239
 
234
240
  # Selects mineral fields
@@ -227,12 +227,20 @@ module Sc2
227
227
  UnitGroup.new(@units.except(...))
228
228
  end
229
229
 
230
- # Returns a hash containing the entries for given tag(s).
230
+ # Returns a new unit group containing the entries for given tag(s).
231
231
  # @return [Sc2::UnitGroup] new unit group
232
232
  def slice(...)
233
233
  UnitGroup.new(@units.slice(...))
234
234
  end
235
235
 
236
+ # Returns a new unit group containing each element found both in self and in all of the given other_arrays; duplicates are omitted; items are compared using eql? (items must also implement hash correctly):
237
+ # @param other_unit_group [UnitGroup]
238
+ # @return [UnitGroup] new unit group
239
+ def intersection(other_unit_group)
240
+ slice(*other_unit_group.tags)
241
+ end
242
+ alias_method :&, :intersection
243
+
236
244
  # Selects a single random Unit without a parameter or an array of Units with a param, i.e. self.random(2)
237
245
  # @return [Api::Unit]
238
246
  def sample(...)
data/lib/sc2ai/version.rb CHANGED
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Sc2
4
4
  # gem version
5
- VERSION = "0.0.4"
5
+ VERSION = "0.0.5"
6
6
  end
@@ -10,5 +10,5 @@ Sc2::Match.new(
10
10
  $bot,
11
11
  Sc2::Player::Computer.new(race: Api::Race::Random, difficulty: Api::Difficulty::VeryEasy)
12
12
  ],
13
- map: "Goldenaura512AIE" # Or any of the downloaded map names
13
+ map: "Goldenaura512V2AIE" # Or any of the downloaded map names
14
14
  ).run