Information Technology Grimoire

Version .0.0.1

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

Watermelon Chess SVG Using Perl

Watermelon chess is a simple strategy game where you surround your opponent to remove them from the game.

We are creating game boards for the CNC router. The first step is to create a SVG file. From the SVG file, we then create the g-code. This article contains rules about watermelon chess, and the Perl used to create the SVG.

Objective

The objective of watermelon chess is to surround your opponent, capturing pieces. Once you get them down to 2 pieces, you win.

Setup

6 pieces on one side and 6 on the other using different colors.

Movement

Move 1 segment at a time on your turn.

Capture

If you surround an opponent’s piece or group of pieces, those pieces are captured.

Watermelon Chess SVG Code in Perl

#!/usr/bin/perl

# Water Melon Chess 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 	=> '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

my $title = "Watermelon Chess";

# create an SVG object, canvas which we use for the rest of the draws
my $svg = SVG->new(
    width  => 11 * SCALE,
    height => 11 * 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
#----------------------------------------------

# everything is based off of the segment length
# of the circle radius, so "legs" of various length
# used throughout rest of the math
my $leg1 = (8/6);	# 1.1428571429
my $leg2 = ($leg1) * 2; # 2.2857142857
my $leg3 = ($leg1) * 3;	# 3.4285714286
my $leg4 = ($leg1) * 4;	# 4.5714285714
my $ctr  = 5.5;

# Circles / Dots
my %circles = (
	# x, y, size, fill
	ca => [$ctr,$ctr,$leg3,0], # the big Juan
	#cb => [$ctr,$ctr-$leg3,$leg1,0], # N but can't do full circles, do arcs instead
	#cc => [$ctr+$leg3,$ctr,$leg1,0], # E, do arcs instead
	#cd => [$ctr,$ctr+$leg3,$leg1,0], # S, do arcs instead
	#ce => [$ctr-$leg3,$ctr,$leg1,0], # W, do arcs instead
	cf => [$ctr,$ctr,$leg1,0],  # the little Juan

	cg => [$ctr-$leg3,$ctr,.1,1], # y-axis
	ch => [$ctr-$leg2+$leg1*.15,$ctr,.1,1], # y-axis
	ci => [$ctr-$leg1,$ctr,.1,1], # y-axis

	cj => [$ctr+$leg1,$ctr,.1,1], # y-axis
	ck => [$ctr+$leg2-$leg1*.15,$ctr,.1,1], # y-axis
	cl => [$ctr+$leg3,$ctr,.1,1], # y-axis

	cm => [$ctr,$ctr,.1,1], #  center

	cn => [$ctr,$ctr-$leg3,.1,1], # x-axis
	co => [$ctr,$ctr-$leg2+$leg1*.15,.1,1], # x-axis
	cp => [$ctr,$ctr-$leg1,.1,1], # x-axis

	cq => [$ctr,$ctr+$leg1,.1,1], # x-axis
	cr => [$ctr,$ctr+$leg2-$leg1*.15,.1,1], # x-axis
	cs => [$ctr,$ctr+$leg3,.1,1], # x-axis

	ct => [$ctr-$leg3+$leg1*.167,$ctr+$leg1-$leg1*.017,.1,1], # arc dots, W
	cu => [$ctr-$leg3+$leg1*.167,$ctr-$leg1+$leg1*.017,.1,1], # arc dots, W
	cv => [$ctr+$leg3-$leg1*.167,$ctr+$leg1-$leg1*.017,.1,1], # arc dots, E
	cw => [$ctr+$leg3-$leg1*.167,$ctr-$leg1+$leg1*.017,.1,1], # arc dots, E

	cx => [$ctr-$leg1+$leg1*.017,$ctr-$leg3+$leg1*.167,.1,1], # arc dots, N
	cy => [$ctr+$leg1-$leg1*.017,$ctr-$leg3+$leg1*.167,.1,1], # arc dots, N
	cz => [$ctr-$leg1+$leg1*.017,$ctr+$leg3-$leg1*.167,.1,1], # arc dots, S
	c0 => [$ctr+$leg1-$leg1*.017,$ctr+$leg3-$leg1*.167,.1,1], # arc dots, S

);

# Lines are xy start and xy stop coordinates, in inches
my %lines = (
	# x  y   x   y
	lb => [$ctr,$ctr-$leg3,$ctr,$ctr+$leg3],	# N to S
	lc => [$ctr-$leg3,$ctr,$ctr+$leg3,$ctr],	# E to W
);

# Squares are xy start then l,w in inches
my %squares = (
	sa => [0,0,11,11,0],		# border/edge of board
);

# so we need some custom paths, not sure how to do arcs with this module
my %arcs = (
	# x,y (start), x,y (stop)
	aa => [$circles{'ct'}[0],$circles{'ct'}[1],$circles{'cu'}[0],$circles{'cu'}[1],0,0], # West Arc
	ab => [$circles{'cx'}[0],$circles{'cx'}[1],$circles{'cy'}[0],$circles{'cy'}[1],0,0], # North Arc
	ac => [$circles{'cv'}[0],$circles{'cv'}[1],$circles{'cw'}[0],$circles{'cw'}[1],0,1], # East Arc
	ad => [$circles{'cz'}[0],$circles{'cz'}[1],$circles{'c0'}[0],$circles{'c0'}[1],0,1], # South Arc

);

#----------------------------------------------
# Logic, no reason for sort but to help me see data when debugging
#----------------------------------------------

foreach my $arc (sort keys %arcs) {
	arcs($arc, $arcs{$arc}[0],$arcs{$arc}[1],$arcs{$arc}[2],$arcs{$arc}[3],$arcs{$arc}[4],$arcs{$arc}[5]);
}

# make our circles/dots
foreach my $circle (sort keys %circles) {
	circles($circles{$circle}[0],$circles{$circle}[1],$circles{$circle}[2],$circles{$circle}[3]);
}

# add some lines
foreach my $cor (sort keys %lines) {
	lines($lines{$cor}[0],$lines{$cor}[1],$lines{$cor}[2],$lines{$cor}[3]);
}

# 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);

#----------------------------------------------
# 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);
}
#----------------------------------------------
# customs - accepts custom data paths
#----------------------------------------------
sub arcs {
	# M = Absolute move to X,Y
	# A = Absolute Arc Radius X and Y
	# Rotation
	# Large ARC Flag
	# Sweep Flag
	# End X,Y of Arc
	# seems to be ignoring the A rx, ry
    my ($id,$sx,$sy,$ex,$ey,$a1,$a2) = @_;
    $sx = $sx * SCALE;
    $sy = $sy * SCALE;
    $ex = $ex * SCALE;
    $ey = $ey * SCALE;
    my $sz = ($leg1-$leg1 * .167) * SCALE;
    my $string = "M $sx,$sy A $sz,$sz 90 $a1 $a2 $ex,$ey";
    my $tag = $svg->path(
        d => $string,
        id    => 'arc_'.$id,
	    style => {
        	'fill'           => FILL,
        	'stroke'         => STROKE,
        	'stroke-width'   =>  3,
        	'stroke-opacity' =>  1,
        	'fill-opacity'   =>  0,
    	},
    );
}
#----------------------------------------------
# circles - x,y start, radius and fill opacity
#----------------------------------------------
sub circles {
	my $x = shift;
	my $y = shift;
	my $r = shift;
	my $fop = shift;

	$svg->circle(
	    cx => $x * SCALE,
	    cy => $y * SCALE,
	    r  => $r * SCALE,
	    style => {
        	'fill'           => FILL,
        	'stroke'         => STROKE,
        	'stroke-width'   =>  3,
        	'stroke-opacity' =>  1,
        	'fill-opacity'   =>  $fop,
    	},
	);
}

#----------------------------------------------
# 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,
	    },
	);
}

#----------------------------------------------
# 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;

Last updated on 11 Jan 2019
Published on 11 Jan 2019