bangkok 0.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.
- data/ChangeLog +5 -0
- data/Credits +6 -0
- data/README +227 -0
- data/Rakefile +83 -0
- data/TODO +26 -0
- data/bin/bangkok +40 -0
- data/examples/announcer.rb +71 -0
- data/examples/game.pgn +15 -0
- data/examples/program_changes.rb +17 -0
- data/install.rb +95 -0
- data/lib/bangkok.rb +2 -0
- data/lib/bangkok/board.rb +110 -0
- data/lib/bangkok/chessgame.rb +50 -0
- data/lib/bangkok/gamelistener.rb +181 -0
- data/lib/bangkok/info.rb +6 -0
- data/lib/bangkok/move.rb +106 -0
- data/lib/bangkok/piece.rb +243 -0
- data/lib/bangkok/square.rb +47 -0
- data/test/mock_game_listener.rb +22 -0
- data/test/test_piece.rb +106 -0
- data/test/test_square.rb +109 -0
- metadata +75 -0
data/lib/bangkok/info.rb
ADDED
data/lib/bangkok/move.rb
ADDED
@@ -0,0 +1,106 @@
|
|
1
|
+
require 'bangkok/square'
|
2
|
+
|
3
|
+
|
4
|
+
# A Move is a single piece's move in a chess match. There are two Move objects
|
5
|
+
# created for each chess match move: one for white and one for black.
|
6
|
+
class Move
|
7
|
+
attr_reader :color, :piece, :square, :from_rank_or_file, :modifier
|
8
|
+
|
9
|
+
# Parse the chess piece move +text+ and set piece, square, and modifier.
|
10
|
+
def initialize(color, text)
|
11
|
+
@color = color # :white or :black
|
12
|
+
@orig_text = text
|
13
|
+
|
14
|
+
# Note: I don't have to worry about "e.p." en passant notation; the data
|
15
|
+
# files do not use that.
|
16
|
+
case text
|
17
|
+
when 'O-O-O', 'O-O', '0-0-0', '0-0'
|
18
|
+
@modifier = text.gsub(/0/, 'O') # zeroes to capital o's
|
19
|
+
@piece = 'K'
|
20
|
+
@square = Square::OFF_BOARD # Not really, of course; square is never used
|
21
|
+
when /^([KQRBNa-h1-8])x([a-h][1-8])(.*)$/
|
22
|
+
piece_or_from_rank_or_file, file_and_rank, @modifier = $1, $2, $3
|
23
|
+
@square = Square.new(file_and_rank)
|
24
|
+
case piece_or_from_rank_or_file
|
25
|
+
when /[a-h1-8]/ # first char is file or rank; it's a pawn
|
26
|
+
@piece = 'P'
|
27
|
+
@from_rank_or_file = Square.new(piece_or_from_rank_or_file)
|
28
|
+
else # first char is piece name
|
29
|
+
@piece = piece_or_from_rank_or_file
|
30
|
+
end
|
31
|
+
@modifier ||= ''
|
32
|
+
@modifier << 'x'
|
33
|
+
when /^([KQRBN]?)([a-h1-8]?)([a-h][1-8])(.*)$/
|
34
|
+
@piece, from_rank_or_file, file_and_rank, @modifier = $1, $2, $3, $4
|
35
|
+
@square = Square.new(file_and_rank)
|
36
|
+
unless from_rank_or_file.empty?
|
37
|
+
@from_rank_or_file = Square.new(from_rank_or_file)
|
38
|
+
end
|
39
|
+
else
|
40
|
+
raise "I can't understand the move \"#{@orig_text}\""
|
41
|
+
end
|
42
|
+
|
43
|
+
@piece = 'P' if @piece.empty?
|
44
|
+
@piece = @piece.intern
|
45
|
+
end
|
46
|
+
|
47
|
+
def to_s
|
48
|
+
return @orig_text
|
49
|
+
end
|
50
|
+
|
51
|
+
# Returns true if @modifier is not null and includes +str+.
|
52
|
+
def has_modifier?(str)
|
53
|
+
return false unless @modifier
|
54
|
+
return @modifier.include?(str)
|
55
|
+
end
|
56
|
+
|
57
|
+
# Returns true if this is a capture
|
58
|
+
def capture?
|
59
|
+
has_modifier?('x')
|
60
|
+
end
|
61
|
+
|
62
|
+
# Returns true if this is a castle (either side)
|
63
|
+
def castle?
|
64
|
+
has_modifier?('O-O') # Also true if O-O-O
|
65
|
+
end
|
66
|
+
|
67
|
+
# Returns true if this is a queenside castle
|
68
|
+
def queenside_castle?
|
69
|
+
has_modifier?('O-O-O')
|
70
|
+
end
|
71
|
+
|
72
|
+
# Returns true if this is a kingside castle
|
73
|
+
def kingside_castle?
|
74
|
+
has_modifier?('O-O') && !queenside_castle?
|
75
|
+
end
|
76
|
+
|
77
|
+
# Returns true if this move results in a pawn promotion
|
78
|
+
def pawn_promotion?
|
79
|
+
has_modifier?('Q')
|
80
|
+
end
|
81
|
+
|
82
|
+
# Returns true if this move results in a check
|
83
|
+
def check?
|
84
|
+
has_modifier?('+')
|
85
|
+
end
|
86
|
+
|
87
|
+
# Returns true if this move results in a checkmate
|
88
|
+
def checkmate?
|
89
|
+
has_modifier?('#')
|
90
|
+
end
|
91
|
+
|
92
|
+
# Returns true if this move was a good one
|
93
|
+
def good_move?
|
94
|
+
has_modifier?('!')
|
95
|
+
end
|
96
|
+
|
97
|
+
# Returns true if this move was a bad one
|
98
|
+
def bad_move?
|
99
|
+
has_modifier?('?') && !blunder?
|
100
|
+
end
|
101
|
+
|
102
|
+
# Returns true if this move was a blunder
|
103
|
+
def blunder?
|
104
|
+
has_modifier?('??')
|
105
|
+
end
|
106
|
+
end
|
@@ -0,0 +1,243 @@
|
|
1
|
+
require 'bangkok/square'
|
2
|
+
|
3
|
+
class Piece
|
4
|
+
attr_reader :color, :piece, :square
|
5
|
+
|
6
|
+
# Factory method that creates a piece of the proper subclass
|
7
|
+
def Piece.create(board, listener, color, piece_sym, square)
|
8
|
+
return case piece_sym
|
9
|
+
when :K
|
10
|
+
King.new(board, listener, color, square)
|
11
|
+
when :Q
|
12
|
+
Queen.new(board, listener, color, square)
|
13
|
+
when :B
|
14
|
+
Bishop.new(board, listener, color, square)
|
15
|
+
when :N
|
16
|
+
Knight.new(board, listener, color, square)
|
17
|
+
when :R
|
18
|
+
Rook.new(board, listener, color, square)
|
19
|
+
when :P
|
20
|
+
Pawn.new(board, listener, color, square)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def initialize(board, listener, color, piece, square)
|
25
|
+
@board, @listener, @color, @piece, @square =
|
26
|
+
board, listener, color, piece, square
|
27
|
+
end
|
28
|
+
|
29
|
+
def move_to(square)
|
30
|
+
puts "#{self} moving to #{square}" if $verbose
|
31
|
+
@listener.move(self, @square, square)
|
32
|
+
@square = square
|
33
|
+
end
|
34
|
+
|
35
|
+
def move_off_board
|
36
|
+
return move_to(Square::OFF_BOARD)
|
37
|
+
end
|
38
|
+
|
39
|
+
# Make sure this piece can perform +move+. This implementation checks the
|
40
|
+
# basics (the color and type of this piece and the emptiness or color of the
|
41
|
+
# piece at the destination square); subclasses add further checks.
|
42
|
+
def could_perform_move(move)
|
43
|
+
p = @board.at(move.square)
|
44
|
+
return @color == move.color && piece == move.piece &&
|
45
|
+
(p.nil? || p.color != @color)
|
46
|
+
end
|
47
|
+
|
48
|
+
# Checks diagonals and straight horizontal/vertical lines. Won't work
|
49
|
+
# correctly for anything else.
|
50
|
+
def clear_to?(square)
|
51
|
+
curr_file = @square.file
|
52
|
+
end_file = square.file
|
53
|
+
file_delta = @square.file < square.file ? 1 :
|
54
|
+
(@square.file == square.file ? 0 : -1)
|
55
|
+
curr_file += file_delta unless curr_file == end_file # Skip current loc
|
56
|
+
|
57
|
+
curr_rank = @square.rank
|
58
|
+
end_rank = square.rank
|
59
|
+
rank_delta = @square.rank < square.rank ? 1 :
|
60
|
+
(@square.rank == square.rank ? 0 : -1)
|
61
|
+
curr_rank += rank_delta unless curr_rank == end_rank # Skip current loc
|
62
|
+
|
63
|
+
if file_delta == 0 && rank_delta == 0
|
64
|
+
raise "error: trying to move to same space #{file}#{rank}"
|
65
|
+
end
|
66
|
+
|
67
|
+
while curr_file != end_file || curr_rank != end_rank
|
68
|
+
return false unless @board.empty_at?(Square.new(curr_file, curr_rank))
|
69
|
+
curr_file += file_delta
|
70
|
+
curr_rank += rank_delta
|
71
|
+
end
|
72
|
+
|
73
|
+
return true
|
74
|
+
end
|
75
|
+
|
76
|
+
def to_s
|
77
|
+
str = "#{@color.to_s.capitalize} #@piece "
|
78
|
+
str << "at " if @square.on_board?
|
79
|
+
str << @square.to_s
|
80
|
+
str
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
class King < Piece
|
85
|
+
def initialize(board, listener, color, square)
|
86
|
+
super(board, listener, color, :K, square)
|
87
|
+
end
|
88
|
+
|
89
|
+
# There is no King#could_move_to method because it would only be called if
|
90
|
+
# there were more than one King of the same color on the board, which can
|
91
|
+
# not happen.
|
92
|
+
end
|
93
|
+
|
94
|
+
class Queen < Piece
|
95
|
+
def initialize(board, listener, color, square)
|
96
|
+
super(board, listener, color, :Q, square)
|
97
|
+
end
|
98
|
+
|
99
|
+
# There can be more than one queen on the board, thus this method must be
|
100
|
+
# implemented.
|
101
|
+
def could_perform_move(move)
|
102
|
+
return false unless super
|
103
|
+
|
104
|
+
# Check for horizontal or vertical
|
105
|
+
square = move.square
|
106
|
+
if @square.file == square.file || @square.rank == square.rank
|
107
|
+
return clear_to?(square)
|
108
|
+
end
|
109
|
+
|
110
|
+
# Check for diagonal
|
111
|
+
return false unless square.color == @square.color
|
112
|
+
d_file = (@square.file - square.file).abs
|
113
|
+
d_rank = (@square.rank - square.rank).abs
|
114
|
+
return false unless d_file == d_rank # diagonal
|
115
|
+
return clear_to?(square)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
class Bishop < Piece
|
120
|
+
def initialize(board, listener, color, square)
|
121
|
+
super(board, listener, color, :B, square)
|
122
|
+
end
|
123
|
+
|
124
|
+
def could_perform_move(move)
|
125
|
+
return false unless super
|
126
|
+
|
127
|
+
# Quick square color check
|
128
|
+
square = move.square
|
129
|
+
return false unless square.color == @square.color
|
130
|
+
|
131
|
+
d_file = (@square.file - square.file).abs
|
132
|
+
d_rank = (@square.rank - square.rank).abs
|
133
|
+
return false unless d_file == d_rank # diagonal
|
134
|
+
return clear_to?(square)
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
class Knight < Piece
|
139
|
+
def initialize(board, listener, color, square)
|
140
|
+
super(board, listener, color, :N, square)
|
141
|
+
end
|
142
|
+
|
143
|
+
def could_perform_move(move)
|
144
|
+
return false unless super
|
145
|
+
|
146
|
+
square = move.square
|
147
|
+
d_file = (@square.file - square.file).abs
|
148
|
+
d_rank = (@square.rank - square.rank).abs
|
149
|
+
return (d_file == 2 && d_rank == 1) || (d_file == 1 && d_rank == 2)
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
class Rook < Piece
|
154
|
+
def initialize(board, listener, color, square)
|
155
|
+
super(board, listener, color, :R, square)
|
156
|
+
end
|
157
|
+
|
158
|
+
def could_perform_move(move)
|
159
|
+
return false unless super
|
160
|
+
|
161
|
+
square = move.square
|
162
|
+
return false if square.file != @square.file && square.rank != @square.rank
|
163
|
+
return clear_to?(square)
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
class Pawn < Piece
|
168
|
+
attr_reader :moved
|
169
|
+
|
170
|
+
def initialize(board, listener, color, square)
|
171
|
+
super(board, listener, color, :P, square)
|
172
|
+
@moved = false
|
173
|
+
end
|
174
|
+
|
175
|
+
def move_to(square)
|
176
|
+
@moved = true
|
177
|
+
return super
|
178
|
+
end
|
179
|
+
|
180
|
+
def could_perform_move(move)
|
181
|
+
return false unless super
|
182
|
+
|
183
|
+
square = move.square
|
184
|
+
if @color == :white
|
185
|
+
if square.file == @square.file && square.rank == @square.rank + 1
|
186
|
+
# single step forwards
|
187
|
+
return @board.empty_at?(square)
|
188
|
+
elsif square.file + 1 == @square.file && square.rank == @square.rank + 1
|
189
|
+
# take a piece queenside
|
190
|
+
return !@board.empty_at?(square)
|
191
|
+
elsif square.file == @square.file + 1 && square.rank == @square.rank + 1
|
192
|
+
# take a piece kingside
|
193
|
+
return !@board.empty_at?(square)
|
194
|
+
elsif !@moved && square.file == @square.file &&
|
195
|
+
square.rank == @square.rank + 2
|
196
|
+
# first move: 2 squares forward
|
197
|
+
return @board.empty_at?(Square.new(@square.file, @square.rank + 1)) &&
|
198
|
+
@board.empty_at?(square)
|
199
|
+
|
200
|
+
# TODO Implement en passant checking. The following code was wrong.
|
201
|
+
|
202
|
+
# elsif !@moved && square.file + 1 == @square.file &&
|
203
|
+
# square.rank == @square.rank + 2
|
204
|
+
# # e.p. queenside
|
205
|
+
# return !@board.empty_at?(square)
|
206
|
+
# elsif !@moved && square.file == @square.file + 1 &&
|
207
|
+
# square.rank == @square.rank + 2
|
208
|
+
# # e.p. kingside
|
209
|
+
# return !@board.empty_at?(square)
|
210
|
+
|
211
|
+
end
|
212
|
+
else
|
213
|
+
# black
|
214
|
+
if square.file == @square.file && square.rank == @square.rank - 1
|
215
|
+
# single step forwards
|
216
|
+
return @board.empty_at?(square)
|
217
|
+
elsif square.file + 1 == @square.file && square.rank == @square.rank - 1
|
218
|
+
# take a piece queenside
|
219
|
+
return !@board.empty_at?(square)
|
220
|
+
elsif square.file == @square.file + 1 && square.rank == @square.rank - 1
|
221
|
+
# take a piece kingside
|
222
|
+
return !@board.empty_at?(square)
|
223
|
+
elsif !@moved && square.file == @square.file &&
|
224
|
+
square.rank == @square.rank - 2
|
225
|
+
# first move: 2 spaces forward
|
226
|
+
return @board.empty_at?(Square.new(@square.file, @square.rank - 1)) &&
|
227
|
+
@board.empty_at?(square)
|
228
|
+
|
229
|
+
# TODO Implement en passant checking. The following code was wrong.
|
230
|
+
|
231
|
+
# elsif !@moved && square.file + 1 == @square.file &&
|
232
|
+
# square.rank == @square.rank - 2
|
233
|
+
# # en passant queenside
|
234
|
+
# return !@board.empty_at?(square)
|
235
|
+
# elsif !@moved && square.file == @square.file + 1 &&
|
236
|
+
# square.rank == @square.rank - 2
|
237
|
+
# # en passant kingside
|
238
|
+
# return !@board.empty_at?(square)
|
239
|
+
|
240
|
+
end
|
241
|
+
end
|
242
|
+
end
|
243
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# Represents a location on the board. Immutable.
|
2
|
+
class Square
|
3
|
+
attr_reader :file, :rank # Always 0-7
|
4
|
+
attr_reader :color # :white or :black
|
5
|
+
|
6
|
+
def initialize(*args)
|
7
|
+
@file = @rank = @color = nil
|
8
|
+
case args[0]
|
9
|
+
when Square # copy values from another Square
|
10
|
+
@file = args[0].file
|
11
|
+
@rank = args[0].rank
|
12
|
+
@color = args[0].color
|
13
|
+
when Numeric # (file number, rank number)
|
14
|
+
@file = args[0].to_i # If floating point, make integer
|
15
|
+
@rank = args[1].to_i
|
16
|
+
@color = (((@file & 1) == (@rank & 1)) ? :black : :white) if on_board?
|
17
|
+
when /[a-h][1-8]/ # a3, d8
|
18
|
+
@file = args[0][0] - ?a
|
19
|
+
@rank = args[0][1,1].to_i - 1
|
20
|
+
@color = ((@file & 1) == (@rank & 1)) ? :black : :white
|
21
|
+
when /[a-h]/ # Either (file letter, rank) or a file 'a'
|
22
|
+
@file = args[0][0] - ?a
|
23
|
+
@rank = (args[1].to_i - 1) if args[1]
|
24
|
+
when /[1-8]/ # 1, 5
|
25
|
+
@rank = args[0].to_i - 1
|
26
|
+
when nil
|
27
|
+
# both file and rank are nil
|
28
|
+
else
|
29
|
+
raise "don't understand Square ctor args (#{args.join(',')})"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def ==(square)
|
34
|
+
@file == square.file && @rank == square.rank
|
35
|
+
end
|
36
|
+
|
37
|
+
def on_board?
|
38
|
+
return @file && @rank
|
39
|
+
end
|
40
|
+
|
41
|
+
def to_s
|
42
|
+
return "<off-board>" if @file.nil? || @rank.nil?
|
43
|
+
return "#{@file ? (?a + file).chr : ''}#{@rank + 1}"
|
44
|
+
end
|
45
|
+
|
46
|
+
OFF_BOARD = Square.new
|
47
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
class MockGameListener
|
2
|
+
def start_game
|
3
|
+
end
|
4
|
+
|
5
|
+
def end_game
|
6
|
+
end
|
7
|
+
|
8
|
+
def move(piece, from, to)
|
9
|
+
end
|
10
|
+
|
11
|
+
def capture(attacker, loser)
|
12
|
+
end
|
13
|
+
|
14
|
+
def check
|
15
|
+
end
|
16
|
+
|
17
|
+
def checkmate
|
18
|
+
end
|
19
|
+
|
20
|
+
def pawn_to_queen(pawn)
|
21
|
+
end
|
22
|
+
end
|
data/test/test_piece.rb
ADDED
@@ -0,0 +1,106 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
$LOAD_PATH[0, 0] = File.dirname(__FILE__)
|
3
|
+
$LOAD_PATH[0, 0] = File.join(File.dirname(__FILE__), '..', 'lib')
|
4
|
+
require 'bangkok/piece'
|
5
|
+
require 'bangkok/square'
|
6
|
+
require 'bangkok/move'
|
7
|
+
require 'bangkok/board'
|
8
|
+
require 'mock_game_listener'
|
9
|
+
|
10
|
+
class PieceTest < Test::Unit::TestCase
|
11
|
+
|
12
|
+
def setup
|
13
|
+
@listener = MockGameListener.new
|
14
|
+
@board = Board.new(@listener)
|
15
|
+
@w_pawn = @board.at(Square.new('a2'))
|
16
|
+
@b_pawn = @board.at(Square.new('e7'))
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_create
|
20
|
+
assert_instance_of(King, Piece.create(@board, @listener, :white, :K,
|
21
|
+
Square::OFF_BOARD))
|
22
|
+
assert_instance_of(Queen, Piece.create(@board, @listener, :white, :Q,
|
23
|
+
Square::OFF_BOARD))
|
24
|
+
assert_instance_of(Bishop, Piece.create(@board, @listener, :white, :B,
|
25
|
+
Square::OFF_BOARD))
|
26
|
+
assert_instance_of(Knight, Piece.create(@board, @listener, :white, :N,
|
27
|
+
Square::OFF_BOARD))
|
28
|
+
assert_instance_of(Rook, Piece.create(@board, @listener, :white, :R,
|
29
|
+
Square::OFF_BOARD))
|
30
|
+
assert_instance_of(Pawn, Piece.create(@board, @listener, :white, :P,
|
31
|
+
Square::OFF_BOARD))
|
32
|
+
assert_nil(Piece.create(@board, @listener, :white, nil, Square::OFF_BOARD))
|
33
|
+
end
|
34
|
+
|
35
|
+
def test_moved
|
36
|
+
assert(!@w_pawn.moved)
|
37
|
+
@w_pawn.move_to(Square.new('a4'))
|
38
|
+
assert(@w_pawn.moved)
|
39
|
+
end
|
40
|
+
|
41
|
+
def test_move_to
|
42
|
+
assert_equal(Square.new('a2'), @w_pawn.square)
|
43
|
+
|
44
|
+
dest = Square.new('b3')
|
45
|
+
@w_pawn.move_to(dest)
|
46
|
+
assert_equal(dest, @w_pawn.square)
|
47
|
+
|
48
|
+
dest = Square::OFF_BOARD
|
49
|
+
@w_pawn.move_off_board
|
50
|
+
assert_equal(dest, @w_pawn.square)
|
51
|
+
end
|
52
|
+
|
53
|
+
def test_pawn_could_move_to
|
54
|
+
assert(@w_pawn.could_perform_move(Move.new(:white, 'a3')))
|
55
|
+
assert(@w_pawn.could_perform_move(Move.new(:white, 'a4')))
|
56
|
+
|
57
|
+
diag = Move.new(:white, 'b3')
|
58
|
+
assert(!@w_pawn.could_perform_move(diag)) # no piece there
|
59
|
+
|
60
|
+
# put a black piece there so we can capture it
|
61
|
+
@board.at(Square.new('d8')).move_to(Square.new('b3'))
|
62
|
+
assert(@w_pawn.could_perform_move(diag)) # now we can capture it
|
63
|
+
|
64
|
+
assert(@b_pawn.could_perform_move(Move.new(:black, 'e6')))
|
65
|
+
assert(@b_pawn.could_perform_move(Move.new(:black, 'e5')))
|
66
|
+
|
67
|
+
diag = Move.new(:black, 'd6')
|
68
|
+
assert(!@b_pawn.could_perform_move(diag)) # no piece there
|
69
|
+
|
70
|
+
# put a white piece there so we can capture it
|
71
|
+
@board.at(Square.new('d1')).move_to(Square.new('d6'))
|
72
|
+
assert(@b_pawn.could_perform_move(diag)) # now we can capture it
|
73
|
+
end
|
74
|
+
|
75
|
+
def test_pawn_en_passant
|
76
|
+
# Warning: en passant is not handled yet by the code
|
77
|
+
end
|
78
|
+
|
79
|
+
def test_rook_could_move_to
|
80
|
+
rook = @board.at(Square.new('a8')) # black rook
|
81
|
+
assert(!rook.could_perform_move(Move.new(:black, 'Ra7')))
|
82
|
+
@board.at(Square.new('a7')).move_off_board # remove pawn in front
|
83
|
+
(3..7).each { | rank |
|
84
|
+
assert(rook.could_perform_move(Move.new(:black, 'Ra' + rank.to_s)))
|
85
|
+
}
|
86
|
+
|
87
|
+
assert_not_nil(@board.at(Square.new('a2')))
|
88
|
+
assert(rook.could_perform_move(Move.new(:black, 'Ra2'))) # can capture
|
89
|
+
|
90
|
+
assert_not_nil(@board.at(Square.new('a1')))
|
91
|
+
assert(!rook.could_perform_move(Move.new(:black, 'Ra1'))) # can't get there
|
92
|
+
end
|
93
|
+
|
94
|
+
def test_knight_could_move_to
|
95
|
+
end
|
96
|
+
|
97
|
+
def test_bishop_could_move_to
|
98
|
+
end
|
99
|
+
|
100
|
+
def test_queen_could_move_to
|
101
|
+
end
|
102
|
+
|
103
|
+
def test_king_could_move_to
|
104
|
+
end
|
105
|
+
|
106
|
+
end
|