<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"><title>flowblok’s blog</title><link href="/" rel="alternate"></link><link href="feeds/all.atom.xml" rel="self"></link><id>/</id><updated>2019-04-08T00:00:00+10:00</updated><link href="http://pubsubhubbub.appspot.com/" rel="hub"></link><entry><title>Galactic Puzzle Hunt 2019: Reflection</title><link href="2019-04/galactic-puzzle-hunt-2019-reflection.html" rel="alternate"></link><published>2019-04-08T00:00:00+10:00</published><updated>2019-04-08T00:00:00+10:00</updated><author><name>Peter Ward</name></author><id>tag:None,2019-04-08:2019-04/galactic-puzzle-hunt-2019-reflection.html</id><summary type="html">&lt;p&gt;I recently organized and participated in a team for the 2019 edition of the
&lt;a class="reference external" href="https://2019.galacticpuzzlehunt.com/"&gt;Galactic Puzzle Hunt&lt;/a&gt;. This post is my reflections on that process.&lt;/p&gt;
&lt;div class="section" id="organizing-a-team"&gt;
&lt;h2&gt;Organizing a team&lt;/h2&gt;
&lt;p&gt;Teams for the puzzle hunt can be up to 10 people &lt;a class="footnote-reference" href="#id6" id="id2"&gt;[1]&lt;/a&gt;, so the first step was to
attempt to find other …&lt;/p&gt;&lt;/div&gt;</summary><content type="html">&lt;p&gt;I recently organized and participated in a team for the 2019 edition of the
&lt;a class="reference external" href="https://2019.galacticpuzzlehunt.com/"&gt;Galactic Puzzle Hunt&lt;/a&gt;. This post is my reflections on that process.&lt;/p&gt;
&lt;div class="section" id="organizing-a-team"&gt;
&lt;h2&gt;Organizing a team&lt;/h2&gt;
&lt;p&gt;Teams for the puzzle hunt can be up to 10 people &lt;a class="footnote-reference" href="#id6" id="id2"&gt;[1]&lt;/a&gt;, so the first step was to
attempt to find other people who are at least as insane as I am. I put out a
&lt;a class="reference external" href="https://twitter.com/flowblok/status/1100166796785504256"&gt;tweet&lt;/a&gt; linking to a Google Form (and also messaged a few friends), asking
(amongst a few other things) for a time commitment, with the intent that I’d
select team members most happy to spend time on it.&lt;/p&gt;
&lt;p&gt;As it happened, I only got seven signups (including myself), so that was my
team formed. I thought that was a reasonable number, but it did have a lot of
people only able to make a small time commitment, and I should have kept
searching for more people. My perception was we had ~2.5 daytime solvers on
average.&lt;/p&gt;
&lt;p&gt;There were a few people who gave us some help during the hunt, so I think that
means I need to promote signups more next year.&lt;/p&gt;
&lt;p&gt;Having brought together some people, we needed a team name. A bunch of ideas we
had (some of which followed the Antarctica theme for the puzzle hunt):&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Production Freeze&lt;/li&gt;
&lt;li&gt;By accepting this team name you agree to our EULA&lt;/li&gt;
&lt;li&gt;Fizz puzz&lt;/li&gt;
&lt;li&gt;Ice climbers&lt;/li&gt;
&lt;li&gt;TE-901&lt;/li&gt;
&lt;li&gt;UNTITL~1.GPH&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;But “How about ☃ repeated as many times as the registration form will allow?”
was most liked. Confession time: I didn’t actually do this. Instead, I went for
three rows of snowmen (as displayed on my laptop). So while we didn’t have the
longest team name, we did have the most vertical space in the team list.&lt;/p&gt;
&lt;p&gt;Perhaps next year we’ll figure out how to consume even more vertical space with
our team name?&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="team-coordination"&gt;
&lt;h2&gt;Team coordination&lt;/h2&gt;
&lt;p&gt;We used Slack and Google Docs to coordinate. In the Slack workspace, there was a
&lt;tt class="docutils literal"&gt;#coordination&lt;/tt&gt; channel which had login details, a periodically updated “best
puzzles to work on” post, celebrations of solved puzzles, and occasional calls
for help. Puzzle solving didn’t happen in that channel: instead, we created a
channel per puzzle.&lt;/p&gt;
&lt;p&gt;It’s hard to predict what technology will be best to solve a puzzle, but
generally we always want a document to describing what we’ve tried and with
partial progress (which eventually morphs into a “how to solve the puzzle” doc).
All of these documents got put into a shared Google Drive folder, and each
puzzle channel in Slack had a topic linking to the relevant doc for easy access.&lt;/p&gt;
&lt;p&gt;A few of the puzzles needed more than a doc. We also used:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Photos shared in Slack (for a physical jigsaw-like puzzle)&lt;/li&gt;
&lt;li&gt;Images shared in Slack&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://www.google.com/mymaps"&gt;Google My Maps&lt;/a&gt; in Drive (super-useful for visualising latitude/longitude
pairs and looking for patterns)&lt;/li&gt;
&lt;li&gt;Google Spreadsheets in Drive (although a few times I started to create one,
and then replaced it with a table in a Google Doc)&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://colab.research.google.com"&gt;Google Colaboratory&lt;/a&gt; in Drive (omg sharable Python code with notes!)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I was generally happy with the technology choices, although I need to figure out
how to use Colaboratory in a way that involves less clicking and makes better
use of my vim muscle memory.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="puzzle-solving"&gt;
&lt;h2&gt;Puzzle solving&lt;/h2&gt;
&lt;p&gt;Something I keep saying about puzzle hunts is that it’s important to have lots
of people on the team, even if they aren’t spending gross amounts of time
solving puzzles. What’s really important is that there’s a continuous stream of
“have you tried &amp;lt;thing&amp;gt;?” ideas and “that looks kinda like &amp;lt;other thing&amp;gt;”
observations: otherwise, you get stuck trying to solve a puzzle and then get
frustrated at not having ideas, which isn’t a great mental state to be producing
ideas with.&lt;/p&gt;
&lt;p&gt;I’m very grateful to my team members for contributing their time: I think I
spent less time in that stuck state than I have in previous years, but there’s
definitely still room for improvement there.&lt;/p&gt;
&lt;p&gt;There were also a few times where I think we had trouble because we didn’t quite
know what “rules” the puzzle authors were playing by, so to speak. In one case,
I wrote a bunch of Python code looking for patterns in some data because of a
perceived hint in the puzzle title, which turned out to be a wild goose chase.
There was also at least one hint spent on something where we should have just
been able to get it out by being more rigorous.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="results"&gt;
&lt;h2&gt;Results&lt;/h2&gt;
&lt;p&gt;&lt;a class="reference external" href="https://2019.galacticpuzzlehunt.com/teams?team=%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83"&gt;Our team&lt;/a&gt; solved the first 12 puzzles, which put us in &lt;a class="reference external" href="https://2019.galacticpuzzlehunt.com/teams"&gt;239th&lt;/a&gt; place.&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Out of 717 registered teams: top 33%&lt;/li&gt;
&lt;li&gt;Out of 505 teams who solved at least one puzzle: top 47%&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I’m pretty happy with how we went: I only wish we’d managed to finish our
half-solved metapuzzle in time!&lt;/p&gt;
&lt;p&gt;For next year’s Galactic Puzzle Hunt (or any other ones for that matter), I’ve
been thinking lately about doing some training sessions (where you solve a
puzzle, but there’s zero-cost hints as soon as you get stuck). If this sounds
interesting, hit me up?&lt;/p&gt;
&lt;hr class="docutils" /&gt;
&lt;table class="docutils footnote" frame="void" id="id6" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#id2"&gt;[1]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;Although the &lt;a class="reference external" href="https://2019.galacticpuzzlehunt.com/rules"&gt;rules&lt;/a&gt; also say “You may use any external sources for
help, including other people, as long as they aren’t helping other
teams and aren’t actively participating in the hunt.”&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
</content><category term="puzzles"></category><category term="gph"></category></entry><entry><title>Python C Extension Module Corruption</title><link href="2018-06/python-c-extension-module-corruption.html" rel="alternate"></link><published>2018-06-23T00:00:00+10:00</published><updated>2018-06-23T00:00:00+10:00</updated><author><name>Peter Ward</name></author><id>tag:None,2018-06-23:2018-06/python-c-extension-module-corruption.html</id><summary type="html">&lt;p&gt;If you follow me on Twitter, you might have seen this &lt;a class="reference external" href="https://twitter.com/flowblok/status/1009765246750187520"&gt;recent thread&lt;/a&gt;, in
which I spent a few days metaphorically banging my head against a wall trying to
figure out why my Python program would occasionally segfault (with no apparent
trigger).&lt;/p&gt;
&lt;p&gt;This post is an attempt to write up …&lt;/p&gt;</summary><content type="html">&lt;p&gt;If you follow me on Twitter, you might have seen this &lt;a class="reference external" href="https://twitter.com/flowblok/status/1009765246750187520"&gt;recent thread&lt;/a&gt;, in
which I spent a few days metaphorically banging my head against a wall trying to
figure out why my Python program would occasionally segfault (with no apparent
trigger).&lt;/p&gt;
&lt;p&gt;This post is an attempt to write up the cause (and my workaround) in a clearer
form, and hopefully make it Google-able for the next poor soul to encounter
this (if it's not fixed before then).&lt;/p&gt;
&lt;div class="section" id="dramatis-personae"&gt;
&lt;h2&gt;Dramatis Personæ&lt;/h2&gt;
&lt;p&gt;The program in question here is a Python web application, written in &lt;a class="reference external" href="http://flask.pocoo.org/"&gt;Flask&lt;/a&gt;,
and run using &lt;a class="reference external" href="http://gunicorn.org/"&gt;gunicorn&lt;/a&gt; (using a few sync workers). It also uses
&lt;a class="reference external" href="https://sqlalchemy.org/"&gt;SQLAlchemy&lt;/a&gt; (via &lt;a class="reference external" href="http://flask-sqlalchemy.pocoo.org/"&gt;Flask-SQLAlchemy&lt;/a&gt;) to access a &lt;a class="reference external" href="https://sqlite.org/"&gt;sqlite3&lt;/a&gt; database.
It’s all running on a &lt;a class="reference external" href="https://cloud.google.com/compute/"&gt;Google Compute Engine&lt;/a&gt; VM running &lt;a class="reference external" href="https://debian.org/"&gt;Debian Linux&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Most of those details aren’t relevant, but I mention them just to give a sense
of the scope: each of them was a suspect at some stage or another during my
investigation.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="the-evidence"&gt;
&lt;h2&gt;The Evidence&lt;/h2&gt;
&lt;p&gt;Aside from seeing 5xx responses from my web server, the only information I had
was in syslog:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
kernel: [1234.567890] gunicorn[1234]: segfault at ad0 ip 0000000000000ad0 sp 00007fffc0401448
                                      error 14 in python3.5[55f0889ee000+3ef000]
&lt;/pre&gt;
&lt;p&gt;The gunicorn logs noted that it had started a new worker to replace the
segfaulted one, but nothing more. The syslog entry is really quite odd: the
&lt;tt class="docutils literal"&gt;at ad0&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;ip 0000000000000ad0&lt;/tt&gt; parts indicate that the program tried to
jump to (and hence execute) code at the address 0xad0, which is &lt;em&gt;quite&lt;/em&gt;
suspicious: I don't know offhand what's at that address, but it’s not usually
program code.&lt;/p&gt;
&lt;p&gt;Clearly I needed more information to debug this, so I tried to get a
&lt;a class="reference external" href="https://en.wikipedia.org/wiki/Core_dump"&gt;core dump&lt;/a&gt;. For reasons I’m still not entirely sure of, getting a core dump
didn’t just work out-of-the-box—I believe Linux is meant to write a file named
&lt;tt class="docutils literal"&gt;core&lt;/tt&gt; to the process’s working directory (provided ulimit is set correctly).&lt;/p&gt;
&lt;p&gt;Some quick Googling revealed that &lt;a class="reference external" href="https://www.freedesktop.org/software/systemd/man/systemd-coredump.html"&gt;systemd-coredump&lt;/a&gt; is a thing, so I installed
that, and then my program didn’t segfault for an hour, so I tweeted thinking
that this was a &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Heisenbug"&gt;Heisenbug&lt;/a&gt;. It turned out not to be the case: the segfault
only happened a few times an hour, and I was just unlucky.&lt;/p&gt;
&lt;p&gt;Anyhow, I later came back and used &lt;tt class="docutils literal"&gt;coredumpctl list&lt;/tt&gt; to see when the program
had segfaulted, and &lt;tt class="docutils literal"&gt;coredumpctl gdb&lt;/tt&gt; to launch &lt;a class="reference external" href="https://www.gnu.org/software/gdb/"&gt;gdb&lt;/a&gt; with the core dump
loaded. For all the hate systemd seems to get for reinventing the wheel, I will
say that systemd-coredump seems quite good at its job.&lt;/p&gt;
&lt;pre class="literal-block"&gt;
$ coredumpctl gdb
...
Core was generated by `/usr/bin/python3'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0  0x0000000000000ad0 in ?? ()
(gdb)
&lt;/pre&gt;
&lt;p&gt;Great: there's the 0xad0 address from my syslog entry, so the crash is the same
as the one I’m seeing! This might seem like a minor point, but fervently
verifying the world is as you expect it to be is critically important in
debugging. Let’s look at a backtrace to see what the program was doing when it
segfaulted:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
(gdb) bt
#0  0x0000000000000ad0 in ?? ()
#1  0x0000556dce8116df in PyCFunction_Call () at ../Objects/methodobject.c:109
#2  0x0000556dce7d16e9 in call_function (oparg=&amp;lt;optimized out&amp;gt;, pp_stack=0x7ffd2fc7e1e0) at ../Python/ceval.c:4720
#3  PyEval_EvalFrameEx () at ../Python/ceval.c:3251
#4  0x0000556dce7d193f in fast_function (nk=&amp;lt;optimized out&amp;gt;, na=&amp;lt;optimized out&amp;gt;, n=&amp;lt;optimized out&amp;gt;, pp_stack=0x7ffd2fc7e310, func=&amp;lt;optimized out&amp;gt;) at ../Python/ceval.c:4818
#5  call_function (oparg=&amp;lt;optimized out&amp;gt;, pp_stack=0x7ffd2fc7e310) at ../Python/ceval.c:4745
#6  PyEval_EvalFrameEx () at ../Python/ceval.c:3251
#7  0x0000556dce7d193f in fast_function (nk=&amp;lt;optimized out&amp;gt;, na=&amp;lt;optimized out&amp;gt;, n=&amp;lt;optimized out&amp;gt;, pp_stack=0x7ffd2fc7e440, func=&amp;lt;optimized out&amp;gt;) at ../Python/ceval.c:4818
#8  call_function (oparg=&amp;lt;optimized out&amp;gt;, pp_stack=0x7ffd2fc7e440) at ../Python/ceval.c:4745
#9  PyEval_EvalFrameEx () at ../Python/ceval.c:3251
#10 0x0000556dce7d6286 in _PyEval_EvalCodeWithName () at ../Python/ceval.c:4033
#11 0x0000556dce7d24c9 in fast_function (nk=&amp;lt;optimized out&amp;gt;, na=&amp;lt;optimized out&amp;gt;, n=&amp;lt;optimized out&amp;gt;, pp_stack=0x7ffd2fc7e650, func=&amp;lt;optimized out&amp;gt;) at ../Python/ceval.c:4828
#12 call_function (oparg=&amp;lt;optimized out&amp;gt;, pp_stack=0x7ffd2fc7e650) at ../Python/ceval.c:4745
#13 PyEval_EvalFrameEx () at ../Python/ceval.c:3251
#14 0x0000556dce7d193f in fast_function (nk=&amp;lt;optimized out&amp;gt;, na=&amp;lt;optimized out&amp;gt;, n=&amp;lt;optimized out&amp;gt;, pp_stack=0x7ffd2fc7e780, func=&amp;lt;optimized out&amp;gt;) at ../Python/ceval.c:4818
#15 call_function (oparg=&amp;lt;optimized out&amp;gt;, pp_stack=0x7ffd2fc7e780) at ../Python/ceval.c:4745
#16 PyEval_EvalFrameEx () at ../Python/ceval.c:3251
#17 0x0000556dce7d70df in _PyEval_EvalCodeWithName (qualname=0x0, name=&amp;lt;optimized out&amp;gt;, closure=0x0, kwdefs=0x0, defcount=0, defs=0x0, kwcount=0, kws=&amp;lt;optimized out&amp;gt;, argcount=&amp;lt;optimized out&amp;gt;,
    args=&amp;lt;optimized out&amp;gt;, locals=&amp;lt;optimized out&amp;gt;, globals=&amp;lt;optimized out&amp;gt;, _co=&amp;lt;optimized out&amp;gt;) at ../Python/ceval.c:4033
#18 PyEval_EvalCodeEx () at ../Python/ceval.c:4054
#19 0x0000556dce8135d3 in function_call.lto_priv () at ../Objects/funcobject.c:627
#20 0x0000556dce85a647 in PyObject_Call () at ../Objects/abstract.c:2166
#21 0x0000556dce77a94e in method_call.lto_priv () at ../Objects/classobject.c:330
#22 0x0000556dce85a647 in PyObject_Call () at ../Objects/abstract.c:2166
#23 0x0000556dce81aebf in slot_tp_iter () at ../Objects/typeobject.c:6214
#24 0x0000556dce85a4a3 in PyObject_GetIter () at ../Objects/abstract.c:2755
#25 0x0000556dce84cd56 in listextend.lto_priv.683 (
    b=&amp;lt;BaseQuery(_group_by=False, _params={&amp;lt;_anonymous_label at remote 0x7f26c3adbd48&amp;gt;: 'flowblok.id.au'}, _entities=[&amp;lt;_MapperEntity(entity_zero=&amp;lt;Mapper(self_and_descendants=&amp;lt;WeakSequence(_storage=[&amp;lt;weakref at remote 0x7f26c3a9bf98&amp;gt;]) at remote 0x7f26c3aae898&amp;gt;, inherits=None, version_id_generator=&amp;lt;function at remote 0x7f26c3e837b8&amp;gt;, confirm_deleted_rows=True, _readonly_props=set(), local_table=&amp;lt;Table(name=&amp;lt;quoted_name at remote 0x7f26c3e8c1c8&amp;gt;, description=&amp;lt;...&amp;gt;, _cloned_set={&amp;lt;...&amp;gt;}, constraints={&amp;lt;ForeignKeyConstraint(name='domain_admin_group_constraint', initially=None, dialect_kwargs=&amp;lt;_DialectArgView(obj=&amp;lt;...&amp;gt;) at remote 0x7f26c82b5160&amp;gt;, parent=&amp;lt;...&amp;gt;, _referred_table=&amp;lt;Table(name=&amp;lt;quoted_name at remote 0x7f26c3e8cbc8&amp;gt;, description=&amp;lt;...&amp;gt;, _cloned_set={&amp;lt;...&amp;gt;}, constraints={&amp;lt;ForeignKeyConstraint(name=None, initially=None, parent=&amp;lt;...&amp;gt;, _referred_table=&amp;lt;...&amp;gt;, columns=&amp;lt;ColumnCollection at remote 0x7f26c3e22cc0&amp;gt;, ondelete=None, elements=[&amp;lt;ForeignKey(name=None, column=&amp;lt;Column(_cloned_set={&amp;lt;...&amp;gt;}, constraints=set(), forei...(truncated),
    self=0x7f26c3433608) at ../Objects/listobject.c:831
...
(truncated for your sanity)
&lt;/pre&gt;
&lt;p&gt;This isn’t terribly useful: it’s full of Python interpreter stuff. Knowing the
title of the blog, you &lt;em&gt;might&lt;/em&gt; notice that the calling function is
&lt;tt class="docutils literal"&gt;PyCFunction_Call&lt;/tt&gt;, which you might start to get ideas from: but that could be
from anything. Fortunately, gdb has &lt;em&gt;amazing&lt;/em&gt; Python integration, so I can do
this instead:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
(gdb) py-bt
Traceback (most recent call first):
  &amp;lt;error reading variable: Cannot access memory at address 0xe90&amp;gt;
  File &amp;quot;/…/lib/python3.5/site-packages/sqlalchemy/engine/base.py&amp;quot;, line 1024, in _execute_clauseelement
    distilled_params = _distill_params(multiparams, params)
  File &amp;quot;/…/lib/python3.5/site-packages/sqlalchemy/sql/elements.py&amp;quot;, line 269, in _execute_on_connection
    return connection._execute_clauseelement(self, multiparams, params)
  File &amp;quot;/…/lib/python3.5/site-packages/sqlalchemy/engine/base.py&amp;quot;, line 948, in execute
    return meth(self, multiparams, params)
  File &amp;quot;/…/lib/python3.5/site-packages/sqlalchemy/orm/query.py&amp;quot;, line 2958, in _execute_and_instances
    result = conn.execute(querycontext.statement, self._params)
  File &amp;quot;/…/lib/python3.5/site-packages/sqlalchemy/orm/query.py&amp;quot;, line 2935, in __iter__
    return self._execute_and_instances(context)
  File &amp;quot;/…/lib/python3.5/site-packages/sqlalchemy/orm/query.py&amp;quot;, line 2864, in one_or_none
    ret = list(self)
  File &amp;quot;/…/lib/python3.5/site-packages/sqlalchemy/orm/query.py&amp;quot;, line 2894, in one
    ret = self.one_or_none()
  File &amp;quot;/…/lib/python3.5/site-packages/sqlalchemy/orm/loading.py&amp;quot;, line 250, in load_on_pk_identity
    return q.one()
  File &amp;quot;/…/lib/python3.5/site-packages/sqlalchemy/orm/query.py&amp;quot;, line 962, in _get_impl
    return db_load_fn(self, primary_key_identity)
  File &amp;quot;/…/lib/python3.5/site-packages/sqlalchemy/orm/query.py&amp;quot;, line 887, in get
    ident, loading.load_on_pk_identity)
  File &amp;quot;/…/app.py&amp;quot;, line 47, in setup_context
...
(truncated for your sanity)
&lt;/pre&gt;
&lt;p&gt;The last frame of that backtrace is my application code, trying to run a
SQLAlchemy query. The rest of the backtrace is SQLAlchemy trying to execute it.
But the first frame is most interesting: SQLAlchemy is calling a function
&lt;tt class="docutils literal"&gt;_distill_params&lt;/tt&gt;, but we don't have the source for that call.&lt;/p&gt;
&lt;p&gt;If you look at the source for &lt;a class="reference external" href="https://bitbucket.org/zzzeek/sqlalchemy/src/rel_1_2_8/lib/sqlalchemy/engine/base.py"&gt;sqlalchemy/engine/base.py&lt;/a&gt;, you’ll see that
&lt;tt class="docutils literal"&gt;_distill_params&lt;/tt&gt; gets imported from &lt;a class="reference external" href="https://bitbucket.org/zzzeek/sqlalchemy/src/rel_1_2_8/lib/sqlalchemy/engine/util.py"&gt;sqlalchemy/engine/util.py&lt;/a&gt;, which has
this:&lt;/p&gt;
&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;71
72
73
74&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;sqlalchemy.cutils&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;_distill_params&lt;/span&gt;
&lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="ne"&gt;ImportError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nb"&gt;globals&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;py_fallback&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;p&gt;That is, &lt;tt class="docutils literal"&gt;_distill_params&lt;/tt&gt; comes from a C extension module, unless it’s not
importable, in which case there’s a pure Python version as a fallback.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="c-extension-modules"&gt;
&lt;h2&gt;C Extension Modules&lt;/h2&gt;
&lt;p&gt;At this point, I’ve figured out the first part of what’s gone wrong: something
is causing Python to think that the address of the _distill_params function is
0xad0, instead of whatever it’s meant to actually be.&lt;/p&gt;
&lt;p&gt;For the next part, we need to delve a little into how C extension modules work.
Here’s part of the source for &lt;a class="reference external" href="https://bitbucket.org/zzzeek/sqlalchemy/src/rel_1_2_8/lib/sqlalchemy/cextension/utils.c"&gt;sqlalchemy/cextension/utils.c&lt;/a&gt;, which provides
the &lt;tt class="docutils literal"&gt;sqlalchemy.cutils&lt;/tt&gt; module imported above.&lt;/p&gt;
&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;178
179
180
181
182&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;PyMethodDef&lt;/span&gt; &lt;span class="n"&gt;module_methods&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;_distill_params&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;distill_params&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;METH_VARARGS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="s"&gt;&amp;quot;Distill an execute() parameter structure.&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;        &lt;span class="cm"&gt;/* Sentinel */&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;p&gt;This C file gets compiled into a &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Library_(computing)#Shared_libraries"&gt;shared library&lt;/a&gt;: on my system it’s a file
named &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;cutils.cpython-35m-x86_64-linux-gnu.so&lt;/span&gt;&lt;/tt&gt;. The CPython interpreter loads
extension modules in &lt;a class="reference external" href="https://hg.python.org/cpython/file/3.5/Python/importdl.c"&gt;Python/importdl.c&lt;/a&gt;, which uses the
&lt;tt class="docutils literal"&gt;_PyImport_FindSharedFuncptr&lt;/tt&gt; function in &lt;a class="reference external" href="https://hg.python.org/cpython/file/3.5/Python/dynload_shlib.c"&gt;Python/dynload_shlib.c&lt;/a&gt;, which
uses &lt;a class="reference external" href="http://man7.org/linux/man-pages/man3/dlopen.3.html"&gt;dlopen&lt;/a&gt; to load that &lt;tt class="docutils literal"&gt;.so&lt;/tt&gt; file into memory and get a function pointer.&lt;/p&gt;
&lt;p&gt;Most of those details weren’t important: the key part is that we have some C
code, being loaded into the process’s memory at &lt;em&gt;runtime&lt;/em&gt;. So, let’s attach GDB
to a running process, and have at look at that &lt;tt class="docutils literal"&gt;module_methods&lt;/tt&gt; array:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
$ gdb -p 1234
...
(gdb) p 'sqlalchemy/cextension/utils.c'::module_methods
$1 = {{ml_name = 0x7f66abdfde90 &amp;quot;_distill_params&amp;quot;, ml_meth = 0x7f66abdfdad0 &amp;lt;distill_params&amp;gt;,
       ml_flags = 1, ml_doc = 0x7f66abdfdef8 &amp;quot;Distill an execute() parameter structure.&amp;quot;},
      {ml_name = 0x0, ml_meth = 0x0, ml_flags = 0, ml_doc = 0x0}}
&lt;/pre&gt;
&lt;p&gt;That’s more or less what I’d expect to see: &lt;tt class="docutils literal"&gt;ml_name&lt;/tt&gt; has the Python name of
the function, and &lt;tt class="docutils literal"&gt;ml_meth&lt;/tt&gt; is a pointer to the &lt;tt class="docutils literal"&gt;distill_params&lt;/tt&gt; C function.
What happens if we try the same thing in our core dump?&lt;/p&gt;
&lt;pre class="literal-block"&gt;
$ coredumpctl gdb
...
(gdb) p 'sqlalchemy/cextension/utils.c'::module_methods
$1 = {{ml_name = 0xe90 &amp;lt;error: Cannot access memory at address 0xe90&amp;gt;, ml_meth = 0xad0,
       ml_flags = 1, ml_doc = 0xef8 &amp;lt;error: Cannot access memory at address 0xef8&amp;gt;},
      {ml_name = 0x0, ml_meth = 0x0, ml_flags = 0, ml_doc = 0x0}}
&lt;/pre&gt;
&lt;p&gt;Well. That’s not right.&lt;/p&gt;
&lt;p&gt;But wait, those hex numbers look kinda similar to the correct numbers…&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;{:48b}&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mh"&gt;0x7f66abdfde90&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="go"&gt; 11111110110011010101011110111111101111010010000&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;{:48b}&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mh"&gt;0xe90&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="go"&gt;                                    111010010000&lt;/span&gt;

&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;{:48b}&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mh"&gt;0x7f66abdfdad0&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="go"&gt; 11111110110011010101011110111111101101011010000&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;{:48b}&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mh"&gt;0xad0&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="go"&gt;                                    101011010000&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Huh? Sometime between being a perfectly happy process chugging along, and it
segfaulting, the &lt;tt class="docutils literal"&gt;module_methods&lt;/tt&gt; struct in a C extension module gets all its
numbers truncated to 12 &lt;a class="footnote-reference" href="#id5" id="id3"&gt;[*]&lt;/a&gt; bits‽&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="time-and-space"&gt;
&lt;h2&gt;Time and Space&lt;/h2&gt;
&lt;p&gt;Thinking that the problem was something &lt;em&gt;writing&lt;/em&gt; to the memory to corrupt it, I
spent quite some time looking through &lt;a class="reference external" href="https://strace.io/"&gt;strace&lt;/a&gt; and &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Ltrace"&gt;ltrace&lt;/a&gt; outputs to try and
find something touching the relevant addresses in memory, but to no avail.&lt;/p&gt;
&lt;p&gt;My breakthrough came when I compared &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;/proc/$PID/smaps&lt;/span&gt;&lt;/tt&gt; from a running
process and a almost-segfaulted process. To capture the latter, I attached GDB,
and then typed in &lt;tt class="docutils literal"&gt;shell cat /proc/1234/smaps &amp;gt; 1234.smaps&lt;/tt&gt; and hit enter, so
it would get run when the program had segfaulted (but before Linux forgot the
process state). I also attempted using a breakpoint, but in hindsight I don’t
think that worked. Regardless, I found the section of memory in which
the &lt;tt class="docutils literal"&gt;module_methods&lt;/tt&gt; struct resided, and found this difference:&lt;/p&gt;
&lt;p&gt;Before:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
7f26c44e2000-7f26c44e3000 rw-p 00002000 08:10 666070 /…/cutils.cpython-35m-x86_64-linux-gnu.so
Size:                  4 kB
Rss:                   4 kB
Pss:                   4 kB
Shared_Clean:          0 kB
Shared_Dirty:          0 kB
Private_Clean:         0 kB
Private_Dirty:         4 kB
Referenced:            4 kB
Anonymous:             4 kB
AnonHugePages:         0 kB
ShmemPmdMapped:        0 kB
Shared_Hugetlb:        0 kB
Private_Hugetlb:       0 kB
Swap:                  0 kB
SwapPss:               0 kB
KernelPageSize:        4 kB
MMUPageSize:           4 kB
Locked:                0 kB
VmFlags: rd wr mr mw me ac sd
&lt;/pre&gt;
&lt;p&gt;After:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
7f26c44e2000-7f26c44e3000 rw-p 00002000 08:10 666070 /…/cutils.cpython-35m-x86_64-linux-gnu.so
Size:                  4 kB
Rss:                   4 kB
Pss:                   4 kB
Shared_Clean:          0 kB
Shared_Dirty:          0 kB
Private_Clean:         4 kB
Private_Dirty:         0 kB
Referenced:            4 kB
Anonymous:             0 kB
AnonHugePages:         0 kB
ShmemPmdMapped:        0 kB
Shared_Hugetlb:        0 kB
Private_Hugetlb:       0 kB
Swap:                  0 kB
SwapPss:               0 kB
KernelPageSize:        4 kB
MMUPageSize:           4 kB
Locked:                0 kB
&lt;/pre&gt;
&lt;p&gt;The key difference here is that this section of memory went from having 4 kB of
&lt;tt class="docutils literal"&gt;Private_Dirty&lt;/tt&gt; data to having 4 kB of &lt;tt class="docutils literal"&gt;Private_Clean&lt;/tt&gt; data.&lt;/p&gt;
&lt;p&gt;Combined with the knowledge that the numbers inside that section of memory
seemed to be being “truncated”, I realised what was happening:
&lt;em&gt;this section of memory was being reloaded from the shared library file&lt;/em&gt;.
I’m glossing over some details here, but on disk the shared library has various
pointers to code and data within that shared library, but when it gets loaded
into a process’s memory, it’ll be loaded at some offset, and all the pointers
within the shared library need to be adjusted in order to work.&lt;/p&gt;
&lt;p&gt;So on disk, inside the &lt;tt class="docutils literal"&gt;module_methods&lt;/tt&gt; struct, the &lt;tt class="docutils literal"&gt;ml_name&lt;/tt&gt; pointer is
actually 0xe90, but when it initially gets loaded into memory, it gets adjusted
to 0x7f66abdfde90 so it’s pointing at the location where the string
&lt;tt class="docutils literal"&gt;&amp;quot;_distill_params&amp;quot;&lt;/tt&gt; got loaded into memory.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="the-piping-gun"&gt;
&lt;h2&gt;The Piping Gun&lt;/h2&gt;
&lt;p&gt;Knowing that I was looking for something which was causing the shared library
file to be reloaded from disk, I remembered that I had a bug in my automation
which was unnecessarily reinstalling all Python libraries using &lt;a class="reference external" href="https://pip.pypa.io/"&gt;pip&lt;/a&gt;.
I didn’t think it was a huge problem: the downloads are cached locally, and
reinstalling a package with the same version should just be a no-op, right?&lt;/p&gt;
&lt;p&gt;Turns out not so much. When pip installs a wheel, it &lt;a class="reference external" href="https://github.com/pypa/pip/blob/10.0.1/src/pip/_internal/wheel.py#L292"&gt;replaces all the files
using shutil.copyfile&lt;/a&gt;. This opens the file for writing, truncates it, and
then copies the content in.&lt;/p&gt;
&lt;p&gt;Because this is changing the &lt;em&gt;same file&lt;/em&gt; which is also memory mapped into a
running process, Linux notices that section of data has been re-written (even
with exactly the same bytes), and invalidates all unwritten changes in active
processes (this is what makes the memory mapping go from &lt;tt class="docutils literal"&gt;Private_Dirty&lt;/tt&gt; to
&lt;tt class="docutils literal"&gt;Private_Clean&lt;/tt&gt;), thus undoing the offset adjustments the process made when
loading the shared library.&lt;/p&gt;
&lt;p&gt;Honestly, I’m not sure where the bug is here. Probably after loading a shared
library, Python should unassociate the data section from the file it was loaded
from: though I’m not sure if the C functions to do that even exist.
I’d also say pip should do atomic file replacements to avoid the problem, but
this is more of a bandaid than an actual fix.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="my-workaround"&gt;
&lt;h2&gt;My Workaround&lt;/h2&gt;
&lt;p&gt;After figuring out what was happening, I had a realisation that this problem
could be mitigated in the application, with a pure Python solution.&lt;/p&gt;
&lt;p&gt;Instead of trying to unassociate the memory mapping, I can atomically replace
the file it loaded with a copy of itself, thus making the memory mapping in the
process point at data which no longer exists in the filesystem.&lt;/p&gt;
&lt;p&gt;So here is a Python module which you can import, which will periodically find C
extension modules loaded into the Python process and perform shenanigans on them
to make them resilient against this failure mode. This potentially costs memory,
but I doubt it’ll ever be an amount you care about.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# Copyright 2018 Google LLC&lt;/span&gt;
&lt;span class="c1"&gt;#&lt;/span&gt;
&lt;span class="c1"&gt;# Licensed under the Apache License, Version 2.0 (the &amp;quot;License&amp;quot;);&lt;/span&gt;
&lt;span class="c1"&gt;# you may not use this file except in compliance with the License.&lt;/span&gt;
&lt;span class="c1"&gt;# You may obtain a copy of the License at&lt;/span&gt;
&lt;span class="c1"&gt;#&lt;/span&gt;
&lt;span class="c1"&gt;#     https://www.apache.org/licenses/LICENSE-2.0&lt;/span&gt;
&lt;span class="c1"&gt;#&lt;/span&gt;
&lt;span class="c1"&gt;# Unless required by applicable law or agreed to in writing, software&lt;/span&gt;
&lt;span class="c1"&gt;# distributed under the License is distributed on an &amp;quot;AS IS&amp;quot; BASIS,&lt;/span&gt;
&lt;span class="c1"&gt;# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.&lt;/span&gt;
&lt;span class="c1"&gt;# See the License for the specific language governing permissions and&lt;/span&gt;
&lt;span class="c1"&gt;# limitations under the License.&lt;/span&gt;
&lt;span class="sd"&gt;&amp;#39;&amp;#39;&amp;#39;&lt;/span&gt;
&lt;span class="sd"&gt;Module sopin implements &amp;quot;pinning&amp;quot; of shared libraries.&lt;/span&gt;

&lt;span class="sd"&gt;This is a workaround for the problem where rewriting a Python C extension module&lt;/span&gt;
&lt;span class="sd"&gt;on disk corrupts the state of any Python process which has previously imported&lt;/span&gt;
&lt;span class="sd"&gt;it: for more details, see&lt;/span&gt;
&lt;span class="sd"&gt;https://blog.flowblok.id.au/2018-06/python-c-extension-module-corruption.html&lt;/span&gt;

&lt;span class="sd"&gt;Usage:&lt;/span&gt;
&lt;span class="sd"&gt;    import sopin&lt;/span&gt;
&lt;span class="sd"&gt;    sopin.start()&lt;/span&gt;
&lt;span class="sd"&gt;&amp;#39;&amp;#39;&amp;#39;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;shutil&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;sys&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;tempfile&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;threading&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;warnings&lt;/span&gt;

&lt;span class="n"&gt;SUFFIXES&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;.so&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,)&lt;/span&gt;

&lt;span class="c1"&gt;# Modules previously pinned.&lt;/span&gt;
&lt;span class="n"&gt;_pinned&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="c1"&gt;# Modules which we couldn&amp;#39;t pin (so shouldn&amp;#39;t reattempt).&lt;/span&gt;
&lt;span class="n"&gt;_blacklisted&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;pin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;filename&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;_pinned&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;filename&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;_blacklisted&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# Atomically create a unique tempfile.&lt;/span&gt;
        &lt;span class="c1"&gt;# Don&amp;#39;t delete, because we&amp;#39;ll move it over the old file.&lt;/span&gt;
        &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;tempfile&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NamedTemporaryFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;delete&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="c1"&gt;# Copy the content+permissions of the old file into our new file.&lt;/span&gt;
            &lt;span class="n"&gt;shutil&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;copy2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="c1"&gt;# Move the new file over the old file.&lt;/span&gt;
            &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rename&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="ne"&gt;IOError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;warnings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;warn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;could not pin &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;_blacklisted&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;_pinned&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;pin_all&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;module&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;modules&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;filename&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;module&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="vm"&gt;__file__&lt;/span&gt;
        &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="ne"&gt;AttributeError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;continue&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;endswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SUFFIXES&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;pin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;period_seconds&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;threading&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Timer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;period_seconds&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pin_all&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;table class="docutils footnote" frame="void" id="id5" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#id3"&gt;[*]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;Twitter followers may note this is not the 13 or 14 that I tweeted: I was
actually debugging &lt;tt class="docutils literal"&gt;processors.c&lt;/tt&gt; at that point, so these details are
slightly different.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
</content><category term="c"></category><category term="debugging"></category><category term="linux"></category><category term="python"></category><category term="software"></category></entry><entry><title>Synchronized Music on micro:bits</title><link href="2018-02/synchronized-music-on-microbits.html" rel="alternate"></link><published>2018-02-24T22:02:00+11:00</published><updated>2018-02-24T22:02:00+11:00</updated><author><name>Peter Ward</name></author><id>tag:None,2018-02-24:2018-02/synchronized-music-on-microbits.html</id><summary type="html">&lt;p&gt;It’s been a while since I last posted. So, uh, sorry about that.
I’m going to make it up to y’all with this post. It’s in two parts: the video
demonstrates the project in action, and the rest of this blog post details all
the technical …&lt;/p&gt;</summary><content type="html">&lt;p&gt;It’s been a while since I last posted. So, uh, sorry about that.
I’m going to make it up to y’all with this post. It’s in two parts: the video
demonstrates the project in action, and the rest of this blog post details all
the technical details. I’d recommend starting with the video to see the cool
demo, and if you make it to the end, read the rest of the blog post.&lt;/p&gt;
&lt;p class="ratio-16-9"&gt;&lt;iframe src="https://www.youtube.com/embed/KhXfi021K3c"&gt;&lt;/iframe&gt;&lt;/p&gt;&lt;div class="section" id="introduction"&gt;
&lt;h2&gt;Introduction&lt;/h2&gt;
&lt;p&gt;For many years now, I’ve tutored at the &lt;a class="reference external" href="http://ncss.edu.au/summer-school"&gt;National Computer Science School&lt;/a&gt;,
which teaches computer science and programming to Australian high school kids.
The school is divided into two parts: a web stream, which builds a website using
Python, and an embedded stream, which (nowadays) uses &lt;a class="reference external" href="https://micropython.org/"&gt;MicroPython&lt;/a&gt; on the
&lt;a class="reference external" href="https://microbit.org/"&gt;micro:bit&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Last year I tutored the web stream, and so it was only during the closing
ceremony while watching videos demonstrating all the embedded students’ projects
that I started wondering about the potential of the micro:bit, and whether I
could build a mesh network so they could play music synchronized across a large
area.
So when work started getting quieter over the Christmas / New Year break, and
this year’s NCSS started approaching, I decided to buy a couple of micro:bits
and start hacking.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="breaking-it-down"&gt;
&lt;h2&gt;Breaking it down&lt;/h2&gt;
&lt;p&gt;I broke down the project into a number of distinct components:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;strong&gt;network protocol&lt;/strong&gt;: for sending structured data as micro:bit radio packets.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;time sync&lt;/strong&gt;: getting all micro:bits on the network to share a common time.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;triggers&lt;/strong&gt;: getting all micro:bits to do an action at a given time.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;music&lt;/strong&gt;: making the micro:bits play notes on a piezo buzzer.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Writing micro:bits all the time is too hard, so I’m going to refer to them from
here on as &lt;em&gt;nodes&lt;/em&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="time-sync"&gt;
&lt;h2&gt;Time Sync&lt;/h2&gt;
&lt;p&gt;Time is hard. It’s a little easier in this application, because we don’t care
what the &lt;em&gt;actual&lt;/em&gt; wall-clock time is, just that all the nodes have the same
time.&lt;/p&gt;
&lt;p&gt;The first step in figuring out how to synchronize the nodes was to head off to
Wikipedia and do some reading on the way I already knew computers synchronized
time, &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Network_Time_Protocol"&gt;Network Time Protocol (NTP)&lt;/a&gt;. After very little reading, it seemed
fairly obvious that NTP was far too complicated for this application: I didn’t
need to deal with leap seconds, and polling multiple servers and doing filtering
on the answers seemed like too much effort.&lt;/p&gt;
&lt;p&gt;Fortunately it took little effort to find the &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Precision_Time_Protocol"&gt;Precision Time Protocol (PTP)&lt;/a&gt;.
PTP is a really nice simple protocol, though the Wikipedia page does have much
more detail than is really needed. It’s designed to work on multiple network
segments (hey, that sounds &lt;em&gt;kinda&lt;/em&gt; like the network topology we have), and on
each segment, there’s a master which all the other nodes synchronize to.
Each node periodically pings (called &lt;tt class="docutils literal"&gt;Delay_Req&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;Delay_Resp&lt;/tt&gt;, but I
like ping better) the master to approximate their Round Trip Time (RTT), and the
master periodically sends out a &lt;tt class="docutils literal"&gt;Sync&lt;/tt&gt; message with its current time.
Add in some maths, and hey presto, you have synchronized time.&lt;/p&gt;
&lt;p&gt;In order to adapt the algorithm for a mesh network, there were a few changes I
needed to make. PTP has a &lt;em&gt;best master clock algorithm&lt;/em&gt; for selecting the
master, but since my nodes might drop in and out of network connectivity all the
time, my system needed to continuously adapt which nodes were sending timestamps
to synchronize to, based on which nodes could talk to them. I decided to term
those nodes &lt;em&gt;thought leaders&lt;/em&gt; (partially because I really disliked the &amp;quot;master&amp;quot;
terminology, partially because I like being stupid 😛).&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="network-protocol"&gt;
&lt;h2&gt;Network Protocol&lt;/h2&gt;
&lt;p&gt;All nodes in my network are assigned an &lt;em&gt;id&lt;/em&gt; and &lt;em&gt;level&lt;/em&gt;. Given the scale of my
network, both of these are unsigned bytes (i.e. 0–255). The id is chosen
randomly at startup. Astute readers will note this falls victim to the &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Birthday_problem"&gt;Birthday
Problem&lt;/a&gt;, so after 20 nodes there’s a 50% chance two nodes will share an id.
This turns out not to actually be a problem in practice: the presence of
duplicate ids on the network is only an issue if another node can see both of
them (&lt;em&gt;cough&lt;/em&gt; and the nodes are sufficiently unreliable that they restart
occasionally anyway &lt;em&gt;cough&lt;/em&gt;).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Levels:&lt;/strong&gt;
One node is designated as the &amp;quot;root&amp;quot; (by pressing buttons on the micro:bit),
and is assigned level 0. All other nodes start with level 31 (chosen because it
can be easily displayed in binary on the micro:bit LEDs).
The level is an estimate of how close the node is to the root, and nodes will
only synchronize with nodes closer to the root than it.
As nodes do so, they decrease their own level (but never more than one more than
the level of the node it’s synchronizing with).
If the node hasn’t managed to synchronize for a few seconds, its level increases
by one, ensuring that nodes always choose good nodes to synchronize with.&lt;/p&gt;
&lt;p&gt;Here’s an example network:&lt;/p&gt;
&lt;object data="static/images/tlt-levels.dot.svg" type="image/svg+xml"&gt;
Diagram showing several nodes with levels, with arrows indicating
which nodes synchronize from each other.&lt;/object&gt;
&lt;p&gt;The level 1 and 4 nodes can both synchronize from the level 0 node (since it’s
less than 1 and 4). The level 4 doesn’t synchronize from the level 2 node in
this example, just because it can’t see it over the radio network (but it would
if it could). As the level 4 node becomes more in sync, it will reduce to a
level 3 node, then a level 2 node, and finally a level 1 node—at which point it
would stop synchronizing from the other level 1 node.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Pings:&lt;/strong&gt;
All nodes periodically send &lt;tt class="docutils literal"&gt;PING_REQUEST&lt;/tt&gt; messages on the network, which
contain:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;req_node&lt;/tt&gt;: the id of the node requesting the ping&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;req_level&lt;/tt&gt;: the node’s level&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;ping_id&lt;/tt&gt;: a unique (16 bit) id for this ping request&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;votes&lt;/tt&gt;: a list of node ids this node would like to sync from.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The &lt;tt class="docutils literal"&gt;votes&lt;/tt&gt; list is just the ids of nodes which the node has seen &lt;tt class="docutils literal"&gt;PING_REQUEST&lt;/tt&gt;
messages from, where &lt;tt class="docutils literal"&gt;req_level&lt;/tt&gt; was less than its own: this may be out-of-date,
but if so, the node can just ignore the &lt;tt class="docutils literal"&gt;SYNC&lt;/tt&gt; message later (and it won't send
that node id next time).&lt;/p&gt;
&lt;p&gt;Any node which sees a &lt;tt class="docutils literal"&gt;PING_REQUEST&lt;/tt&gt; message from another node responds back
with a &lt;tt class="docutils literal"&gt;PING_RESPONSE&lt;/tt&gt; message, which contains:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;req_node&lt;/tt&gt;: copied from the &lt;tt class="docutils literal"&gt;PING_REQUEST&lt;/tt&gt;&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;resp_node&lt;/tt&gt;: the id of the node responding to the ping&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;resp_level&lt;/tt&gt;: the node’s level&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;ping_id&lt;/tt&gt;: copied from the &lt;tt class="docutils literal"&gt;PING_REQUEST&lt;/tt&gt;&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;req_end_timestamp&lt;/tt&gt;: the time at which the &lt;tt class="docutils literal"&gt;PING_REQUEST&lt;/tt&gt; was received,
according to the responding node’s clock&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Syncs:&lt;/strong&gt;
Periodically, nodes use the &lt;tt class="docutils literal"&gt;votes&lt;/tt&gt; it has seen from other nodes to see if
it’s the best node to send a &lt;tt class="docutils literal"&gt;SYNC&lt;/tt&gt; message. If it is, it does, which
contains:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;node&lt;/tt&gt;: the node sending the &lt;tt class="docutils literal"&gt;SYNC&lt;/tt&gt;&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;level&lt;/tt&gt;: the node’s level&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;timestamp&lt;/tt&gt;: the node’s timestamp at time of sending&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;triggers&lt;/tt&gt;: a list of trigger ids and timestamps (sent as deltas relative to
&lt;tt class="docutils literal"&gt;timestamp&lt;/tt&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;When a node receives a &lt;tt class="docutils literal"&gt;PING&lt;/tt&gt; from a node with a lower level, it uses the same
formula from PTP to adjust its own clock. Assuming:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;span class="math"&gt;\(T_1\)&lt;/span&gt; is the time the node sent a &lt;tt class="docutils literal"&gt;PING_REQUEST&lt;/tt&gt; to the node sending a
&lt;tt class="docutils literal"&gt;SYNC&lt;/tt&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="math"&gt;\(T'_1\)&lt;/span&gt; is the &lt;tt class="docutils literal"&gt;req_end_timestamp&lt;/tt&gt; from the corresponding
&lt;tt class="docutils literal"&gt;PING_RESPONSE&lt;/tt&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="math"&gt;\(T_2\)&lt;/span&gt; is the &lt;tt class="docutils literal"&gt;timestamp&lt;/tt&gt; in the &lt;tt class="docutils literal"&gt;SYNC&lt;/tt&gt; message&lt;/li&gt;
&lt;li&gt;&lt;span class="math"&gt;\(T'_2\)&lt;/span&gt; is the time the &lt;tt class="docutils literal"&gt;SYNC&lt;/tt&gt; message was received&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Then the offset &lt;span class="math"&gt;\(õ\)&lt;/span&gt; between the two nodes’ clocks is:&lt;/p&gt;
&lt;div class="math"&gt;
\begin{equation*}
õ = \frac{T'_1 - T_1 - T'_2 + T_2}{2}
\end{equation*}
&lt;/div&gt;
&lt;p&gt;In practice, I use &lt;a class="reference external" href="//en.wikipedia.org/wiki/Exponential_smoothing"&gt;exponential smoothing&lt;/a&gt; on this process, which reduces the
impact of outliers.
Earlier I mentioned that nodes reduce their level as they become &amp;quot;more in sync&amp;quot;:
nodes actually reduce their level every time they do an offset adjustment where
the adjustment is small enough (&lt;span class="math"&gt;\(|õ| &amp;lt; \varepsilon \)&lt;/span&gt;, where &lt;span class="math"&gt;\(\varepsilon  =\)&lt;/span&gt; 10ms).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Side note:&lt;/strong&gt; Originally, I intended to use MicroPython on the micro:bit to
implement this project. However, after just trying to implement something to
decode the network protocol messages into in-memory data structures, I ran out
of memory using MicroPython. I briefly toyed with the idea of having some code
to &lt;em&gt;generate&lt;/em&gt; the MicroPython code (so I could write sensible high-level code,
and have it spit out stupid code), but that was far too much effort, so I
switched to using the &lt;a class="reference external" href="https://lancaster-university.github.io/microbit-docs/"&gt;micro:bit C++ API&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="simulation"&gt;
&lt;h2&gt;Simulation&lt;/h2&gt;
&lt;p&gt;There’s a lot of detail in the previous section, without much explanation of why
I designed it that way. This section isn’t going to help much, but it will
explain the process a little better.
When I started, I realised that attempting to develop the time synchronization
protocol on the actual micro:bits would be very slow and tedious, so I started
by writing a Python simulation.&lt;/p&gt;
&lt;p&gt;The simulation let me quickly iterate on the design of the protocol, testing
various number of nodes with different network connection topologies, and meant
that when the network didn’t converge to a common time, I could debug the
algorithm by adding things into my Python code, rather than messing around with
micro:bit serial cables.&lt;/p&gt;
&lt;p&gt;Unfortunately, I didn’t save all the diagrams along the way, but here’s the last
one I generated, which shows 13 nodes all converging at different times to the
same time (within an accuracy of 20ms).&lt;/p&gt;
&lt;object data="static/images/tlt-simulation.svg" type="image/svg+xml"&gt;
A diagram showing latencies of various nodes, and lots of coloured
circles. It’s not entirely clear what it represents.&lt;/object&gt;
&lt;p&gt;The 0.1ms line in the diagram is a lie: it should be 0, but that doesn’t work so
well with the logarithmic y-axis.
The coloured dots on the lines indicate the &lt;em&gt;level&lt;/em&gt; of the node at that point in
time. Well. That’s what my notes claim, at least.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="triggers"&gt;
&lt;h2&gt;Triggers&lt;/h2&gt;
&lt;p&gt;So far, we’ve managed to get a mesh network of nodes synchronized to the same
time, but we haven’t managed to get them to do anything &lt;em&gt;useful&lt;/em&gt; yet.
This is where &lt;em&gt;triggers&lt;/em&gt; come in.&lt;/p&gt;
&lt;p&gt;A trigger is just an event with an id (saying what you’d like to happen), and a
timestamp in the future (when you’d like it to happen).
Every &lt;tt class="docutils literal"&gt;SYNC&lt;/tt&gt; message contains a list of triggers: the root node can schedule
triggers, and every node in the network propagates on that list (while the
triggers’ timestamps are still in the future) to all the other nodes.&lt;/p&gt;
&lt;p&gt;In order to get triggers into the network, the root node listens on the serial
port for a six-character hex number: the first two characters are a trigger id,
and the remaining four are in how many milliseconds from now to schedule the
trigger (for example, &lt;tt class="docutils literal"&gt;2a0fa0&lt;/tt&gt; schedules trigger #42 to happen in 4 seconds).&lt;/p&gt;
&lt;p&gt;Just before a trigger is scheduled to happen, the node checks if it’s reasonably
in sync (the last adjustment &lt;span class="math"&gt;\(õ &amp;lt; \varepsilon \)&lt;/span&gt;): if not, it skips the trigger (on the
assumption that not playing some music is better than playing out of time).&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="music"&gt;
&lt;h2&gt;Music&lt;/h2&gt;
&lt;p&gt;When a trigger fires, I want to play a &lt;em&gt;segment&lt;/em&gt; (roughly five seconds long)
of music. This means that if the node gets out of sync, it’ll only be silent for
five seconds. Also, if the time synchronization is off by a noticable amount,
it’ll only sound weird for five seconds.&lt;/p&gt;
&lt;p&gt;Since I want the micro:bits to play music, I need some way to input music into
the program. I started with &lt;a class="reference external" href="http://lilypond.org/"&gt;Lilypond&lt;/a&gt;, an excellent music engraving program
that I've used before (it’s kinda like LaTeX for music). As an example for this
section, I’ve written Beethoven’s &lt;em&gt;Ode to Joy&lt;/em&gt; in Lilypond:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;\version&lt;/span&gt; &amp;quot;2.18.2&amp;quot;

&lt;span class="k"&gt;\include&lt;/span&gt; &amp;quot;articulate.ly&amp;quot;

music = &lt;span class="k"&gt;\new&lt;/span&gt; Staff &lt;span class="k"&gt;\relative&lt;/span&gt; c&amp;#39;&amp;#39; &lt;span class="nb"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;\tempo&lt;/span&gt; 4=160
  &lt;span class="k"&gt;\time&lt;/span&gt; 4/4
  &lt;span class="k"&gt;\key&lt;/span&gt; c &lt;span class="k"&gt;\major&lt;/span&gt;
  e4 e f g |
  g f e d |
  c c d e |
  e4. d8 d2 |
  &lt;span class="k"&gt;\bar&lt;/span&gt; &amp;quot;|.&amp;quot;
&lt;span class="nb"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;\score&lt;/span&gt; &lt;span class="nb"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;\articulate&lt;/span&gt; &lt;span class="k"&gt;\music&lt;/span&gt;
  &lt;span class="k"&gt;\midi&lt;/span&gt; &lt;span class="nb"&gt;{}&lt;/span&gt;
&lt;span class="nb"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;\score&lt;/span&gt; &lt;span class="nb"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;\music&lt;/span&gt;
  &lt;span class="k"&gt;\layout&lt;/span&gt; &lt;span class="nb"&gt;{}&lt;/span&gt;
&lt;span class="nb"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This Lilypond file results in this PDF output:&lt;/p&gt;
&lt;img alt="Musical score for Ode to Joy." src="static/images/tlt-ode-to-joy.png" /&gt;
&lt;p&gt;It also generates a MIDI file (in case you’re wondering: the &lt;tt class="docutils literal"&gt;\articulate&lt;/tt&gt;
command just makes the MIDI output sound a lot nicer), which I can conveniently
read using in Python using the &lt;a class="reference external" href="https://mido.readthedocs.io/"&gt;Mido library&lt;/a&gt;, and thus generate some C++
arrays representing the data. These look like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;typedef&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;uint16_t&lt;/span&gt; &lt;span class="n"&gt;period_us&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kt"&gt;uint16_t&lt;/span&gt; &lt;span class="n"&gt;duration_ms&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="n"&gt;music_event_t&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;typedef&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;start_event&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;n_events&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="n"&gt;music_segment_t&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;typedef&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;music_event_t&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;events&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;n_events&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;music_segment_t&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;segments&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;n_segments&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;music_event_t&lt;/span&gt; &lt;span class="n"&gt;_song_events&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;{.&lt;/span&gt;&lt;span class="n"&gt;period_us&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1517&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;duration_ms&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;328&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{.&lt;/span&gt;&lt;span class="n"&gt;period_us&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;duration_ms&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;47&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{.&lt;/span&gt;&lt;span class="n"&gt;period_us&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1517&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;duration_ms&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;328&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{.&lt;/span&gt;&lt;span class="n"&gt;period_us&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;duration_ms&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;47&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{.&lt;/span&gt;&lt;span class="n"&gt;period_us&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1432&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;duration_ms&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;328&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{.&lt;/span&gt;&lt;span class="n"&gt;period_us&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;duration_ms&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;47&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{.&lt;/span&gt;&lt;span class="n"&gt;period_us&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1276&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;duration_ms&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;328&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{.&lt;/span&gt;&lt;span class="n"&gt;period_us&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;duration_ms&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;47&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{.&lt;/span&gt;&lt;span class="n"&gt;period_us&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1276&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;duration_ms&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;328&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{.&lt;/span&gt;&lt;span class="n"&gt;period_us&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;duration_ms&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;47&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{.&lt;/span&gt;&lt;span class="n"&gt;period_us&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1432&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;duration_ms&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;328&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{.&lt;/span&gt;&lt;span class="n"&gt;period_us&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;duration_ms&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;47&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{.&lt;/span&gt;&lt;span class="n"&gt;period_us&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1517&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;duration_ms&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;328&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{.&lt;/span&gt;&lt;span class="n"&gt;period_us&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;duration_ms&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;47&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{.&lt;/span&gt;&lt;span class="n"&gt;period_us&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1703&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;duration_ms&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;328&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{.&lt;/span&gt;&lt;span class="n"&gt;period_us&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;duration_ms&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;47&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{.&lt;/span&gt;&lt;span class="n"&gt;period_us&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1911&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;duration_ms&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;328&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{.&lt;/span&gt;&lt;span class="n"&gt;period_us&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;duration_ms&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;47&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{.&lt;/span&gt;&lt;span class="n"&gt;period_us&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1911&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;duration_ms&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;328&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{.&lt;/span&gt;&lt;span class="n"&gt;period_us&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;duration_ms&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;47&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{.&lt;/span&gt;&lt;span class="n"&gt;period_us&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1703&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;duration_ms&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;328&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{.&lt;/span&gt;&lt;span class="n"&gt;period_us&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;duration_ms&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;47&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{.&lt;/span&gt;&lt;span class="n"&gt;period_us&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1517&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;duration_ms&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;328&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{.&lt;/span&gt;&lt;span class="n"&gt;period_us&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;duration_ms&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;47&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{.&lt;/span&gt;&lt;span class="n"&gt;period_us&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1517&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;duration_ms&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;492&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{.&lt;/span&gt;&lt;span class="n"&gt;period_us&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;duration_ms&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;70&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{.&lt;/span&gt;&lt;span class="n"&gt;period_us&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1703&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;duration_ms&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;164&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{.&lt;/span&gt;&lt;span class="n"&gt;period_us&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;duration_ms&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;23&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{.&lt;/span&gt;&lt;span class="n"&gt;period_us&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1703&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;duration_ms&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;656&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;music_segment_t&lt;/span&gt; &lt;span class="n"&gt;_song_segments&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;{.&lt;/span&gt;&lt;span class="n"&gt;start_event&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;n_events&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;26&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{.&lt;/span&gt;&lt;span class="n"&gt;start_event&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;26&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;n_events&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;music_data_t&lt;/span&gt; &lt;span class="n"&gt;song_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;events&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_song_events&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;n_events&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;sizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_song_events&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;
                &lt;span class="k"&gt;sizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_song_events&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;segments&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_song_segments&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;n_segments&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;sizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_song_segments&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;
                  &lt;span class="k"&gt;sizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_song_segments&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The resulting &lt;tt class="docutils literal"&gt;song_data&lt;/tt&gt; has two segments, which index into the events array.
The intention there was that if music has repeating sections, the segments could
reference the same events, saving program space: as it turned out, I had plenty
of program space and finding repeating sections is hard, so this was a bit of a
premature optimization.&lt;/p&gt;
&lt;p&gt;Then all that remained was to hook up trigger &lt;em&gt;n&lt;/em&gt; to play segment &lt;em&gt;n&lt;/em&gt; of the
music, using &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Pulse-width_modulation"&gt;PWM&lt;/a&gt; on the piezo buzzer I’d attached to pin 0 of my micro:bits.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="reliability"&gt;
&lt;h2&gt;Reliability&lt;/h2&gt;
&lt;p&gt;Having written all this code, I found that on the micro:bits, the program would
occasionally get stuck: either the CPU would freeze up entirely, or the radio
module would get into a bad state and not receive / transmit messages, or other
weirdness would happen.&lt;/p&gt;
&lt;p&gt;Instead of trying to fix these, I used the watchdog timer of the nRF51 (the
CPU which runs the program): this lets me configure up to 8 &amp;quot;reload requests&amp;quot;,
and a period. If my application code doesn’t trigger all the enabled reload
requests within the period, the CPU will reset itself.&lt;/p&gt;
&lt;p&gt;I configured the period to two seconds, and set up two reload requests:
one in the main loop that I use for display (as a &amp;quot;the program has just stopped&amp;quot;
check), and one inside the time sync logic, which triggers the reload request
iff the time has synced (again, &lt;span class="math"&gt;\(|õ| &amp;lt; \varepsilon \)&lt;/span&gt;) within the last 30 seconds.
This second check is really useful: if the network gets into a weird state where
none of the nodes can sync, the nodes will all be obviously displaying this by
resetting themselves every 30 seconds.&lt;/p&gt;
&lt;p&gt;The other huge reliability concern was the root node in the network: if that
dies, all the other nodes lose their sync. As a way to reduce this impact, I
made it so any node could become the root node (by holding down some buttons):
that way, if I noticed the root node die, I could promote a standby node, and
since it already had almost the same time, the other nodes wouldn’t lose their
synchronization.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="epilogue"&gt;
&lt;h2&gt;Epilogue&lt;/h2&gt;
&lt;p&gt;So while this project was really cool and fun to build, I didn’t really have a
use for it until NCSS started. We run a programming competition at the camp,
which this year was Hitchhiker’s Guide to the Galaxy themed.
I did the theming for the scoreboard, and had a little intro animation (the
words “Don’t Panic” zooming in over a background of starts), and had the
micro:bits play &lt;em&gt;Journey of a Sorcerer&lt;/em&gt; (the theme music for HG2G) during that.&lt;/p&gt;
&lt;p&gt;I think it went reasonably well, though the big unforeseen issue was that the
piezo buzzers on most of the micro:bits weren’t as loud as the ones I had been
testing with, so the effect was somewhat, uh, muted. 😛&lt;/p&gt;
&lt;p&gt;If I was to do more on this project, there’s a few things I would improve:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;The time sync algorithm doesn’t &lt;em&gt;actually&lt;/em&gt; need the &lt;tt class="docutils literal"&gt;SYNC&lt;/tt&gt; packets: it could
be done as part of the &lt;tt class="docutils literal"&gt;PING_RESPONSE&lt;/tt&gt; packets, which would simplify it
somewhat.&lt;/li&gt;
&lt;li&gt;Send the music data across the network: compiling and flashing all the nodes
every time I want to play a different song is very tedious.&lt;/li&gt;
&lt;li&gt;Relatedly, move the song data to flash storage: that way the music data from
the network would be persisted across reboots.&lt;/li&gt;
&lt;li&gt;Use something other than piezo buzzers. I was watching the &lt;a class="reference external" href="https://youtu.be/IvUU8joBb1Q"&gt;Wintergatan marble
machine&lt;/a&gt; (well, actually the videos for the construction of its
replacement), and it’s made me really want to do something where the micro:bit
triggers physical actions to make noise (maybe plucking a violin string)?&lt;/li&gt;
&lt;li&gt;Support multiple channels of MIDI. There’s actually very little stopping the
micro:bits from playing polyphonic sounds: if I had compiled the nodes with
different song data (but matching segment lengths), they could already do it
pretty easily.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Source code:&lt;/strong&gt; is in &lt;a class="reference external" href="http://hg.flowblok.id.au/thought-leadering-time"&gt;this Mercurial repository&lt;/a&gt;.
It’s open source, under the Apache 2.0 license.&lt;/p&gt;
&lt;/div&gt;
&lt;script type='text/javascript'&gt;if (!document.getElementById('mathjaxscript_pelican_#%@#$@#')) {
    var align = "center",
        indent = "0em",
        linebreak = "false";

    if (false) {
        align = (screen.width &lt; 768) ? "left" : align;
        indent = (screen.width &lt; 768) ? "0em" : indent;
        linebreak = (screen.width &lt; 768) ? 'true' : linebreak;
    }

    var mathjaxscript = document.createElement('script');
    mathjaxscript.id = 'mathjaxscript_pelican_#%@#$@#';
    mathjaxscript.type = 'text/javascript';
    mathjaxscript.src = 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.0/MathJax.js?config=TeX-AMS-MML_HTMLorMML';
    mathjaxscript[(window.opera ? "innerHTML" : "text")] =
        "MathJax.Hub.Config({" +
        "    config: ['MMLorHTML.js']," +
        "    TeX: { extensions: ['AMSmath.js','AMSsymbols.js','noErrors.js','noUndefined.js'], equationNumbers: { autoNumber: 'AMS' } }," +
        "    jax: ['input/TeX','input/MathML','output/HTML-CSS']," +
        "    extensions: ['tex2jax.js','mml2jax.js','MathMenu.js','MathZoom.js']," +
        "    displayAlign: '"+ align +"'," +
        "    displayIndent: '"+ indent +"'," +
        "    showMathMenu: true," +
        "    messageStyle: 'normal'," +
        "    tex2jax: { " +
        "        inlineMath: [ ['\\\\(','\\\\)'] ], " +
        "        displayMath: [ ['$$','$$'] ]," +
        "        processEscapes: true," +
        "        preview: 'TeX'," +
        "    }, " +
        "    'HTML-CSS': { " +
        "        styles: { '.MathJax_Display, .MathJax .mo, .MathJax .mi, .MathJax .mn': {color: 'inherit ! important'} }," +
        "        linebreaks: { automatic: "+ linebreak +", width: '90% container' }," +
        "    }, " +
        "}); " +
        "if ('default' !== 'default') {" +
            "MathJax.Hub.Register.StartupHook('HTML-CSS Jax Ready',function () {" +
                "var VARIANT = MathJax.OutputJax['HTML-CSS'].FONTDATA.VARIANT;" +
                "VARIANT['normal'].fonts.unshift('MathJax_default');" +
                "VARIANT['bold'].fonts.unshift('MathJax_default-bold');" +
                "VARIANT['italic'].fonts.unshift('MathJax_default-italic');" +
                "VARIANT['-tex-mathit'].fonts.unshift('MathJax_default-italic');" +
            "});" +
            "MathJax.Hub.Register.StartupHook('SVG Jax Ready',function () {" +
                "var VARIANT = MathJax.OutputJax.SVG.FONTDATA.VARIANT;" +
                "VARIANT['normal'].fonts.unshift('MathJax_default');" +
                "VARIANT['bold'].fonts.unshift('MathJax_default-bold');" +
                "VARIANT['italic'].fonts.unshift('MathJax_default-italic');" +
                "VARIANT['-tex-mathit'].fonts.unshift('MathJax_default-italic');" +
            "});" +
        "}";
    (document.body || document.getElementsByTagName('head')[0]).appendChild(mathjaxscript);
}
&lt;/script&gt;</content><category term="embedded"></category><category term="microbit"></category><category term="music"></category><category term="ncss"></category><category term="software"></category></entry><entry><title>Finding Good Answers Quickly</title><link href="2015-10/finding-good-answers-quickly.html" rel="alternate"></link><published>2015-10-08T00:05:00+11:00</published><updated>2015-10-08T00:05:00+11:00</updated><author><name>Peter Ward</name></author><id>tag:None,2015-10-08:2015-10/finding-good-answers-quickly.html</id><summary type="html">&lt;p&gt;I recently had a difficult problem to solve: assigning 94 students into teams of
four. The first reason it’s difficult is that you end up with 23.5 teams, and
since we can’t chop students in half, we have to go with 22 teams of four, and
two …&lt;/p&gt;</summary><content type="html">&lt;p&gt;I recently had a difficult problem to solve: assigning 94 students into teams of
four. The first reason it’s difficult is that you end up with 23.5 teams, and
since we can’t chop students in half, we have to go with 22 teams of four, and
two teams of three.&lt;/p&gt;
&lt;p&gt;But we’re not done yet: the competition should be fair, in the sense that teams
should be roughly equally matched: we don’t want to have one really good team
that easily wins. So we get some estimations of students’ abilities—
let’s say they get a score of 1 (worst), 2 or 3 (best).
Then we can define a team’s strength as the sum of the students’ ability scores,
and we want teams strengths to be roughly equal.&lt;/p&gt;
&lt;p&gt;But we might even still have further requirements: maybe we want students to not
be in teams with students they’ve already worked with (so they meet more
people), or not put students who have done the competition together (as
experience isn’t quite the same thing as ability).&lt;/p&gt;
&lt;p&gt;Now this problem isn’t difficult because it’s hard to solve, but because it’s
difficult to figure out exactly what the requirements are.
The way I’d like to solve this problem is by specifying a small part of the
problem, having something give me a possible solution, and then iterating to
further define the problem (to avoid the bad cases I see).&lt;/p&gt;
&lt;p&gt;I’ve had to solve a few problems of this nature, and after writing &lt;em&gt;almost&lt;/em&gt; the
same Python script each time, I refactored out the common code into
&lt;a class="reference external" href="http://hg.flowblok.id.au/hufflepuff"&gt;Hufflepuff&lt;/a&gt;, an open-source project I released today.
Like any good UNIX tool, it’s not especially interesting or complex itself,
but lets you glue together other simple tools to produce something useful.&lt;/p&gt;
&lt;p&gt;Let’s see how it works with a cut-down version of the example above.
First, create a working directory, install a Python virtualenv and install
Hufflepuff from pypi, and test it works:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$ mkdir -p ~/tmp/teams&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;cd&lt;/span&gt; ~/tmp/teams
$ virtualenv .
$ . bin/activate
&lt;span class="o"&gt;(&lt;/span&gt;teams&lt;span class="o"&gt;)&lt;/span&gt;$ pip install hufflepuff
&lt;span class="o"&gt;(&lt;/span&gt;teams&lt;span class="o"&gt;)&lt;/span&gt;$ hufflepuff -h
usage: hufflepuff &lt;span class="o"&gt;[&lt;/span&gt;-h&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;-c CONFIG_FILE&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;--initial-binary INITIAL_BINARY&lt;span class="o"&gt;]&lt;/span&gt;
                  &lt;span class="o"&gt;[&lt;/span&gt;--initial-file INITIAL_FILE&lt;span class="o"&gt;]&lt;/span&gt;
                  &lt;span class="o"&gt;[&lt;/span&gt;--mutate-binary MUTATE_BINARY&lt;span class="o"&gt;]&lt;/span&gt;
                  &lt;span class="o"&gt;[&lt;/span&gt;--score-binary SCORE_BINARY&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;--beam-width BEAM_WIDTH&lt;span class="o"&gt;]&lt;/span&gt;
                  &lt;span class="o"&gt;[&lt;/span&gt;--expand-mantissa EXPAND_MANTISSA&lt;span class="o"&gt;]&lt;/span&gt;
                  &lt;span class="o"&gt;[&lt;/span&gt;--expand-multiplier EXPAND_MULTIPLIER&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;--target TARGET&lt;span class="o"&gt;]&lt;/span&gt;
                  &lt;span class="o"&gt;[&lt;/span&gt;--num-iterations NUM_ITERATIONS&lt;span class="o"&gt;]&lt;/span&gt;
                  &lt;span class="o"&gt;[&lt;/span&gt;--parallelism PARALLELISM&lt;span class="o"&gt;]&lt;/span&gt;
...
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;We’ll also need some test data to play with, and for simplicity, we’ll just have
a few students:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;teams&lt;span class="o"&gt;)&lt;/span&gt;$ python
&amp;gt;&amp;gt;&amp;gt; import json, random
&amp;gt;&amp;gt;&amp;gt; with open&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;students.json&amp;#39;&lt;/span&gt;, &lt;span class="s1"&gt;&amp;#39;w&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; as f:
...   json.dump&lt;span class="o"&gt;([&lt;/span&gt;
...     &lt;span class="o"&gt;{&lt;/span&gt;
...       &lt;span class="s1"&gt;&amp;#39;name&amp;#39;&lt;/span&gt;: name,
...       &lt;span class="s1"&gt;&amp;#39;skill&amp;#39;&lt;/span&gt;: random.randint&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;, &lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;,
...       &lt;span class="s1"&gt;&amp;#39;experience&amp;#39;&lt;/span&gt;: random.random&lt;span class="o"&gt;()&lt;/span&gt; &amp;lt; &lt;span class="m"&gt;0&lt;/span&gt;.3,
...     &lt;span class="o"&gt;}&lt;/span&gt;
...     &lt;span class="k"&gt;for&lt;/span&gt; name in &lt;span class="s1"&gt;&amp;#39;ABCDEFGHIJKLMN&amp;#39;&lt;/span&gt;
...   &lt;span class="o"&gt;]&lt;/span&gt;, f&lt;span class="o"&gt;)&lt;/span&gt;
...
&amp;gt;&amp;gt;&amp;gt; &lt;span class="o"&gt;[&lt;/span&gt;Ctrl-D&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You should now have a &lt;tt class="docutils literal"&gt;students.json&lt;/tt&gt; file with some students, each with a
unique name, skill level, and experience indicating whether they’ve done the
competition before or not.&lt;/p&gt;
&lt;p&gt;Now we need to provide three things to Hufflepuff: an initial assignment of
students to teams, a program to mutate those teams, and a program to score a
team assignment.&lt;/p&gt;
&lt;p&gt;For the initial assignment, we need to specify the team sizes, and provide a
valid assignment: so let’s just put the students in order. Here’s
&lt;tt class="docutils literal"&gt;initial.py&lt;/tt&gt;, which goes into your working directory:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;json&lt;/span&gt;

&lt;span class="c1"&gt;# For 14 students, two teams of four, and two of three.&lt;/span&gt;
&lt;span class="n"&gt;team_sizes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;

&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;students.json&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;students&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="nb"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;team_sizes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;students&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;students&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;name&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;team_size&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;team_size&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;team_sizes&lt;/span&gt;
&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;students&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now when you run it, you should get the following output.
It should be fairly obvious: the first team has four people (A, B, C, and D).&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;teams&lt;span class="o"&gt;)&lt;/span&gt;$ python initial.py
&lt;span class="o"&gt;[[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;A&amp;quot;&lt;/span&gt;, &lt;span class="s2"&gt;&amp;quot;B&amp;quot;&lt;/span&gt;, &lt;span class="s2"&gt;&amp;quot;C&amp;quot;&lt;/span&gt;, &lt;span class="s2"&gt;&amp;quot;D&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;, &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;E&amp;quot;&lt;/span&gt;, &lt;span class="s2"&gt;&amp;quot;F&amp;quot;&lt;/span&gt;, &lt;span class="s2"&gt;&amp;quot;G&amp;quot;&lt;/span&gt;, &lt;span class="s2"&gt;&amp;quot;H&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;, &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;I&amp;quot;&lt;/span&gt;, &lt;span class="s2"&gt;&amp;quot;J&amp;quot;&lt;/span&gt;, &lt;span class="s2"&gt;&amp;quot;K&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;, &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;L&amp;quot;&lt;/span&gt;, &lt;span class="s2"&gt;&amp;quot;M&amp;quot;&lt;/span&gt;, &lt;span class="s2"&gt;&amp;quot;N&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;]]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The next thing to provide to Hufflepuff is a way of mutating the state.
In this example, the easiest thing to do is to swap a pair of students in
different teams. We need to be carefuly to only produce valid states
(every student should be in exactly one team), and ideally we shouldn’t
duplicate states— so since teams are a set of students, let’s keep them in
sorted order within the team.&lt;/p&gt;
&lt;p&gt;Here’s &lt;cite&gt;mutate.py&lt;/cite&gt;, also to be placed in the working directory:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;json&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;random&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;sys&lt;/span&gt;

&lt;span class="c1"&gt;# Read lines from stdin (ending cleanly on EOF):&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;iter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stdin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;readline&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;teams&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Swap between 1-3 pairs.&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;randint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)):&lt;/span&gt;
        &lt;span class="c1"&gt;# Select two teams to swap between.&lt;/span&gt;
        &lt;span class="n"&gt;team_a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;team_b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sample&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;teams&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;# Choose a person from each team.&lt;/span&gt;
        &lt;span class="n"&gt;person_a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;choice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;team_a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;person_b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;choice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;team_b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;# Swap them.&lt;/span&gt;
        &lt;span class="n"&gt;team_a&lt;/span&gt;&lt;span class="p"&gt;[:]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;sorted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;person_b&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;person_a&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;
            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;team_a&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;team_b&lt;/span&gt;&lt;span class="p"&gt;[:]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;sorted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;person_a&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;person_b&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;
            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;team_b&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;teams&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You should be able to run it as below.
The first JSON list is input, the second is output, and it should continue
accepting input until you send EOF (by pressing Ctrl-D).&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;teams&lt;span class="o"&gt;)&lt;/span&gt;$ python mutate.py
&lt;span class="o"&gt;[[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;A&amp;quot;&lt;/span&gt;, &lt;span class="s2"&gt;&amp;quot;B&amp;quot;&lt;/span&gt;, &lt;span class="s2"&gt;&amp;quot;C&amp;quot;&lt;/span&gt;, &lt;span class="s2"&gt;&amp;quot;D&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;, &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;E&amp;quot;&lt;/span&gt;, &lt;span class="s2"&gt;&amp;quot;F&amp;quot;&lt;/span&gt;, &lt;span class="s2"&gt;&amp;quot;G&amp;quot;&lt;/span&gt;, &lt;span class="s2"&gt;&amp;quot;H&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;, &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;I&amp;quot;&lt;/span&gt;, &lt;span class="s2"&gt;&amp;quot;J&amp;quot;&lt;/span&gt;, &lt;span class="s2"&gt;&amp;quot;K&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;, &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;L&amp;quot;&lt;/span&gt;, &lt;span class="s2"&gt;&amp;quot;M&amp;quot;&lt;/span&gt;, &lt;span class="s2"&gt;&amp;quot;N&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;]]&lt;/span&gt;
&lt;span class="o"&gt;[[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;B&amp;quot;&lt;/span&gt;, &lt;span class="s2"&gt;&amp;quot;C&amp;quot;&lt;/span&gt;, &lt;span class="s2"&gt;&amp;quot;J&amp;quot;&lt;/span&gt;, &lt;span class="s2"&gt;&amp;quot;M&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;, &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;E&amp;quot;&lt;/span&gt;, &lt;span class="s2"&gt;&amp;quot;F&amp;quot;&lt;/span&gt;, &lt;span class="s2"&gt;&amp;quot;G&amp;quot;&lt;/span&gt;, &lt;span class="s2"&gt;&amp;quot;L&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;, &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;D&amp;quot;&lt;/span&gt;, &lt;span class="s2"&gt;&amp;quot;I&amp;quot;&lt;/span&gt;, &lt;span class="s2"&gt;&amp;quot;K&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;, &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;A&amp;quot;&lt;/span&gt;, &lt;span class="s2"&gt;&amp;quot;H&amp;quot;&lt;/span&gt;, &lt;span class="s2"&gt;&amp;quot;N&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;]]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You’ll have noticed that it swaps a few pairs of people around.
The initial state will be pretty bad, so we want to make a lot of swaps, to
quickly head towards the better state.
Towards the end of the search, lots of swaps will usually be bad,
and we want single swaps to settle on the local optimum.
There are more sophisticated things you could do, but I’ve found this is easy
and works well enough in practice.&lt;/p&gt;
&lt;p&gt;The last thing we need to provide is some way of scoring the state—
nicely congregating all your experimental decisions of what to optimise into a
simple problem: giving a score for a team assignment.&lt;/p&gt;
&lt;p&gt;In this case, it’s easiest to specify the score as a “cost”, and say how bad an
assignment is. We’ll inflict penalties for teams with more than one experienced
student, and for the difference between a team’s strength and the average team
strength. Here’s &lt;tt class="docutils literal"&gt;score.py&lt;/tt&gt;, also to be placed in the working directory:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;json&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;sys&lt;/span&gt;

&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;students.json&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;students_by_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;student&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;name&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;student&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;student&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;experience_penalty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;teams&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;penalty&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;team&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;teams&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;n_experienced&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;student&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;team&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;student&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;experience&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;n_experienced&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;penalty&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;score&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;skill_penalty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;teams&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;strengths&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="nb"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;student&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;skill&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;student&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;team&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;team&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;teams&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="n"&gt;average_strength&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;strengths&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;strengths&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nb"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;average_strength&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;strength&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;strength&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;strengths&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;iter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stdin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;readline&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;teams&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Replace students&amp;#39; names with their full info.&lt;/span&gt;
    &lt;span class="n"&gt;teams&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;students_by_name&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;team&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;team&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;teams&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;experience_penalty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;teams&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;skill_penalty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;teams&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="n"&gt;score&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If you run it, and provide it the initial team JSON, it should print out some
score (which will vary based on your &lt;tt class="docutils literal"&gt;students.json&lt;/tt&gt; file):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;teams&lt;span class="o"&gt;)&lt;/span&gt;$ python score.py
&lt;span class="o"&gt;[[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;A&amp;quot;&lt;/span&gt;, &lt;span class="s2"&gt;&amp;quot;B&amp;quot;&lt;/span&gt;, &lt;span class="s2"&gt;&amp;quot;C&amp;quot;&lt;/span&gt;, &lt;span class="s2"&gt;&amp;quot;D&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;, &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;E&amp;quot;&lt;/span&gt;, &lt;span class="s2"&gt;&amp;quot;F&amp;quot;&lt;/span&gt;, &lt;span class="s2"&gt;&amp;quot;G&amp;quot;&lt;/span&gt;, &lt;span class="s2"&gt;&amp;quot;H&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;, &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;I&amp;quot;&lt;/span&gt;, &lt;span class="s2"&gt;&amp;quot;J&amp;quot;&lt;/span&gt;, &lt;span class="s2"&gt;&amp;quot;K&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;, &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;L&amp;quot;&lt;/span&gt;, &lt;span class="s2"&gt;&amp;quot;M&amp;quot;&lt;/span&gt;, &lt;span class="s2"&gt;&amp;quot;N&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;]]&lt;/span&gt;
&lt;span class="m"&gt;104&lt;/span&gt;.0
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;A side note: it’s handy to put a “debug mode” into the scoring program, so you
can give it a solution, and quickly see how well it does on each of the
conditions. This can be useful for distinguishing between multiple
equally-scoring solutions, and also for telling if the search found the optimum.&lt;/p&gt;
&lt;p&gt;Finally, we’ll write a &lt;tt class="docutils literal"&gt;search.cfg&lt;/tt&gt; file to specify what we want Hufflepuff to
do (we could also just pass the parameters as command line args, but this is
nicer):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="na"&gt;initial-binary&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;python initial.py&lt;/span&gt;
&lt;span class="na"&gt;mutate-binary&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;python mutate.py&lt;/span&gt;
&lt;span class="na"&gt;score-binary&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;python score.py&lt;/span&gt;
&lt;span class="na"&gt;target&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;min&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Then, all we need to do is to run Hufflepuff, which will take all those things,
and run a &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Beam_search"&gt;beam search&lt;/a&gt;. As it runs, it prints out the scores of the items in
it’s current search beam to stderr, and finally prints out &lt;tt class="docutils literal"&gt;[score, state]&lt;/tt&gt;
JSON-encoded lists to stdout of the best states it ever saw.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;teams&lt;span class="o"&gt;)&lt;/span&gt;$ hufflepuff -c search.cfg
...
&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;.0, &lt;span class="o"&gt;[[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;A&amp;quot;&lt;/span&gt;, &lt;span class="s2"&gt;&amp;quot;C&amp;quot;&lt;/span&gt;, &lt;span class="s2"&gt;&amp;quot;D&amp;quot;&lt;/span&gt;, &lt;span class="s2"&gt;&amp;quot;K&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;, &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;B&amp;quot;&lt;/span&gt;, &lt;span class="s2"&gt;&amp;quot;G&amp;quot;&lt;/span&gt;, &lt;span class="s2"&gt;&amp;quot;J&amp;quot;&lt;/span&gt;, &lt;span class="s2"&gt;&amp;quot;L&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;, &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;F&amp;quot;&lt;/span&gt;, &lt;span class="s2"&gt;&amp;quot;H&amp;quot;&lt;/span&gt;, &lt;span class="s2"&gt;&amp;quot;I&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;, &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;E&amp;quot;&lt;/span&gt;, &lt;span class="s2"&gt;&amp;quot;M&amp;quot;&lt;/span&gt;, &lt;span class="s2"&gt;&amp;quot;N&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;]]]&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;.0, &lt;span class="o"&gt;[[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;A&amp;quot;&lt;/span&gt;, &lt;span class="s2"&gt;&amp;quot;C&amp;quot;&lt;/span&gt;, &lt;span class="s2"&gt;&amp;quot;D&amp;quot;&lt;/span&gt;, &lt;span class="s2"&gt;&amp;quot;K&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;, &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;B&amp;quot;&lt;/span&gt;, &lt;span class="s2"&gt;&amp;quot;G&amp;quot;&lt;/span&gt;, &lt;span class="s2"&gt;&amp;quot;J&amp;quot;&lt;/span&gt;, &lt;span class="s2"&gt;&amp;quot;L&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;, &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;F&amp;quot;&lt;/span&gt;, &lt;span class="s2"&gt;&amp;quot;H&amp;quot;&lt;/span&gt;, &lt;span class="s2"&gt;&amp;quot;N&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;, &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;E&amp;quot;&lt;/span&gt;, &lt;span class="s2"&gt;&amp;quot;I&amp;quot;&lt;/span&gt;, &lt;span class="s2"&gt;&amp;quot;M&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;]]]&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;.0, &lt;span class="o"&gt;[[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;A&amp;quot;&lt;/span&gt;, &lt;span class="s2"&gt;&amp;quot;C&amp;quot;&lt;/span&gt;, &lt;span class="s2"&gt;&amp;quot;E&amp;quot;&lt;/span&gt;, &lt;span class="s2"&gt;&amp;quot;G&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;, &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;D&amp;quot;&lt;/span&gt;, &lt;span class="s2"&gt;&amp;quot;H&amp;quot;&lt;/span&gt;, &lt;span class="s2"&gt;&amp;quot;J&amp;quot;&lt;/span&gt;, &lt;span class="s2"&gt;&amp;quot;M&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;, &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;B&amp;quot;&lt;/span&gt;, &lt;span class="s2"&gt;&amp;quot;I&amp;quot;&lt;/span&gt;, &lt;span class="s2"&gt;&amp;quot;L&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;, &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;F&amp;quot;&lt;/span&gt;, &lt;span class="s2"&gt;&amp;quot;K&amp;quot;&lt;/span&gt;, &lt;span class="s2"&gt;&amp;quot;N&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;]]]&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;.0, &lt;span class="o"&gt;[[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;A&amp;quot;&lt;/span&gt;, &lt;span class="s2"&gt;&amp;quot;C&amp;quot;&lt;/span&gt;, &lt;span class="s2"&gt;&amp;quot;G&amp;quot;&lt;/span&gt;, &lt;span class="s2"&gt;&amp;quot;L&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;, &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;I&amp;quot;&lt;/span&gt;, &lt;span class="s2"&gt;&amp;quot;J&amp;quot;&lt;/span&gt;, &lt;span class="s2"&gt;&amp;quot;K&amp;quot;&lt;/span&gt;, &lt;span class="s2"&gt;&amp;quot;M&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;, &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;D&amp;quot;&lt;/span&gt;, &lt;span class="s2"&gt;&amp;quot;F&amp;quot;&lt;/span&gt;, &lt;span class="s2"&gt;&amp;quot;N&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;, &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;B&amp;quot;&lt;/span&gt;, &lt;span class="s2"&gt;&amp;quot;E&amp;quot;&lt;/span&gt;, &lt;span class="s2"&gt;&amp;quot;H&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;]]]&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;.0, &lt;span class="o"&gt;[[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;A&amp;quot;&lt;/span&gt;, &lt;span class="s2"&gt;&amp;quot;C&amp;quot;&lt;/span&gt;, &lt;span class="s2"&gt;&amp;quot;L&amp;quot;&lt;/span&gt;, &lt;span class="s2"&gt;&amp;quot;N&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;, &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;I&amp;quot;&lt;/span&gt;, &lt;span class="s2"&gt;&amp;quot;J&amp;quot;&lt;/span&gt;, &lt;span class="s2"&gt;&amp;quot;K&amp;quot;&lt;/span&gt;, &lt;span class="s2"&gt;&amp;quot;M&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;, &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;D&amp;quot;&lt;/span&gt;, &lt;span class="s2"&gt;&amp;quot;F&amp;quot;&lt;/span&gt;, &lt;span class="s2"&gt;&amp;quot;G&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;, &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;B&amp;quot;&lt;/span&gt;, &lt;span class="s2"&gt;&amp;quot;E&amp;quot;&lt;/span&gt;, &lt;span class="s2"&gt;&amp;quot;H&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;]]]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;As you can see, it found a bunch of possible solutions, all of which had score
2.0. I can then manually inspect those states, determine if there’s any major
problems with them, and refine the scoring program if necessary.&lt;/p&gt;
&lt;p&gt;A cute little trick is that you can take those states (without the score), put
them into &lt;tt class="docutils literal"&gt;best.json&lt;/tt&gt;, and then run
&lt;tt class="docutils literal"&gt;hufflepuff &lt;span class="pre"&gt;-c&lt;/span&gt; search.cfg &lt;span class="pre"&gt;--initial-binary='cat&lt;/span&gt; &lt;span class="pre"&gt;$initial-file'&lt;/span&gt;
&lt;span class="pre"&gt;--initial-file=best.json&lt;/span&gt;&lt;/tt&gt;
to start the search from those states (and you can make it less ugly by defining
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;initial-binary&lt;/span&gt;&lt;/tt&gt; in &lt;tt class="docutils literal"&gt;search.cfg&lt;/tt&gt; since that’s the default).&lt;/p&gt;
&lt;p&gt;Pull requests, &lt;a class="reference external" href="http://hg.flowblok.id.au/hufflepuff/issues"&gt;bugs and feature requests&lt;/a&gt; are welcome, and I’m also happy to
answer questions in the comments below.
And if case you were wondering, &lt;a class="reference external" href="https://youtu.be/5NANaYemJdg?t=53s"&gt;here’s the inspiration for the name&lt;/a&gt;.&lt;/p&gt;
</content><category term="software"></category></entry><entry><title>Solving Problems Badly</title><link href="2015-10/solving-problems-badly.html" rel="alternate"></link><published>2015-10-03T14:02:00+10:00</published><updated>2015-10-03T14:02:00+10:00</updated><author><name>Peter Ward</name></author><id>tag:None,2015-10-03:2015-10/solving-problems-badly.html</id><summary type="html">&lt;p&gt;&lt;strong&gt;Perfect is the enemy of good.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;I have an interesting story to tell today, and I’m sure the title of this post
probably surprised you. Well, it’s not &lt;a class="reference external" href="http://writebadlywell.blogspot.com.au/"&gt;satire&lt;/a&gt; or &lt;a class="reference external" href="http://thedailywtf.com/"&gt;schadenfreude&lt;/a&gt;, but
actually serious advice.
This is the story of some code I wrote (back in 2013) to …&lt;/p&gt;</summary><content type="html">&lt;p&gt;&lt;strong&gt;Perfect is the enemy of good.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;I have an interesting story to tell today, and I’m sure the title of this post
probably surprised you. Well, it’s not &lt;a class="reference external" href="http://writebadlywell.blogspot.com.au/"&gt;satire&lt;/a&gt; or &lt;a class="reference external" href="http://thedailywtf.com/"&gt;schadenfreude&lt;/a&gt;, but
actually serious advice.
This is the story of some code I wrote (back in 2013) to solve a problem, where
I deliberately chose a “bad” solution.&lt;/p&gt;
&lt;p&gt;One of the things I’ve done for many years now is tutor high school students to
learn programming as part of the &lt;a class="reference external" href="https://groklearning.com/challenge/"&gt;NCSS Challenge&lt;/a&gt;. Each week, for five weeks,
students are given a set of problems to solve, accompanied by notes explaining
all the Python features and concepts they need to solve them.
Many high school computing teachers sign up their class (sometimes using it as
an assessment task), but many students also sign up themselves.
In addition to the notes, students can also chat and discuss the problems (but
not share code) on forums, or seek help privately from our volunteer tutors.&lt;/p&gt;
&lt;p&gt;The challenge uses &lt;a class="reference external" href="https://www.discourse.org/"&gt;Discourse&lt;/a&gt; for the forums, which works well, but also for
the private messaging (with some &lt;a class="reference external" href="https://github.com/groklearning/discourse-public"&gt;custom modifications&lt;/a&gt;), which works less
well. There are two main problems, which the following sequence illustrates:&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;Student 1 sends a message (sending a notification to all tutors).&lt;/li&gt;
&lt;li&gt;Tutor A starts writing a response.&lt;/li&gt;
&lt;li&gt;Tutor B starts writing a response&lt;/li&gt;
&lt;li&gt;Tutor A posts their response.&lt;/li&gt;
&lt;li&gt;Tutor B either doesn’t notice (meaning the student gets two responses,
which might attempt to solve the problem in different ways), or does notice
and is annoyed that their time has been wasted.&lt;/li&gt;
&lt;li&gt;Tutor A goes back to the notifications, and starts clicking through the
unread posts, but they’ve all been answered by other tutors.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;To solve the problem of multiple tutors answering the same post, we set up an
IRC channel that tutors joined, so they could announce which threads they were
going to work on. Various tutors wrote IRC bots to automate bits of this, and
then I combined all the functionality into a single bot for keeping track of
what state (unreplied/replied) threads were in, and which tutor was currently
assigned to answer it.&lt;/p&gt;
&lt;p&gt;And now you have enough background to get onto what my bad solution was.
One of the parts of this IRC bot was the “threads backend”, which stores thread
metadata: author, title, unreplied/replied state, and the tutor assigned to it.
Now the obvious answer to this is to use a database: &lt;a class="reference external" href="https://www.sqlite.org/"&gt;SQLite&lt;/a&gt; if it’s a toy
problem, and &lt;a class="reference external" href="http://www.postgresql.org/"&gt;PostgreSQL&lt;/a&gt; if it’s big enough.&lt;/p&gt;
&lt;p&gt;Instead of doing that, however, I went with something far more crude:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;os&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Thread&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;object&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;
        &lt;span class="o"&gt;...&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;to_json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="s1"&gt;&amp;#39;title&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="o"&gt;...&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;load_threads&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;threads.json&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;r&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;threads&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Thread&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;kwargs&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;threads&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;iteritems&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;save_threads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;threads&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;threads.json.new&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;w&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# If this crashes, we end up with an empty threads.json.new file,&lt;/span&gt;
        &lt;span class="c1"&gt;# but threads.json is still intact.&lt;/span&gt;
        &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dump&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
            &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;thread&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;to_json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;thread&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;threads&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;iteritems&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rename&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;threads.json.new&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;threads.json&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="o"&gt;...&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;request_handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;threads&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;load_threads&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;action&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;get_post&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;get_post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;threads&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;action&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;new_post&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;new_post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;threads&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;save_threads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;threads&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;It’s terrible! On every request, the entire database is read from disk, and
whenever a thread gets modified, the entire database has to be written back to
disk (not just the parts which changed).
There’s some other problems too: it doesn’t let you handle requests
concurrently, and it doesn’t let you create secondary indices to speed up
queries. It’s also missing an &lt;cite&gt;os.fsync()&lt;/cite&gt; call to ensure the data is &lt;em&gt;actually&lt;/em&gt;
written to disk, but let’s not talk about that one. 😉&lt;/p&gt;
&lt;p&gt;The secondary indices isn’t just a throwaway comment either: one of the
operations this backend supports is filtering threads by various attributes.
Obviously, the only way this backend can support that is by doing a linear scan
over all threads, and returning the ones which match.&lt;/p&gt;
&lt;p&gt;But actually, despite the number of raised eyebrows, it’s not actually quite as
terrible as it first seems. Firstly, it’s a backend to an IRC bot, which means
that any response it gives is going to be seen on an IRC channel, which is (sort
of) a serialised output: so not handling concurrent requests isn’t a big deal.
It also doesn’t have to handle a lot of traffic (much less than one request per
second), and even a 1s latency wouldn’t be obviously bad on IRC.&lt;/p&gt;
&lt;p&gt;The other thing to consider is that the database isn’t actually that big!
In the first year, there were ~500 threads: even with a generous 1KiB of
metadata per thread, that’s still less than a mebibyte, which is easily written
to disk in less than a second. It’s also small enough to fit in the OS buffer
cache, so reading the database from “disk” isn’t expensive either.
And you can also see that those linear scans aren’t that expensive either.&lt;/p&gt;
&lt;p&gt;Futhermore, there’s some advantages to the code.
It’s &lt;strong&gt;simple&lt;/strong&gt;: there’s full visibility into the internal state, serialisation and
deserialisation processes (unlike many ORMs, e.g. &lt;a class="reference external" href="http://www.sqlalchemy.org/"&gt;SQLAlchemy&lt;/a&gt;).
It’s &lt;strong&gt;debuggable&lt;/strong&gt;: it’s just JSON, so it’s incredibly easy to manually inspect
(after pretty-printing with &lt;tt class="docutils literal"&gt;python &lt;span class="pre"&gt;-m&lt;/span&gt; json.tool&lt;/tt&gt;) or write custom tools to
analyse it.
&lt;strong&gt;Schema migrations are easy&lt;/strong&gt;: tell &lt;tt class="docutils literal"&gt;Thread.__init__&lt;/tt&gt; about the new key
(providing a default value), and have &lt;tt class="docutils literal"&gt;Thread.to_json&lt;/tt&gt; emit whatever schema
you want stored: on the next database write, all record will be rewritten to the
new schema.&lt;/p&gt;
&lt;p&gt;This system performed adequately for the first year it was used.
There were many schema changes, but it had no data corruption or was the cause
of any outages. It’s performance was good enough: by the end, some filtering
requests took just over a second to respond to, but it was never bad enough
for users to notice.&lt;/p&gt;
&lt;p&gt;The next year, I rewrote it to use &lt;a class="reference external" href="http://www.sqlalchemy.org/"&gt;SQLAlchemy&lt;/a&gt; backed by &lt;a class="reference external" href="https://www.sqlite.org/"&gt;SQLite&lt;/a&gt; and added
indices to optimise the most frequent filter requests.
But I didn’t see that as admitting defeat: on the contrary: by starting with a
terrible implementation, I learnt exactly what the bottlenecks were, and could
then make informed decisions when designing the next version.&lt;/p&gt;
&lt;hr class="docutils" /&gt;
&lt;p&gt;Perhaps you weren’t surprised I went with a “bad” solution first: the idea is
&lt;a class="reference external" href="http://c2.com/cgi/wiki?PrematureOptimization"&gt;certainly&lt;/a&gt; &lt;a class="reference external" href="http://c2.com/cgi/wiki?PlanToThrowOneAway"&gt;not&lt;/a&gt; &lt;a class="reference external" href="http://c2.com/cgi/wiki?YouArentGonnaNeedIt"&gt;new&lt;/a&gt;. It’s an interesting strategy, but you need to
be careful with it: if you don’t make it bad enough, you won’t be motivated
enough to rewrite it in the future, and if you make it too bad, then it’ll crash
and burn before you’re ready to rewrite.&lt;/p&gt;
&lt;p&gt;Even if this story hasn’t fully convinced you that deliberately choosing
imperfect solutions is a good idea, I hope you can see it’s a viable strategy in
some cases.&lt;/p&gt;
</content><category term="random"></category></entry><entry><title>Introducing vipdf</title><link href="2014-03/introducing-vipdf.html" rel="alternate"></link><published>2014-03-09T12:41:00+11:00</published><updated>2014-03-09T12:41:00+11:00</updated><author><name>Peter Ward</name></author><id>tag:None,2014-03-09:2014-03/introducing-vipdf.html</id><summary type="html">&lt;p&gt;I mentioned in an earlier post (almost a year ago now, eep!) that I had some
more software to release to the world, and as promised, this is the next one.
The word “introducing” in the title is perhaps a bit misleading— I’ve written
(but not released) several similar …&lt;/p&gt;</summary><content type="html">&lt;p&gt;I mentioned in an earlier post (almost a year ago now, eep!) that I had some
more software to release to the world, and as promised, this is the next one.
The word “introducing” in the title is perhaps a bit misleading— I’ve written
(but not released) several similar programs to this, and I’ve been using this
particular program for a few years now, so it’s not really new.&lt;/p&gt;
&lt;p&gt;But enough of that, let’s get on to what this thing is.&lt;/p&gt;
&lt;div class="section" id="rationale"&gt;
&lt;h2&gt;Rationale&lt;/h2&gt;
&lt;p&gt;The rationale for vipdf is that I’m unhappy with a lot of existing presentation
software, for a variety of reasons, and I realised I could solve a lot of these
problems with a vi-style keyboard mapping for navigating presentations.&lt;/p&gt;
&lt;p&gt;The things which I wanted specifically were:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;strong&gt;Moving to an arbitrary slide.&lt;/strong&gt;
If you get to the end of a presentation and then someone in your audience asks
you a question about something on slide 15, you should be able to go directly
to that slide— you shouldn’t have to move through all the intermediate slides.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Skipping over slides.&lt;/strong&gt;
Suppose you’re giving a talk, and you realise that you haven’t got time to
talk about something in depth. No matter, your software should let you just
skip over the next slide (or two), without flashing those slides at your
audience.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Keyboard controls.&lt;/strong&gt;
If I have to use a mouse while giving a presentation, I’m going to
accidentally click things I didn’t mean to and miss things that I meant to
click on. Since I can touch-type, it makes sense to control everything by the
keyboard, and by using familiar vi-style keybindings, I can reduce the
cognitive overhead of remembering shortcuts.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;At the moment, vipdf only supports PDFs (just in case the name didn’t make that
obvious), which makes it great for pairing with beamer (as much as I dislike
TeX, I haven’t found anything better yet), but you can easily export PDFs from
most other presentation software (Microsoft Powerpoint, Keynote and LibreOffice
Impress all have this functionality).&lt;/p&gt;
&lt;p&gt;Yes, this will remove the gratuitous animations you had carefully wasted time
setting up.
No, there’s no support for embedded videos (in case you didn’t know, yes, you
can do that with PDFs).&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="controls"&gt;
&lt;h2&gt;Controls&lt;/h2&gt;
&lt;p&gt;As I mentioned, there are vi-style keyboard controls:&lt;/p&gt;
&lt;dl class="docutils"&gt;
&lt;dt&gt;&lt;tt class="docutils literal"&gt;&amp;lt;Left&amp;gt;&lt;/tt&gt;&lt;/dt&gt;
&lt;dd&gt;Move to the previous slide&lt;/dd&gt;
&lt;dt&gt;N &lt;tt class="docutils literal"&gt;&amp;lt;Left&amp;gt;&lt;/tt&gt;&lt;/dt&gt;
&lt;dd&gt;Go back &lt;strong&gt;N&lt;/strong&gt; slides
(e.g. &lt;tt class="docutils literal"&gt;3&amp;lt;Left&amp;gt;&lt;/tt&gt; will move you back 3 slides)&lt;/dd&gt;
&lt;dt&gt;&lt;tt class="docutils literal"&gt;&amp;lt;Right&amp;gt;&lt;/tt&gt;&lt;/dt&gt;
&lt;dd&gt;Move to the next slide&lt;/dd&gt;
&lt;dt&gt;N &lt;tt class="docutils literal"&gt;&amp;lt;Right&amp;gt;&lt;/tt&gt;&lt;/dt&gt;
&lt;dd&gt;Go forward &lt;strong&gt;N&lt;/strong&gt; slides
(e.g. &lt;tt class="docutils literal"&gt;2&amp;lt;Right&amp;gt;&lt;/tt&gt; will skip forward over a slide)&lt;/dd&gt;
&lt;dt&gt;N &lt;tt class="docutils literal"&gt;g&lt;/tt&gt;&lt;/dt&gt;
&lt;dd&gt;Goto slide &lt;strong&gt;N&lt;/strong&gt;
(e.g. &lt;tt class="docutils literal"&gt;33g&lt;/tt&gt; takes you to slide 33)&lt;/dd&gt;
&lt;dt&gt;&lt;tt class="docutils literal"&gt;b&lt;/tt&gt;&lt;/dt&gt;
&lt;dd&gt;Toggle blackout&lt;/dd&gt;
&lt;/dl&gt;
&lt;p&gt;Yeah, I didn’t map &lt;tt class="docutils literal"&gt;hjkl&lt;/tt&gt;. If you feel strongly about that, you should let me
know in the comments what the mapping should be (should both &lt;tt class="docutils literal"&gt;h&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;j&lt;/tt&gt;
move back, or only &lt;tt class="docutils literal"&gt;h&lt;/tt&gt;?)&lt;/p&gt;
&lt;p&gt;There’s also controls for auto-advancing slides
(&lt;tt class="docutils literal"&gt;t&lt;/tt&gt; is for timer, btw):&lt;/p&gt;
&lt;dl class="docutils"&gt;
&lt;dt&gt;&lt;tt class="docutils literal"&gt;t&lt;/tt&gt;&lt;/dt&gt;
&lt;dd&gt;Toggle auto-advance on or off.&lt;/dd&gt;
&lt;dt&gt;N &lt;tt class="docutils literal"&gt;t&lt;/tt&gt;&lt;/dt&gt;
&lt;dd&gt;Start auto-advance (if not already started), and set the time interval to
advance every &lt;strong&gt;N&lt;/strong&gt; seconds.
The default time interval is 5 seconds.&lt;/dd&gt;
&lt;dt&gt;N &lt;tt class="docutils literal"&gt;[&lt;/tt&gt;&lt;/dt&gt;
&lt;dd&gt;Set the start slide to slide &lt;strong&gt;N&lt;/strong&gt;, or unset if &lt;strong&gt;N&lt;/strong&gt; is 0.&lt;/dd&gt;
&lt;dt&gt;N &lt;tt class="docutils literal"&gt;]&lt;/tt&gt;&lt;/dt&gt;
&lt;dd&gt;Set the end slide to slide &lt;strong&gt;N&lt;/strong&gt;, or unset if &lt;strong&gt;N&lt;/strong&gt; is 0.
If auto-advance reaches the end slide, it will loop back to the start slide
if one is set, otherwise it will turn off that auto-advance.&lt;/dd&gt;
&lt;/dl&gt;
&lt;/div&gt;
&lt;div class="section" id="api"&gt;
&lt;h2&gt;API&lt;/h2&gt;
&lt;p&gt;There’s also a DBUS api available for use, and there’s also a sample control
script provided so you can write your own scripts to control your presentation:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$ vipdf-control list
&lt;span class="c1"&gt;# will give you a list of viewer ids, though at the moment there&amp;#39;s just one&lt;/span&gt;
&lt;span class="c1"&gt;# you should take that and pass it into the following commands&lt;/span&gt;

$ vipdf-control next &lt;span class="nv"&gt;$viewerid&lt;/span&gt;
&lt;span class="c1"&gt;# advances by 1 slide&lt;/span&gt;

$ vipdf-control next &lt;span class="nv"&gt;$viewerid&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;
&lt;span class="c1"&gt;# advances by 2 slides&lt;/span&gt;

$ vipdf-control prev &lt;span class="nv"&gt;$viewerid&lt;/span&gt;
&lt;span class="c1"&gt;# goes back 1 slide&lt;/span&gt;

$ vipdf-control goto &lt;span class="nv"&gt;$viewerid&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;
&lt;span class="c1"&gt;# goto slide 10&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Unfortunately, the API doesn’t make a lot of sense at the moment, as you can’t
have more than one presentation open in a vipdf instance at a time.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="implementation"&gt;
&lt;h2&gt;Implementation&lt;/h2&gt;
&lt;p&gt;vipdf is written in Vala, using Poppler and Clutter.
You can find &lt;a class="reference external" href="http://hg.flowblok.id.au/vipdf"&gt;the code&lt;/a&gt; here.&lt;/p&gt;
&lt;p&gt;Quick build instructions for Debian:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$ sudo aptitude install valac libgee-dev libpoppler-glib-dev libclutter-1.0-dev
$ make
...
$ bin/vipdf /path/to/file.pdf
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I’ve also written a &lt;a class="reference external" href="http://hg.flowblok.id.au/vipdf-web"&gt;web interface&lt;/a&gt; for it
(which also integrates with &lt;a class="reference external" href="2012-11/controlling-projectors-with-pjlink.html"&gt;pjlink&lt;/a&gt;).&lt;/p&gt;
&lt;/div&gt;
</content><category term="software"></category></entry><entry><title>Python: Wat</title><link href="2014-03/python-wat.html" rel="alternate"></link><published>2014-03-08T19:18:00+11:00</published><updated>2014-03-08T19:18:00+11:00</updated><author><name>Peter Ward</name></author><id>tag:None,2014-03-08:2014-03/python-wat.html</id><summary type="html">&lt;p&gt;You’ve all seen &lt;a class="reference external" href="https://www.destroyallsoftware.com/talks/wat"&gt;wat&lt;/a&gt;, right?
Of course you have.&lt;/p&gt;
&lt;p&gt;Of course, Python doesn’t have any Wats, right?
Well, not so much.&lt;/p&gt;
&lt;div class="section" id="lets-talk-about-python"&gt;
&lt;h2&gt;Let’s talk about Python!&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;toys&amp;quot;r&amp;quot;us&amp;#39;&lt;/span&gt;
&lt;span class="s1"&gt;&amp;#39;toys&amp;quot;r&amp;quot;us&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Makes sense.
So if we change those outer quotes to double quotes, we should get a …&lt;/p&gt;&lt;/div&gt;</summary><content type="html">&lt;p&gt;You’ve all seen &lt;a class="reference external" href="https://www.destroyallsoftware.com/talks/wat"&gt;wat&lt;/a&gt;, right?
Of course you have.&lt;/p&gt;
&lt;p&gt;Of course, Python doesn’t have any Wats, right?
Well, not so much.&lt;/p&gt;
&lt;div class="section" id="lets-talk-about-python"&gt;
&lt;h2&gt;Let’s talk about Python!&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;toys&amp;quot;r&amp;quot;us&amp;#39;&lt;/span&gt;
&lt;span class="s1"&gt;&amp;#39;toys&amp;quot;r&amp;quot;us&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Makes sense.
So if we change those outer quotes to double quotes, we should get a syntax
error, right?&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;toys&amp;quot;&lt;/span&gt;&lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;us&amp;quot;&lt;/span&gt;
&lt;span class="s1"&gt;&amp;#39;toysus&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Wat.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="id2"&gt;
&lt;h2&gt;Let’s talk about Python!&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;
&lt;span class="mi"&gt;42&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;
&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This is because before True and False were available as builtins, people would
write &lt;tt class="docutils literal"&gt;True = 1&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;False = 0&lt;/tt&gt; at the top of their Python files.
When &lt;a class="reference external" href="http://python.org/dev/peps/pep-0285/"&gt;they were introduced&lt;/a&gt;, they made &lt;tt class="docutils literal"&gt;bool&lt;/tt&gt; a subclass of &lt;tt class="docutils literal"&gt;int&lt;/tt&gt;,
&lt;tt class="docutils literal"&gt;True == 1&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;False == 0&lt;/tt&gt;.&lt;/p&gt;
&lt;p&gt;So of course, you can do arithmetic with booleans.&lt;/p&gt;
&lt;p&gt;Wat.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="id4"&gt;
&lt;h2&gt;Let’s talk about Python!&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;one&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="s1"&gt;&amp;#39;1&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;won&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;true&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Right, it’s a dictionary with three items: the keys have different types, but
the basic idea is that we map objects to some string representation of them.
Right?&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;true&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;1&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;won&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;There are three behaviours of dictionaries which come into play here.
The first is that keys in dictionaries compare equal if they have the same hash
value (&lt;tt class="docutils literal"&gt;__hash__&lt;/tt&gt;), &lt;em&gt;and&lt;/em&gt; compare equal (&lt;tt class="docutils literal"&gt;__eq__&lt;/tt&gt;).
The second is that dictionary literals are evaluated by starting with an empty
dictionary, and inserting each item in order (left to right).
The third is that if you are inserting an item into a dictionary and the key is
already there, the value will be replaced, but not the key.&lt;/p&gt;
&lt;p&gt;Which means that in the example above, this is what happens:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;one&amp;#39;&lt;/span&gt;
&lt;span class="c1"&gt;# {1: &amp;#39;one&amp;#39;}&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;1&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;won&amp;#39;&lt;/span&gt;
&lt;span class="c1"&gt;# {1: &amp;#39;one&amp;#39;, &amp;#39;1&amp;#39;: &amp;#39;won&amp;#39;}&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;true&amp;#39;&lt;/span&gt;
&lt;span class="c1"&gt;# {1: &amp;#39;true&amp;#39;, &amp;#39;1&amp;#39;: &amp;#39;won&amp;#39;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Wait, why did the last insertion replace the value for 1?
Well because &lt;tt class="docutils literal"&gt;hash(True) == hash(1)&lt;/tt&gt;, and &lt;tt class="docutils literal"&gt;True == 1&lt;/tt&gt;, of course.&lt;/p&gt;
&lt;p&gt;Wat.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="enough-making-fun-of-languages-that-suck-lets-talk-about-json"&gt;
&lt;h2&gt;Enough making fun of languages that suck, let’s talk about JSON!&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;json&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;1&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;won&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;true&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="s1"&gt;&amp;#39;{&amp;quot;1&amp;quot;: &amp;quot;won&amp;quot;, &amp;quot;1&amp;quot;: &amp;quot;true&amp;quot;}&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This &lt;a class="reference external" href="http://stackoverflow.com/a/19927061"&gt;is valid JSON&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Wat.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="id6"&gt;
&lt;h2&gt;Let’s talk about Python.&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;
&lt;span class="bp"&gt;False&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Uh, wait, but 1 &amp;gt; 2 isn’t true, so it should be False, right?
Like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;
&lt;span class="bp"&gt;True&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Hmmm, that works, maybe it’s just a precedence thing:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="bp"&gt;True&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Nope (and note the re-occurence of &lt;tt class="docutils literal"&gt;False == 0&lt;/tt&gt; in this).
This is actually a case of Python’s chained comparisons:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;
&lt;span class="bp"&gt;False&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Chained comparisons are actually awesome…&lt;/p&gt;
&lt;img alt="a t-rex playing" src="static/images/awesome.png" /&gt;
&lt;p&gt;…but if you ever use it with disparate operators like this, Wat.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="and-lets-finish-up-with-some-python"&gt;
&lt;h2&gt;And let’s finish up with some Python!&lt;/h2&gt;
&lt;p&gt;This was fixed in Python 3, but there’s still plenty of Python 2 programmers
just waiting to bump into this one.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;GiantSpiders&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;    &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;    &lt;span class="n"&gt;legs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;        &lt;span class="s1"&gt;&amp;#39;leg &lt;/span&gt;&lt;span class="si"&gt;%d&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;    &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;GiantSpiders&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt;
&lt;span class="mi"&gt;7&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Arrrghh! Help, spiders!&lt;/p&gt;
&lt;p&gt;Wat.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="also-the-one-which-didnt-qualify"&gt;
&lt;h2&gt;Also, the one which didn’t qualify&lt;/h2&gt;
&lt;p&gt;&lt;a class="reference external" href="https://mail.python.org/pipermail/python-ideas/2014-March/026446.html"&gt;Time objects representing midnight are falsey&lt;/a&gt;.
Except, it's &lt;a class="reference external" href="https://mail.python.org/pipermail/python-ideas/2014-March/026647.html"&gt;not quite as simple&lt;/a&gt; as that.
Unfortunately, this only qualifies for an honourable mention: there’s an
&lt;a class="reference external" href="http://bugs.python.org/issue13936"&gt;open bug&lt;/a&gt; for this issue, so it might be fixed.&lt;/p&gt;
&lt;/div&gt;
</content><category term="software"></category></entry><entry><title>Learning Go with AppEngine</title><link href="2013-04/learning-go-with-appengine.html" rel="alternate"></link><published>2013-04-06T18:24:00+11:00</published><updated>2013-04-06T18:24:00+11:00</updated><author><name>Peter Ward</name></author><id>tag:None,2013-04-06:2013-04/learning-go-with-appengine.html</id><summary type="html">&lt;p&gt;I’ve been meaning to learn Go for a while, and I had a nice opportunity today to
do that. But before I talk about Go, let me explain why I was writing a Go
program in the first place.&lt;/p&gt;
&lt;p&gt;Last night, I saw that &lt;a class="reference external" href="https://twitter.com/_alexanderhogue"&gt;Alex&lt;/a&gt; (a friend of mine …&lt;/p&gt;</summary><content type="html">&lt;p&gt;I’ve been meaning to learn Go for a while, and I had a nice opportunity today to
do that. But before I talk about Go, let me explain why I was writing a Go
program in the first place.&lt;/p&gt;
&lt;p&gt;Last night, I saw that &lt;a class="reference external" href="https://twitter.com/_alexanderhogue"&gt;Alex&lt;/a&gt; (a friend of mine) had written a little website
for unicode faces called &lt;a class="reference external" href="http://textfac.es/"&gt;textfac.es&lt;/a&gt;. At the moment, you need to ask him to
add new faces, but there’s a dynamic voting system: every time you click on a
face, that’s counted as a vote.&lt;/p&gt;
&lt;p&gt;Of course, with such a simple voting system, it’s pretty obvious that it’s going
to be exploitable in various ways, so I set out to explore some of them.
The first thing to do is to have a look at how clicks translate into votes in
the browser. Looking at the JavaScript, I saw (essentially) this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;faces&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;button.facebtn&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;clip&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;mousedown&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;attr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;face-id&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ajax&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;/increment?id=&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Which means that when you click it, it sends a GET request to a URL like
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;http://textfac.es/increment?id=37&lt;/span&gt;&lt;/tt&gt;.&lt;/p&gt;
&lt;div class="section" id="the-attack-i-didnt-do"&gt;
&lt;h2&gt;The attack I didn’t do&lt;/h2&gt;
&lt;p&gt;The obvious thing to do would be to run a brute-force attack to
increment the count as much as possible, like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$ &lt;span class="k"&gt;while&lt;/span&gt; true&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; wget -qO - http://textfac.es/increment?id&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;37&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&lt;/span&gt;&amp;gt;/dev/null&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;done&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Bor-ing.&lt;/p&gt;
&lt;p&gt;Not only that, it’s also very easy to block with a per-ip rate limit
(which was put in place).
I wanted to be more subtle about it, and make it more difficult to identify
whether it was spam or not.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="my-first-attack"&gt;
&lt;h2&gt;My first attack&lt;/h2&gt;
&lt;p&gt;Having voting happen by a GET request is clearly a mistake.
According to RFC2616 (Section 9.1.1), GET requests should not change any state
on the server:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;In particular, the convention has been established that the GET and
HEAD methods SHOULD NOT have the significance of taking an action
other than retrieval. These methods ought to be considered &amp;quot;safe&amp;quot;.
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;What this means is that it’s fairly easy to get a browser to send a GET request
for you, since the browser &amp;quot;knows&amp;quot; it’s a harmless request.&lt;/p&gt;
&lt;p&gt;And so, I added a little bit of HTML to the bottom of all the pages of my blog:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;img&lt;/span&gt; &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;http://textfac.es/increment?id=37&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;display:none&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Then I needed to generate some traffic, so I &lt;a class="reference external" href="2013-04/introducing-gst-launch-dynamic.html"&gt;wrote a blog post&lt;/a&gt; (that I had
been meaning to write for a while) and posted it to Google+, Twitter and
Facebook.&lt;/p&gt;
&lt;p&gt;So if you were one of the nice people who visited my blog since last night,
your browser helpfully loaded that &amp;quot;image&amp;quot; with a GET request, thus casting a
vote using your IP. Why thank you, random stranger!&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="my-second-attack-with-go"&gt;
&lt;h2&gt;My second attack, with Go!&lt;/h2&gt;
&lt;p&gt;Great, this generated some traffic, and I was sitting at #5 for a while.
But I wanted to get higher up, and so I had the thought: you know, it would be
really nice if I could use Google’s network to spam the voting system for me…&lt;/p&gt;
&lt;p&gt;I knew that it was possible to make outbound web requests with AppEngine, and
I’d been meaning to write something in Go for a while, so this seemed like a
good excuse.&lt;/p&gt;
&lt;p&gt;I just needed to write a simple application to take a url and a number, and send
that many GET requests to the url. &lt;a class="reference external" href="http://hg.flowblok.id.au/textfaces-hackity/src"&gt;Easy, peasy.&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;So I wrote that, and then did some testing, and discovered that there was still
only a fairly limited number of IPs which it would generate traffic from, though
I could vary the IPs by changing where my request to AppEngine came from.&lt;/p&gt;
&lt;p&gt;My final variation was to hard-code the url and number, and to use that in an
&lt;tt class="docutils literal"&gt;img&lt;/tt&gt; tag on my blog, so that the requests to AppEngine would be from a
diverse IP range. I have no idea if that’s actually working or not!&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="finally-he-gets-onto-actually-talking-about-go"&gt;
&lt;h2&gt;Finally he gets onto actually talking about Go…&lt;/h2&gt;
&lt;p&gt;I only ran into two interesting things with Go. The first one was when I was
doing authentication: I wanted to restrict it to a set of valid logins, which
basically meant testing to see if a list contained a certain item.&lt;/p&gt;
&lt;p&gt;I was very surprised that Go doesn’t have a builtin function for this, I don’t
really expect to have to write linear search in 2013.
I did find &lt;a class="reference external" href="http://play.golang.org/p/Wb4oB3OckI"&gt;someone’s recipe for this&lt;/a&gt; on the golang-nuts mailing list, which I
shamelessly stole^Wappropriated.&lt;/p&gt;
&lt;p&gt;On this topic, it’s quite possible that the Go language does has this built in,
and I just didn’t find it: while the documentation does appear to be complete,
I didn’t find it anywhere near as readable or searchable as the Python
documentation (hmmm, I think Python spoils me).&lt;/p&gt;
&lt;p&gt;The other thing which I found interesting was that Go doesn’t have a &lt;tt class="docutils literal"&gt;map()&lt;/tt&gt;
primitive using goroutines. While it’s not overly difficult to express the same
thing with channels, I felt that my code would have looked cleaner.&lt;/p&gt;
&lt;p&gt;Here’s a contrived example using explicit goroutines and channels:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nx"&gt;square&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ch&lt;/span&gt; &lt;span class="kd"&gt;chan&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;ch&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;n&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;

    &lt;span class="nx"&gt;ch&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;chan&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="nx"&gt;square&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ch&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nx"&gt;sum&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;sum&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="nx"&gt;ch&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And here’s what the equivalent code would look like with a concurrent map
function:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nx"&gt;square&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;values&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nx"&gt;results&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nx"&gt;go_map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;values&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;square&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nx"&gt;sum&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="nx"&gt;results&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;sum&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Of course, I could have left that last example as pseudocode, but I didn’t like
that, so I wrote out an implementation of &lt;tt class="docutils literal"&gt;go_map&lt;/tt&gt;. Unfortunately, my go-fu
isn’t at the point where I can write a generic version for any type.
Perhaps someone will write one, and leave a note in the comments.
Even better, perhaps someone will point out that this already exists in the
standard library somewhere?&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nx"&gt;go_map_wrap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fn&lt;/span&gt; &lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ch&lt;/span&gt; &lt;span class="kd"&gt;chan&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;ch&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="nx"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nx"&gt;go_map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;values&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fn&lt;/span&gt; &lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;results&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;values&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="nx"&gt;ch&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;chan&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="nx"&gt;values&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="nx"&gt;go_map_wrap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ch&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;values&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="nx"&gt;ch&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;results&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="epilogue"&gt;
&lt;h2&gt;Epilogue&lt;/h2&gt;
&lt;p&gt;By the way, that last complaint wasn’t because I’m a functional programming nut
or anything, it’s because I wanted to send off N concurrent web requests, and it
seemed like a pain to have to manually manage the channel.&lt;/p&gt;
&lt;p&gt;On the whole I do like Go, and in my book it’s up with Vala and Scheme as nice
languages. Python is still vastly more usable, but it’s likely a language which
I’ll use again in the future.&lt;/p&gt;
&lt;p&gt;P.S: Alex is not an awful developer!
He did realise the hole when he initially coded it, and just didn’t get around
to fixing it until I started messing around.
So don’t blame him, he’s awesome.&lt;/p&gt;
&lt;/div&gt;
</content><category term="random"></category></entry><entry><title>Introducing gst-launch-dynamic</title><link href="2013-04/introducing-gst-launch-dynamic.html" rel="alternate"></link><published>2013-04-05T23:15:00+11:00</published><updated>2013-04-05T23:15:00+11:00</updated><author><name>Peter Ward</name></author><id>tag:None,2013-04-05:2013-04/introducing-gst-launch-dynamic.html</id><summary type="html">&lt;p&gt;I thought it would probably be a good idea to write posts about the random
projects I do. As it happens, this particular post is about a tool which still
could do with more work, but is in a reasonable enough state to present to the
world. I’ve got …&lt;/p&gt;</summary><content type="html">&lt;p&gt;I thought it would probably be a good idea to write posts about the random
projects I do. As it happens, this particular post is about a tool which still
could do with more work, but is in a reasonable enough state to present to the
world. I’ve got another tool which I’ll write a post about after doing a little
clean up.&lt;/p&gt;
&lt;p&gt;So, on to the entertainment.
GStreamer is a multimedia framework, which lets you build applications which do
all kinds of strange and wonderful things with multimedia (audio / video /
anything you can stream).&lt;/p&gt;
&lt;p&gt;I like to think of it as UNIX pipelines for multimedia:
a GStreamer pipeline is built out of many elements (processes),
and each element has a number of pads (stdin / stdout / stderr / other fds)
which can be joined to other elements.
Indeed, there’s even a nice tool called &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;gst-launch&lt;/span&gt;&lt;/tt&gt; which lets you express
the pipeline in a UNIX-like syntax— in fact, it’s better, since it lets you
connect any element to any other element, which is not possible (as far as I can
tell) in the POSIX shell language.&lt;/p&gt;
&lt;div class="section" id="what-is-it"&gt;
&lt;h2&gt;What is it?&lt;/h2&gt;
&lt;p&gt;Great, so that’s what GStreamer is, what does my tool do?
Well, it’s almost the same as &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;gst-launch&lt;/span&gt;&lt;/tt&gt;, except for one little thing:
&lt;em&gt;it lets you modify the pipeline at runtime&lt;/em&gt;.
I think this is both amazing and slightly insane: it greatly lowers the barrier
for ad-hoc experimentation with pipelines, and allows for easy scripting of
pipelines.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="how-does-it-work"&gt;
&lt;h2&gt;How does it work?&lt;/h2&gt;
&lt;p&gt;It’s very simple. To run it, call it in the same way you would normally call
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;gst-launch&lt;/span&gt;&lt;/tt&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$ gst-launch-dynamic videotestsrc ! autovideosink
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;It will construct the pipeline, and start playing it, in this case showing the
&lt;a class="reference external" href="https://en.wikipedia.org/wiki/SMPTE_color_bars"&gt;SMPTE colour bars&lt;/a&gt;. But, to let you change the pipeline, it also watches
standard input for commands, so we can type this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;videotestsrc0.pattern = 1
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This sets the &lt;tt class="docutils literal"&gt;pattern&lt;/tt&gt; property on the &lt;tt class="docutils literal"&gt;videotestsrc0&lt;/tt&gt; element
(since we didn’t give it a name, it gets automatically numbered) to 1,
which corresponds to &amp;quot;snow&amp;quot; (i.e., black &amp;amp; white noise).
As soon as you press enter, the property is set, and the video displayed on your
screen will change.&lt;/p&gt;
&lt;p&gt;It’s probably worth noting a bug (well, more of an inconsistency) here: the
thing on the right hand side of the equals sign is interpreted as a Python
object (using &lt;tt class="docutils literal"&gt;ast.literal_eval&lt;/tt&gt;), which isn’t exactly the same syntax that
the command-line arguments (and &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;gst-launch&lt;/span&gt;&lt;/tt&gt;) uses.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="other-commands"&gt;
&lt;h2&gt;Other commands&lt;/h2&gt;
&lt;p&gt;Aside from changing properties, you can control the state of the pipeline with
these commands:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;play
pause
stop
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You can rewire the pipeline by adding, removing, linking and unlinking elements.
This is a little unstable at the moment, since it’s just translating commands
into GStreamer API calls without any sanity checking.
It is possible to replace an element with another at runtime, but you need to be
careful about unlinking and relinking the pads.&lt;/p&gt;
&lt;p&gt;Link an element to another: &lt;tt class="docutils literal"&gt;element.sink &lt;span class="pre"&gt;-&amp;gt;&lt;/span&gt; element.src&lt;/tt&gt;
(in this case, both pad names are optional)&lt;/p&gt;
&lt;p&gt;Unlink an element from another: &lt;tt class="docutils literal"&gt;element.sink x&amp;gt; element.src&lt;/tt&gt;&lt;/p&gt;
&lt;p&gt;Add an element: &lt;tt class="docutils literal"&gt;+ type key=value …&lt;/tt&gt;&lt;/p&gt;
&lt;p&gt;Remove an element: &lt;tt class="docutils literal"&gt;- element&lt;/tt&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="scripting"&gt;
&lt;h2&gt;Scripting&lt;/h2&gt;
&lt;p&gt;One immediate possibility is to write a program which sends these commands.
A side note: during development, FIFO files are great for keeping the pipeline
open while you modify your script. For example:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$ mkfifo commands.fifo
$ gst-launch-dynamic … &amp;lt; commands.fifo
$ ./my-random-script &amp;gt;&amp;gt; commands.fifo
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;As an example of how this can be completely useless, but &lt;em&gt;very entertaining&lt;/em&gt;,
here’s how to make any video psychedelic.&lt;/p&gt;
&lt;p&gt;&lt;tt class="docutils literal"&gt;psychedelic.py&lt;/tt&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;time&lt;/span&gt;

&lt;span class="n"&gt;STEP&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.01&lt;/span&gt;
&lt;span class="n"&gt;DELAY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.01&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;drange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stop&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;step&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;stop&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;
        &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;step&lt;/span&gt;

&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;drange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;STEP&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;vb.hue =&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;
        &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DELAY&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Usage:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$ python -u psychedelic.py &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  gst-launch-dynamic filesrc &lt;span class="nv"&gt;location&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;gitannex.ogv ! &lt;span class="se"&gt;\&lt;/span&gt;
    decodebin2 ! videobalance &lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;vb ! autovideosink
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="finale"&gt;
&lt;h2&gt;Finale&lt;/h2&gt;
&lt;p&gt;After I wrote this tool, I realised that nothing I had built so far really
utilised its potential, so I really hope that someone will find this and build
something cool with it.&lt;/p&gt;
&lt;p&gt;Of course, you can’t do that if I don’t point you at &lt;a class="reference external" href="http://hg.flowblok.id.au/gst-launch-dynamic"&gt;the code&lt;/a&gt;!&lt;/p&gt;
&lt;/div&gt;
</content><category term="software"></category></entry><entry><title>Blackmagic DeckLink, ConsoleKit and TTYs</title><link href="2013-02/blackmagic-decklink-consolekit-and-ttys.html" rel="alternate"></link><published>2013-02-19T07:11:00+11:00</published><updated>2013-02-19T07:11:00+11:00</updated><author><name>Peter Ward</name></author><id>tag:None,2013-02-19:2013-02/blackmagic-decklink-consolekit-and-ttys.html</id><summary type="html">&lt;p&gt;Regular readers may skip this blog post, it’s being posted so people find the
solution when the google it, not because it’s interesting.&lt;/p&gt;
&lt;p&gt;I had a computer with a Blackmagic capture card, and discovered that it wouldn’t
shut down properly.
After tracing my way through PolicyKit, I …&lt;/p&gt;</summary><content type="html">&lt;p&gt;Regular readers may skip this blog post, it’s being posted so people find the
solution when the google it, not because it’s interesting.&lt;/p&gt;
&lt;p&gt;I had a computer with a Blackmagic capture card, and discovered that it wouldn’t
shut down properly.
After tracing my way through PolicyKit, I discovered that it thought that my
login session wasn’t local, but was a remote session.&lt;/p&gt;
&lt;p&gt;Looking at the output of &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;ck-list-sessions&lt;/span&gt;&lt;/tt&gt;, ConsoleKit didn’t have a
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;x11-display-device&lt;/span&gt;&lt;/tt&gt; entry for my session
(it’s normally something like &lt;tt class="docutils literal"&gt;/dev/tty7&lt;/tt&gt;).
Confusingly though, it did have an &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;x11-display&lt;/span&gt;&lt;/tt&gt; (which was &lt;tt class="docutils literal"&gt;:0&lt;/tt&gt; as expected).&lt;/p&gt;
&lt;p&gt;ConsoleKit determines the x11-display-device using a helper program, which on my
system is located at &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;/usr/lib/ConsoleKit/ck-get-x11-display-device&lt;/span&gt;&lt;/tt&gt;.&lt;/p&gt;
&lt;p&gt;This helper program parses &lt;tt class="docutils literal"&gt;/proc/tty/drivers&lt;/tt&gt;, into a struct in
&lt;a class="reference external" href="http://cgit.freedesktop.org/ConsoleKit/tree/src/ck-sysdeps-linux.c?id=b8a961f91105df661957f6b86922f744bac8b91c#n91"&gt;src/ck-sysdeps-linux.c:92&lt;/a&gt;.
I discovered the name in &lt;tt class="docutils literal"&gt;/proc/tty/drivers&lt;/tt&gt; was more than 16 characters, and
hence overflowed the &lt;tt class="docutils literal"&gt;name&lt;/tt&gt; array, causing the program to segfault.&lt;/p&gt;
&lt;p&gt;Since ConsoleKit’s project page indicates that there’s no longer any active
development on it, I decided on the hacky solution to solve my problem, rather
than fixing the actual problem and bumped the size of the name array up by a
couple of bytes.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="gh"&gt;Index: consolekit-0.4.5/src/ck-sysdeps-linux.c&lt;/span&gt;
&lt;span class="gh"&gt;===================================================================&lt;/span&gt;
&lt;span class="gd"&gt;--- consolekit-0.4.5.orig/src/ck-sysdeps-linux.c    2012-07-14 14:08:02.298395776 +1000&lt;/span&gt;
&lt;span class="gi"&gt;+++ consolekit-0.4.5/src/ck-sysdeps-linux.c 2012-07-14 14:11:07.282389984 +1000&lt;/span&gt;
&lt;span class="gu"&gt;@@ -93,7 +93,7 @@&lt;/span&gt;
         guint major_number;
         guint minor_first;
         guint minor_last;
&lt;span class="gd"&gt;-        char name[16];&lt;/span&gt;
&lt;span class="gi"&gt;+        char name[24];&lt;/span&gt;
         char devfs_type;
 } tty_map_node;
&lt;/pre&gt;&lt;/div&gt;
</content><category term="software"></category></entry><entry><title>Shell startup scripts</title><link href="2013-02/shell-startup-scripts.html" rel="alternate"></link><published>2013-02-17T05:50:00+11:00</published><updated>2013-02-17T05:50:00+11:00</updated><author><name>Peter Ward</name></author><id>tag:None,2013-02-17:2013-02/shell-startup-scripts.html</id><summary type="html">&lt;p&gt;If you’re a regular shell user, you’ve almost certainly got a
&lt;tt class="docutils literal"&gt;.bash_profile&lt;/tt&gt; or &lt;tt class="docutils literal"&gt;.bashrc&lt;/tt&gt; script in your home folder,
which usually contains various tweaks, such as setting environment variables
(adding that directory to &lt;tt class="docutils literal"&gt;$PATH&lt;/tt&gt;), telling your shell to do clever things
(like &lt;tt class="docutils literal"&gt;set &lt;span class="pre"&gt;-o&lt;/span&gt; noclobber&lt;/tt&gt;) and adding various …&lt;/p&gt;</summary><content type="html">&lt;p&gt;If you’re a regular shell user, you’ve almost certainly got a
&lt;tt class="docutils literal"&gt;.bash_profile&lt;/tt&gt; or &lt;tt class="docutils literal"&gt;.bashrc&lt;/tt&gt; script in your home folder,
which usually contains various tweaks, such as setting environment variables
(adding that directory to &lt;tt class="docutils literal"&gt;$PATH&lt;/tt&gt;), telling your shell to do clever things
(like &lt;tt class="docutils literal"&gt;set &lt;span class="pre"&gt;-o&lt;/span&gt; noclobber&lt;/tt&gt;) and adding various aliases to commands
(like &lt;tt class="docutils literal"&gt;alias please=sudo&lt;/tt&gt;).&lt;/p&gt;
&lt;p&gt;(If you’re really organised, you’ll have all your dotfiles in a repository
somewhere so that you can keep your settings synchronised across all the
machines you work on.)&lt;/p&gt;
&lt;p&gt;Anyhow, I suspect that few people know when things like &lt;tt class="docutils literal"&gt;.bash_profile&lt;/tt&gt; and
&lt;tt class="docutils literal"&gt;.bashrc&lt;/tt&gt; actually get executed. When I started, I just followed people’s
advice of putting stuff in &lt;tt class="docutils literal"&gt;.bashrc&lt;/tt&gt;, and then when it didn’t work, into
&lt;tt class="docutils literal"&gt;.bash_profile&lt;/tt&gt;.
I could stop here and describe just the bash startup process (as silly as it
is), but there’s a complication in that I switched to zsh a few years ago
(and haven’t looked back), but occasionally use bash on machines which don’t
have zsh installed.&lt;/p&gt;
&lt;p&gt;In order to handle this nicely then, I need to be able to specify things which
are specific to bash or zsh in their own files, and then to specify things
which any POSIX-compliant shell (like aliases and environment variables) can
understand in a common startup file.&lt;/p&gt;
&lt;p&gt;My solution to this problem is to define some new dotfile folders,
one for each shell (&lt;tt class="docutils literal"&gt;.bash/&lt;/tt&gt;, &lt;tt class="docutils literal"&gt;.zsh/&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;.sh/&lt;/tt&gt;), and one for the
shell-independent files (&lt;tt class="docutils literal"&gt;.shell/&lt;/tt&gt;):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;.bash/
    env
    interactive
    login
    logout
.sh/
    env
    interactive
    login
.shell/
    env
    interactive
    login
    logout
.zsh/
    env
    interactive
    login
    logout
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;“But!”, you say, &lt;a class="footnote-reference" href="#since" id="id1"&gt;[1]&lt;/a&gt; “what do the different files in here do?”
Ah, well I’m glad you asked.
There are two kinds of shells:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;[non-]interactive shells (you type into them / shell scripts)&lt;/li&gt;
&lt;li&gt;[non-]login shells (the shell run when you first login / subshells)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;All shells will first run &lt;tt class="docutils literal"&gt;env&lt;/tt&gt;, then login shells will run &lt;tt class="docutils literal"&gt;login&lt;/tt&gt;,
then interactive shells will run &lt;tt class="docutils literal"&gt;interactive&lt;/tt&gt;.
Once finished, login shells will run &lt;tt class="docutils literal"&gt;logout&lt;/tt&gt;.&lt;/p&gt;
&lt;div class="section" id="where-to-put-stuff"&gt;
&lt;h2&gt;Where to put stuff&lt;/h2&gt;
&lt;p&gt;It all depends on when it needs to be run.&lt;/p&gt;
&lt;p&gt;If it’s setting / modifying environment variables, it should go in &lt;tt class="docutils literal"&gt;login&lt;/tt&gt;.
If it’s alias or a terminal-specific environment variable (e.g., GREP_COLOR),
it should go in &lt;tt class="docutils literal"&gt;interactive&lt;/tt&gt;.
In my &lt;tt class="docutils literal"&gt;.shell/env&lt;/tt&gt; file, I have my umask set, and also define some useful
functions for modifying colon-separated path environment variables (like
$PATH).&lt;/p&gt;
&lt;p&gt;Even if you don’t adopt anything else from my scheme, I’d recommend you take a
look at the what my functions are doing which differs from something like
&lt;tt class="docutils literal"&gt;export &lt;span class="pre"&gt;PATH=$PATH:/path/to/dir&lt;/span&gt;&lt;/tt&gt;.&lt;/p&gt;
&lt;p&gt;That particular pattern is way too common, and is very dangerous if you
consider the case when &lt;tt class="docutils literal"&gt;$PATH&lt;/tt&gt; (or whatever your variable is, like
&lt;tt class="docutils literal"&gt;$LD_LIBRARY_PATH&lt;/tt&gt;) isn’t set.
Then, the value will be &lt;tt class="docutils literal"&gt;:/path/to/dir&lt;/tt&gt;, which usually means both
&lt;tt class="docutils literal"&gt;/path/to/dir&lt;/tt&gt; &lt;em&gt;and the current directory&lt;/em&gt;, which is usually both unexpected
behaviour and a security concern.&lt;/p&gt;
&lt;p&gt;With my implementation (see &lt;tt class="docutils literal"&gt;.shell/env_functions&lt;/tt&gt;), you can append, prepend
and remove directories from any colon-separated environment variable, and when
appending or prepending, you are guaranteed that directory will only then
appear in that variable once.&lt;/p&gt;
&lt;p&gt;As a side note, I’m very disappointed in my &lt;tt class="docutils literal"&gt;indirect_expand()&lt;/tt&gt; function,
so if you have a better solution, please let me know (or send me a pull request).&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="implementation"&gt;
&lt;h2&gt;Implementation&lt;/h2&gt;
&lt;p&gt;In order to implement this, you first need an understanding of which startup
files are run in each case.
Of course, this isn’t hard, since all the shells have the same, sensible
system. Ahahaha, no.&lt;/p&gt;
&lt;p&gt;Fortunately, I’ve read the man pages for you, and drawn a pretty diagram.
To read it, pick your shell, whether it's a login shell, whether it's
interactive, and follow the same colour through the diagram.
When the arrows split out to multiple files, it means that the shell will try
to read each one in turn (working left to right), and will use the first one it
can read.&lt;/p&gt;
&lt;img alt="" src="static/images/shell-startup.png" /&gt;
&lt;p&gt;An important point here is that if you have a non-login, non-interactive shell
running a plain POSIX shell (dash, bash in sh compatibility mode), it &lt;em&gt;won’t
fulfil&lt;/em&gt; our contract in that it won’t read &lt;tt class="docutils literal"&gt;.sh/env&lt;/tt&gt;
(and hence &lt;tt class="docutils literal"&gt;.shell/env&lt;/tt&gt;).&lt;/p&gt;
&lt;p&gt;The other special case which exists is Bash’s remote shell mode (which is
rather warped), where it tries to detect if it’s running under ssh or rsh (I
assume by looking at the process name of its parent), and if so, follows a
different path, which is indicated on the diagram.&lt;/p&gt;
&lt;p&gt;Except that diagram shows what happens according to the man page, and not what
happens when you actually try it out in real life.
This second diagram more accurately captures the insanity of bash:&lt;/p&gt;
&lt;img alt="" src="static/images/shell-startup-actual.png" /&gt;
&lt;p&gt;See how remote interactive login shells read /etc/bash.bashrc, but normal
interactive login shells don’t? Sigh.&lt;/p&gt;
&lt;p&gt;Finally, &lt;a class="reference external" href="http://hg.flowblok.id.au/shell-startup"&gt;here’s a repository&lt;/a&gt; containing my implementation and the graphviz
files for the above diagram.
If your POSIX-compliant shell isn’t listed here, or if I’ve made a horrible
mistake (or just a tiny one), please send me a pull request or make a comment
below, and I’ll update this post accordingly.&lt;/p&gt;
&lt;table class="docutils footnote" frame="void" id="since" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#id1"&gt;[1]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;and since I’m writing this, I can make you say whatever I want for the purposes of narrative.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
</content><category term="software"></category></entry><entry><title>PSA: Hotmail is Borken</title><link href="2013-02/psa-hotmail-is-borken.html" rel="alternate"></link><published>2013-02-09T15:49:00+11:00</published><updated>2013-02-09T15:49:00+11:00</updated><author><name>Peter Ward</name></author><id>tag:None,2013-02-09:2013-02/psa-hotmail-is-borken.html</id><summary type="html">&lt;p&gt;I’ve just debugged an issue where someone was trying to send an email to my
domain, and got back an error message, which they took to mean that the email
address no longer existed.&lt;/p&gt;
&lt;p&gt;Of the very little debugging information I managed to get out, was this snippet
(some …&lt;/p&gt;</summary><content type="html">&lt;p&gt;I’ve just debugged an issue where someone was trying to send an email to my
domain, and got back an error message, which they took to mean that the email
address no longer existed.&lt;/p&gt;
&lt;p&gt;Of the very little debugging information I managed to get out, was this snippet
(some information anonymised):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;Reporting-MTA: dns;blu0-omc2-s22.blu0.hotmail.com
Received-From-MTA: dns;BLU168-W101
Arrival-Date: Fri, 8 Feb 2013 19:04:02 -0800

Final-Recipient: rfc822;bob@example.net
Action: failed
Status: 5.5.0
Diagnostic-Code: smtp;550 relay not permitted
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If you search for &amp;quot;hotmail 550 relay not permitted&amp;quot;, you will find &lt;a class="reference external" href="http://productforums.google.com/d/msg/apps/IgNq3qeh1J8/Ztt4MxcVpncJ"&gt;this post&lt;/a&gt;,
which describes the almost unbelievable problem.&lt;/p&gt;
&lt;p&gt;Turns out hotmail will try to send mail to port 25 of the hosts configured as
your A records first, and if it manages to make a connection, will completely
ignore your MX records.&lt;/p&gt;
&lt;p&gt;What.&lt;/p&gt;
&lt;p&gt;I checked the mail server logs on my web server (I do run a mail server there,
for other reasons unrelated to that domain), and discovered, yes, there was an
attempted mail delivery:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;2013-02-09 14:04:26 H=blu0-omc2-s22.blu0.hotmail.com [65.55.111.97] F=&amp;lt;someone@hotmail.com&amp;gt; rejected RCPT &amp;lt;bob@example.net&amp;gt;: relay not permitted
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;How anyone could think this was a good idea defies belief.
I think I’m still in shock.&lt;/p&gt;
</content><category term="random"></category></entry><entry><title>Controlling Projectors with PJLink</title><link href="2012-11/controlling-projectors-with-pjlink.html" rel="alternate"></link><published>2012-11-30T22:41:00+11:00</published><updated>2012-11-30T22:41:00+11:00</updated><author><name>Peter Ward</name></author><id>tag:None,2012-11-30:2012-11/controlling-projectors-with-pjlink.html</id><summary type="html">&lt;p&gt;I regularly need to control a data projector, and previously the only way to do
this has been using a web interface created by the manufacturer.
Of course the interface is, while usable, poorly designed, and somewhat
sluggish.&lt;/p&gt;
&lt;p&gt;Fortunately, there’s a protocol for controlling projectors, &lt;a class="reference external" href="http://pjlink.jbmia.or.jp/english/"&gt;PJLink&lt;/a&gt;,
but unfortunately, there …&lt;/p&gt;</summary><content type="html">&lt;p&gt;I regularly need to control a data projector, and previously the only way to do
this has been using a web interface created by the manufacturer.
Of course the interface is, while usable, poorly designed, and somewhat
sluggish.&lt;/p&gt;
&lt;p&gt;Fortunately, there’s a protocol for controlling projectors, &lt;a class="reference external" href="http://pjlink.jbmia.or.jp/english/"&gt;PJLink&lt;/a&gt;,
but unfortunately, there aren’t many implementations
(I did find a &lt;a class="reference external" href="http://search.cpan.org/dist/Net-PJLink/lib/Net/PJLink.pm"&gt;perl module&lt;/a&gt; in my search),
so I decided to write my own implementation in Python.&lt;/p&gt;
&lt;p&gt;&lt;a class="reference external" href="http://hg.flowblok.id.au/pjlink"&gt;So I did&lt;/a&gt;, and it took me just a single morning, which I was quite impressed
with. The specification is reasonable simple, and aside from one or two stupid
mistakes, implementing it was straight forward.
In addition to the Python API, I also wrote a simple command line utility for
communicating with the projector, which is probably more useful than the API
(since you can throw it into your own shell scripts).&lt;/p&gt;
&lt;p&gt;If you want to try it out, you can install it from &lt;a class="reference external" href="http://pypi.python.org/pypi/pjlink"&gt;PyPI&lt;/a&gt; using pip.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$ pip install --user pjlink
&lt;span class="c1"&gt;# or if you&amp;#39;re in a virtualenv&lt;/span&gt;
$ pip install pjlink
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The location of projectors can be specified in ~/.config/pjlink/pjlink.conf
(on Linux, see &lt;a class="reference external" href="http://pypi.python.org/pypi/appdirs"&gt;appdirs&lt;/a&gt; for your platform):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;[default]&lt;/span&gt;
&lt;span class="na"&gt;host&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;myprojector&lt;/span&gt;
&lt;span class="na"&gt;port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;4212&lt;/span&gt;
&lt;span class="na"&gt;password&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;JBMIAProjectorLink&lt;/span&gt;

&lt;span class="k"&gt;[other]&lt;/span&gt;
&lt;span class="na"&gt;host&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;bob&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Note this stores your passwords in plain text: this is an indication of what I
think of the authentication protocol which PJLink uses. I couldn’t find a
specific vulnerability, but I still wouldn’t trust it.&lt;/p&gt;
&lt;p&gt;Then, you can use -p to reference the projector in the config file.
You can also just specify a host and port manually (but not a password).&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# get power state of myprojector:4212 (using password)&lt;/span&gt;
$ pjlink -p default power
&lt;span class="c1"&gt;# get power state of bob:4352 (default port, no password)&lt;/span&gt;
$ pjlink -p other power

&lt;span class="c1"&gt;# for the &amp;quot;default&amp;quot; projector, you can omit -p&lt;/span&gt;
&lt;span class="c1"&gt;# this is the same as the first command&lt;/span&gt;
$ pjlink power

&lt;span class="c1"&gt;# get available inputs&lt;/span&gt;
$ pjlink inputs

&lt;span class="c1"&gt;# get current input&lt;/span&gt;
$ pjlink input

&lt;span class="c1"&gt;# switch to input RGB-1&lt;/span&gt;
$ pjlink input RGB &lt;span class="m"&gt;1&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And there are many more commands available: everything in the specification is
implemented as plainly as possible, so if the projector can do it, my tool
supports it.&lt;/p&gt;
&lt;p&gt;And for the record, I disclaim any responsibility for inappropriate use of this
tool. (because I can think of a few!)&lt;/p&gt;
</content><category term="software"></category></entry><entry><title>Awesome + GNOME Configuration</title><link href="2012-11/awesome-gnome-configuration.html" rel="alternate"></link><published>2012-11-28T10:31:00+11:00</published><updated>2012-11-28T10:31:00+11:00</updated><author><name>Peter Ward</name></author><id>tag:None,2012-11-28:2012-11/awesome-gnome-configuration.html</id><summary type="html">&lt;p&gt;I was recently prompted to publish my configuration for integrating GNOME and
awesome. At the time I wrote this setup, the awesome wiki was recommending a
configuration which didn’t correctly autostart applications,
nor integrate well with anything expecting to find a session manager.
However, it now contains &lt;a class="reference external" href="http://awesome.naquadah.org/wiki/Quickly_Setting_up_Awesome_with_Gnome#Configuration:_3.0_.3C.3D_gnome_.3C_3.4"&gt;instructions for …&lt;/a&gt;&lt;/p&gt;</summary><content type="html">&lt;p&gt;I was recently prompted to publish my configuration for integrating GNOME and
awesome. At the time I wrote this setup, the awesome wiki was recommending a
configuration which didn’t correctly autostart applications,
nor integrate well with anything expecting to find a session manager.
However, it now contains &lt;a class="reference external" href="http://awesome.naquadah.org/wiki/Quickly_Setting_up_Awesome_with_Gnome#Configuration:_3.0_.3C.3D_gnome_.3C_3.4"&gt;instructions for a similar setup&lt;/a&gt;, so you should have a
look at that too.&lt;/p&gt;
&lt;p&gt;The first thing you need is an entry to tell your display manager about a new
session type, so put this in &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;/usr/share/xsessions/awesome-gnome.desktop&lt;/span&gt;&lt;/tt&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;[Desktop Entry]&lt;/span&gt;
&lt;span class="na"&gt;Name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;awesome-gnome&lt;/span&gt;
&lt;span class="na"&gt;Comment&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;This session logs you into GNOME using the Awesome WM&lt;/span&gt;
&lt;span class="na"&gt;Exec&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;gnome-session --session=awesome-gnome&lt;/span&gt;
&lt;span class="na"&gt;TryExec&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;gnome-session&lt;/span&gt;
&lt;span class="na"&gt;Icon&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;
&lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;Application&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Then you need to tell gnome-session about the &amp;quot;awesome-gnome&amp;quot; session.
You might want to modify the notification daemon here.
Put this in &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;~/.config/gnome-session/sessions/awesome-gnome.session&lt;/span&gt;&lt;/tt&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;[GNOME Session]&lt;/span&gt;
&lt;span class="na"&gt;Name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;awesome-gnome&lt;/span&gt;
&lt;span class="na"&gt;RequiredComponents&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;gnome-settings-daemon;&lt;/span&gt;
&lt;span class="na"&gt;RequiredProviders&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;windowmanager;&lt;/span&gt;
&lt;span class="na"&gt;DefaultProvider-windowmanager&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;awesome&lt;/span&gt;
&lt;span class="na"&gt;DefaultProvider-notifications&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;notification-daemon&lt;/span&gt;
&lt;span class="na"&gt;FallbackSession&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;gnome-fallback&lt;/span&gt;
&lt;span class="na"&gt;DesktopName&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;GNOME&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Then, GNOME expects to find a XDG application entry for &amp;quot;awesome&amp;quot;,
so put this in &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;~/.local/share/applications/awesome.desktop&lt;/span&gt;&lt;/tt&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;[Desktop Entry]&lt;/span&gt;
&lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;Application&lt;/span&gt;
&lt;span class="na"&gt;Name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;Awesome&lt;/span&gt;
&lt;span class="na"&gt;Comment&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;Window manager&lt;/span&gt;
&lt;span class="na"&gt;Exec&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;sh -c &amp;#39;/usr/bin/awesome &amp;amp;&amp;#39;&lt;/span&gt;
&lt;span class="na"&gt;NoDisplay&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;true&lt;/span&gt;
&lt;span class="na"&gt;X-GNOME-WMName&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;awesome&lt;/span&gt;
&lt;span class="na"&gt;X-GNOME-Autostart-Phase&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;WindowManager&lt;/span&gt;
&lt;span class="na"&gt;X-GNOME-Provides&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;windowmanager&lt;/span&gt;
&lt;span class="na"&gt;X-GNOME-Autostart-Notify&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;true&lt;/span&gt;
&lt;span class="na"&gt;X-GNOME-AutoRestart&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;false&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Then you should be able to logout, and immediately see the &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;awesome-gnome&lt;/span&gt;&lt;/tt&gt;
session which you can log into.
Your display manager usually remembers the last session you logged into,
but using this method, you can easily switch between GNOME shell,
GNOME fallback and awesome-gnome.&lt;/p&gt;
</content><category term="software"></category></entry><entry><title>Prettify script for PC^2</title><link href="2012-11/prettify-script-for-pc2.html" rel="alternate"></link><published>2012-11-17T17:23:00+11:00</published><updated>2012-11-17T17:23:00+11:00</updated><author><name>Peter Ward</name></author><id>tag:None,2012-11-17:2012-11/prettify-script-for-pc2.html</id><summary type="html">&lt;p&gt;When I was doing programming competitions at uni, I wrote this script, which
takes the default HTML output from PC^2 and add some colour to it to indicate
passed / failed problems. Since it was written / enhanced on the day of the
competition, there’s definitely some hacky code in …&lt;/p&gt;</summary><content type="html">&lt;p&gt;When I was doing programming competitions at uni, I wrote this script, which
takes the default HTML output from PC^2 and add some colour to it to indicate
passed / failed problems. Since it was written / enhanced on the day of the
competition, there’s definitely some hacky code in there, but I’m publishing it
so future generations can steal it for their own nefarious purposes.&lt;/p&gt;
&lt;p&gt;This code is released into the &lt;a class="reference external" href="http://creativecommons.org/licenses/CC0/1.0/"&gt;public domain&lt;/a&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# prettify.py&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;re&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;string&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;sys&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;lxml&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;etree&lt;/span&gt;

&lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;etree&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;prettify.xsl&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;rU&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;xslt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;etree&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;XSLT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stdin&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;passed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;compile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;^[0-9]*/[1-9][0-9]*$&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;failed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;compile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;^.*[1-9]/[-0]*$&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;xpath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;//td&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;&amp;#39;&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;passed&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;attrib&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;class&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;passed&amp;#39;&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;failed&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;attrib&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;class&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;failed&amp;#39;&lt;/span&gt;

&lt;span class="n"&gt;problems&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;zip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;uppercase&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s1"&gt;&amp;#39;An Industrial Spy&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;&amp;#39;Simple Polygon&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;&amp;#39;High Score&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;&amp;#39;Rankings&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;&amp;#39;Wormholes&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;&amp;#39;Fractal&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;&amp;#39;Rewards for Math!&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;&amp;#39;Divisible Subsequences&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;]))&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;xpath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;//th/strong/u&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;problems&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;xslt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;#body = doc.xpath(&amp;#39;//html/body&amp;#39;)&lt;/span&gt;
&lt;span class="c1"&gt;#d = etree.Element(&amp;#39;div&amp;#39;)&lt;/span&gt;
&lt;span class="c1"&gt;#d.attrib[&amp;#39;class&amp;#39;] = &amp;#39;timer&amp;#39;&lt;/span&gt;
&lt;span class="c1"&gt;#&lt;/span&gt;
&lt;span class="c1"&gt;#import sys&lt;/span&gt;
&lt;span class="c1"&gt;#from datetime import datetime&lt;/span&gt;
&lt;span class="c1"&gt;#n = datetime.now()&lt;/span&gt;
&lt;span class="c1"&gt;#t = datetime(2011, 9, 3, 17, 0, 0, 0)&lt;/span&gt;
&lt;span class="c1"&gt;#print&amp;gt;&amp;gt;sys.stderr, n - t&lt;/span&gt;

&lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tostring&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c"&gt;&amp;lt;!-- prettify.xsl --&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;xsl:stylesheet&lt;/span&gt; &lt;span class="na"&gt;version=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;1.0&amp;quot;&lt;/span&gt;
    &lt;span class="na"&gt;xmlns:xsl=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;http://www.w3.org/1999/XSL/Transform&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;xsl:variable&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;stage&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;select=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;0&amp;quot;&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;xsl:output&lt;/span&gt; &lt;span class="na"&gt;method=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;xml&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;indent=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;yes&amp;quot;&lt;/span&gt;
        &lt;span class="na"&gt;doctype-system=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd&amp;quot;&lt;/span&gt;
        &lt;span class="na"&gt;doctype-public=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;-//W3C//DTD XHTML 1.0 Transitional//EN&amp;quot;&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;

    &lt;span class="c"&gt;&amp;lt;!-- Document structure --&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;xsl:template&lt;/span&gt; &lt;span class="na"&gt;match=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;/&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;html&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;xsl:apply-templates&lt;/span&gt; &lt;span class="na"&gt;select=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;/html/head&amp;quot;&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;xsl:apply-templates&lt;/span&gt; &lt;span class="na"&gt;select=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;/html/body&amp;quot;&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/xsl:template&amp;gt;&lt;/span&gt;

    &lt;span class="c"&gt;&amp;lt;!-- Head for styling, etc. --&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;xsl:template&lt;/span&gt; &lt;span class="na"&gt;match=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;/html/head&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;xsl:copy-of&lt;/span&gt; &lt;span class="na"&gt;select=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;/html/head/node()&amp;quot;&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;script&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;text/javascript&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;http://cornify.com/js/cornify.js&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
            &lt;span class="c"&gt;&amp;lt;!--&amp;lt;meta http-equiv=&amp;quot;refresh&amp;quot; content=&amp;quot;5&amp;quot; /&amp;gt;--&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;http://fonts.googleapis.com/css?family=Questrial&amp;quot;&lt;/span&gt;
                &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;stylesheet&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;text/css&amp;quot;&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;style&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;text/css&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                html, body { margin: 0; padding: 0; }
                a { color: #729fcf; }
                body {
                    font-family: &amp;#39;Questrial&amp;#39;, sans-serif;
                    color: #eeeeec;
                    background: #2e3436;
                }
                table { font-size: 20pt; }
                .passed, .failed { font-weight: bold; }
                .passed {
                    background: #8ae234;
                    color: black;
                }
                .failed {
                    background: #ef2929;
                    color: white;
                }

                h1 { text-align: center; margin: 0; }

                thead th { font-size: 12pt; }
                th.team-name {
                    font-weight: normal;
                    text-align: left;
                }

                th:nth-child(2) { width: 50px; }
                td:nth-child(4) { width: 3.5em; }
                td:nth-child(12) { width: 3.5em; }
            &lt;span class="nt"&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/xsl:template&amp;gt;&lt;/span&gt;

    &lt;span class="c"&gt;&amp;lt;!-- Body --&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;xsl:template&lt;/span&gt; &lt;span class="na"&gt;match=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;/html/body/table[1]&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;table&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;scoreboard&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;thead&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;tr&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;xsl:for-each&lt;/span&gt; &lt;span class="na"&gt;select=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;tr[1]/th&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;th&lt;/span&gt; &lt;span class="na"&gt;scope=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;col&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;xsl:value-of&lt;/span&gt; &lt;span class="na"&gt;select=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;strong/u&amp;quot;&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&amp;lt;/th&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;/xsl:for-each&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;/tr&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/thead&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;tbody&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;xsl:for-each&lt;/span&gt; &lt;span class="na"&gt;select=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;tr[position() &amp;gt; 2]&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;tr&amp;gt;&lt;/span&gt;
                        &lt;span class="c"&gt;&amp;lt;!-- Rank --&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;td&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;rank&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;xsl:value-of&lt;/span&gt; &lt;span class="na"&gt;select=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;td[1]&amp;quot;&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
                        &lt;span class="c"&gt;&amp;lt;!-- Name --&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;th&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;team-name&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;scope=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;row&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;xsl:value-of&lt;/span&gt; &lt;span class="na"&gt;select=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;td[2]&amp;quot;&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&amp;lt;/th&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;xsl:copy-of&lt;/span&gt; &lt;span class="na"&gt;select=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;td[position() &amp;gt; 2]&amp;quot;&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;/tr&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;/xsl:for-each&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/tbody&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/table&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/xsl:template&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;xsl:template&lt;/span&gt; &lt;span class="na"&gt;match=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;/html/body&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;xsl:if&lt;/span&gt; &lt;span class="na"&gt;test=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;$stage &amp;gt;= 1&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Competition is over! Congratulations to all!!!&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/xsl:if&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;xsl:apply-templates&lt;/span&gt; &lt;span class="na"&gt;select=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;table[1]&amp;quot;&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;Prettify script by Peter Ward&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;xsl:if&lt;/span&gt; &lt;span class="na"&gt;test=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;$stage &amp;gt;= 2&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;script&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;text/javascript&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="cp"&gt;&amp;lt;![CDATA[&lt;/span&gt;
&lt;span class="cp"&gt;            var count = 0;&lt;/span&gt;
&lt;span class="cp"&gt;            var delay = 10000;&lt;/span&gt;
&lt;span class="cp"&gt;            function magical() {&lt;/span&gt;
&lt;span class="cp"&gt;                cornify_add();&lt;/span&gt;
&lt;span class="cp"&gt;                count++;&lt;/span&gt;
&lt;span class="cp"&gt;                if (count &amp;gt; 60) {&lt;/span&gt;
&lt;span class="cp"&gt;                    var child = null;&lt;/span&gt;
&lt;span class="cp"&gt;                    var nodes = document.body.children;&lt;/span&gt;
&lt;span class="cp"&gt;                    for (var i = 0; i &amp;lt; nodes.length; i++) {&lt;/span&gt;
&lt;span class="cp"&gt;                        if (nodes[i].tagName == &amp;quot;DIV&amp;quot;) {&lt;/span&gt;
&lt;span class="cp"&gt;                            child = nodes[i];&lt;/span&gt;
&lt;span class="cp"&gt;                            break;&lt;/span&gt;
&lt;span class="cp"&gt;                        }&lt;/span&gt;
&lt;span class="cp"&gt;                    }&lt;/span&gt;
&lt;span class="cp"&gt;                    document.body.removeChild(child);&lt;/span&gt;
&lt;span class="cp"&gt;                }&lt;/span&gt;
&lt;span class="cp"&gt;                window.setTimeout(magical, delay * (Math.random() / 2.0 + 0.75));&lt;/span&gt;
&lt;span class="cp"&gt;                if (delay &amp;gt; 10)&lt;/span&gt;
&lt;span class="cp"&gt;                    delay *= 0.9;&lt;/span&gt;
&lt;span class="cp"&gt;            }&lt;/span&gt;
&lt;span class="cp"&gt;            window.onload = magical;&lt;/span&gt;
&lt;span class="cp"&gt;            ]]&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/xsl:if&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/xsl:template&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/xsl:stylesheet&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
</content><category term="pc2"></category><category term="progcomp"></category><category term="acm"></category><category term="icpc"></category><category term="script"></category><category term="xslt"></category><category term="university"></category></entry><entry><title>End of Honours</title><link href="2012-11/end-of-honours.html" rel="alternate"></link><published>2012-11-15T09:57:00+11:00</published><updated>2012-11-15T09:57:00+11:00</updated><author><name>Peter Ward</name></author><id>tag:None,2012-11-15:2012-11/end-of-honours.html</id><summary type="html">&lt;p&gt;Well, I’ve handed in my thesis
(Providing Useful Feedback to Beginner Programmers),
and done a presentation for it (twice, for logistical reasons),
so I’ve now finished everything I need to do for uni!
I’m very happy about this, and I’ve been enjoying the little bubble of …&lt;/p&gt;</summary><content type="html">&lt;p&gt;Well, I’ve handed in my thesis
(Providing Useful Feedback to Beginner Programmers),
and done a presentation for it (twice, for logistical reasons),
so I’ve now finished everything I need to do for uni!
I’m very happy about this, and I’ve been enjoying the little bubble of
happiness about its completion the past few days.&lt;/p&gt;
&lt;img alt="Me and my thesis by flowblok" class="well" src="static/images/me-and-my-thesis.jpg" /&gt;
&lt;p&gt;We also continued the tradition of doing a prank on my supervisor’s office,
by turning it into a fish tank / sea themed room.
Photos from the setup and the final office are in &lt;a class="reference external" href="https://photos.app.goo.gl/WecD3L8DjFX380BT2"&gt;this photo album&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;And for the few people who haven’t already caught up with this news,
I start working full-time at Google on Monday.&lt;/p&gt;
</content><category term="honours"></category><category term="google"></category><category term="prank"></category><category term="fish"></category><category term="office"></category><category term="university"></category></entry><entry><title>gvfs-gphoto2-volume-monitor Memory Usage</title><link href="2012-11/gvfs-gphoto2-volume-monitor-memory-usage.html" rel="alternate"></link><published>2012-11-11T15:25:00+11:00</published><updated>2012-11-11T15:25:00+11:00</updated><author><name>Peter Ward</name></author><id>tag:None,2012-11-11:2012-11/gvfs-gphoto2-volume-monitor-memory-usage.html</id><summary type="html">&lt;p&gt;I’ve noticed over the past few weeks that occasionally there was a
gvfs-gphoto2-volume-monitor process taking an unreasonable amount of memory.
Since my computer just ran out of memory, I was prompted to investigate why,
and here’s what I found.&lt;/p&gt;
&lt;p&gt;I’m not sure how much of this is …&lt;/p&gt;</summary><content type="html">&lt;p&gt;I’ve noticed over the past few weeks that occasionally there was a
gvfs-gphoto2-volume-monitor process taking an unreasonable amount of memory.
Since my computer just ran out of memory, I was prompted to investigate why,
and here’s what I found.&lt;/p&gt;
&lt;p&gt;I’m not sure how much of this is relevant, but I’m running the awesome window
manager with a partial GNOME desktop setup.
The particular problem seems to be that when my Samsung Galaxy S3 is plugged in
as a MTP device, if I have rhythmbox running with the MTP plugin,
it will load the device in rhythmbox but (for some unknown reason) disconnect it
again.
Then, a moment later, it will load the device again, and disconnect again,
and so on.&lt;/p&gt;
&lt;p&gt;I have no idea what gvfs-gphoto2-volume-monitor is doing, but at a guess, each
time it loads the device, it causes that process to do some work, reading stuff
into memory, and then not freeing it again when the devices gets reloaded.&lt;/p&gt;
&lt;p&gt;Since I’m not using it regularly, my solution was simply to disable the MTP
plugin in Rhythmbox.
Hopefully this information will help someone else figure out why they have no
memory, and maybe follow up the full story and fix it.&lt;/p&gt;
</content><category term="gvfs"></category><category term="gphoto2"></category><category term="memory"></category><category term="galaxy-s3"></category><category term="mtp"></category><category term="software"></category></entry><entry><title>Restarting this blog</title><link href="2012-11/restarting-this-blog.html" rel="alternate"></link><published>2012-11-07T18:40:00+11:00</published><updated>2012-11-07T18:40:00+11:00</updated><author><name>Peter Ward</name></author><id>tag:None,2012-11-07:2012-11/restarting-this-blog.html</id><summary type="html">&lt;p&gt;Welcome to my blog!
Apparently I think these things are still useful for something
(I may be slightly delusional on this point).&lt;/p&gt;
&lt;p&gt;I’m using Pelican, and have imported all the content from my old blog(s),
but there are still some formatting issues with the import which I need …&lt;/p&gt;</summary><content type="html">&lt;p&gt;Welcome to my blog!
Apparently I think these things are still useful for something
(I may be slightly delusional on this point).&lt;/p&gt;
&lt;p&gt;I’m using Pelican, and have imported all the content from my old blog(s),
but there are still some formatting issues with the import which I need to
manually fix up.&lt;/p&gt;
&lt;p&gt;In other news, I submitted my honours thesis today
(“Providing useful feedback to beginner programmers”),
which means I have only a presentation to go before I have completely finished
doing everything I need to for my Bachelor of Information Technologies degree.
Happy face!&lt;/p&gt;
</content><category term="random"></category></entry><entry><title>Keys</title><link href="2011-08/keys.html" rel="alternate"></link><published>2011-08-07T04:35:00+10:00</published><updated>2011-08-07T04:35:00+10:00</updated><author><name>Peter Ward</name></author><id>tag:None,2011-08-07:2011-08/keys.html</id><summary type="html">&lt;p&gt;Just a quick note for the people who care about trivial things like
privacy. ;)&lt;/p&gt;
&lt;p&gt;I’m decommissioning my four year old 1024-bit DSA key (1024D/D1ED1C9F),
and replacing it with a new 4096-bit RSA key (4096R/3906C4AF). The
fingerprint of the new key is
&lt;tt class="docutils literal"&gt;410D 28F0 1DFB 0DC4 0E0C&amp;nbsp; D663 …&lt;/tt&gt;&lt;/p&gt;</summary><content type="html">&lt;p&gt;Just a quick note for the people who care about trivial things like
privacy. ;)&lt;/p&gt;
&lt;p&gt;I’m decommissioning my four year old 1024-bit DSA key (1024D/D1ED1C9F),
and replacing it with a new 4096-bit RSA key (4096R/3906C4AF). The
fingerprint of the new key is
&lt;tt class="docutils literal"&gt;410D 28F0 1DFB 0DC4 0E0C&amp;nbsp; D663 E060 B7CD 3906 C4AF&lt;/tt&gt;, and it has been
signed by the old key.&lt;/p&gt;
&lt;p&gt;I’ve set the old key to expire at Christmas this year.&lt;/p&gt;
</content><category term="random"></category></entry><entry><title>Semester 1, 2011</title><link href="2011-07/semester-1-2011.html" rel="alternate"></link><published>2011-07-11T10:58:00+10:00</published><updated>2011-07-11T10:58:00+10:00</updated><author><name>Peter Ward</name></author><id>tag:None,2011-07-11:2011-07/semester-1-2011.html</id><summary type="html">&lt;p&gt;Another year, another semester, another set of results. This time, a
little worse than usual, for reasons I’ll explain shortly. However, let
me first give you an update of what I’ve been up to since I last posted
on my blog (more than six months ago :O).&lt;/p&gt;
&lt;p&gt;First …&lt;/p&gt;</summary><content type="html">&lt;p&gt;Another year, another semester, another set of results. This time, a
little worse than usual, for reasons I’ll explain shortly. However, let
me first give you an update of what I’ve been up to since I last posted
on my blog (more than six months ago :O).&lt;/p&gt;
&lt;p&gt;First up was the work I did with CHAI over the holidays—I integrated
WebKit into the Cruiser tabletop framework, and built a tagging
interface to simplify finding content in large collections of images.
There is a &lt;a class="reference external" href="http://chai.it.usyd.edu.au/TabletopVideoMay2011"&gt;video demonstrating the Cruiser framework&lt;/a&gt;, which shows
some of my work—my stuff starts about 80% of the way through, look for
spiders. ;)&lt;/p&gt;
&lt;p&gt;Straight after completing my CHAI work, I went back into uni mode! I
took four courses:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Object Oriented Design (info3220),&lt;/li&gt;
&lt;li&gt;Project Management in IT (info3402),&lt;/li&gt;
&lt;li&gt;Artificial Intelligence (comp3608) and&lt;/li&gt;
&lt;li&gt;a project unit with &lt;a class="reference external" href="http://it.usyd.edu.au/~james"&gt;James Curran&lt;/a&gt; (info3911).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In the project unit, I was working on reproducing the &lt;a class="reference external" href="http://tagme.di.unipi.it/"&gt;Tagme&lt;/a&gt; system
for use in linking &amp;quot;mentions&amp;quot; (useful snippets of text) in documents to
their correct Wikipedia articles. In addition to these units, I was also
tutoring the second-year databases course, info2120.&lt;/p&gt;
&lt;p&gt;Now, let me tell you a little about info3220. The first thing about this
unit is that it purports to teach Object Oriented Design, however in
reality, the main focus of the unit was on teaching C++’s implementation
of classes. It seems no-one had the idea that OO might actually have
been implemented in more than one language? And that it might not have
been implemented in exactly the same way in each language? Personally, I
would love to see OO integrated into the Programming Languages and
Paradigms unit (and the compilers part of that course should be split
out into a separate unit to make room), where you can show all the
wonderful ways of expressing the same operation in different ways (some
of which are more maintainable than others).&lt;/p&gt;
&lt;p&gt;Putting that aside, however, at the start of semester, we were offered
the choice of doing standard assignments (which would be writing a
vending machine-like program, and sounded boring), or doing an advanced
assignment. Well, I made a very silly decision, and let
&lt;a class="reference external" href="http://twitter.com/carlozancanaro"&gt;&amp;#64;carlozancanaro&lt;/a&gt; talk me into doing an advanced project, which was
building an introspection system for C++. Well, that was a bad idea, and
we didn’t get very far. So, we scraped through with minimal marks on the
assignment, and then played catch-up with the exam.&lt;/p&gt;
&lt;p&gt;Anyhow, enough with the excuses already! I did enjoy the artificial
intelligence course, which had two good assignments. Anyhow, I got a
pass (info3220), a distinction (info3402) and two high distinctions.
Exact marks are &lt;a class="reference external" href="http://rp-www.cs.usyd.edu.au/~pwar3236/academic.shtml"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;It’s now July, and I’m currently working four days a week with CHAI,
developing applications for two projects they’re working on—one is for
Sydnovate, the other is a university project for a kinect-controlled
display for the SIT building. I’m looking forward to next semester (with
some hopefully better marks!), with the all-important project unit. ;)&lt;/p&gt;
&lt;p&gt;Finally, I’m going to participate in &lt;a class="reference external" href="http://nanowrimo.org"&gt;NaNoWriMo&lt;/a&gt;, which means that in
November, I’ll be trying to write a 50,000 word novel (in whatever spare
time I have left lying around the place). Why, you ask? Well, I do a lot
of programming. And I think that the best program is one with very
little code which achieves a lot (e.g., Python &amp;amp; UNIX). Which means that
most of my time is spent &lt;em&gt;avoiding writing code&lt;/em&gt;. And then, when it
comes around to documenting the systems I’ve build, I naturally tend to
write terse descriptions of what it does, very factually correct and
precise. BOR-ING. So one reason I’m participating in NaNoWriMo is to
improve my documentation writing skills. But, the actual reason is far
less complicated. I’m doing it because I think it’ll be fun. ;)&lt;/p&gt;
&lt;p&gt;Anyhow, this is the public announcement so that I won’t be able to
forget about it, or make excuses that “I don’t have time”, etc. :P&lt;/p&gt;
</content><category term="chai"></category><category term="nanowrimo"></category><category term="results"></category><category term="university"></category></entry><entry><title>Semester 2 Results</title><link href="2010-12/semester-2-results.html" rel="alternate"></link><published>2010-12-04T07:58:00+11:00</published><updated>2010-12-04T07:58:00+11:00</updated><author><name>Peter Ward</name></author><id>tag:None,2010-12-04:2010-12/semester-2-results.html</id><summary type="html">&lt;p&gt;An update to my previous post - I did reasonably well this semester,
with 2 HDs in COMP3109 (compilers) and INFO2912, a D in COMP2907
(algorithms), and a C in MATH2988 (number theory &amp;amp; crypto). I would have
preferred a HD in algorithms, but I guess the exam brought it down.
Also …&lt;/p&gt;</summary><content type="html">&lt;p&gt;An update to my previous post - I did reasonably well this semester,
with 2 HDs in COMP3109 (compilers) and INFO2912, a D in COMP2907
(algorithms), and a C in MATH2988 (number theory &amp;amp; crypto). I would have
preferred a HD in algorithms, but I guess the exam brought it down.
Also: yay, I survived maths again! ;) The complete gory details can be
&lt;a class="reference external" href="http://it.usyd.edu.au/~pwar3236/academic.shtml"&gt;found here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In other news, I’m enjoying my scholarship work with CHAI — I’ve already
made something useful, and I’m slowly making progress. I do get the
feeling that redesigning Cruiser will be a side project. ;)&lt;/p&gt;
&lt;p&gt;I’m also excited about &lt;a class="reference external" href="http://www.ncss.edu.au/"&gt;NCSS&lt;/a&gt; — I’m sure it’s going to be a heap of fun
this (well, actually, next) year!&lt;/p&gt;
</content><category term="university"></category></entry><entry><title>Box specific bar numbers in Lilypond</title><link href="2010-11/box-specific-bar-numbers-in-lilypond.html" rel="alternate"></link><published>2010-11-05T04:30:00+11:00</published><updated>2010-11-05T04:30:00+11:00</updated><author><name>Peter Ward</name></author><id>tag:None,2010-11-05:2010-11/box-specific-bar-numbers-in-lilypond.html</id><summary type="html">&lt;p&gt;Just a bit of Lilypond / Scheme I wrote last night — if you want to have
boxed bar numbers at certain bar numbers (which don't fit every nth bar
- see the docs for that), you can use this code to do it:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;\override&lt;/span&gt; Score.BarNumber #&amp;#39;break-visibility = #end-of-line-invisible
&lt;span class="k"&gt;\override&lt;/span&gt; Score.BarNumber #&amp;#39;self-alignment-X …&lt;/pre&gt;&lt;/div&gt;</summary><content type="html">&lt;p&gt;Just a bit of Lilypond / Scheme I wrote last night — if you want to have
boxed bar numbers at certain bar numbers (which don't fit every nth bar
- see the docs for that), you can use this code to do it:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;\override&lt;/span&gt; Score.BarNumber #&amp;#39;break-visibility = #end-of-line-invisible
&lt;span class="k"&gt;\override&lt;/span&gt; Score.BarNumber #&amp;#39;self-alignment-X = #0
&lt;span class="k"&gt;\override&lt;/span&gt; Score.BarNumber #&amp;#39;font-size = #2
&lt;span class="k"&gt;\override&lt;/span&gt; Score.BarNumber #&amp;#39;stencil = #(make-stencil-boxer 0.1 0.25 ly:text-interface::print)
&lt;span class="k"&gt;\set&lt;/span&gt; Score.barNumberVisibility = #(lambda (x) (not (eq? (member x &amp;#39;(4 9 16 25)) #f)))
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This will put boxed numbers at bars 4, 9, 16 and 25.&lt;/p&gt;
</content><category term="software"></category></entry><entry><title>Life update</title><link href="2010-11/life-update.html" rel="alternate"></link><published>2010-11-02T01:27:00+11:00</published><updated>2010-11-02T01:27:00+11:00</updated><author><name>Peter Ward</name></author><id>tag:None,2010-11-02:2010-11/life-update.html</id><summary type="html">&lt;p&gt;Hurrah, I’ve reached the end of semester! Unfortunately, this does not
mean that I can stop working… sigh. Exams are coming up, speaking of
which, grumble at the person who organised my &lt;a class="reference external" href="http://it.usyd.edu.au/~pwar3236/timetable.shtml"&gt;exam timetable&lt;/a&gt;. :(&lt;/p&gt;
&lt;p&gt;Here’s my predictions for marks: high distinction in comp3109 (a great
course, btw), a …&lt;/p&gt;</summary><content type="html">&lt;p&gt;Hurrah, I’ve reached the end of semester! Unfortunately, this does not
mean that I can stop working… sigh. Exams are coming up, speaking of
which, grumble at the person who organised my &lt;a class="reference external" href="http://it.usyd.edu.au/~pwar3236/timetable.shtml"&gt;exam timetable&lt;/a&gt;. :(&lt;/p&gt;
&lt;p&gt;Here’s my predictions for marks: high distinction in comp3109 (a great
course, btw), a pass or a credit for math2988 (given it’s advanced, and
my past record of maths), distinction or high distinction in comp2907
(my assignment 2 was a little iffy, and we’ll see how the exam goes),
and probably a HD in info2912 (provided that I get my reports done in
time).&lt;/p&gt;
&lt;p&gt;Now, if you’re currently reading the RSS feed (or Planet NCSS), you
should definitely come and look at the fantastic theme I designed (and
either admire it, or tell me that I should give up design for life!).
Because I have plenty of work to be doing at the moment (info2912
reports, exam study, etc.), I decided to redesign my websites (the old
theme was from &lt;a class="reference external" href="http://www.oswd.org/"&gt;OSWD&lt;/a&gt;, and I’d seen it on a few other websites).
Fortunately, due to &lt;a class="reference external" href="https://home.flowblok.id.au/hg/flowblok/stable/Themere"&gt;some software I wrote&lt;/a&gt; doing this sort of redesign
is relatively painless, and doesn’t require remembering how Trac and
Mercurial do templating. ;) I’d also noticed a few bugs with WSGIProxy,
so I’ve written my own proxy and put it in Themere (see themere.proxy).&lt;/p&gt;
&lt;p&gt;So, now that you’ve managed to survive reading through my rambling this
far, I’ll continue rambling a bit more about what I’m going to be doing
over the summer break. Item one is a Summer Scholarship with &lt;a class="reference external" href="http://it.usyd.edu.au/"&gt;SIT&lt;/a&gt;,
working on Surface Computing and Tabletops with the CHAI group, which
should be fun.&lt;/p&gt;
&lt;p&gt;I’m also going to offer to be a tutor at &lt;a class="reference external" href="http://www.ncss.edu.au/"&gt;NCSS&lt;/a&gt; again. So, &lt;strong&gt;if you’re
an Australian year 10/11/12 student interested in computing&lt;/strong&gt;, go put in
your &lt;a class="reference external" href="http://www.ncss.edu.au/summer_school/applications.html"&gt;application&lt;/a&gt; now! Who knows, you might even see me there. ;)&lt;/p&gt;
</content><category term="university"></category></entry><entry><title>Semester 1 marks</title><link href="2010-07/semester-1-marks.html" rel="alternate"></link><published>2010-07-11T12:22:00+10:00</published><updated>2010-07-11T12:22:00+10:00</updated><author><name>Peter Ward</name></author><id>tag:None,2010-07-11:2010-07/semester-1-marks.html</id><summary type="html">&lt;p&gt;Another year, another set of marks — I’m quite happy with them,
especially maths. I wasn’t sure about doing advanced level and didn’t
think I did particularly well in the exam, so getting a Credit was
especially fortunate.&lt;/p&gt;
&lt;p&gt;So, here they are:&lt;/p&gt;
&lt;dl class="docutils"&gt;
&lt;dt&gt;COMP2129 — Operating Systems and Machine Principles …&lt;/dt&gt;&lt;/dl&gt;</summary><content type="html">&lt;p&gt;Another year, another set of marks — I’m quite happy with them,
especially maths. I wasn’t sure about doing advanced level and didn’t
think I did particularly well in the exam, so getting a Credit was
especially fortunate.&lt;/p&gt;
&lt;p&gt;So, here they are:&lt;/p&gt;
&lt;dl class="docutils"&gt;
&lt;dt&gt;COMP2129 — Operating Systems and Machine Principles&lt;/dt&gt;
&lt;dd&gt;93 (High Distinction)&lt;/dd&gt;
&lt;dt&gt;INFO2820 — Database Systems 1 (Advanced)&lt;/dt&gt;
&lt;dd&gt;92 (High Distinction)&lt;/dd&gt;
&lt;dt&gt;INFO2911 — IT Special Project 2A&lt;/dt&gt;
&lt;dd&gt;92 (High Distinction)&lt;/dd&gt;
&lt;dt&gt;MATH2969 — Discrete Mathematics &amp;amp; Graph Theory Adv&lt;/dt&gt;
&lt;dd&gt;65 (Credit)&lt;/dd&gt;
&lt;/dl&gt;
&lt;p&gt;I went last week to &lt;a class="reference external" href="http://iscfleadershipconference.wordpress.com/"&gt;ISCF Conference 2010&lt;/a&gt;, where not only did I have
an amazing time and met some amazing people, but also got to do some
user testing with Presenter! ;) The two things which were especially
needed were backgrounds and keyboard shortcuts. So, today, with some
very much appreciated assistance from Jamie, these features have been
added! If you’re game, grab the latest version from &lt;a class="reference external" href="https://home.flowblok.id.au/hg/flowblok/Presenter"&gt;the mercurial
repository&lt;/a&gt;. Alternatively, you can wait until I release Presenter 0.8,
which will hopefully be sometime in the next two weeks.&lt;/p&gt;
</content><category term="university"></category></entry><entry><title>Semester 2 marks</title><link href="2010-07/semester-2-marks.html" rel="alternate"></link><published>2010-07-11T11:46:00+10:00</published><updated>2010-07-11T11:46:00+10:00</updated><author><name>Peter Ward</name></author><id>tag:None,2010-07-11:2010-07/semester-2-marks.html</id><summary type="html">&lt;p&gt;Avast! Here be (dungeons and?) dragons! Actually, it's just my marks for
Semester 2 (which I've been rather slow in actually posting).&lt;/p&gt;
&lt;dl class="docutils"&gt;
&lt;dt&gt;ELEC1601 - Foundations of Computer Systems&lt;/dt&gt;
&lt;dd&gt;90 (High Distinction)&lt;/dd&gt;
&lt;dt&gt;INFO1905 - Data Structures (Advanced)&lt;/dt&gt;
&lt;dd&gt;86 (High Distinction)&lt;/dd&gt;
&lt;dt&gt;MATH1003 - Integral Calculus and Modelling&lt;/dt&gt;
&lt;dd&gt;71 (Credit)&lt;/dd&gt;
&lt;dt&gt;MATH1004 - Discrete Maths&lt;/dt&gt;
&lt;dd&gt;80 (Distinction)&lt;/dd&gt;
&lt;dt&gt;PHIL1012 …&lt;/dt&gt;&lt;/dl&gt;</summary><content type="html">&lt;p&gt;Avast! Here be (dungeons and?) dragons! Actually, it's just my marks for
Semester 2 (which I've been rather slow in actually posting).&lt;/p&gt;
&lt;dl class="docutils"&gt;
&lt;dt&gt;ELEC1601 - Foundations of Computer Systems&lt;/dt&gt;
&lt;dd&gt;90 (High Distinction)&lt;/dd&gt;
&lt;dt&gt;INFO1905 - Data Structures (Advanced)&lt;/dt&gt;
&lt;dd&gt;86 (High Distinction)&lt;/dd&gt;
&lt;dt&gt;MATH1003 - Integral Calculus and Modelling&lt;/dt&gt;
&lt;dd&gt;71 (Credit)&lt;/dd&gt;
&lt;dt&gt;MATH1004 - Discrete Maths&lt;/dt&gt;
&lt;dd&gt;80 (Distinction)&lt;/dd&gt;
&lt;dt&gt;PHIL1012 - Introductory Logic&lt;/dt&gt;
&lt;dd&gt;92 (High Distinction)&lt;/dd&gt;
&lt;/dl&gt;
&lt;p&gt;A Merry Christmas to you all!&lt;/p&gt;
&lt;p&gt;(on a side note, Presenter is coming along quite nicely, and has gained
support for song lyrics and images - unfortunately, video support is
still a little buggy...)&lt;/p&gt;
</content><category term="university"></category></entry><entry><title>Mouse activity</title><link href="2010-05/mouse-activity.html" rel="alternate"></link><published>2010-05-12T00:41:00+10:00</published><updated>2010-05-12T00:41:00+10:00</updated><author><name>Peter Ward</name></author><id>tag:None,2010-05-12:2010-05/mouse-activity.html</id><summary type="html">&lt;p&gt;&lt;a class="reference external" href="http://www.sourceguru.net/hour-life-mouse/"&gt;As seen on&lt;/a&gt; &lt;a class="reference external" href="http://planet.debian.org/"&gt;Planet Debian&lt;/a&gt;.&lt;/p&gt;
&lt;blockquote&gt;
“&lt;a class="reference external" href="http://iographica.com/"&gt;IOGraph&lt;/a&gt;” is a small Java program that tracks and draws the
status of your mouse over a period of time. Lines are movement,
circles are places where the mouse has stopped… The bigger the
circle, the longer it was there.&lt;/blockquote&gt;
&lt;img alt="" src="static/images/mouse_activity.png" /&gt;
&lt;p&gt;I suspect the large density …&lt;/p&gt;</summary><content type="html">&lt;p&gt;&lt;a class="reference external" href="http://www.sourceguru.net/hour-life-mouse/"&gt;As seen on&lt;/a&gt; &lt;a class="reference external" href="http://planet.debian.org/"&gt;Planet Debian&lt;/a&gt;.&lt;/p&gt;
&lt;blockquote&gt;
“&lt;a class="reference external" href="http://iographica.com/"&gt;IOGraph&lt;/a&gt;” is a small Java program that tracks and draws the
status of your mouse over a period of time. Lines are movement,
circles are places where the mouse has stopped… The bigger the
circle, the longer it was there.&lt;/blockquote&gt;
&lt;img alt="" src="static/images/mouse_activity.png" /&gt;
&lt;p&gt;I suspect the large density of horizontal lines in the center is due to
my habit of always selecting the text I'm currently reading. It's
interesting to see the areas of the screen which I don't really use
(yes, this only tracks mouse activity, but my eyes usually follow my
mouse).&lt;/p&gt;
</content><category term="random"></category></entry><entry><title>WSGI Resources</title><link href="2010-04/wsgi-resources.html" rel="alternate"></link><published>2010-04-09T01:02:00+10:00</published><updated>2010-04-09T01:02:00+10:00</updated><author><name>Peter Ward</name></author><id>tag:None,2010-04-09:2010-04/wsgi-resources.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;&lt;p class="first"&gt;&lt;a class="reference external" href="http://lucumr.pocoo.org/2007/5/21/getting-started-with-wsgi"&gt;Getting Started with WSGI&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;&lt;a class="reference external" href="http://www.python.org/dev/peps/pep-0333/"&gt;PEP 333, the official WSGI spec&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;dl class="first docutils"&gt;
&lt;dt&gt;&lt;a class="reference external" href="http://pythonpaste.org/"&gt;Python Paste, a useful WSGI library&lt;/a&gt;&lt;/dt&gt;
&lt;dd&gt;&lt;p class="first last"&gt;Tips:&lt;/p&gt;
&lt;/dd&gt;
&lt;/dl&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Start a PasteDeploy-based WSGI project: paster create -t
paste_deploy $APP_NAME&lt;/li&gt;
&lt;li&gt;Serve the webapp: (Note: I usually rename docs/ to config/) cd
$APP_NAME paster serve docs/devel_config.ini&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;If you're going the framework route …&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;&lt;p class="first"&gt;&lt;a class="reference external" href="http://lucumr.pocoo.org/2007/5/21/getting-started-with-wsgi"&gt;Getting Started with WSGI&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;&lt;a class="reference external" href="http://www.python.org/dev/peps/pep-0333/"&gt;PEP 333, the official WSGI spec&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;dl class="first docutils"&gt;
&lt;dt&gt;&lt;a class="reference external" href="http://pythonpaste.org/"&gt;Python Paste, a useful WSGI library&lt;/a&gt;&lt;/dt&gt;
&lt;dd&gt;&lt;p class="first last"&gt;Tips:&lt;/p&gt;
&lt;/dd&gt;
&lt;/dl&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Start a PasteDeploy-based WSGI project: paster create -t
paste_deploy $APP_NAME&lt;/li&gt;
&lt;li&gt;Serve the webapp: (Note: I usually rename docs/ to config/) cd
$APP_NAME paster serve docs/devel_config.ini&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;If you're going the framework route, look at &lt;a class="reference external" href="http://pylonshq.com/"&gt;Pylons&lt;/a&gt; or &lt;a class="reference external" href="http://djangoproject.com/"&gt;Django&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;If you're going the microframework route, you might be interested in
&lt;a class="reference external" href="http://bottle.paws.de/"&gt;Bottle&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
</content><category term="software"></category></entry><entry><title>Hello Planet NCSS!</title><link href="2010-04/hello-planet-ncss.html" rel="alternate"></link><published>2010-04-01T10:32:00+11:00</published><updated>2010-04-01T10:32:00+11:00</updated><author><name>Peter Ward</name></author><id>tag:None,2010-04-01:2010-04/hello-planet-ncss.html</id><summary type="html">&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Hello Planet NCSS!&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Thanks to Tim for making this happen so quickly!&lt;/p&gt;
</summary><content type="html">&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Hello Planet NCSS!&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Thanks to Tim for making this happen so quickly!&lt;/p&gt;
</content><category term="random"></category></entry><entry><title>I want to stab this error in the face.</title><link href="2010-03/i-want-to-stab-this-error-in-the-face.html" rel="alternate"></link><published>2010-03-27T03:13:00+11:00</published><updated>2010-03-27T03:13:00+11:00</updated><author><name>Peter Ward</name></author><id>tag:None,2010-03-27:2010-03/i-want-to-stab-this-error-in-the-face.html</id><summary type="html">&lt;pre class="literal-block"&gt;
The program 'presenter' received an X Window System error.
This probably reflects a bug in the program.
The error was 'BadShmSeg (invalid shared segment parameter)'.
  (Details: serial 351 error_code 158 request_code 132 minor_code 19)
  (Note to programmers: normally, X errors are reported asynchronously;
   that is, you will receive the error …&lt;/pre&gt;</summary><content type="html">&lt;pre class="literal-block"&gt;
The program 'presenter' received an X Window System error.
This probably reflects a bug in the program.
The error was 'BadShmSeg (invalid shared segment parameter)'.
  (Details: serial 351 error_code 158 request_code 132 minor_code 19)
  (Note to programmers: normally, X errors are reported asynchronously;
   that is, you will receive the error a while after causing it.
   To debug your program, run it with the --sync command line
   option to change this behavior. You can then get a meaningful
   backtrace from your debugger if you break on the gdk_x_error() function.)
&lt;/pre&gt;
&lt;p&gt;It's really annoying to have an application which does what it's meant
to, but crashes all the time.&lt;/p&gt;
</content><category term="software"></category></entry><entry><title>Mercurial hook for notify.io</title><link href="2010-02/mercurial-hook-for-notifyio.html" rel="alternate"></link><published>2010-02-07T14:06:00+11:00</published><updated>2010-02-07T14:06:00+11:00</updated><author><name>Peter Ward</name></author><id>tag:None,2010-02-07:2010-02/mercurial-hook-for-notifyio.html</id><summary type="html">&lt;p&gt;&lt;strong&gt;Update (9/2/10):&lt;/strong&gt; Fixed bug when pushing multiple commits at once,
silenced wget's output.&lt;/p&gt;
&lt;p&gt;Here is a Mercurial hook to send notifications to notify.io whenever it
gets new changesets pushed to it. It requires python, wget, bash,
mercurial, internet access and an uncommon sense of humour (the latter …&lt;/p&gt;</summary><content type="html">&lt;p&gt;&lt;strong&gt;Update (9/2/10):&lt;/strong&gt; Fixed bug when pushing multiple commits at once,
silenced wget's output.&lt;/p&gt;
&lt;p&gt;Here is a Mercurial hook to send notifications to notify.io whenever it
gets new changesets pushed to it. It requires python, wget, bash,
mercurial, internet access and an uncommon sense of humour (the latter
is only needed for bug reports).&lt;/p&gt;
&lt;p&gt;To use, put something like this into your hgrc (either for a specific
repo, or user or system-wide):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;[hooks]&lt;/span&gt;
&lt;span class="na"&gt;incoming.notifyio&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;/path/to/script&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And then put this into a shell script, where you referenced
/path/to/script before:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="ch"&gt;#!/bin/bash&lt;/span&gt;

&lt;span class="c1"&gt;# User data (change this)&lt;/span&gt;

&lt;span class="nv"&gt;email&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;YOUREMAILHERE&amp;quot;&lt;/span&gt;
&lt;span class="nv"&gt;apikey&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;YOURAPIKEYHERE&amp;quot;&lt;/span&gt;

&lt;span class="c1"&gt;# Work out api url to send to.&lt;/span&gt;

&lt;span class="nv"&gt;emailhash&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; -n &lt;span class="nv"&gt;$email&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; md5sum &lt;span class="p"&gt;|&lt;/span&gt; cut -f &lt;span class="m"&gt;1&lt;/span&gt; -d&lt;span class="s2"&gt;&amp;quot; &amp;quot;&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;
&lt;span class="nv"&gt;url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;http://api.notify.io/v1/notify/&lt;/span&gt;&lt;span class="nv"&gt;$emailhash&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;

&lt;span class="c1"&gt;# Quotes urls.&lt;/span&gt;
&lt;span class="nb"&gt;alias&lt;/span&gt; &lt;span class="nv"&gt;quote&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;python -c &amp;quot;import urllib, sys; print urllib.quote_plus(sys.stdin.read())&amp;quot;&amp;#39;&lt;/span&gt;

&lt;span class="c1"&gt;# Get repo information&lt;/span&gt;
&lt;span class="nv"&gt;desc&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;hg log -r &lt;span class="nv"&gt;$HG_NODE&lt;/span&gt; --template &lt;span class="s2"&gt;&amp;quot;{desc}&amp;quot;&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;
&lt;span class="nv"&gt;author&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;hg log -r &lt;span class="nv"&gt;$HG_NODE&lt;/span&gt; --template &lt;span class="s2"&gt;&amp;quot;{author}&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; sed &lt;span class="s1"&gt;&amp;#39;s/ &amp;amp;lt;.*//&amp;#39;&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;
&lt;span class="nv"&gt;text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$desc&lt;/span&gt;&lt;span class="s2"&gt; (&lt;/span&gt;&lt;span class="nv"&gt;$author&lt;/span&gt;&lt;span class="s2"&gt;)&amp;quot;&lt;/span&gt;

&lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="nb"&gt;pwd&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; awk &lt;span class="s1"&gt;&amp;#39;BEGIN{FS=&amp;quot;/&amp;quot;}{print $NF}&amp;#39;&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;

&lt;span class="nv"&gt;title&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Mercurial changeset added&amp;quot;&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; -n &lt;span class="nv"&gt;$name&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
 &lt;span class="nv"&gt;title&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$title&lt;/span&gt;&lt;span class="s2"&gt; in &lt;/span&gt;&lt;span class="nv"&gt;$name&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c1"&gt;# text - The notification text. Only required field.&lt;/span&gt;
&lt;span class="c1"&gt;# title - The title or subject of the notification.&lt;/span&gt;
&lt;span class="c1"&gt;# icon - The URL for an icon to use for the notification.&lt;/span&gt;
&lt;span class="c1"&gt;# link - The URL for a link you want to associate with a notification.&lt;/span&gt;
&lt;span class="c1"&gt;# sticky - Set this to &amp;#39;true&amp;#39; if you want the notification to be sticky.&lt;/span&gt;
&lt;span class="c1"&gt;# api_key - If you don&amp;#39;t want to set it in the query string&lt;/span&gt;

&lt;span class="nv"&gt;text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; -n &lt;span class="nv"&gt;$text&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; quote&lt;span class="sb"&gt;`&lt;/span&gt;
&lt;span class="nv"&gt;title&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; -n &lt;span class="nv"&gt;$title&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; quote&lt;span class="sb"&gt;`&lt;/span&gt;

&lt;span class="nv"&gt;data&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;text=&lt;/span&gt;&lt;span class="nv"&gt;$text&lt;/span&gt;&lt;span class="s2"&gt;&amp;amp;amp;title=&lt;/span&gt;&lt;span class="nv"&gt;$title&lt;/span&gt;&lt;span class="s2"&gt;&amp;amp;amp;api_key=&lt;/span&gt;&lt;span class="nv"&gt;$apikey&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
&lt;span class="c1"&gt;#echo wget -O - --post-data $data $url&lt;/span&gt;
wget -q -O - --post-data &lt;span class="nv"&gt;$data&lt;/span&gt; &lt;span class="nv"&gt;$url&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I take no responsibility for any loss of life, possessions or injuries sustained
through the use of this hack. Please direct correspondence, thanks and hate mail
to B.Posters, Devilish Lane, Cranbrook.&lt;/p&gt;
</content><category term="software"></category></entry><entry><title>Productivity</title><link href="2009-12/productivity.html" rel="alternate"></link><published>2009-12-10T10:20:00+11:00</published><updated>2009-12-10T10:20:00+11:00</updated><author><name>Peter Ward</name></author><id>tag:None,2009-12-10:2009-12/productivity.html</id><summary type="html">&lt;p&gt;Any resemblance to reality is purely coincidental.&lt;/p&gt;
&lt;img alt="because you can never have enough Comic Sans" src="static/images/productivity1.png" /&gt;
</summary><content type="html">&lt;p&gt;Any resemblance to reality is purely coincidental.&lt;/p&gt;
&lt;img alt="because you can never have enough Comic Sans" src="static/images/productivity1.png" /&gt;
</content><category term="university"></category></entry><entry><title>Test post</title><link href="2009-11/test-post.html" rel="alternate"></link><published>2009-11-11T03:31:00+11:00</published><updated>2009-11-11T03:31:00+11:00</updated><author><name>Peter Ward</name></author><id>tag:None,2009-11-11:2009-11/test-post.html</id><summary type="html">&lt;p&gt;Congratulations, you've won a prize! Nope...nothing to see here, move
along.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;Congratulations, you've won a prize! Nope...nothing to see here, move
along.&lt;/p&gt;
</content><category term="random"></category></entry><entry><title>Deliverance</title><link href="2009-11/deliverance.html" rel="alternate"></link><published>2009-11-01T06:46:00+11:00</published><updated>2009-11-01T06:46:00+11:00</updated><author><name>Peter Ward</name></author><id>tag:None,2009-11-01:2009-11/deliverance.html</id><summary type="html">&lt;p&gt;I've been trying out &lt;a class="reference external" href="http://deliverance.openplans.org/"&gt;deliverance&lt;/a&gt;, and it is a really nice way of
theming websites.&lt;/p&gt;
&lt;p&gt;My motivation to use it is this: I want a website, which has various
apps (in my case, Trac, HGWeb, SVN, and more), and I want to give all
the pages a common theme. Instead …&lt;/p&gt;</summary><content type="html">&lt;p&gt;I've been trying out &lt;a class="reference external" href="http://deliverance.openplans.org/"&gt;deliverance&lt;/a&gt;, and it is a really nice way of
theming websites.&lt;/p&gt;
&lt;p&gt;My motivation to use it is this: I want a website, which has various
apps (in my case, Trac, HGWeb, SVN, and more), and I want to give all
the pages a common theme. Instead of learning each of those
application's theming languages (which would all be different, and some
don't even have theme support), I want something to work as a &amp;quot;filter&amp;quot;
at the web-server level...which is what Deliverance does, basically.&lt;/p&gt;
&lt;p&gt;Unfortunately, it has a few things which annoys me. For one, it
implements it's own web-server (deliverance-proxy uses
paste.httpserver), and doesn't let you choose how to deploy it (e.g.:
mod_wsgi on apache would be nice for me). Also, only half of it has
been written as WSGI middleware (well, let's make that easily accessible
WSGI middleware) - the bit which takes a theme and rules and transforms
the content is available as
deliverance.middleware.DeliveranceMiddleware. Unfortunately, link
rewriting, response and request modifications aren't included in that,
so you have to use deliverance-proxy to get them. :(&lt;/p&gt;
&lt;p&gt;I've drawn two diagrams to illustrate my thoughts of how Deliverance
should work, the first being how I currently understand it to be, and
the second showing how I would like it to be. A disclaimer: representing
Deliverance in a diagram was tricky, so it's not 100% accurate. Please
don't take my lack of pedantry in drawing diagrams for a lack of
understanding of how Deliverance works.&lt;/p&gt;
&lt;p&gt;How deliverance works (according to me):&lt;/p&gt;
&lt;img alt="A diagram showing the scope of Deliverance - it notably includes deliverance-proxy, the front-end server." src="static/images/deliverance_old.png" /&gt;
&lt;p&gt;How I think Deliverance should work:&lt;/p&gt;
&lt;img alt="A diagram showing my thoughts on the future of Deliverance - notably, it's entirely one bit of middleware, and just wraps another application (which could use URLMap and Proxy to simulate the previous behaviour)" src="static/images/deliverance_new.png" /&gt;
&lt;p&gt;Some notes on things I'd like to see changed:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Deliverance becomes completely usable as a WSGI application - any
server can run all the components of Deliverance, instead of just
deliverance-proxy.&lt;/li&gt;
&lt;li&gt;Deliverance wraps another application, which then does any proxying.
This way, XML configuration files are replaced with Paste Deploy ini
files (using URLMap and Proxy for previous behaviour).&lt;/li&gt;
&lt;li&gt;Allow different rules to use different themes (I've coded up some
preliminary support - see &lt;a class="reference external" href="https://projects.openplans.org/deliverance/ticket/17"&gt;#17&lt;/a&gt; for more details).&lt;/li&gt;
&lt;/ul&gt;
</content><category term="software"></category></entry><entry><title>Links to stuff</title><link href="2009-10/links-to-stuff.html" rel="alternate"></link><published>2009-10-04T06:56:00+11:00</published><updated>2009-10-04T06:56:00+11:00</updated><author><name>Peter Ward</name></author><id>tag:None,2009-10-04:2009-10/links-to-stuff.html</id><summary type="html">&lt;p&gt;&lt;a class="reference external" href="static/images/tehdailywtf.png"&gt;The Daily WTF failing&lt;/a&gt;, &lt;a class="reference external" href="static/images/random_tracks.png"&gt;my iRobot Simulation winning&lt;/a&gt;, &lt;a class="reference external" href="static/images/robot1.ogv"&gt;my iRobot
program failing&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In other news, &lt;a class="reference external" href="http://www.ncss.edu.au/"&gt;NCSS&lt;/a&gt; &lt;a class="reference external" href="http://www.ncss.edu.au/summer_school/index.html"&gt;Summer School&lt;/a&gt; &lt;a class="reference external" href="http://www.ncss.edu.au/summer_school/applications.html"&gt;applications are open&lt;/a&gt;!&lt;/p&gt;
</summary><content type="html">&lt;p&gt;&lt;a class="reference external" href="static/images/tehdailywtf.png"&gt;The Daily WTF failing&lt;/a&gt;, &lt;a class="reference external" href="static/images/random_tracks.png"&gt;my iRobot Simulation winning&lt;/a&gt;, &lt;a class="reference external" href="static/images/robot1.ogv"&gt;my iRobot
program failing&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In other news, &lt;a class="reference external" href="http://www.ncss.edu.au/"&gt;NCSS&lt;/a&gt; &lt;a class="reference external" href="http://www.ncss.edu.au/summer_school/index.html"&gt;Summer School&lt;/a&gt; &lt;a class="reference external" href="http://www.ncss.edu.au/summer_school/applications.html"&gt;applications are open&lt;/a&gt;!&lt;/p&gt;
</content><category term="random"></category></entry><entry><title>Update time</title><link href="2009-09/update-time.html" rel="alternate"></link><published>2009-09-22T01:06:00+10:00</published><updated>2009-09-22T01:06:00+10:00</updated><author><name>Peter Ward</name></author><id>tag:None,2009-09-22:2009-09/update-time.html</id><summary type="html">&lt;p&gt;Things I've done recently:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Participated in the ACM South-Pacific regional with Carlo &amp;amp; Ronny -
we were the &lt;a class="reference external" href="http://www.sppcontest.org/2009/SP_Results_2009.html#Special"&gt;top 1st year team&lt;/a&gt;!&lt;/li&gt;
&lt;li&gt;Wrote Huffman compression utility for INFO1905 assignment - Carlo &amp;amp; I
worked out a really tiny way of storing the header. Now, it's just
back to writing unit tests and documenting... (incidentally …&lt;/li&gt;&lt;/ul&gt;</summary><content type="html">&lt;p&gt;Things I've done recently:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Participated in the ACM South-Pacific regional with Carlo &amp;amp; Ronny -
we were the &lt;a class="reference external" href="http://www.sppcontest.org/2009/SP_Results_2009.html#Special"&gt;top 1st year team&lt;/a&gt;!&lt;/li&gt;
&lt;li&gt;Wrote Huffman compression utility for INFO1905 assignment - Carlo &amp;amp; I
worked out a really tiny way of storing the header. Now, it's just
back to writing unit tests and documenting... (incidentally, bits &amp;amp;
bytes in Java != fun)&lt;/li&gt;
&lt;li&gt;Got an &lt;a class="reference external" href="http://identi.ca"&gt;identi.ca&lt;/a&gt; account - getting real-time updates about stuff
people are doing with Python is fun.&lt;/li&gt;
&lt;li&gt;NCSS Challenge has officially finished - the Python streams finished
a week ago, and the Embedded stream finished on Sunday, but there's
an extra challenge for the Embedded stream - and a nice prize to got
with it!&lt;/li&gt;
&lt;li&gt;Change the name of this blog - again. We can't have it being
consistent, can we?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Things to do:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Work on Presenter - this will probably be one of the things to do in
my mid-semester break. &lt;a class="reference external" href="http://flowblok.selfip.net:8001/trac/presenter/query"&gt;The list of things to do&lt;/a&gt; has been slowly
lengthening...&lt;/li&gt;
&lt;li&gt;Complete everything on my TODO list... :(&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Something I discovered today: &lt;a class="reference external" href="http://www.steike.com/code/useless/zip-file-quine/"&gt;ZIP File Quine&lt;/a&gt;. I'm wondering if I can
do the something clever like this with my INFO1905 assignment... :P&lt;/p&gt;
</content><category term="university"></category></entry><entry><title>Busy, busy, busy.</title><link href="2009-09/busy-busy-busy.html" rel="alternate"></link><published>2009-09-01T00:12:00+10:00</published><updated>2009-09-01T00:12:00+10:00</updated><author><name>Peter Ward</name></author><id>tag:None,2009-09-01:2009-09/busy-busy-busy.html</id><summary type="html">&lt;p&gt;Uni assignments, NCSS Challenge, math quizzes (urgh!), Presenter, and
also ACM training. Wow there's heaps to do. I'm trying to uses Getting
Things GNOME! to organise myself, and it works, but only when I use it.
:( Clearly every organisation system should work even when I don't use
it. ;)&lt;/p&gt;
&lt;p&gt;In other …&lt;/p&gt;</summary><content type="html">&lt;p&gt;Uni assignments, NCSS Challenge, math quizzes (urgh!), Presenter, and
also ACM training. Wow there's heaps to do. I'm trying to uses Getting
Things GNOME! to organise myself, and it works, but only when I use it.
:( Clearly every organisation system should work even when I don't use
it. ;)&lt;/p&gt;
&lt;p&gt;In other news, I got myself an account on bitbucket (yay for OpenID!),
and &lt;a class="reference external" href="http://bitbucket.org/flowblok/presenter/"&gt;published Presenter&lt;/a&gt; on it. Thanks to that, I noticed that I
hadn't updated the README before releasing 0.2. Whoops! I've also been
thinking about the UI for adding / removing files from the runsheet, and
I've got an idea which should be pretty, logical and intuitive - I just
need to find the time to implement it! Also, a terminal handler will be
coming as soon as I get some time... (which should be awesome for
teaching Python with)&lt;/p&gt;
&lt;p&gt;Also, PyPy is one of the more interesting things to compile, thanks to
it's fractal drawing. I should be bothered to write one of them these
days, shouldn't I?&lt;/p&gt;
&lt;p&gt;The NCSS Challenge introduced an Embedded Stream, where the students get
some modified Arduino boards to play with - lots of fun, especially the
accelerometer. Unfortunately, the online marker is somewhat buggy and
sensitive, so answering questions on the forum is usually not that much
fun. :(&lt;/p&gt;
&lt;p&gt;Uni is going fairly well, except for my Integral Calculus course. I had
a rather odd assignment for INFO1905, where they were assessing
graph-searching (disguised as a degrees of separation problem which
needed Breadth-First Search), when they haven't even covered graphs in
the lectures, let alone graph searching. I know we're an advanced class,
but seriously - that just means we'll be able to learn faster, not know
it already (even though I did...).&lt;/p&gt;
&lt;p&gt;Finally, if you're using IDLE on Windows, note that
len(string.lowercase) != 26, unlike every other Python environment on
the planet. Stupid, stupid, stupid. Fine, we'll use
string.ascii_lowercase instead.&lt;/p&gt;
</content><category term="university"></category></entry><entry><title>Google Reader</title><link href="2009-08/google-reader.html" rel="alternate"></link><published>2009-08-18T00:37:00+10:00</published><updated>2009-08-18T00:37:00+10:00</updated><author><name>Peter Ward</name></author><id>tag:None,2009-08-18:2009-08/google-reader.html</id><summary type="html">&lt;p&gt;It's been nice knowing you, but I'm giving you up.&lt;/p&gt;
&lt;p&gt;No more reading RSS feeds during my computer labs, no more worrying
about whether Google is spying on me by working out what kind of
subscriptions I read.&lt;/p&gt;
&lt;p&gt;Goodbye Google Reader, Hello undisclosed client-side RSS reader.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;It's been nice knowing you, but I'm giving you up.&lt;/p&gt;
&lt;p&gt;No more reading RSS feeds during my computer labs, no more worrying
about whether Google is spying on me by working out what kind of
subscriptions I read.&lt;/p&gt;
&lt;p&gt;Goodbye Google Reader, Hello undisclosed client-side RSS reader.&lt;/p&gt;
</content><category term="software"></category></entry><entry><title>Presenter Prerelease</title><link href="2009-08/presenter-prerelease.html" rel="alternate"></link><published>2009-08-08T01:42:00+10:00</published><updated>2009-08-08T01:42:00+10:00</updated><author><name>Peter Ward</name></author><id>tag:None,2009-08-08:2009-08/presenter-prerelease.html</id><summary type="html">&lt;p&gt;In the spirit of &amp;quot;release early, release often&amp;quot;, here is another release
of Presenter. This is an Alpha release, so it's still got plenty of bugs
(the obvious one is with Video playback).&lt;/p&gt;
&lt;p&gt;I've provided a .deb package, so if you're a Debian/Ubuntu user, it
should be relatively easy …&lt;/p&gt;</summary><content type="html">&lt;p&gt;In the spirit of &amp;quot;release early, release often&amp;quot;, here is another release
of Presenter. This is an Alpha release, so it's still got plenty of bugs
(the obvious one is with Video playback).&lt;/p&gt;
&lt;p&gt;I've provided a .deb package, so if you're a Debian/Ubuntu user, it
should be relatively easy to install. If you don't install any of the
recommended packages (python-evince, python-gnome2-desktop,
python-gst0.10), you'll find it rather boring.&lt;/p&gt;
&lt;p&gt;There are two main problems with the .deb package: it explicitly depends
on python2.5, and the icon for the menu item doesn't seem to appear
unless you logout/login your session. These are known problems, directly
related to how I created the package - I'm working on fixing them before
the beta release.&lt;/p&gt;
&lt;p&gt;Also, if you can be stuffed, it is possible to get Presenter working on
Windows, and video playback works - you'll need PyGTK and PyGST
(gstreamer bindings for Python).&lt;/p&gt;
&lt;p&gt;You can &lt;a class="reference external" href="http://pypi.python.org/pypi/Presenter"&gt;get it from PyPI&lt;/a&gt;. &lt;a class="reference external" href="http://pypi.python.org/packages/2.5/P/Presenter/Presenter_0.2_all.deb#md5=56b822ee445c8c5a5c52ce310dbf662d"&gt;Deb package&lt;/a&gt; &lt;a class="reference external" href="http://pypi.python.org/packages/source/P/Presenter/Presenter-0.2dev.tar.gz#md5=44a43a5a4ea0ad667b6ad0cf2e0aff32"&gt;Tar Gzip&lt;/a&gt; &lt;a class="reference external" href="http://pypi.python.org/packages/2.5/P/Presenter/Presenter-0.2dev-py2.5.egg#md5=137443faf2574276e2918e52dc76dcf0"&gt;Egg&lt;/a&gt; &lt;a class="reference external" href="http://pypi.python.org/packages/2.5/P/Presenter/Presenter-0.2dev.win32.exe#md5=1d409abc67b108e332f3a7399c453651"&gt;Win32
Installer&lt;/a&gt;&lt;/p&gt;
</content><category term="software"></category></entry><entry><title>Leaving Semester 1, Entering Semester 2</title><link href="2009-08/leaving-semester-1-entering-semester-2.html" rel="alternate"></link><published>2009-08-01T00:43:00+10:00</published><updated>2009-08-01T00:43:00+10:00</updated><author><name>Peter Ward</name></author><id>tag:None,2009-08-01:2009-08/leaving-semester-1-entering-semester-2.html</id><summary type="html">&lt;p&gt;So, I finished Semester 1, and got my results back. They're not
brilliant, but they're enough...&lt;/p&gt;
&lt;p&gt;Professional Engineering and IT: 71 (Credit) Fundamentals of Elec and
Electronic Engineering: 59 (Pass) Differential Calculus: 71 (Credit)
Linear Algebra: 74 (Credit) Informatics (Advanced): 95 (High
Distinction)&lt;/p&gt;
&lt;p&gt;And the subjects which I'm taking this …&lt;/p&gt;</summary><content type="html">&lt;p&gt;So, I finished Semester 1, and got my results back. They're not
brilliant, but they're enough...&lt;/p&gt;
&lt;p&gt;Professional Engineering and IT: 71 (Credit) Fundamentals of Elec and
Electronic Engineering: 59 (Pass) Differential Calculus: 71 (Credit)
Linear Algebra: 74 (Credit) Informatics (Advanced): 95 (High
Distinction)&lt;/p&gt;
&lt;p&gt;And the subjects which I'm taking this semester are: Data Structures
(Advanced), Discrete Mathematics, Introductory Logic, Integral Calculus
and Modelling, Foundations of Computer Systems.&lt;/p&gt;
&lt;p&gt;In other news, I set up Deliverance on &lt;a class="reference external" href="http://flowblok.selfip.net:8001/"&gt;aurora&lt;/a&gt;, make apache on aurora
a lot faster, and also did some work on &lt;a class="reference external" href="http://flowblok.selfip.net:8001/trac/presenter"&gt;Presenter&lt;/a&gt;.&lt;/p&gt;
</content><category term="university"></category></entry><entry><title>IWSGAY</title><link href="2009-06/iwsgay.html" rel="alternate"></link><published>2009-06-22T11:10:00+10:00</published><updated>2009-06-22T11:10:00+10:00</updated><author><name>Peter Ward</name></author><id>tag:None,2009-06-22:2009-06/iwsgay.html</id><summary type="html">&lt;p&gt;Oh, dear. Why did I waste my time with this, again?&lt;/p&gt;
&lt;img alt="Piglatin in WSGI" src="http://flowblok.files.wordpress.com/2009/06/piglatin.png" /&gt;
</summary><content type="html">&lt;p&gt;Oh, dear. Why did I waste my time with this, again?&lt;/p&gt;
&lt;img alt="Piglatin in WSGI" src="http://flowblok.files.wordpress.com/2009/06/piglatin.png" /&gt;
</content><category term="programming"></category><category term="software"></category></entry><entry><title>Latex - inserting a box with no content</title><link href="2009-05/latex-inserting-a-box-with-no-content.html" rel="alternate"></link><published>2009-05-04T06:36:00+10:00</published><updated>2009-05-04T06:36:00+10:00</updated><author><name>Peter Ward</name></author><id>tag:None,2009-05-04:2009-05/latex-inserting-a-box-with-no-content.html</id><summary type="html">&lt;p&gt;Or, an empty box! Working out that query on Google took me some time, so
I'll put the answer here:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;\fbox&lt;/span&gt;&lt;span class="nb"&gt;{&lt;/span&gt;&lt;span class="k"&gt;\begin&lt;/span&gt;&lt;span class="nb"&gt;{&lt;/span&gt;minipage&lt;span class="nb"&gt;}{&lt;/span&gt;5in&lt;span class="nb"&gt;}&lt;/span&gt;&lt;span class="k"&gt;\hfill\vspace&lt;/span&gt;&lt;span class="nb"&gt;{&lt;/span&gt;3in&lt;span class="nb"&gt;}&lt;/span&gt;&lt;span class="k"&gt;\end&lt;/span&gt;&lt;span class="nb"&gt;{&lt;/span&gt;minipage&lt;span class="nb"&gt;}}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;RealLife (tm) Update: I'm enjoying University, and I'm thinking of
moving into C/C++ via Vala.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;Or, an empty box! Working out that query on Google took me some time, so
I'll put the answer here:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;\fbox&lt;/span&gt;&lt;span class="nb"&gt;{&lt;/span&gt;&lt;span class="k"&gt;\begin&lt;/span&gt;&lt;span class="nb"&gt;{&lt;/span&gt;minipage&lt;span class="nb"&gt;}{&lt;/span&gt;5in&lt;span class="nb"&gt;}&lt;/span&gt;&lt;span class="k"&gt;\hfill\vspace&lt;/span&gt;&lt;span class="nb"&gt;{&lt;/span&gt;3in&lt;span class="nb"&gt;}&lt;/span&gt;&lt;span class="k"&gt;\end&lt;/span&gt;&lt;span class="nb"&gt;{&lt;/span&gt;minipage&lt;span class="nb"&gt;}}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;RealLife (tm) Update: I'm enjoying University, and I'm thinking of
moving into C/C++ via Vala.&lt;/p&gt;
</content><category term="software"></category></entry><entry><title>Update on Presenter</title><link href="2009-04/update-on-presenter.html" rel="alternate"></link><published>2009-04-18T02:23:00+10:00</published><updated>2009-04-18T02:23:00+10:00</updated><author><name>Peter Ward</name></author><id>tag:None,2009-04-18:2009-04/update-on-presenter.html</id><summary type="html">&lt;p&gt;I'm building a different, better idea for Presenter.&lt;/p&gt;
&lt;p&gt;Details here: &lt;a class="reference external" href="http://flowblok.selfip.net:8001/trac/presenter"&gt;http://flowblok.selfip.net:8001/trac/presenter&lt;/a&gt;&lt;/p&gt;
</summary><content type="html">&lt;p&gt;I'm building a different, better idea for Presenter.&lt;/p&gt;
&lt;p&gt;Details here: &lt;a class="reference external" href="http://flowblok.selfip.net:8001/trac/presenter"&gt;http://flowblok.selfip.net:8001/trac/presenter&lt;/a&gt;&lt;/p&gt;
</content><category term="software"></category></entry><entry><title>How to do plugins using eggs</title><link href="2009-04/how-to-do-plugins-using-eggs.html" rel="alternate"></link><published>2009-04-17T04:57:00+10:00</published><updated>2009-04-17T04:57:00+10:00</updated><author><name>Peter Ward</name></author><id>tag:None,2009-04-17:2009-04/how-to-do-plugins-using-eggs.html</id><summary type="html">&lt;p&gt;(Just so that I don't forget, and because some of you may not have seen
this before.)&lt;/p&gt;
&lt;p&gt;This is the magic Python code which automagically gets eggs with the
entry point &amp;quot;my.plugin.namespace&amp;quot; declared.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;egg_plugin&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;pkg_resources&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;iter_entry_points&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;my.plugin.namespace&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="n"&gt;egg_plugin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
</summary><content type="html">&lt;p&gt;(Just so that I don't forget, and because some of you may not have seen
this before.)&lt;/p&gt;
&lt;p&gt;This is the magic Python code which automagically gets eggs with the
entry point &amp;quot;my.plugin.namespace&amp;quot; declared.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;egg_plugin&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;pkg_resources&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;iter_entry_points&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;my.plugin.namespace&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="n"&gt;egg_plugin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
</content><category term="software"></category></entry><entry><title>New Machines (aka my interwebs are slow)</title><link href="2009-04/new-machines-aka-my-interwebs-are-slow.html" rel="alternate"></link><published>2009-04-13T03:47:00+10:00</published><updated>2009-04-13T03:47:00+10:00</updated><author><name>Peter Ward</name></author><id>tag:None,2009-04-13:2009-04/new-machines-aka-my-interwebs-are-slow.html</id><summary type="html">&lt;p&gt;So, 2 weekends ago, I got a new computer! It's been named &amp;quot;polaris&amp;quot;. It
was built from parts by my brother (since I don't generally like
descending down into the hardware level).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Stats&lt;/strong&gt;: Processor: 2x Intel(R) Core(TM)2 Duo CPU E8400 &amp;#64; 3.00GHz
Memory : 4GB (but not being …&lt;/p&gt;</summary><content type="html">&lt;p&gt;So, 2 weekends ago, I got a new computer! It's been named &amp;quot;polaris&amp;quot;. It
was built from parts by my brother (since I don't generally like
descending down into the hardware level).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Stats&lt;/strong&gt;: Processor: 2x Intel(R) Core(TM)2 Duo CPU E8400 &amp;#64; 3.00GHz
Memory : 4GB (but not being utilized, as I'm using the wrong kernel atm)
Operating System: Debian GNU/Linux squeeze/sid Display Resolution:
1680x1050 pixels&lt;/p&gt;
&lt;p&gt;My mother also recieved a similar machine (3.16 GHz processer instead),
and is running Windows XP Professional. (It's name is &amp;quot;m2&amp;quot;, as her old
one was &amp;quot;m1&amp;quot;) I also took what I could from my mother's old machine, and
put them into my old machine, and named is &amp;quot;aurora&amp;quot;. (answer to last
post: it's my new development server)&lt;/p&gt;
&lt;p&gt;Unfortunately, this meant installing 3 operating systems from the
internet (XP was CD-based, but it needed SP{2,3} and all the various
upgrades). Also, I didn't think to download a real Debian CD/DVD
(instead of my netinst install CD), so it also meant downloading 3
operatings systems.&lt;/p&gt;
&lt;p&gt;Thus I am currently on dial-up speed internet.&lt;/p&gt;
&lt;p&gt;Oh well, at least I've got a development server to play with (I'll set
up version control stuff, Trac, and anything else I can think up).&lt;/p&gt;
&lt;p&gt;Hey look - I've deviated from the original point of this post, so I'd
better stop typing soon - but, before I go, which of these do people
think I should work on:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;strong&gt;Improved Sprite and Scene System:&lt;/strong&gt; on
&lt;a class="reference external" href="http://pygame.org/wiki/gsoc2009ideas"&gt;http://pygame.org/wiki/gsoc2009ideas&lt;/a&gt; (I can't do GSOC this year, due
to the age limit) - it looks like I could rewrite Bezel Engine to
become part of PyGame. (which would be awesome)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Presenter&lt;/strong&gt; - the control freak's presentation tool.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Web development&lt;/strong&gt; - make my super-awesome WSGI app for developing
web applications (yeah, sounds recursive). (and hard!)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So people, tell me what I should focus on - I'll continue doing small
disjointed development on all of them for the moment, but if people tell
that they really want to see me make one of these, I'll put a lot of
time and effort into it.&lt;/p&gt;
</content><category term="university"></category></entry><entry><title>Erm, whoops!</title><link href="2009-04/erm-whoops.html" rel="alternate"></link><published>2009-04-12T22:32:00+10:00</published><updated>2009-04-12T22:32:00+10:00</updated><author><name>Peter Ward</name></author><id>tag:None,2009-04-12:2009-04/erm-whoops.html</id><summary type="html">&lt;pre class="literal-block"&gt;
flowblok&amp;#64;aurora:~git/cgit-0.8.2.1 Z make
zsh: command not found: make
&lt;/pre&gt;
&lt;p&gt;Clearly, I should have installed build-essentials a long time ago! ;-)
More about what this &amp;quot;aurora&amp;quot; machine is later.&lt;/p&gt;
</summary><content type="html">&lt;pre class="literal-block"&gt;
flowblok&amp;#64;aurora:~git/cgit-0.8.2.1 Z make
zsh: command not found: make
&lt;/pre&gt;
&lt;p&gt;Clearly, I should have installed build-essentials a long time ago! ;-)
More about what this &amp;quot;aurora&amp;quot; machine is later.&lt;/p&gt;
</content><category term="random"></category></entry><entry><title>Presentation software implementation</title><link href="2009-02/presentation-software-implementation.html" rel="alternate"></link><published>2009-02-24T05:51:00+11:00</published><updated>2009-02-24T05:51:00+11:00</updated><author><name>Peter Ward</name></author><id>tag:None,2009-02-24:2009-02/presentation-software-implementation.html</id><summary type="html">&lt;p&gt;WARNING: This article talks about pre-alpha code (i.e.: still in
development). Use at your own risk.&lt;/p&gt;
&lt;p&gt;Just over a week ago, I posted &lt;a class="reference external" href="http://flowblok.wordpress.com/2009/02/13/presentation-software-idea/"&gt;my idea for presentation software&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Since then, I've put together a PyGTK[1] application for this purpose.&lt;/p&gt;
&lt;p&gt;Features:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Presenter console: Provides a window for easy navigation …&lt;/li&gt;&lt;/ul&gt;</summary><content type="html">&lt;p&gt;WARNING: This article talks about pre-alpha code (i.e.: still in
development). Use at your own risk.&lt;/p&gt;
&lt;p&gt;Just over a week ago, I posted &lt;a class="reference external" href="http://flowblok.wordpress.com/2009/02/13/presentation-software-idea/"&gt;my idea for presentation software&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Since then, I've put together a PyGTK[1] application for this purpose.&lt;/p&gt;
&lt;p&gt;Features:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Presenter console: Provides a window for easy navigation of slides,
just by double-clicking.&lt;/li&gt;
&lt;li&gt;Presentation window: Dual-head support, with monitor detection and
switching at runtime.&lt;/li&gt;
&lt;li&gt;Application: Keyboard shortcuts for all features.&lt;/li&gt;
&lt;li&gt;Document format: ZIP file of images for each of the slides.&lt;/li&gt;
&lt;li&gt;Document format: PDF - linux only, using the python bindings for
poppler (Debian/Ubuntu: python-poppler)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I've uploaded it to the Python Package Index:
&lt;a class="reference external" href="http://pypi.python.org/pypi/Presenter/"&gt;http://pypi.python.org/pypi/Presenter/&lt;/a&gt;. Please be aware of the
dependency on PyGTK (Debian/Ubuntu: python-gtk2)&lt;/p&gt;
&lt;p&gt;Let me know if you have any installation problems (relating to my
software, not it's dependencies), and any bugs you encounter.&lt;/p&gt;
&lt;p&gt;Also, I haven't quite put a license on it definitively for the final
release, however, &lt;strong&gt;this particular release&lt;/strong&gt; is licensed under the
terms of the GNU General Public Licence, version 3.&lt;/p&gt;
&lt;p&gt;[1] Sorry to those of you who don't like GTK, or use Macs - whilst I
would like to support you, this is just a prototype with a GUI toolkit I
know.&lt;/p&gt;
</content><category term="software"></category></entry><entry><title>Presentation software idea</title><link href="2009-02/presentation-software-idea.html" rel="alternate"></link><published>2009-02-13T10:47:00+11:00</published><updated>2009-02-13T10:47:00+11:00</updated><author><name>Peter Ward</name></author><id>tag:None,2009-02-13:2009-02/presentation-software-idea.html</id><summary type="html">&lt;p&gt;I now very much dislike how OpenOffice.org Impress was built in Java.&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;It's slow to load.&lt;/li&gt;
&lt;li&gt;It's unresponsive.&lt;/li&gt;
&lt;li&gt;It has an amazing ability to crash, on the most simple actions.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;However, I do like the OpenDocument format. Also, presentations are to
be presented, not just to be edited. And …&lt;/p&gt;</summary><content type="html">&lt;p&gt;I now very much dislike how OpenOffice.org Impress was built in Java.&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;It's slow to load.&lt;/li&gt;
&lt;li&gt;It's unresponsive.&lt;/li&gt;
&lt;li&gt;It has an amazing ability to crash, on the most simple actions.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;However, I do like the OpenDocument format. Also, presentations are to
be presented, not just to be edited. And everyone presents with
dual-monitor setups nowadays, and if not, they should.&lt;/p&gt;
&lt;p&gt;So, I want presentation software which:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;is Free Open Source Software.&lt;/li&gt;
&lt;li&gt;works cross-platform.&lt;/li&gt;
&lt;li&gt;reads in .odp (OpenDocument Presentation) files&lt;/li&gt;
&lt;li&gt;displays a controlling window on one desktop, and a viewing window on
a second desktop&lt;/li&gt;
&lt;li&gt;allows for non-linear viewing of slides (can switch to any slide at
any time)&lt;/li&gt;
&lt;li&gt;(future) allows for editing / creation of slides on-the-fly&lt;/li&gt;
&lt;li&gt;(future) allows for a presentation to be made up of various
presentation files, added and removed on-the-fly.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The first step would be to just write a viewer, and the second step
would be to write a nice editor. Then I wouldn't have to deal with so
many program crashes.&lt;/p&gt;
&lt;p&gt;Incidently, this is not intended to replace OpenOffice.org completely.
Impress has various features which I have not addressed here, the point
of this program is to have something which even your mother could use to
do a presentation safely.&lt;/p&gt;
&lt;p&gt;In terms of implementation, it doesn't really matter, as long as it is
responsive to users, and stable. If I get around to implementing it
(after some of my current projects are out of a development stage), then
I'd probably use Python, wxPython and &lt;a class="reference external" href="http://opendocumentfellowship.com/projects/odfpy"&gt;odfpy&lt;/a&gt;.&lt;/p&gt;
</content><category term="programming"></category><category term="software"></category></entry><entry><title>University</title><link href="2009-02/university.html" rel="alternate"></link><published>2009-02-06T22:43:00+11:00</published><updated>2009-02-06T22:43:00+11:00</updated><author><name>Peter Ward</name></author><id>tag:None,2009-02-06:2009-02/university.html</id><summary type="html">&lt;p&gt;By the way, I have enrolled at the University of Sydney for a Bachelor
of Computer Science and Technology.&lt;/p&gt;
&lt;p&gt;But you knew that already, didn't you?&lt;/p&gt;
</summary><content type="html">&lt;p&gt;By the way, I have enrolled at the University of Sydney for a Bachelor
of Computer Science and Technology.&lt;/p&gt;
&lt;p&gt;But you knew that already, didn't you?&lt;/p&gt;
</content><category term="university"></category></entry><entry><title>A magical network client</title><link href="2009-01/a-magical-network-client.html" rel="alternate"></link><published>2009-01-20T00:27:00+11:00</published><updated>2009-01-20T00:27:00+11:00</updated><author><name>Peter Ward</name></author><id>tag:None,2009-01-20:2009-01/a-magical-network-client.html</id><summary type="html">&lt;p&gt;Game Engine:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;bezel.networking.engine&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;NetworkedGame&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;exposeclass&lt;/span&gt; &lt;span class="n"&gt;GameEngine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;NetworkedGame&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="nb"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;GameEngine&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="fm"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

    &lt;span class="nd"&gt;@expose&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;say&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;something&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Simon says &amp;quot;&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s1"&gt;&amp;quot;&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;something&lt;/span&gt;

    &lt;span class="nd"&gt;@expose&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;num&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;num&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Client:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="ch"&gt;#!/usr/bin/env …&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;</summary><content type="html">&lt;p&gt;Game Engine:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;bezel.networking.engine&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;NetworkedGame&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;exposeclass&lt;/span&gt; &lt;span class="n"&gt;GameEngine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;NetworkedGame&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="nb"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;GameEngine&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="fm"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

    &lt;span class="nd"&gt;@expose&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;say&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;something&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Simon says &amp;quot;&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s1"&gt;&amp;quot;&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;something&lt;/span&gt;

    &lt;span class="nd"&gt;@expose&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;num&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;num&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Client:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="ch"&gt;#!/usr/bin/env python&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;bezel.networking.client&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;JSONGameClient&lt;/span&gt;
&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;JSONGameClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8602&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;engine&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;engine&lt;/span&gt;
&lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;say&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;blah&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;count + 1 =&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Missing is the network server (the code for that is fairly standard, but
I've changed a few things, which may confuse people) - suffice to say
that it's a threading TCP server with automatic Avahi publishing.&lt;/p&gt;
&lt;p&gt;Example usage (from the root of the bezel-engine repository):&lt;/p&gt;
&lt;pre class="literal-block"&gt;
$ demos/network/main.py &amp;amp;
[1] 9422
$ demos/network/client.py
Simon says &amp;quot;blah&amp;quot;
count + 1 = 1
$ demos/network/client.py
Simon says &amp;quot;blah&amp;quot;
count + 1 = 2
&lt;/pre&gt;
&lt;p&gt;Not yet done:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Load testing for the server.&lt;/li&gt;
&lt;li&gt;Client (player) authentication.&lt;/li&gt;
&lt;li&gt;A class to discover the servers through avahi.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Life update: Main round offers are coming out tomorrow, so I'm hoping to
get one for Sydney Uni. It probably won't be a BIT or a BCST (Adv), but
as long as I get in there and do well, I should be able to transfer
fairly easily.&lt;/p&gt;
</content><category term="programming"></category><category term="school"></category></entry><entry><title>class FailEpicallyError(Exception): pass</title><link href="2009-01/class-failepicallyerrorexception-pass.html" rel="alternate"></link><published>2009-01-16T01:56:00+11:00</published><updated>2009-01-16T01:56:00+11:00</updated><author><name>Peter Ward</name></author><id>tag:None,2009-01-16:2009-01/class-failepicallyerrorexception-pass.html</id><summary type="html">&lt;p&gt;Error #1: Me failing epically at recursive callbacks:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
RuntimeErrorRuntimeErrorRuntimeErrorRuntimeErrorRuntimeError
RuntimeErrorRuntimeErrorRuntimeErrorRuntimeErrorRuntimeError
RuntimeErrorRuntimeErrorRuntimeErrorRuntimeErrorRuntimeError
RuntimeErrorRuntimeErrorRuntimeErrorRuntimeErrorRuntimeError
RuntimeErrorRuntimeErrorRuntimeErrorRuntimeErrorRuntimeError
RuntimeError^C
Traceback (most recent call last):
&amp;nbsp; File &amp;quot;src/bin/ua-sidebar&amp;quot;, line 71, in do_forall
&amp;nbsp;&amp;nbsp;&amp;nbsp; def do_forall(self, internal, callback, data):
KeyboardInterrupt
RuntimeErrorRuntimeErrorRuntimeErrorRuntimeErrorRuntimeError
RuntimeErrorRuntimeErrorRuntimeErrorRuntimeErrorRuntimeError
RuntimeErrorRuntimeErrorRuntimeErrorRuntimeErrorRuntimeError
RuntimeErrorRuntimeErrorRuntimeErrorRuntimeErrorRuntimeError
RuntimeErrorRuntimeErrorRuntimeErrorRuntimeErrorRuntimeError^C
Traceback (most recent call last):
&amp;nbsp; File &amp;quot;src/bin/ua-sidebar …&lt;/pre&gt;</summary><content type="html">&lt;p&gt;Error #1: Me failing epically at recursive callbacks:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
RuntimeErrorRuntimeErrorRuntimeErrorRuntimeErrorRuntimeError
RuntimeErrorRuntimeErrorRuntimeErrorRuntimeErrorRuntimeError
RuntimeErrorRuntimeErrorRuntimeErrorRuntimeErrorRuntimeError
RuntimeErrorRuntimeErrorRuntimeErrorRuntimeErrorRuntimeError
RuntimeErrorRuntimeErrorRuntimeErrorRuntimeErrorRuntimeError
RuntimeError^C
Traceback (most recent call last):
&amp;nbsp; File &amp;quot;src/bin/ua-sidebar&amp;quot;, line 71, in do_forall
&amp;nbsp;&amp;nbsp;&amp;nbsp; def do_forall(self, internal, callback, data):
KeyboardInterrupt
RuntimeErrorRuntimeErrorRuntimeErrorRuntimeErrorRuntimeError
RuntimeErrorRuntimeErrorRuntimeErrorRuntimeErrorRuntimeError
RuntimeErrorRuntimeErrorRuntimeErrorRuntimeErrorRuntimeError
RuntimeErrorRuntimeErrorRuntimeErrorRuntimeErrorRuntimeError
RuntimeErrorRuntimeErrorRuntimeErrorRuntimeErrorRuntimeError^C
Traceback (most recent call last):
&amp;nbsp; File &amp;quot;src/bin/ua-sidebar&amp;quot;, line 75, in do_forall
&amp;nbsp;&amp;nbsp;&amp;nbsp; for child in self.get_children():
KeyboardInterrupt
RuntimeErrorRuntimeErrorRuntimeErrorRuntimeErrorRuntimeError
RuntimeErrorRuntimeErrorRuntimeErrorRuntimeErrorRuntimeError
^CRuntimeErrorRuntimeError^CTraceback (most recent call last):
&amp;nbsp; File &amp;quot;src/bin/ua-sidebar&amp;quot;, line 75, in do_forall
&amp;nbsp;&amp;nbsp;&amp;nbsp; for child in self.get_children():
KeyboardInterrupt
RuntimeErrorRuntimeErrorRuntimeError^CTraceback (most recent call last):
&amp;nbsp; File &amp;quot;src/bin/ua-sidebar&amp;quot;, line 71, in do_forall
&amp;nbsp;&amp;nbsp;&amp;nbsp; def do_forall(self, internal, callback, data):
KeyboardInterrupt
RuntimeErrorRuntimeErrorRuntimeErrorRuntimeErrorRuntimeError
RuntimeErrorRuntimeErrorRuntimeErrorRuntimeErrorRuntimeError
RuntimeErrorRuntimeErrorRuntimeErrorRuntimeErrorRuntimeError
RuntimeErrorRuntimeErrorRuntimeErrorRuntimeErrorRuntimeError
RuntimeErrorRuntimeErrorRuntimeErrorRuntimeErrorRuntimeError
RuntimeErrorRuntimeErrorRuntimeError^C^C^C^C^C^C^CRuntimeError
RuntimeError^CTraceback (most recent call last):
&amp;nbsp; File &amp;quot;src/bin/ua-sidebar&amp;quot;, line 75, in do_forall
&amp;nbsp;&amp;nbsp;&amp;nbsp; for child in self.get_children():
KeyboardInterrupt
RuntimeErrorRuntimeErrorRuntimeErrorRuntimeErrorRuntimeError
^CTraceback (most recent call last):
&amp;nbsp; File &amp;quot;src/bin/ua-sidebar&amp;quot;, line 75, in do_forall
&amp;nbsp;&amp;nbsp;&amp;nbsp; for child in self.get_children():
KeyboardInterrupt
RuntimeError^CTraceback (most recent call last):
&amp;nbsp; File &amp;quot;src/bin/ua-sidebar&amp;quot;, line 71, in do_forall
&amp;nbsp;&amp;nbsp;&amp;nbsp; def do_forall(self, internal, callback, data):
KeyboardInterrupt
RuntimeErrorRuntimeError^CTraceback (most recent call last):
&amp;nbsp; File &amp;quot;src/bin/ua-sidebar&amp;quot;, line 75, in do_forall
&amp;nbsp;&amp;nbsp;&amp;nbsp; for child in self.get_children():
KeyboardInterrupt
RuntimeErrorRuntimeError^CTraceback (most recent call last):
&amp;nbsp; File &amp;quot;src/bin/ua-sidebar&amp;quot;, line 75, in do_forall
&amp;nbsp;&amp;nbsp;&amp;nbsp; for child in self.get_children():
KeyboardInterrupt
RuntimeError^CRuntimeErrorRuntimeErrorRuntimeError
RuntimeErrorRuntimeError^CRuntimeErrorRuntimeError
RuntimeErrorRuntimeErrorRuntimeErrorRuntimeError^C
Traceback (most recent call last):
&amp;nbsp; File &amp;quot;src/bin/ua-sidebar&amp;quot;, line 75, in do_forall
&amp;nbsp;&amp;nbsp;&amp;nbsp; for child in self.get_children():
KeyboardInterrupt
RuntimeErrorRuntimeError^CRuntimeErrorRuntimeError
RuntimeErrorRuntimeErrorRuntimeError^CRuntimeError
RuntimeErrorRuntimeErrorRuntimeErrorRuntimeError
RuntimeError^CTraceback (most recent call last):
&amp;nbsp; File &amp;quot;src/bin/ua-sidebar&amp;quot;, line 75, in do_forall
&amp;nbsp;&amp;nbsp;&amp;nbsp; for child in self.get_children():
KeyboardInterrupt
RuntimeErrorRuntimeError^CTraceback (most recent call last):
&amp;nbsp; File &amp;quot;src/bin/ua-sidebar&amp;quot;, line 75, in do_forall
&amp;nbsp;&amp;nbsp;&amp;nbsp; for child in self.get_children():
KeyboardInterrupt
RuntimeErrorRuntimeError^CTraceback (most recent call last):
&amp;nbsp; File &amp;quot;src/bin/ua-sidebar&amp;quot;, line 75, in do_forall
&amp;nbsp;&amp;nbsp;&amp;nbsp; for child in self.get_children():
KeyboardInterrupt
RuntimeError^CTraceback (most recent call last):
&amp;nbsp; File &amp;quot;src/bin/ua-sidebar&amp;quot;, line 71, in do_forall
&amp;nbsp;&amp;nbsp;&amp;nbsp; def do_forall(self, internal, callback, data):
KeyboardInterrupt
^CRuntimeErrorRuntimeErrorRuntimeError^CTraceback (most recent call last):
&amp;nbsp; File &amp;quot;src/bin/ua-sidebar&amp;quot;, line 71, in do_forall
&amp;nbsp;&amp;nbsp;&amp;nbsp; def do_forall(self, internal, callback, data):
KeyboardInterrupt
RuntimeErrorRuntimeErrorRuntimeError^CTraceback (most recent call last):
&amp;nbsp; File &amp;quot;src/bin/ua-sidebar&amp;quot;, line 75, in do_forall
&amp;nbsp;&amp;nbsp;&amp;nbsp; for child in self.get_children():
KeyboardInterrupt
RuntimeError^CTraceback (most recent call last):
&amp;nbsp; File &amp;quot;src/bin/ua-sidebar&amp;quot;, line 75, in do_forall
&amp;nbsp;&amp;nbsp;&amp;nbsp; for child in self.get_children():
KeyboardInterrupt
RuntimeError^C^C^C^C^C^CRuntimeError^Z
&lt;/pre&gt;
&lt;p&gt;Error #2: My shell stopped responding, so I drew some artwork in it.&lt;/p&gt;
&lt;pre class="literal-block"&gt;
^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C
^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C
^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C
^Z^Z^Z^Z^Z^Z^Z^Z^Z^Z^Z^Z^Z^Z^Z^Z^Z^Z^Z^Z^Z^Z^Z
^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^Z
^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^Z^Z^Z^Z^Z^Z
^C^C^C^C^C^C^C^C^C^C^C^C^C^Z^Z^Z^Z^Z^C^C^C^C^C
^C^C^C^C^C^C^C^C^C^C^C^C^C^Z^C^C^C^C^C^C^C^C^C
^C^C^C^C^C^Z^Z^Z^C^C^C^C^C^Z^C^C^C^C^C^C^C^C^C
^C^C^C^C^C^Z^C^Z^C^C^C^C^C^Z^C^C^C^Z^Z^Z^C^C^C
^C^C^C^C^C^Z^C^Z^Z^Z^Z^Z^Z^Z^C^C^C^Z^C^Z^C^C^C
^C^C^C^C^C^Z^C^C^C^C^C^C^C^C^C^C^C^Z^C^Z^Z^Z^C
^C^C^C^C^C^Z^C^C^C^C^C^C^C^C^C^C^C^Z^C^C^C^Z^C
^C^C^C^C^C^Z^C^C^C^C^C^C^C^C^C^C^Z^Z^C^C^C^Z^C
^C^C^C^C^C^Z^Z^Z^Z^Z^Z^C^C^C^C^C^Z^C^Z^Z^Z^Z^C
^C^C^C^C^C^C^C^C^C^C^Z^Z^Z^Z^Z^Z^Z^C^Z^C^C^C^C
^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^Z^C^C^C^C
^C^C^C^C^C^C^C^C^C^C^C^Z^Z^Z^Z^Z^Z^Z^Z^C^C^C^C
^C^C^C^C^C^Z^Z^Z^Z^Z^Z^Z^C^C^C^C^C^C^C^C^C^C^C
^C^Z^Z^Z^Z^Z^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C
^Z^Z^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C
^Z^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C
&lt;/pre&gt;
</content><category term="software"></category></entry><entry><title>HSC Results &amp; UAI</title><link href="2008-12/hsc-results-uai.html" rel="alternate"></link><published>2008-12-17T22:30:00+11:00</published><updated>2008-12-17T22:30:00+11:00</updated><author><name>Peter Ward</name></author><id>tag:None,2008-12-17:2008-12/hsc-results-uai.html</id><summary type="html">&lt;table border="1" class="docutils"&gt;
&lt;colgroup&gt;
&lt;col width="8%" /&gt;
&lt;col width="30%" /&gt;
&lt;col width="17%" /&gt;
&lt;col width="17%" /&gt;
&lt;col width="10%" /&gt;
&lt;col width="17%" /&gt;
&lt;/colgroup&gt;
&lt;thead valign="bottom"&gt;
&lt;tr&gt;&lt;th class="head"&gt;Units&lt;/th&gt;
&lt;th class="head"&gt;Course Name&lt;/th&gt;
&lt;th class="head"&gt;Examination Mark&lt;/th&gt;
&lt;th class="head"&gt;Assessment Mark&lt;/th&gt;
&lt;th class="head"&gt;HSC Mark&lt;/th&gt;
&lt;th class="head"&gt;Performance Band&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Business Studies&lt;/td&gt;
&lt;td&gt;79/100&lt;/td&gt;
&lt;td&gt;84/100&lt;/td&gt;
&lt;td&gt;82&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Chemistry&lt;/td&gt;
&lt;td&gt;72/100&lt;/td&gt;
&lt;td&gt;69/100&lt;/td&gt;
&lt;td&gt;71&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;English (Advanced)&lt;/td&gt;
&lt;td&gt;76/100&lt;/td&gt;
&lt;td&gt;81/100&lt;/td&gt;
&lt;td&gt;79&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Mathematics&lt;/td&gt;
&lt;td&gt;92/100&lt;/td&gt;
&lt;td&gt;87/100&lt;/td&gt;
&lt;td&gt;90&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Mathematics Extension 1&lt;/td&gt;
&lt;td&gt;46 …&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;</summary><content type="html">&lt;table border="1" class="docutils"&gt;
&lt;colgroup&gt;
&lt;col width="8%" /&gt;
&lt;col width="30%" /&gt;
&lt;col width="17%" /&gt;
&lt;col width="17%" /&gt;
&lt;col width="10%" /&gt;
&lt;col width="17%" /&gt;
&lt;/colgroup&gt;
&lt;thead valign="bottom"&gt;
&lt;tr&gt;&lt;th class="head"&gt;Units&lt;/th&gt;
&lt;th class="head"&gt;Course Name&lt;/th&gt;
&lt;th class="head"&gt;Examination Mark&lt;/th&gt;
&lt;th class="head"&gt;Assessment Mark&lt;/th&gt;
&lt;th class="head"&gt;HSC Mark&lt;/th&gt;
&lt;th class="head"&gt;Performance Band&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Business Studies&lt;/td&gt;
&lt;td&gt;79/100&lt;/td&gt;
&lt;td&gt;84/100&lt;/td&gt;
&lt;td&gt;82&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Chemistry&lt;/td&gt;
&lt;td&gt;72/100&lt;/td&gt;
&lt;td&gt;69/100&lt;/td&gt;
&lt;td&gt;71&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;English (Advanced)&lt;/td&gt;
&lt;td&gt;76/100&lt;/td&gt;
&lt;td&gt;81/100&lt;/td&gt;
&lt;td&gt;79&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Mathematics&lt;/td&gt;
&lt;td&gt;92/100&lt;/td&gt;
&lt;td&gt;87/100&lt;/td&gt;
&lt;td&gt;90&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Mathematics Extension 1&lt;/td&gt;
&lt;td&gt;46/50&lt;/td&gt;
&lt;td&gt;40/50&lt;/td&gt;
&lt;td&gt;43&lt;/td&gt;
&lt;td&gt;E3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Physics&lt;/td&gt;
&lt;td&gt;74/100&lt;/td&gt;
&lt;td&gt;72/100&lt;/td&gt;
&lt;td&gt;73&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Software Design and Development&lt;/td&gt;
&lt;td&gt;95/100&lt;/td&gt;
&lt;td&gt;94/100&lt;/td&gt;
&lt;td&gt;95&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;And my UAI is 88.75.&lt;/p&gt;
</content><category term="exams"></category><category term="school"></category></entry><entry><title>Contrast in Gmail Planets Theme</title><link href="2008-12/contrast-in-gmail-planets-theme.html" rel="alternate"></link><published>2008-12-04T03:58:00+11:00</published><updated>2008-12-04T03:58:00+11:00</updated><author><name>Peter Ward</name></author><id>tag:None,2008-12-04:2008-12/contrast-in-gmail-planets-theme.html</id><summary type="html">&lt;p&gt;If you're reading this on an LCD screen, you may need to tilt it (or
move your head) to see this clearly.&lt;/p&gt;
&lt;img alt="Black on dark blue, anyone?" src="static/images/gmail-planets-contrast.jpg" /&gt;
</summary><content type="html">&lt;p&gt;If you're reading this on an LCD screen, you may need to tilt it (or
move your head) to see this clearly.&lt;/p&gt;
&lt;img alt="Black on dark blue, anyone?" src="static/images/gmail-planets-contrast.jpg" /&gt;
</content><category term="random"></category></entry><entry><title>You're doing it wrong!</title><link href="2008-12/youre-doing-it-wrong.html" rel="alternate"></link><published>2008-12-01T23:07:00+11:00</published><updated>2008-12-01T23:07:00+11:00</updated><author><name>Peter Ward</name></author><id>tag:None,2008-12-01:2008-12/youre-doing-it-wrong.html</id><summary type="html">&lt;p&gt;And I apologise for the correct spelling and grammar of the title.&lt;/p&gt;
&lt;p&gt;On &lt;a class="reference external" href="http://www.protocolostomy.com/"&gt;his blog&lt;/a&gt;, Brian Jones &lt;a class="reference external" href="http://www.protocolostomy.com/2008/10/29/stop-doing-things-that-dont-work-aka-excel-and-virtual-private-servers-are-evil/"&gt;points out&lt;/a&gt; (amongst other things) that
you shouldn't use Excel (or any other spreadsheet product) as a
replacement for a real database.&lt;/p&gt;
&lt;p&gt;But this isn't all that is wrong in the world:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Don't …&lt;/li&gt;&lt;/ul&gt;</summary><content type="html">&lt;p&gt;And I apologise for the correct spelling and grammar of the title.&lt;/p&gt;
&lt;p&gt;On &lt;a class="reference external" href="http://www.protocolostomy.com/"&gt;his blog&lt;/a&gt;, Brian Jones &lt;a class="reference external" href="http://www.protocolostomy.com/2008/10/29/stop-doing-things-that-dont-work-aka-excel-and-virtual-private-servers-are-evil/"&gt;points out&lt;/a&gt; (amongst other things) that
you shouldn't use Excel (or any other spreadsheet product) as a
replacement for a real database.&lt;/p&gt;
&lt;p&gt;But this isn't all that is wrong in the world:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Don't use one single big table in Word - using Excel or another
spreadsheet program.&lt;/li&gt;
&lt;li&gt;Don't use absolute positioning in Word - just get some real Desktop
Publishing Software!&lt;/li&gt;
&lt;li&gt;Don't use Access for website databases. Here's one which I have done,
and the reason why it's a bad idea is because it means reading a file
(often a big file) from the filesystem, typically every page request.
It's also near impossible to use it reliably on a platform other than
Windows.&lt;/li&gt;
&lt;li&gt;Don't use any presentation software, especially Powerpoint, unless
you know how to give an interesting talk without it. (I might talk
about presentation software and presentations on this blog in the
future - stay tuned!)&lt;/li&gt;
&lt;li&gt;FrontPage is still considered by some as a real HTML editor. Don't
use it! It was bad when I first used it, and it hasn't changed. I
still write HTML by hand, and when I need a graphical HTML editor, my
preference is &lt;a class="reference external" href="http://www.wymeditor.org/"&gt;WYMeditor&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There's certainly other failures in this area, and we could &lt;a class="reference external" href="http://thedailywtf.com/"&gt;spend all
day talking about failures of software&lt;/a&gt;, but I think we've all got
better things to do, don't we?&lt;/p&gt;
&lt;p&gt;Oh, and this isn't all Microsoft's fault. I'm just using their product
names as they happen to be the most common nowadays. These faults are
just as easily replicated with Free Open Source Software.&lt;/p&gt;
</content><category term="random"></category></entry><entry><title>Programming Meme - Fantasy VM 2</title><link href="2008-11/programming-meme-fantasy-vm-2.html" rel="alternate"></link><published>2008-11-26T01:53:00+11:00</published><updated>2008-11-26T01:53:00+11:00</updated><author><name>Peter Ward</name></author><id>tag:None,2008-11-26:2008-11/programming-meme-fantasy-vm-2.html</id><summary type="html">&lt;p&gt;Those of you who participated in the &lt;a class="reference external" href="http://challenge.ncss.edu.au/"&gt;NCSS Challenge&lt;/a&gt; this year, or
attended &lt;a class="reference external" href="http://www.ncss.edu.au/"&gt;NCSS&lt;/a&gt; at the start of the year may have encountered Fantasy
VM, a rather silly Virtual Machine with direct hardware support for
variables!&lt;/p&gt;
&lt;p&gt;Some people have started a &lt;a class="reference external" href="http://www.eflorenzano.com/blog/post/trying-start-programming-meme/"&gt;programming meme&lt;/a&gt;, so I thought I'd join
in.&lt;/p&gt;
&lt;p&gt;For …&lt;/p&gt;</summary><content type="html">&lt;p&gt;Those of you who participated in the &lt;a class="reference external" href="http://challenge.ncss.edu.au/"&gt;NCSS Challenge&lt;/a&gt; this year, or
attended &lt;a class="reference external" href="http://www.ncss.edu.au/"&gt;NCSS&lt;/a&gt; at the start of the year may have encountered Fantasy
VM, a rather silly Virtual Machine with direct hardware support for
variables!&lt;/p&gt;
&lt;p&gt;Some people have started a &lt;a class="reference external" href="http://www.eflorenzano.com/blog/post/trying-start-programming-meme/"&gt;programming meme&lt;/a&gt;, so I thought I'd join
in.&lt;/p&gt;
&lt;p&gt;For this program however, it soon became apparent that the simple
Fantasy VM would not come up to the job, as it had no support for
strings. However, with a little bit of hacking, I've created Fantasy VM
2! The specifications for the VM are similar, it's fully backwards
compatible, but adds a few instructions to make working with strings
possible. The silliest part which I've added is the support of the ADD
operator for strings, but considering that it's a very silly language,
I'm regarding it as a feature, not a bug.&lt;/p&gt;
&lt;p&gt;So, here's the program in FVM2:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
SET name_prompt &amp;quot;Please enter your name:&amp;quot;
SET age_prompt &amp;quot;Please enter your age:&amp;quot;
SET counter 0
SET test 0
SET one 1
SET start &amp;quot;) Hello, &amp;quot;
READ$ name name_prompt
READ$ age age_prompt
INT age age
SUB age age one
SUB test age counter # pretest
JGE test 13 # skip
JGE one 19
STR counter_s counter # skip
ADD greeting start name
ADD greeting counter_s greeting
PRINT greeting
ADD counter counter one
JGE one 10 #pretest
NOP
&lt;/pre&gt;
&lt;p&gt;And here is &lt;a class="reference external" href="http://www.ncss.edu.au/~pward1/Stuff/fvm2.py"&gt;an interpreter&lt;/a&gt; for the language, written in Python. To
run, just save the above program to a file, and call &amp;quot;python fvm2.py
filename.vm&amp;quot;.&lt;/p&gt;
</content><category term="school"></category></entry><entry><title>After the HSC</title><link href="2008-11/after-the-hsc.html" rel="alternate"></link><published>2008-11-18T00:41:00+11:00</published><updated>2008-11-18T00:41:00+11:00</updated><author><name>Peter Ward</name></author><id>tag:None,2008-11-18:2008-11/after-the-hsc.html</id><summary type="html">&lt;p&gt;It's been a while since my last HSC exam, and I thought I'd blog about
what's been happening since.&lt;/p&gt;
&lt;p&gt;I've got my Year 12 formal tonight, which promises to be a heap of fun,
lack of sleep and hopefully provide enough blackmail material for the
rest of my classmates for …&lt;/p&gt;</summary><content type="html">&lt;p&gt;It's been a while since my last HSC exam, and I thought I'd blog about
what's been happening since.&lt;/p&gt;
&lt;p&gt;I've got my Year 12 formal tonight, which promises to be a heap of fun,
lack of sleep and hopefully provide enough blackmail material for the
rest of my classmates for the rest of the century! (mwahahaha!) Or maybe
not. :(&lt;/p&gt;
&lt;p&gt;I've been working on my application for the &lt;a class="reference external" href="http://www.it.usyd.edu.au/mra_scholarship"&gt;Microsoft Research Asia
Scholarship in IT&lt;/a&gt; for the past week or two (and been thinking about
working on it for far longer than that). Writing documents isn't my
favourite bit of work to do, so it's been slow work, but hopefully I'll
be shortlisted and get an interview.&lt;/p&gt;
&lt;p&gt;I had thought that my desk would get a lot cleaner after I got all my
school stuff of it. I really should have known better. Maybe it'll get
cleaner once I get all my scholarship stuff of it. I doubt it.&lt;/p&gt;
&lt;p&gt;On the programming front, I've been working on &lt;a class="reference external" href="https://launchpad.net/universal-applets"&gt;Universal Applets&lt;/a&gt;, the
successor to Screenlets. I can't say the code's nice, the style seems to
be half C and half BASIC, but I'm working through the core modules, and
cleaning as I fix stuff. We should have a release out next year.&lt;/p&gt;
&lt;p&gt;And on a completely different note, we've got a large pile of mulch in
our driveway. I wonder what people walking by think of it. It'll take a
while to move into places around our backyard, so I might get a photo
sometime in the next week and post it.&lt;/p&gt;
&lt;p&gt;I'm going off to finish my application. Also, some of my future posts
may disallow comments, depending on whether I want commentary or not.
I'm also going to rename the blog sometime within the next month or two.&lt;/p&gt;
</content><category term="school"></category></entry><entry><title>Google is reading my mind?!</title><link href="2008-11/google-is-reading-my-mind.html" rel="alternate"></link><published>2008-11-14T08:32:00+11:00</published><updated>2008-11-14T08:32:00+11:00</updated><author><name>Peter Ward</name></author><id>tag:None,2008-11-14:2008-11/google-is-reading-my-mind.html</id><summary type="html">&lt;p&gt;I wanted to look up the past tense of &amp;quot;lead&amp;quot; (not Pb) today, and so
typed it into the search box on my Google homepage. Those of you who
have been paying attention will have noticed that autocomplete is now
more promintent among the Google services, so I wasn't suprised …&lt;/p&gt;</summary><content type="html">&lt;p&gt;I wanted to look up the past tense of &amp;quot;lead&amp;quot; (not Pb) today, and so
typed it into the search box on my Google homepage. Those of you who
have been paying attention will have noticed that autocomplete is now
more promintent among the Google services, so I wasn't suprised by the
little suggestions box appearing below my search.&lt;/p&gt;
&lt;p&gt;What caused me the shock was that the first item in the list was &amp;quot;past
tense of lead&amp;quot; - after only typing &amp;quot;past tense &amp;quot;!&lt;/p&gt;
&lt;p&gt;I'm wondering if we're going to be seeing &amp;quot;Google MindReader&amp;quot; coming out
soon... maybe that's what the guys who made &lt;a class="reference external" href="http://www.google.com/mentalplex/"&gt;MentalPlex&lt;/a&gt; have been
working on...&lt;/p&gt;
</content><category term="random"></category></entry><entry><title>HSC is finally over!</title><link href="2008-11/hsc-is-finally-over.html" rel="alternate"></link><published>2008-11-06T09:03:00+11:00</published><updated>2008-11-06T09:03:00+11:00</updated><author><name>Peter Ward</name></author><id>tag:None,2008-11-06:2008-11/hsc-is-finally-over.html</id><summary type="html">&lt;p&gt;YAY!!!!&lt;/p&gt;
&lt;p&gt;(Yes, that's all.)&lt;/p&gt;
&lt;p&gt;kthnxbai&lt;/p&gt;
</summary><content type="html">&lt;p&gt;YAY!!!!&lt;/p&gt;
&lt;p&gt;(Yes, that's all.)&lt;/p&gt;
&lt;p&gt;kthnxbai&lt;/p&gt;
</content><category term="exams"></category><category term="school"></category></entry><entry><title>Software which makes my life easier</title><link href="2008-11/software-which-makes-my-life-easier.html" rel="alternate"></link><published>2008-11-03T09:38:00+11:00</published><updated>2008-11-03T09:38:00+11:00</updated><author><name>Peter Ward</name></author><id>tag:None,2008-11-03:2008-11/software-which-makes-my-life-easier.html</id><summary type="html">&lt;div class="section" id="firefox-3-0"&gt;
&lt;h2&gt;Firefox 3.0&lt;/h2&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;The super-duper cool address bar - search &lt;em&gt;within&lt;/em&gt; URLs and titles. I
think that it's sorted by a 'popularity', based on if you've
bookmarked it, and how many times you've visited the page.&lt;/li&gt;
&lt;li&gt;Save tabs. If you've got a window with multiple tabs open, you can
save them …&lt;/li&gt;&lt;/ol&gt;&lt;/div&gt;</summary><content type="html">&lt;div class="section" id="firefox-3-0"&gt;
&lt;h2&gt;Firefox 3.0&lt;/h2&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;The super-duper cool address bar - search &lt;em&gt;within&lt;/em&gt; URLs and titles. I
think that it's sorted by a 'popularity', based on if you've
bookmarked it, and how many times you've visited the page.&lt;/li&gt;
&lt;li&gt;Save tabs. If you've got a window with multiple tabs open, you can
save them so that you get them opened when you next start Firefox.
This feature was originally just a crash recovery feature, and I
started using it by killing Firefox, and then asking it to restore my
tabs, but now it's a proper feature. It's so much easier to save the
tabs, shutdown my computer and go to bed, wake up the next morning,
and open Firefox ready to continue reading whatever blog/technical
manual/story/whatever I was reading last night.&lt;/li&gt;
&lt;li&gt;All the plugins. Everything from Ubiquity to TabRenamizer (yes, I am
paranoid).&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;div class="section" id="xournal"&gt;
&lt;h2&gt;Xournal&lt;/h2&gt;
&lt;p&gt;Take a &lt;a class="reference external" href="http://xournal.sourceforge.net"&gt;look&lt;/a&gt;. Annotation support may come along sometime for Evince,
but until then, I've been using this to annotate PDFs. Just it's PDF
support and highlighter make it a worthwhile application.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="openoffice-org"&gt;
&lt;h2&gt;OpenOffice.org&lt;/h2&gt;
&lt;p&gt;Sure, it's not perfect: there's a heap of stuff which it icky to use,
and there are plenty of features which I &lt;em&gt;never&lt;/em&gt; use.&lt;/p&gt;
&lt;p&gt;But the main features which I use are generally a lot better than
Microsoft Office. Formatting is actually nice to use - it uses
hierarchial styles, so to change all your body text to &lt;a class="reference external" href="http://bancomicsans.com"&gt;Comic Sans MS&lt;/a&gt;
means only changing the &amp;quot;Default&amp;quot; or &amp;quot;Text Body&amp;quot; style, depending on how
much of your text you want to change.&lt;/p&gt;
&lt;p&gt;I've already &lt;a class="reference external" href="http://flowblok.wordpress.com/2008/05/25/presenter-screen-in-openoffice-30/"&gt;blogged about the Presenter Screen&lt;/a&gt;, and it's a great
step forward.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="workspaces"&gt;
&lt;h2&gt;Workspaces&lt;/h2&gt;
&lt;p&gt;I generally have at least 2 windows open at a time, and generally they
correspond to two different tasks. For example, I might have Pidgin and
OpenOffice.org both open - I'm chatting with a friend in one window, and
typing an assignment in the other.&lt;/p&gt;
&lt;p&gt;These tasks have nothing to do with each other, so why should they be
grouped together by my computer? Workspaces (called Workspaces in Linux,
Spaces in Mac OS X Leopard, and Virtual Desktops in Windows) let you put
windows into seperate areas.&lt;/p&gt;
&lt;p&gt;Less than 90x20 pixels of screen space on my computer to show me a
representation of my workspaces is a wonderful trade-off for the ability
to organise my windows in a logical and sensible way.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="wrapup"&gt;
&lt;h2&gt;Wrapup&lt;/h2&gt;
&lt;p&gt;So, there's a few applications which makes my life easier - feel free to
mention any which you use regularly in the comments.&lt;/p&gt;
&lt;p&gt;I might expand on some of the smaller little 'bits and pieces' of my
desktop later in another blog post, but for now here's two - &lt;a class="reference external" href="http://google.com/ig"&gt;iGoogle&lt;/a&gt;
and &lt;a class="reference external" href="http://www.nongnu.org/mailnotify/"&gt;Mail Notification&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I've got 2 HSC exams tomorrow: Physics and SDD, and then Chemistry on
Thursday. It's still a little scary to think that in 3 days it'll all be
over!&lt;/p&gt;
&lt;/div&gt;
</content><category term="software"></category></entry><entry><title>OpenOffice 3.0 available + HSC Progress</title><link href="2008-10/openoffice-30-available-hsc-progress.html" rel="alternate"></link><published>2008-10-17T06:57:00+11:00</published><updated>2008-10-17T06:57:00+11:00</updated><author><name>Peter Ward</name></author><id>tag:None,2008-10-17:2008-10/openoffice-30-available-hsc-progress.html</id><summary type="html">&lt;p&gt;&lt;a class="reference external" href="http://www.openoffice.org/news/"&gt;http://www.openoffice.org/news/&lt;/a&gt; &lt;a class="reference external" href="http://download.openoffice.org/"&gt;http://download.openoffice.org/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Get it while it's hot!&lt;/p&gt;
&lt;p&gt;On another note, my Business Studies and English Paper 1 have been done
- B.S. went fairly well, I'm not so sure about English.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;&lt;a class="reference external" href="http://www.openoffice.org/news/"&gt;http://www.openoffice.org/news/&lt;/a&gt; &lt;a class="reference external" href="http://download.openoffice.org/"&gt;http://download.openoffice.org/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Get it while it's hot!&lt;/p&gt;
&lt;p&gt;On another note, my Business Studies and English Paper 1 have been done
- B.S. went fairly well, I'm not so sure about English.&lt;/p&gt;
</content><category term="school"></category></entry><entry><title>Analysis of the HSC courses</title><link href="2008-10/analysis-of-the-hsc-courses.html" rel="alternate"></link><published>2008-10-10T03:25:00+11:00</published><updated>2008-10-10T03:25:00+11:00</updated><author><name>Peter Ward</name></author><id>tag:None,2008-10-10:2008-10/analysis-of-the-hsc-courses.html</id><summary type="html">&lt;p&gt;See my last post for a link to my full data set.&lt;/p&gt;
&lt;p&gt;Here's a short list of the HSC courses (Male subjects at top, Female
subjects at bottom).&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;Software Design and Development&lt;/li&gt;
&lt;li&gt;Physics&lt;/li&gt;
&lt;li&gt;Information Processes and Technology&lt;/li&gt;
&lt;li&gt;Mathematics Extension 2&lt;/li&gt;
&lt;li&gt;Mathematics Extension 1&lt;/li&gt;
&lt;li&gt;Chemistry&lt;/li&gt;
&lt;li&gt;Mathematics&lt;/li&gt;
&lt;li&gt;English (Standard)&lt;/li&gt;
&lt;li&gt;Modern History&lt;/li&gt;
&lt;li&gt;English …&lt;/li&gt;&lt;/ol&gt;</summary><content type="html">&lt;p&gt;See my last post for a link to my full data set.&lt;/p&gt;
&lt;p&gt;Here's a short list of the HSC courses (Male subjects at top, Female
subjects at bottom).&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;Software Design and Development&lt;/li&gt;
&lt;li&gt;Physics&lt;/li&gt;
&lt;li&gt;Information Processes and Technology&lt;/li&gt;
&lt;li&gt;Mathematics Extension 2&lt;/li&gt;
&lt;li&gt;Mathematics Extension 1&lt;/li&gt;
&lt;li&gt;Chemistry&lt;/li&gt;
&lt;li&gt;Mathematics&lt;/li&gt;
&lt;li&gt;English (Standard)&lt;/li&gt;
&lt;li&gt;Modern History&lt;/li&gt;
&lt;li&gt;English (Advanced)&lt;/li&gt;
&lt;li&gt;Biology&lt;/li&gt;
&lt;li&gt;English Extension 1&lt;/li&gt;
&lt;li&gt;English Extension 2&lt;/li&gt;
&lt;li&gt;Drama&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The most interesting trend to me is the English / Maths trend. As you
move up the Extension courses, Maths moves towards more males, and
English moves towards more females. Also, the sciences are spread
throughout the list, which is interesting, because at my school I
haven't noticed any major differences between gender ratios in science
classes.&lt;/p&gt;
&lt;p&gt;And finally a note on my HSC study - it was going well until I opened my
computer. :(&lt;/p&gt;
&lt;/p&gt;</content><category term="school"></category></entry><entry><title>From the depths of HSC...</title><link href="2008-10/from-the-depths-of-hsc.html" rel="alternate"></link><published>2008-10-03T10:01:00+10:00</published><updated>2008-10-03T10:01:00+10:00</updated><author><name>Peter Ward</name></author><id>tag:None,2008-10-03:2008-10/from-the-depths-of-hsc.html</id><summary type="html">&lt;p&gt;Study, study and ... ohnoimnotdoingenoughstudy
ifeelreallyguiltyaboutplayingalltheseflashgames
damnyourandallgetoutofmyheadand
oohlookuniversalappletsisgoingtobereleasednextweekand
oohbugsformetosolveand gameenginestowriteand...&lt;/p&gt;
&lt;p&gt;So, if you managed to survive that, allow me to present the last half
hour's efforts: &lt;a class="reference external" href="http://spreadsheets.google.com/pub?key=pV7I0y4ZZscYx-NPUDBR6bA"&gt;Male / Female ratios of the HSC courses&lt;/a&gt;. Source is
the BOS' media guide. Highlighted rows are merely courses which were
relevant to me (i.e …&lt;/p&gt;</summary><content type="html">&lt;p&gt;Study, study and ... ohnoimnotdoingenoughstudy
ifeelreallyguiltyaboutplayingalltheseflashgames
damnyourandallgetoutofmyheadand
oohlookuniversalappletsisgoingtobereleasednextweekand
oohbugsformetosolveand gameenginestowriteand...&lt;/p&gt;
&lt;p&gt;So, if you managed to survive that, allow me to present the last half
hour's efforts: &lt;a class="reference external" href="http://spreadsheets.google.com/pub?key=pV7I0y4ZZscYx-NPUDBR6bA"&gt;Male / Female ratios of the HSC courses&lt;/a&gt;. Source is
the BOS' media guide. Highlighted rows are merely courses which were
relevant to me (i.e.: I have a friend who does course x).&lt;/p&gt;
&lt;p&gt;And my SP3 update just failed because &amp;quot;Permission Denied&amp;quot; (Yes, I am
logged in as an administrative user). It's nice to know that Microsoft
didn't give themselves permission to run an update on my system when I
ask for it. Ahhhhh. That'll show them who's in control!&lt;/p&gt;
</content><category term="school"></category></entry><entry><title>Freezing in Sydney</title><link href="2008-08/freezing-in-sydney.html" rel="alternate"></link><published>2008-08-07T05:03:00+10:00</published><updated>2008-08-07T05:03:00+10:00</updated><author><name>Peter Ward</name></author><id>tag:None,2008-08-07:2008-08/freezing-in-sydney.html</id><summary type="html">&lt;p&gt;I've suspended my quest for a Python website system, in favour of
Drupal. Drupal is an free open-source website engine. It's a modular
design, so you can grab a .tar.gz or similar from their website (or
anywhere, really), extract it to the modules (or themes) directory, and
then run …&lt;/p&gt;</summary><content type="html">&lt;p&gt;I've suspended my quest for a Python website system, in favour of
Drupal. Drupal is an free open-source website engine. It's a modular
design, so you can grab a .tar.gz or similar from their website (or
anywhere, really), extract it to the modules (or themes) directory, and
then run the update script.&lt;/p&gt;
&lt;p&gt;The only things I'd like to see in it are an automatic updater (so that
I don't have to FTP my files to my server), and some pretty looking
graphs.&lt;/p&gt;
&lt;p&gt;You can take a look at it on &lt;a class="reference external" href="http://rccb.org.au/"&gt;a site developed for my local concert
band&lt;/a&gt;. Until I find something this good with Python, Drupal will
definitely be one of my 'liked' bits of software.&lt;/p&gt;
&lt;p&gt;On a completely different note, my HSC trials are almost over (despite
the fact that we're just less than halfway time-wise), with Maths
Extension 1 tomorrow (oh dear...), and Physics next Wednesday (meh!).&lt;/p&gt;
&lt;p&gt;And as to the title, when I started to type this post, it started to
hail. And of course, all my Trial exams are in our school hall, whose
temperature is proportional to the number of people inside the hall.
(Yes, it has heaters, but since when did that matter.)&lt;/p&gt;
</content><category term="exams"></category><category term="school"></category></entry><entry><title>Programming Competitions</title><link href="2008-07/programming-competitions.html" rel="alternate"></link><published>2008-07-27T07:45:00+10:00</published><updated>2008-07-27T07:45:00+10:00</updated><author><name>Peter Ward</name></author><id>tag:None,2008-07-27:2008-07/programming-competitions.html</id><summary type="html">&lt;p&gt;We got through to the finals of ProgComp (&lt;a class="reference external" href="http://cgi.cse.unsw.edu.au/~progcomp/08provisional.php"&gt;results here&lt;/a&gt;), which I was
very happy about, and the NCSS Challenge is starting tomorrow. Yay!&lt;/p&gt;
&lt;p&gt;Also, I've been trying to embed a Python Console into presentation
software. I've spent most of today creating a Java applet which runs the
Python process …&lt;/p&gt;</summary><content type="html">&lt;p&gt;We got through to the finals of ProgComp (&lt;a class="reference external" href="http://cgi.cse.unsw.edu.au/~progcomp/08provisional.php"&gt;results here&lt;/a&gt;), which I was
very happy about, and the NCSS Challenge is starting tomorrow. Yay!&lt;/p&gt;
&lt;p&gt;Also, I've been trying to embed a Python Console into presentation
software. I've spent most of today creating a Java applet which runs the
Python process and has a terminal applet to interact with it. The brick
wall here has been OpenOffice.org, which refused to let me insert a Java
applet. Looking around, there's a bunch of QA issues with this, so I
suspect that it just doesn't work. Looking at my OO.o 3.0 release for
Windows shows that they've removed it from the GUI, so no luck there.
I'll see how I go with NeoOffice tomorrow.&lt;/p&gt;
&lt;p&gt;Note: I saw &lt;a class="reference external" href="http://www.noogz.net/website/blog/life/20080503-TucsLaunch.html"&gt;Chris Neugebauer&lt;/a&gt;'s &lt;a class="reference external" href="http://video.google.com/videoplay?docid=2129832886753561201"&gt;excellent use&lt;/a&gt; of &lt;a class="reference external" href="http://pypi.python.org/pypi/bruce"&gt;Bruce&lt;/a&gt; to
achieve this, but I would prefer it to be done in OO.o/NeoOffice,
because it's already installed on my home computer and the computers I'm
presenting on.&lt;/p&gt;
&lt;p&gt;And lastly a message to Chatswood High students: if you're interested in
programming, and you'd consider doing the NCSS Challenge, which doesn't
require you to know anything about programming, please come along to Ms
Ho's introduction session on Monday lunch (Room 43 or 46), and/or my
training session Friday after school (Room 43).&lt;/p&gt;
</content><category term="programming"></category><category term="school"></category></entry><entry><title>FRisT P0S+!!!1!!!1!</title><link href="2008-06/frist-p0s11.html" rel="alternate"></link><published>2008-06-20T08:13:00+10:00</published><updated>2008-06-20T08:13:00+10:00</updated><author><name>Peter Ward</name></author><id>tag:None,2008-06-20:2008-06/frist-p0s11.html</id><summary type="html">&lt;p&gt;Sorry, I couldn't resist that one.&lt;/p&gt;
&lt;p&gt;I'm just trying out how to blog using gnome-blog. It lets me type my
blog posts from my panel. It's not as pretty as using the wordpress html
editor, but it's a lot simpler and more responsive (on my slow, low
memory computer).&lt;/p&gt;
</summary><content type="html">&lt;p&gt;Sorry, I couldn't resist that one.&lt;/p&gt;
&lt;p&gt;I'm just trying out how to blog using gnome-blog. It lets me type my
blog posts from my panel. It's not as pretty as using the wordpress html
editor, but it's a lot simpler and more responsive (on my slow, low
memory computer).&lt;/p&gt;
</content><category term="random"></category></entry><entry><title>Presenter Screen in OpenOffice 3.0</title><link href="2008-05/presenter-screen-in-openoffice-30.html" rel="alternate"></link><published>2008-05-25T07:54:00+10:00</published><updated>2008-05-25T07:54:00+10:00</updated><author><name>Peter Ward</name></author><id>tag:None,2008-05-25:2008-05/presenter-screen-in-openoffice-30.html</id><summary type="html">&lt;p&gt;Last night, I downloaded the &lt;a class="reference external" href="http://download.openoffice.org/3.0beta/"&gt;beta of OpenOffice 3.0&lt;/a&gt; (+ en-GB language
pack). Then, I got &lt;a class="reference external" href="http://extensions.services.openoffice.org/project/presenter-screen"&gt;Presenter Screen&lt;/a&gt;, and installed it all.&lt;/p&gt;
&lt;p&gt;The result is, that OpenOffice 3.0 has a super-cool presenter mode that
surpasses (IMHO) Microsoft's Presenter Tools for PowerPoint.&lt;/p&gt;
&lt;p&gt;The extension works whenever you run a presentation …&lt;/p&gt;</summary><content type="html">&lt;p&gt;Last night, I downloaded the &lt;a class="reference external" href="http://download.openoffice.org/3.0beta/"&gt;beta of OpenOffice 3.0&lt;/a&gt; (+ en-GB language
pack). Then, I got &lt;a class="reference external" href="http://extensions.services.openoffice.org/project/presenter-screen"&gt;Presenter Screen&lt;/a&gt;, and installed it all.&lt;/p&gt;
&lt;p&gt;The result is, that OpenOffice 3.0 has a super-cool presenter mode that
surpasses (IMHO) Microsoft's Presenter Tools for PowerPoint.&lt;/p&gt;
&lt;p&gt;The extension works whenever you run a presentation with multiple
monitors, and it displays a console on the main screen for presenters to
use. There are 3 views:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Show large previews of the current and next slide.&lt;/li&gt;
&lt;li&gt;Show small previews of the current and next slide, along with the
(scalable) notes for the slide.&lt;/li&gt;
&lt;li&gt;Show all slides, which can be clicked on to switch to any slide.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;All 3 views also display &amp;quot;# of #&amp;quot; for which slide you're on, the current
time (for knowing when morning tea is), and the time since the
presentation was started (for knowing how far overtime you've gone).&lt;/p&gt;
&lt;p&gt;Also provided is a small help window, which details all the keyboard
shortcuts. There really are some insightful ideas for these - typing a
slide number then enter (e.g: &amp;quot;20[Enter]&amp;quot;) will switch you to that slide
number.&lt;/p&gt;
&lt;p&gt;Ever since I came across the notes feature in PowerPoint, I wondered
what the point of it was, since you'd have to print it out anyway. The
'Notes' view in Presenter Screen is great, since you can see the notes
for the current slide, and zoom them so that you can stand far away from
the laptop (and secretly read your speech out!)&lt;/p&gt;
&lt;p&gt;However, the feature which really makes me love this is the 'Slides'
view. I produce presentations for church services every Sunday, and
there's a high probability that the order of events which was given to
us on Saturday is completely wrong, and songs, talks and other
presentation stuff will get randomly swapped.&lt;/p&gt;
&lt;p&gt;In order to manage this confusion, I wrote 3 macros which went onto
buttons in PowerPoint 2000, NextSlide, PrevSlide and ChangeTo
(essentially Int(InputBox()) ), and then used the Slide Sorter view to
monitor slides.&lt;/p&gt;
&lt;p&gt;With Presenter Screen, this task is made so much easier, as it's faster
(my macros took 1/2 second to actually do stuff), and prettier (always a
good thing!). The only problem I've found has been losing the ability to
edit slides on-the-fly. I can still switch back to my main window, but
OpenOffice doesn't like me editing while in Live Mode. Fortunately,
though, I rarely need to do this, and so the benefits out-weigh the
cons.&lt;/p&gt;
&lt;p&gt;Main Presenter Screen:&lt;/p&gt;
&lt;img alt="Large previews of current and next slides" src="static/images/oo3-currnext.jpg" /&gt;
&lt;p&gt;Notes View:&lt;/p&gt;
&lt;img alt="Small previews of current and next slide, and scalable notes." src="static/images/oo3-notes.jpg" /&gt;
&lt;p&gt;Slides View:&lt;/p&gt;
&lt;img alt="Shows all the slides in the presentation, clicking changes to the slide." src="static/images/oo3-slides.jpg" /&gt;
</content><category term="software"></category></entry><entry><title>Music players for GNOME</title><link href="2008-04/music-players-for-gnome.html" rel="alternate"></link><published>2008-04-16T23:20:00+10:00</published><updated>2008-04-16T23:20:00+10:00</updated><author><name>Peter Ward</name></author><id>tag:None,2008-04-16:2008-04/music-players-for-gnome.html</id><summary type="html">&lt;p&gt;&lt;em&gt;Apologies for the short sections, and weird layouting stuff that you
don't usually find in blog posts. Or maybe not.&amp;nbsp; After all, this isn't a
blog post!&lt;/em&gt;&lt;/p&gt;
&lt;div class="section" id="introduction"&gt;
&lt;h2&gt;Introduction&lt;/h2&gt;
&lt;p&gt;After installing Debian Etch on my computer, I started looking at music
players for GNOME.&amp;nbsp; The default for GNOME is Rythymbox, however …&lt;/p&gt;&lt;/div&gt;</summary><content type="html">&lt;p&gt;&lt;em&gt;Apologies for the short sections, and weird layouting stuff that you
don't usually find in blog posts. Or maybe not.&amp;nbsp; After all, this isn't a
blog post!&lt;/em&gt;&lt;/p&gt;
&lt;div class="section" id="introduction"&gt;
&lt;h2&gt;Introduction&lt;/h2&gt;
&lt;p&gt;After installing Debian Etch on my computer, I started looking at music
players for GNOME.&amp;nbsp; The default for GNOME is Rythymbox, however, because
it was so featureless and ugly, I also tried out a few others.&lt;/p&gt;
&lt;p&gt;Later, I upgraded to Debian Lenny, and GNOME 2.22.&amp;nbsp; Yay!&amp;nbsp; So, I then had
access to more music players, and I launched a second investigation.&lt;/p&gt;
&lt;p&gt;The music players I looked at were Banshee, Quod Libet, Exaile, Listen
and Muine.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="analysis"&gt;
&lt;h2&gt;Analysis&lt;/h2&gt;
&lt;p&gt;In no particular order, here is my thoughts on each of these players.&lt;/p&gt;
&lt;div class="section" id="exaile"&gt;
&lt;h3&gt;Exaile&lt;/h3&gt;
&lt;p&gt;Exaile claims to be Amarok, but for GTK.&amp;nbsp; It appeared to be a good idea,
and it's interface was pretty (though perhaps a little &lt;em&gt;too&lt;/em&gt; pretty for
my liking), and is a good player to try out.&amp;nbsp; However, it didn't appear
to play Last.fm streams.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="quod-libet"&gt;
&lt;h3&gt;Quod Libet&lt;/h3&gt;
&lt;p&gt;This was my first choice after discovering the problems in Rythymbox,
and is a good organiser of music, and a generally good player.&amp;nbsp; I've
only had a few crashes with it, which I believe to be the fault of it's
notification icon.&amp;nbsp; However, it also didn't have Last.fm stream
integration.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="listen"&gt;
&lt;h3&gt;Listen&lt;/h3&gt;
&lt;p&gt;Listen had a lot of the features I wanted, but it's GUI didn't feel nice
enough for me, and it's Last.fm stream support was dodgy.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="muine"&gt;
&lt;h3&gt;Muine&lt;/h3&gt;
&lt;p&gt;Muine was too featureless for me, and crashed whilst importing my music
library.&amp;nbsp; Repeatedly.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="and-the-winner-is"&gt;
&lt;h2&gt;And the winner is...&lt;/h2&gt;
&lt;p&gt;Rhythmbox!&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Wait a moment... but didn't you just say that it was featureless and
ugly?&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Well, yes, but that was &lt;em&gt;before&lt;/em&gt; GNOME 2.22.&amp;nbsp; It now is a really nice
player, which manages to import my disorganised music library with no
problems, and has support for things like Podcasts (which I don't use),
and it's Last.fm stream integration is &lt;em&gt;almost&lt;/em&gt; always brilliant.
(Occasionally it will fail to get the song metadata for the current
song)&lt;/p&gt;
&lt;p&gt;It hasn't crashed, and is part of GNOME, so I'm happy.&lt;/p&gt;
&lt;div class="section" id="the-future"&gt;
&lt;h3&gt;The future?&lt;/h3&gt;
&lt;p&gt;Well, the more astute of you will have realised that I've left out
Banshee.&amp;nbsp; That's because it deserves a special section to itself.
Banshee was able to do all the things that I've mentioned about
Rhythmbox, including the nice GUI (although it's Last.fm stream
integration is a little more dodgy), and it &lt;strong&gt;plays video&lt;/strong&gt;!&lt;/p&gt;
&lt;p&gt;So, I think that it's very possible that Banshee will take on the
features from Rythymbox, or vice-versa, and one of them will emerge as
the winner of the GNOME desktop music player.&lt;/p&gt;
&lt;p&gt;Which one?&amp;nbsp; How would I know?!&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
</content><category term="software"></category></entry><entry><title>Exams - Almost the end</title><link href="2008-04/exams-almost-the-end.html" rel="alternate"></link><published>2008-04-05T10:02:00+11:00</published><updated>2008-04-05T10:02:00+11:00</updated><author><name>Peter Ward</name></author><id>tag:None,2008-04-05:2008-04/exams-almost-the-end.html</id><summary type="html">&lt;p&gt;My exams are almost over - done all except Physics. Apart from Chemistry
and Maths Extension 1, I reckon I've done fairly well.&lt;/p&gt;
&lt;p&gt;I've been generally unable to work on my Python web
framework-like-thing, but have been able to have a think about some
ideas for changes.&lt;/p&gt;
&lt;p&gt;This isn't really a …&lt;/p&gt;</summary><content type="html">&lt;p&gt;My exams are almost over - done all except Physics. Apart from Chemistry
and Maths Extension 1, I reckon I've done fairly well.&lt;/p&gt;
&lt;p&gt;I've been generally unable to work on my Python web
framework-like-thing, but have been able to have a think about some
ideas for changes.&lt;/p&gt;
&lt;p&gt;This isn't really a post (like this really isn't a blog) - I'll have a
little more time to write something useful next week. Yay for my 4-day
weekends!&lt;/p&gt;
</content><category term="school"></category></entry><entry><title>π and a T-shirt</title><link href="2008-03/p-and-a-t-shirt.html" rel="alternate"></link><published>2008-03-14T05:30:00+11:00</published><updated>2008-03-14T05:30:00+11:00</updated><author><name>Peter Ward</name></author><id>tag:None,2008-03-14:2008-03/p-and-a-t-shirt.html</id><summary type="html">&lt;p&gt;Well, today was π day!&amp;nbsp; And for those of you who can't see that symbol,
get some better software!&amp;nbsp; (Of course, it &lt;em&gt;could&lt;/em&gt; be my encoding or
something useless, but I doubt it.)&lt;/p&gt;
&lt;p&gt;So, what did I do? Not much. I've got a teacher involved, so that when
we get …&lt;/p&gt;</summary><content type="html">&lt;p&gt;Well, today was π day!&amp;nbsp; And for those of you who can't see that symbol,
get some better software!&amp;nbsp; (Of course, it &lt;em&gt;could&lt;/em&gt; be my encoding or
something useless, but I doubt it.)&lt;/p&gt;
&lt;p&gt;So, what did I do? Not much. I've got a teacher involved, so that when
we get to 22/7, we'll do something. As I said, don't worry about missing
pi day, the 22nd of July is &lt;em&gt;close enough&lt;/em&gt;! (Insert groans here)&lt;/p&gt;
&lt;p&gt;Also, when I got home from school, I found my T-shirt from Google - I
participated in the GHOP contest, and just told them where I live, my
bank information (just joking!), and they sent me out a nice T-shirt for
taking part in the contest - pretty good deal, if you ask me!&lt;/p&gt;
</content><category term="school"></category></entry><entry><title>Robin Hood - A Critisism</title><link href="2008-03/robin-hood-a-critisism.html" rel="alternate"></link><published>2008-03-13T05:39:00+11:00</published><updated>2008-03-13T05:39:00+11:00</updated><author><name>Peter Ward</name></author><id>tag:None,2008-03-13:2008-03/robin-hood-a-critisism.html</id><summary type="html">&lt;p&gt;Those of you who have been watching ABC1 TV on Sunday nights will be
familiar with the BBC production of Robin Hood.&amp;nbsp; I've been rather
impressed with the originality of the program, and I think that it's
been well-adapted, even if the episodes do have the unfortunate effect
of almost …&lt;/p&gt;</summary><content type="html">&lt;p&gt;Those of you who have been watching ABC1 TV on Sunday nights will be
familiar with the BBC production of Robin Hood.&amp;nbsp; I've been rather
impressed with the originality of the program, and I think that it's
been well-adapted, even if the episodes do have the unfortunate effect
of almost always ending how they started.&lt;/p&gt;
&lt;p&gt;However. The last episode (Sunday 9th March), which I believe was titled
&amp;quot;For England!&amp;quot;, had a rather interesting point.&amp;nbsp; Robin sneaks into the
Sheriff's meeting with the Black Knights, and throws daggers at each of
them, only to discover that they have nice little blocks (of wood?)
protecting them.&amp;nbsp; Funny that they predicted where he'd throw the knives,
and that they knew that he would use knives.&amp;nbsp; He could have shot each of
them in the head, and so on.&lt;/p&gt;
&lt;p&gt;But my main problem is when Robin managed to take the clothes off one of
the Black Knights.&amp;nbsp; How come, when he stripped the clothes off himself,
did he miss noticing the block of wood?&amp;nbsp; Surely he would have noticed it
when taking off his outer clothes!&amp;nbsp; In fact, the whole scene felt a
little too much like &amp;quot;something happens, *magic occurs* and everything
has just been reset&amp;quot;.&lt;/p&gt;
&lt;p&gt;That, is my main problem with episodal (my word for shows which are
based around episodes) TV programs - they always finish off where they
started.&amp;nbsp; I've seen it in Doctor Who*, Firefly and now Robin Hood.
It's really getting quite annoying.&lt;/p&gt;
&lt;p&gt;* I haven't been too annoyed with the last 2 series of Doctor Who for
this reason, but most of the earlier episodes had this problem.&amp;nbsp; Mind
you, my problems with the new version of Doctor Who could fill up
another few posts!&lt;/p&gt;
</content><category term="random"></category></entry><entry><title>Spellcaster Documentation</title><link href="2008-02/spellcaster-documentation.html" rel="alternate"></link><published>2008-02-26T05:36:00+11:00</published><updated>2008-02-26T05:36:00+11:00</updated><author><name>Peter Ward</name></author><id>tag:None,2008-02-26:2008-02/spellcaster-documentation.html</id><summary type="html">&lt;p&gt;Spellcaster Documentation for D&amp;amp;U:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Project Statement (done)&lt;/li&gt;
&lt;li&gt;IPO Charts (done)&lt;/li&gt;
&lt;li&gt;Storyboard (done)&lt;/li&gt;
&lt;li&gt;DFDs (done)&lt;/li&gt;
&lt;li&gt;Screen Designs (done)&lt;/li&gt;
&lt;li&gt;Limited Prototype (done)&lt;/li&gt;
&lt;li&gt;Data Dictionary (done)&lt;/li&gt;
&lt;li&gt;Gantt Chart (done)&lt;/li&gt;
&lt;li&gt;Hierarchy Chart (done)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As you can see, this isn't a really verbose post, but enough to see
where I'm at, struggling through the …&lt;/p&gt;</summary><content type="html">&lt;p&gt;Spellcaster Documentation for D&amp;amp;U:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Project Statement (done)&lt;/li&gt;
&lt;li&gt;IPO Charts (done)&lt;/li&gt;
&lt;li&gt;Storyboard (done)&lt;/li&gt;
&lt;li&gt;DFDs (done)&lt;/li&gt;
&lt;li&gt;Screen Designs (done)&lt;/li&gt;
&lt;li&gt;Limited Prototype (done)&lt;/li&gt;
&lt;li&gt;Data Dictionary (done)&lt;/li&gt;
&lt;li&gt;Gantt Chart (done)&lt;/li&gt;
&lt;li&gt;Hierarchy Chart (done)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As you can see, this isn't a really verbose post, but enough to see
where I'm at, struggling through the uselessness of SDD documentation.&lt;/p&gt;
&lt;p&gt;[Edit]Sorry about the abbr tag left open! It's now fixed.[/Edit]&lt;/p&gt;
</content><category term="school"></category></entry><entry><title>SDD Walkthrough</title><link href="2008-02/sdd-walkthrough.html" rel="alternate"></link><published>2008-02-13T07:16:00+11:00</published><updated>2008-02-13T07:16:00+11:00</updated><author><name>Peter Ward</name></author><id>tag:None,2008-02-13:2008-02/sdd-walkthrough.html</id><summary type="html">&lt;p&gt;So I did my SDD Walkthrough for Spellcaster today, and passed
successfully along with (I believe) all the other members of my Software
class.&lt;/p&gt;
&lt;p&gt;I've already done the Initial Prototype, so that's one step from D&amp;amp;U out
of the way, but there's all the nasty documentation to get done …&lt;/p&gt;</summary><content type="html">&lt;p&gt;So I did my SDD Walkthrough for Spellcaster today, and passed
successfully along with (I believe) all the other members of my Software
class.&lt;/p&gt;
&lt;p&gt;I've already done the Initial Prototype, so that's one step from D&amp;amp;U out
of the way, but there's all the nasty documentation to get done.&lt;/p&gt;
</content><category term="school"></category><category term="software"></category></entry><entry><title>PCMS Update</title><link href="2008-02/pcms-update.html" rel="alternate"></link><published>2008-02-11T08:06:00+11:00</published><updated>2008-02-11T08:06:00+11:00</updated><author><name>Peter Ward</name></author><id>tag:None,2008-02-11:2008-02/pcms-update.html</id><summary type="html">&lt;p&gt;Well, development of PCMS is coming along slowly but I'm quite happy
with progress.&lt;/p&gt;
&lt;p&gt;I've separated my modules and templates, which would make it feasible in
the future to change to a different templating system.&lt;/p&gt;
&lt;p&gt;Each module is responsible for a XML node, which itself can include
subnodes (i.e …&lt;/p&gt;</summary><content type="html">&lt;p&gt;Well, development of PCMS is coming along slowly but I'm quite happy
with progress.&lt;/p&gt;
&lt;p&gt;I've separated my modules and templates, which would make it feasible in
the future to change to a different templating system.&lt;/p&gt;
&lt;p&gt;Each module is responsible for a XML node, which itself can include
subnodes (i.e. other modules), or just data in it's attributes.&lt;/p&gt;
&lt;p&gt;My &lt;em&gt;current&lt;/em&gt; templating system is based on just matching each tag name
with a template in the current theme's folder.&amp;nbsp; It was originally
designed to be used with XSLT, but since Python 2.4/2.5 doesn't come
with a XSLT processor included by default, I decided to seperate the
templating side of things, and let it use whatever system you decide to
implement.&lt;/p&gt;
&lt;p&gt;This change was responsible for breaking most of the code in PCMS, which
has caused some backtracking, but it's not that much, and the migration
to the new XML system is really quite easy to implement.&lt;/p&gt;
&lt;p&gt;--Update--&lt;/p&gt;
&lt;p&gt;Just tonight I finished off support for viewing articles (which are
generic posts/information bits), and also added in the support for
styles (which are stylesheets used by themes).&amp;nbsp; Next is moving the
styles into the themes folder.&lt;/p&gt;
&lt;p&gt;Once again, if I'm online, you can look at my &lt;a class="reference external" href="http://flowblok.selfip.net:8001/~flowblok/PCMS"&gt;development version&lt;/a&gt;.&lt;/p&gt;
</content><category term="pcms-programming"></category><category term="software"></category></entry><entry><title>Today's lessons</title><link href="2008-02/todays-lessons.html" rel="alternate"></link><published>2008-02-04T08:51:00+11:00</published><updated>2008-02-04T08:51:00+11:00</updated><author><name>Peter Ward</name></author><id>tag:None,2008-02-04:2008-02/todays-lessons.html</id><summary type="html">&lt;p&gt;Well, today I did a number of things.&lt;/p&gt;
&lt;p&gt;One useful thing was giving a mini-lecture on CGI (with a direction
towards Python), and Python 3k to my software class.&amp;nbsp; They really don't
understand the usefulness of one-liners in programming.&amp;nbsp; Hmm.&amp;nbsp; Maybe
'usefulness' is the wrong word.&lt;/p&gt;
&lt;p&gt;In other news, I've …&lt;/p&gt;</summary><content type="html">&lt;p&gt;Well, today I did a number of things.&lt;/p&gt;
&lt;p&gt;One useful thing was giving a mini-lecture on CGI (with a direction
towards Python), and Python 3k to my software class.&amp;nbsp; They really don't
understand the usefulness of one-liners in programming.&amp;nbsp; Hmm.&amp;nbsp; Maybe
'usefulness' is the wrong word.&lt;/p&gt;
&lt;p&gt;In other news, I've named my 2nd USB disk partition &amp;quot;The Horse&amp;quot;.&amp;nbsp; It
makes absolutely no sense unless you mount it (Massive groans from the
not audience).&amp;nbsp; (1st partition is a persistent filesystem for my Ubuntu
Live CD - it still is not working well, but I haven't really tinkered
with it yet)&lt;/p&gt;
&lt;p&gt;And today's final topic is my software project, of which I have to
present a practical way of implementing my game in a 10 minute interview
with my teacher.&amp;nbsp; Unfortunately, our computing rooms are exclusively Mac
OS X, and thus I have a limited idea of how to demo my work on them. I
know that I can use PyGTK on Windows &amp;amp; Linux, but unfortunately the only
real way for Mac is to rewrite the GTK libraries wrapping Tkinter.
Suggestions welcome!&lt;/p&gt;
&lt;p&gt;And I'm going to try and link up this not blog to some blogs of friends
&amp;amp; people I know.&lt;/p&gt;
</content><category term="school"></category></entry><entry><title>Projects</title><link href="2008-02/projects.html" rel="alternate"></link><published>2008-02-02T05:49:00+11:00</published><updated>2008-02-02T05:49:00+11:00</updated><author><name>Peter Ward</name></author><id>tag:None,2008-02-02:2008-02/projects.html</id><summary type="html">&lt;p&gt;OK, here's an update on some of my projects.&lt;/p&gt;
&lt;p&gt;I'm currently working on a Python-based CMS. It's going slowly at the
moment, due to my slow algorithm design. The latest version is available
&lt;a class="reference external" href="http://flowblok.selfip.net:8001/~flowblok/PCMS"&gt;on my private server&lt;/a&gt;, which probably isn't online at the moment.&lt;/p&gt;
&lt;p&gt;I've also started work on my …&lt;/p&gt;</summary><content type="html">&lt;p&gt;OK, here's an update on some of my projects.&lt;/p&gt;
&lt;p&gt;I'm currently working on a Python-based CMS. It's going slowly at the
moment, due to my slow algorithm design. The latest version is available
&lt;a class="reference external" href="http://flowblok.selfip.net:8001/~flowblok/PCMS"&gt;on my private server&lt;/a&gt;, which probably isn't online at the moment.&lt;/p&gt;
&lt;p&gt;I've also started work on my HSC SDD personal project work, which is a
PyGTK implementation of Spellcaster (aka Waving Hands, Firetop Mountain,
Warlocks). I highly recommend you take a look at one of the
implementations (Warlocks is the latest), and have a try of the game.&lt;/p&gt;
</content><category term="pcms-programming"></category><category term="programming"></category><category term="school"></category></entry><entry><title>Not a first post</title><link href="2008-02/not-a-first-post.html" rel="alternate"></link><published>2008-02-02T04:50:00+11:00</published><updated>2008-02-02T04:50:00+11:00</updated><author><name>Peter Ward</name></author><id>tag:None,2008-02-02:2008-02/not-a-first-post.html</id><summary type="html">&lt;p&gt;This is obviously not the first post of my ^(blog). It's where I say
^(FRSIT!!111!!!!!!!)&lt;/p&gt;
&lt;p&gt;So. Firstly, about myself. If you're not aware, my name is Peter Ward,
and I'm a (coder|programmer), who went to &lt;a class="reference external" href="http://www.ncss.edu.au/"&gt;NCSS&lt;/a&gt; 2008 recently.&lt;/p&gt;
&lt;p&gt;I like w3c valid XHTML / CSS, and have a problem …&lt;/p&gt;</summary><content type="html">&lt;p&gt;This is obviously not the first post of my ^(blog). It's where I say
^(FRSIT!!111!!!!!!!)&lt;/p&gt;
&lt;p&gt;So. Firstly, about myself. If you're not aware, my name is Peter Ward,
and I'm a (coder|programmer), who went to &lt;a class="reference external" href="http://www.ncss.edu.au/"&gt;NCSS&lt;/a&gt; 2008 recently.&lt;/p&gt;
&lt;p&gt;I like w3c valid XHTML / CSS, and have a problem with my &amp;quot;x&amp;quot; key on my
keyboard, which makes typing words with &amp;quot;x&amp;quot; very difficult.&lt;/p&gt;
&lt;p&gt;Rumours that I am starting a blog are untrue and completely unfounded.&lt;/p&gt;
</content><category term="random"></category></entry></feed>