sc2ai 0.0.8 → 0.1.0
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.
- checksums.yaml +4 -4
- data/data/sc2ai/protocol/raw.proto +1 -1
- data/data/sc2ai/protocol/ui.proto +1 -1
- data/lib/docker_build/Dockerfile.ruby +1 -1
- data/lib/sc2ai/cli/cli.rb +1 -1
- data/lib/sc2ai/connection.rb +1 -0
- data/lib/sc2ai/local_play/client_manager.rb +2 -0
- data/lib/sc2ai/local_play/match.rb +0 -11
- data/lib/sc2ai/paths.rb +1 -1
- data/lib/sc2ai/player/{geometry.rb → geo.rb} +39 -3
- data/lib/sc2ai/player/units.rb +7 -1
- data/lib/sc2ai/player.rb +6 -41
- data/lib/sc2ai/protocol/extensions/color.rb +1 -1
- data/lib/sc2ai/protocol/extensions/unit.rb +1 -1
- data/lib/sc2ai/protocol/extensions/unit_type.rb +3 -3
- data/lib/sc2ai/unit_group/action_ext.rb +2 -2
- data/lib/sc2ai/unit_group/filter_ext.rb +20 -0
- data/lib/sc2ai/version.rb +1 -1
- data/lib/sc2ai.rb +0 -7
- data/lib/templates/new/api/raw.proto +1 -1
- data/lib/templates/new/api/ui.proto +1 -1
- data/sig/sc2ai.rbs +798 -950
- metadata +46 -19
- data/lib/sc2ai/data.rb +0 -101
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7feb936b57c5b3e4c5cae670ea743f43ccb36a5111bf39abb88ad6e63702ffc5
|
4
|
+
data.tar.gz: 3b25e577bae3d33255f4e648fa69a8005e67af47e207cffdb7d0e2c790b86961
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3c0d3dac4646702e85531561eb94d11ce8c5a5da53a255832a18803f34daad07f42958be0309f2b043a003581cdb5a9ac198b1326f13a93cd7a6f92211f75fa0
|
7
|
+
data.tar.gz: 43baa9be2700f945f0f9aed19c81b9bd77ee21df9fa43a151caa2b7d09646de17f2fe69814d8cfd0384e1644106123d01d96b2e34a413563411da41d5f67b0c1
|
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®
|
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."
|
data/lib/sc2ai/connection.rb
CHANGED
@@ -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)
|
@@ -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
|
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
|
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
|
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
|
data/lib/sc2ai/player/units.rb
CHANGED
@@ -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
|
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/
|
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::
|
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::
|
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
|
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
|
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:)
|
@@ -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
|
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
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
|
|