bcpm 0.11

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,133 @@
1
+ require 'fileutils'
2
+ require 'shellwords'
3
+
4
+ # :nodoc: namespace
5
+ module Bcpm
6
+
7
+ # Battle-code distribution management.
8
+ module Dist
9
+ # Hooks a player's code into the installed battlecode distribution.
10
+ def self.add_player(player_path)
11
+ team_path = File.join dist_path, 'teams', File.basename(player_path)
12
+ if /mingw/ =~ RUBY_PLATFORM || (/win/ =~ RUBY_PLATFORM && /darwin/ !~ RUBY_PLATFORM)
13
+ Dir.rmdir team_path if File.exist? team_path
14
+ command = Shellwords.shelljoin(['cmd', '/C', 'mklink', '/D', team_path.gsub('/', '\\'),
15
+ player_path.gsub('/', '\\')])
16
+ Kernel.`(command)
17
+ else
18
+ File.unlink team_path if File.exist? team_path
19
+ FileUtils.ln_s player_path, team_path
20
+ end
21
+ end
22
+
23
+ # Unhooks a player's code from the installed battlecode distribution.
24
+ def self.remove_player(player_path)
25
+ return unless contains_player?(player_path)
26
+ team_path = File.join dist_path, 'teams', File.basename(player_path)
27
+ if /mingw/ =~ RUBY_PLATFORM || (/win/ =~ RUBY_PLATFORM && /darwin/ !~ RUBY_PLATFORM)
28
+ Dir.rmdir team_path
29
+ else
30
+ File.unlink team_path
31
+ end
32
+ bin_path = File.join dist_path, 'bin', File.basename(player_path)
33
+ FileUtils.rm_rf bin_path if File.exist?(bin_path)
34
+ end
35
+
36
+ # True if the given path is a player that is hooked into the distribution.
37
+ def self.contains_player?(player_path)
38
+ team_path = File.join dist_path, 'teams', File.basename(player_path)
39
+ if /mingw/ =~ RUBY_PLATFORM || (/win/ =~ RUBY_PLATFORM && /darwin/ !~ RUBY_PLATFORM)
40
+ File.exist? team_path
41
+ else
42
+ File.exist?(team_path) && File.symlink?(team_path) && File.readlink(team_path) == player_path
43
+ end
44
+ end
45
+
46
+ # Upgrades the installed battlecode distribution to the latest version.
47
+ #
48
+ # Does a fresh install if no distribution is configured.
49
+ def self.upgrade
50
+ return install unless installed?
51
+ Bcpm::Git.update_repo dist_path
52
+ end
53
+
54
+ # Installs the latest battlecode distribution.
55
+ def self.install
56
+ Bcpm::Git.clone_repo repo_uri, 'master', dist_path
57
+ Bcpm::Config[:dist_repo_uri] = repo_uri
58
+ Bcpm::Config[:dist_path] = dist_path
59
+ end
60
+
61
+ # True if a battlecode distribution is installed on the local machine.
62
+ def self.installed?
63
+ Bcpm::Config.config.has_key? :dist_path
64
+ end
65
+
66
+ # Removes the battlecode distribution installed on the location machine.
67
+ def self.uninstall
68
+ return unless installed?
69
+ FileUtils.rm_rf dist_path
70
+ Bcpm::Config[:dist_path] = nil
71
+ end
72
+
73
+ # Path to the battlecode ant file.
74
+ def self.ant_file
75
+ File.join dist_path, 'build.xml'
76
+ end
77
+
78
+ # Path to the battlecode maps directory.
79
+ def self.maps_path
80
+ File.join dist_path, 'maps'
81
+ end
82
+
83
+ # Path to the battlecode simulator configuration file.
84
+ def self.conf_file
85
+ File.join dist_path, 'bc.conf'
86
+ end
87
+
88
+ # Path to the locally installed battlecode distribution.
89
+ def self.dist_path
90
+ Bcpm::Config[:dist_path] || default_dist_path
91
+ end
92
+
93
+ # Path to the locally installed battlecode distribution.
94
+ def self.default_dist_path
95
+ if File.exist?('.metadata') && File.directory?('.metadata')
96
+ File.expand_path './battlecode'
97
+ else
98
+ File.expand_path '~/battlecode'
99
+ end
100
+ end
101
+
102
+ # Git URI to the distribution repository.
103
+ def self.repo_uri
104
+ Bcpm::Config[:dist_repo_uri] || default_repo_uri
105
+ end
106
+
107
+ # Git URI to the distribution repository.
108
+ def self.default_repo_uri
109
+ 'git@git.pwnb.us:six370/battlecode2011.git'
110
+ end
111
+
112
+ # Maps installed in the battlecode distribution.
113
+ def self.maps
114
+ Dir.glob(File.join(maps_path, '*.xml')).map do |f|
115
+ File.basename(f).sub(/\.xml$/, '')
116
+ end
117
+ end
118
+
119
+ # Copies a battlecode distribution map.
120
+ def self.copy_map(map_name, destination)
121
+ map_file = File.join maps_path, map_name + '.xml'
122
+ unless File.exist?(map_file)
123
+ puts "No map found at#{map_file}"
124
+ return
125
+ end
126
+ if File.exist?(destination) && File.directory?(destination)
127
+ destionation = File.join destination, map_name + '.xml'
128
+ end
129
+ FileUtils.cp map_file, destination
130
+ end
131
+ end
132
+
133
+ end # namespace Bcpm
@@ -0,0 +1,153 @@
1
+ require 'fileutils'
2
+ require 'thread'
3
+
4
+ # :nodoc: namespace
5
+ module Bcpm
6
+
7
+ # Pits players against other players.
8
+ module Duel
9
+ # Has two players fight on all maps in both positions.
10
+ def self.duel_pair(player1_name, player2_name, show_progress = false,
11
+ maps = nil)
12
+ maps ||= Bcpm::Dist.maps.sort
13
+
14
+ # Abort in case of typos.
15
+ [player1_name, player2_name].each do |player|
16
+ unless Bcpm::Player.wired? player
17
+ puts "Player #{player} is not installed\n"
18
+ exit 1
19
+ end
20
+ end
21
+
22
+ # Abort in case of compile errors.
23
+ env = Bcpm::Tests::Environment.new player1_name
24
+ unless env.build
25
+ print env.build_log
26
+ exit 1
27
+ end
28
+
29
+ # Compute the matches.
30
+ matches = maps.map { |map_name|
31
+ [:a, :b].map do |side|
32
+ Bcpm::Tests::TestMatch.new side, player2_name, map_name, env
33
+ end
34
+ }.flatten
35
+
36
+ # Compute stats.
37
+ score, wins, losses, errors = 0, [], [], []
38
+ multiplex_matches(matches) do |match|
39
+ score_delta = case match.winner
40
+ when :a, :b
41
+ (match.winner == match.side) ? 1 : -1
42
+ else
43
+ 0
44
+ end
45
+ if show_progress
46
+ print "#{player1_name} #{match.description}: #{outcome_string(score_delta)}\n"
47
+ STDOUT.flush
48
+ end
49
+ score += score_delta if score_delta
50
+ case score_delta
51
+ when 1
52
+ wins << match
53
+ when -1
54
+ losses << match
55
+ when 0
56
+ errors << match
57
+ end
58
+ end
59
+ { :score => score, :wins => wins, :losses => losses, :errors => errors }
60
+ end
61
+
62
+ # Runs may matches in parallel.
63
+ #
64
+ # Returns the given matches. If given a block, also yields each match as it
65
+ # becomes available.
66
+ def self.multiplex_matches(matches)
67
+ # Schedule matches.
68
+ in_queue = Queue.new
69
+ matches.each do |match|
70
+ in_queue.push match
71
+ end
72
+ match_threads.times { in_queue.push nil }
73
+
74
+ # Execute matches.
75
+ out_queue = Queue.new
76
+ old_abort = Thread.abort_on_exception
77
+ Thread.abort_on_exception = true
78
+ match_threads.times do
79
+ Thread.new do
80
+ loop do
81
+ break unless match = in_queue.pop
82
+ match.run false
83
+ out_queue.push match
84
+ end
85
+ end
86
+ end
87
+ Thread.abort_on_exception = old_abort
88
+
89
+ matches.length.times do
90
+ match = out_queue.pop
91
+ if Kernel.block_given?
92
+ yield match
93
+ end
94
+ end
95
+ matches
96
+ end
97
+
98
+ # The string to be shown for a match outcome.
99
+ def self.outcome_string(score_delta)
100
+ # TODO(pwnall): ANSI color codes
101
+ if /mingw/ =~ RUBY_PLATFORM ||
102
+ (/win/ =~ RUBY_PLATFORM && /darwin/ !~ RUBY_PLATFORM)
103
+ {0 => "error", 1 => "won", -1 => "lost"}[score_delta]
104
+ else
105
+ {
106
+ 0 => "\33[33merror\33[0m",
107
+ 1 => "\33[32mwon\33[0m",
108
+ -1 => "\33[31mlost\33[0m"
109
+ }[score_delta]
110
+ end
111
+ end
112
+
113
+ # Number of threads to use for simulating matches.
114
+ def self.match_threads
115
+ (Bcpm::Config[:match_threads] ||= default_match_threads).to_i
116
+ end
117
+
118
+ # Number of threads to use for simulating matches.
119
+ def self.default_match_threads
120
+ 1
121
+ end
122
+
123
+ # Has all players compete against each other.
124
+ def self.rank_players(player_list, show_progress = false, maps = nil)
125
+ scores = {}
126
+ 0.upto(player_list.length - 1) do |i|
127
+ 0.upto(i - 1) do |j|
128
+ outcome = duel_pair player_list[i], player_list[j], show_progress, maps
129
+ scores[i] ||= 0
130
+ scores[i] += outcome[:score]
131
+ scores[j] ||= 0
132
+ scores[j] -= outcome[:score]
133
+ end
134
+ end
135
+ scores.map { |k, v| [v, player_list[k]] }.
136
+ sort_by { |score, player| [-score, player] }
137
+ end
138
+
139
+ # Scores one player against the other players.
140
+ def self.score_player(player, player_list, show_progress = false, maps = nil)
141
+ player_list = player_list - [player]
142
+ scores = player_list.map do |opponent|
143
+ [
144
+ duel_pair(player, opponent, show_progress, maps)[:score],
145
+ opponent
146
+ ]
147
+ end
148
+ { :points => scores.map(&:first).inject(0) { |a, b| a + b},
149
+ :scores => scores.sort_by { |score, player| [score, player] } }
150
+ end
151
+ end # module Bcpm::Duel
152
+
153
+ end # namespace Bcpm
@@ -0,0 +1,93 @@
1
+ require 'fileutils'
2
+ require 'socket'
3
+
4
+ # :nodoc: namespace
5
+ module Bcpm
6
+
7
+ # Git repository operations.
8
+ module Git
9
+ # Clones a repository.
10
+ #
11
+ # Returns true for success, false if something went wrong.
12
+ def self.clone_repo(repo_uri, repo_branch, local_path)
13
+ FileUtils.mkdir_p File.dirname(local_path)
14
+ success = nil
15
+ Dir.chdir File.dirname(local_path) do
16
+ FileUtils.rm_rf File.basename(local_path) if File.exists?(local_path)
17
+ if repo_branch == 'master'
18
+ success = Kernel.system 'git', 'clone', repo_uri,
19
+ File.basename(local_path)
20
+ else
21
+ success = Kernel.system 'git', 'clone', '--branch', repo_branch,
22
+ repo_uri, File.basename(local_path)
23
+ unless success
24
+ success = Kernel.system 'git', 'clone', repo_uri,
25
+ File.basename(local_path)
26
+ if success
27
+ Dir.chdir File.basename(local_path) do
28
+ success = Kernel.system 'git', 'checkout', '-q',
29
+ 'origin/' + repo_branch
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ FileUtils.rm_rf local_path unless success
36
+ success
37
+ end
38
+
39
+ # Downloads a snapshot of a repository.
40
+ #
41
+ # Returns true for success, false if something went wrong.
42
+ def self.checkpoint_repo(repo_uri, repo_branch, local_path)
43
+ FileUtils.mkdir_p local_path
44
+ success = nil
45
+ Dir.chdir File.dirname(local_path) do
46
+ zip_file = File.basename(local_path) + '.zip'
47
+ success = Kernel.system('git', 'archive', '--format=zip',
48
+ '--remote', repo_uri, '--output', zip_file, '-9', repo_branch)
49
+ if success
50
+ Dir.chdir File.basename(File.basename(local_path)) do
51
+ success = Kernel.system 'unzip', '-qq', File.join('..', zip_file)
52
+ end
53
+ end
54
+ File.unlink zip_file if File.exist?(zip_file)
55
+ end
56
+ unless success
57
+ puts "Trying workaround for old git"
58
+ success = clone_repo repo_uri, repo_branch, local_path
59
+ FileUtils.rm_rf File.join(local_path, '.git') if success
60
+ end
61
+
62
+ FileUtils.rm_rf local_path unless success
63
+ success
64
+ end
65
+
66
+ # Checks out the working copy of the repository.
67
+ #
68
+ # Returns true for success, false if something went wrong.
69
+ def self.checkpoint_local_repo(repo_path, local_path)
70
+ return false unless File.exist?(repo_path)
71
+ FileUtils.mkdir_p local_path
72
+ Dir.entries(repo_path).each do |entry|
73
+ next if ['.', '..', '.git'].include? entry
74
+ FileUtils.cp_r File.join(repo_path, entry), local_path
75
+ end
76
+ true
77
+ end
78
+
79
+ # Pulls the latest changes into the repository.
80
+ def self.update_repo(local_path)
81
+ Dir.chdir local_path do
82
+ Kernel.system 'git', 'pull'
83
+ end
84
+ end
85
+
86
+ # Temporary directory name.
87
+ def self.tempdir
88
+ File.join Dir.tmpdir, 'bcpm',
89
+ "update_#{Socket.hostname}_#{(Time.now.to_f * 1000).to_i}_#{$PID}"
90
+ end
91
+ end # module Bcpm::Git
92
+
93
+ end # namespace Bcpm
@@ -0,0 +1,275 @@
1
+ require 'English'
2
+ require 'fileutils'
3
+ require 'shellwords'
4
+ require 'socket'
5
+ require 'thread'
6
+ require 'tmpdir'
7
+
8
+ # :nodoc: namespace
9
+ module Bcpm
10
+
11
+ # Runs matches between players.
12
+ module Match
13
+ # Runs a match between two players.
14
+ def self.run(player1_name, player2_name, map_name, mode)
15
+ env = Bcpm::Tests::Environment.new player1_name
16
+ options = {}
17
+ if mode == :debug
18
+ Bcpm::Config[:breakpoints] ||= true
19
+ Bcpm::Config[:debugcode] ||= true
20
+ Bcpm::Config[:noupkeep] ||= false
21
+ Bcpm::Config[:debuglimit] ||= 1_000_000
22
+ options = Hash[engine_options.map { |k, v| [v, Bcpm::Config[k]] }]
23
+ end
24
+ match = Bcpm::Tests::TestMatch.new :a, player2_name, map_name, env, options
25
+ match.run(mode != :file)
26
+ "Winner side: #{match.winner.to_s.upcase}\n" + match.stash_data
27
+ end
28
+
29
+ # Key-value pairs for friendly => canonical names of battlecode engine options.
30
+ def self.engine_options
31
+ {
32
+ 'breakpoints' => 'bc.engine.breakpoints',
33
+ 'debugcode' => 'bc.engine.debug-methods',
34
+ 'noupkeep' => 'bc.engine.upkeep',
35
+ 'debuglimit' => 'bc.engine.debug-max-bytecodes'
36
+ }
37
+ end
38
+
39
+ # Runs a match between two players and returns the log data.
40
+ #
41
+ # Args:
42
+ # player1_name:: name of locally installed player (A)
43
+ # player2_name:: name of locally installed player (B)
44
+ # silence_b:: if true, B is silenced; otherwise, A is silenced
45
+ # map_name:: name of map .xml file (or full path for custom map)
46
+ # run_live:: if true, tries to run the match using the live UI
47
+ # bc_options:: hash of simulator settings to be added to bc.conf
48
+ def self.match_data(player1_name, player2_name, silence_b, map_name, run_live, bc_options = {})
49
+ uid = tempfile
50
+ tempdir = File.expand_path File.join(Dir.tmpdir, 'bcpm', 'match_' + uid)
51
+ FileUtils.mkdir_p tempdir
52
+ binfile = File.join tempdir, 'match.rms'
53
+ txtfile = File.join tempdir, 'match.txt'
54
+ build_log = File.join tempdir, 'build.log'
55
+ match_log = File.join tempdir, 'match.log'
56
+ scribe_log = File.join tempdir, 'scribe.log'
57
+
58
+ bc_config = simulator_config player1_name, player2_name, silence_b, map_name, binfile, txtfile
59
+ bc_config.merge! bc_options
60
+ conf_file = File.join tempdir, 'bc.conf'
61
+ write_config conf_file, bc_config
62
+ write_ui_config conf_file, true, bc_config if run_live
63
+ build_file = File.join tempdir, 'build.xml'
64
+ write_build build_file, conf_file
65
+
66
+ if run_live
67
+ run_build_script tempdir, build_file, match_log, 'run', 'Stop buffering match'
68
+ else
69
+ run_build_script tempdir, build_file, match_log, 'file'
70
+ end
71
+ run_build_script tempdir, build_file, scribe_log, 'transcribe'
72
+
73
+ textlog = File.exist?(txtfile) ? File.open(txtfile, 'rb') { |f| f.read } : ''
74
+ binlog = File.exist?(binfile) ? File.open(binfile, 'rb') { |f| f.read } : ''
75
+ antlog = File.exist?(match_log) ? File.open(match_log, 'rb') { |f| f.read } : ''
76
+ FileUtils.rm_rf tempdir
77
+
78
+ { :ant => extract_ant_log(antlog), :rms => binlog, :script => textlog, :uid => uid }
79
+ end
80
+
81
+ # Replays a match using the binlog (.rms file).
82
+ def self.replay(binfile)
83
+ tempdir = File.join Dir.tmpdir, 'bcpm', 'match_' + tempfile
84
+ FileUtils.mkdir_p tempdir
85
+ match_log = File.join tempdir, 'match.log'
86
+
87
+ bc_config = simulator_config nil, nil, true, nil, binfile, nil
88
+ conf_file = File.join tempdir, 'bc.conf'
89
+ write_config conf_file, bc_config
90
+ write_ui_config conf_file, false, bc_config
91
+ build_file = File.join tempdir, 'build.xml'
92
+ write_build build_file, conf_file
93
+
94
+ run_build_script tempdir, build_file, match_log, 'run'
95
+ FileUtils.rm_rf tempdir
96
+ end
97
+
98
+ # Options to be overridden for the battlecode simulator.
99
+ def self.simulator_config(player1_name, player2_name, silence_b, map_name, binfile, txtfile)
100
+ Bcpm::Config[:client3d] ||= 'off'
101
+ Bcpm::Config[:sound] ||= 'off'
102
+ config = {
103
+ 'bc.engine.silence-a' => !silence_b,
104
+ 'bc.engine.silence-b' => !!silence_b,
105
+ 'bc.dialog.skip' => true,
106
+ 'bc.server.throttle' => 'yield',
107
+ 'bc.server.throttle-count' => 100000,
108
+ 'bc.client.opengl' => Bcpm::Config[:client3d],
109
+ 'bc.client.sound-on' => Bcpm::Config[:sound] || 'off',
110
+
111
+ # Healthy production defaults.
112
+ 'bc.engine.breakpoints' => false,
113
+ 'bc.engine.debug-methods' => false,
114
+ 'bc.engine.upkeep' => true
115
+ }
116
+ map_path = nil
117
+ if map_name
118
+ if File.basename(map_name) != map_name
119
+ map_path = File.dirname map_name
120
+ map_name = File.basename(map_name).sub(/\.xml$/, '')
121
+ end
122
+ end
123
+ config['bc.game.maps'] = map_name if map_name
124
+ config['bc.game.map-path'] = map_path if map_path
125
+ config['bc.game.team-a'] = player1_name if player1_name
126
+ config['bc.game.team-b'] = player2_name if player2_name
127
+ config['bc.server.save-file'] = binfile if binfile
128
+ config['bc.server.transcribe-input'] = binfile if binfile
129
+ config['bc.server.transcribe-output'] = txtfile if txtfile
130
+ config
131
+ end
132
+
133
+ # Memory to be dedicated to the simulation JVM (in megabytes.)
134
+ def self.vm_ram
135
+ (Bcpm::Config['vm_ram'] ||= default_vm_ram).to_i
136
+ end
137
+
138
+ # Memory to be dedicated to the simulation JVM (in megabytes.)
139
+ def self.default_vm_ram
140
+ 1024
141
+ end
142
+
143
+ # Writes a patched buildfile that references the given configuration file.
144
+ def self.write_build(buildfile, conffile)
145
+ contents = Bcpm::Player.ant_config conffile
146
+ File.open(buildfile, 'wb') { |f| f.write contents }
147
+ end
148
+
149
+ # Writes a cleaned up battlecode simulator configuration file.
150
+ def self.write_config(conffile, options = {})
151
+ lines = File.read(Bcpm::Dist.conf_file).split("\n")
152
+ lines = lines.reject do |line|
153
+ key = line.split('=', 2).first
154
+ options.has_key? key
155
+ end
156
+ lines += options.map { |key, value| "#{key}=#{value}" }
157
+ File.open(conffile, 'wb') { |f| f.write lines.join("\n") + "\n" }
158
+ end
159
+
160
+ # Writes the configuration for the battlecode UI.
161
+ #
162
+ # This is a singleton file, so only one client should run at a time.
163
+ def self.write_ui_config(conffile, run_live, options = {})
164
+ save_path = options['bc.server.save-file'] || ''
165
+ if /mingw/ =~ RUBY_PLATFORM || (/win/ =~ RUBY_PLATFORM && /darwin/ !~ RUBY_PLATFORM)
166
+ save_path = save_path.dup
167
+ save_path.gsub! '\\', '\\\\\\\\'
168
+ save_path.gsub! '/', '\\\\\\\\'
169
+ if save_path[1, 2] == ':\\'
170
+ save_path[1, 2] = '\\:\\'
171
+ end
172
+ end
173
+
174
+ choice = run_live ? 'LOCAL' : 'FILE'
175
+ File.open(File.expand_path('~/.battlecode.ui'), 'wb') do |f|
176
+ f.write <<END_CONFIG
177
+ choice=#{choice}
178
+ save=#{run_live}
179
+ save-file=#{save_path}
180
+ file=#{save_path}
181
+ host=
182
+ analyzeFile=false
183
+ glclient=#{options['bc.client.opengl'] || 'false'}
184
+ showMinimap=false
185
+ MAP=#{options['bc.game.maps']}
186
+ maps=#{options['bc.game.maps']}
187
+ TEAM_A=#{options['bc.game.team-a']}
188
+ TEAM_B=#{options['bc.game.team-b']}
189
+ lockstep=false
190
+ END_CONFIG
191
+ end
192
+ end
193
+
194
+ # Runs the battlecode Ant script.
195
+ def self.run_build_script(target_dir, build_file, log_file, target, run_live = false)
196
+ if run_live
197
+ Dir.chdir target_dir do
198
+ command = Shellwords.shelljoin(['ant', '-noinput', '-buildfile',
199
+ build_file, target])
200
+
201
+ # Start the build as a subprocess, dump its output to the queue as
202
+ # string fragments. nil means the subprocess completed.
203
+ queue = Queue.new
204
+ thread = Thread.start do
205
+ IO.popen command do |f|
206
+ begin
207
+ loop { queue << f.readpartial(1024) }
208
+ rescue EOFError
209
+ queue << nil
210
+ end
211
+ end
212
+ end
213
+
214
+ build_output = ''
215
+ while fragment = queue.pop
216
+ # Dump the build output to the screen as the simulation happens.
217
+ print fragment
218
+ STDOUT.flush
219
+ build_output << fragment
220
+
221
+ # Let bcpm carry on when the simulation completes.
222
+ break if build_output.index(run_live)
223
+ end
224
+ build_output << "\n" if build_output[-1] != ?\n
225
+
226
+ # Pretend everything was put in a log file.
227
+ File.open(log_file, 'wb') { |f| f.write build_output }
228
+ return thread
229
+ end
230
+ else
231
+ command = Shellwords.shelljoin(['ant', '-noinput', '-buildfile',
232
+ build_file, '-logfile', log_file, target])
233
+ if /mingw/ =~ RUBY_PLATFORM ||
234
+ (/win/ =~ RUBY_PLATFORM && /darwin/ !~ RUBY_PLATFORM)
235
+ Dir.chdir target_dir do
236
+ output = Kernel.`(command)
237
+ # If there is no log file, dump the output to the log.
238
+ unless File.exist?(log_file)
239
+ File.open(log_file, 'wb') { |f| f.write output }
240
+ end
241
+ end
242
+ else
243
+ pid = fork do
244
+ Dir.chdir target_dir do
245
+ output = Kernel.`(command)
246
+ # If there is no log file, dump the output to the log.
247
+ unless File.exist?(log_file)
248
+ File.open(log_file, 'wb') { |f| f.write output }
249
+ end
250
+ end
251
+ end
252
+ Process.wait pid
253
+ end
254
+ end
255
+ end
256
+
257
+ # Selects the battlecode simulator log out of an ant log.
258
+ def self.extract_ant_log(contents)
259
+ lines = []
260
+ contents.split("\n").each do |line|
261
+ start = line.index '[java] '
262
+ next unless start
263
+ lines << line[(start + 7)..-1]
264
+ break if line.index('- Match Finished -')
265
+ end
266
+ lines.join("\n")
267
+ end
268
+
269
+ # Temporary file name.
270
+ def self.tempfile
271
+ "#{Socket.hostname}_#{'%x' % (Time.now.to_f * 1000).to_i}_#{$PID}_#{'%x' % Thread.current.object_id}"
272
+ end
273
+ end # module Bcpm::Match
274
+
275
+ end # namespace Bcpm