Class: ChessValidator::MoveLogic
- Inherits:
-
Object
- Object
- ChessValidator::MoveLogic
- Defined in:
- lib/move_logic.rb
Class Method Summary collapse
- .advance_pawn?(pawn, board, move) ⇒ Boolean
- .castled?(piece, move) ⇒ Boolean
- .collision?(position, destination, occupied_spaces, i1, i2) ⇒ Boolean
- .diagonal_collision?(position, destination, occupied_spaces) ⇒ Boolean
- .empty_square?(board, move) ⇒ Boolean
- .en_passant?(piece, move, square) ⇒ Boolean
- .find_king_and_spaces(board, color) ⇒ Object
- .find_piece(board, position) ⇒ Object
- .find_target(board, piece, move) ⇒ Object
- .handle_castle(board, move) ⇒ Object
- .handle_en_passant(board, pawn_color, move) ⇒ Object
- .handle_king(king, board, move, fen, occupied_spaces) ⇒ Object
- .handle_pawn(piece, board, move, fen) ⇒ Object
- .king_is_safe?(king_color, board, king_position, occupied_spaces) ⇒ Boolean
- .king_will_be_safe?(piece, board, move) ⇒ Boolean
- .load_move_data(board, piece, fen) ⇒ Object
- .make_move(piece, move, fen_notation) ⇒ Object
- .make_random_move(fen_notation, pieces_with_moves) ⇒ Object
- .moves_diagonal(vertical, horizontal, position) ⇒ Object
- .moves_for_bishop(position) ⇒ Object
- .moves_for_king(position) ⇒ Object
- .moves_for_knight(position) ⇒ Object
- .moves_for_pawn(pawn) ⇒ Object
- .moves_for_piece(piece) ⇒ Object
- .moves_for_queen(position) ⇒ Object
- .moves_for_rook(position) ⇒ Object
- .moves_horizontal(position) ⇒ Object
- .moves_vertical(position) ⇒ Object
- .next_moves(fen) ⇒ Object
- .previous_char(char) ⇒ Object
- .remove_out_of_bounds(moves) ⇒ Object
- .resolve_piece_type(piece_type, move) ⇒ Object
- .spaces_near_king(position) ⇒ Object
- .valid_destination?(piece, board, move) ⇒ Boolean
- .valid_move?(piece, board, move, fen) ⇒ Boolean
- .valid_move_path?(piece, move, occupied_spaces) ⇒ Boolean
- .with_next_move(piece, board, move) ⇒ Object
Class Method Details
.advance_pawn?(pawn, board, move) ⇒ Boolean
191 192 193 194 195 196 197 198 199 200 201 |
# File 'lib/move_logic.rb', line 191 def advance_pawn?(pawn, board, move) if (pawn.position[1].to_i - move[1].to_i).abs == 1 empty_square?(board, move) else occupied_spaces = [] board.values.each do |piece| occupied_spaces << piece.position end valid_move_path?(pawn, move, occupied_spaces) && empty_square?(board, move) end end |
.castled?(piece, move) ⇒ Boolean
120 121 122 |
# File 'lib/move_logic.rb', line 120 def castled?(piece, move) piece.piece_type.downcase == 'k' && (piece.position[0].ord - move[0].ord).abs == 2 end |
.collision?(position, destination, occupied_spaces, i1, i2) ⇒ Boolean
233 234 235 236 237 238 239 240 241 242 243 244 245 |
# File 'lib/move_logic.rb', line 233 def collision?(position, destination, occupied_spaces, i1, i2) start_index = position[i1] end_index = destination[i1] if start_index > end_index start_index = destination[i1] end_index = position[i1] end occupied_spaces.select do |space| space[i2] == position[i2] && space[i1] > start_index && space[i1] < end_index end.size > 0 end |
.diagonal_collision?(position, destination, occupied_spaces) ⇒ Boolean
247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 |
# File 'lib/move_logic.rb', line 247 def diagonal_collision?(position, destination, occupied_spaces) difference = (position[1].to_i - destination[1].to_i).abs - 1 horizontal_multiplyer = 1 horizontal_multiplyer = -1 if position[0] > destination[0] vertical_multiplyer = 1 vertical_multiplyer = -1 if position[1] > destination[1] move_path = [] difference.times do |n| column = (position[0].ord + ((n + 1) * horizontal_multiplyer)).chr row = (position[1].to_i + ((n + 1) * vertical_multiplyer)).to_s move_path << column + row end !(move_path & occupied_spaces).empty? end |
.empty_square?(board, move) ⇒ Boolean
216 217 218 |
# File 'lib/move_logic.rb', line 216 def empty_square?(board, move) board.values.detect { |piece| piece.position == move }.nil? end |
.en_passant?(piece, move, square) ⇒ Boolean
124 125 126 |
# File 'lib/move_logic.rb', line 124 def en_passant?(piece, move, square) piece.piece_type.downcase == 'p' && piece.position[0] != move[0] && !square end |
.find_king_and_spaces(board, color) ⇒ Object
134 135 136 137 138 139 140 141 142 |
# File 'lib/move_logic.rb', line 134 def find_king_and_spaces(board, color) occupied_spaces = [] king = nil board.values.each do |p| king = p if p.piece_type.downcase == 'k' && p.color == color occupied_spaces << p.position end [king, occupied_spaces] end |
.find_piece(board, position) ⇒ Object
212 213 214 |
# File 'lib/move_logic.rb', line 212 def find_piece(board, position) board.values.detect { |piece| piece.position == position } end |
.find_target(board, piece, move) ⇒ Object
32 33 34 35 36 37 38 39 |
# File 'lib/move_logic.rb', line 32 def find_target(board, piece, move) if board[INDEX_KEY[move]] board[INDEX_KEY[move]] elsif piece.piece_type.downcase == 'p' && piece.position[0] != move[0] en_passant_position = piece.color == 'w' ? move[0] + '5' : move[0] + '4' board[INDEX_KEY[en_passant_position]] end end |
.handle_castle(board, move) ⇒ Object
87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 |
# File 'lib/move_logic.rb', line 87 def handle_castle(board, move) case move when 'c1' board.delete(57) board[60] = Piece.new('R', 60) when 'g1' board.delete(64) board[62] = Piece.new('R', 62) when 'c8' board.delete(1) board[4] = Piece.new('r', 4) when 'g8' board.delete(8) board[6] = Piece.new('r', 6) end board end |
.handle_en_passant(board, pawn_color, move) ⇒ Object
76 77 78 79 80 81 82 83 84 85 |
# File 'lib/move_logic.rb', line 76 def handle_en_passant(board, pawn_color, move) if pawn_color == 'w' index = INDEX_KEY[move[0] + '5'] else index = INDEX_KEY[move[0] + '4'] end board.delete(index) board end |
.handle_king(king, board, move, fen, occupied_spaces) ⇒ Object
144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 |
# File 'lib/move_logic.rb', line 144 def handle_king(king, board, move, fen, occupied_spaces) if (king.position[0].ord - move[0].ord).abs == 2 empty_b_square = true if move[0] == 'c' castle_code = 'q' between = 'd' + move[1] empty_b_square = empty_square?(board, 'b' + move[1]) else castle_code = 'k' between = 'f' + move[1] end (fen.castling.include?(castle_code) && king.color == 'b' || fen.castling.include?(castle_code.upcase) && king.color == 'w') && king_is_safe?(king.color, board, king.position, occupied_spaces) && king_is_safe?(king.color, board, between, occupied_spaces) && king_is_safe?(king.color, board, move, occupied_spaces) && board.values.none? { |piece| [between, move].include?(piece.position) } && empty_b_square else valid_destination?(king, board, move) && king_is_safe?(king.color, board, move, occupied_spaces) end end |
.handle_pawn(piece, board, move, fen) ⇒ Object
179 180 181 182 183 184 185 186 187 188 189 |
# File 'lib/move_logic.rb', line 179 def handle_pawn(piece, board, move, fen) position = piece.position if position[0] == move[0] valid = advance_pawn?(piece, board, move) else target_piece = find_piece(board, move) valid = (target_piece && target_piece.color != piece.color) || move == fen.en_passant end valid && king_will_be_safe?(piece, board, move) end |
.king_is_safe?(king_color, board, king_position, occupied_spaces) ⇒ Boolean
166 167 168 169 170 171 172 173 174 175 176 177 |
# File 'lib/move_logic.rb', line 166 def king_is_safe?(king_color, board, king_position, occupied_spaces) board.values.none? do |piece| piece.color != king_color && moves_for_piece(piece).any? do |move| if piece.piece_type.downcase == 'p' king_position == move && piece.position[0] != king_position[0] else king_position == move && valid_move_path?(piece, king_position, occupied_spaces) end end end end |
.king_will_be_safe?(piece, board, move) ⇒ Boolean
128 129 130 131 132 |
# File 'lib/move_logic.rb', line 128 def king_will_be_safe?(piece, board, move) new_board = with_next_move(piece, board, move) king, occupied_spaces = find_king_and_spaces(new_board, piece.color) king_is_safe?(king.color, new_board, king.position, occupied_spaces) end |
.load_move_data(board, piece, fen) ⇒ Object
20 21 22 23 24 25 26 27 28 29 30 |
# File 'lib/move_logic.rb', line 20 def load_move_data(board, piece, fen) moves_for_piece(piece).each do |move| if valid_move?(piece, board, move, fen) piece.valid_moves << move target = find_target(board, piece, move) piece.targets << target if target else piece.move_potential << move end end end |
.make_move(piece, move, fen_notation) ⇒ Object
112 113 114 115 116 117 118 |
# File 'lib/move_logic.rb', line 112 def make_move(piece, move, fen_notation) fen = PGN::FEN.new(fen_notation) board = BoardLogic.build_board(fen) new_board = with_next_move(piece, board, move) BoardLogic.to_fen_notation(new_board, fen, piece, move) end |
.make_random_move(fen_notation, pieces_with_moves) ⇒ Object
106 107 108 109 110 |
# File 'lib/move_logic.rb', line 106 def make_random_move(fen_notation, pieces_with_moves) piece_to_move = pieces_with_moves.sample move = piece_to_move.valid_moves.sample make_move(piece_to_move, move, fen_notation) end |
.moves_diagonal(vertical, horizontal, position) ⇒ Object
296 297 298 299 300 301 302 303 304 305 306 307 |
# File 'lib/move_logic.rb', line 296 def moves_diagonal(vertical, horizontal, position) column = position[0] row = position[1] possible_moves = [] while column >= 'a' && column <= 'h' && row >= '1' && row <= '8' do column = horizontal == 'left' ? previous_char(column) : column.next row = vertical == 'up' ? row.next : previous_char(row) possible_moves << column + row end remove_out_of_bounds(possible_moves) end |
.moves_for_bishop(position) ⇒ Object
287 288 289 290 291 292 293 294 |
# File 'lib/move_logic.rb', line 287 def moves_for_bishop(position) top_right = moves_diagonal('up', 'right', position) top_left = moves_diagonal('up', 'left', position) bottom_left = moves_diagonal('down', 'left', position) bottom_right = moves_diagonal('down', 'right', position) top_right + top_left + bottom_left + bottom_right end |
.moves_for_king(position) ⇒ Object
334 335 336 337 |
# File 'lib/move_logic.rb', line 334 def moves_for_king(position) castle_moves = [(position[0].ord - 2).chr + position[1], position[0].next.next + position[1]] remove_out_of_bounds(spaces_near_king(position) + castle_moves) end |
.moves_for_knight(position) ⇒ Object
339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 |
# File 'lib/move_logic.rb', line 339 def moves_for_knight(position) column = position[0].ord row = position[1].to_i moves = [ (column - 2).chr + (row + 1).to_s, (column - 2).chr + (row - 1).to_s, (column + 2).chr + (row + 1).to_s, (column + 2).chr + (row - 1).to_s, (column - 1).chr + (row + 2).to_s, (column - 1).chr + (row - 2).to_s, (column + 1).chr + (row + 2).to_s, (column + 1).chr + (row - 2).to_s ] remove_out_of_bounds(moves) end |
.moves_for_pawn(pawn) ⇒ Object
361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 |
# File 'lib/move_logic.rb', line 361 def moves_for_pawn(pawn) column = pawn.position[0].ord row = pawn.color == 'w' ? pawn.position[1].to_i + 1 : pawn.position[1].to_i - 1 moves = [ (column - 1).chr + row.to_s, (column + 1).chr + row.to_s, column.chr + row.to_s ] if pawn.color == 'w' && pawn.position[1] == '2' || pawn.color == 'b' && pawn.position[1] == '7' two_forward = pawn.color == 'w' ? row + 1 : row - 1 moves << column.chr + two_forward.to_s end remove_out_of_bounds(moves) end |
.moves_for_piece(piece) ⇒ Object
266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 |
# File 'lib/move_logic.rb', line 266 def moves_for_piece(piece) case piece.piece_type.downcase when 'r' moves_for_rook(piece.position) when 'b' moves_for_bishop(piece.position) when 'q' moves_for_queen(piece.position) when 'k' moves_for_king(piece.position) when 'n' moves_for_knight(piece.position) when 'p' moves_for_pawn(piece) end end |
.moves_for_queen(position) ⇒ Object
313 314 315 |
# File 'lib/move_logic.rb', line 313 def moves_for_queen(position) moves_for_rook(position) + moves_for_bishop(position) end |
.moves_for_rook(position) ⇒ Object
283 284 285 |
# File 'lib/move_logic.rb', line 283 def moves_for_rook(position) moves_horizontal(position) + moves_vertical(position) end |
.moves_horizontal(position) ⇒ Object
378 379 380 381 382 383 384 385 386 387 388 389 |
# File 'lib/move_logic.rb', line 378 def moves_horizontal(position) possible_moves = [] column = 'a' row = position[1] 8.times do possible_moves << column + row unless column == position[0] column = column.next end possible_moves end |
.moves_vertical(position) ⇒ Object
391 392 393 394 395 396 397 398 399 400 401 402 |
# File 'lib/move_logic.rb', line 391 def moves_vertical(position) possible_moves = [] column = position[0] row = '1' 8.times do possible_moves << column + row unless row == position[1] row = row.next end possible_moves end |
.next_moves(fen) ⇒ Object
7 8 9 10 11 12 13 14 15 16 17 18 |
# File 'lib/move_logic.rb', line 7 def next_moves(fen) board = BoardLogic.build_board(fen) pieces = [] board.values.each do |piece| if piece.color == fen.active load_move_data(board, piece, fen) pieces << piece if piece.valid_moves.size > 0 end end pieces end |
.previous_char(char) ⇒ Object
309 310 311 |
# File 'lib/move_logic.rb', line 309 def previous_char(char) (char.ord - 1).chr end |
.remove_out_of_bounds(moves) ⇒ Object
357 358 359 |
# File 'lib/move_logic.rb', line 357 def remove_out_of_bounds(moves) moves.select { |move| ('a'..'h').include?(move[0]) && ('1'..'8').include?(move[1..-1]) } end |
.resolve_piece_type(piece_type, move) ⇒ Object
68 69 70 71 72 73 74 |
# File 'lib/move_logic.rb', line 68 def resolve_piece_type(piece_type, move) if piece_type.downcase == 'p' && ['1', '8'].include?(move[1]) move[1] == '8' ? 'Q' : 'q' else piece_type end end |
.spaces_near_king(position) ⇒ Object
317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 |
# File 'lib/move_logic.rb', line 317 def spaces_near_king(position) column = position[0].ord row = position[1].to_i moves = [ (column - 1).chr + row.to_s, (column + 1).chr + row.to_s, (column - 1).chr + (row - 1).to_s, (column - 1).chr + (row + 1).to_s, (column + 1).chr + (row - 1).to_s, (column + 1).chr + (row + 1).to_s, column.chr + (row + 1).to_s, column.chr + (row - 1).to_s ] remove_out_of_bounds(moves) end |
.valid_destination?(piece, board, move) ⇒ Boolean
203 204 205 206 207 208 209 210 |
# File 'lib/move_logic.rb', line 203 def valid_destination?(piece, board, move) target_piece = find_piece(board, move) if target_piece target_piece.color != piece.color else true end end |
.valid_move?(piece, board, move, fen) ⇒ Boolean
41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
# File 'lib/move_logic.rb', line 41 def valid_move?(piece, board, move, fen) occupied_spaces = [] board.values.each { |p| occupied_spaces << p.position } case piece.piece_type.downcase when 'k' handle_king(piece, board, move, fen, occupied_spaces) when 'p' handle_pawn(piece, board, move, fen) else valid_move_path?(piece, move, occupied_spaces) && valid_destination?(piece, board, move) && king_will_be_safe?(piece, board, move) end end |
.valid_move_path?(piece, move, occupied_spaces) ⇒ Boolean
220 221 222 223 224 225 226 227 228 229 230 231 |
# File 'lib/move_logic.rb', line 220 def valid_move_path?(piece, move, occupied_spaces) position = piece.position if piece.piece_type.downcase == 'n' true elsif position[0] == move[0] !collision?(piece.position, move, occupied_spaces, 1, 0) elsif position[1] == move[1] !collision?(piece.position, move, occupied_spaces, 0, 1) else !diagonal_collision?(piece.position, move, occupied_spaces) end end |
.with_next_move(piece, board, move) ⇒ Object
56 57 58 59 60 61 62 63 64 65 66 |
# File 'lib/move_logic.rb', line 56 def with_next_move(piece, board, move) index = INDEX_KEY[move] new_board = board.clone piece_type = resolve_piece_type(piece.piece_type, move) new_piece = Piece.new(piece_type, index) new_board.delete(piece.square_index) new_board[index] = new_piece new_board = handle_castle(new_board, move) if castled?(piece, move) new_board = handle_en_passant(new_board, piece.color, move) if en_passant?(piece, move, new_board[index]) new_board end |