feen 1.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.
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'dumper/board'
4
+ require_relative 'dumper/pieces_in_hand'
5
+ require_relative 'dumper/turn'
6
+
7
+ module FEEN
8
+ # The dumper module.
9
+ module Dumper
10
+ # Dump position params into a FEEN string.
11
+ #
12
+ # @param active_side [Integer] The identifier of the player who must play.
13
+ # @param indexes [Array] The shape of the board.
14
+ # @param pieces_in_hand_by_players [Array] The list of pieces in hand
15
+ # grouped by players.
16
+ # @param squares [Array] The list of squares on the board.
17
+ #
18
+ # @example Dump Four-player chess's starting position
19
+ # call(
20
+ # active_side: 0,
21
+ # indexes: [14, 14],
22
+ # pieces_in_hand_by_players: [
23
+ # [],
24
+ # [],
25
+ # [],
26
+ # []
27
+ # ],
28
+ # squares: [
29
+ # nil , nil , nil , "yR", "yN", "yB", "yK", "yQ", "yB", "yN", "yR", nil , nil , nil ,
30
+ # nil , nil , nil , "yP", "yP", "yP", "yP", "yP", "yP", "yP", "yP", nil , nil , nil ,
31
+ # nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , nil ,
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
43
+ # ]
44
+ # )
45
+ # # => "3,yR,yN,yB,yK,yQ,yB,yN,yR,3/3,yP,yP,yP,yP,yP,yP,yP,yP,3/14/bR,bP,10,gP,gR/bN,bP,10,gP,gN/bB,bP,10,gP,gB/bK,bP,10,gP,gQ/bQ,bP,10,gP,gK/bB,bP,10,gP,gB/bN,bP,10,gP,gN/bR,bP,10,gP,gR/14/3,rP,rP,rP,rP,rP,rP,rP,rP,3/3,rR,rN,rB,rQ,rK,rB,rN,rR,3 0 ///"
46
+ #
47
+ # @return [String] The FEEN string representing the position.
48
+ def self.call(active_side:, indexes:, pieces_in_hand_by_players:, squares:)
49
+ [
50
+ Board.new(*indexes).to_s(*squares),
51
+ Turn.dump(active_side, pieces_in_hand_by_players.length),
52
+ PiecesInHand.dump(*pieces_in_hand_by_players)
53
+ ].join(' ')
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'inconsistent_size_error'
4
+
5
+ module FEEN
6
+ module Dumper
7
+ # The board class.
8
+ class Board
9
+ # @param indexes [Array] The shape of the board.
10
+ def initialize(*indexes)
11
+ @indexes = indexes
12
+ end
13
+
14
+ # @param squares [Array] The list of squares on the board.
15
+ #
16
+ # @return [String] The string representing the board.
17
+ def to_s(*squares)
18
+ raise InconsistentSizeError unless squares.length == @indexes.inject(:*)
19
+
20
+ unflatten(squares, *@indexes)
21
+ end
22
+
23
+ private
24
+
25
+ def unflatten(squares, *remaining_indexes)
26
+ return row(*squares) if remaining_indexes.length == 1
27
+
28
+ squares
29
+ .each_slice(squares.length / remaining_indexes.fetch(0))
30
+ .to_a
31
+ .map { |sub_squares| unflatten(sub_squares, *remaining_indexes[1..]) }
32
+ .join('/' * remaining_indexes.length.pred)
33
+ end
34
+
35
+ def row(*squares)
36
+ squares
37
+ .map { |square| square.nil? ? 1 : square }
38
+ .join(',')
39
+ .gsub(/1,[1,]*1/) { |str| str.split(',').length }
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module FEEN
4
+ module Dumper
5
+ # The inconsistent size error class.
6
+ class InconsistentSizeError < ::StandardError; end
7
+ end
8
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module FEEN
4
+ module Dumper
5
+ # The pieces in hand class.
6
+ class PiecesInHand
7
+ # Serialize pieces in hand lists into a string.
8
+ #
9
+ # @param pieces_in_hand_by_players [Array] The list of pieces in hand
10
+ # grouped by players.
11
+ #
12
+ # @return [String] A string representing the pieces in hand of both
13
+ # players.
14
+ def self.dump(*pieces_in_hand_by_players)
15
+ pieces_in_hand_by_players.map { |pieces| new(*pieces).to_s }.join('/')
16
+ end
17
+
18
+ # @param pieces [Array] A list of pieces in hand.
19
+ def initialize(*pieces)
20
+ @pieces = pieces.sort
21
+ end
22
+
23
+ # @return [String] A string representing the pieces in hand.
24
+ def to_s
25
+ @pieces.join(',')
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module FEEN
4
+ module Dumper
5
+ # The turn class.
6
+ module Turn
7
+ # @param active_side [Integer] The identifier of the active player.
8
+ # @param sides_count [Integer] The number of players.
9
+ #
10
+ # @return [String] The number that identify the player who have to play.
11
+ def self.dump(active_side, sides_count)
12
+ String(Integer(active_side) % Integer(sides_count))
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'parser/board'
4
+ require_relative 'parser/pieces_in_hand'
5
+ require_relative 'parser/shape'
6
+ require_relative 'parser/turn'
7
+
8
+ module FEEN
9
+ # The parser module.
10
+ module Parser
11
+ # Parse a FEEN string into position params.
12
+ #
13
+ # @param feen [String] The FEEN string representing a position.
14
+ #
15
+ # @example Parse Four-player chess's starting position
16
+ # call("3,yR,yN,yB,yK,yQ,yB,yN,yR,3/3,yP,yP,yP,yP,yP,yP,yP,yP,3/14/bR,bP,10,gP,gR/bN,bP,10,gP,gN/bB,bP,10,gP,gB/bK,bP,10,gP,gQ/bQ,bP,10,gP,gK/bB,bP,10,gP,gB/bN,bP,10,gP,gN/bR,bP,10,gP,gR/14/3,rP,rP,rP,rP,rP,rP,rP,rP,3/3,rR,rN,rB,rQ,rK,rB,rN,rR,3 0 ///")
17
+ # # => {
18
+ # # active_side: 0,
19
+ # # indexes: [14, 14],
20
+ # # pieces_in_hand_by_players: [
21
+ # # [],
22
+ # # [],
23
+ # # [],
24
+ # # []
25
+ # # ],
26
+ # # squares: [
27
+ # # nil , nil , nil , "yR", "yN", "yB", "yK", "yQ", "yB", "yN", "yR", nil , nil , nil ,
28
+ # # nil , nil , nil , "yP", "yP", "yP", "yP", "yP", "yP", "yP", "yP", nil , nil , nil ,
29
+ # # nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , nil , nil ,
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
41
+ # # ]
42
+ # # }
43
+ #
44
+ # @return [Hash] The position params representing the position.
45
+ def self.call(feen)
46
+ params(*feen.split(' '))
47
+ end
48
+
49
+ # Parse the FEEN string's three fields and return the position params.
50
+ #
51
+ # @param board [String] The flatten board.
52
+ # @param active_side [String] The active side identifier.
53
+ # @param in_hand [String] The captured actors.
54
+ #
55
+ # @return [Hash] The position params representing the position.
56
+ private_class_method def self.params(board, active_side, in_hand)
57
+ {
58
+ active_side: Turn.parse(active_side),
59
+ indexes: Shape.new(board).to_a,
60
+ pieces_in_hand_by_players: PiecesInHand.parse(in_hand),
61
+ squares: Board.new(board).to_a
62
+ }
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module FEEN
4
+ module Parser
5
+ # The board class.
6
+ #
7
+ # @example Parse a Shogi problem board
8
+ # Board.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
+ class Board
21
+ # @param board [String] The flatten board.
22
+ def initialize(board)
23
+ @board = board
24
+ end
25
+
26
+ # @return [Array] The list of squares on the board.
27
+ def to_a
28
+ @board
29
+ .split(%r{[/,]+})
30
+ .flat_map { |str| row(str) }
31
+ end
32
+
33
+ private
34
+
35
+ def row(string)
36
+ string.match?(/[0-9]+/) ? ::Array.new(Integer(string)) : string
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module FEEN
4
+ module Parser
5
+ # The pieces in hand class.
6
+ module PiecesInHand
7
+ # The list of pieces in hand grouped by players.
8
+ #
9
+ # @param pieces_in_hand_by_players_str [String] The serialized list of
10
+ # pieces in hand grouped by players.
11
+ #
12
+ # @example Parse a list of serialized pieces in hand
13
+ # parse("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
+ # # %w[S],
16
+ # # %w[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
+ # # ]
18
+ #
19
+ # @return [Array] The list of pieces in hand grouped by players.
20
+ def self.parse(pieces_in_hand_by_players_str)
21
+ pieces_in_hand_by_players_str
22
+ .split('/', -1)
23
+ .map { |pieces_in_hand_str| pieces_in_hand_str.split(',') }
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module FEEN
4
+ module Parser
5
+ # The shape class.
6
+ #
7
+ # @example Parse the shape of a shogiban
8
+ # Shape.new("3,s,k,s,3/9/4,+P,4/9/7,+B,1/9/9/9/9").to_a # => [9, 9]
9
+ class Shape
10
+ # @param board [String] The flatten board.
11
+ def initialize(board)
12
+ @board = board
13
+ end
14
+
15
+ # @return [Array] The size of each dimension of the board.
16
+ def to_a
17
+ indexes(@board, @board.scan(%r{/+}).sort.fetch(-1))
18
+ end
19
+
20
+ private
21
+
22
+ def indexes(string, separator)
23
+ if separator.empty?
24
+ last_index = string.split(',').inject(0) do |counter, sub_string|
25
+ number = sub_string.match?(/[0-9]+/) ? Integer(sub_string) : 1
26
+ counter + number
27
+ end
28
+
29
+ return [last_index]
30
+ end
31
+
32
+ sub_strings = string.split(separator)
33
+ [sub_strings.length] + indexes(sub_strings.fetch(0), separator[1..])
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module FEEN
4
+ module Parser
5
+ # The turn class.
6
+ module Turn
7
+ # @param active_side_id [String] The identifier of bottom-side and
8
+ # top-side.
9
+ #
10
+ # @example Parse the number that identify the player who have to play
11
+ # parse("0") # => 0
12
+ #
13
+ # @return [Integer] The number that identify the player who have to play.
14
+ def self.parse(active_side_id)
15
+ Integer(active_side_id)
16
+ end
17
+ end
18
+ end
19
+ end
metadata ADDED
@@ -0,0 +1,156 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: feen
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Cyril Kato
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2020-08-16 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
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'
27
+ - !ruby/object:Gem::Dependency
28
+ name: byebug
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rubocop-performance
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rubocop-thread_safety
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: simplecov
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: yard
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ description: A Ruby interface for data serialization and deserialization in FEEN format.
112
+ email: contact@cyril.email
113
+ executables: []
114
+ extensions: []
115
+ extra_rdoc_files: []
116
+ files:
117
+ - LICENSE.md
118
+ - README.md
119
+ - lib/feen.rb
120
+ - lib/feen/dumper.rb
121
+ - lib/feen/dumper/board.rb
122
+ - lib/feen/dumper/inconsistent_size_error.rb
123
+ - lib/feen/dumper/pieces_in_hand.rb
124
+ - lib/feen/dumper/turn.rb
125
+ - lib/feen/parser.rb
126
+ - lib/feen/parser/board.rb
127
+ - lib/feen/parser/pieces_in_hand.rb
128
+ - lib/feen/parser/shape.rb
129
+ - lib/feen/parser/turn.rb
130
+ homepage: https://developer.sashite.com/specs/forsyth-edwards-expanded-notation
131
+ licenses:
132
+ - MIT
133
+ metadata:
134
+ bug_tracker_uri: https://github.com/sashite/feen.rb/issues
135
+ documentation_uri: https://rubydoc.info/gems/feen/index
136
+ source_code_uri: https://github.com/sashite/feen.rb
137
+ post_install_message:
138
+ rdoc_options: []
139
+ require_paths:
140
+ - lib
141
+ required_ruby_version: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ required_rubygems_version: !ruby/object:Gem::Requirement
147
+ requirements:
148
+ - - ">="
149
+ - !ruby/object:Gem::Version
150
+ version: '0'
151
+ requirements: []
152
+ rubygems_version: 3.1.2
153
+ signing_key:
154
+ specification_version: 4
155
+ summary: FEEN support for the Ruby language.
156
+ test_files: []