chessmate 0.3.0 → 0.4.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6be20b4d6b07a0f81f64db1d90580456b3f9c8cfc516ede8a669139f473640d5
4
- data.tar.gz: fc4eee51ad0a83400cc605c111a012ca37b02f54cf9eafb4dc3b9b054c72a917
3
+ metadata.gz: 332ee0002250b755f4e40ba2555e71b3f1bb1fd878a78ee796272710a52e573e
4
+ data.tar.gz: 4704b9a6ae98589f5737ebd1a80f4dd75dcbdc8c90bfef2dd7bac0c4f6b926c7
5
5
  SHA512:
6
- metadata.gz: 77431e006f69ea5c8aaa3788d2faaa8a08e919dc6462c6e621f7086d60ca5589dc2ceea3b3f056e6ae57ff6931425be7d912c87da886a416cdff55055b7efd0e
7
- data.tar.gz: 9081e8d1c6f7d846c4f9a95696e981c9e18c0683ff4c39830a1853b80982b15f19787b6f690192e72dc311db14aa227f63411e400fa5825444299de4ffe2f588
6
+ metadata.gz: '06902d36017ad152fcf830da34e710f7fcde12ce009a71ce95ea883eee5e07d32ad3b98a7abe907046d39f8f55a1532ea42ed6ea6face6bf778286bfa7ffac36'
7
+ data.tar.gz: b2df6ef5de229691765377a3cdef36c8e4c733f6fb4869dd811a0861417fc0a0b68964915d41f0cab52a8e21773006672b703af45566af388f112873d0b3411e
@@ -1,226 +1,285 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class ChessMate
2
- require 'helpers/notation_parser'
3
- require 'pieces/pawn'
4
- require 'pieces/rook'
5
- require 'pieces/bishop'
6
- require 'pieces/knight'
7
- require 'pieces/queen'
8
- require 'pieces/king'
9
-
10
- attr_reader :board, :turn, :in_check
11
-
12
- def initialize(board=nil,turn=nil)
13
- if board.nil?
14
- @board =
15
- [
16
- ['BR', 'BN', 'BB', 'BQ', 'BK', 'BB', 'BN', 'BR'],
17
- ['BP', 'BP', 'BP', 'BP', 'BP', 'BP', 'BP', 'BP'],
18
- [nil, nil, nil, nil, nil, nil, nil, nil],
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
- ['WP', 'WP', 'WP', 'WP', 'WP', 'WP', 'WP', 'WP'],
23
- ['WR', 'WN', 'WB', 'WQ', 'WK', 'WB', 'WN', 'WR']
24
- ]
25
- else
26
- @board = board
27
- end
28
-
29
- if turn.nil?
30
- @turn = 1
31
- else
32
- @turn = turn
33
- end
34
-
35
- @in_check = {
36
- "white": false,
37
- "black": false
38
- }
39
- end
40
-
41
- def update(orig, dest=nil)
42
- orig_y = orig[0]
43
- orig_x = orig[1]
44
- dest_y = dest[0]
45
- dest_x = dest[1]
46
- piece_type = @board[orig_y][orig_x]
47
-
48
- # Hacky way of doing this. Will fail for en passant and castling.
49
- @board[orig_y][orig_x] = nil
50
- @board[dest_y][dest_x] = piece_type
51
-
52
- @turn += 1
53
- end
54
-
55
- def in_check?(board=nil)
56
- board = board.nil? ? @board : board
57
- wk_coords = bk_coords = nil
58
-
59
- board.each_with_index do |row, y|
60
- if row.include?("WK")
61
- wk_coords = [y, row.index("WK")]
62
- end
63
- if row.include?("BK")
64
- bk_coords = [y, row.index("BK")]
65
- end
66
- end
67
-
68
- if wk_coords.nil? || bk_coords.nil?
69
- return { "white": false, "black": false }
70
- end
71
-
72
- wk_pos = NotationParser.encode_notation(wk_coords)
73
- bk_pos = NotationParser.encode_notation(bk_coords)
74
-
75
- white_in_check = black_in_check = false
76
- board.each_with_index do |row, y|
77
- row.each_with_index do |col, x|
78
- if !col.nil?
79
- piece_pos = NotationParser.encode_notation([y,x])
80
- if col[0] == "W"
81
- if move(piece_pos, bk_pos, true)
82
- black_in_check = true
83
- end
84
- elsif col[0] == "B"
85
- if move(piece_pos, wk_pos, true)
86
- white_in_check = true
87
- end
88
- end
89
- end
90
- end
91
- end
92
-
93
- { "white": white_in_check, "black": black_in_check }
94
-
95
- end
96
-
97
- def move(orig, dest, test=false, test_board=nil)
98
- orig_pos = NotationParser.parse_notation(orig)
99
- dest_pos = NotationParser.parse_notation(dest)
100
-
101
- if orig_pos.nil? || dest_pos.nil?
102
- return false
103
- end
104
-
105
- orig_y = orig_pos[0]
106
- orig_x = orig_pos[1]
107
-
108
- piece = @board[orig_y][orig_x]
109
- piece_type = piece[1]
110
-
111
- if piece[0].downcase() == "w"
112
- piece_color = :white
113
- elsif piece[0].downcase() == "b"
114
- piece_color = :black
115
- else
116
- piece_color = nil
117
- end
118
-
119
- valid_move = nil
120
- board = test_board.nil? ? @board : test_board
121
- case piece_type
122
- when "P"
123
- valid_move = Pawn.move_is_valid?(orig_pos,dest_pos,board)
124
- when "R"
125
- valid_move = Rook.move_is_valid?(orig_pos,dest_pos,board)
126
- when "B"
127
- valid_move = Bishop.move_is_valid?(orig_pos,dest_pos,board)
128
- when "N"
129
- valid_move = Knight.move_is_valid?(orig_pos,dest_pos,board)
130
- when "Q"
131
- valid_move = Queen.move_is_valid?(orig_pos,dest_pos,board)
132
- when "K"
133
- valid_move = King.move_is_valid?(orig_pos,dest_pos,board)
134
- else
135
- valid_move = false
136
- end
137
-
138
- if !test
139
- @in_check = self.in_check?
140
- in_check_after_move = in_check_after_move?(orig_pos,dest_pos)
141
- end
142
-
143
- if valid_move && !test && !in_check_after_move
144
- self.update(orig_pos, dest_pos)
145
- end
146
-
147
- valid_move && !@in_check[piece_color] && !in_check_after_move
148
- end
149
-
150
- def in_check_after_move?(orig, dest)
151
- test_board = @board.map(&:dup)
152
-
153
- orig_y = orig[0]
154
- orig_x = orig[1]
155
- dest_y = dest[0]
156
- dest_x = dest[1]
157
- piece = test_board[orig_y][orig_x]
158
-
159
- # Hacky way of doing this. Will fail for en passant and castling.
160
- test_board[orig_y][orig_x] = nil
161
- test_board[dest_y][dest_x] = piece
162
-
163
- piece_color = piece[0]
164
- king = piece_color + "K"
165
-
166
- king_coords = nil
167
- test_board.each_with_index do |row, y|
168
- if row.include?(king)
169
- king_coords = [y, row.index(king)]
170
- end
171
- end
172
-
173
- if king_coords.nil?
174
- return false
175
- end
176
-
177
- king_pos = NotationParser.encode_notation(king_coords)
178
-
179
- test_board.each_with_index do |row, y|
180
- row.each_with_index do |col, x|
181
- if !col.nil? && col[0] != piece_color
182
- piece_pos = NotationParser.encode_notation([y,x])
183
- if move(piece_pos, king_pos, true, test_board)
184
- return true
185
- end
186
- end
187
- end
188
- end
189
-
190
- false
191
- end
192
-
193
- def any_valid_moves?(color)
194
- test_board = @board.map(&:dup)
195
-
196
- test_board.each_with_index do |row, orig_y|
197
- row.each_with_index do |col, orig_x|
198
- if !col.nil? && col[0] == color
199
- orig_pos = NotationParser.encode_notation([orig_y,orig_x])
200
- 8.times do |dest_x|
201
- 8.times do |dest_y|
202
- dest_pos = NotationParser.encode_notation([dest_y, dest_x])
203
- if move(orig_pos, dest_pos, true)
204
- if !in_check_after_move?([orig_y,orig_x],[dest_y,dest_x])
205
- return true
206
- end
207
- end
208
- end
209
- end
210
- end
211
- end
212
- end
213
-
214
- false
215
- end
216
-
217
- def checkmate?(color)
218
- piece_color = color.downcase == "w" ? :white : :black
219
- !any_valid_moves?(color) && in_check?[piece_color]
220
- end
221
-
222
- def draw?(color)
223
- piece_color = color.downcase == "w" ? :white : :black
224
- !any_valid_moves?(color) && in_check?[piece_color] == false
225
- end
226
- end
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