Through two previous answers by @StevenB.Segletes (1 and 2), I'm 99% able to parse the 4 most important fields in SGF files/strings: B and W for Black and White moves, respectively; AB and AW for added or edited Black and White stones respectively. (What I'm trying to do is handle single-branch versions of SGFs, not all of it.)
In those answers, @StevenB.Segletes used this to parse things:
\setsepchar{;||(||)/[||]}
; splits the SGF string around nodes, and then the rest splits the string into key[value] data. However, I unfortunately took too long to realize that AB and AW are typically written inside the same node. So I think, it should be better to directly parse the string as key[value]. I've already tried this (and some other variations), but it didn't work:
\setsepchar{[||]}
In the previous way, this SGF string:
(;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])
is basically this:
(
;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]
)
and would have been parsed like this, I think (before the parsing of the first 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])
But, that wouldn't be able to capture AB or AW in the code below, I don't think. I think that if we directly parse through key[value], then it's possible to do so. The string would be like this:
(;GM[1]FF[4]- ...
;B[as];W[bs]- ...
;PL[W]AB[dq]AW[eq]
In truth, we would then have to check for ;B, ;W instead, but it's pretty much it, I think. Or maybe we should strip the of ; outright?
This is the main part of the code:
\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
}{}
}
}
Here's the code I'm using, which is a slight modification to this previous answer (the important part is the \parseSgf macro):
\documentclass{article}
\usepackage{tikz}
\usepackage{listofitems}
\usepackage{ifthen}
% From this answer by @DavidCarlisle.
\newcommand\notwhite{black}
\newcommand\notblack{white}
% From this answer by @DavidCarlisle.
\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}



ABorAWare encountered in the input string? Treat them asBandW? – Steven B. Segletes Feb 10 '24 at 00:07B=AB, andW=AW. – psygo Feb 10 '24 at 00:15]first? – Jasper Habicht Feb 12 '24 at 07:35