sashite-pin 3.2.0 → 3.3.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 +142 -411
- data/lib/sashite/pin.rb +524 -43
- metadata +4 -10
- data/lib/sashite/pin/identifier.rb +0 -442
data/lib/sashite/pin.rb
CHANGED
|
@@ -1,68 +1,549 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative "pin/identifier"
|
|
4
|
-
|
|
5
3
|
module Sashite
|
|
6
|
-
# PIN (Piece Identifier Notation) implementation for Ruby
|
|
4
|
+
# PIN (Piece Identifier Notation) implementation for Ruby.
|
|
5
|
+
#
|
|
6
|
+
# PIN provides an ASCII-based format for representing pieces in abstract strategy
|
|
7
|
+
# board games. It translates piece attributes from the Game Protocol into a compact,
|
|
8
|
+
# portable notation system.
|
|
9
|
+
#
|
|
10
|
+
# == Format
|
|
11
|
+
#
|
|
12
|
+
# [<state-modifier>]<letter>[<terminal-marker>]
|
|
13
|
+
#
|
|
14
|
+
# - *Letter* (+A-Z+, +a-z+): Piece type and side
|
|
15
|
+
# - *State modifier*: <tt>+</tt> (enhanced), <tt>-</tt> (diminished), or none (normal)
|
|
16
|
+
# - *Terminal marker*: <tt>^</tt> (terminal piece) or none
|
|
17
|
+
#
|
|
18
|
+
# == Attributes
|
|
19
|
+
#
|
|
20
|
+
# A PIN token encodes exactly these attributes:
|
|
7
21
|
#
|
|
8
|
-
#
|
|
9
|
-
#
|
|
22
|
+
# - *Piece Name* → one ASCII letter chosen by the Game / Rule System
|
|
23
|
+
# - *Piece Side* → the case of that letter (uppercase = first, lowercase = second)
|
|
24
|
+
# - *Piece State* → an optional prefix (<tt>+</tt> for enhanced, <tt>-</tt> for diminished)
|
|
25
|
+
# - *Terminal status* → an optional suffix (<tt>^</tt>)
|
|
10
26
|
#
|
|
11
|
-
#
|
|
12
|
-
# - State modifier: "+" (enhanced), "-" (diminished), or none (normal)
|
|
13
|
-
# - Letter: A-Z (first player), a-z (second player)
|
|
27
|
+
# == Examples
|
|
14
28
|
#
|
|
15
|
-
#
|
|
16
|
-
#
|
|
17
|
-
#
|
|
18
|
-
#
|
|
19
|
-
#
|
|
29
|
+
# pin = Sashite::Pin.parse("K")
|
|
30
|
+
# pin.type # => :K
|
|
31
|
+
# pin.side # => :first
|
|
32
|
+
# pin.state # => :normal
|
|
33
|
+
# pin.terminal # => false
|
|
20
34
|
#
|
|
21
|
-
#
|
|
22
|
-
|
|
23
|
-
|
|
35
|
+
# pin = Sashite::Pin.parse!("+R")
|
|
36
|
+
# pin.to_s # => "+R"
|
|
37
|
+
#
|
|
38
|
+
# pin = Sashite::Pin.parse("k^")
|
|
39
|
+
# pin.terminal # => true
|
|
40
|
+
#
|
|
41
|
+
# Sashite::Pin.valid?("K^") # => true
|
|
42
|
+
# Sashite::Pin.valid?("invalid") # => false
|
|
43
|
+
#
|
|
44
|
+
# See the PIN Specification (https://sashite.dev/specs/pin/1.0.0/) for details.
|
|
45
|
+
class Pin
|
|
46
|
+
# Valid piece types (uppercase symbols)
|
|
47
|
+
VALID_TYPES = %i[A B C D E F G H I J K L M N O P Q R S T U V W X Y Z].freeze
|
|
48
|
+
|
|
49
|
+
# Valid sides
|
|
50
|
+
VALID_SIDES = %i[first second].freeze
|
|
51
|
+
|
|
52
|
+
# Valid states
|
|
53
|
+
VALID_STATES = %i[normal enhanced diminished].freeze
|
|
54
|
+
|
|
55
|
+
# Pattern for validating PIN strings
|
|
56
|
+
PIN_PATTERN = /\A(?<prefix>[-+])?(?<letter>[a-zA-Z])(?<terminal>\^)?\z/
|
|
57
|
+
|
|
58
|
+
# @return [Symbol] Piece type (:A to :Z, always uppercase)
|
|
59
|
+
attr_reader :type
|
|
60
|
+
|
|
61
|
+
# @return [Symbol] Player side (:first or :second)
|
|
62
|
+
attr_reader :side
|
|
63
|
+
|
|
64
|
+
# @return [Symbol] Piece state (:normal, :enhanced, or :diminished)
|
|
65
|
+
attr_reader :state
|
|
66
|
+
|
|
67
|
+
# @return [Boolean] Terminal status
|
|
68
|
+
attr_reader :terminal
|
|
69
|
+
|
|
70
|
+
# ========================================================================
|
|
71
|
+
# Creation and Parsing
|
|
72
|
+
# ========================================================================
|
|
73
|
+
|
|
74
|
+
# Creates a new PIN instance.
|
|
75
|
+
#
|
|
76
|
+
# @param type [Symbol] Piece type (:A to :Z)
|
|
77
|
+
# @param side [Symbol] Player side (:first or :second)
|
|
78
|
+
# @param state [Symbol] Piece state (:normal, :enhanced, or :diminished)
|
|
79
|
+
# @param terminal [Boolean] Terminal status
|
|
80
|
+
# @return [Pin] A new frozen Pin instance
|
|
81
|
+
#
|
|
82
|
+
# @example
|
|
83
|
+
# Sashite::Pin.new(:K, :first)
|
|
84
|
+
# # => #<Sashite::Pin K>
|
|
85
|
+
#
|
|
86
|
+
# Sashite::Pin.new(:R, :second, :enhanced)
|
|
87
|
+
# # => #<Sashite::Pin +r>
|
|
88
|
+
#
|
|
89
|
+
# Sashite::Pin.new(:K, :first, :normal, terminal: true)
|
|
90
|
+
# # => #<Sashite::Pin K^>
|
|
91
|
+
def initialize(type, side, state = :normal, terminal: false)
|
|
92
|
+
validate_type!(type)
|
|
93
|
+
validate_side!(side)
|
|
94
|
+
validate_state!(state)
|
|
95
|
+
|
|
96
|
+
@type = type
|
|
97
|
+
@side = side
|
|
98
|
+
@state = state
|
|
99
|
+
@terminal = !!terminal
|
|
100
|
+
|
|
101
|
+
freeze
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# Parses a PIN string into a Pin instance.
|
|
105
|
+
#
|
|
106
|
+
# @param pin_string [String] The PIN string to parse
|
|
107
|
+
# @return [Pin] A new Pin instance
|
|
108
|
+
# @raise [ArgumentError] If the string is not a valid PIN
|
|
109
|
+
#
|
|
110
|
+
# @example
|
|
111
|
+
# Sashite::Pin.parse("K")
|
|
112
|
+
# # => #<Sashite::Pin K>
|
|
113
|
+
#
|
|
114
|
+
# Sashite::Pin.parse("+r")
|
|
115
|
+
# # => #<Sashite::Pin +r>
|
|
116
|
+
#
|
|
117
|
+
# Sashite::Pin.parse("K^")
|
|
118
|
+
# # => #<Sashite::Pin K^>
|
|
119
|
+
#
|
|
120
|
+
# Sashite::Pin.parse("invalid")
|
|
121
|
+
# # => ArgumentError: Invalid PIN string: invalid
|
|
122
|
+
def self.parse(pin_string)
|
|
123
|
+
raise ArgumentError, "Invalid PIN string: #{pin_string.inspect}" unless pin_string.is_a?(String)
|
|
124
|
+
|
|
125
|
+
match = PIN_PATTERN.match(pin_string)
|
|
126
|
+
raise ArgumentError, "Invalid PIN string: #{pin_string}" unless match
|
|
127
|
+
|
|
128
|
+
letter = match[:letter]
|
|
129
|
+
prefix = match[:prefix]
|
|
130
|
+
terminal_marker = match[:terminal]
|
|
131
|
+
|
|
132
|
+
type = letter.upcase.to_sym
|
|
133
|
+
side = letter == letter.upcase ? :first : :second
|
|
134
|
+
|
|
135
|
+
state = case prefix
|
|
136
|
+
when "+" then :enhanced
|
|
137
|
+
when "-" then :diminished
|
|
138
|
+
else :normal
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
terminal = terminal_marker == "^"
|
|
142
|
+
|
|
143
|
+
new(type, side, state, terminal: terminal)
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
# Checks if a string is a valid PIN notation.
|
|
24
147
|
#
|
|
25
148
|
# @param pin_string [String] The string to validate
|
|
26
|
-
# @return [Boolean] true if valid
|
|
149
|
+
# @return [Boolean] true if valid, false otherwise
|
|
27
150
|
#
|
|
28
151
|
# @example
|
|
29
152
|
# Sashite::Pin.valid?("K") # => true
|
|
30
153
|
# Sashite::Pin.valid?("+R") # => true
|
|
31
|
-
# Sashite::Pin.valid?("
|
|
32
|
-
# Sashite::Pin.valid?("
|
|
33
|
-
# Sashite::Pin.valid?("++K") # => false
|
|
154
|
+
# Sashite::Pin.valid?("K^") # => true
|
|
155
|
+
# Sashite::Pin.valid?("invalid") # => false
|
|
34
156
|
def self.valid?(pin_string)
|
|
35
|
-
|
|
157
|
+
return false unless pin_string.is_a?(String)
|
|
158
|
+
|
|
159
|
+
PIN_PATTERN.match?(pin_string)
|
|
36
160
|
end
|
|
37
161
|
|
|
38
|
-
#
|
|
162
|
+
# ========================================================================
|
|
163
|
+
# Conversion
|
|
164
|
+
# ========================================================================
|
|
165
|
+
|
|
166
|
+
# Converts the Pin to its string representation.
|
|
167
|
+
#
|
|
168
|
+
# @return [String] The PIN string
|
|
39
169
|
#
|
|
40
|
-
# @param pin_string [String] PIN notation string
|
|
41
|
-
# @return [Pin::Identifier] new identifier instance
|
|
42
|
-
# @raise [ArgumentError] if the PIN string is invalid
|
|
43
170
|
# @example
|
|
44
|
-
# Sashite::Pin.
|
|
45
|
-
# Sashite::Pin.
|
|
46
|
-
# Sashite::Pin.
|
|
47
|
-
def
|
|
48
|
-
|
|
171
|
+
# Sashite::Pin.new(:K, :first).to_s # => "K"
|
|
172
|
+
# Sashite::Pin.new(:R, :second, :enhanced).to_s # => "+r"
|
|
173
|
+
# Sashite::Pin.new(:K, :first, :normal, terminal: true).to_s # => "K^"
|
|
174
|
+
def to_s
|
|
175
|
+
"#{prefix}#{letter}#{suffix}"
|
|
49
176
|
end
|
|
50
177
|
|
|
51
|
-
#
|
|
178
|
+
# Returns the letter representation of the PIN.
|
|
179
|
+
#
|
|
180
|
+
# @return [String] The letter (uppercase for first player, lowercase for second)
|
|
52
181
|
#
|
|
53
|
-
# @param type [Symbol] piece type (:A to :Z)
|
|
54
|
-
# @param side [Symbol] player side (:first or :second)
|
|
55
|
-
# @param state [Symbol] piece state (:normal, :enhanced, or :diminished)
|
|
56
|
-
# @param terminal [Boolean] whether the piece is a terminal piece
|
|
57
|
-
# @return [Pin::Identifier] new identifier instance
|
|
58
|
-
# @raise [ArgumentError] if parameters are invalid
|
|
59
182
|
# @example
|
|
60
|
-
# Sashite::Pin.
|
|
61
|
-
# Sashite::Pin.
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
183
|
+
# Sashite::Pin.new(:K, :first).letter # => "K"
|
|
184
|
+
# Sashite::Pin.new(:K, :second).letter # => "k"
|
|
185
|
+
def letter
|
|
186
|
+
case side
|
|
187
|
+
when :first then type.to_s
|
|
188
|
+
when :second then type.to_s.downcase
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
# Returns the state prefix of the PIN.
|
|
193
|
+
#
|
|
194
|
+
# @return [String] "+" for enhanced, "-" for diminished, "" for normal
|
|
195
|
+
#
|
|
196
|
+
# @example
|
|
197
|
+
# Sashite::Pin.new(:K, :first, :enhanced).prefix # => "+"
|
|
198
|
+
# Sashite::Pin.new(:K, :first, :diminished).prefix # => "-"
|
|
199
|
+
# Sashite::Pin.new(:K, :first, :normal).prefix # => ""
|
|
200
|
+
def prefix
|
|
201
|
+
case state
|
|
202
|
+
when :enhanced then "+"
|
|
203
|
+
when :diminished then "-"
|
|
204
|
+
else ""
|
|
205
|
+
end
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
# Returns the terminal suffix of the PIN.
|
|
209
|
+
#
|
|
210
|
+
# @return [String] "^" if terminal, "" otherwise
|
|
211
|
+
#
|
|
212
|
+
# @example
|
|
213
|
+
# Sashite::Pin.new(:K, :first, :normal, terminal: true).suffix # => "^"
|
|
214
|
+
# Sashite::Pin.new(:K, :first).suffix # => ""
|
|
215
|
+
def suffix
|
|
216
|
+
terminal ? "^" : ""
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
# ========================================================================
|
|
220
|
+
# State Transformations
|
|
221
|
+
# ========================================================================
|
|
222
|
+
|
|
223
|
+
# Returns a new Pin with enhanced state.
|
|
224
|
+
#
|
|
225
|
+
# @return [Pin] A new Pin with :enhanced state
|
|
226
|
+
#
|
|
227
|
+
# @example
|
|
228
|
+
# pin = Sashite::Pin.new(:K, :first)
|
|
229
|
+
# pin.enhance.state # => :enhanced
|
|
230
|
+
def enhance
|
|
231
|
+
return self if state == :enhanced
|
|
232
|
+
|
|
233
|
+
self.class.new(type, side, :enhanced, terminal: terminal)
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
# Returns a new Pin with diminished state.
|
|
237
|
+
#
|
|
238
|
+
# @return [Pin] A new Pin with :diminished state
|
|
239
|
+
#
|
|
240
|
+
# @example
|
|
241
|
+
# pin = Sashite::Pin.new(:K, :first)
|
|
242
|
+
# pin.diminish.state # => :diminished
|
|
243
|
+
def diminish
|
|
244
|
+
return self if state == :diminished
|
|
245
|
+
|
|
246
|
+
self.class.new(type, side, :diminished, terminal: terminal)
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
# Returns a new Pin with normal state.
|
|
250
|
+
#
|
|
251
|
+
# @return [Pin] A new Pin with :normal state
|
|
252
|
+
#
|
|
253
|
+
# @example
|
|
254
|
+
# pin = Sashite::Pin.new(:K, :first, :enhanced)
|
|
255
|
+
# pin.normalize.state # => :normal
|
|
256
|
+
def normalize
|
|
257
|
+
return self if state == :normal
|
|
258
|
+
|
|
259
|
+
self.class.new(type, side, :normal, terminal: terminal)
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
# ========================================================================
|
|
263
|
+
# Side Transformations
|
|
264
|
+
# ========================================================================
|
|
265
|
+
|
|
266
|
+
# Returns a new Pin with the opposite side.
|
|
267
|
+
#
|
|
268
|
+
# @return [Pin] A new Pin with flipped side
|
|
269
|
+
#
|
|
270
|
+
# @example
|
|
271
|
+
# pin = Sashite::Pin.new(:K, :first)
|
|
272
|
+
# pin.flip.side # => :second
|
|
273
|
+
def flip
|
|
274
|
+
new_side = side == :first ? :second : :first
|
|
275
|
+
self.class.new(type, new_side, state, terminal: terminal)
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
# ========================================================================
|
|
279
|
+
# Terminal Transformations
|
|
280
|
+
# ========================================================================
|
|
281
|
+
|
|
282
|
+
# Returns a new Pin marked as terminal.
|
|
283
|
+
#
|
|
284
|
+
# @return [Pin] A new Pin with terminal: true
|
|
285
|
+
#
|
|
286
|
+
# @example
|
|
287
|
+
# pin = Sashite::Pin.new(:K, :first)
|
|
288
|
+
# pin.mark_terminal.terminal # => true
|
|
289
|
+
def mark_terminal
|
|
290
|
+
return self if terminal
|
|
291
|
+
|
|
292
|
+
self.class.new(type, side, state, terminal: true)
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
# Returns a new Pin unmarked as terminal.
|
|
296
|
+
#
|
|
297
|
+
# @return [Pin] A new Pin with terminal: false
|
|
298
|
+
#
|
|
299
|
+
# @example
|
|
300
|
+
# pin = Sashite::Pin.new(:K, :first, :normal, terminal: true)
|
|
301
|
+
# pin.unmark_terminal.terminal # => false
|
|
302
|
+
def unmark_terminal
|
|
303
|
+
return self unless terminal
|
|
304
|
+
|
|
305
|
+
self.class.new(type, side, state, terminal: false)
|
|
306
|
+
end
|
|
307
|
+
|
|
308
|
+
# ========================================================================
|
|
309
|
+
# Attribute Transformations
|
|
310
|
+
# ========================================================================
|
|
311
|
+
|
|
312
|
+
# Returns a new Pin with a different type.
|
|
313
|
+
#
|
|
314
|
+
# @param new_type [Symbol] The new piece type (:A to :Z)
|
|
315
|
+
# @return [Pin] A new Pin with the specified type
|
|
316
|
+
#
|
|
317
|
+
# @example
|
|
318
|
+
# pin = Sashite::Pin.new(:K, :first)
|
|
319
|
+
# pin.with_type(:Q).type # => :Q
|
|
320
|
+
def with_type(new_type)
|
|
321
|
+
return self if type == new_type
|
|
322
|
+
|
|
323
|
+
self.class.new(new_type, side, state, terminal: terminal)
|
|
324
|
+
end
|
|
325
|
+
|
|
326
|
+
# Returns a new Pin with a different side.
|
|
327
|
+
#
|
|
328
|
+
# @param new_side [Symbol] The new side (:first or :second)
|
|
329
|
+
# @return [Pin] A new Pin with the specified side
|
|
330
|
+
#
|
|
331
|
+
# @example
|
|
332
|
+
# pin = Sashite::Pin.new(:K, :first)
|
|
333
|
+
# pin.with_side(:second).side # => :second
|
|
334
|
+
def with_side(new_side)
|
|
335
|
+
return self if side == new_side
|
|
336
|
+
|
|
337
|
+
self.class.new(type, new_side, state, terminal: terminal)
|
|
338
|
+
end
|
|
339
|
+
|
|
340
|
+
# Returns a new Pin with a different state.
|
|
341
|
+
#
|
|
342
|
+
# @param new_state [Symbol] The new state (:normal, :enhanced, or :diminished)
|
|
343
|
+
# @return [Pin] A new Pin with the specified state
|
|
344
|
+
#
|
|
345
|
+
# @example
|
|
346
|
+
# pin = Sashite::Pin.new(:K, :first)
|
|
347
|
+
# pin.with_state(:enhanced).state # => :enhanced
|
|
348
|
+
def with_state(new_state)
|
|
349
|
+
return self if state == new_state
|
|
350
|
+
|
|
351
|
+
self.class.new(type, side, new_state, terminal: terminal)
|
|
352
|
+
end
|
|
353
|
+
|
|
354
|
+
# Returns a new Pin with a different terminal status.
|
|
355
|
+
#
|
|
356
|
+
# @param new_terminal [Boolean] The new terminal status
|
|
357
|
+
# @return [Pin] A new Pin with the specified terminal status
|
|
358
|
+
#
|
|
359
|
+
# @example
|
|
360
|
+
# pin = Sashite::Pin.new(:K, :first)
|
|
361
|
+
# pin.with_terminal(true).terminal # => true
|
|
362
|
+
def with_terminal(new_terminal)
|
|
363
|
+
return self if terminal == !!new_terminal
|
|
364
|
+
|
|
365
|
+
self.class.new(type, side, state, terminal: !!new_terminal)
|
|
366
|
+
end
|
|
367
|
+
|
|
368
|
+
# ========================================================================
|
|
369
|
+
# State Queries
|
|
370
|
+
# ========================================================================
|
|
371
|
+
|
|
372
|
+
# Checks if the Pin has normal state.
|
|
373
|
+
#
|
|
374
|
+
# @return [Boolean] true if normal state
|
|
375
|
+
#
|
|
376
|
+
# @example
|
|
377
|
+
# Sashite::Pin.new(:K, :first).normal? # => true
|
|
378
|
+
def normal?
|
|
379
|
+
state == :normal
|
|
380
|
+
end
|
|
381
|
+
|
|
382
|
+
# Checks if the Pin has enhanced state.
|
|
383
|
+
#
|
|
384
|
+
# @return [Boolean] true if enhanced state
|
|
385
|
+
#
|
|
386
|
+
# @example
|
|
387
|
+
# Sashite::Pin.new(:K, :first, :enhanced).enhanced? # => true
|
|
388
|
+
def enhanced?
|
|
389
|
+
state == :enhanced
|
|
390
|
+
end
|
|
391
|
+
|
|
392
|
+
# Checks if the Pin has diminished state.
|
|
393
|
+
#
|
|
394
|
+
# @return [Boolean] true if diminished state
|
|
395
|
+
#
|
|
396
|
+
# @example
|
|
397
|
+
# Sashite::Pin.new(:K, :first, :diminished).diminished? # => true
|
|
398
|
+
def diminished?
|
|
399
|
+
state == :diminished
|
|
400
|
+
end
|
|
401
|
+
|
|
402
|
+
# ========================================================================
|
|
403
|
+
# Side Queries
|
|
404
|
+
# ========================================================================
|
|
405
|
+
|
|
406
|
+
# Checks if the Pin belongs to the first player.
|
|
407
|
+
#
|
|
408
|
+
# @return [Boolean] true if first player
|
|
409
|
+
#
|
|
410
|
+
# @example
|
|
411
|
+
# Sashite::Pin.new(:K, :first).first_player? # => true
|
|
412
|
+
def first_player?
|
|
413
|
+
side == :first
|
|
414
|
+
end
|
|
415
|
+
|
|
416
|
+
# Checks if the Pin belongs to the second player.
|
|
417
|
+
#
|
|
418
|
+
# @return [Boolean] true if second player
|
|
419
|
+
#
|
|
420
|
+
# @example
|
|
421
|
+
# Sashite::Pin.new(:K, :second).second_player? # => true
|
|
422
|
+
def second_player?
|
|
423
|
+
side == :second
|
|
424
|
+
end
|
|
425
|
+
|
|
426
|
+
# ========================================================================
|
|
427
|
+
# Terminal Queries
|
|
428
|
+
# ========================================================================
|
|
429
|
+
|
|
430
|
+
# Checks if the Pin is a terminal piece.
|
|
431
|
+
#
|
|
432
|
+
# @return [Boolean] true if terminal
|
|
433
|
+
#
|
|
434
|
+
# @example
|
|
435
|
+
# Sashite::Pin.new(:K, :first, :normal, terminal: true).terminal? # => true
|
|
436
|
+
def terminal?
|
|
437
|
+
terminal
|
|
438
|
+
end
|
|
439
|
+
|
|
440
|
+
# ========================================================================
|
|
441
|
+
# Comparison
|
|
442
|
+
# ========================================================================
|
|
443
|
+
|
|
444
|
+
# Checks if two Pins have the same type.
|
|
445
|
+
#
|
|
446
|
+
# @param other [Pin] The other Pin to compare
|
|
447
|
+
# @return [Boolean] true if same type
|
|
448
|
+
#
|
|
449
|
+
# @example
|
|
450
|
+
# pin1 = Sashite::Pin.parse("K")
|
|
451
|
+
# pin2 = Sashite::Pin.parse("k")
|
|
452
|
+
# pin1.same_type?(pin2) # => true
|
|
453
|
+
def same_type?(other)
|
|
454
|
+
type == other.type
|
|
455
|
+
end
|
|
456
|
+
|
|
457
|
+
# Checks if two Pins have the same side.
|
|
458
|
+
#
|
|
459
|
+
# @param other [Pin] The other Pin to compare
|
|
460
|
+
# @return [Boolean] true if same side
|
|
461
|
+
#
|
|
462
|
+
# @example
|
|
463
|
+
# pin1 = Sashite::Pin.parse("K")
|
|
464
|
+
# pin2 = Sashite::Pin.parse("Q")
|
|
465
|
+
# pin1.same_side?(pin2) # => true
|
|
466
|
+
def same_side?(other)
|
|
467
|
+
side == other.side
|
|
468
|
+
end
|
|
469
|
+
|
|
470
|
+
# Checks if two Pins have the same state.
|
|
471
|
+
#
|
|
472
|
+
# @param other [Pin] The other Pin to compare
|
|
473
|
+
# @return [Boolean] true if same state
|
|
474
|
+
#
|
|
475
|
+
# @example
|
|
476
|
+
# pin1 = Sashite::Pin.parse("+K")
|
|
477
|
+
# pin2 = Sashite::Pin.parse("+Q")
|
|
478
|
+
# pin1.same_state?(pin2) # => true
|
|
479
|
+
def same_state?(other)
|
|
480
|
+
state == other.state
|
|
481
|
+
end
|
|
482
|
+
|
|
483
|
+
# Checks if two Pins have the same terminal status.
|
|
484
|
+
#
|
|
485
|
+
# @param other [Pin] The other Pin to compare
|
|
486
|
+
# @return [Boolean] true if same terminal status
|
|
487
|
+
#
|
|
488
|
+
# @example
|
|
489
|
+
# pin1 = Sashite::Pin.parse("K^")
|
|
490
|
+
# pin2 = Sashite::Pin.parse("Q^")
|
|
491
|
+
# pin1.same_terminal?(pin2) # => true
|
|
492
|
+
def same_terminal?(other)
|
|
493
|
+
terminal == other.terminal
|
|
494
|
+
end
|
|
495
|
+
|
|
496
|
+
# Checks equality with another Pin.
|
|
497
|
+
#
|
|
498
|
+
# @param other [Object] The object to compare
|
|
499
|
+
# @return [Boolean] true if equal
|
|
500
|
+
def ==(other)
|
|
501
|
+
return false unless other.is_a?(self.class)
|
|
502
|
+
|
|
503
|
+
type == other.type &&
|
|
504
|
+
side == other.side &&
|
|
505
|
+
state == other.state &&
|
|
506
|
+
terminal == other.terminal
|
|
507
|
+
end
|
|
508
|
+
|
|
509
|
+
alias eql? ==
|
|
510
|
+
|
|
511
|
+
# Returns a hash code for the Pin.
|
|
512
|
+
#
|
|
513
|
+
# @return [Integer] Hash code
|
|
514
|
+
def hash
|
|
515
|
+
[type, side, state, terminal].hash
|
|
516
|
+
end
|
|
517
|
+
|
|
518
|
+
# Returns an inspect string for the Pin.
|
|
519
|
+
#
|
|
520
|
+
# @return [String] Inspect representation
|
|
521
|
+
def inspect
|
|
522
|
+
"#<#{self.class} #{self}>"
|
|
523
|
+
end
|
|
524
|
+
|
|
525
|
+
private
|
|
526
|
+
|
|
527
|
+
# ========================================================================
|
|
528
|
+
# Private Validation
|
|
529
|
+
# ========================================================================
|
|
530
|
+
|
|
531
|
+
def validate_type!(type)
|
|
532
|
+
return if VALID_TYPES.include?(type)
|
|
533
|
+
|
|
534
|
+
raise ArgumentError, "Type must be a symbol from :A to :Z, got: #{type.inspect}"
|
|
535
|
+
end
|
|
536
|
+
|
|
537
|
+
def validate_side!(side)
|
|
538
|
+
return if VALID_SIDES.include?(side)
|
|
539
|
+
|
|
540
|
+
raise ArgumentError, "Side must be :first or :second, got: #{side.inspect}"
|
|
541
|
+
end
|
|
542
|
+
|
|
543
|
+
def validate_state!(state)
|
|
544
|
+
return if VALID_STATES.include?(state)
|
|
545
|
+
|
|
546
|
+
raise ArgumentError, "State must be :normal, :enhanced, or :diminished, got: #{state.inspect}"
|
|
66
547
|
end
|
|
67
548
|
end
|
|
68
549
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: sashite-pin
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 3.
|
|
4
|
+
version: 3.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Cyril Kato
|
|
@@ -10,14 +10,9 @@ cert_chain: []
|
|
|
10
10
|
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
11
|
dependencies: []
|
|
12
12
|
description: |
|
|
13
|
-
PIN (Piece Identifier Notation)
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
principles. PIN uses single ASCII letters with optional state modifiers, terminal markers,
|
|
17
|
-
and case-based side encoding (A-Z for first player, a-z for second player), enabling
|
|
18
|
-
precise and portable identification of pieces across multiple games. Perfect for game
|
|
19
|
-
engines, board game notation systems, and hybrid gaming platforms requiring compact,
|
|
20
|
-
stateful piece representation.
|
|
13
|
+
PIN (Piece Identifier Notation) implementation for Ruby.
|
|
14
|
+
Provides a rule-agnostic format for identifying pieces in abstract strategy
|
|
15
|
+
board games with immutable identifier objects and functional programming principles.
|
|
21
16
|
email: contact@cyril.email
|
|
22
17
|
executables: []
|
|
23
18
|
extensions: []
|
|
@@ -27,7 +22,6 @@ files:
|
|
|
27
22
|
- README.md
|
|
28
23
|
- lib/sashite-pin.rb
|
|
29
24
|
- lib/sashite/pin.rb
|
|
30
|
-
- lib/sashite/pin/identifier.rb
|
|
31
25
|
homepage: https://github.com/sashite/pin.rb
|
|
32
26
|
licenses:
|
|
33
27
|
- MIT
|