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 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