pnn 1.0.1 → 1.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8c243c47d75b03455ad8fa85bebd13cf90b6e3abb2c37f93cf425a1a51f14b16
4
- data.tar.gz: 89a3d75180c92c5627e6dfb9c301f0ba3637e59c923448b7e5c9cde175805144
3
+ metadata.gz: b48e598bec9d6aeb11a7212428d279f153bbfd00ae6f1a7dc3d0485f4985695c
4
+ data.tar.gz: d7d8c7da11021a1056e048ab17605bd5db9abdc4cbbdafc90bd00820e651fedc
5
5
  SHA512:
6
- metadata.gz: 688721867ee73331f8b2b49606caefafe3239efccc7756d122b1cb76453c472d9a71dba2de13224388a6ea110651abb5386d87039a3c110e0366a03c00cfd4f0
7
- data.tar.gz: 82e0e2c1d4f058a8d09c107ad1f6beef50a0a2c826b9467504fca411281ce766d8cac9468cca105760ba16db1a014d5d0db1c92b6a4fdd268f24c07c273274e6
6
+ metadata.gz: 3957d2e98391d216817a37cf4c9ebc479172c1aeb3415ecc19c1e8abb2d9b29298277312c5b187cdbf1ede2a6f30414bf11b5b05a35ef324abf6a314fc5e7108
7
+ data.tar.gz: 10665b2f56ae9869dc68db08066dd2e1520691bc4e649ae48cf22643100427d0d0af9981708e3427aab0b8c0894feef37feda79a8c73bd036c020eb423052730
data/README.md CHANGED
@@ -40,7 +40,7 @@ A PNN record consists of a single ASCII letter that represents a piece, with opt
40
40
  Where:
41
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
42
  - `<prefix>` is an optional modifier preceding the letter (`+` or `-`)
43
- - `<suffix>` is an optional modifier following the letter (`=`, `<`, or `>`)
43
+ - `<suffix>` is an optional modifier following the letter (`'`)
44
44
 
45
45
  ## Basic Usage
46
46
 
@@ -60,12 +60,12 @@ result = Pnn.parse("+k")
60
60
  # => { letter: "k", prefix: "+" }
61
61
 
62
62
  # With suffix
63
- result = Pnn.parse("k=")
64
- # => { letter: "k", suffix: "=" }
63
+ result = Pnn.parse("k'")
64
+ # => { letter: "k", suffix: "'" }
65
65
 
66
66
  # With both prefix and suffix
67
- result = Pnn.parse("+k=")
68
- # => { letter: "k", prefix: "+", suffix: "=" }
67
+ result = Pnn.parse("+k'")
68
+ # => { letter: "k", prefix: "+", suffix: "'" }
69
69
  ```
70
70
 
71
71
  ### Safe Parsing
@@ -76,8 +76,8 @@ Parse a PNN string without raising exceptions:
76
76
  require "pnn"
77
77
 
78
78
  # Valid PNN string
79
- result = Pnn.safe_parse("+k=")
80
- # => { letter: "k", prefix: "+", suffix: "=" }
79
+ result = Pnn.safe_parse("+k'")
80
+ # => { letter: "k", prefix: "+", suffix: "'" }
81
81
 
82
82
  # Invalid PNN string
83
83
  result = Pnn.safe_parse("invalid pnn string")
@@ -100,12 +100,12 @@ Pnn.dump(letter: "p", prefix: "+")
100
100
  # => "+p"
101
101
 
102
102
  # With suffix
103
- Pnn.dump(letter: "k", suffix: "=")
104
- # => "k="
103
+ Pnn.dump(letter: "k", suffix: "'")
104
+ # => "k'"
105
105
 
106
106
  # With both prefix and suffix
107
- Pnn.dump(letter: "p", prefix: "+", suffix: ">")
108
- # => "+p>"
107
+ Pnn.dump(letter: "p", prefix: "+", suffix: "'")
108
+ # => "+p'"
109
109
  ```
110
110
 
111
111
  ### Validation
@@ -117,37 +117,79 @@ require "pnn"
117
117
 
118
118
  Pnn.valid?("k") # => true
119
119
  Pnn.valid?("+p") # => true
120
- Pnn.valid?("k=") # => true
121
- Pnn.valid?("+p>") # => true
120
+ Pnn.valid?("k'") # => true
121
+ Pnn.valid?("+p'") # => true
122
122
 
123
123
  Pnn.valid?("") # => false
124
124
  Pnn.valid?("kp") # => false
125
125
  Pnn.valid?("++k") # => false
