pnn 1.0.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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 464d54188d055a98263efb823fb1e646f5b9c4998c14fac4b1a2ffa1f5d5f63d
4
+ data.tar.gz: 7a7e8826c51a585879d935a7811316cfc983dfa64e8a9ad2863ca297f7af2f20
5
+ SHA512:
6
+ metadata.gz: faca96d2dd6e2b2faed85d52c8d45c5ed9725f98010963bf1d6e16c81490bb364795d0dd13acb8099c9dd009f1a209600f31554aaa3348cb3d5dc391f4bf3bfa
7
+ data.tar.gz: 0c1732d83a4b2bf8be7c64ad4eb8c50137fb7b04ea4cf54d9899eb15e24fcf5258c1e1104f853da12201b8066611bd4a594f43ac6e1c53ead6c9cc1413327132
data/LICENSE.md ADDED
@@ -0,0 +1,21 @@
1
+ # The MIT License
2
+
3
+ Copyright (c) 2025 Sashité
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,176 @@
1
+ # Pnn.rb
2
+
3
+ [![Version](https://img.shields.io/github/v/tag/sashite/pnn.rb?label=Version&logo=github)](https://github.com/sashite/pnn.rb/tags)
4
+ [![Yard documentation](https://img.shields.io/badge/Yard-documentation-blue.svg?logo=github)](https://rubydoc.info/github/sashite/pnn.rb/main)
5
+ ![Ruby](https://github.com/sashite/pnn.rb/actions/workflows/main.yml/badge.svg?branch=main)
6
+ [![License](https://img.shields.io/github/license/sashite/pnn.rb?label=License&logo=github)](https://github.com/sashite/pnn.rb/raw/main/LICENSE.md)
7
+
8
+ > **PNN** (Piece Name Notation) support for the Ruby language.
9
+
10
+ ## What is PNN?
11
+
12
+ PNN (Piece Name Notation) is a consistent and rule-agnostic format for representing pieces in abstract strategy board games. It defines a standardized way to identify and represent pieces independent of any specific game rules or mechanics.
13
+
14
+ This gem implements the [PNN Specification v1.0.0](https://sashite.dev/documents/pnn/1.0.0/), providing a Ruby interface for:
15
+ - Serializing piece identifiers to PNN strings
16
+ - Parsing PNN strings into their component parts
17
+ - Validating PNN strings according to the specification
18
+
19
+ ## Installation
20
+
21
+ ```ruby
22
+ # In your Gemfile
23
+ gem "pnn"
24
+ ```
25
+
26
+ Or install manually:
27
+
28
+ ```sh
29
+ gem install pnn
30
+ ```
31
+
32
+ ## PNN Format
33
+
34
+ A PNN record consists of a single ASCII letter that represents a piece, with optional prefixes and/or suffixes to indicate modifiers or state information:
35
+
36
+ ```
37
+ [<prefix>]<letter>[<suffix>]
38
+ ```
39
+
40
+ Where:
41
+ - `<letter>` is a single ASCII letter (`a-z` or `A-Z`), with uppercase representing the first player's pieces and lowercase representing the second player's pieces
42
+ - `<prefix>` is an optional modifier preceding the letter (`+` or `-`)
43
+ - `<suffix>` is an optional modifier following the letter (`=`, `<`, or `>`)
44
+
45
+ ## Basic Usage
46
+
47
+ ### Parsing PNN Strings
48
+
49
+ Convert a PNN string into a structured Ruby hash:
50
+
51
+ ```ruby
52
+ require "pnn"
53
+
54
+ # Basic letter
55
+ result = Pnn.parse("k")
56
+ # => { letter: "k" }
57
+
58
+ # With prefix
59
+ result = Pnn.parse("+k")
60
+ # => { letter: "k", prefix: "+" }
61
+
62
+ # With suffix
63
+ result = Pnn.parse("k=")
64
+ # => { letter: "k", suffix: "=" }
65
+
66
+ # With both prefix and suffix
67
+ result = Pnn.parse("+k=")
68
+ # => { letter: "k", prefix: "+", suffix: "=" }
69
+ ```
70
+
71
+ ### Safe Parsing
72
+
73
+ Parse a PNN string without raising exceptions:
74
+
75
+ ```ruby
76
+ require "pnn"
77
+
78
+ # Valid PNN string
79
+ result = Pnn.safe_parse("+k=")
80
+ # => { letter: "k", prefix: "+", suffix: "=" }
81
+
82
+ # Invalid PNN string
83
+ result = Pnn.safe_parse("invalid pnn string")
84
+ # => nil
85
+ ```
86
+
87
+ ### Creating PNN Strings
88
+
89
+ Convert piece components into a PNN string:
90
+
91
+ ```ruby
92
+ require "pnn"
93
+
94
+ # Basic letter
95
+ Pnn.dump(letter: "k")
96
+ # => "k"
97
+
98
+ # With prefix
99
+ Pnn.dump(letter: "p", prefix: "+")
100
+ # => "+p"
101
+
102
+ # With suffix
103
+ Pnn.dump(letter: "k", suffix: "=")
104
+ # => "k="
105
+
106
+ # With both prefix and suffix
107
+ Pnn.dump(letter: "p", prefix: "+", suffix: ">")
108
+ # => "+p>"
109
+ ```
110
+
111
+ ### Validation
112
+
113
+ Check if a string is valid PNN notation:
114
+
115
+ ```ruby
116
+ require "pnn"
117
+
118
+ Pnn.valid?("k") # => true
119
+ Pnn.valid?("+p") # => true
120
+ Pnn.valid?("k=") # => true
121
+ Pnn.valid?("+p>") # => true
122
+
123
+ Pnn.valid?("") # => false
124
+ Pnn.valid?("kp") # => false
125
+ Pnn.valid?("++k") # => false
126
+ Pnn.valid?("k==") # => false
127
+ ```
128
+
129
+ ### Piece Modifiers
130
+
131
+ PNN supports prefixes and suffixes for pieces to denote various states or capabilities:
132
+
133
+ - **Prefix `+`**: Alternative or enhanced state
134
+ - Example in shogi: `+p` may represent a promoted pawn
135
+
136
+ - **Prefix `-`**: Diminished or restricted state
137
+ - Example: `-k` may represent a king with restricted movement
138
+
139
+ - **Suffix `=`**: Bidirectional or dual-option state
140
+ - Example in chess: `k=` may represent a king eligible for both kingside and queenside castling
141
+
142
+ - **Suffix `<`**: Left-side constraint or condition
143
+ - Example in chess: `k<` may represent a king eligible for queenside castling only
144
+ - Example in chess: `p<` may represent a pawn that may be captured _en passant_ from the left
145
+
146
+ - **Suffix `>`**: Right-side constraint or condition
147
+ - Example in chess: `k>` may represent a king eligible for kingside castling only
148
+ - Example in chess: `p>` may represent a pawn that may be captured en passant from the right
149
+
150
+ These modifiers have no defined semantics in the PNN specification itself but provide a flexible framework for representing piece-specific conditions while maintaining PNN's rule-agnostic nature.
151
+
152
+ ## Properties of PNN
153
+
154
+ * **Rule-agnostic**: PNN does not encode legality, validity, or game-specific conditions.
155
+ * **Canonical representation**: Ensures that equivalent pieces yield identical strings.
156
+ * **State modifiers**: Express special conditions without compromising rule neutrality.
157
+
158
+ ## Constraints
159
+
160
+ * PNN supports exactly **two players**.
161
+ * Players are assigned distinct casing: **uppercase letters** (`A-Z`) represent pieces of the player who moves first in the initial position; **lowercase letters** (`a-z`) represent the second player.
162
+ * A maximum of **26 unique piece types per player** is allowed, as identifiers must use single letters (`a-z` or `A-Z`).
163
+ * Modifiers can only be applied to pieces **on the board**, as they express state information. Pieces held off the board (e.g., pieces in hand or captured pieces) must never include modifiers; only the base letter identifier is used.
164
+
165
+ ## Documentation
166
+
167
+ - [Official PNN Specification](https://sashite.dev/documents/pnn/1.0.0/)
168
+ - [API Documentation](https://rubydoc.info/github/sashite/pnn.rb/main)
169
+
170
+ ## License
171
+
172
+ The [gem](https://rubygems.org/gems/pnn) is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
173
+
174
+ ## About Sashité
175
+
176
+ This project is maintained by [Sashité](https://sashite.com/) - a project dedicated to promoting chess variants and sharing the beauty of Chinese, Japanese, and Western chess cultures.
data/lib/pnn/dumper.rb ADDED
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pnn
4
+ # Serializes piece components into PNN strings
5
+ class Dumper
6
+ # Valid prefix modifiers
7
+ VALID_PREFIXES = ["+", "-", nil].freeze
8
+
9
+ # Valid suffix modifiers
10
+ VALID_SUFFIXES = ["=", "<", ">", nil].freeze
11
+
12
+ # Serialize piece components into a PNN string
13
+ #
14
+ # @param letter [String] The single ASCII letter identifier
15
+ # @param prefix [String, nil] Optional prefix modifier
16
+ # @param suffix [String, nil] Optional suffix modifier
17
+ # @return [String] PNN notation string
18
+ # @raise [ArgumentError] If any component is invalid
19
+ def self.dump(letter:, prefix: nil, suffix: nil)
20
+ letter = String(letter)
21
+
22
+ unless letter.match?(/^[a-zA-Z]$/)
23
+ raise ArgumentError, "Letter must be a single ASCII letter (a-z or A-Z): #{letter}"
24
+ end
25
+
26
+ raise ArgumentError, "Invalid prefix: #{prefix}. Must be '+', '-', or nil." unless VALID_PREFIXES.include?(prefix)
27
+
28
+ unless VALID_SUFFIXES.include?(suffix)
29
+ raise ArgumentError, "Invalid suffix: #{suffix}. Must be '=', '<', '>', or nil."
30
+ end
31
+
32
+ "#{prefix}#{letter}#{suffix}"
33
+ end
34
+ end
35
+ end
data/lib/pnn/parser.rb ADDED
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pnn
4
+ # Parses PNN strings into their component parts
5
+ class Parser
6
+ # PNN regex capture groups for parsing
7
+ PATTERN = /^(?<prefix>[-+])?(?<letter>[a-zA-Z])(?<suffix>[=<>])?$/
8
+
9
+ # Parse a PNN string into its components
10
+ #
11
+ # @param pnn_string [String] The PNN string to parse
12
+ # @return [Hash] Hash containing the parsed components
13
+ # @raise [ArgumentError] If the PNN string is invalid
14
+ def self.parse(pnn_string)
15
+ pnn_string = String(pnn_string)
16
+
17
+ matches = PATTERN.match(pnn_string)
18
+
19
+ raise ArgumentError, "Invalid PNN string: #{pnn_string}" if matches.nil?
20
+
21
+ {
22
+ letter: matches[:letter],
23
+ prefix: matches[:prefix],
24
+ suffix: matches[:suffix]
25
+ }.compact
26
+ end
27
+
28
+ # Safely parse a PNN string without raising exceptions
29
+ #
30
+ # @param pnn_string [String] The PNN string to parse
31
+ # @return [Hash, nil] Hash containing the parsed components or nil if invalid
32
+ def self.safe_parse(pnn_string)
33
+ parse(pnn_string)
34
+ rescue ArgumentError
35
+ nil
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pnn
4
+ # Validates PNN strings according to the specification
5
+ class Validator
6
+ # PNN validation pattern matching the JSON schema pattern in the spec
7
+ PATTERN = /\A[-+]?[a-zA-Z][=<>]?\z/
8
+
9
+ # Class method to validate PNN strings
10
+ #
11
+ # @param pnn_string [String] The PNN string to validate
12
+ # @return [Boolean] True if the string is valid according to PNN specification
13
+ def self.valid?(pnn_string)
14
+ String(pnn_string).match?(PATTERN)
15
+ end
16
+ end
17
+ end
data/lib/pnn.rb ADDED
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative File.join("pnn", "dumper")
4
+ require_relative File.join("pnn", "parser")
5
+ require_relative File.join("pnn", "validator")
6
+
7
+ # This module provides a Ruby interface for serialization and
8
+ # deserialization of piece identifiers in PNN format.
9
+ #
10
+ # PNN (Piece Name Notation) defines a consistent and rule-agnostic
11
+ # format for representing pieces in abstract strategy board games.
12
+ #
13
+ # @see https://sashite.dev/documents/pnn/1.0.0/
14
+ module Pnn
15
+ # Serializes a piece identifier into a PNN string.
16
+ #
17
+ # @param prefix [String, nil] Optional modifier preceding the letter ('+' or '-')
18
+ # @param letter [String] A single ASCII letter ('a-z' or 'A-Z')
19
+ # @param suffix [String, nil] Optional modifier following the letter ('=', '<', or '>')
20
+ # @return [String] PNN notation string
21
+ # @raise [ArgumentError] If any parameter is invalid
22
+ # @example
23
+ # Pnn.dump(letter: "k", suffix: "=")
24
+ # # => "k="
25
+ def self.dump(letter:, prefix: nil, suffix: nil)
26
+ Dumper.dump(letter:, prefix:, suffix:)
27
+ end
28
+
29
+ # Parses a PNN string into its component parts.
30
+ #
31
+ # @param pnn_string [String] PNN notation string
32
+ # @return [Hash] Hash containing the parsed piece data with the following keys:
33
+ # - :letter [String] - The base letter identifier
34
+ # - :prefix [String, nil] - The prefix modifier if present
35
+ # - :suffix [String, nil] - The suffix modifier if present
36
+ # @raise [ArgumentError] If the PNN string is invalid
37
+ # @example
38
+ # Pnn.parse("+k=")
39
+ # # => { letter: "k", prefix: "+", suffix: "=" }
40
+ def self.parse(pnn_string)
41
+ Parser.parse(pnn_string)
42
+ end
43
+
44
+ # Safely parses a PNN string into its component parts without raising exceptions.
45
+ #
46
+ # @param pnn_string [String] PNN notation string
47
+ # @return [Hash, nil] Hash containing the parsed piece data or nil if parsing fails
48
+ # @example
49
+ # # Valid PNN string
50
+ # Pnn.safe_parse("+k=")
51
+ # # => { letter: "k", prefix: "+", suffix: "=" }
52
+ #
53
+ # # Invalid PNN string
54
+ # Pnn.safe_parse("invalid")
55
+ # # => nil
56
+ def self.safe_parse(pnn_string)
57
+ Parser.safe_parse(pnn_string)
58
+ end
59
+
60
+ # Validates if the given string is a valid PNN string
61
+ #
62
+ # @param pnn_string [String] PNN string to validate
63
+ # @return [Boolean] True if the string is a valid PNN string
64
+ # @example
65
+ # Pnn.valid?("k=") # => true
66
+ # Pnn.valid?("invalid") # => false
67
+ def self.valid?(pnn_string)
68
+ Validator.valid?(pnn_string)
69
+ end
70
+ end
metadata ADDED
@@ -0,0 +1,54 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: pnn
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Cyril Kato
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies: []
12
+ description: A Ruby interface for serialization and deserialization of piece identifiers
13
+ in PNN format. PNN is a consistent and rule-agnostic format for representing pieces
14
+ in abstract strategy board games, providing a standardized way to identify pieces
15
+ independent of any specific game rules or mechanics.
16
+ email: contact@cyril.email
17
+ executables: []
18
+ extensions: []
19
+ extra_rdoc_files: []
20
+ files:
21
+ - LICENSE.md
22
+ - README.md
23
+ - lib/pnn.rb
24
+ - lib/pnn/dumper.rb
25
+ - lib/pnn/parser.rb
26
+ - lib/pnn/validator.rb
27
+ homepage: https://github.com/sashite/pnn.rb
28
+ licenses:
29
+ - MIT
30
+ metadata:
31
+ bug_tracker_uri: https://github.com/sashite/pnn.rb/issues
32
+ documentation_uri: https://rubydoc.info/github/sashite/pnn.rb/main
33
+ homepage_uri: https://github.com/sashite/pnn.rb
34
+ source_code_uri: https://github.com/sashite/pnn.rb
35
+ specification_uri: https://sashite.dev/documents/pnn/1.0.0/
36
+ rubygems_mfa_required: 'true'
37
+ rdoc_options: []
38
+ require_paths:
39
+ - lib
40
+ required_ruby_version: !ruby/object:Gem::Requirement
41
+ requirements:
42
+ - - ">="
43
+ - !ruby/object:Gem::Version
44
+ version: 3.2.0
45
+ required_rubygems_version: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - ">="
48
+ - !ruby/object:Gem::Version
49
+ version: '0'
50
+ requirements: []
51
+ rubygems_version: 3.6.7
52
+ specification_version: 4
53
+ summary: PNN (Piece Name Notation) support for the Ruby language.
54
+ test_files: []