sashite-pan 3.0.0 → 4.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.
metadata CHANGED
@@ -1,20 +1,46 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sashite-pan
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.0
4
+ version: 4.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Cyril Kato
8
8
  bindir: bin
9
9
  cert_chain: []
10
10
  date: 1980-01-02 00:00:00.000000000 Z
11
- dependencies: []
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: sashite-cell
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: '2.0'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - "~>"
24
+ - !ruby/object:Gem::Version
25
+ version: '2.0'
26
+ - !ruby/object:Gem::Dependency
27
+ name: sashite-epin
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - "~>"
31
+ - !ruby/object:Gem::Version
32
+ version: '1.1'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '1.1'
12
40
  description: |
13
- Parse and generate Portable Action Notation (PAN) strings for representing moves
14
- in chess, shogi, and other strategy board games. PAN provides a compact,
15
- human-readable format for move logging, game transmission, and database storage.
16
- Supports simple moves (e2-e4), captures (exd5), and piece drops (*e4) with
17
- comprehensive validation and error handling.
41
+ Parse and generate Portable Action Notation (PAN) strings for representing atomic actions in abstract strategy board games including chess, shogi, xiangqi, and others. PAN provides an intuitive operator-based syntax with six core operators: "-" (move to empty square), "+" (capture), "~" (special moves with side effects), "*" (drop to board), "." (drop with capture), and "=" (in-place transformation), plus "..." (pass turn).
42
+ Supports coordinates via CELL specification and piece identifiers via EPIN specification. Handles transformations ("e7-e8=Q"), enhanced/diminished states ("+R", "-P"), and style derivation markers ("K'"). Provides comprehensive validation, immutable action objects, and functional API design.
43
+ Examples: "e2-e4" (move), "d1+f3" (capture), "e1~g1" (castling), "P*e5" (drop), "e7-e8=Q" (promotion), "..." (pass), "+d4" (static capture), "e4=+P" (modify).
18
44
  email: contact@cyril.email
19
45
  executables: []
20
46
  extensions: []
@@ -24,8 +50,15 @@ files:
24
50
  - README.md
25
51
  - lib/sashite-pan.rb
26
52
  - lib/sashite/pan.rb
27
- - lib/sashite/pan/dumper.rb
28
- - lib/sashite/pan/parser.rb
53
+ - lib/sashite/pan/action.rb
54
+ - lib/sashite/pan/action/capture.rb
55
+ - lib/sashite/pan/action/drop.rb
56
+ - lib/sashite/pan/action/drop_capture.rb
57
+ - lib/sashite/pan/action/modify.rb
58
+ - lib/sashite/pan/action/move.rb
59
+ - lib/sashite/pan/action/pass.rb
60
+ - lib/sashite/pan/action/special.rb
61
+ - lib/sashite/pan/action/static_capture.rb
29
62
  homepage: https://github.com/sashite/pan.rb
30
63
  licenses:
31
64
  - MIT
@@ -34,7 +67,7 @@ metadata:
34
67
  documentation_uri: https://rubydoc.info/github/sashite/pan.rb/main
35
68
  homepage_uri: https://github.com/sashite/pan.rb
36
69
  source_code_uri: https://github.com/sashite/pan.rb
37
- specification_uri: https://sashite.dev/documents/pan/1.0.0/
70
+ specification_uri: https://sashite.dev/specs/pan/1.0.0/
38
71
  rubygems_mfa_required: 'true'
39
72
  rdoc_options: []
40
73
  require_paths:
@@ -50,8 +83,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
50
83
  - !ruby/object:Gem::Version
51
84
  version: '0'
52
85
  requirements: []
53
- rubygems_version: 3.6.9
86
+ rubygems_version: 3.7.1
54
87
  specification_version: 4
55
- summary: Compact notation for board game moves - parse chess, shogi, and strategy
88
+ summary: Portable Action Notation (PAN) - operator-based notation for abstract strategy
56
89
  game actions
57
90
  test_files: []
