chiark / gitweb /
wip alchemists clarifications
[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   ${\ anychip(2) }
270 END
271
272   $o .= $exchline->(0, 4, <<END);
273   ${\ anychip(4) }
274 END
275
276   $o .= exposition(<<END);
277 If the previous chip placed was a pumpkin,
278 you may exchange it as follows:
279
280  
281 END
282
283   $o;
284 }
285
286 sub book_lotus () {
287   my $o = general_book($lotus, [8, 0]);
288
289   $o .= <<END;
290 /Times-Bold findfont 15 scalefont setfont $black
291
292 gsave
293   0.36 0.38 dc translate
294   0.80 dup scale
295   gsave ${\ chip($lotus,0) } grestore
296
297   9 -4 moveto (=) show
298 grestore
299 gsave
300   0.20 0.15 dc translate
301   0.80 dup scale
302   gsave ${\ chip($pumpkin,0) } grestore
303   chip 0.5 mul 0 translate
304   gsave ${\ chip($pumpkin,0) } grestore
305
306   10 -4.5 moveto (... +1) show
307
308 grestore
309 END
310 <<END;
311 gsave
312   0.16 0.20  dc translate
313   0.60 dup scale
314
315   gsave ${\ veepsspot(chip($pumpkin, 0)) } grestore
316   gsave 0 20 translate ${\ chip($purple, 1) } grestore
317   27 0 translate
318 grestore
319 END
320
321
322   $o .= exposition(<<END);
323 The value of this chip is
324 1 higher than the number of pumpkins
325 previously placed in the pot (but max.4)
326 END
327
328   $o;
329 }
330
331 sub book_purple () {
332   my $o = general_book($purple, [10, 1]);
333
334   my $input = <<END;
335   gsave ${\ veepsspot(chip($pumpkin, 0)) } grestore
336   gsave 0 20 translate ${\ chip($purple, 1) } grestore
337 END
338
339   $o .= <<END;
340 /Times-Bold findfont 15 scalefont setfont $black
341 gsave
342   0.16 0.20  dc translate
343   0.60 dup scale
344
345 $input
346   8 -12 moveto (...) show
347   27 0 translate
348 $input
349 grestore
350
351 gsave
352 /Times-Roman findfont
353   32 scalefont setfont
354   0.48 0.14 dc moveto (}) show
355 grestore
356
357 gsave
358    0.83 0.25 dc translate
359    0.85 dup scale
360    ${\ chip($anychip, 0) }
361 grestore
362
363 gsave
364   0.72 0.22 dc translate
365   0.50 dup scale
366    $black 0 0 moveto (?+?) show
367 grestore
368 END
369
370   $o .= exposition(<<END);
371 For each pumpkin in the pot (but
372  not more than the number of purple chips),
373 add up the VPs of the covered spaces.
374 Buy 1/2 chips of up to that total value.
375 END
376
377   $o;
378 }
379
380 sub book_yellow () {
381   my $o = general_book($yellow, [qw(5 11 18)]);
382
383   $o .= <<END;
384 gsave
385   0.52 0.32 dc translate
386   0.80 dup scale
387   -26 0 translate
388   gsave 26 0 translate ${\ chip($yellow,0) } grestore
389   gsave 46 0 translate ${\ chip($pumpkin,0) } grestore
390   34 14 translate
391   -22.5 rotate
392   1 -1 scale
393   ${\ arrow_any("
394     gsave
395      arrowlen 0 translate
396   -85 rotate
397     -40 0
398       40
399       300 0 arc
400     stroke
401     grestore
402   ") }
403 grestore
404 END
405
406   my $exchip = sub {
407     my ($that, $pips) = @_;
408     <<END;
409   gsave ${\ chip($that, $pips) } grestore
410 END
411   };
412   my $exslash = sub {
413     <<END;
414     8 -5 moveto (/) show
415     20 0 translate
416 END
417   };
418   
419   $o .= exposition(<<END);
420 Move up to 1/2/4 pumpkins
421 already in your pot, to after the yellow.
422 (always immediately after, only one space,
423 regardless of other rules)
424 END
425
426   $o;
427 }
428
429 sub black_common ($) {
430   my ($second) = @_;
431   my $font = <<END;
432 /Helvetica-Bold findfont 5.5 scalefont setfont
433 END
434   <<END;
435     gsave
436       .45 .27 dc translate
437       droplet_image
438       3.5 -1 translate .7 dup scale
439       arrow
440     grestore
441     gsave
442       .40 .08 dc translate
443       $font
444       ($second) 
445         dup stringwidth .5 mul exch .5 mul exch translate
446         dup stringwidth -1 mul exch -1 mul exch moveto show
447       .7 dup scale 7 3 translate
448       ${\ ruby() }
449     grestore
450     gsave
451       .15 .37 dc translate
452
453       8.2 3.5 moveto
454       $font
455       (closest) show
456
457       .7 dup scale
458       gsave
459         17 0 translate
460         180 rotate
461         arrow
462       grestore
463       gsave
464         35 0 translate
465         arrow
466         newpath 0 0 moveto -18 0 rlineto stroke
467       grestore
468
469       ${\ chip($pumpkin,0) }
470
471       51 0 translate
472       ${\ chip($moth,   1) }
473     grestore
474 END
475 }
476
477 sub book_black_pair () {
478   my $o = general_book($moth, [10, 1]);
479
480   $o .= num_players(2);
481   $o .= black_common('equal:');
482
483   $o .= exposition(<<END);
484 The player with the shortest distance
485 between a black chip and a pumpkin
486 gets to move their droplet.
487
488 If tied, both players get a ruby.
489 END
490
491   $o;
492 }
493
494 sub book_black_more () {
495   my $o = general_book($moth, [10, 1]);
496
497   $o .= num_players(3,'+');
498   $o .= black_common('2nd:');
499
500   $o .= exposition(<<END);
501
502 The player(s) with the shortest distance
503 between a black chip and a pumpkin
504 get to move their droplet.
505 If only one player won a droplet, the
506 player(s) with the next-shortest distance
507 get a ruby.
508 END
509
510   $o;
511 }
512 ps_start('7 30 translate');
513
514 sub def_image ($$) {
515   my ($ncomps, $name) = @_;
516
517   my $ext = $ncomps == 1 ? 'pgm' : $ncomps == 3 ? 'ppm' : die;
518   my $oper = $ncomps == 1 ? 'image' :
519              $ncomps == 3 ? 'false 3 colorimage' : die;
520   my $magic = $ncomps == 1 ? 'P2' : $ncomps == 3 ? 'P3' : die;
521
522   open B, "$name.$ext" or die $!;
523   <B> eq "$magic\n" or die "$name.$ext expected $magic";
524   local $/ = undef;
525   my @d = split ' ', <B>;
526   close B;
527
528   my $w = shift @d;
529   my $h = shift @d;
530   my $maxval = shift @d;
531   die unless $maxval eq 255;
532
533   my $sz = $w/17.0;
534   my $hsz = $sz/2;
535
536   $ps_framing .= <<END . '{<';
537 /${name}_image {
538   $w $h 8 
539   [ -$sz 0 0  -$sz  $w 2 div  $h 2 div  ]
540 END
541   for (my $i=0; $i< $w*$h*$ncomps; $i++) {
542     $ps_framing .= sprintf "%02x", shift @d;
543   }
544   $ps_framing .= <<END;
545 >} $oper
546 } def
547 END
548 }
549
550 sub tile {
551   my @tiles;
552   my $index = 1;
553
554   foreach my $spec (@_) {
555     my @datas;
556     foreach my $stem (split m{/}, $spec) {
557       my $func = $stem;
558       $func =~ y/-/_/;
559       $func = ${*::}{"book_$func"} // die "$func ?";
560       my $data = $func->();
561       $data .= <<END;
562     /Courier-Bold findfont 6 scalefont setfont
563     -0.98 0.94 dc moveto
564     ($index) show
565 END
566       open F, ">book-$stem.ps" or die $!;
567       print F $ps_framing, $data, "\nshowpage\n" or die $!;
568       close F or die $!;
569       push @datas, $data;
570     }
571     foreach my $dd (@datas) {
572       push @tiles, $dd;
573     }
574     $index++;
575   }
576
577   my $pos = 0;
578   my $o = '';
579   my $showpage = sub {
580     $o .= <<END;
581 showpage
582 $page_pre
583 END
584   };
585
586   while (my $d = pop @tiles) {
587     if ($pos >= 5) {
588       $pos -= 5;
589       $showpage->();
590     }
591     $o .= <<END;
592 gsave
593 END
594     if ($pos < 3) {
595       $o .= <<END
596         0   th 10 add  $pos mul  translate
597 END
598     } else {
599       $o .= <<END
600 tw 2 mul 7 add
601    dup    th add  0 translate
602    90 rotate
603    $pos 3 sub mul  0 translate
604 END
605     }
606     $o .= "\n".$d."\n";
607     $o .= <<END;
608 grestore
609 END
610     $pos++;
611   }
612   $showpage->();
613   $o;
614 }
615
616 def_image(1, "bag");
617 def_image(1, "pot");
618 def_image(3, "droplet");
619
620 print $ps_framing or die $!;
621
622 print "gsave\n" or die $!;
623
624 print tile(@ARGV) or die $! if 1;
625