Permalink
Browse files

Initial work on ratemycurve

  • Loading branch information...
1 parent 3d9c2b5 commit b1d7af8e556de9eb3dbae64601753383d7408112 Nick Johnson committed May 29, 2012
View
15 python/appengine/app.yaml
@@ -7,6 +7,19 @@ threadsafe: true
handlers:
- url: /random
script: randomcurve.app
+- url: /.*
+ script: main.app
builtins:
-- remote_api: on
+- remote_api: on
+- deferred: on
+
+libraries:
+- name: PIL
+ version: "1.1.7"
+- name: jinja2
+ version: "2.6"
+- name: webapp2
+ version: "2.3"
+- name: webob
+ version: "1.1.1"
View
60 python/appengine/ga.py
@@ -0,0 +1,60 @@
+import inspect
+import random
+
+import piclang
+
+ATOM_MUTATION_RATE = 1.0 # Atom mutations per organism
+OP_MUTATION_RATE = 0.5 # Operator mutations per organism
+CHANGE_TYPE_PROBABILITY = 0.1 # Chance an atom will change type
+
+def cut_and_splice(g1, g2):
+ p1 = random.randrange(len(g1))
+ p2 = random.randrange(len(g2))
+ return g1[:p1] + g2[p2:], g2[:p2] + g1[p1:]
+
+def mutate(genome):
+ atom_probability = ATOM_MUTATION_RATE / len(genome)
+ op_probability = OP_MUTATION_RATE / len(genome)
+ for i in range(len(genome)):
+ if isinstance(genome[i], (int, float, tuple, piclang.PlatonicCircle, piclang.PlatonicLine)):
+ if random.random() < atom_probability:
+ genome[i] = mutate_atom(genome[i])
+ else:
+ if random.random() < op_probability:
+ genome[i] = mutate_op(genome[i])
+ return genome
+
+def mutate_atom(instruction):
+ if random.random() < CHANGE_TYPE_PROBABILITY:
+ return random_atom(instruction)
+ if isinstance(instruction, (int, float)):
+ return mutate_number(instruction)
+ if isinstance(instruction, tuple):
+ if random.random < 0.5:
+ return (instruction[0], mutate_number(instruction[1]))
+ else:
+ return (mutate_number(instruction[0]), instruction[1])
+ atom_curves = (piclang.PlatonicCircle, piclang.PlatonicLine)
+ if isinstance(instruction, atom_curves):
+ return random.choice(atom_curves)()
+ return instruction
+
+def random_atom(old_atom=None):
+ typ = random.choice([float, tuple, piclang.PlatonicCircle, piclang.PlatonicLine])
+ if typ == float:
+ return random.random()
+ if typ == tuple:
+ return (random.random(), random.random())
+ return typ()
+
+def mutate_number(num):
+ if random.random() < 0.5:
+ # Smaller
+ return num * (1 - random.random() * 0.5)
+ else:
+ # Bigger
+ return num * (1 + random.random())
+
+def mutate_op(instruction):
+ ops = [piclang.translate, piclang.scale, piclang.rotate, piclang.reverse, piclang.concat, piclang.repeat, piclang.step]
+ return random.choice(ops)
View
24 python/appengine/main.py
@@ -0,0 +1,24 @@
+import webapp2
+from google.appengine.ext import ndb
+from webapp2_extras import jinja2
+
+import model
+
+class BaseHandler(webapp2.RequestHandler):
+ @webapp2.cached_property
+ def jinja2(self):
+ return jinja2.get_jinja2(app=self.app)
+
+ def render_template(self, filename, **template_args):
+ self.response.write(self.jinja2.render_template(filename, **template_args))
+
+
+class IndividualHandler(BaseHandler):
+ def get(self, id):
+ individual = model.Individual.get_by_id(int(id))
+ self.render_template('individual.html', individual=individual)
+
+
+app = webapp2.WSGIApplication([
+ (r'/individual/(\d+)', IndividualHandler),
+])
View
35 python/appengine/model.py
@@ -0,0 +1,35 @@
+from google.appengine.api import files
+from google.appengine.api import images
+from google.appengine.ext import ndb
+
+import piclang
+
+class Individual(ndb.Model):
+ genome = ndb.PickleProperty(compressed=True)
+ generation = ndb.IntegerProperty(required=True)
+ parents = ndb.KeyProperty(kind='Individual', repeated=True)
+ fitness = ndb.IntegerProperty(default=1500)
+ matches = ndb.IntegerProperty(default=0)
+ image = ndb.BlobKeyProperty(required=True)
+
+ @classmethod
+ def create(cls, genome, generation, parents):
+ fun = piclang.stackparse(genome)
+ image = piclang.render(fun, points=8192)
+ filename = files.blobstore.create(mime_type='image/png')
+ with files.open(filename, 'a') as f:
+ image.save(f, "PNG")
+ files.finalize(filename)
+ blob_key = files.blobstore.get_blob_key(filename)
+ individual = cls(
+ genome=genome,
+ generation=generation,
+ parents=parents,
+ image=blob_key
+ )
+ individual.put()
+ return individual
+
+ @property
+ def image_url(self):
+ return images.get_serving_url(self.image)
View
1 python/appengine/piclang.py
View
9 python/appengine/templates/base.html
@@ -0,0 +1,9 @@
+<html>
+<head>
+ <title>{% block title %}Rate My Curve{% endblock %}</title>
+ {% block head %}{% endblock %}
+</head>
+<body>
+ {% block body %}{% endblock %}
+</body>
+</html>
View
16 python/appengine/templates/individual.html
@@ -0,0 +1,16 @@
+{% extends "base.html" %}
+{% block title %}Individual #{{individual.key.id()}}{% endblock %}
+{% block body %}
+<h2>Individual #{{individual.key.id()}}</h2>
+<img src="{{individual.image_url}}" />
+<table>
+ <tr><th>Generation</th><td>{{individual.generation}}</td></tr>
+ <tr><th>Parents</th><td>
+ {% for parent_key in individual.parents %}
+ <a href="/individual/{{parent_key.id()}}">#{{parent_key.id()}}</a>
+ {% endfor %}
+ </td></tr>
+ <tr><th>Matches</th><td>{{individual.matches}}</td></tr>
+ <tr><th>Score</th><td>{{individual.fitness}}</td></tr>
+</table>
+{% endblock %}
View
31 python/piclang.py
@@ -135,7 +135,10 @@
import inspect
import math
import numbers
-import readline
+try:
+ import readline
+except ImportError:
+ pass
try:
from PIL import Image, ImageDraw
except ImportError:
@@ -302,16 +305,24 @@ def __repr__(self):
l = "(%s)" % l
return "%s // %r" % (l, self.steps)
-@FunctionCurve
-def circle(t):
- theta = 2 * math.pi * t
- return (math.sin(theta), math.cos(theta))
+class PlatonicCircle(Curve):
+ def __call__(self, t):
+ theta = 2 * math.pi * t
+ return (math.sin(theta), math.cos(theta))
+
+ def __repr__(self):
+ return "circle"
+
+circle = PlatonicCircle()
+class PlatonicLine(Curve):
+ def __call__(self, t):
+ return (t, t)
-@FunctionCurve
-def line(t):
- return (t, t)
+ def __repr__(self):
+ return "line"
+line = PlatonicLine()
def boustro(func, times):
return repeat(concat(func, reverse(func)), times * 0.5)
@@ -345,7 +356,7 @@ def render(f, points=1000, size=800, penwidth=6, gapwidth=6, bgcolor=(0, 0, 0),
for src, dest in zip(point_list, point_list[1:]):
draw.line((src, dest), fill=bgcolor, width=penwidth+gapwidth*2)
draw.line((src, dest), fill=fgcolor, width=penwidth)
- im.show()
+ return im
class PicStack(object):
def __init__(self):
@@ -377,7 +388,7 @@ def stackparse(expr):
"""Parses a stack-based representation of a curve expression, returning the expression tree."""
stack = PicStack()
for token in expr:
- if token is circle or token is line:
+ if isinstance(token, (PlatonicCircle, PlatonicLine)):
stack.push(token)
elif isinstance(token, (int, float, tuple)):
stack.push(token)

0 comments on commit b1d7af8

Please sign in to comment.