joshua_son_of_nun 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/Battleship.Rakefile +12 -0
- data/README.rdoc +15 -0
- data/Rakefile +54 -0
- data/game_simulator.rb +151 -0
- data/init.rb +13 -0
- data/lib/joshua_son_of_nun/board.rb +39 -0
- data/lib/joshua_son_of_nun/joshua_son_of_nun.rb +5 -0
- data/lib/joshua_son_of_nun/player.rb +54 -0
- data/lib/joshua_son_of_nun/ship.rb +38 -0
- data/lib/joshua_son_of_nun/space.rb +172 -0
- data/lib/joshua_son_of_nun/strategy/base.rb +47 -0
- data/lib/joshua_son_of_nun/strategy/diagonal.rb +19 -0
- data/lib/joshua_son_of_nun/strategy/knight.rb +17 -0
- data/lib/joshua_son_of_nun/strategy/random.rb +14 -0
- data/lib/joshua_son_of_nun/strategy/targeting_reaction.rb +34 -0
- data/spec/joshua_son_of_nun/board_spec.rb +43 -0
- data/spec/joshua_son_of_nun/player_spec.rb +22 -0
- data/spec/joshua_son_of_nun/ship_spec.rb +17 -0
- data/spec/joshua_son_of_nun/space_spec.rb +73 -0
- data/spec/joshua_son_of_nun/strategy_spec.rb +110 -0
- data/spec/spec.opts +7 -0
- data/spec/spec_helper.rb +22 -0
- metadata +80 -0
data/Battleship.Rakefile
ADDED
@@ -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', '--no-html']
|
12
|
+
end
|
data/README.rdoc
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
== Joshua, Son of Nun
|
2
|
+
|
3
|
+
This is my entry for the ruby battleship sparring tournament (http://sparring.rubyforge.org/battleship/index.html).
|
4
|
+
|
5
|
+
I also built a game simulator so testing of my player didn't have to depend on LimeLight. You can run 100 games like so:
|
6
|
+
|
7
|
+
ruby game_simulator.rb 100
|
8
|
+
|
9
|
+
You can also specify which strategy you would like each player to have. So, if you want to run 200 games where player 1 uses the Random strategy and player 2 uses the Knight strategy, you would use the following command:
|
10
|
+
|
11
|
+
ruby game_simulator.rb Random Knight 200
|
12
|
+
|
13
|
+
== License
|
14
|
+
|
15
|
+
This is mine. Don't steal it! :-)
|
data/Rakefile
ADDED
@@ -0,0 +1,54 @@
|
|
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.spec_opts = ['--options', 'spec/spec.opts']
|
10
|
+
t.rcov = false
|
11
|
+
end
|
12
|
+
task :default => :spec
|
13
|
+
|
14
|
+
PKG_NAME = "joshua_son_of_nun"
|
15
|
+
PKG_VERSION = "1.0"
|
16
|
+
|
17
|
+
spec = Gem::Specification.new do |s|
|
18
|
+
s.name = PKG_NAME
|
19
|
+
s.version = PKG_VERSION
|
20
|
+
s.files = FileList['**/*'].to_a.reject!{ |f| f =~ /report/ }
|
21
|
+
s.require_path = 'lib'
|
22
|
+
s.test_files = Dir.glob('spec/*_spec.rb')
|
23
|
+
s.bindir = 'bin'
|
24
|
+
s.executables = []
|
25
|
+
s.summary = "Battleship Player:joshua_son_of_nun"
|
26
|
+
s.rubyforge_project = "sparring"
|
27
|
+
s.homepage = "http://sparring.rubyforge.org/"
|
28
|
+
|
29
|
+
###########################################
|
30
|
+
##
|
31
|
+
## You are encouraged to modify the following
|
32
|
+
## spec attributes.
|
33
|
+
##
|
34
|
+
###########################################
|
35
|
+
s.description = "(using scary WWF wrestler voice) Just like the walls of Jericho, the same will happen to all battleship opponents of Joshua, son of Nun!!"
|
36
|
+
s.author = "Steve Iannopollo"
|
37
|
+
s.email = "steve@iannopollo.com"
|
38
|
+
end
|
39
|
+
|
40
|
+
Rake::GemPackageTask.new(spec) do |pkg|
|
41
|
+
pkg.need_zip = false
|
42
|
+
pkg.need_tar = false
|
43
|
+
end
|
44
|
+
|
45
|
+
desc "Submit your player"
|
46
|
+
task :submit do
|
47
|
+
submitter = BattleshipTournament::Submit.new(PKG_NAME)
|
48
|
+
submitter.submit
|
49
|
+
end
|
50
|
+
|
51
|
+
desc 'Locally build the gem'
|
52
|
+
task :make_gem do
|
53
|
+
`cd #{File.expand_path(File.dirname(__FILE__))} && rm -f pkg/joshua_son_of_nun-1.0.gem && rake gem && sudo gem uninstall joshua_son_of_nun && sudo gem install pkg/joshua_son_of_nun-1.0.gem`
|
54
|
+
end
|
data/game_simulator.rb
ADDED
@@ -0,0 +1,151 @@
|
|
1
|
+
require 'init'
|
2
|
+
|
3
|
+
OLD_STRATEGIES = JoshuaSonOfNun::Strategy.strategies.dup
|
4
|
+
JoshuaSonOfNun::Strategy.module_eval do
|
5
|
+
class << self
|
6
|
+
attr_accessor :strategies
|
7
|
+
end
|
8
|
+
end
|
9
|
+
JoshuaSonOfNun::Strategy.strategies = OLD_STRATEGIES
|
10
|
+
|
11
|
+
def Space(string)
|
12
|
+
coordinates, orientation = string.split(' ')
|
13
|
+
row, column = coordinates.scan(/(\w)(\d{1,2})/).first
|
14
|
+
JoshuaSonOfNun::Space.new(row, column, orientation)
|
15
|
+
end
|
16
|
+
|
17
|
+
class GameSimulator
|
18
|
+
attr_reader :current_player, :game_over, :opponent,
|
19
|
+
:player_one_strategy, :player_two_strategy,
|
20
|
+
:player_one, :player_two, :players, :results,
|
21
|
+
:ship_placement, :ships
|
22
|
+
|
23
|
+
alias_method :game_over?, :game_over
|
24
|
+
|
25
|
+
def initialize(player_one_strategy, player_two_strategy)
|
26
|
+
@player_one_strategy, @player_two_strategy = player_one_strategy, player_two_strategy
|
27
|
+
@player_one = JoshuaSonOfNun::Player.new
|
28
|
+
@player_two = JoshuaSonOfNun::Player.new
|
29
|
+
tag_players!
|
30
|
+
|
31
|
+
@moves = []
|
32
|
+
@results = {@player_one.name => [], @player_two.name => []}
|
33
|
+
end
|
34
|
+
|
35
|
+
def determine_damage(target)
|
36
|
+
hit, sunk = nil
|
37
|
+
ship_placement[opponent.name].each do |ship, spaces|
|
38
|
+
space = spaces.delete(target)
|
39
|
+
if space
|
40
|
+
hit = true
|
41
|
+
sunk = spaces.empty?
|
42
|
+
break
|
43
|
+
end
|
44
|
+
end
|
45
|
+
@game_over = ship_placement[opponent.name].values.flatten.empty?
|
46
|
+
[hit, sunk]
|
47
|
+
end
|
48
|
+
|
49
|
+
def fire_on_opponent
|
50
|
+
target = current_player.next_target
|
51
|
+
ship_hit, ship_sunk = determine_damage(target)
|
52
|
+
|
53
|
+
current_player.target_result(target.to_s, ship_hit, ship_sunk)
|
54
|
+
opponent.enemy_targeting(target.to_s)
|
55
|
+
end
|
56
|
+
|
57
|
+
def force_strategy(strategy)
|
58
|
+
JoshuaSonOfNun::Strategy.strategies = [strategy]
|
59
|
+
end
|
60
|
+
|
61
|
+
def gather_ship_placement
|
62
|
+
players.each do |player|
|
63
|
+
placement = {}
|
64
|
+
ships.each do |ship|
|
65
|
+
placement[ship] = Space(player.send("#{ship}_placement")).spaces_for_placement(player.instance_variable_get("@#{ship}").length)
|
66
|
+
end
|
67
|
+
|
68
|
+
ship_placement[player.name] = placement
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def prepare_report
|
73
|
+
@filename = File.dirname(__FILE__) + "/reports/#{strategy_name(@player_one)}_vs_#{strategy_name(@player_two)}.csv"
|
74
|
+
`test -f #{@filename} && echo '' || echo '#{strategy_name(@player_one)},#{strategy_name(@player_two)},moves' > #{@filename}`
|
75
|
+
end
|
76
|
+
|
77
|
+
def report_results
|
78
|
+
result = (@current_player == @player_one ? '1,0,' : '0,1,') + (@moves.last/2.0).ceil.to_s
|
79
|
+
`echo '#{result}' >> #{@filename}`
|
80
|
+
print "\e[32m.\e[0m"; STDOUT.flush
|
81
|
+
end
|
82
|
+
|
83
|
+
def reset
|
84
|
+
@players = [@player_one, @player_two]
|
85
|
+
|
86
|
+
force_strategy(player_one_strategy) unless player_one_strategy.nil?
|
87
|
+
@player_one.new_game(@player_two.name)
|
88
|
+
force_strategy(player_two_strategy) unless player_two_strategy.nil?
|
89
|
+
@player_two.new_game(@player_one.name)
|
90
|
+
prepare_report
|
91
|
+
|
92
|
+
@ship_placement = {}
|
93
|
+
@ships = [:battleship, :carrier, :destroyer, :patrolship, :submarine]
|
94
|
+
gather_ship_placement
|
95
|
+
|
96
|
+
@moves << 0
|
97
|
+
@game_over = false
|
98
|
+
end
|
99
|
+
|
100
|
+
def run!
|
101
|
+
reset
|
102
|
+
|
103
|
+
until game_over?
|
104
|
+
switch_turns
|
105
|
+
fire_on_opponent
|
106
|
+
end
|
107
|
+
|
108
|
+
@results[@current_player.name] << strategy_name(@current_player)
|
109
|
+
report_results
|
110
|
+
end
|
111
|
+
|
112
|
+
def strategy_name(player)
|
113
|
+
player.instance_variable_get('@strategy').class.name.split("::").last
|
114
|
+
end
|
115
|
+
|
116
|
+
def summarized_results
|
117
|
+
summary = results.collect do |player, winning_strategies|
|
118
|
+
output = "#{player}:"
|
119
|
+
output << " #{winning_strategies.size} wins"
|
120
|
+
strategy_count = OLD_STRATEGIES.uniq.collect do |strategy|
|
121
|
+
count = winning_strategies.select {|s| s == strategy}.size
|
122
|
+
"#{strategy} => #{count}"
|
123
|
+
end
|
124
|
+
output << " (#{strategy_count * ', '})"
|
125
|
+
end.reverse * "\n"
|
126
|
+
average_moves = @moves.inject(0) {|sum, n| sum += n} / (2 * @moves.size.to_f)
|
127
|
+
summary << "\n#{average_moves} moves per game\n"
|
128
|
+
summary
|
129
|
+
end
|
130
|
+
|
131
|
+
def switch_turns
|
132
|
+
@current_player = @players.shift
|
133
|
+
@players << @current_player
|
134
|
+
@opponent = @players.first
|
135
|
+
@moves << @moves.pop + 1
|
136
|
+
end
|
137
|
+
|
138
|
+
def tag_players!
|
139
|
+
class << player_one
|
140
|
+
def name; 'player_one'; end
|
141
|
+
end
|
142
|
+
class << player_two
|
143
|
+
def name; 'player_two'; end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
number_of_games = (ARGV.pop || 21).to_i
|
149
|
+
simulator = GameSimulator.new(ARGV.shift, ARGV.shift)
|
150
|
+
number_of_games.times {simulator.run!}
|
151
|
+
puts '', simulator.summarized_results, ''
|
data/init.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
$: << File.expand_path(File.dirname(__FILE__) + '/lib')
|
2
|
+
|
3
|
+
require 'joshua_son_of_nun/ship'
|
4
|
+
require 'joshua_son_of_nun/board'
|
5
|
+
require 'joshua_son_of_nun/space'
|
6
|
+
|
7
|
+
require 'joshua_son_of_nun/strategy/base'
|
8
|
+
require 'joshua_son_of_nun/strategy/random'
|
9
|
+
require 'joshua_son_of_nun/strategy/diagonal'
|
10
|
+
require 'joshua_son_of_nun/strategy/knight'
|
11
|
+
require 'joshua_son_of_nun/strategy/targeting_reaction'
|
12
|
+
|
13
|
+
require 'joshua_son_of_nun/player'
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module JoshuaSonOfNun
|
2
|
+
class Board
|
3
|
+
ROWS = ('A'..'J').to_a
|
4
|
+
COLUMNS = ('1'..'10').to_a
|
5
|
+
|
6
|
+
attr_reader :occupied_spaces, :valid_spaces
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@occupied_spaces, @valid_spaces = [], []
|
10
|
+
ROWS.each do |row|
|
11
|
+
COLUMNS.each do |column|
|
12
|
+
@valid_spaces << Space.new(row, column)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def adjacent_to_occupied_spaces?(space, ship_length)
|
18
|
+
spaces = space.spaces_for_placement(ship_length)
|
19
|
+
occupied_spaces.inject(false) do |memo, occupied_space|
|
20
|
+
memo |= spaces.inject(false) {|m, s| m |= s.adjacent?(occupied_space)}
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def accomodate?(space, ship_length)
|
25
|
+
space.spaces_for_placement(ship_length).inject(true) do |success, possible_space|
|
26
|
+
success &= !occupied_spaces.include?(possible_space) && valid_spaces.include?(possible_space)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def placement(ship_length)
|
31
|
+
space = nil
|
32
|
+
while space.nil? || !accomodate?(space, ship_length) || adjacent_to_occupied_spaces?(space, ship_length)
|
33
|
+
space = Space.generate
|
34
|
+
end
|
35
|
+
occupied_spaces.concat(space.spaces_for_placement(ship_length))
|
36
|
+
space.to_s
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module JoshuaSonOfNun
|
2
|
+
class Player
|
3
|
+
def battleship_placement
|
4
|
+
@battleship.initial_placement
|
5
|
+
end
|
6
|
+
|
7
|
+
def carrier_placement
|
8
|
+
@carrier.initial_placement
|
9
|
+
end
|
10
|
+
|
11
|
+
def destroyer_placement
|
12
|
+
@destroyer.initial_placement
|
13
|
+
end
|
14
|
+
|
15
|
+
def patrolship_placement
|
16
|
+
@patrolship.initial_placement
|
17
|
+
end
|
18
|
+
|
19
|
+
def submarine_placement
|
20
|
+
@submarine.initial_placement
|
21
|
+
end
|
22
|
+
|
23
|
+
def next_target
|
24
|
+
@strategy.next_target.to_s
|
25
|
+
end
|
26
|
+
|
27
|
+
def target_result(coordinates, was_hit, ship_sunk)
|
28
|
+
@strategy.register_result!(was_hit, ship_sunk)
|
29
|
+
end
|
30
|
+
|
31
|
+
def enemy_targeting(*args); end
|
32
|
+
def game_over(*args); end
|
33
|
+
|
34
|
+
private
|
35
|
+
def reset(*args)
|
36
|
+
srand
|
37
|
+
|
38
|
+
@personal_board = Board.new
|
39
|
+
@opponent_board = Board.new
|
40
|
+
|
41
|
+
@battleship = Battleship.new(@personal_board)
|
42
|
+
@carrier = Carrier.new(@personal_board)
|
43
|
+
@destroyer = Destroyer.new(@personal_board)
|
44
|
+
@patrolship = Patrolship.new(@personal_board)
|
45
|
+
@submarine = Submarine.new(@personal_board)
|
46
|
+
|
47
|
+
@strategy = Strategy.select(@opponent_board)
|
48
|
+
end
|
49
|
+
|
50
|
+
alias_method :initialize, :reset
|
51
|
+
alias_method :new_game, :reset
|
52
|
+
public :initialize, :new_game
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module JoshuaSonOfNun
|
2
|
+
class Ship
|
3
|
+
class << self
|
4
|
+
attr_accessor :length
|
5
|
+
end
|
6
|
+
|
7
|
+
attr_reader :initial_placement
|
8
|
+
|
9
|
+
def initialize(board)
|
10
|
+
@board = board
|
11
|
+
@initial_placement = @board.placement(length)
|
12
|
+
end
|
13
|
+
|
14
|
+
def length
|
15
|
+
self.class.length
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
class Battleship < Ship
|
20
|
+
self.length = 4
|
21
|
+
end
|
22
|
+
|
23
|
+
class Carrier < Ship
|
24
|
+
self.length = 5
|
25
|
+
end
|
26
|
+
|
27
|
+
class Destroyer < Ship
|
28
|
+
self.length = 3
|
29
|
+
end
|
30
|
+
|
31
|
+
class Patrolship < Ship
|
32
|
+
self.length = 2
|
33
|
+
end
|
34
|
+
|
35
|
+
class Submarine < Ship
|
36
|
+
self.length = 3
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,172 @@
|
|
1
|
+
module JoshuaSonOfNun
|
2
|
+
class Space
|
3
|
+
ROWS = Board::ROWS
|
4
|
+
COLUMNS = Board::COLUMNS
|
5
|
+
|
6
|
+
def self.directions
|
7
|
+
[:southeast, :southwest, :northeast, :northwest]
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.generate
|
11
|
+
new(ROWS[rand(10).to_i], COLUMNS[rand(10).to_i], %w(horizontal vertical)[rand(2)])
|
12
|
+
end
|
13
|
+
|
14
|
+
attr_reader :row, :column, :orientation
|
15
|
+
|
16
|
+
def initialize(row, column, orientation = nil)
|
17
|
+
raise 'Invalid space' unless ROWS.include?(row) && COLUMNS.include?(column)
|
18
|
+
|
19
|
+
@row, @column, @orientation = row, column, orientation
|
20
|
+
end
|
21
|
+
|
22
|
+
def adjacent?(other)
|
23
|
+
adjacent_row_range.include?(other.row_index) && adjacent_column_range.include?(other.column_index)
|
24
|
+
end
|
25
|
+
|
26
|
+
def column_index
|
27
|
+
COLUMNS.index(column)
|
28
|
+
end
|
29
|
+
|
30
|
+
def crosswise_spaces
|
31
|
+
top = (index = row_index - 1) < 0 ? nil : Space.new(ROWS[index], column)
|
32
|
+
right = (index = column_index + 1) > 9 ? nil : Space.new(row, COLUMNS[index])
|
33
|
+
bottom = (index = row_index + 1) > 9 ? nil : Space.new(ROWS[index], column)
|
34
|
+
left = (index = column_index - 1) < 0 ? nil : Space.new(row, COLUMNS[index])
|
35
|
+
[top, right, bottom, left].compact
|
36
|
+
end
|
37
|
+
|
38
|
+
def linear_spaces(other, all_illegal_spaces = [], successful_illegal_spaces = [])
|
39
|
+
top, left, bottom, right = nil
|
40
|
+
illegal_spaces = all_illegal_spaces.dup.concat([self, other]).uniq
|
41
|
+
successful_spaces = successful_illegal_spaces.dup
|
42
|
+
|
43
|
+
if row_index == other.row_index
|
44
|
+
left, right = assign_left_and_right(other.column_index, illegal_spaces, successful_spaces)
|
45
|
+
elsif column_index == other.column_index
|
46
|
+
top, bottom = assign_top_and_bottom(other.row_index, illegal_spaces, successful_spaces)
|
47
|
+
end
|
48
|
+
|
49
|
+
[top, left, bottom, right].compact
|
50
|
+
end
|
51
|
+
|
52
|
+
def linear?(other)
|
53
|
+
row_index == other.row_index || column_index == other.column_index
|
54
|
+
end
|
55
|
+
|
56
|
+
def row_index
|
57
|
+
ROWS.index(row)
|
58
|
+
end
|
59
|
+
|
60
|
+
def spaces_for_placement(ship_length)
|
61
|
+
args = case orientation
|
62
|
+
when 'horizontal': lambda {|i| [row, COLUMNS[column_index + i].to_s]}
|
63
|
+
when 'vertical': lambda {|i| [ROWS[row_index + i].to_s, column]}
|
64
|
+
end
|
65
|
+
|
66
|
+
(0..(ship_length - 1)).collect {|i| Space.new(*args.call(i)) rescue nil}
|
67
|
+
end
|
68
|
+
|
69
|
+
def spaces_in_knighted_move(direction)
|
70
|
+
row_offset, column_offset = case direction
|
71
|
+
when :northeast: [[-1, 2], [-2, 1]][rand(2)]
|
72
|
+
when :southeast: [[1, 2], [2, 1]][rand(2)]
|
73
|
+
when :southwest: [[1, -2], [2, -1]][rand(2)]
|
74
|
+
when :northwest: [[-1, -2], [-2, -1]][rand(2)]
|
75
|
+
end
|
76
|
+
|
77
|
+
r_index, c_index = row_index + row_offset, column_index + column_offset
|
78
|
+
if r_index < 0 || c_index < 0 || r_index > 9 || c_index > 9
|
79
|
+
nil
|
80
|
+
else
|
81
|
+
Space.new(ROWS[r_index], COLUMNS[c_index])
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def spaces_on_diagonal(direction)
|
86
|
+
spaces, boundary_reached = [], false
|
87
|
+
until boundary_reached
|
88
|
+
row_offset, column_offset = case direction
|
89
|
+
when :northeast: [-1, 1]
|
90
|
+
when :southeast: [1, 1]
|
91
|
+
when :southwest: [1, -1]
|
92
|
+
when :northwest: [-1, -1]
|
93
|
+
end
|
94
|
+
|
95
|
+
seed_space = spaces.last || self
|
96
|
+
r_index, c_index = seed_space.row_index + row_offset, seed_space.column_index + column_offset
|
97
|
+
|
98
|
+
if r_index < 0 || c_index < 0 || r_index > 9 || c_index > 9
|
99
|
+
boundary_reached = true
|
100
|
+
else
|
101
|
+
spaces << Space.new(ROWS[r_index], COLUMNS[c_index])
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
spaces
|
106
|
+
end
|
107
|
+
|
108
|
+
def to_s
|
109
|
+
[row.to_s + column.to_s, orientation].compact * ' '
|
110
|
+
end
|
111
|
+
alias_method :inspect, :to_s
|
112
|
+
|
113
|
+
def ==(other)
|
114
|
+
to_s == other.to_s
|
115
|
+
end
|
116
|
+
|
117
|
+
private
|
118
|
+
def adjacent_column_range
|
119
|
+
((column_index == 0 ? 0 : column_index-1)..column_index+1)
|
120
|
+
end
|
121
|
+
|
122
|
+
def adjacent_row_range
|
123
|
+
((row_index == 0 ? 0 : row_index-1)..row_index+1)
|
124
|
+
end
|
125
|
+
|
126
|
+
def assign_left_and_right(other_column_index, illegal_spaces, successful_spaces)
|
127
|
+
left_index = (column_index < other_column_index ? column_index : other_column_index)
|
128
|
+
right_index = left_index + 1
|
129
|
+
|
130
|
+
surrounding_linear_spaces(left_index, right_index, illegal_spaces, successful_spaces) do |i, row, column|
|
131
|
+
Space.new(row, COLUMNS[i])
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def assign_top_and_bottom(other_row_index, illegal_spaces, successful_spaces)
|
136
|
+
top_index = (row_index < other_row_index ? row_index : other_row_index)
|
137
|
+
bottom_index = top_index + 1
|
138
|
+
|
139
|
+
surrounding_linear_spaces(top_index, bottom_index, illegal_spaces, successful_spaces) do |i, row, column|
|
140
|
+
Space.new(ROWS[i], column)
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
def surrounding_linear_spaces(side_one_index, side_two_index, illegal_spaces, successful_spaces, &space_creation_block)
|
145
|
+
side_one_spaces = (0..side_one_index).collect {|i| space_creation_block.call(i, row, column)}
|
146
|
+
side_two_spaces = (side_two_index..9).collect {|i| space_creation_block.call(i, row, column)}
|
147
|
+
|
148
|
+
illegal_spaces.each do |space|
|
149
|
+
side_one_spaces.delete(space)
|
150
|
+
side_two_spaces.delete(space)
|
151
|
+
end
|
152
|
+
|
153
|
+
spaces = {
|
154
|
+
:side_one_space => side_one_spaces.pop,
|
155
|
+
:side_two_space => side_two_spaces.shift
|
156
|
+
}
|
157
|
+
|
158
|
+
unless illegal_spaces.empty? || successful_spaces.empty?
|
159
|
+
unsuccessful_spaces = illegal_spaces.reject {|s| successful_spaces.include?(s)}
|
160
|
+
|
161
|
+
[:side_one_space, :side_two_space].each do |key|
|
162
|
+
space = spaces[key]
|
163
|
+
spaces[key] = nil if !space.nil? &&
|
164
|
+
unsuccessful_spaces.detect {|s| space.adjacent?(s)} &&
|
165
|
+
!successful_spaces.detect {|s| space.adjacent?(s)}
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
[spaces[:side_one_space], spaces[:side_two_space]]
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module JoshuaSonOfNun
|
2
|
+
module Strategy
|
3
|
+
def self.strategies
|
4
|
+
[['Random']*4, 'Diagonal', ['Knight']*2].flatten
|
5
|
+
end
|
6
|
+
|
7
|
+
def self.select(board)
|
8
|
+
const_get(strategies[rand(strategies.size)]).new(board)
|
9
|
+
end
|
10
|
+
|
11
|
+
class Base
|
12
|
+
attr_reader :current_target, :expended_targets, :immediate_targets,
|
13
|
+
:possible_targets, :successful_targets, :targets
|
14
|
+
|
15
|
+
def initialize(board)
|
16
|
+
@possible_targets = board.valid_spaces.dup
|
17
|
+
@targets = assign_targets
|
18
|
+
@expended_targets, @successful_targets, @immediate_targets = [], [], []
|
19
|
+
end
|
20
|
+
|
21
|
+
def next_target
|
22
|
+
new_target = immediate_targets.shift || targets.first
|
23
|
+
targets.delete(new_target)
|
24
|
+
|
25
|
+
@current_target = new_target
|
26
|
+
expended_targets << @current_target
|
27
|
+
@current_target
|
28
|
+
end
|
29
|
+
|
30
|
+
def register_result!(ship_hit, ship_sunk)
|
31
|
+
if ship_hit
|
32
|
+
successful_targets << current_target
|
33
|
+
@immediate_targets = TargetingReaction.new(self, ship_sunk).react!
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
def choose_target(index = rand(possible_targets.size))
|
39
|
+
possible_targets.delete(possible_targets[index])
|
40
|
+
end
|
41
|
+
|
42
|
+
def random_direction
|
43
|
+
Space.directions[rand(Space.directions.size)]
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module JoshuaSonOfNun
|
2
|
+
module Strategy
|
3
|
+
class Diagonal < Base
|
4
|
+
private
|
5
|
+
def assign_targets
|
6
|
+
targets = []
|
7
|
+
until possible_targets.empty?
|
8
|
+
target = choose_target
|
9
|
+
diagonal_targets = target.spaces_on_diagonal(random_direction)
|
10
|
+
targets << target
|
11
|
+
diagonal_targets.each do |diagonal_target|
|
12
|
+
targets << possible_targets.delete(diagonal_target)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
targets.compact
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module JoshuaSonOfNun
|
2
|
+
module Strategy
|
3
|
+
class Knight < Base
|
4
|
+
private
|
5
|
+
def assign_targets
|
6
|
+
targets = [choose_target]
|
7
|
+
until possible_targets.empty?
|
8
|
+
next_target = targets.last.spaces_in_knighted_move(random_direction)
|
9
|
+
next_target = choose_target if targets.include?(next_target) || next_target.nil?
|
10
|
+
possible_targets.delete(next_target)
|
11
|
+
targets << next_target
|
12
|
+
end
|
13
|
+
targets
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module JoshuaSonOfNun
|
2
|
+
module Strategy
|
3
|
+
class TargetingReaction
|
4
|
+
attr_reader :strategy
|
5
|
+
|
6
|
+
def initialize(strategy, ship_sunk)
|
7
|
+
@strategy, @ship_sunk = strategy, ship_sunk
|
8
|
+
end
|
9
|
+
|
10
|
+
def react!
|
11
|
+
return [] if @ship_sunk
|
12
|
+
|
13
|
+
if targets_lined_up?
|
14
|
+
targets = strategy.successful_targets
|
15
|
+
reject_expended(targets[-2].linear_spaces(targets.last, strategy.expended_targets, strategy.successful_targets))
|
16
|
+
else
|
17
|
+
reject_expended(strategy.current_target.crosswise_spaces)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
protected
|
22
|
+
def reject_expended(spaces)
|
23
|
+
spaces.reject do |space|
|
24
|
+
strategy.expended_targets.include?(space)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def targets_lined_up?
|
29
|
+
targets = strategy.successful_targets
|
30
|
+
targets.size > 1 && targets[-2].linear?(targets.last)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
describe JoshuaSonOfNun::Board do
|
4
|
+
before do
|
5
|
+
@model = JoshuaSonOfNun::Board.new
|
6
|
+
end
|
7
|
+
|
8
|
+
it "should know about which spaces are valid" do
|
9
|
+
@model.valid_spaces.should include(Space('A1'))
|
10
|
+
@model.valid_spaces.should include(Space('A10'))
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should calculate whether it can accomodate a ship at a certain space" do
|
14
|
+
@model.accomodate?(Space('A1 horizontal'), 5).should be_true
|
15
|
+
@model.accomodate?(Space('A10 horizontal'), 5).should be_false
|
16
|
+
@model.accomodate?(Space('H1 vertical'), 3).should be_true
|
17
|
+
@model.accomodate?(Space('H1 vertical'), 4).should be_false
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should keep track of it's occupied spaces" do
|
21
|
+
@model.placement(4)
|
22
|
+
@model.occupied_spaces.size.should == 4
|
23
|
+
|
24
|
+
@model.placement(5)
|
25
|
+
@model.occupied_spaces.size.should == 9
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should be able to tell if a space is adjacent to the occupied spaces" do
|
29
|
+
space = Space('D3 horizontal')
|
30
|
+
@model.occupied_spaces.concat(space.spaces_for_placement(5)) # D3, D4, D5, D6, D7
|
31
|
+
|
32
|
+
@model.adjacent_to_occupied_spaces?(Space('C3 horizontal'), 5).should be_true
|
33
|
+
@model.adjacent_to_occupied_spaces?(Space('E3 horizontal'), 5).should be_true
|
34
|
+
@model.adjacent_to_occupied_spaces?(Space('E3 vertical'), 2).should be_true
|
35
|
+
@model.adjacent_to_occupied_spaces?(Space('C8 vertical'), 2).should be_true
|
36
|
+
@model.adjacent_to_occupied_spaces?(Space('E2 vertical'), 2).should be_true
|
37
|
+
|
38
|
+
@model.adjacent_to_occupied_spaces?(Space('A1 horizontal'), 5).should be_false
|
39
|
+
@model.adjacent_to_occupied_spaces?(Space('A1 vertical'), 5).should be_false
|
40
|
+
@model.adjacent_to_occupied_spaces?(Space('A4 vertical'), 2).should be_false
|
41
|
+
@model.adjacent_to_occupied_spaces?(Space('C9 vertical'), 5).should be_false
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + "/../spec_helper")
|
2
|
+
|
3
|
+
describe JoshuaSonOfNun::Player do
|
4
|
+
before do
|
5
|
+
@model = JoshuaSonOfNun::Player.new
|
6
|
+
end
|
7
|
+
|
8
|
+
it "should conform to the battleship API" do
|
9
|
+
@model.carrier_placement.should_not be_nil
|
10
|
+
@model.battleship_placement.should_not be_nil
|
11
|
+
@model.destroyer_placement.should_not be_nil
|
12
|
+
@model.submarine_placement.should_not be_nil
|
13
|
+
@model.patrolship_placement.should_not be_nil
|
14
|
+
|
15
|
+
100.times do
|
16
|
+
@model.next_target.should_not == ''
|
17
|
+
end
|
18
|
+
|
19
|
+
lambda {@model.target_result('A1', false, false)}.should_not raise_error
|
20
|
+
lambda {@model.new_game('bob')}.should_not raise_error
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
describe "Ships" do
|
4
|
+
before do
|
5
|
+
@board = JoshuaSonOfNun::Board.new
|
6
|
+
@model = JoshuaSonOfNun::Battleship.new(@board)
|
7
|
+
end
|
8
|
+
|
9
|
+
it "should have a length" do
|
10
|
+
@model.length.should == 4
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should have an initial placement on the board" do
|
14
|
+
@model.initial_placement.should_not be_nil
|
15
|
+
@model.initial_placement.should =~ /[A-Z]\d{1,2} [horizontal|vertical]/
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
describe JoshuaSonOfNun::Space do
|
4
|
+
before do
|
5
|
+
@model = Space('A1')
|
6
|
+
end
|
7
|
+
|
8
|
+
it "should know which spaces are close to other spaces" do
|
9
|
+
@model.adjacent?(Space('A1')).should be_true
|
10
|
+
@model.adjacent?(Space('A2')).should be_true
|
11
|
+
@model.adjacent?(Space('B1')).should be_true
|
12
|
+
@model.adjacent?(Space('B2')).should be_true
|
13
|
+
Space('E5').adjacent?(Space('D6')).should be_true
|
14
|
+
|
15
|
+
@model.adjacent?(Space('A10')).should be_false
|
16
|
+
@model.adjacent?(Space('C3')).should be_false
|
17
|
+
Space('H7').adjacent?(Space('J7')).should be_false
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should return the spaces for placement for a ship of given length" do
|
21
|
+
Space('A1 horizontal').spaces_for_placement(4).should ==
|
22
|
+
space_array('A1', 'A2', 'A3', 'A4')
|
23
|
+
|
24
|
+
Space('A1 vertical').spaces_for_placement(5).should ==
|
25
|
+
space_array('A1', 'B1', 'C1', 'D1', 'E1')
|
26
|
+
|
27
|
+
Space('A8 horizontal').spaces_for_placement(5).should ==
|
28
|
+
space_array('A8', 'A9', 'A10').concat([nil, nil])
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should know which spaces are crosswise to the given space" do
|
32
|
+
@model.crosswise_spaces.should == space_array('A2', 'B1')
|
33
|
+
Space('C10').crosswise_spaces.should == space_array('B10', 'D10', 'C9')
|
34
|
+
Space('E5').crosswise_spaces.should == space_array('D5', 'E6', 'F5', 'E4')
|
35
|
+
Space('A7').crosswise_spaces.should == space_array('A8', 'B7', 'A6')
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should know which spaces are in the diagonal of a given direction" do
|
39
|
+
@model = Space('E5')
|
40
|
+
@model.spaces_on_diagonal(:northeast).should == space_array('D6', 'C7', 'B8', 'A9')
|
41
|
+
@model.spaces_on_diagonal(:southeast).should == space_array('F6', 'G7', 'H8', 'I9', 'J10')
|
42
|
+
@model.spaces_on_diagonal(:southwest).should == space_array('F4', 'G3', 'H2', 'I1')
|
43
|
+
@model.spaces_on_diagonal(:northwest).should == space_array('D4', 'C3', 'B2', 'A1')
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should know which spaces are in an 'L' shape to the given square and direction" do
|
47
|
+
@model.spaces_in_knighted_move(:northeast).should be_nil
|
48
|
+
space_array('C2', 'B3').should include(@model.spaces_in_knighted_move(:southeast))
|
49
|
+
@model.spaces_in_knighted_move(:southwest).should be_nil
|
50
|
+
@model.spaces_in_knighted_move(:northwest).should be_nil
|
51
|
+
|
52
|
+
@model = Space('E5')
|
53
|
+
space_array('C6', 'D7').should include(@model.spaces_in_knighted_move(:northeast))
|
54
|
+
space_array('G6', 'F7').should include(@model.spaces_in_knighted_move(:southeast))
|
55
|
+
space_array('F3', 'G4').should include(@model.spaces_in_knighted_move(:southwest))
|
56
|
+
space_array('D3', 'C4').should include(@model.spaces_in_knighted_move(:northwest))
|
57
|
+
end
|
58
|
+
|
59
|
+
describe "linear spaces" do
|
60
|
+
it "should know which spaces are linear to two given spaces, and return the two immediate closest spaces" do
|
61
|
+
Space('E6').linear_spaces(Space('E5')).should == space_array('E4', 'E7')
|
62
|
+
Space('E5').linear_spaces(Space('D5')).should == space_array('C5', 'F5')
|
63
|
+
@model.linear_spaces(Space('A2')).should == [Space('A3')]
|
64
|
+
@model.linear_spaces(Space('B1')).should == [Space('C1')]
|
65
|
+
end
|
66
|
+
|
67
|
+
it "should not return spaces that are beyond spaces that have been targeted but were not successful" do
|
68
|
+
expended = space_array('A2', 'A3', 'A4', 'A5', 'A7')
|
69
|
+
successful = space_array('A4', 'A5')
|
70
|
+
Space('A4').linear_spaces(Space('A5'), expended, successful).should == [Space('A6')]
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
describe JoshuaSonOfNun::Strategy::Random do
|
4
|
+
before do
|
5
|
+
@board = JoshuaSonOfNun::Board.new
|
6
|
+
@model = JoshuaSonOfNun::Strategy::Random.new(@board)
|
7
|
+
end
|
8
|
+
|
9
|
+
it "should target all spaces" do
|
10
|
+
@model.targets.uniq.size.should == 100
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should shift targets out of the array to gather targets" do
|
14
|
+
target = nil
|
15
|
+
lambda do
|
16
|
+
@model.next_target
|
17
|
+
@model.next_target
|
18
|
+
target = @model.next_target
|
19
|
+
end.should change(@model.targets, :size).by(-3)
|
20
|
+
@model.current_target.should == target
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should receive results of the current target, and react accordingly" do
|
24
|
+
target = @model.targets.delete(Space('E5'))
|
25
|
+
@model.instance_variable_set '@current_target', target
|
26
|
+
@model.register_result! true, false
|
27
|
+
|
28
|
+
@model.next_target.should == Space('D5') # First crosswise space to attack
|
29
|
+
@model.next_target.should == Space('E6')
|
30
|
+
@model.next_target.should == Space('F5')
|
31
|
+
@model.next_target.should == Space('E4')
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should not target differently if nothing was hit" do
|
35
|
+
target = @model.targets.delete(Space('E5'))
|
36
|
+
@model.instance_variable_set '@current_target', target
|
37
|
+
next_target = @model.targets.first
|
38
|
+
@model.register_result! false, false
|
39
|
+
|
40
|
+
@model.next_target.should == next_target
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should target in a line if two or more targets have been hit" do
|
44
|
+
@model.instance_variable_set '@immediate_targets', [Space('E5')]
|
45
|
+
@model.next_target; @model.register_result! true, false # first shot
|
46
|
+
|
47
|
+
@model.instance_variable_set '@immediate_targets', [Space('E6')]
|
48
|
+
@model.next_target; @model.register_result! true, false # second shot
|
49
|
+
|
50
|
+
@model.next_target.should == Space('E4')
|
51
|
+
@model.register_result! false, false
|
52
|
+
|
53
|
+
@model.next_target.should == Space('E7')
|
54
|
+
@model.register_result! true, false
|
55
|
+
|
56
|
+
@model.next_target.should == Space('E8')
|
57
|
+
end
|
58
|
+
|
59
|
+
it "should track successful targets" do
|
60
|
+
target_one = @model.next_target
|
61
|
+
@model.register_result! true, false
|
62
|
+
|
63
|
+
target_two = @model.next_target
|
64
|
+
@model.register_result! true, false
|
65
|
+
|
66
|
+
@model.successful_targets.should == [target_one, target_two]
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
describe JoshuaSonOfNun::Strategy::Diagonal do
|
71
|
+
before do
|
72
|
+
JoshuaSonOfNun::Space.stub!(:directions).and_return([:southeast])
|
73
|
+
|
74
|
+
@board = JoshuaSonOfNun::Board.new
|
75
|
+
@model = JoshuaSonOfNun::Strategy::Diagonal.new(@board)
|
76
|
+
end
|
77
|
+
|
78
|
+
it "should target all spaces" do
|
79
|
+
@model.targets.uniq.size.should == 100
|
80
|
+
end
|
81
|
+
|
82
|
+
it "should search across the board in a diagonal fashion" do
|
83
|
+
target = Space('E5')
|
84
|
+
expected_next_target = Space('F6')
|
85
|
+
|
86
|
+
# One of these spaces may have already been placed in the targeting array,
|
87
|
+
# so checking to make sure at least one of these spaces is the next space
|
88
|
+
space_array('F6', 'G7', 'H8', 'I9', 'J10').should include(@model.targets[@model.targets.index(target) + 1])
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
describe JoshuaSonOfNun::Strategy::Knight do
|
93
|
+
before do
|
94
|
+
JoshuaSonOfNun::Space.stub!(:directions).and_return([:southeast])
|
95
|
+
|
96
|
+
@board = JoshuaSonOfNun::Board.new
|
97
|
+
@model = JoshuaSonOfNun::Strategy::Knight.new(@board)
|
98
|
+
end
|
99
|
+
|
100
|
+
it "should target all spaces" do
|
101
|
+
@model.targets.uniq.size.should == 100
|
102
|
+
# The specifics of this strategy are not tested here because the next move
|
103
|
+
# from space E5 may have already been placed in the targeting array,
|
104
|
+
# making the order very difficult to test since sometimes the test will
|
105
|
+
# pass, other times the test will fail. So, testing whether or not we have
|
106
|
+
# all the targets is good enough.
|
107
|
+
#
|
108
|
+
# Also, moving like a knight is tested in the space_spec
|
109
|
+
end
|
110
|
+
end
|
data/spec/spec.opts
ADDED
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'spec'
|
3
|
+
|
4
|
+
require 'init'
|
5
|
+
|
6
|
+
module Kernel
|
7
|
+
require 'cgi'
|
8
|
+
|
9
|
+
def rputs(*args)
|
10
|
+
puts *["<pre>", args.collect {|a| CGI.escapeHTML(a.inspect)}, "</pre>"]
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def Space(string)
|
15
|
+
coordinates, orientation = string.split(' ')
|
16
|
+
row, column = coordinates.scan(/(\w)(\d{1,2})/).first
|
17
|
+
JoshuaSonOfNun::Space.new(row, column, orientation)
|
18
|
+
end
|
19
|
+
|
20
|
+
def space_array(*coordinates)
|
21
|
+
coordinates.collect {|c| Space(c)}
|
22
|
+
end
|
metadata
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: joshua_son_of_nun
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: "1.0"
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Steve Iannopollo
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2008-11-29 00:00:00 -05:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description: (using scary WWF wrestler voice) Just like the walls of Jericho, the same will happen to all battleship opponents of Joshua, son of Nun!!
|
17
|
+
email: steve@iannopollo.com
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files: []
|
23
|
+
|
24
|
+
files:
|
25
|
+
- Battleship.Rakefile
|
26
|
+
- game_simulator.rb
|
27
|
+
- init.rb
|
28
|
+
- lib
|
29
|
+
- lib/joshua_son_of_nun
|
30
|
+
- lib/joshua_son_of_nun/board.rb
|
31
|
+
- lib/joshua_son_of_nun/joshua_son_of_nun.rb
|
32
|
+
- lib/joshua_son_of_nun/player.rb
|
33
|
+
- lib/joshua_son_of_nun/ship.rb
|
34
|
+
- lib/joshua_son_of_nun/space.rb
|
35
|
+
- lib/joshua_son_of_nun/strategy
|
36
|
+
- lib/joshua_son_of_nun/strategy/base.rb
|
37
|
+
- lib/joshua_son_of_nun/strategy/diagonal.rb
|
38
|
+
- lib/joshua_son_of_nun/strategy/knight.rb
|
39
|
+
- lib/joshua_son_of_nun/strategy/random.rb
|
40
|
+
- lib/joshua_son_of_nun/strategy/targeting_reaction.rb
|
41
|
+
- pkg
|
42
|
+
- Rakefile
|
43
|
+
- README.rdoc
|
44
|
+
- spec
|
45
|
+
- spec/joshua_son_of_nun
|
46
|
+
- spec/joshua_son_of_nun/board_spec.rb
|
47
|
+
- spec/joshua_son_of_nun/player_spec.rb
|
48
|
+
- spec/joshua_son_of_nun/ship_spec.rb
|
49
|
+
- spec/joshua_son_of_nun/space_spec.rb
|
50
|
+
- spec/joshua_son_of_nun/strategy_spec.rb
|
51
|
+
- spec/spec.opts
|
52
|
+
- spec/spec_helper.rb
|
53
|
+
has_rdoc: false
|
54
|
+
homepage: http://sparring.rubyforge.org/
|
55
|
+
post_install_message:
|
56
|
+
rdoc_options: []
|
57
|
+
|
58
|
+
require_paths:
|
59
|
+
- lib
|
60
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
61
|
+
requirements:
|
62
|
+
- - ">="
|
63
|
+
- !ruby/object:Gem::Version
|
64
|
+
version: "0"
|
65
|
+
version:
|
66
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
67
|
+
requirements:
|
68
|
+
- - ">="
|
69
|
+
- !ruby/object:Gem::Version
|
70
|
+
version: "0"
|
71
|
+
version:
|
72
|
+
requirements: []
|
73
|
+
|
74
|
+
rubyforge_project: sparring
|
75
|
+
rubygems_version: 1.2.0
|
76
|
+
signing_key:
|
77
|
+
specification_version: 2
|
78
|
+
summary: Battleship Player:joshua_son_of_nun
|
79
|
+
test_files: []
|
80
|
+
|