[Edu-sig] More Pythonic Soccer Ball Geometry (was HexaPent)

kirby urner kirby.urner at gmail.com
Sat Jul 22 19:42:01 CEST 2006

So here's a simple CS approach to the first/simplest hexapent:  the
soccer ball (at least until Adidas messed with it).  You've got the 12
pentagons and the 20 hexagons.

This lesson plan should fly easily in cultures which already have that
image deeply engrained (USAers still lag in this respect, probably
always will, given their "pig skin" fascination).

I'm using what I'd call intermediate-to-beginner Python here.  No
class definitions, everything procedural.  However, I do use globals,
and take for granted the set, list comprehension, slicing.  Lots of
basic syntax is already in play.

It's a script for building a data structure, step by step, starting
from an encoding of the icosahedron's 20 faces, in terms of 3-tuples.
The letters A-L label the 12 vertices.

Those 12 vertices transform into the 12 pentagons of any hexa-pent,
leaving the triangles to become 20 hexagons (Glenn calls this a
12/20).  Then I go through a lot of selecting and sorting with the
following goal in mind:  build a dictionary such that every face has a
name (the keys) and every face is paired with its five or six
neighbors (the values).

Note that the tricks I use to build this data structure (globaldata)
aren't necessarily going to scale when we move to the higher frequency
hexapents (12 pentagons, more hexagons).

Here' we go:


# here is a topological icosahedron, expressed as openings (faces)

# Note: I recycled this from my rbf.py but changed the letters to
# something friendlier

baseicosa = [('A', 'C', 'I'),
	     ('A', 'L', 'B'),
	     ('B', 'D', 'K'),
	     ('D', 'C', 'J'),
	     ('I', 'E', 'G'),
	     ('G', 'J', 'H'),
	     ('K', 'F', 'H'),
	     ('L', 'F', 'E'),
	     ('A', 'B', 'D'),
	     ('A', 'C', 'D'),
	     ('A', 'L', 'I'),
	     ('I', 'E', 'L'),
	     ('C', 'I', 'G'),
	     ('C', 'G', 'J'),
	     ('L', 'B', 'F'),
	     ('K', 'B', 'F'),
	     ('D', 'K', 'J'),
	     ('H', 'K', 'J'),
	     ('F', 'E', 'H'),
	     ('G', 'E', 'H')]

# we will consider these the names of 12 pentagons then add a
# 'soccer ball' pattern of 20 hexagons, named for the pentagons
# at their 3 alternate edges, e.g. hexagon ACI.

# Every hexagon will have six neighbors:  three pentagons with
# one letter in common, three hexagons with two letters is common
# Every pentagon with have five neighbors:  five hexagons with
# one letter in common.

pents = ['A','B','C','D','E','F','G','H','I','J','K','L']  # 20
print "Pents: %s" % pents

def gethexes():
    thelist = []
    for t in baseicosa:
	thehex = ''.join(sorted(t))
    return thelist

hexes = gethexes()
print "Hexes: %s" % hexes

# and let's get the edges while we're at it.  Every edge of every
# triangle is an edge.  We'll use the set data structure to police
# against duplicates

def getedges():
    theset = set()
    for t in baseicosa:
 	for i in (t[:2], t[1:], t[-1]+t[0]):
    return sorted(list(theset))	

edges = getedges()
print "Edges: %s" % edges

# now we'd like a global dictionary in which every pentagon is paired
# with its 5 neighbors.  These will simply be all hexagons with
# a letter in common.

globaldata = {}

def buildpents():
    result = {}
    for p in pents:
	neighbors = []
	for h in hexes:
	    if p in h:
	result[p] = neighbors
    return result

result = buildpents()
print "Pent neighbors: %s" % result

# adding to our globaldata, will be all the hexagons paired with
# their six neighbors:  3 pentagons, and 3 hexagons across an edge.
# We detect edge sharing as a boundary condition, i.e. if a panel
# has two (and only two) letters in common, it's across an edge

def buildhexes():
    result = {}
    for h in hexes:
	neighbors = list(h) # 3 pentagons
	for candidate in hexes:
	    votes = 0
	    for i in range(3):
		if h[i] in candidate:
		   votes += 1
	    if votes == 2:
	result[h] = neighbors
    return result

result = buildhexes()
print "Hexa neighbors: %s" % result

More information about the Edu-sig mailing list