2 # Books of Pumpkins, etc. Extensions to Quacks of Quedlinburg
3 # SPDX-License-Identifier: GPL-3.0-or-later OR CC-BY-SA-4.0
4 # Copyright 2020-2021 Ian Jackson
8 BEGIN { unshift @INC, '.'; }
11 my $anychip = ['0.8', '0'];
13 our $page_pre = <<END;
22 if ($c =~ m{^(\d+)/(\d+)/(\d+)$}) {
23 return (join ' ', map { $_ / 255.0 } ($1,$2,$3)).' setrgbcolor';
24 } elsif ($c =~ m/[^ 0-9.]/) {
26 } elsif ($c =~ m/^\s*\S+\s*$/) {
28 } elsif ($c =~ m/./) {
29 return "$c setrgbcolor";
35 our $black = colour('0');
38 my ($cary, $pips) = @_; # put in a gsave translate
41 0 0 chip 0.5 mul 0 360 arc
42 gsave 1 setlinewidth $black stroke grestore
43 ${\ colour($cary->[0]) } fill
47 ${\ colour($cary->[1]) }
54 spot 0.5 sqrt mul 1.1 mul dup
61 $spot->( 0, 0) if $pips & 1;
62 $spot->(-1,-1) if $pips & 6;
63 $spot->(+1,+1) if $pips & 6;
64 $spot->(-1,+1) if $pips & 4;
65 $spot->(+1,-1) if $pips & 4;
74 0 chip -0.5 mul translate
76 newpath 0 0 chip 0.5 mul 0 360 arc stroke
77 /Times-Bold findfont 7 scalefont setfont
81 -1 -1 moveto 6 0 rlineto 0 7 rlineto -6 0 rlineto
82 closepath 0.5 setlinewidth stroke
93 chip -0.5 mul $fsz -0.30 mul moveto
94 /Helvetica-Bold findfont $fsz scalefont setfont
95 (Any) dup stringwidth 3 2 roll show
98 ${\ chip($anychip, $pips) }
103 sub ruby () { # put in gsave translate
111 ${\ colour('1 .2 .2') } gsave fill grestore
112 $black 1 setlinewidth stroke
120 /Times-Roman findfont $fontsz scalefont setfont $black
123 my @lines = split /\n/, $text;
124 foreach my $y (0..$#lines) {
126 $l =~ s/[()\\]/\\$&/g;
127 my $yd = $fontsz * (0.5*@lines - $y);
131 ($l) dup stringwidth pop -0.5 mul 0 rmoveto
138 sub num_players ($;$) {
144 -0.94 0.50 dc translate
149 pot_image 18 0 translate
152 $o .= <<END if defined $plus;
154 /Helvetica-Bold findfont 25 scalefont setfont
164 sub general_book ($$) { # put in a gsave
165 my ($this, $costs) = @_;
187 0 thirdlineh dc rlineto
188 -1 thirdlineh dc lineto stroke
190 /thirddivline { % xprop
192 dup -3 div 0 dc moveto
193 -3 div thirdlineh dc lineto stroke
199 $o .= <<END if @$costs == 3;
204 foreach my $costi (0..2) {
207 $cost = $costs->[$costi];
208 $pips = qw(1 2 4)[$costi];
210 next unless $costi == 1;
216 -2.5 $costi add 3 div
218 dup costcirch dc translate
219 ${\ chip($this,$pips) }
221 costtexth exch costtextdx add exch dc moveto
224 dup stringwidth pop -0.5 mul costtextsz neg rmoveto
233 my $o = general_book($green, [qw(5 9 15)]);
236 gsave 0.25 0.15 dc translate ${\ chip($pumpkin,0) } grestore
237 gsave 0.25 0.40 dc translate ${\ chip($green,0) } grestore
242 0.40 0.17 dc moveto (last) show
245 0.40 0.09 dc moveto (3) show
246 /Times-Roman findfont
248 0.45 0.14 dc moveto (}) show
251 0.85 0.275 dc translate
256 $o .= exposition(<<END);
257 For each pumpkin in the last 3 chips,
259 But, not more rubies than the number
260 of green chips in your pot.
267 my $o = general_book($red, [qw(4 9 16)]);
270 gsave 0.35 0.30 dc translate ${\ chip($pumpkin,0) } grestore
274 0.50 0.24 dc moveto (+1) show
278 $o .= exposition(<<END);
279 The next 1/2/4 pumpkins you place are
280 each moved one extra space.
281 (After applying any other special effects;
282 one extra space no matter how many reds)
289 my $o = general_book($blue, [qw(4 10 18)]);
292 /Times-Bold findfont 15 scalefont setfont $black
295 0.50 0.65 dc translate
297 gsave -10 0 translate ${\ chip($pumpkin,0) } grestore
303 my ($that, $pips) = @_;
305 gsave ${\ chip($that, $pips) } grestore
316 my ($y, $pips, $content) = @_;
319 0.16 0.15 0.16 $y mul add dc translate
321 gsave ${\ chip($blue,$pips) } grestore
331 $o .= $exchline->(2, 1, <<END);
332 ${\ $exchip->($green,1) } ${\ $exslash->() }
333 ${\ $exchip->($red, 1) } ${\ $exslash->() }
334 ${\ $exchip->($blue, 1) } ${\ $exslash->() }
335 ${\ $exchip->($yellow, 1) }
338 $o .= $exchline->(1, 2, <<END);
339 ${\ $exchip->($moth,1) } ${\ $exslash->() }
340 ${\ $exchip->($purple,1) } ${\ $exslash->() }
344 $o .= $exchline->(0, 4, <<END);
348 $o .= exposition(<<END);
349 If the previous chip placed was a pumpkin,
350 you may exchange it as follows:
359 my $o = general_book($lotus, [8, 0]);
362 /Times-Bold findfont 15 scalefont setfont $black
365 0.36 0.38 dc translate
367 gsave ${\ chip($lotus,0) } grestore
372 0.20 0.15 dc translate
374 gsave ${\ chip($pumpkin,0) } grestore
375 chip 0.5 mul 0 translate
376 gsave ${\ chip($pumpkin,0) } grestore
378 10 -4.5 moveto (... +1) show
384 0.16 0.20 dc translate
387 gsave ${\ veepsspot(chip($pumpkin, 0)) } grestore
388 gsave 0 20 translate ${\ chip($purple, 1) } grestore
394 $o .= exposition(<<END);
395 The value of this chip is
396 1 higher than the number of pumpkins
397 previously placed in the pot (but max.4)
404 my $o = general_book($purple, [10, 1]);
407 gsave ${\ veepsspot(chip($pumpkin, 0)) } grestore
408 gsave 0 20 translate ${\ chip($purple, 1) } grestore
412 /Times-Bold findfont 15 scalefont setfont $black
414 0.16 0.20 dc translate
418 8 -12 moveto (...) show
424 /Times-Roman findfont
426 0.48 0.14 dc moveto (}) show
430 0.83 0.25 dc translate
432 ${\ chip($anychip, 0) }
436 0.72 0.22 dc translate
438 $black 0 0 moveto (?+?) show
442 $o .= exposition(<<END);
443 For each pumpkin in the pot (but
444 not more than the number of purple chips),
445 add up the VPs of the covered spaces.
446 Buy 1/2 chips of up to that total value.
453 my $o = general_book($yellow, [qw(7 12 19)]);
457 0.50 0.62 dc translate
460 gsave 26 0 translate ${\ chip($anychip,0) } grestore
461 gsave 46 0 translate ${\ chip($pumpkin,0) } grestore
462 gsave -30 rotate bag_image grestore
479 my ($that, $pips) = @_;
481 gsave ${\ chip($that, $pips) } grestore
492 my ($y, $pips, $content) = @_;
495 0.20 0.12 0.16 $y mul add dc translate
497 gsave ${\ chip($yellow,$pips) } grestore
507 $o .= $exchline->(2, 1, <<END);
508 ${\ $exchip->($white,1) } ${\ $exslash->() }
509 ${\ $exchip->($anychip,1) } ${\ $exslash->() }
510 ${\ $exchip->($lotus,0) }
513 $o .= $exchline->(1, 2, <<END);
514 ${\ $exchip->($white,2) } ${\ $exslash->() }
515 ${\ $exchip->($anychip,2) }
518 $o .= $exchline->(0, 4, <<END);
519 ${\ $exchip->($white,3) } ${\ $exslash->() }
520 ${\ $exchip->($anychip,4) }
523 $o .= exposition(<<END);
524 Put a chip, no bigger than the yellow,
525 whose next placed chip is a pumpkin,
534 sub black_common ($) {
537 /Helvetica-Bold findfont 5.5 scalefont setfont
543 3.5 -1 translate .7 dup scale
550 dup stringwidth .5 mul exch .5 mul exch translate
551 dup stringwidth -1 mul exch -1 mul exch moveto show
552 .7 dup scale 7 3 translate
571 newpath 0 0 moveto -18 0 rlineto stroke
574 ${\ chip($pumpkin,0) }
582 sub book_black_pair () {
583 my $o = general_book($moth, [10, 1]);
585 $o .= num_players(2);
586 $o .= black_common('equal:');
588 $o .= exposition(<<END);
589 The player with the shortest distance
590 between a black chip and a pumpkin
591 gets to move their droplet.
593 If tied, both players get a ruby.
599 sub book_black_more () {
600 my $o = general_book($moth, [10, 1]);
602 $o .= num_players(3,'+');
603 $o .= black_common('2nd:');
605 $o .= exposition(<<END);
607 The player(s) with the shortest distance
608 between a black chip and a pumpkin
609 get to move their droplet.
610 If only one player won a droplet, the
611 player(s) with the next-shortest distance
618 sub arrow_any ($) { <<END;
619 $black 1 setlinewidth
623 arrowhead dup neg exch rmoveto
624 arrowhead dup neg rlineto
625 arrowhead neg dup rlineto
631 our $ps_framing = <<END;
649 /costtextdx -0.03 def
650 /costfont /Heletica-BoldOblique findfont costtextsz scalefont def
654 % diagonal conversion
656 dup th mul % xprop yprop y
657 3 1 roll % y xprop yprop
658 bdiag mul neg % y xprop x-margin-at-this-height
659 tw add % y xprop x-width-at-this-height
660 exch mul % y x-width-at-this-height xprop
665 ${\ arrow_any("0 0 moveto arrowlen 0 rlineto") }
671 my ($ncomps, $name) = @_;
673 my $ext = $ncomps == 1 ? 'pgm' : $ncomps == 3 ? 'ppm' : die;
674 my $oper = $ncomps == 1 ? 'image' :
675 $ncomps == 3 ? 'false 3 colorimage' : die;
676 my $magic = $ncomps == 1 ? 'P2' : $ncomps == 3 ? 'P3' : die;
678 open B, "$name.$ext" or die $!;
679 <B> eq "$magic\n" or die "$name.$ext expected $magic";
681 my @d = split ' ', <B>;
686 my $maxval = shift @d;
687 die unless $maxval eq 255;
692 $ps_framing .= <<END . '{<';
695 [ -$sz 0 0 -$sz $w 2 div $h 2 div ]
697 for (my $i=0; $i< $w*$h*$ncomps; $i++) {
698 $ps_framing .= sprintf "%02x", shift @d;
700 $ps_framing .= <<END;
710 foreach my $spec (@_) {
712 foreach my $stem (split m{/}, $spec) {
715 $func = ${*::}{"book_$func"} // die "$func ?";
716 my $data = $func->();
718 /Courier-Bold findfont 6 scalefont setfont
722 open F, ">book-$stem.ps" or die $!;
723 print F $ps_framing, $data, "\nshowpage\n" or die $!;
727 foreach my $dd (@datas) {
742 while (my $d = pop @tiles) {
752 0 th 10 add $pos mul translate
757 dup th add 0 translate
759 $pos 3 sub mul 0 translate
774 def_image(3, "droplet");
776 print $ps_framing or die $!;
778 print "gsave\n" or die $!;
780 print tile(@ARGV) or die $! if 1;