alpha 1.7
Sign up to get free protection for your applications and to get access to all the features.
- data/Battleship.Rakefile +12 -0
- data/Rakefile +47 -0
- data/lib/alpha/alpha.rb +217 -0
- data/lib/alpha/grid.rb +91 -0
- data/lib/alpha/mover.rb +51 -0
- data/lib/alpha/randomizer.rb +39 -0
- data/pkg/alpha-1.1.gem +0 -0
- data/pkg/alpha-1.2.gem +0 -0
- data/pkg/alpha-1.3.gem +0 -0
- data/pkg/alpha-1.4.gem +0 -0
- data/pkg/alpha-1.5.gem +0 -0
- data/pkg/paul-1.0.gem +0 -0
- data/spec/alpha/alpha_spec.rb +133 -0
- data/spec/alpha/grid_spec.rb +78 -0
- data/spec/alpha/randomizer_spec.rb +25 -0
- data/spec/spec_helper.rb +4 -0
- metadata +73 -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/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 = "alpha"
|
13
|
+
PKG_VERSION = "1.7"
|
14
|
+
|
15
|
+
spec = Gem::Specification.new do |s|
|
16
|
+
s.name = PKG_NAME
|
17
|
+
s.version = PKG_VERSION
|
18
|
+
s.files = FileList['**/*'].to_a
|
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:alpha"
|
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 = "The Alpha bot"
|
34
|
+
s.author = "Paul W Pagel"
|
35
|
+
s.email = "paul@8thlight.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
|
data/lib/alpha/alpha.rb
ADDED
@@ -0,0 +1,217 @@
|
|
1
|
+
require 'alpha/grid'
|
2
|
+
require "alpha/randomizer"
|
3
|
+
require "alpha/mover"
|
4
|
+
|
5
|
+
module Alpha
|
6
|
+
|
7
|
+
# Battleship Player
|
8
|
+
#
|
9
|
+
# Battleship is board game between two players. See http://en.wikipedia.org/wiki/Battleship for more information and
|
10
|
+
# game rules.
|
11
|
+
#
|
12
|
+
# A player represents the conputer AI to play a game of Battleship. It should know how to place ships and target
|
13
|
+
# the opponents ships.
|
14
|
+
#
|
15
|
+
# This version of Battleship is played on a 10 x 10 grid where rows are labled by the letters A - J and
|
16
|
+
# columns are labled by the numbers 1 - 10. At the start of the game, each player will be asked for ship placements.
|
17
|
+
# Once the ships are placed, play proceeeds by each player targeting one square on their opponents map. A player
|
18
|
+
# may only target one square, reguardless of whether it resulted in a hit or not, before changing turns with her opponent.
|
19
|
+
#
|
20
|
+
class Alpha
|
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
|
+
end
|
31
|
+
|
32
|
+
# Returns the placement of the carrier. A carrier consumes 5 squares.
|
33
|
+
#
|
34
|
+
# The return value is a string that describes the placements of the ship.
|
35
|
+
# The placement string must be in the following format:
|
36
|
+
#
|
37
|
+
# "#{ROW}#{COL} #{ORIENTATION}"
|
38
|
+
#
|
39
|
+
# eg
|
40
|
+
#
|
41
|
+
# A1 horizontal # the ship will occupy A1, A2, A3, A4, and A5
|
42
|
+
# A1 vertical # the ship will occupy A1, B1, C1, D1, and E1
|
43
|
+
# F5 horizontal # the ship will occupy F5, F6, F7, F8, and F9
|
44
|
+
# F5 vertical # the ship will occupy F5, G5, H5, I5, and J5
|
45
|
+
#
|
46
|
+
# The ship must not fall off the edge of the map. For example, a carrier placement of 'A8 horizontal' would
|
47
|
+
# not leave enough space in the A row to accomidate the carrier since it requires 5 squares.
|
48
|
+
#
|
49
|
+
# Ships may not overlap with other ships. For example a carrier placement of 'A1 horizontal' and a submarine
|
50
|
+
# placement of 'A1 vertical' would be invalid because bothe ships are trying to occupy the square A1.
|
51
|
+
#
|
52
|
+
# Invalid ship placements will result in disqualification of the player.
|
53
|
+
#
|
54
|
+
|
55
|
+
def get_placement(block_number)
|
56
|
+
placement = "#{@randomizer.rand_column}#{@randomizer.rand_row} #{@randomizer.rand_direction}"
|
57
|
+
square, direction = parse_placement(placement)
|
58
|
+
return get_placement(block_number) unless @my_grid.can_place_block?(square, direction, block_number)
|
59
|
+
return placement
|
60
|
+
end
|
61
|
+
|
62
|
+
def parse_placement(placement)
|
63
|
+
parts = placement.split(" ")
|
64
|
+
return parts[0], parts[1]
|
65
|
+
end
|
66
|
+
|
67
|
+
def place(occupied_squares)
|
68
|
+
placement = get_placement(occupied_squares)
|
69
|
+
square, direction = parse_placement(placement)
|
70
|
+
@my_grid.place_block(square, direction, occupied_squares)
|
71
|
+
return placement
|
72
|
+
end
|
73
|
+
|
74
|
+
def carrier_placement
|
75
|
+
return place(5)
|
76
|
+
end
|
77
|
+
|
78
|
+
# Returns the placement of the battleship. A battleship consumes 4 squares.
|
79
|
+
def battleship_placement
|
80
|
+
return place(4)
|
81
|
+
end
|
82
|
+
|
83
|
+
# Returns the placement of the destroyer. A destroyer consumes 3 squares.
|
84
|
+
def destroyer_placement
|
85
|
+
return place(3)
|
86
|
+
end
|
87
|
+
|
88
|
+
# Returns the placement of the submarine. A submarine consumes 3 squares.
|
89
|
+
def submarine_placement
|
90
|
+
return place(3)
|
91
|
+
end
|
92
|
+
|
93
|
+
# Returns the placement of the patrolship. A patrolship consumes 2 squares.
|
94
|
+
def patrolship_placement
|
95
|
+
return place(2)
|
96
|
+
end
|
97
|
+
|
98
|
+
# Returns the coordinates of the players next target. This method will be called once per turn. The player
|
99
|
+
# should return target coordinates as a string in the form of:
|
100
|
+
#
|
101
|
+
# "#{ROW}#{COL}"
|
102
|
+
#
|
103
|
+
# eg
|
104
|
+
#
|
105
|
+
# A1 # the square in Row A and Column 1
|
106
|
+
# F5 # the square in Row F and Column 5
|
107
|
+
#
|
108
|
+
# 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
|
109
|
+
# COL should be 1, 2, 3, 4, 5, 6, 7, 8, 9, or 10
|
110
|
+
#
|
111
|
+
# Returning coordinates outside the range or in an invalid format will result in the players disqualification.
|
112
|
+
#
|
113
|
+
# It is illegal to illegal to target a sector more than once. Doing so will also result in disqualification.
|
114
|
+
#
|
115
|
+
def next_target
|
116
|
+
target = target_for_current_shot
|
117
|
+
@shots_taken += 1
|
118
|
+
return target
|
119
|
+
end
|
120
|
+
|
121
|
+
# target_result will be called by the system after a call to next_target. The paramters supplied inform the player
|
122
|
+
# of the results of the target.
|
123
|
+
#
|
124
|
+
# coordinates : string. The coordinates targeted. It will be the same value returned by the previous call to next_target
|
125
|
+
# was_hit : boolean. true if the target was occupied by a ship. false otherwise.
|
126
|
+
# ship_sunk : symbol. nil if the target did not result in the sinking of a ship. If the target did result in
|
127
|
+
# in the sinking of a ship, the ship type is supplied (:carrier, :battleship, :destroyer, :submarine, :patrolship).
|
128
|
+
#
|
129
|
+
# An intelligent player will use the information to better play the game. For example, if the result indicates a
|
130
|
+
# hit, a player my choose to target neighboring squares to hit and sink the remainder of the ship.
|
131
|
+
#
|
132
|
+
def target_result(coordinates, was_hit, ship_sunk)
|
133
|
+
result = nil
|
134
|
+
if was_hit
|
135
|
+
result = :hit
|
136
|
+
@red_zone = true unless ship_sunk
|
137
|
+
else
|
138
|
+
result = :miss
|
139
|
+
end
|
140
|
+
@red_zone = false if ship_sunk
|
141
|
+
|
142
|
+
@opponents_grid.place(coordinates, result)
|
143
|
+
end
|
144
|
+
|
145
|
+
# enemy_targeting is called by the system to inform a player of their apponents move. When the opponent targets
|
146
|
+
# a square, this method is called with the coordinates.
|
147
|
+
#
|
148
|
+
# Players may use this information to understand an opponents targeting strategy and place ships differently
|
149
|
+
# in subsequent games.
|
150
|
+
#
|
151
|
+
def enemy_targeting(coordinates)
|
152
|
+
end
|
153
|
+
|
154
|
+
# Called by the system at the end of a game to inform the player of the results.
|
155
|
+
#
|
156
|
+
# result : 1 of 3 possible values (:victory, :defeate, :disqualified)
|
157
|
+
# disqualification_reason : nil unless the game ended as the result of a disqualification. In the event of a
|
158
|
+
# disqualification, this paramter will hold a string description of the reason for disqualification. Both
|
159
|
+
# players will be informed of the reason.
|
160
|
+
#
|
161
|
+
# :victory # indicates the player won the game
|
162
|
+
# :defeat # indicates the player lost the game
|
163
|
+
# :disqualified # indicates the player was disqualified
|
164
|
+
#
|
165
|
+
def game_over(result, disqualification_reason=nil)
|
166
|
+
end
|
167
|
+
|
168
|
+
# Non API methods #####################################
|
169
|
+
|
170
|
+
attr_reader :opponent, :targets, :enemy_targeted_sectors, :result, :disqualification_reason #:nodoc:
|
171
|
+
attr_reader :battle_queue, :opponents_grid, :red_zone
|
172
|
+
|
173
|
+
def initialize #:nodoc:
|
174
|
+
reset
|
175
|
+
end
|
176
|
+
|
177
|
+
private ###############################################
|
178
|
+
|
179
|
+
def reset
|
180
|
+
@shots_taken = 0
|
181
|
+
@randomizer = Randomizer.new
|
182
|
+
@my_grid = Grid.new
|
183
|
+
@opponents_grid = Grid.new
|
184
|
+
@red_zone = false
|
185
|
+
end
|
186
|
+
|
187
|
+
def target_for_current_shot
|
188
|
+
if @red_zone
|
189
|
+
mover = Mover.new
|
190
|
+
last_move = @opponents_grid.moves.last
|
191
|
+
row = @opponents_grid.get_row(last_move)
|
192
|
+
column = @opponents_grid.get_column(last_move)
|
193
|
+
|
194
|
+
result = mover.go_right(row, column, @opponents_grid)
|
195
|
+
return result if result
|
196
|
+
|
197
|
+
result = mover.go_left(row, column, @opponents_grid)
|
198
|
+
return result if result
|
199
|
+
|
200
|
+
result = mover.go_down(row, column, @opponents_grid)
|
201
|
+
return result if result
|
202
|
+
|
203
|
+
result = mover.go_up(row, column, @opponents_grid)
|
204
|
+
return result if result
|
205
|
+
end
|
206
|
+
return shot_candidate
|
207
|
+
|
208
|
+
end
|
209
|
+
|
210
|
+
def shot_candidate
|
211
|
+
candidate = "#{@randomizer.rand_column}#{@randomizer.rand_row}"
|
212
|
+
return shot_candidate unless @opponents_grid.query(candidate) == :empty
|
213
|
+
return candidate
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
end
|
data/lib/alpha/grid.rb
ADDED
@@ -0,0 +1,91 @@
|
|
1
|
+
|
2
|
+
module Alpha
|
3
|
+
class Grid
|
4
|
+
attr_reader :moves
|
5
|
+
@@LETTERS = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J"]
|
6
|
+
def initialize
|
7
|
+
@grid = {}
|
8
|
+
@moves = []
|
9
|
+
|
10
|
+
@@LETTERS.each do |letter|
|
11
|
+
10.times {|times| @grid["#{letter}#{times + 1}"] = :empty }
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def letters
|
16
|
+
return @@LETTERS
|
17
|
+
end
|
18
|
+
|
19
|
+
def place(key, value)
|
20
|
+
@moves << key
|
21
|
+
return @grid[key] = value
|
22
|
+
end
|
23
|
+
|
24
|
+
def query(criteria)
|
25
|
+
return @grid[criteria]
|
26
|
+
end
|
27
|
+
|
28
|
+
def last_a_hit?
|
29
|
+
return @grid[@moves.last] == :hit
|
30
|
+
end
|
31
|
+
|
32
|
+
def place_block(placement, direction, occupied_squares)
|
33
|
+
if direction == "horizontal"
|
34
|
+
row = get_row(placement)
|
35
|
+
column = get_column(placement)
|
36
|
+
|
37
|
+
occupied_squares.times do |col|
|
38
|
+
place("#{row}#{col + column}", :placed)
|
39
|
+
end
|
40
|
+
else
|
41
|
+
row = index_of(get_row(placement))
|
42
|
+
column = get_column(placement)
|
43
|
+
occupied_squares.times do |new_row|
|
44
|
+
place("#{@@LETTERS[(row + new_row)] }#{column}", :placed)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
|
50
|
+
def can_place_block?(placement, direction, occupied_squares)
|
51
|
+
if direction == "horizontal"
|
52
|
+
row = get_row(placement)
|
53
|
+
column = get_column(placement)
|
54
|
+
|
55
|
+
occupied_squares.times do |col|
|
56
|
+
return false if query("#{row}#{(column + col)}") != :empty
|
57
|
+
end
|
58
|
+
else
|
59
|
+
row = index_of(get_row(placement))
|
60
|
+
column = get_column(placement)
|
61
|
+
occupied_squares.times do |new_row|
|
62
|
+
return false if query("#{@@LETTERS[(row + new_row)] }#{column}") != :empty
|
63
|
+
end
|
64
|
+
end
|
65
|
+
return true
|
66
|
+
end
|
67
|
+
|
68
|
+
|
69
|
+
def get_row(placement)
|
70
|
+
return placement[0,1]
|
71
|
+
end
|
72
|
+
|
73
|
+
def get_column(placement)
|
74
|
+
if placement.size == 2
|
75
|
+
return placement[1,2].to_i
|
76
|
+
else
|
77
|
+
return placement[1,3].to_i
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
|
82
|
+
def index_of(item)
|
83
|
+
@@LETTERS.each_with_index do |letter, index|
|
84
|
+
return index if letter == item
|
85
|
+
end
|
86
|
+
|
87
|
+
raise "ITEM #{item} is not in LETTERS LIST"
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
data/lib/alpha/mover.rb
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
module Alpha
|
2
|
+
|
3
|
+
class Mover
|
4
|
+
|
5
|
+
def go_right(row, column, grid)
|
6
|
+
i = 1
|
7
|
+
while true
|
8
|
+
square_candidate_value = grid.query("#{row}#{column + i}")
|
9
|
+
return "#{row}#{column + i}" if square_candidate_value == :empty
|
10
|
+
break unless square_candidate_value == :hit
|
11
|
+
i += 1
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def go_left(row, column, grid)
|
16
|
+
i = 1
|
17
|
+
while true
|
18
|
+
square_candidate_value = grid.query("#{row}#{column - i}")
|
19
|
+
return "#{row}#{column - i}" if square_candidate_value == :empty
|
20
|
+
break unless square_candidate_value == :hit
|
21
|
+
i += 1
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def go_down(row, column, grid)
|
26
|
+
i = 1
|
27
|
+
while true
|
28
|
+
index = grid.index_of(row)
|
29
|
+
|
30
|
+
square_candidate_value = grid.query("#{grid.letters[index + i]}#{column}")
|
31
|
+
return "#{grid.letters[index + i]}#{column}" if square_candidate_value == :empty
|
32
|
+
break unless square_candidate_value == :hit
|
33
|
+
i += 1
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def go_up(row, column, grid)
|
38
|
+
i = 1
|
39
|
+
while true
|
40
|
+
index = grid.index_of(row)
|
41
|
+
|
42
|
+
square_candidate_value = grid.query("#{grid.letters[index - i]}#{column}")
|
43
|
+
return "#{grid.letters[index - i]}#{column}" if square_candidate_value == :empty
|
44
|
+
break unless square_candidate_value == :hit
|
45
|
+
i += 1
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Alpha
|
2
|
+
class Randomizer
|
3
|
+
|
4
|
+
def rand_row
|
5
|
+
return random.to_s
|
6
|
+
end
|
7
|
+
|
8
|
+
def rand_column
|
9
|
+
return @@LETTERS[random]
|
10
|
+
end
|
11
|
+
|
12
|
+
def rand_direction
|
13
|
+
return @@DIRECTIONS[Kernel.rand(2)]
|
14
|
+
end
|
15
|
+
|
16
|
+
def random
|
17
|
+
return Kernel.rand(10)
|
18
|
+
end
|
19
|
+
|
20
|
+
@@DIRECTIONS = {
|
21
|
+
0 => "horizontal",
|
22
|
+
1 => "vertical"
|
23
|
+
}
|
24
|
+
|
25
|
+
@@LETTERS = {
|
26
|
+
0 => "A",
|
27
|
+
1 => "B",
|
28
|
+
2 => "C",
|
29
|
+
3 => "D",
|
30
|
+
4 => "E",
|
31
|
+
5 => "F",
|
32
|
+
6 => "G",
|
33
|
+
7 => "H",
|
34
|
+
8 => "I",
|
35
|
+
9 => "J"
|
36
|
+
}
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
data/pkg/alpha-1.1.gem
ADDED
Binary file
|
data/pkg/alpha-1.2.gem
ADDED
Binary file
|
data/pkg/alpha-1.3.gem
ADDED
Binary file
|
data/pkg/alpha-1.4.gem
ADDED
Binary file
|
data/pkg/alpha-1.5.gem
ADDED
Binary file
|
data/pkg/paul-1.0.gem
ADDED
Binary file
|
@@ -0,0 +1,133 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + "/../spec_helper")
|
2
|
+
require 'alpha/alpha'
|
3
|
+
|
4
|
+
|
5
|
+
describe Alpha::Alpha, "placement" do
|
6
|
+
|
7
|
+
before(:each) do
|
8
|
+
@alpha = Alpha::Alpha.new
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should find a random column" do
|
12
|
+
|
13
|
+
@alpha.carrier_placement.should_not be(nil)
|
14
|
+
@alpha.battleship_placement.should_not be(nil)
|
15
|
+
@alpha.destroyer_placement.should_not be(nil)
|
16
|
+
@alpha.submarine_placement.should_not be(nil)
|
17
|
+
@alpha.patrolship_placement.should_not be(nil)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe Alpha::Alpha do
|
22
|
+
|
23
|
+
before(:each) do
|
24
|
+
@alpha = Alpha::Alpha.new
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should remember results of a miss" do
|
28
|
+
@alpha.target_result("A1", false, false)
|
29
|
+
@alpha.opponents_grid.query("A1").should == :miss
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should remember results of a hit" do
|
33
|
+
@alpha.target_result("A1", true, false)
|
34
|
+
@alpha.opponents_grid.query("A1").should == :hit
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should remember results of many hits" do
|
38
|
+
@alpha.target_result("A1", true, false)
|
39
|
+
@alpha.opponents_grid.query("A1").should == :hit
|
40
|
+
@alpha.target_result("A2", true, false)
|
41
|
+
@alpha.opponents_grid.query("A2").should == :hit
|
42
|
+
@alpha.target_result("A3", true, false)
|
43
|
+
@alpha.opponents_grid.query("A3").should == :hit
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should flag if it is a hit and not sink" do
|
47
|
+
@alpha.target_result("A1", true, false)
|
48
|
+
@alpha.red_zone.should == true
|
49
|
+
@alpha.target_result("A1", true, true)
|
50
|
+
@alpha.red_zone.should == false
|
51
|
+
end
|
52
|
+
|
53
|
+
it "should move right if it has a hit" do
|
54
|
+
@alpha.target_result("A1", true, false)
|
55
|
+
@alpha.opponents_grid.query("A1").should == :hit
|
56
|
+
@alpha.next_target.should == "A2"
|
57
|
+
end
|
58
|
+
|
59
|
+
it "should not move right if it is off the board" do
|
60
|
+
@alpha.target_result("A10", true, false)
|
61
|
+
@alpha.opponents_grid.query("A10").should == :hit
|
62
|
+
@alpha.next_target.should == "A9"
|
63
|
+
end
|
64
|
+
|
65
|
+
it "should not move right if it is off the board" do
|
66
|
+
@alpha.target_result("A10", true, false)
|
67
|
+
@alpha.opponents_grid.query("A10").should == :hit
|
68
|
+
@alpha.next_target.should == "A9"
|
69
|
+
end
|
70
|
+
|
71
|
+
it "should continue to move right if there are hits" do
|
72
|
+
@alpha.opponents_grid.place("A6", :hit)
|
73
|
+
@alpha.target_result("A5", true, false)
|
74
|
+
@alpha.next_target.should == "A7"
|
75
|
+
end
|
76
|
+
|
77
|
+
it "should continue to move right until it hits a miss, then try left" do
|
78
|
+
@alpha.opponents_grid.place("A6", :hit)
|
79
|
+
@alpha.opponents_grid.place("A7", :miss)
|
80
|
+
@alpha.target_result("A5", true, false)
|
81
|
+
@alpha.next_target.should == "A4"
|
82
|
+
end
|
83
|
+
|
84
|
+
it "should continue to move right until it hits a miss, then try left" do
|
85
|
+
@alpha.opponents_grid.place("A6", :hit)
|
86
|
+
@alpha.opponents_grid.place("A7", :hit)
|
87
|
+
@alpha.target_result("A5", true, false)
|
88
|
+
@alpha.next_target.should == "A8"
|
89
|
+
end
|
90
|
+
|
91
|
+
it "should continue left if right dowesn't work" do
|
92
|
+
@alpha.opponents_grid.place("A9", :hit)
|
93
|
+
@alpha.opponents_grid.place("A8", :hit)
|
94
|
+
@alpha.target_result("A10", true, false)
|
95
|
+
@alpha.next_target.should == "A7"
|
96
|
+
end
|
97
|
+
|
98
|
+
it "should go DOWN if left and right don't work" do
|
99
|
+
@alpha.opponents_grid.place("C5", :miss)
|
100
|
+
@alpha.opponents_grid.place("C3", :miss)
|
101
|
+
@alpha.target_result("C4", true, false)
|
102
|
+
@alpha.next_target.should == "D4"
|
103
|
+
end
|
104
|
+
|
105
|
+
it "should go UP if left and right and down don't work" do
|
106
|
+
@alpha.opponents_grid.place("C5", :miss)
|
107
|
+
@alpha.opponents_grid.place("C3", :miss)
|
108
|
+
@alpha.opponents_grid.place("D4", :miss)
|
109
|
+
@alpha.target_result("C4", true, false)
|
110
|
+
@alpha.next_target.should == "B4"
|
111
|
+
end
|
112
|
+
|
113
|
+
# it "should not go more than once in a bad direction" do
|
114
|
+
# @alpha.target_result("C4", true, false)
|
115
|
+
# @alpha.next_target.should == "C5"
|
116
|
+
# @alpha.target_result("C5", false, false)
|
117
|
+
# @alpha.next_target.should == "C3"
|
118
|
+
# end
|
119
|
+
|
120
|
+
|
121
|
+
end
|
122
|
+
|
123
|
+
# A B C D E F G H I J
|
124
|
+
# 1 | | | | | | | | | | |
|
125
|
+
# 2 | | | | | | | | | | |
|
126
|
+
# 3 | | | | | | | | | | |
|
127
|
+
# 4 | | | | | | | | | | |
|
128
|
+
# 5 | | | | | | | | | | |
|
129
|
+
# 6 | | | | | | | | | | |
|
130
|
+
# 7 | | | | | | | | | | |
|
131
|
+
# 8 | | | | | | | | | | |
|
132
|
+
# 9 | | | | | | | | | | |
|
133
|
+
# 10| | | | | | | | | | |
|
@@ -0,0 +1,78 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + "/../spec_helper")
|
2
|
+
require 'alpha/grid'
|
3
|
+
|
4
|
+
describe Alpha::Grid do
|
5
|
+
|
6
|
+
before(:each) do
|
7
|
+
@grid = Alpha::Grid.new
|
8
|
+
end
|
9
|
+
|
10
|
+
it "should mark the spot" do
|
11
|
+
@grid.place("A1", :miss)
|
12
|
+
|
13
|
+
@grid.query("A1").should == :miss
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should place a horizontal block on a grid" do
|
17
|
+
@grid.place_block("A1", "horizontal", 5)
|
18
|
+
|
19
|
+
@grid.query("A1").should == :placed
|
20
|
+
@grid.query("A2").should == :placed
|
21
|
+
@grid.query("A3").should == :placed
|
22
|
+
@grid.query("A4").should == :placed
|
23
|
+
@grid.query("A5").should == :placed
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should place a horizontal block on a grid" do
|
27
|
+
@grid.place_block("A2", "horizontal", 5)
|
28
|
+
|
29
|
+
@grid.query("A2").should == :placed
|
30
|
+
@grid.query("A3").should == :placed
|
31
|
+
@grid.query("A4").should == :placed
|
32
|
+
@grid.query("A5").should == :placed
|
33
|
+
@grid.query("A6").should == :placed
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should place a horizontal block on a grid" do
|
37
|
+
@grid.place_block("C3", "horizontal", 5)
|
38
|
+
|
39
|
+
@grid.query("C3").should == :placed
|
40
|
+
@grid.query("C4").should == :placed
|
41
|
+
@grid.query("C5").should == :placed
|
42
|
+
@grid.query("C6").should == :placed
|
43
|
+
@grid.query("C7").should == :placed
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should place a vertical block" do
|
47
|
+
@grid.place_block("A1", "vertical", 5)
|
48
|
+
|
49
|
+
@grid.query("A1").should == :placed
|
50
|
+
@grid.query("B1").should == :placed
|
51
|
+
@grid.query("C1").should == :placed
|
52
|
+
@grid.query("D1").should == :placed
|
53
|
+
@grid.query("E1").should == :placed
|
54
|
+
end
|
55
|
+
|
56
|
+
it "should place a block on a grid" do
|
57
|
+
@grid.can_place_block?("A1", "horizontal", 5).should == true
|
58
|
+
@grid.can_place_block?("A1", "vertical", 5).should == true
|
59
|
+
|
60
|
+
@grid.can_place_block?("A10", "horizontal", 5).should == false
|
61
|
+
@grid.can_place_block?("J10", "vertical", 5).should == false
|
62
|
+
end
|
63
|
+
|
64
|
+
it "should keep a queue of moves" do
|
65
|
+
@grid.place("A1", :hit)
|
66
|
+
|
67
|
+
@grid.moves.size.should == 1
|
68
|
+
@grid.moves[0].should == "A1"
|
69
|
+
end
|
70
|
+
|
71
|
+
it "should tell me it the last one was a hit" do
|
72
|
+
@grid.place("A1", :miss)
|
73
|
+
@grid.last_a_hit?.should == false
|
74
|
+
|
75
|
+
@grid.place("A2", :hit)
|
76
|
+
@grid.last_a_hit?.should == true
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + "/../spec_helper")
|
2
|
+
require "alpha/randomizer"
|
3
|
+
|
4
|
+
describe Alpha::Randomizer do
|
5
|
+
|
6
|
+
before(:each) do
|
7
|
+
@randomizer = Alpha::Randomizer.new
|
8
|
+
end
|
9
|
+
|
10
|
+
it "should randomize numbers between one and ten" do
|
11
|
+
Kernel.should_receive(:rand).with(10)
|
12
|
+
@randomizer.rand_row
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should randomize colomn numbers" do
|
16
|
+
Kernel.should_receive(:rand).with(10)
|
17
|
+
@randomizer.rand_column
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should randomize the direction" do
|
21
|
+
Kernel.should_receive(:rand).with(2)
|
22
|
+
@randomizer.rand_direction
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: alpha
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: "1.7"
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Paul W Pagel
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2008-12-01 00:00:00 -06:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description: The Alpha bot
|
17
|
+
email: paul@8thlight.com
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files: []
|
23
|
+
|
24
|
+
files:
|
25
|
+
- Battleship.Rakefile
|
26
|
+
- lib
|
27
|
+
- lib/alpha
|
28
|
+
- lib/alpha/alpha.rb
|
29
|
+
- lib/alpha/grid.rb
|
30
|
+
- lib/alpha/mover.rb
|
31
|
+
- lib/alpha/randomizer.rb
|
32
|
+
- pkg
|
33
|
+
- pkg/alpha-1.1.gem
|
34
|
+
- pkg/alpha-1.2.gem
|
35
|
+
- pkg/alpha-1.3.gem
|
36
|
+
- pkg/alpha-1.4.gem
|
37
|
+
- pkg/alpha-1.5.gem
|
38
|
+
- pkg/paul-1.0.gem
|
39
|
+
- Rakefile
|
40
|
+
- spec
|
41
|
+
- spec/alpha
|
42
|
+
- spec/alpha/alpha_spec.rb
|
43
|
+
- spec/alpha/grid_spec.rb
|
44
|
+
- spec/alpha/randomizer_spec.rb
|
45
|
+
- spec/spec_helper.rb
|
46
|
+
has_rdoc: false
|
47
|
+
homepage: http://sparring.rubyforge.org/
|
48
|
+
post_install_message:
|
49
|
+
rdoc_options: []
|
50
|
+
|
51
|
+
require_paths:
|
52
|
+
- lib
|
53
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
54
|
+
requirements:
|
55
|
+
- - ">="
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
version: "0"
|
58
|
+
version:
|
59
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
60
|
+
requirements:
|
61
|
+
- - ">="
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
version: "0"
|
64
|
+
version:
|
65
|
+
requirements: []
|
66
|
+
|
67
|
+
rubyforge_project: sparring
|
68
|
+
rubygems_version: 1.3.0
|
69
|
+
signing_key:
|
70
|
+
specification_version: 2
|
71
|
+
summary: Battleship Player:alpha
|
72
|
+
test_files: []
|
73
|
+
|