hangman_tournament 1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|