126
- Pnn.valid?("k==") # => false
126
+ Pnn.valid?("k''") # => false
127
127
  ```
128
128
 
129
129
  ### Piece Modifiers
130
130
 
131
- PNN supports prefixes and suffixes for pieces to denote various states or capabilities:
131
+ PNN supports prefixes and suffixes for pieces to denote various states or capabilities. It's important to note that these modifiers are rule-agnostic - they provide a framework for representing piece states, but their specific meaning is determined by the game implementation:
132
132
 
133
- - **Prefix `+`**: Alternative or enhanced state
134
- - Example in shogi: `+p` may represent a promoted pawn
133
+ - **Prefix `+`**: Enhanced state
134
+ - Example in shogi: `+p` represents a promoted pawn with enhanced movement capabilities
135
+ - Example in chess variants: `+Q` might represent a queen with special powers
135
136
 
136
- - **Prefix `-`**: Diminished or restricted state
137
- - Example: `-k` may represent a king with restricted movement
137
+ - **Prefix `-`**: Diminished state
138
+ - Example in variants: `-R` might represent a rook with restricted movement abilities
139
+ - Example in weakened pieces: `-N` could indicate a knight that has been partially immobilized
138
140
 
139
- - **Suffix `=`**: Bidirectional or dual-option state
140
- - Example in chess: `k=` may represent a king eligible for both kingside and queenside castling
141
+ - **Suffix `'`**: Intermediate state
142
+ - Example in chess: `R'` represents a rook that can still be used for castling
143
+ - Example in chess: `P'` represents a pawn that can be captured en passant
144
+ - Example in variants: `B'` might indicate a bishop with a special one-time ability
141
145
 
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
146
+ These modifiers have no intrinsic semantics in the PNN specification itself. They merely provide a flexible framework for representing piece-specific conditions or states while maintaining PNN's rule-agnostic nature. Game implementations are responsible for interpreting these modifiers according to their specific rules.
145
147
 
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
148
+ ## Examples of PNN in Common Games
149
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.
150
+ The following examples demonstrate how PNN might be used in familiar games. Remember that PNN itself defines only the notation format, not the game-specific interpretations.
151
+
152
+ ### Chess Examples
153
+
154
+ In the context of chess:
155
+
156
+ ```
157
+ K # King (first player)
158
+ k # King (second player)
159
+ Q # Queen (first player)
160
+ R ' # Rook that has not moved yet and can be used for castling
161
+ P' # Pawn that can be captured en passant
162
+ ```
163
+
164
+ ### Shogi Examples
165
+
166
+ In the context of shogi:
167
+
168
+ ```
169
+ K # King (first player)
170
+ k # King (second player)
171
+ +P # Promoted pawn (tokin)
172
+ +L # Promoted lance (narikyou)
173
+ ```
174
+
175
+ ### Example: A Complete Chess Position with PNN
176
+
177
+ A chess position might contain a mix of standard and modified pieces. Here's an example after the moves 1. e4 c5 2. e5 d5:
178
+
179
+ ```
180
+ r' n b q k b n r' # Eighth rank with unmoved rooks (castling rights)
181
+ p p . . p p p p # Seventh rank pawns (d and c pawns have moved)
182
+ . . . . . . . . # Empty sixth rank
183
+ . . p p' P . . . # Fifth rank with pawn that can be captured en passant (d5) and other pawns
184
+ . . . . . . . . # Empty fourth rank
185
+ . . . . . . . . # Empty third rank
186
+ P P P P . P P P # Second rank pawns (e pawn has moved)
187
+ R' N B Q K B N R' # First rank with unmoved rooks (castling rights)
188
+ ```
189
+
190
+ In this position, White could capture Black's queen pawn (d5) en passant with the e5 pawn moving to d6.
191
+
192
+ Note: The above representation is merely illustrative; PNN itself only defines the notation for individual pieces, not complete board states.
151
193
 
152
194
  ## Properties of PNN
153
195
 
