just_chess 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.
- checksums.yaml +7 -0
- data/.gitignore +9 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +74 -0
- data/Rakefile +10 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/just_chess.gemspec +29 -0
- data/lib/just_chess.rb +8 -0
- data/lib/just_chess/direction.rb +35 -0
- data/lib/just_chess/errors/causes_check_error.rb +22 -0
- data/lib/just_chess/errors/error.rb +20 -0
- data/lib/just_chess/errors/invalid_move_error.rb +22 -0
- data/lib/just_chess/errors/invalid_promotion_error.rb +22 -0
- data/lib/just_chess/errors/moved_into_check_error.rb +22 -0
- data/lib/just_chess/errors/no_piece_error.rb +22 -0
- data/lib/just_chess/errors/not_players_turn_error.rb +22 -0
- data/lib/just_chess/errors/off_board_error.rb +22 -0
- data/lib/just_chess/game_state.rb +323 -0
- data/lib/just_chess/piece_factory.rb +68 -0
- data/lib/just_chess/pieces/bishop.rb +23 -0
- data/lib/just_chess/pieces/king.rb +97 -0
- data/lib/just_chess/pieces/knight.rb +23 -0
- data/lib/just_chess/pieces/pawn.rb +103 -0
- data/lib/just_chess/pieces/piece.rb +121 -0
- data/lib/just_chess/pieces/queen.rb +23 -0
- data/lib/just_chess/pieces/rook.rb +23 -0
- data/lib/just_chess/point.rb +52 -0
- data/lib/just_chess/square.rb +124 -0
- data/lib/just_chess/square_set.rb +407 -0
- data/lib/just_chess/vector.rb +86 -0
- data/lib/just_chess/version.rb +4 -0
- metadata +120 -0
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'just_chess/errors/error'
|
2
|
+
|
3
|
+
module JustChess
|
4
|
+
|
5
|
+
# = NotPlayersTurnError
|
6
|
+
#
|
7
|
+
# A not players turn error with a message
|
8
|
+
class NotPlayersTurnError < Error
|
9
|
+
|
10
|
+
# New not players turn errors can be instantiated with
|
11
|
+
#
|
12
|
+
# @option [String] message
|
13
|
+
# the message to display.
|
14
|
+
#
|
15
|
+
# ==== Example:
|
16
|
+
# # Instantiates a new NotPlayersTurnError
|
17
|
+
# JustChess::NotPlayersTurnError.new("Custom Message")
|
18
|
+
def initialize(message="It is not the player's turn yet.")
|
19
|
+
super
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'just_chess/errors/error'
|
2
|
+
|
3
|
+
module JustChess
|
4
|
+
|
5
|
+
# = OffBoardError
|
6
|
+
#
|
7
|
+
# An off board error with a message
|
8
|
+
class OffBoardError < Error
|
9
|
+
|
10
|
+
# New off board errors can be instantiated with
|
11
|
+
#
|
12
|
+
# @option [String] message
|
13
|
+
# the message to display.
|
14
|
+
#
|
15
|
+
# ==== Example:
|
16
|
+
# # Instantiates a new OffBoardError
|
17
|
+
# JustChess::OffBoardError.new("Custom Message")
|
18
|
+
def initialize(message="Cannot move off board.")
|
19
|
+
super
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,323 @@
|
|
1
|
+
require 'just_chess/errors/no_piece_error'
|
2
|
+
require 'just_chess/errors/not_players_turn_error'
|
3
|
+
require 'just_chess/errors/invalid_move_error'
|
4
|
+
require 'just_chess/errors/invalid_promotion_error'
|
5
|
+
require 'just_chess/errors/moved_into_check_error'
|
6
|
+
require 'just_chess/square_set'
|
7
|
+
|
8
|
+
module JustChess
|
9
|
+
|
10
|
+
# = Game State
|
11
|
+
#
|
12
|
+
# Represents a game of Chess in progress.
|
13
|
+
class GameState
|
14
|
+
|
15
|
+
# They piece types that a pawn can promote to.
|
16
|
+
PROMOTABLE_PIECE_TYPES = ['queen', 'knight', 'bishop', 'rook']
|
17
|
+
|
18
|
+
def initialize(current_player_number: , squares: [], last_double_step_pawn_id: nil)
|
19
|
+
@current_player_number = current_player_number
|
20
|
+
@squares = if squares.is_a?(SquareSet)
|
21
|
+
squares
|
22
|
+
else
|
23
|
+
SquareSet.new(squares: squares)
|
24
|
+
end
|
25
|
+
@last_double_step_pawn_id = last_double_step_pawn_id
|
26
|
+
@last_change = {}
|
27
|
+
@errors = []
|
28
|
+
end
|
29
|
+
|
30
|
+
attr_reader :current_player_number, :squares, :last_change, :errors, :last_double_step_pawn_id
|
31
|
+
|
32
|
+
# Instantiates a new GameState object in the starting position
|
33
|
+
#
|
34
|
+
# @return [GameState]
|
35
|
+
def self.default
|
36
|
+
new({
|
37
|
+
current_player_number: 1,
|
38
|
+
squares: [
|
39
|
+
{ id: 'a8', x: 0, y: 0, piece: { id: 1, player_number: 2, type: 'rook' } },
|
40
|
+
{ id: 'b8', x: 1, y: 0, piece: { id: 2, player_number: 2, type: 'knight' } },
|
41
|
+
{ id: 'c8', x: 2, y: 0, piece: { id: 3, player_number: 2, type: 'bishop' } },
|
42
|
+
{ id: 'd8', x: 3, y: 0, piece: { id: 4, player_number: 2, type: 'queen' } },
|
43
|
+
{ id: 'e8', x: 4, y: 0, piece: { id: 5, player_number: 2, type: 'king' } },
|
44
|
+
{ id: 'f8', x: 5, y: 0, piece: { id: 6, player_number: 2, type: 'bishop' } },
|
45
|
+
{ id: 'g8', x: 6, y: 0, piece: { id: 7, player_number: 2, type: 'knight' } },
|
46
|
+
{ id: 'h8', x: 7, y: 0, piece: { id: 8, player_number: 2, type: 'rook' } },
|
47
|
+
|
48
|
+
{ id: 'a7', x: 0, y: 1, piece: { id: 9, player_number: 2, type: 'pawn' } },
|
49
|
+
{ id: 'b7', x: 1, y: 1, piece: { id: 10, player_number: 2, type: 'pawn' } },
|
50
|
+
{ id: 'c7', x: 2, y: 1, piece: { id: 11, player_number: 2, type: 'pawn' } },
|
51
|
+
{ id: 'd7', x: 3, y: 1, piece: { id: 12, player_number: 2, type: 'pawn' } },
|
52
|
+
{ id: 'e7', x: 4, y: 1, piece: { id: 13, player_number: 2, type: 'pawn' } },
|
53
|
+
{ id: 'f7', x: 5, y: 1, piece: { id: 14, player_number: 2, type: 'pawn' } },
|
54
|
+
{ id: 'g7', x: 6, y: 1, piece: { id: 15, player_number: 2, type: 'pawn' } },
|
55
|
+
{ id: 'h7', x: 7, y: 1, piece: { id: 16, player_number: 2, type: 'pawn' } },
|
56
|
+
|
57
|
+
{ id: 'a6', x: 0, y: 2, piece: nil },
|
58
|
+
{ id: 'b6', x: 1, y: 2, piece: nil },
|
59
|
+
{ id: 'c6', x: 2, y: 2, piece: nil },
|
60
|
+
{ id: 'd6', x: 3, y: 2, piece: nil },
|
61
|
+
{ id: 'e6', x: 4, y: 2, piece: nil },
|
62
|
+
{ id: 'f6', x: 5, y: 2, piece: nil },
|
63
|
+
{ id: 'g6', x: 6, y: 2, piece: nil },
|
64
|
+
{ id: 'h6', x: 7, y: 2, piece: nil },
|
65
|
+
|
66
|
+
{ id: 'a5', x: 0, y: 3, piece: nil },
|
67
|
+
{ id: 'b5', x: 1, y: 3, piece: nil },
|
68
|
+
{ id: 'c5', x: 2, y: 3, piece: nil },
|
69
|
+
{ id: 'd5', x: 3, y: 3, piece: nil },
|
70
|
+
{ id: 'e5', x: 4, y: 3, piece: nil },
|
71
|
+
{ id: 'f5', x: 5, y: 3, piece: nil },
|
72
|
+
{ id: 'g5', x: 6, y: 3, piece: nil },
|
73
|
+
{ id: 'h5', x: 7, y: 3, piece: nil },
|
74
|
+
|
75
|
+
{ id: 'a4', x: 0, y: 4, piece: nil },
|
76
|
+
{ id: 'b4', x: 1, y: 4, piece: nil },
|
77
|
+
{ id: 'c4', x: 2, y: 4, piece: nil },
|
78
|
+
{ id: 'd4', x: 3, y: 4, piece: nil },
|
79
|
+
{ id: 'e4', x: 4, y: 4, piece: nil },
|
80
|
+
{ id: 'f4', x: 5, y: 4, piece: nil },
|
81
|
+
{ id: 'g4', x: 6, y: 4, piece: nil },
|
82
|
+
{ id: 'h4', x: 7, y: 4, piece: nil },
|
83
|
+
|
84
|
+
{ id: 'a3', x: 0, y: 5, piece: nil },
|
85
|
+
{ id: 'b3', x: 1, y: 5, piece: nil },
|
86
|
+
{ id: 'c3', x: 2, y: 5, piece: nil },
|
87
|
+
{ id: 'd3', x: 3, y: 5, piece: nil },
|
88
|
+
{ id: 'e3', x: 4, y: 5, piece: nil },
|
89
|
+
{ id: 'f3', x: 5, y: 5, piece: nil },
|
90
|
+
{ id: 'g3', x: 6, y: 5, piece: nil },
|
91
|
+
{ id: 'h3', x: 7, y: 5, piece: nil },
|
92
|
+
|
93
|
+
{ id: 'a2', x: 0, y: 6, piece: { id: 17, player_number: 1, type: 'pawn' } },
|
94
|
+
{ id: 'b2', x: 1, y: 6, piece: { id: 18, player_number: 1, type: 'pawn' } },
|
95
|
+
{ id: 'c2', x: 2, y: 6, piece: { id: 19, player_number: 1, type: 'pawn' } },
|
96
|
+
{ id: 'd2', x: 3, y: 6, piece: { id: 20, player_number: 1, type: 'pawn' } },
|
97
|
+
{ id: 'e2', x: 4, y: 6, piece: { id: 21, player_number: 1, type: 'pawn' } },
|
98
|
+
{ id: 'f2', x: 5, y: 6, piece: { id: 22, player_number: 1, type: 'pawn' } },
|
99
|
+
{ id: 'g2', x: 6, y: 6, piece: { id: 23, player_number: 1, type: 'pawn' } },
|
100
|
+
{ id: 'h2', x: 7, y: 6, piece: { id: 24, player_number: 1, type: 'pawn' } },
|
101
|
+
|
102
|
+
{ id: 'a1', x: 0, y: 7, piece: { id: 25, player_number: 1, type: 'rook' } },
|
103
|
+
{ id: 'b1', x: 1, y: 7, piece: { id: 26, player_number: 1, type: 'knight' } },
|
104
|
+
{ id: 'c1', x: 2, y: 7, piece: { id: 27, player_number: 1, type: 'bishop' } },
|
105
|
+
{ id: 'd1', x: 3, y: 7, piece: { id: 28, player_number: 1, type: 'queen' } },
|
106
|
+
{ id: 'e1', x: 4, y: 7, piece: { id: 29, player_number: 1, type: 'king' } },
|
107
|
+
{ id: 'f1', x: 5, y: 7, piece: { id: 30, player_number: 1, type: 'bishop' } },
|
108
|
+
{ id: 'g1', x: 6, y: 7, piece: { id: 31, player_number: 1, type: 'knight' } },
|
109
|
+
{ id: 'h1', x: 7, y: 7, piece: { id: 32, player_number: 1, type: 'rook' } },
|
110
|
+
]
|
111
|
+
})
|
112
|
+
end
|
113
|
+
|
114
|
+
# serializes the game state as a hash
|
115
|
+
#
|
116
|
+
# @return [Hash]
|
117
|
+
def as_json
|
118
|
+
{
|
119
|
+
current_player_number: current_player_number,
|
120
|
+
squares: squares.as_json,
|
121
|
+
last_double_step_pawn_id: last_double_step_pawn_id
|
122
|
+
}
|
123
|
+
end
|
124
|
+
|
125
|
+
# deep clone of the game state
|
126
|
+
#
|
127
|
+
# @return [GameState]
|
128
|
+
def clone
|
129
|
+
self.class.new(as_json)
|
130
|
+
end
|
131
|
+
|
132
|
+
# Moves a piece owned by the player, from one square, to another, with an optional promotion.
|
133
|
+
#
|
134
|
+
# It moves the piece and returns true if the move is valid and it's the player's turn.
|
135
|
+
# It returns false otherwise.
|
136
|
+
#
|
137
|
+
# ==== Example:
|
138
|
+
# # Moves a piece from a square to perform a move
|
139
|
+
# game_state.move(1, 'h7', 'h6')
|
140
|
+
#
|
141
|
+
# @param [Fixnum] player_number
|
142
|
+
# the player number, 1 or 2.
|
143
|
+
#
|
144
|
+
# @param [String] from_id
|
145
|
+
# the id of the from square
|
146
|
+
#
|
147
|
+
# @param [String] to_id
|
148
|
+
# the id of the to square
|
149
|
+
#
|
150
|
+
# @option [String] promote_to
|
151
|
+
# the type of the piece that the pawn will promote to
|
152
|
+
#
|
153
|
+
# @return [Boolean]
|
154
|
+
def move(player_number, from_id, to_id, promote_to=nil)
|
155
|
+
@errors = []
|
156
|
+
|
157
|
+
from = squares.find_by_id(from_id)
|
158
|
+
to = squares.find_by_id(to_id)
|
159
|
+
|
160
|
+
if current_player_number != player_number
|
161
|
+
@errors.push JustChess::NotPlayersTurnError.new
|
162
|
+
elsif from.unoccupied?
|
163
|
+
@errors.push JustChess::NoPieceError.new
|
164
|
+
elsif !((0..7).include?(to.x) && (0..7).include?(to.y))
|
165
|
+
@errors.push JustChess::OffBoardError.new
|
166
|
+
elsif !promote_to.nil? && !PROMOTABLE_PIECE_TYPES.include?(promote_to)
|
167
|
+
@errors.push JustChess::InvalidPromotion.new
|
168
|
+
elsif from.piece.can_move?(from, to, self)
|
169
|
+
|
170
|
+
duplicate = self.clone
|
171
|
+
duplicate.perform_complete_move(player_number, from_id, to_id, promote_to)
|
172
|
+
|
173
|
+
if duplicate.in_check?(current_player_number)
|
174
|
+
@errors.push JustChess::MovedIntoCheckError.new
|
175
|
+
else
|
176
|
+
perform_complete_move(player_number, from_id, to_id, promote_to)
|
177
|
+
end
|
178
|
+
else
|
179
|
+
@errors.push JustChess::InvalidMoveError.new
|
180
|
+
end
|
181
|
+
@errors.empty?
|
182
|
+
end
|
183
|
+
|
184
|
+
# The player number of the winner. It returns nil if there is no winner.
|
185
|
+
#
|
186
|
+
# @return [Fixnum,NilClass]
|
187
|
+
def winner
|
188
|
+
case
|
189
|
+
when in_checkmate?(1)
|
190
|
+
2
|
191
|
+
when in_checkmate?(2)
|
192
|
+
1
|
193
|
+
else
|
194
|
+
nil
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
# Moves a piece owned by the player, from one square, to another, with an optional promotion without validation
|
199
|
+
#
|
200
|
+
# It handles castling, en passant and promotion.
|
201
|
+
# It moves the piece and returns true if the move is valid and it's the player's turn.
|
202
|
+
# It returns false otherwise.
|
203
|
+
#
|
204
|
+
# ==== Example:
|
205
|
+
# # Moves a piece from a square to perform a move
|
206
|
+
# game_state.move(1, 'h7', 'h6')
|
207
|
+
#
|
208
|
+
# @param [Fixnum] player_number
|
209
|
+
# the player number, 1 or 2.
|
210
|
+
#
|
211
|
+
# @param [String] from_id
|
212
|
+
# the id of the from square
|
213
|
+
#
|
214
|
+
# @param [String] to_id
|
215
|
+
# the id of the to square
|
216
|
+
#
|
217
|
+
# @option [String] promote_to
|
218
|
+
# the type of the piece that the pawn will promote to
|
219
|
+
#
|
220
|
+
# @return [Boolean]
|
221
|
+
def perform_complete_move(player_number, from_id, to_id, promote_to=nil)
|
222
|
+
from = squares.find_by_id(from_id)
|
223
|
+
to = squares.find_by_id(to_id)
|
224
|
+
|
225
|
+
captured = captured_square(from, to)
|
226
|
+
double_step_pawn = from.piece.is_a?(JustChess::Pawn) && Vector.new(from,to).magnitude == 2
|
227
|
+
@last_double_step_pawn_id = double_step_pawn ? from.piece.id : nil
|
228
|
+
|
229
|
+
@last_change = { type: 'move', data: {player_number: player_number, from: from_id, to: to_id} }
|
230
|
+
|
231
|
+
rook_castle = rook_castle_move(from, to)
|
232
|
+
perform_move(rook_castle.from, rook_castle.to, nil) if rook_castle
|
233
|
+
|
234
|
+
perform_move(from, to, captured)
|
235
|
+
|
236
|
+
promote(to, promote_to) if pawn_moved_to_last_rank(to)
|
237
|
+
pass_turn
|
238
|
+
end
|
239
|
+
|
240
|
+
def in_check?(player_number)
|
241
|
+
king_square = squares.find_king_for_player(player_number)
|
242
|
+
threatened_by = squares.threatened_by(opposing_player_number(player_number), self)
|
243
|
+
threatened_by.include?(king_square)
|
244
|
+
end
|
245
|
+
|
246
|
+
def in_checkmate?(player_number)
|
247
|
+
in_check?(player_number) && king_cannot_move?(player_number)
|
248
|
+
end
|
249
|
+
|
250
|
+
|
251
|
+
def king_cannot_move?(player_number)
|
252
|
+
king_square = squares.find_king_for_player(player_number)
|
253
|
+
threatened_by = squares.threatened_by(opposing_player_number(player_number), self)
|
254
|
+
destinations = king_square.piece.destinations(king_square, self)
|
255
|
+
remove_threats = destinations - threatened_by
|
256
|
+
remove_threats.none?
|
257
|
+
end
|
258
|
+
|
259
|
+
private
|
260
|
+
|
261
|
+
def captured_square(from, to)
|
262
|
+
if to.occupied?
|
263
|
+
to
|
264
|
+
else
|
265
|
+
if from.piece.is_a?(Pawn) && from.piece.en_passant_square(from, self)
|
266
|
+
squares.find_by_piece_id(last_double_step_pawn_id)
|
267
|
+
else
|
268
|
+
nil
|
269
|
+
end
|
270
|
+
end
|
271
|
+
end
|
272
|
+
|
273
|
+
def rook_castle_move(from, to)
|
274
|
+
if from.occupied? && from.piece.is_a?(King) && from.piece.castle(from, self).include?(to)
|
275
|
+
vector = Vector.new(from, to)
|
276
|
+
|
277
|
+
rook_from_x = vector.direction.x > 0 ? 7 : 0
|
278
|
+
rook_from_y = from.y
|
279
|
+
rook_from = squares.find_by_x_and_y(rook_from_x, rook_from_y)
|
280
|
+
|
281
|
+
rook_to_x = vector.direction.x > 0 ? (from.x + 1) : (from.x -1)
|
282
|
+
rook_to_y = from.y
|
283
|
+
rook_to = squares.find_by_x_and_y(rook_to_x, rook_to_y)
|
284
|
+
|
285
|
+
Struct.new(:from, :to).new(rook_from, rook_to)
|
286
|
+
else
|
287
|
+
nil
|
288
|
+
end
|
289
|
+
end
|
290
|
+
|
291
|
+
def pawn_moved_to_last_rank(square)
|
292
|
+
square.piece && square.piece.is_a?(Pawn) && square.last_rank(square.piece.player_number)
|
293
|
+
end
|
294
|
+
|
295
|
+
def pass_turn
|
296
|
+
@current_player_number = opposing_player_number
|
297
|
+
end
|
298
|
+
|
299
|
+
def opposing_player_number(player_number=nil)
|
300
|
+
if player_number
|
301
|
+
other_player_number(player_number)
|
302
|
+
else
|
303
|
+
other_player_number(@current_player_number)
|
304
|
+
end
|
305
|
+
end
|
306
|
+
|
307
|
+
def other_player_number(player_number)
|
308
|
+
(player_number == 1) ? 2 : 1
|
309
|
+
end
|
310
|
+
|
311
|
+
def perform_move(from, to, captured)
|
312
|
+
captured.piece = nil if captured
|
313
|
+
to.piece = from.piece
|
314
|
+
from.piece = nil
|
315
|
+
to.piece.moved
|
316
|
+
end
|
317
|
+
|
318
|
+
def promote(square, promote_to)
|
319
|
+
new_piece = PieceFactory.new(type: promote_to, id: square.piece.id, player_number: square.piece.player_number).build
|
320
|
+
square.piece = new_piece
|
321
|
+
end
|
322
|
+
end
|
323
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'just_chess/pieces/pawn'
|
2
|
+
require 'just_chess/pieces/rook'
|
3
|
+
require 'just_chess/pieces/knight'
|
4
|
+
require 'just_chess/pieces/bishop'
|
5
|
+
require 'just_chess/pieces/queen'
|
6
|
+
require 'just_chess/pieces/king'
|
7
|
+
|
8
|
+
module JustChess
|
9
|
+
|
10
|
+
# = Piece Factory
|
11
|
+
#
|
12
|
+
# Generates pieces from a hash of arguments
|
13
|
+
class PieceFactory
|
14
|
+
|
15
|
+
# A mapping of type descriptions to classes
|
16
|
+
CLASSES = {
|
17
|
+
'pawn' => Pawn,
|
18
|
+
'rook' => Rook,
|
19
|
+
'knight' => Knight,
|
20
|
+
'bishop' => Bishop,
|
21
|
+
'queen' => Queen,
|
22
|
+
'king' => King
|
23
|
+
}
|
24
|
+
|
25
|
+
# New objects can be instantiated by passing in a hash or the piece.
|
26
|
+
#
|
27
|
+
# @param [Hash,Piece] args
|
28
|
+
# the initial attributes of the piece, hash requires type key
|
29
|
+
#
|
30
|
+
# ==== Example:
|
31
|
+
# # Instantiates a new PieceFactory
|
32
|
+
# JustChess::PieceFactory.new({
|
33
|
+
# type: 'pawn',
|
34
|
+
# id: 1,
|
35
|
+
# player_number: 2
|
36
|
+
# })
|
37
|
+
def initialize(args)
|
38
|
+
@args = args
|
39
|
+
end
|
40
|
+
|
41
|
+
# Returns a piece based on the initial arguments.
|
42
|
+
#
|
43
|
+
# @return [Piece]
|
44
|
+
def build
|
45
|
+
case @args
|
46
|
+
when Hash
|
47
|
+
build_from_hash
|
48
|
+
when Piece
|
49
|
+
@args
|
50
|
+
when nil
|
51
|
+
nil
|
52
|
+
else
|
53
|
+
raise ArgumentError, "piece must be Hash or NilClass"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
def build_from_hash
|
60
|
+
klass = CLASSES[@args[:type]]
|
61
|
+
if klass
|
62
|
+
klass.new(@args)
|
63
|
+
else
|
64
|
+
raise ArgumentError, "invalid piece type: #{@args[:type].to_s}"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'just_chess/pieces/piece'
|
2
|
+
|
3
|
+
module JustChess
|
4
|
+
|
5
|
+
# = Bishop
|
6
|
+
#
|
7
|
+
# The piece that moves diagonally any number of squares
|
8
|
+
class Bishop < Piece
|
9
|
+
|
10
|
+
# All the squares that the piece can move to and/or capture.
|
11
|
+
#
|
12
|
+
# @param [Square] square
|
13
|
+
# the origin square.
|
14
|
+
#
|
15
|
+
# @param [GameState] game_state
|
16
|
+
# the current game state.
|
17
|
+
#
|
18
|
+
# @return [SquareSet]
|
19
|
+
def destinations(square, game_state)
|
20
|
+
game_state.squares.diagonal(square).unoccupied_or_occupied_by_opponent(player_number).unblocked(square, game_state.squares)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|