ensign_erratic 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
@@ -0,0 +1,41 @@
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 = "ensign_erratic"
13
+ PKG_VERSION = "1.0.2"
14
+
15
+ spec = Gem::Specification.new do |s|
16
+ s.name = PKG_NAME
17
+ s.version = PKG_VERSION
18
+ s.files = FileList['**/*'].to_a
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:Ensign Erratic"
24
+ s.rubyforge_project = "sparring"
25
+ s.homepage = "http://sparring.rubyforge.org/"
26
+
27
+ s.description = "Relatively simple Battleship AI that utilizes random targeting and ship placement"
28
+ s.author = "Jim Suchy"
29
+ s.email = "jim@8thlight.com"
30
+ end
31
+
32
+ Rake::GemPackageTask.new(spec) do |pkg|
33
+ pkg.need_zip = false
34
+ pkg.need_tar = false
35
+ end
36
+
37
+ desc "Submit your player"
38
+ task :submit do
39
+ submitter = BattleshipTournament::Submit.new(PKG_NAME)
40
+ submitter.submit
41
+ end
@@ -0,0 +1,56 @@
1
+ module EnsignErratic
2
+ class Board
3
+
4
+ attr_reader :placed_ship_sectors
5
+
6
+ def initialize
7
+ @placed_ship_sectors = []
8
+ end
9
+
10
+ Row = %w{ A B C D E F G H I J }
11
+ PlacementRegex = /(([A-J])([1-9]|10)) (HORIZONTAL|VERTICAL)/i
12
+ CoordinatesRegex = /([A-J])([1-9|10])/i
13
+
14
+ def clear
15
+ @placed_ship_sectors = []
16
+ end
17
+
18
+ def place_ship(size, placement)
19
+ @possible_ship_sectors = []
20
+
21
+ match = PlacementRegex.match(placement)
22
+ start_coordinates = match[1]
23
+ row = match[2]
24
+ column = match[3].to_i
25
+ orientation = match[4].upcase
26
+
27
+ add_sector(start_coordinates)
28
+ (size - 1).times do |index|
29
+ new_coordinates = orientation == "HORIZONTAL" ? move_right(row, column, index) : move_down(row, column, index)
30
+ add_sector(new_coordinates)
31
+ end
32
+
33
+ @placed_ship_sectors.concat(@possible_ship_sectors)
34
+ end
35
+
36
+ private ###############################################
37
+
38
+ def add_sector(coordinates)
39
+ raise "Ships overlap at #{coordinates}" if @placed_ship_sectors.include?(coordinates)
40
+ @possible_ship_sectors << coordinates
41
+ end
42
+
43
+ def move_right(row, column, number)
44
+ new_column = column + number + 1
45
+ raise "Ship would fall off right edge" if new_column > 10
46
+ return "#{row}#{new_column}"
47
+ end
48
+
49
+ def move_down(row, column, number)
50
+ new_row = Row[Row.index(row) + number + 1]
51
+ raise "Ship would fall off bottom edge" if new_row.nil?
52
+ return "#{new_row}#{column}"
53
+ end
54
+
55
+ end
56
+ end
@@ -0,0 +1,206 @@
1
+ require 'ensign_erratic/board'
2
+
3
+ module EnsignErratic
4
+
5
+ class EnsignErratic
6
+
7
+ attr_reader :carrier_placement, :battleship_placement, :destroyer_placement, :submarine_placement, :patrolship_placement
8
+ attr_reader :opponent_name, :result, :disqualification_reason
9
+ attr_reader :turns, :enemy_targets, :hits
10
+
11
+ def name
12
+ return "Ensign Erratic"
13
+ end
14
+
15
+ def new_game(opponent_name)
16
+ @opponent_name = opponent_name
17
+ reset
18
+ setup_ships
19
+ end
20
+
21
+ def next_target
22
+ return seek_and_destroy? ? smart_target : random_target
23
+ end
24
+
25
+ def target_result(coordinates, was_hit, ship_sunk)
26
+ @turns << coordinates
27
+ @hits << coordinates if was_hit
28
+ calculate_seek_and_destroy(was_hit, ship_sunk)
29
+ end
30
+
31
+ def enemy_targeting(coordinates)
32
+ @enemy_targets << coordinates
33
+ end
34
+
35
+ def game_over(result, disqualification_reason=nil)
36
+ @result = result
37
+ @disqualification_reason = disqualification_reason
38
+ end
39
+
40
+ def seek_and_destroy?
41
+ return @seek_and_destroy
42
+ end
43
+
44
+ private ###############################################
45
+
46
+ def reset
47
+ @turns = []
48
+ @hits = []
49
+ @enemy_targets = []
50
+ @seek_and_destroy = false
51
+ @board = Board.new
52
+ Kernel.srand
53
+ end
54
+
55
+ def random_target
56
+ coordinates = random_coordinates
57
+ return already_targeted?(coordinates) ? random_target : coordinates
58
+ end
59
+
60
+ def smart_target
61
+ row, column = last_hit_coordinates
62
+
63
+ if @seek_and_destroy_direction == "left"
64
+ return seek_left(row, column)
65
+ elsif @seek_and_destroy_direction == "right"
66
+ return target_right(row, column.to_i)
67
+ elsif @seek_and_destroy_direction == "up"
68
+ return seek_up(row, column)
69
+ elsif @seek_and_destroy_direction == "down"
70
+ return target_down(row, column)
71
+ end
72
+ end
73
+
74
+ def target_left(row, column)
75
+ return "#{row}#{column.to_i - 1}"
76
+ end
77
+
78
+ def target_right(row, column)
79
+ column += 1
80
+ next_try = "#{row}#{column}"
81
+
82
+ while already_targeted?(next_try)
83
+ column += 1
84
+ next_try = "#{row}#{column}"
85
+ end
86
+
87
+ return next_try
88
+ end
89
+
90
+ def target_up(row, column)
91
+ row_above = Board::Row[Board::Row.index(row) - 1]
92
+ return "#{row_above}#{column}"
93
+ end
94
+
95
+ def target_down(row, column)
96
+ row_below = Board::Row[Board::Row.index(row) + 1]
97
+ next_try = "#{row_below}#{column}"
98
+ while already_targeted?(next_try)
99
+ row_below = Board::Row[Board::Row.index(row_below) + 1]
100
+ next_try = "#{row_below}#{column}"
101
+ end
102
+
103
+ return next_try
104
+ end
105
+
106
+ def seek_left(row, column)
107
+ next_target = target_left(row, column)
108
+ if valid_target?(next_target)
109
+ return next_target
110
+ else
111
+ return target_right(row, column.to_i)
112
+ end
113
+ end
114
+
115
+ def seek_up(row, column)
116
+ next_target = target_up(row, column)
117
+ if valid_target?(next_target)
118
+ return next_target
119
+ else
120
+ return target_down(row, column)
121
+ end
122
+ end
123
+
124
+ def calculate_seek_and_destroy(was_hit, ship_sunk)
125
+ if @seek_and_destroy
126
+ if ship_sunk
127
+ @seek_and_destroy = false
128
+ elsif !was_hit
129
+ if @seek_and_destroy_direction == "left"
130
+ @seek_and_destroy_direction = "right"
131
+ elsif @seek_and_destroy_direction == "right"
132
+ @seek_and_destroy_direction = "up"
133
+ elsif @seek_and_destroy_direction == "up"
134
+ @seek_and_destroy_direction = "down"
135
+ end
136
+ end
137
+ elsif was_hit
138
+ @seek_and_destroy = true
139
+ @seek_and_destroy_direction = "left"
140
+ end
141
+ end
142
+
143
+ def valid_target?(coordinates)
144
+ row, column = row_and_column_for(coordinates)
145
+ return false if column.to_i <= 0
146
+ return false if already_targeted?(coordinates)
147
+ return true
148
+ end
149
+
150
+ def already_targeted?(coordinates)
151
+ return @turns.include?(coordinates)
152
+ end
153
+
154
+ def last_hit_coordinates
155
+ return row_and_column_for(@hits.last)
156
+ end
157
+
158
+ def row_and_column_for(coordinates)
159
+ match = Board::CoordinatesRegex.match(coordinates)
160
+ return match[1], match[2]
161
+ end
162
+
163
+ def setup_ships
164
+ valid_placement = false
165
+ while !valid_placement
166
+ randomize_ship_placement
167
+ begin
168
+ place_ships
169
+ valid_placement = true
170
+ rescue Exception => e
171
+ # puts e.message
172
+ end
173
+ end
174
+ end
175
+
176
+ def randomize_ship_placement
177
+ @carrier_placement = random_ship_placement
178
+ @battleship_placement = random_ship_placement
179
+ @destroyer_placement = random_ship_placement
180
+ @submarine_placement = random_ship_placement
181
+ @patrolship_placement = random_ship_placement
182
+ end
183
+
184
+ def random_ship_placement
185
+ return "#{random_coordinates} #{random_orientation}"
186
+ end
187
+
188
+ def random_coordinates
189
+ return "#{Board::Row[Kernel.rand(10)]}#{Kernel.rand(10)+1}"
190
+ end
191
+
192
+ def random_orientation
193
+ return Kernel.rand(2) == 1 ? "horizontal" : "vertical"
194
+ end
195
+
196
+ def place_ships
197
+ @board.clear
198
+ @board.place_ship(5, @carrier_placement)
199
+ @board.place_ship(4, @battleship_placement)
200
+ @board.place_ship(3, @destroyer_placement)
201
+ @board.place_ship(3, @submarine_placement)
202
+ @board.place_ship(2, @patrolship_placement)
203
+ end
204
+
205
+ end
206
+ end
@@ -0,0 +1,55 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper'))
2
+ require 'ensign_erratic/board'
3
+
4
+ describe EnsignErratic::Board do
5
+
6
+ before(:each) do
7
+ @board = EnsignErratic::Board.new
8
+ end
9
+
10
+ it "should place ship horizontally" do
11
+ @board.place_ship(5, "A1 horizontal")
12
+
13
+ @board.placed_ship_sectors.size.should == 5
14
+ @board.placed_ship_sectors.should include("A1")
15
+ @board.placed_ship_sectors.should include("A2")
16
+ @board.placed_ship_sectors.should include("A3")
17
+ @board.placed_ship_sectors.should include("A4")
18
+ @board.placed_ship_sectors.should include("A5")
19
+ end
20
+
21
+ it "should place ship vertically" do
22
+ @board.place_ship(3, "B1 vertical")
23
+
24
+ @board.placed_ship_sectors.size.should == 3
25
+ @board.placed_ship_sectors.should include("B1")
26
+ @board.placed_ship_sectors.should include("C1")
27
+ @board.placed_ship_sectors.should include("D1")
28
+ end
29
+
30
+ it "should raise exception if ships would overlap" do
31
+ @board.place_ship(5, "B2 horizontal")
32
+
33
+ lambda {@board.place_ship(3, "A3 vertical")}.should raise_error("Ships overlap at B3")
34
+ @board.placed_ship_sectors.size.should == 5
35
+ end
36
+
37
+ it "should raise exception if ship would fall off right edge" do
38
+ lambda {@board.place_ship(5, "A9 horizontal")}.should raise_error("Ship would fall off right edge")
39
+ @board.placed_ship_sectors.size.should == 0
40
+ end
41
+
42
+ it "should raise exception if ship would fall off bottom edge" do
43
+ lambda {@board.place_ship(5, "I3 vertical")}.should raise_error("Ship would fall off bottom edge")
44
+ @board.placed_ship_sectors.size.should == 0
45
+ end
46
+
47
+ it "should clear placed ship sectors" do
48
+ @board.place_ship(3, "B1 vertical")
49
+
50
+ @board.clear
51
+
52
+ @board.placed_ship_sectors.size.should == 0
53
+ end
54
+
55
+ end
@@ -0,0 +1,204 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper'))
2
+ require 'ensign_erratic/ensign_erratic'
3
+ require 'ensign_erratic/board'
4
+
5
+ describe EnsignErratic do
6
+
7
+ before(:each) do
8
+ EnsignErratic::Board.stub!(:new).and_return(mock("board", :place_ship => nil, :clear => nil))
9
+
10
+ @player = EnsignErratic::EnsignErratic.new
11
+ @player.new_game("some player")
12
+ end
13
+
14
+ it "should have a name" do
15
+ @player.name.should == "Ensign Erratic"
16
+ end
17
+
18
+ it "should store opponent's name" do
19
+ @player.new_game("your opponent")
20
+
21
+ @player.opponent_name.should == "your opponent"
22
+ end
23
+
24
+ it "should seed rng for each new game" do
25
+ Kernel.should_receive(:srand)
26
+
27
+ @player.new_game("foo")
28
+ end
29
+
30
+ it "should have game over" do
31
+ @player.game_over(:disqualified, "no reason")
32
+
33
+ @player.result.should == :disqualified
34
+ @player.disqualification_reason.should == "no reason"
35
+ end
36
+
37
+ it "should target a random location" do
38
+ Kernel.should_receive(:rand).with(10).twice.and_return(3, 7)
39
+
40
+ @player.next_target.should == "D8"
41
+ end
42
+
43
+ it "should not fire at a location that has already been fired at" do
44
+ @player.target_result("D8", false, nil)
45
+ @player.target_result("C6", false, nil)
46
+ Kernel.should_receive(:rand).with(10).and_return(3, 7, 2, 5, 1, 6)
47
+
48
+ @player.next_target.should == "B7"
49
+ end
50
+
51
+ it "should store whether opponent's target was a hit" do
52
+ @player.target_result("A1", true, nil)
53
+ @player.target_result("C8", false, nil)
54
+ @player.target_result("F4", true, nil)
55
+
56
+ @player.hits.size.should == 2
57
+ @player.hits[0].should == "A1"
58
+ @player.hits[1].should == "F4"
59
+ end
60
+
61
+ it "should store taken turns" do
62
+ @player.target_result("A1", true, nil)
63
+ @player.target_result("C8", false, nil)
64
+ @player.target_result("F4", true, nil)
65
+
66
+ @player.turns[0].should == "A1"
67
+ @player.turns[1].should == "C8"
68
+ @player.turns[2].should == "F4"
69
+ @player.hits[0].should == "A1"
70
+ @player.hits[1].should == "F4"
71
+ end
72
+
73
+ it "should store opponent's targets" do
74
+ @player.enemy_targeting("B7")
75
+ @player.enemy_targeting("A3")
76
+
77
+ @player.enemy_targets[0].should == "B7"
78
+ @player.enemy_targets[1].should == "A3"
79
+ end
80
+
81
+ it "should go to seek and destroy mode after first hit of a ship" do
82
+ @player.target_result("A1", true, nil)
83
+
84
+ @player.seek_and_destroy?.should == true
85
+ end
86
+
87
+ it "should stay in seek and destroy after missing next shot" do
88
+ @player.target_result("A7", true, nil)
89
+ @player.target_result("A6", false, nil)
90
+
91
+ @player.seek_and_destroy?.should == true
92
+ end
93
+
94
+ it "should go out of seek and destroy mode after ship is sunk" do
95
+ @player.target_result("A2", true, nil)
96
+
97
+ @player.target_result("A1", true, :patrolship)
98
+
99
+ @player.seek_and_destroy?.should == false
100
+ end
101
+
102
+ it "should first target to the left of the hit" do
103
+ @player.target_result("D3", true, nil)
104
+
105
+ @player.next_target.should == "D2"
106
+ end
107
+
108
+ it "should target right if missed to the left" do
109
+ @player.target_result("D3", true, nil)
110
+ @player.target_result("D2", false, nil)
111
+
112
+ @player.next_target.should == "D4"
113
+ end
114
+
115
+ it "should continue to the left if hit" do
116
+ @player.target_result("D3", true, nil)
117
+ @player.target_result("D2", true, nil)
118
+
119
+ @player.next_target.should == "D1"
120
+ end
121
+
122
+ it "should target to the right if hit was on the left edge" do
123
+ @player.target_result("A1", true, nil)
124
+
125
+ @player.next_target.should == "A2"
126
+ end
127
+
128
+ it "should target to the right if sector to left of hit was already targeted" do
129
+ @player.target_result("D4", false, nil)
130
+ @player.target_result("D5", true, nil)
131
+
132
+ @player.next_target.should == "D6"
133
+ end
134
+
135
+ it "should target up if no hits to left or right" do
136
+ @player.target_result("C5", true, nil)
137
+ @player.target_result("C4", false, nil)
138
+ @player.target_result("C6", false, nil)
139
+
140
+ @player.next_target.should == "B5"
141
+ end
142
+
143
+ it "should target down if no hits left, right or up" do
144
+ @player.target_result("C5", true, nil)
145
+ @player.target_result("C4", false, nil)
146
+ @player.target_result("C6", false, nil)
147
+ @player.target_result("B5", false, nil)
148
+
149
+ @player.next_target.should == "D5"
150
+ end
151
+
152
+ it "should target down after hitting one above then missing above that" do
153
+ @player.target_result("C5", true, nil)
154
+ @player.target_result("C4", false, nil)
155
+ @player.target_result("C6", false, nil)
156
+ @player.target_result("B5", true, nil)
157
+ @player.target_result("A5", false, nil)
158
+
159
+ @player.next_target.should == "D5"
160
+ end
161
+
162
+ it "should target down if sector above hit was already targeted" do
163
+ @player.target_result("B5", false, nil)
164
+ @player.target_result("C5", true, nil)
165
+ @player.target_result("C4", false, nil)
166
+
167
+ @player.next_target.should == "C6"
168
+ end
169
+
170
+ it "should target right if sector above and below already targeted" do
171
+ @player.target_result("B5", false, nil)
172
+ @player.target_result("D5", false, nil)
173
+ @player.target_result("C4", false, nil)
174
+ @player.target_result("C5", true, nil)
175
+
176
+ @player.next_target.should == "C6"
177
+ end
178
+
179
+ # it "should not try to the right when already know the ship is vertical" do
180
+ # @player.target_result("C8", true, nil)
181
+ # @player.target_result("C7", false, nil)
182
+ # @player.target_result("B8", true, nil)
183
+ # @player.target_result("A8", false, nil)
184
+ #
185
+ # @player.next_target.should == "D8"
186
+ # end
187
+
188
+ # it "should handle initial hit in top corner" do
189
+ # @player.target_result("A10", true, nil)
190
+ # @player.target_result("A9", false, nil)
191
+ #
192
+ # @player.next_target.should == "B10"
193
+ # end
194
+ #
195
+ # it "should handle initial hit on right edge" do
196
+ # @player.target_result("D10", true, nil)
197
+ # @player.target_result("D9", false, nil)
198
+ # @player.target_result("C10", false, nil)
199
+ #
200
+ # @player.next_target.should == "E10"
201
+ # end
202
+
203
+ end
204
+
@@ -0,0 +1 @@
1
+ $: << File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib'))
metadata ADDED
@@ -0,0 +1,62 @@
1
+ --- !ruby/object:Gem::Specification
2
+ extensions: []
3
+ homepage: http://sparring.rubyforge.org/
4
+ executables: []
5
+ version: !ruby/object:Gem::Version
6
+ version: 1.0.2
7
+ post_install_message:
8
+ date: 2008-12-01 06:00:00 +00:00
9
+ files:
10
+ - Battleship.Rakefile
11
+ - lib
12
+ - pkg
13
+ - Rakefile
14
+ - spec
15
+ - lib/ensign_erratic
16
+ - lib/ensign_erratic/board.rb
17
+ - lib/ensign_erratic/ensign_erratic.rb
18
+ - pkg/ensign_erratic-1.0.0.1.gem
19
+ - pkg/ensign_erratic-1.0.0.2.gem
20
+ - pkg/ensign_erratic-1.0.0.3.gem
21
+ - pkg/ensign_erratic-1.0.0.gem
22
+ - pkg/ensign_erratic-1.0.1.gem
23
+ - spec/ensign_erratic
24
+ - spec/spec_helper.rb
25
+ - spec/ensign_erratic/board_spec.rb
26
+ - spec/ensign_erratic/ensign_erratic_spec.rb
27
+ rubygems_version: 1.2.0
28
+ rdoc_options: []
29
+ signing_key:
30
+ cert_chain: []
31
+ name: ensign_erratic
32
+ has_rdoc: false
33
+ platform: ruby
34
+ summary: Battleship Player:Ensign Erratic
35
+ default_executable:
36
+ bindir: bin
37
+ required_rubygems_version: !ruby/object:Gem::Requirement
38
+ version:
39
+ requirements:
40
+ - - '>='
41
+ - !ruby/object:Gem::Version
42
+ version: !str 0
43
+ required_ruby_version: !ruby/object:Gem::Requirement
44
+ version:
45
+ requirements:
46
+ - - '>='
47
+ - !ruby/object:Gem::Version
48
+ version: !str 0
49
+ require_paths:
50
+ - lib
51
+ specification_version: 2
52
+ test_files: []
53
+ dependencies: []
54
+ description: Relatively simple Battleship AI that utilizes random targeting and ship
55
+ placement
56
+ email: jim@8thlight.com
57
+ authors:
58
+ - Jim Suchy
59
+ extra_rdoc_files: []
60
+ requirements: []
61
+ rubyforge_project: sparring
62
+ autorequire: