sashite-sin 2.1.0 → 3.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.
@@ -1,405 +1,249 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "constants"
4
+ require_relative "errors"
5
+
3
6
  module Sashite
4
7
  module Sin
5
- # Represents an identifier in SIN (Style Identifier Notation) format.
6
- #
7
- # ## Concept
8
- #
9
- # SIN addresses the fundamental need to identify which style system governs piece behavior
10
- # while simultaneously indicating which player controls pieces of that style. In cross-style
11
- # scenarios where different players use different game traditions, this dual encoding becomes
12
- # essential for unambiguous piece identification.
13
- #
14
- # ## Dual-Purpose Encoding
15
- #
16
- # Each SIN identifier serves two functions:
17
- # - **Style Family Identification**: The family choice indicates which rule system applies
18
- # - **Player Assignment**: The side indicates which player uses this style as their native system
19
- #
20
- # ## Format Structure
21
- #
22
- # An identifier consists of a single ASCII letter with case-based side encoding:
23
- # - Uppercase letter: first player (A, B, C, ..., Z)
24
- # - Lowercase letter: second player (a, b, c, ..., z)
25
- #
26
- # The letter representation combines two distinct semantic components:
27
- # - **Style Family**: The underlying ASCII character (A-Z), representing the game tradition or rule system
28
- # - **Player Assignment**: The case of the character (uppercase/lowercase), representing which player uses this style
8
+ # Represents a parsed SIN (Style Identifier Notation) identifier.
29
9
  #
30
- # Examples of letter composition:
31
- # - Family :C + Side :first → Letter "C" (Chess, First player)
32
- # - Family :C + Side :second Letter "c" (Chess, Second player)
33
- # - Family :S + Side :first → Letter "S" (Shōgi, First player)
34
- # - Family :S + Side :second → Letter "s" (Shōgi, Second player)
10
+ # An Identifier encodes two attributes:
11
+ # - Style: the piece style (A-Z as uppercase symbol)
12
+ # - Side: the player side (:first or :second)
35
13
  #
36
- # ## Canonical Representation
14
+ # Instances are immutable (frozen after creation).
37
15
  #
38
- # SIN enforces canonical representation where each style-player combination has exactly one
39
- # valid identifier within a given context. This ensures consistent interpretation across
40
- # different implementations while allowing flexibility for collision resolution.
16
+ # @example Creating identifiers
17
+ # sin = Identifier.new(:C, :first)
18
+ # sin = Identifier.new(:S, :second)
41
19
  #
42
- # ## Immutability
20
+ # @example String conversion
21
+ # Identifier.new(:C, :first).to_s # => "C"
22
+ # Identifier.new(:C, :second).to_s # => "c"
43
23
  #
44
- # All instances are immutable - transformation methods return new instances.
45
- # This follows the SIN Specification v1.0.0 functional design principles.
46
- #
47
- # @example Basic usage with traditional game styles
48
- # # Chess family identifiers
49
- # chess_white = Sashite::Sin::Identifier.parse("C") # Family :C, Side :first
50
- # chess_black = Sashite::Sin::Identifier.parse("c") # Family :C, Side :second
51
- #
52
- # # Shōgi family identifiers
53
- # shogi_sente = Sashite::Sin::Identifier.parse("S") # Family :S, Side :first
54
- # shogi_gote = Sashite::Sin::Identifier.parse("s") # Family :S, Side :second
55
- #
56
- # @example Dual-purpose encoding demonstration
57
- # identifier = Sashite::Sin::Identifier.parse("C")
58
- # identifier.family # => :C (Style Family)
59
- # identifier.side # => :first (Player Assignment)
60
- # identifier.letter # => "C" (Combined representation)
61
- #
62
- # @example Cross-style scenarios
63
- # # Different families in one match (requires compatible board structures)
64
- # chess_style = Sashite::Sin::Identifier.parse("C") # First player uses Chess family
65
- # ogi_style = Sashite::Sin::Identifier.parse("o") # Second player uses Ōgi family
66
- #
67
- # @see https://sashite.dev/specs/sin/1.0.0/ SIN Specification v1.0.0
24
+ # @see https://sashite.dev/specs/sin/1.0.0/
68
25
  class Identifier
