通过@StevenB.Segletes 之前的两个回答(1和2),我 99% 能够解析SGF 文件/字符串:B
分别W
表示黑棋和白棋的移动;AB
分别AW
表示添加或编辑的黑棋和白棋。(我试图处理的是 SGF 的单分支版本,而不是全部。)
在这些答案中,@StevenB.Segletes 使用这个来解析事物:
\setsepchar{;||(||)/[||]}
;
将 SGF 字符串拆分为节点,然后其余部分将字符串拆分为key[value]
数据。但是,不幸的是,我花了很长时间才意识到这一点,AB
并且AW
通常写在同一个节点内。所以我认为,最好直接将字符串解析为key[value]
。我已经尝试过这个(和其他一些变体),但没有用:
\setsepchar{[||]}
按照以前的方式,这个 SGF 字符串:
(;GM[1]FF[4]CA[UTF-8]AP[Sabaki:0.52.2]KM[6.5]SZ[19]DT[2024-02-05];B[as];W[bs];B[cs];PL[W]AB[dq]AW[eq])
基本上是这样的:
(
;GM[1]FF[4]CA[UTF-8]AP[Sabaki:0.52.2]KM[6.5]SZ[19]DT[2024-02-05]
;B[as]
;W[bs]
;B[cs]
;PL[W]AB[dq]AW[eq]
)
并且我认为会被解析成这样(在解析第一个之前key[value]
):
(
GM[1]FF[4]CA[UTF-8]AP[Sabaki:0.52.2]KM[6.5]SZ[19]DT[2024-02-05]
B[as]
W[bs]
B[cs]
PL[W]AB[dq]AW[eq]
)
但是,我认为下面的代码无法捕获AB
或。我认为如果我们直接解析,那么就可以做到这一点。字符串将如下所示:AW
key[value]
(;GM[1]
FF[4]
- ...
;B[as]
;W[bs]
- ...
;PL[W]
AB[dq]
AW[eq]
事实上,我们随后必须检查;B
,;W
但我认为,这差不多就是它了。或者也许我们应该;
直接去掉 ?
这是代码的主要部分:
\long\def\Firstof#1#2\endFirstof{#1}
\newcommand\thecolorofB{black}
\newcommand\thecolorofAB{black}
\newcommand\thecolorofW{white}
\newcommand\thecolorofAW{white}
\ignoreemptyitems
\newcommand{\parseSgf}[1]{
% Maybe first strip the SGF string from `(`, `)`, and `;`?
\setsepchar{;||(||)/[||]} % This is where things should change the most, I think
\readlist*\Z{#1}
\foreachitem \i \in \Z[]{
\itemtomacro\Z[\icnt, 1]\Color
\itemtomacro\Z[\icnt, 2]\sgfCoords
\edef\tmp{{\csname thecolorof\Color\endcsname}{\sgfCoords}}
\ifthenelse{
\equal{\Color}{B} \OR % maybe use some sort of `\contains` in TeX to simplify this if?
\equal{\Color}{W} \OR
\equal{\Color}{AB} \OR
\equal{\Color}{AW}
}{
\expandafter\drawStoneFromSgfCoords\tmp
}{}
}
}
下面是我使用的代码,略作修改先前的答案(重要的部分是\parseSgf
宏):
\documentclass{article}
\usepackage{tikz}
\usepackage{listofitems}
\usepackage{ifthen}
% From [this answer by @DavidCarlisle](https://tex.stackexchange.com/a/708876/64441).
\newcommand\notwhite{black}
\newcommand\notblack{white}
% From [this answer by @DavidCarlisle](https://tex.stackexchange.com/a/708893/64441).
\ExplSyntaxOn
\cs_generate_variant:Nn \int_from_alph:n {e}
\NewExpandableDocumentCommand{\stringToCoordX}{ m }{
\int_from_alph:e { \use_i:nn #1 }
}
\NewExpandableDocumentCommand{\stringToCoordY}{ m }{
\int_from_alph:e { \use_ii:nn #1 }
}
\ExplSyntaxOff
\newcommand{\drawStoneFromSgfCoords}[2]{
\pgfmathsetmacro{\x}{\stringToCoordX{#2} - 1}
\pgfmathsetmacro{\y}{\stringToCoordY{#2} - 1}
\draw[draw = \UseName{not#1}, fill = #1, line width = 0.1mm]
(\x * 10cm / 18, \y * 10cm / 18)
circle [radius = 0.2575cm];
}
\long\def\Firstof#1#2\endFirstof{#1}
\newcommand\thecolorofB{black}
\newcommand\thecolorofAB{black}
\newcommand\thecolorofW{white}
\newcommand\thecolorofAW{white}
\ignoreemptyitems
\newcommand{\parseSgf}[1]{
\setsepchar{;||(||)/[||]} % This is where things should change, I think
\readlist*\Z{#1}
\foreachitem \i \in \Z[]{
\itemtomacro\Z[\icnt, 1]\Color
\itemtomacro\Z[\icnt, 2]\sgfCoords
\edef\tmp{{\csname thecolorof\Color\endcsname}{\sgfCoords}}
\ifthenelse{
\equal{\Color}{B} \OR
\equal{\Color}{W} \OR
\equal{\Color}{AB} \OR
\equal{\Color}{AW}
}{
\expandafter\drawStoneFromSgfCoords\tmp
}{}
}
}
\def\sgfA{;B[ab];W[cd]}
\def\sgfB{(;GM[1]FF[4]CA[UTF-8]AP[Sabaki:0.52.2]KM[6.5]SZ[19]DT[2024-02-05];B[as];W[bs];B[cs])}
\def\sgfC{(;GM[1]FF[4]CA[UTF-8]AP[Sabaki:0.52.2]KM[6.5]SZ[19]DT[2024-02-05];B[as];W[bs];B[cs];PL[W]AB[dq]AW[eq])}
\begin{document}
\begin{tikzpicture}
\pgfmathsetmacro{\step}{10 / 18}
\draw[step=\step] (0, 0) grid (10, 10);
% \drawStoneFromSgfCoords{black}{ab}
% \drawStoneFromSgfCoords{white}{cd}
\parseSgf{\sgfC}
\end{tikzpicture}
\end{document}
答案1
首先,相对于我之前的答案,我必须提高解析 SGF 输入的能力。下面是一个简短的 MWE,仅显示该解析。我用来listofitems
执行 3 深度(嵌套)解析:最高级别解析;
、(
和)
,第二级解析]
,第三级解析[
。
列表中的空白项被设置为被忽略,因此(;
实际上对解析列表没有任何贡献(而不是在(
和之间贡献一个空白条目;
)。
首先,在输出中,\KeyName[\KeyValue]
嵌套循环的每个解析\foreachitem
都以空格分隔符显示。如果检测到颜色键,则\rightarrow
在条目前插入一个。我们看到,所有类型为B
、W
、AB
和的键AW
都被成功检测为颜色键,而所有其他键都被排除,这是本练习的目标。
为了查看解析的详细信息,MWE 的最后三行输出被清楚地提供。MWE 输入中给出的 SGF 定义如下
\def\sgfC{(
;GM[1]FF[4]CA[UTF-8]AP[Sabaki:0.52.2]KM[6.5]SZ[19]DT[2024-02-05]
;B[as]
;W[bs]
;B[cs]
;PL[W]AB[dq]AW[eq]
)}
被解析为名为 的列表\Z
。我们看到 的第 5 个元素\Z
,记为Z[5]
,是,对应于位于字符、或PL[W]AB[dq]AW[eq]
之间的第 5 个非空文本块。;
(
)
第二级解析以]
作为分隔符,应用于 的每个元素<i>
。\Z[<i>]
元素\Z[5,2]
是 的子列表\Z[5]
,显示为AB[dq
。现在,我们能够分离与其关联值链接的每个键。最后,第三级解析应用于次级列表的每个项目,这样\Z[5,2,1]
给出为AB
,\Z[5,2,2]
给出为dq
。因此,我们分离了每个键并可以独立访问与其关联的值。
与本系列问题中的先前答案一样,我们引入了诸如这样的宏,\newcommand\thecolorofB{black}
以便可以将诸如这样的 SGF 键B
转换为实际的颜色black
。但我们现在还引入了诸如这样的宏,\newcommand\thekeytypeofAB{C}
以指示该键AB
是与颜色属性相关联的键。
\documentclass{article}
\usepackage{listofitems}
\long\def\Firstof#1#2\endFirstof{#1}
\newcommand\thecolorofB{black}
\newcommand\thecolorofAB{black}
\newcommand\thecolorofW{white}
\newcommand\thecolorofAW{white}
\long\def\Keytypeof#1{\csname thekeytypeof#1\endcsname}
\newcommand\thekeytypeofB{C}% C DENOTING A "COLOR" KEY
\newcommand\thekeytypeofAB{C}
\newcommand\thekeytypeofW{C}
\newcommand\thekeytypeofAW{C}
\ignoreemptyitems
\newcommand{\parseSgf}[1]{%
\setsepchar{;||(||)/]/[}%
\readlist*\Z{#1}%
\foreachitem \i \in \Z[]{%
\foreachitem \z \in \Z[\icnt]{%
\itemtomacro\Z[\icnt, \zcnt, 1]\KeyName
\itemtomacro\Z[\icnt, \zcnt, 2]\KeyValue
\if\Keytypeof\KeyName C
\edef\tmp{{\csname thecolorof\KeyName\endcsname}{\KeyValue}}%
\expandafter\drawStoneFromSgfCoords\tmp
\fi
\KeyName[\KeyValue]
}}%
}
\newcommand{\drawStoneFromSgfCoords}[2]{\unskip$\rightarrow$}
\def\sgfC{(
;GM[1]FF[4]CA[UTF-8]AP[Sabaki:0.52.2]KM[6.5]SZ[19]DT[2024-02-05]
;B[as]
;W[bs]
;B[cs]
;PL[W]AB[dq]AW[eq]
)}
\begin{document}
\parseSgf{\sgfC}
\bigskip\textbackslash Z[5] is \Z[5]
\bigskip\textbackslash Z[5,2] is \Z[5,2]
\bigskip\textbackslash Z[5,2,1] and \textbackslash Z[5,2,2]
are \Z[5,2,1] and \Z[5,2,2]
\end{document}
现在,我们准备将上面显示的解析方法应用于\drawStoneFromSgfCoords
OP 提供的实际宏。完成后,不仅会绘制B
和W
键中的棋子,还会绘制AB
和AW
命令中的棋子——实际上,任何颜色键都会引发绘制操作。
\documentclass{article}
\usepackage{tikz}
% From [this answer by @DavidCarlisle](https://tex.stackexchange.com/a/708876/64441).
\newcommand\notwhite{black}
\newcommand\notblack{white}
% From [this answer by @DavidCarlisle](https://tex.stackexchange.com/a/708893/64441).
\ExplSyntaxOn
\cs_generate_variant:Nn \int_from_alph:n {e}
\NewExpandableDocumentCommand{\stringToCoordX}{ m }{
\int_from_alph:e { \use_i:nn #1 }
}
\NewExpandableDocumentCommand{\stringToCoordY}{ m }{
\int_from_alph:e { \use_ii:nn #1 }
}
\ExplSyntaxOff
\newcommand{\drawStoneFromSgfCoords}[2]{%
\pgfmathsetmacro{\x}{\stringToCoordX{#2} - 1}
\pgfmathsetmacro{\y}{\stringToCoordY{#2} - 1}
%
\draw[draw = \UseName{not#1}, fill = #1, line width = 0.1mm]
(\x * 10cm / 18, \y * 10cm / 18)
circle [radius = 0.2575cm];
}
\usepackage{listofitems}
% From [this answer by @StevenB.Segletes](https://tex.stackexchange.com/a/709014/64441).
\long\def\Firstof#1#2\endFirstof{#1}
\newcommand\thecolorofB{black}
\newcommand\thecolorofAB{black}
\newcommand\thecolorofW{white}
\newcommand\thecolorofAW{white}
\long\def\Keytypeof#1{\csname thekeytypeof#1\endcsname}
\newcommand\thekeytypeofB{C}% C DENOTING A "COLOR" KEY
\newcommand\thekeytypeofAB{C}
\newcommand\thekeytypeofW{C}
\newcommand\thekeytypeofAW{C}
\ignoreemptyitems
\newcommand{\parseSgf}[1]{%
\setsepchar{;||(||)/]/[}%
\readlist*\Z{#1}%
\foreachitem \i \in \Z[]{%
\foreachitem \z \in \Z[\icnt]{%
\itemtomacro\Z[\icnt, \zcnt, 1]\KeyName
\itemtomacro\Z[\icnt, \zcnt, 2]\KeyValue
\if\Keytypeof\KeyName C%
\edef\tmp{{\csname thecolorof\KeyName\endcsname}{\KeyValue}}%
\expandafter\drawStoneFromSgfCoords\tmp
\fi
}}%
}
\def\sgfA{;B[ab];W[cd]}
\def\sgfB{(
;GM[1]FF[4]CA[UTF-8]AP[Sabaki:0.52.2]KM[6.5]SZ[19]DT[2024-02-05]
;B[as]
;W[bs]
;B[cs]
)}
\def\sgfC{(
;GM[1]FF[4]CA[UTF-8]AP[Sabaki:0.52.2]KM[6.5]SZ[19]DT[2024-02-05]
;B[as]
;W[bs]
;B[cs]
;PL[W]AB[dq]AW[eq]
)}
\begin{document}
\begin{tikzpicture}
\pgfmathsetmacro{\step}{10 / 18}
\draw[step=\step] (0, 0) grid (10, 10);
\parseSgf{\sgfC}
\end{tikzpicture}
\end{document}
奖金
针对如何处理拆分键(按 拆分:
)的评论,以下是如何...基于 添加另一层解析。我已经定义了检测定义的拆分键(必须包含分隔符)以及可选拆分键(可能包含或不包含分隔符):
的逻辑。:
:
\documentclass{article}
\usepackage{listofitems}
\long\def\Firstof#1#2\endFirstof{#1}
\newcommand\thecolorofB{black}
\newcommand\thecolorofAB{black}
\newcommand\thecolorofW{white}
\newcommand\thecolorofAW{white}
\long\def\Keytypeof#1{\csname thekeytypeof#1\endcsname}
\newcommand\thekeytypeofB{C}% C DENOTING A "COLOR" KEY
\newcommand\thekeytypeofAB{C}
\newcommand\thekeytypeofW{C}
\newcommand\thekeytypeofAW{C}
\newcommand\thekeytypeofAP{S}% DENOTING SPLIT KEY VALUE
\ignoreemptyitems
\newcommand{\parseSgf}[1]{%
\setsepchar{;||(||)/]/[/:}%
\readlist*\Z{#1}%
\foreachitem \i \in \Z[]{%
\foreachitem \z \in \Z[\icnt]{%
\itemtomacro\Z[\icnt, \zcnt, 1]\KeyName
\itemtomacro\Z[\icnt, \zcnt, 2]\KeyValue
\if\Keytypeof\KeyName C
\edef\tmp{{\csname thecolorof\KeyName\endcsname}{\KeyValue}}%
\expandafter\drawStoneFromSgfCoords\tmp
\fi
\if \Keytypeof\KeyName S
DEFINED SPLIT KEY ``\Z[\icnt, \zcnt, 2, 1]'' and
``\Z[\icnt, \zcnt, 2, 2]''
\else
\ifnum\listlen\Z[\icnt, \zcnt, 2]=2
OPTIONAL SPLIT KEY ``\Z[\icnt, \zcnt, 2, 1]'' and
``\Z[\icnt, \zcnt, 2, 2]''
\fi\fi
\KeyName[\KeyValue]
}}%
}
\newcommand{\drawStoneFromSgfCoords}[2]{\unskip$\rightarrow$}
\def\sgfC{(
;GM[1]FF[4]CA[UTF-8]AP[Sabaki:0.52.2]KM[6.5]SZ[19]DT[2024-02-05]
;B[as]
;W[bs]
;B[cs]
;PL[W]AB[dq]AW[eq]LB[bs:labelname]
)}
\begin{document}
\parseSgf{\sgfC}
\end{document}