qi 10.0.0.beta4 → 10.0.0.beta6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (6) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +37 -8
  3. data/lib/qi.rb +124 -3
  4. metadata +2 -4
  5. data/lib/kernel.rb +0 -18
  6. data/lib/qi/action.rb +0 -34
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ba69668b35781db7bbad0806b6940d99924fa3693355f01538b14bb8ebf159e7
4
- data.tar.gz: 8447f2df3687f661762d1a99adffc31640d693f52a4012adf2f89825b2614239
3
+ metadata.gz: 638a4dafc9645e200d72d7799afb3f28cca3a81f7cd887e5f29c64974e5776e0
4
+ data.tar.gz: 5e74a8a31f894187b404a3c41ed1a1434916880925934f2fe627e19472f99084
5
5
  SHA512:
6
- metadata.gz: 00aa0f50dd054bc1c15a0de068b4c7e2e3e18d2fc63da9db8c781f72058efc472409fe474545a6b088cb9a5b62ad87120baebbb3ad24e3cada79d6a3726f02c6
7
- data.tar.gz: d6909071d69cd7308e2655a5331b1d522e424cf05b002a73da5a54e4c7f570533d44c7db23111f4b609d876bf415b742127e0613b38685a49ccc465638c57427
6
+ metadata.gz: 41a6e8c87db922ca87d992b62909c6cf5d87d8900d12c9bc0b43d94d1882040d168678ee9d3f6be85904b7f275a0eaa9c42e32364b0d7c5e4766ec84953fdb10
7
+ data.tar.gz: 14323df90b7e942fbae1a1db4f3e23f071c4fdd62bd969797a39909c710e599534c5eaa2fd4b6a01ffbe91f48d25122a28c32b599fc5731f9b3c14b72c2f8a74
data/README.md CHANGED
@@ -13,7 +13,7 @@
13
13
  Add this line to your application's Gemfile:
14
14
 
15
15
  ```ruby
16
- gem "qi", ">= 10.0.0.beta3"
16
+ gem "qi", ">= 10.0.0.beta5"
17
17
  ```
18
18
 
19
19
  And then execute:
@@ -33,13 +33,42 @@ gem install qi --pre
33
33
  ```ruby
34
34
  require "qi"
35
35
 
36
- captures = %w[S 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]
37
- squares = { "3": "s", "4": "k", "5": "s", "22": "+P", "43": "+B" }
38
-
39
- captures, squares = Qi(*captures, **squares).call("43": nil, "13": "+B")
40
-
41
- captures # => ["S", "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"]
42
- squares # => {:"3"=>"s", :"4"=>"k", :"5"=>"s", :"22"=>"+P", :"13"=>"+B"}
36
+ is_north_turn = false
37
+ 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]
38
+ south_captures = %w[S]
39
+ squares = { 3 => "s", 4 => "k", 5 => "s", 22 => "+P", 43 => "+B" }
40
+
41
+ qi0 = Qi.new(is_north_turn, north_captures, south_captures, squares)
42
+
43
+ qi0.north_captures # => ["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"]
44
+ qi0.south_captures # => ["S"]
45
+ qi0.squares # => {3=>"s", 4=>"k", 5=>"s", 22=>"+P", 43=>"+B"}
46
+ qi0.north_turn? # => false
47
+ qi0.south_turn? # => true
48
+ qi0.serialize # => "SouthTurn===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===3:s,4:k,5:s,22:+P,43:+B"
49
+ qi0.inspect # => "<Qi SouthTurn===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===3:s,4:k,5:s,22:+P,43:+B>"
50
+
51
+ qi0.to_a
52
+ # [false,
53
+ # ["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"],
54
+ # ["S"],
55
+ # {3=>"s", 4=>"k", 5=>"s", 22=>"+P", 43=>"+B"}]
56
+
57
+ qi1 = qi0.commit({ 43 => nil, 13 => "+B" })
58
+
59
+ qi1.north_captures # => ["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"]
60
+ qi1.south_captures # => ["S"]
61
+ qi1.squares # => {3=>"s", 4=>"k", 5=>"s", 22=>"+P", 13=>"+B"}
62
+ qi1.north_turn? # => true
63
+ qi1.south_turn? # => false
64
+ qi1.serialize # => "NorthTurn===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===3:s,4:k,5:s,22:+P,13:+B"
65
+ qi1.inspect # => "<Qi NorthTurn===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===3:s,4:k,5:s,22:+P,13:+B>"
66
+
67
+ qi1.to_a
68
+ # [true,
69
+ # ["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"],
70
+ # ["S"],
71
+ # {3=>"s", 4=>"k", 5=>"s", 22=>"+P", 13=>"+B"}]
43
72
  ```
