alpha 1.7
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/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
|
+
|