feen 5.0.0.beta0 → 5.0.0.beta1
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 +12 -29
- data/lib/feen/dumper/piece_placement.rb +2 -1
- data/lib/feen/dumper.rb +6 -12
- data/lib/feen/parser/board_shape.rb +6 -4
- data/lib/feen/parser.rb +22 -11
- data/lib/feen.rb +7 -11
- metadata +2 -5
- data/lib/feen/dumper/pieces_in_hand.rb +0 -28
- data/lib/feen/parser/piece_placement.rb +0 -61
- data/lib/feen/parser/pieces_in_hand.rb +0 -34
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2fa56bccbb1911f15569f2f5643646986cbf83661efba3165c69978e7c85fd56
|
4
|
+
data.tar.gz: 13d706d1bfe1c09f6d015d0a8f28af621754677711074ec60ec96e395f1a1061
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5479cbd02c902aa8c45cc12ecfab31c7b7283eb1f8ec60b110c889505a7ebbcf76fa7b0d2e6ad44c60da954312fa85ae03cd4981d929207b0612754f1d62a033
|
7
|
+
data.tar.gz: b6268ce619808bf2c2f889261b55007edbfc030bc37b8edea7f73f505de55e8c0fc1cd4a268e8e2bdc71fe21c9fa33f904c8b26a23b92b471e406e109494bd97
|
data/README.md
CHANGED
@@ -6,30 +6,18 @@
|
|
6
6
|
[](https://github.com/sashite/feen.rb/actions?query=workflow%3Arubocop+branch%3Amain)
|
7
7
|
[](https://github.com/sashite/feen.rb/raw/main/LICENSE.md)
|
8
8
|
|
9
|
-
> __FEEN__ (
|
9
|
+
> __FEEN__ (Forsyth–Edwards Expanded Notation) support for the Ruby language.
|
10
10
|
|
11
11
|
## Overview
|
12
12
|
|
13
|
-
This is an implementation of [FEEN](https://
|
14
|
-
|
15
|
-
A __FEEN__ string consists of a single line of ASCII text containing three data fields, separated by a space. These are:
|
16
|
-
|
17
|
-
1. Piece placement
|
18
|
-
2. Side to move
|
19
|
-
3. Pieces in hand
|
20
|
-
|
21
|
-
The main chess variants may be supported, including [Chess](https://en.wikipedia.org/wiki/Chess), [Janggi](https://en.wikipedia.org/wiki/Janggi), [Makruk](https://en.wikipedia.org/wiki/Makruk), [Shogi](https://en.wikipedia.org/wiki/Shogi), [Xiangqi](https://en.wikipedia.org/wiki/Xiangqi).
|
22
|
-
|
23
|
-
More exotic variants may be also supported, like: [Dai dai shogi](https://en.wikipedia.org/wiki/Dai_dai_shogi), [Four-player chess](https://en.wikipedia.org/wiki/Four-player_chess), or [Three-dimensional chess](https://en.wikipedia.org/wiki/Three-dimensional_chess) 🖖
|
24
|
-
|
25
|
-

|
13
|
+
This is an implementation of [FEEN](https://github.com/sashite/specs/blob/main/forsyth-edwards-expanded-notation.md), a flexible and minimalist format for describing chess variant positions.
|
26
14
|
|
27
15
|
## Installation
|
28
16
|
|
29
17
|
Add this line to your application's Gemfile:
|
30
18
|
|
31
19
|
```ruby
|
32
|
-
gem "feen", ">= 5.0.0.
|
20
|
+
gem "feen", ">= 5.0.0.beta1"
|
33
21
|
```
|
34
22
|
|
35
23
|
And then execute:
|
@@ -50,22 +38,20 @@ gem install feen --pre
|
|
50
38
|
|
51
39
|
A position can be serialized by filling in these fields:
|
52
40
|
|
53
|
-
- **
|
54
|
-
- **
|
55
|
-
- **
|
56
|
-
- **Board shape**: An array of integers. For instance, it would be `[10, 9]` in Xiangqi. And it would be `[8, 8]` in Chess.
|
41
|
+
- **Board shape**: An array of integers. For instance, it would be `[10, 9]` for a Xiangqi board. Or it would be `[8, 8]` for a Chess board.
|
42
|
+
- **Piece placement**: Describes the placement of pieces on the board with a hash that references each piece on the board.
|
43
|
+
- **Side to move**: A char that indicates who moves next. In chess, "`w`" would mean that White can play a move.
|
57
44
|
|
58
|
-
####
|
45
|
+
#### Example
|
59
46
|
|
60
|
-
|
47
|
+
From a classic Tsume Shogi problem:
|
61
48
|
|
62
49
|
```ruby
|
63
50
|
require "feen"
|
64
51
|
|
65
52
|
Feen.dump(
|
66
|
-
side_to_move: "s",
|
67
|
-
pieces_in_hand: %w[S r r b g g g g s n n n n p p p p p p p p p p p p p p p p p],
|
68
53
|
board_shape: [9, 9],
|
54
|
+
side_to_move: "s",
|
69
55
|
piece_placement: {
|
70
56
|
3 => "s",
|
71
57
|
4 => "k",
|
@@ -74,23 +60,20 @@ Feen.dump(
|
|
74
60
|
43 => "+B"
|
75
61
|
}
|
76
62
|
)
|
77
|
-
# => "
|
63
|
+
# => "3sks3/9/4+P4/9/7+B1/9/9/9/9 s"
|
78
64
|
```
|
79
65
|
|
80
66
|
### Deserialization
|
81
67
|
|
82
68
|
Serialized positions can be converted back to fields.
|
83
69
|
|
84
|
-
####
|
85
|
-
|
86
|
-
##### A classic Tsume Shogi problem
|
70
|
+
#### Example
|
87
71
|
|
88
72
|
```ruby
|
89
73
|
require "feen"
|
90
74
|
|
91
|
-
Feen.parse("
|
75
|
+
Feen.parse("3sks3/9/4+P4/9/7+B1/9/9/9/9 s")
|
92
76
|
# {:board_shape=>[9, 9],
|
93
|
-
# :pieces_in_hand=>["S", "b", "g", "g", "g", "g", "n", "n", "n", "n", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "r", "r", "s"],
|
94
77
|
# :piece_placement=>{3=>"s", 4=>"k", 5=>"s", 22=>"+P", 43=>"+B"},
|
95
78
|
# :side_to_move=>"s"}
|
96
79
|
```
|
@@ -44,7 +44,7 @@ module Feen
|
|
44
44
|
# 88 => "傌",
|
45
45
|
# 89 => "俥"
|
46
46
|
# }
|
47
|
-
# ).to_s # => "
|
47
|
+
# ).to_s # => "車馬象士將士象馬車/9/1砲5砲1/卒1卒1卒1卒1卒/9/9/兵1兵1兵1兵1兵/1炮5炮1/9/俥傌相仕帥仕相傌俥"
|
48
48
|
class PiecePlacement
|
49
49
|
# @param indexes [Array] The shape of the board.
|
50
50
|
# @param piece_placement [Hash] The index of each piece on the board.
|
@@ -79,6 +79,7 @@ module Feen
|
|
79
79
|
.map { |square| square.nil? ? 1 : square }
|
80
80
|
.join(",")
|
81
81
|
.gsub(/1,[1,]*1/) { |str| str.split(",").length }
|
82
|
+
.delete(",")
|
82
83
|
end
|
83
84
|
end
|
84
85
|
end
|
data/lib/feen/dumper.rb
CHANGED
@@ -1,23 +1,20 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require_relative File.join("dumper", "piece_placement")
|
4
|
-
require_relative File.join("dumper", "pieces_in_hand")
|
5
4
|
|
6
5
|
module Feen
|
7
6
|
# The dumper module.
|
8
7
|
module Dumper
|
9
8
|
# Dump position params into a FEEN string.
|
10
9
|
#
|
11
|
-
# @param side_to_move [String] Identify the active side.
|
12
|
-
# @param pieces_in_hand [Array, nil] The list of pieces in hand.
|
13
10
|
# @param board_shape [Array] The shape of the board.
|
11
|
+
# @param side_to_move [String] Identify the active side.
|
14
12
|
# @param piece_placement [Hash] The index of each piece on the board.
|
15
13
|
#
|
16
14
|
# @example Dump a classic Tsume Shogi problem
|
17
15
|
# call(
|
18
|
-
# "side_to_move": "s",
|
19
|
-
# "pieces_in_hand": %w[S r r b g g g g s n n n n p p p p p p p p p p p p p p p p p],
|
20
16
|
# "board_shape": [9, 9],
|
17
|
+
# "side_to_move": "s",
|
21
18
|
# "piece_placement": {
|
22
19
|
# 3 => "s",
|
23
20
|
# 4 => "k",
|
@@ -26,17 +23,14 @@ module Feen
|
|
26
23
|
# 43 => "+B"
|
27
24
|
# }
|
28
25
|
# )
|
29
|
-
# # => "
|
26
|
+
# # => "3sks3/9/4+P4/9/7+B1/9/9/9/9 s"
|
30
27
|
#
|
31
28
|
# @return [String] The FEEN string representing the position.
|
32
|
-
def self.call(board_shape:, side_to_move:, piece_placement
|
33
|
-
|
29
|
+
def self.call(board_shape:, side_to_move:, piece_placement:)
|
30
|
+
[
|
34
31
|
PiecePlacement.new(board_shape, piece_placement).to_s,
|
35
32
|
side_to_move
|
36
|
-
]
|
37
|
-
|
38
|
-
array << PiecesInHand.dump(pieces_in_hand) if Array(pieces_in_hand).any?
|
39
|
-
array.join(" ")
|
33
|
+
].join(" ")
|
40
34
|
end
|
41
35
|
end
|
42
36
|
end
|
@@ -5,11 +5,12 @@ module Feen
|
|
5
5
|
# The BoardShape class.
|
6
6
|
#
|
7
7
|
# @example Parse the shape of a shogiban
|
8
|
-
# BoardShape.new("
|
8
|
+
# BoardShape.new("3sks3/9/4+P4/9/7+B1/9/9/9/9").to_a # => [9, 9]
|
9
9
|
class BoardShape
|
10
10
|
# @param board_str [String] The flatten board.
|
11
|
-
def initialize(board_str)
|
11
|
+
def initialize(board_str, regex: /\+?[a-z]/i)
|
12
12
|
@board_str = board_str
|
13
|
+
@regex = regex
|
13
14
|
end
|
14
15
|
|
15
16
|
# @return [Array] The size of each dimension of the board.
|
@@ -21,8 +22,9 @@ module Feen
|
|
21
22
|
|
22
23
|
def indexes(string, separator)
|
23
24
|
if separator.empty?
|
24
|
-
last_index = string.
|
25
|
-
|
25
|
+
last_index = string.scan(/(\d+|#{@regex})/).inject(0) do |counter, match|
|
26
|
+
sub_string = match[0]
|
27
|
+
number = sub_string.match?(/\d+/) ? Integer(sub_string) : 1
|
26
28
|
counter + number
|
27
29
|
end
|
28
30
|
|
data/lib/feen/parser.rb
CHANGED
@@ -1,8 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require_relative File.join("parser", "board_shape")
|
4
|
-
require_relative File.join("parser", "pieces_in_hand")
|
5
|
-
require_relative File.join("parser", "piece_placement")
|
6
4
|
|
7
5
|
module Feen
|
8
6
|
# The parser module.
|
@@ -12,11 +10,10 @@ module Feen
|
|
12
10
|
# @param feen [String] The FEEN string representing a position.
|
13
11
|
#
|
14
12
|
# @example Parse a classic Tsume Shogi problem
|
15
|
-
# call("
|
13
|
+
# call("3sks3/9/4+P4/9/7+B1/9/9/9/9 s")
|
16
14
|
# # => {
|
17
|
-
# # "side_to_move": "s",
|
18
|
-
# # "pieces_in_hand": ["S", "b", "g", "g", "g", "g", "n", "n", "n", "n", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "r", "r", "s"],
|
19
15
|
# # "board_shape": [9, 9],
|
16
|
+
# # "side_to_move": "s",
|
20
17
|
# # "piece_placement": {
|
21
18
|
# # 3 => "s",
|
22
19
|
# # 4 => "k",
|
@@ -26,15 +23,29 @@ module Feen
|
|
26
23
|
# # }
|
27
24
|
#
|
28
25
|
# @return [Hash] The position params representing the position.
|
29
|
-
def self.call(feen)
|
30
|
-
|
26
|
+
def self.call(feen, regex: /\+?[a-z]/i)
|
27
|
+
piece_placement_str, side_to_move_str = feen.split
|
31
28
|
|
32
29
|
{
|
33
|
-
board_shape: BoardShape.new(
|
34
|
-
|
35
|
-
|
36
|
-
side_to_move:
|
30
|
+
board_shape: BoardShape.new(piece_placement_str, regex:).to_a,
|
31
|
+
piece_placement: piece_placement(piece_placement_str, regex:),
|
32
|
+
side_to_move: side_to_move_str
|
37
33
|
}
|
38
34
|
end
|
35
|
+
|
36
|
+
def self.piece_placement(string, regex:)
|
37
|
+
hash = {}
|
38
|
+
index = 0
|
39
|
+
string.scan(/(\d+|#{regex})/) do |match|
|
40
|
+
if /\d+/.match?(match[0])
|
41
|
+
index += match[0].to_i
|
42
|
+
else
|
43
|
+
hash[index] = match[0]
|
44
|
+
index += 1
|
45
|
+
end
|
46
|
+
end
|
47
|
+
hash
|
48
|
+
end
|
49
|
+
private_class_method :piece_placement
|
39
50
|
end
|
40
51
|
end
|
data/lib/feen.rb
CHANGED
@@ -6,20 +6,18 @@ require_relative File.join("feen", "parser")
|
|
6
6
|
# This module provides a Ruby interface for data serialization and
|
7
7
|
# deserialization in FEEN format.
|
8
8
|
#
|
9
|
-
# @see https://
|
9
|
+
# @see https://github.com/sashite/specs/blob/main/forsyth-edwards-expanded-notation.md
|
10
10
|
module Feen
|
11
11
|
# Dumps position params into a FEEN string.
|
12
12
|
#
|
13
|
-
# @param pieces_in_hand [Array, nil] The list of pieces in hand.
|
14
13
|
# @param board_shape [Array] The shape of the board.
|
15
14
|
# @param side_to_move [String] The identifier of the player who must play.
|
16
15
|
# @param piece_placement [Hash] The index of each piece on the board.
|
17
16
|
#
|
18
17
|
# @example Dump a classic Tsume Shogi problem
|
19
18
|
# dump(
|
20
|
-
# "side_to_move": "s",
|
21
|
-
# "pieces_in_hand": %w[S r r b g g g g s n n n n p p p p p p p p p p p p p p p p p],
|
22
19
|
# "board_shape": [9, 9],
|
20
|
+
# "side_to_move": "s",
|
23
21
|
# "piece_placement": {
|
24
22
|
# 3 => "s",
|
25
23
|
# 4 => "k",
|
@@ -28,12 +26,11 @@ module Feen
|
|
28
26
|
# 43 => "+B"
|
29
27
|
# }
|
30
28
|
# )
|
31
|
-
# # => "
|
29
|
+
# # => "3sks3/9/4+P4/9/7+B1/9/9/9/9 s"
|
32
30
|
#
|
33
31
|
# @return [String] The FEEN string representing the position.
|
34
|
-
def self.dump(board_shape:, side_to_move:, piece_placement
|
32
|
+
def self.dump(board_shape:, side_to_move:, piece_placement:)
|
35
33
|
Dumper.call(
|
36
|
-
pieces_in_hand:,
|
37
34
|
board_shape:,
|
38
35
|
side_to_move:,
|
39
36
|
piece_placement:
|
@@ -45,9 +42,8 @@ module Feen
|
|
45
42
|
# @param feen [String] The FEEN string representing a position.
|
46
43
|
#
|
47
44
|
# @example Parse a classic Tsume Shogi problem
|
48
|
-
# parse("
|
45
|
+
# parse("3sks3/9/4+P4/9/7+B1/9/9/9/9 s")
|
49
46
|
# # => {
|
50
|
-
# # "pieces_in_hand": ["S", "b", "g", "g", "g", "g", "n", "n", "n", "n", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "r", "r", "s"],
|
51
47
|
# # "board_shape": [9, 9],
|
52
48
|
# # "side_to_move": "s",
|
53
49
|
# # "piece_placement": {
|
@@ -59,7 +55,7 @@ module Feen
|
|
59
55
|
# # }
|
60
56
|
#
|
61
57
|
# @return [Hash] The position params representing the position.
|
62
|
-
def self.parse(feen)
|
63
|
-
Parser.call(feen)
|
58
|
+
def self.parse(feen, regex: /\+?[a-z]/i)
|
59
|
+
Parser.call(feen, regex:)
|
64
60
|
end
|
65
61
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: feen
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 5.0.0.
|
4
|
+
version: 5.0.0.beta1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Cyril Kato
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-04-
|
11
|
+
date: 2023-04-27 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: A Ruby interface for data serialization and deserialization in FEEN format.
|
14
14
|
email: contact@cyril.email
|
@@ -21,11 +21,8 @@ files:
|
|
21
21
|
- lib/feen.rb
|
22
22
|
- lib/feen/dumper.rb
|
23
23
|
- lib/feen/dumper/piece_placement.rb
|
24
|
-
- lib/feen/dumper/pieces_in_hand.rb
|
25
24
|
- lib/feen/parser.rb
|
26
25
|
- lib/feen/parser/board_shape.rb
|
27
|
-
- lib/feen/parser/piece_placement.rb
|
28
|
-
- lib/feen/parser/pieces_in_hand.rb
|
29
26
|
homepage: https://github.com/sashite/feen.rb
|
30
27
|
licenses:
|
31
28
|
- MIT
|
@@ -1,28 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Feen
|
4
|
-
module Dumper
|
5
|
-
# A module that serializes pieces in hand lists into a string.
|
6
|
-
module PiecesInHand
|
7
|
-
# Serialize pieces in hand lists into a string.
|
8
|
-
#
|
9
|
-
# @param piece_names [Array] A list of pieces in hand.
|
10
|
-
#
|
11
|
-
# @example Dump a list of pieces in hand
|
12
|
-
# dump(["S", "b", "g", "g", "g", "g", "n", "n", "n", "n", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "r", "r", "s"])
|
13
|
-
# # => "S,b,g*4,n*4,p*17,r*2,s"
|
14
|
-
#
|
15
|
-
# @example Dump an empty list of pieces in hand
|
16
|
-
# dump([])
|
17
|
-
# # => nil
|
18
|
-
#
|
19
|
-
# @return [String, nil] A serialized list of pieces in hand.
|
20
|
-
def self.dump(piece_names)
|
21
|
-
return if piece_names.empty?
|
22
|
-
|
23
|
-
hash = piece_names.group_by(&:itself).transform_values(&:count)
|
24
|
-
hash.map { |k, v| v > 1 ? "#{k}*#{v}" : k }.sort.join(",")
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
@@ -1,61 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Feen
|
4
|
-
module Parser
|
5
|
-
# The PiecePlacement class.
|
6
|
-
#
|
7
|
-
# @example Parse a Shogi problem board and return an array
|
8
|
-
# PiecePlacement.new("3,s,k,s,3/9/4,+P,4/9/7,+B,1/9/9/9/9").to_a
|
9
|
-
# # => [
|
10
|
-
# # nil, nil, nil, "s", "k", "s", nil, nil, nil,
|
11
|
-
# # nil, nil, nil, nil, nil, nil, nil, nil, nil,
|
12
|
-
# # nil, nil, nil, nil, "+P", nil, nil, nil, nil,
|
13
|
-
# # nil, nil, nil, nil, nil, nil, nil, nil, nil,
|
14
|
-
# # nil, nil, nil, nil, nil, nil, nil, "+B", nil,
|
15
|
-
# # nil, nil, nil, nil, nil, nil, nil, nil, nil,
|
16
|
-
# # nil, nil, nil, nil, nil, nil, nil, nil, nil,
|
17
|
-
# # nil, nil, nil, nil, nil, nil, nil, nil, nil,
|
18
|
-
# # nil, nil, nil, nil, nil, nil, nil, nil, nil
|
19
|
-
# # ]
|
20
|
-
#
|
21
|
-
# @example Parse a Shogi problem board and return a hash
|
22
|
-
# PiecePlacement.new("3,s,k,s,3/9/4,+P,4/9/7,+B,1/9/9/9/9").to_h
|
23
|
-
# # => {
|
24
|
-
# # 3 => "s",
|
25
|
-
# # 4 => "k",
|
26
|
-
# # 5 => "s",
|
27
|
-
# # 22 => "+P",
|
28
|
-
# # 43 => "+B"
|
29
|
-
# # }
|
30
|
-
class PiecePlacement
|
31
|
-
# @param piece_placement_str [String] The placement of pieces on the board.
|
32
|
-
def initialize(piece_placement_str)
|
33
|
-
@piece_placement_str = piece_placement_str
|
34
|
-
end
|
35
|
-
|
36
|
-
# @return [Array] The list of pieces on the board.
|
37
|
-
def to_a
|
38
|
-
@piece_placement_str
|
39
|
-
.split(%r{[/,]+})
|
40
|
-
.flat_map { |str| row(str) }
|
41
|
-
end
|
42
|
-
|
43
|
-
# @return [Hash] The index of each piece on the board.
|
44
|
-
def to_h
|
45
|
-
to_a
|
46
|
-
.each_with_index
|
47
|
-
.inject({}) do |h, (v, i)|
|
48
|
-
next h if v.nil?
|
49
|
-
|
50
|
-
h.merge(i => v)
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
private
|
55
|
-
|
56
|
-
def row(string)
|
57
|
-
string.match?(/[0-9]+/) ? ::Array.new(Integer(string)) : string
|
58
|
-
end
|
59
|
-
end
|
60
|
-
end
|
61
|
-
end
|
@@ -1,34 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Feen
|
4
|
-
module Parser
|
5
|
-
# The pieces in hand module.
|
6
|
-
module PiecesInHand
|
7
|
-
# The list of pieces in hand grouped by players.
|
8
|
-
#
|
9
|
-
# @param pieces_in_hand [String, nil] The serialized list of pieces in hand.
|
10
|
-
#
|
11
|
-
# @example Parse a list of serialized pieces in hand
|
12
|
-
# parse("S,b,g*4,n*4,p*17,r*2,s")
|
13
|
-
# # => ["S", "b", "g", "g", "g", "g", "n", "n", "n", "n", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "r", "r", "s"]
|
14
|
-
#
|
15
|
-
# @example Parse an empty list of serialized pieces in hand
|
16
|
-
# parse("-")
|
17
|
-
# # => []
|
18
|
-
#
|
19
|
-
# @return [Array] The list of pieces in hand grouped by players.
|
20
|
-
def self.parse(pieces_in_hand)
|
21
|
-
return if pieces_in_hand.nil?
|
22
|
-
|
23
|
-
pieces_in_hand.split(",").flat_map do |piece|
|
24
|
-
if piece.include?("*")
|
25
|
-
letter, count = piece.split("*")
|
26
|
-
[letter] * count.to_i
|
27
|
-
else
|
28
|
-
piece
|
29
|
-
end
|
30
|
-
end.sort
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|