uss_monte_carlo 2.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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,test,shoulda', '--no-html']
12
+ end
data/Rakefile ADDED
@@ -0,0 +1,47 @@
1
+ require 'rake'
2
+ require 'rake/gempackagetask'
3
+ require 'spec/rake/spectask'
4
+ require 'battleship_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 = "uss_monte_carlo"
13
+ PKG_VERSION = "2.0.2"
14
+
15
+ spec = Gem::Specification.new do |s|
16
+ s.name = PKG_NAME
17
+ s.version = PKG_VERSION
18
+ s.files = FileList['**/*'].to_a.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 = "Battleship Player:USS Monte Carlo"
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 = "Flagship of the Monaco Navy"
34
+ s.author = "Joshua Ballanco"
35
+ s.email = "jballanc@gmail.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 = BattleshipTournament::Submit.new(PKG_NAME)
46
+ submitter.submit
47
+ end
@@ -0,0 +1,12 @@
1
+ SHIPS = [:carrier, :battleship, :destroyer, :submarine, :patrolship]
2
+ SHIP_SIZES = {:carrier => 5, :battleship => 4, :destroyer => 3, :submarine => 3, :patrolship => 2}
3
+ ROWS = %w(A B C D E F G H I J)
4
+ COLUMNS = %w(1 2 3 4 5 6 7 8 9 10)
5
+ ORIENTATIONS = %w(horizontal vertical)
6
+ WIN_RESULT = :victory
7
+ LOOSE_RESULT = :defeat
8
+ DQ_RESULT = :disqualified
9
+ GRID_DIMENSIONS = [ROWS.length, COLUMNS.length]
10
+ GRID_SIZE = ROWS.length
11
+
12
+ class GunnerError < RuntimeError; end
@@ -0,0 +1,107 @@
1
+ require 'uss_monte_carlo/game_constants'
2
+
3
+ class HomingGunner
4
+
5
+ def initialize(targets_remaining, hit_location, shots_fired)
6
+ @smallest_ship = targets_remaining.inject(5) { |min, s| SHIP_SIZES[s] < min ? SHIP_SIZES[s] : min }
7
+ @original_hit = @shot = hit_location
8
+ @shots_fired = shots_fired
9
+ @hits = [hit_location]
10
+ pick_direction_and_orientation
11
+ end
12
+
13
+ def next_shot
14
+ raise GunnerError.new("All shots have been taken: #{@shots_fired}") if @shots_fired.length >= (ROWS.length * COLUMNS.length)
15
+ candidate_shot = move_from(@shot)
16
+ if candidate_shot == @shot
17
+ self.send next_strategy
18
+ candidate_shot = move_from(@shot)
19
+ end
20
+ @shot = candidate_shot
21
+
22
+ while @shots_fired.include? @shot
23
+ @shot = @original_hit
24
+ self.send next_strategy
25
+ @shot = move_from(@shot)
26
+ end
27
+ @shot
28
+ end
29
+
30
+ def shot_result(coordinates, was_hit, ship_sunk)
31
+ @shots_fired << coordinates unless @shots_fired.include? coordinates
32
+ if ship_sunk
33
+ remove_hits(ship_sunk)
34
+ elsif was_hit
35
+ @hits << coordinates
36
+ else
37
+ @shot = @original_hit
38
+ self.send next_strategy
39
+ end
40
+ end
41
+
42
+ def unaccounted_hits
43
+ @hits
44
+ end
45
+
46
+
47
+ private
48
+
49
+ def pick_direction_and_orientation
50
+ @orientation = [ROWS, COLUMNS][rand(2)]
51
+ @direction ||= [:+, :-][rand(2)]
52
+
53
+ # Eliminate orientations that can't fit one of the remaining ships
54
+ check_pos = @shot
55
+ next_pos = move_from(check_pos)
56
+ while (check_pos != next_pos) && !(@shots_fired.include? next_pos)
57
+ check_pos = next_pos
58
+ next_pos = move_from(check_pos)
59
+ end
60
+ reverse_direction
61
+ gap_size = 0
62
+ next_pos = move_from(check_pos)
63
+ while (check_pos != next_pos) && !(@shots_fired.include? next_pos)
64
+ check_pos = next_pos
65
+ next_pos = move_from(check_pos)
66
+ gap_size += 1
67
+ end
68
+ reverse_direction
69
+ reverse_orientation if gap_size < @smallest_ship
70
+ end
71
+
72
+ def remove_hits(ship_sunk)
73
+ reverse_direction
74
+ shot_to_remove = @shot
75
+ SHIP_SIZES[ship_sunk].times do
76
+ @hits -= [shot_to_remove]
77
+ shot_to_remove = move_from(shot_to_remove)
78
+ end
79
+ end
80
+
81
+ def reverse_direction
82
+ @direction = @direction == :+ ? :- : :+
83
+ end
84
+
85
+ def reverse_orientation
86
+ @orientation = @orientation == ROWS ? COLUMNS : ROWS
87
+ end
88
+
89
+ def move_from(shot)
90
+ row, *column = shot.split('')
91
+ column = column.join
92
+ if @orientation == ROWS
93
+ index = ROWS.index(row).send(@direction, 1)
94
+ row = ROWS[index] if (0 <= index) && (index < ROWS.length)
95
+ else
96
+ index = COLUMNS.index(column).send(@direction, 1)
97
+ column = COLUMNS[index] if (0 <= index) && (index < COLUMNS.length)
98
+ end
99
+ "#{row}#{column}"
100
+ end
101
+
102
+ def next_strategy
103
+ @strategy ||= [:reverse_orientation, :reverse_direction]
104
+ (@strategy << @strategy.shift)[0]
105
+ end
106
+
107
+ end
@@ -0,0 +1,39 @@
1
+ require 'uss_monte_carlo/game_constants'
2
+
3
+ class RandomGunner
4
+
5
+ def initialize(targets_remaining, shots_fired)
6
+ @targets_remaining = targets_remaining
7
+ @shots_fired = shots_fired
8
+ @shot = @shots_fired.last || ROWS[rand(ROWS.length)] + COLUMNS[rand(COLUMNS.length)]
9
+ end
10
+
11
+ def next_shot
12
+ raise GunnerError.new("All shots have been taken: #{@shots_fired}") if @shots_fired.length >= (ROWS.length * COLUMNS.length)
13
+ @shot = random_coord
14
+ while @shots_fired.include? @shot
15
+ @shot = random_coord
16
+ end
17
+ @shot
18
+ end
19
+
20
+ def shot_result(coordinates, was_hit, ship_sunk)
21
+ @shots_fired << coordinates unless @shots_fired.include? coordinates
22
+ end
23
+
24
+ private
25
+
26
+ def random_coord
27
+ last_row, *last_column = @shot.split('')
28
+ last_column = last_column.join
29
+ row_num = ( ROWS.index(last_row) + random_offset ) % GRID_SIZE
30
+ column_num = ( COLUMNS.index(last_column) + random_offset ) % GRID_SIZE
31
+ "#{ROWS[row_num]}#{COLUMNS[column_num]}"
32
+ end
33
+
34
+ def random_offset
35
+ random_ship = @targets_remaining[rand(@targets_remaining.length)]
36
+ [SHIP_SIZES[random_ship], 1][rand(2)]
37
+ end
38
+
39
+ end
@@ -0,0 +1,232 @@
1
+ module UssMonteCarlo
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
+ require 'uss_monte_carlo/game_constants'
17
+ require 'uss_monte_carlo/random_gunner'
18
+ require 'uss_monte_carlo/homing_gunner'
19
+
20
+ class UssMonteCarlo
21
+
22
+ # This method is called at the beginning of each game. A player may only be instantiated once and used to play many games.
23
+ # So new_game should reset any internal state acquired in previous games so that it is prepared for a new game.
24
+ #
25
+ # The name of the opponent player is passed in. This allows for the possibility to learn opponent strategy and
26
+ # play the game differently based on the opponent.
27
+ #
28
+ def new_game(opponent_name)
29
+ reset
30
+ @opponent = opponent_name
31
+ end
32
+
33
+ # Returns the placement of the carrier. A carrier consumes 5 squares.
34
+ #
35
+ # The return value is a string that describes the placements of the ship.
36
+ # The placement string must be in the following format:
37
+ #
38
+ # "#{ROW}#{COL} #{ORIENTATION}"
39
+ #
40
+ # eg
41
+ #
42
+ # A1 horizontal # the ship will occupy A1, A2, A3, A4, and A5
43
+ # A1 vertical # the ship will occupy A1, B1, C1, D1, and E1
44
+ # F5 horizontal # the ship will occupy F5, F6, F7, F8, and F9
45
+ # F5 vertical # the ship will occupy F5, G5, H5, I5, and J5
46
+ #
47
+ # The ship must not fall off the edge of the map. For example, a carrier placement of 'A8 horizontal' would
48
+ # not leave enough space in the A row to accomidate the carrier since it requires 5 squares.
49
+ #
50
+ # Ships may not overlap with other ships. For example a carrier placement of 'A1 horizontal' and a submarine
51
+ # placement of 'A1 vertical' would be invalid because bothe ships are trying to occupy the square A1.
52
+ #
53
+ # Invalid ship placements will result in disqualification of the player.
54
+ #
55
+ def carrier_placement
56
+ @ship_placements ||= place_ships
57
+ @ship_placements[:carrier]
58
+ end
59
+
60
+ # Returns the placement of the battleship. A battleship consumes 4 squares.
61
+ #
62
+ # See carrier_placement for details on ship placement
63
+ #
64
+ def battleship_placement
65
+ @ship_placements ||= place_ships
66
+ @ship_placements[:battleship]
67
+ end
68
+
69
+ # Returns the placement of the destroyer. A destroyer consumes 3 squares.
70
+ #
71
+ # See carrier_placement for details on ship placement
72
+ #
73
+ def destroyer_placement
74
+ @ship_placements ||= place_ships
75
+ @ship_placements[:destroyer]
76
+ end
77
+
78
+ # Returns the placement of the submarine. A submarine consumes 3 squares.
79
+ #
80
+ # See carrier_placement for details on ship placement
81
+ #
82
+ def submarine_placement
83
+ @ship_placements ||= place_ships
84
+ @ship_placements[:submarine]
85
+ end
86
+
87
+ # Returns the placement of the patrolship. A patrolship consumes 2 squares.
88
+ #
89
+ # See carrier_placement for details on ship placement
90
+ #
91
+ def patrolship_placement
92
+ @ship_placements ||= place_ships
93
+ @ship_placements[:patrolship]
94
+ end
95
+
96
+ # Returns the coordinates of the players next target. This method will be called once per turn. The player
97
+ # should return target coordinates as a string in the form of:
98
+ #
99
+ # "#{ROW}#{COL}"
100
+ #
101
+ # eg
102
+ #
103
+ # A1 # the square in Row A and Column 1
104
+ # F5 # the square in Row F and Column 5
105
+ #
106
+ # 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
107
+ # COL should be 1, 2, 3, 4, 5, 6, 7, 8, 9, or 10
108
+ #
109
+ # Returning coordinates outside the range or in an invalid format will result in the players disqualification.
110
+ #
111
+ # It is illegal to target a sector more than once. Doing so will also result in disqualification.
112
+ #
113
+ def next_target
114
+ target = @current_gunner.next_shot
115
+ @shots_fired << target
116
+ return target
117
+ end
118
+
119
+ # target_result will be called by the system after a call to next_target. The paramters supplied inform the player
120
+ # of the results of the target.
121
+ #
122
+ # coordinates : string. The coordinates targeted. It will be the same value returned by the previous call to next_target
123
+ # was_hit : boolean. true if the target was occupied by a ship. false otherwise.
124
+ # ship_sunk : symbol. nil if the target did not result in the sinking of a ship. If the target did result in
125
+ # in the sinking of a ship, the ship type is supplied (:carrier, :battleship, :destroyer, :submarine, :patrolship).
126
+ #
127
+ # An intelligent player will use the information to better play the game. For example, if the result indicates a
128
+ # hit, a player my choose to target neighboring squares to hit and sink the remainder of the ship.
129
+ #
130
+ def target_result(coordinates, was_hit, ship_sunk)
131
+ @current_gunner.shot_result(coordinates, was_hit, ship_sunk)
132
+ if ship_sunk
133
+ @targets_remaining -= [ship_sunk]
134
+ @unaccounted_hits += @current_gunner.unaccounted_hits
135
+
136
+ # Protect against an infinite loop
137
+ @unaccounted_hits.uniq!
138
+
139
+ if @unaccounted_hits.empty?
140
+ @current_gunner = RandomGunner.new(@targets_remaining, @shots_fired)
141
+ else
142
+ @current_gunner = HomingGunner.new(@targets_remaining, @unaccounted_hits.shift, @shots_fired)
143
+ end
144
+ elsif was_hit && !@current_gunner.kind_of?(HomingGunner)
145
+ @current_gunner = HomingGunner.new(@targets_remaining, coordinates, @shots_fired)
146
+ end
147
+ end
148
+
149
+ # enemy_targeting is called by the system to inform a player of their apponents move. When the opponent targets
150
+ # a square, this method is called with the coordinates.
151
+ #
152
+ # Players may use this information to understand an opponents targeting strategy and place ships differently
153
+ # in subsequent games.
154
+ #
155
+ def enemy_targeting(coordinates)
156
+ end
157
+
158
+ # Called by the system at the end of a game to inform the player of the results.
159
+ #
160
+ # result : 1 of 3 possible values (:victory, :defeate, :disqualified)
161
+ # disqualification_reason : nil unless the game ended as the result of a disqualification. In the event of a
162
+ # disqualification, this paramter will hold a string description of the reason for disqualification. Both
163
+ # players will be informed of the reason.
164
+ #
165
+ # :victory # indicates the player won the game
166
+ # :defeat # indicates the player lost the game
167
+ # :disqualified # indicates the player was disqualified
168
+ #
169
+ def game_over(result, disqualification_reason=nil)
170
+ @result = result
171
+ @disqualification_reason = disqualification_reason
172
+ end
173
+
174
+ # Non API methods #####################################
175
+
176
+ attr_reader :opponent, :result, :targets_remaining, :disqualification_reason, :enemy_targeted_sectors
177
+
178
+ def initialize #:nodoc:
179
+ reset
180
+ end
181
+
182
+ private ###############################################
183
+
184
+ def reset
185
+ @shots_fired = []
186
+ @unaccounted_hits = []
187
+ @targets_remaining = SHIPS
188
+ @current_gunner = RandomGunner.new(@targets_remaining, @shots_fired)
189
+
190
+ @enemy_targeted_sectors = []
191
+ @result = nil
192
+ @disqualification_reason = nil
193
+ end
194
+
195
+ def place_ships
196
+ placements = {}
197
+ occupied_squares = []
198
+ SHIPS.each do |ship|
199
+ if rand(2) == 0
200
+ first_coord, second_coord = ROWS, COLUMNS
201
+ else
202
+ second_coord, first_coord = ROWS, COLUMNS
203
+ end
204
+ first_coord_index = rand(first_coord.length)
205
+ second_coord_index = rand(second_coord.length - SHIP_SIZES[ship])
206
+
207
+ placed_coords = []
208
+ if first_coord == COLUMNS
209
+ SHIP_SIZES[ship].times{|n| placed_coords += ["#{second_coord[second_coord_index + n]}#{first_coord[first_coord_index]}"]}
210
+ first_coord_index, second_coord_index = second_coord_index, first_coord_index
211
+ first_coord, second_coord = second_coord, first_coord
212
+ direction = "vertical"
213
+ else
214
+ SHIP_SIZES[ship].times{|n| placed_coords += ["#{first_coord[first_coord_index]}#{second_coord[second_coord_index + n]}"]}
215
+ direction = "horizontal"
216
+ end
217
+
218
+ overlap = false
219
+ placed_coords.each{|coord| overlap ||= occupied_squares.include? coord}
220
+ if overlap
221
+ redo
222
+ else
223
+ occupied_squares += placed_coords
224
+ placements[ship] = "#{first_coord[first_coord_index]}#{second_coord[second_coord_index]} #{direction}"
225
+ end
226
+ end
227
+ placements
228
+ end
229
+
230
+ end
231
+
232
+ end
@@ -0,0 +1,16 @@
1
+ $: << File.expand_path(File.dirname(__FILE__) + "/../lib")
2
+
3
+ require 'rubygems'
4
+ require 'minitest/unit'
5
+ require 'uss_monte_carlo/game_constants'
6
+
7
+ def valid_placements
8
+ # Construct list of valid placements
9
+ @list ||= ROWS.map{|r| COLUMNS.map{|c| ORIENTATIONS.map{|o| r + c + ' ' + o }}}.flatten
10
+ end
11
+
12
+ def grid_coords
13
+ # Construct list of valid coordinates
14
+ @coords ||= ROWS.map{|r| COLUMNS.map{|c| r + c }}.flatten
15
+ end
16
+
@@ -0,0 +1,31 @@
1
+ require File.expand_path(File.dirname(__FILE__) + "/../spec_helper")
2
+ require 'uss_monte_carlo/uss_monte_carlo'
3
+ require 'uss_monte_carlo/game_constants'
4
+ require 'uss_monte_carlo/homing_gunner'
5
+
6
+
7
+ class HomingGunnerTest < MiniTest::Unit::TestCase
8
+
9
+ def setup
10
+ @gunner = HomingGunner.new(SHIPS, "E5", ["E5"])
11
+ end
12
+
13
+ def test_search
14
+ adjacent_coords = %w(E4 E6 D5 F5)
15
+
16
+ # Fire a shot and make sure it was adjacent
17
+ shot = @gunner.next_shot
18
+ assert_includes(adjacent_coords, shot)
19
+
20
+ # Signal a miss, and check that the search continues
21
+ 3.times do
22
+ @gunner.shot_result(shot, false, false)
23
+ adjacent_coords -= [shot]
24
+ shot = @gunner.next_shot
25
+ assert_includes(adjacent_coords, shot)
26
+ end
27
+ end
28
+
29
+ end
30
+
31
+ MiniTest::Unit.autorun
@@ -0,0 +1,31 @@
1
+ require File.expand_path(File.dirname(__FILE__) + "/../spec_helper")
2
+ require 'uss_monte_carlo/uss_monte_carlo'
3
+ require 'uss_monte_carlo/game_constants'
4
+ require 'uss_monte_carlo/random_gunner'
5
+
6
+ class RandomGunnerTests < MiniTest::Unit::TestCase
7
+
8
+ def setup
9
+ @gunner = RandomGunner.new(SHIPS, [])
10
+ end
11
+
12
+ def test_initialization
13
+ refute_nil @gunner
14
+ end
15
+
16
+ def test_valid_shots
17
+ # Make sure we're returning valid coordinates
18
+ assert_includes(grid_coords, @gunner.next_shot)
19
+
20
+ # Make sure that we're not overlapping with pre-existing shots
21
+ one_shot_gunner = RandomGunner.new(SHIPS, grid_coords[0..-2])
22
+ assert_equal(grid_coords[-1], one_shot_gunner.next_shot)
23
+
24
+ # Make sure we know when we're out of shots to take
25
+ one_shot_gunner.shot_result(grid_coords[-1], false, false)
26
+ assert_raises(GunnerError) { one_shot_gunner.next_shot }
27
+ end
28
+
29
+ end
30
+
31
+ MiniTest::Unit.autorun
@@ -0,0 +1,72 @@
1
+ require File.expand_path(File.dirname(__FILE__) + "/../spec_helper")
2
+ require 'uss_monte_carlo/uss_monte_carlo'
3
+ require 'uss_monte_carlo/game_constants'
4
+
5
+ OPPONENT_NAME = "Test @player"
6
+ DQ_REASON = "Cheater!"
7
+
8
+ class UssMonteCarloTests < MiniTest::Unit::TestCase
9
+ def setup
10
+ # Create @player, start game
11
+ @player = UssMonteCarlo::UssMonteCarlo.new
12
+ @player.new_game(OPPONENT_NAME)
13
+ end
14
+
15
+
16
+ def test_initialization
17
+ #Test successful creation
18
+ refute_nil @player
19
+ end
20
+
21
+
22
+ def test_startup
23
+ #Test new game parameters
24
+ assert_equal(OPPONENT_NAME, @player.opponent)
25
+ assert_equal(SHIPS, @player.targets_remaining)
26
+ assert_equal([], @player.enemy_targeted_sectors)
27
+ assert_nil(@player.result)
28
+ assert_nil(@player.disqualification_reason)
29
+ end
30
+
31
+ def test_restart
32
+ # Win game and check results
33
+ @player.game_over(WIN_RESULT)
34
+ assert_equal(WIN_RESULT, @player.result)
35
+ assert_nil(@player.disqualification_reason)
36
+
37
+ # Check game reset
38
+ @player.new_game(OPPONENT_NAME)
39
+ assert_nil(@player.result)
40
+ assert_nil(@player.disqualification_reason)
41
+
42
+ # Loose game and check results
43
+ @player.game_over(LOOSE_RESULT)
44
+ assert_equal(LOOSE_RESULT, @player.result)
45
+ assert_nil(@player.disqualification_reason)
46
+
47
+ # Reset, DQ, and check results
48
+ @player.new_game(OPPONENT_NAME)
49
+ @player.game_over(DQ_RESULT, DQ_REASON)
50
+ assert_equal(DQ_RESULT, @player.result)
51
+ assert_equal(DQ_REASON, @player.disqualification_reason)
52
+ end
53
+
54
+ def test_valid_placement
55
+ assert_includes(valid_placements, @player.carrier_placement)
56
+ assert_includes(valid_placements, @player.battleship_placement)
57
+ assert_includes(valid_placements, @player.destroyer_placement)
58
+ assert_includes(valid_placements, @player.submarine_placement)
59
+ assert_includes(valid_placements, @player.patrolship_placement)
60
+ end
61
+
62
+ def test_firing_sequence
63
+ 5.times do
64
+ assert_includes(grid_coords, @player.next_target)
65
+ assert_respond_to(@player, :target_result)
66
+ assert_respond_to(@player, :enemy_targeting)
67
+ end
68
+ end
69
+
70
+ end
71
+
72
+ MiniTest::Unit.autorun
metadata ADDED
@@ -0,0 +1,66 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: uss_monte_carlo
3
+ version: !ruby/object:Gem::Version
4
+ version: 2.0.2
5
+ platform: ruby
6
+ authors:
7
+ - Joshua Ballanco
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2008-12-01 00:00:00 -05:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: Flagship of the Monaco Navy
17
+ email: jballanc@gmail.com
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files: []
23
+
24
+ files:
25
+ - Battleship.Rakefile
26
+ - lib
27
+ - lib/uss_monte_carlo
28
+ - lib/uss_monte_carlo/game_constants.rb
29
+ - lib/uss_monte_carlo/homing_gunner.rb
30
+ - lib/uss_monte_carlo/random_gunner.rb
31
+ - lib/uss_monte_carlo/uss_monte_carlo.rb
32
+ - Rakefile
33
+ - spec
34
+ - spec/spec_helper.rb
35
+ - spec/uss_monte_carlo
36
+ - spec/uss_monte_carlo/homing_gunner_spec.rb
37
+ - spec/uss_monte_carlo/random_gunner_spec.rb
38
+ - spec/uss_monte_carlo/uss_monte_carlo_spec.rb
39
+ has_rdoc: false
40
+ homepage: http://sparring.rubyforge.org/
41
+ post_install_message:
42
+ rdoc_options: []
43
+
44
+ require_paths:
45
+ - lib
46
+ required_ruby_version: !ruby/object:Gem::Requirement
47
+ requirements:
48
+ - - ">="
49
+ - !ruby/object:Gem::Version
50
+ version: "0"
51
+ version:
52
+ required_rubygems_version: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: "0"
57
+ version:
58
+ requirements: []
59
+
60
+ rubyforge_project: sparring
61
+ rubygems_version: 1.3.1
62
+ signing_key:
63
+ specification_version: 2
64
+ summary: Battleship Player:USS Monte Carlo
65
+ test_files: []
66
+