hangman_tournament 1.0
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.
- data/CHANGES +6 -0
- data/Rakefile +66 -0
- data/bin/generate_hangman_player +9 -0
- data/bin/submit_hangman_player +9 -0
- data/lib/hangman_tournament/submit.rb +71 -0
- data/lib/init.rb +1 -0
- data/lib/limelight/string.rb +35 -0
- data/lib/limelight/templates/player_templater.rb +33 -0
- data/lib/limelight/templates/sources/Hangman.Rakefile.template +12 -0
- data/lib/limelight/templates/sources/Rakefile.template +47 -0
- data/lib/limelight/templates/sources/bs_player.template +169 -0
- data/lib/limelight/templates/sources/player.template +45 -0
- data/lib/limelight/templates/sources/player_spec.template +12 -0
- data/lib/limelight/templates/sources/spec_helper.template +4 -0
- data/lib/limelight/templates/templater.rb +128 -0
- data/lib/limelight/templates/templater_logger.rb +36 -0
- data/spec/hangman_tournament/submit_spec.rb +36 -0
- data/spec/limelight/templates/player_templater_spec.rb +42 -0
- data/spec/spec_helper.rb +4 -0
- metadata +88 -0
data/CHANGES
ADDED
data/Rakefile
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
require 'rake'
|
|
2
|
+
require 'spec/rake/spectask'
|
|
3
|
+
require 'rake/gempackagetask'
|
|
4
|
+
|
|
5
|
+
desc "Run all specs"
|
|
6
|
+
Spec::Rake::SpecTask.new('spec') do |t|
|
|
7
|
+
t.spec_files = FileList['spec/**/*.rb']
|
|
8
|
+
t.rcov = false
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
PKG_NAME = "hangman_tournament"
|
|
12
|
+
PKG_VERSION = "1.0"
|
|
13
|
+
PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}"
|
|
14
|
+
|
|
15
|
+
spec = Gem::Specification.new do |s|
|
|
16
|
+
s.name = PKG_NAME
|
|
17
|
+
s.version = PKG_VERSION
|
|
18
|
+
s.files = FileList['lib/**/*', 'spec/**/*', 'bin/**/*', 'CHANGES', 'Rakefile'].to_a
|
|
19
|
+
s.require_path = 'lib'
|
|
20
|
+
s.test_files = Dir.glob('spec/*_spec.rb')
|
|
21
|
+
s.bindir = 'bin'
|
|
22
|
+
s.executables = ['generate_hangman_player', 'submit_hangman_player']
|
|
23
|
+
s.email = "sparring-hangman@rubyforge.org"
|
|
24
|
+
s.homepage = "http://sparring.rubyforge.org/"
|
|
25
|
+
s.rubyforge_project = "sparring"
|
|
26
|
+
s.summary = "Hangman tournament."
|
|
27
|
+
s.description = "This gem provides the infrastructure needed to participate in the Hangman Tournament"
|
|
28
|
+
s.author = "Micah Martin"
|
|
29
|
+
s.add_dependency('letter_letdown', ">= 1.0")
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
Rake::GemPackageTask.new(spec) do |pkg|
|
|
33
|
+
pkg.need_zip = false
|
|
34
|
+
pkg.need_tar = false
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
task :verify_user do
|
|
38
|
+
raise "RUBYFORGE_USER environment variable not set!" unless ENV['RUBYFORGE_USER']
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
task :verify_password do
|
|
42
|
+
raise "RUBYFORGE_PASSWORD environment variable not set!" unless ENV['RUBYFORGE_PASSWORD']
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
task :copy_player_gems do
|
|
46
|
+
system "cp ../letter_letdown/pkg/hangman_letter_letdown-1.0.gem pkg"
|
|
47
|
+
system "cp submissions/*.gem pkg"
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
desc "Publish gem+tgz+zip on RubyForge. You must make sure lib/version.rb is aligned with the CHANGELOG file"
|
|
51
|
+
task :publish_packages => [:verify_user, :verify_password, :package, :copy_player_gems] do
|
|
52
|
+
require 'meta_project'
|
|
53
|
+
require 'rake/contrib/xforge'
|
|
54
|
+
release_files = FileList[
|
|
55
|
+
"pkg/#{PKG_FILE_NAME}.gem",
|
|
56
|
+
"pkg/hangman_letter_letdown-1.0.gem"
|
|
57
|
+
]
|
|
58
|
+
|
|
59
|
+
Rake::XForge::Release.new(MetaProject::Project::XForge::RubyForge.new("sparring")) do |xf|
|
|
60
|
+
# Never hardcode user name and password in the Rakefile!
|
|
61
|
+
xf.user_name = ENV['RUBYFORGE_USER']
|
|
62
|
+
xf.password = ENV['RUBYFORGE_PASSWORD']
|
|
63
|
+
xf.files = release_files.to_a
|
|
64
|
+
xf.release_name = "Hangman Tournament #{PKG_VERSION}"
|
|
65
|
+
end
|
|
66
|
+
end
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
require 'rubygems'
|
|
2
|
+
require 'digest/sha1'
|
|
3
|
+
require 'drb'
|
|
4
|
+
|
|
5
|
+
module HangmanTournament
|
|
6
|
+
|
|
7
|
+
class Submit
|
|
8
|
+
|
|
9
|
+
def initialize(gem_name)
|
|
10
|
+
@gem_name = gem_name
|
|
11
|
+
@input = $stdin
|
|
12
|
+
@output = $stdout
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def submit
|
|
16
|
+
data = collect_data
|
|
17
|
+
password = get_password
|
|
18
|
+
password = Digest::SHA1.hexdigest(password)
|
|
19
|
+
data[:password] = password
|
|
20
|
+
|
|
21
|
+
begin
|
|
22
|
+
server = DRbObject.new(nil, 'druby://micahmartin.com:9697')
|
|
23
|
+
authorized = server.authorize(data[:name], password)
|
|
24
|
+
quit "Incorrect password. You are not authorized to submit this player." unless authorized
|
|
25
|
+
result = server.submit_profile(data)
|
|
26
|
+
quit "Failed to submit: #{result}" if result != true
|
|
27
|
+
puts "Player #{data[:name]} has been submitted."
|
|
28
|
+
rescue DRb::DRbConnError => e
|
|
29
|
+
quit "Could not connect to battleship server!"
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def collect_data
|
|
34
|
+
spec = gem_spec
|
|
35
|
+
data = {}
|
|
36
|
+
data[:name] = spec.summary[15..-1]
|
|
37
|
+
data[:author] = spec.author
|
|
38
|
+
data[:email] = spec.email
|
|
39
|
+
data[:description] = spec.description
|
|
40
|
+
data[:gem_file_name] = spec.file_name
|
|
41
|
+
gem_file_path = File.expand_path(File.join(spec.full_gem_path, "..", "..", 'cache', spec.file_name))
|
|
42
|
+
data[:gem_content] = IO.read(gem_file_path)
|
|
43
|
+
return data
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def gem_spec
|
|
47
|
+
if @spec.nil?
|
|
48
|
+
@spec = Gem.source_index.latest_specs.find do |spec|
|
|
49
|
+
spec.name == @gem_name
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
quit "Could not find installed gem named '#{@gem_name}'" if @spec.nil?
|
|
53
|
+
return @spec
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def get_password
|
|
57
|
+
@output.puts "Password?"
|
|
58
|
+
@output.print "> "
|
|
59
|
+
|
|
60
|
+
password = @input.readline.strip
|
|
61
|
+
return password
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def quit(message)
|
|
65
|
+
puts message
|
|
66
|
+
exit -1
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
end
|
data/lib/init.rb
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
$: << File.expand_path(File.dirname(__FILE__))
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
class String
|
|
2
|
+
|
|
3
|
+
# Converts Ruby style names to Java style camal case.
|
|
4
|
+
#
|
|
5
|
+
# "four_score".camalized # => "FourScore"
|
|
6
|
+
# "and_seven_years".camalized(:lower) # => "andSevenYears"
|
|
7
|
+
#
|
|
8
|
+
def camalized(starting_case = :upper)
|
|
9
|
+
value = self.downcase.gsub(/[_| ][a-z]/) { |match| match[-1..-1].upcase }
|
|
10
|
+
value = value[0..0].upcase + value[1..-1] if starting_case == :upper
|
|
11
|
+
return value
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# Converts Java camel case names to ruby style underscored names.
|
|
15
|
+
#
|
|
16
|
+
# "FourScore".underscored # => "four_score"
|
|
17
|
+
# "andSevenYears".underscored # => "and_seven_years"
|
|
18
|
+
#
|
|
19
|
+
def underscored
|
|
20
|
+
value = self[0..0].downcase + self[1..-1]
|
|
21
|
+
value = value.gsub(/[A-Z]/) { |cap| "_#{cap.downcase}" }
|
|
22
|
+
return value
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
# Converts ruby style and camalcase strings into title strings where every word is capitalized and separated by a space.
|
|
27
|
+
#
|
|
28
|
+
# "four_score".titleized # => "Four Score"
|
|
29
|
+
#
|
|
30
|
+
def titleized(starting_case = :upper)
|
|
31
|
+
value = self.gsub(/[a-z0-9][A-Z]/) { |match| "#{match[0..0]} #{match[-1..-1]}" }
|
|
32
|
+
value = value.gsub(/[_| ][a-z]/) { |match| " #{match[-1..-1].upcase}" }
|
|
33
|
+
return value[0..0].upcase + value[1..-1]
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
require 'limelight/templates/templater'
|
|
2
|
+
require 'limelight/string'
|
|
3
|
+
|
|
4
|
+
module Limelight
|
|
5
|
+
module Templates
|
|
6
|
+
|
|
7
|
+
class PlayerTemplater < Templater
|
|
8
|
+
|
|
9
|
+
attr_reader :filename, :tokens
|
|
10
|
+
|
|
11
|
+
def initialize(player_name)
|
|
12
|
+
super(".")
|
|
13
|
+
@tokens = {}
|
|
14
|
+
@tokens[:PLAYER_NAME] = player_name
|
|
15
|
+
clean_name = player_name.gsub(/[\?\*!'\-.;:]/, "").gsub(" ", "_").downcase
|
|
16
|
+
@filename = @tokens[:FILENAME] = clean_name
|
|
17
|
+
@tokens[:CLASSNAME] = clean_name.camalized
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Generates the files
|
|
21
|
+
#
|
|
22
|
+
def generate
|
|
23
|
+
file(File.join(filename, "Rakefile"), "Rakefile.template", @tokens)
|
|
24
|
+
file(File.join(filename, "Hangman.Rakefile"), "Hangman.Rakefile.template", @tokens)
|
|
25
|
+
file(File.join(filename, "lib", filename, "#{filename}.rb"), "player.template", @tokens)
|
|
26
|
+
file(File.join(filename, "spec", "spec_helper.rb"), "spec_helper.template", @tokens)
|
|
27
|
+
file(File.join(filename, "spec", filename, "#{filename}_spec.rb"), "player_spec.template", @tokens)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
#
|
|
2
|
+
# DO NOT tamper with this file. It will lead to disqualification.
|
|
3
|
+
|
|
4
|
+
require 'rake'
|
|
5
|
+
require 'spec/rake/spectask'
|
|
6
|
+
|
|
7
|
+
desc "Run all examples with RCov"
|
|
8
|
+
Spec::Rake::SpecTask.new('spec_with_rcov') do |t|
|
|
9
|
+
t.spec_files = FileList['spec/**/*.rb']
|
|
10
|
+
t.rcov = true
|
|
11
|
+
t.rcov_opts = ['-t', '--exclude', 'spec,rcov', '--no-html']
|
|
12
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
require 'rake'
|
|
2
|
+
require 'rake/gempackagetask'
|
|
3
|
+
require 'spec/rake/spectask'
|
|
4
|
+
require 'hangman_tournament/submit'
|
|
5
|
+
|
|
6
|
+
desc "Run all specs"
|
|
7
|
+
Spec::Rake::SpecTask.new('spec') do |t|
|
|
8
|
+
t.spec_files = FileList['spec/**/*.rb']
|
|
9
|
+
t.rcov = false
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
PKG_NAME = "!-FILENAME-!"
|
|
13
|
+
PKG_VERSION = "1.0"
|
|
14
|
+
|
|
15
|
+
spec = Gem::Specification.new do |s|
|
|
16
|
+
s.name = "hangman_#{PKG_NAME}"
|
|
17
|
+
s.version = PKG_VERSION
|
|
18
|
+
s.files = Dir.glob('**/*').reject{ |f| f =~ /pkg/ }
|
|
19
|
+
s.require_path = 'lib'
|
|
20
|
+
s.test_files = Dir.glob('spec/*_spec.rb')
|
|
21
|
+
s.bindir = 'bin'
|
|
22
|
+
s.executables = []
|
|
23
|
+
s.summary = "Hangman Player:!-PLAYER_NAME-!"
|
|
24
|
+
s.rubyforge_project = "sparring"
|
|
25
|
+
s.homepage = "http://sparring.rubyforge.org/"
|
|
26
|
+
|
|
27
|
+
###########################################
|
|
28
|
+
##
|
|
29
|
+
## You are encouraged to modify the following
|
|
30
|
+
## spec attributes.
|
|
31
|
+
##
|
|
32
|
+
###########################################
|
|
33
|
+
s.description = "A hangman player"
|
|
34
|
+
s.author = "Anonymous"
|
|
35
|
+
s.email = "authors@email.com"
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
Rake::GemPackageTask.new(spec) do |pkg|
|
|
39
|
+
pkg.need_zip = false
|
|
40
|
+
pkg.need_tar = false
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
desc "Submit your player"
|
|
44
|
+
task :submit do
|
|
45
|
+
submitter = HangmanTournament::Submit.new(PKG_NAME)
|
|
46
|
+
submitter.submit
|
|
47
|
+
end
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
module !-CLASSNAME-!
|
|
2
|
+
|
|
3
|
+
# Battleship Player
|
|
4
|
+
#
|
|
5
|
+
# Battleship is board game between two players. See http://en.wikipedia.org/wiki/Battleship for more information and
|
|
6
|
+
# game rules.
|
|
7
|
+
#
|
|
8
|
+
# A player represents the conputer AI to play a game of Battleship. It should know how to place ships and target
|
|
9
|
+
# the opponents ships.
|
|
10
|
+
#
|
|
11
|
+
# This version of Battleship is played on a 10 x 10 grid where rows are labled by the letters A - J and
|
|
12
|
+
# columns are labled by the numbers 1 - 10. At the start of the game, each player will be asked for ship placements.
|
|
13
|
+
# Once the ships are placed, play proceeeds by each player targeting one square on their opponents map. A player
|
|
14
|
+
# may only target one square, reguardless of whether it resulted in a hit or not, before changing turns with her opponent.
|
|
15
|
+
#
|
|
16
|
+
class !-CLASSNAME-!
|
|
17
|
+
|
|
18
|
+
# This method is called at the beginning of each game. A player may only be instantiated once and used to play many games.
|
|
19
|
+
# So new_game should reset any internal state acquired in previous games so that it is prepared for a new game.
|
|
20
|
+
#
|
|
21
|
+
# The name of the opponent player is passed in. This allows for the possibility to learn opponent strategy and
|
|
22
|
+
# play the game differently based on the opponent.
|
|
23
|
+
#
|
|
24
|
+
def new_game(opponent_name)
|
|
25
|
+
reset
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Returns the placement of the carrier. A carrier consumes 5 squares.
|
|
29
|
+
#
|
|
30
|
+
# The return value is a string that describes the placements of the ship.
|
|
31
|
+
# The placement string must be in the following format:
|
|
32
|
+
#
|
|
33
|
+
# "#{ROW}#{COL} #{ORIENTATION}"
|
|
34
|
+
#
|
|
35
|
+
# eg
|
|
36
|
+
#
|
|
37
|
+
# A1 horizontal # the ship will occupy A1, A2, A3, A4, and A5
|
|
38
|
+
# A1 vertical # the ship will occupy A1, B1, C1, D1, and E1
|
|
39
|
+
# F5 horizontal # the ship will occupy F5, F6, F7, F8, and F9
|
|
40
|
+
# F5 vertical # the ship will occupy F5, G5, H5, I5, and J5
|
|
41
|
+
#
|
|
42
|
+
# The ship must not fall off the edge of the map. For example, a carrier placement of 'A8 horizontal' would
|
|
43
|
+
# not leave enough space in the A row to accomidate the carrier since it requires 5 squares.
|
|
44
|
+
#
|
|
45
|
+
# Ships may not overlap with other ships. For example a carrier placement of 'A1 horizontal' and a submarine
|
|
46
|
+
# placement of 'A1 vertical' would be invalid because bothe ships are trying to occupy the square A1.
|
|
47
|
+
#
|
|
48
|
+
# Invalid ship placements will result in disqualification of the player.
|
|
49
|
+
#
|
|
50
|
+
def carrier_placement
|
|
51
|
+
return "A1 horizontal"
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Returns the placement of the battleship. A battleship consumes 4 squares.
|
|
55
|
+
#
|
|
56
|
+
# See carrier_placement for details on ship placement
|
|
57
|
+
#
|
|
58
|
+
def battleship_placement
|
|
59
|
+
return "B1 horizontal"
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Returns the placement of the destroyer. A destroyer consumes 3 squares.
|
|
63
|
+
#
|
|
64
|
+
# See carrier_placement for details on ship placement
|
|
65
|
+
#
|
|
66
|
+
def destroyer_placement
|
|
67
|
+
return "C1 horizontal"
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Returns the placement of the submarine. A submarine consumes 3 squares.
|
|
71
|
+
#
|
|
72
|
+
# See carrier_placement for details on ship placement
|
|
73
|
+
#
|
|
74
|
+
def submarine_placement
|
|
75
|
+
return "D1 horizontal"
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Returns the placement of the patrolship. A patrolship consumes 2 squares.
|
|
79
|
+
#
|
|
80
|
+
# See carrier_placement for details on ship placement
|
|
81
|
+
#
|
|
82
|
+
def patrolship_placement
|
|
83
|
+
return "E1 horizontal"
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Returns the coordinates of the players next target. This method will be called once per turn. The player
|
|
87
|
+
# should return target coordinates as a string in the form of:
|
|
88
|
+
#
|
|
89
|
+
# "#{ROW}#{COL}"
|
|
90
|
+
#
|
|
91
|
+
# eg
|
|
92
|
+
#
|
|
93
|
+
# A1 # the square in Row A and Column 1
|
|
94
|
+
# F5 # the square in Row F and Column 5
|
|
95
|
+
#
|
|
96
|
+
# Since the map contains only 10 rows and 10 columns, the ROW should be A, B, C, D, E, F, G H, I, or J. And the
|
|
97
|
+
# COL should be 1, 2, 3, 4, 5, 6, 7, 8, 9, or 10
|
|
98
|
+
#
|
|
99
|
+
# Returning coordinates outside the range or in an invalid format will result in the players disqualification.
|
|
100
|
+
#
|
|
101
|
+
# It is illegal to illegal to target a sector more than once. Doing so will also result in disqualification.
|
|
102
|
+
#
|
|
103
|
+
def next_target
|
|
104
|
+
target = target_for_current_shot
|
|
105
|
+
@shots_taken += 1
|
|
106
|
+
return target
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# target_result will be called by the system after a call to next_target. The paramters supplied inform the player
|
|
110
|
+
# of the results of the target.
|
|
111
|
+
#
|
|
112
|
+
# coordinates : string. The coordinates targeted. It will be the same value returned by the previous call to next_target
|
|
113
|
+
# was_hit : boolean. true if the target was occupied by a ship. false otherwise.
|
|
114
|
+
# ship_sunk : symbol. nil if the target did not result in the sinking of a ship. If the target did result in
|
|
115
|
+
# in the sinking of a ship, the ship type is supplied (:carrier, :battleship, :destroyer, :submarine, :patrolship).
|
|
116
|
+
#
|
|
117
|
+
# An intelligent player will use the information to better play the game. For example, if the result indicates a
|
|
118
|
+
# hit, a player my choose to target neighboring squares to hit and sink the remainder of the ship.
|
|
119
|
+
#
|
|
120
|
+
def target_result(coordinates, was_hit, ship_sunk)
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
# enemy_targeting is called by the system to inform a player of their apponents move. When the opponent targets
|
|
124
|
+
# a square, this method is called with the coordinates.
|
|
125
|
+
#
|
|
126
|
+
# Players may use this information to understand an opponents targeting strategy and place ships differently
|
|
127
|
+
# in subsequent games.
|
|
128
|
+
#
|
|
129
|
+
def enemy_targeting(coordinates)
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
# Called by the system at the end of a game to inform the player of the results.
|
|
133
|
+
#
|
|
134
|
+
# result : 1 of 3 possible values (:victory, :defeate, :disqualified)
|
|
135
|
+
# disqualification_reason : nil unless the game ended as the result of a disqualification. In the event of a
|
|
136
|
+
# disqualification, this paramter will hold a string description of the reason for disqualification. Both
|
|
137
|
+
# players will be informed of the reason.
|
|
138
|
+
#
|
|
139
|
+
# :victory # indicates the player won the game
|
|
140
|
+
# :defeat # indicates the player lost the game
|
|
141
|
+
# :disqualified # indicates the player was disqualified
|
|
142
|
+
#
|
|
143
|
+
def game_over(result, disqualification_reason=nil)
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
# Non API methods #####################################
|
|
147
|
+
|
|
148
|
+
attr_reader :opponent, :targets, :enemy_targeted_sectors, :result, :disqualification_reason #:nodoc:
|
|
149
|
+
|
|
150
|
+
def initialize #:nodoc:
|
|
151
|
+
reset
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
private ###############################################
|
|
155
|
+
|
|
156
|
+
def reset
|
|
157
|
+
@shots_taken = 0
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
ROWS = %w{ A B C D E F G H I J }
|
|
161
|
+
def target_for_current_shot
|
|
162
|
+
row = ROWS[(@shots_taken) / 10]
|
|
163
|
+
col = @shots_taken % 10 + 1
|
|
164
|
+
return "#{row}#{col}"
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
module !-CLASSNAME-!
|
|
2
|
+
class !-CLASSNAME-!
|
|
3
|
+
|
|
4
|
+
# You may initialize you player but the the initialize method must take NO paramters.
|
|
5
|
+
# The player will only be instantiated once, and will play many games.
|
|
6
|
+
def initialize
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
# Before starting a game, this method will be called to inform the player of all the possible words that may be
|
|
10
|
+
# played.
|
|
11
|
+
def word_list=(list)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# a new game has started. The number of guesses the player has left is passed in (default 6),
|
|
15
|
+
# in case you want to keep track of it.
|
|
16
|
+
def new_game(guesses_left)
|
|
17
|
+
@left = ('a'..'z').to_a
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Each turn your player must make a guess. The word will be a bunch of underscores, one for each letter in the word.
|
|
21
|
+
# after your first turn, correct guesses will appear in the word parameter. If the word was "shoes" and you guessed "s",
|
|
22
|
+
# the word parameter would be "s___s", if you then guess 'o', the next turn it would be "s_o_s", and so on.
|
|
23
|
+
# guesses_left is how many guesses you have left before your player is hung.
|
|
24
|
+
def guess(word, guesses_left)
|
|
25
|
+
@left.shift
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# notifies you that your last guess was incorrect, and passes your guess back to the method
|
|
29
|
+
def incorrect_guess(guess)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# notifies you that your last guess was correct, and passes your guess back to the method
|
|
33
|
+
def correct_guess(guess)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# you lost the game. The reason is in the reason parameter
|
|
37
|
+
def fail(reason)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# The result of the game, it'll be one of 'win', 'loss', or 'fail'.
|
|
41
|
+
# The spelled out word will be provided regardless of the result.
|
|
42
|
+
def game_result(result, word)
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + "/../spec_helper")
|
|
2
|
+
require '!-FILENAME-!/!-FILENAME-!'
|
|
3
|
+
|
|
4
|
+
describe !-CLASSNAME-!::!-CLASSNAME-! do
|
|
5
|
+
|
|
6
|
+
it "should be instantiable with no paramters" do
|
|
7
|
+
|
|
8
|
+
lambda { !-CLASSNAME-!::!-CLASSNAME-!.new }.should_not raise_error
|
|
9
|
+
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
end
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
require 'limelight/templates/templater_logger'
|
|
2
|
+
|
|
3
|
+
module Limelight
|
|
4
|
+
module Templates
|
|
5
|
+
|
|
6
|
+
# A class to create directories and file templates. An instance of Templater must be provided with
|
|
7
|
+
# a target_root and a source_root. The target_root designates a root directory in which all directories and
|
|
8
|
+
# files will be created. The source_root designated a directory where all the file template can be found.
|
|
9
|
+
#
|
|
10
|
+
# A file template is a plain text file. It may optionally contain token markers in the format !-TOKEN_NAME-!.
|
|
11
|
+
# When a file template is installed by the templater, all the token margers will be replaced by tokens provided
|
|
12
|
+
# in a hash.
|
|
13
|
+
#
|
|
14
|
+
class Templater
|
|
15
|
+
|
|
16
|
+
# Return the default source_root for Limelight related file templates.
|
|
17
|
+
#
|
|
18
|
+
# $LIMELIGHT_LIB$/limelight/templates/sources
|
|
19
|
+
#
|
|
20
|
+
def self.source_dir
|
|
21
|
+
return File.join(File.dirname(__FILE__), "sources")
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Carifies a path as relative or absolute. Essentially if makes sure a path begins with a . if it's not
|
|
25
|
+
# an absolute path.
|
|
26
|
+
#
|
|
27
|
+
# Templater.clarity('some/path') -> './some/path'
|
|
28
|
+
# Templater.clarity('/root/path') -> '/root/path'
|
|
29
|
+
#
|
|
30
|
+
def self.clarify(path)
|
|
31
|
+
return path if path[0..0] == '.'
|
|
32
|
+
return path if path == File.expand_path(path)
|
|
33
|
+
return File.join(".", path)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
attr_reader :target_root, :source_root
|
|
37
|
+
|
|
38
|
+
# See TemplaterLogger
|
|
39
|
+
#
|
|
40
|
+
attr_accessor :logger
|
|
41
|
+
|
|
42
|
+
# New instances Templater require a target_root. The source_root may optionally be provided. source_root
|
|
43
|
+
# defaults to Templater.source_dir
|
|
44
|
+
#
|
|
45
|
+
# The logger is initializes as a TemplaterLogger
|
|
46
|
+
#
|
|
47
|
+
def initialize(target_root, source_root=Templater.source_dir)
|
|
48
|
+
@logger = TemplaterLogger.new
|
|
49
|
+
@target_root = Templater.clarify(target_root)
|
|
50
|
+
@source_root = source_root
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Creates a deirectory. If the specified directory's parent directory is missing, it will be created as will its
|
|
54
|
+
# parent directory, and so on.
|
|
55
|
+
#
|
|
56
|
+
# After the following call,
|
|
57
|
+
#
|
|
58
|
+
# templater.directory("dir1/dir2/dir3/dir4")
|
|
59
|
+
#
|
|
60
|
+
# The following directories will exist, inside the target_root, whether they existed prior to the call or not.
|
|
61
|
+
#
|
|
62
|
+
# dir1
|
|
63
|
+
# dir1/dir2
|
|
64
|
+
# dir1/dir2/dir3
|
|
65
|
+
# dir1/dir2/dir3/dir4
|
|
66
|
+
#
|
|
67
|
+
def directory(path)
|
|
68
|
+
full_path = File.join(@target_root, path)
|
|
69
|
+
establish_directory(full_path)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# Creates the specified file from the specified file template. The file will be created withint the target_root.
|
|
73
|
+
# All parent diretories will be created if needed. The source paramter should be a path pointing to a
|
|
74
|
+
# file template in the source_root directory.
|
|
75
|
+
#
|
|
76
|
+
# Assume the the file <code>src/default.txt.template</code> exists in the source_root with the following content.
|
|
77
|
+
#
|
|
78
|
+
# !-SCORES-! score and !-YEARS-! years ago, ...
|
|
79
|
+
#
|
|
80
|
+
# When the following command is executed,
|
|
81
|
+
#
|
|
82
|
+
# templater.file('dir/foo.txt', 'src/default.txt.template', :SCORES => "Four", :YEARS => "seven")
|
|
83
|
+
#
|
|
84
|
+
# The file <code>dir/foo.txt</code> will exist in the target_root with the following content.
|
|
85
|
+
#
|
|
86
|
+
# Four score and seven years ago, ...
|
|
87
|
+
#
|
|
88
|
+
def file(target, source, tokens = {})
|
|
89
|
+
target_path = File.join(@target_root, target)
|
|
90
|
+
source_source = File.join(@source_root, source)
|
|
91
|
+
|
|
92
|
+
establish_directory(File.dirname(target_path))
|
|
93
|
+
|
|
94
|
+
if File.exists?(target_path)
|
|
95
|
+
@logger.file_already_exists(target_path)
|
|
96
|
+
else
|
|
97
|
+
@logger.creating_file(target_path)
|
|
98
|
+
content = IO.read(source_source)
|
|
99
|
+
content = replace_tokens(content, tokens)
|
|
100
|
+
File.open(target_path, 'w') { |file| file.write content }
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
private
|
|
105
|
+
|
|
106
|
+
def establish_directory(full_path)
|
|
107
|
+
return if File.exists?(full_path)
|
|
108
|
+
parent_path = File.dirname(full_path)
|
|
109
|
+
while (!File.exists?(parent_path))
|
|
110
|
+
establish_directory(parent_path)
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
@logger.creating_directory(full_path)
|
|
114
|
+
Dir.mkdir(full_path)
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def replace_tokens(content, tokens)
|
|
118
|
+
return content.gsub(/!-(\w+)-!/) do |value|
|
|
119
|
+
token_name = value[2...-2]
|
|
120
|
+
token = tokens[token_name] || tokens[token_name.to_sym] || "UNKNOWN TOKEN"
|
|
121
|
+
token
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
end
|
|
128
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
module Limelight
|
|
2
|
+
module Templates
|
|
3
|
+
|
|
4
|
+
# Templaters uses this class to log activity.
|
|
5
|
+
#
|
|
6
|
+
class TemplaterLogger
|
|
7
|
+
|
|
8
|
+
# An accessor to the output IO. Defaults to STDOUT
|
|
9
|
+
#
|
|
10
|
+
attr_accessor :output
|
|
11
|
+
|
|
12
|
+
def initialize
|
|
13
|
+
@output = STDOUT
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def creating_directory(name)
|
|
17
|
+
@output.puts "\tcreating directory: #{name}"
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def creating_file(name)
|
|
21
|
+
@output.puts "\tcreating file: #{name}"
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def file_already_exists(name)
|
|
25
|
+
@output.puts "\tfile already exists: #{name}"
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def log(message, name)
|
|
29
|
+
spaces = 21 - message.length - 1
|
|
30
|
+
@output.puts "\t#{message}:#{' '*spaces}#{name}"
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + "/../spec_helper")
|
|
2
|
+
require 'hangman_tournament/submit'
|
|
3
|
+
|
|
4
|
+
describe HangmanTournament::Submit do
|
|
5
|
+
|
|
6
|
+
before(:all) do
|
|
7
|
+
ENV['GEM_PATH'] = '/opt/local/lib/ruby/gems/1.8'
|
|
8
|
+
Gem.clear_paths
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
before(:each) do
|
|
12
|
+
@submit = HangmanTournament::Submit.new("letter_letdown")
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
it "should read a gem file" do
|
|
16
|
+
result = @submit.collect_data
|
|
17
|
+
|
|
18
|
+
result[:name].should == "Letter Letdown"
|
|
19
|
+
result[:author].should == "Micah Martin"
|
|
20
|
+
result[:email].should == "micah@8thlight.com"
|
|
21
|
+
result[:description].should == "A very simple player."
|
|
22
|
+
result[:gem_file_name].should == "letter_letdown-1.0.gem"
|
|
23
|
+
result[:gem_content].should == IO.read("/opt/local/lib/ruby/gems/1.8/cache/letter_letdown-1.0.gem")
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
it "should get password" do
|
|
27
|
+
$stdout.should_receive(:puts).with("Password?")
|
|
28
|
+
$stdout.should_receive(:print).with("> ")
|
|
29
|
+
$stdin.should_receive(:readline).and_return("blah")
|
|
30
|
+
|
|
31
|
+
password = @submit.get_password
|
|
32
|
+
|
|
33
|
+
password.should == "blah"
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + "/../../spec_helper")
|
|
2
|
+
require 'limelight/templates/player_templater'
|
|
3
|
+
|
|
4
|
+
describe Limelight::Templates::PlayerTemplater do
|
|
5
|
+
|
|
6
|
+
it "should know names" do
|
|
7
|
+
templater = Limelight::Templates::PlayerTemplater.new("Simple Bot")
|
|
8
|
+
|
|
9
|
+
templater.filename.should == "simple_bot"
|
|
10
|
+
templater.tokens[:CLASSNAME].should == "SimpleBot"
|
|
11
|
+
templater.tokens[:PLAYER_NAME].should == "Simple Bot"
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
it "should know names with a little punctuation" do
|
|
15
|
+
templater = Limelight::Templates::PlayerTemplater.new("Satan's Revenge!")
|
|
16
|
+
|
|
17
|
+
templater.filename.should == "satans_revenge"
|
|
18
|
+
templater.tokens[:CLASSNAME].should == "SatansRevenge"
|
|
19
|
+
templater.tokens[:PLAYER_NAME].should == "Satan's Revenge!"
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
it "should know names with a lot of punctuation" do
|
|
23
|
+
templater = Limelight::Templates::PlayerTemplater.new("Bl'ah? *Bl-!ah.;")
|
|
24
|
+
|
|
25
|
+
templater.filename.should == "blah_blah"
|
|
26
|
+
templater.tokens[:CLASSNAME].should == "BlahBlah"
|
|
27
|
+
templater.tokens[:PLAYER_NAME].should == "Bl'ah? *Bl-!ah.;"
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
it "should generate files" do
|
|
31
|
+
templater = Limelight::Templates::PlayerTemplater.new("Simple Bot")
|
|
32
|
+
|
|
33
|
+
templater.should_receive(:file).with("simple_bot/Rakefile", "Rakefile.template", anything)
|
|
34
|
+
templater.should_receive(:file).with("simple_bot/Hangman.Rakefile", "Hangman.Rakefile.template", anything)
|
|
35
|
+
templater.should_receive(:file).with("simple_bot/lib/simple_bot/simple_bot.rb", "player.template", anything)
|
|
36
|
+
templater.should_receive(:file).with("simple_bot/spec/spec_helper.rb", "spec_helper.template", anything)
|
|
37
|
+
templater.should_receive(:file).with("simple_bot/spec/simple_bot/simple_bot_spec.rb", "player_spec.template", anything)
|
|
38
|
+
|
|
39
|
+
templater.generate
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: hangman_tournament
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: "1.0"
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Micah Martin
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
|
|
12
|
+
date: 2009-08-17 00:00:00 -05:00
|
|
13
|
+
default_executable:
|
|
14
|
+
dependencies:
|
|
15
|
+
- !ruby/object:Gem::Dependency
|
|
16
|
+
name: letter_letdown
|
|
17
|
+
type: :runtime
|
|
18
|
+
version_requirement:
|
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
20
|
+
requirements:
|
|
21
|
+
- - ">="
|
|
22
|
+
- !ruby/object:Gem::Version
|
|
23
|
+
version: "1.0"
|
|
24
|
+
version:
|
|
25
|
+
description: This gem provides the infrastructure needed to participate in the Hangman Tournament
|
|
26
|
+
email: sparring-hangman@rubyforge.org
|
|
27
|
+
executables:
|
|
28
|
+
- generate_hangman_player
|
|
29
|
+
- submit_hangman_player
|
|
30
|
+
extensions: []
|
|
31
|
+
|
|
32
|
+
extra_rdoc_files: []
|
|
33
|
+
|
|
34
|
+
files:
|
|
35
|
+
- lib/hangman_tournament
|
|
36
|
+
- lib/hangman_tournament/submit.rb
|
|
37
|
+
- lib/init.rb
|
|
38
|
+
- lib/limelight
|
|
39
|
+
- lib/limelight/string.rb
|
|
40
|
+
- lib/limelight/templates
|
|
41
|
+
- lib/limelight/templates/player_templater.rb
|
|
42
|
+
- lib/limelight/templates/sources
|
|
43
|
+
- lib/limelight/templates/sources/bs_player.template
|
|
44
|
+
- lib/limelight/templates/sources/Hangman.Rakefile.template
|
|
45
|
+
- lib/limelight/templates/sources/player.template
|
|
46
|
+
- lib/limelight/templates/sources/player_spec.template
|
|
47
|
+
- lib/limelight/templates/sources/Rakefile.template
|
|
48
|
+
- lib/limelight/templates/sources/spec_helper.template
|
|
49
|
+
- lib/limelight/templates/templater.rb
|
|
50
|
+
- lib/limelight/templates/templater_logger.rb
|
|
51
|
+
- spec/hangman_tournament
|
|
52
|
+
- spec/hangman_tournament/submit_spec.rb
|
|
53
|
+
- spec/limelight
|
|
54
|
+
- spec/limelight/templates
|
|
55
|
+
- spec/limelight/templates/player_templater_spec.rb
|
|
56
|
+
- spec/spec_helper.rb
|
|
57
|
+
- bin/generate_hangman_player
|
|
58
|
+
- bin/submit_hangman_player
|
|
59
|
+
- CHANGES
|
|
60
|
+
- Rakefile
|
|
61
|
+
has_rdoc: false
|
|
62
|
+
homepage: http://sparring.rubyforge.org/
|
|
63
|
+
post_install_message:
|
|
64
|
+
rdoc_options: []
|
|
65
|
+
|
|
66
|
+
require_paths:
|
|
67
|
+
- lib
|
|
68
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
69
|
+
requirements:
|
|
70
|
+
- - ">="
|
|
71
|
+
- !ruby/object:Gem::Version
|
|
72
|
+
version: "0"
|
|
73
|
+
version:
|
|
74
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
75
|
+
requirements:
|
|
76
|
+
- - ">="
|
|
77
|
+
- !ruby/object:Gem::Version
|
|
78
|
+
version: "0"
|
|
79
|
+
version:
|
|
80
|
+
requirements: []
|
|
81
|
+
|
|
82
|
+
rubyforge_project: sparring
|
|
83
|
+
rubygems_version: 1.3.1
|
|
84
|
+
signing_key:
|
|
85
|
+
specification_version: 2
|
|
86
|
+
summary: Hangman tournament.
|
|
87
|
+
test_files: []
|
|
88
|
+
|