Module: Bcpm::Player

Defined in:
lib/bcpm/player.rb

Overview

Manages player packages.

Class Method Summary collapse

Class Method Details

.ant_config(simulator_config) ⇒ Object

The contents of an Ant configuration file (build.xml) pointing to a simulator config file.



268
269
270
271
272
273
274
275
276
277
278
279
280
# File 'lib/bcpm/player.rb', line 268

def self.ant_config(simulator_config)
  contents = File.read Bcpm::Dist.ant_file
  # Point to the distribution instead of current root.
  contents.gsub! 'basedir="."', 'basedir="' + Bcpm::Dist.dist_path + '"'
  contents.gsub! '<property name="path.base" location="."',
      '<property name="path.base" location="' + Bcpm::Dist.dist_path + '"'
  # Set VM memory.
  contents.gsub! /\<jvmarg\s+value\=\"\-Xmx.*\"/,
                 "<jvmarg value=\"-Xmx#{Bcpm::Match::vm_ram}m\""
  # Replace hardcoded bc.conf reference.
  contents.gsub! 'bc.conf', simulator_config
  contents
end

.checkpoint(repo_uri, repo_branch, local_name) ⇒ Object

Downloads player code from a repository for one-time use, without linking it to the repository.

Returns the path to the player on the local system, or nil if something went wrong.



34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# File 'lib/bcpm/player.rb', line 34

def self.checkpoint(repo_uri, repo_branch, local_name)
  old_name = player_name repo_uri
  local_path = File.join local_root, local_name
  if File.exist?(local_path)
    puts "Player already installed at #{local_path}!"
    return nil
  end
  if old_name == repo_uri
    return nil unless Bcpm::Git.checkpoint_local_repo(File.join(local_root, old_name), local_path)
  else
    return nil unless Bcpm::Git.checkpoint_repo(repo_uri, repo_branch, local_path)
  end
  if local_name == old_name
    source_path = package_path(local_path)
  else
    source_path = rename(local_path, old_name)
  end
  unless source_path
    puts "Repository #{repo_uri} doesn't seem to contain a player!"
    FileUtils.rm_rf local_path      
    return nil
  end

  Bcpm::Dist.add_player source_path
  configure local_path
  local_path
end

.configure(local_path) ⇒ Object

Configures a player’s source code project.



155
156
157
158
159
160
161
162
163
164
165
166
167
# File 'lib/bcpm/player.rb', line 155

def self.configure(local_path)
  File.open File.join(local_path, '.project'), 'wb' do |f|
    f.write eclipse_project(local_path)
  end
  
  File.open File.join(local_path, '.classpath'), 'wb' do |f|
    f.write eclipse_classpath(local_path)
  end
  
  File.open File.join(local_path, 'build.xml'), 'wb' do |f|
    f.write ant_config('bc.conf')
  end
end

.create(local_name) ⇒ Object

Creates a player from the built-in template.

Returns the path to the player on the local system, or nil if something went wrong.



65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
# File 'lib/bcpm/player.rb', line 65

def self.create(local_name)
  old_name = 'template'
  local_path = File.join local_root, local_name
  if File.exist?(local_path)
    puts "Player already installed at #{local_path}!"
    return nil
  end
  write_template local_path
  unless source_path = rename(local_path, old_name)
    puts "Repository #{repo_uri} doesn't seem to contain a player!"
    FileUtils.rm_rf local_path      
    return nil
  end

  Bcpm::Dist.add_player source_path
  configure local_path
  local_path
end

.default_local_rootObject

The directory containing all players code.



175
176
177
178
179
180
181
182
# File 'lib/bcpm/player.rb', line 175

def self.default_local_root
  path = Dir.pwd
  unless File.exist?(File.join(path, '.metadata'))
    puts "Please chdir into your Eclipse workspace!"
    exit 1
  end
  path
end

.eclipse_classpath(local_path) ⇒ Object

The contents of an Eclipse .classpath for a player project.



283
284
285
286
287
288
289
290
291
292
293
294
295
# File 'lib/bcpm/player.rb', line 283

def self.eclipse_classpath(local_path)
  jar_path = File.join Bcpm::Dist.dist_path, 'lib', 'battlecode-server.jar'
  
  <<END_CONFIG
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<classpathentry kind="lib" path="#{jar_path}"/>
<classpathentry kind="output" path="bin"/>
</classpath>
END_CONFIG
end

.eclipse_project(local_path) ⇒ Object

The contents of an Eclipse .project file for a player project.



298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
# File 'lib/bcpm/player.rb', line 298

def self.eclipse_project(local_path)
  project_name = File.basename local_path
  
  <<END_CONFIG
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>#{project_name}</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
  <buildCommand>
    <name>org.eclipse.jdt.core.javabuilder</name>
    <arguments>
    </arguments>
  </buildCommand>