69
- # SIN validation pattern matching the specification regular expression
70
- # Grammar: <sin> ::= <uppercase-letter> | <lowercase-letter>
71
- SIN_PATTERN = /\A[A-Za-z]\z/
72
-
73
- # Player side constants following SIN v1.0.0 two-player constraint
74
- FIRST_PLAYER = :first
75
- SECOND_PLAYER = :second
76
-
77
- # Valid families (A-Z)
78
- VALID_FAMILIES = (:A..:Z).to_a.freeze
26
+ # Valid style symbols (A-Z).
27
+ VALID_STYLES = Constants::VALID_STYLES
79
28
 
80
- # Valid sides array for validation
81
- VALID_SIDES = [FIRST_PLAYER, SECOND_PLAYER].freeze
29
+ # Valid side symbols.
30
+ VALID_SIDES = Constants::VALID_SIDES
82
31
 
83
- # Error messages with SIN-compliant terminology
84
- ERROR_INVALID_SIN = "Invalid SIN string: %s. Must be a single ASCII letter (A-Z, a-z)"
85
- ERROR_INVALID_FAMILY = "Family must be a symbol from :A to :Z representing Style Family, got: %s"
86
- ERROR_INVALID_SIDE = "Side must be :first or :second following SIN two-player constraint, got: %s"
32
+ # @return [Symbol] Piece style (:A to :Z, always uppercase)
33
+ attr_reader :style
87
34
 
88
- # @return [Symbol] the style family (:A to :Z)
89
- # This represents the Style Family component - the game tradition or rule system
90
- attr_reader :family
91
-
92
- # @return [Symbol] the player side (:first or :second)
93
- # This represents the Player Assignment component
35
+ # @return [Symbol] Player side (:first or :second)
94
36
  attr_reader :side
95
37
 
96
- # Create a new identifier instance with canonical representation
97
- #
98
- # @param family [Symbol] style family (:A to :Z representing Style Family)
99
- # @param side [Symbol] player side (:first or :second representing Player Assignment)
100
- # @raise [ArgumentError] if parameters are invalid
38
+ # Creates a new Identifier instance.
101
39
  #
102
- # @example Create identifiers with family and side separation
103
- # # Chess family identifiers
104
- # chess_first = Sashite::Sin::Identifier.new(:C, :first) # => Family=:C, Side=:first
105
- # chess_second = Sashite::Sin::Identifier.new(:C, :second) # => Family=:C, Side=:second
40
+ # @param style [Symbol] Piece style (:A to :Z)
41
+ # @param side [Symbol] Player side (:first or :second)
42
+ # @return [Identifier] A new frozen Identifier instance
43
+ # @raise [Errors::Argument] If any attribute is invalid
106
44
  #
107
- # @example Style Family and Player Assignment demonstration
108
- # identifier = Sashite::Sin::Identifier.new(:S, :first)
109
- # identifier.family # => :S (Shōgi Style Family)
110
- # identifier.side # => :first (First Player Assignment)
111
- # identifier.letter # => "S" (Combined representation)
112
- def initialize(family, side)
113
- self.class.validate_family(family)
114
- self.class.validate_side(side)
115
-
116
- @family = family
45
+ # @example
46
+ # Identifier.new(:C, :first)
47
+ # Identifier.new(:S, :second)
48
+ def initialize(style, side)
49
+ validate_style!(style)
50
+ validate_side!(side)
51
+
52
+ @style = style
117
53
  @side = side
118
54
 
119
55
  freeze
120
56
  end
121
57
 
