qi 10.0.0.beta11 → 10.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +68 -42
- data/lib/qi.rb +91 -242
- metadata +7 -7
- data/lib/qi/error/drop.rb +0 -13
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4a98ed653f1c1d21ae215f4a8ab481a0a19dd93551a9a3ba0faf9ee8e541291f
|
4
|
+
data.tar.gz: d1291d436f366c70e07a64a3a6390a3a9d99d56cfeb8b0b85c1002addbe7b540
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 154aa839e292e2cadfbafe8c64e324285216da01a491ef98815479bf1bd735dc2ec747ad31e6ef479ea4d913b262dbcb9ad6399f13cdf5199dddb3403d7091ee
|
7
|
+
data.tar.gz: 52c5ca680fb11577fc2aa84931900b9f8c712e6c300ca5774f918f144fd1c999f1e34387e8827fe5f7f3d55ed192d04dabb310acc538c19bee3b941effb68637
|
data/README.md
CHANGED
@@ -6,14 +6,28 @@
|
|
6
6
|
[![RuboCop](https://github.com/sashite/qi.rb/workflows/RuboCop/badge.svg?branch=main)](https://github.com/sashite/qi.rb/actions?query=workflow%3Arubocop+branch%3Amain)
|
7
7
|
[![License](https://img.shields.io/github/license/sashite/qi.rb?label=License&logo=github)](https://github.com/sashite/qi.rb/raw/main/LICENSE.md)
|
8
8
|
|
9
|
-
|
9
|
+
**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.
|
10
|
+
|
11
|
+
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.
|
12
|
+
|
13
|
+
## Features
|
14
|
+
|
15
|
+
1. **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.
|
16
|
+
2. **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.
|
17
|
+
3. **State Manipulation:** Qi allows for manipulation and update of game states through the `commit` method, allowing transitions between game states.
|
18
|
+
4. **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.
|
19
|
+
5. **Turn Management:** Qi keeps track of the sequence of turns allowing users to identify whose turn it is currently.
|
20
|
+
6. **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`).
|
21
|
+
7. **Customizability:** Qi is flexible and allows for customization as per your needs. The keys and values of the `captures_hash` and `squares_hash` can be any kind of object, as well as the items from `turns` and values from `state`.
|
22
|
+
|
23
|
+
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.
|
10
24
|
|
11
25
|
## Installation
|
12
26
|
|
13
27
|
Add this line to your application's Gemfile:
|
14
28
|
|
15
29
|
```ruby
|
16
|
-
gem "qi"
|
30
|
+
gem "qi"
|
17
31
|
```
|
18
32
|
|
19
33
|
And then execute:
|
@@ -25,58 +39,70 @@ bundle install
|
|
25
39
|
Or install it yourself as:
|
26
40
|
|
27
41
|
```sh
|
28
|
-
gem install qi
|
42
|
+
gem install qi
|
29
43
|
```
|
30
44
|
|
31
|
-
##
|
45
|
+
## Usage
|
46
|
+
|
47
|
+
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.
|
48
|
+
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.
|
49
|
+
|
50
|
+
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.
|
51
|
+
|
52
|
+
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:
|
32
53
|
|
33
54
|
```ruby
|
34
55
|
require "qi"
|
35
56
|
|
36
|
-
|
37
|
-
north_captures
|
38
|
-
south_captures
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
qi0
|
49
|
-
|
50
|
-
|
51
|
-
# [
|
52
|
-
#
|
53
|
-
#
|
54
|
-
#
|
55
|
-
#
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
qi1
|
62
|
-
|
63
|
-
qi1.
|
64
|
-
qi1.
|
65
|
-
qi1.
|
66
|
-
qi1.
|
67
|
-
#
|
68
|
-
#
|
69
|
-
|
70
|
-
|
71
|
-
# {}]
|
57
|
+
# Initialize an array for each player's captured pieces
|
58
|
+
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]
|
59
|
+
south_captures = %w[S]
|
60
|
+
|
61
|
+
# Combine and count each player's captured pieces
|
62
|
+
captures = Hash.new(0)
|
63
|
+
(north_captures + south_captures).each { |piece| captures[piece] += 1 }
|
64
|
+
|
65
|
+
# Define the squares occupied by each piece on the board
|
66
|
+
squares = { 3 => "s", 4 => "k", 5 => "s", 22 => "+P", 43 => "+B" }
|
67
|
+
|
68
|
+
# Create a new game position
|
69
|
+
qi0 = Qi.new(captures, squares, [0, 1])
|
70
|
+
|
71
|
+
# Verify the properties of the game position
|
72
|
+
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"]
|
73
|
+
qi0.captures_hash # => {"r"=>2, "b"=>1, "g"=>4, "s"=>1, "n"=>4, "p"=>17, "S"=>1}
|
74
|
+
qi0.squares_hash # => {3=>"s", 4=>"k", 5=>"s", 22=>"+P", 43=>"+B"}
|
75
|
+
qi0.state # => {}
|
76
|
+
qi0.turn # => 0
|
77
|
+
qi0.turns # => [0, 1]
|
78
|
+
qi0.eql?(Qi.new(captures, squares, [0, 1])) # => true
|
79
|
+
qi0.eql?(Qi.new(captures, squares, [1, 0])) # => false
|
80
|
+
|
81
|
+
# Move a piece on the board and check the game state
|
82
|
+
qi1 = qi0.commit([], [], { 43 => nil, 13 => "+B" }, in_check: true)
|
83
|
+
|
84
|
+
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"]
|
85
|
+
qi1.captures_hash # => {"r"=>2, "b"=>1, "g"=>4, "s"=>1, "n"=>4, "p"=>17, "S"=>1}
|
86
|
+
qi1.squares_hash # => {3=>"s", 4=>"k", 5=>"s", 22=>"+P", 13=>"+B"}
|
87
|
+
qi1.state # => {:in_check=>true}
|
88
|
+
qi1.turn # => 1
|
89
|
+
qi1.turns # => [1, 0]
|
90
|
+
qi1.eql?(Qi.new(captures, squares, [0, 1])) # => false
|
91
|
+
qi1.eql?(Qi.new(captures, squares, [1, 0])) # => false
|
72
92
|
```
|
73
93
|
|
94
|
+
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.
|
95
|
+
|
74
96
|
## License
|
75
97
|
|
76
98
|
The code is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
77
99
|
|
78
100
|
## About Sashité
|
79
101
|
|
80
|
-
This [gem](https://rubygems.org/gems/qi) is maintained by [Sashité](https://sashite.com/).
|
102
|
+
This [gem](https://rubygems.org/gems/qi) is proudly maintained and developed by [Sashité](https://sashite.com/). Our mission is to promote intercultural understanding and appreciation through the universal language of board games.
|
103
|
+
|
104
|
+
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.
|
105
|
+
|
106
|
+
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.
|
81
107
|
|
82
|
-
|
108
|
+
Join us in our journey as we continue to write [code](https://github.com/sashite/) to share the beauty of these cultures, one game at a time.
|
data/lib/qi.rb
CHANGED
@@ -1,269 +1,118 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
# Qi is a class for representing a state of games like Shogi. It includes
|
7
|
-
# information about the current turn, captures, game board, and other game options.
|
3
|
+
# The Qi class provides a consistent representation of a game state
|
4
|
+
# and supports changes in the game state through the commit method.
|
5
|
+
# It is designed to be used in board games such as chess, makruk, shogi, xiangqi.
|
8
6
|
class Qi
|
9
|
-
#
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
#
|
17
|
-
|
18
|
-
|
19
|
-
#
|
20
|
-
|
7
|
+
# @!attribute [r] captures_hash
|
8
|
+
# @return [Hash<Object, Integer>] a hash of captured pieces
|
9
|
+
# @example
|
10
|
+
# {"r"=>2, "b"=>1, "g"=>4, "s"=>1, "n"=>4, "p"=>17, "S"=>1}
|
11
|
+
|
12
|
+
# @!attribute [r] squares_hash
|
13
|
+
# @return [Hash<Object, Object>] A hash where the keys represent square
|
14
|
+
# identifiers and the values represent the piece that will occupy each square.
|
15
|
+
# Both the keys and values can be any type of Ruby object, such as integers, strings, symbols, etc.
|
16
|
+
# @example
|
17
|
+
# {A3: "s", E4: "k", B5: "s", C22: "+P", D43: "+B"}
|
18
|
+
|
19
|
+
# @!attribute [r] state
|
20
|
+
# @return [Hash<Symbol, Object>] a hash of game states
|
21
|
+
# @example
|
22
|
+
# {:in_check=>true}
|
23
|
+
|
24
|
+
# @!attribute [r] turns
|
25
|
+
# @return [Array<Object>] a rotation of turns
|
26
|
+
# @example
|
27
|
+
# ["Sente", "Gote"]
|
28
|
+
|
29
|
+
attr_reader :captures_hash, :squares_hash, :state, :turns
|
30
|
+
|
31
|
+
# @param captures_hash [Hash<Object, Integer>] a hash of captured pieces
|
32
|
+
# @param squares_hash [Hash<Object, Object>] A hash where the keys represent square
|
33
|
+
# identifiers and the values represent the piece that will occupy each square.
|
34
|
+
# Both the keys and values can be any type of Ruby object, such as integers, strings, symbols, etc.
|
35
|
+
# @param turns [Array<Object>] a rotation of turns
|
36
|
+
# @param state [Hash<Symbol, Object>] a hash of game states
|
37
|
+
#
|
38
|
+
# @example
|
39
|
+
# captures = Hash.new(0)
|
40
|
+
# 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]
|
41
|
+
# south_captures = %w[S]
|
42
|
+
# (north_captures + south_captures).each { |piece| captures[piece] += 1 }
|
43
|
+
# squares = { 3 => "s", 4 => "k", 5 => "s", 22 => "+P", 43 => "+B" }
|
44
|
+
# Qi.new(captures, squares, [0, 1])
|
45
|
+
def initialize(captures_hash, squares_hash, turns, **state)
|
46
|
+
@captures_hash = ::Hash.new(0).merge(captures_hash.select { |_, v| v > 0 })
|
47
|
+
@squares_hash = squares_hash.compact
|
48
|
+
@turns = turns
|
49
|
+
@state = state.transform_keys(&:to_sym)
|
21
50
|
|
22
|
-
|
23
|
-
|
51
|
+
freeze
|
52
|
+
end
|
24
53
|
|
25
|
-
#
|
54
|
+
# Return an array of captures containing piece names.
|
26
55
|
#
|
27
|
-
# @
|
28
|
-
#
|
29
|
-
#
|
30
|
-
|
31
|
-
|
32
|
-
# @param [Array] south_captures the pieces captured by the south player
|
33
|
-
# @param [Hash] squares the current state of the game board
|
34
|
-
# @param [Hash] options additional game options
|
35
|
-
def initialize(is_north_turn, north_captures, south_captures, squares, **options)
|
36
|
-
@is_north_turn = is_north_turn
|
37
|
-
@north_captures = north_captures.sort
|
38
|
-
@south_captures = south_captures.sort
|
39
|
-
@squares = squares.compact
|
40
|
-
@options = options
|
56
|
+
# @return [Array<Object>] an array of captures
|
57
|
+
# @example
|
58
|
+
# ["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"]
|
59
|
+
def captures_array
|
60
|
+
captures_hash.flat_map { |piece, count| ::Array.new(count, piece) }.sort
|
41
61
|
end
|
42
62
|
|
43
|
-
#
|
63
|
+
# Commit a change to the game state and return a new Qi object.
|
44
64
|
#
|
45
|
-
#
|
46
|
-
#
|
47
|
-
#
|
48
|
-
#
|
49
|
-
#
|
50
|
-
# @param
|
51
|
-
# @
|
52
|
-
# @
|
53
|
-
#
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
#
|
62
|
-
# @example Applying a drop
|
63
|
-
# qi = Qi.new(true, ['P', 'B', 'G'], ['G', '+B'], {56 => 'P', 64 => '+B'})
|
64
|
-
# qi.commit(nil, 47, 'G', 'G') #=> <Qi South-turn===B,P,+B,G===47:G,56:P,64:+B>
|
65
|
-
#
|
66
|
-
# @return [Qi] the new game state after the move
|
67
|
-
def commit(src_square, dst_square, piece_name, in_hand, **options)
|
68
|
-
raise ::ArgumentError, "Both src_square and in_hand cannot be nil" if src_square.nil? && in_hand.nil?
|
69
|
-
|
70
|
-
modified_captures = update_captures(src_square, in_hand)
|
71
|
-
modified_squares = squares.merge({ src_square => nil, dst_square => piece_name })
|
72
|
-
self.class.new(south_turn?, *modified_captures, modified_squares, **options)
|
65
|
+
# @param add_captures_array [Array<Object>] an array of pieces to be added to captures
|
66
|
+
# @param del_captures_array [Array<Object>] an array of pieces to be deleted from captures
|
67
|
+
# @param edit_squares_hash [Hash<Object, Object>] A hash where the keys represent square
|
68
|
+
# identifiers and the values represent the piece that will occupy each square.
|
69
|
+
# Both the keys and values can be any type of Ruby object, such as integers, strings, symbols, etc.
|
70
|
+
# @param state [Hash<Symbol, Object>] a hash of new game states
|
71
|
+
# @return [Qi] a new Qi object representing the updated game state
|
72
|
+
# @example
|
73
|
+
# qi0.commit([], [], { D43: nil, B13: "+B" }, in_check: true)
|
74
|
+
def commit(add_captures_array, del_captures_array, edit_squares_hash, **state)
|
75
|
+
self.class.new(
|
76
|
+
edit_captures_hash(add_captures_array.compact, del_captures_array.compact, **captures_hash),
|
77
|
+
squares_hash.merge(edit_squares_hash),
|
78
|
+
turns.rotate,
|
79
|
+
**state
|
80
|
+
)
|
73
81
|
end
|
74
82
|
|
75
|
-
#
|
83
|
+
# Check if the current Qi object is equal to another Qi object.
|
76
84
|
#
|
77
|
-
# @
|
78
|
-
#
|
79
|
-
# qi2 = Qi.new(...)
|
80
|
-
# qi1.eql?(qi2) #=> true or false
|
81
|
-
#
|
82
|
-
# @param [Qi] other the other Qi object to compare with
|
83
|
-
# @return [Boolean] true if the two objects represent the same game state, false otherwise
|
85
|
+
# @param other [Qi] another Qi object
|
86
|
+
# @return [Boolean] returns true if the captures_hash, squares_hash, turn, and state of both Qi objects are equal, false otherwise
|
84
87
|
def eql?(other)
|
85
|
-
return false unless other.
|
88
|
+
return false unless other.captures_hash == captures_hash
|
89
|
+
return false unless other.squares_hash == squares_hash
|
90
|
+
return false unless other.turn == turn
|
91
|
+
return false unless other.state == state
|
86
92
|
|
87
|
-
|
93
|
+
true
|
88
94
|
end
|
89
95
|
alias == eql?
|
90
96
|
|
91
|
-
# Get the captures of the current player.
|
92
|
-
#
|
93
|
-
# The method returns the north player's captures when it's their turn,
|
94
|
-
# and the south player's captures when it's their turn.
|
95
|
-
#
|
96
|
-
# @example When it's the north player's turn
|
97
|
-
# qi = Qi.new(true, ['P', 'B'], ['G', '+B'], {56 => 'P', 64 => '+B'})
|
98
|
-
# qi.current_captures #=> ['B', 'P']
|
99
|
-
#
|
100
|
-
# @example When it's the south player's turn
|
101
|
-
# qi = Qi.new(false, ['P', 'B'], ['G', '+B'], {56 => 'P', 64 => '+B'})
|
102
|
-
# qi.current_captures #=> ['+B', 'G']
|
103
|
-
#
|
104
|
-
# @return [Array] the captures of the current player
|
105
|
-
def current_captures
|
106
|
-
north_turn? ? north_captures : south_captures
|
107
|
-
end
|
108
|
-
|
109
|
-
# Get the captures of the opponent player.
|
110
|
-
#
|
111
|
-
# @return [Array] the captures of the opponent player
|
112
|
-
def opponent_captures
|
113
|
-
north_turn? ? south_captures : north_captures
|
114
|
-
end
|
115
|
-
|
116
97
|
# Get the current turn.
|
117
98
|
#
|
118
|
-
# @return [
|
119
|
-
def
|
120
|
-
|
121
|
-
end
|
122
|
-
|
123
|
-
# Get the next turn.
|
124
|
-
#
|
125
|
-
# @return [Symbol] :South if it's the north player's turn, :North otherwise
|
126
|
-
def next_turn
|
127
|
-
north_turn? ? South : North
|
128
|
-
end
|
129
|
-
|
130
|
-
# Check if it's the north player's turn.
|
131
|
-
#
|
132
|
-
# @return [Boolean] true if it's the north player's turn, false otherwise
|
133
|
-
def north_turn?
|
134
|
-
@is_north_turn
|
135
|
-
end
|
136
|
-
|
137
|
-
# Check if it's the south player's turn.
|
138
|
-
#
|
139
|
-
# @return [Boolean] true if it's the south player's turn, false otherwise
|
140
|
-
def south_turn?
|
141
|
-
!north_turn?
|
142
|
-
end
|
143
|
-
|
144
|
-
# Convert the state to an array.
|
145
|
-
#
|
146
|
-
# @example Converting the state to an array
|
147
|
-
# qi.to_a #=> [true, ['P', 'G'], ['B', '+B'], {56 => 'P', 3 => 'g', 64 => '+B'}, {}]
|
148
|
-
#
|
149
|
-
# @return [Array] an array representing the game state
|
150
|
-
def to_a
|
151
|
-
[
|
152
|
-
north_turn?,
|
153
|
-
north_captures,
|
154
|
-
south_captures,
|
155
|
-
squares,
|
156
|
-
options
|
157
|
-
]
|
158
|
-
end
|
159
|
-
|
160
|
-
# Convert the state to a hash.
|
161
|
-
#
|
162
|
-
# @example Converting the state to a hash
|
163
|
-
# qi.to_h #=> {is_north_turn: true, north_captures: ['P', 'G'], south_captures: ['B', '+B'], squares: {56 => 'P', 3 => 'g', 64 => '+B'}, options: {}}
|
164
|
-
#
|
165
|
-
# @return [Hash] a hash representing the game state
|
166
|
-
def to_h
|
167
|
-
{
|
168
|
-
is_north_turn: north_turn?,
|
169
|
-
north_captures:,
|
170
|
-
south_captures:,
|
171
|
-
squares:,
|
172
|
-
options:
|
173
|
-
}
|
174
|
-
end
|
175
|
-
|
176
|
-
# Generate a hash value for the game state.
|
177
|
-
#
|
178
|
-
# @example Generating a hash value
|
179
|
-
# qi.hash #=> "10dfb778f6559dd368c510a27e3b00bdb5b4ad88d4d67f38864d7e36de7c2f9c"
|
180
|
-
#
|
181
|
-
# @return [String] a SHA256 hash of the serialized game state
|
182
|
-
def hash
|
183
|
-
::Digest::SHA256.hexdigest(serialize)
|
184
|
-
end
|
185
|
-
|
186
|
-
# Serialize the game state.
|
187
|
-
#
|
188
|
-
# @example Serializing the game state
|
189
|
-
# qi.serialize #=> "North-turn===P,G,B,+B===3:g,56:P,64:+B"
|
190
|
-
#
|
191
|
-
# @return [String] a string representation of the game state
|
192
|
-
def serialize
|
193
|
-
serialized_turn = "#{current_turn}-turn"
|
194
|
-
serialized_captures = (north_captures + south_captures).join(",")
|
195
|
-
serialized_squares = squares.keys.sort.map { |i| "#{i}:#{squares.fetch(i)}" }.join(",")
|
196
|
-
|
197
|
-
"#{serialized_turn}===#{serialized_captures}===#{serialized_squares}"
|
198
|
-
end
|
199
|
-
|
200
|
-
# Provide a string representation of the game state for debugging.
|
201
|
-
#
|
202
|
-
# @example Getting a string representation of the game state
|
203
|
-
# qi.inspect #=> "<Qi North-turn===P,G,B,+B===3:g,56:P,64:+B>"
|
204
|
-
#
|
205
|
-
# @return [String] a string representation of the game state
|
206
|
-
def inspect
|
207
|
-
"<#{self.class} #{serialize}>"
|
99
|
+
# @return [Object] the current turn
|
100
|
+
def turn
|
101
|
+
turns.fetch(0)
|
208
102
|
end
|
209
103
|
|
210
104
|
private
|
211
105
|
|
212
|
-
#
|
213
|
-
#
|
214
|
-
# If the source square is not nil and in_hand is nil (i.e., we're moving a piece on the board),
|
215
|
-
# the captures remain the same. If the source square is not nil and in_hand is not nil
|
216
|
-
# (i.e., we're capturing a piece that will remain in hand), the piece is added to the
|
217
|
-
# current player's captures. If the source square is nil (i.e., we're dropping a piece from hand),
|
218
|
-
# the piece is removed from the current player's captures.
|
219
|
-
#
|
220
|
-
# @param src_square [Object, nil] the source square, or nil if dropping a piece from hand
|
221
|
-
# @param in_hand [String, nil] the piece in hand, or nil if moving a piece from a square
|
222
|
-
#
|
223
|
-
# @example When moving a piece on the board
|
224
|
-
# qi = Qi.new(true, ['P', 'B'], ['G', '+B'], {56 => 'P', 64 => '+B'})
|
225
|
-
# qi.send(:update_captures, 56, nil) #=> [['B', 'P'], ['G', '+B']]
|
226
|
-
#
|
227
|
-
# @example When capturing a piece that will remain in hand
|
228
|
-
# qi = Qi.new(true, ['P', 'B'], ['G', '+B'], {56 => 'P', 64 => '+B'})
|
229
|
-
# qi.send(:update_captures, 56, 'G') #=> [['B', 'P', 'G'], ['G', '+B']]
|
230
|
-
#
|
231
|
-
# @example When dropping a piece from hand
|
232
|
-
# qi = Qi.new(true, ['P', 'B', 'G'], ['G', '+B'], {56 => 'P', 64 => '+B'})
|
233
|
-
# qi.send(:update_captures, nil, 'G') #=> [['B', 'P'], ['G', '+B']]
|
234
|
-
#
|
235
|
-
# @return [Array] a two-element array with the updated north and south captures
|
236
|
-
def update_captures(src_square, in_hand)
|
237
|
-
return [north_captures, south_captures] if in_hand.nil?
|
238
|
-
|
239
|
-
captures = if src_square.nil?
|
240
|
-
remove_from_captures(in_hand, *current_captures)
|
241
|
-
else
|
242
|
-
current_captures + [in_hand]
|
243
|
-
end
|
244
|
-
|
245
|
-
north_turn? ? [captures, opponent_captures] : [opponent_captures, captures]
|
246
|
-
end
|
247
|
-
|
248
|
-
# Removes a piece from the captures.
|
249
|
-
#
|
250
|
-
# If the piece is not found in the captures, an Error::Drop exception is raised.
|
251
|
-
#
|
252
|
-
# @param piece [String] the piece to remove from the captures
|
253
|
-
# @param captures [Array] the captures to update
|
254
|
-
#
|
255
|
-
# @example
|
256
|
-
# qi = Qi.new(true, ['P', 'B', 'G'], ['G', '+B'], {56 => 'P', 64 => '+B'})
|
257
|
-
# qi.send(:remove_from_captures, 'G', *qi.current_captures) #=> ['P', 'B']
|
258
|
-
#
|
259
|
-
# @raise [Error::Drop] if the piece is not found in the captures
|
106
|
+
# Edits the captures hash and returns it.
|
260
107
|
#
|
261
|
-
# @
|
262
|
-
|
263
|
-
|
264
|
-
|
108
|
+
# @param add_captures_array [Array<Object>] an array of pieces to be added to captures
|
109
|
+
# @param del_captures_array [Array<Object>] an array of pieces to be deleted from captures
|
110
|
+
# @param hash [Hash<Object, Integer>] the current captures hash
|
111
|
+
# @return [Hash<Object, Integer>] the updated captures hash
|
112
|
+
def edit_captures_hash(add_captures_array, del_captures_array, **hash)
|
113
|
+
add_captures_array.each { |piece_name| hash[piece_name] += 1 }
|
114
|
+
del_captures_array.each { |piece_name| hash[piece_name] -= 1 }
|
265
115
|
|
266
|
-
|
267
|
-
captures
|
116
|
+
hash
|
268
117
|
end
|
269
118
|
end
|
metadata
CHANGED
@@ -1,16 +1,17 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: qi
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 10.0.0
|
4
|
+
version: 10.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Cyril Kato
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-05-
|
11
|
+
date: 2023-05-29 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
|
-
description:
|
13
|
+
description: A flexible and customizable library for representing and manipulating
|
14
|
+
game states, ideal for developing board games like chess, shogi, or xiangqi.
|
14
15
|
email: contact@cyril.email
|
15
16
|
executables: []
|
16
17
|
extensions: []
|
@@ -19,7 +20,6 @@ files:
|
|
19
20
|
- LICENSE.md
|
20
21
|
- README.md
|
21
22
|
- lib/qi.rb
|
22
|
-
- lib/qi/error/drop.rb
|
23
23
|
homepage: https://github.com/sashite/qi.rb
|
24
24
|
licenses:
|
25
25
|
- MIT
|
@@ -36,12 +36,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
36
36
|
version: 3.2.0
|
37
37
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
38
38
|
requirements:
|
39
|
-
- - "
|
39
|
+
- - ">="
|
40
40
|
- !ruby/object:Gem::Version
|
41
|
-
version:
|
41
|
+
version: '0'
|
42
42
|
requirements: []
|
43
43
|
rubygems_version: 3.4.10
|
44
44
|
signing_key:
|
45
45
|
specification_version: 4
|
46
|
-
summary:
|
46
|
+
summary: Versatile Board Game Position Representation
|
47
47
|
test_files: []
|
data/lib/qi/error/drop.rb
DELETED
@@ -1,13 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
class Qi
|
4
|
-
# The Error module contains custom error classes for the Qi game.
|
5
|
-
module Error
|
6
|
-
# The Drop class is a custom error class that inherits from IndexError.
|
7
|
-
# It is raised when a drop operation in the game of Qi is invalid.
|
8
|
-
#
|
9
|
-
# @see IndexError
|
10
|
-
class Drop < ::IndexError
|
11
|
-
end
|
12
|
-
end
|
13
|
-
end
|