| Bytes | Lang | Time | Link |
|---|---|---|---|
| 019 | Jelly | 250621T161929Z | Unrelate |
| 133 | C gcc | 250505T021000Z | a stone |
| 022 | Jelly | 250507T195517Z | Jonathan |
| 086 | JavaScript V8 | 250504T173518Z | Arnauld |
| 031 | 05AB1E | 250506T083320Z | Kevin Cr |
| 093 | Python | 250505T184622Z | Albert.L |
| 087 | Ruby | 250504T215018Z | Level Ri |
| 162 | Google Sheets | 250504T174535Z | doubleun |
| 034 | Charcoal | 250504T174012Z | Neil |
Jelly, 19 bytes
2p5Ḋ>þ¤Ẏ€ị⁾.oUp$K€G
Took some trial and error to beat 22, to the point that it took me several minutes to even notice this worked when I threw it together with half-baked ideas to "fix" it back to another 22 already in my head!
>þ Table greater-than over rows from
5Ḋ [2 .. 5]
5 ¤ and columns from [1 .. 5].
2p Cartesian product of [1, 2] with that.
Ẏ€ Flatten the pairs,
ị⁾.o and modular 1-index them into ".o":
2p >þ ị⁾.o 0 becomes 'o', 1 becomes '.', and 2 becomes 'o'.
Up Take the Cartesian product of those reversed results with
$ the un-reversed results.
K€ Join each of those pairs on a space,
G and grid format.
C (gcc), 135 133 bytes
-2 from @ceilingcat by using Unicode tricks
Includes a "direction change character" (0x202e) and a "digit shape selection character" (0x206f)
k;l(i){printf(L""+"_^<XPONLH@"[i]/k%2);}f(i){for(i=100;i--;puts("")){for(k=1;k<32;)k*=l(i/10);for(printf(" ");k/=2;)l(i%10);}}
Try it online! Each line has 1 trailing space, and there is 1 trailing newline at the end.
Ungolfed/Explained (old version):
k; // finger is stored here
l(i) {
printf("%c ", ".o"["_^<XPONLH@"[i] >> k & 1]);
// "_^<XPONLH@" is a bitmask that tells which fingers should be up
// for each value. This finds the current digit value in the bitmask,
// compares it to the current finger, and outputs accordingly
// There's probably a better way to do it than this...
// (like @Arnauld's solution: https://codegolf.stackexchange.com/a/279501/)
}
f(i) {
for(i = 100; i--; puts("")) { // For each possible combination, (output newline after iteration)
for(k = 0; k<5; k++) // For each finger from 0 to 4 on the left hand:
l(i/10); // print expected output
for(printf(" "); k--;) // For each finger from 4 to 0 on the left hand:
l(i%10); // print expected output
}
}
Jelly, 22 bytes
⁾.o,Ɱṗ4ṢƑƇUƬƊKŒpżḢFƊ€G
A niladic Link that yields a list of characters, or a full program that prints to stdout.
How?
⁾.o,Ɱṗ4ṢƑƇUƬƊKŒpżḢFƊ€G - Link: no arguments
⁾.o - set the left argument to ".o"
Ɗ - last three links as a monad - f(".o"):
ṗ4 - Cartesian power 4 -> All lists of '.' and 'o' of length four
Ƈ - keep those for which:
Ƒ - is invariant under?:
Ṣ - sort
-> ["....", "...o", "..oo", ".ooo", "oooo"]
Ƭ - collect up while distinct under:
U - reverse each
-> [["....", "...o", "..oo", ".ooo", "oooo"],
["....", "o...", "oo..", "ooo.", "oooo"],
]
Ɱ - map across that with:
, - {".o"} paired with {element}
-> [[".o",
["....", "...o", "..oo", ".ooo", "oooo"],
],
[".o",
["....", "o...", "oo..", "ooo.", "oooo"],
],
]
K - join with space characters (well, one in this case)
-> [".o",
["....", "...o", "..oo", ".ooo", "oooo"],
' ',
".o",
["....", "o...", "oo..", "ooo.", "oooo"],
]
Œp - Cartesian product
-> [['.', ".....", ' ', '.', "....."],
['.', ".....", ' ', '.', "o...."],
['.', ".....", ' ', '.', "oo..."],
...
['o', "ooooo", ' ', 'o', "ooo.."],
['o', "ooooo", ' ', 'o', "oooo."],
['o', "ooooo", ' ', 'o', "ooooo"],
]
€ - for each:
Ɗ - last three links as a monad:
Ḣ - remove the head and yield it
ż - {TheRest} zip with {that}
F - flatten
e.g. ['o', ".....", ' ', 'o', "....."]
-> ".....o o....."
G - format as a grid
JavaScript (V8), 86 bytes
A full program.
for(n=~99;q=9,n++;)print(...[..."12345 54321"].map(c=>+c?n/~q%(5<<c/5)<c?"o":".":q=c))
Commented
for( // main loop:
n = ~99; // start with n = -100 and count upwards
q = 9, // at each iteration, start with q = 9
n++; // stop when n = 0
) //
print( // print:
... // spread the result of map()
[..."12345 54321"] // go from 1 to 5 for 10's
// and then from 5 to 1 for units
.map(c => // for each value c:
+c ? // if c is not a space:
n / ~q % // divide n by ~q (-10 or -1)
(5 << c / 5) // reduce modulo 10 if c = 5
// or modulo 5 otherwise
< c ? // if it's less than c:
"o" // output "o"
: // else:
"." // output "."
: // else (central column):
q = c // output " " and make q zero'ish
) // end of map()
) // end of print()
C (gcc), 108 bytes
-2 thanks to ceilingcat
Essentially the same algorithm. But each line ends with a trailing space.
q,i,c;f(n){for(n=~99;q=9,n++;puts(""))for(i=6;c=6-abs(--i);)printf("%c ",c>5?q=0,32:".o"[n/~q%(5<<c/5)<c]);}
05AB1E, 31 bytes
„.o5LÐ䛀ÁsDδ@«TиD{ís‚øèðδ.ý€˜»
Explanation:
„.o # Push string ".o", which we'll use later on
5L # Push list [1,2,3,4,5]
Ð # Triplicate it
δ # Pop two, and apply double-vectorized:
› # Larger than check
€ # Map over each row:
Á # Rotate it once clockwise
s # Swap to get the remaining list
D # Duplicate it again
δ # Pop both, and apply double-vectorized again:
@ # Larger than or equals check
« # Merge the two matrices together
Tи # Repeat it 10 times
D # Duplicate this list of 100 rows
{í # Sort each row of this copy in reversed order
s‚ # Swap and pair the two list of 100 rows together
ø # Zip/transpose; swapping rows/columns,
# to get 100 pairs of rows of 5 bits each
è # 0-based index each bit into the ".o"
δ # Map over each pair of rows:
ð .ý # Intersperse it with a space character
€ # Map over each again:
˜ # Flatten it to a list of 11 items
» # Then join each inner list with space-delimiter;
# and then all strings with newline-delimiter
# (after which the result is output implicitly)
5LÐ䛀Á results in matrix:
[[0,0,0,0,0],
[0,1,0,0,0],
[0,1,1,0,0],
[0,1,1,1,0],
[0,1,1,1,1]]
sDδ@ results in matrix:
[[1,0,0,0,0],
[1,1,0,0,0],
[1,1,1,0,0],
[1,1,1,1,0],
[1,1,1,1,1]]
Python, 93 bytes
a=['.o'[x>4]+x%5*'o'+~x%5*"."for x in range(10)]
[print(*x[::-1]+' '+y)for x in a for y in a]
Originally, I missed the intermittent spaces:
Python, 88 bytes
a=['.o'[x>4]+x%5*'o'+~x%5*"."for x in range(10)]
[print(x[::-1],y)for x in a for y in a]
Ruby, 87 bytes
f=->i{". "*(4-j=i%5)+"o "*j+".o"[i/5]}
100.times{|k|puts f[k/10]+" "+f[k%10].reverse}
String manipulation seems the best way to go.
Ruby, 99 bytes
100.times{|i|puts ("%05b %05b"%[62<<i/10%5&31|1-i/50,15>>i%5|1-i%10/5<<4]).tr("01","o.").chars*" "}
This alternate approach is bitshift, convert to text representation of binary, and format. Didn't work out as short as I had hoped.
Google Sheets, 162 bytes
=index(let(a,". . . .",b,"o o o o",tocol(torow({a,". . . o",". . o o",". o o o",b}&{" .";" o"})&" "&tocol({". ";"o "}&{a,"o . . .","o o . .","o o o .",b}),,1)))
Basic string concatenation using vertical and horizontal arrays with alternating tocol() and torow() transformations.

Charcoal, 34 bytes
≔⪪”{∨8≔➙↓@}▶νI”χυ↑Eυ⭆ι×λχ→↑×⮌υχUE¹
Try it online! Link is to verbose version of code. Explanation:
≔⪪”{∨8≔➙↓@}▶νI”χυ
Get a list of strings o....o.... oo...oo... ooo..ooo.. oooo.oooo. ooooo...... (Boring I know, but my arithmetical approaches came out up to 50% longer.)
↑Eυ⭆ι×λχ
Print each string vertically, repeating each character 10 times.
→
Leave an extra column between each half.
↑×⮌υχ
Print the strings vertically in reverse order, repeating each string 10 times.
UE¹
Leave more extra columns.