module Move

Public Instance Methods

castle(index) click to toggle source

Castle: king cannot move into check, or through check

# File lib/move.rb, line 287
def castle(index)
  valid = []
  dangerous_tiles = attack_vectors

  # King may never have moved
  return valid unless [5, 61].include?(index) && @@pieces[index][:moved] == false

  # Ensure empty space between a King and a Rook
  if unoccupied?(index - 1) && unoccupied?(index - 2) && unoccupied?(index - 3) && @@pieces[index - 4][:moved] == false
    # Ensure king does not move through check or into check, and then add its castle position
    valid << index - 2 if !dangerous_tiles.include?(index - 1) && !dangerous_tiles.include?(index - 2)
  end

  if unoccupied?(index + 1) && unoccupied?(index + 2) && @@pieces[index + 3][:moved] == false
    valid << index + 2 if !dangerous_tiles.include?(index + 1) && !dangerous_tiles.include?(index + 2)
  end

  valid
end
constants(piece_mapping, color, piece) click to toggle source
# File lib/move.rb, line 5
def constants(piece_mapping, color, piece)
  @@pieces = piece_mapping
  @@color  = color
  @@type   = piece
  @@enemy_color = ([:black, :red] - [color]).first
end
get_col_from_index(index) click to toggle source

Obtain chess board column number (1 - 8) from an index (1 - 64)

# File lib/move.rb, line 334
def get_col_from_index(index)
  index % 8 == 0 ? 8 : index % 8
end
get_row_from_index(index) click to toggle source

Obtain chess board row number (1 + 8) from an index (1 - 64)

# File lib/move.rb, line 329
def get_row_from_index(index)
  (index - 1)/8 + 1
end
move_diagonal(index, limit = 8) click to toggle source

Diagonal movements. Will return all valid diagonal movies for a piece at index By default, it will extend the piece movement diagonally until it hits a board edge or until it hits a piece. This can be changed by passing the limit argument For example, the king can only move diagonally 1 position, so it would pass limit=1

# File lib/move.rb, line 219
def move_diagonal(index, limit = 8)

  row = get_row_from_index(index)
  col = get_col_from_index(index)
  left, right = [col-1, limit].min, [8-col, limit].min
  up, down = [row-1, limit].min, [8-row, limit].min
  valid = []

  # up and to the right
  ([up, right, limit].min).times do |i|
    next_pos = index - (i+1)*7
    # Valid move if position is unoccupied
    if unoccupied?(next_pos)
      valid << next_pos
    else
      # Valid move is piece is an enemy, but then no subsequent tiles are attackable
      # if the piece is not an enemy, it's not added as a valid move, and no subsequent tiles are attackable
      # This function doesn't filter out the king from a valid enemy, but the Board class will drop King indexes
      if piece_color(next_pos) == @@enemy_color
        valid << next_pos
      end
      break
    end
  end

  # up and to the left
  ([up, left, limit].min).times do |i|
    next_pos = index - (i+1)*9
    if unoccupied?(next_pos)
      valid << next_pos
    else
      if piece_color(next_pos) == @@enemy_color
        valid << next_pos
      end
      break
    end
  end

  # down and to the right
  ([down, right, limit].min).times do |i|
    next_pos = index + (i+1)*9
    if unoccupied?(next_pos)
      valid << next_pos
    else
      if piece_color(next_pos) == @@enemy_color
        valid << next_pos
      end
      break
    end
  end

  # down and to the left
  ([down, left, limit].min).times do |i|
    next_pos = index + (i+1)*7
    if unoccupied?(next_pos)
      valid << next_pos
    else
      if piece_color(next_pos) == @@enemy_color
        valid << next_pos
      end
      break
    end
  end

  valid
end
move_knight(p1) click to toggle source

Returns valid positions a knight at index p1 can move to

# File lib/move.rb, line 96
def move_knight(p1)
  row = get_row_from_index(p1)
  col = get_col_from_index(p1)

  valid = []
  valid_moves_no_friendly_fire = []

  # Valid knight moves based on its board position
  if row < 7 && col < 8
    valid << (p1 + 17)
  end
  if row < 7 && col > 1
    valid << (p1 + 15)
  end
  if row < 8 && col < 7
    valid << (p1 + 10)
  end
  if row < 8 && col > 2
    valid << (p1 + 6)
  end
  if row > 1 && col < 7
    valid << (p1 - 6)
  end
  if row > 1 && col > 2
    valid << (p1 - 10)
  end
  if row > 2 && col < 8
    valid << (p1 - 15)
  end
  if row > 2 && col > 1
    valid << (p1 - 17)
  end

  # All possible moves for the knight based on board boundaries will added
  # This iterator filters for friendly fire, and removes indexes pointing to same color pices
  valid.each do |pos|
    unless piece_color(pos) == @@color
      valid_moves_no_friendly_fire << pos
    end
  end

  valid_moves_no_friendly_fire
end
move_lateral(index, limit = 8) click to toggle source

Lateral movements (Left, Right, Up, Down). Will return all valid lateral movies for a piece at index By default, it will extend the piece movement laterally until it hits a board edge or until it hits a piece. This can be changed by passing the limit argument For example, the king can only move laterally 1 position, so it would pass limit=1

