sc2ai 0.6.1 → 0.6.5
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/docker_build/Dockerfile.aiarenabot +12 -0
- data/docker_build/Dockerfile.ruby +17 -17
- data/docker_build/docker-compose-base-image.yml +0 -1
- data/docker_build/docker-compose-ladderzip.yml +0 -1
- data/docker_build/docker-compose-versus-bot.yml +15 -0
- data/lib/sc2ai/cli/cli.rb +7 -28
- data/lib/sc2ai/cli/ladderzip.rb +5 -0
- data/lib/sc2ai/cli/versus_bot.rb +305 -0
- data/lib/sc2ai/local_play/client_manager.rb +3 -1
- data/lib/sc2ai/player/game_state.rb +2 -7
- data/lib/sc2ai/player/geo.rb +19 -2
- data/lib/sc2ai/player/units.rb +19 -0
- data/lib/sc2ai/player.rb +3 -6
- data/lib/sc2ai/protocol/extensions/unit.rb +22 -23
- data/lib/sc2ai/unit_group/filter_ext.rb +13 -2
- data/lib/sc2ai/version.rb +1 -1
- data/lib/templates/ladderzip/bin/ladder.tt +1 -7
- data/lib/templates/ladderzip/ladderbots.json.tt +11 -0
- data/lib/templates/new/.ladderignore +1 -1
- data/sig/sc2ai.rbs +53 -24
- metadata +25 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9da11456b2d9014945dc54f3a92dfc47cef7ecf40be7facaceb75bc8a04df9b1
|
4
|
+
data.tar.gz: e5926c8416141ef1a8677e8fb0832126414cdbed3729951f236ecab14274d336
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3ce8e35ea773557def35d44eff9dfc63a475b09c82ad573644cae647b222da83f65047a0a935d0465aad5919002144e6c28543792539f4783dad88700225f5ef
|
7
|
+
data.tar.gz: 5848c68e72fad356096f6b7bf2da049ee9f7f3a4c36676f682401d9e612f71d26d8950d94c733e4cda04887592a2be030faadc3f6e2d1b918528c2aa63e5eada
|
@@ -4,12 +4,12 @@ LABEL service="bot-ruby-local"
|
|
4
4
|
USER root
|
5
5
|
WORKDIR /root/ruby-builder
|
6
6
|
|
7
|
-
ARG RUBY_VERSION=3.4.
|
7
|
+
ARG RUBY_VERSION=3.4.3
|
8
8
|
ARG DEBIAN_DISABLE_RUBYGEMS_INTEGRATION=true
|
9
9
|
|
10
10
|
# Deps - Ruby build
|
11
11
|
RUN apt-get update
|
12
|
-
RUN apt install --assume-yes rustc curl build-essential libssl-dev zlib1g-dev libgmp-dev libffi-dev uuid-dev
|
12
|
+
RUN apt install --assume-yes rustc curl build-essential libssl-dev zlib1g-dev libgmp-dev libffi-dev uuid-dev
|
13
13
|
|
14
14
|
# Deps - libyaml from source
|
15
15
|
RUN curl https://pyyaml.org/download/libyaml/yaml-0.2.5.tar.gz -o yaml-0.2.5.tar.gz
|
@@ -33,20 +33,20 @@ RUN rm yaml-0.2.5.tar.gz
|
|
33
33
|
|
34
34
|
# Package config
|
35
35
|
# numo-linalg needs openblas, copy to ruby-prefix/lib/ dir.
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
36
|
+
RUN apt download libopenblas0-serial
|
37
|
+
RUN mkdir openblas
|
38
|
+
RUN dpkg-deb -x ./libopenblas*.deb openblas
|
39
|
+
RUN cp -d openblas/usr/lib/x86_64-linux-gnu/openblas-serial/* /root/ruby-builder/.ruby/lib/
|
40
|
+
RUN rm -rf ./openblas
|
41
|
+
RUN rm ./libopenblas0-serial*.deb
|
42
|
+
|
43
|
+
RUN apt download libgfortran5
|
44
|
+
RUN mkdir libgfortran5
|
45
|
+
RUN dpkg-deb -x ./libgfortran*.deb libgfortran5
|
46
|
+
RUN find libgfortran5
|
47
|
+
RUN cp libgfortran5/usr/lib/x86_64-linux-gnu/libgfortran.so.5 /root/ruby-builder/.ruby/lib/
|
48
|
+
RUN rm -rf ./libgfortran5
|
49
|
+
RUN rm ./libgfortran5*.deb
|
50
50
|
|
51
51
|
RUN /root/ruby-builder/.ruby/bin/ruby --yjit -v
|
52
52
|
|
@@ -65,7 +65,7 @@ RUN /root/ruby-builder/.ruby/bin/bundle config set --global without test develop
|
|
65
65
|
RUN apt-get update
|
66
66
|
RUN apt-get purge $(aptitude search '~i!~M!~prequired!~pimportant!~R~prequired!~R~R~prequired!~R~pimportant!~R~R~pimportant!busybox!grub!initramfs-tools' | awk '{print $2}') --assume-yes
|
67
67
|
RUN apt-get purge aptitude --assume-yes
|
68
|
-
RUN apt-get install zip openssl build-essential
|
68
|
+
RUN apt-get install zip openssl build-essential libffi-dev --assume-yes
|
69
69
|
RUN apt-get autoremove --assume-yes
|
70
70
|
RUN apt-get clean --assume-yes
|
71
71
|
|
@@ -0,0 +1,15 @@
|
|
1
|
+
services:
|
2
|
+
versus_bot:
|
3
|
+
container_name: versus_bot_container
|
4
|
+
image: dysonreturns/arenaclient-bot:local
|
5
|
+
environment:
|
6
|
+
- CPPFLAGS=-DPNG_ARM_NEON_OPT=0
|
7
|
+
platform: "linux/amd64"
|
8
|
+
tty: true
|
9
|
+
build:
|
10
|
+
dockerfile: ./Dockerfile.aiarenabot
|
11
|
+
ports:
|
12
|
+
- "${SERVER_BASE_PORT}:${SERVER_BASE_PORT}/udp"
|
13
|
+
- "${SERVER_GAME_PORT}:${SERVER_GAME_PORT}"
|
14
|
+
- "${CLIENT_BASE_PORT}:${CLIENT_BASE_PORT}/udp"
|
15
|
+
- "${CLIENT_GAME_PORT}:${CLIENT_GAME_PORT}"
|
data/lib/sc2ai/cli/cli.rb
CHANGED
@@ -2,6 +2,7 @@ require "thor"
|
|
2
2
|
|
3
3
|
require "sc2ai/cli/new"
|
4
4
|
require "sc2ai/cli/ladderzip"
|
5
|
+
require "sc2ai/cli/versus_bot"
|
5
6
|
|
6
7
|
module Sc2
|
7
8
|
# Command line utilities
|
@@ -16,6 +17,7 @@ module Sc2
|
|
16
17
|
|
17
18
|
register(Sc2::Cli::New, "new", "new BOTNAME RACE", "Creates botname folder with bot template")
|
18
19
|
register(Sc2::Cli::Ladderzip, "ladderzip", "ladderzip BOTNAME", "Prepares a zip via docker for supplied aiarena bot name")
|
20
|
+
register(Sc2::Cli::VersusBot, "vsbot", "vsbot FILENAME", "Copies target zip to docker and starts a match")
|
19
21
|
|
20
22
|
# TODO: Use thor's #ask and other methods helpers of raw $stdin and puts.
|
21
23
|
desc "setup410", "Downloads and install SC2 v4.10"
|
@@ -70,7 +72,11 @@ module Sc2
|
|
70
72
|
say "Generating sc2ai.yml to always use 4.10..."
|
71
73
|
Sc2.config.config_file.write({"version" => "4.10"}.to_yaml.to_s)
|
72
74
|
say ""
|
73
|
-
say "Done. You're good to go."
|
75
|
+
say "Done. You're good to go."
|
76
|
+
say "To run an example match, execute:"
|
77
|
+
say ""
|
78
|
+
say "ruby run_example_match.rb", :green
|
79
|
+
say ""
|
74
80
|
ensure
|
75
81
|
Sc2::ClientManager.stop(0)
|
76
82
|
end
|
@@ -95,23 +101,6 @@ module Sc2
|
|
95
101
|
Sc2.logger.info " interface_options: #{$bot.interface_options}"
|
96
102
|
end
|
97
103
|
|
98
|
-
# desc "ladderzip", "Uses docker to cross-compile a compatible binary for the ladder"
|
99
|
-
# def ladderzip
|
100
|
-
# end
|
101
|
-
|
102
|
-
# desc "ladderquickzip", "Ladder zip, if you have no additional gems installed."
|
103
|
-
# long_desc <<-LONGDESC
|
104
|
-
# `sc2ai ladderzip_basic` will download a portable ruby and gems, then copy your bot files into a zip file.
|
105
|
-
#
|
106
|
-
# If you have added any gems which need compiling, this option is not for you.
|
107
|
-
# Any gems built with native extensions will likely not be targeting the linux platform and be nonfunctional.
|
108
|
-
#
|
109
|
-
# For a cross-platform build, use ladderzip instead. This will launch a Docker container to compile a zip for you.
|
110
|
-
# LONGDESC
|
111
|
-
# def ladderquickzip
|
112
|
-
# raise Sc2::Error, "Not yet implemented."
|
113
|
-
# end
|
114
|
-
|
115
104
|
desc "laddermatch", "Joins a ladder match as per aiarena spec"
|
116
105
|
option :GamePort, required: true, desc: "SC2 port. Corresponds to SC2 launch option '-port'"
|
117
106
|
option :LadderServer, required: true, desc: "SC2 server ip or hostname"
|
@@ -147,16 +136,6 @@ module Sc2
|
|
147
136
|
$bot.play
|
148
137
|
end.wait
|
149
138
|
end
|
150
|
-
|
151
|
-
# desc "install APP_NAME", "install one of the available apps" # [4]
|
152
|
-
# method_options :force => :boolean, :alias => :string # [5]
|
153
|
-
# def install(name)
|
154
|
-
# user_alias = options[:alias]
|
155
|
-
# if options.force?
|
156
|
-
# # do something
|
157
|
-
# end
|
158
|
-
# # other code
|
159
|
-
# end
|
160
139
|
end
|
161
140
|
# standard:enable Style/GlobalVars
|
162
141
|
end
|
data/lib/sc2ai/cli/ladderzip.rb
CHANGED
@@ -78,6 +78,10 @@ module Sc2
|
|
78
78
|
# create_link("./.build/#{botname}", "./bin/ladder")
|
79
79
|
end
|
80
80
|
|
81
|
+
def create_ladderbots_json
|
82
|
+
template("ladderzip/ladderbots.json", "./.build/ladderbots.json")
|
83
|
+
end
|
84
|
+
|
81
85
|
def start_container
|
82
86
|
cmd = "docker compose -f #{@compose_file} up bot -d --force-recreate"
|
83
87
|
Kernel.system(cmd)
|
@@ -95,6 +99,7 @@ module Sc2
|
|
95
99
|
ignore_pattern -= include_pattern
|
96
100
|
|
97
101
|
include_pattern.collect! { |pt| pt.delete_prefix("!") }
|
102
|
+
include_pattern.collect! { |pt| pt + "**/*" if pt.ends_with?("/") }
|
98
103
|
|
99
104
|
files = Dir.glob("**/*")
|
100
105
|
|
@@ -0,0 +1,305 @@
|
|
1
|
+
module Sc2
|
2
|
+
# Command line utilities
|
3
|
+
class Cli < Thor
|
4
|
+
# Command line action allowing your bot to play vs a ladder bot via docker
|
5
|
+
class VersusBot < Thor::Group
|
6
|
+
# standard:disable Style/GlobalVars
|
7
|
+
include Thor::Actions
|
8
|
+
|
9
|
+
# Define arguments and options
|
10
|
+
argument :filename, desc: "Path to zip. Relative or absolute."
|
11
|
+
argument :map, desc: "Absolute path or shot name, i.e. 'ThunderbirdAIE', 'ThunderbirdAIE.SC2Map'.", default: "ThunderbirdAIE"
|
12
|
+
desc "Sends a bot zip to the ladder and makes it play against us"
|
13
|
+
|
14
|
+
def get_botname
|
15
|
+
@enemy_botname = File.basename(filename, ".zip")
|
16
|
+
end
|
17
|
+
|
18
|
+
def docker_exists
|
19
|
+
if Kernel.system("docker --version", out: File::NULL, err: File::NULL)
|
20
|
+
say_status("docker", "found", :green)
|
21
|
+
else
|
22
|
+
say_status("docker", "not found", :red)
|
23
|
+
say("Please install 'docker' and/or ensure it's in your PATH to continue.")
|
24
|
+
raise SystemExit
|
25
|
+
end
|
26
|
+
|
27
|
+
if Kernel.system("docker info", out: File::NULL, err: File::NULL)
|
28
|
+
say_status("docker engine", "found", :green)
|
29
|
+
else
|
30
|
+
say(" ")
|
31
|
+
say_status("docker engine", "offline", :red)
|
32
|
+
say("Please start docker engine. If you have Docker Desktop installed, open it before retrying.")
|
33
|
+
raise SystemExit
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def bot_validation
|
38
|
+
if Pathname("./boot.rb").exist?
|
39
|
+
say_status("detected boot.rb", "found", :green)
|
40
|
+
else
|
41
|
+
say_status("detected boot.rb", "not found", :red)
|
42
|
+
raise Sc2::Error, "boot.rb not found. Bot started from wrong directory."
|
43
|
+
end
|
44
|
+
|
45
|
+
ENV.delete("AIARENA")
|
46
|
+
require "./boot"
|
47
|
+
|
48
|
+
if $bot.is_a? Sc2::Player
|
49
|
+
say_status("instance $bot", $bot.class, :green)
|
50
|
+
else
|
51
|
+
say_status("instance $bot", $bot.class, :red)
|
52
|
+
raise Sc2::Error, "$bot instance nil or not Sc2::Player. Review boot.rb to ensure it creates $bot."
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.source_paths
|
57
|
+
[Pathname(".").expand_path.to_s]
|
58
|
+
end
|
59
|
+
|
60
|
+
def self.source_root
|
61
|
+
Paths.template_root.to_s
|
62
|
+
end
|
63
|
+
|
64
|
+
def port_configuration
|
65
|
+
# Start with basic port setup
|
66
|
+
@port_config = Sc2::Ports.port_config_auto(num_players: 2)
|
67
|
+
say_status("port config", "configured", :green)
|
68
|
+
ENV["SERVER_BASE_PORT"] = @port_config.server_port_set.base_port.to_s
|
69
|
+
ENV["SERVER_GAME_PORT"] = @port_config.server_port_set.game_port.to_s
|
70
|
+
ENV["CLIENT_BASE_PORT"] = @port_config.client_port_sets.first.base_port.to_s
|
71
|
+
ENV["CLIENT_GAME_PORT"] = @port_config.client_port_sets.first.game_port.to_s
|
72
|
+
|
73
|
+
# @port_config_env =
|
74
|
+
# "-e SERVER_BASE_PORT=#{@port_config.server_port_set.base_port} " +
|
75
|
+
# "-e SERVER_GAME_PORT=#{@port_config.server_port_set.game_port} " +
|
76
|
+
# "-e CLIENT_BASE_PORT=#{@port_config.client_port_sets.first.base_port} " +
|
77
|
+
# "-e CLIENT_GAME_PORT=#{@port_config.client_port_sets.first.game_port} "
|
78
|
+
|
79
|
+
# # Create a modified version of ports for docker bot
|
80
|
+
# @client_port_config = @port_config.dup
|
81
|
+
# # Adjust ports for docker
|
82
|
+
# @client_port_config.client_port_sets[0].base_port = 8453
|
83
|
+
# @client_port_config.client_port_sets[0].game_port = 2359
|
84
|
+
#
|
85
|
+
# # Get dynamic assigned ports for host
|
86
|
+
# real_client_base_port = `docker port versus_bot_container 8453/udp`.strip.split(":").last
|
87
|
+
# real_client_game_port = `docker port versus_bot_container 2359`.strip.split(":").last
|
88
|
+
# # Fix our client ports to forward to the docker bot
|
89
|
+
# @port_config.client_port_sets[0].base_port = real_client_base_port.to_i
|
90
|
+
# @port_config.client_port_sets[0].game_port = real_client_game_port.to_i
|
91
|
+
end
|
92
|
+
|
93
|
+
def set_compose_file
|
94
|
+
say_status("docker", "build local play container", :cyan)
|
95
|
+
@compose_file = Sc2::Paths.gem_root
|
96
|
+
.join("docker_build", "docker-compose-versus-bot.yml")
|
97
|
+
.expand_path.to_s
|
98
|
+
@compose_args = "-f #{@compose_file} #{@port_config_env}"
|
99
|
+
cmd = "docker compose #{@compose_args} build"
|
100
|
+
Kernel.system(cmd)
|
101
|
+
end
|
102
|
+
|
103
|
+
def versus_bot_validation
|
104
|
+
if Pathname(filename).exist?
|
105
|
+
say_status("checking zip #{filename}", "found", :green)
|
106
|
+
else
|
107
|
+
say_status("checking zip #{filename}", "not found", :red)
|
108
|
+
raise Sc2::Error, "#{filename} not found. Try an absolute path if relative will not detect."
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def contains_ladderbot_json
|
113
|
+
# Max size of the portion where zip filenames are stored
|
114
|
+
max_end_of_cd_size = (1 << 16) - 1 + 22 + 20
|
115
|
+
searching_for_file = "ladderbots.json" # what we want to find
|
116
|
+
found = File.open(Pathname(filename).realpath, "rb") do |f|
|
117
|
+
# Borrowed code, will search near the end of a zip
|
118
|
+
if f.size > max_end_of_cd_size
|
119
|
+
f.seek(-max_end_of_cd_size, IO::SEEK_END)
|
120
|
+
end
|
121
|
+
content = f.read
|
122
|
+
content.include?(searching_for_file)
|
123
|
+
end
|
124
|
+
|
125
|
+
if found
|
126
|
+
say_status("zip contains ladderbots.json", "found", :green)
|
127
|
+
else
|
128
|
+
say_status("zip contains ladderbots.json", "not found", :red)
|
129
|
+
puts "#{filename} should contain ladderbots.json. Add the file to the zip and try again."
|
130
|
+
raise SystemExit
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
def start_container
|
135
|
+
say_status("docker", "starting bot container", :cyan)
|
136
|
+
cmd = "docker compose #{@compose_args} up versus_bot -d" ## { rand(20) == 0 ? "--force-recreate" : "" }
|
137
|
+
Kernel.system(cmd)
|
138
|
+
end
|
139
|
+
|
140
|
+
def copy_opponent_zip
|
141
|
+
say_status("docker", "copying opponent...", :cyan)
|
142
|
+
cmd = "docker compose #{@compose_args} cp #{Pathname(filename).realpath} versus_bot:/bots/"
|
143
|
+
Kernel.system(cmd)
|
144
|
+
end
|
145
|
+
|
146
|
+
def extract_opponent
|
147
|
+
say_status("docker", "extracting opponent...", :cyan)
|
148
|
+
cmd = "docker compose #{@compose_args} exec --workdir /bots versus_bot unzip -o #{Pathname(filename).basename} -d #{@enemy_botname}"
|
149
|
+
Kernel.system(cmd)
|
150
|
+
end
|
151
|
+
|
152
|
+
def pull_ladderbots_json
|
153
|
+
require "tempfile"
|
154
|
+
|
155
|
+
say_status("docker", "pulling enemy 'ladderbots.json'", :cyan)
|
156
|
+
|
157
|
+
@temp_path = Tempfile.new("enemy_ladderbots.json").path
|
158
|
+
|
159
|
+
cmd = "docker compose #{@compose_args} cp versus_bot:/bots/#{@enemy_botname}/ladderbots.json #{@temp_path}"
|
160
|
+
Kernel.system(cmd)
|
161
|
+
if File.exist?(@temp_path)
|
162
|
+
say_status("docker", "pulled 'ladderbots.json' ok", :green)
|
163
|
+
else
|
164
|
+
say_status("docker", "error pulling 'ladderbots.json'", :red)
|
165
|
+
exit
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
def parse_enemy_config
|
170
|
+
say_status("docker", "configuring enemy", :cyan)
|
171
|
+
json = JSON.parse(File.read(@temp_path))
|
172
|
+
bot_json = json["Bots"][@enemy_botname]
|
173
|
+
@enemy_runtime = bot_json["Type"].downcase # "BinaryCpp",
|
174
|
+
@enemy_race = bot_json["Race"].upcase.to_sym
|
175
|
+
@enemy_filename = bot_json["FileName"]
|
176
|
+
@command = case @enemy_runtime
|
177
|
+
when "cppwin32"
|
178
|
+
"wine #{@enemy_filename}.exe"
|
179
|
+
when "cpplinux", "binarycpp"
|
180
|
+
"/bots/#{@enemy_filename}/#{@enemy_filename}"
|
181
|
+
when "dotnetcore"
|
182
|
+
"dotnet #{@enemy_filename}.dll"
|
183
|
+
when "java"
|
184
|
+
"java -jar #{@enemy_filename}.jar"
|
185
|
+
when "nodejs"
|
186
|
+
"node #{@enemy_filename}.js"
|
187
|
+
when "python"
|
188
|
+
"python run.py"
|
189
|
+
else
|
190
|
+
@enemy_filename
|
191
|
+
end
|
192
|
+
|
193
|
+
# Make executable
|
194
|
+
if %w[cpplinux binarycpp].include?(@enemy_runtime)
|
195
|
+
cmd = "docker compose #{@compose_args} exec --workdir /bots/#{@enemy_botname} versus_bot chmod +x #{@command}"
|
196
|
+
Kernel.system(cmd)
|
197
|
+
end
|
198
|
+
|
199
|
+
# Make data dir
|
200
|
+
say_status("docker", "ensure data directory exists", :cyan)
|
201
|
+
cmd = "docker compose #{@compose_args} exec --workdir /bots/#{@enemy_botname} versus_bot sh -c \"mkdir data && chmod +w data\""
|
202
|
+
Kernel.system(cmd)
|
203
|
+
end
|
204
|
+
|
205
|
+
def run_match
|
206
|
+
Async do
|
207
|
+
Sc2.logger.level = :info
|
208
|
+
|
209
|
+
client = Sc2::ClientManager.obtain(0)
|
210
|
+
enemy_client = Sc2::ClientManager.obtain(1)
|
211
|
+
|
212
|
+
if Gem.win_platform?
|
213
|
+
sleep(10)
|
214
|
+
end
|
215
|
+
|
216
|
+
# Ladder specific overrides
|
217
|
+
$bot.realtime = false
|
218
|
+
$bot.opponent_id = @enemy_botname
|
219
|
+
|
220
|
+
say_status("host", "connecting...", :cyan)
|
221
|
+
$bot.connect(host: client.host, port: client.port)
|
222
|
+
say_status("host", "creating game...", :cyan)
|
223
|
+
$bot.create_game(map: MapFile.new(map.to_s), players: [
|
224
|
+
$bot,
|
225
|
+
Sc2::Player.new(race: @enemy_race, name: @enemy_botname, type: Api::PlayerType::PARTICIPANT)
|
226
|
+
], realtime: false)
|
227
|
+
|
228
|
+
begin
|
229
|
+
Async do
|
230
|
+
say_status("host", "joining game...", :cyan)
|
231
|
+
$bot.join_game(
|
232
|
+
server_host: client.host,
|
233
|
+
port_config: @port_config
|
234
|
+
)
|
235
|
+
$bot.add_listener($bot, klass: Sc2::Connection::StatusListener)
|
236
|
+
$bot.play
|
237
|
+
|
238
|
+
begin
|
239
|
+
safe_player_name = $bot.name.gsub(/\s*[^A-Za-z0-9.-]\s*/, "_").downcase
|
240
|
+
response = $bot.api.save_replay
|
241
|
+
path = Pathname(Paths.bot_data_replay_dir).join("autosave-#{safe_player_name}-vs-#{@enemy_botname}.SC2Replay")
|
242
|
+
f = File.new(path, "wb:ASCII-8BIT")
|
243
|
+
f.write_nonblock(response.data)
|
244
|
+
f.close
|
245
|
+
rescue
|
246
|
+
# Replay save failed
|
247
|
+
end
|
248
|
+
$bot.disconnect
|
249
|
+
end
|
250
|
+
|
251
|
+
Async do
|
252
|
+
say_status("opponent", "joining game...", :cyan)
|
253
|
+
|
254
|
+
# 192... ip
|
255
|
+
# host_ladder_ip = `docker compose #{@compose_args} exec versus_bot sh -c "getent ahostsv4 host.docker.internal | grep STREAM"`.split(" ").first.strip
|
256
|
+
# Local ip
|
257
|
+
# host_ladder_ip = `docker inspect -f '{{range.NetworkSettings.Networks}}{{.Gateway}}{{end}}' versus_bot_container`.strip
|
258
|
+
enemy_command = [@command,
|
259
|
+
# "--LadderServer #{client.host}",
|
260
|
+
# "--RealTime 0"
|
261
|
+
"--LadderServer host.docker.internal",
|
262
|
+
"--GamePort #{enemy_client.port}",
|
263
|
+
"--StartPort #{@port_config.start_port}",
|
264
|
+
"--OpponentId 00000000-0000-0000-0000-000000000000"]
|
265
|
+
.join(" ")
|
266
|
+
|
267
|
+
# cmd = "docker compose #{@compose_args} exec --workdir /bots/#{@enemy_botname} versus_bot bash -c '#{enemy_command}'"
|
268
|
+
cmd = "docker compose #{@compose_args} exec --workdir /bots/#{@enemy_botname} versus_bot bash -c '#{enemy_command}'"
|
269
|
+
|
270
|
+
puts cmd
|
271
|
+
process_options = {}
|
272
|
+
if Gem.win_platform?
|
273
|
+
process_options[:new_pgroup] = true
|
274
|
+
else
|
275
|
+
process_options[:pgroup] = true
|
276
|
+
end
|
277
|
+
# ::Async::Process.spawn(cmd, **process_options)
|
278
|
+
unless Kernel.system(cmd)
|
279
|
+
say_status("opponent", "exited with bad status", :red)
|
280
|
+
raise SystemExit
|
281
|
+
end
|
282
|
+
end
|
283
|
+
rescue => e
|
284
|
+
say_status("error", e.message, :red)
|
285
|
+
raise SystemExit
|
286
|
+
# Replay save failed
|
287
|
+
end
|
288
|
+
end.wait
|
289
|
+
ensure
|
290
|
+
ClientManager.stop_all
|
291
|
+
end
|
292
|
+
|
293
|
+
def stop_container
|
294
|
+
cmd = "docker stop versus_bot_container"
|
295
|
+
Kernel.system(cmd)
|
296
|
+
end
|
297
|
+
|
298
|
+
def bye
|
299
|
+
say "Game over."
|
300
|
+
end
|
301
|
+
end
|
302
|
+
|
303
|
+
# standard:enable Style/GlobalVars
|
304
|
+
end
|
305
|
+
end
|
@@ -10,7 +10,7 @@ module Sc2
|
|
10
10
|
|
11
11
|
class << self
|
12
12
|
extend Forwardable
|
13
|
-
def_delegators :instance, :obtain, :get, :start, :stop, :stop_all
|
13
|
+
def_delegators :instance, :obtain, :get, :start, :stop, :stop_all, :host
|
14
14
|
end
|
15
15
|
|
16
16
|
# Gets client for player X or starts an instance
|
@@ -63,6 +63,8 @@ module Sc2
|
|
63
63
|
end
|
64
64
|
end
|
65
65
|
|
66
|
+
attr_accessor :host
|
67
|
+
|
66
68
|
private
|
67
69
|
|
68
70
|
attr_accessor :clients, :ports
|
@@ -27,15 +27,10 @@ module Sc2
|
|
27
27
|
# Access useful game information. Used in parsed pathing grid, terrain height, placement grid.
|
28
28
|
# Holds Api::ResponseGameInfo::#start_locations.
|
29
29
|
# @return [Api::ResponseGameInfo]
|
30
|
-
|
31
|
-
if @game_info_task&.running?
|
32
|
-
@game_info_task&.wait
|
33
|
-
@game_info_task = nil
|
34
|
-
end
|
35
|
-
@game_info
|
36
|
-
end
|
30
|
+
attr_reader :game_info
|
37
31
|
|
38
32
|
def game_info=(new_info)
|
33
|
+
@game_info_loop = game_loop || 0
|
39
34
|
@game_info = new_info
|
40
35
|
end
|
41
36
|
|
data/lib/sc2ai/player/geo.rb
CHANGED
@@ -81,6 +81,16 @@ module Sc2
|
|
81
81
|
0..(map_height - 1)
|
82
82
|
end
|
83
83
|
|
84
|
+
# Ensures a Sc2::Position's x/y stays in map tile range
|
85
|
+
# Prevents out of bound exceptions when working with minimap
|
86
|
+
# @param position [Sc2::Position, Api::Point2D]
|
87
|
+
# @return [Sc2::Position, Api::Point2D]
|
88
|
+
def clamp_to_grid(position)
|
89
|
+
position.y = position.y.clamp(map_tile_range_y)
|
90
|
+
position.x = position.x.clamp(map_tile_range_x)
|
91
|
+
position
|
92
|
+
end
|
93
|
+
|
84
94
|
# Map Parsing functions -----
|
85
95
|
|
86
96
|
# Returns whether a x/y (integer) is placeable as per minimap image data.
|
@@ -300,6 +310,13 @@ module Sc2
|
|
300
310
|
# Fix endian for Numo bit parser
|
301
311
|
data = image_data.data.unpack("b*").pack("B*")
|
302
312
|
@parsed_pathing_grid = Numo::Bit.from_binary(data, [image_data.size.y, image_data.size.x])
|
313
|
+
|
314
|
+
# Remove erroneous mineral blockers as pathable
|
315
|
+
bot.neutral.select_type(Api::UnitTypeId::MINERALFIELD450).each do |mineral|
|
316
|
+
@parsed_pathing_grid[mineral.pos.y.to_i, mineral.pos.x] = 0
|
317
|
+
@parsed_pathing_grid[mineral.pos.y.to_i, mineral.pos.x - 1] = 0
|
318
|
+
end
|
319
|
+
|
303
320
|
end
|
304
321
|
@parsed_pathing_grid
|
305
322
|
end
|
@@ -455,7 +472,7 @@ module Sc2
|
|
455
472
|
while inner_y < length
|
456
473
|
inner_x = 0
|
457
474
|
while inner_x < length
|
458
|
-
if
|
475
|
+
if input_grid[y + inner_y, x + inner_x].zero?
|
459
476
|
output_grid[y / length, x / length] = 0
|
460
477
|
inner_y = length
|
461
478
|
break
|
@@ -507,7 +524,7 @@ module Sc2
|
|
507
524
|
end
|
508
525
|
|
509
526
|
# Split resources by Z axis
|
510
|
-
resources = bot.neutral.minerals + bot.neutral.geysers
|
527
|
+
resources = bot.neutral.minerals.reject_type(Api::UnitTypeId::MINERALFIELD450) + bot.neutral.geysers
|
511
528
|
resource_group_z = resources.group_by do |resource|
|
512
529
|
resource.pos.z.round # 32 units of Y, most maps will have use 3. round to nearest.
|
513
530
|
end
|
data/lib/sc2ai/player/units.rb
CHANGED
@@ -304,6 +304,25 @@ module Sc2
|
|
304
304
|
true
|
305
305
|
end
|
306
306
|
|
307
|
+
# Checks whether you have met the tech requirements for building a specific unit type
|
308
|
+
# @param [Integer] unit_type_id
|
309
|
+
# @return [Boolean]
|
310
|
+
def tech_requirement_met?(unit_type_id)
|
311
|
+
created_from_unit_type = Api::TechTree.unit_created_from(unit_type_id: unit_type_id)
|
312
|
+
required_building = Api::TechTree.unit_type_creation_abilities(
|
313
|
+
source: created_from_unit_type.first,
|
314
|
+
target: unit_type_id
|
315
|
+
)[:required_building]
|
316
|
+
|
317
|
+
# Ensure we have required building, if there's such a requirement
|
318
|
+
if required_building
|
319
|
+
return false unless all_units.owned.select_type(required_building).completed.size > 0
|
320
|
+
end
|
321
|
+
|
322
|
+
# Ensure we have a completed source which it's created from
|
323
|
+
all_units.owned.select_type(created_from_unit_type).completed.size > 0
|
324
|
+
end
|
325
|
+
|
307
326
|
# Micro/Unit-Specific ------
|
308
327
|
|
309
328
|
# Returns whether Query Available Ability is true for unit and tag
|
data/lib/sc2ai/player.rb
CHANGED
@@ -529,8 +529,6 @@ module Sc2
|
|
529
529
|
!IDENTIFIED_RACES.include?(race)
|
530
530
|
end
|
531
531
|
|
532
|
-
private
|
533
|
-
|
534
532
|
# @private
|
535
533
|
CALLBACK_METHODS = %i[on_finish
|
536
534
|
on_random_race_detected
|
@@ -545,6 +543,7 @@ module Sc2
|
|
545
543
|
on_structure_started
|
546
544
|
on_structure_completed
|
547
545
|
on_unit_damaged]
|
546
|
+
private_constant :CALLBACK_METHODS
|
548
547
|
|
549
548
|
# @private
|
550
549
|
# @return [Array<Symbol>] callbacks implemented on player class
|
@@ -599,6 +598,7 @@ module Sc2
|
|
599
598
|
# Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
600
599
|
step_to_loop = @realtime ? game_loop + @step_count : nil
|
601
600
|
response_observation = @api.observation(game_loop: step_to_loop)
|
601
|
+
return if response_observation.nil?
|
602
602
|
|
603
603
|
# Check if match has a result and callback
|
604
604
|
on_player_result(response_observation.player_result) unless response_observation.player_result.empty?
|
@@ -671,10 +671,7 @@ module Sc2
|
|
671
671
|
# Refreshes bot#game_info ignoring all caches
|
672
672
|
# @return [void]
|
673
673
|
public def refresh_game_info
|
674
|
-
|
675
|
-
self.game_info = @api.game_info
|
676
|
-
@game_info_task = nil
|
677
|
-
end
|
674
|
+
self.game_info = @api.game_info
|
678
675
|
end
|
679
676
|
|
680
677
|
# Enemy -----------------------
|
@@ -58,75 +58,74 @@ module Api
|
|
58
58
|
# @return [Boolean] whether unit has attribute
|
59
59
|
# @example
|
60
60
|
# unit.has_attribute?(Api::Attribute::MECHANICAL)
|
61
|
-
# unit.has_attribute?(:MECHANICAL)
|
62
61
|
def has_attribute?(attribute)
|
63
62
|
attributes.include? attribute
|
64
63
|
end
|
65
64
|
|
66
65
|
# Checks if unit is light
|
67
|
-
# @return [Boolean] whether unit has attribute :
|
66
|
+
# @return [Boolean] whether unit has attribute :LIGHT
|
68
67
|
def is_light?
|
69
|
-
has_attribute?(
|
68
|
+
has_attribute?(Api::Attribute::LIGHT)
|
70
69
|
end
|
71
70
|
|
72
71
|
# Checks if unit is armored
|
73
|
-
# @return [Boolean] whether unit has attribute :
|
72
|
+
# @return [Boolean] whether unit has attribute :ARMORED
|
74
73
|
def is_armored?
|
75
|
-
has_attribute?(
|
74
|
+
has_attribute?(Api::Attribute::ARMORED)
|
76
75
|
end
|
77
76
|
|
78
77
|
# Checks if unit is biological
|
79
|
-
# @return [Boolean] whether unit has attribute :
|
78
|
+
# @return [Boolean] whether unit has attribute :BIOLOGICAL
|
80
79
|
def is_biological?
|
81
|
-
has_attribute?(
|
80
|
+
has_attribute?(Api::Attribute::BIOLOGICAL)
|
82
81
|
end
|
83
82
|
|
84
83
|
# Checks if unit is mechanical
|
85
|
-
# @return [Boolean] whether unit has attribute :
|
84
|
+
# @return [Boolean] whether unit has attribute :MECHANICAL
|
86
85
|
def is_mechanical?
|
87
|
-
has_attribute?(
|
86
|
+
has_attribute?(Api::Attribute::MECHANICAL)
|
88
87
|
end
|
89
88
|
|
90
89
|
# Checks if unit is robotic
|
91
|
-
# @return [Boolean] whether unit has attribute :
|
90
|
+
# @return [Boolean] whether unit has attribute :ROBOTIC
|
92
91
|
def is_robotic?
|
93
|
-
has_attribute?(
|
92
|
+
has_attribute?(Api::Attribute::ROBOTIC)
|
94
93
|
end
|
95
94
|
|
96
95
|
# Checks if unit is psionic
|
97
|
-
# @return [Boolean] whether unit has attribute :
|
96
|
+
# @return [Boolean] whether unit has attribute :PSIONIC
|
98
97
|
def is_psionic?
|
99
|
-
has_attribute?(
|
98
|
+
has_attribute?(Api::Attribute::PSIONIC)
|
100
99
|
end
|
101
100
|
|
102
101
|
# Checks if unit is massive
|
103
|
-
# @return [Boolean] whether unit has attribute :
|
102
|
+
# @return [Boolean] whether unit has attribute :MASSIVE
|
104
103
|
def is_massive?
|
105
|
-
has_attribute?(
|
104
|
+
has_attribute?(Api::Attribute::MASSIVE)
|
106
105
|
end
|
107
106
|
|
108
107
|
# Checks if unit is structure
|
109
|
-
# @return [Boolean] whether unit has attribute :
|
108
|
+
# @return [Boolean] whether unit has attribute :STRUCTURE
|
110
109
|
def is_structure?
|
111
|
-
has_attribute?(
|
110
|
+
has_attribute?(Api::Attribute::STRUCTURE)
|
112
111
|
end
|
113
112
|
|
114
113
|
# Checks if unit is hovering
|
115
|
-
# @return [Boolean] whether unit has attribute :
|
114
|
+
# @return [Boolean] whether unit has attribute :HOVER
|
116
115
|
def is_hover?
|
117
|
-
has_attribute?(
|
116
|
+
has_attribute?(Api::Attribute::HOVER)
|
118
117
|
end
|
119
118
|
|
120
119
|
# Checks if unit is heroic
|
121
|
-
# @return [Boolean] whether unit has attribute :
|
120
|
+
# @return [Boolean] whether unit has attribute :HEROIC
|
122
121
|
def is_heroic?
|
123
|
-
has_attribute?(
|
122
|
+
has_attribute?(Api::Attribute::HEROIC)
|
124
123
|
end
|
125
124
|
|
126
125
|
# Checks if unit is summoned
|
127
|
-
# @return [Boolean] whether unit has attribute :
|
126
|
+
# @return [Boolean] whether unit has attribute :SUMMONED
|
128
127
|
def is_summoned?
|
129
|
-
has_attribute?(
|
128
|
+
has_attribute?(Api::Attribute::SUMMONED)
|
130
129
|
end
|
131
130
|
|
132
131
|
# @!group Virtual properties
|
@@ -86,6 +86,17 @@ module Sc2
|
|
86
86
|
Api::UnitTypeId::SPORECRAWLER
|
87
87
|
].freeze
|
88
88
|
|
89
|
+
# Protoss: An array of unit types warped from Warp Gate.
|
90
|
+
# @return [Array<Integer>]
|
91
|
+
TYPE_WARPGATE_UNIT = [
|
92
|
+
Api::UnitTypeId::ZEALOT,
|
93
|
+
Api::UnitTypeId::STALKER,
|
94
|
+
Api::UnitTypeId::HIGHTEMPLAR,
|
95
|
+
Api::UnitTypeId::DARKTEMPLAR,
|
96
|
+
Api::UnitTypeId::SENTRY,
|
97
|
+
Api::UnitTypeId::ADEPT
|
98
|
+
].freeze
|
99
|
+
|
89
100
|
# Returns a new UnitGroup containing all units matching unit type id(s)
|
90
101
|
# Multiple values work as an "OR" filter
|
91
102
|
# @example
|
@@ -172,7 +183,7 @@ module Sc2
|
|
172
183
|
def select_attribute(attributes)
|
173
184
|
cached("#{__method__}:#{attributes.hash}") do
|
174
185
|
attributes = [attributes] unless attributes.is_a? Array
|
175
|
-
attributes = attributes.map { |a| a.is_a?(Symbol) ?
|
186
|
+
attributes = attributes.map { |a| a.is_a?(Symbol) ? Api::Attribute.lookup(a) : a }
|
176
187
|
select do |unit|
|
177
188
|
attributes & unit.attributes == attributes
|
178
189
|
end
|
@@ -192,7 +203,7 @@ module Sc2
|
|
192
203
|
def reject_attribute(attributes)
|
193
204
|
cached("#{__method__}:#{attributes.hash}") do
|
194
205
|
attributes = [attributes] unless attributes.is_a? Array
|
195
|
-
attributes = attributes.map { |a| a.is_a?(Symbol) ?
|
206
|
+
attributes = attributes.map { |a| a.is_a?(Symbol) ? Api::Attribute.lookup(a) : a }
|
196
207
|
reject do |unit|
|
197
208
|
unit.attributes & attributes == attributes
|
198
209
|
end
|
data/lib/sc2ai/version.rb
CHANGED
@@ -8,13 +8,7 @@ ruby_dir=$base_dir/.ruby
|
|
8
8
|
export PATH=$ruby_dir/bin:$PATH
|
9
9
|
export LD_LIBRARY_PATH=$(ruby -e 'puts RbConfig::CONFIG["libdir"]')
|
10
10
|
|
11
|
-
|
12
|
-
export RUBY_GC_HEAP_INIT_SIZE_40_SLOTS=340000
|
13
|
-
export RUBY_GC_HEAP_INIT_SIZE_80_SLOTS=50000
|
14
|
-
export RUBY_GC_HEAP_INIT_SIZE_160_SLOTS=18000
|
15
|
-
export RUBY_GC_HEAP_INIT_SIZE_320_SLOTS=2000
|
16
|
-
export RUBY_GC_HEAP_INIT_SIZE_640_SLOTS=1000
|
17
|
-
|
11
|
+
export RUBY_YJIT_ENABLE=1
|
18
12
|
export AIARENA=true
|
19
13
|
sc2ai ladderconfig
|
20
14
|
exec sc2ai laddermatch "$@" 1>&2
|
data/sig/sc2ai.rbs
CHANGED
@@ -1012,6 +1012,11 @@ module Sc2
|
|
1012
1012
|
# Checks whether you have the resources to
|
1013
1013
|
def can_afford_upgrade?: (untyped upgrade_id) -> bool
|
1014
1014
|
|
1015
|
+
# Checks whether you have met the tech requirements for building a specific unit type
|
1016
|
+
#
|
1017
|
+
# _@param_ `unit_type_id`
|
1018
|
+
def tech_requirement_met?: (Integer unit_type_id) -> bool
|
1019
|
+
|
1015
1020
|
# Returns whether Query Available Ability is true for unit and tag
|
1016
1021
|
# Queries API if necessary. Uses batching in the background.
|
1017
1022
|
#
|
@@ -1142,6 +1147,11 @@ module Sc2
|
|
1142
1147
|
# Checks whether you have the resources to
|
1143
1148
|
def can_afford_upgrade?: (untyped upgrade_id) -> bool
|
1144
1149
|
|
1150
|
+
# Checks whether you have met the tech requirements for building a specific unit type
|
1151
|
+
#
|
1152
|
+
# _@param_ `unit_type_id`
|
1153
|
+
def tech_requirement_met?: (Integer unit_type_id) -> bool
|
1154
|
+
|
1145
1155
|
# Returns whether Query Available Ability is true for unit and tag
|
1146
1156
|
# Queries API if necessary. Uses batching in the background.
|
1147
1157
|
#
|
@@ -1268,6 +1278,12 @@ module Sc2
|
|
1268
1278
|
# Returns zero to map_height-1 as range
|
1269
1279
|
def map_tile_range_y: () -> ::Range[untyped]
|
1270
1280
|
|
1281
|
+
# Ensures a Sc2::Position's x/y stays in map tile range
|
1282
|
+
# Prevents out of bound exceptions when working with minimap
|
1283
|
+
#
|
1284
|
+
# _@param_ `position`
|
1285
|
+
def clamp_to_grid: ((Sc2::Position | Api::Point2D) position) -> (Sc2::Position | Api::Point2D)
|
1286
|
+
|
1271
1287
|
# Returns whether a x/y (integer) is placeable as per minimap image data.
|
1272
1288
|
# It does not say whether a position is occupied by another building.
|
1273
1289
|
# One pixel covers one whole block. Corrects floats on your behalf
|
@@ -1860,6 +1876,11 @@ module Sc2
|
|
1860
1876
|
# Checks whether you have the resources to
|
1861
1877
|
def can_afford_upgrade?: (untyped upgrade_id) -> bool
|
1862
1878
|
|
1879
|
+
# Checks whether you have met the tech requirements for building a specific unit type
|
1880
|
+
#
|
1881
|
+
# _@param_ `unit_type_id`
|
1882
|
+
def tech_requirement_met?: (Integer unit_type_id) -> bool
|
1883
|
+
|
1863
1884
|
# Returns whether Query Available Ability is true for unit and tag
|
1864
1885
|
# Queries API if necessary. Uses batching in the background.
|
1865
1886
|
#
|
@@ -2321,6 +2342,11 @@ module Sc2
|
|
2321
2342
|
# Checks whether you have the resources to
|
2322
2343
|
def can_afford_upgrade?: (untyped upgrade_id) -> bool
|
2323
2344
|
|
2345
|
+
# Checks whether you have met the tech requirements for building a specific unit type
|
2346
|
+
#
|
2347
|
+
# _@param_ `unit_type_id`
|
2348
|
+
def tech_requirement_met?: (Integer unit_type_id) -> bool
|
2349
|
+
|
2324
2350
|
# Returns whether Query Available Ability is true for unit and tag
|
2325
2351
|
# Queries API if necessary. Uses batching in the background.
|
2326
2352
|
#
|
@@ -3097,6 +3123,7 @@ module Sc2
|
|
3097
3123
|
TYPE_REACTOR: ::Array[Integer]
|
3098
3124
|
TYPE_BASES: ::Array[Integer]
|
3099
3125
|
TYPE_DETECTORS: ::Array[Integer]
|
3126
|
+
TYPE_WARPGATE_UNIT: ::Array[Integer]
|
3100
3127
|
|
3101
3128
|
# _@param_ `units` — default to be added.
|
3102
3129
|
#
|
@@ -3727,6 +3754,7 @@ module Sc2
|
|
3727
3754
|
TYPE_REACTOR: ::Array[Integer]
|
3728
3755
|
TYPE_BASES: ::Array[Integer]
|
3729
3756
|
TYPE_DETECTORS: ::Array[Integer]
|
3757
|
+
TYPE_WARPGATE_UNIT: ::Array[Integer]
|
3730
3758
|
|
3731
3759
|
# _@param_ `unit_group`
|
3732
3760
|
def initialize: (Sc2::UnitGroup unit_group) -> void
|
@@ -4068,6 +4096,9 @@ module Sc2
|
|
4068
4096
|
|
4069
4097
|
def initialize: () -> void
|
4070
4098
|
|
4099
|
+
# Returns the value of attribute host.
|
4100
|
+
attr_accessor host: untyped
|
4101
|
+
|
4071
4102
|
# Returns the value of attribute clients.
|
4072
4103
|
attr_accessor clients: untyped
|
4073
4104
|
|
@@ -8475,63 +8506,62 @@ module Api
|
|
8475
8506
|
#
|
8476
8507
|
# ```ruby
|
8477
8508
|
# unit.has_attribute?(Api::Attribute::MECHANICAL)
|
8478
|
-
# unit.has_attribute?(:MECHANICAL)
|
8479
8509
|
# ```
|
8480
8510
|
def has_attribute?: (untyped attribute) -> bool
|
8481
8511
|
|
8482
8512
|
# Checks if unit is light
|
8483
8513
|
#
|
8484
|
-
# _@return_ — whether unit has attribute :
|
8514
|
+
# _@return_ — whether unit has attribute :LIGHT
|
8485
8515
|
def is_light?: () -> bool
|
8486
8516
|
|
8487
8517
|
# Checks if unit is armored
|
8488
8518
|
#
|
8489
|
-
# _@return_ — whether unit has attribute :
|
8519
|
+
# _@return_ — whether unit has attribute :ARMORED
|
8490
8520
|
def is_armored?: () -> bool
|
8491
8521
|
|
8492
8522
|
# Checks if unit is biological
|
8493
8523
|
#
|
8494
|
-
# _@return_ — whether unit has attribute :
|
8524
|
+
# _@return_ — whether unit has attribute :BIOLOGICAL
|
8495
8525
|
def is_biological?: () -> bool
|
8496
8526
|
|
8497
8527
|
# Checks if unit is mechanical
|
8498
8528
|
#
|
8499
|
-
# _@return_ — whether unit has attribute :
|
8529
|
+
# _@return_ — whether unit has attribute :MECHANICAL
|
8500
8530
|
def is_mechanical?: () -> bool
|
8501
8531
|
|
8502
8532
|
# Checks if unit is robotic
|
8503
8533
|
#
|
8504
|
-
# _@return_ — whether unit has attribute :
|
8534
|
+
# _@return_ — whether unit has attribute :ROBOTIC
|
8505
8535
|
def is_robotic?: () -> bool
|
8506
8536
|
|
8507
8537
|
# Checks if unit is psionic
|
8508
8538
|
#
|
8509
|
-
# _@return_ — whether unit has attribute :
|
8539
|
+
# _@return_ — whether unit has attribute :PSIONIC
|
8510
8540
|
def is_psionic?: () -> bool
|
8511
8541
|
|
8512
8542
|
# Checks if unit is massive
|
8513
8543
|
#
|
8514
|
-
# _@return_ — whether unit has attribute :
|
8544
|
+
# _@return_ — whether unit has attribute :MASSIVE
|
8515
8545
|
def is_massive?: () -> bool
|
8516
8546
|
|
8517
8547
|
# Checks if unit is structure
|
8518
8548
|
#
|
8519
|
-
# _@return_ — whether unit has attribute :
|
8549
|
+
# _@return_ — whether unit has attribute :STRUCTURE
|
8520
8550
|
def is_structure?: () -> bool
|
8521
8551
|
|
8522
8552
|
# Checks if unit is hovering
|
8523
8553
|
#
|
8524
|
-
# _@return_ — whether unit has attribute :
|
8554
|
+
# _@return_ — whether unit has attribute :HOVER
|
8525
8555
|
def is_hover?: () -> bool
|
8526
8556
|
|
8527
8557
|
# Checks if unit is heroic
|
8528
8558
|
#
|
8529
|
-
# _@return_ — whether unit has attribute :
|
8559
|
+
# _@return_ — whether unit has attribute :HEROIC
|
8530
8560
|
def is_heroic?: () -> bool
|
8531
8561
|
|
8532
8562
|
# Checks if unit is summoned
|
8533
8563
|
#
|
8534
|
-
# _@return_ — whether unit has attribute :
|
8564
|
+
# _@return_ — whether unit has attribute :SUMMONED
|
8535
8565
|
def is_summoned?: () -> bool
|
8536
8566
|
|
8537
8567
|
# Returns whether the unit is cloaked. Revealed cloak units also return true.
|
@@ -20424,63 +20454,62 @@ module Api
|
|
20424
20454
|
#
|
20425
20455
|
# ```ruby
|
20426
20456
|
# unit.has_attribute?(Api::Attribute::MECHANICAL)
|
20427
|
-
# unit.has_attribute?(:MECHANICAL)
|
20428
20457
|
# ```
|
20429
20458
|
def has_attribute?: (untyped attribute) -> bool
|
20430
20459
|
|
20431
20460
|
# Checks if unit is light
|
20432
20461
|
#
|
20433
|
-
# _@return_ — whether unit has attribute :
|
20462
|
+
# _@return_ — whether unit has attribute :LIGHT
|
20434
20463
|
def is_light?: () -> bool
|
20435
20464
|
|
20436
20465
|
# Checks if unit is armored
|
20437
20466
|
#
|
20438
|
-
# _@return_ — whether unit has attribute :
|
20467
|
+
# _@return_ — whether unit has attribute :ARMORED
|
20439
20468
|
def is_armored?: () -> bool
|
20440
20469
|
|
20441
20470
|
# Checks if unit is biological
|
20442
20471
|
#
|
20443
|
-
# _@return_ — whether unit has attribute :
|
20472
|
+
# _@return_ — whether unit has attribute :BIOLOGICAL
|
20444
20473
|
def is_biological?: () -> bool
|
20445
20474
|
|
20446
20475
|
# Checks if unit is mechanical
|
20447
20476
|
#
|
20448
|
-
# _@return_ — whether unit has attribute :
|
20477
|
+
# _@return_ — whether unit has attribute :MECHANICAL
|
20449
20478
|
def is_mechanical?: () -> bool
|
20450
20479
|
|
20451
20480
|
# Checks if unit is robotic
|
20452
20481
|
#
|
20453
|
-
# _@return_ — whether unit has attribute :
|
20482
|
+
# _@return_ — whether unit has attribute :ROBOTIC
|
20454
20483
|
def is_robotic?: () -> bool
|
20455
20484
|
|
20456
20485
|
# Checks if unit is psionic
|
20457
20486
|
#
|
20458
|
-
# _@return_ — whether unit has attribute :
|
20487
|
+
# _@return_ — whether unit has attribute :PSIONIC
|
20459
20488
|
def is_psionic?: () -> bool
|
20460
20489
|
|
20461
20490
|
# Checks if unit is massive
|
20462
20491
|
#
|
20463
|
-
# _@return_ — whether unit has attribute :
|
20492
|
+
# _@return_ — whether unit has attribute :MASSIVE
|
20464
20493
|
def is_massive?: () -> bool
|
20465
20494
|
|
20466
20495
|
# Checks if unit is structure
|
20467
20496
|
#
|
20468
|
-
# _@return_ — whether unit has attribute :
|
20497
|
+
# _@return_ — whether unit has attribute :STRUCTURE
|
20469
20498
|
def is_structure?: () -> bool
|
20470
20499
|
|
20471
20500
|
# Checks if unit is hovering
|
20472
20501
|
#
|
20473
|
-
# _@return_ — whether unit has attribute :
|
20502
|
+
# _@return_ — whether unit has attribute :HOVER
|
20474
20503
|
def is_hover?: () -> bool
|
20475
20504
|
|
20476
20505
|
# Checks if unit is heroic
|
20477
20506
|
#
|
20478
|
-
# _@return_ — whether unit has attribute :
|
20507
|
+
# _@return_ — whether unit has attribute :HEROIC
|
20479
20508
|
def is_heroic?: () -> bool
|
20480
20509
|
|
20481
20510
|
# Checks if unit is summoned
|
20482
20511
|
#
|
20483
|
-
# _@return_ — whether unit has attribute :
|
20512
|
+
# _@return_ — whether unit has attribute :SUMMONED
|
20484
20513
|
def is_summoned?: () -> bool
|
20485
20514
|
|
20486
20515
|
# Returns whether the unit is cloaked. Revealed cloak units also return true.
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sc2ai
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.6.
|
4
|
+
version: 0.6.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dyson Returns
|
8
8
|
bindir: exe
|
9
9
|
cert_chain: []
|
10
|
-
date:
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
11
11
|
dependencies:
|
12
12
|
- !ruby/object:Gem::Dependency
|
13
13
|
name: async
|
@@ -65,6 +65,20 @@ dependencies:
|
|
65
65
|
- - "~>"
|
66
66
|
- !ruby/object:Gem::Version
|
67
67
|
version: '1.3'
|
68
|
+
- !ruby/object:Gem::Dependency
|
69
|
+
name: logger
|
70
|
+
requirement: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - ">="
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: '0'
|
75
|
+
type: :runtime
|
76
|
+
prerelease: false
|
77
|
+
version_requirements: !ruby/object:Gem::Requirement
|
78
|
+
requirements:
|
79
|
+
- - ">="
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: '0'
|
68
82
|
- !ruby/object:Gem::Dependency
|
69
83
|
name: numo-narray
|
70
84
|
requirement: !ruby/object:Gem::Requirement
|
@@ -108,7 +122,7 @@ dependencies:
|
|
108
122
|
- !ruby/object:Gem::Version
|
109
123
|
version: '1.0'
|
110
124
|
- !ruby/object:Gem::Dependency
|
111
|
-
name:
|
125
|
+
name: fiddle
|
112
126
|
requirement: !ruby/object:Gem::Requirement
|
113
127
|
requirements:
|
114
128
|
- - ">="
|
@@ -122,7 +136,7 @@ dependencies:
|
|
122
136
|
- !ruby/object:Gem::Version
|
123
137
|
version: '0'
|
124
138
|
- !ruby/object:Gem::Dependency
|
125
|
-
name:
|
139
|
+
name: cgi
|
126
140
|
requirement: !ruby/object:Gem::Requirement
|
127
141
|
requirements:
|
128
142
|
- - ">="
|
@@ -136,7 +150,7 @@ dependencies:
|
|
136
150
|
- !ruby/object:Gem::Version
|
137
151
|
version: '0'
|
138
152
|
- !ruby/object:Gem::Dependency
|
139
|
-
name:
|
153
|
+
name: irb
|
140
154
|
requirement: !ruby/object:Gem::Requirement
|
141
155
|
requirements:
|
142
156
|
- - ">="
|
@@ -354,9 +368,11 @@ files:
|
|
354
368
|
- data/setup/setup.SC2Replay
|
355
369
|
- data/stableid.json
|
356
370
|
- data/versions.json
|
371
|
+
- docker_build/Dockerfile.aiarenabot
|
357
372
|
- docker_build/Dockerfile.ruby
|
358
373
|
- docker_build/docker-compose-base-image.yml
|
359
374
|
- docker_build/docker-compose-ladderzip.yml
|
375
|
+
- docker_build/docker-compose-versus-bot.yml
|
360
376
|
- exe/sc2ai
|
361
377
|
- lib/sc2ai.rb
|
362
378
|
- lib/sc2ai/api/ability_id.rb
|
@@ -370,6 +386,7 @@ files:
|
|
370
386
|
- lib/sc2ai/cli/cli.rb
|
371
387
|
- lib/sc2ai/cli/ladderzip.rb
|
372
388
|
- lib/sc2ai/cli/new.rb
|
389
|
+
- lib/sc2ai/cli/versus_bot.rb
|
373
390
|
- lib/sc2ai/configuration.rb
|
374
391
|
- lib/sc2ai/connection.rb
|
375
392
|
- lib/sc2ai/connection/connection_listener.rb
|
@@ -419,6 +436,7 @@ files:
|
|
419
436
|
- lib/sc2ai/unit_group/geo_ext.rb
|
420
437
|
- lib/sc2ai/version.rb
|
421
438
|
- lib/templates/ladderzip/bin/ladder.tt
|
439
|
+
- lib/templates/ladderzip/ladderbots.json.tt
|
422
440
|
- lib/templates/new/.ladderignore
|
423
441
|
- lib/templates/new/Gemfile.tt
|
424
442
|
- lib/templates/new/api/common.proto
|
@@ -454,14 +472,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
454
472
|
requirements:
|
455
473
|
- - ">="
|
456
474
|
- !ruby/object:Gem::Version
|
457
|
-
version: 3.3.
|
475
|
+
version: 3.3.8
|
458
476
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
459
477
|
requirements:
|
460
478
|
- - ">="
|
461
479
|
- !ruby/object:Gem::Version
|
462
480
|
version: '0'
|
463
481
|
requirements: []
|
464
|
-
rubygems_version: 3.6.
|
482
|
+
rubygems_version: 3.6.8
|
465
483
|
specification_version: 4
|
466
484
|
summary: STARCRAFT® II AI API
|
467
485
|
test_files: []
|