Skip to content

Building Positions

Cyril Kato edited this page Oct 5, 2025 · 1 revision

Building Positions

FEEN positions can be built programmatically by constructing the three core components: Placement, Hands, and Styles. This is useful for generating positions, testing, or creating game engines.

Basic Construction

A position requires three components:

require "sashite/feen"

# 1. Build Placement (board configuration)
ranks = [
  [nil, nil, nil, nil, nil, nil, nil, nil],
  [nil, nil, nil, nil, nil, nil, nil, nil],
  [nil, nil, nil, nil, nil, nil, nil, nil],
  [nil, nil, nil, nil, nil, nil, nil, nil],
  [nil, nil, nil, nil, nil, nil, nil, nil],
  [nil, nil, nil, nil, nil, nil, nil, nil],
  [nil, nil, nil, nil, nil, nil, nil, nil],
  [nil, nil, nil, nil, nil, nil, nil, nil]
]
separators = ["/", "/", "/", "/", "/", "/", "/"]

placement = Sashite::Feen::Placement.new(ranks, separators, 2)

# 2. Build Hands (captured pieces)
hands = Sashite::Feen::Hands.new([], [])

# 3. Build Styles (game style and turn)
active = Sashite::Sin.parse("C")
inactive = Sashite::Sin.parse("c")
styles = Sashite::Feen::Styles.new(active, inactive)

# 4. Create Position
position = Sashite::Feen::Position.new(placement, hands, styles)

puts position.to_s
# => "8/8/8/8/8/8/8/8 / C/c"

Building with Pieces

Create pieces using EPIN and place them on the board:

# Parse pieces from EPIN strings
white_king = Sashite::Epin.parse("K")
white_queen = Sashite::Epin.parse("Q")
white_rook = Sashite::Epin.parse("R")

black_king = Sashite::Epin.parse("k")
black_queen = Sashite::Epin.parse("q")
black_rook = Sashite::Epin.parse("r")

# Build ranks (8×8 board)
ranks = [
  [black_rook, nil, nil, black_queen, black_king, nil, nil, black_rook],
  Array.new(8, nil),
  Array.new(8, nil),
  Array.new(8, nil),
  Array.new(8, nil),
  Array.new(8, nil),
  Array.new(8, nil),
  [white_rook, nil, nil, white_queen, white_king, nil, nil, white_rook]
]
separators = ["/"] * 7

placement = Sashite::Feen::Placement.new(ranks, separators, 2)
hands = Sashite::Feen::Hands.new([], [])
styles = Sashite::Feen::Styles.new(
  Sashite::Sin.parse("C"),
  Sashite::Sin.parse("c")
)

position = Sashite::Feen::Position.new(placement, hands, styles)

puts position.to_s
# => "r2qk2r/8/8/8/8/8/8/R2QK2R / C/c"

Adding Captured Pieces

Build hands with captured pieces:

# Create pieces for hands
white_pawns = [
  Sashite::Epin.parse("P"),
  Sashite::Epin.parse("P")
]

black_pawn = Sashite::Epin.parse("p")

# Empty board for clarity
ranks = [Array.new(8, nil)] * 8
separators = ["/"] * 7
placement = Sashite::Feen::Placement.new(ranks, separators, 2)

# Hands with captured pieces
hands = Sashite::Feen::Hands.new(white_pawns, [black_pawn])

styles = Sashite::Feen::Styles.new(
  Sashite::Sin.parse("C"),
  Sashite::Sin.parse("c")
)

position = Sashite::Feen::Position.new(placement, hands, styles)

puts position.to_s
# => "8/8/8/8/8/8/8/8 2P/p C/c"

Building 1D Positions

For one-dimensional boards, use empty separators:

# Single rank (1D board)
king = Sashite::Epin.parse("K")
pawn = Sashite::Epin.parse("P")
enemy_king = Sashite::Epin.parse("k")

ranks = [[king, nil, nil, pawn, nil, nil, nil, enemy_king]]
separators = []  # No separators for 1D

placement = Sashite::Feen::Placement.new(ranks, separators, 1)
hands = Sashite::Feen::Hands.new([], [])
styles = Sashite::Feen::Styles.new(
  Sashite::Sin.parse("C"),
  Sashite::Sin.parse("c")
)

