+sub dispdomino {
+ my $code = shift;
+ my ($prefix, @dots) = $code =~ m/\A(-?)(\d)(\d)/ or return $code;
+ # unicode glyph alternative as DOMINO TILE HORIZONTAL-0a-0b
+ return $prefix . chr(0x1F031 + ($dots[0] * 7) + $dots[1]);
+}
+
+sub dispdash {
+ my $code = shift;
+ my ($prefix, @dots) = $code =~ m/\A(-?)(\d)(\d)/ or return $code;
+ my ($w, $h) = (max(6, 4 * max(@dots)), 9);
+ my ($w0, $w1) = ($w / $dots[0], $dots[1] ? $w / $dots[1] : 1);
+ return sprintf(
+ '<svg height="20"%s viewBox="-.5 -.5 %s %s">'
+ . '<path d="%s" /></svg>',
+ $prefix && ' style="opacity:.5"',
+ $w + 1, $h + 1, join(' ',
+ "m0,$h l+$w0,-$h" x $dots[0], # slashes
+ "m0,$h l-$w1,-$h" x $dots[1], # backslashes
+ )
+ );
+}
+
+sub dispblock {
+ my $code = shift;
+ my ($prefix, $shape, $rotate) = $code =~ m/\A(-?)(\w)(\d?)/
+ or return $code;
+ my %path = (
+ S => 'm0,1h1v-1h1',
+ Z => 'm0,0h1v1h1',
+ L => 'm0,0v2h1',
+ v => 'm0,0v1h1',
+ J => 'm1,0v2h-1',
+ T => 'm0,0h2.5h-1.5v1',
+ O => 'm0,0h1v1h-1',
+ I => 'm0,1h3',
+ i => 'm0,1h2',
+ o => 'm0,1h1',
+ );
+ my %col = (
+ S => 120, Z => 0, J => 240, L => 30, T => 300, O => 60, I => 180,
+ v => '45,50%', i => '165,50%', o => '165,0%',
+ );
+ s/\z(?<!%)/,100%/ for values %col;
+ my @gaps = (grep $_,
+ $code =~ /[Ii]$|T[23]|L3?$|S1|Z$|J1|v3?$/ ? 'gapl1' : (),
+ $code =~ /T$|L2|Z$/ ? 'gapr1' : (),
+ );
+ return sprintf(
+ '<svg height="24" viewBox="-.5 -.5 %s 4"%s>'
+ . '<path d="%s" stroke="%s" fill="none"%s /></svg>',
+ $code eq 'I' ? 4 : $code =~ /T3|[LJO]$|[Iio]1/ ? 2 : 3,
+ @gaps ? qq( class="@gaps") : '',
+ $path{$shape}, "hsl($col{$shape},50%)", join('',
+ $rotate && sprintf(' transform="rotate(%s 1 1)"', $rotate * 90),
+ $prefix && ' style="opacity:.5"',
+ ),
+ );
+}
+