Agnon Board Game in Perl SVG
Calculating hexagons is fun, right? Well, hand drawing them is no fun. There are several fun games based on hexagon boards and we wanted to make them on the CNC router. Here is the Agnon game board and rules, as well as the Perl script that created it for our CNC router.
This Perl script produces the SVG files. We use the SVG files and convert them to G-Code for the router. Though there are more elegant ways to create the SVG, this scrip works. Here is the code, sharing for educational purposes and because we enjoy the game itself!
Agnon
Agnon is a game game that was popular in the 18th century and there is a reference to a copyright in 1780 by Adam Vaugeois. It has simple rules and complex strategy.
Agnon Alternate Names
- Queen’s Guard
- Royal Guard
- Guard
- Advance and Retreat
- Tournament
Agnon Objective
The winner is the first person to surround their queen on the center (purple hex) with her 6 guards.
Agnon Game Setup
Queen is placed first on the outer ring. The opoosing queen is placed exactly opposite.
Players take turns placing their remaining counters, 1 at a time on the outer ring. Once all 6 guards have been placed, players take turns moving their pieces with the player who went second getting the first opportunity to move their peice.
Agnon Game Play
Counters may move to an adjacent vacant cell. Movement is only sideways or inwards, never away from the center. Only the queen may move into the center. Counters may not jump or pass other counters.
Capture is done by surrounding an opponent with 2 of your pieces. All 3 must be in the same ring. When captured, the enemy is returned to the outer ring in any empty cell on the next move. If a queen is captured, she can move to any vacant cell on the board. If you are captured, your entire next turn is used to relocate your piece to the outer ring.
You cannot move safely into a capture. Moving your piece between 2 enemy pieces results in a capture. Multiple captures are possible and the captured player must then take 1 turn to relocate each piece back to the outer ring. If a queen and guard are trapped at the same time, the queen must be relocated first.
A player forfeits if he surrounds the center with guards but does not have a queen located on the center square.
Perl Code to Create Hexagon Game Board, Agnon
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 |
#!/usr/bin/perl # Hex Mapper use strict; use warnings; use SVG; # https://metacpan.org/pod/SVG # using scale of 66 here, and then 66 in estlecam to create # the CNC files to fit on an 11x11 board of wood use constant SCALE => '66'; # dots per inch scale, adjust to fit on your board use constant DOTSIZE => '7'; # should be an odd number use constant LINEWIDTH => '3'; # should be an odd number use constant FILL => 'black'; # can be 'rgb(0,0,0)' too use constant STROKE => 'black'; # can be 'rgb(0,0,0)' too # title auto centers my $title = "6x6 Hex Board (Agnon)"; # create an SVG object, canvas which we use for the rest of the draws my $svg = SVG->new( width => 11 * SCALE, height => 11 * SCALE, ); # only adjust this for changing hexes per page # 4x4= 2.5 # 5x5= 3.0 # 6x6= 3.5 # Larger hexsize = smaller hexes my $hexsize = 3.5; #---------------------------------------------- # Define the dots, lines, squares, circles etc # everything is based off of the segment length # of the circle radius, so "legs" of various length # used throughout rest of the math #---------------------------------------------- # 8 inch game board (11 inches with 1.5 inch boarder) # done so I could scale easier to fit my workpiece my $leg1 = (8/6); # 1.3333333333 (does this matter?) my $leg2 = ($leg1) * 2; # 2.6666666666 my $leg3 = ($leg1) * 3; # 3.9999999999 my $hex = (SCALE/$hexsize); # my $ctr = 5.5; # because our board is 11x11, so 5.5x5.5 is center # Squares are xy start then l,w in inches my %squares = ( sa => [0,0,11,11,0], # border/edge of board ); # color was used for debugging my %c = ( 'b' => 'black', 'r' => 'red', 'l' => 'blue', 'w' => 'white', 'g' => 'green', 'y' => 'yellow', 'o' => 'orange', 'k' => 'pink', 'p' => 'purple', 'e' => 'grey', 'lg' => 'LightGray', # D3D3D3 'bg' => 'Beige' # F5F5DC ); # xysteps are how far to go until next hex my $ystep = 0; my $xstep = 0; # what is the center of our board? my $xs = $ctr * SCALE; my $ys = $ctr * SCALE; my $hc = 100; # just a counter to start the hex ID # find the NW corner to start from. Keep in mind, 5x5 # matches scale. So, instead of moving this, change hex size my $ystart = hexstart(5,$hex,$xs,$ys,'y'); # lower to move hex down, don't touch to keep centered my $xstart = hexstart(5,$hex,$xs,$ys,'x'); hexes($hc, $hex , $xstart,$ystart, $c{w},'sw'); # ok, do the rest, in a smaller and smaller radius, clockwise # first group is the largest ring for (0..3) {hexes($hc, $hex , $xs,$ys, $c{w},'sw');} for (0..4) {hexes($hc, $hex , $xs,$ys, $c{w},'se');} for (0..4) {hexes($hc, $hex , $xs,$ys, $c{w},'e');} for (0..4) {hexes($hc, $hex , $xs,$ys, $c{w},'ne');} for (0..4) {hexes($hc, $hex , $xs,$ys, $c{w},'nw');} for (0..3) {hexes($hc, $hex , $xs,$ys, $c{w},'w');} hexes($hc, $hex , $xs,$ys, $c{w},'sw'); for (0..3) {hexes($hc, $hex , $xs,$ys, $c{e},'sw');} for (0..3) {hexes($hc, $hex , $xs,$ys, $c{e},'se');} for (0..3) {hexes($hc, $hex , $xs,$ys, $c{e},'e');} for (0..3) {hexes($hc, $hex , $xs,$ys, $c{e},'ne');} for (0..3) {hexes($hc, $hex , $xs,$ys, $c{e},'nw');} for (0..2) {hexes($hc, $hex , $xs,$ys, $c{e},'w');} hexes($hc, $hex , $xs,$ys, $c{e},'sw'); for (0..2) {hexes($hc, $hex , $xs,$ys, $c{w},'sw');} for (0..2) {hexes($hc, $hex , $xs,$ys, $c{w},'se');} for (0..2) {hexes($hc, $hex , $xs,$ys, $c{w},'e');} for (0..2) {hexes($hc, $hex , $xs,$ys, $c{w},'ne');} for (0..2) {hexes($hc, $hex , $xs,$ys, $c{w},'nw');} for (0..1) {hexes($hc, $hex , $xs,$ys, $c{w},'w');} hexes($hc, $hex , $xs,$ys, $c{w},'sw'); for (0..1) {hexes($hc, $hex , $xs,$ys, $c{e},'sw');} for (0..1) {hexes($hc, $hex , $xs,$ys, $c{e},'se');} for (0..1) {hexes($hc, $hex , $xs,$ys, $c{e},'e');} for (0..1) {hexes($hc, $hex , $xs,$ys, $c{e},'ne');} for (0..1) {hexes($hc, $hex , $xs,$ys, $c{e},'nw');} for (1..1) {hexes($hc, $hex , $xs,$ys, $c{e},'w');} hexes($hc, $hex , $xs,$ys, $c{e},'sw'); for (1..1) {hexes($hc, $hex , $xs,$ys, $c{w},'sw');} for (1..1) {hexes($hc, $hex , $xs,$ys, $c{w},'se');} for (1..1) {hexes($hc, $hex , $xs,$ys, $c{w},'e');} for (1..1) {hexes($hc, $hex , $xs,$ys, $c{w},'ne');} for (1..1) {hexes($hc, $hex , $xs,$ys, $c{w},'nw');} for (1..1) {hexes($hc, $hex , $xs,$ys, $c{w},'sw');} hexes($hc, $hex , $xs,$ys, $c{p},'sw'); #---------------------------------------------- # Logic, no reason for sort but to help me see data when debugging #---------------------------------------------- # make our squares foreach my $square (sort keys %squares) { squares($squares{$square}[0],$squares{$square}[1],$squares{$square}[2],$squares{$square}[3],$squares{$square}[4]); } # for SVG< keep - but will probably delete for boards title($title); #---------------------------------------------- # customs - accepts custom data paths #---------------------------------------------- sub hexstart { my $hops = shift; my $size = shift; my $strtx = shift; my $strty = shift; my $xy = shift; my ($hx1,$hy1,$hx2,$hy2,$hx3,$hy3,$hx4,$hy4,$hx5,$hy5,$hx6,$hy6,$hx7,$hy7); $hy6 = $strty + ((sqrt(3)/2) * $size) * 2; $hy2 = $strty - ((sqrt(3)/2) * $size); if ($xy eq 'y') {return $strty + ($hy2 - $hy6) * $hops;} if ($xy eq 'x') {return $strtx - (1.5 * $size) * $hops;} } sub hexes { #d='M 3130,5385 4012,5385 4453,6149 4012,6912 3130,6912 2689,6149 3130,5385 Z' # takes 6 points to make a hex. # takes xy start and builds hex with length from there my $id = shift; my $sz = shift; my $sx = shift; my $sy = shift; my $color = shift; my $dir = shift; my ($hx1,$hy1,$hx2,$hy2,$hx3,$hy3,$hx4,$hy4,$hx5,$hy5,$hx6,$hy6,$hx7,$hy7); # xy coorids for a scaled single hex based on sx and sy starts # begins at sw the goes clockwise for others, building custom string $hx1 = $sx - 1.5 * $sz; $hy1 = $sy + ((sqrt(3)/2) * $sz); $hx2 = $sx - 1.5 * $sz; $hy2 = $sy - ((sqrt(3)/2) * $sz); $hx3 = $sx; $hy3 = $sy - ((sqrt(3)/2) * $sz) * 2; $hx4 = $sx + .5 * 3 * $sz; $hy4 = $sy - ((sqrt(3)/2) * $sz); $hx5 = $sx + .5 * 3 * $sz; $hy5 = $sy + ((sqrt(3)/2) * $sz); $hx6 = $sx; $hy6 = $sy + ((sqrt(3)/2) * $sz) * 2; $hx7 = $hx1; $hy7 = $hy1; # step direction is which way to place the next hex # feed this sub a number of hexes and it goes that # direction, that many times. if ($dir eq 'nw') { $xstep = $sx - 1.5 * $sz; $ystep = $sy - ($hy6 - $hy2); $xs = $xstep; $ys = $ystep; } elsif ($dir eq 'ne') { $xstep = $sx + 1.5 * $sz; $ystep = $sy - ($hy6 - $hy2); $xs = $xstep; $ys = $ystep; } elsif ($dir eq 'se') { $xstep = $sx + 1.5 * $sz; $ystep = $sy + $hy6 - $hy2; $xs = $xstep; $ys = $ystep; } elsif ($dir eq 'sw') { $xstep = $sx - 1.5 * $sz; $ystep = $sy + $hy6 - $hy2; $xs = $xstep; $ys = $ystep; } elsif ($dir eq 'e') { $xstep = $sx + (1.5 * $sz) * 2; $xs = $xstep; $ys = $ystep; } elsif ($dir eq 'w') { $xstep = $sx - (1.5 * $sz) * 2; $xs = $xstep; $ys = $ystep; } elsif ($dir == 0) { $xs = $ctr * SCALE; $ys = $ctr * SCALE; } # no real hex method for perl SVG module, so custom string it: my $string = "M $hx1,$hy1 $hx2,$hy2 $hx3,$hy3 $hx4,$hy4 $hx5,$hy5 $hx6,$hy6 $hx7,$hy7 Z"; # add the object. my $tag = $svg->path( d => $string, id => 'hex_'.$id, style => { 'fill' => $color, 'stroke' => STROKE, 'stroke-width' => 3, 'stroke-opacity' => 1, 'fill-opacity' => 1, }, ); # advance hex id counter $hc++; } #---------------------------------------------- # Text, at SCALE of 142, 6.5 letters per inch, Serif Font # Text, at SCALE of 96, 5 letters per inche, Serif Font # Text, at SCALE of 67, 4.5 bleh bleh # Take half of estimated lenght of title, subtract it from center # that should start half before center, and then half after center # adjust the /5 down to move left, up to move right #---------------------------------------------- sub title { my $ltitle = length($title); my $xtitle = 5.5 * SCALE - ($ltitle /4.5 * SCALE)/2; $svg->text( id => 'l1', x => $xtitle, y => SCALE * .95, style => { 'font' => 'Serif', 'font-size' => SCALE / 2, 'fill' => FILL, }, )->cdata($title); } #---------------------------------------------- # square sub #---------------------------------------------- sub squares { # accepts 6 values: # x y coord, w h specs, fill opacity and line weight my $x = shift; my $y = shift; my $w = shift; my $h = shift; my $fop = shift; $svg->rectangle( x => $x * SCALE, y => $y * SCALE, width => $w * SCALE, height => $h * SCALE, style => { 'fill' => FILL, 'stroke' => STROKE, 'stroke-width' => LINEWIDTH, 'stroke-opacity' => 1, 'fill-opacity' => $fop, # must be 0, for lines, or 1 for solid squares } ); } # now render the SVG object, implicitly use svg namespace print $svg->xmlify; |
Agnon SVG File
Here is the actual SVG file for the Agnon Game Board. You are free to use it for personal use, but please credit us with it’s creation.
Custom Perl Programming
Of course, if you want something created in Perl or any of a variety of languages please contact us for custom programming