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