qi 6.1.0 → 7.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.
Files changed (5) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +22 -124
  3. data/lib/qi.rb +80 -3
  4. metadata +4 -5
  5. data/lib/qi/position.rb +0 -114
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cee3167f3c32021191d7cb31829437f5d33c186314d8ebdeb6ce271ca25f9651
4
- data.tar.gz: 2802d9e46a694d78d476251e2a194a2de725fc984e5181ff48a9c6cd9512a0e9
3
+ metadata.gz: 18ca7bb7fca15a8ce854e8a205160b05a14127970039a904c3ce8ad6f8baf8fc
4
+ data.tar.gz: fc4b915c93488f39835f762b1f89fb44a74ab25ca3faa47fb4a6ea846db66c77
5
5
  SHA512:
6
- metadata.gz: 34dd51e3c9179833ccebcbee0a99df78144e4a6867b30f2d9b404b166bc6f5fb039ee922b939056e2f07b80588271e94e627765622cc0e65434c1fefd402a120
7
- data.tar.gz: bfbe4d8074dcf3c5a196c257f1126e29dcde90bbd0af4221a67a58cd94e93893cccd065ec058c5d3016c2fa5f39946d074f203532e9c13294459195544821b85
6
+ metadata.gz: 6079a5cb3f9b5b83a5bbc0db41694e6037a46a8e5ef6ce8bd4e65f36863d17008cb5d08d806681c4b17daff89ef2d43abd4e260c4ba068b7ac499fe51a36053c
7
+ data.tar.gz: 164bb2211d24d9119061a9c771438d0b77f96c0e2d51d4f18c3cfe3f82acdeaf50bd532d4eafb071441a64a18be68cefc9bae9a6dcfcf81b633cf3498912e76c
data/README.md CHANGED
@@ -1,18 +1,18 @@
1
- # <span lang="zh"><ruby>棋<rt>Qi</rt></ruby></span>.rb
1
+ # Qi.rb
2
2
 
