11# xiangting
22
33[ ![ Crate] ( https://img.shields.io/crates/v/xiangting.svg )] ( https://crates.io/crates/xiangting )
4+ [ ![ Minimum Supported Rust Version] ( https://img.shields.io/crates/msrv/xiangting )] ( https://crates.io/crates/xiangting )
45[ ![ API] ( https://img.shields.io/badge/api-main-yellow.svg )] ( https://apricot-s.github.io/xiangting/xiangting )
56[ ![ API] ( https://docs.rs/xiangting/badge.svg )] ( https://docs.rs/xiangting )
7+ [ ![ Ask DeepWiki] ( https://deepwiki.com/badge.svg )] ( https://deepwiki.com/Apricot-S/xiangting )
68
79A library for calculating the deficiency number (a.k.a. xiangting number, 向聴数).
810
9- This library is based on the algorithm in [ Cryolite's Nyanten] ( https://github.com/Cryolite/nyanten ) .
10- However, it introduces the following additional features:
11-
12- - Supports rules that include and exclude melded tiles when determining if a hand contains four identical tiles.
13- - Supports three-player mahjong.
14-
1511Documentation:
1612
1713- [ API reference (main branch)] ( https://Apricot-S.github.io/xiangting/xiangting )
@@ -25,6 +21,10 @@ Documentation:
2521- [ Theoretical Background of Nyanten (Efficient Computation of Shanten/Deficiency Numbers) #麻雀 - Qiita] ( https://qiita.com/Cryolite/items/75d504c7489426806b87 )
2622- [ A Fast and Space-Efficient Algorithm for Calculating Deficient Numbers (a.k.a. Shanten Numbers).pdf] ( https://www.slideshare.net/slideshow/a-fast-and-space-efficient-algorithm-for-calculating-deficient-numbers-a-k-a-shanten-numbers-pdf/269706674 )
2723
24+ ## Language Bindings
25+
26+ - Python: [ xiangting-py] ( https://github.com/Apricot-S/xiangting-py )
27+
2828## Installation
2929
3030``` sh
@@ -57,7 +57,7 @@ The correspondence between the index and the tile is shown in the table below.
5757Calculates the replacement number, which is equal to the deficiency number (a.k.a. xiangting number, 向聴数) + 1.
5858
5959``` rust
60- use xiangting :: calculate_replacement_number;
60+ use xiangting :: { PlayerCount , calculate_replacement_number} ;
6161
6262fn main () {
6363 // 123m456p789s11222z
@@ -68,54 +68,55 @@ fn main() {
6868 2 , 3 , 0 , 0 , 0 , 0 , 0 , // z
6969 ];
7070
71- let replacement_number = calculate_replacement_number (& hand , None );
72- assert_eq! (replacement_number . unwrap () , 0u8 );
71+ let replacement_number = calculate_replacement_number (& hand , & PlayerCount :: Four ) . unwrap ( );
72+ assert_eq! (replacement_number , 0u8 );
7373}
7474```
7575
76- ### Handling Melds
76+ ### Necessary and Unnecessary Tiles
7777
78- In the calculation for a hand with melds (副露),
79- the melded tiles can be included or excluded when counting tiles to determine if a hand contains four identical ones.
78+ It is also possible to calculate necessary or unnecessary tiles together with the replacement number.
8079
81- If melds are excluded (e.g., 天鳳 (Tenhou), 雀魂 (Mahjong Soul)), specify ` None ` for ` fulu_mianzi_list ` .
80+ - Necessary tiles
81+ - Tiles needed to win with the minimum number of replacements
82+ - Tiles that reduce the replacement number when drawn
83+ - In Japanese, these are referred to as * 有効牌 (yūkōhai)* or * 受け入れ (ukeire)*
8284
83- If melds are included (e.g., World Riichi Championship, M.LEAGUE), the melds should be included in the ` fulu_mianzi_list ` .
85+ - Unnecessary tiles
86+ - Tiles not needed to win with the minimum number of replacements
87+ - Tiles that can be discarded without changing the replacement number
88+ - In Japanese, these are referred to as * 不要牌 (fuyōhai)* or * 余剰牌 (yojōhai)*
8489
8590``` rust
86- use xiangting :: {ClaimedTilePosition , FuluMianzi , calculate_replacement_number };
91+ use xiangting :: {PlayerCount , calculate_necessary_tiles, calculate_unnecessary_tiles };
8792
8893fn main () {
89- // 123m1z
94+ // 199m146779p12s246z
9095 let hand : [u8 ; 34 ] = [
91- 1 , 1 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , // m
92- 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , // p
93- 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , // s
94- 1 , 0 , 0 , 0 , 0 , 0 , 0 , // z
96+ 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 2 , // m
97+ 1 , 0 , 0 , 1 , 0 , 1 , 2 , 0 , 1 , // p
98+ 1 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , // s
99+ 0 , 1 , 0 , 1 , 0 , 1 , 0 , // z
95100 ];
96101
97- // 456p 7777s 111z
98- let melds = [
99- FuluMianzi :: Shunzi (12 , ClaimedTilePosition :: Low ),
100- FuluMianzi :: Gangzi (24 ),
101- FuluMianzi :: Kezi (27 ),
102- ];
102+ let (replacement_number1 , necessary_tiles ) =
103+ calculate_necessary_tiles (& hand , & PlayerCount :: Four ). unwrap ();
104+ let (replacement_number2 , unnecessary_tiles ) =
105+ calculate_unnecessary_tiles (& hand , & PlayerCount :: Four ). unwrap ();
103106
104- let replacement_number_wo_melds = calculate_replacement_number (& hand , None );
105- assert_eq! (replacement_number_wo_melds . unwrap (), 1u8 );
106-
107- let replacement_number_w_melds = calculate_replacement_number (& hand , Some (& melds ));
108- assert_eq! (replacement_number_w_melds . unwrap (), 2u8 );
107+ assert_eq! (replacement_number1 , 5 );
108+ assert_eq! (replacement_number1 , replacement_number2 );
109+ assert_eq! (necessary_tiles , 0b1111111_100000111_111111111_100000111 ); // 1239m123456789p1239s1234567z
110+ assert_eq! (unnecessary_tiles , 0b0101010_000000011_101101001_000000001 ); // 1m14679p12s246z
109111}
110112```
111113
112114### Support for Three-Player Mahjong
113115
114116In three-player mahjong, the tiles from 2m (二萬) to 8m (八萬) are not used.
115- In addition, melded sequences (明順子) are not allowed.
116117
117118``` rust
118- use xiangting :: {calculate_replacement_number, calculate_replacement_number_3_player };
119+ use xiangting :: {PlayerCount , calculate_necessary_tiles, calculate_unnecessary_tiles };
119120
120121fn main () {
121122 // 1111m111122233z
@@ -126,11 +127,17 @@ fn main() {
126127 4 , 3 , 2 , 0 , 0 , 0 , 0 , // z
127128 ];
128129
129- let replacement_number_4p = calculate_replacement_number (& hand , None );
130- assert_eq! (replacement_number_4p . unwrap (), 2u8 );
131-
132- let replacement_number_3p = calculate_replacement_number_3_player (& hand , None );
133- assert_eq! (replacement_number_3p . unwrap (), 3u8 );
130+ let (rn_4p , nt_4p ) = calculate_necessary_tiles (& hand , & PlayerCount :: Four ). unwrap ();
131+ let (_ , ut_4p ) = calculate_unnecessary_tiles (& hand , & PlayerCount :: Four ). unwrap ();
132+ assert_eq! (rn_4p , 2u8 );
133+ assert_eq! (nt_4p , 0b0000000_000000000_000000000_000000110 ); // 23m
134+ assert_eq! (ut_4p , 0b0000001_000000000_000000000_000000000 ); // 1z
135+
136+ let (rn_3p , nt_3p ) = calculate_necessary_tiles (& hand , & PlayerCount :: Three ). unwrap ();
137+ let (_ , ut_3p ) = calculate_unnecessary_tiles (& hand , & PlayerCount :: Three ). unwrap ();
138+ assert_eq! (rn_3p , 3u8 );
139+ assert_eq! (nt_3p , 0b1111100_111111111_111111111_100000000 ); // 9m123456789p123456789s34567z
140+ assert_eq! (ut_3p , 0b0000001_000000000_000000000_000000001 ); // 1m1z
134141}
135142```
136143
0 commit comments