44
73
 
45
74
  ## License
data/lib/qi.rb CHANGED
@@ -1,7 +1,128 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "kernel"
3
+ # A class that represents the state of a game.
4
+ class Qi
5
+ # @!attribute [r] north_captures
6
+ # @return [Array] an array of pieces captured by the north player
7
+ # @!attribute [r] south_captures
8
+ # @return [Array] an array of pieces captured by the south player
9
+ # @!attribute [r] squares
10
+ # @return [Hash] a hash of pieces on the board
11
+ attr_reader :north_captures, :south_captures, :squares
4
12
 
5
- # The Qi abstraction.
6
- module Qi
13
+ # Initializes a new Qi object with the given attributes.
14
+ #
15
+ # @param is_north_turn [Boolean] a boolean value indicating whose turn it is
16
+ # @param north_captures [Array<Object>] an array of pieces captured by the north player
17
+ # @param south_captures [Array<Object>] an array of pieces captured by the south player
18
+ # @param squares [Hash<Object, Object>] a hash of squares on the board
19
+ def initialize(is_north_turn, north_captures, south_captures, squares)
20
+ # Assign the parameters to instance variables.
21
+ @is_north_turn = is_north_turn
22
+ @north_captures = north_captures.sort
23
+ @south_captures = south_captures.sort
24
+ @squares = squares.compact
25
+ end
26
+
27
+ # Returns a new Qi object that represents the state after applying the given changes.
28
+ #
29
+ # @param diffs [Hash<Object, Object>] a hash of changes to apply to the squares hash
30
+ # @param in_hand [Object, nil] the piece that is in hand or nil if none
31
+ # @param is_drop [Boolean] a boolean value indicating whether the in hand piece is dropped or not
32
+ # @return [Qi] a new Qi object with modified attributes, where:
33
+ # - the turn is switched
34
+ # - the captures are updated according to the in hand piece and the drop flag
35
+ # - the squares are merged with the diffs hash
36
+ def commit(diffs = {}, in_hand = nil, is_drop: false)
37
+ modified_squares = squares.merge(diffs)
38
+ modified_captures = update_captures(in_hand, is_drop:)
39
+ self.class.new(!north_turn?, *modified_captures, modified_squares)
40
+ end
41
+
42
+ # Checks if it is the north turn or not.
43
+ #
44
+ # @return [Boolean] true if it is the north turn and false otherwise
45
+ def north_turn?
46
+ @is_north_turn
47
+ end
48
+
49
+ # Checks if it is not the north turn or not.
50
+ #
51
+ # @return [Boolean] true if it is not the north turn and false otherwise
52
+ def south_turn?
53
+ !north_turn?
54
+ end
55
+
56
+ # Returns an array representation of the Qi object's attributes.
57
+ #
58
+ # @return [Array(Boolean, Array<Object>, Array<Object>, Hash<Object, Object>)] an array containing four elements:
59
+ # - a boolean value indicating whose turn it is
60
+ # - an array of pieces captured by the north player
61
+ # - an array of pieces captured by the south player
62
+ # - a hash of squares on the board
63
+ def to_a
64
+ [
65
+ north_turn?,
66
+ north_captures,
67
+ south_captures,
68
+ squares
69
+ ]
70
+ end
71
+
72
+ # Returns a string representation of the Qi object's attributes.
73
+ #
74
+ # @return [String] a string containing three parts separated by "===":
75
+ # - the current turn, either "NorthTurn" or "SouthTurn"
76
+ # - the captures, sorted and joined by ","
77
+ # - the squares, mapped to "coordinate:piece" pairs and joined by ","
78
+ def serialize
79
+ serialized_turn = (north_turn? ? "NorthTurn" : "SouthTurn")
80
+ serialized_captures = (north_captures + south_captures).sort.join(",")
81
+ serialized_squares = squares.keys.map { |i| "#{i}:#{squares.fetch(i)}" }.join(",")
82
+
83
+ "#{serialized_turn}===#{serialized_captures}===#{serialized_squares}"
84
+ end
85
+
86
+ # Returns a human-readable representation of the Qi object.
87
+ #
88
+ # @return [String] a string containing the class name and the serialized attributes
89
+ def inspect
90
+ "<#{self.class} #{serialize}>"
91
+ end
92
+
93
+ private
94
+
95
+ # Updates the captures arrays based on the piece in hand and whether it is dropped or not.
96
+ #
97
+ # @param in_hand [Object, nil] the piece that is in hand or nil if none
98
+ # @param is_drop [Boolean] a boolean value indicating whether the in hand piece is dropped or not
99
+ # @return [Array] an array containing the updated north and south captures arrays
100
+ def update_captures(in_hand, is_drop:)
101
+ return [north_captures, south_captures] if in_hand.nil?
102
+
103
+ captures = north_turn? ? north_captures : south_captures
104
+ other_captures = north_turn? ? south_captures : north_captures
105
+
106
+ if is_drop
107
+ captures = remove_from_captures(in_hand, *captures)
108
+ else
109
+ captures += [in_hand]
110
+ end
111
+
112
+ north_turn? ? [captures, other_captures] : [other_captures, captures]
113
+ end
114
+
115
+ # Removes the last occurrence of a piece from an array of captures and returns the modified array.
116
+ #
117
+ # @param piece [Object] the piece to be removed
118
+ # @param captures [Array<Object>] the array of captures
119
+ # @return [Array<Object>] the modified array of captures
120
+ # @raise [IndexError] if the piece is not found in the array
121
+ def remove_from_captures(piece, *captures)
122
+ index = captures.rindex(piece)
123
+ raise ::IndexError, "#{piece} not found!" if index.nil?
124
+
125
+ captures.delete_at(index)
126
+ captures
127
+ end
7
128
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: qi
3
3
  version: !ruby/object:Gem::Version
