%!PS-Adobe-3.0 %% Hex map generation PostScript program %% by Thomas W. Phinney %% created 2001 %% revised 2011/02/16 %% Licensed under Creative Commons Attribution 3.0 (CC-BY 3.0) %% %% To generate a PDF from this code, change the extension to .EPS, then drop on Adobe Acrobat Distiller, %% If creating a PDF, remember to first change your paper size settings in Distiller to match the board size. %% Or, change the extension to EPS, and place the file in a PostScript drawing program such as Adobe Illustrator. %% Because this is hand-coded PostScript it produces very compact files. :) %% %% You can adjust variables below to change the type of grid (hexes vs offset squares), %% size of the board, turn space numbering on/off, adjust line weight, adjust size of spaces, and so forth. %% %% All numbers without a measuring system specified are in points (1/72 inch). %% %% The key decision is "boardtype" (values are 0 for standard, 1 for hexagonal, 2 for %% offset pseudo-hexagonal squares) %% A standard board has hexes covering the whole sheet. A hexagonal "arena style" %% board has a bunch of hexes themselves arranged to form a large hexagon. Pseudo-hex %% squares have each column offset vertically by half a square, and may optionally have their widths %% calculated so that they have the same effective size as a hex. %% %% Note that hexes which are 1" across from top to bottom do not fit cross-wise quite the way a %% non-geometrically inclined person might expect. A sheet of paper 42" high by 72" across can have %% 82 columns of 1" hexes. %% %% For boards that are larger than the physical paper size, %% my assumption is that the user will design the board at full intended design size, %% and then use the "tiling" feature of their printer driver, Adobe Acrobat or their graphics program %% at print time, splitting the map into individual pages which will be cut and taped together in some way. %% %% To do a "full bleed" hex map (one where the hexes go right off the edge of the map), %% you can set an appropriate negative margin value. Use half the hex height vertically, %% and half a hexside horizontally. %% %% KNOWN LIMITATIONS: space numbering only works on boards up to 999x999 spaces %% %% Some standard settings: %% %% Empire/Logistics board: %% boardtype 2, spaceheight 1 inch, hexheight 1 inch, spacenumbering 1, %% paperheight 42 inch, paperwidth 72 inch %% vertmargin 0, hormargin 0 %% %% Wizard Kings maps: spaceheight 158 pt, paperheight 5 hexes (790 pt), paper 11"x15.22" %% paperwidth 1096 pt, vertmargin 0, hormargin "hexside neg", numrows 6, numcolumns 9 %% boardtype 0 %% %% Victory, Blocks of War maps: spaceheight 132 pt, paperheight 6 hexes (792 pt), %% paperwidth 1142 pt, vertmargin 0, hormargin "hexside neg", numrows 8, numcolumns 11 %% paper 11" x 15.86" %% boardtype 0 %% %% Stymie board: %% boardtype 1, autohex 1, spacenumbering 0, hormargin 0.5 inch, vertmargin 0.5 inch, %% hexperside 7, grayfill .875, outerborder 1, borderweight 4, symbolweight 1, symbolsize 10 % SOME CONSTANTS /inch {72 mul} def /cm {72 mul 2.45 div} def % MOSTLY USER-ADJUSTABLE VARIABLES /boardtype 2 def % 0 for standard rectangular board with hexes, 1 for hexagonal board with hexes, % 2 for pseudo-hex rectangles or squares % Board types 0 and 2 have spaces covering the whole sheet /colormap 0 def % 0 for black/white/gray board, 1 for full color board /spaceheight 1 inch def % set space size (top-to-bottom distance) /autohex 1 def % for type 1 (hexagonal arena) board only, automatically % make the hexes the largest that will fit within the paper size (fixed number of hexes, of variable size) /hexside 0.5 spaceheight mul 30 cos div def % defines length of hexside based on height (DO NOT CHANGE THIS) /spacenumbering 1 def % For type 1 and type 2 boards, should spaces be numbered? 0=no, 1=yes. /transparentsquares 1 def % Should spaces be transparent? 0=opaque, 1=transparent. colormap 0 eq {/numcolor {0 setgray} def} {/numcolor {.625 .25 0 .625 setcmykcolor} def} ifelse % color of hex numbering /truesquares 1 def % for "type 2" board, whether to use (1) true squares instead of (0) hexagonal-equivalent rectangles /numberfont (MyriadPro-Regular) def % set font for space numbering. Try also Courier-Bold, CourierNew-Bold, Times-Roman, TimesNewRomanPSMT /numberfontsize 14 def % set font size for space numbering /paperwidth 42 inch def % set paper width (NOTE: Paper size will also have to /paperheight 72 inch def % set paper height be set in Distiller and/or at the printer!) /numrows 0 def % number of rows (standard board only). Use zero to fill the sheet automatically /numcolumns 0 def % number of columns (standard board only). Use zero to fill the sheet automatically /hexline 2 def % weight in points of line for drawing hexes /hormargin 0 inch def % horizontal margin (how far from the page edge the drawing should start) /vertmargin 0 inch def % vertical margin (how far from the page edge the drawing should start) % Some variables that only affect "arena" boards /hexesperside 1 def % number of hexes per side for arena board /shadeouterhexes 1 def % whether to shade outside hexes for hexagonal arena board /grayfill .875 def % set whiteness for shaded hexes (e.g. .9 = 10% gray) for arena board /outerborder 1 def % whether to have a heavier border for hexagonal arena board /borderweight 4 def % weight in points (1/72 of an inch) of outer border for arena board % Some variables that only affect "Stymie" boards /stymiesymbols 0 def % whether to add symbols for Stymie game (only for hexagonal board, w 7 hexes per side /symbolweight 1.3 def % weight in points of lines for Stymie symbols /symbolsize 12 def % set size of symbols within hexes by stroke length in points % CALCULATED VARIABLES & PROCEDURES (not intended to be directly user-adjustable) truesquares 1 eq % if using "true squares" set square width to equal square height {/squarewidth spaceheight def} {/squarewidth 1.5 hexside mul def} ifelse % otherwise make rectangular to be equivalent to true hexes /centerstring { % procedure to find the advance width of a string in the current font % and then center it left-to-right by moving back half the string width % NOTE: takes string as argument! stringwidth pop 2 div neg 0 rmoveto } def boardtype 1 eq {/longrowhex 2 hexesperside mul 1 sub def} if % number of columns, and width of longest one, for hexagonal board boardtype 1 ne numrows 0 eq and {/numrows paperheight vertmargin sub vertmargin sub spaceheight div truncate def} if % autocalc length of long column for standard board if num rows unspecified autohex 1 eq boardtype 1 eq and { % if auto hex size AND hexagonal arena board selected, calc hex size /spaceheight paperwidth borderweight sub hormargin 2 mul sub 30 cos mul longrowhex 30 cos mul 30 cos mul 1 add div def } if /hexside 0.5 spaceheight mul 30 cos div def % defines length of hexside based on height numcolumns 0 eq boardtype 0 eq and {/numcolumns paperwidth hormargin 2 mul sub hexside 2 mul sub hexside 1.5 mul div 1 add truncate def} if % if number of columns is set to zero (automatic), and board type is rectangular hexes, calc number to fill numcolumns 0 eq boardtype 2 eq and {/numcolumns paperwidth hormargin 2 mul sub squarewidth div truncate def} if % if number of columns is set to zero (automatic), and board type is rectangular pseudo-hexes, calc number to fill numrows 0 eq {/numrows paperheight vertmargin 2 mul sub spaceheight div truncate def} if % if number of rows is set to zero (automatic), calc number to fill /hexdigits 4 def numcolumns 99 gt numrows 99 gt or {/hexdigits 5 def} if numcolumns 99 gt numrows 99 gt and {/hexdigits 6 def} if /columnmult 100 def numrows 99 gt {/columnmult 1000 def} if /drawspace { % def draws a hex for board types 0 and 1, or offset rectangles for board type 2 gsave hexline setlinewidth 0 setgray boardtype 2 ne {-0.5 hexside mul 0.5 spaceheight mul rmoveto} % move from center of hex to a corner {-0.5 squarewidth mul 0.5 spaceheight mul rmoveto} ifelse % move from center of rectangular space to corner boardtype 2 ne {5 {hexside 0 rlineto -60 rotate} repeat closepath -60 rotate} % draw a hex {squarewidth 0 rlineto 0 spaceheight neg rlineto squarewidth neg 0 rlineto closepath} ifelse % or draw a rectangle transparentsquares 0 eq % if squares are set to opaque {gsave 1 setgray % fills the spaces with white, to overprint any outside border fill grestore} if gsave stroke % actually write the line grestore spacenumbering 1 eq boardtype 1 ne and { % if hex numbering is on with standard or rectangle board, do numbers numberfont findfont numberfontsize scalefont setfont numcolor boardtype 0 eq {0.5 hexside mul -0.5 spaceheight mul rmoveto} if % move back to center of hex boardtype 2 eq {0.5 squarewidth mul -0.5 spaceheight mul rmoveto} if % move back to center of rectangular space /hexnum columncount columnmult mul rownum add def hexdigits 4 eq { % print routine for 4-digit hex numbers (0000) stringwidth pop 2 div neg 0.5 spaceheight mul 1.2 numberfontsize mul sub rmoveto % move to centered top of hex for numbering incl offset down for font size hexnum 1000 lt { % add a leading zero if needed (0) hexnum 3 string cvs concatenate show} % if {hexnum 4 string cvs show} ifelse % print the hex number } if % end print routine for 4-digit hex numbers hexdigits 5 eq numrows 100 lt and { % print routine for 5-digit hex numbers with >99 columns but <100 rows (00000) stringwidth pop 2 div neg 0.5 spaceheight mul 1.2 numberfontsize mul sub rmoveto % move to centered top of hex for numbering incl offset down for font size hexnum 1000 lt % begin if { % add two leading zeros if needed (00) hexnum 4 string cvs concatenate show} if hexnum 10000 lt hexnum 1000 gt and % begin if { % add single leading zero if needed (0) hexnum 5 string cvs concatenate show} if % add a leading zero hexnum 10000 ge {hexnum 6 string cvs show} if % print the hex number } if % end print routine for 6-digit hex numbers hexdigits 5 eq numrows 100 ge and { % print routine for 5-digit hex numbers with <100 columns but 100+ rows (00000) stringwidth pop 2 div neg 0.5 spaceheight mul 1.2 numberfontsize mul sub rmoveto % move to centered top of hex for numbering incl offset down for font size hexnum 10000 lt % begin if { % add a leading zero if needed (0) hexnum 4 string cvs concatenate show} if hexnum 10000 ge {hexnum 5 string cvs show} if % print the hex number } if % end print routine for 6-digit hex numbers hexdigits 6 eq { % print routine for 6-digit hex numbers (000000) stringwidth pop 2 div neg 0.5 spaceheight mul 1.2 numberfontsize mul sub rmoveto % move to centered top of hex for numbering incl offset down for font size hexnum 10000 lt % begin if { % add two leading zeros if needed (00) hexnum 4 string cvs concatenate show} if hexnum 100000 lt hexnum 10000 gt and % begin if { % add single leading zero if needed (0) hexnum 5 string cvs concatenate show} if % add a leading zero hexnum 100000 ge {hexnum 6 string cvs show} if % print the hex number } if % end print routine for 6-digit hex numbers } if grestore } def /drawshadedhex { % def gsave hexline setlinewidth -0.5 hexside mul 0.5 spaceheight mul rmoveto % move from center of hex to a corner 5 {hexside 0 rlineto -60 rotate} repeat closepath gsave colormap 1 eq {.25 .125 0 0 setcmykcolor} {grayfill setgray} ifelse fill grestore stroke % 0.5 hexside mul -0.5 spaceheight mul rmoveto % move back to center of hex grestore } def /concatenate % (string1) (string2) concatenate string3 % array1 array2 concatenate array3 { % def dup type 2 index type 2 copy ne { %if pop pop errordict begin (concatenate) typecheck end} { %else /stringtype ne exch /arraytype ne and {errordict begin (concatenate) typecheck end } if } ifelse dup length 2 index length add 1 index type /arraytype eq { array }{ string } ifelse % stack: arg1 arg2 new dup 0 4 index putinterval % stack: arg1 arg2 new dup 4 -1 roll length 4 -1 roll putinterval % stack: new } bind def % end "concatenate" procedure % MAIN PROGRAM % % the program for the hexagonal board generation vs standard board generation % all in one big ifelse statement starting here boardtype 0 eq % Begin routine for a rectangular hex map { /startx hormargin hexside add def % x drawing starts at middle of 1st hex (left side) /starty paperheight 0.5 spaceheight mul sub vertmargin sub def % start position in y for middle of first hex of standard board (top of page) /columncount 0 def startx starty moveto 1 1 numcolumns 1 add 2 div truncate { columncount numcolumns lt { /columncount columncount 1 add def gsave /rownum 0 def 1 1 numrows { /rownum rownum 1 add def drawspace 0 spaceheight neg rmoveto } for % draw long column of hexes grestore 1.5 hexside mul -0.5 spaceheight mul rmoveto } if columncount numcolumns lt { /columncount columncount 1 add def gsave /rownum 0 def 1 1 numrows 1 sub { /rownum rownum 1 add def drawspace 0 spaceheight neg rmoveto } for % draw short column of hexes grestore 1.5 hexside mul 0.5 spaceheight mul rmoveto } if } for } if % end routine for a standard board boardtype 2 eq % Begin routine for a pseudo-hex map of offset squares or rectangles, like Empire/Logistics { /startx hormargin squarewidth 0.5 mul add def % x drawing starts at middle of 1st rect (left side) /starty paperheight 0.5 spaceheight mul sub vertmargin sub def % start position in y for middle of 1st rect of board (top of page) /columncount 0 def startx starty moveto 1 1 numcolumns 1 add 2 div truncate { columncount numcolumns lt { /columncount columncount 1 add def gsave /rownum 0 def 1 1 numrows { /rownum rownum 1 add def drawspace 0 spaceheight neg rmoveto } for % draw long column of hexes grestore squarewidth -0.5 spaceheight mul rmoveto } if columncount numcolumns lt { /columncount columncount 1 add def gsave /rownum 0 def 1 1 numrows 1 sub { /rownum rownum 1 add def drawspace 0 spaceheight neg rmoveto } for % draw short column of hexes grestore squarewidth 0.5 spaceheight mul rmoveto } if } for % numberfont findfont % numberfontsize 3 mul scalefont % setfont % numcolor % startx starty moveto % squarewidth 72 div 10 8 string cvrs show } if % end routine for a pseudo-hex map of rectangles, like Empire/Logistics % Begin main routine for a hexagonal board boardtype 1 eq { /startx paperwidth 0.5 mul hexesperside 1 sub hexside mul 1.5 mul sub def % x drawing starts at middle of 1st hex (left side) - centers map in x /starty paperheight 0.5 mul longrowhex 1 sub 0.25 mul spaceheight mul add def % start position in y for middle of first hex of hexagonal board - centers map in y startx starty moveto /currentlength hexesperside def /maxreached 0 def outerborder 1 eq { gsave /borderweight borderweight 2 mul def % double border weight to make up for it being behind the hexes and only on outside of border 1 setlinejoin paperwidth 0.5 mul paperheight 0.5 mul moveto 0 hexesperside 0.5 sub spaceheight mul rmoveto -60 rotate 6 % repeat six times { % loop to draw border on current side of arena map 60 rotate hexside 0.5 mul 0 rlineto hexesperside 1 sub { -60 rotate hexside 0 rlineto 60 rotate hexside 0 rlineto } repeat -60 rotate hexside 0.5 mul 0 rlineto -60 rotate } repeat % ends loop to draw outer border borderweight setlinewidth colormap 1 eq {1 .6 0 0 setcmykcolor} if stroke grestore } if 1 1 longrowhex % for loop starts, iterates through columns getting longer & shorter { % for loop: draw columns of hexes gsave currentlength % loop to draw hexes in current column { drawspace 0 spaceheight neg rmoveto } repeat % ends loop to draw column of hexes grestore currentlength longrowhex eq {/maxreached 1 def} if % set flag if max length reached maxreached 1 eq {/currentlength currentlength 1 sub def} {/currentlength currentlength 1 add def} ifelse % increase or decrease length of column depending if max length reached maxreached 1 eq {1.5 hexside mul -0.5 spaceheight mul rmoveto} {1.5 hexside mul 0.5 spaceheight mul rmoveto} ifelse % next column start is higher until max length reached, then lower } for % this ends the loop of columns of hexes shadeouterhexes 1 eq { gsave paperwidth 0.5 mul paperheight 0.5 mul moveto 0 hexesperside 1 sub spaceheight mul rmoveto 60 rotate 6 % repeat six times { % loop to draw hexes on current side hexesperside 1 sub { drawshadedhex 0 spaceheight neg rmoveto } repeat -60 rotate } repeat % ends loop to draw sides of hexes grestore } if % SYMBOLS FOR STYMIE GAME /symbol3 { gsave 0 symbolsize 30 sin 30 cos div mul rmoveto -150 rotate % center the triangle in the hex and ready for first line 0 symbolsize rlineto -120 rotate 0 symbolsize rlineto closepath symbolweight 1.1 mul setlinewidth colormap 1 eq { .5 1 0 .125 setcmykcolor} if stroke grestore } def % end symbol3 /symbol4 { gsave 15 rotate 0 symbolsize -0.5 mul rmoveto 0 symbolsize rlineto symbolsize -0.5 mul symbolsize -0.5 mul rmoveto symbolsize 0 rlineto symbolweight 1.3 mul setlinewidth colormap 1 eq { 0 .75 1 0 setcmykcolor} if stroke grestore } def % end symbol4 /symbol5 { gsave 9 rotate 72 cos symbolsize mul neg 72 sin .5 mul symbolsize mul neg rmoveto % center the 5-pointed star in the hex -18 rotate 4 {0 symbolsize rlineto -144 rotate } repeat closepath symbolweight setlinewidth colormap 1 eq { .875 0 1 0 setcmykcolor} if stroke grestore } def % end symbol5 /symbol6 { gsave 15 rotate 6 {0 symbolsize 0.5 mul rlineto 0 symbolsize -0.5 mul rmoveto 60 rotate } repeat symbolweight setlinewidth colormap 1 eq { 0 1 1 .1 setcmykcolor} if stroke grestore } def % end symbol6 % DRAW STYMIE SYMBOLS ON THE MAP (Stymie arena only) stymiesymbols 1 eq { % Only draw if symbol switch is on gsave paperwidth 0.5 mul paperheight 0.5 mul moveto 0 6 spaceheight mul rmoveto % assumes 7 hexes per side here and elsewhere in stymiesymbols 60 rotate 6 % repeat six times { % loop to draw hexes on current side symbol6 0 hexesperside 1 sub spaceheight mul neg rmoveto -60 rotate } repeat % ends loop to draw sides of hexes grestore gsave paperwidth 0.5 mul paperheight 0.5 mul moveto 0 5 spaceheight mul rmoveto 60 rotate % draw symbols on 1st side of arena 0 3 spaceheight mul neg rmoveto symbol4 0 spaceheight neg rmoveto symbol5 0 spaceheight neg rmoveto symbol3 -60 rotate % draw symbols on 2nd side 0 5 spaceheight mul neg rmoveto symbol5 -60 rotate % draw symbols on 3rd side 0 spaceheight neg rmoveto symbol4 0 4 spaceheight mul neg rmoveto symbol3 -60 rotate % draw symbols on 4th side 0 spaceheight neg rmoveto symbol5 0 2 spaceheight mul neg rmoveto symbol4 0 2 spaceheight mul neg rmoveto -60 rotate % draw symbols on 5th side 0 2 spaceheight mul neg rmoveto symbol5 0 3 spaceheight mul neg rmoveto -60 rotate % draw symbols on 6th side symbol3 0 spaceheight neg rmoveto symbol4 0 2 spaceheight mul neg rmoveto symbol5 0 2 spaceheight mul neg rmoveto -60 rotate grestore } if % this ends the Stymie symbols drawing } if % this ends the hexagonal arena board drawing