chiark / gitweb /
cut line
[quacks.git] / pumpkin-books.ps.pl
1 #!/usr/bin/perl -w
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
5
6 use strict;
7
8 BEGIN { unshift @INC, '.'; }
9 use Quacks;
10
11 sub veepsspot ($) {
12   my ($chip) = @_;
13   <<END;
14 gsave
15   0 chip -0.5 mul translate
16   $black
17   newpath 0 0 chip 0.5 mul 0 360 arc stroke
18   /Times-Bold findfont 7 scalefont setfont
19   -1 -5 translate
20   0 0 moveto (?) show
21   newpath
22    -1 -1 moveto 6 0 rlineto 0 7 rlineto -6 0 rlineto
23     closepath 0.5 setlinewidth stroke
24 grestore
25   $chip
26 END
27 }
28
29 sub anychip ($) {
30   my ($pips) = @_;
31   my $fsz = 10;
32   <<END;
33 gsave
34   chip -0.5 mul  $fsz -0.30 mul  moveto
35   /Helvetica-Bold findfont $fsz scalefont setfont
36   (Any) dup stringwidth  3 2 roll show
37   pop pop
38   20 0 translate
39   ${\ chip($anychip, $pips) }
40 grestore
41 END
42 }
43
44 sub ruby () { # put in gsave translate
45   <<END;
46   newpath
47     rubysz neg  0 moveto
48     0  rubysz neg lineto
49     rubysz      0 lineto
50     0      rubysz lineto
51     closepath
52     ${\ colour('1 .2 .2') } gsave fill grestore
53     $black 1 setlinewidth stroke
54 END
55 }
56
57 sub exposition ($) {
58   my ($text) = @_;
59   my $fontsz = 6;
60   my $o = <<END;
61     /Times-Roman findfont $fontsz scalefont setfont $black
62 END
63   $text =~ s/\n$//;
64   my @lines = split /\n/, $text;
65   foreach my $y (0..$#lines) {
66     my $l = $lines[$y];
67     $l =~ s/[()\\]/\\$&/g;
68     my $yd = $fontsz * (0.5*@lines - $y);
69     $o .= <<END;
70     0 0.70 dc moveto
71     0 $yd rmoveto
72     ($l)  dup stringwidth pop -0.5 mul 0 rmoveto
73     show
74 END
75   }
76   $o;
77 }
78
79 sub num_players ($;$) {
80   my ($n, $plus) = @_;
81   my $o = '';
82
83   $o .= <<END;
84   gsave
85     -0.94 0.50 dc translate
86     0.3 dup scale
87 END
88
89   $o .= <<END x $n;
90     pot_image 18 0 translate
91 END
92
93   $o .= <<END if defined $plus;
94     -9 -5 moveto
95     /Helvetica-Bold findfont 25 scalefont setfont
96     ($plus) show
97 END
98
99   $o .= <<END;
100   grestore
101 END
102   return $o;
103 }
104
105 sub general_book ($$) { # put in a gsave
106   my ($this, $costs) = @_;
107   my $o = <<END;
108 tw  0  translate
109
110 3 setlinewidth
111 0.9 setgray
112 newpath
113 0 0 dc moveto
114 0 1 dc lineto stroke
115
116 $black
117 1 setlinewidth
118
119 newpath
120 -1 0  dc moveto
121 +1 0  dc lineto
122 +1 1  dc lineto
123 -1 1  dc lineto
124 closepath stroke
125
126 newpath
127 0 0 dc         moveto
128 0 thirdlineh dc rlineto
129 -1 thirdlineh dc lineto stroke
130
131 /thirddivline {               % xprop
132   newpath
133   dup -3 div  0               dc moveto
134       -3 div  thirdlineh      dc lineto stroke
135                               %
136 } def
137
138 END
139
140   $o .= <<END if @$costs == 3;
141 1 thirddivline
142 2 thirddivline
143 END
144
145   foreach my $costi (0..2) {
146     my ($cost, $pips);
147     if (@$costs == 3) {
148       $cost = $costs->[$costi];
149       $pips = qw(1 2 4)[$costi];
150     } else {
151       next unless $costi == 1;
152       $cost = $costs->[0];
153       $pips = $costs->[1];
154     }
155     $o .= <<END
156   costfont setfont
157   -2.5 $costi add 3 div
158 gsave
159   dup costcirch dc translate
160   ${\ chip($this,$pips) }
161 grestore
162   costtexth exch costtextdx add exch dc moveto
163   $black
164   ($cost)
165   dup stringwidth pop  -0.5 mul  costtextsz neg  rmoveto
166   show
167 END
168   }
169
170   $o;
171 }
172   
173 sub book_green () {
174   my $o = general_book($green, [qw(5 9 15)]);
175
176   $o .= <<END;
177 gsave 0.25 0.15 dc translate ${\ chip($pumpkin,0) } grestore
178 gsave 0.25 0.40 dc translate ${\ chip($green,0) } grestore
179
180 /Times-Bold findfont
181 dup
182   6 scalefont setfont
183   0.40 0.17 dc moveto (last) show
184 %
185   8 scalefont setfont
186   0.40 0.09 dc moveto (3) show
187 /Times-Roman findfont
188   38 scalefont setfont
189   0.45 0.14 dc moveto (}) show
190
191 gsave
192   0.85 0.275 dc translate
193   ${\ ruby() }
194 grestore
195 END
196
197   $o .= exposition(<<END);
198 For each pumpkin in the last 3 chips,
199 receive 1 ruby.
200 But, not more rubies than the number
201 of green chips in your pot.
202 END
203
204   $o;
205 }
206   
207 sub book_red () {
208   my $o = general_book($red, [qw(4 9 16)]);
209
210   $o .= <<END;
211 gsave 0.35 0.30 dc translate ${\ chip($pumpkin,0) } grestore
212
213 /Times-Bold findfont
214   15 scalefont setfont
215   0.50 0.24 dc moveto (+1) show
216
217 END
218
219   $o .= exposition(<<END);
220 The next 1/2/4 pumpkins you place are
221 each moved one extra space.
222 (After applying any other special effects;
223 one extra space no matter how many reds)
224 END
225
226   $o;
227 }
228
229 sub book_blue () {
230   my $o = general_book($blue, [qw(4 10 18)]);
231
232   $o .= <<END;
233 /Times-Bold findfont 15 scalefont setfont $black
234
235 gsave
236   0.50 0.65 dc translate
237   0.65 dup scale
238   gsave -10 0 translate ${\ chip($pumpkin,0) } grestore
239   arrow
240 grestore
241 END
242
243   my $exchip = sub {
244     my ($that, $pips) = @_;
245     <<END;
246   gsave ${\ chip($that, $pips) } grestore
247 END
248   };
249   my $exslash = sub {
250     <<END;
251     8 -5 moveto (/) show
252     20 0 translate
253 END
254   };
255   
256   my $exchline = sub {
257     my ($y, $pips, $content) = @_;
258     <<END;
259 gsave 
260   0.16 0.15 0.16 $y mul add dc translate
261   0.60 dup scale
262   gsave ${\ chip($blue,$pips) } grestore
263   8 -3 moveto (:) show
264   3 0 translate
265   0.8 dup scale
266   21 0 translate
267 $content
268 grestore
269 END
270   };
271
272   $o .= $exchline->(2, 1, <<END);
273   ${\ $exchip->($green,1) }  ${\ $exslash->() }
274   ${\ $exchip->($red,  1) }  ${\ $exslash->() }
275   ${\ $exchip->($blue, 1) }  ${\ $exslash->() }
276   ${\ $exchip->($yellow, 1) }
277 END
278
279   $o .= $exchline->(1, 2, <<END);
280   ${\ $exchip->($moth,1) }  ${\ $exslash->() }
281   ${\ $exchip->($purple,1) }  ${\ $exslash->() }
282   ${\ anychip(2) }
283 END
284
285   $o .= $exchline->(0, 4, <<END);
286   ${\ anychip(4) }
287 END
288
289   $o .= exposition(<<END);
290 If the previous chip placed was a pumpkin,
291 you may exchange it as follows:
292
293  
294 END
295
296   $o;
297 }
298
299 sub book_lotus () {
300   my $o = general_book($lotus, [8, 0]);
301
302   $o .= <<END;
303 /Times-Bold findfont 15 scalefont setfont $black
304
305 gsave
306   0.36 0.38 dc translate
307   0.80 dup scale
308   gsave ${\ chip($lotus,0) } grestore
309
310   9 -4 moveto (=) show
311 grestore
312 gsave
313   0.20 0.15 dc translate
314   0.80 dup scale
315   gsave ${\ chip($pumpkin,0) } grestore
316   chip 0.5 mul 0 translate
317   gsave ${\ chip($pumpkin,0) } grestore
318
319   10 -4.5 moveto (... +1) show
320
321 grestore
322 END
323 <<END;
324 gsave
325   0.16 0.20  dc translate
326   0.60 dup scale
327
328   gsave ${\ veepsspot(chip($pumpkin, 0)) } grestore
329   gsave 0 20 translate ${\ chip($purple, 1) } grestore
330   27 0 translate
331 grestore
332 END
333
334
335   $o .= exposition(<<END);
336 The value of this chip is
337 1 higher than the number of pumpkins
338 previously placed in the pot (but max.4)
339 END
340
341   $o;
342 }
343
344 sub book_purple () {
345   my $o = general_book($purple, [10, 1]);
346
347   my $input = <<END;
348   gsave ${\ veepsspot(chip($pumpkin, 0)) } grestore
349   gsave 0 20 translate ${\ chip($purple, 1) } grestore
350 END
351
352   $o .= <<END;
353 /Times-Bold findfont 15 scalefont setfont $black
354 gsave
355   0.16 0.20  dc translate
356   0.60 dup scale
357
358 $input
359   8 -12 moveto (...) show
360   27 0 translate
361 $input
362 grestore
363
364 gsave
365 /Times-Roman findfont
366   32 scalefont setfont
367   0.48 0.14 dc moveto (}) show
368 grestore
369
370 gsave
371    0.83 0.25 dc translate
372    0.85 dup scale
373    ${\ chip($anychip, 0) }
374 grestore
375
376 gsave
377   0.72 0.22 dc translate
378   0.50 dup scale
379    $black 0 0 moveto (?+?) show
380 grestore
381 END
382
383   $o .= exposition(<<END);
384 For each pumpkin in the pot (but
385  not more than the number of purple chips),
386 add up the VPs of the covered spaces.
387 Buy 1/2 chips of up to that total value.
388 END
389
390   $o;
391 }
392
393 sub book_yellow () {
394   my $o = general_book($yellow, [qw(7 12 19)]);
395
396   $o .= <<END;
397 gsave
398   0.50 0.62 dc translate
399   0.65 dup scale
400   -26 0 translate
401   gsave 26 0 translate ${\ chip($anychip,0) } grestore
402   gsave 46 0 translate ${\ chip($pumpkin,0) } grestore
403   gsave -30 rotate bag_image grestore
404   8 14 translate
405   -120 rotate
406   ${\ arrow_any("
407     gsave
408      arrowlen 0 translate
409   -85 rotate
410     putback_len neg 0
411       putback_len
412       240 0 arc
413     stroke
414     grestore
415   ") }
416 grestore
417 END
418
419   my $exchip = sub {
420     my ($that, $pips) = @_;
421     <<END;
422   gsave ${\ chip($that, $pips) } grestore
423 END
424   };
425   my $exslash = sub {
426     <<END;
427     8 -5 moveto (/) show
428     20 0 translate
429 END
430   };
431   
432   my $exchline = sub {
433     my ($y, $pips, $content) = @_;
434     <<END;
435 gsave 
436   0.20 0.12 0.16 $y mul add dc translate
437   0.60 dup scale
438   gsave ${\ chip($yellow,$pips) } grestore
439   8 -3 moveto (:) show
440   3 0 translate
441   0.8 dup scale
442   24 0 translate
443 $content
444 grestore
445 END
446   };
447
448   $o .= $exchline->(2, 1, <<END);
449   ${\ $exchip->($white,1) }  ${\ $exslash->() }
450   ${\ $exchip->($anychip,1) }  ${\ $exslash->() }
451   ${\ $exchip->($lotus,0) }
452 END
453
454   $o .= $exchline->(1, 2, <<END);
455   ${\ $exchip->($white,2) }  ${\ $exslash->() }
456   ${\ $exchip->($anychip,2) }
457 END
458
459   $o .= $exchline->(0, 4, <<END);
460   ${\ $exchip->($white,3) }  ${\ $exslash->() }
461   ${\ $exchip->($anychip,4) }
462 END
463
464   $o .= exposition(<<END);
465 Put a chip, no bigger than the yellow,
466 whose next placed chip is a pumpkin,
467 back in your bag.                
468
469  
470 END
471
472   $o;
473 }
474
475 sub black_common ($) {
476   my ($second) = @_;
477   my $font = <<END;
478 /Helvetica-Bold findfont 5.5 scalefont setfont
479 END
480   <<END;
481     gsave
482       .45 .27 dc translate
483       droplet_image
484       3.5 -1 translate .7 dup scale
485       arrow
486     grestore
487     gsave
488       .40 .08 dc translate
489       $font
490       ($second) 
491         dup stringwidth .5 mul exch .5 mul exch translate
492         dup stringwidth -1 mul exch -1 mul exch moveto show
493       .7 dup scale 7 3 translate
494       ${\ ruby() }
495     grestore
496     gsave
497       .15 .37 dc translate
498
499       8.2 3.5 moveto
500       $font
501       (closest) show
502
503       .7 dup scale
504       gsave
505         17 0 translate
506         180 rotate
507         arrow
508       grestore
509       gsave
510         35 0 translate
511         arrow
512         newpath 0 0 moveto -18 0 rlineto stroke
513       grestore
514
515       ${\ chip($pumpkin,0) }
516
517       51 0 translate
518       ${\ chip($moth,   1) }
519     grestore
520 END
521 }
522
523 sub book_black_pair () {
524   my $o = general_book($moth, [10, 1]);
525
526   $o .= num_players(2);
527   $o .= black_common('equal:');
528
529   $o .= exposition(<<END);
530 The player with the shortest distance
531 between a black chip and a pumpkin
532 gets to move their droplet.
533
534 If tied, both players get a ruby.
535 END
536
537   $o;
538 }
539
540 sub book_black_more () {
541   my $o = general_book($moth, [10, 1]);
542
543   $o .= num_players(3,'+');
544   $o .= black_common('2nd:');
545
546   $o .= exposition(<<END);
547
548 The player(s) with the shortest distance
549 between a black chip and a pumpkin
550 get to move their droplet.
551 If only one player won a droplet, the
552 player(s) with the next-shortest distance
553 get a ruby.
554 END
555
556   $o;
557 }
558 ps_start('7 30 translate');
559
560 sub def_image ($$) {
561   my ($ncomps, $name) = @_;
562
563   my $ext = $ncomps == 1 ? 'pgm' : $ncomps == 3 ? 'ppm' : die;
564   my $oper = $ncomps == 1 ? 'image' :
565              $ncomps == 3 ? 'false 3 colorimage' : die;
566   my $magic = $ncomps == 1 ? 'P2' : $ncomps == 3 ? 'P3' : die;
567
568   open B, "$name.$ext" or die $!;
569   <B> eq "$magic\n" or die "$name.$ext expected $magic";
570   local $/ = undef;
571   my @d = split ' ', <B>;
572   close B;
573
574   my $w = shift @d;
575   my $h = shift @d;
576   my $maxval = shift @d;
577   die unless $maxval eq 255;
578
579   my $sz = $w/17.0;
580   my $hsz = $sz/2;
581
582   $ps_framing .= <<END . '{<';
583 /${name}_image {
584   $w $h 8 
585   [ -$sz 0 0  -$sz  $w 2 div  $h 2 div  ]
586 END
587   for (my $i=0; $i< $w*$h*$ncomps; $i++) {
588     $ps_framing .= sprintf "%02x", shift @d;
589   }
590   $ps_framing .= <<END;
591 >} $oper
592 } def
593 END
594 }
595
596 sub tile {
597   my @tiles;
598   my $index = 1;
599
600   foreach my $spec (@_) {
601     my @datas;
602     foreach my $stem (split m{/}, $spec) {
603       my $func = $stem;
604       $func =~ y/-/_/;
605       $func = ${*::}{"book_$func"} // die "$func ?";
606       my $data = $func->();
607       $data .= <<END;
608     /Courier-Bold findfont 6 scalefont setfont
609     -0.98 0.94 dc moveto
610     ($index) show
611 END
612       open F, ">book-$stem.ps" or die $!;
613       print F $ps_framing, $data, "\nshowpage\n" or die $!;
614       close F or die $!;
615       push @datas, $data;
616     }
617     foreach my $dd (@datas) {
618       push @tiles, $dd;
619     }
620     $index++;
621   }
622
623   my $pos = 0;
624   my $o = '';
625   my $showpage = sub {
626     $o .= <<END;
627 showpage
628 $page_pre
629 END
630   };
631
632   while (my $d = pop @tiles) {
633     if ($pos >= 5) {
634       $pos -= 5;
635       $showpage->();
636     }
637     $o .= <<END;
638 gsave
639 END
640     if ($pos < 3) {
641       $o .= <<END
642         0   th 10 add  $pos mul  translate
643 END
644     } else {
645       $o .= <<END
646 tw 2 mul 7 add
647    dup    th add  0 translate
648    90 rotate
649    $pos 3 sub mul  0 translate
650 END
651     }
652     $o .= "\n".$d."\n";
653     $o .= <<END;
654 grestore
655 END
656     $pos++;
657   }
658   $showpage->();
659   $o;
660 }
661
662 def_image(1, "bag");
663 def_image(1, "pot");
664 def_image(3, "droplet");
665
666 print $ps_framing or die $!;
667
668 print "gsave\n" or die $!;
669
670 print tile(@ARGV) or die $! if 1;
671