-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathshogi-board.typ
146 lines (139 loc) · 3.59 KB
/
shogi-board.typ
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
// 参考: SFENの形式については以下を参照してください。
// http://shogidokoro.starfree.jp/usi.html
#let shogi-board(ranks, sente: (""), gote: ("")) = {
// SFENの駒文字列では、成駒を表す文字が2文字で1つの駒を表す
// #str.clusters()で分解するために便宜上アクセント記号を使っている。
let pieces = (
K: "王",
R: "飛",
B: "角",
G: "金",
S: "銀",
N: "桂",
L: "香",
P: "歩",
Ṙ: "龍",
Ḃ: "馬",
Ṡ: "全",
Ṅ: "圭",
Ḷ: "杏",
Ṗ: "と",
k: rotate(180deg, "玉"),
r: rotate(180deg, "飛"),
b: rotate(180deg, "角"),
g: rotate(180deg, "金"),
s: rotate(180deg, "銀"),
n: rotate(180deg, "桂"),
l: rotate(180deg, "香"),
p: rotate(180deg, "歩"),
ṙ: rotate(180deg, "龍"),
ḃ: rotate(180deg, "馬"),
ṡ: rotate(180deg, "全"),
ṅ: rotate(180deg, "圭"),
ḷ: rotate(180deg, "杏"),
ṗ: rotate(180deg, "と"),
" ": "",
)
let gote-mochigoma = grid(
align: center + horizon,
stack(
dir: ttb,
spacing: 1em,
"⛊",
for ps in gote.chunks(4) {
stack(dir: ltr, ..ps.map(i => pieces.at(i)))
}))
let sente-mochigoma = grid(
align: center + horizon,
stack(
dir: ttb,
spacing: 1em,
"☗",
for ps in sente.chunks(4) {
stack(dir: ltr, ..ps.map(i => pieces.at(i)))
}))
grid(
columns: (auto, auto, auto),
gote-mochigoma,
pad(x: 1em,
grid(
columns: (1.5em,) * 10,
rows: (1.5em,) * 10,
align: center + horizon,
..(range(10)
.map(i => grid.hline(y: i + 1, start: 0, end: 9))),
..(range(10)
.map(i => grid.vline(x: i, start: 1, end: 10))),
..(range(9)
.rev()
.map(i => text(0.8em, str(i + 1)))),
..(" 一二三四五六七八九".clusters()
.enumerate()
.map(i => grid.cell(text(0.6em, i.at(1)), x: 9, y: i.at(0)))),
..for rank in ranks {
rank.clusters().map(i => pieces.at(i))
}
),
),
sente-mochigoma,
)
}
#let sfen-banmen-parser(sfen) = {
let convert = (
R: "Ṙ",
B: "Ḃ",
S: "Ṡ",
N: "Ṅ",
L: "Ḷ",
P: "Ṗ",
r: "ṙ",
b: "ḃ",
s: "ṡ",
n: "ṅ",
l: "ḷ",
p: "ṗ",
)
let ranks = sfen.split("/").map(rank => {
let result = ""
let i = 0
let rank = rank.replace(regex("\+[RBSNLPrbsnlp]"), dict => convert.at(dict.at("text").at(-1)))
for c in rank.clusters() {
if (c.contains(regex("\d"))) {
result += " " * int(c)
} else {
result += c
}
}
result
})
ranks
}
#let sfen-mochigoma-parser(sfen) = {
let mochigoma = sfen.replace(regex("\d+[RBGSNLPrbgsnlp]"), dict => {
let count = int(dict.at("text").slice(0, -1))
let piece = dict.at("text").slice(-1)
piece * count
})
let sente = mochigoma
.clusters()
.filter(c => c.match(regex("[RBGSNLP]")) != none)
let gote = mochigoma
.clusters()
.filter(c => c.match(regex("[rbgsnlp]")) != none)
(sente, gote)
}
#let sfen-kyokumen-parser(sfen) = {
let (bannmen, teban, mochigoma) = sfen.split(" ")
let ranks = sfen-banmen-parser(bannmen)
let (sente, gote) = sfen-mochigoma-parser(mochigoma)
arguments(ranks, sente: sente, gote: gote)
}
/// This function creates a shogi board from a SFEN string.
///
/// https://en.wikipedia.org/wiki/Shogi_notation#SFEN
///
/// - sfen (str): A SFEN string.
/// -> content
#let shogi-board-from-sfen(sfen) = {
shogi-board(..sfen-kyokumen-parser(sfen))
}