-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathREADME.Rmd
234 lines (182 loc) · 8.84 KB
/
README.Rmd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
---
output: github_document
---
<!-- README.md is generated from README.Rmd. Please edit that file -->
```{r, include = FALSE}
knitr::opts_chunk$set(
collapse = TRUE,
comment = "#>",
fig.path = "man/figures/README-",
out.width = "100%"
)
pkgload::load_all(export_all = FALSE)
```
# shikakusphere
<!-- badges: start -->
[![shikakusphere status badge](https://paithiov909.r-universe.dev/badges/shikakusphere)](https://paithiov909.r-universe.dev/shikakusphere)
[![R-CMD-check](https://github.com/paithiov909/shikakusphere/actions/workflows/R-CMD-check.yaml/badge.svg)](https://github.com/paithiov909/shikakusphere/actions/workflows/R-CMD-check.yaml)
[![codecov](https://codecov.io/gh/paithiov909/shikakusphere/branch/main/graph/badge.svg)](https://app.codecov.io/gh/paithiov909/shikakusphere)
<!-- badges: end -->
shikakusphere is a collection of miscellaneous funcitons for Japanese mahjong that wraps C++ sources derived from [tomohxx/shanten-number](https://github.com/tomohxx/shanten-number) and [TadaoYamaoka/cmajiang](https://github.com/TadaoYamaoka/cmajiang).
The name "shikakusphere" is a compound word of "shikaku" + "sphere". "shikaku" means "square" in Japanese.
It comes from "Shikakui Uchuu De Matteru Yo", which is the third ED of the anime "Saki", and sounds exactly like mahjong games.
## Installation
You can install the development version of shikakusphere from [GitHub](https://github.com/paithiov909/shikakusphere) with:
``` r
# install.packages("pak")
pak::pak("paithiov909/shikakusphere")
```
## Example
### 手牌の表現
手牌は[kobalab/majiang-core](https://github.com/kobalab/majiang-core)の記法で文字列として表現します。
**参考**
- [牌 · kobalab/majiang-core Wiki](https://github.com/kobalab/majiang-core/wiki/%E7%89%8C)
- [面子 · kobalab/majiang-core Wiki](https://github.com/kobalab/majiang-core/wiki/%E9%9D%A2%E5%AD%90)
- [牌姿 · kobalab/majiang-core Wiki](https://github.com/kobalab/majiang-core/wiki/%E7%89%8C%E5%A7%BF)
- [電脳麻将のプログラム中の中国語一覧 - koba::blog](https://blog.kobalab.net/entry/20170722/1500688645)
`paistr()`は、こうした手牌の文字列表現を扱いやすくするためのS3クラス(`<skksph_paistr>`)です。`print()`したときに、その文字列に含まれる牌の枚数を表示します。また、`plot(<skksph_paistr>)`で牌姿を画像としてプロットできます。
```{r}
library(shikakusphere)
hands <- c(
"m11p234s555z11122*",
"p1122334455667",
"m19p19s19z1234567",
"m055z7z7,m78-9,z5555,z666=",
"m055z7z7,m78-9,z5555,z666=,"
)
hands <- paistr(hands)
hands
plot(hands[4])
```
`<skksph_paistr>`を`print()`したときに表示される牌の枚数は不正確な場合があります。手牌の表現にどの牌が何枚含まれるかを正確に確認するには、`tidy(<skksph_paistr>)`を使うことができます。
```{r}
tidy(hands)
```
このかたちの表現は`lineup()`でlist of factorsにすることができます。
```{r}
tidy(hands) |>
lineup()
```
さらに、このかたちのlist of factors(またはlist of characters)は`lipai()`で文字列表現に変換できます(ただし、`lipai()`では`_*+=-,`は扱えません)。
```{r}
c(paste0("m", 1:9), paste0("s", 4:6), paste0("z", c(1, 1))) |>
lipai()
```
### シャンテン数・有効牌の確認
手牌のうちでも、副露面子を除いた打牌可能な部分のことを兵牌(bingpai)と呼びます。このパッケージで使用している実装における「シャンテン数」は、大まかには「その手牌の兵牌に注目したとき、手牌を聴牌形(和了形)に変形させるために必要なツモの最小数」のことを指しています。そのため、少牌だったりして素朴な意味での和了形になっていないかたちであっても、兵牌がn面子1雀頭のかたちになってさえいれば、ここでのシャンテン数は`-1`を返します。
また、ここでの「有効牌」というのは、その牌を新たにツモったときに、この意味でのシャンテン数が減少する牌のことです。兵牌の枚数が14, 11, 8, 5, 2枚の場合や、すでに和了形であって有効牌が定義できないときには、`character(0)`を有効牌として返します。なお、兵牌のなかに同じ牌が4枚ある場合についてのみ、有効牌であっても待ち牌としません。
```{r}
# シャンテン数
n_xiangting <- calc_xiangting(hands)
n_xiangting
# 有効牌
collect_tingpai(hands[n_xiangting$num >= 0])
collect_tingpai(c("m1234444p456s789", "m13p456s789z11,m2222"))
```
「あと何枚ツモれば手牌全体として聴牌形にできるか」という意味でのシャンテン数は、たとえば次のようにして計算できると思います。
```{r}
dplyr::tibble(
hand = paistr(c("m1", "m1134", "m1,m45-6", "m1122p334455,s6-78", "m19p19s19z112345")),
calc_xiangting(hand),
calculate(hand)
) |>
dplyr::mutate(
bingpai = rowSums(bingpai),
n_zimo_to_hule = dplyr::case_when(
mode == "yiban" & bingpai + n_fulou * 3 < 13 ~ 13 - bingpai - n_fulou * 3,
.default = num
)
)
```
### 得点・あがり役の確認
`calc_defen()`はvectorizeされていないため、必ず、長さが1の文字列表現を渡してください。
```{r}
# 得点
score <- calc_defen(hands[n_xiangting$num == -1][1], baopai = "z1")
score
# ロン和了のときはこんなふうに書く
calc_defen("m345567p234s3378", baopai = "z1", rongpai = "s9=")
# あがり役の表示
parse_hupai(score$hupai, "jp")
```
### 牌譜ファイルを扱う
```{r include = FALSE}
stopifnot(requireNamespace("convlog", quietly = TRUE))
```
[convlog](https://github.com/paithiov909/convlog)パッケージを使うと、天鳳のJSON形式(`tenhou.net/6`)のファイルから牌譜を読み込むことができます。
`convlog::read_tenhou6()`の戻り値は、次のようなかたちをしています。
```{r}
json_log <-
list.files(
system.file("testdata/", package = "convlog"),
pattern = "*.json$",
full.names = TRUE
) |>
convlog::read_tenhou6()
json_log
```
この関数は、天鳳のJSON形式を一度MJAIフォーマットのJSON文字列へと変換したうえでパースするため、牌の表現などがshikakusphereで用いているものとは異なっています。MJAIフォーマットにおける牌の表現をshikakusphereで用いている表現に変換するには、`trans_tile()`, `mjai_target()`, `mjai_conv()`を使って、次のようにします。
```{r}
paifu <-
json_log[["paifu"]] |>
dplyr::filter(
type %in% c("tsumo", "dahai", "chi", "pon", "daiminkan", "kakan", "ankan", "reach")
) |>
dplyr::mutate(
# ツモ切りかを判定する場合、あらかじめ`NA`を`FALSE`で埋めておく
tsumogiri = dplyr::if_else(is.na(tsumogiri), FALSE, tsumogiri),
# 牌の表現の変換
pai = trans_tile(pai)
) |>
dplyr::mutate(
# 副露メンツの変換
pai = mjai_conv(type, pai, consumed, mjai_target(actor, target)),
# ツモ切りなら末尾に"_"を付ける
pai = dplyr::if_else(tsumogiri,
paste0(pai, "_"),
pai
),
# リーチ宣言牌なら末尾に"*"を付ける
pai = dplyr::if_else(
dplyr::lag(type, default = "") == "reach",
paste0(pai, "*"),
pai
),
.by = c(game_id, round_id, actor)
)
paifu
```
`proceed()`を使うと、ゲームのある時点におけるプレイヤーの手牌の状態を再現することができます。この関数には、各プレイヤーの配牌とその時点までのツモ・打牌をそれぞれlistとして与えます。チー・ポンと大明槓についてはツモの一部、暗槓と加槓については打牌の一部とします。
たとえば、ここで読み込んでいるデータについて、各本場の終わりの時点におけるプレイヤーの手牌を再現するには、次のようにします。
```{r}
qipai <-
json_log[["round_info"]] |>
dplyr::rowwise() |>
dplyr::reframe(
game_id = game_id,
round_id = round_id,
actor = 0:3,
tehais
) |>
dplyr::mutate(
qipai = list(trans_tile(as.character(tehais))),
.by = c(game_id, round_id, actor),
.keep = "unused"
)
paifu |>
dplyr::left_join(qipai, by = dplyr::join_by(game_id, round_id, actor)) |>
dplyr::summarise(
zimo = list(pai[which(type %in% c("tsumo", "chi", "pon", "daiminkan"))]),
dapai = list(pai[which(type %in% c("dahai", "kakan", "ankan"))]),
.by = c(game_id, round_id, actor)
) |>
dplyr::left_join(qipai, by = dplyr::join_by(game_id, round_id, actor)) |>
dplyr::reframe(
game_id = game_id,
round_id = round_id,
player = actor,
last_state = proceed(qipai, zimo, dapai)
)
```
# License
GPL (>= 3). This package contains the [tinyxml2](https://github.com/leethomason/tinyxml2) code released under the zlib license.