sashite-qpi 2.0.0 → 2.1.0

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.
data/lib/sashite/qpi.rb CHANGED
@@ -1,135 +1,92 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "qpi/constants"
4
+ require_relative "qpi/errors"
3
5
  require_relative "qpi/identifier"
6
+ require_relative "qpi/parser"
4
7
 
5
8
  module Sashite
6
- # QPI (Qualified Piece Identifier) implementation for Ruby
9
+ # QPI (Qualified Piece Identifier) implementation for Ruby.
7
10
  #
8
- # Provides complete piece identification by combining two primitive notations:
9
- # - SIN (Style Identifier Notation) identifies the piece style
10
- # - PIN (Piece Identifier Notation) identifies the piece attributes
11
+ # QPI provides complete piece identification by combining two primitive notations:
12
+ # - SIN (Style Identifier Notation) for Piece Style
13
+ # - PIN (Piece Identifier Notation) for Piece Name, Side, State, and Terminal Status
11
14
  #
12
- # A QPI identifier is simply a pair of (SIN, PIN) with one constraint:
13
- # both components must represent the same player.
15
+ # == Format
14
16
  #
15
- # ## Core Concept
17
+ # <sin>:<pin>
16
18
  #
17
- # QPI is pure composition:
19
+ # - *SIN*: Single ASCII letter encoding Piece Style and a side tag
20
+ # - *PIN*: Piece identifier with optional state modifier and terminal marker
18
21
  #
19
- # sin = Sashite::Sin.parse("C")
20
- # pin = Sashite::Pin.parse("K^")
21
- # qpi = Sashite::Qpi.new(sin, pin)
22
- # qpi.to_s # => "C:K^"
23
- # qpi.sin # => SIN::Identifier instance
24
- # qpi.pin # => PIN::Identifier instance
22
+ # == Piece Identity Attributes
25
23
  #
26
- # All piece attributes come from the components.
24
+ # A QPI token encodes complete Piece Identity:
27
25
  #
28
- # ## Five Fundamental Attributes
26
+ # - *Piece Style* → SIN letter (case-insensitive identity)
27
+ # - *Piece Name* → PIN letter (case-insensitive identity)
28
+ # - *Piece Side* → PIN letter case (uppercase = first, lowercase = second)
29
+ # - *Piece State* → PIN modifier (+/-)
30
+ # - *Terminal Status* → PIN marker (^)
29
31
  #
30
- # QPI exposes all five attributes from the Sashité Game Protocol:
31
- # - **Piece Style** — via qpi.sin.family
32
- # - **Piece Name** — via qpi.pin.type
33
- # - **Piece Side** — via qpi.sin.side or qpi.pin.side
34
- # - **Piece State** — via qpi.pin.state
35
- # - **Terminal Status** — via qpi.pin.terminal?
32
+ # == Native/Derived Relationship
36
33
  #
37
- # ## Format Specification
34
+ # QPI defines a deterministic relationship based on case comparison:
38
35
  #
39
- # Structure: `<sin>:<pin>`
36
+ # - *Native*: SIN case matches PIN case (sin.side == pin.side)
37
+ # - *Derived*: SIN case differs from PIN case (sin.side != pin.side)
40
38
  #
41
- # Grammar (BNF):
42
- # <qpi> ::= <uppercase-qpi> | <lowercase-qpi>
43
- # <uppercase-qpi> ::= <uppercase-letter> ":" <uppercase-pin>
44
- # <lowercase-qpi> ::= <lowercase-letter> ":" <lowercase-pin>
45
- # <uppercase-pin> ::= ["+" | "-"] <uppercase-letter> ["^"]
46
- # <lowercase-pin> ::= ["+" | "-"] <lowercase-letter> ["^"]
39
+ # == Examples
47
40
  #
48
- # Regular Expression: `/\A([A-Z]:[-+]?[A-Z]\^?|[a-z]:[-+]?[a-z]\^?)\z/`
49
- #
50
- # ## Semantic Constraint
51
- #
52
- # The SIN and PIN components must represent the same player:
53
- # - Valid: "C:K" (both first player), "c:k" (both second player)
54
- # - Invalid: "C:k" (side mismatch), "c:K" (side mismatch)
55
- #
56
- # ## Examples
57
- #
58
- # # Parse QPI string
59
41
  # qpi = Sashite::Qpi.parse("C:K^")
60
- # qpi.sin.family # => :C (Piece Style)
61
- # qpi.pin.type # => :K (Piece Name)
62
- # qpi.sin.side # => :first (Piece Side)
63
- # qpi.pin.state # => :normal (Piece State)
64
- # qpi.pin.terminal? # => true (Terminal Status)
65
- #
66
- # # Create from components
67
- # sin = Sashite::Sin.parse("S")
68
- # pin = Sashite::Pin.parse("+R^")
69
- # qpi = Sashite::Qpi.new(sin, pin)
70
- # qpi.to_s # => "S:+R^"
71
- #
72
- # # Transform via components
73
- # qpi.with_sin(qpi.sin.with_family(:C)) # => "C:+R^"
74
- # qpi.with_pin(qpi.pin.with_type(:B)) # => "S:+B^"
42
+ # qpi.sin.style # => :C
43
+ # qpi.pin.type # => :K
44
+ # qpi.pin.side # => :first
45
+ # qpi.pin.state # => :normal
46
+ # qpi.pin.terminal? # => true
47
+ # qpi.native? # => true
75
48
  #
