uss_monte_carlo 2.0.2

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.
@@ -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
+