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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ddd8bd1e37b50ba6d4e89c24fb330d646589d869cc53cf96a69624eaa0e6133c
4
- data.tar.gz: d88b2674f094621c11aa7de9a0c59ae82a6668aa6c882783a54236776ceb9f37
3
+ metadata.gz: 9da11456b2d9014945dc54f3a92dfc47cef7ecf40be7facaceb75bc8a04df9b1
4
+ data.tar.gz: e5926c8416141ef1a8677e8fb0832126414cdbed3729951f236ecab14274d336
5
5
  SHA512:
6
- metadata.gz: 10c922d086149c4f2e4cac906607d7f5fb2c31fde1ace225186b0d83342efaea6720c48b799eb01e4d29fec5b295a4890d44b9cf803bb0c47e75e5830489c466
7
- data.tar.gz: 7430bdcd6935e457b88b562838c0dc112f28ecdfbacd072293ddfdf358d065287dcdbc300bb7cc76b4efe0e671a82006f9d53766b79bd2d006496982061e3fc7
6
+ metadata.gz: 3ce8e35ea773557def35d44eff9dfc63a475b09c82ad573644cae647b222da83f65047a0a935d0465aad5919002144e6c28543792539f4783dad88700225f5ef
7
+ data.tar.gz: 5848c68e72fad356096f6b7bf2da049ee9f7f3a4c36676f682401d9e612f71d26d8950d94c733e4cda04887592a2be030faadc3f6e2d1b918528c2aa63e5eada
@@ -0,0 +1,12 @@
1
+ FROM aiarena/arenaclient-bot:latest AS build
2
+ LABEL service="bot-ruby-sc2ai-local"
3
+
4
+ USER root
5
+ WORKDIR /bots/
6
+
7
+ ARG DEBIAN_DISABLE_RUBYGEMS_INTEGRATION=true
8
+
9
+ RUN apt-get update
10
+ RUN apt-get install -y unzip
11
+
12
+ ENTRYPOINT ["/bin/bash"]
@@ -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.2
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 libopenblas0-serial
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
- #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
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 libopenblas0-serial libffi-dev --assume-yes
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
 
@@ -1,4 +1,3 @@
1
- version: "3.9"
2
1
  services:
3
2
  bot:
4
3
  image: dysonreturns/aiarena-ruby-builder:latest
@@ -1,4 +1,3 @@
1
- version: "3.9"
2
1
  services:
3
2
  bot:
4
3
  image: dysonreturns/aiarena-ruby-builder:latest
@@ -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.", :green
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
@@ -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
- def game_info
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
 
@@ -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 (input_grid[y + inner_y, x + inner_x]).zero?
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
@@ -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
- @game_info_task = Async do
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 :Light
66
+ # @return [Boolean] whether unit has attribute :LIGHT
68
67
  def is_light?
69
- has_attribute?(:LIGHT)
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 :Armored
72
+ # @return [Boolean] whether unit has attribute :ARMORED
74
73
  def is_armored?
75
- has_attribute?(:ARMORED)
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 :Biological
78
+ # @return [Boolean] whether unit has attribute :BIOLOGICAL
80
79
  def is_biological?
81
- has_attribute?(:BIOLOGICAL)
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 :Mechanical
84
+ # @return [Boolean] whether unit has attribute :MECHANICAL
86
85
  def is_mechanical?
87
- has_attribute?(:MECHANICAL)
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 :Robotic
90
+ # @return [Boolean] whether unit has attribute :ROBOTIC
92
91
  def is_robotic?
93
- has_attribute?(:ROBOTIC)
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 :Psionic
96
+ # @return [Boolean] whether unit has attribute :PSIONIC
98
97
  def is_psionic?
99
- has_attribute?(:PSIONIC)
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 :Massive
102
+ # @return [Boolean] whether unit has attribute :MASSIVE
104
103
  def is_massive?
105
- has_attribute?(:MASSIVE)
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 :Structure
108
+ # @return [Boolean] whether unit has attribute :STRUCTURE
110
109
  def is_structure?
111
- has_attribute?(:STRUCTURE)
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 :Hover
114
+ # @return [Boolean] whether unit has attribute :HOVER
116
115
  def is_hover?
117
- has_attribute?(:HOVER)
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 :Heroic
120
+ # @return [Boolean] whether unit has attribute :HEROIC
122
121
  def is_heroic?
123
- has_attribute?(:HEROIC)
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 :Summoned
126
+ # @return [Boolean] whether unit has attribute :SUMMONED
128
127
  def is_summoned?
129
- has_attribute?(:SUMMONED)
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) ? a : Api::Attribute.lookup(a) }
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) ? a : Api::Attribute.lookup(a) }
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
@@ -1,5 +1,5 @@
1
1
  module Sc2
2
2
  # gem version
3
3
  # @return [String]
4
- VERSION = "0.6.1"
4
+ VERSION = "0.6.5"
5
5
  end
@@ -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
- # Conservative heap sizes are based on 2x a tested medium-sized game
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
@@ -0,0 +1,11 @@
1
+ {
2
+ "Bots": {
3
+ "<%= botname %>": {
4
+ "Race": "<%= Api::Race.lookup($bot.race).to_s.capitalize %>",
5
+ "Type": "BinaryCpp",
6
+ "RootPath": "./",
7
+ "FileName": "<%= botname %>",
8
+ "Debug": false
9
+ }
10
+ }
11
+ }
@@ -21,7 +21,7 @@ test/
21
21
  tmp/
22
22
  .byebug_history
23
23
  .bundle/
24
- vendor/bundle
24
+ vendor/bundle/
25
25
  lib/bundler/man/
26
26
 
27
27
  # Other
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 :Light
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 :Armored
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 :Biological
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 :Mechanical
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 :Robotic
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 :Psionic
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 :Massive
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 :Structure
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 :Hover
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 :Heroic
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 :Summoned
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 :Light
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 :Armored
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 :Biological
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 :Mechanical
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 :Robotic
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 :Psionic
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 :Massive
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 :Structure
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 :Hover
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 :Heroic
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 :Summoned
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.1
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: 2025-02-27 00:00:00.000000000 Z
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: logger
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: fiddle
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: reline
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.7
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.3
482
+ rubygems_version: 3.6.8
465
483
  specification_version: 4
466
484
  summary: STARCRAFT® II AI API
467
485
  test_files: []