From 3a319b3920fb724257a0f21f43acf16f3595c72e Mon Sep 17 00:00:00 2001 From: dakkar Date: Sun, 31 Jul 2016 16:38:20 +0100 Subject: builder for a Dobble-like deck --- dobble.pl | 168 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ dobble.txt | 68 +++++++++++++++++++++++++ 2 files changed, 236 insertions(+) create mode 100644 dobble.pl create mode 100644 dobble.txt diff --git a/dobble.pl b/dobble.pl new file mode 100644 index 0000000..d219351 --- /dev/null +++ b/dobble.pl @@ -0,0 +1,168 @@ +#!/usr/bin/env perl +use strict; +use warnings; +use 5.024; +use utf8; +use experimental 'signatures'; +use open ':std','utf8'; + +# see http://images.math.cnrs.fr/Dobble-et-la-geometrie-finie.html + +package FiniteProjectivePlane { + use Moo; + + has mod => (is=>'ro',required=>1); + + sub Inf() { 0+'Inf' } + + sub multadd($self,$x,$a,$m) { + return $a if $x == Inf; + + return ($x*$a+$m) % $self->mod; + } + + sub range($self) { + return 0..($self->mod-1); + } + + sub line($self,$a,$m) { + my @ret; + + if ($a == Inf) { # vertical line + @ret = map { [$m,$_] } $self->range; + push @ret, [Inf,Inf]; # odd one out + } + else { + @ret = map { + [$_,$self->multadd($_,$a,$m)] + } $self->range,Inf; + } + + return @ret; + } + + sub scan_lines($self) { + my %lines; + for my $a ($self->range,Inf) { + for my $m ($self->range) { + $lines{"$a-$m"} = [ $self->line($a,$m) ]; + } + } + $lines{"Inf-Inf"} = [ $self->line(Inf,Inf) ]; + return \%lines; + } +}; + +package CardsTable { + use Moo; + + has symbols => (is=>'lazy'); + has _symbol_for => (is=>'ro',default=>sub{ +{} }); + has _last_symbol_used => (is=>'rw',default=>0); + + has padding => (is=>'ro',default=>0); + + around BUILDARGS => sub($orig,$self,@args) { + my $args = $self->$orig(@args); + delete $args->{symbols} unless defined $args->{symbols}; + return $args; + }; + + sub _build_symbols($self) { + return [ + split //, + q{🌃🌈🌉🌍🌙🌞🌟🌧🌰🌷🌶🍁🍄🍇🍋🍒🍔🍕🍞🍟🍨🍩🍰🍵🍺🎅🎉🎜🎡🎥🎨🎮🎲🎷🎸🎺🎻🏠🐌🐍🐔🐖🐘🐙🐞🐢🐧🐨🐫🐵🐼👂👁💋👅👒👓👕👖👜👟💉💊💎💲📚📸📱🔋🔦🔩🖂🗹🚀🚁🚲🁂✀✐☃☢☣☺⚽♛⛺😈👼👾💀💄}, + ]; + } + + sub _next_symbol($self) { + my $l = $self->_last_symbol_used; + my $symbol = $self->symbols->[$l]; + $self->_last_symbol_used($l+1); + return [$symbol,$l]; + } + + sub symbol_for($self,$point) { + my $pointid = join ',',$point->@*; + if (my $s = $self->_symbol_for->{$pointid}) { + return $s; + } + else { + my $s = $self->_next_symbol; + $self->_symbol_for->{$pointid} = $s; + return $s; + } + } + + sub table($self,$lines) { + my @ret = (''); + + # this thing is isomorphic to its dual! we don't need to pivot + # lines/points, it's all the same + + for my $line (sort keys $lines->%*) { + my @row; + for my $point ($lines->{$line}->@*) { + + my $symbol = $self->symbol_for($point); + my $column = $symbol->[1]; + $row[$column] = $symbol->[0]; + } + push @ret,' '; + for my $s (@row) { + next unless $s or $self->padding; + push @ret, ' '; + } + push @ret, ' '; + } + push @ret,'
',$s||' ','
'; + return \@ret; + } +}; + +sub load_symbols($file) { + return undef unless $file; + use Path::Tiny; + return [ path($file)->lines_utf8 ]; +} + +use Getopt::Long::Descriptive; + +my ($opt,$usage) = describe_options( + '%c %o', + [ 'order|o=i', 'order of the finite field', { required => 1 } ], + [ 'padding|p!', 'vertically align symbols', { default => 0 } ], + [ 'symbols|s=s', 'file with one symbol per line' ], + [], + ['help|h', 'show this help text', { shortcircuit => 1 } ], +); +print($usage->text), exit if $opt->help; + +my $order = $opt->order; +my $plane = FiniteProjectivePlane->new(mod=>$order); +my $table = CardsTable->new( + padding=>$opt->padding, + symbols => load_symbols($opt->symbols), +); + +# sanity check +my $needed = $order*$order+$order+1; +if ((my $actual = $table->symbols->@*) < $needed) { + die "For order $order, we need $needed symbols, but we only have $actual; please pass a (larger) file to --symbols\n"; +} + +say <<'HTML'; + + + + Dobble of order $order + + +HTML + +say for $table->table($plane->scan_lines)->@*; + +say <<'HTML'; + + +HTML diff --git a/dobble.txt b/dobble.txt new file mode 100644 index 0000000..aa182fc --- /dev/null +++ b/dobble.txt @@ -0,0 +1,68 @@ +Dobble http://images.math.cnrs.fr/Dobble-et-la-geometrie-finie.html + +projective finite plane based on ℤ₇ + +one point per card, one line per symbol, two points per line, 7+1 +lines per point + +cards: 7² (point pairs) + 7 (parallel lines) + 1 (extra parallel, the +"vertical" one) (???) + +------- + +let's try ℤ₃p + +(0,0) (1,0) (2,0) | (i,0) +(0,1) (1,1) (2,1) | (i,1) +(0,2) (1,2) (2,2) | (i,2) +------------------+ +(0,i) + +[a] x=0 (0,0) (0,1) (0,2) (0,i) +[b] x=1 (1,0) (1,1) (1,2) (0,i) +[c] x=2 (2,0) (2,1) (2,2) (0,i) +[d] y=0 (0,0) (1,0) (2,0) (i,0) +[e] y=1 (0,1) (1,1) (2,1) (i,0) +[f] y=2 (0,2) (1,2) (2,2) (i,0) +[g] x+y=0 (0,0) (1,2) (2,1) (i,1) +[h] x+y=1 (0,1) (1,0) (2,2) (i,1) +[i] x+y=2 (0,2) (1,1) (2,0) (i,1) +[j] x+2y=0 (0,0) (1,1) (2,2) (i,2) +[k] x+2y=1 (0,2) (1,0) (2,1) (i,2) +[l] x+2y=2 (0,1) (1,2) (2,0) (i,2) +[m] x+y=i (i,0) (i,1) (i,2) (0,i) + +13 lines=symbols, 13 point=cards + +card symbols +----- ------- +(0,0) a d g j +(0,1) a e h l +(0,2) a f i k +(0,i) abc m +(1,0) b d h k +(1,1) b e ij +(1,2) b fg l +(2,0) cd i l +(2,1) c e g k +(2,2) c f h j +(i,0) def m +(i,1) ghi m +(i,2) jklm + + +in "my" order: + +(i,2) jklm +(i,1) hgi m +(i,0) efd m +(0,i) abc m +(2,0) c d i l +(1,2) b f g l +(0,1) a e h l +(2,1) ce g k +(1,0) b dh k +(0,2) a f i k +(2,2) c f h j +(1,1) b e ij +(0,0) a d g j -- cgit v1.2.3