feen 5.0.0.beta0 → 5.0.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
[![RuboCop](https://github.com/sashite/feen.rb/workflows/RuboCop/badge.svg?branch=main)](https://github.com/sashite/feen.rb/actions?query=workflow%3Arubocop+branch%3Amain)
|
7
7
|
[![License](https://img.shields.io/github/license/sashite/feen.rb?label=License&logo=github)](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
|
-
![3D chess on Star Trek (from the episode "Court Martial")](https://github.com/sashite/feen.rb/raw/main/star-trek-chess.jpg)
|
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
|