122
- # Parse an SIN string into an Identifier object with dual-purpose encoding
123
- #
124
- # The family and side are inferred from both the character choice (Style Family)
125
- # and case (Player Assignment):
126
- # - Uppercase letter → Style Family + First player
127
- # - Lowercase letter → Style Family + Second player
128
- #
129
- # @param sin_string [String] SIN notation string (single ASCII letter)
130
- # @return [Identifier] parsed identifier object with Family and Side attributes
131
- # @raise [ArgumentError] if the SIN string is invalid
132
- #
133
- # @example Parse SIN strings with case-based Player Assignment inference
134
- # Sashite::Sin::Identifier.parse("C") # => Family=:C, Side=:first (Chess, White)
135
- # Sashite::Sin::Identifier.parse("c") # => Family=:C, Side=:second (Chess, Black)
136
- # Sashite::Sin::Identifier.parse("S") # => Family=:S, Side=:first (Shōgi, Sente)
137
- # Sashite::Sin::Identifier.parse("s") # => Family=:S, Side=:second (Shōgi, Gote)
138
- #
139
- # @example Traditional game styles from SIN Examples
140
- # # Chess (8×8 board)
141
- # chess_white = Sashite::Sin::Identifier.parse("C") # First player (White pieces)
142
- # chess_black = Sashite::Sin::Identifier.parse("c") # Second player (Black pieces)
143
- #
144
- # # Xiangqi (9×10 board)
145
- # xiangqi_red = Sashite::Sin::Identifier.parse("X") # First player (Red pieces)
146
- # xiangqi_black = Sashite::Sin::Identifier.parse("x") # Second player (Black pieces)
147
- def self.parse(sin_string)
148
- string_value = String(sin_string)
149
- validate_sin_string(string_value)
150
-
151
- # Extract Style Family (case-insensitive) and Player Assignment (case-sensitive)
152
- family_symbol = string_value.upcase.to_sym
153
- identifier_side = string_value == string_value.upcase ? FIRST_PLAYER : SECOND_PLAYER
154
-
155
- new(family_symbol, identifier_side)
156
- end
58
+ # ========================================================================
59
+ # String Conversion
60
+ # ========================================================================
157
61
 
158
- # Check if a string is a valid SIN notation according to specification
159
- #
160
- # Validates against the SIN grammar:
161
- # <sin> ::= <uppercase-letter> | <lowercase-letter>
162
- #
163
- # @param sin_string [String] the string to validate
164
- # @return [Boolean] true if valid SIN, false otherwise
165
- #
166
- # @example Validate SIN strings against specification
167
- # Sashite::Sin::Identifier.valid?("C") # => true (Chess first player)
168
- # Sashite::Sin::Identifier.valid?("c") # => true (Chess second player)
169
- # Sashite::Sin::Identifier.valid?("CHESS") # => false (multi-character)
170
- # Sashite::Sin::Identifier.valid?("1") # => false (not ASCII letter)
171
- def self.valid?(sin_string)
172
- return false unless sin_string.is_a?(::String)
173
-
174
- sin_string.match?(SIN_PATTERN)
175
- end
176
-
177
- # Convert the identifier to its SIN string representation
178
- #
179
- # Returns the canonical SIN notation with proper case encoding for Player Assignment.
62
+ # Returns the SIN string representation.
180
63
  #
181
- # @return [String] SIN notation string (single ASCII letter)
64
+ # @return [String] The single-character SIN string
182
65
  #
183
- # @example Display identifiers in canonical SIN format
184
- # chess_first.to_s # => "C" (Chess family, first player)
185
- # chess_second.to_s # => "c" (Chess family, second player)
186
- # shogi_first.to_s # => "S" (Shōgi family, first player)
66
+ # @example
67
+ # Identifier.new(:C, :first).to_s # => "C"
68
+ # Identifier.new(:C, :second).to_s # => "c"
187
69
  def to_s
188
70
  letter
189
71
  end
190
72
 
191
- # Get the letter representation combining Style Family and Player Assignment
73
+ # Returns the letter component of the SIN.
192
74
  #
193
- # @return [String] letter representation with proper case encoding
75
+ # @return [String] Uppercase for first player, lowercase for second
194
76
  #
195
- # @example Letter representation with dual-purpose encoding
196
- # chess_first.letter # => "C" (Chess family, first player)
197
- # chess_second.letter # => "c" (Chess family, second player)
198
- # shogi_first.letter # => "S" (Shōgi family, first player)
77
+ # @example
78
+ # Identifier.new(:C, :first).letter # => "C"
79
+ # Identifier.new(:C, :second).letter # => "c"
199
80
  def letter
200
- first_player? ? family.to_s.upcase : family.to_s.downcase
81
+ base = String(style)
82
+
83
+ case side
84
+ when :first then base.upcase
85
+ when :second then base.downcase
86
+ end
201
87
  end
202
88
 
