class Tak::Board

Constants

ROAD

What constitutes a road-forming piece

Attributes

black_piece_set[R]
board[RW]
size[RW]
white_piece_set[R]

Public Class Methods

new(size, ptn = nil) click to toggle source

Creates a new Tak Board

@param size [Integer] Size of the board @param ptn [Array] Previous game moveset to reconstruct from

# File lib/tak/board.rb, line 19
def initialize(size, ptn = nil)
  @size  = size
  @board = generate_board(ptn)

  @white_piece_set = Tak::PieceSet.new(board_size: size)
  @black_piece_set = Tak::PieceSet.new(board_size: size)

  @piece_sets = {
    white: white_piece_set,
    black: black_piece_set
  }
end

Public Instance Methods

bit_board(color) click to toggle source

Converts the current board into a bit variant in which viable road pieces for `color` are represented as 1s

@param color [Symbol]

@return [Array[Array]]

# File lib/tak/board.rb, line 75
def bit_board(color)
  @board.map { |row|
    row.map { |cell| ROAD[color].include?(cell.last) ? 1 : 0 }
  }
end
distribute_pieces(move) click to toggle source

Distributes pieces from a stack move across the board

@param move [Tak::Move]

@return [Boolean]

# File lib/tak/board.rb, line 146
def distribute_pieces(move)
  stack = pieces_at(*move.origin).pop(move.size)

  move.coordinates.each do |(x, y)|
    @board[x][y].push(stack.pop)
  end

  true
end
empty_piece_set?() click to toggle source

Checks to see if any piece sets are empty

@return [Boolean]

# File lib/tak/board.rb, line 49
def empty_piece_set?
  @piece_sets.any? { |c, piece_set| piece_set.empty? }
end
flat_counts() click to toggle source

Gets the current visible flat counts on the board

@return [Hash[Symbol, Integer]]

# File lib/tak/board.rb, line 35
def flat_counts
  count_hash = Hash.new { |h,k| h[k] = 0 }

  @board.each_with_object(count_hash) do |row, counts|
    row.each do |cell|
      counts[:white] += 1 if cell.last == 'w'
      counts[:black] += 1 if cell.last == 'b'
    end
  end
end
flat_winner() click to toggle source

Returns who the flat winner is, or false if there is none yet

Not a fan of the Bool/Sym response, consider refactor later.

@return [Boolean || Symbol]

# File lib/tak/board.rb, line 58
def flat_winner
  return false unless empty_piece_set?

  white_flats, black_flats = flat_counts.values
  case white_flats <=> black_flats
  when  0 then :tie
  when  1 then :white
  when -1 then :black
  end
end
move!(ptn, color) click to toggle source

Moves pieces according to PTN notation

@param ptn [String]

String representation of a Tak move

@param color [Symbol]

Which player is making the move

@return [Boolean]

Success status
# File lib/tak/board.rb, line 128
def move!(ptn, color)
  move = Tak::Move.new(ptn, self, color)

  return false unless move.valid? && square_owner(*move.origin)

  case move.type
  when 'movement'
    distribute_pieces(move)
  when 'placement'
    place_piece(move, color)
  end
end
pieces_at(x, y) click to toggle source

Retrieves the pieces present at a coordinate

@param x [Integer] @param y [Integer]

@return [Array]

# File lib/tak/board.rb, line 114
def pieces_at(x, y)
  @board[x][y]
end
place_piece(move, color) click to toggle source

Places a piece at a given move coordinate

@param move [Tak::Move] @param color [Symbol]

@return [Boolean]

Success of placement
# File lib/tak/board.rb, line 163
def place_piece(move, color)
  square = pieces_at(*move.origin)

  return false unless square.empty? && @piece_sets[color].remove(move.piece_type)

  square.push(move.piece)

  true
end
road_win?(color) click to toggle source

Checks if a road win for `color` is present

@param color [Symbol] Color to check for a win

@return [Boolean]

# File lib/tak/board.rb, line 86
def road_win?(color)
  current_bit_board = bit_board(color)

  # Begin traversing through the board for potential roads.
  path_search(0, 0, nil, current_bit_board) || !!@size.times.find { |n|
    path_search(1, n, :horizontal, current_bit_board) ||
    path_search(n, 1, :vertical, current_bit_board)
  }
end
square_owner(x, y) click to toggle source

Checks which color player currently owns a square

@param x [Integer] @param y [Integer]

@return [Symbol]

# File lib/tak/board.rb, line 102
def square_owner(x, y)
  return true if @board[x][y].empty?

  @board[x][y].last == 'b' ? :black : :white
end
to_s() click to toggle source

Consider moving this all out into a board formatter later. It'd be cleaner.

May Matz forgive me for my debugging code here.

# File lib/tak/board.rb, line 176
def to_s
  max_size = board.flatten(1).max.join(' ').size + 1

  counts = flat_counts.map { |c, i|
    set = @piece_sets[c]
    "#{c}: #{i} flats, #{set.flats} remaining pieces, #{set.capstones} remaining capstones"
  }.join("\n")

  board_state = board.reverse.map.with_index { |row, i|
    row_head = "  #{size - i} "

    row_head + row.map { |cell|
      "[%#{max_size}s]" % cell.join(' ')
    }.join(' ')
  }.join("\n")

  footer = ('a'..'h').take(size).map { |c| "%#{max_size + 2}s" % c }.join(' ')

  "#{counts}\n\n#{board_state}\n   #{footer}"
end

Private Instance Methods

generate_board(ptn) click to toggle source
# File lib/tak/board.rb, line 263
def generate_board(ptn)
  return Array.new(size) { Array.new(size) { [] } } unless ptn
end
out_of_bounds?(x,y) click to toggle source

Checks whether a given coordinate pair is out of bounds

@param x [Integer] @param y [Integer]

@return [Boolean]

# File lib/tak/board.rb, line 205
def out_of_bounds?(x,y)
  x < 0 || y < 0 || x > @size - 1 || y > @size - 1
end
road_end?(direction, x, y) click to toggle source

Checks to see if a given x y coordinate pair is counted as a road win by being on the appropriate opposite end from the starting direction of the road.

@param direction [Symbol] Starting direction of the road @param x [Integer] @param y [Integer]

@return [Boolean]

# File lib/tak/board.rb, line 245
def road_end?(direction, x, y)
  case direction
  when :horizontal
    x == @size - 1
  when :vertical
    y == @size - 1
  else
    x == @size - 1 || y == @size - 1
  end
end
visited_board() click to toggle source

Creates a board to mark previous traversals of the path search

@return [Array[Array]]

# File lib/tak/board.rb, line 259
def visited_board
  Array.new(@size) { Array.new(@size, false) }
end