sc2ai 0.0.2 → 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/data/data.json +1 -1
- data/data/data_readable.json +238 -134
- data/data/stableid.json +2574 -404
- data/lib/sc2ai/api/ability_id.rb +319 -12
- data/lib/sc2ai/api/buff_id.rb +11 -1
- data/lib/sc2ai/api/tech_tree.rb +0 -1
- data/lib/sc2ai/api/tech_tree_data.rb +32 -28
- data/lib/sc2ai/api/unit_type_id.rb +58 -6
- data/lib/sc2ai/api/upgrade_id.rb +2 -0
- data/lib/sc2ai/cli/cli.rb +35 -33
- data/lib/sc2ai/cli/ladderzip.rb +22 -3
- data/lib/sc2ai/player/actions.rb +4 -3
- data/lib/sc2ai/player/debug.rb +50 -4
- data/lib/sc2ai/player/geometry.rb +75 -6
- data/lib/sc2ai/player/units.rb +2 -2
- data/lib/sc2ai/player.rb +7 -7
- data/lib/sc2ai/protocol/extensions/point.rb +13 -1
- data/lib/sc2ai/protocol/extensions/unit.rb +54 -7
- data/lib/sc2ai/unit_group/action_ext.rb +22 -2
- data/lib/sc2ai/unit_group.rb +22 -10
- data/lib/sc2ai/version.rb +1 -1
- data/lib/templates/new/my_bot.rb.tt +2 -3
- data/sig/sc2ai.rbs +10183 -0
- metadata +44 -72
- data/sc2ai.gemspec +0 -80
@@ -2012,11 +2012,63 @@ module Api
|
|
2012
2012
|
MINERALFIELD450 = 1996
|
2013
2013
|
MINERALFIELDOPAQUE = 1997
|
2014
2014
|
MINERALFIELDOPAQUE900 = 1998
|
2015
|
-
|
2016
|
-
|
2017
|
-
|
2018
|
-
|
2019
|
-
|
2020
|
-
|
2015
|
+
MECHAZERGLINGACGLUESCREENDUMMY_2 = 1999
|
2016
|
+
MECHABANELINGACGLUESCREENDUMMY_2 = 2000
|
2017
|
+
MECHAHYDRALISKACGLUESCREENDUMMY_2 = 2001
|
2018
|
+
MECHAINFESTORACGLUESCREENDUMMY_2 = 2002
|
2019
|
+
MECHACORRUPTORACGLUESCREENDUMMY_2 = 2003
|
2020
|
+
MECHAULTRALISKACGLUESCREENDUMMY_2 = 2004
|
2021
|
+
MECHAOVERSEERACGLUESCREENDUMMY_2 = 2005
|
2022
|
+
MECHALURKERACGLUESCREENDUMMY_2 = 2006
|
2023
|
+
MECHABATTLECARRIERLORDACGLUESCREENDUMMY_2 = 2007
|
2024
|
+
MECHASPINECRAWLERACGLUESCREENDUMMY_2 = 2008
|
2025
|
+
MECHASPORECRAWLERACGLUESCREENDUMMY_2 = 2009
|
2026
|
+
TROOPERMENGSKACGLUESCREENDUMMY_2 = 2010
|
2027
|
+
MEDIVACMENGSKACGLUESCREENDUMMY_2 = 2011
|
2028
|
+
BLIMPMENGSKACGLUESCREENDUMMY_2 = 2012
|
2029
|
+
MARAUDERMENGSKACGLUESCREENDUMMY_2 = 2013
|
2030
|
+
GHOSTMENGSKACGLUESCREENDUMMY_2 = 2014
|
2031
|
+
SIEGETANKMENGSKACGLUESCREENDUMMY_2 = 2015
|
2032
|
+
THORMENGSKACGLUESCREENDUMMY_2 = 2016
|
2033
|
+
VIKINGMENGSKACGLUESCREENDUMMY_2 = 2017
|
2034
|
+
BATTLECRUISERMENGSKACGLUESCREENDUMMY_2 = 2018
|
2035
|
+
BUNKERDEPOTMENGSKACGLUESCREENDUMMY_2 = 2019
|
2036
|
+
MISSILETURRETMENGSKACGLUESCREENDUMMY_2 = 2020
|
2037
|
+
ARTILLERYMENGSKACGLUESCREENDUMMY_2 = 2021
|
2038
|
+
LOADOUTSPRAY1_2 = 2022
|
2039
|
+
LOADOUTSPRAY2_2 = 2023
|
2040
|
+
LOADOUTSPRAY3_2 = 2024
|
2041
|
+
LOADOUTSPRAY4_2 = 2025
|
2042
|
+
LOADOUTSPRAY5_2 = 2026
|
2043
|
+
LOADOUTSPRAY6_2 = 2027
|
2044
|
+
LOADOUTSPRAY7_2 = 2028
|
2045
|
+
LOADOUTSPRAY8_2 = 2029
|
2046
|
+
LOADOUTSPRAY9_2 = 2030
|
2047
|
+
LOADOUTSPRAY10_2 = 2031
|
2048
|
+
LOADOUTSPRAY11_2 = 2032
|
2049
|
+
LOADOUTSPRAY12_2 = 2033
|
2050
|
+
LOADOUTSPRAY13_2 = 2034
|
2051
|
+
LOADOUTSPRAY14_2 = 2035
|
2052
|
+
COLLAPSIBLEROCKTOWERDEBRISRAMPLEFTGREEN = 2036
|
2053
|
+
COLLAPSIBLEROCKTOWERDEBRISRAMPRIGHTGREEN = 2037
|
2054
|
+
COLLAPSIBLEROCKTOWERPUSHUNITRAMPLEFTGREEN = 2038
|
2055
|
+
COLLAPSIBLEROCKTOWERPUSHUNITRAMPRIGHTGREEN = 2039
|
2056
|
+
COLLAPSIBLEROCKTOWERRAMPLEFTGREEN = 2040
|
2057
|
+
COLLAPSIBLEROCKTOWERRAMPRIGHTGREEN = 2041
|
2058
|
+
DUMMYUNIT000 = 2042
|
2059
|
+
DUMMYUNIT001 = 2043
|
2060
|
+
DUMMYUNIT002 = 2044
|
2061
|
+
DUMMYUNIT003 = 2045
|
2062
|
+
DUMMYUNIT004 = 2046
|
2063
|
+
DUMMYUNIT005 = 2047
|
2064
|
+
DUMMYUNIT006 = 2048
|
2065
|
+
DUMMYUNIT007 = 2049
|
2066
|
+
DUMMYUNIT008 = 2050
|
2067
|
+
DUMMYUNIT009 = 2051
|
2068
|
+
DUMMYUNIT010 = 2052
|
2069
|
+
DUMMYUNIT011 = 2053
|
2070
|
+
DUMMYUNIT012 = 2054
|
2071
|
+
DUMMYUNIT013 = 2055
|
2072
|
+
DUMMYUNIT014 = 2056
|
2021
2073
|
end
|
2022
2074
|
end
|
data/lib/sc2ai/api/upgrade_id.rb
CHANGED
data/lib/sc2ai/cli/cli.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require "thor"
|
2
|
+
|
2
3
|
require "sc2ai/cli/new"
|
3
4
|
require "sc2ai/cli/ladderzip"
|
4
5
|
|
@@ -20,36 +21,36 @@ module Sc2
|
|
20
21
|
desc "setup410", "Downloads and install SC2 v4.10"
|
21
22
|
# downloads and install SC2 v4.10
|
22
23
|
def setup410
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
puts "We do not record this action, but depend on software goverend by that license to continue."
|
24
|
+
say " "
|
25
|
+
say "This script sets up SC2 at version 4.10, which we use competitively."
|
26
|
+
say "Press any key to continue..."
|
27
|
+
ask ""
|
28
|
+
|
29
|
+
say "You must accept the Blizzard® Starcraft® II AI and Machine Learning License at"
|
30
|
+
say "https://blzdistsc2-a.akamaihd.net/AI_AND_MACHINE_LEARNING_LICENSE.html"
|
31
|
+
say "It is PERMISSIVE and grants you freedoms over the standard EULA."
|
32
|
+
say "We do not record this action, but depend on software goverend by that license to continue."
|
33
33
|
puts 'If you accept, type "iagreetotheeula" (without quotes) and press ENTER to continue:'
|
34
34
|
|
35
35
|
while $stdin.gets.chomp != "iagreetotheeula"
|
36
|
-
|
36
|
+
say ""
|
37
37
|
puts 'Type "iagreetotheeula" (without quotes) and press ENTER to continue:'
|
38
38
|
end
|
39
|
-
|
40
|
-
|
41
|
-
|
39
|
+
say ""
|
40
|
+
say ""
|
41
|
+
say "Great decision."
|
42
42
|
|
43
|
+
require "sc2ai"
|
43
44
|
Async do
|
44
45
|
Sc2.logger.level = :fatal
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
46
|
+
say "SC2 will launch a blank window, be unresponsive, but download about 100mb in the background."
|
47
|
+
say "Let it finish and close itself."
|
48
|
+
say "Press ENTER if you understand."
|
49
|
+
ask ""
|
49
50
|
|
50
|
-
|
51
|
-
|
52
|
-
|
51
|
+
say ""
|
52
|
+
say ""
|
53
|
+
say "This will only take a minute..."
|
53
54
|
|
54
55
|
Sc2.config.version = nil
|
55
56
|
client = Sc2::ClientManager.obtain(0)
|
@@ -68,22 +69,23 @@ module Sc2
|
|
68
69
|
base_build = "75689"
|
69
70
|
path = Sc2::Paths.executable(base_build: base_build)
|
70
71
|
if path.include?(base_build) && Pathname(path).exist?
|
71
|
-
|
72
|
-
|
73
|
-
|
72
|
+
say " "
|
73
|
+
say "Success. Download complete.", :green
|
74
|
+
say " "
|
74
75
|
else
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
76
|
+
say "Error. Slightly worrying, but no fear.", :red
|
77
|
+
say "To manually setup, grab the latest ladder maps and add them to your map folder."
|
78
|
+
say "Grab the latest maps from https://aiarena.net/wiki/maps/"
|
79
|
+
say "Detected map folder: #{Sc2::Paths.maps_dir}"
|
80
|
+
say "Then, download any recent replay from https://aiarena.net/ and double click to launch"
|
80
81
|
end
|
81
82
|
|
83
|
+
observer.api.quit
|
82
84
|
observer.disconnect
|
83
|
-
|
85
|
+
say "Generating sc2ai.yml to always use 4.10..."
|
84
86
|
Sc2.config.config_file.write({"version" => "4.10"}.to_yaml.to_s)
|
85
|
-
|
86
|
-
|
87
|
+
say ""
|
88
|
+
say "Done. You're good to go.", :green
|
87
89
|
ensure
|
88
90
|
Sc2::ClientManager.stop(0)
|
89
91
|
end
|
@@ -133,7 +135,7 @@ module Sc2
|
|
133
135
|
option :RealTime, type: :boolean, default: false, desc: "Forces realtime flag"
|
134
136
|
def laddermatch
|
135
137
|
require "sc2ai"
|
136
|
-
|
138
|
+
|
137
139
|
unless Sc2.ladder?
|
138
140
|
raise Sc2::Error, "This command is only for competing on aiarena.net"
|
139
141
|
end
|
data/lib/sc2ai/cli/ladderzip.rb
CHANGED
@@ -18,11 +18,21 @@ module Sc2
|
|
18
18
|
desc "Builds a ladder zip using docker (requires docker)"
|
19
19
|
|
20
20
|
def docker_exists
|
21
|
-
if Kernel.system("docker --version")
|
21
|
+
if Kernel.system("docker --version", out: File::NULL, err: File::NULL)
|
22
22
|
say_status("docker", "found", :green)
|
23
23
|
else
|
24
24
|
say_status("docker", "not found", :red)
|
25
25
|
say("Please install 'docker' and/or ensure it's in your PATH to continue.")
|
26
|
+
raise SystemExit
|
27
|
+
end
|
28
|
+
|
29
|
+
if Kernel.system("docker info", out: File::NULL, err: File::NULL)
|
30
|
+
say_status("docker engine", "found", :green)
|
31
|
+
else
|
32
|
+
say(" ")
|
33
|
+
say_status("docker engine", "offline", :red)
|
34
|
+
say("Please start docker engine. If you have Docker Desktop installed, open it before retrying.")
|
35
|
+
raise SystemExit
|
26
36
|
end
|
27
37
|
end
|
28
38
|
|
@@ -64,7 +74,8 @@ module Sc2
|
|
64
74
|
|
65
75
|
def create_executable
|
66
76
|
template("ladderzip/bin/ladder", "./.build/bin/ladder")
|
67
|
-
|
77
|
+
# This fails on Windows. creating by hand in #link_ladder function below.
|
78
|
+
# create_link("./.build/#{botname}", "./bin/ladder")
|
68
79
|
end
|
69
80
|
|
70
81
|
def start_container
|
@@ -86,9 +97,11 @@ module Sc2
|
|
86
97
|
include_pattern.collect! { |pt| pt.delete_prefix("!") }
|
87
98
|
|
88
99
|
files = Dir.glob("**/*")
|
100
|
+
|
89
101
|
ignored_files = files
|
90
102
|
.select { |f| ignore_pattern.any? { |p| File.fnmatch?(p, f) } }
|
91
|
-
.reject
|
103
|
+
.reject { |f| include_pattern.any? { |p| File.fnmatch?(p, f) } }
|
104
|
+
|
92
105
|
files -= ignored_files unless ignored_files.nil?
|
93
106
|
|
94
107
|
files.each do |file|
|
@@ -103,6 +116,12 @@ module Sc2
|
|
103
116
|
Kernel.system(cmd)
|
104
117
|
end
|
105
118
|
|
119
|
+
def link_ladder
|
120
|
+
say_status("docker", "linking executable...", :cyan)
|
121
|
+
cmd = "docker compose -f #{@compose_file} exec --workdir /root/ruby-builder bot ln -s ./bin/ladder ./#{botname}"
|
122
|
+
Kernel.system(cmd)
|
123
|
+
end
|
124
|
+
|
106
125
|
def install_gems
|
107
126
|
say_status("docker", "bundle install...", :cyan)
|
108
127
|
cmd = "docker compose -f #{@compose_file} exec --workdir /root/ruby-builder bot bundle install"
|
data/lib/sc2ai/player/actions.rb
CHANGED
@@ -17,7 +17,7 @@ module Sc2
|
|
17
17
|
# Queues a Api::ActionRaw. Perform ability on unit_tags optionally on target_world_space_pos/target_unit_tag
|
18
18
|
# @param unit_tags [Array<Integer>]
|
19
19
|
# @param ability_id [Integer]
|
20
|
-
# @param queue_command [Boolean]
|
20
|
+
# @param queue_command [Boolean] shift+command
|
21
21
|
# @param target_world_space_pos [Api::Point2D]
|
22
22
|
# @param target_unit_tag [Integer]
|
23
23
|
def action_raw_unit_command(unit_tags:, ability_id:, queue_command: false, target_world_space_pos: nil, target_unit_tag: nil)
|
@@ -39,7 +39,7 @@ module Sc2
|
|
39
39
|
# @param units [Array<Integer>,Integer,Api::Unit] can be an Api::Unit, array of Api::Unit#tag or single tag
|
40
40
|
# @param ability_id [Integer]
|
41
41
|
# @param target [Api::Unit, Integer, Api::Point2D] is a unit, unit tag or a Api::Point2D
|
42
|
-
# @param queue_command [Boolean]
|
42
|
+
# @param queue_command [Boolean] shift+command
|
43
43
|
def action(units:, ability_id:, target: nil, queue_command: false)
|
44
44
|
unit_tags = unit_tags_from_source(units)
|
45
45
|
|
@@ -79,6 +79,7 @@ module Sc2
|
|
79
79
|
# Warps in unit type at target (location or pylon) with optional source units (warp gates)
|
80
80
|
# When not specifying the specific warp gate(s), all warpgates will be used
|
81
81
|
# @param unit_type_id [Integer] Api::UnitTypeId the unit type which will do the creation
|
82
|
+
# @param queue_command [Boolean] shift+command
|
82
83
|
# @param target [Api::Point2D, Integer] is a unit tag or a Api::Point2D
|
83
84
|
def warp(unit_type_id:, target:, queue_command:, units: nil)
|
84
85
|
warp_ability = Api::TechTree.unit_type_creation_abilities(
|
@@ -128,7 +129,7 @@ module Sc2
|
|
128
129
|
# @param ability_id [Api::AbilityId]
|
129
130
|
# @param target_screen_coord [Api::Point2I]
|
130
131
|
# @param target_minimap_coord [Api::Point2I]
|
131
|
-
# @param queue_command [Boolean]
|
132
|
+
# @param queue_command [Boolean] shift+command
|
132
133
|
# @return [void]
|
133
134
|
def action_spatial_unit_command(ability_id:, target_screen_coord: nil, target_minimap_coord: nil, queue_command: false)
|
134
135
|
queue_action Api::Action.new(
|
data/lib/sc2ai/player/debug.rb
CHANGED
@@ -37,6 +37,52 @@ module Sc2
|
|
37
37
|
)
|
38
38
|
end
|
39
39
|
|
40
|
+
# Prints text on screen from top and left
|
41
|
+
# @param text [String] will respect newlines
|
42
|
+
# @param left_percent [Numeric] range 0..100. percent from left of screen
|
43
|
+
# @param top_percent [Numeric] range 0..100. percent from top of screen
|
44
|
+
# @param color [Api::Color] default white
|
45
|
+
# @param size [Size] of font, default 14px
|
46
|
+
# @return [void]
|
47
|
+
def debug_text_screen(text, left_percent: 1.0, top_percent: 1.0, color: nil, size: 14)
|
48
|
+
queue_debug_command Api::DebugCommand.new(
|
49
|
+
draw: Api::DebugDraw.new(
|
50
|
+
text: [
|
51
|
+
Api::DebugText.new(
|
52
|
+
text:,
|
53
|
+
virtual_pos: Api::Point.new(
|
54
|
+
x: left_percent.to_f / 100,
|
55
|
+
y: top_percent.to_f / 100
|
56
|
+
),
|
57
|
+
color:,
|
58
|
+
size:
|
59
|
+
)
|
60
|
+
]
|
61
|
+
)
|
62
|
+
)
|
63
|
+
end
|
64
|
+
|
65
|
+
# Prints text on screen at 3d world position
|
66
|
+
# @param text [String] will respect newlines
|
67
|
+
# @param point [Api::Point] point in the world, i.e. unit.pos
|
68
|
+
# @param color [Api::Color] default white
|
69
|
+
# @param size [Size] of font, default 14px
|
70
|
+
# @return [void]
|
71
|
+
def debug_text_world(text, point:, color: nil, size: 14)
|
72
|
+
queue_debug_command Api::DebugCommand.new(
|
73
|
+
draw: Api::DebugDraw.new(
|
74
|
+
text: [
|
75
|
+
Api::DebugText.new(
|
76
|
+
text:,
|
77
|
+
world_pos: point,
|
78
|
+
color:,
|
79
|
+
size:
|
80
|
+
)
|
81
|
+
]
|
82
|
+
)
|
83
|
+
)
|
84
|
+
end
|
85
|
+
|
40
86
|
# Draws a line between two Api::Point's for color
|
41
87
|
# @param p0 [Api::Point] the first point
|
42
88
|
# @param p1 [Api::Point] the second point
|
@@ -63,8 +109,8 @@ module Sc2
|
|
63
109
|
# # Draws a box on structure placement grid
|
64
110
|
# debug_draw_box(point: unit.pos, radius: unit.footprint_radius)
|
65
111
|
#
|
66
|
-
#
|
67
|
-
#
|
112
|
+
# @note Api::Color RGB is broken for this command. Will use min(r,b)
|
113
|
+
# @note Z index is elevated 0.02 so the line is visible and doesn't clip through terrain
|
68
114
|
# @param point [Api::Point]
|
69
115
|
# @param radius [Float] default one tile wide, 1.0
|
70
116
|
# @param color [Api::Color] default white. min(r,b) is used for both r&b
|
@@ -74,8 +120,8 @@ module Sc2
|
|
74
120
|
draw: Api::DebugDraw.new(
|
75
121
|
boxes: [
|
76
122
|
Api::DebugBox.new(
|
77
|
-
min: Api::Point.new(x: point.x - radius, y: point.y - radius, z: point.z + 0.
|
78
|
-
max: Api::Point.new(x: point.x + radius, y: point.y + radius, z: point.z + (radius * 2) + 0.
|
123
|
+
min: Api::Point.new(x: point.x - radius, y: point.y - radius, z: point.z + 0.02),
|
124
|
+
max: Api::Point.new(x: point.x + radius, y: point.y + radius, z: point.z + (radius * 2) + 0.02),
|
79
125
|
color:
|
80
126
|
)
|
81
127
|
]
|
@@ -7,7 +7,7 @@ module Sc2
|
|
7
7
|
class Player
|
8
8
|
# Holds map and geography helper functions
|
9
9
|
class Geometry
|
10
|
-
# @!attribute
|
10
|
+
# @!attribute bot
|
11
11
|
# @return [Sc2::Player] player with active connection
|
12
12
|
attr_accessor :bot
|
13
13
|
|
@@ -72,6 +72,7 @@ module Sc2
|
|
72
72
|
# Each value in [row][column] holds a boolean value represented as an integer
|
73
73
|
# It does not say whether a position is occupied by another building.
|
74
74
|
# One pixel covers one whole block. Rounds fractionated positions down.
|
75
|
+
# @return [Numo::Bit]
|
75
76
|
def parsed_placement_grid
|
76
77
|
if @parsed_placement_grid.nil?
|
77
78
|
image_data = bot.game_info.start_raw.placement_grid
|
@@ -83,6 +84,7 @@ module Sc2
|
|
83
84
|
end
|
84
85
|
|
85
86
|
# Returns a grid where ony the expo locations are marked
|
87
|
+
# @return [Numo::Bit]
|
86
88
|
def expo_placement_grid
|
87
89
|
if @expo_placement_grid.nil?
|
88
90
|
@expo_placement_grid = Numo::Bit.zeros(map_height, map_width)
|
@@ -97,6 +99,7 @@ module Sc2
|
|
97
99
|
end
|
98
100
|
|
99
101
|
# Returns a grid where powered locations are marked true
|
102
|
+
# @return [Numo::Bit]
|
100
103
|
def parsed_power_grid
|
101
104
|
# Cache for based on power unit tags
|
102
105
|
cache_key = bot.power_sources.map(&:tag).sort.hash
|
@@ -202,7 +205,7 @@ module Sc2
|
|
202
205
|
# @param y [Float, Integer]
|
203
206
|
# @return [Boolean] true if location is powered
|
204
207
|
def powered?(x:, y:)
|
205
|
-
|
208
|
+
parsed_power_grid[y.to_i, x.to_i] != 0
|
206
209
|
end
|
207
210
|
|
208
211
|
# Returns whether a x/y block is pathable as per minimap
|
@@ -399,12 +402,25 @@ module Sc2
|
|
399
402
|
output_grid
|
400
403
|
end
|
401
404
|
|
405
|
+
# Returns own 2d start position as set by initial camera
|
406
|
+
# This differs from position of first base structure
|
407
|
+
# @return [Api::Point2D]
|
408
|
+
def start_position
|
409
|
+
@start_position ||= bot.observation.raw_data.player.camera
|
410
|
+
end
|
411
|
+
|
412
|
+
# Returns the enemy 2d start position
|
413
|
+
# @return [Api::Point2D]
|
414
|
+
def enemy_start_position
|
415
|
+
bot.game_info.start_raw.start_locations.first
|
416
|
+
end
|
417
|
+
|
402
418
|
# Gets expos and surrounding minerals
|
403
419
|
# The index is a build location for an expo and the value is a UnitGroup, which has minerals and geysers
|
404
420
|
# @example
|
405
|
-
# random_expo = expansions.keys.sample #=> Point2D
|
421
|
+
# random_expo = geo.expansions.keys.sample #=> Point2D
|
406
422
|
# expo_resources = geo.expansions[random_expo] #=> UnitGroup
|
407
|
-
# alive_minerals = expo_resources.minerals
|
423
|
+
# alive_minerals = expo_resources.minerals & neutral.minerals
|
408
424
|
# geysers = expo_resources.geysers
|
409
425
|
# @return [Hash<Api::Point2D, UnitGroup>] Location => UnitGroup of resources (minerals+geysers)
|
410
426
|
def expansions
|
@@ -503,24 +519,77 @@ module Sc2
|
|
503
519
|
end
|
504
520
|
|
505
521
|
# Returns a slice of #expansions where a base hasn't been built yet
|
522
|
+
# The has index is a build position and the value is a UnitGroup of resources for the base
|
506
523
|
# @example
|
507
524
|
# # Lets find the nearest unoccupied expo
|
508
525
|
# expo_pos = expansions_unoccupied.keys.min { |p2d| p2d.distance_to(structures.hq.first) }
|
509
526
|
# # What minerals/geysers does it have?
|
510
527
|
# puts expansions_unoccupied[expo_pos].minerals # or expansions[expo_pos]... => UnitGroup
|
511
528
|
# puts expansions_unoccupied[expo_pos].geysers # or expansions[expo_pos]... => UnitGroup
|
512
|
-
# @return [Hash<Api::Point2D
|
529
|
+
# @return [Hash<Api::Point2D, UnitGroup>] Location => UnitGroup of resources (minerals+geysers)
|
513
530
|
def expansions_unoccupied
|
514
531
|
taken_bases = bot.structures.hq.map { |hq| hq.pos.to_p2d } + bot.enemy.structures.hq.map { |hq| hq.pos.to_p2d }
|
515
532
|
remaining_points = expansion_points - taken_bases
|
516
533
|
expansions.slice(*remaining_points)
|
517
534
|
end
|
518
535
|
|
536
|
+
# Gets minerals for a base or base position
|
537
|
+
# @param base [Api::Unit, Sc2::Position] base Unit or Position
|
538
|
+
# @return [Sc2::UnitGroup] UnitGroup of minerals for the base
|
539
|
+
def minerals_for_base(base)
|
540
|
+
# resources_for_base contains what we need, but slice neutral.minerals,
|
541
|
+
# so that only active patches remain
|
542
|
+
bot.neutral.minerals.slice(*resources_for_base(base).minerals.tags)
|
543
|
+
end
|
544
|
+
|
545
|
+
# Gets geysers for a base or base position
|
546
|
+
# @param base [Api::Unit, Sc2::Position] base Unit or Position
|
547
|
+
# @return [Sc2::UnitGroup] UnitGroup of geysers for the base
|
548
|
+
def geysers_for_base(base)
|
549
|
+
resources_for_base(base).geysers
|
550
|
+
end
|
551
|
+
|
552
|
+
# @private
|
553
|
+
# @param base [Api::Unit, Sc2::Position] base Unit or Position
|
554
|
+
# @return [Sc2::UnitGroup] UnitGroup of resources (minerals+geysers)
|
555
|
+
private def resources_for_base(base)
|
556
|
+
pos = base.is_a?(Api::Unit) ? base.pos : base
|
557
|
+
|
558
|
+
# If we have a base setup for this exact position, use it
|
559
|
+
if expansions.has_key?(pos)
|
560
|
+
return expansions[pos]
|
561
|
+
end
|
562
|
+
|
563
|
+
# Tolerance for misplaced base: Find the nearest base to this position
|
564
|
+
pos = expansion_points.min_by { |p| p.distance_to(pos) }
|
565
|
+
|
566
|
+
expansions[pos]
|
567
|
+
end
|
568
|
+
|
569
|
+
# Gets gasses for a base or base position
|
570
|
+
# @param base [Api::Unit, Sc2::Position] base Unit or Position
|
571
|
+
# @return [Sc2::UnitGroup] UnitGroup of geysers for the base
|
572
|
+
def gas_for_base(base)
|
573
|
+
# No gas structures at all yet, return nothing
|
574
|
+
return UnitGroup.new if bot.structures.gas.size.zero?
|
575
|
+
|
576
|
+
geysers = geysers_for_base(base)
|
577
|
+
|
578
|
+
# Mineral-only base, return nothing
|
579
|
+
return UnitGroup.new if geysers.size == 0
|
580
|
+
|
581
|
+
# Loop and collect gasses places exactly on-top of geysers
|
582
|
+
bot.structures.gas.select do |gas|
|
583
|
+
geysers.any? { |geyser| geyser.pos.to_p2d.eql?(gas.pos.to_p2d) }
|
584
|
+
end
|
585
|
+
end
|
586
|
+
|
519
587
|
# Gets buildable point grid for squares of size, i.e. 3 = 3x3 placements
|
520
588
|
# Uses pathing grid internally, to ignore taken positions
|
521
589
|
# Does not query the api and is generally fast.
|
522
590
|
# @param length [Integer] length of the building, 2 for depot/pylon, 3 for rax/gate
|
523
|
-
# @param on_creep [Boolean] whether this build
|
591
|
+
# @param on_creep [Boolean] whether this build location should be on creep
|
592
|
+
# @return [Array<Array<(Float, Float)>>] Array of [x,y] tuples
|
524
593
|
def build_coordinates(length:, on_creep: false, in_power: false)
|
525
594
|
length = 1 if length < 1
|
526
595
|
@_build_coordinates ||= {}
|
data/lib/sc2ai/player/units.rb
CHANGED
@@ -47,10 +47,10 @@ module Sc2
|
|
47
47
|
|
48
48
|
# Event-driven unit groups ---
|
49
49
|
|
50
|
+
# @!attribute event_units_created
|
50
51
|
# Units created since last frame (visible only, units not structures)
|
51
52
|
# Read this on_step. Alternative to callback on_unit_created
|
52
|
-
#
|
53
|
-
# @!attribute event_units_created
|
53
|
+
# @note Morphed units should watch #event_units_type_changed
|
54
54
|
# @return [Sc2::UnitGroup] group of created units
|
55
55
|
attr_accessor :event_units_created
|
56
56
|
|
data/lib/sc2ai/player.rb
CHANGED
@@ -97,7 +97,7 @@ module Sc2
|
|
97
97
|
@difficulty = difficulty
|
98
98
|
@ai_build = ai_build
|
99
99
|
@realtime = false
|
100
|
-
@step_count =
|
100
|
+
@step_count = 2
|
101
101
|
|
102
102
|
@enable_feature_layer = false
|
103
103
|
@interface_options = {}
|
@@ -201,7 +201,7 @@ module Sc2
|
|
201
201
|
|
202
202
|
# Override to customize initialization
|
203
203
|
# Alias of before_join
|
204
|
-
# You can enable_feature_layer=true, set
|
204
|
+
# You can enable_feature_layer=true, set step_count, define
|
205
205
|
# @example
|
206
206
|
# def configure
|
207
207
|
# step_count = 4 # Update less frequently
|
@@ -490,6 +490,8 @@ module Sc2
|
|
490
490
|
def started
|
491
491
|
# Calculate expansions
|
492
492
|
geo.expansions
|
493
|
+
# Set our start position base on camera
|
494
|
+
geo.start_position
|
493
495
|
end
|
494
496
|
|
495
497
|
# Moves emulation ahead and calls back #on_step
|
@@ -498,9 +500,7 @@ module Sc2
|
|
498
500
|
# Sc2.logger.debug "#{self.class} step_forward"
|
499
501
|
|
500
502
|
unless @realtime
|
501
|
-
|
502
|
-
num_steps = 1
|
503
|
-
@api.step(num_steps)
|
503
|
+
@api.step(@step_count)
|
504
504
|
end
|
505
505
|
|
506
506
|
refresh_state
|
@@ -610,9 +610,9 @@ module Sc2
|
|
610
610
|
# ##TODO: perfect loop implementation
|
611
611
|
# observation has an optional param game_loop and will only return once that step is reached (blocking).
|
612
612
|
# without it, it returns things as they are.
|
613
|
-
# broadly, i think this is what it should be doing, with
|
613
|
+
# broadly, i think this is what it should be doing, with step_count being minimum of 1, so no zero-steps occur.
|
614
614
|
# @example
|
615
|
-
# desired_game_loop = current_game_loop +
|
615
|
+
# desired_game_loop = current_game_loop + step_count
|
616
616
|
# response = client.observation(game_loop: desired_game_loop)
|
617
617
|
#
|
618
618
|
# if response.game_loop > desired_game_loop {
|
@@ -1,6 +1,15 @@
|
|
1
1
|
module Api
|
2
2
|
# Adds additional functionality to message object Api::Point
|
3
3
|
module PointExtension
|
4
|
+
# @private
|
5
|
+
def hash
|
6
|
+
[x, y, z].hash
|
7
|
+
end
|
8
|
+
|
9
|
+
def eql?(other)
|
10
|
+
self.class == other.class && hash == other.hash
|
11
|
+
end
|
12
|
+
|
4
13
|
# Creates a Point2D using x and y
|
5
14
|
# @return [Api::Point2D]
|
6
15
|
def to_p2d
|
@@ -13,6 +22,9 @@ module Api
|
|
13
22
|
# @example
|
14
23
|
# Api::Point[1,2,3] # Where x is 1.0, y is 2.0 and z is 3.0
|
15
24
|
# @return [Api::Point]
|
25
|
+
# @param [Float] x
|
26
|
+
# @param [Float] y
|
27
|
+
# @param [Float] z
|
16
28
|
def [](x, y, z)
|
17
29
|
Api::Point.new(x: x, y: y, z: z)
|
18
30
|
end
|
@@ -20,4 +32,4 @@ module Api
|
|
20
32
|
end
|
21
33
|
end
|
22
34
|
Api::Point.include Api::PointExtension
|
23
|
-
Api::Point.
|
35
|
+
Api::Point.extend Api::PointExtension::ClassMethods
|