@@ -173,4 +215,4 @@ The [gem](https://rubygems.org/gems/pnn) is available as open source under the t
173
215
 
174
216
  ## About Sashité
175
217
 
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.
218
+ This project is maintained by [Sashité](https://sashite.com/) promoting chess variants and sharing the beauty of Chinese, Japanese, and Western chess cultures.
data/lib/pnn/dumper.rb CHANGED
@@ -7,7 +7,15 @@ module Pnn
7
7
  VALID_PREFIXES = ["+", "-", nil].freeze
8
8
 
9
9
  # Valid suffix modifiers
10
- VALID_SUFFIXES = ["=", "<", ">", nil].freeze
10
+ VALID_SUFFIXES = ["'", nil].freeze
11
+
12
+ # Letter validation pattern
13
+ LETTER_PATTERN = /\A[a-zA-Z]\z/
14
+
15
+ # Error messages
16
+ ERROR_INVALID_LETTER = "Letter must be a single ASCII letter (a-z or A-Z): %s"
17
+ ERROR_INVALID_PREFIX = "Invalid prefix: %s. Must be '+', '-', or nil."
18
+ ERROR_INVALID_SUFFIX = "Invalid suffix: %s. Must be ''', or nil."
11
19
 
12
20
  # Serialize piece components into a PNN string
13
21
  #
@@ -17,19 +25,48 @@ module Pnn
17
25
  # @return [String] PNN notation string
18
26
  # @raise [ArgumentError] If any component is invalid
19
27
  def self.dump(letter:, prefix: nil, suffix: nil)
20
- letter = String(letter)
28
+ validate_letter(letter)
29
+ validate_prefix(prefix)
30
+ validate_suffix(suffix)
21
31
 
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
32
+ "#{prefix}#{letter}#{suffix}"
33
+ end
25
34
 
26
- raise ArgumentError, "Invalid prefix: #{prefix}. Must be '+', '-', or nil." unless VALID_PREFIXES.include?(prefix)
35
+ # Validates that the letter is a single ASCII letter
36
+ #
37
+ # @param letter [Object] The letter to validate
38
+ # @return [void]
39
+ # @raise [ArgumentError] If the letter is invalid
40
+ def self.validate_letter(letter)
41
+ letter_str = String(letter)
27
42
 
28
- unless VALID_SUFFIXES.include?(suffix)
29
- raise ArgumentError, "Invalid suffix: #{suffix}. Must be '=', '<', '>', or nil."
30
- end
43
+ return if letter_str.match?(LETTER_PATTERN)
31
44
 
32
- "#{prefix}#{letter}#{suffix}"
45
+ raise ArgumentError, format(ERROR_INVALID_LETTER, letter_str)
33
46
  end
47
+
48
+ # Validates that the prefix is valid
49
+ #
50
+ # @param prefix [String, nil] The prefix to validate
51
+ # @return [void]
52
+ # @raise [ArgumentError] If the prefix is invalid
53
+ def self.validate_prefix(prefix)
54
+ return if VALID_PREFIXES.include?(prefix)
55
+
56
+ raise ArgumentError, format(ERROR_INVALID_PREFIX, prefix)
57
+ end
58
+
59
+ # Validates that the suffix is valid
60
+ #
61
+ # @param suffix [String, nil] The suffix to validate
62
+ # @return [void]
63
+ # @raise [ArgumentError] If the suffix is invalid
64
+ def self.validate_suffix(suffix)
65
+ return if VALID_SUFFIXES.include?(suffix)
66
+
67
+ raise ArgumentError, format(ERROR_INVALID_SUFFIX, suffix)
68
+ end
69
+
70
+ private_class_method :validate_letter, :validate_prefix, :validate_suffix
34
71
  end
35
72
  end
data/lib/pnn/parser.rb CHANGED
@@ -4,7 +4,13 @@ module Pnn
4
4
  # Parses PNN strings into their component parts
5
5
  class Parser
6
6
  # PNN regex capture groups for parsing
7
- PATTERN = /\A(?<prefix>[-+])?(?<letter>[a-zA-Z])(?<suffix>[=<>])?\z/
7
+ PATTERN = /\A(?<prefix>[-+])?(?<letter>[a-zA-Z])(?<suffix>['])?\z/
8
+
9
+ # Error message for invalid PNN string
10
+ ERROR_INVALID_PNN = "Invalid PNN string: %s"
11
+
12
+ # Component keys for the result hash
13
+ COMPONENT_KEYS = %i[letter prefix suffix].freeze
8
14
 
9
15
  # Parse a PNN string into its components
10
16
  #
@@ -12,17 +18,9 @@ module Pnn
12
18
  # @return [Hash] Hash containing the parsed components
13
19
  # @raise [ArgumentError] If the PNN string is invalid
14
20
  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
21
+ string_value = String(pnn_string)
22
+ matches = match_pattern(string_value)
23
+ extract_components(matches)
26
24
  end
27
25
 
28
26
  # Safely parse a PNN string without raising exceptions
@@ -34,5 +32,31 @@ module Pnn
34
32
  rescue ArgumentError
35
33
  nil
36
34
  end
35
+
36
+ # Match the PNN pattern against a string
37
+ #
38
+ # @param string [String] The string to match
39
+ # @return [MatchData] The match data
40
+ # @raise [ArgumentError] If the string doesn't match the pattern
41
+ def self.match_pattern(string)
42
+ matches = PATTERN.match(string)
43
+
44
+ return matches if matches
45
+
46
+ raise ArgumentError, format(ERROR_INVALID_PNN, string)
47
+ end
48
+
49
+ # Extract components from match data
50
+ #
51
+ # @param matches [MatchData] The match data
52
+ # @return [Hash] Hash containing the parsed components
53
+ def self.extract_components(matches)
54
+ COMPONENT_KEYS.each_with_object({}) do |key, result|
55
+ value = matches[key]
56
+ result[key] = value if value
57
+ end
58
+ end
59
+
60
+ private_class_method :match_pattern, :extract_components
37
61
  end
38
62
  end
data/lib/pnn/validator.rb CHANGED
@@ -4,14 +4,24 @@ module Pnn
4
4
  # Validates PNN strings according to the specification
5
5
  class Validator
6
6
  # PNN validation pattern matching the JSON schema pattern in the spec
7
- PATTERN = /\A[-+]?[a-zA-Z][=<>]?\z/
7
+ PATTERN = /\A[-+]?[a-zA-Z][']?\z/
8
8
 
9
9
  # Class method to validate PNN strings
10
10
  #
11
- # @param pnn_string [String] The PNN string to validate
11
+ # @param pnn_string [Object] The PNN string to validate
12
12
  # @return [Boolean] True if the string is valid according to PNN specification
13
13
  def self.valid?(pnn_string)
14
- String(pnn_string).match?(PATTERN)
14
+ validate_string(String(pnn_string))
15
15
  end
16
+
17
+ # Validates the given string against the PNN pattern
18
+ #
19
+ # @param string [String] The string to validate
20
+ # @return [Boolean] True if the string matches the PNN pattern
21
+ def self.validate_string(string)
22
+ string.match?(PATTERN)
23
+ end
24
+
25
+ private_class_method :validate_string
16
26
  end
17
27
  end
data/lib/pnn.rb CHANGED
@@ -14,14 +14,14 @@ require_relative File.join("pnn", "validator")
14
14
  module Pnn
15
15
  # Serializes a piece identifier into a PNN string.
16
16
  #
17
- # @param prefix [String, nil] Optional modifier preceding the letter ('+' or '-')
18
17
  # @param letter [String] A single ASCII letter ('a-z' or 'A-Z')
19
- # @param suffix [String, nil] Optional modifier following the letter ('=', '<', or '>')
18
+ # @param prefix [String, nil] Optional modifier preceding the letter ('+' or '-')
19
+ # @param suffix [String, nil] Optional modifier following the letter (''')
20
20
  # @return [String] PNN notation string
21
21
  # @raise [ArgumentError] If any parameter is invalid
22
22
  # @example
23
- # Pnn.dump(letter: "k", suffix: "=")
24
- # # => "k="
23
+ # Pnn.dump(letter: "k", suffix: "'")
24
+ # # => "k'"
25
25
  def self.dump(letter:, prefix: nil, suffix: nil)
26
26
  Dumper.dump(letter:, prefix:, suffix:)
27
27
  end
@@ -35,8 +35,8 @@ module Pnn
35
35
  # - :suffix [String, nil] - The suffix modifier if present
36
36
  # @raise [ArgumentError] If the PNN string is invalid
37
37
  # @example
38
- # Pnn.parse("+k=")
39
- # # => { letter: "k", prefix: "+", suffix: "=" }
38
+ # Pnn.parse("+k'")
39
+ # # => { letter: "k", prefix: "+", suffix: "'" }
40
40
  def self.parse(pnn_string)
41
41
  Parser.parse(pnn_string)
42
42
  end
@@ -47,8 +47,8 @@ module Pnn
47
47
  # @return [Hash, nil] Hash containing the parsed piece data or nil if parsing fails
48
48
  # @example
49
49
  # # Valid PNN string
50
- # Pnn.safe_parse("+k=")
51
- # # => { letter: "k", prefix: "+", suffix: "=" }
50
+ # Pnn.safe_parse("+k'")
51
+ # # => { letter: "k", prefix: "+", suffix: "'" }
52
52
  #
53
53
  # # Invalid PNN string
54
54
  # Pnn.safe_parse("invalid")
@@ -62,7 +62,7 @@ module Pnn
62
62
  # @param pnn_string [String] PNN string to validate
63
63
  # @return [Boolean] True if the string is a valid PNN string
64
64
  # @example
65
- # Pnn.valid?("k=") # => true
65
+ # Pnn.valid?("k'") # => true
66
66
  # Pnn.valid?("invalid") # => false
67
67
  def self.valid?(pnn_string)
68
68
  Validator.valid?(pnn_string)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pnn
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Cyril Kato