sc2ai 0.0.4 → 0.0.5

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