4
- version: 10.0.0.beta4
4
+ version: 10.0.0.beta6
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-04-21 00:00:00.000000000 Z
11
+ date: 2023-04-23 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: An abstraction that could help to update positions for games like Shogi.
14
14
  email: contact@cyril.email
@@ -18,9 +18,7 @@ extra_rdoc_files: []
18
18
  files:
19
19
  - LICENSE.md
20
20
  - README.md
21
- - lib/kernel.rb
22
21
  - lib/qi.rb
23
- - lib/qi/action.rb
24
22
  homepage: https://github.com/sashite/qi.rb
25
23
  licenses:
26
24
  - MIT
data/lib/kernel.rb DELETED
@@ -1,18 +0,0 @@
1
- # frozen_string_literal: true
2
- # shareable_constant_value: none
3
-
4
- # warn_indent: true
5
-
6
- require_relative File.join("qi", "action")
7
-
8
- # The Kernel module.
9
- module Kernel
10
- # Abstraction to build positions.
11
- #
12
- # @return [Array] An action to change the position.
13
- #
14
- # @api public
15
- def Qi(*captures, **squares)
16
- ::Qi::Action.new(*captures, **squares)
17
- end
18
- end
data/lib/qi/action.rb DELETED
@@ -1,34 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Qi
4
- # The Action abstraction.
5
- class Action
6
- CAPTURE_CHAR = "&"
7
- DROP_CHAR = "*"
8
-
9
- # Action initializer.
10
- def initialize(*captures, **squares)
11
- @captures = captures
12
- @squares = squares
13
- end
14
-
15
- # Commit an action to the position.
16
- #
17
- # @return [Array] An action to change the position.
18
- def call(**diffs)
19
- captures = @captures + Array(diffs.delete(CAPTURE_CHAR))
20
- drop = diffs.delete(DROP_CHAR)
21
-
22
- unless drop.nil?
23
- index = captures.rindex(drop)
24
- raise ::IndexError, "Piece #{drop.inspect} not captured!" if index.nil?
25
-
26
- captures.delete_at(index)
27
- end
28
-
29
- squares = @squares.merge(diffs).compact
30
-
31
- [captures.sort, squares]
32
- end
33
- end
34
- end