qi 6.1.0 → 7.0.0

Sign up to get free protection for your applications and to get access to all the features.
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