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