sc2ai 0.0.6 → 0.0.7

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.
@@ -299,14 +299,14 @@ module Api
299
299
  DIGGINGCLAWS = 293
300
300
  CARRIERCARRIERCAPACITY = 294
301
301
  CARRIERLEASHRANGEUPGRADE = 295
302
- TEMPESTGROUNDATTACKUPGRADE = 296
303
- MICROBIALSHROUD = 297
304
- SUNDERINGIMPACT = 298
305
- AMPLIFIEDSHIELDING = 299
306
- PSIONICAMPLIFIERS = 300
307
- SECRETEDCOATING = 301
308
- ENHANCEDSHOCKWAVES = 302
309
- HURRICANETHRUSTERS = 303
310
- INTERFERENCEMATRIX = 304
302
+ HURRICANETHRUSTERS = 296
303
+ TEMPESTGROUNDATTACKUPGRADE = 297
304
+ MICROBIALSHROUD = 298
305
+ INTERFERENCEMATRIX = 299
306
+ SUNDERINGIMPACT = 300
307
+ AMPLIFIEDSHIELDING = 301
308
+ PSIONICAMPLIFIERS = 302
309
+ SECRETEDCOATING = 303
310
+ ENHANCEDSHOCKWAVES = 304
311
311
  end
312
312
  end
@@ -279,10 +279,11 @@ module Sc2
279
279
  # Advances the game simulation by step_count. Not used in realtime mode.
280
280
  # Only constant step size supported - subsequent requests use cache.
281
281
  def step(step_count = 1)