203
- # Create a new identifier with opposite Player Assignment (flip sides)
204
- #
205
- # Transforms Player Assignment while maintaining Style Family:
206
- # - First player → Second player (uppercase → lowercase)
207
- # - Second player First player (lowercase → uppercase)
208
- #
209
- # @return [Identifier] new immutable identifier instance with flipped Player Assignment
89
+ # ========================================================================
90
+ # Side Transformations
91
+ # ========================================================================
92
+
93
+ # Returns a new Identifier with the opposite side.
210
94
  #
211
- # @example Flip Player Assignment within same Style Family
212
- # chess_white = Sashite::Sin::Identifier.parse("C")
213
- # chess_black = chess_white.flip # => Family=:C, Side=:second
95
+ # @return [Identifier] A new Identifier with flipped side
214
96
  #
215
- # shogi_sente = Sashite::Sin::Identifier.parse("S")
216
- # shogi_gote = shogi_sente.flip # => Family=:S, Side=:second
97
+ # @example
98
+ # sin = Identifier.new(:C, :first)
99
+ # sin.flip.to_s # => "c"
217
100
  def flip
218
- self.class.new(family, opposite_side)
101
+ new_side = first_player? ? :second : :first
102
+ self.class.new(style, new_side)
219
103
  end
220
104
 
221
- # Create a new identifier with a different Style Family (keeping same Player Assignment)
222
- #
223
- # Changes the Style Family component while preserving Player Assignment.
224
- #
225
- # @param new_family [Symbol] new Style Family (:A to :Z)
226
- # @return [Identifier] new immutable identifier instance with different Style Family
105
+ # ========================================================================
106
+ # Attribute Transformations
107
+ # ========================================================================
108
+
109
+ # Returns a new Identifier with a different style.
227
110
  #
228
- # @example Change Style Family while preserving Player Assignment
229
- # chess_white = Sashite::Sin::Identifier.parse("C") # Chess, first player
230
- # shogi_white = chess_white.with_family(:S) # Shōgi, first player
111
+ # @param new_style [Symbol] The new piece style (:A to :Z)
112
+ # @return [Identifier] A new Identifier with the specified style
113
+ # @raise [Errors::Argument] If the style is invalid
231
114
  #
232
- # chess_black = Sashite::Sin::Identifier.parse("c") # Chess, second player
233
- # xiangqi_black = chess_black.with_family(:X) # Xiangqi, second player
234
- def with_family(new_family)
235
- self.class.validate_family(new_family)
236
- return self if family == new_family
115
+ # @example
116
+ # sin = Identifier.new(:C, :first)
117
+ # sin.with_style(:S).to_s # => "S"
118
+ def with_style(new_style)
119
+ return self if style.equal?(new_style)
237
120
 
238
- self.class.new(new_family, side)
121
+ self.class.new(new_style, side)
239
122
  end
240
123
 
241
- # Create a new identifier with a different Player Assignment (keeping same Style Family)
242
- #
243
- # Changes the Player Assignment component while preserving Style Family.
124
+ # Returns a new Identifier with a different side.
244
125
  #
245
- # @param new_side [Symbol] :first or :second
246
- # @return [Identifier] new immutable identifier instance with different Player Assignment
126
+ # @param new_side [Symbol] The new side (:first or :second)
127
+ # @return [Identifier] A new Identifier with the specified side
128
+ # @raise [Errors::Argument] If the side is invalid
247
129
  #
248
- # @example Change Player Assignment within same Style Family
249
- # chess_white = Sashite::Sin::Identifier.parse("C") # Chess, first player
250
- # chess_black = chess_white.with_side(:second) # Chess, second player
251
- #
252
- # shogi_sente = Sashite::Sin::Identifier.parse("S") # Shōgi, first player
253
- # shogi_gote = shogi_sente.with_side(:second) # Shōgi, second player
130
+ # @example
131
+ # sin = Identifier.new(:C, :first)
132
+ # sin.with_side(:second).to_s # => "c"
254
133
  def with_side(new_side)
255
- self.class.validate_side(new_side)
256
- return self if side == new_side
134
+ return self if side.equal?(new_side)
257
135
 
258
- self.class.new(family, new_side)
136
+ self.class.new(style, new_side)
259
137
  end
260
138
 
261
- # Check if the identifier belongs to the first player
139
+ # ========================================================================
140
+ # Side Queries
141
+ # ========================================================================
142
+
143
+ # Checks if the Identifier belongs to the first player.
262
144
  #
