Drawing a german abacus as in the books of Adam Ries

I am teaching a course in history of mathematics and would like to draw something like the following: The bullet points should be possible to draw on the lines and in between. Can anyone help me? Thank you very much!

• Welcome to TeX.SE! Can you please add the code you have tried so far? – Kurt Apr 25 at 7:47
• You will find a tutorial for such shapes at the beginning of TikZ manual (the one dedicated to drawing algorithm). It is very well explained. All you have to do is to turn the white circles of its example into black. – sztruks Apr 25 at 7:49
• Are the grid sizes fixed as in your MWE (4 horizontal lines and 2 vertical), or are they also variable? – Andrew Apr 25 at 8:27
• Just a random thought: You can tweak qcircuit – pushpen.paul Apr 25 at 17:33

As you intend to draw "quite a few" of these you'll need a reasonable interface, so how about using code like this

  \abacus{{2,1},{1,1},{3,3,2},{},{4,2},{1}}
\abacus{{1},{},{},{},{2},{1}}


to produce something like this: The way that this works is that the \abacus command accepts a comma separated list of "abacus rows", which start from the bottom of the abacus and climb upwards. Each "abacus row" is itself a comma separated list that gives the number of balls in each column of the abacus, from left to right.

Here's the full code:

\documentclass{article}
\usepackage{tikz}

\tikzset{
pics/abacus/.style = {
code={
\ifnum#1>0% have to treat 0 separately
\foreach \ball [evaluate=\ball as \x
using {(\ball-0.5-#1/2)*0.2}] in {1,...,#1} {
\fill[black] (\x,0) circle (2.5pt);
}
\fi
}
},
/tikz/abacus/.is family,% default values
/tikz/abacus,
xscale/.initial=1,  % default xscale=1 (no scaling)
yscale/.initial=0.6,% default yscale=0.6
columns/.initial=2, % default of 2 vertical rules
rows/.initial=4     % default of 4 horizontal rules
}
% shortcut for accessing options
\newcommand\AbacusOption{\pgfkeysvalueof{/tikz/abacus/#1}}

\newcommand\abacus[]{\tikzset{abacus, #1}%
\begin{tikzpicture}[
xscale=\AbacusOption{xscale},
yscale=\AbacusOption{yscale}
]
\foreach \ypos in {1,...,\AbacusOption{rows}} {
\draw(0,2*\ypos-1)--++(2*\AbacusOption{columns},0);
}
\foreach \xpos in {1,...,\AbacusOption{columns}} {
\draw(2*\xpos, 0)--++(0,2*\AbacusOption{rows});
}
\foreach \row [count=\ypos] in {#2} {
\foreach \col [count=\xpos] in \row {
\draw (2*\xpos-1,\ypos) pic{abacus={\col}};
}
}
\end{tikzpicture}%
}

\begin{document}

\abacus{{2,1},{1,1},{3,3,2},{},{4,2},{1}}
\abacus{{1},{},{},{},{2},{1}}

\end{document}


The \abacus command accepts an optional argument that accepts a comma separated list of key-value pairs for changing the default number of rows and columns and the x and y scaling. For example,

\abacus[rows=2, columns=3, xscale=0.5, yscale=0.3]{{1,2,3,4},{0,3,1}}


produces: Here I draw the first picture. You can draw the other based on this.

\documentclass[tikz]{standalone}
\begin{document}
\begin{tikzpicture}[x=1.5cm]
\foreach \i in {1,2} \draw (\i,-.2) -- (\i,3.2);
\foreach \i in {0,1,2,3} \draw (0,\i) -- (3,\i);
\foreach \x/\y in {
.5/0,
.4/2,
.6/2,
.5/2.5
} \fill (\x,\y) circle (2pt);
\end{tikzpicture}
\end{document} 