Below is the file 'forms.py' from this revision. You can also download the file.
import random import config def haiku(doc, form=[5,7,5]): markov = doc.symbol_state.forward_markov def pickfrom(possible, total, get_total): k = random.randint(0, total - 1) for seq in possible: k -= get_total(seq) if k < 0: return seq raise Exception("Failed to pick a number - 'total' miscalcuation? (%d, %d)" % (k, total)) def generate_line(target, state): state = tuple(state) line = [] count = 0 syl = doc.syllables.lookup if len(state) < markov.size: # we'll have to pick a starting point # this is the most accurate, but way too slow # #possible = filter(lambda seq: sum(map(syl, seq)) < form[0], markov.scores) #total_possible = sum(map(lambda seq: markov.scores[seq].total, possible)) #seq = pickfrom(possible, total_possible, lambda seq: markov.scores[seq].total) # seq = pickfrom(markov.scores, markov.total, lambda seq: markov.scores[seq].total) count += sum(map(syl, seq)) line += list(seq) state = seq elif len(state) > markov.size: state = state[-1*markov.size:] while count < target: maxsize = target - count score = markov.scores[state] # okay, if count + syl(next_token) != target then we need there # to be an entry in the symbolstate for that next potential # symbol. this lets us restrict further and not fall down holes # so often def is_not_deadend(id): next_count = count + syl(id) if next_count == target: return True next_state = (state + (id,))[1:] next_score = markov.scores.get(next_state) return next_score != None possible = set(filter(lambda id: syl(id) <= maxsize and is_not_deadend(id), score.scores)) print "status:", target, state, score.scores.keys(), possible total_possible = sum(map(lambda tok: score.scores[tok], possible)) if not possible: break token = pickfrom(possible, total_possible, lambda seq: score.scores[seq]) count += syl(token) state = (state + (token,))[1:] line.append(token) if count != target: return None else: return line rv = [] last_line= [] for length in form: print "** target length is:", length for i in xrange(config.haiku_line_attempts): line = generate_line(length, last_line) if line != None: break print "** resulting line is:", line if not line: return None rv.append(line) last_line = line return rv