chessmate 0.1.0 → 0.3.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: f042254ab0fe6dc50c0213638d69d171f375ab391dfca12e299b2de32ef28600
4
- data.tar.gz: 8b21d6530f391a31f13242305d855f5a04cedc670d8ea8eb6d11b297b144d517
3
+ metadata.gz: 6be20b4d6b07a0f81f64db1d90580456b3f9c8cfc516ede8a669139f473640d5
4
+ data.tar.gz: fc4eee51ad0a83400cc605c111a012ca37b02f54cf9eafb4dc3b9b054c72a917
5
5
  SHA512:
6
- metadata.gz: 90a11d0a8bcb5f1f2b95d0533f314c6288a2629fb8c2b7254017af75af5f78d5a010a838aba431bf4369700470feb9b25dc7168180fd7a8cbf6fcf6dc3372c5c
7
- data.tar.gz: 49066fb580c9ce0900e433162e61bc9f5967ec6abc2467aba4551242fee1391f70774db4371d72d46142426702768e2683b89cbb4500b9e6c5e2ad37fd554cb6
6
+ metadata.gz: 77431e006f69ea5c8aaa3788d2faaa8a08e919dc6462c6e621f7086d60ca5589dc2ceea3b3f056e6ae57ff6931425be7d912c87da886a416cdff55055b7efd0e
7
+ data.tar.gz: 9081e8d1c6f7d846c4f9a95696e981c9e18c0683ff4c39830a1853b80982b15f19787b6f690192e72dc311db14aa227f63411e400fa5825444299de4ffe2f588
@@ -0,0 +1,226 @@
1
+ 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
@@ -0,0 +1,24 @@
1
+ module NotationParser
2
+ def self.parse_notation(square)
3
+ col = square[0].downcase().ord - 97
4
+ row = 7 - ( square[1].to_i - 1 )
5
+
6
+ if col >= 0 && col < 8 && row >= 0 && row < 8
7
+ [row,col]
8
+ else
9
+ return nil
10
+ end
11
+ end
12
+
13
+ def self.encode_notation(coords)
14
+ row, col = coords
15
+
16
+ if col >= 0 && col < 8 && row >= 0 && row < 8
17
+ col = (col + 97).chr
18
+ row = (8 - row).to_s
19
+ return col + row
20
+ else
21
+ return nil
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,12 @@
1
+ require 'pieces/piece'
2
+
3
+ class Bishop < Piece
4
+ def self.move_is_valid?(orig, dest, board)
5
+ not_obstructed = !self.is_obstructed?(orig, dest, board)
6
+
7
+ not_obstructed &&
8
+ ( !self.destination_occupied?(dest, board) || self.is_capturable?(orig, dest, board) ) &&
9
+ (orig[0] - dest[0]).abs == (orig[1] - dest[1]).abs
10
+ end
11
+
12
+ end
@@ -0,0 +1,9 @@
1
+ require 'pieces/piece'
2
+
3
+ class King < Piece
4
+ def self.move_is_valid?(orig, dest, board)
5
+ (orig[0] - dest[0]).abs <= 1 && (orig[1] - dest[1]).abs <= 1 &&
6
+ ( !self.destination_occupied?(dest, board) || self.is_capturable?(orig, dest, board) )
7
+ end
8
+
9
+ end
@@ -0,0 +1,12 @@
1
+ require 'pieces/piece'
2
+
3
+ class Knight < Piece
4
+ def self.move_is_valid?(orig, dest, board)
5
+ x_offset = (orig[0] - dest[0]).abs
6
+ y_offset = (orig[1] - dest[1]).abs
7
+
8
+ ( !self.destination_occupied?(dest, board) || self.is_capturable?(orig, dest, board) ) &&
9
+ (x_offset == 2 && y_offset == 1) || (x_offset == 1 && y_offset == 2)
10
+ end
11
+
12
+ end
@@ -0,0 +1,34 @@
1
+ require 'pieces/piece'
2
+
3
+ class Pawn < Piece
4
+ def self.move_is_valid?(orig, dest, board)
5
+ orig_y = orig[0]
6
+ orig_x = orig[1]
7
+ dest_y = dest[0]
8
+ dest_x = dest[1]
9
+ piece_type = board[orig_y][orig_x]
10
+ piece_color = piece_type[0].downcase()
11
+
12
+ if piece_color == "w"
13
+ direction = 1
14
+ elsif piece_color == "b"
15
+ direction = -1
16
+ else
17
+ return false
18
+ end
19
+
20
+ if self.is_capturable?(orig,dest,board) &&
21
+ (orig_x - dest_x).abs == 1 &&
22
+ (orig_y - dest_y) * direction == 1
23
+ return true
24
+ end
25
+
26
+ not_obstructed = !self.is_obstructed?(orig, dest, board)
27
+
28
+ basic_move = ( (orig_y - dest_y) * direction == 1 && orig_x == dest_x )
29
+ move_double_on_first_turn = ( orig_y - dest_y == (2 * direction) ) && ( orig_x == dest_x )
30
+
31
+ move_double_on_first_turn && not_obstructed || basic_move
32
+ end
33
+
34
+ end
@@ -0,0 +1,76 @@
1
+ class Piece
2
+ def self.is_obstructed?(orig, dest, board)
3
+ orig_y = orig[0]
4
+ orig_x = orig[1]
5
+ dest_y = dest[0]
6
+ dest_x = dest[1]
7
+
8
+ if orig_y == dest_y
9
+
10
+ direction = orig_x > dest_x ? 1 : -1
11
+ ( (orig_x - dest_x).abs - 1 ).times do |x|
12
+ test_pos = orig_x - (x * direction) - direction
13
+ if !board[orig_y][test_pos].nil?
14
+ return true
15
+ end
16
+ end
17
+ return false
18
+
19
+ elsif orig_x == dest_x
20
+
21
+ direction = orig_y > dest_y ? 1 : -1
22
+ ( (orig_y - dest_y).abs - 1 ).times do |y|
23
+ test_pos = orig_y - (y * direction) - direction
24
+ if !board[test_pos][orig_x].nil?
25
+ return true
26
+ end
27
+ end
28
+
29
+ return false
30
+
31
+ elsif (orig_y - dest_y).abs == (orig_x - dest_x).abs
32
+
33
+ x_direction = orig_x > dest_x ? 1 : -1
34
+ y_direction = orig_y > dest_y ? 1 : -1
35
+
36
+ ( (orig_y - dest_y).abs - 1 ).times do |v|
37
+ test_y_pos = orig_y - (v * y_direction) - y_direction
38
+ test_x_pos = orig_x - (v * x_direction) - x_direction
39
+ if !board[test_y_pos][test_x_pos].nil?
40
+ return true
41
+ end
42
+ end
43
+
44
+ return false
45
+
46
+ else
47
+ return nil
48
+ end
49
+ end
50
+
51
+ def self.is_capturable?(orig, dest, board)
52
+ orig_y = orig[0]
53
+ orig_x = orig[1]
54
+ dest_y = dest[0]
55
+ dest_x = dest[1]
56
+ orig_piece = board[orig_y][orig_x]
57
+ dest_piece = board[dest_y][dest_x]
58
+
59
+ if orig_piece && dest_piece
60
+ orig_piece_color = orig_piece[0]
61
+ dest_piece_color = dest_piece[0]
62
+ else
63
+ return false
64
+ end
65
+
66
+ orig_piece_color != dest_piece_color
67
+
68
+ end
69
+
70
+ def self.destination_occupied?(dest, board)
71
+ dest_y = dest[0]
72
+ dest_x = dest[1]
73
+
74
+ !board[dest_y][dest_x].nil?
75
+ end
76
+ end
@@ -0,0 +1,17 @@
1
+ require 'pieces/piece'
2
+
3
+ class Queen < Piece
4
+ def self.move_is_valid?(orig, dest, board)
5
+
6
+ not_obstructed = !self.is_obstructed?(orig, dest, board)
7
+
8
+ not_obstructed &&
9
+ ( !self.destination_occupied?(dest, board) || self.is_capturable?(orig, dest, board) ) &&
10
+ (
11
+ (orig[0] - dest[0]).abs == (orig[1] - dest[1]).abs ||
12
+ orig[0] == dest[0] || orig[1] == dest[1]
13
+ )
14
+
15
+ end
16
+
17
+ end
@@ -0,0 +1,16 @@
1
+ require 'pieces/piece'
2
+
3
+ class Rook < Piece
4
+ def self.move_is_valid?(orig, dest, board)
5
+
6
+ not_obstructed = !self.is_obstructed?(orig, dest, board)
7
+
8
+ not_obstructed &&
9
+ ( !self.destination_occupied?(dest, board) || self.is_capturable?(orig, dest, board) ) &&
10
+ (
11
+ orig[0] == dest[0] || orig[1] == dest[1]
12
+ )
13
+
14
+ end
15
+
16
+ end