76
- # # Flip both components (only convenience method)
77
- # qpi.flip # => "s:+r^"
49
+ # qpi = Sashite::Qpi.parse("C:k")
50
+ # qpi.derived? # => true
51
+ # qpi.native.to_s # => "C:K"
78
52
  #
79
- # ## Design Properties
53
+ # Sashite::Qpi.valid?("C:K^") # => true
54
+ # Sashite::Qpi.valid?("invalid") # => false
80
55
  #
81
- # - **Rule-agnostic**: Independent of game mechanics
82
- # - **Pure composition**: Zero feature duplication
83
- # - **Minimal API**: Only 5 core methods
84
- # - **Component transparency**: Direct primitive access
85
- # - **Immutable**: Frozen instances
86
- # - **Semantic validation**: Automatic side consistency
87
- #
88
- # @see https://sashite.dev/specs/qpi/1.0.0/ QPI Specification v1.0.0
89
- # @see https://sashite.dev/specs/sin/1.0.0/ Style Identifier Notation (SIN)
90
- # @see https://sashite.dev/specs/pin/1.0.0/ Piece Identifier Notation (PIN)
56
+ # @see https://sashite.dev/specs/qpi/1.0.0/
91
57
  module Qpi
92
- # Check if a string is a valid QPI notation
58
+ # Parses a QPI string into an Identifier.
93
59
  #
94
- # @param qpi_string [String] the string to validate
95
- # @return [Boolean] true if valid QPI, false otherwise
60
+ # @param string [String] The QPI string to parse
61
+ # @return [Identifier] A new Identifier instance
62
+ # @raise [Errors::Argument] If the string is not a valid QPI
96
63
  #
97
64
  # @example
98
- # Sashite::Qpi.valid?("C:K^") # => true
99
- # Sashite::Qpi.valid?("C:k") # => false (side mismatch)
100
- def self.valid?(qpi_string)
101
- Identifier.valid?(qpi_string)
102
- end
103
-
104
- # Parse a QPI string into an Identifier object
65
+ # Sashite::Qpi.parse("C:K")
66
+ # # => #<Sashite::Qpi::Identifier C:K>
105
67
  #
106
- # @param qpi_string [String] QPI notation string (format: sin:pin)
107
- # @return [Qpi::Identifier] identifier with sin and pin components
108
- # @raise [ArgumentError] if invalid or semantically inconsistent
68
+ # Sashite::Qpi.parse("s:+r^")
69
+ # # => #<Sashite::Qpi::Identifier s:+r^>
109
70
  #
110
- # @example
111
- # qpi = Sashite::Qpi.parse("C:K^")
112
- # qpi.sin.family # => :C
113
- # qpi.pin.type # => :K
114
- # qpi.pin.terminal? # => true
115
- def self.parse(qpi_string)
116
- Identifier.parse(qpi_string)
71
+ # Sashite::Qpi.parse("invalid")
72
+ # # => raises Errors::Argument
73
+ def self.parse(string)
74
+ components = Parser.parse(string)
75
+
76
+ Identifier.new(components[:sin], components[:pin])
117
77
  end
118
78
 
119
- # Create a new identifier from SIN and PIN components
79
+ # Checks if a string is a valid QPI notation.
120
80
  #
121
- # @param sin [Sin::Identifier] SIN component
122
- # @param pin [Pin::Identifier] PIN component
123
- # @return [Qpi::Identifier] new identifier instance
124
- # @raise [ArgumentError] if components have different sides
81
+ # @param string [String] The string to validate
82
+ # @return [Boolean] true if valid, false otherwise
125
83
  #
126
84
  # @example
127
- # sin = Sashite::Sin.parse("C")
128
- # pin = Sashite::Pin.parse("K^")
129
- # qpi = Sashite::Qpi.new(sin, pin)
130
- # qpi.to_s # => "C:K^"
131
- def self.new(sin, pin)
132
- Identifier.new(sin, pin)
85
+ # Sashite::Qpi.valid?("C:K") # => true
86
+ # Sashite::Qpi.valid?("s:+r^") # => true
87
+ # Sashite::Qpi.valid?("invalid") # => false
88
+ def self.valid?(string)
89
+ Parser.valid?(string)
133
90
  end
134
91
  end
135
92
  end
