def move(p1, p2)
      manifest = piece_manifest

      p1 = get_index_from_rowcol(p1.to_s)
      p2 = get_index_from_rowcol(p2.to_s)

      # Find valid positions and subtract king current position as nobody can directly take king piece
      valid_positions = possible_moves(p1, manifest, true)
      valid_positions -= king_positions

      # If player is moving out of turn, display message
      # `return p ...` is so we print value and return it from the function
      #  this is so the unit tests can get the value directly. There are better ways to do this
      unless @player_turn == @piece_locations[p1][:color]
        return p "It is #{@player_turn}'s turn. Please move a #{@player_turn} piece."
      end

      return p "Please select a valid destination." unless valid_positions.include?(p2)

      create_board_after_piece_moved(p1, p2)

      # If player is in check with the new proposed board, disallow the movement
      if check?(@player_turn, @piece_locations_buffer)
        return p "Please move #{@player_turn} king out of check to continue"
      end

      # At this point, the move appears to be valid
      @taken_pieces << @piece_locations[p2] unless @piece_locations[p2][:type].nil?

      # Check for Pawn Promotion (if pawn reaches end of the board, promote it)
      if @piece_locations_buffer[p2][:type] == :pawn
        if p2 < 9 && @piece_locations_buffer[p2][:color] == :red
          promote(p2)
        elsif p2 > 56 && @piece_locations_buffer[p2][:color] == :black
          promote(p2)
        end
      end

      # Check for Castling - https://en.m.wikipedia.org/wiki/Castling
      if @piece_locations_buffer[p2][:type] == :king && (p2 - p1).abs == 2

        y_offset = p2 < 9 ? 0 : 56

        if p2 > p1
          @piece_locations_buffer[6 + y_offset] = @piece_locations_buffer[8 + y_offset]
          @piece_locations_buffer[8 + y_offset] = {
            type:   nil,
            number: nil,
            color:  nil
          }
        else
          @piece_locations_buffer[4 + y_offset] = @piece_locations_buffer[1 + y_offset]
          @piece_locations_buffer[1 + y_offset] = {
            type:   nil,
            number: nil,
            color:  nil
          }
        end
      end

      commit_board_piece_movement

      if (winner = player_in_checkmate(@piece_locations))
        return p Messages.black_winner if winner == :black
        return p Messages.red_winner   if winner == :red
      end

      p Messages.piece_moved
    end
def move(p1, p2)
      manifest = piece_manifest

      p1 = get_index_from_rowcol(p1.to_s)
      p2 = get_index_from_rowcol(p2.to_s)

      # Find valid positions and subtract king current position as nobody can directly take king piece
      valid_positions = possible_moves(p1, manifest, true)
      valid_positions -= king_positions

      # If player is moving out of turn, display message
      # `return p ...` is so we print value and return it from the function
      #  this is so the unit tests can get the value directly. There are better ways to do this
      unless @player_turn == @piece_locations[p1][:color]
        return p "It is #{@player_turn}'s turn. Please move a #{@player_turn} piece."
      end

      return p "Please select a valid destination." unless valid_positions.include?(p2)

      create_board_after_piece_moved(p1, p2)

      # If player is in check with the new proposed board, disallow the movement
      if check?(@player_turn, @piece_locations_buffer)
        return p "Please move #{@player_turn} king out of check to continue"
      end

      # At this point, the move appears to be valid
      @taken_pieces << @piece_locations[p2] unless @piece_locations[p2][:type].nil?

      # Check for Pawn Promotion (if pawn reaches end of the board, promote it)
      if @piece_locations_buffer[p2][:type] == :pawn
        if p2 < 9 && @piece_locations_buffer[p2][:color] == :red
          promote(p2)
        elsif p2 > 56 && @piece_locations_buffer[p2][:color] == :black
          promote(p2)
        end
      end

      # Check for Castling - https://en.m.wikipedia.org/wiki/Castling
      if @piece_locations_buffer[p2][:type] == :king && (p2 - p1).abs == 2

        y_offset = p2 < 9 ? 0 : 56

        if p2 > p1
          @piece_locations_buffer[6 + y_offset] = @piece_locations_buffer[8 + y_offset]
          @piece_locations_buffer[8 + y_offset] = {
            type:   nil,
            number: nil,
            color:  nil
          }
        else
          @piece_locations_buffer[4 + y_offset] = @piece_locations_buffer[1 + y_offset]
          @piece_locations_buffer[1 + y_offset] = {
            type:   nil,
            number: nil,
            color:  nil
          }
        end
      end

      commit_board_piece_movement

      if (winner = player_in_checkmate(@piece_locations))
        return p Messages.black_winner if winner == :black
        return p Messages.red_winner   if winner == :red
      end

      p Messages.piece_moved
    end
Game logic