white_horseman 1.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,45 @@
1
+ module WhiteHorseman
2
+ class Ship
3
+ attr_accessor :symbol
4
+ def initialize(symbol)
5
+ @symbol = symbol.to_sym
6
+ end
7
+
8
+ def size
9
+ case @symbol
10
+ when :battleship
11
+ return 4
12
+ when :carrier
13
+ return 5
14
+ when :patrolship
15
+ return 2
16
+ when :submarine
17
+ return 3
18
+ when :destroyer
19
+ return 3
20
+ else
21
+ raise "bad ship type #{@symbol}"
22
+ end
23
+ end
24
+
25
+ def ==(other)
26
+ self.symbol == other.symbol
27
+ end
28
+
29
+ class << self
30
+ def each(&block)
31
+ [:carrier, :battleship, :destroyer, :submarine, :patrolship].each do |symbol|
32
+ yield(Ship.new(symbol))
33
+ end
34
+ end
35
+
36
+ def all
37
+ ships = []
38
+ self.each{|ship| ships << ship}
39
+ return ships
40
+ end
41
+ end
42
+
43
+ end
44
+
45
+ end
@@ -0,0 +1,187 @@
1
+ unless defined?(DATA_DIR)
2
+ require 'tmpdir'
3
+ DATA_DIR = "#{Dir.tmpdir}/white_horseman_battleship_data"
4
+ end
5
+
6
+ require 'white_horseman/opponent'
7
+ require 'white_horseman/fleet_admiral'
8
+ require 'white_horseman/captain'
9
+
10
+ module WhiteHorseman
11
+
12
+ # Battleship Player
13
+ #
14
+ # Battleship is board game between two players. See http://en.wikipedia.org/wiki/Battleship for more information and
15
+ # game rules.
16
+ #
17
+ # A player represents the conputer AI to play a game of Battleship. It should know how to place ships and target
18
+ # the opponents ships.
19
+ #
20
+ # This version of Battleship is played on a 10 x 10 grid where rows are labled by the letters A - J and
21
+ # columns are labled by the numbers 1 - 10. At the start of the game, each player will be asked for ship placements.
22
+ # Once the ships are placed, play proceeeds by each player targeting one square on their opponents map. A player
23
+ # may only target one square, reguardless of whether it resulted in a hit or not, before changing turns with her opponent.
24
+ #
25
+ class WhiteHorseman
26
+
27
+ # This method is called at the beginning of each game. A player may only be instantiated once and used to play many games.
28
+ # So new_game should reset any internal state acquired in previous games so that it is prepared for a new game.
29
+ #
30
+ # The name of the opponent player is passed in. This allows for the possibility to learn opponent strategy and
31
+ # play the game differently based on the opponent.
32
+ #
33
+ def new_game(opponent_name)
34
+ unless @opponent && @opponent.name == opponent_name
35
+ @opponent = Opponent.new(opponent_name)
36
+ end
37
+
38
+ @admiral = FleetAdmiral.new
39
+ @admiral.heat_map = @opponent.heat_map
40
+ @admiral.calculate
41
+ @opponent.new_game
42
+ reset
43
+ end
44
+
45
+ # Returns the placement of the carrier. A carrier consumes 5 squares.
46
+ #
47
+ # The return value is a string that describes the placements of the ship.
48
+ # The placement string must be in the following format:
49
+ #
50
+ # "#{ROW}#{COL} #{ORIENTATION}"
51
+ #
52
+ # eg
53
+ #
54
+ # A1 horizontal # the ship will occupy A1, A2, A3, A4, and A5
55
+ # A1 vertical # the ship will occupy A1, B1, C1, D1, and E1
56
+ # F5 horizontal # the ship will occupy F5, F6, F7, F8, and F9
57
+ # F5 vertical # the ship will occupy F5, G5, H5, I5, and J5
58
+ #
59
+ # The ship must not fall off the edge of the map. For example, a carrier placement of 'A8 horizontal' would
60
+ # not leave enough space in the A row to accomidate the carrier since it requires 5 squares.
61
+ #
62
+ # Ships may not overlap with other ships. For example a carrier placement of 'A1 horizontal' and a submarine
63
+ # placement of 'A1 vertical' would be invalid because bothe ships are trying to occupy the square A1.
64
+ #
65
+ # Invalid ship placements will result in disqualification of the player.
66
+ #
67
+ def carrier_placement
68
+ return @admiral.carrier
69
+ end
70
+
71
+ # Returns the placement of the battleship. A battleship consumes 4 squares.
72
+ #
73
+ # See carrier_placement for details on ship placement
74
+ #
75
+ def battleship_placement
76
+ return @admiral.battleship
77
+ end
78
+
79
+ # Returns the placement of the destroyer. A destroyer consumes 3 squares.
80
+ #
81
+ # See carrier_placement for details on ship placement
82
+ #
83
+ def destroyer_placement
84
+ return @admiral.destroyer
85
+ end
86
+
87
+ # Returns the placement of the submarine. A submarine consumes 3 squares.
88
+ #
89
+ # See carrier_placement for details on ship placement
90
+ #
91
+ def submarine_placement
92
+ return @admiral.submarine
93
+ end
94
+
95
+ # Returns the placement of the patrolship. A patrolship consumes 2 squares.
96
+ #
97
+ # See carrier_placement for details on ship placement
98
+ #
99
+ def patrolship_placement
100
+ return @admiral.patrolship
101
+ end
102
+
103
+ # Returns the coordinates of the players next target. This method will be called once per turn. The player
104
+ # should return target coordinates as a string in the form of:
105
+ #
106
+ # "#{ROW}#{COL}"
107
+ #
108
+ # eg
109
+ #
110
+ # A1 # the square in Row A and Column 1
111
+ # F5 # the square in Row F and Column 5
112
+ #
113
+ # 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
114
+ # COL should be 1, 2, 3, 4, 5, 6, 7, 8, 9, or 10
115
+ #
116
+ # Returning coordinates outside the range or in an invalid format will result in the players disqualification.
117
+ #
118
+ # It is illegal to illegal to target a sector more than once. Doing so will also result in disqualification.
119
+ #
120
+ def next_target
121
+ return @captain.next_target
122
+ end
123
+
124
+ # target_result will be called by the system after a call to next_target. The paramters supplied inform the player
125
+ # of the results of the target.
126
+ #
127
+ # coordinates : string. The coordinates targeted. It will be the same value returned by the previous call to next_target
128
+ # was_hit : boolean. true if the target was occupied by a ship. false otherwise.
129
+ # ship_sunk : symbol. nil if the target did not result in the sinking of a ship. If the target did result in
130
+ # in the sinking of a ship, the ship type is supplied (:carrier, :battleship, :destroyer, :submarine, :patrolship).
131
+ #
132
+ # An intelligent player will use the information to better play the game. For example, if the result indicates a
133
+ # hit, a player my choose to target neighboring squares to hit and sink the remainder of the ship.
134
+ #
135
+ def target_result(coordinates, was_hit, ship_sunk)
136
+ @captain.target_result(coordinates, was_hit, ship_sunk)
137
+ end
138
+
139
+ # enemy_targeting is called by the system to inform a player of their apponents move. When the opponent targets
140
+ # a square, this method is called with the coordinates.
141
+ #
142
+ # Players may use this information to understand an opponents targeting strategy and place ships differently
143
+ # in subsequent games.
144
+ #
145
+ def enemy_targeting(coordinates)
146
+ @opponent.record_target(coordinates)
147
+ end
148
+
149
+ # Called by the system at the end of a game to inform the player of the results.
150
+ #
151
+ # result : 1 of 3 possible values (:victory, :defeate, :disqualified)
152
+ # disqualification_reason : nil unless the game ended as the result of a disqualification. In the event of a
153
+ # disqualification, this paramter will hold a string description of the reason for disqualification. Both
154
+ # players will be informed of the reason.
155
+ #
156
+ # :victory # indicates the player won the game
157
+ # :defeat # indicates the player lost the game
158
+ # :disqualified # indicates the player was disqualified
159
+ #
160
+ def game_over(result, disqualification_reason=nil)
161
+ @opponent.save
162
+ end
163
+
164
+ # Non API methods #####################################
165
+
166
+ attr_reader :opponent, :targets, :enemy_targeted_sectors, :result, :disqualification_reason #:nodoc:
167
+
168
+ def initialize #:nodoc:
169
+ reset
170
+ end
171
+
172
+ private ###############################################
173
+
174
+ def reset
175
+ @captain = Captain.new
176
+ end
177
+
178
+ ROWS = %w{ A B C D E F G H I J }
179
+ def target_for_current_shot
180
+ row = ROWS[(@shots_taken) / 10]
181
+ col = @shots_taken % 10 + 1
182
+ return "#{row}#{col}"
183
+ end
184
+
185
+ end
186
+
187
+ end
@@ -0,0 +1,6 @@
1
+ $: << File.expand_path(File.dirname(__FILE__) + "/../lib")
2
+
3
+ require 'rubygems'
4
+ require 'spec'
5
+
6
+ DATA_DIR = File.expand_path(File.dirname(__FILE__)) + "/../test_data" unless defined?(DATA_DIR)
@@ -0,0 +1,52 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+ require 'white_horseman/analyst'
3
+ require 'white_horseman/battlefield'
4
+ require 'white_horseman/cell_status'
5
+
6
+ describe WhiteHorseman::Analyst do
7
+ before(:each) do
8
+ @hit_map = WhiteHorseman::Battlefield.new
9
+ @hit_map.set_all(WhiteHorseman::CellStatus.new)
10
+ @live_ships = WhiteHorseman::Ship.all
11
+ @analyst = WhiteHorseman::Analyst.new(@hit_map, @live_ships)
12
+ end
13
+
14
+ it "should zero probability if it's already been missed" do
15
+ @hit_map["G5"] = WhiteHorseman::CellStatus.new(false)
16
+ @analyst.hit_probability("G5").should == 0.0
17
+ end
18
+
19
+ it "should have starting probablity" do
20
+ @analyst.hit_probability("F5").should == 0.17
21
+ end
22
+
23
+ it "should have a sure hit" do
24
+ @hit_map["D5"] = WhiteHorseman::CellStatus.new(false)
25
+ @hit_map["E5"] = WhiteHorseman::CellStatus.new(true)
26
+ @hit_map["E6"] = WhiteHorseman::CellStatus.new(false)
27
+ @hit_map["E4"] = WhiteHorseman::CellStatus.new(false)
28
+ @analyst.hit_probability("F5").should be >= 1.0
29
+ end
30
+
31
+ it "should no chance if nothing will fit there" do
32
+ @hit_map["A2"] = WhiteHorseman::CellStatus.new(false)
33
+ @hit_map["B1"] = WhiteHorseman::CellStatus.new(false)
34
+ @analyst.hit_probability("A1").should == 0.0
35
+ end
36
+
37
+ it "should have no chance if the only ship that will fit there has already been sunk" do
38
+ @live_ships.delete(WhiteHorseman::Ship.new(:patrolship))
39
+ @hit_map["A2"] = WhiteHorseman::CellStatus.new(false)
40
+ @hit_map["B2"] = WhiteHorseman::CellStatus.new(false)
41
+ @hit_map["C1"] = WhiteHorseman::CellStatus.new(false)
42
+
43
+ @analyst.hit_probability("A1").should == 0.0
44
+ @analyst.hit_probability("B1").should == 0.0
45
+ end
46
+
47
+ it "should have a greater probability when there is a hit two cells away" do
48
+ prob = @analyst.hit_probability("G7")
49
+ @hit_map["G5"] = WhiteHorseman::CellStatus.new(true)
50
+ @analyst.hit_probability("G7").should be > prob
51
+ end
52
+ end
@@ -0,0 +1,121 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+ require 'white_horseman/battlefield'
3
+ describe WhiteHorseman::Battlefield do
4
+ before(:each) do
5
+ @battlefield = WhiteHorseman::Battlefield.new
6
+ end
7
+ it "should have the grid" do
8
+ @battlefield.record_target("A1")
9
+ @battlefield.record_target("B4")
10
+ @battlefield.record_target("F8")
11
+
12
+ @battlefield.shots.should == 3
13
+ @battlefield.grid.should == [
14
+ [99,0,0,0,0,0,0,0,0,0],
15
+ [0,0,0,98,0,0,0,0,0,0],
16
+ [0,0,0,0,0,0,0,0,0,0],
17
+ [0,0,0,0,0,0,0,0,0,0],
18
+ [0,0,0,0,0,0,0,0,0,0],
19
+ [0,0,0,0,0,0,0,97,0,0],
20
+ [0,0,0,0,0,0,0,0,0,0],
21
+ [0,0,0,0,0,0,0,0,0,0],
22
+ [0,0,0,0,0,0,0,0,0,0],
23
+ [0,0,0,0,0,0,0,0,0,0],
24
+ ]
25
+ end
26
+
27
+ it "should grids for multiple games" do
28
+ @battlefield.record_target("J10")
29
+ @battlefield.record_target("G4")
30
+ @battlefield.record_target("f1")
31
+ @battlefield.record_target("B9")
32
+
33
+ @battlefield.shots.should == 4
34
+ @battlefield.grid.should == [
35
+ [0,0,0,0,0,0,0,0,0,0],
36
+ [0,0,0,0,0,0,0,0,96,0],
37
+ [0,0,0,0,0,0,0,0,0,0],
38
+ [0,0,0,0,0,0,0,0,0,0],
39
+ [0,0,0,0,0,0,0,0,0,0],
40
+ [97,0,0,0,0,0,0,0,0,0],
41
+ [0,0,0,98,0,0,0,0,0,0],
42
+ [0,0,0,0,0,0,0,0,0,0],
43
+ [0,0,0,0,0,0,0,0,0,0],
44
+ [0,0,0,0,0,0,0,0,0,99],
45
+ ]
46
+ end
47
+
48
+ it "should build with a grid" do
49
+ @battlefield = WhiteHorseman::Battlefield.new([
50
+ [0,0,0,0,0,0,0,0,0,0],
51
+ [0,0,0,0,0,0,0,0,96,0],
52
+ [0,0,0,0,0,0,0,0,0,0],
53
+ [0,0,0,0,0,0,0,0,0,0],
54
+ [0,0,0,0,0,0,0,0,0,0],
55
+ [97,0,0,0,0,0,0,0,0,0],
56
+ [0,0,0,98,0,0,0,0,0,0],
57
+ [0,0,0,0,0,0,0,0,0,0],
58
+ [0,0,0,0,0,0,0,0,0,0],
59
+ [0,0,0,0,0,0,0,0,0,99],
60
+ ])
61
+
62
+ @battlefield.grid.should == [
63
+ [0,0,0,0,0,0,0,0,0,0],
64
+ [0,0,0,0,0,0,0,0,96,0],
65
+ [0,0,0,0,0,0,0,0,0,0],
66
+ [0,0,0,0,0,0,0,0,0,0],
67
+ [0,0,0,0,0,0,0,0,0,0],
68
+ [97,0,0,0,0,0,0,0,0,0],
69
+ [0,0,0,98,0,0,0,0,0,0],
70
+ [0,0,0,0,0,0,0,0,0,0],
71
+ [0,0,0,0,0,0,0,0,0,0],
72
+ [0,0,0,0,0,0,0,0,0,99],
73
+ ]
74
+ end
75
+
76
+ it "should set cells" do
77
+ @battlefield["A1"] = 124
78
+ @battlefield["A1"].should == 124
79
+ end
80
+
81
+ it "should set all" do
82
+ @battlefield.set_all(100)
83
+ @battlefield.grid.flatten.all?{ |cell| cell == 100}.should == true
84
+ end
85
+
86
+ it "should average multiple battlefields" do
87
+ battlefield1 = WhiteHorseman::Battlefield.new([
88
+ [5,0,0,0,0,0,0,0,0,0],
89
+ [0,10,0,0,0,0,0,0,0,0],
90
+ [0,0,15,0,0,0,0,0,0,0],
91
+ [0,20,0,0,0,0,0,0,0,0],
92
+ [25,0,0,0,0,0,0,0,0,0],
93
+ [0,0,0,0,0,0,0,0,0,0],
94
+ [0,0,0,0,0,0,0,0,0,0],
95
+ [0,0,0,0,0,0,0,0,0,0],
96
+ [0,0,0,0,0,0,0,0,0,0],
97
+ [0,0,0,0,0,0,0,0,0,0],
98
+ ])
99
+ battlefield2 = WhiteHorseman::Battlefield.new([
100
+ [10,0,0,0,0,0,0,0,0,0],
101
+ [0,10,0,0,0,0,0,0,0,0],
102
+ [0,0,25,0,0,0,0,0,0,0],
103
+ [0,0,0,0,0,0,0,0,0,0],
104
+ [100,0,0,0,0,0,0,0,0,0],
105
+ [0,0,0,0,0,0,0,0,0,0],
106
+ [0,0,0,0,0,0,0,0,0,0],
107
+ [0,0,0,0,0,0,0,0,0,0],
108
+ [0,0,0,0,0,0,0,0,0,0],
109
+ [0,0,0,0,0,0,0,0,0,0],
110
+ ])
111
+ average = WhiteHorseman::Battlefield.average([battlefield1, battlefield2])
112
+ average["A1"].should == 7.5
113
+ average["B2"].should == 10
114
+ average["C3"].should == 20
115
+ average["D2"].should == 10
116
+ average["E1"].should == 62.5
117
+
118
+ end
119
+
120
+
121
+ end
@@ -0,0 +1,57 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ require 'white_horseman/captain'
4
+ require 'white_horseman/coordinates'
5
+ describe WhiteHorseman::Captain do
6
+ before(:each) do
7
+ @analyst = mock("analyst", :hit_probability => 0.17)
8
+ WhiteHorseman::Analyst.stub!(:new).and_return(@analyst)
9
+ @captain = WhiteHorseman::Captain.new
10
+ end
11
+
12
+ it "should record a miss" do
13
+ @captain.target_result("A1", false, nil)
14
+ @captain.hit_map["A1"].should be_miss
15
+ end
16
+
17
+ it "should record a hit" do
18
+ @captain.target_result("C1", true, nil)
19
+ @captain.hit_map["C1"].should be_hit
20
+ end
21
+
22
+ it "should select a target" do
23
+ target = WhiteHorseman::Coordinates.new(:string =>"I5")
24
+ @analyst.should_receive(:hit_probability).with(target).and_return(1.5)
25
+ @captain.next_target.should == "I5"
26
+ end
27
+
28
+ it "should choose randomly from target of equal probablity" do
29
+ @captain.next_target.should_not == @captain.next_target
30
+ end
31
+
32
+ it "should not target areas already targeted" do
33
+ def @analyst.hit_probability(coords)
34
+ coords.to_s.should_not == "A6"
35
+ coords.to_s.should_not == "A8"
36
+ return 0.17
37
+ end
38
+
39
+ @captain.target_result("A6", true, nil)
40
+ @captain.target_result("A8", false, nil)
41
+
42
+ @captain.next_target
43
+ end
44
+
45
+ it "should have live ships" do
46
+ @captain.live_ships.size.should == 5
47
+ end
48
+
49
+ it "should record sunken ships" do
50
+ @captain.target_result("G5", true, nil)
51
+ @captain.target_result("G6", true, :patrolship)
52
+ @captain.hit_map["G6"].should have_ship
53
+ @captain.hit_map["G5"].should have_ship
54
+ @captain.live_ships.size.should == 4
55
+ end
56
+
57
+ end
@@ -0,0 +1,37 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+ require 'white_horseman/cell_status'
3
+ describe WhiteHorseman::CellStatus do
4
+ before(:each) do
5
+ @cell = WhiteHorseman::CellStatus.new
6
+ end
7
+ it "should hit" do
8
+ @cell.should_not be_hit
9
+ @cell.hit
10
+ @cell.should be_hit
11
+ end
12
+
13
+ it "should miss" do
14
+ @cell.should_not be_miss
15
+ @cell.miss
16
+ @cell.should be_miss
17
+ end
18
+
19
+ it "should initailize with hit" do
20
+ WhiteHorseman::CellStatus.new(true).should be_hit
21
+ WhiteHorseman::CellStatus.new(false).should be_miss
22
+ end
23
+
24
+ it "should have no shot" do
25
+ @cell.should be_empty
26
+ @cell.hit
27
+ @cell.should_not be_empty
28
+ end
29
+
30
+ it "should have ship" do
31
+ @cell.should_not have_ship
32
+ @cell.ship = :battleship
33
+ @cell.should have_ship
34
+ @cell.ship.should == :battleship
35
+ end
36
+
37
+ end
@@ -0,0 +1,171 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+ require 'white_horseman/coordinates'
3
+
4
+ describe WhiteHorseman::Coordinates do
5
+ it "should have row and column" do
6
+ @coordinates = WhiteHorseman::Coordinates.new(:string => "A1")
7
+ @coordinates.row.should == "A"
8
+ @coordinates.column.should == 1
9
+ end
10
+
11
+ it "should be A1 by default" do
12
+ WhiteHorseman::Coordinates.new.to_s.should == "A1"
13
+ end
14
+
15
+ it "should initialize by row and column" do
16
+ @coordinates = WhiteHorseman::Coordinates.new(:row => "A", :column => 1)
17
+ @coordinates.row.should == "A"
18
+ @coordinates.column.should == 1
19
+ @coordinates.to_s.should == "A1"
20
+ end
21
+
22
+ it "should raise an error on out of bounds" do
23
+ lambda {WhiteHorseman::Coordinates.new(:string => "K9")}.should raise_error("Row out of range: K")
24
+ lambda {WhiteHorseman::Coordinates.new(:string => "a9")}.should_not raise_error
25
+ lambda {WhiteHorseman::Coordinates.new(:string => "A0")}.should raise_error("Column out of range: 0")
26
+ lambda {WhiteHorseman::Coordinates.new(:string => "A11")}.should raise_error("Column out of range: 11")
27
+ end
28
+
29
+ it "should have row index" do
30
+ WhiteHorseman::Coordinates.new(:string => "A1").row_index.should == 0
31
+ WhiteHorseman::Coordinates.new(:string => "J1").row_index.should == 9
32
+ end
33
+
34
+ it "should have column index" do
35
+ WhiteHorseman::Coordinates.new(:string => "A1").column_index.should == 0
36
+ WhiteHorseman::Coordinates.new(:string => "J10").column_index.should == 9
37
+ end
38
+
39
+ it "should add a to_coord method to the String class" do
40
+ "A1".to_coord.row.should == "A"
41
+ "B10".to_coord.column.should == 10
42
+ end
43
+
44
+ it "should have a to_coord method" do
45
+ coord = WhiteHorseman::Coordinates.new(:string => "A1")
46
+ coord.to_coord.should == coord
47
+ end
48
+
49
+ it "should increment row" do
50
+ coord = "G5".to_coord
51
+
52
+ coord.inc_column
53
+
54
+ coord.column.should == 6
55
+ end
56
+
57
+ it "should increment column" do
58
+ coord = "G5".to_coord
59
+
60
+ coord.inc_row
61
+
62
+ coord.row.should == "H"
63
+ end
64
+
65
+ it "should handle values on inc row" do
66
+ coord = "G5".to_coord
67
+
68
+ coord.inc_row -2
69
+
70
+ coord.row.should == "E"
71
+ end
72
+
73
+ it "should handle values on inc column" do
74
+ coord = "G5".to_coord
75
+
76
+ coord.inc_column 4
77
+
78
+ coord.to_s.should == "G9"
79
+ end
80
+
81
+ it "should have equality operator" do
82
+ ("G5".to_coord == "G5".to_coord).should == true
83
+ ("G5".to_coord == "A5".to_coord).should == false
84
+ ("G5".to_coord == "G8".to_coord).should == false
85
+ ("G5".to_coord != "A5".to_coord).should == true
86
+ ("G5".to_coord != "G8".to_coord).should == true
87
+ end
88
+
89
+ it "should dup" do
90
+ one = "G5".to_coord
91
+ two = one.dup
92
+ two.column.should == 5
93
+ two.row.should == "G"
94
+ one.row = "B"
95
+ two.row.should == "G"
96
+ one.object_id.should_not == two.object_id
97
+ end
98
+
99
+ it "should be colinear horizontal" do
100
+ WhiteHorseman::Coordinates.should be_colinear(["F5", "F6"], :horizontal)
101
+ WhiteHorseman::Coordinates.should_not be_colinear(["F5", "G5"], :horizontal)
102
+ end
103
+
104
+ it "should be colinear vertical" do
105
+ WhiteHorseman::Coordinates.should be_colinear(["F5", "G5"], :vertical)
106
+ WhiteHorseman::Coordinates.should_not be_colinear(["F5", "F8"], :vertical)
107
+ end
108
+
109
+ it "should be just plain colinear" do
110
+ WhiteHorseman::Coordinates.should be_colinear(["F5", "G5"])
111
+ WhiteHorseman::Coordinates.should be_colinear(["F5", "F6"])
112
+ end
113
+
114
+ it "should be colinear with just one point" do
115
+ WhiteHorseman::Coordinates.should be_colinear(["F5"], :vertical)
116
+ WhiteHorseman::Coordinates.should be_colinear(["F5"], :horizontal)
117
+ end
118
+
119
+ it "should do each" do
120
+ num = 0
121
+ WhiteHorseman::Coordinates.each do |coordinate|
122
+ num += 1
123
+ end
124
+ num.should == 100
125
+ end
126
+
127
+ it "should have neighbors" do
128
+ coord = WhiteHorseman::Coordinates.new(:string => "G5")
129
+ coord.neighbors.should include("F5")
130
+ coord.neighbors.should include("H5")
131
+ coord.neighbors.should include("G4")
132
+ coord.neighbors.should include("G6")
133
+ end
134
+
135
+ it "should have neighbors on the borders" do
136
+ coord = WhiteHorseman::Coordinates.new(:string => "A1")
137
+ coord.neighbors.size.should == 2
138
+ coord.neighbors.should include("A2")
139
+ coord.neighbors.should include("B1")
140
+ end
141
+
142
+ it "should have neighbors on the south eastern border" do
143
+ coord = WhiteHorseman::Coordinates.new(:string => "J10")
144
+ coord.neighbors.size.should == 2
145
+ coord.neighbors.should include("J9")
146
+ coord.neighbors.should include("I10")
147
+ end
148
+
149
+ it "should have neighbors 2 doors down" do
150
+ coord = WhiteHorseman::Coordinates.new(:string => "G5")
151
+ coord.neighbors(2).should include("E5")
152
+ coord.neighbors(2).should include("I5")
153
+ coord.neighbors(2).should include("G3")
154
+ coord.neighbors(2).should include("G7")
155
+ end
156
+
157
+ it "should have neighbors two doors down on the edge" do
158
+ coord = WhiteHorseman::Coordinates.new(:string => "B2")
159
+ coord.neighbors(2).size.should == 2
160
+ coord.neighbors(2).should include("B4")
161
+ coord.neighbors(2).should include("D2")
162
+ end
163
+
164
+ it "should have neighbors two doors down on the bottom right" do
165
+ coord = WhiteHorseman::Coordinates.new(:string => "I9")
166
+ coord.neighbors(2).size.should == 2
167
+ coord.neighbors(2).should include("I7")
168
+ coord.neighbors(2).should include("G9")
169
+ end
170
+
171
+ end