chessmate 0.1.0 → 0.3.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: 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