position = Sashite::Feen::Position.new(placement, hands, styles)

puts position.to_s
# => "K2P3k / C/c"

puts position.placement.one_dimensional?
# => true

Building 3D Positions

Use double slashes for three-dimensional boards:

# Simple 2×2×2 cube
ranks = [
  [nil, nil],  # Plane 1, Rank 1
  [nil, nil],  # Plane 1, Rank 2
  [nil, nil],  # Plane 2, Rank 1
  [nil, nil]   # Plane 2, Rank 2
]

# Single "/" between ranks, "//" between planes
separators = ["/", "//", "/"]

placement = Sashite::Feen::Placement.new(ranks, separators, 3)
hands = Sashite::Feen::Hands.new([], [])
styles = Sashite::Feen::Styles.new(
  Sashite::Sin.parse("R"),
  Sashite::Sin.parse("r")
)

position = Sashite::Feen::Position.new(placement, hands, styles)

puts position.to_s
# => "2/2//2/2 / R/r"

puts position.placement.dimension
# => 3

Piece State Modifiers

Create pieces with enhanced or diminished states:

# Enhanced pieces (promoted/upgraded)
enhanced_rook = Sashite::Epin.parse("+R")
enhanced_pawn = Sashite::Epin.parse("+P")

# Diminished pieces (weakened/restricted)
diminished_king = Sashite::Epin.parse("-K")

# Foreign pieces (using opponent's style)
foreign_pawn = Sashite::Epin.parse("p'")

ranks = [
  [enhanced_rook, enhanced_pawn, nil, nil, nil, nil, nil, nil],
  Array.new(8, nil),
  Array.new(8, nil),
  Array.new(8, nil),
  Array.new(8, nil),
  Array.new(8, nil),
  Array.new(8, nil),
  [nil, nil, nil, nil, nil, nil, diminished_king, nil]
]
separators = ["/"] * 7

placement = Sashite::Feen::Placement.new(ranks, separators, 2)
hands = Sashite::Feen::Hands.new([foreign_pawn], [])
styles = Sashite::Feen::Styles.new(
  Sashite::Sin.parse("C"),
  Sashite::Sin.parse("s")
)

position = Sashite::Feen::Position.new(placement, hands, styles)

puts position.to_s
# => "+R+P6/8/8/8/8/8/8/6-K1 p'/ C/s"

Helper Function Example

Create a builder function for common scenarios:

def empty_chess_position(active_player: :white)
  ranks = [Array.new(8, nil)] * 8
  separators = ["/"] * 7
  placement = Sashite::Feen::Placement.new(ranks, separators, 2)

  hands = Sashite::Feen::Hands.new([], [])

  active, inactive = if active_player == :white
    [Sashite::Sin.parse("C"), Sashite::Sin.parse("c")]
  else
    [Sashite::Sin.parse("c"), Sashite::Sin.parse("C")]
  end

  styles = Sashite::Feen::Styles.new(active, inactive)

  Sashite::Feen::Position.new(placement, hands, styles)
end

# Use the helper
position = empty_chess_position(active_player: :black)

puts position.to_s
# => "8/8/8/8/8/8/8/8 / c/C"

Modifying Positions

Positions are immutable, so create new instances for changes:

original = Sashite::Feen.parse("8/8/8/8/8/8/8/8 / C/c")

# To "modify", create a new position with changed component
new_styles = Sashite::Feen::Styles.new(
  original.styles.inactive,  # Swap active/inactive
  original.styles.active
)

updated = Sashite::Feen::Position.new(
  original.placement,
  original.hands,
  new_styles
)

puts original.to_s
# => "8/8/8/8/8/8/8/8 / C/c"

puts updated.to_s
# => "8/8/8/8/8/8/8/8 / c/C"

Summary

  • Build positions from three components: Placement, Hands, Styles
  • Use EPIN to create piece objects with states and derivation
  • Use SIN to create style identifiers
  • Ranks and separators define board structure
  • All components are immutable - create new instances to "modify"
  • Helper functions can simplify common position creation patterns

Clone this wiki locally