feen 1.0.0 → 2.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 +33 -337
- data/lib/feen.rb +30 -677
- data/lib/feen/dumper.rb +25 -36
- data/lib/feen/dumper/board.rb +50 -12
- data/lib/feen/dumper/pieces_in_hand.rb +11 -4
- data/lib/feen/dumper/turn.rb +7 -4
- data/lib/feen/parser.rb +24 -35
- data/lib/feen/parser/board.rb +22 -1
- data/lib/feen/parser/pieces_in_hand.rb +6 -6
- data/lib/feen/parser/shape.rb +1 -1
- data/lib/feen/parser/turn.rb +1 -1
- metadata +16 -3
- data/lib/feen/dumper/inconsistent_size_error.rb +0 -8
data/lib/feen/dumper.rb
CHANGED
@@ -1,56 +1,45 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative
|
4
|
-
require_relative
|
5
|
-
require_relative
|
3
|
+
require_relative "dumper/board"
|
4
|
+
require_relative "dumper/pieces_in_hand"
|
5
|
+
require_relative "dumper/turn"
|
6
6
|
|
7
7
|
module FEEN
|
8
8
|
# The dumper module.
|
9
9
|
module Dumper
|
10
10
|
# Dump position params into a FEEN string.
|
11
11
|
#
|
12
|
-
# @param
|
12
|
+
# @param active_side_id [Integer] The identifier of the player who must play.
|
13
|
+
# @param board [Hash] The indexes of each piece on the board.
|
13
14
|
# @param indexes [Array] The shape of the board.
|
14
|
-
# @param
|
15
|
+
# @param pieces_in_hand_grouped_by_sides [Array] The list of pieces in hand
|
15
16
|
# grouped by players.
|
16
|
-
# @param squares [Array] The list of squares on the board.
|
17
17
|
#
|
18
|
-
# @example Dump
|
18
|
+
# @example Dump a classic Tsume Shogi problem
|
19
19
|
# call(
|
20
|
-
#
|
21
|
-
#
|
22
|
-
#
|
23
|
-
#
|
24
|
-
#
|
25
|
-
#
|
26
|
-
#
|
27
|
-
#
|
28
|
-
#
|
29
|
-
#
|
30
|
-
#
|
31
|
-
#
|
32
|
-
# "bR", "bP", nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , "gP", "gR",
|
33
|
-
# "bN", "bP", nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , "gP", "gN",
|
34
|
-
# "bB", "bP", nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , "gP", "gB",
|
35
|
-
# "bK", "bP", nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , "gP", "gQ",
|
36
|
-
# "bQ", "bP", nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , "gP", "gK",
|
37
|
-
# "bB", "bP", nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , "gP", "gB",
|
38
|
-
# "bN", "bP", nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , "gP", "gN",
|
39
|
-
# "bR", "bP", nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , "gP", "gR",
|
40
|
-
# nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , nil ,
|
41
|
-
# nil , nil , nil , "rP", "rP", "rP", "rP", "rP", "rP", "rP", "rP", nil , nil , nil ,
|
42
|
-
# nil , nil , nil , "rR", "rN", "rB", "rQ", "rK", "rB", "rN", "rR", nil , nil , nil
|
20
|
+
# "active_side_id": 0,
|
21
|
+
# "board": {
|
22
|
+
# "3": "s",
|
23
|
+
# "4": "k" ,
|
24
|
+
# "5": "s",
|
25
|
+
# "22": "+P",
|
26
|
+
# "43": "+B"
|
27
|
+
# },
|
28
|
+
# "indexes": [9, 9],
|
29
|
+
# "pieces_in_hand_grouped_by_sides": [
|
30
|
+
# %w[S],
|
31
|
+
# %w[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]
|
43
32
|
# ]
|
44
33
|
# )
|
45
|
-
# # => "3,
|
34
|
+
# # => "3,s,k,s,3/9/4,+P,4/9/7,+B,1/9/9/9/9 0 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"
|
46
35
|
#
|
47
36
|
# @return [String] The FEEN string representing the position.
|
48
|
-
def self.call(
|
37
|
+
def self.call(active_side_id:, board:, indexes:, pieces_in_hand_grouped_by_sides:)
|
49
38
|
[
|
50
|
-
Board.new(*indexes).to_s
|
51
|
-
Turn.dump(
|
52
|
-
PiecesInHand.dump(*
|
53
|
-
].join(
|
39
|
+
Board.new(*indexes, **board).to_s,
|
40
|
+
Turn.dump(active_side_id, pieces_in_hand_grouped_by_sides.length),
|
41
|
+
PiecesInHand.dump(*pieces_in_hand_grouped_by_sides)
|
42
|
+
].join(" ")
|
54
43
|
end
|
55
44
|
end
|
56
45
|
end
|
data/lib/feen/dumper/board.rb
CHANGED
@@ -1,27 +1,65 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative 'inconsistent_size_error'
|
4
|
-
|
5
3
|
module FEEN
|
6
4
|
module Dumper
|
7
5
|
# The board class.
|
6
|
+
#
|
7
|
+
# @example Dump an empty board of Xiangqi
|
8
|
+
# Board.new(10, 9).to_s # => "9/9/9/9/9/9/9/9/9/9"
|
9
|
+
#
|
10
|
+
# @example Dump the Xiangqi starting position board
|
11
|
+
# Board.new(10, 9,
|
12
|
+
# "0": "車",
|
13
|
+
# "1": "馬",
|
14
|
+
# "2": "象",
|
15
|
+
# "3": "士",
|
16
|
+
# "4": "將",
|
17
|
+
# "5": "士",
|
18
|
+
# "6": "象",
|
19
|
+
# "7": "馬",
|
20
|
+
# "8": "車",
|
21
|
+
# "19": "砲",
|
22
|
+
# "25": "砲",
|
23
|
+
# "27": "卒",
|
24
|
+
# "29": "卒",
|
25
|
+
# "31": "卒",
|
26
|
+
# "33": "卒",
|
27
|
+
# "35": "卒",
|
28
|
+
# "54": "兵",
|
29
|
+
# "56": "兵",
|
30
|
+
# "58": "兵",
|
31
|
+
# "60": "兵",
|
32
|
+
# "62": "兵",
|
33
|
+
# "64": "炮",
|
34
|
+
# "70": "炮",
|
35
|
+
# "81": "俥",
|
36
|
+
# "82": "傌",
|
37
|
+
# "83": "相",
|
38
|
+
# "84": "仕",
|
39
|
+
# "85": "帥",
|
40
|
+
# "86": "仕",
|
41
|
+
# "87": "相",
|
42
|
+
# "88": "傌",
|
43
|
+
# "89": "俥").to_s # => "車,馬,象,士,將,士,象,馬,車/9/1,砲,5,砲,1/卒,1,卒,1,卒,1,卒,1,卒/9/9/兵,1,兵,1,兵,1,兵,1,兵/1,炮,5,炮,1/9/俥,傌,相,仕,帥,仕,相,傌,俥"
|
8
44
|
class Board
|
9
45
|
# @param indexes [Array] The shape of the board.
|
10
|
-
|
46
|
+
# @param board [Hash] The indexes of each piece on the board.
|
47
|
+
def initialize(*indexes, **board)
|
11
48
|
@indexes = indexes
|
49
|
+
@squares = Array.new(length) { |i| board.fetch(i.to_s.to_sym, nil) }
|
12
50
|
end
|
13
51
|
|
14
|
-
# @param squares [Array] The list of squares on the board.
|
15
|
-
#
|
16
52
|
# @return [String] The string representing the board.
|
17
|
-
def to_s
|
18
|
-
|
19
|
-
|
20
|
-
unflatten(squares, *@indexes)
|
53
|
+
def to_s
|
54
|
+
unflatten(@squares, *@indexes)
|
21
55
|
end
|
22
56
|
|
23
57
|
private
|
24
58
|
|
59
|
+
def length
|
60
|
+
@indexes.inject(:*)
|
61
|
+
end
|
62
|
+
|
25
63
|
def unflatten(squares, *remaining_indexes)
|
26
64
|
return row(*squares) if remaining_indexes.length == 1
|
27
65
|
|
@@ -29,14 +67,14 @@ module FEEN
|
|
29
67
|
.each_slice(squares.length / remaining_indexes.fetch(0))
|
30
68
|
.to_a
|
31
69
|
.map { |sub_squares| unflatten(sub_squares, *remaining_indexes[1..]) }
|
32
|
-
.join(
|
70
|
+
.join("/" * remaining_indexes.length.pred)
|
33
71
|
end
|
34
72
|
|
35
73
|
def row(*squares)
|
36
74
|
squares
|
37
75
|
.map { |square| square.nil? ? 1 : square }
|
38
|
-
.join(
|
39
|
-
.gsub(/1,[1,]*1/) { |str| str.split(
|
76
|
+
.join(",")
|
77
|
+
.gsub(/1,[1,]*1/) { |str| str.split(",").length }
|
40
78
|
end
|
41
79
|
end
|
42
80
|
end
|
@@ -3,16 +3,23 @@
|
|
3
3
|
module FEEN
|
4
4
|
module Dumper
|
5
5
|
# The pieces in hand class.
|
6
|
+
#
|
7
|
+
# @example Serialize a list of pieces in hand grouped by sides
|
8
|
+
# PiecesInHand.dump(
|
9
|
+
# %w[S],
|
10
|
+
# %w[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]
|
11
|
+
# )
|
12
|
+
# # => "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"
|
6
13
|
class PiecesInHand
|
7
14
|
# Serialize pieces in hand lists into a string.
|
8
15
|
#
|
9
|
-
# @param
|
16
|
+
# @param pieces_in_hand_grouped_by_sides [Array] The list of pieces in hand
|
10
17
|
# grouped by players.
|
11
18
|
#
|
12
19
|
# @return [String] A string representing the pieces in hand of both
|
13
20
|
# players.
|
14
|
-
def self.dump(*
|
15
|
-
|
21
|
+
def self.dump(*pieces_in_hand_grouped_by_sides)
|
22
|
+
pieces_in_hand_grouped_by_sides.map { |pieces| new(*pieces).to_s }.join("/")
|
16
23
|
end
|
17
24
|
|
18
25
|
# @param pieces [Array] A list of pieces in hand.
|
@@ -22,7 +29,7 @@ module FEEN
|
|
22
29
|
|
23
30
|
# @return [String] A string representing the pieces in hand.
|
24
31
|
def to_s
|
25
|
-
@pieces.join(
|
32
|
+
@pieces.join(",")
|
26
33
|
end
|
27
34
|
end
|
28
35
|
end
|
data/lib/feen/dumper/turn.rb
CHANGED
@@ -2,14 +2,17 @@
|
|
2
2
|
|
3
3
|
module FEEN
|
4
4
|
module Dumper
|
5
|
-
# The turn
|
5
|
+
# The turn module.
|
6
6
|
module Turn
|
7
|
-
# @param
|
7
|
+
# @param active_side_id [Integer] The identifier of the active player.
|
8
8
|
# @param sides_count [Integer] The number of players.
|
9
9
|
#
|
10
|
+
# @example Dump the number that identify the player who have to play
|
11
|
+
# dump(0, 2) # => "0"
|
12
|
+
#
|
10
13
|
# @return [String] The number that identify the player who have to play.
|
11
|
-
def self.dump(
|
12
|
-
String(Integer(
|
14
|
+
def self.dump(active_side_id, sides_count)
|
15
|
+
String(Integer(active_side_id) % Integer(sides_count))
|
13
16
|
end
|
14
17
|
end
|
15
18
|
end
|
data/lib/feen/parser.rb
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative
|
4
|
-
require_relative
|
5
|
-
require_relative
|
6
|
-
require_relative
|
3
|
+
require_relative "parser/board"
|
4
|
+
require_relative "parser/pieces_in_hand"
|
5
|
+
require_relative "parser/shape"
|
6
|
+
require_relative "parser/turn"
|
7
7
|
|
8
8
|
module FEEN
|
9
9
|
# The parser module.
|
@@ -12,53 +12,42 @@ module FEEN
|
|
12
12
|
#
|
13
13
|
# @param feen [String] The FEEN string representing a position.
|
14
14
|
#
|
15
|
-
# @example Parse
|
16
|
-
# call("3,
|
15
|
+
# @example Parse a classic Tsume Shogi problem
|
16
|
+
# call("3,s,k,s,3/9/4,+P,4/9/7,+B,1/9/9/9/9 0 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")
|
17
17
|
# # => {
|
18
|
-
# #
|
19
|
-
# #
|
20
|
-
# #
|
21
|
-
# #
|
22
|
-
# #
|
23
|
-
# #
|
24
|
-
# #
|
25
|
-
# #
|
26
|
-
# #
|
27
|
-
# #
|
28
|
-
# #
|
29
|
-
# #
|
30
|
-
# # "bR", "bP", nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , "gP", "gR",
|
31
|
-
# # "bN", "bP", nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , "gP", "gN",
|
32
|
-
# # "bB", "bP", nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , "gP", "gB",
|
33
|
-
# # "bK", "bP", nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , "gP", "gQ",
|
34
|
-
# # "bQ", "bP", nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , "gP", "gK",
|
35
|
-
# # "bB", "bP", nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , "gP", "gB",
|
36
|
-
# # "bN", "bP", nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , "gP", "gN",
|
37
|
-
# # "bR", "bP", nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , "gP", "gR",
|
38
|
-
# # nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , nil ,
|
39
|
-
# # nil , nil , nil , "rP", "rP", "rP", "rP", "rP", "rP", "rP", "rP", nil , nil , nil ,
|
40
|
-
# # nil , nil , nil , "rR", "rN", "rB", "rQ", "rK", "rB", "rN", "rR", nil , nil , nil
|
18
|
+
# # "active_side_id": 0,
|
19
|
+
# # "board": {
|
20
|
+
# # "3": "s",
|
21
|
+
# # "4": "k" ,
|
22
|
+
# # "5": "s",
|
23
|
+
# # "22": "+P",
|
24
|
+
# # "43": "+B"
|
25
|
+
# # },
|
26
|
+
# # "indexes": [9, 9],
|
27
|
+
# # "pieces_in_hand_grouped_by_sides": [
|
28
|
+
# # %w[S],
|
29
|
+
# # %w[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]
|
41
30
|
# # ]
|
42
31
|
# # }
|
43
32
|
#
|
44
33
|
# @return [Hash] The position params representing the position.
|
45
34
|
def self.call(feen)
|
46
|
-
params(*feen.split(
|
35
|
+
params(*feen.split(" "))
|
47
36
|
end
|
48
37
|
|
49
38
|
# Parse the FEEN string's three fields and return the position params.
|
50
39
|
#
|
51
40
|
# @param board [String] The flatten board.
|
52
|
-
# @param
|
41
|
+
# @param active_side_id [String] The active side identifier.
|
53
42
|
# @param in_hand [String] The captured actors.
|
54
43
|
#
|
55
44
|
# @return [Hash] The position params representing the position.
|
56
|
-
private_class_method def self.params(board,
|
45
|
+
private_class_method def self.params(board, active_side_id, in_hand)
|
57
46
|
{
|
58
|
-
|
47
|
+
active_side_id: Turn.parse(active_side_id),
|
48
|
+
board: Board.new(board).to_h,
|
59
49
|
indexes: Shape.new(board).to_a,
|
60
|
-
|
61
|
-
squares: Board.new(board).to_a
|
50
|
+
pieces_in_hand_grouped_by_sides: PiecesInHand.parse(in_hand)
|
62
51
|
}
|
63
52
|
end
|
64
53
|
end
|
data/lib/feen/parser/board.rb
CHANGED
@@ -4,7 +4,7 @@ module FEEN
|
|
4
4
|
module Parser
|
5
5
|
# The board class.
|
6
6
|
#
|
7
|
-
# @example Parse a Shogi problem board
|
7
|
+
# @example Parse a Shogi problem board and return an array
|
8
8
|
# Board.new("3,s,k,s,3/9/4,+P,4/9/7,+B,1/9/9/9/9").to_a
|
9
9
|
# # => [
|
10
10
|
# # nil, nil, nil, "s", "k", "s", nil, nil, nil,
|
@@ -17,6 +17,16 @@ module FEEN
|
|
17
17
|
# # nil, nil, nil, nil, nil, nil, nil, nil, nil,
|
18
18
|
# # nil, nil, nil, nil, nil, nil, nil, nil, nil
|
19
19
|
# # ]
|
20
|
+
#
|
21
|
+
# @example Parse a Shogi problem board and return a hash
|
22
|
+
# Board.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
|
+
# # }
|
20
30
|
class Board
|
21
31
|
# @param board [String] The flatten board.
|
22
32
|
def initialize(board)
|
@@ -30,6 +40,17 @@ module FEEN
|
|
30
40
|
.flat_map { |str| row(str) }
|
31
41
|
end
|
32
42
|
|
43
|
+
# @return [Hash] The indexes 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.to_s.to_sym => v)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
33
54
|
private
|
34
55
|
|
35
56
|
def row(string)
|
@@ -2,11 +2,11 @@
|
|
2
2
|
|
3
3
|
module FEEN
|
4
4
|
module Parser
|
5
|
-
# The pieces in hand
|
5
|
+
# The pieces in hand module.
|
6
6
|
module PiecesInHand
|
7
7
|
# The list of pieces in hand grouped by players.
|
8
8
|
#
|
9
|
-
# @param
|
9
|
+
# @param pieces_in_hand_grouped_by_sides_str [String] The serialized list of
|
10
10
|
# pieces in hand grouped by players.
|
11
11
|
#
|
12
12
|
# @example Parse a list of serialized pieces in hand
|
@@ -17,10 +17,10 @@ module FEEN
|
|
17
17
|
# # ]
|
18
18
|
#
|
19
19
|
# @return [Array] The list of pieces in hand grouped by players.
|
20
|
-
def self.parse(
|
21
|
-
|
22
|
-
.split(
|
23
|
-
.map { |pieces_in_hand_str| pieces_in_hand_str.split(
|
20
|
+
def self.parse(pieces_in_hand_grouped_by_sides_str)
|
21
|
+
pieces_in_hand_grouped_by_sides_str
|
22
|
+
.split("/", -1)
|
23
|
+
.map { |pieces_in_hand_str| pieces_in_hand_str.split(",") }
|
24
24
|
end
|
25
25
|
end
|
26
26
|
end
|
data/lib/feen/parser/shape.rb
CHANGED
@@ -21,7 +21,7 @@ module FEEN
|
|
21
21
|
|
22
22
|
def indexes(string, separator)
|
23
23
|
if separator.empty?
|
24
|
-
last_index = string.split(
|
24
|
+
last_index = string.split(",").inject(0) do |counter, sub_string|
|
25
25
|
number = sub_string.match?(/[0-9]+/) ? Integer(sub_string) : 1
|
26
26
|
counter + number
|
27
27
|
end
|
data/lib/feen/parser/turn.rb
CHANGED
metadata
CHANGED
@@ -1,15 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: feen
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Cyril Kato
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-08-
|
11
|
+
date: 2020-08-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: brutal
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
13
27
|
- !ruby/object:Gem::Dependency
|
14
28
|
name: bundler
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -119,7 +133,6 @@ files:
|
|
119
133
|
- lib/feen.rb
|
120
134
|
- lib/feen/dumper.rb
|
121
135
|
- lib/feen/dumper/board.rb
|
122
|
-
- lib/feen/dumper/inconsistent_size_error.rb
|
123
136
|
- lib/feen/dumper/pieces_in_hand.rb
|
124
137
|
- lib/feen/dumper/turn.rb
|
125
138
|
- lib/feen/parser.rb
|