data/lib/sashite-qpi.rb CHANGED
@@ -1,14 +1,3 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "sashite/qpi"
4
-
5
- # Sashité namespace for board game notation libraries
6
- #
7
- # Sashité provides a collection of libraries for representing and manipulating
8
- # board game concepts according to the Game Protocol specifications.
9
- #
10
- # @see https://sashite.dev/game-protocol/ Game Protocol Foundation
11
- # @see https://sashite.dev/specs/ Sashité Specifications
12
- # @author Sashité
13
- module Sashite
14
- end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sashite-qpi
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0
4
+ version: 2.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Cyril Kato
@@ -15,56 +15,57 @@ dependencies:
15
15
  requirements:
16
16
  - - "~>"
17
17
  - !ruby/object:Gem::Version
18
- version: 3.2.0
18
+ version: 4.0.0
19
19
  type: :runtime
20
20
  prerelease: false
21
21
  version_requirements: !ruby/object:Gem::Requirement
22
22
  requirements:
23
23
  - - "~>"
24
24
  - !ruby/object:Gem::Version
25
- version: 3.2.0
25
+ version: 4.0.0
26
26
  - !ruby/object:Gem::Dependency
27
27
  name: sashite-sin
28
28
  requirement: !ruby/object:Gem::Requirement
29
29
  requirements:
30
30
  - - "~>"
31
31
  - !ruby/object:Gem::Version
32
- version: 2.1.0
32
+ version: 3.0.0
33
33
  type: :runtime
34
34
  prerelease: false
35
35
  version_requirements: !ruby/object:Gem::Requirement
36
36
  requirements:
37
37
  - - "~>"
38
38
  - !ruby/object:Gem::Version
39
- version: 2.1.0
40
- description: |
41
- QPI (Qualified Piece Identifier) provides a rule-agnostic format for identifying game pieces
42
- in abstract strategy board games by combining Style Identifier Notation (SIN) and Piece
43
- Identifier Notation (PIN) primitives. This gem implements the QPI Specification v1.0.0 with
44
- a modern Ruby interface featuring immutable identifier objects and functional programming
45
- principles. QPI enables complete piece identification with all four fundamental attributes
46
- (family, type, side, state) while supporting cross-style gaming environments. Perfect for
47
- multi-tradition board games, hybrid gaming systems, and game engines requiring comprehensive
48
- piece identification across different game styles and traditions.
39
+ version: 3.0.0
40
+ description: QPI (Qualified Piece Identifier) implementation for Ruby. Provides a
41
+ rule-agnostic format for complete piece identification in abstract strategy board
42
+ games by combining SIN and PIN primitives, with Native/Derived relationship support.
49
43
  email: contact@cyril.email
50
44
  executables: []
51
45
  extensions: []
52
46
  extra_rdoc_files: []
53
47
  files:
54
- - LICENSE.md
48
+ - LICENSE
55
49
  - README.md
56
50
  - lib/sashite-qpi.rb
57
51
  - lib/sashite/qpi.rb
52
+ - lib/sashite/qpi/constants.rb
53
+ - lib/sashite/qpi/errors.rb
54
+ - lib/sashite/qpi/errors/argument.rb
55
+ - lib/sashite/qpi/errors/argument/messages.rb
58
56
  - lib/sashite/qpi/identifier.rb
57
+ - lib/sashite/qpi/parser.rb
59
58
  homepage: https://github.com/sashite/qpi.rb
60
59
  licenses:
61
- - MIT
60
+ - Apache-2.0
62
61
  metadata:
63
62
  bug_tracker_uri: https://github.com/sashite/qpi.rb/issues
64
63
  documentation_uri: https://rubydoc.info/github/sashite/qpi.rb/main
65
64
  homepage_uri: https://github.com/sashite/qpi.rb
66
65
  source_code_uri: https://github.com/sashite/qpi.rb
67
66
  specification_uri: https://sashite.dev/specs/qpi/1.0.0/
67
+ wiki_uri: https://sashite.dev/specs/qpi/1.0.0/examples/
68
+ funding_uri: https://github.com/sponsors/sashite
68
69
  rubygems_mfa_required: 'true'
69
70
  rdoc_options: []
70
71
  require_paths:
@@ -80,7 +81,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
80
81
  - !ruby/object:Gem::Version
81
82
  version: '0'
82
83
  requirements: []
83
- rubygems_version: 3.7.2
84
+ rubygems_version: 4.0.3
84
85
  specification_version: 4
85
86
  summary: QPI (Qualified Piece Identifier) implementation for Ruby with immutable identifier
86
87
  objects
data/LICENSE.md DELETED
@@ -1,22 +0,0 @@
1
- Copyright (c) 2014-2025 Sashite
2
-
3
- MIT License
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining
6
- a copy of this software and associated documentation files (the
7
- "Software"), to deal in the Software without restriction, including
8
- without limitation the rights to use, copy, modify, merge, publish,
9
- distribute, sublicense, and/or sell copies of the Software, and to
10
- permit persons to whom the Software is furnished to do so, subject to
11
- the following conditions:
12
-
13
- The above copyright notice and this permission notice shall be
14
- included in all copies or substantial portions of the Software.
15
-
16
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
- LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
- OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
- WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.