Python3 Create SVG Gameboard 9 Mens Morris
9 Men’s Morris is one of my favorite games. It’s simple enough to play against most 5 year olds, but complex enough to challenge most adults. You can print out this game board created with python and see how to create SVG files in Python.
This python script is a demo of using python to create a simple board game. Similar code to this is used for converting SVG to GCode for my CNC router I didn’t have the SVG I wanted, so this was written in Perl. Then I realized nobody except me likes Perl! So I wrote it in Python.
The game, 9 men’s morris, is very old, and has been found carved in stones in Egyptian pyramids.
Installation
You probably already know how to setup a python script. However, for the class we were teaching, we’ve included the basics that an absolute beginner can follow:
- install python3 https://www.python.org/downloads/
- add it to path, install pip, add to path
- pip install svgwrite
- pip install json
- save the python script and the json file in same directory
Topics Learned
- python module svgwrite
- python pip installing modules
- python comments
- python multiline comments
- python escaping characters
- python dictionary data structure
- python dictionary of lists structure
- python for loops
- python splitting strings
- python converting string to float
- python functions
- python passing variables
- python basic math operator
- SVG Basics
- SVG File Creation
- SVG Text
- SVG Circles
- SVG Squares
- SVG Lines/Paths
- SVG Styles
- JSON Basics
- JSON loading json file
- JSON writing json file
- JSON indenting json file
- 9 Men’s Morris Rules
The JSON File:
We create a JSON file to store dots and lines in. This would likely be easier as data in the script, but for the class we were teaching, we wanted a JSON demo. As we had about 40 games using various lines and configs, this seemed like a good use of JSON.
{
"title": "9 Mens Morris",
"dots": [
".5,.5",".5,4.5",".5,8.5",
"1.5,1.5","1.5,4.5","1.5,7.5",
"2.5,2.5","2.5,4.5","2.5,6.5",
"4.5,.5","4.5,1.5","4.5,2.5","4.5,6.5","4.5,7.5","4.5,8.5",
"6.5,2.5","6.5,4.5","6.5,6.5",
"7.5,1.5","7.5,4.5","7.5,7.5",
"8.5,.5","8.5,4.5","8.5,8.5"
],
"lines": [
"4.5,.5,4.5,2.5",
"6.5,4.5,8.5,4.5",
"4.5,6.5,4.5,8.5",
".5,4.5,2.5,4.5"
],
"squares": [
".5,.5,8,8",
"1.5,1.5,6,6",
"2.5,2.5,4,4"
]
}
Source Code for 9 Mens’ Morris in Python
Keep in mind, this only makes the “game board”. But it does demonstrate how to use Python to create SVG files.
import svgwrite
import json
# adjust as needed, it is a "S"VG after all
# 82 with min margins my Brother HL-L5200DW fit full page
# 67 with zero margins is designed for the wooden boards and CNC
scale = 82 # 82 for printing, 67 for CNC
dotsize = .15 # between .12 and .2 probably
stroke = 5 # should probably be an odd number so it's centered, probably 5 or 7
# didn't work, should be scale * 8 (paper) or 9.5 (CNC)
#dbwide = 570
#dbhigh = 570
# where will this be written?
# And create the dwg object
#dwg = svgwrite.Drawing('9menspython.svg', profile='tiny')
'''fixme:
couldn't get size/viewBox to work properly
this would be useful for printing easier without
messing with print dialogues, or to standardize
CNC conversions. These options work but do not
actually limit the size/viewBox.
particularly, I couldn't use a variable in the setttings
'''
dwg = svgwrite.Drawing(
'9menspython.svg',
#size=(637,637),
#viewBox=(0,0,637,637)
)
# open json file
with open("9men.json", "r") as read_file:
# put it into a dictionary called "data"
data = json.load(read_file)
'''
makedots:
takes xy coords, radius and fill color
makes a dot at that location
'''
def makedots(x,y,r,color):
x = x * scale
y = y * scale
r = dotsize * scale
dwg.add(
dwg.circle(
center=(x,y),
r=r,
stroke=svgwrite.rgb(10, 10, 10),
fill=color
)
)
'''
makelines:
takes xy start and stopcoords
makes a line from xy1 to xy2
'''
def makelines(x1,y1,x2,y2):
x1 = x1 * scale
y1 = y1 * scale
x2 = x2 * scale
y2 = y2 * scale
dwg.add(
dwg.line(
start=(x1,y1),
end=(x2,y2),
stroke=svgwrite.rgb(10, 10, 20),
stroke_width=stroke
)
)
'''
makesquares:
takes xy near and far corners
draws rectangle using those points
'''
def makesquares(x1,y1,x2,y2):
# draw a nofill box
x1 = x1 * scale
y1 = y1 * scale
x2 = x2 * scale
y2 = y2 * scale
dwg.add(
dwg.rect(
(x1, y1), (x2, y2),
stroke=svgwrite.rgb(10, 10, 20),
stroke_width=stroke,
fill='none'
)
)
def rules(x,y,size,text):
x = x * scale
y = y * scale
dwg.add(
dwg.text(
text,
insert=(x,y),
stroke='none',
fill=svgwrite.rgb(15,15,15),
font_size=size,
font_weight="bold",
style="font-family:Arial"
)
)
# functions are defined above, so...
# do whatever the json says now
# Rules/Text:
rules(3.65,2.90,'18px',data['title'])
rules(2.74,3.15,'11px',"Each player starts with 9 stones. (Black vs White)")
rules(2.74,3.50,'11px',"Stage 1 (Placement): Take turns placing stones.")
rules(2.74,3.75,'11px',"Get 3 in a row, on a connected line, remove 1 of your")
rules(2.74,4.00,'11px',"opponent\'s pieces (break strings as last choice.)")
rules(2.74,4.40,'11px',"Stage 2 (Movement): Take turns moving your pieces.")
rules(2.74,4.60,'11px',"1 at a time, attempting to get 3 in a row, again.")
rules(2.74,4.80,'11px',"If you get 3 in a row, take 1 of your opponent\'s")
rules(2.74,5.00,'11px',"pices (break strings as a last choice)")
rules(2.74,5.40,'11px',"Stage 3 (Underdog): When anyone gets down to only")
rules(2.74,5.60,'11px',"3 pieces, they are not bound by lines and can jump")
rules(2.74,5.80,'11px',"to any unoccupied spot on board.")
rules(2.74,6.20,'11px',"To Win: Capture opponent down to 2 pieces")
# DOTS:
# loop over the len number of items in this list
# assign the numbers to "i", 0,1,2,3 etc
for i in range(len(data['dots'])):
# create a string from this data
chunk = data['dots'][i]
# spolit string into another list, on every comma
xy = chunk.split(',')
# make the data back to a float from string
xx = float(xy[0])
yy = float(xy[1])
# call the function
makedots(xx,yy,10,'black')
# LINES:
for i in range(len(data['lines'])):
chunk = data['lines'][i]
xy = chunk.split(',')
x1 = float(xy[0])
y1 = float(xy[1])
x2 = float(xy[2])
y2 = float(xy[3])
makelines(x1,y1,x2,y2)
# SQUARES:
for i in range(len(data['squares'])):
chunk = data['squares'][i]
xy = chunk.split(',')
x1 = float(xy[0])
y1 = float(xy[1])
x2 = float(xy[2])
y2 = float(xy[3])
makesquares(x1,y1,x2,y2)
#we did read it, but I want to see
# how python sees it.
with open('9mendict.json', 'w') as outfile:
json.dump(data, outfile,indent=4)
# output our svg image as raw xml
#print(dwg.tostring())
# write svg file to disk
dwg.save()
Download the SVG for 9 Men’s Morris
You may want to download the actual svg for 9 men’s morris created by this script.