282
- @_cached_request_step ||= Api::Request.new(
282
+ @_cached_request_step ||= {}
283
+ @_cached_request_step[step_count] ||= Api::Request.new(
283
284
  step: Api::RequestStep.new(count: step_count)
284
285
  ).to_proto
285
- send_request_and_ignore(@_cached_request_step)
286
+ send_request_and_ignore(@_cached_request_step[step_count])
286
287
  end
287
288
 
288
289
  # Additional methods for inspecting game state. Synchronous and must wait on response
@@ -291,7 +292,7 @@ module Sc2
291
292
  # @param placements [Array<Api::RequestQueryBuildingPlacement>]
292
293
  # @param ignore_resource_requirements [Boolean] Ignores requirements like food, minerals and so on.
293
294
  # @return [Api::ResponseQuery]
294
- def query(pathing: nil, abilities: nil, placements: nil, ignore_resource_requirements: true)
295
+ def query(pathing: nil, abilities: nil, placements: nil, ignore_resource_requirements: false)
295
296
  send_request_for query: Api::RequestQuery.new(
296
297
  pathing:,
297
298
  abilities:,
@@ -316,7 +317,7 @@ module Sc2
316
317
  # @param queries [Array<Api::RequestQueryAvailableAbilities>] one or more pathing queries
317
318
  # @param ignore_resource_requirements [Boolean] Ignores requirements like food, minerals and so on.
318
319
  # @return [Array<Api::ResponseQueryAvailableAbilities>] one or more results depending on input size
319
- def query_abilities(queries, ignore_resource_requirements: true)
320
+ def query_abilities(queries, ignore_resource_requirements: false)
320
321
  arr_queries = queries.is_a?(Array) ? queries : [queries]
321
322
 
322
323
  response = send_request_for query: Api::RequestQuery.new(
@@ -330,7 +331,8 @@ module Sc2
330
331
  # @param unit_tags [Array<Integer>] an array of unit tags or a single tag
331
332
  # @param ignore_resource_requirements [Boolean] Ignores requirements like food, minerals and so on.
332
333
  # @return [Array<Api::ResponseQueryAvailableAbilities>] one or more results depending on input size
333
- def query_abilities_for_unit_tags(unit_tags, ignore_resource_requirements: true)
334
+ def query_abilities_for_unit_tags(unit_tags, ignore_resource_requirements: false)
335
+ return [] if unit_tags.nil?
334
336
  queries = []
335
337
  unit_tags = [unit_tags] unless unit_tags.is_a? Array
336
338
  unit_tags.each do |unit_tag|
@@ -345,13 +347,14 @@ module Sc2
345
347
  # and can just return an array of ability ids.
346
348
  # Note: Querying single units are expensive and should be batched with #query_abilities_for_unit_tags
347
349
  # @param unit [Api::Unit, Integer] a unit or a tag.
348
- def query_ability_ids_for_unit(unit, ignore_resource_requirements: true)
350
+ # @return [Array<Integer>] array of ability ids
351
+ def query_ability_ids_for_unit(unit, ignore_resource_requirements: false)
349
352
  tag = unit.is_a?(Api::Unit) ? unit.tag : unit
350
353
  result = query_abilities_for_unit_tags([tag], ignore_resource_requirements:)
351
354
  if result.nil?
352
355
  []
353
356
  else
354
- result.first.abilities
357
+ result.first.abilities.map(&:ability_id)
355
358
  end
356
359
  end
357
360
 
data/lib/sc2ai/data.rb ADDED
@@ -0,0 +1,101 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "api/ability_id"
4
+ require_relative "api/unit_type_id"
5
+ require_relative "api/upgrade_id"
6
+ require_relative "api/buff_id"
7
+ require_relative "api/effect_id"
8
+ require_relative "api/tech_tree"
9
+
10
+ module Sc2
11
+ # Holds game data from tech tree and Api::ResponseData
12
+ # Called once on game start
13
+ class Data
14
+ # @!attribute abilities
15
+ # @return [Hash<Integer, Api::AbilityData]>] AbilitId => AbilityData
16
+ attr_accessor :abilities
17
+ # @!attribute units
18
+ # @return [Hash<Integer, Api::UnitTypeData>] UnitId => UnitData
19
+ attr_accessor :units
20
+ # @!attribute upgrades
21
+ # @return [Hash<Integer, Api::UpgradeData>] UpgradeId => UpgradeData
22
+ attr_accessor :upgrades
23
+ # @!attribute buffs
24
+ # Not particularly useful data. Just use BuffId directly
25
+ # @return [Hash<Integer, Api::UpgradeData>] BuffId => BuffData
26
+ attr_accessor :buffs
27
+ # @!attribute effects
28
+ # Not particularly useful data. Just use EffectId directly
29
+ # @return [Hash<Integer, Api::UpgradeData>] EffectId => EffectData
30
+ attr_accessor :effects
31
+
32
+ # @param data [Api::ResponseData]
33
+ def initialize(data)
34
+ return unless data
35
+
36
+ @abilities = abilities_from_proto(data.abilities)
37
+ @units = units_from_proto(data.units)
38
+ @upgrades = upgrades_from_proto(data.upgrades)
39
+ @buffs = buffs_from_proto(data.buffs)
40
+ @effects = effects_from_proto(data.effects)
41
+ end
42
+
43
+ private
44
+
45
+ # Indexes ability data by ability id
46
+ # @param abilities [Array<Api::AbilityData>]
47
+ # @return [Hash<Integer, Api::AbilityData] indexed data
48
+ def abilities_from_proto(abilities)
49
+ result = {}
50
+
51
+ ability_ids = Api::AbilityId.constants.map { |c| Api::AbilityId.const_get(c) }
52
+ abilities.each do |a|
53
+ next if a.ability_id.zero?
54
+ next if ability_ids.delete(a.ability_id).nil?
55
+
56
+ result[a.ability_id] = a
57
+ end
58
+ result
59
+ end
60
+
61
+ # Indexes unit data by id
62
+ # @param units [Array<Api::UnitTypeData>]
63
+ # @return [Hash<Integer, Api::UnitTypeData>] indexed data
64
+ def units_from_proto(units)
65
+ result = {}
66
+ units.each do |u|
67
+ next unless u.available
68
+
69
+ result[u.unit_id] = u
70
+ end
71
+ result
72
+ end
73
+
74
+ # Indexes upgrades data by id
75
+ # @param upgrades [Array<Api::UpgradeData>]
76
+ # @return [Hash<Integer, Api::UpgradeData] indexed data
77
+ def upgrades_from_proto(upgrades)
78
+ result = {}
79
+ upgrades.each do |u|
80
+ result[u.upgrade_id] = u
81
+ end
82
+ result
83
+ end
84
+
85
+ def effects_from_proto(effects)
86
+ result = {}
87
+ effects.each do |e|
88
+ result[e.effect_id] = e
89
+ end
90
+ result
91
+ end
92
+
93
+ def buffs_from_proto(buffs)
94
+ result = {}
95
+ buffs.each do |b|
96
+ result[b.buff_id] = b
97
+ end
98
+ result
99
+ end
100
+ end
101
+ end
@@ -23,8 +23,6 @@ module Sc2
23
23
  # end
24
24
  end
25
25
 
26
- # TODO: DEFINE REALTIME AS A PARAM
27
-
28
26
  # @!attribute players Sets the Player(s) for the match
29
27
  # @return [Array<Sc2::Player>] an array of assigned players (ai,bots,humans,observers)
30
28
  attr_accessor :players
@@ -66,7 +64,7 @@ module Sc2
66
64
  connect_players
67
65
  setup_player_hooks
68
66
 
69
- player_host.create_game(map:, players: @players)
67
+ player_host.create_game(map:, players: @players, realtime: player_host.realtime)
70
68
 
71
69
  api_players.each_with_index do |player, player_index|
72
70
  run_task.async do
@@ -45,9 +45,12 @@ module Sc2
45
45
 
46
46
  target_pos = nil
47
47
  target_unit_tag = nil
48
- if target.is_a? Api::Point2D
48
+ case target
49
+ when Api::Point2D
49
50
  target_pos = target
50
- elsif target.is_a? Api::Unit
51
+ when Api::Point
52
+ target_pos = target.to_p2d
53
+ when Api::Unit
51
54
  target_unit_tag = target.tag
52
55
  else
53
56
  target_unit_tag = target
@@ -65,7 +68,7 @@ module Sc2
65
68
 
66
69
  # Builds target unit type using units as source at optional target
67
70
  # @param units [Array<Integer>,Integer,Api::Unit] can be an Api::Unit, array of Api::Unit#tag or single tag
68
- # @param unit_type_id [Integer] Api::UnitTypeId the unit type which will do the creation
71
+ # @param unit_type_id [Integer] Api::UnitTypeId the unit type you wish to build
69
72
  # @param target [Api::Point2D, Integer, nil] is a unit tag or a Api::Point2D. Nil for addons/orbital
70
73
  # @param queue_command [Boolean] shift+command
71
74
  def build(units:, unit_type_id:, target: nil, queue_command: false)
@@ -79,9 +82,10 @@ module Sc2
79
82
 
80
83
  # Warps in unit type at target (location or pylon) with optional source units (warp gates)
81
84
  # When not specifying the specific warp gate(s), all warpgates will be used
82
- # @param unit_type_id [Integer] Api::UnitTypeId the unit type which will do the creation
85
+ # @param unit_type_id [Integer] Api::UnitTypeId the unit type you wish to build
83
86
  # @param queue_command [Boolean] shift+command
84
87
  # @param target [Api::Point2D, Integer] is a unit tag or a Api::Point2D
88
+ # @param units [Array<Integer>, Integer, Api::Unit]
85
89
  def warp(unit_type_id:, target:, queue_command:, units: nil)
86
90
  warp_ability = Api::TechTree.unit_type_creation_abilities(
87
91
  source: Api::UnitTypeId::WARPGATE,
@@ -133,7 +133,7 @@ module Sc2
133
133
  # Debug draws a sphere at position with a radius in color
134
134
  # @param point [Api::Point]
135
135
  # @param radius [Float] default one tile wide, 1.0
136
- # @param color [Api::Color] default white. min(r,b) is used for both r&b
136
+ # @param color [Api::Color] default white
137
137
  # @return [void]
138
138
  def debug_draw_sphere(point:, radius: 1.0, color: nil)
139
139
  queue_debug_command Api::DebugCommand.new(
@@ -151,14 +151,13 @@ module Sc2
151
151
 
152
152
  # Other Commands ---
153
153
 
154
- # Toggles cheat commands on/off (send only once to enable)
155
154
  # @param command [Integer] one of Api::DebugGameState::*
156
155
  # Possible values:
157
156
  # Api::DebugGameState::Show_map
158
157
  # Api::DebugGameState::Control_enemy
159
158
  # Api::DebugGameState::Food
160
159
  # Api::DebugGameState::Free
161
- # Api::DebugGameState::all_resources
160
+ # Api::DebugGameState::All_resources
162
161
  # Api::DebugGameState::God
163
162
  # Api::DebugGameState::Minerals
164
163
  # Api::DebugGameState::Gas
@@ -106,6 +106,34 @@ module Sc2
106
106
  @chats_received || []
107
107
  end
108
108
 
109
+ # @private
110
+ # @!attribute available_abilities_loop
111
+ # This is the last loop at which available_abilities was queried.
112
+ # Used to determine staleness.
113
+ # @return [Integer]
114
+ attr_accessor :available_abilities_loop
115
+ private :available_abilities_loop
116
+
117
+ # A Hash by unit tag, holding an array of available ability ids
118
+ # Synchronously calls RequestQueryAvailableAbilities and caches for this game loop.
119
+ # @return [Hash<Integer, Array<Integer>>] { unit_tag => [ability_id, ...], ... }
120
+ def available_abilities
121
+ # Save/check when last we refreshed abilities
122
+ if @available_abilities_loop != game_loop
123
+
124
+ # Query abilities for all our units + structure tags combined
125
+ abilities = api.query_abilities_for_unit_tags(units.tags + structures.tags, ignore_resource_requirements: false)
126
+ # Build the hash by unit tag
127
+ fresh_available_abilities = {}
128
+ abilities.each do |row|
129
+ fresh_available_abilities[row.unit_tag] = row.abilities.map(&:ability_id)
130
+ end
131
+ @available_abilities = fresh_available_abilities
132
+ @available_abilities_loop = game_loop
133
+ end
134
+ @available_abilities
135
+ end
136
+
109
137
  # An alias for observation.player_common to allow easier access to i.e. common.minerals
110
138
  # @return [Api::PlayerCommon] common info such as minerals, vespene, supply
111
139
  def common
@@ -31,6 +31,12 @@ module Sc2
31
31
  @map_height ||= bot.game_info.start_raw.map_size.y
32
32
  end
33
33
 
34
+ # Center of the map
35
+ # @return [Api::Point2D]
36
+ def map_center
37
+ @map_center ||= Api::Point2D[map_width / 2, map_height / 2]
38
+ end
39
+
34
40
  # Returns zero to map_width as range
35
41
  # @return [Range] 0 to map_width
36
42
  def map_range_x
@@ -100,8 +106,16 @@ module Sc2
100
106
  expansion_points.each do |point|
101
107
  x = point.x.floor
102
108
  y = point.y.floor
103
- @expo_placement_grid[(y - 2).clamp(map_tile_range_y)..(y + 2).clamp(map_tile_range_y),
104
- (x - 2).clamp(map_tile_range_y)..(x + 2).clamp(map_tile_range_y)] = 1
109
+
110
+ # For zerg, reserve a layer at the bottom for larva->egg
111
+ if bot.race == Api::Race::Zerg
112
+ # Reserve one row lower, meaning (y-3) instead of (y-2)
113
+ @expo_placement_grid[(y - 3).clamp(map_tile_range_y)..(y + 2).clamp(map_tile_range_y),
114
+ (x - 2).clamp(map_tile_range_y)..(x + 2).clamp(map_tile_range_y)] = 1
115
+ else
116
+ @expo_placement_grid[(y - 2).clamp(map_tile_range_y)..(y + 2).clamp(map_tile_range_y),
117
+ (x - 2).clamp(map_tile_range_y)..(x + 2).clamp(map_tile_range_y)] = 1
118
+ end
105
119
  end
106
120
  end
107
121
  @expo_placement_grid
@@ -121,40 +135,11 @@ module Sc2
121
135
  return result
122
136
  end
123
137
 
124
- radius = power_source.radius
125
- radius_tile = radius.ceil
126
-
127
- # Keep this code-block, should we need to make power sources dynamic again:
128
- # START: Dynamic blueprint
129
- # # Build a blueprint and mark it everywhere we need to
130
- # # Lets mark everything as powered with 1 and then disable non-powered with a 0
131
- # blueprint = Numo::Bit.ones(radius.ceil * 2, radius.ceil * 2)
132
- # #blueprint[radius_tile, radius_tile] = 0
133
- # blueprint[(radius_tile - 1)..radius_tile, (radius_tile - 1)..radius_tile] = 0
134
- # # Loop over top-right quadrant of a circle, so we don't have to +/- for distance calcs.
135
- # # Additionally, we only measure if in the upper triangle, since the inner is all inside the circle.
136
- # # Then apply to all four quadrants.
137
- # quadrant_size = radius_tile - 1
138
- # point_search_offsets = (0..quadrant_size).to_a.product((0..quadrant_size).to_a)
139
- # point_search_offsets.each do |y, x|
140
- # next if x < quadrant_size - y # Only upper Triangle
141
- #
142
- # dist = Math.hypot(x, y)
143
- # if dist >= radius
144
- # # Mark as outside x4
145
- # blueprint[radius_tile + y, radius_tile + x] = 0
146
- # blueprint[radius_tile + y, radius_tile - 1 - x] = 0
147
- # blueprint[radius_tile - 1 - y, radius_tile + x] = 0
148
- # blueprint[radius_tile - 1 - y, radius_tile - 1 - x] = 0
149
- # end
150
- # end
151
- # END: Dynamic blueprint ---
152
-
153
138
  # Hard-coding this shape for pylon power
154
139
  # 00001111110000
140
+ # 00011111111000
155
141
  # 00111111111100
156
142
  # 01111111111110
157
- # 01111111111110
158
143
  # 11111111111111
159
144
  # 11111111111111
160
145
  # 11111100111111
@@ -162,15 +147,35 @@ module Sc2
162
147
  # 11111111111111
163
148
  # 11111111111111
164
149
  # 01111111111110
165
- # 01111111111110
166
150
  # 00111111111100
151
+ # 00011111111000
167
152
  # 00001111110000
168
-
169
153
  # perf: Saving pre-created shape for speed (0.5ms saved) by using hardcode from .to_binary.unpack("C*")
170
- blueprint_data = [240, 3, 255, 227, 255, 249, 127, 255, 255, 255, 255, 243, 255, 252, 255, 255, 255, 239, 255, 249, 127, 252, 15, 252, 0].pack("C*")
171
- blueprint = ::Numo::Bit.from_binary(blueprint_data, [radius_tile * 2, radius_tile * 2])
172
-
154
+ blueprint_data = [0, 0, 254, 193, 255, 248, 127, 254, 159, 255, 231, 243, 249, 124, 254, 159, 255, 231, 255, 241, 63, 248, 7, 0, 0].pack("C*")
155
+ blueprint_pylon = ::Numo::Bit.from_binary(blueprint_data, [14, 14])
156
+
157
+ # Warp Prism
158
+ # 00011000
159
+ # 01111110
160
+ # 01111110
161
+ # 11111111
162
+ # 11111111
163
+ # 01111110
164
+ # 01111110
165
+ # 00011000
166
+ blueprint_data = [24, 126, 126, 255, 255, 126, 126, 24].pack("C*")
167
+ blueprint_prism = ::Numo::Bit.from_binary(blueprint_data, [8, 8])
168
+
169
+ # Print each power-source on map using shape above
173
170
  bot.power_sources.each do |ps|
171
+ radius_tile = ps.radius.ceil
172
+ # Select blueprint for 7-tile radius (Pylon) or 4-tile radius (Prism)
173
+ blueprint = if radius_tile == 4
174
+ blueprint_prism
175
+ else
176
+ blueprint_pylon
177
+ end
178
+
174
179
  x_tile = ps.pos.x.floor
175
180
  y_tile = ps.pos.y.floor
176
181
  replace_start_x = (x_tile - radius_tile)
@@ -424,7 +429,7 @@ module Sc2
424
429
  # This differs from position of first base structure
425
430
  # @return [Api::Point2D]
426
431
  def start_position
427
- @start_position ||= bot.observation.raw_data.player.camera
432
+ @start_position ||= bot.observation.raw_data.player.camera.to_p2d
428
433
  end
429
434
 
430
435
  # Returns the enemy 2d start position
@@ -751,7 +756,7 @@ module Sc2
751
756
  )
752
757
  end
753
758
  nearest = @_build_coordinate_tree[cache_key].nearestk(target.x, target.y, random)
754
- return nil if nearest.nil?
759
+ return nil if nearest.nil? || nearest.empty?
755
760
 
756
761
  coordinates[nearest.sample].to_p2d
757
762
  end
@@ -16,6 +16,7 @@ module Sc2
16
16
  @all_units = bot.all_units
17
17
  @units = bot.units
18
18
  @structures = bot.structures
19
+ @placeholders = bot.placeholders
19
20
  @neutral = bot.neutral
20
21
  @effects = bot.effects
21
22
  @power_sources = bot.power_sources
@@ -15,9 +15,14 @@ module Sc2
15
15
 
16
16
  # A full list of all your structures (non-units)
17
17
  # @!attribute units
18
- # @return [Sc2::UnitGroup] a group of units
18
+ # @return [Sc2::UnitGroup] a group of structures
19
19
  attr_accessor :structures
20
20
 
21
+ # A list of structures which haven't started
22
+ # @!attribute units
23
+ # @return [Sc2::UnitGroup] a group of placeholder structures
24
+ attr_accessor :placeholders
25
+
21
26
  # All units with alliance :Neutral
22
27
  # @!attribute neutral
23
28
  # @return [Sc2::UnitGroup] a group of neutral units
@@ -125,13 +130,14 @@ module Sc2
125
130
 
126
131
  # An array of Sensor tower rings as per minimap. It has a `pos` and a `radius`
127
132
  # @!attribute power_sources
128
- # @return [Array<Api::RadarRing>] an array of power sources
133
+ # @return [Array<Api::RadarRing>] an array of radar rings sources
129
134
  attr_accessor :radar_rings # not a unit but has a tag
130
135
 
131
136
  # @private
132
137
  # @!attribute all_seen_unit_tags
133
138
  # Privately keep track of all seen Unit tags (excl structures) in order to detect new created units
134
139
  attr_accessor :_all_seen_unit_tags
140
+ private :_all_seen_unit_tags
135
141
 
136
142
  # Event-driven unit groups ---
137
143
 
@@ -288,6 +294,17 @@ module Sc2
288
294
  true
289
295
  end
290
296
 
297
+ # Micro/Unit-Specific ------
298
+
299
+ # Returns whether Query Available Ability is true for unit and tag
300
+ # Queries API if necessary. Uses batching in the background.
301
+ # @param [Integer] unit_tag
302
+ # @param [Integer] ability_id
303
+ # @return [Boolean]
304
+ def unit_ability_available?(unit_tag:, ability_id:)
305
+ !!available_abilities[unit_tag]&.include?(ability_id)
306
+ end
307
+
291
308
  private
292
309
 
293
310
  # @private
@@ -299,8 +316,10 @@ module Sc2
299
316
  # Clear previous units and prep for categorization
300
317
  @units = UnitGroup.new
301
318
  @structures = UnitGroup.new
319
+ @placeholders = UnitGroup.new
302
320
  @enemy.units = UnitGroup.new
303
321
  @enemy.structures = UnitGroup.new
322
+ @enemy.all_units = UnitGroup.new
304
323
  @neutral = UnitGroup.new
305
324
  @effects = observation.raw_data.effects # not a unit
306
325
  @power_sources = observation.raw_data.player.power_sources # not a unit
@@ -335,6 +354,8 @@ module Sc2
335
354
  # Categorize own units/structures, enemy units/structures, neutral
336
355
  if unit.is_blip
337
356
  @blips[tag] = unit
357
+ elsif unit.display_type == :Placeholder
358
+ @placeholders[tag] = unit
338
359
  elsif unit.alliance == own_alliance || unit.alliance == enemy_alliance
339
360
  if unit.alliance == own_alliance
340
361
  structure_collection = @structures
@@ -342,6 +363,7 @@ module Sc2
342
363
  else
343
364
  structure_collection = @enemy.structures
344
365
  unit_collection = @enemy.units
366
+ @enemy.all_units[tag] = unit
345
367
  end
346
368
 
347
369
  unit_data = unit_data(unit.unit_type)
@@ -355,7 +377,7 @@ module Sc2
355
377
  end
356
378
 
357
379
  # Dont parse callbacks on first loop or for neutral units
358
- if !game_loop.zero? &&
380
+ if !@previous.all_units.nil? &&
359
381
  unit.alliance != :Neutral &&
360
382
  unit.display_type != :Placeholder &&
361
383
  unit.is_blip == false
data/lib/sc2ai/player.rb CHANGED
@@ -233,7 +233,7 @@ module Sc2
233
233
  perform_actions
234
234
  perform_debug_commands # TODO: Detect IS_LADDER? -> unless IS_LADDER?
235
235
  step_forward
236
- print "\e[2K#{@step_count} Steps Took (ms): #{(::Process.clock_gettime(::Process::CLOCK_MONOTONIC) - r) * 1000}\n\e[1A\r"
236
+ print "\e[2K#{game_loop - @previous.game_loop} Steps Took (ms): #{(::Process.clock_gettime(::Process::CLOCK_MONOTONIC) - r) * 1000}\n\e[1A\r"
237
237
  return @result unless @result.nil?
238
238
  break if @status != :in_game
239
239
  end
@@ -549,7 +549,7 @@ module Sc2
549
549
  # Having loaded all the necessities for the current state...
550
550
  # If we're on the first frame of the game, say previous state and current are the same
551
551
  # This is better than having a bunch of random zero and nil values
552
- @previous.reset(self) if game_loop.zero?
552
+ @previous.reset(self) if @previous.all_units.nil?
553
553
 
554
554
  # TODO: remove @events attributes if we don't use them for performance gains
555
555
  # Actions performed and errors (only if implemented)
@@ -586,7 +586,7 @@ module Sc2
586
586
  self.game_info = @api.game_info
587
587
  end
588
588
 
589
- # Data Parsing -----------------------
589
+ # Enemy -----------------------
590
590
 
591
591
  # If you're random, best to set #race to match after launched
592
592
  def set_race_for_random
@@ -23,8 +23,26 @@
23
23
  # class Unit < Google::Protobuf::AbstractMessage; end;
24
24
  # # Protobuf virtual class.
25
25
  # class UnitTypeData < Google::Protobuf::AbstractMessage; end;
26
+ # # Protobuf virtual class.
27
+ # class AvailableAbility < Google::Protobuf::AbstractMessage; end;
28
+ # # Protobuf virtual class.
29
+ # class UnitOrder < Google::Protobuf::AbstractMessage; end;
30
+ # # Protobuf virtual class.
31
+ # class ActionRawUnitCommand < Google::Protobuf::AbstractMessage; end;
32
+ # # Protobuf virtual class.
33
+ # class ActionRawToggleAutocast < Google::Protobuf::AbstractMessage; end;
34
+ # # Protobuf virtual class.
35
+ # class ActionError < Google::Protobuf::AbstractMessage; end;
36
+ # # Protobuf virtual class.
37
+ # class ActionSpatialUnitCommand < Google::Protobuf::AbstractMessage; end;
38
+ # # Protobuf virtual class.
39
+ # class BuildItem < Google::Protobuf::AbstractMessage; end;
40
+ # # Protobuf virtual class.
41
+ # class ActionToggleAutocast < Google::Protobuf::AbstractMessage; end;
26
42
  # end
27
43
 
44
+
45
+
28
46
  # Protobuf enums ---
29
47
  # Protobuf classes
30
48
  # @!parse
@@ -12,7 +12,7 @@ module Api
12
12
 
13
13
  # Create a new 3d Point, by adding a y axis.
14
14
  # @return [Api::Point]
15
- def to_3d(z:)
15
+ def to_3d(z: 0)
16
16
  Api::Point[x, y, z]
17
17
  end
18
18
 
@@ -0,0 +1,11 @@
1
+ # This class was partially generated with the help of AI.
2
+
3
+ module Api
4
+ # Adds additional functionality to message object Api::Unit
5
+ module PointDistanceExtension
6
+ end
7
+ end
8
+ Api::Point.include Api::PointDistanceExtension
9
+ Api::Point2D.include Api::PointDistanceExtension
10
+ Api::PointI.include Api::PointDistanceExtension
11
+ Api::Size2DI.include Api::PointDistanceExtension
@@ -84,7 +84,6 @@ module Sc2
84
84
  # raise NoMethodError unless location == self
85
85
 
86
86
  # Perf: Memoizing attributes which are hit hard, show gain
87
-
88
87
  @y ||= send(:method_missing, :y)
89
88
  end
90
89
 
@@ -97,9 +96,9 @@ module Sc2
97
96
 
98
97
  # Randomly adjusts both x and y by a range of: -offset..offset
99
98
  # @param offset [Float]
100
- # @return [Api::Point2D]
99
+ # @return [Sc2::Position] new Position
101
100
  def random_offset(offset)
102
- Api::Point2D.new[x, y].random_offset!(offset)
101
+ dup.random_offset!(offset)
103
102
  end
104
103
 
105
104
  # Changes this point's x and y by the supplied offset
@@ -112,17 +111,16 @@ module Sc2
112
111
  end
113
112
 
114
113
  # Creates a new point with x and y which is offset
115
- # @return [Api::Point2D] self
116
- def offset(x, y)
117
- Api::Point2D.new[x, y].offset!(x, y)
118
- self
114
+ # @return [Sc2::Position] new Position
115
+ def offset(x = 0, y = 0)
116
+ dup.offset!(x, y)
119
117
  end
120
118
 
121
119
  # Changes this point's x and y by the supplied offset
122
120
  # @return [Sc2::Position] self
123
- def offset!(x, y)
124
- self.x -= x
125
- self.y -= y
121
+ def offset!(x = 0, y = 0)
122
+ self.x += x
123
+ self.y += y
126
124
  self
127
125
  end
128
126