Here’s my solution… It’s the bare minimum of what I wanted to do.
I’ve been wanting to do some L-System type stuff for a while, so I
focused a bit on that. There’s lots more I want to put into it, like
fractional levels, and support for contextual grammars (this is
context-free).
The design was inspired by Dennis Ranke’s second solution to the dice
rolling quiz (#61). I had intended to reuse parts of the turtle
graphics quiz, but seeing as how I’ve also got “learn SVG” on my to-do
list, I did some quick and dirty SVG output. (Mozilla should be able
to open the file.)
class LSystem
def initialize(&block)
@rules = {}
instance_eval(&block)
end
def rule(var)
raise “Rule for #{var} must be unique!” if
@rules.include?(var.to_sym)
@rules[var.to_sym] = yield.map { |x| x.to_sym }
end
def start(var)
@start = var.to_sym
end
def evaluate(n)
product = [@start]
n.to_i.times do |i|
product.map! do |s|
@rules[s.to_sym] || s.to_sym
end.flatten!
end
product.each do |p|
send(p)
end
end
end
koch = LSystem.new do
start :F
rule(:F) { %w(F + F - F - F + F) }
def F
nx, ny = @x + @dx, @y + @dy
puts <<-LINE
LINE
@x, @y = nx, ny
end
def +
@dx, @dy = -@dy, @dx
end
def -
@dx, @dy = @dy, -@dx
end
def evaluate(n)
raise “N must be non-negative” if n < 0
@x, @y = 0, 0
@dx, @dy = 900 / (3 ** n), 0
puts <<-HEADER
<?xml version="1.0" encoding="ISO-8859-1" standalone="no"?>
HEADER
super
puts <<-FOOTER
FOOTER
end
end
koch.evaluate(ARGV[0].to_i)