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 +7 -0
- data/LICENSE.md +21 -0
- data/README.md +176 -0
- data/lib/pnn/dumper.rb +35 -0
- data/lib/pnn/parser.rb +38 -0
- data/lib/pnn/validator.rb +17 -0
- data/lib/pnn.rb +70 -0
- metadata +54 -0
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
|
+
[](https://github.com/sashite/pnn.rb/tags)
|
4
|
+
[](https://rubydoc.info/github/sashite/pnn.rb/main)
|
5
|
+

|
6
|
+
[](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: []
|