sc2ai 0.6.1 → 0.6.2
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 +16 -16
- 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 +2 -27
- data/lib/sc2ai/cli/ladderzip.rb +6 -1
- 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.rb +3 -6
- 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 +9 -0
- metadata +6 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a52a7a32bf219355f194817695d5505e47a897da5f3023ff856f4c88a66cc4d0
|
4
|
+
data.tar.gz: 94a565f3dc3c3f43896559664961242a9c06b30429bb41345183eb3fd680f29c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8275c1840c4c7a9548a08ccf90215d41d8eabea15f7915109221f0c8c1deba96847103fc7f552927304c23b702a53aab5f091d8af7a52da33d7d0c419cfe2369
|
7
|
+
data.tar.gz: c547597a3bfb0babfbc2765c0d9fb6af7fab9137077faaf6f419ff51cad0cdfc30dd9816bab8383e9559b1cabc88aa5ec908aaf66348e3ac5570192708c23959
|
@@ -9,7 +9,7 @@ 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"
|
@@ -95,23 +97,6 @@ module Sc2
|
|
95
97
|
Sc2.logger.info " interface_options: #{$bot.interface_options}"
|
96
98
|
end
|
97
99
|
|
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
100
|
desc "laddermatch", "Joins a ladder match as per aiarena spec"
|
116
101
|
option :GamePort, required: true, desc: "SC2 port. Corresponds to SC2 launch option '-port'"
|
117
102
|
option :LadderServer, required: true, desc: "SC2 server ip or hostname"
|
@@ -147,16 +132,6 @@ module Sc2
|
|
147
132
|
$bot.play
|
148
133
|
end.wait
|
149
134
|
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
135
|
end
|
161
136
|
# standard:enable Style/GlobalVars
|
162
137
|
end
|
data/lib/sc2ai/cli/ladderzip.rb
CHANGED
@@ -78,8 +78,12 @@ 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
|
-
cmd = "docker compose -f #{@compose_file} up
|
86
|
+
cmd = "docker compose -f #{@compose_file} up versus_bot -d --force-recreate"
|
83
87
|
Kernel.system(cmd)
|
84
88
|
end
|
85
89
|
|
@@ -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.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 -----------------------
|
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
@@ -1268,6 +1268,12 @@ module Sc2
|
|
1268
1268
|
# Returns zero to map_height-1 as range
|
1269
1269
|
def map_tile_range_y: () -> ::Range[untyped]
|
1270
1270
|
|
1271
|
+
# Ensures a Sc2::Position's x/y stays in map tile range
|
1272
|
+
# Prevents out of bound exceptions when working with minimap
|
1273
|
+
#
|
1274
|
+
# _@param_ `position`
|
1275
|
+
def clamp_to_grid: ((Sc2::Position | Api::Point2D) position) -> (Sc2::Position | Api::Point2D)
|
1276
|
+
|
1271
1277
|
# Returns whether a x/y (integer) is placeable as per minimap image data.
|
1272
1278
|
# It does not say whether a position is occupied by another building.
|
1273
1279
|
# One pixel covers one whole block. Corrects floats on your behalf
|
@@ -4068,6 +4074,9 @@ module Sc2
|
|
4068
4074
|
|
4069
4075
|
def initialize: () -> void
|
4070
4076
|
|
4077
|
+
# Returns the value of attribute host.
|
4078
|
+
attr_accessor host: untyped
|
4079
|
+
|
4071
4080
|
# Returns the value of attribute clients.
|
4072
4081
|
attr_accessor clients: untyped
|
4073
4082
|
|
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.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dyson Returns
|
8
8
|
bindir: exe
|
9
9
|
cert_chain: []
|
10
|
-
date: 2025-
|
10
|
+
date: 2025-04-08 00:00:00.000000000 Z
|
11
11
|
dependencies:
|
12
12
|
- !ruby/object:Gem::Dependency
|
13
13
|
name: async
|
@@ -354,9 +354,11 @@ files:
|
|
354
354
|
- data/setup/setup.SC2Replay
|
355
355
|
- data/stableid.json
|
356
356
|
- data/versions.json
|
357
|
+
- docker_build/Dockerfile.aiarenabot
|
357
358
|
- docker_build/Dockerfile.ruby
|
358
359
|
- docker_build/docker-compose-base-image.yml
|
359
360
|
- docker_build/docker-compose-ladderzip.yml
|
361
|
+
- docker_build/docker-compose-versus-bot.yml
|
360
362
|
- exe/sc2ai
|
361
363
|
- lib/sc2ai.rb
|
362
364
|
- lib/sc2ai/api/ability_id.rb
|
@@ -370,6 +372,7 @@ files:
|
|
370
372
|
- lib/sc2ai/cli/cli.rb
|
371
373
|
- lib/sc2ai/cli/ladderzip.rb
|
372
374
|
- lib/sc2ai/cli/new.rb
|
375
|
+
- lib/sc2ai/cli/versus_bot.rb
|
373
376
|
- lib/sc2ai/configuration.rb
|
374
377
|
- lib/sc2ai/connection.rb
|
375
378
|
- lib/sc2ai/connection/connection_listener.rb
|
@@ -419,6 +422,7 @@ files:
|
|
419
422
|
- lib/sc2ai/unit_group/geo_ext.rb
|
420
423
|
- lib/sc2ai/version.rb
|
421
424
|
- lib/templates/ladderzip/bin/ladder.tt
|
425
|
+
- lib/templates/ladderzip/ladderbots.json.tt
|
422
426
|
- lib/templates/new/.ladderignore
|
423
427
|
- lib/templates/new/Gemfile.tt
|
424
428
|
- lib/templates/new/api/common.proto
|