Information Technology Grimoire

Version .0.0.1

IT Notes from various projects because I forget, and hopefully they help you too.

SVG Perl Script to Make 9 Mens 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.


9 Men’s Morris Game Board


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.

<svg width="603" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" height="670">
	<rect y="33.5" width="536" style="fill: black; fill-opacity: 0; stroke: black; stroke-opacity: 1; stroke-width: 3" x="33.5" height="536"></rect>
	<rect y="100.5" width="402" style="fill: black; fill-opacity: 0; stroke: black; stroke-opacity: 1; stroke-width: 3" x="100.5" height="402"></rect>
	<rect y="167.5" width="268" style="fill: black; fill-opacity: 0; stroke: black; stroke-opacity: 1; stroke-width: 3" x="167.5" height="268"></rect>
	<circle cy="33.5" cx="33.5" r="7"></circle>
	<circle cy="301.5" cx="33.5" r="7"></circle>
	<circle cy="569.5" cx="33.5" r="7"></circle>
	<circle cy="100.5" cx="100.5" r="7"></circle>
	<circle cy="301.5" cx="100.5" r="7"></circle>
	<circle cy="502.5" cx="100.5" r="7"></circle>
	<circle cy="167.5" cx="167.5" r="7"></circle>
	<circle cy="301.5" cx="167.5" r="7"></circle>
	<circle cy="435.5" cx="167.5" r="7"></circle>
	<circle cy="33.5" cx="301.5" r="7"></circle>
	<circle cy="100.5" cx="301.5" r="7"></circle>
	<circle cy="167.5" cx="301.5" r="7"></circle>
	<circle cy="435.5" cx="301.5" r="7"></circle>
	<circle cy="502.5" cx="301.5" r="7"></circle>
	<circle cy="569.5" cx="301.5" r="7"></circle>
	<circle cy="167.5" cx="435.5" r="7"></circle>
	<circle cy="301.5" cx="435.5" r="7"></circle>
	<circle cy="435.5" cx="435.5" r="7"></circle>
	<circle cy="100.5" cx="502.5" r="7"></circle>
	<circle cy="301.5" cx="502.5" r="7"></circle>
	<circle cy="502.5" cx="502.5" r="7"></circle>
	<circle cy="33.5" cx="569.5" r="7"></circle>
	<circle cy="301.5" cx="569.5" r="7"></circle>
	<circle cy="569.5" cx="569.5" r="7"></circle>
	<polygon style="fill: black; fill-opacity: 1; stroke: black; stroke-opacity: 1; stroke-width: 3" points="301.5,33.5 301.5,167.5 "></polygon>
	<polygon style="fill: black; fill-opacity: 1; stroke: black; stroke-opacity: 1; stroke-width: 3" points="435.5,301.5 569.5,301.5 "></polygon>
	<polygon style="fill: black; fill-opacity: 1; stroke: black; stroke-opacity: 1; stroke-width: 3" points="301.5,435.5 301.5,569.5 "></polygon>
	<polygon style="fill: black; fill-opacity: 1; stroke: black; stroke-opacity: 1; stroke-width: 3" points="33.5,301.5 167.5,301.5 "></polygon>
	<text y="636.5" x="197.277777777778" style="fill: black; font: Serif; font-size: 32" id="l1">9 Men's Morris</text>

</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.

#!/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.

Last updated on 18 Dec 2018
Published on 18 Dec 2018