chessmate 0.3.0 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/chessmate.rb +284 -225
- data/lib/helpers/notation_parser.rb +18 -20
- data/lib/pieces/bishop.rb +9 -8
- data/lib/pieces/king.rb +44 -6
- data/lib/pieces/knight.rb +9 -8
- data/lib/pieces/pawn.rb +54 -31
- data/lib/pieces/piece.rb +68 -75
- data/lib/pieces/queen.rb +12 -13
- data/lib/pieces/rook.rb +11 -12
- data/spec/chessmate_spec.rb +1306 -923
- data/spec/notation_parser_spec.rb +45 -44
- data/spec/piece_spec.rb +83 -81
- data/spec/spec_helper.rb +53 -53
- metadata +16 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 332ee0002250b755f4e40ba2555e71b3f1bb1fd878a78ee796272710a52e573e
|
4
|
+
data.tar.gz: 4704b9a6ae98589f5737ebd1a80f4dd75dcbdc8c90bfef2dd7bac0c4f6b926c7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: '06902d36017ad152fcf830da34e710f7fcde12ce009a71ce95ea883eee5e07d32ad3b98a7abe907046d39f8f55a1532ea42ed6ea6face6bf778286bfa7ffac36'
|
7
|
+
data.tar.gz: b2df6ef5de229691765377a3cdef36c8e4c733f6fb4869dd811a0861417fc0a0b68964915d41f0cab52a8e21773006672b703af45566af388f112873d0b3411e
|
data/lib/chessmate.rb
CHANGED
@@ -1,226 +1,285 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
class ChessMate
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
4
|
+
require 'helpers/notation_parser'
|
5
|
+
require 'pieces/pawn'
|
6
|
+
require 'pieces/rook'
|
7
|
+
require 'pieces/bishop'
|
8
|
+
require 'pieces/knight'
|
9
|
+
require 'pieces/queen'
|
10
|
+
require 'pieces/king'
|
11
|
+
|
12
|
+
attr_reader :board, :turn, :in_check, :promotable, :en_passant, :castling
|
13
|
+
|
14
|
+
def initialize(board = nil, turn = nil)
|
15
|
+
@board = if board.nil?
|
16
|
+
[
|
17
|
+
%w[BR BN BB BQ BK BB BN BR],
|
18
|
+
%w[BP BP BP BP BP BP BP BP],
|
19
|
+
[nil, nil, nil, nil, nil, nil, nil, nil],
|
20
|
+
[nil, nil, nil, nil, nil, nil, nil, nil],
|
21
|
+
[nil, nil, nil, nil, nil, nil, nil, nil],
|
22
|
+
[nil, nil, nil, nil, nil, nil, nil, nil],
|
23
|
+
%w[WP WP WP WP WP WP WP WP],
|
24
|
+
%w[WR WN WB WQ WK WB WN WR]
|
25
|
+
]
|
26
|
+
else
|
27
|
+
board
|
28
|
+
end
|
29
|
+
|
30
|
+
@turn = if turn.nil?
|
31
|
+
1
|
32
|
+
else
|
33
|
+
turn
|
34
|
+
end
|
35
|
+
|
36
|
+
@promotable = nil
|
37
|
+
@en_passant = { white: nil, black: nil }
|
38
|
+
@castling = {
|
39
|
+
white: {
|
40
|
+
kingside: true,
|
41
|
+
queenside: true
|
42
|
+
},
|
43
|
+
black: {
|
44
|
+
kingside: true,
|
45
|
+
queenside: true
|
46
|
+
}
|
47
|
+
}
|
48
|
+
@in_check = { "white": false, "black": false }
|
49
|
+
end
|
50
|
+
|
51
|
+
def update(orig, dest = nil)
|
52
|
+
orig_y = orig[0]
|
53
|
+
orig_x = orig[1]
|
54
|
+
dest_y = dest[0]
|
55
|
+
dest_x = dest[1]
|
56
|
+
piece_type = @board[orig_y][orig_x]
|
57
|
+
opposite_color = piece_type[0] == 'W' ? :black : :white
|
58
|
+
direction = opposite_color == :white ? -1 : 1
|
59
|
+
|
60
|
+
if piece_type[1] == 'P' && @en_passant[opposite_color] == [dest_y + direction, dest_x]
|
61
|
+
en_passant = true
|
62
|
+
en_passant_coords = [@en_passant[opposite_color][0], @en_passant[opposite_color][1]]
|
63
|
+
end
|
64
|
+
|
65
|
+
if piece_type[1] == 'K' && (orig_x - dest_x).abs == 2
|
66
|
+
castle = true
|
67
|
+
old_rook_x_position = orig_x < dest_x ? 7 : 0
|
68
|
+
new_rook_x_position = orig_x < dest_x ? 5 : 3
|
69
|
+
end
|
70
|
+
|
71
|
+
@board[en_passant_coords[0]][en_passant_coords[1]] = nil if en_passant
|
72
|
+
if castle
|
73
|
+
@board[orig_y][old_rook_x_position] = nil
|
74
|
+
@board[orig_y][new_rook_x_position] = piece_type[0] + 'R'
|
75
|
+
end
|
76
|
+
|
77
|
+
@board[orig_y][orig_x] = nil
|
78
|
+
@board[dest_y][dest_x] = piece_type
|
79
|
+
|
80
|
+
@turn += 1
|
81
|
+
end
|
82
|
+
|
83
|
+
def in_check?(board = nil)
|
84
|
+
board = board.nil? ? @board : board
|
85
|
+
wk_coords = bk_coords = nil
|
86
|
+
|
87
|
+
board.each_with_index do |row, y|
|
88
|
+
wk_coords = [y, row.index('WK')] if row.include?('WK')
|
89
|
+
bk_coords = [y, row.index('BK')] if row.include?('BK')
|
90
|
+
end
|
91
|
+
|
92
|
+
wk_pos = NotationParser.encode_notation(wk_coords) if wk_coords
|
93
|
+
bk_pos = NotationParser.encode_notation(bk_coords) if bk_coords
|
94
|
+
|
95
|
+
white_in_check = black_in_check = false
|
96
|
+
board.each_with_index do |row, y|
|
97
|
+
row.each_with_index do |col, x|
|
98
|
+
next if col.nil?
|
99
|
+
|
100
|
+
piece_pos = NotationParser.encode_notation([y, x])
|
101
|
+
if col[0] == 'W' && bk_pos
|
102
|
+
black_in_check = true if move(piece_pos, bk_pos, true)
|
103
|
+
elsif col[0] == 'B' && wk_pos
|
104
|
+
white_in_check = true if move(piece_pos, wk_pos, true)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
{ "white": white_in_check, "black": black_in_check }
|
110
|
+
end
|
111
|
+
|
112
|
+
def move(orig, dest, test = false, test_board = nil)
|
113
|
+
orig_pos = NotationParser.parse_notation(orig)
|
114
|
+
dest_pos = NotationParser.parse_notation(dest)
|
115
|
+
|
116
|
+
return false if orig_pos.nil? || dest_pos.nil?
|
117
|
+
|
118
|
+
orig_y = orig_pos[0]
|
119
|
+
orig_x = orig_pos[1]
|
120
|
+
|
121
|
+
piece = @board[orig_y][orig_x]
|
122
|
+
piece_type = piece[1]
|
123
|
+
|
124
|
+
piece_color = if piece[0].downcase == 'w'
|
125
|
+
:white
|
126
|
+
elsif piece[0].downcase == 'b'
|
127
|
+
:black
|
128
|
+
end
|
129
|
+
|
130
|
+
@en_passant[piece_color] = nil if @en_passant[piece_color]
|
131
|
+
|
132
|
+
board = test_board.nil? ? @board : test_board
|
133
|
+
valid_move = case piece_type
|
134
|
+
when 'P'
|
135
|
+
Pawn.move_is_valid?(orig_pos, dest_pos, board, @en_passant)
|
136
|
+
when 'R'
|
137
|
+
Rook.move_is_valid?(orig_pos, dest_pos, board)
|
138
|
+
when 'B'
|
139
|
+
Bishop.move_is_valid?(orig_pos, dest_pos, board)
|
140
|
+
when 'N'
|
141
|
+
Knight.move_is_valid?(orig_pos, dest_pos, board)
|
142
|
+
when 'Q'
|
143
|
+
Queen.move_is_valid?(orig_pos, dest_pos, board)
|
144
|
+
when 'K'
|
145
|
+
King.move_is_valid?(orig_pos, dest_pos, board, @castling)
|
146
|
+
else
|
147
|
+
false
|
148
|
+
end
|
149
|
+
|
150
|
+
unless test
|
151
|
+
@in_check = in_check?
|
152
|
+
in_check_after_move = in_check_after_move?(orig_pos, dest_pos)
|
153
|
+
if valid_move
|
154
|
+
case piece_type
|
155
|
+
when 'P'
|
156
|
+
@en_passant[piece_color] = dest_pos if (orig_pos[0] - dest_pos[0]).abs > 1
|
157
|
+
when 'K'
|
158
|
+
@castling[piece_color].keys.each do |direction|
|
159
|
+
@castling[piece_color][direction] = false
|
160
|
+
end
|
161
|
+
when 'R'
|
162
|
+
if [0, 7].include?(orig_x)
|
163
|
+
direction = orig_x == 7 ? :kingside : :queenside
|
164
|
+
@castling[piece_color][direction] = false
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
if valid_move && !test && !in_check_after_move
|
171
|
+
update(orig_pos, dest_pos)
|
172
|
+
@promotable = dest_pos if piece_type == 'P' && promote?(dest_pos)
|
173
|
+
end
|
174
|
+
|
175
|
+
valid_move && !@in_check[piece_color] && !in_check_after_move
|
176
|
+
end
|
177
|
+
|
178
|
+
def in_check_after_move?(orig, dest)
|
179
|
+
test_board = @board.map(&:dup)
|
180
|
+
|
181
|
+
orig_y = orig[0]
|
182
|
+
orig_x = orig[1]
|
183
|
+
dest_y = dest[0]
|
184
|
+
dest_x = dest[1]
|
185
|
+
piece = test_board[orig_y][orig_x]
|
186
|
+
piece_type = test_board[orig_y][orig_x]
|
187
|
+
opposite_color = piece_type[0] == 'W' ? :black : :white
|
188
|
+
direction = opposite_color == :white ? -1 : 1
|
189
|
+
|
190
|
+
if piece_type[1] == 'P' && @en_passant[opposite_color] == [dest_y + direction, dest_x]
|
191
|
+
en_passant = true
|
192
|
+
en_passant_coords = [@en_passant[opposite_color][0], @en_passant[opposite_color][1]]
|
193
|
+
end
|
194
|
+
|
195
|
+
test_board[en_passant_coords[0]][en_passant_coords[1]] = nil if en_passant
|
196
|
+
|
197
|
+
test_board[orig_y][orig_x] = nil
|
198
|
+
test_board[dest_y][dest_x] = piece_type
|
199
|
+
|
200
|
+
piece_color = piece[0]
|
201
|
+
king = piece_color + 'K'
|
202
|
+
|
203
|
+
king_coords = nil
|
204
|
+
test_board.each_with_index do |row, y|
|
205
|
+
king_coords = [y, row.index(king)] if row.include?(king)
|
206
|
+
end
|
207
|
+
|
208
|
+
return false if king_coords.nil?
|
209
|
+
|
210
|
+
king_pos = NotationParser.encode_notation(king_coords)
|
211
|
+
|
212
|
+
test_board.each_with_index do |row, y|
|
213
|
+
row.each_with_index do |col, x|
|
214
|
+
next unless !col.nil? && col[0] != piece_color
|
215
|
+
|
216
|
+
piece_pos = NotationParser.encode_notation([y, x])
|
217
|
+
return true if move(piece_pos, king_pos, true, test_board)
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
false
|
222
|
+
end
|
223
|
+
|
224
|
+
def any_valid_moves?(color)
|
225
|
+
test_board = @board.map(&:dup)
|
226
|
+
|
227
|
+
test_board.each_with_index do |row, orig_y|
|
228
|
+
row.each_with_index do |col, orig_x|
|
229
|
+
next unless !col.nil? && col[0] == color
|
230
|
+
|
231
|
+
orig_pos = NotationParser.encode_notation([orig_y, orig_x])
|
232
|
+
8.times do |dest_x|
|
233
|
+
8.times do |dest_y|
|
234
|
+
dest_pos = NotationParser.encode_notation([dest_y, dest_x])
|
235
|
+
next unless move(orig_pos, dest_pos, true)
|
236
|
+
return true unless in_check_after_move?([orig_y, orig_x], [dest_y, dest_x])
|
237
|
+
end
|
238
|
+
end
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
false
|
243
|
+
end
|
244
|
+
|
245
|
+
def checkmate?(color)
|
246
|
+
piece_color = color.downcase == 'w' ? :white : :black
|
247
|
+
!any_valid_moves?(color) && in_check?[piece_color]
|
248
|
+
end
|
249
|
+
|
250
|
+
def draw?(color)
|
251
|
+
piece_color = color.downcase == 'w' ? :white : :black
|
252
|
+
!any_valid_moves?(color) && in_check?[piece_color] == false
|
253
|
+
end
|
254
|
+
|
255
|
+
def promote?(square)
|
256
|
+
square_y = square[0]
|
257
|
+
square_x = square[1]
|
258
|
+
piece = @board[square_y][square_x][0]
|
259
|
+
promote_column = piece.downcase == 'w' ? 0 : 7
|
260
|
+
promote_column == square_y
|
261
|
+
end
|
262
|
+
|
263
|
+
def promote!(square, piece)
|
264
|
+
square_y = square[0]
|
265
|
+
square_x = square[1]
|
266
|
+
|
267
|
+
old_piece = @board[square_y][square_x]
|
268
|
+
return nil if old_piece.nil? || !promote?(square)
|
269
|
+
|
270
|
+
case piece.downcase
|
271
|
+
when 'rook'
|
272
|
+
piece_type = 'R'
|
273
|
+
when 'knight'
|
274
|
+
piece_type = 'N'
|
275
|
+
when 'bishop'
|
276
|
+
piece_type = 'B'
|
277
|
+
when 'queen'
|
278
|
+
piece_type = 'Q'
|
279
|
+
else
|
280
|
+
return nil
|
281
|
+
end
|
282
|
+
|
283
|
+
@board[square_y][square_x] = old_piece[0] + piece_type
|
284
|
+
end
|
285
|
+
end
|