-
Notifications
You must be signed in to change notification settings - Fork 0
Cross‐Style Games
FEEN supports cross-style games where players use different piece sets or rule systems. This enables hybrid games like Chess vs Shōgi, Chess vs Makruk, or any combination of abstract strategy game styles.
In a cross-style game:
- Each player has a native style (their default piece tradition)
- Pieces can use foreign styles (the opponent's tradition)
- The style-turn field indicates which style each player uses
- Foreign pieces are marked with the derivation suffix
'
FEEN uses SIN (Style Identifier Notation) for game styles:
-
C/c- Chess (Western) -
S/s- Shōgi (Japanese) -
M/m- Makruk (Thai) -
X/x- Xiangqi (Chinese) - And more...
Case indicates the player:
- Uppercase = First player
- Lowercase = Second player
Chess vs Makruk starting position:
require "sashite/feen"
# First player uses Chess, second player uses Makruk
feen = "rnsmksnr/8/pppppppp/8/8/8/+P+P+P+P+P+P+P+P/+RNBQ+KBN+R / C/m"
position = Sashite::Feen.parse(feen)
# Check the styles
puts position.styles.active.to_s
# => "C" (Chess player to move)
puts position.styles.inactive.to_s
# => "m" (Makruk player waiting)
# Verify it's cross-style
first_style = position.styles.active.to_s.upcase
second_style = position.styles.inactive.to_s.upcase
puts first_style != second_style
# => true (different game styles)Pieces belong to one of two categories:
- Native pieces (no suffix): Use the controlling player's default style
-
Foreign pieces (
'suffix): Use the opponent's style
# Chess vs Shōgi with piece exchanges
feen = "lnsgkgsnl/1r5b1/pppppppp/9/9/9/+P+P+P+P+P+P+P+P/+RNBQ+KBN+R / C/s"
position = Sashite::Feen.parse(feen)
# Top ranks: Shōgi pieces (native for second player)
# Bottom ranks: Chess pieces (native for first player)
# If first player captures a shōgi piece, it becomes foreign:
# The captured piece would be notated as "l'" (foreign lance)When pieces are captured in cross-style games, they often become foreign pieces:
# Chess player holds captured Shōgi pieces (foreign)
feen = "lnsgkgsnl/1r5b1/pppppppp/9/9/9/+P+P+P+P+P+P+P+P/+RNBQ+KBN+R 2p'/s' C/s"
position = Sashite::Feen.parse(feen)
# Check first player's hand (Chess player)
first_hand = position.hands.first_player
puts first_hand.size
# => 2 (two pawns)
# Check if they're foreign pieces
puts first_hand.map(&:to_s)
# => ["p'", "p'"]
puts first_hand.all? { |piece| piece.to_s.end_with?("'") }
# => true (all foreign pieces)The active style indicates whose turn it is:
# Chess player to move
chess_turn = "8/8/8/8/8/8/8/8 / C/m"
pos1 = Sashite::Feen.parse(chess_turn)
puts pos1.styles.active.to_s
# => "C"
# Makruk player to move
makruk_turn = "8/8/8/8/8/8/8/8 / m/C"
pos2 = Sashite::Feen.parse(makruk_turn)
puts pos2.styles.active.to_s
# => "m"A complex Chess vs Shōgi position with captured pieces:
# Chess (first player) vs Shōgi (second player)
# Chess player has captured: 2 shōgi pawns, 1 silver (all foreign)
# Shōgi player has captured: 1 chess pawn (foreign)
feen = "lnsgkgsnl/1r5b1/pppp1pppp/9/4p4/2P6/+P+P+P1+P+P+P+P/+RNBQ+KBN+R/LNSGKGSNL 2p's'/P' C/s"
position = Sashite::Feen.parse(feen)
# Analyze the position
puts "Active player: #{position.styles.active}"
# => "C" (Chess to move)
# First player's hand (Chess player with foreign pieces)
first_hand = position.hands.first_player
puts "First player pieces in hand: #{first_hand.size}"
# => 3
puts "Foreign pieces: #{first_hand.map(&:to_s)}"
# => ["p'", "p'", "s'"]
# Second player's hand (Shōgi player with foreign piece)
second_hand = position.hands.second_player
puts "Second player pieces in hand: #{second_hand.size}"
# => 1
puts "Foreign pieces: #{second_hand.map(&:to_s)}"
# => ["P'"]While FEEN is rule-agnostic, your game engine can implement style-specific rules:
def can_drop_piece?(piece, style)
piece_style = piece.to_s.end_with?("'") ? "foreign" : "native"
case style.to_s.upcase
when "S" # Shōgi rules
# Shōgi allows dropping captured pieces
true
when "C" # Chess rules
# Standard chess doesn't allow drops
false
when "M" # Makruk rules
# Makruk has specific drop rules
can_drop_makruk_piece?(piece)
end
end
# Example usage
feen = "8/8/8/8/8/8/8/8 p'/P S/c"
position = Sashite::Feen.parse(feen)
active_style = position.styles.active
captured_piece = position.hands.first_player.first
puts can_drop_piece?(captured_piece, active_style)
# => true (Shōgi allows drops)Some cross-style games can be symmetric (both players use same style):
# Both players use Chess style
feen = "+rnbq+kbn+r/+p+p+p+p+p+p+p+p/8/8/8/8/+P+P+P+P+P+P+P+P/+RNBQ+KBN+R / C/c"
position = Sashite::Feen.parse(feen)
first_style = position.styles.active.to_s.upcase
second_style = position.styles.inactive.to_s.upcase
puts first_style == second_style
# => true (both use Chess)
# Even in same-style games, foreign pieces can exist
# (e.g., from promotion rules or special mechanics)Build cross-style positions programmatically:
# Create pieces using EPIN
first_king = Sashite::Epin.parse("K") # Native chess king
second_king = Sashite::Epin.parse("k") # Native shōgi king
# Create foreign pieces
foreign_pawn = Sashite::Epin.parse("p'") # Shōgi pawn (foreign to chess player)
# Build placement
ranks = [
[second_king],
Array.new(8, 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),
[first_king]
]
separators = ["/"] * 8
placement = Sashite::Feen::Placement.new(ranks, separators, 2)
# Build hands with foreign pieces
first_hand = [foreign_pawn]
second_hand = []
hands = Sashite::Feen::Hands.new(first_hand, second_hand)
# Build styles (Chess vs Shōgi)
active = Sashite::Sin.parse("C")
inactive = Sashite::Sin.parse("s")
styles = Sashite::Feen::Styles.new(active, inactive)
# Create position
position = Sashite::Feen::Position.new(placement, hands, styles)
puts position.to_s
# => "k/8/8/8/8/8/8/8/K p'/ C/s"- FEEN fully supports cross-style games with different rule systems
- Foreign pieces are marked with
'suffix - Style identifiers (SIN) use case to indicate player ownership
- Captured pieces can become foreign pieces in cross-style games
- FEEN remains rule-agnostic - your engine implements style-specific behavior