class ChessMate
Attributes
allow_out_of_turn[R]
board[R]
castling[R]
en_passant[R]
in_check[R]
move_history[R]
promotable[R]
turn[R]
Public Class Methods
new(board: nil, turn: nil, promotable: nil, en_passant: nil, castling: nil, in_check: nil, allow_out_of_turn: nil, move_history: nil, ignore_logging: false)
click to toggle source
# File lib/chessmate.rb, line 25 def initialize(board: nil, turn: nil, promotable: nil, en_passant: nil, castling: nil, in_check: nil, allow_out_of_turn: nil, move_history: nil, ignore_logging: false) @board = board || DEFAULT[:board].map(&:dup) @turn = turn || DEFAULT[:turn] @promotable = promotable @en_passant = en_passant || DeepDup.deep_dup(DEFAULT[:en_passant]) @castling = castling || DeepDup.deep_dup(DEFAULT[:castling]) @in_check = in_check || DeepDup.deep_dup(DEFAULT[:in_check]) @allow_out_of_turn = if allow_out_of_turn.nil? ENV['TEST'] == 'true' || false elsif [true, false].include?(allow_out_of_turn) allow_out_of_turn else false end @move_history = move_history || [] @ignore_logging = ignore_logging end
Public Instance Methods
any_valid_moves?(color)
click to toggle source
# File lib/chessmate.rb, line 221 def any_valid_moves?(color) test_board = @board.map(&:dup) test_board.each_with_index do |row, orig_y| row.each_with_index do |col, orig_x| next unless !col.nil? && col[0] == color orig_pos = NotationParser.encode_notation([orig_y, orig_x]) 8.times do |dest_x| 8.times do |dest_y| dest_pos = NotationParser.encode_notation([dest_y, dest_x]) next unless move(orig_pos, dest_pos, true) return true unless in_check_after_move?([orig_y, orig_x], [dest_y, dest_x]) end end end end false end
checkmate?(color)
click to toggle source
# File lib/chessmate.rb, line 242 def checkmate?(color) piece_color = color.downcase == 'w' ? :white : :black !any_valid_moves?(color) && in_check?[piece_color] end
draw?(color)
click to toggle source
# File lib/chessmate.rb, line 247 def draw?(color) piece_color = color.downcase == 'w' ? :white : :black !any_valid_moves?(color) && in_check?[piece_color] == false end
in_check?(board = nil)
click to toggle source
# File lib/chessmate.rb, line 106 def in_check?(board = nil) board = board.nil? ? @board : board wk_coords = bk_coords = nil board.each_with_index do |row, y| wk_coords = [y, row.index('WK')] if row.include?('WK') bk_coords = [y, row.index('BK')] if row.include?('BK') end wk_pos = NotationParser.encode_notation(wk_coords) if wk_coords bk_pos = NotationParser.encode_notation(bk_coords) if bk_coords white_in_check = black_in_check = false board.each_with_index do |row, y| row.each_with_index do |col, x| next if col.nil? piece_pos = NotationParser.encode_notation([y, x]) if col[0] == 'W' && bk_pos black_in_check = true if move(piece_pos, bk_pos, test: true) elsif col[0] == 'B' && wk_pos white_in_check = true if move(piece_pos, wk_pos, test: true) end end end { white: white_in_check, black: black_in_check } end
in_check_after_move?(orig, dest)
click to toggle source
# File lib/chessmate.rb, line 177 def in_check_after_move?(orig, dest) test_board = @board.map(&:dup) orig_y, orig_x = orig dest_y, dest_x = dest piece = test_board[orig_y][orig_x] piece_type = test_board[orig_y][orig_x] opposite_color = piece_type[0] == 'W' ? :black : :white direction = opposite_color == :white ? -1 : 1 if piece_type[1] == 'P' && @en_passant[opposite_color] == [dest_y + direction, dest_x] en_passant = true en_passant_coords = [@en_passant[opposite_color][0], @en_passant[opposite_color][1]] end test_board[en_passant_coords[0]][en_passant_coords[1]] = nil if en_passant test_board[orig_y][orig_x] = nil test_board[dest_y][dest_x] = piece_type piece_color = piece[0] king = piece_color + 'K' king_coords = nil test_board.each_with_index do |row, y| king_coords = [y, row.index(king)] if row.include?(king) end return false if king_coords.nil? king_pos = NotationParser.encode_notation(king_coords) test_board.each_with_index do |row, y| row.each_with_index do |col, x| next unless !col.nil? && col[0] != piece_color piece_pos = NotationParser.encode_notation([y, x]) return true if move(piece_pos, king_pos, true, test_board) end end false end
move(orig, dest, test = false, test_board = nil)
click to toggle source
# File lib/chessmate.rb, line 135 def move(orig, dest, test = false, test_board = nil) return false if @promotable orig_pos = NotationParser.parse_notation(orig) dest_pos = NotationParser.parse_notation(dest) return false if orig_pos.nil? || dest_pos.nil? orig_y = orig_pos[0] orig_x = orig_pos[1] piece = @board[orig_y][orig_x] piece_type = piece[1] allowed_turn = piece[0] == 'W' ? :odd? : :even? return false unless @allow_out_of_turn || @turn.send(allowed_turn) || test board = test_board.nil? ? @board : test_board valid_move = case piece_type when 'P' Pawn.move_is_valid?(orig_pos, dest_pos, board, @en_passant.dup) when 'R' Rook.move_is_valid?(orig_pos, dest_pos, board) when 'B' Bishop.move_is_valid?(orig_pos, dest_pos, board) when 'N' Knight.move_is_valid?(orig_pos, dest_pos, board) when 'Q' Queen.move_is_valid?(orig_pos, dest_pos, board) when 'K' King.move_is_valid?(orig_pos, dest_pos, board, @castling.dup) else false end unless test in_check_after_move = in_check_after_move?(orig_pos, dest_pos) update(orig_pos, dest_pos) if valid_move && !test && !in_check_after_move end valid_move && !in_check_after_move end
promote!(square, piece)
click to toggle source
# File lib/chessmate.rb, line 260 def promote!(square, piece) square_y = square[0] square_x = square[1] old_piece = @board[square_y][square_x] return nil if old_piece.nil? || @promotable != [square_y, square_x] case piece.downcase when 'rook' piece_type = 'R' when 'knight' piece_type = 'N' when 'bishop' piece_type = 'B' when 'queen' piece_type = 'Q' else return nil end @board[square_y][square_x] = old_piece[0] + piece_type @promotable = nil return if @ignore_logging logger = ChessLogger.new(nil, nil, nil, promotion_type: piece_type, history: @move_history) @move_history[-1] += logger.log_promotion end
promote?(square)
click to toggle source
# File lib/chessmate.rb, line 252 def promote?(square) square_y = square[0] square_x = square[1] piece_color = @board[square_y][square_x][0] promote_column = piece_color.downcase == 'w' ? 1 : 6 promote_column == square_y end
update(orig, dest = nil)
click to toggle source
# File lib/chessmate.rb, line 52 def update(orig, dest = nil) orig_y, orig_x = orig dest_y, dest_x = dest piece_type = @board[orig_y][orig_x] piece_color = piece_type[0] == 'W' ? :white : :black opposite_color = piece_type[0] == 'W' ? :black : :white direction = opposite_color == :white ? -1 : 1 @en_passant[piece_color] = nil if @en_passant[piece_color] case piece_type[1] when 'P' @en_passant[piece_color] = dest if (orig_y - dest_y).abs > 1 if @en_passant[opposite_color] == [dest_y + direction, dest_x] en_passant = true en_passant_coords = @en_passant[opposite_color] end when 'K' @castling[piece_color].keys.each do |side| @castling[piece_color][side] = false end if (orig_x - dest_x).abs == 2 castle = true old_rook_x_position = orig_x < dest_x ? 7 : 0 new_rook_x_position = orig_x < dest_x ? 5 : 3 end when 'R' if [0, 7].include?(orig_x) direction = orig_x == 7 ? :kingside : :queenside @castling[piece_color][direction] = false end end @board[en_passant_coords[0]][en_passant_coords[1]] = nil if en_passant if castle @board[orig_y][old_rook_x_position] = nil @board[orig_y][new_rook_x_position] = piece_type[0] + 'R' end @promotable = dest if piece_type[1] == 'P' && promote?(orig) unless @ignore_logging logger = ChessLogger.new(orig, dest, @board) @move_history << logger.log_move end @board[orig_y][orig_x] = nil @board[dest_y][dest_x] = piece_type @in_check = in_check? @turn += 1 end