from google.appengine.ext import webapp
import random
import pickle
from operator import mul
import operator
import HTML
from google.appengine.ext import ndb
from google.appengine.api import memcache
import csv
from google.appengine.ext import deferred
MEMCACHE_MAX_ITEM_SIZE = 900 * 1024
TOSS = 500
PLAYERS = 100
class simple_counter(ndb.Model):
counter = ndb.IntegerProperty(default=0)
class score_history(ndb.Model):
chart_data = ndb.TextProperty()
dt = ndb.DateTimeProperty(auto_now=True)
class highest_score(ndb.Model):
score = ndb.TextProperty()
@classmethod
def return_current_highest(cls):
s = memcache.get('largest_ever')
if s is None:
x = highest_score.get_or_insert('current_highest')
score = x.score
if score is None:
memcache.set('largest_ever',0)
return 0
else:
memcache.set('largest_ever',int(score))
return int(score)
else:
return s
@classmethod
def set_new_highest_score(cls,score):
x = highest_score.get_or_insert('current_highest') # todo hmm.
saved_highest = x.score
if saved_highest is None:
x.score = str(score)
x.put()
memcache.set('largest_ever',int(score))
saved_highest = x.score
if int(saved_highest)<score:
x.score = str(score)
x.put()
c = score_history()
c.chart_data= str(score)
c.put()
memcache.set('largest_ever',int(score))
class current_round_data(ndb.Model):
data = ndb.PickleProperty()
# todo track two strings side by side marking changes as you go, as tool.
def create_new_generation():
memcache_data = memcache.get('saved_round_data')
if memcache_data is None:
saved_data = current_round_data.get_or_insert('saved_round_data')
else:
saved_data = memcache_data
sorted_candidate_list = make_ten_generations(saved_data.data)
sorted_candidate_list = sorted(sorted_candidate_list,key=operator.itemgetter(1)) # sort by the score in the tuple
sorted_candidate_list.reverse() # reverse it so that the highest score is first
winner = sorted_candidate_list[0][1] # The person with the highest product wins. [0] is the first item in the list, and [1] is the second item in that item, i.e. the score
highest_score.set_new_highest_score(winner)
saved_data.data=sorted_candidate_list
d= saved_data.data
saved_data.put()
memcache.set('saved_round_data',saved_data)
class generations(webapp.RequestHandler):
def get(self):
memcache_data = memcache.get('saved_round_data')
if memcache_data is None:
saved_data = current_round_data.get_or_insert('saved_round_data')
if saved_data.data is None:
create_new_generation()
self.redirect('/')
return
else:
saved_data = memcache_data.data
render_details(self,saved_data)
def make_ten_generations(saved_data=None):
sc = simple_counter.get_or_insert('just_the_one')
sc.counter+=1 # update the counters only when we make a new generation
sc.put()
if saved_data is None:
data = play_a_round() # generate a list ['H','H','T',...]
else:
n = []
for d, score in saved_data:
n.append(d)
data = n # use what we're passed
#todo this sucks
candidate_list = determine_score(data) #score the data by creating a tuple with the data in postion 0 and the score in position 1
i = 0
while i<1:
candidate_list=new_generation(candidate_list)
i+=1
return candidate_list
class MainHandler(webapp.RequestHandler):
def get(self):
create_new_generation()
def render_details(self,sorted_candidate_list):
sc = simple_counter.get_or_insert('just_the_one')
memd = get('prerendered_data'+str(sc.counter))
if memd:
self.response.out.write(memd)
return
winner = sorted_candidate_list[0][1] # The person with the highest product wins. [0] is the first item in the list, and [1] is the second item in that item, i.e. the score
s = []
if winner > 10**60:
s.append("we have a winner with a score of {0}".format(winner))
else:
s.append('A new generation will be created every 1 minute. Mutation rate is in 1 in 10 that a particular toss value will change to a randomly selected value that might be the same as the value it is replacing.<br>'
'This is iteration {3}<br> Refresh the page in a minute to see the next generation.'
'Highest score this round {0}<br>'
'Which is<br>{1}<br>away from the target of <br>'
'{2}'
''
''.format(winner,10**60-winner,10**60,sc.counter))
s.append('<br>Largest score generated so far: {0}'.format(highest_score.return_current_highest()))
table = render_table(sorted_candidate_list)
ss = ''.join(s)
sss = ss + table
set('prerendered_data'+str(sc.counter),sss)
self.response.out.write(sss)
#todo graph score over time.
def render_table(current_round_list_of_tuples):
t = HTML.Table(header_row=['Score'])
try:
for i, (data, score) in enumerate(current_round_list_of_tuples):
colored_data = []
for toss in data:
if toss =='H':
colored_data.append(HTML.TableCell('H', bgcolor='green'))
else:
colored_data.append(HTML.TableCell('T', bgcolor='grey'))
x = [score]
x.extend(colored_data)
t.rows.append(x)
except ValueError:
pass
htmlcode = str(t)
return htmlcode
def split_list(alist, wanted_parts=1):
length = len(alist)
return [alist[i * length // wanted_parts: (i + 1) * length // wanted_parts]
for i in range(wanted_parts)]
def determine_score(list_list_of_tosses):
round_score = []
for r in list_list_of_tosses:
counter = 0
score = []
for toss in r:
if toss == 'H':
# As they do so, they record all runs of heads
counter+=1
if toss == 'T':
# so that if they toss H T T H H H T H T T H H H H T T T,
if counter > 0:
# they will record: 1, 3, 1, 4, representing the number of heads in each run.
score.append(counter)
counter = 0
round_score.append(score)
final_scores = []
for i, player_scores in enumerate(round_score):
# At the end of each round, each player computes the product of their runs-of-heads.
final_scores.append((list_list_of_tosses[i],reduce(mul,player_scores))) # keep the scores and the list together via a tuple
return final_scores
def point_mutate(a_list):
new_list = a_list[:]
for i, toss in enumerate(a_list):
counter = random.randint(0,100)
if counter > 90:
new_list[i] = (random.choice(['H','T'])) # generate a new value
return new_list
def play_a_round():
round = []
for t in range(PLAYERS):
# On each turn, players toss a fair coin 500 times.
player_round = []
for p in range(TOSS):
player_round.append(random.choice(['H','T']))
round.append(player_round)
return round
def new_generation(sorted_candidate_list):
l = len(sorted_candidate_list)
half = l/2
sorted_candidate_list = sorted(sorted_candidate_list,key=operator.itemgetter(1)) # sort by the score in the tuple
sorted_candidate_list.reverse()
without_bottom_50 = sorted_candidate_list[:half]
new_50_items = []
try:
for existing_item, score in without_bottom_50:
new_string = point_mutate(existing_item)
new_50_items.append(new_string)
except ValueError:
pass
data_only = []
for data, score in without_bottom_50: # get rid of the tuple score now.
data_only.append(data)
data_only.extend(new_50_items)
new_scored_list = determine_score(data_only)
return new_scored_list
def delete(key):
chunk_keys = memcache.get(key)
if chunk_keys is None:
return False
chunk_keys.append(key)
memcache.delete_multi(chunk_keys)
return True
def set(key, value):
pickled_value = pickle.dumps(value)
# delete previous entity with the given key
# in order to conserve available memcache space.
delete(key)
pickled_value_size = len(pickled_value)
chunk_keys = []
for pos in range(0, pickled_value_size, MEMCACHE_MAX_ITEM_SIZE):
# TODO: use memcache.set_multi() for speedup, but don't forget
# about batch operation size limit (32Mb currently).
chunk = pickled_value[pos:pos + MEMCACHE_MAX_ITEM_SIZE]
# the pos is used for reliable distinction between chunk keys.
# the random suffix is used as a counter-measure for distinction
# between different values, which can be simultaneously written
# under the same key.
chunk_key = '%s%d%d' % (key, pos, random.getrandbits(31))
is_success = memcache.set(chunk_key, chunk)
if not is_success:
return False
chunk_keys.append(chunk_key)
return memcache.set(key, chunk_keys)
def get(key):
chunk_keys = memcache.get(key)
if chunk_keys is None:
return None
chunks = []
for chunk_key in chunk_keys:
# TODO: use memcache.get_multi() for speedup.
# Don't forget about the batch operation size limit (currently 32Mb).
chunk = memcache.get(chunk_key)
if chunk is None:
return None
chunks.append(chunk)
pickled_value = ''.join(chunks)
try:
return pickle.loads(pickled_value)
except Exception:
return None
app = webapp.WSGIApplication([
('/random', MainHandler) ,
('/', generations)
],
debug=True)