</buildSpec>
<natures>
  <nature>org.eclipse.jdt.core.javanature</nature>
</natures>
</projectDescription>
END_CONFIG
end

.has_suite?(local_path) ⇒ Boolean

True if a battlecode distribution is installed on the local machine.

Returns:

  • (Boolean)


150
151
152
# File 'lib/bcpm/player.rb', line 150

def self.has_suite?(local_path)
  File.exist? File.join(local_path, 'suite')
end

.install(repo_uri, repo_branch) ⇒ Object

Clones a player code from a repository, and sets it up for development on the local machine.

Returns the player’s name, or nil if something went wrong.



11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# File 'lib/bcpm/player.rb', line 11

def self.install(repo_uri, repo_branch)
  name = player_name repo_uri
  local_path = File.join local_root, name    
  if File.exist?(local_path)
    puts "Player already installed at #{local_path}!"
    return nil
  end
  return nil unless Bcpm::Git.clone_repo(repo_uri, repo_branch, local_path)
  
  unless source_path = package_path(local_path)
    puts "Repository #{repo_uri} doesn't seem to contain a player!"
    FileUtils.rm_rf local_path      
    return nil
  end
      
  Bcpm::Dist.add_player source_path
  configure local_path
  name
end

.listObject

All installed players.

This might contain false positives.



187
188
189
190
191
# File 'lib/bcpm/player.rb', line 187

def self.list
  Dir.glob(File.join(local_root, '*', '.project')).map do |project_file|
    File.basename File.dirname(project_file)
  end
end

.list_activeObject

All installed players who are properly hooked into the distribution.

These players can be used into tests and simulations.



196
197
198
# File 'lib/bcpm/player.rb', line 196

def self.list_active
  list.select { |player_name| wired? player_name }
end

.local_rootObject

The directory containing all players code.



170
171
172
# File 'lib/bcpm/player.rb', line 170

def self.local_root
  Bcpm::Config[:player_root] ||= default_local_root    
end

.new_suite(local_path) ⇒ Object

Creates a Suite instance for running all the tests.



138
139
140
141
142
# File 'lib/bcpm/player.rb', line 138

def self.new_suite(local_path)
  suite = Bcpm::Tests::Suite.new local_path
  suite.add_cases suite_files(local_path)
  suite
end

.package_path(local_path, name_override = nil, silent = false) ⇒ Object

Extracts the path to a player’s source package given their repository.

Args:

local_path:: path to the player's git repository on the local machine
name_override:: (optional) supplies the player name; if not set, the name is extracted from
                the path, by convention 
silent:: if set, won't output errors that help the user debug the problem


243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
# File 'lib/bcpm/player.rb', line 243

def self.package_path(local_path, name_override = nil, silent = false)
  # All the packages should be in the 'src' directory.
  source_dir = File.join local_path, 'src'
  unless File.exist? source_dir
    puts "Missing src directory" unless silent
    return nil
  end
  
  # Ignore maintainance files/folder such as .gitignore / .svn.
  package_dirs = Dir.glob(File.join(source_dir, '*')).
                     reject { |path| File.basename(path)[0, 1] == '.' }
  unless package_dirs.length == 1
    puts "src directory doesn't contain exactly one package directory!" unless silent
    return nil
  end

  path = package_dirs.first
  unless (name_override || File.basename(local_path)) == File.basename(path)
    puts "The package in the src directory doesn't match the player name!" unless silent
    return nil
  end
  path
end

.player_name(repo_uri) ⇒ Object

Extracts the player name out of the git repository URI for the player’s code.



213
214
215
216
217
# File 'lib/bcpm/player.rb', line 213

def self.player_name(repo_uri)
  name = File.basename(repo_uri)
  name = name[0...-4] if name[-4, 4] == '.git'
  name
end

.reconfigure(local_name) ⇒ Object

Re-configures a player’s source code project.



97
98
99
100
101
102
103
104
105
106
# File 'lib/bcpm/player.rb', line 97

def self.reconfigure(local_name)
  local_path = File.join local_root, local_name
  unless source_path = package_path(local_path)
    puts "Directory #{local_path} doesn't seem to contain a player!"
    FileUtils.rm_rf local_path      
    return nil
  end
  Bcpm::Dist.add_player source_path
  configure local_path
end

.rename(local_path, old_name) ⇒ Object

Renames a player to match its path on the local system.

Returns the path to the player’s source package.



222
223
224
225
226
227
228
229
230
231
232
233
234
# File 'lib/bcpm/player.rb', line 222

def self.rename(local_path, old_name)
  new_name = File.basename local_path    
  return nil unless old_source_dir = package_path(local_path, old_name)
  new_source_dir = File.join File.dirname(old_source_dir), new_name
  FileUtils.mv old_source_dir, new_source_dir
  
  Dir.glob(File.join(new_source_dir, '**', '*.java')).each do |file|
    contents = File.read file
    contents.gsub! /(^|[^A-Za-z0-9_.])#{old_name}([^A-Za-z0-9_]|$)/, "\\1#{new_name}\\2"
    File.open(file, 'wb') { |f| f.write contents }
  end
  new_source_dir