# File lib/move.rb, line 145
def move_lateral(index, limit = 8)
  row = get_row_from_index(index)
  col = get_col_from_index(index)

  left, right = [col-1, limit].min, [8-col, limit].min
  up, down = [row-1, limit].min, [8-row, limit].min

  valid = []

  # Move down N places until board limit, piece in the way, or specified limit
  down.times do |i|
    next_pos = index + (i+1)*8
    # Valid move if position is unoccupied
    if unoccupied?(next_pos)
      valid << next_pos
    else
      # Valid move is piece is an enemy, but then no subsequent tiles are attackable
      # if the piece is not an enemy, it's not added as a valid move, and no subsequent tiles are attackable
      # This function doesn't filter out the king from a valid enemy, but the Board class will drop King indexes
      if piece_color(next_pos) == @@enemy_color
        valid << next_pos
      end

      break
    end
  end

  # Move up N places until board limit, piece in the way, or specified limit
  up.times do |i|
    next_pos = index - (i+1)*8
    if unoccupied?(next_pos)
      valid << next_pos
    else
      if piece_color(next_pos) == @@enemy_color
        valid << next_pos
      end
      break
    end
  end

  # Move right N places until board limit, piece in the way, or specified limit
  right.times do |i|
    next_pos = index + (i+1)
    if unoccupied?(next_pos)
      valid << next_pos
    else
      if piece_color(next_pos) == @@enemy_color
        valid << next_pos
      end
      break
    end
  end

  # Move left N places until board limit, piece in the way, or specified limit
  left.times do |i|
    next_pos = index - (i+1)
    if unoccupied?(next_pos)
      valid << next_pos
    else
      if piece_color(next_pos) == @@enemy_color
        valid << next_pos
      end
      break
    end
  end

  valid
end
move_pawn(p1) click to toggle source

Returns all valid positions a pawn at index p1 can move to

# File lib/move.rb, line 53
def move_pawn(p1)
  row = get_row_from_index(p1)
  col = get_col_from_index(p1)
  valid = []

  # Piece color defines direction of travel. Enemy presence defines
  # the validity of diagonal movements
  if @@color == :red
    if unoccupied?(p1 - 8)
      valid << (p1 - 8)
    end
    if piece_color(p1 - 7) == @@enemy_color && col < 8
      valid << (p1 - 7)
    end
    if piece_color(p1 - 9) == @@enemy_color && col > 1
      valid << (p1 - 9)
    end
    # Only if the pieces is unmoved, can it move forward two rows
    if !@@pieces[p1][:moved] && unoccupied?(p1 - 8) && unoccupied?(p1 - 16)
      valid << (p1 - 16)
    end

  elsif @@color == :black

    if unoccupied?(p1 + 8)
      valid << (p1 + 8)
    end
    if piece_color(p1 + 7) == @@enemy_color && col > 1
      valid << (p1 + 7)
    end
    if piece_color(p1 + 9) == @@enemy_color && col < 8
      valid << (p1 + 9)
    end
    if !@@pieces[p1][:moved] && unoccupied?(p1 + 8) && unoccupied?(p1 + 16)
      valid << (p1 + 16)
    end
  end

  valid
end
moved?(index) click to toggle source

Return true if the piece has moved before

# File lib/move.rb, line 314
def moved?(index)
  @@pieces[index][:moved] ? true : false
end
not_king(index) click to toggle source

Method used when moving, to verify the piece at index (1 - 64) is not of type “king”

# File lib/move.rb, line 324
def not_king(index)
  @@piece_locations[index][:type] == :king
end
piece_color(index) click to toggle source

Return piece color (“red” or “black”) from index (1 - 64)

# File lib/move.rb, line 319
def piece_color(index)
  @@pieces[index][:color]
end
possible_moves(p1, manifest, castling = false) click to toggle source

Calls methods below to return a list of positions which are valid moves for piece at index p1, given the current board layout as defined in manifest

# File lib/move.rb, line 15
def possible_moves(p1, manifest, castling = false)
  return [] if manifest[p1][:type].nil?

  allowed   = []
  type      = manifest[p1][:type]
  my_color  = manifest[p1][:color]

  constants(manifest, my_color, type)

  return [] if unoccupied?(p1)

  if type == :king
    allowed += [move_lateral(p1, 1)].flatten
    allowed += [move_diagonal(p1, 1)].flatten
    allowed += [castle(p1)].flatten if castling

  elsif type == :queen
    allowed += [move_lateral(p1)].flatten
    allowed += [move_diagonal(p1)].flatten

  elsif type == :rook
    allowed += [move_lateral(p1)].flatten

  elsif type == :bishop
    allowed += [move_diagonal(p1)].flatten

  elsif type == :pawn
    allowed += [move_pawn(p1)].flatten

  elsif type == :knight
    allowed += [move_knight(p1)].flatten
  end

  allowed
end
unoccupied?(index) click to toggle source

Check if board tile currently has a piece

# File lib/move.rb, line 309
def unoccupied?(index)
  @@pieces[index][:color] == nil ? true : false
end