sc2ai 0.0.5 → 0.0.7

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