SVG Perl Script to Make 9 Men’s Morris Game
9 Men’s Morris is a great simple strategy game that has been around for thousands of years. You can play it by scratching some lines in the dirt (or paper) and then using 9 tokens each. Here is a Perl script that generates a game board for you in SVG format.
9 Men’s Morris Game Board
The game board is pretty easy to make and has been found in tombs in Egypt carved into stones. Game pieces can be pennies and dimes, or a bag of glass beads from walmart for $1.
Objective
Reduce your opponent down to 2 pieces by making 3 in a row and taking 1 piece each time you create a 3 in a row.
If your opponent is trapped completely and cannot move, they lose.
Setup (Stage 1)
Take turns putting your 9 pieces down, trying to get 3 in a row. Each time you get three in a row, take an unprotected piece from your opponent and remove it from the game. 3 in a Row can only be done on lines, not diagonal.
Movement (Stage 2)
Take turns sliding your piece trying to get 3 in a row again. Each time you get three in a row, take an unprotected piece from your opponent and remove it from the game.
Underdog Rule (Stage 3)
If a player only has 3 pieces left, they are no longer bound to the 1 segment move rule and can put any 1 of their pieces on any location instead of sliding 1 segment. This allows a losing player to have a very strong fighting chance even if they were obliterated early on in the game.
Additional Rules
- You cannot take from a string, unless there is no other option to take first
- If your opponent is trapped and has no legal moves, you win.
9 Men’s Morris Strategy
Your opponent will easily see you put down 2 in a row and will block your third play. So, you must think of ways to get multiple 3 in a rows that your opponent cannot block both. When you beat your opponent down to 3, they can fly anywhere on the board and block your moves. So, you need to patiently setup a double 3 in a row that they cannot block both.
9 Men’s Morris SVG Code
This code was generated with the Perl script at the bottom of the article. You can view it in any browser.
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 |
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd"> <svg height="670" width="603" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> <rect height="536" style="fill: black; fill-opacity: 0; stroke: black; stroke-opacity: 1; stroke-width: 3" width="536" x="33.5" y="33.5" /> <rect height="402" style="fill: black; fill-opacity: 0; stroke: black; stroke-opacity: 1; stroke-width: 3" width="402" x="100.5" y="100.5" /> <rect height="268" style="fill: black; fill-opacity: 0; stroke: black; stroke-opacity: 1; stroke-width: 3" width="268" x="167.5" y="167.5" /> <circle cx="33.5" cy="33.5" r="7" /> <circle cx="33.5" cy="301.5" r="7" /> <circle cx="33.5" cy="569.5" r="7" /> <circle cx="100.5" cy="100.5" r="7" /> <circle cx="100.5" cy="301.5" r="7" /> <circle cx="100.5" cy="502.5" r="7" /> <circle cx="167.5" cy="167.5" r="7" /> <circle cx="167.5" cy="301.5" r="7" /> <circle cx="167.5" cy="435.5" r="7" /> <circle cx="301.5" cy="33.5" r="7" /> <circle cx="301.5" cy="100.5" r="7" /> <circle cx="301.5" cy="167.5" r="7" /> <circle cx="301.5" cy="435.5" r="7" /> <circle cx="301.5" cy="502.5" r="7" /> <circle cx="301.5" cy="569.5" r="7" /> <circle cx="435.5" cy="167.5" r="7" /> <circle cx="435.5" cy="301.5" r="7" /> <circle cx="435.5" cy="435.5" r="7" /> <circle cx="502.5" cy="100.5" r="7" /> <circle cx="502.5" cy="301.5" r="7" /> <circle cx="502.5" cy="502.5" r="7" /> <circle cx="569.5" cy="33.5" r="7" /> <circle cx="569.5" cy="301.5" r="7" /> <circle cx="569.5" cy="569.5" r="7" /> <polygon points="301.5,33.5 301.5,167.5 " style="fill: black; fill-opacity: 1; stroke: black; stroke-opacity: 1; stroke-width: 3" /> <polygon points="435.5,301.5 569.5,301.5 " style="fill: black; fill-opacity: 1; stroke: black; stroke-opacity: 1; stroke-width: 3" /> <polygon points="301.5,435.5 301.5,569.5 " style="fill: black; fill-opacity: 1; stroke: black; stroke-opacity: 1; stroke-width: 3" /> <polygon points="33.5,301.5 167.5,301.5 " style="fill: black; fill-opacity: 1; stroke: black; stroke-opacity: 1; stroke-width: 3" /> <text id="l1" style="fill: black; font: Serif; font-size: 32" x="197.277777777778" y="636.5">9 Men's Morris</text> <!-- Generated using the Perl SVG Module V2.84 by Ronan Oger Info: http://www.roitsystems.com/ --> </svg> |
Perl Script to Generate Game Boards
The lines are actually 2 point polygons, which seemed easier than drawing lines another way. The end result is the same.
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 |
#!/usr/bin/perl # 9 Men's Morris Board done in SVG use strict; use warnings; use SVG; # https://metacpan.org/pod/SVG # with margins at .25, SCALE of 142*9.5 (last row of dots) fits on an A4 use constant SCALE => '67'; # dots per inch scale, adjust to fit on your board use constant DOTSIZE => '7'; # should be an odd number to exactly center use constant LINEWIDTH => '3'; # should be an odd number to exactly center use constant FILL => 'black'; # can be 'rgb(0,0,0)' too use constant STROKE => 'black'; # can be 'rgb(0,0,0)' too my $title = "9 Men's Morris"; # create an SVG object, canvas which we use for the rest of the draws my $svg = SVG->new( width => 9 * SCALE, height => 10 * SCALE, ); #---------------------------------------------- # Define the dots, lines, squares, circles etc # Order isn't important, all is eventually drawn # SVG has max of 2-4k objects before browser gets sluggish # keys are sorted alpha for debug reasons #---------------------------------------------- # Dots are x,y cooridianates, in inches (scaled inches anyway). my %dots = ( # col1 da => [.5,.5], db => [.5,4.5], dc => [.5,8.5], # col2 dd => [1.5,1.5], de => [1.5,4.5], df => [1.5,7.5], # col3 dg => [2.5,2.5], dh => [2.5,4.5], di => [2.5,6.5], # col4 dj => [4.5,.5], dk => [4.5,1.5], dl => [4.5,2.5], dm => [4.5,6.5], dn => [4.5,7.5], do => [4.5,8.5], # col5 dp => [6.5,2.5], dq => [6.5,4.5], dr => [6.5,6.5], # col6 ds => [7.5,1.5], dt => [7.5,4.5], du => [7.5,7.5], # col7 dv => [8.5,.5], dw => [8.5,4.5], dx => [8.5,8.5] ); # Lines are xy start and xy stop coordinates, in inches my %l = ( # x y x y la => [4.5,.5,4.5,2.5], # N lb => [6.5,4.5,8.5,4.5], # E lc => [4.5,6.5,4.5,8.5], # S ld => [.5,4.5,2.5,4.5], # W ); # Squares are xy start then l,w in inches my %sqs = ( sa => [.5,.5,8,8], # Outer sb => [1.5,1.5,6,6], # Middle sc => [2.5,2.5,4,4] # Inner ); #---------------------------------------------- # Logic, no reason for sort but to help me see data #---------------------------------------------- # make our squares foreach my $sq (sort keys %sqs) { squares($sqs{$sq}[0],$sqs{$sq}[1],$sqs{$sq}[2],$sqs{$sq}[3]); } # add the dots foreach my $cor (sort keys %dots) { dots($dots{$cor}[0],$dots{$cor}[1]); } # add some lines foreach my $cor (sort keys %l) { lines($l{$cor}[0],$l{$cor}[1],$l{$cor}[2],$l{$cor}[3]); } # Text, at SCALE of 142, 6.5 letters per inch, Serif Font # Text, at SCALE of 96, 5 letters per inche, Serif Font # 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 # my $ltitle = length($title); my $xtitle = 4.5 * SCALE - ($ltitle/4.5 * SCALE)/2; $svg->text( id => 'l1', x => $xtitle, y => SCALE * 9.5, style => { 'font' => 'Serif', 'font-size' => 32, 'fill' => FILL, }, )->cdata($title); #---------------------------------------------- # square sub #---------------------------------------------- sub squares { my $x = shift; my $y = shift; my $w = shift; my $h = 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' => 0, # must be 0, for lines, or 1 for solid squares } ); } #---------------------------------------------- # dots sub #---------------------------------------------- sub dots { # takes 2 arguments, makes a dot my $x = shift; my $y = shift; $svg->circle( cx => $x * SCALE, cy => $y * SCALE, r => DOTSIZE, ); } #---------------------------------------------- # lines sub # really a 2 point polygon, and lines #---------------------------------------------- sub lines { # 4 arguments, xstart, ystart, xstop, ystop my $xstart = SCALE * shift; my $ystart = SCALE * shift; my $xstop = SCALE * shift; my $ystop = SCALE * shift; my $path = $svg->get_path( x => [$xstart,$xstop], y => [$ystart,$ystop], -type => 'polygon'); $svg->polygon( %$path, style => { 'fill' => FILL, 'stroke' => STROKE, 'stroke-width' => LINEWIDTH, 'stroke-opacity' => 1, 'fill-opacity' => 1, }, ); } # now render the SVG object, implicitly use svg namespace print $svg->xmlify; |
Gift Ideas
Carving this into a piece of wood and buying some shiny glass beads would make a great game gift. If you want to do the fast/cheap method just print out this graphic and use some pennies/dimes. it’s a very fun game.