Qi (Chinese: 棋; pinyin: qí) is a lightweight, flexible, and adaptable tool for representing board game positions, built in Ruby. It is designed to be game-agnostic and can be used with a variety of board games such as Chess, Four-Player Chess, Go, Makruk, Shogi, and Xiangqi.
Qi uses a unique approach where the state of a game is represented through capturing the pieces in play, the arrangement of pieces on the board, the sequence of turns, and other possible states that a game can have.
- Game Agnostic: Qi can be used to represent board game positions for a wide variety of games. Whether you are playing Chess, Makruk, Shogi, or Xiangqi, Qi's flexible structure allows you to accurately capture the state of your game.
- Flexible Position Representation: Qi captures the state of the game by recording the pieces in play, their arrangement on the board, the sequence of turns, and other additional states of the game. This enables a comprehensive view of the game at any given point.
- State Manipulation: Qi allows for manipulation and update of game states through the
commit
method, allowing transitions between game states. - Equality Checks: With the
eql?
method, Qi allows for comparisons between different game states, which can be useful for tracking game progress, detecting repeats, or even in creating AI for your games. - Turn Management: Qi keeps track of the sequence of turns allowing users to identify whose turn it is currently.
- Access to Game Data: Qi provides methods to access the current arrangement of pieces on the board (
squares_hash
) and the pieces captured by each player (captures_hash
), helping users understand the current status of the game. It also allows access to a list of captured pieces (captures_array
). - Customizability: Qi is flexible and allows for customization as per your needs. The keys and values of the
captures_hash
andsquares_hash
can be any kind of object, as well as the items fromturns
and values fromstate
.
While Qi
does not generate game moves itself, it serves as a solid foundation upon which game engines can be built. Its design is focused on providing a robust and adaptable representation of game states, paving the way for the development of diverse board game applications.
Add this line to your application's Gemfile:
gem "qi"
And then execute:
bundle install
Or install it yourself as:
gem install qi
The following usage example is derived from a classic tsume shogi (詰将棋) problem, which translates to mate shogi - a popular genre of shogi problems where the goal is to checkmate the opponent's king. In the provided setup, the attacking side is in possession of a silver general (S), a promoted bishop (+B) positioned on square 43, and a promoted pawn (+P) on square 22.
On the defending side, there is a king (k) situated on square 4, surrounded by two silver generals (s) on squares 3 and 5 respectively.
In this scenario, Qi
allows us to represent the state of the game and apply changes as moves are made. Please follow the given example to understand how to create such a representation and how to update it:
require "qi"
# Initialize an array for each player's captured pieces
north_captures = %w[r r b g g g g s n n n n p p p p p p p p p p p p p p p p p]
south_captures = %w[S]
# Combine and count each player's captured pieces
captures = Hash.new(0)
(north_captures + south_captures).each { |piece| captures[piece] += 1 }
# Define the squares occupied by each piece on the board
squares = { 3 => "s", 4 => "k", 5 => "s", 22 => "+P", 43 => "+B" }
# Create a new game position
qi0 = Qi.new(captures, squares, [0, 1])
# Verify the properties of the game position
qi0.captures_array # => ["S", "b", "g", "g", "g", "g", "n", "n", "n", "n", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "r", "r", "s"]
qi0.captures_hash # => {"r"=>2, "b"=>1, "g"=>4, "s"=>1, "n"=>4, "p"=>17, "S"=>1}
qi0.squares_hash # => {3=>"s", 4=>"k", 5=>"s", 22=>"+P", 43=>"+B"}
qi0.state # => {}
qi0.turn # => 0
qi0.turns # => [0, 1]
qi0.eql?(Qi.new(captures, squares, [0, 1])) # => true
qi0.eql?(Qi.new(captures, squares, [1, 0])) # => false
# Move a piece on the board and check the game state
qi1 = qi0.commit([], [], { 43 => nil, 13 => "+B" }, in_check: true)
qi1.captures_array # => ["S", "b", "g", "g", "g", "g", "n", "n", "n", "n", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "r", "r", "s"]
qi1.captures_hash # => {"r"=>2, "b"=>1, "g"=>4, "s"=>1, "n"=>4, "p"=>17, "S"=>1}
qi1.squares_hash # => {3=>"s", 4=>"k", 5=>"s", 22=>"+P", 13=>"+B"}
qi1.state # => {:in_check=>true}
qi1.turn # => 1
qi1.turns # => [1, 0]
qi1.eql?(Qi.new(captures, squares, [0, 1])) # => false
qi1.eql?(Qi.new(captures, squares, [1, 0])) # => false
In this example, we first create a Qi
object to represent a game position with Qi.new
. Then, we check various aspects of the game state using the methods provided by Qi
. After that, we create a new game state qi1
by committing changes to the existing state qi0
. Finally, we again check various aspects of the new game state.
The code is available as open source under the terms of the MIT License.
This gem is proudly maintained and developed by Sashité. Our mission is to promote intercultural understanding and appreciation through the universal language of board games.
At Sashité, we believe in the power of games as a medium for sharing and appreciating the richness of different cultures. From Chinese to Japanese, and Western traditions, every culture has its unique representation in the world of board games, particularly in chess.
Our Qi
gem is a testament to this belief - a flexible, efficient, and inclusive software that allows for the representation and interaction of diverse chess systems. This piece of software is not just a tool; it is a bridge connecting different cultures under the love of strategic play.
Join us in our journey as we continue to write code to share the beauty of these cultures, one game at a time.