263
- # @return [Boolean] true if first player (uppercase letter)
145
+ # @return [Boolean] true if first player
264
146
  #
265
- # @example Player identification
266
- # Sashite::Sin::Identifier.parse("C").first_player? # => true
267
- # Sashite::Sin::Identifier.parse("c").first_player? # => false
147
+ # @example
148
+ # Identifier.new(:C, :first).first_player? # => true
268
149
  def first_player?
269
- side == FIRST_PLAYER
150
+ side.equal?(:first)
270
151
  end
271
152
 
272
- # Check if the identifier belongs to the second player
153
+ # Checks if the Identifier belongs to the second player.
273
154
  #
274
- # @return [Boolean] true if second player (lowercase letter)
155
+ # @return [Boolean] true if second player
275
156
  #
276
- # @example Player identification
277
- # Sashite::Sin::Identifier.parse("c").second_player? # => true
278
- # Sashite::Sin::Identifier.parse("C").second_player? # => false
157
+ # @example
158
+ # Identifier.new(:C, :second).second_player? # => true
279
159
  def second_player?
280
- side == SECOND_PLAYER
160
+ side.equal?(:second)
281
161
  end
282
162
 
283
- # Check if this identifier has the same Style Family as another
284
- #
285
- # Compares the Style Family component, ignoring Player Assignment.
286
- # This is useful for identifying pieces from the same game tradition in cross-style scenarios.
287
- #
288
- # @param other [Identifier] identifier to compare with
289
- # @return [Boolean] true if both identifiers use the same Style Family
163
+ # ========================================================================
164
+ # Comparison Queries
165
+ # ========================================================================
166
+
167
+ # Checks if two Identifiers have the same style.
290
168
  #
291
- # @example Compare Style Families across different Player Assignments
292
- # chess_white = Sashite::Sin::Identifier.parse("C") # Chess, first player
293
- # chess_black = Sashite::Sin::Identifier.parse("c") # Chess, second player
294
- # shogi_white = Sashite::Sin::Identifier.parse("S") # Shōgi, first player
169
+ # @param other [Identifier] The other Identifier to compare
170
+ # @return [Boolean] true if same style
295
171
  #
296
- # chess_white.same_family?(chess_black) # => true (both Chess family)
297
- # chess_white.same_family?(shogi_white) # => false (different families)
298
- def same_family?(other)
299
- return false unless other.is_a?(self.class)
300
-
301
- family == other.family
172
+ # @example
173
+ # sin1 = Identifier.new(:C, :first)
174
+ # sin2 = Identifier.new(:C, :second)
175
+ # sin1.same_style?(sin2) # => true
176
+ def same_style?(other)
177
+ style.equal?(other.style)
302
178
  end
303
179
 
304
- # Check if this identifier belongs to the same Player Assignment as another
305
- #
306
- # Compares the Player Assignment component of identifiers across different Style Families.
307
- # This is useful for grouping pieces by controlling player in multi-style games.
180
+ # Checks if two Identifiers have the same side.
308
181
  #
309
- # @param other [Identifier] identifier to compare with
310
- # @return [Boolean] true if both identifiers belong to the same Player Assignment
182
+ # @param other [Identifier] The other Identifier to compare
183
+ # @return [Boolean] true if same side
311
184
  #
312
- # @example Compare Player Assignments across different Style Families
313
- # chess_white = Sashite::Sin::Identifier.parse("C") # Chess, first player
314
- # shogi_white = Sashite::Sin::Identifier.parse("S") # Shōgi, first player
315
- # chess_black = Sashite::Sin::Identifier.parse("c") # Chess, second player
316
- #
317
- # chess_white.same_side?(shogi_white) # => true (both first player)
318
- # chess_white.same_side?(chess_black) # => false (different players)
185
+ # @example
186
+ # sin1 = Identifier.new(:C, :first)
187
+ # sin2 = Identifier.new(:S, :first)
188
+ # sin1.same_side?(sin2) # => true
319
189
  def same_side?(other)
320
- return false unless other.is_a?(self.class)
321
-
322
- side == other.side
190
+ side.equal?(other.side)
323
191
  end
324
192
 