@@ -1,121 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Sashite
4
- module Pan
5
- # Dumper for converting structured move data to PAN strings
6
- module Dumper
7
- class Error < ::StandardError
8
- end
9
-
10
- # Convert structured move data to PAN string
11
- #
12
- # @param move_data [Hash] Move data with type, source, destination
13
- # @return [String] PAN string representation
14
- # @raise [Dumper::Error] If the move data is invalid
15
- def self.call(move_data)
16
- raise Dumper::Error, "Move data cannot be nil" if move_data.nil?
17
- raise Dumper::Error, "Move data must be a Hash" unless move_data.is_a?(::Hash)
18
-
19
- validate_move_data(move_data)
20
-
21
- case move_data[:type]
22
- when :move
23
- dump_simple_move(move_data)
24
- when :capture
25
- dump_capture_move(move_data)
26
- when :drop
27
- dump_drop_move(move_data)
28
- else
29
- raise Dumper::Error, "Invalid move type: #{move_data[:type]}"
30
- end
31
- end
32
-
33
- private
34
-
35
- # Validate the structure of move data
36
- #
37
- # @param move_data [Hash] Move data to validate
38
- # @raise [Dumper::Error] If move data is invalid
39
- def self.validate_move_data(move_data)
40
- unless move_data.key?(:type)
41
- raise Dumper::Error, "Move data must have :type key"
42
- end
43
-
44
- unless move_data.key?(:destination)
45
- raise Dumper::Error, "Move data must have :destination key"
46
- end
47
-
48
- validate_coordinate(move_data[:destination], "destination")
49
-
50
- case move_data[:type]
51
- when :move, :capture
52
- unless move_data.key?(:source)
53
- raise Dumper::Error, "Move and capture types must have :source key"
54
- end
55
- validate_coordinate(move_data[:source], "source")
56
- validate_different_coordinates(move_data[:source], move_data[:destination])
57
- when :drop
58
- if move_data.key?(:source)
59
- raise Dumper::Error, "Drop type cannot have :source key"
60
- end
61
- else
62
- raise Dumper::Error, "Invalid move type: #{move_data[:type]}"
63
- end
64
- end
65
-
66
- # Validate a coordinate follows PAN format
67
- #
68
- # @param coordinate [String] Coordinate to validate
69
- # @param field_name [String] Name of the field for error messages
70
- # @raise [Dumper::Error] If coordinate is invalid
71
- def self.validate_coordinate(coordinate, field_name)
72
- if coordinate.nil? || coordinate.empty?
73
- raise Dumper::Error, "#{field_name.capitalize} coordinate cannot be nil or empty"
74
- end
75
-
76
- unless coordinate.is_a?(::String)
77
- raise Dumper::Error, "#{field_name.capitalize} coordinate must be a String"
78
- end
79
-
80
- unless coordinate.match?(/\A[a-z][0-9]\z/)
81
- raise Dumper::Error, "Invalid #{field_name} coordinate format: #{coordinate}. Must be lowercase letter followed by digit (e.g., 'e4')"
82
- end
83
- end
84
-
85
- # Validate that source and destination are different
86
- #
87
- # @param source [String] Source coordinate
88
- # @param destination [String] Destination coordinate
89
- # @raise [Dumper::Error] If coordinates are the same
90
- def self.validate_different_coordinates(source, destination)
91
- if source == destination
92
- raise Dumper::Error, "Source and destination coordinates cannot be identical: #{source}"
93
- end
94
- end
95
-
96
- # Generate PAN string for simple move
97
- #
98
- # @param move_data [Hash] Move data with :source and :destination
99
- # @return [String] PAN string in format "source-destination"
100
- def self.dump_simple_move(move_data)
101
- "#{move_data[:source]}-#{move_data[:destination]}"
102
- end
103
-
104
- # Generate PAN string for capture move
105
- #
106
- # @param move_data [Hash] Move data with :source and :destination
107
- # @return [String] PAN string in format "sourcexdestination"
108
- def self.dump_capture_move(move_data)
109
- "#{move_data[:source]}x#{move_data[:destination]}"
110
- end
111
-
112
- # Generate PAN string for drop move
113
- #
114
- # @param move_data [Hash] Move data with :destination
115
- # @return [String] PAN string in format "*destination"
116
- def self.dump_drop_move(move_data)
117
- "*#{move_data[:destination]}"
118
- end
119
- end
120
- end
121
- end
@@ -1,104 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Sashite
4
- module Pan
5
- # Parser for Portable Action Notation (PAN) strings
6
- module Parser
7
- class Error < ::StandardError
8
- end
9
-
10
- # Regular expression pattern for validating PAN strings
11
- PAN_PATTERN = /\A(\*|[a-z][0-9][-x])([a-z][0-9])\z/
12
-
13
- # Parse a PAN string into structured move data
14
- #
15
- # @param pan_string [String] The PAN string to parse
16
- # @return [Hash] Structured move data with type, source, and destination
17
- # @raise [Parser::Error] If the PAN string is invalid
18
- def self.call(pan_string)
19
- raise Parser::Error, "PAN string cannot be nil" if pan_string.nil?
20
- raise Parser::Error, "PAN string must be a String" unless pan_string.is_a?(::String)
21
- raise Parser::Error, "PAN string cannot be empty" if pan_string.empty?
22
-
23
- validate_format(pan_string)
24
- parse_move(pan_string)
25
- end
26
-
27
- private
28
-
29
- # Validate the basic format of a PAN string
30
- #
31
- # @param pan_string [String] The PAN string to validate
32
- # @raise [Parser::Error] If format is invalid
33
- def self.validate_format(pan_string)
34
- unless pan_string.match?(PAN_PATTERN)
35
- raise Parser::Error, "Invalid PAN format: #{pan_string}"
36
- end
37
-
38
- # Additional validation for source != destination in moves and captures
39
- if pan_string.include?('-') || pan_string.include?('x')
40
- source = pan_string[0..1]
41
- destination = pan_string[-2..-1]
42
- if source == destination
43
- raise Parser::Error, "Source and destination cannot be identical: #{source}"
44
- end
45
- end
46
- end
47
-
48
- # Parse the move based on its type
49
- #
50
- # @param pan_string [String] The validated PAN string
51
- # @return [Hash] Structured move data
52
- def self.parse_move(pan_string)
53
- case pan_string
54
- when /\A([a-z][0-9])-([a-z][0-9])\z/
55
- parse_simple_move($1, $2)
56
- when /\A([a-z][0-9])x([a-z][0-9])\z/
57
- parse_capture_move($1, $2)
58
- when /\A\*([a-z][0-9])\z/
59
- parse_drop_move($1)
60
- else
61
- # This should never happen due to earlier validation
62
- raise Parser::Error, "Unexpected PAN format: #{pan_string}"
63
- end
64
- end
65
-
66
- # Parse a simple move (non-capture)
67
- #
68
- # @param source [String] Source coordinate
69
- # @param destination [String] Destination coordinate
70
- # @return [Hash] Move data for simple move
71
- def self.parse_simple_move(source, destination)
72
- {
73
- type: :move,
74
- source: source,
75
- destination: destination
76
- }
77
- end
78
-
79
- # Parse a capture move
80
- #
81
- # @param source [String] Source coordinate
82
- # @param destination [String] Destination coordinate
83
- # @return [Hash] Move data for capture move
84
- def self.parse_capture_move(source, destination)
85
- {
86
- type: :capture,
87
- source: source,
88
- destination: destination
89
- }
90
- end
91
-
92
- # Parse a drop/placement move
93
- #
94
- # @param destination [String] Destination coordinate
95
- # @return [Hash] Move data for drop move
96
- def self.parse_drop_move(destination)
97
- {
98
- type: :drop,
99
- destination: destination
100
- }
101
- end
102
- end
103
- end
104
- end