# Haferman Carpet

The Haferman Carpet is a fractal, which kind of looks like a woven carpet. To generate it, you start with a single black pixel and apply in each cycle a set of rules.
In each generation every pixel in the carpet will be replaced by nine pixels according to the rules. A black pixel is represented by a 0, a white one by a 1.

#### The rules

• $0 \rightarrow \left( \begin{array}{ccc}1&0&1\\0&1&0\\1&0&1\end{array} \right) \text{and } 1 \rightarrow \left( \begin{array}{ccc}0&0&0\\0&0&0\\0&0&0\end{array} \right)$

# Python 2.7.7 Code
# Pygame 1.9.1 (for Python 2.7.7)
# Jonathan Frech 26th of February, 2016

# importing needed modules
import pygame, sys, time, math, os, random

# maximum iterations
ITERATIONS = 7

# usage: 'q' weaves the carpet, 'space' saves a screenshot
# (Weaving can take some time, let your computer calculate!)

""" GAME """
# game class
class GAME():
# initialize program
def __init__(self):
self.width, self.height = 3**ITERATIONS, 3**ITERATIONS
self.size = [self.width, self.height]
self.surface = pygame.Surface(self.size)
self.screen = pygame.display.set_mode(self.size)

self.ticks = 0
self.running = True

self.iteration = 0
self.carpet = pygame.Surface([3**self.iteration, 3**self.iteration])

# functions
pygame.display.set_caption("Haferman Carpet")

# weave get to the next generation
def weave(self):
# do not overweave!
if self.iteration >= ITERATIONS:
return

# the new carpet will be 3 times as big in each dimension
self.iteration += 1
carpet = pygame.Surface([3**self.iteration, 3**self.iteration])

# go thru old carpet
rct = self.carpet.get_rect()
for x in range(0, rct.right):
for y in range(0, rct.bottom):
# apply rules
if self.carpet.get_at([x, y]) != (255, 255, 255, 255):
carpet.set_at([3*x, 3*y], [255, 255, 255])
carpet.set_at([3*x+2, 3*y], [255, 255, 255])
carpet.set_at([3*x+1, 3*y+1], [255, 255, 255])
carpet.set_at([3*x, 3*y+2], [255, 255, 255])
carpet.set_at([3*x+2, 3*y+2], [255, 255, 255])

# update carpet
self.carpet = carpet

# tick function
def tick(self):
# handle events
for event in pygame.event.get():
if event.type == pygame.QUIT:
self.quit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
self.screenshot()

if event.key == pygame.K_q:
self.weave()

# render function
def render(self):
# fill
self.surface.fill([0, 0, 0])

self.surface.blit(pygame.transform.scale(self.carpet, self.size), [0, 0])

# blit and flip
self.screen.blit(self.surface, [0, 0])
pygame.display.flip()

# quits
def quit(self):
self.running = False

# takes a screenshot
def screenshot(self):
path = os.getcwd() + "/out/"

try:
if not os.path.isdir(path):
os.mkdir(path)

name = "img" + str(len(os.listdir(path))) + ".png"
pygame.image.save(self.surface, path + name)
except:
pass

# run function (uses tick() and render())
def run(self):
ticksPerSecond = 60
lastTime = time.time() * 1000000000
nsPerTick =  1000000000.0 / float(ticksPerSecond)

ticks = 0
frames = 0

lastTimer = time.time() * 1000
delta = 0.0

while self.running:
now = time.time() * 1000000000
delta += float(now - lastTime) / float(nsPerTick)
lastTime = now
shouldRender = False

while delta >= 1:
ticks += 1
self.ticks += 1
self.tick()
delta -= 1
shouldRender = True

if shouldRender:
frames += 1
self.render()

if time.time() * 1000 - lastTimer >= 1000:
lastTimer += 1000

# debug
#print("Frames: " + str(frames) + ", ticks: " + str(ticks))

frames = 0
ticks = 0

# start game
G = GAME()
G.run()