patternpythonMinor
Small turtle program using instruction strings to draw patterns
Viewed 0 times
turtledrawprograminstructionpatternssmallusingstrings
Problem
A while ago, I wrote a program that combined L-systems and turtles. This was before I understood OOP (I still don't fully understand classes, or if they are even needed here). I tried to add some documentation and included some demos as functions. If I ever get around to it, I hope to include a simple GUI with user-adjustable patterns and parameters (like a typical Java applet).
Generate L-system
The inclusion of
Run the turtle
```
def run_turtle(var, start, rules, iters, angle, startdir=0):
"""Var, start, rules and iters, correspond to (V, w, P, n) of the
l-system function. The distance moved is scaled down from size.
The turtle starts facing startdir.
Instructions are defined as the following:
F, G: Draw forward
M, N: Move forward (don't draw)
[, ]: Push and pop angle and location
+, -: Turn left and right by angle degrees
Variables not described can be used as constants.
"""
# Initialization
terry = turtle.Turtle()
turtle.mode("world") # Coordinate system
terry.pensize(1)
terry.pencolor("blue")
terry.speed(0) # Instant speed
turtle.tracer(0, 0) # Don't draw anything yet (could change in future)
turtle.setup(width=900, height=900, startx=None, starty=None) # Square pixels
terry.hideturtle()
dist = 1
positions = []
Generate L-system
The inclusion of
V, the alphabet, is for completeness in the formal description of a L-system as a 3-tuple. import turtle
def l_system(V, w, P, n):
"""Generates an L-system run for n rounds.
They are defined as
G = (V, w, P)
V = The alphabet (tuple, not actually used, can be specified as None)
w = The start (string)
P = The production rules (dictionary for replacement)
"""
# Make sure all production rules are in alphabet
if V:
assert(all(key in V for key in P))
current = w
for i in range(n):
current = [P[x] if x in P else x for x in list(current)]
current = ''.join(current)
return currentRun the turtle
```
def run_turtle(var, start, rules, iters, angle, startdir=0):
"""Var, start, rules and iters, correspond to (V, w, P, n) of the
l-system function. The distance moved is scaled down from size.
The turtle starts facing startdir.
Instructions are defined as the following:
F, G: Draw forward
M, N: Move forward (don't draw)
[, ]: Push and pop angle and location
+, -: Turn left and right by angle degrees
Variables not described can be used as constants.
"""
# Initialization
terry = turtle.Turtle()
turtle.mode("world") # Coordinate system
terry.pensize(1)
terry.pencolor("blue")
terry.speed(0) # Instant speed
turtle.tracer(0, 0) # Don't draw anything yet (could change in future)
turtle.setup(width=900, height=900, startx=None, starty=None) # Square pixels
terry.hideturtle()
dist = 1
positions = []
Solution
I like the concept of the question (using a domain-specific language to specify a fractal) and your implementation. An object-oriented rewrite could make it better, but it's not bad as it is.
Namely,
Aside from that, I just have a few minor remarks:
l_system() could be better written asdef l_system(V, w, P, n):
…
cmd_seq = w
for _ in range(n):
cmd_seq = itertools.chain(*(P.get(cmd, cmd) for cmd in cmd_seq))
return ''.join(cmd_seq)Namely,
cmd_seqwould be more meaningful thancurrent.cmdwould be more meaningful thanx.
- Use
_for a variable whose value is disregarded.
- String are directly iterable; you don't have to convert them into lists first.
- Dictionary lookup with a default can be done using
get(key, default).
- Instead of re-forming the string with each iteration, just chain them, and join the result just once at the end.
Aside from that, I just have a few minor remarks:
- The parameters are
l_system(V, w, P, n)butrun_turtle(var, start, rules, iters, …)— why not use consistent notation?
- As you said, the
l_system()function doesn't really needV. I would prefer to just leave it out altogether.
- In various places, you refer to the turtle's heading as
terry.heading(),startdir, andangles. I suggest using consistent terminology based on "heading".
update_bounds(bounds)takes aboundsarray as an explicit parameter, but takesterrythrough its scope chain. I'd prefer to see either both variables as explicit parameters, or both via the closure.
- Rather than having two separate stacks for
positionsandangles, I would prefer to see oneturtle_statestack that stores(position, heading)tuples.
- The epilogue could use a comment, like
# Extend the shorter dimension of the window to make it square
Code Snippets
def l_system(V, w, P, n):
…
cmd_seq = w
for _ in range(n):
cmd_seq = itertools.chain(*(P.get(cmd, cmd) for cmd in cmd_seq))
return ''.join(cmd_seq)Context
StackExchange Code Review Q#114121, answer score: 3
Revisions (0)
No revisions yet.