end

.run_case(case_name, live, local_name, branch = 'master') ⇒ Object

Runs a case in the test suite against a player codebase.



119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
# File 'lib/bcpm/player.rb', line 119

def self.run_case(case_name, live, local_name, branch = 'master')
  local_path = File.join local_root, local_name
  unless has_suite? local_path
    puts "No test suite found at #{local_path}!"
    return false
  end    
  
  case_file = case_name + '.rb'
  files = suite_files(local_path).select { |file| File.basename(file) == case_file }
  unless files.length == 1
    puts "Ambiguous case name! It matched #{files.count} cases.\n#{files.join("\n")}\n"
    return false
  end
  suite = Bcpm::Tests::Suite.new(local_path)
  suite.add_cases files
  suite.run live
end

.run_suite(local_name) ⇒ Object

Runs the player’s test suite.



109
110
111
112
113
114
115
116
# File 'lib/bcpm/player.rb', line 109

def self.run_suite(local_name)
  local_path = File.join local_root, local_name
  unless has_suite? local_path
    puts "No test suite found at #{local_path}!"
    return false
  end
  new_suite(local_path).run false
end

.suite_files(local_path) ⇒ Object

All the test cases in the suite of the given player.



145
146
147
# File 'lib/bcpm/player.rb', line 145

def self.suite_files(local_path)
  Dir.glob File.join(local_path, 'suite', '**', '*.rb')
end

.uninstall(local_name) ⇒ Object

Undoes the effects of an install or checkpoint call.

Returns true for success, or false if something went wrong.



87
88
89
90
91
92
93
94
# File 'lib/bcpm/player.rb', line 87

def self.uninstall(local_name)
  local_path = File.join local_root, local_name
  source_path = package_path local_path
  Bcpm::Dist.remove_player source_path if source_path
  return false unless File.exist? local_path    
  FileUtils.rm_rf local_path
  true
end

.uninstall_allObject

Cleans up all the installed players.



208
209
210
# File 'lib/bcpm/player.rb', line 208

def self.uninstall_all
  list_good.each { |player_name| uninstall player_name }
end

.wired?(player_name) ⇒ Boolean

True if the given player name is installed and wired.

Returns:

  • (Boolean)


201
202
203
204
205
# File 'lib/bcpm/player.rb', line 201

def self.wired?(player_name)
  local_path = File.join local_root, player_name
  source_path = package_path local_path, nil, true
  (source_path && Bcpm::Dist.contains_player?(source_path)) ? true : false
end

.write_template(local_path) ⇒ Object

Writes the built-in player template.



323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
# File 'lib/bcpm/player.rb', line 323

def self.write_template(local_path)
  src_path = File.join local_path, 'src', 'template'
  FileUtils.mkdir_p src_path
  File.open File.join(src_path, 'RobotPlayer.java'), 'wb' do |f| 
    f.write <<END_SOURCE
package template;

import battlecode.common.RobotController;

public class RobotPlayer implements Runnable {
public static RobotController rc;

public RobotPlayer(RobotController controller) {
  rc = controller;
}

public void run() {
  while (true) {
    rc.yield();
  }
}
}
END_SOURCE
  end
  
  src_path = File.join local_path, 'src', 'template', 'test', 'players'
  FileUtils.mkdir_p src_path
  File.open File.join(src_path, 'RobotPlayer.java'), 'wb' do |f| 
    f.write <<END_SOURCE
package template.test.players;

import battlecode.common.RobotController;

public class RobotPlayer implements Runnable {
public static RobotController rc;

public RobotPlayer(RobotController controller) {
  rc = controller;
}

public void run() {
  while (true) {
    rc.yield();
  }
}
}
END_SOURCE
  end
  
  suite_path = File.join local_path, 'suite'
  FileUtils.mkdir_p suite_path
  File.open File.join(suite_path, 'win_vs_yield.rb'), 'wb' do |f| 
    f.write <<END_SOURCE
vs 'yield'
suite_map '#{Bcpm::Dist.maps.first}'
replace_class 'RobotPlayer', 'test.players.RobotPlayer'

match do
it 'must win in any way' do
  should_win
end
end
END_SOURCE
  end
  
  maps_path = File.join suite_path, 'maps'
  FileUtils.mkdir_p maps_path
  Bcpm::Dist.copy_map Bcpm::Dist.maps.first, maps_path
  
  File.open File.join(local_path, '.gitignore'), 'wb' do |f|
    f.write <<END_SOURCE
# Auto-generated by bcpm.
build.xml
.classpath
.project

# Build output.
bin

# Temporary files.
~*
.DS_Store
END_SOURCE
  end    
end