sc2ai 0.0.8 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c1251b565054a0ef77f14f73faf35e150b2e5b08a7228f9c47b505956fbe8007
4
- data.tar.gz: e27ea67a1cbb29d00e421dff8d387ab6fe7dd570e3f3fd5ba755e45ede5e58f8
3
+ metadata.gz: 7feb936b57c5b3e4c5cae670ea743f43ccb36a5111bf39abb88ad6e63702ffc5
4
+ data.tar.gz: 3b25e577bae3d33255f4e648fa69a8005e67af47e207cffdb7d0e2c790b86961
5
5
  SHA512:
6
- metadata.gz: d81392a64774357b5755d5a6298d2b672fdead2f60019aa7a63ee5993974f40e3100178f1ff3f43519bdbe53227340318d22cf58d6f49455cbf7dbf8e6c23eca
7
- data.tar.gz: 17ffe6116796d81b8b301aa45e23546399e9d4e3e09608751e5064ce1ac6eea06cb6f8e8229a174dd03adc37fd08c580b468d5a2b5ff670a79eac366475c7767
6
+ metadata.gz: 3c0d3dac4646702e85531561eb94d11ce8c5a5da53a255832a18803f34daad07f42958be0309f2b043a003581cdb5a9ac198b1326f13a93cd7a6f92211f75fa0
7
+ data.tar.gz: 43baa9be2700f945f0f9aed19c81b9bd77ee21df9fa43a151caa2b7d09646de17f2fe69814d8cfd0384e1644106123d01d96b2e34a413563411da41d5f67b0c1
@@ -46,7 +46,7 @@ message PowerSource {
46
46
  message PlayerRaw {
47
47
  repeated PowerSource power_sources = 1;
48
48
  optional Point camera = 2;
49
- repeated uint32 upgrade_ids = 3; // TODO: Add to UI observation?
49
+ repeated uint32 upgrade_ids = 3;
50
50
  }
51
51
 
52
52
  message UnitOrder {
@@ -52,7 +52,7 @@ message MultiPanel {
52
52
  message CargoPanel {
53
53
  optional UnitInfo unit = 1;
54
54
  repeated UnitInfo passengers = 2;
55
- optional int32 slots_available = 3; // TODO: Change to cargo size
55
+ optional int32 slots_available = 3;
56
56
  }
57
57
 
58
58
  message BuildItem {
@@ -4,7 +4,7 @@ LABEL service="bot-ruby-local"
4
4
  USER root
5
5
  WORKDIR /root/ruby-builder
6
6
 
7
- ARG RUBY_VERSION=3.3.4
7
+ ARG RUBY_VERSION=3.3.5
8
8
  ARG DEBIAN_DISABLE_RUBYGEMS_INTEGRATION=true
9
9
 
10
10
  # Deps - Ruby build
data/lib/sc2ai/cli/cli.rb CHANGED
@@ -26,7 +26,7 @@ module Sc2
26
26
  say "Press any key to continue..."
27
27
  ask ""
28
28
 
29
- say "You must accept the Blizzard® Starcraft® II AI and Machine Learning License at"
29
+ say "You must accept the Blizzard® StarCraft® II AI and Machine Learning License at"
30
30
  say "https://blzdistsc2-a.akamaihd.net/AI_AND_MACHINE_LEARNING_LICENSE.html"
31
31
  say "It is PERMISSIVE and grants you freedoms over the standard EULA."
32
32
  say "We do not record this action, but depend on software goverend by that license to continue."
@@ -78,6 +78,7 @@ module Sc2
78
78
  # Add a listener of specific callback type
79
79
  # @param listener [Object]
80
80
  # @param klass [Module<Sc2::Connection::ConnectionListener>,Module<Sc2::Connection::StatusListener>]
81
+ # @return [void]
81
82
  def add_listener(listener, klass:)
82
83
  @listeners[klass.to_s] ||= []
83
84
  @listeners[klass.to_s].push(listener)
@@ -53,6 +53,8 @@ module Sc2
53
53
  @clients[player_index] = nil
54
54
  end
55
55
 
56
+ # Stops all clients
57
+ # @return [void]
56
58
  def stop_all
57
59
  @clients.compact.each do |client|
58
60
  client.stop
@@ -10,17 +10,6 @@ module Sc2
10
10
  # Callback when game status changes
11
11
  def on_status_change(status)
12
12
  Sc2.logger.debug { "Status from Match: #{status}" }
13
-
14
- # if status == :ended
15
- # # Go through each player, looking for result if we don't have one.
16
- # api_players.each do |player|
17
- # Sc2.logger.debug { "TODO: Get results for players" }
18
- # # result = player.result
19
- # Sc2.logger.debug { "Leaving Game and Disconnecting players" }
20
- # player.leave_game
21
- # player.disconnect
22
- # end
23
- # end
24
13
  end
25
14
 
26
15
  # @!attribute players Sets the Player(s) for the match
data/lib/sc2ai/paths.rb CHANGED
@@ -7,7 +7,7 @@ module Sc2
7
7
  # Helps determine common paths to sc2 install dir, executable and maps.
8
8
  # It maintains some semblance of compatibility with python-sc2 config
9
9
  #
10
- # ENV['SC2PATH'] can be set manually to Starcraft 2 base directory for Linux
10
+ # ENV['SC2PATH'] can be set manually to StarCraft 2 base directory for Linux
11
11
  # ENV['SC2PF'] can and should be manually set to "WineLinux" when running Wine
12
12
  # Credit to Hannes, Sean and Burny for setting the standard
13
13
  class Paths
@@ -6,7 +6,7 @@ require "rumale/pairwise_metric"
6
6
  module Sc2
7
7
  class Player
8
8
  # Holds map and geography helper functions
9
- class Geometry
9
+ class Geo
10
10
  # @!attribute bot
11
11
  # @return [Sc2::Player] player with active connection
12
12
  attr_accessor :bot
@@ -98,7 +98,7 @@ module Sc2
98
98
  expo_placement_grid[y.to_i, x.to_i] == 1
99
99
  end
100
100
 
101
- # Returns a grid where ony the expo locations are marked
101
+ # Returns a grid where only the expo locations are marked
102
102
  # @return [Numo::Bit]
103
103
  def expo_placement_grid
104
104
  if @expo_placement_grid.nil?
@@ -121,6 +121,42 @@ module Sc2
121
121
  @expo_placement_grid
122
122
  end
123
123
 
124
+ # Returns a grid where only placement obstructions are marked
125
+ # includes Tumors and lowered Supply Depots
126
+ # @return [Numo::Bit]
127
+ private def placement_obstruction_grid
128
+ # Get obstructing structures
129
+ obstructure_types = [Api::UnitTypeId::SUPPLYDEPOTLOWERED, Api::UnitTypeId::CREEPTUMORQUEEN, Api::UnitTypeId::CREEPTUMOR, Api::UnitTypeId::CREEPTUMORBURROWED]
130
+ obstructures = bot.structures.select_type(obstructure_types) + bot.enemy.structures.select_type(obstructure_types)
131
+ cache_key = obstructures.tags.sort.hash
132
+
133
+ # Return cache if obstructing structures haven't changed
134
+ if !@placement_obstruction_grid.nil? && @placement_obstruction_grid[1] == cache_key
135
+ return @placement_obstruction_grid[0]
136
+ end
137
+ map = Numo::Bit.zeros(map_height, map_width)
138
+
139
+ # Cache empty, if no obstructing structures
140
+ if obstructures.size.zero?
141
+ @placement_obstruction_grid = [map, cache_key]
142
+ return map
143
+ end
144
+
145
+ # For each obstructing structure, mark it's tiles with 1's
146
+ obstructures.each do |structure|
147
+ x = structure.pos.x.floor
148
+ y = structure.pos.y.floor
149
+
150
+ if structure.unit_type == Api::UnitTypeId::SUPPLYDEPOTLOWERED
151
+ map[(y - 1)..(y), (x - 1)..(x)] = 1
152
+ else
153
+ map[y, x] = 1
154
+ end
155
+ end
156
+ @placement_obstruction_grid = [map, cache_key]
157
+ map
158
+ end
159
+
124
160
  # Returns a grid where powered locations are marked true
125
161
  # @return [Numo::Bit]
126
162
  def parsed_power_grid
@@ -680,7 +716,7 @@ module Sc2
680
716
  return @_build_coordinates[cache_key] if !@_build_coordinates[cache_key].nil? && !bot.game_info_stale?
681
717
 
682
718
  result = []
683
- input_grid = parsed_pathing_grid & parsed_placement_grid & ~expo_placement_grid
719
+ input_grid = parsed_pathing_grid & parsed_placement_grid & ~expo_placement_grid & ~placement_obstruction_grid
684
720
  input_grid = if on_creep
685
721
  parsed_creep & input_grid
686
722
  else
@@ -41,6 +41,12 @@ module Sc2
41
41
  # @return [Array<Integer>] a group of neutral units
42
42
  def upgrades_completed = observation&.raw_data&.player&.upgrade_ids.to_a || [] # not a unit
43
43
 
44
+ # Returns true if this upgrade has finished researching
45
+ # @return [Boolean]
46
+ def upgrade_completed?(upgrade_id)
47
+ upgrades_completed.include?(upgrade_id)
48
+ end
49
+
44
50
  # Returns the upgrade ids which are researching or queued
45
51
  # Not set for enemy.
46
52
  # @return [Array<Integer>]
@@ -69,7 +75,7 @@ module Sc2
69
75
  result - upgrades_completed
70
76
  end
71
77
 
72
- # Returns the upgrade ids which are researching or queued
78
+ # Returns true if the upgrade is busy researching
73
79
  # @return [Boolean]
74
80
  def upgrade_in_progress?(upgrade_id)
75
81
  structure_unit_type_id = Api::TechTree.upgrade_researched_from(upgrade_id: upgrade_id)
data/lib/sc2ai/player.rb CHANGED
@@ -1,15 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "forwardable"
4
+ require "numo/narray"
3
5
  require_relative "api/data"
4
6
  require_relative "connection/connection_listener"
5
7
  require_relative "connection/status_listener"
6
8
  require_relative "player/game_state"
7
9
  require_relative "player/units"
8
10
  require_relative "player/previous_state"
9
- require_relative "player/geometry"
11
+ require_relative "player/geo"
10
12
  require_relative "player/actions"
11
13
  require_relative "player/debug"
12
- require "numo/narray"
13
14
 
14
15
  module Sc2
15
16
  # Allows defining Ai, Bot, BotProcess (external), Human or Observer for a Match
@@ -188,13 +189,13 @@ module Sc2
188
189
  attr_accessor :previous
189
190
 
190
191
  # @!attribute geo
191
- # @return [Sc2::Player::Geometry] geo and map helper functions
192
+ # @return [Sc2::Player::Geo] geo and map helper functions
192
193
  attr_accessor :geo
193
194
 
194
195
  def initialize(race:, name:)
195
196
  super(race:, name:, type: Api::PlayerType::Participant, difficulty: nil, ai_build: nil)
196
197
  @previous = Sc2::Player::PreviousState.new
197
- @geo = Sc2::Player::Geometry.new(self)
198
+ @geo = Sc2::Player::Geo.new(self)
198
199
 
199
200
  configure
200
201
  end
@@ -231,7 +232,7 @@ module Sc2
231
232
  loop do
232
233
  r = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
233
234
  perform_actions
234
- perform_debug_commands # TODO: Detect IS_LADDER? -> unless IS_LADDER?
235
+ perform_debug_commands unless Sc2.ladder?
235
236
  step_forward
236
237
  print "\e[2K#{game_loop - @previous.game_loop} Steps Took (ms): #{(::Process.clock_gettime(::Process::CLOCK_MONOTONIC) - r) * 1000}\n\e[1A\r"
237
238
  return @result unless @result.nil?
@@ -551,7 +552,6 @@ module Sc2
551
552
  # This is better than having a bunch of random zero and nil values
552
553
  @previous.reset(self) if @previous.all_units.nil?
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)
556
556
  on_actions_performed(response_observation.actions) unless response_observation.actions.empty?
557
557
  on_action_errors(response_observation.action_errors) unless response_observation.action_errors.empty?
@@ -608,41 +608,6 @@ module Sc2
608
608
  end
609
609
  end
610
610
 
611
- # Misc -------------------------------
612
- # ##TODO: perfect loop implementation
613
- # observation has an optional param game_loop and will only return once that step is reached (blocking).
614
- # without it, it returns things as they are.
615
- # broadly, i think this is what it should be doing, with step_count being minimum of 1, so no zero-steps occur.
616
- # @example
617
- # desired_game_loop = current_game_loop + step_count
618
- # response = client.observation(game_loop: desired_game_loop)
619
- #
620
- # if response.game_loop > desired_game_loop {
621
- #
622
- # # our requested point-in-time has passed. bot too slow or unlucky timing.
623
- # # so, just re-query things as they stand right now:
624
- # missed_response = response
625
- # # note no game_loop argument supplied this time
626
- # response = client.observation()
627
- #
628
- # # Combine observations so you didn't miss anything
629
- # # Merges
630
- # response.actions.merge(missed_response.actions)
631
- # response.action_errors.merge(missed_response.action_errors)
632
- # response.chat.merge(missed_response.chat)
633
- #
634
- # # Overrides
635
- # if missed_response.player_result && response.player_result.empty?
636
- # response.player_result = player_result
637
- # end
638
- #
639
- # # Note we don't touch reponse.observation and keep the latest
640
- # end
641
- # current_game_loop = response.game_loop
642
- # return response # or dispatch events with it
643
- # def perfect_loop
644
- # end
645
-
646
611
  private
647
612
 
648
613
  # @private
@@ -2,7 +2,7 @@ module Api
2
2
  # Adds additional functionality and fixes quirks with color specifically pertaining to debug commands
3
3
  module ColorExtension
4
4
  # For lines: r & b are swapped.
5
- def initialize(r:, g:, b:)
5
+ def initialize(r: 0, g: 0, b: 0)
6
6
  super(r: b, g: g, b: r)
7
7
  end
8
8
 
@@ -243,7 +243,7 @@ module Api
243
243
 
244
244
  # Builds target unit type, i.e. issuing a build command to worker.build(...Api::UnitTypeId::BARRACKS)
245
245
  # @param unit_type_id [Integer] Api::UnitTypeId the unit type you wish to build
246
- # @param target [Api::Point2D, Integer, nil] is a unit tag or a Api::Point2D. Nil for addons/orbital
246
+ # @param target [Api::Point2D, Api::Unit, Integer, nil] is a unit tag or a Api::Point2D. Nil for addons/orbital
247
247
  # @param queue_command [Boolean] shift+command
248
248
  def build(unit_type_id:, target: nil, queue_command: false)
249
249
  @bot.build(units: self, unit_type_id:, target:, queue_command:)
@@ -1,9 +1,9 @@
1
1
  module Api
2
2
  # Adds additional functionality to message object Api::Unit
3
3
  module UnitTypeExtension
4
- def mood
5
- "Crafty"
6
- end
4
+ # def mood
5
+ # "Crafty"
6
+ # end
7
7
  end
8
8
  end
9
9
  Api::UnitTypeData.prepend Api::UnitTypeExtension
@@ -6,7 +6,7 @@ module Sc2
6
6
  # A set of action related tasks for unit groups
7
7
  class UnitGroup
8
8
  # Our first unit's bot object.
9
- # Returns nil if units are empty, so use safetly operator bot&.method(...)
9
+ # Returns nil if units are empty, so use safety operator bot&.method(...)
10
10
  # @return [Sc2::Player, nil] player with active connection
11
11
  def bot
12
12
  first&.bot
@@ -27,7 +27,7 @@ module Sc2
27
27
 
28
28
  # Builds target unit type, i.e. issuing a build command to worker.build(...Api::UnitTypeId::BARRACKS)
29
29
  # @param unit_type_id [Integer] Api::UnitTypeId the unit type you wish to build
30
- # @param target [Api::Point2D, Integer, nil] is a unit tag or a Api::Point2D. Nil for addons/orbital
30
+ # @param target [Api::Point2D, Api::Unit, Integer, nil] is a unit tag or a Api::Point2D. Nil for addons/orbital
31
31
  # @param queue_command [Boolean] shift+command
32
32
  def build(unit_type_id:, target: nil, queue_command: false)
33
33
  return if size.zero?
@@ -6,7 +6,11 @@ require "kdtree"
6
6
  module Sc2
7
7
  # Manage virtual control groups of units, similar to Hash or Array.
8
8
  class UnitGroup
9
+ # An array of all worker unit type ids
10
+ # @return [Array<Integer>]
9
11
  TYPE_WORKER = [Api::UnitTypeId::SCV, Api::UnitTypeId::MULE, Api::UnitTypeId::DRONE, Api::UnitTypeId::DRONEBURROWED, Api::UnitTypeId::PROBE].freeze
12
+ # An array of all gas structure unit type ids
13
+ # @return [Array<Integer>]
10
14
  TYPE_GAS_STRUCTURE = [
11
15
  Api::UnitTypeId::REFINERY,
12
16
  Api::UnitTypeId::REFINERYRICH,
@@ -15,6 +19,8 @@ module Sc2
15
19
  Api::UnitTypeId::EXTRACTOR,
16
20
  Api::UnitTypeId::EXTRACTORRICH
17
21
  ].freeze
22
+ # An array of all mineral unit type ids
23
+ # @return [Array<Integer>]
18
24
  TYPE_MINERAL = [
19
25
  Api::UnitTypeId::MINERALCRYSTAL,
20
26
  Api::UnitTypeId::RICHMINERALFIELD,
@@ -33,6 +39,8 @@ module Sc2
33
39
  Api::UnitTypeId::MINERALFIELDOPAQUE,
34
40
  Api::UnitTypeId::MINERALFIELDOPAQUE900
35
41
  ].freeze
42
+ # An array of all open geyser unit type ids
43
+ # @return [Array<Integer>]
36
44
  TYPE_GEYSER = [
37
45
  Api::UnitTypeId::VESPENEGEYSER,
38
46
  Api::UnitTypeId::SPACEPLATFORMGEYSER,
@@ -41,6 +49,8 @@ module Sc2
41
49
  Api::UnitTypeId::PURIFIERVESPENEGEYSER,
42
50
  Api::UnitTypeId::SHAKURASVESPENEGEYSER
43
51
  ].freeze
52
+ # An array of all debris unit type ids
53
+ # @return [Array<Integer>]
44
54
  TYPE_REJECT_DEBRIS = ((TYPE_MINERAL + TYPE_GEYSER) << Api::UnitTypeId::XELNAGATOWER).freeze
45
55
  TYPE_TECHLAB = [
46
56
  Api::UnitTypeId::TECHLAB,
@@ -48,12 +58,16 @@ module Sc2
48
58
  Api::UnitTypeId::FACTORYTECHLAB,
49
59
  Api::UnitTypeId::STARPORTTECHLAB
50
60
  ].freeze
61
+ # An array of all reactor structure unit type ids
62
+ # @return [Array<Integer>]
51
63
  TYPE_REACTOR = [
52
64
  Api::UnitTypeId::REACTOR,
53
65
  Api::UnitTypeId::BARRACKSREACTOR,
54
66
  Api::UnitTypeId::FACTORYREACTOR,
55
67
  Api::UnitTypeId::STARPORTREACTOR
56
68
  ].freeze
69
+ # An array of all base structures unit type ids
70
+ # @return [Array<Integer>]
57
71
  TYPE_BASES = [
58
72
  Api::UnitTypeId::COMMANDCENTER, Api::UnitTypeId::COMMANDCENTERFLYING,
59
73
  Api::UnitTypeId::ORBITALCOMMAND, Api::UnitTypeId::ORBITALCOMMANDFLYING,
@@ -301,6 +315,12 @@ module Sc2
301
315
  end
302
316
  alias_method :larvae, :larva
303
317
 
318
+ # Selects eggs. Eggs come from Larva and turn into Units.
319
+ # @return [Sc2::UnitGroup] eggs
320
+ def eggs
321
+ select_type(Api::UnitTypeId::EGG)
322
+ end
323
+
304
324
  # Selects queens
305
325
  # @return [Sc2::UnitGroup] queens
306
326
  def queens
data/lib/sc2ai/version.rb CHANGED
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Sc2
4
4
  # gem version
5
- VERSION = "0.0.8"
5
+ VERSION = "0.1.0"
6
6
  end
data/lib/sc2ai.rb CHANGED
@@ -1,12 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # TODO: MOVE RUBY_GC_* to load into shell ENV, because they apparently can't be set at runtime.
4
- # Arbitrary very large numbers which should not be reached
5
- # RUBY_GC_MALLOC_LIMIT=128000000000;
6
- # RUBY_GC_OLDMALLOC_LIMIT=128000000000;
7
- # RUBY_GC_OLDMALLOC_LIMIT_MAX=128000000000;
8
- # RUBY_GC_HEAP_INIT_SLOTS=329375
9
-
10
3
  # In the event major runs, let it compact
11
4
  GC.auto_compact = true
12
5
 
@@ -46,7 +46,7 @@ message PowerSource {
46
46
  message PlayerRaw {
47
47
  repeated PowerSource power_sources = 1;
48
48
  optional Point camera = 2;
49
- repeated uint32 upgrade_ids = 3; // TODO: Add to UI observation?
49
+ repeated uint32 upgrade_ids = 3;
50
50
  }
51
51
 
52
52
  message UnitOrder {
@@ -52,7 +52,7 @@ message MultiPanel {
52
52
  message CargoPanel {
53
53
  optional UnitInfo unit = 1;
54
54
  repeated UnitInfo passengers = 2;
55
- optional int32 slots_available = 3; // TODO: Change to cargo size
55
+ optional int32 slots_available = 3;
56
56
  }
57
57
 
58
58
  message BuildItem {