From 7046b53f201b2c1979243961f5adeb3ca3d656cb Mon Sep 17 00:00:00 2001 From: "Brian S. Stephan" Date: Thu, 21 May 2015 21:41:42 -0500 Subject: [PATCH] pi: add an app to simulate pi via monte carlo --- dr_botzo/dr_botzo/settings.py | 1 + dr_botzo/pi/__init__.py | 0 dr_botzo/pi/admin.py | 8 +++ dr_botzo/pi/ircplugin.py | 42 ++++++++++++++++ dr_botzo/pi/migrations/0001_initial.py | 22 ++++++++ dr_botzo/pi/migrations/__init__.py | 0 dr_botzo/pi/models.py | 69 ++++++++++++++++++++++++++ 7 files changed, 142 insertions(+) create mode 100644 dr_botzo/pi/__init__.py create mode 100644 dr_botzo/pi/admin.py create mode 100644 dr_botzo/pi/ircplugin.py create mode 100644 dr_botzo/pi/migrations/0001_initial.py create mode 100644 dr_botzo/pi/migrations/__init__.py create mode 100644 dr_botzo/pi/models.py diff --git a/dr_botzo/dr_botzo/settings.py b/dr_botzo/dr_botzo/settings.py index 4108d45..2cedc57 100644 --- a/dr_botzo/dr_botzo/settings.py +++ b/dr_botzo/dr_botzo/settings.py @@ -39,6 +39,7 @@ INSTALLED_APPS = ( 'ircbot', 'karma', 'markov', + 'pi', 'races', 'seen', ) diff --git a/dr_botzo/pi/__init__.py b/dr_botzo/pi/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/dr_botzo/pi/admin.py b/dr_botzo/pi/admin.py new file mode 100644 index 0000000..2746ab2 --- /dev/null +++ b/dr_botzo/pi/admin.py @@ -0,0 +1,8 @@ +"""Manage pi models in the admin interface.""" + +from django.contrib import admin + +from pi.models import PiLog + + +admin.site.register(PiLog) diff --git a/dr_botzo/pi/ircplugin.py b/dr_botzo/pi/ircplugin.py new file mode 100644 index 0000000..5e80e8e --- /dev/null +++ b/dr_botzo/pi/ircplugin.py @@ -0,0 +1,42 @@ +# coding: utf-8 +from __future__ import unicode_literals + +import logging + +from ircbot.lib import Plugin +from pi.models import PiLog + + +log = logging.getLogger('pi.ircplugin') + + +class Pi(Plugin): + + """Use the Monte Carlo method to simulate pi.""" + + def start(self): + """Set up the handlers.""" + + self.connection.reactor.add_global_regex_handler(['pubmsg', 'privmsg'], r'^!pi$', + self.handle_pi, -20) + + super(Pi, self).start() + + def stop(self): + """Tear down handlers.""" + + self.connection.reactor.remove_global_regex_handler(['pubmsg', 'privmsg'], self.handle_pi) + + super(Pi, self).stop() + + def handle_pi(self, connection, event, match): + """Handle the pi command by generating another value and presenting it.""" + + newest, x, y, hit = PiLog.objects.simulate() + msg = ("({0:.10f}, {1:.10f}) is {2}within the unit circle. π is {5:.10f}. (i:{3:d} p:{4:d})" + "".format(x, y, "" if hit else "not ", newest.count_inside, newest.count_total, newest.value())) + + return self.bot.reply(event, msg) + + +plugin = Pi diff --git a/dr_botzo/pi/migrations/0001_initial.py b/dr_botzo/pi/migrations/0001_initial.py new file mode 100644 index 0000000..3fcedfb --- /dev/null +++ b/dr_botzo/pi/migrations/0001_initial.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='PiLog', + fields=[ + ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), + ('count_inside', models.PositiveIntegerField()), + ('count_total', models.PositiveIntegerField()), + ('created', models.DateTimeField(auto_now_add=True)), + ], + ), + ] diff --git a/dr_botzo/pi/migrations/__init__.py b/dr_botzo/pi/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/dr_botzo/pi/models.py b/dr_botzo/pi/models.py new file mode 100644 index 0000000..113caaa --- /dev/null +++ b/dr_botzo/pi/models.py @@ -0,0 +1,69 @@ +"""Karma logging models.""" + +from __future__ import unicode_literals + +import logging +import math +import pytz +import random + +from django.conf import settings +from django.db import models + + +log = logging.getLogger('pi.models') + + +class PiLogManager(models.Manager): + + """Assemble some queries against PiLog.""" + + def simulate(self): + """Add one more entry to the log, and return it.""" + + try: + latest = self.latest() + except PiLog.DoesNotExist: + latest = PiLog(count_inside=0, count_total=0) + latest.save() + + inside = latest.count_inside + total = latest.count_total + + x = random.random() + y = random.random() + hit = True if math.hypot(x,y) < 1 else False + + if hit: + newest = PiLog(count_inside=inside+1, count_total=total+1) + else: + newest = PiLog(count_inside=inside, count_total=total+1) + newest.save() + + return newest, x, y, hit + + +class PiLog(models.Model): + + """Track pi as it is estimated over time.""" + + count_inside = models.PositiveIntegerField() + count_total = models.PositiveIntegerField() + created = models.DateTimeField(auto_now_add=True) + + objects = PiLogManager() + + class Meta: + get_latest_by = 'created' + + def __unicode__(self): + """String representation.""" + + tz = pytz.timezone(settings.TIME_ZONE) + return "({0:d}/{1:d}) @ {2:s}".format(self.count_inside, self.count_total, + self.created.astimezone(tz).strftime('%Y-%m-%d %H:%M:%S %Z')) + + def value(self): + """Return this log entry's value of pi.""" + + return 4.0 * int(self.count_inside) / int(self.count_total)