Python 3 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 python 3 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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
{ "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.
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 |
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.