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.
- checksums.yaml +4 -4
- data/README.md +506 -157
- data/lib/sashite/pan/action/capture.rb +253 -0
- data/lib/sashite/pan/action/drop.rb +261 -0
- data/lib/sashite/pan/action/drop_capture.rb +261 -0
- data/lib/sashite/pan/action/modify.rb +221 -0
- data/lib/sashite/pan/action/move.rb +253 -0
- data/lib/sashite/pan/action/pass.rb +189 -0
- data/lib/sashite/pan/action/special.rb +255 -0
- data/lib/sashite/pan/action/static_capture.rb +207 -0
- data/lib/sashite/pan/action.rb +180 -0
- data/lib/sashite/pan.rb +28 -149
- data/lib/sashite-pan.rb +9 -10
- metadata +45 -12
- data/lib/sashite/pan/dumper.rb +0 -121
- data/lib/sashite/pan/parser.rb +0 -104
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:
|
|
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
|
-
|
|
15
|
-
|
|
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/
|
|
28
|
-
- lib/sashite/pan/
|
|
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/
|
|
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.
|
|
86
|
+
rubygems_version: 3.7.1
|
|
54
87
|
specification_version: 4
|
|
55
|
-
summary:
|
|
88
|
+
summary: Portable Action Notation (PAN) - operator-based notation for abstract strategy
|
|
56
89
|
game actions
|
|
57
90
|
test_files: []
|
data/lib/sashite/pan/dumper.rb
DELETED
|
@@ -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
|
data/lib/sashite/pan/parser.rb
DELETED
|
@@ -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
|