3
3
  [![Build Status](https://travis-ci.org/sashite/qi.rb.svg?branch=master)](https://travis-ci.org/sashite/qi.rb)
4
4
  [![Gem Version](https://badge.fury.io/rb/qi.svg)][gem]
5
5
  [![Inline docs](https://inch-ci.org/github/sashite/qi.rb.svg?branch=master)][inchpages]
6
6
  [![Documentation](https://img.shields.io/:yard-docs-38c800.svg)][rubydoc]
7
7
 
8
- > Instantiate [Portable Chess Notation](https://developer.sashite.com/specs/portable-chess-notation)'s positions and apply [Portable Move Notation](https://developer.sashite.com/specs/portable-move-notation)'s moves.
8
+ > `Qi` () is an abstraction for updating positions of chess variants (including Chess, Janggi, Markruk, Shogi, Xiangqi), with a move.
9
9
 
10
10
  ## Installation
11
11
 
12
12
  Add this line to your application's Gemfile:
13
13
 
14
14
  ```ruby
15
- gem 'qi'
15
+ gem "qi"
16
16
  ```
17
17
 
18
18
  And then execute:
@@ -25,136 +25,34 @@ Or install it yourself as:
25
25
 
26
26
  ## Examples
27
27
 
28
- Let's replay [The Shortest Possible Game of Shogi](https://userpages.monmouth.com/~colonel/shortshogi.html):
29
-
30
- ```ruby
31
- require 'qi'
32
-
33
- starting_position = Qi::Position.new(
34
- 'l', 'n', 's', 'g', 'k', 'g', 's', 'n', 'l',
35
- nil, 'r', nil, nil, nil, nil, nil, 'b', nil,
36
- 'p', 'p', 'p', 'p', 'p', 'p', 'p', 'p', 'p',
37
- nil, nil, nil, nil, nil, nil, nil, nil, nil,
38
- nil, nil, nil, nil, nil, nil, nil, nil, nil,
39
- nil, nil, nil, nil, nil, nil, nil, nil, nil,
40
- 'P', 'P', 'P', 'P', 'P', 'P', 'P', 'P', 'P',
41
- nil, 'B', nil, nil, nil, nil, nil, 'R', nil,
42
- 'L', 'N', 'S', 'G', 'K', 'G', 'S', 'N', 'L'
43
- )
44
-
45
- starting_position.topside_in_hand_pieces # => []
46
- starting_position.squares # => ["l", "n", "s", "g", "k", "g", "s", "n", "l",
47
- # nil, "r", nil, nil, nil, nil, nil, "b", nil,
48
- # "p", "p", "p", "p", "p", "p", "p", "p", "p",
49
- # nil, nil, nil, nil, nil, nil, nil, nil, nil,
50
- # nil, nil, nil, nil, nil, nil, nil, nil, nil,
51
- # nil, nil, nil, nil, nil, nil, nil, nil, nil,
52
- # "P", "P", "P", "P", "P", "P", "P", "P", "P",
53
- # nil, "B", nil, nil, nil, nil, nil, "R", nil,
54
- # "L", "N", "S", "G", "K", "G", "S", "N", "L"]
55
- starting_position.bottomside_in_hand_pieces # => []
56
- starting_position.in_hand_pieces # => []
57
- starting_position.turn_to_topside? # => false
58
-
59
- moves = [
60
- [ 56, 47, 'P' ],
61
- [ 3, 11, 'g' ],
62
- [ 64, 24, '+B', 'P' ],
63
- [ 5, 14, 'g' ],
64
- [ 24, 14, '+B', 'G' ],
65
- [ 4, 3, 'k' ],
66
- [ nil, 13, 'G' ]
67
- ]
68
-
69
- last_position = moves.reduce(starting_position) do |position, move|
70
- position.call(move)
71
- end
72
-
73
- last_position.topside_in_hand_pieces # => []
74
- last_position.squares # => ["l", "n", "s", "k", nil, nil, "s", "n", "l",
75
- # nil, "r", "g", nil, "G", "+B", nil, "b", nil,
76
- # "p", "p", "p", "p", "p", "p", nil, "p", "p",
77
- # nil, nil, nil, nil, nil, nil, nil, nil, nil,
78
- # nil, nil, nil, nil, nil, nil, nil, nil, nil,
79
- # nil, nil, "P", nil, nil, nil, nil, nil, nil,
80
- # "P", "P", nil, "P", "P", "P", "P", "P", "P",
81
- # nil, nil, nil, nil, nil, nil, nil, "R", nil,
82
- # "L", "N", "S", "G", "K", "G", "S", "N", "L"]
83
- last_position.bottomside_in_hand_pieces # => ["P"]
84
- last_position.in_hand_pieces # => []
85
- last_position.turn_to_topside? # => true
86
- ```
87
-
88
- Another example with Xiangqi's Short Double Cannons Checkmate:
89
-
90
28
  ```ruby
91
- require 'qi'
92
-
93
- starting_position = Qi::Position.new(
94
- '車', '馬', '象', '士', '將', '士', '象', '馬', '車',
95
- nil, nil, nil, nil, nil, nil, nil, nil, nil,
96
- nil, '砲', nil, nil, nil, nil, nil, '砲', nil,
97
- '卒', nil, '卒', nil, '卒', nil, '卒', nil, '卒',
98
- nil, nil, nil, nil, nil, nil, nil, nil, nil,
99
- nil, nil, nil, nil, nil, nil, nil, nil, nil,
100
- '兵', nil, '兵', nil, '兵', nil, '兵', nil, '兵',
101
- nil, '炮', nil, nil, nil, nil, nil, '炮', nil,
102
- nil, nil, nil, nil, nil, nil, nil, nil, nil,
103
- '俥', '傌', '相', '仕', '帥', '仕', '相', '傌', '俥'
29
+ require "qi"
30
+
31
+ Qi.call(
32
+ [43, 13, "+B"],
33
+ "side_id": 0,
34
+ "board": {
35
+ 3 => "s",
36
+ 4 => "k",
37
+ 5 => "s",
38
+ 22 => "+P",
39
+ 43 => "+B"
40
+ },
41
+ "hands": [
42
+ %w[S],
43
+ %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]
44
+ ]
104
45
  )
105
-
106
- starting_position.topside_in_hand_pieces # => []
107
- starting_position.squares # => ["車", "馬", "象", "士", "將", "士", "象", "馬", "車",
108
- # nil, nil, nil, nil, nil, nil, nil, nil, nil,
109
- # nil, "砲", nil, nil, nil, nil, nil, "砲", nil,
110
- # "卒", nil, "卒", nil, "卒", nil, "卒", nil, "卒",
111
- # nil, nil, nil, nil, nil, nil, nil, nil, nil,
112
- # nil, nil, nil, nil, nil, nil, nil, nil, nil,
113
- # "兵", nil, "兵", nil, "兵", nil, "兵", nil, "兵",
114
- # nil, "炮", nil, nil, nil, nil, nil, "炮", nil,
115
- # nil, nil, nil, nil, nil, nil, nil, nil, nil,
116
- # "俥", "傌", "相", "仕", "帥", "仕", "相", "傌", "俥"]
117
- starting_position.bottomside_in_hand_pieces # => []
118
- starting_position.in_hand_pieces # => []
119
- starting_position.turn_to_topside? # => false
120
-
121
- moves = [
122
- [ 64, 67, '炮' ],
123
- [ 25, 22, '砲' ],
124
- [ 70, 52, '炮' ],
125
- [ 19, 55, '砲' ],
126
- [ 67, 31, '炮' ],
127
- [ 22, 58, '砲' ],
128
- [ 52, 49, '炮' ]
129
- ]
130
-
131
- last_position = moves.reduce(starting_position) do |position, move|
132
- position.call(move)
133
- end
134
-
135
- last_position.topside_in_hand_pieces # => []
136
- last_position.squares # => ["車", "馬", "象", "士", "將", "士", "象", "馬", "車",
137
- # nil, nil, nil, nil, nil, nil, nil, nil, nil,
138
- # nil, nil, nil, nil, nil, nil, nil, nil, nil,
139
- # "卒", nil, "卒", nil, "炮", nil, "卒", nil, "卒",
140
- # nil, nil, nil, nil, nil, nil, nil, nil, nil,
141
- # nil, nil, nil, nil, "炮", nil, nil, nil, nil,
142
- # "兵", "砲", "兵", nil, "砲", nil, "兵", nil, "兵",
143
- # nil, nil, nil, nil, nil, nil, nil, nil, nil,
144
- # nil, nil, nil, nil, nil, nil, nil, nil, nil,
145
- # "俥", "傌", "相", "仕", "帥", "仕", "相", "傌", "俥"]
146
- last_position.bottomside_in_hand_pieces # => []
147
- last_position.in_hand_pieces # => []
148
- last_position.turn_to_topside? # => true
46
+ # => {:side_id=>1, :board=>{3=>"s", 4=>"k", 5=>"s", 22=>"+P", 13=>"+B"}, :hands=>[["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"]]}
149
47
  ```
150
48
 
151
49
  ## License
152
50
 
153
- The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
51
+ The code is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
154
52
 
155
53
  ## About Sashite
156
54
 
157
- The `qi` gem is maintained by [Sashite](https://sashite.com/).
55
+ This [gem](https://rubygems.org/gems/qi) is maintained by [Sashite](https://sashite.com/).
158
56
 
159
57
  With some [lines of code](https://github.com/sashite/), let's share the beauty of Chinese, Japanese and Western cultures through the game of chess!
160
58
 
data/lib/qi.rb CHANGED
@@ -1,7 +1,84 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # The Qi module.
3
+ # The Qi abstraction.
4
+ #
5
+ # @example
6
+ # Qi.call(
7
+ # [43, 13, "+B"],
8
+ # "side_id": 0,
9
+ # "board": {
10
+ # 3 => "s",
11
+ # 4 => "k",
12
+ # 5 => "s",
13
+ # 22 => "+P",
14
+ # 43 => "+B"
15
+ # },
16
+ # "hands": [
17
+ # %w[S],
18
+ # %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]
19
+ # ]
20
+ # )
21
+ # # => {:side_id=>1, :board=>{3=>"s", 4=>"k", 5=>"s", 22=>"+P", 13=>"+B"}, :hands=>[["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"]]}
4
22
  module Qi
5
- end
23
+ # Apply a move to the position.
24
+ #
25
+ # @param move [Array] The move to play.
26
+ # @param side_id [Integer] The identifier of the player who must play.
27
+ # @param board [Hash] The indexes of each piece on the board.
28
+ # @param hands [Array] The list of pieces in hand grouped by players.
29
+ #
30
+ # @see https://developer.sashite.com/specs/portable-chess-notation
31
+ # @see https://developer.sashite.com/specs/portable-move-notation
32
+ #
33
+ # @example
34
+ # call(
35
+ # [43, 13, "+B"],
36
+ # "side_id": 0,
37
+ # "board": {
38
+ # 3 => "s",
39
+ # 4 => "k",
40
+ # 5 => "s",
41
+ # 22 => "+P",
42
+ # 43 => "+B"
43
+ # },
44
+ # "hands": [
45
+ # %w[S],
46
+ # %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]
47
+ # ]
48
+ # )
49
+ # # => {:side_id=>1, :board=>{3=>"s", 4=>"k", 5=>"s", 22=>"+P", 13=>"+B"}, :hands=>[["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"]]}
50
+ #
51
+ # @return [Hash] The next position.
52
+ def self.call(move, side_id:, board:, hands:)
53
+ updated_board = board.dup
54
+ updated_in_hand_pieces = hands.fetch(side_id).dup
55
+
56
+ actions = move.each_slice(4)
57
+
58
+ actions.each do |action|
59
+ src_square_id = action.fetch(0)
60
+ dst_square_id = action.fetch(1)
61
+ moved_piece_name = action.fetch(2)
62
+ captured_piece_name = action.fetch(3, nil)
63
+
64
+ if src_square_id.nil?
65
+ piece_in_hand_id = updated_in_hand_pieces.index(moved_piece_name)
66
+ updated_in_hand_pieces.delete_at(piece_in_hand_id) unless piece_in_hand_id.nil?
67
+ else
68
+ updated_board.delete(src_square_id)
69
+ end
6
70
 
7
- require_relative 'qi/position'
71
+ updated_board[dst_square_id] = moved_piece_name
72
+ updated_in_hand_pieces.push(captured_piece_name) unless captured_piece_name.nil?
73
+ end
74
+
75
+ updated_hands = hands.dup
76
+ updated_hands[side_id] = updated_in_hand_pieces
77
+
78
+ {
79
+ side_id: side_id.succ % hands.length,
80
+ board: updated_board,
81
+ hands: updated_hands
82
+ }
83
+ end
84
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: qi
3
3
  version: !ruby/object:Gem::Version
4
- version: 6.1.0
4
+ version: 7.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-07-26 00:00:00.000000000 Z
11
+ date: 2020-09-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: brutal
@@ -117,7 +117,6 @@ files:
117
117
  - LICENSE.md
118
118
  - README.md
119
119
  - lib/qi.rb
120
- - lib/qi/position.rb
121
120
  homepage: https://developer.sashite.com/specs/
122
121
  licenses:
123
122
  - MIT
@@ -133,7 +132,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
133
132
  requirements:
134
133
  - - ">="
135
134
  - !ruby/object:Gem::Version
136
- version: '0'
135
+ version: 2.7.0
137
136
  required_rubygems_version: !ruby/object:Gem::Requirement
138
137
  requirements:
139
138
  - - ">="
@@ -143,5 +142,5 @@ requirements: []
143
142
  rubygems_version: 3.1.2
144
143
  signing_key:
145
144
  specification_version: 4
146
- summary: Represent positions and play moves.
145
+ summary: Update positions with a move.
147
146
  test_files: []
@@ -1,114 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Qi
4
- # The position class.
5
- #
6
- # @see https://developer.sashite.com/specs/portable-chess-notation
7
- class Position
8
- # The list of squares of on the board.
9
- #
10
- # @!attribute [r] squares
11
- # @return [Array] The list of squares.
12
- attr_reader :squares
13
-
14
- # The list of pieces in hand owned by the bottomside player.
15
- #
16
- # @!attribute [r] bottomside_in_hand_pieces
17
- # @return [Array] The list of bottomside's pieces in hand.
18
- attr_reader :bottomside_in_hand_pieces
19
-
20
- # The list of pieces in hand owned by the topside player.
21
- #
22
- # @!attribute [r] topside_in_hand_pieces
23
- # @return [Array] The list of topside's pieces in hand.
24
- attr_reader :topside_in_hand_pieces
25
-
26
- # Initialize a position.
27
- #
28
- # @param squares [Array] The list of squares of on the board.
29
- # @param is_turn_to_topside [Boolean] The player who must play.
30
- # @param bottomside_in_hand_pieces [Array] The list of bottom-side's pieces in hand.
31
- # @param topside_in_hand_pieces [Array] The list of top-side's pieces in hand.
32
- #
33
- # @example The Shogi's starting position
34
- # Position.new(
35
- # 'l', 'n', 's', 'g', 'k', 'g', 's', 'n', 'l',
36
- # nil, 'r', nil, nil, nil, nil, nil, 'b', nil,
37
- # 'p', 'p', 'p', 'p', 'p', 'p', 'p', 'p', 'p',
38
- # nil, nil, nil, nil, nil, nil, nil, nil, nil,
39
- # nil, nil, nil, nil, nil, nil, nil, nil, nil,
40
- # nil, nil, nil, nil, nil, nil, nil, nil, nil,
41
- # 'P', 'P', 'P', 'P', 'P', 'P', 'P', 'P', 'P',
42
- # nil, 'B', nil, nil, nil, nil, nil, 'R', nil,
43
- # 'L', 'N', 'S', 'G', 'K', 'G', 'S', 'N', 'L'
44
- # )
45
- def initialize(*squares, is_turn_to_topside: false, bottomside_in_hand_pieces: [], topside_in_hand_pieces: [])
46
- @squares = squares
47
- @is_turn_to_topside = is_turn_to_topside
48
- @bottomside_in_hand_pieces = bottomside_in_hand_pieces
49
- @topside_in_hand_pieces = topside_in_hand_pieces
50
-
51
- freeze
52
- end
53
-
54
- # Apply a move in PMN (Portable Move Notation) format.
55
- #
56
- # @see https://developer.sashite.com/specs/portable-move-notation
57
- # @return [Position] The new position.
58
- def call(move)
59
- updated_squares = squares.dup
60
- updated_bottomside_in_hand_pieces = bottomside_in_hand_pieces.dup
61
- updated_topside_in_hand_pieces = topside_in_hand_pieces.dup
62
-
63
- actions = move.each_slice(4)
64
-
65
- actions.each do |action|
66
- src_square_id = action.fetch(0)
67
- dst_square_id = action.fetch(1)
68
- moved_piece_name = action.fetch(2)
69
- captured_piece_name = action.fetch(3, nil)
70
-
71
- if src_square_id.nil?
72
- if turn_to_topside?
73
- piece_in_hand_id = updated_topside_in_hand_pieces.index(moved_piece_name)
74
- updated_topside_in_hand_pieces.delete_at(piece_in_hand_id)
75
- else
76
- piece_in_hand_id = updated_bottomside_in_hand_pieces.index(moved_piece_name)
77
- updated_bottomside_in_hand_pieces.delete_at(piece_in_hand_id)
78
- end
79
- else
80
- updated_squares[src_square_id] = nil
81
- end
82
-
83
- updated_squares[dst_square_id] = moved_piece_name
84
-
85
- unless captured_piece_name.nil?
86
- if turn_to_topside?
87
- updated_topside_in_hand_pieces.push(captured_piece_name)
88
- else
89
- updated_bottomside_in_hand_pieces.push(captured_piece_name)
90
- end
91
- end
92
- end
93
-
94
- self.class.new(*updated_squares, is_turn_to_topside: !turn_to_topside?,
95
- bottomside_in_hand_pieces: updated_bottomside_in_hand_pieces,
96
- topside_in_hand_pieces: updated_topside_in_hand_pieces)
97
- end
98
-
99
- # The list of pieces in hand owned by the current player.
100
- #
101
- # @return [Array] Topside's pieces in hand if turn to topside, bottomside's
102
- # ones otherwise.
103
- def in_hand_pieces
104
- turn_to_topside? ? topside_in_hand_pieces : bottomside_in_hand_pieces
105
- end
106
-
107
- # The side who must play.
108
- #
109
- # @return [Boolean] True if it is turn to topside, false otherwise.
110
- def turn_to_topside?
111
- @is_turn_to_topside
112
- end
113
- end
114
- end