sc2ai 0.0.5 → 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.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/data/stableid.json +424 -2562
  3. data/data/versions.json +8 -0
  4. data/lib/docker_build/Dockerfile.ruby +1 -1
  5. data/lib/sc2ai/api/ability_id.rb +12 -314
  6. data/lib/sc2ai/api/buff_id.rb +9 -16
  7. data/lib/sc2ai/api/data.rb +1 -4
  8. data/lib/sc2ai/api/tech_tree.rb +35 -0
  9. data/lib/sc2ai/api/tech_tree_data.rb +14 -9
  10. data/lib/sc2ai/api/unit_type_id.rb +6 -58
  11. data/lib/sc2ai/api/upgrade_id.rb +9 -9
  12. data/lib/sc2ai/connection/requests.rb +34 -16
  13. data/lib/sc2ai/data.rb +101 -0
  14. data/lib/sc2ai/local_play/match.rb +1 -3
  15. data/lib/sc2ai/player/actions.rb +8 -4
  16. data/lib/sc2ai/player/debug.rb +2 -3
  17. data/lib/sc2ai/player/game_state.rb +36 -5
  18. data/lib/sc2ai/player/geometry.rb +138 -55
  19. data/lib/sc2ai/player/previous_state.rb +2 -1
  20. data/lib/sc2ai/player/units.rb +66 -3
  21. data/lib/sc2ai/player.rb +5 -3
  22. data/lib/sc2ai/protocol/_meta_documentation.rb +18 -0
  23. data/lib/sc2ai/protocol/extensions/ability_remapable.rb +22 -0
  24. data/lib/sc2ai/protocol/extensions/point.rb +3 -1
  25. data/lib/sc2ai/protocol/extensions/point_2_d.rb +1 -1
  26. data/lib/sc2ai/protocol/extensions/point_distance.rb +11 -0
  27. data/lib/sc2ai/protocol/extensions/position.rb +48 -13
  28. data/lib/sc2ai/protocol/extensions/unit.rb +54 -3
  29. data/lib/sc2ai/protocol/extensions/unit_type.rb +9 -0
  30. data/lib/sc2ai/unit_group/action_ext.rb +3 -3
  31. data/lib/sc2ai/unit_group/filter_ext.rb +33 -7
  32. data/lib/sc2ai/unit_group/geo_ext.rb +28 -0
  33. data/lib/sc2ai/unit_group.rb +3 -0
  34. data/lib/sc2ai/version.rb +1 -1
  35. data/lib/templates/new/run_example_match.rb.tt +1 -1
  36. data/sig/sc2ai.rbs +653 -567
  37. metadata +22 -31
@@ -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:,
@@ -301,36 +302,37 @@ module Sc2
301
302
  end
302
303
 
303
304
  # Queries one or more pathing queries
304
- # @param queries [Array<Api::RequestQueryPathing>, Api::RequestQueryPathing] one or more pathing queries
305
- # @return [Array<Api::ResponseQueryPathing>, Api::ResponseQueryPathing] one or more results depending on input size
305
+ # @param queries [Array<Api::RequestQueryPathing>] one or more pathing queries
306
+ # @return [Array<Api::ResponseQueryPathing>] one or more results depending on input size
306
307
  def query_pathings(queries)
307
308
  arr_queries = queries.is_a?(Array) ? queries : [queries]
308
309
 
309
310
  response = send_request_for query: Api::RequestQuery.new(
310
311
  pathing: arr_queries
311
312
  )
312
- (arr_queries.size > 1) ? response.pathing : response.pathing.first
313
+ response.pathing
313
314
  end
314
315
 
315
316
  # Queries one or more ability-available checks
316
- # @param queries [Array<Api::RequestQueryAvailableAbilities>, Api::RequestQueryAvailableAbilities] one or more pathing queries
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
- # @return [Array<Api::ResponseQueryAvailableAbilities>, Api::ResponseQueryAvailableAbilities] one or more results depending on input size
319
- def query_abilities(queries, ignore_resource_requirements: true)
319
+ # @return [Array<Api::ResponseQueryAvailableAbilities>] one or more results depending on input size
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(
323
324
  abilities: arr_queries,
324
325
  ignore_resource_requirements:
325
326
  )
