commodore_cox 1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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,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 = "commodore_cox"
13
+ PKG_VERSION = "1.0"
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:Commodore Cox"
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 = "Commodore Cox"
34
+ s.author = "Andrew Cox"
35
+ s.email = "3coxy4@gmail.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
@@ -0,0 +1,264 @@
1
+ class Array
2
+ def random
3
+ self[size*rand]
4
+ end
5
+ end
6
+
7
+ module CommodoreCox
8
+
9
+ class AI
10
+
11
+ SHIPS = {
12
+ :carrier => 5,
13
+ :battleship => 4,
14
+ :destroyer => 3,
15
+ :submarine => 3,
16
+ :patrolship => 2
17
+ }
18
+
19
+ OPPOSITE = {
20
+ :north => :south,
21
+ :east => :west,
22
+ :south => :north,
23
+ :west => :east
24
+ }
25
+
26
+ attr_accessor :placestack, :direction, :hitstack
27
+
28
+ def setup
29
+ @hitstack = []
30
+ @placestack = []
31
+ @taken_squares = []
32
+ @backtracking_stack = []
33
+ @direction = nil
34
+ a = (1..10).to_a
35
+ a.each do |x|
36
+ a.each do |y|
37
+ Grid[y,x] = Square.new(y,x)
38
+ end
39
+ end
40
+ end
41
+
42
+ def hit_in_progress?
43
+ @hitstack.any?
44
+ end
45
+
46
+ def backtracking?
47
+ @backtracking_stack.any?
48
+ end
49
+
50
+ def direction?
51
+ !@direction.nil?
52
+ end
53
+
54
+ def ships
55
+ SHIPS
56
+ end
57
+
58
+ def opposite
59
+ OPPOSITE
60
+ end
61
+
62
+ def last_placement
63
+ @placestack.last.to_s
64
+ end
65
+
66
+ def change_direction
67
+ @direction = opposite[@direction]
68
+ end
69
+
70
+ def pick_from_random_neighbour(square,set_direction=true)
71
+ neighbour = nil
72
+ neighbours = square.neighbours
73
+ if neighbours.any?
74
+ neighbour = neighbours.random
75
+ @direction = neighbour[0] if set_direction
76
+ neighbour = neighbour[1]
77
+ Grid.delete(neighbour)
78
+ end
79
+ neighbour
80
+ end
81
+
82
+ def pick_from_backtrack
83
+ return nil unless backtracking?
84
+
85
+ square = if direction?
86
+ @backtracking_stack.last.send(@direction)
87
+ else
88
+ pick_from_random_neighbour(@backtracking_stack.last,false)
89
+ end
90
+ if square.nil?
91
+ @backtracking_stack.pop
92
+ square = pick_from_backtrack
93
+ end
94
+ square
95
+ end
96
+
97
+ def pick_from_direction
98
+ square = @hitstack.last.send(@direction)
99
+ if square.nil?
100
+ change_direction
101
+ square = @hitstack.first.send(@direction)
102
+ if square.nil?
103
+ @direction = nil
104
+ square = smart_next_square
105
+ end
106
+ end
107
+ square
108
+ end
109
+
110
+ def smart_next_square
111
+ return nil unless hit_in_progress?
112
+ square = if backtracking?
113
+ pick_from_backtrack
114
+ elsif direction?
115
+ pick_from_direction
116
+ else
117
+ pick_from_random_neighbour(@hitstack.last)
118
+ end
119
+ unless square.nil?
120
+ Grid.delete(square)
121
+ end
122
+ square
123
+ end
124
+
125
+ def next_target(force=nil)
126
+ square = nil
127
+ unless force.nil?
128
+ square = Grid[force]
129
+ Grid.delete(force)
130
+ else
131
+ square = smart_next_square || Grid.pick
132
+ end
133
+ @placestack << square
134
+ square.to_s unless square.nil?
135
+ end
136
+
137
+ def target_result(coordinates, was_hit, ship_sunk)
138
+ if was_hit
139
+ @hitstack << @placestack.last
140
+ end
141
+ if ship_sunk
142
+ # assuming the ship sunk was the last n squares on the stack
143
+ SHIPS[ship_sunk].times{@hitstack.pop}
144
+ @backtracking_stack = @hitstack.reverse
145
+ if @hitstack.size > 1
146
+ change_direction
147
+ else
148
+ @direction = nil
149
+ end
150
+ end
151
+ end
152
+
153
+
154
+
155
+ def place(ship,square=false,direction=false)
156
+ length = SHIPS[ship]
157
+ directions = {:x=>"horizontal",:y=>"vertical"}
158
+ direction ||= directions.keys.random
159
+ square = if square
160
+ Grid[square]
161
+ else
162
+ Grid.slice(length,direction,@taken_squares).random
163
+ end
164
+ @taken_squares << square.to_s
165
+ trying_squares = []
166
+ next_square = square
167
+ (length-1).times do
168
+ next_square = next_square.send(directions[direction])
169
+ return place(ship) if @taken_squares.include?(next_square.to_s)
170
+ trying_squares << next_square.to_s
171
+ end
172
+ @taken_squares += trying_squares
173
+ "#{square.to_s} #{directions[direction]}"
174
+ end
175
+
176
+ end
177
+
178
+ class Grid
179
+
180
+ @@ROWS = %w{ A B C D E F G H I J }
181
+ @@squares = {}
182
+
183
+ class << self
184
+ def [](y,x=nil)
185
+ y = index(y.ceil) unless y.is_a? String
186
+ x = x.ceil unless x.nil?
187
+ squares["#{y}#{x}"]
188
+ end
189
+ def []=(y,x,s)
190
+ y = index(y.ceil) unless y.is_a? String
191
+ squares["#{y}#{x.ceil}"] = s
192
+ end
193
+
194
+ def random
195
+ s = squares.to_a
196
+ s.random unless s.nil?
197
+ end
198
+
199
+ def pick
200
+ s = random
201
+ unless s.nil?
202
+ delete(s[0])
203
+ s[1]
204
+ end
205
+ end
206
+
207
+ def slice(length,direction,taken_squares=[])
208
+ n = 9 - length
209
+ @@squares.values.find_all do |square|
210
+ (1..n).include?(square.send(direction)) && !taken_squares.include?(square.to_s)
211
+ end
212
+ end
213
+
214
+ def delete(square)
215
+ @@squares.delete(square.to_s)
216
+ end
217
+
218
+ def squares
219
+ @@squares
220
+ end
221
+ def index(n)
222
+ @@ROWS[n-1] unless n < 1
223
+ end
224
+
225
+ end
226
+
227
+ end
228
+
229
+ class Square
230
+
231
+ attr_accessor :y, :x
232
+
233
+ def initialize(y,x)
234
+ @y, @x = y, x
235
+ end
236
+
237
+ def north
238
+ Grid[y-1,x]
239
+ end
240
+ def east
241
+ Grid[y,x+1]
242
+ end
243
+ def south
244
+ Grid[y+1,x]
245
+ end
246
+ def west
247
+ Grid[y,x-1]
248
+ end
249
+
250
+ # for use in ship placements
251
+ alias :horizontal :east
252
+ alias :vertical :south
253
+
254
+ def neighbours
255
+ h = { :north => north, :east => east, :west => west, :south => south }
256
+ h.delete_if{ |k,v| v.nil? }
257
+ h.to_a
258
+ end
259
+ def to_s
260
+ "#{Grid.index(y)}#{x}"
261
+ end
262
+ end
263
+
264
+ end
@@ -0,0 +1,55 @@
1
+ require 'commodore_cox/ai'
2
+ module CommodoreCox
3
+
4
+ class CommodoreCox
5
+
6
+ def new_game(opponent_name)
7
+ @AI.setup
8
+ end
9
+
10
+ def carrier_placement
11
+ @AI.place :carrier
12
+ end
13
+
14
+ def battleship_placement
15
+ @AI.place :battleship
16
+ end
17
+
18
+ def destroyer_placement
19
+ @AI.place :destroyer
20
+ end
21
+
22
+ def submarine_placement
23
+ @AI.place :submarine
24
+ end
25
+
26
+ def patrolship_placement
27
+ @AI.place :patrolship
28
+ end
29
+
30
+ def next_target
31
+ @AI.next_target
32
+ end
33
+
34
+ def target_result(coordinates, was_hit, ship_sunk)
35
+ @AI.target_result(coordinates, was_hit, ship_sunk)
36
+ end
37
+
38
+ def enemy_targeting(coordinates)
39
+ end
40
+
41
+ def game_over(result, disqualification_reason=nil)
42
+ end
43
+
44
+ # Non API methods #####################################
45
+
46
+ attr_reader :opponent, :targets, :enemy_targeted_sectors, :result, :disqualification_reason #:nodoc:
47
+
48
+ def initialize #:nodoc:
49
+ @AI = AI.new
50
+ @AI.setup
51
+ end
52
+
53
+ end
54
+
55
+ end
@@ -0,0 +1,108 @@
1
+ require File.expand_path(File.dirname(__FILE__) + "/../spec_helper")
2
+ require 'commodore_cox/ai'
3
+
4
+ describe CommodoreCox::AI do
5
+
6
+ before(:each) do
7
+ @ai = CommodoreCox::AI.new
8
+ @ai.setup
9
+ end
10
+
11
+ it "should create random placements for all ships" do
12
+ @ai.ships.keys.each do |ship|
13
+ @ai.place(ship).should match /^[A-Z][0-9]{1,2}\s(horizontal|vertical)$/
14
+ end
15
+ end
16
+
17
+ it "should not overlap 2 ships" do
18
+ @ai.place(:carrier,"A6",:y).should eql("A6 vertical")
19
+ @ai.place(:destroyer,"D5",:x).should_not eql("D5 horizontal")
20
+ end
21
+
22
+ it "should generate a next target 100 times" do
23
+ 100.times{ @ai.next_target.should_not be_nil }
24
+ end
25
+
26
+ it "should not generate a 101st target" do
27
+ 100.times{ @ai.next_target.should_not be_nil }
28
+ @ai.next_target.should be_nil
29
+ end
30
+
31
+ it "should choose a neighbour on hit" do
32
+ @ai.next_target
33
+ square = @ai.placestack.last
34
+ neighbours = square.neighbours.map{ |e| e[1].to_s }
35
+ @ai.target_result(square.to_s,true,nil)
36
+ neighbours.should include(@ai.next_target)
37
+ end
38
+
39
+ it "should choose a neighbour in the correct direction in 2 successive hits" do
40
+ square = nil
41
+ 2.times do
42
+ @ai.next_target
43
+ square = @ai.placestack.last
44
+ @ai.target_result("",true,nil)
45
+ end
46
+ @ai.direction.should_not be_nil
47
+ next_square = square.send(@ai.direction)
48
+ @ai.next_target.should eql(next_square.to_s) unless next_square.nil?
49
+ end
50
+
51
+ it "should change direction and go back to the first hit on 2 successive hits, then a miss" do
52
+ @ai.next_target("E5")
53
+ @ai.target_result("",true,nil)
54
+ @ai.next_target
55
+ @ai.target_result("",true,nil)
56
+ direction = @ai.direction
57
+ @ai.next_target
58
+ @ai.target_result("",false,nil)
59
+ @ai.next_target
60
+ @ai.direction.should eql(@ai.opposite[direction])
61
+ end
62
+
63
+ it "should reset on sink" do
64
+ @ai.next_target("I2")
65
+ @ai.target_result("",true,nil)
66
+ @ai.next_target("J2")
67
+ @ai.target_result("",true,nil)
68
+ @ai.direction = :south
69
+ @ai.next_target
70
+ @ai.last_placement.should eql("H2")
71
+ @ai.target_result("",true,nil)
72
+ @ai.next_target
73
+ @ai.last_placement.should eql("G2")
74
+ @ai.target_result("",true,:battleship)
75
+ @ai.hitstack.size.should eql(0)
76
+ end
77
+
78
+ it "should backtrack to one of first hit's neighbours after sink with remaining hit" do
79
+ @ai.next_target("A2")
80
+ @ai.target_result("",true,nil)
81
+ @ai.next_target("A3")
82
+ @ai.target_result("",true,nil)
83
+ @ai.direction = :east
84
+ @ai.next_target("A4")
85
+ @ai.target_result("",true,nil)
86
+ @ai.next_target("A5")
87
+ @ai.target_result("",true,:submarine)
88
+ @ai.backtracking?.should be_true
89
+ @ai.next_target
90
+ ["A1","B2"].should include(@ai.last_placement)
91
+ end
92
+
93
+ it "should backtrack in reverse direction after sink with remaining hits" do
94
+ @ai.next_target("A2")
95
+ @ai.target_result("",true,nil)
96
+ @ai.next_target("A3")
97
+ @ai.target_result("",true,nil)
98
+ @ai.direction = :east
99
+ @ai.next_target("A4")
100
+ @ai.target_result("",true,nil)
101
+ @ai.next_target("A5")
102
+ @ai.target_result("",true,:patrolship)
103
+ @ai.backtracking?.should be_true
104
+ @ai.next_target
105
+ @ai.last_placement.should eql("A1")
106
+ end
107
+
108
+ end
@@ -0,0 +1,10 @@
1
+ require File.expand_path(File.dirname(__FILE__) + "/../spec_helper")
2
+ require 'commodore_cox/commodore_cox'
3
+
4
+ describe CommodoreCox::CommodoreCox do
5
+
6
+ before(:each) do
7
+ @cc = CommodoreCox::CommodoreCox.new
8
+ end
9
+
10
+ end
@@ -0,0 +1,4 @@
1
+ $: << File.expand_path(File.dirname(__FILE__) + "/../lib")
2
+
3
+ require 'rubygems'
4
+ require 'spec'
metadata ADDED
@@ -0,0 +1,64 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: commodore_cox
3
+ version: !ruby/object:Gem::Version
4
+ version: "1.0"
5
+ platform: ruby
6
+ authors:
7
+ - Andrew Cox
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2008-11-21 00:00:00 +00:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: Commodore Cox
17
+ email: 3coxy4@gmail.com
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files: []
23
+
24
+ files:
25
+ - Battleship.Rakefile
26
+ - lib
27
+ - lib/commodore_cox
28
+ - lib/commodore_cox/ai.rb
29
+ - lib/commodore_cox/commodore_cox.rb
30
+ - pkg
31
+ - Rakefile
32
+ - spec
33
+ - spec/commodore_cox
34
+ - spec/commodore_cox/ai_spec.rb
35
+ - spec/commodore_cox/commodore_cox_spec.rb
36
+ - spec/spec_helper.rb
37
+ has_rdoc: false
38
+ homepage: http://sparring.rubyforge.org/
39
+ post_install_message:
40
+ rdoc_options: []
41
+
42
+ require_paths:
43
+ - lib
44
+ required_ruby_version: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ version: "0"
49
+ version:
50
+ required_rubygems_version: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: "0"
55
+ version:
56
+ requirements: []
57
+
58
+ rubyforge_project: sparring
59
+ rubygems_version: 1.3.1
60
+ signing_key:
61
+ specification_version: 2
62
+ summary: Battleship Player:Commodore Cox
63
+ test_files: []
64
+