FEEN (Forsyth–Edwards Essential Notation) support for the Ruby language.
FEEN (Forsyth–Edwards Essential Notation) is a compact, canonical, and rule-agnostic textual format for representing static board positions in two-player piece-placement games.
This gem implements the FEEN Specification v1.0.0, providing a Ruby interface for:
- Multiple game types (chess, shogi, xiangqi, etc.)
- Hybrid or cross-game positions
- Arbitrary-dimensional boards
- Pieces in hand (as used in Shogi)
# In your Gemfile
gem "feen", ">= 5.0.0.beta2"
Or install manually:
gem install feen --pre
A FEEN record consists of three space-separated fields:
<PIECE-PLACEMENT> <GAMES-TURN> <PIECES-IN-HAND>
Convert a FEEN string into a structured Ruby object:
require "feen"
feen_string = "rnbqk=bnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQK=BNR CHESS/chess -"
position = Feen.parse(feen_string)
# Result is a hash with structured position data
# position[:piece_placement] # 2D array of board pieces
# position[:games_turn] # Details about active player and game
# position[:pieces_in_hand] # Array of pieces held for dropping
Convert position components to a FEEN string using named arguments:
require "feen"
piece_placement = [
[{ id: "r" }, { id: "n" }, { id: "b" }, { id: "q" }, { id: "k", suffix: "=" }, { id: "b" }, { id: "n" }, { id: "r" }],
[{ id: "p" }, { id: "p" }, { id: "p" }, { id: "p" }, { id: "p" }, { id: "p" }, { id: "p" }, { id: "p" }],
[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],
[{ id: "P" }, { id: "P" }, { id: "P" }, { id: "P" }, { id: "P" }, { id: "P" }, { id: "P" }, { id: "P" }],
[{ id: "R" }, { id: "N" }, { id: "B" }, { id: "Q" }, { id: "K", suffix: "=" }, { id: "B" }, { id: "N" }, { id: "R" }]
]
result = Feen.dump(
piece_placement: piece_placement,
active_variant: "CHESS",
inactive_variant: "chess",
pieces_in_hand: []
)
# => "rnbqk=bnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQK=BNR CHESS/chess -"
Check if a string is valid FEEN notation:
require "feen"
Feen.valid?("rnbqk=bnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQK=BNR CHESS/chess -")
# => true
Feen.valid?("invalid feen string")
# => false
require "feen"
fen_string = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"
feen_string = Feen.from_fen(fen_string)
# => "rnbqk=bnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQK=BNR CHESS/chess -"
require "feen"
feen_string = "rnbqk=bnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQK=BNR CHESS/chess -"
fen_string = Feen.to_fen(feen_string)
# => "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"
⚠️ Feen.to_fen
only supports FEEN positions wheregames_turn
isCHESS/chess
orchess/CHESS
.
FEEN can represent positions from shogi (Japanese chess) with full support for promoted pieces and pieces in hand:
lnsgk3l/5g3/p1ppB2pp/9/8B/2P6/P2PPPPPP/3K3R1/5rSNL SHOGI/shogi N5P2g2snl
In this position:
SHOGI/shogi
indicates it's Sente's (Black's) turn to moveN5P2g2snl
shows the pieces in hand: Sente (Black) has a Knight and 5 Pawns, while Gote (White) has 2 Golds, 2 Silvers, a Knight, and a Lance
- By convention, Sente's (Black's) pieces are represented in uppercase, while Gote's (White's) pieces are in lowercase
- Unlike in chess, in shogi, Black (Sente) is positioned at the bottom (south) of the board, and in the initial position, Black plays first (similar to White in chess)
- In the
SHOGI/shogi
games-turn field, uppercaseSHOGI
refers to the player using uppercase pieces (Sente/Black)
This demonstrates how FEEN adapts naturally to different game conventions while maintaining consistent notation principles.
FEEN supports arbitrary-dimensional board configurations:
require "feen"
# 3D board
piece_placement = [
[
[{ id: "r" }, { id: "n" }, { id: "b" }],
[{ id: "q" }, { id: "k" }, { id: "p" }]
],
[
[{ id: "P" }, { id: "R" }, nil],
[nil, { id: "K" }, { id: "Q" }]
]
]
result = Feen.dump(
piece_placement: piece_placement,
active_variant: "CHESS",
inactive_variant: "chess",
pieces_in_hand: []
)
# => "rnb/qkp//PR1/1KQ CHESS/chess -"
FEEN supports prefixes and suffixes for pieces to convey special states or capabilities:
-
Prefix
+
: Indicates promotion or special state- Example:
+P
represents a promoted pawn in Shogi (e.g., a Dragon Horse)
- Example:
-
Suffix
=
: Indicates dual-option status or special capability- Example:
K=
represents a king eligible for both kingside and queenside castling - Example:
P=
represents a pawn that may be captured en passant from both left and right
- Example:
-
Suffix
<
: Indicates left-side constraint or condition- Example:
K<
represents a king eligible for queenside castling only - Example:
P<
represents a pawn that may be captured en passant from the left
- Example:
-
Suffix
>
: Indicates right-side constraint or condition- Example:
K>
represents a king eligible for kingside castling only - Example:
P>
represents a pawn that may be captured en passant from the right
- Example:
These modifiers allow FEEN to encode rule-specific information like castling rights and en passant possibilities while maintaining its rule-agnostic design. When a piece is captured and becomes a "piece in hand" (available for dropping), its modifiers are typically removed.
FEEN includes utilities to clean FEN strings by validating and removing invalid castling rights and en passant targets:
require "feen"
# FEN with invalid castling rights
fen_string = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQ1BNR w KQkq - 0 1"
cleaned_fen = Feen::Sanitizer.clean_fen(fen_string)
# => "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQ1BNR w kq - 0 1"
The gem is available as open source under the terms of the MIT License.
This gem is maintained by Sashité.
With some lines of code, let's share the beauty of Chinese, Japanese and Western cultures through the game of chess!