326
- (arr_queries.size > 1) ? response.abilities : response.abilities.first
327
+ response.abilities
327
328
  end
328
329
 
329
330
  # Queries available abilities for units
330
- # @param unit_tags [Array<Integer>, Integer] an array of unit tags or a single tag
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
- # @return [Array<Api::ResponseQueryAvailableAbilities>, Api::ResponseQueryAvailableAbilities] one or more results depending on input size
333
- def query_abilities_for_unit_tags(unit_tags, ignore_resource_requirements: true)
333
+ # @return [Array<Api::ResponseQueryAvailableAbilities>] one or more results depending on input size
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|
@@ -340,15 +342,31 @@ module Sc2
340
342
  query_abilities(queries, ignore_resource_requirements:)
341
343
  end
342
344
 
345
+ # Queries available ability ids for one unit
346
+ # Shortened response over #query_abilities_for_unit_tags, since we know the tag already
347
+ # and can just return an array of ability ids.
348
+ # Note: Querying single units are expensive and should be batched with #query_abilities_for_unit_tags
349
+ # @param unit [Api::Unit, Integer] a unit or a tag.
350
+ # @return [Array<Integer>] array of ability ids
351
+ def query_ability_ids_for_unit(unit, ignore_resource_requirements: false)
352
+ tag = unit.is_a?(Api::Unit) ? unit.tag : unit
353
+ result = query_abilities_for_unit_tags([tag], ignore_resource_requirements:)
354
+ if result.nil?
355
+ []
356
+ else
357
+ result.first.abilities.map(&:ability_id)
358
+ end
359
+ end
360
+
343
361
  # Queries one or more pathing queries
344
- # @param queries [Array<Api::RequestQueryBuildingPlacement>, Api::RequestQueryBuildingPlacement] one or more placement queries
345
- # @return [Array<Api::ResponseQueryBuildingPlacement>, Api::ResponseQueryBuildingPlacement] one or more results depending on input size
362
+ # @param queries [Array<Api::RequestQueryBuildingPlacement>] one or more placement queries
363
+ # @return [Array<Api::ResponseQueryBuildingPlacement>] one or more results depending on input size
346
364
  def query_placements(queries)
347
365
  arr_queries = queries.is_a?(Array) ? queries : [queries]
348
366
 
349
367
  response = query(placements: arr_queries)
350
368
 
351
- (arr_queries.size > 1) ? response.placements : response.placements.first
369
+ response.placements
352
370
  end
353
371
 
354
372
  # Generates a replay.
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
@@ -16,9 +16,12 @@ module Sc2
16
16
 
17
17
  extend Forwardable
18
18
 
19
- # @!attribute game_loop
20
- # @return [Integer] current game loop
21
- def_delegators :observation, :game_loop
19
+ attr_writer :game_loop
20
+
21
+ # @return [Integer] current game loop
22
+ def game_loop
23
+ @game_loop || 0
24
+ end
22
25
 
23
26
  # @!attribute game_info [rw]
24
27
  # Access useful game information. Used in parsed pathing grid, terrain height, placement grid.
@@ -38,7 +41,7 @@ module Sc2
38
41
  attr_accessor :game_info_loop
39
42
 
40
43
  # Determines if your game_info will be refreshed at this moment
41
- # Has a hard-capped refresh of only ever 2 steps
44
+ # Has a hard-capped refresh of only ever 4 steps
42
45
  # In general game_info is only refreshed Player::Bot reads from pathing_grid or placement_grid
43
46
  # @return [Boolean]
44
47
  def game_info_stale?
@@ -47,7 +50,7 @@ module Sc2
47
50
 
48
51
  # Note: No minimum step count set anymore
49
52
  # We can do something like, only updating every 2+ frames:
50
- game_info_loop + 2 <= game_loop
53
+ game_info_loop + 4 <= game_loop
51
54
  end
52
55
 
53
56
  # @!attribute data
@@ -103,6 +106,34 @@ module Sc2
103
106
  @chats_received || []
104
107
  end
105
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
+
106
137
  # An alias for observation.player_common to allow easier access to i.e. common.minerals
107
138
  # @return [Api::PlayerCommon] common info such as minerals, vespene, supply
108
139
  def common