325
- # Compatibility alias for same_family? to maintain API consistency
326
- #
327
- # @deprecated Use {#same_family?} instead for clearer semantics
328
- # @param other [Identifier] identifier to compare with
329
- # @return [Boolean] true if both identifiers use the same Style Family
330
- def same_letter?(other)
331
- same_family?(other)
332
- end
193
+ # ========================================================================
194
+ # Equality
195
+ # ========================================================================
333
196
 
334
- # Custom equality comparison
197
+ # Checks equality with another Identifier.
335
198
  #
336
- # Two identifiers are equal if they have identical Family and Side attributes.
199
+ # @param other [Object] The object to compare
200
+ # @return [Boolean] true if equal
337
201
  #
338
- # @param other [Object] object to compare with
339
- # @return [Boolean] true if both objects are identifiers with identical Family and Side
340
- #
341
- # @example Equality comparison
342
- # id1 = Sashite::Sin::Identifier.parse("C")
343
- # id2 = Sashite::Sin::Identifier.parse("C")
344
- # id3 = Sashite::Sin::Identifier.parse("c")
345
- #
346
- # id1 == id2 # => true (identical Family and Side)
347
- # id1 == id3 # => false (different Player Assignment)
202
+ # @example
203
+ # sin1 = Identifier.new(:C, :first)
204
+ # sin2 = Identifier.new(:C, :first)
205
+ # sin1 == sin2 # => true
348
206
  def ==(other)
349
- return false unless other.is_a?(self.class)
207
+ return false unless self.class === other
350
208
 
351
- family == other.family && side == other.side
209
+ style.equal?(other.style) && side.equal?(other.side)
352
210
  end
353
211
 
354
- # Alias for == to ensure Set functionality works correctly
355
212
  alias eql? ==
356
213
 
357
- # Custom hash implementation for use in collections
214
+ # Returns a hash code for the Identifier.
358
215
  #
359
- # @return [Integer] hash value based on class, Family, and Side
216
+ # @return [Integer] Hash code
360
217
  def hash
361
- [self.class, family, side].hash
218
+ [style, side].hash
362
219
  end
363
220
 
364
- # Validate that the family is a valid Style Family symbol
221
+ # Returns an inspect string for the Identifier.
365
222
  #
366
- # @param family [Symbol] the family to validate
367
- # @raise [ArgumentError] if invalid
368
- def self.validate_family(family)
369
- return if VALID_FAMILIES.include?(family)
370
-
371
- raise ::ArgumentError, format(ERROR_INVALID_FAMILY, family.inspect)
223
+ # @return [String] Inspect representation
224
+ #
225
+ # @example
226
+ # Identifier.new(:C, :first).inspect # => "#<Sashite::Sin::Identifier C>"
227
+ def inspect
228
+ "#<#{self.class} #{self}>"
372
229
  end
373
230
 
374
- # Validate that the side follows SIN two-player constraint
375
- #
376
- # @param side [Symbol] the side to validate
377
- # @raise [ArgumentError] if invalid
378
- def self.validate_side(side)
379
- return if VALID_SIDES.include?(side)
231
+ private
380
232
 
381
- raise ::ArgumentError, format(ERROR_INVALID_SIDE, side.inspect)
382
- end
233
+ # ========================================================================
234
+ # Private Validation
235
+ # ========================================================================
383
236
 
384
- # Validate SIN string format against specification grammar
385
- #
386
- # @param string [String] string to validate
387
- # @raise [ArgumentError] if string doesn't match SIN pattern
388
- def self.validate_sin_string(string)
389
- return if string.match?(SIN_PATTERN)
237
+ def validate_style!(style)
238
+ return if ::Symbol === style && Constants::VALID_STYLES.include?(style)
390
239
 
391
- raise ::ArgumentError, format(ERROR_INVALID_SIN, string)
240
+ raise Errors::Argument, Errors::Argument::Messages::INVALID_STYLE
392
241
  end
393
242
 
394
- private_class_method :validate_sin_string
243
+ def validate_side!(side)
244
+ return if ::Symbol === side && Constants::VALID_SIDES.include?(side)
395
245
 
396
- private
397
-
398
- # Get the opposite Player Assignment
399
- #
400
- # @return [Symbol] the opposite side
401
- def opposite_side
402
- first_player? ? SECOND_PLAYER : FIRST_PLAYER
246
+ raise Errors::Argument, Errors::Argument::Messages::INVALID_SIDE
403
247
  end
404
248
  end
405
249
  end