Fahrenheit 451

Farneheit 451

by Ray Bradbury, 1953.

A tale of future urban America, in which political or critical thought is suppressed by overwhelming popular entertainment and by burning all books, told with Bradbury's typical simple and beautiful prose. Rarely has a tale suffused me with such peace and tranquillity than my making the transition from the frantic, advertising-saturated dystopia into the moonlit fields and abandoned railroads of the closing chapters.

The novel has a curiously bifurcated message for modern times. We would seem to be even more overwhelmed by noisy and crass distractions of types unimaginable in Bradbury's time, often unable to pry ourselves free from a myriad compelling sources of entertainment.

However, for all the negative impact this internet may have had on our peace of mind and our attention spans, it has also provided tremendous benefits that to a great extent defuse the novel's warnings. Books are now infinitely reproduceable. Dissenting thought or new ideas can easily be published and read by millions, free from fear of retribution by employers or governments, those weary giants of flesh and steel. Writing by any person in the world is routinely disseminated to and from the furthest corners of the globe in seconds. Instantly findable. Free from censorship. Archived forever. Unburnable.

This ease-of-use provides each of us with exactly what we demand of it. Just as Bradbury foretells, those of a studious mind, those with legitimate concerns, those with a desire to change the world or make something more out of life, these people will always gather to talk and plan and act. Only now, they can do so globally, instantly and effectively. For each of us, our salvation requires only that we choose to save ourselves.

Rating:

10/10 if you fancy a bit of charming retro SF with a beautiful turn of phrase

0/10 if you think worrying about fulfilment in life, political affairs, or meaning in life is a big waste of time, especially if it's done by way of science-fiction metaphors and hyperthetical future scenarios. Let's go shopping!

Legend of Zelda : The Phantom Hourglass

Nintendo, 2008

Nintendo spin off a fourteenth iteration of the familiar and oh-so-beloved franchise. I'd only previously played the preposterously-sub-titled Ocarina of Time on the N64, of which I retain fond, sepia-coloured memories.

I found Phantom Hourglass to be engaging, and played it to a thoroughly jolly completion over the course of a couple of months. The gameplay and plot are a tad more casual and lightweight than Ocarina, as befitting play snatched while on-the-go on the handheld platform.

Using the DS stylus to control Link is delightfully transparent and intuitive. Just as for a mouse, however, expert users will eventually eschew it for many actions, preferring the speed, precision and tactility of distinct physical buttons.

On the down side, some of the puzzles are downright contrived. After introducing a handy little map, on which you can scrawl notes and reminders for yourself, the game then promptly takes it away again for exactly the levels on which it would be of most use, proclaiming 'you can't use the map here, some sort of magic prevents it.' Right. Up yours.

Equally intrusive, the dialog is entirely Nintendo-esque cutesy Japanese schmultz, and it would have improved the game no end for it to have been entirely absent.

Such niggles are few and far between, though. Time for another go of the newfangled rating system.

Rating: Place yourself somewhere between...

10/10 if you're into a deftly executed handheld riff on the Zelda series, beautifully integrated with the affordances and sensibilities of the hosting hardware in that way that only Nintendo can do. Lots to love here.

0/10 if you're done with revisiting timeworn ideas, even ones as compelling and charming as this, and yearn for a resurgence in experimental originality to kick the gaming industry out of one or two ruts (such as having a blind spot for crappy dialog.)

Shadow of the Colossus

PS2, Team ICO, 2005.

As is common for a title released in the autumn years of a console's lifetime, the programmers at Team ICO have mastered the PS2 hardware, and wring the utmost from it for this virtuoso performance.

The gameplay consists of nothing more than a series of sixteen boss fights between your character and a succession of gigantic mythical creatures, each rising from the landscape upon your approach. Half stone, half furry flesh, these colossi shake the earth as they walk, and facing up to them, armed with nothing but a sword and a bow, elicits a genuine rush of fear, firstly as their size and magnificence become apparent, then as they stand up, and then, finally, terrifyingly, as they see you. Like moments from a piercing dream, playing this game is an experience that will not be forgotten.

It's in this emotive ability that the game's strength lies, and the initial adrenaline is only the most coarse example of it. Those disposed to gallop directly in the hinted direction of the the next objective would be missing out on large parts of what Colossus has to offer, and would be well advised to ease back, enjoy the view, and take some pleasure from the journey. For fully half your time will be spent traversing this epic landscape in search of your next quarry, and the landscape is fantastic, in every sense. Across open plains, rent by canyons and gorges spanned by ancient, elegant arches, past thundering waterfalls, though shafts of sunlight across boulder-strewn forest floors. Cutting through mighty, vine-covered ruins, now occupied only by lizards and hawks.

This tranquil exploration is punctuated by the excitement of the battles, forming a delightful sense of anticipation. The battles themselves are gripping and rewarding, and provides a enough of a challenge to stimulate a corresponding level of victorious elation upon bringing down each of the colossi. Interestingly, as the game progresses, the tone of the animations and solemn music begin to subtly expose these victories for what they are: the slaughter of a magical, unique and irreplaceable creature, leaving the world a more forlorn and empty place. This effectively leverages the player's third-person complicity in the killings, eventually evoking feelings of guilt for your premeditated attacks on your ultimately vulnerable victims. Reflective of your character's determination, heedlessly pursuing an outcome that is hinted at in his own deteriorating appearance, you have no choice but to carry on.

Overlaid on this evolving cyclic structure, a delicate narrative is unobtrusively woven, complete with possibly the most affecting death of a character in any videogame to date. The story culminates by exquisitely dovetailing with the opening of the game Ico (PS2, Team Ico, 2001), making this Ico's prequel (and finally explaining that game's protagonist's stigmatic horns.)

I've said it before and I'll say it again. Videogames are a nascent, immature medium. None amongst us can so much as dream of what they will one day be capable of. Look upon works such as this as history in the making, the first time a human has wrought a creative endeavour such as this, pushing forward the expectations and outlook of the whole industry, and spellbinding a generation.

Alright, we're done here. You may leave. Time for me to begin my experiments with a new subjectivity-proof rating system:

Rating: Position yourself appropriately somewhere between:

10/10 if you wish to enjoy the state of the art in what videogames as a medium are capable of.

0/10 if you are incapable of appreciating beauty or have no soul.

Beginning Game Development With Python and PyGame

by Will McGugan

Introduces Python, PyGame and game development ideas from the ground up, in a really pleasant writing style which is both unintimidating, clear and covers all the bases. It's definitely a book for beginners though, walking through high-school topics like bitmaps and vectors and matrices, but having laid the foundations, it works up to an excellent hands-on overview of using OpenGL in the final three chapters, including lighting, textures and fog. It makes all the topics very accessible, and does an incredible job of putting all the various topics to work together to form a cohesive and illustrating whole.

Nothing extremely hardcore is covered though. An 'ants chasing spiders' state machine is probably the most involved example in here, barring the latter parts of the OpenGL section, with four-state creatures beautifully explained and presented. If you're already doing OpenGL or game development, even as a piss-poor amateur like myself, you likely already know many of the things between these covers, aside from the PyGame specifics, which are generally transparent enough to figure out yourself.

Update: In the months since I wrote this review, I've found my thoughts returning to this book time and again, and have come to the conclusion that I gained more from its extremely clear exposition than I realised at the time. I'm revising the review scores significantly upwards to reflect this.

Rating: 10/10 for beginners. 5/10 for experts. Consider yourself somewhere between the two.

Wall-E

wall-e

Director: Andrew Stanton

I resisted for quite a while, expecting cloying superficiality. I was wrong. Or at least I like this kind of cloying superficiality. Clearly made with love by a truly dedicated army of master artisans. Featuring sound effects from the same guru sound guy who worked on the original Star Wars, along with an animation team who watched every single Buster Keaton and Charlie Chaplin movie over their lunchtimes, to inspire them in the ways of visual story telling. With a hardcore Larry Niven reference and a serious environmental message in the background to boot. Huge fun. I watched it twice.

Rating: 8/10 - Thoroughly engaging, emotive and humorous.

Expert Python Programming

expert-python-programming

by Tarek Ziadé, 2008. (on Packt)

Publisher Packt were nice enough to send me a copy of this, so I'm completely biased. Fortunately for everyone, I really liked it. But I have to say that. But I really did!

. . .

I've been using Python for a couple of years now, but only on a single project, so while there are parts of it that I know very well, there is doubtlessly a lack of cross-fertilisation in the things I am exposed to. So I was looking forward to this book.

Surprisingly, for such a straightforward-sounding title, it is not at all what I expected.

What I expected was analyses and illustrations of using Python's more powerful features: dynamic designs; creating classes on the fly; functional programming styles; closures and metaclasses.

Sure enough, there is an early couple of chapters devoted to advanced language features. First up, iterators, and generator expressions, and then the .send, .throw and .close methods on a generator, which induce the yield statement to return values or raise exceptions. This is then used to handily illustrate coroutines as a method of co-operative multi-tasking without the calamity involved with getting all multi-threaded. It's exactly the sort of feature I'd pondered writing for myself for a personal project, oblivious that the language provides it out of the box.

Other low-level topics covered include the indispensable itertools module, interesting uses of function decorators, best practices for subclassing built-in types, sensible use of descriptors and properties, understanding method resolution order and using super, the often-overlooked slots, and finally meta-programming and metaclasses.

Interestingly, this list has only one item of overlap with my expectations. Tarek has done a good job of choosing important but sometimes overlooked topics, and while, inevitably, I was very familiar with some of the things he talked about, other sections were complete revelations for me.

However, this is only chapters 2 and 3! The rest of the book expands in scope beyond Python the language, to look at the environments and tools that make up the Python ecosystem. In a way, this sounded less intriguing to me than the computer-science oriented exploration of language features that I had expected. But having finished the book, I now realise that it was exactly what I needed.

The opening chapter goes through installing Python - a topic which I didn't think needed discussion. But Tarek goes on to cover using MinGW and MSYS to set up a credible command-line environment under Windows. I've always used Cygwin for this in the past, and trying out MSYS (coupled with the project Console) is a breath of fresh air.

This cross-platform development environment is then rounded out a little by installing and using things like setuptools, and some thoughtful notes on integrating Python development into editors like Vim and Emacs, or an IDE like Eclipse.

The rest of the book covers some of the major tools in the Python world.

I've never previously been forced to get to grips with Distutils. Applying lessons from the book to a personal project got me completely up to speed with using Distutils to create packages, making source and binary distributions, using eggs, and distributing dependant packages that together make up an application. The only thing really missing from this is maybe generating stand-alone executables using py2exe (for Windows) or py2app (for Macs), although this idea is mentioned in passing.

The following chapters give competent overviews of a wide variety of topics:

8. Version control systems: centralised, illustrated by Subversion, or distributed like Mercurial. Continuous integration using Buildbot. Presumably you will either already know these inside-out or else will lap them up hungrily.

9. Waterfall, spiral and iterative project life cycles. I'm not sure that waterfall is really used by anyone except in case studies of 'what not to do', and I'm also not sure how you could be a developer without being aware of this, but maybe that's precisely the point: You can't be a developer if you don't do this. This chapter then covers setting up an instance of Trac and using it to manage a project's deliverables, defects, and milestones.

10. Documenting a project using ReStructuredText and Sphinx, and including hints on good technical writing. This caused me to completely revamp the content of my small personal project's documentation, and as a result it is both much improved, and shorter to boot. Wins all round.

11. Test-Driven Development. This chapter would be a superb overview of the topic for someone who didn't know about TDD. Although I use TDD extensively at work, I've never used nose, fearing that getting to grips with it might be too intrusive or disruptive. In fact, it is seamless to begin using it in a small way and slowly extend into its more advanced features as and when you need them.

12. Optimisation : General principles (ie. don't) and profiling techniques. I had never used the cProfile module described here, having just whipped up homespun profilers on-the-spot whenever I needed them, and it's a valuable addition to my arsenal.

13. Optimisation : Solutions. Big O notation. Correct use of different collection types. Multi-threading, multi-processing, caching. Not much that is tremendously new to me here, but it is obviously a huge topic to cover in a a single chapter, and it covers the bases competently.

14. Design Patterns, and how they apply (or don't) to Python. It has been said that design patterns, as fundamental and necessary as they are to a software engineer's mindset, are a symptom a language's lack of [expressivity]{#query .query}. You shouldn't have to write huge gobs of code to express simple conceptual relationships. Although Tarek never explicitly says this, to some extent it is born out by this chapter. Trivial (and much-maligned) ideas like the Singleton, after a page or two of alternate implementations, boil down to simply 'use a module', i.e. zero lines of code. Slightly more complex patterns such as Visitor, are only a few lines. It is still useful to discuss and name patterns, but on the whole reference implementations are so trivial as to be unnecessary, except perhaps as the most concise and precise method of illustration.

The book overall, then, is a grab-bag of different topics. Each chapter could clearly be expanded into one or more whole books. As a result, no part can be covered in very great depth, but Tarek does an admirable job of getting the reader up to speed enough in each area that they can be self-sufficient and direct their own learning from that point.

As as result of the broad scope of the book it serves as a brilliant annotated laundry list of 'things the Python developer ought to know'. While there will doubtlessly be some sections that you are already deeply familiar with, if you have any interest in discovering your own deficiencies as a Python programmer, I can highly recommend it.

Rating: 7.5/10 - Made concrete improvements to the way I work every day.

Using assertAlmostEquals to compare to N Significant Figures

I want a Python unittest.assertAlmostEquals that compares numbers to N significant figures, instead of N decimal places.

test.assertAlmostEquals(1e-8, 5e-14) # passes

Even though these two numbers differ by a factor of 5 million, when comparing them to seven decimal places (assertAlmostEquals default behaviour) they are equal.

I've made a first stab this by overriding assertAlmostEquals on a subclass of TestCase, which retains the behaviour of the original, but provides an extra keyword parameter sigfig.

The code and a unittest for it are below. One of the tests currently fails, due to floating-point inaccuracy problems. I'll have to look at it some more. Isn't this problem already solved somewhere?

import math
import unittest

class TestCase(unittest.TestCase):
    "Augments the assert methods of a unittest.TestCase"

    def __str__(self):
        return "%s.%s" % (self.__class__.__name__, self.__testMethodName)

    def _assertAlmostEquals(self, a, b, sigfig=7):
        if sigfig < 1:
            raise ValueError("assertAlmostEquals: 'sigfig' must be >=1")

        magarg = a or b
        if (magarg == 0) or ((a - b) == 0):
            return

        magnitude = int(math.floor((math.log10(magarg))))
        margin = 10**(magnitude - sigfig + 1) / 2.0
        diff_gt_margin = abs(a - b) - margin > -1e-15
        if diff_gt_margin:
            msg = '%s != %s to %d significant figures' % (a, b, sigfig)
            raise AssertionError(msg)

    def assertAlmostEquals(self, a, b, places=None, sigfig=None):
        if places is not None and sigfig is not None:
            raise ValueError("assertAlmostEquals: "
                "cannot specify both 'places' and 'sigfig'")
        elif places is not None:
            unittest.TestCase.assertAlmostEquals(self, a, b, places)
        elif sigfig is not None:
            self._assertAlmostEquals(a, b, sigfig)
        else:
            unittest.TestCase.assertAlmostEquals(self, a, b)

    def assertRaisesWithMessage(
        self, exceptionType, message, func, *args, **kwargs):

        expected = "Expected %s(%r)," % (exceptionType.__name__, message)
        try:
            func(*args, **kwargs)
        except exceptionType, e:
            if str(e) != message:
                msg = "%s got %s(%r)" % (
                    expected, exceptionType.__name__, str(e))
               raise AssertionError(msg)
         except Exception, e:
             msg = "%s got %s(%r)" % (expected, e.__class__.__name__, str(e))
             raise AssertionError(msg)
         else:
             raise AssertionError("%s no exception was raised" % expected)

And a test:

import unittest

from TestCase import TestCase

class TestableTestCase(TestCase):

    def testNothing(self):
        pass # pun intended

class AssertAlmostEqualsTest(TestCase):

    def setUp(self):
        self.test = TestableTestCase("testNothing")

    def testPreservesUnittestBehaviour(self):
        a = 0.1234567
        eps1 = 0.000000049
        self.test.assertAlmostEquals(a, a + eps1)
        self.test.assertAlmostEquals(a, a - eps1)

        eps2 = 0.000000051
        self.assertRaises(AssertionError,
            self.test.assertAlmostEquals,
            a, a + eps2)
        self.assertRaises(AssertionError,
            self.test.assertAlmostEquals,
            a, a - eps2)

    def testPreservesUnittestBehaviourWithPlaces(self):
        a = 0.1234567
        eps1 = 0.000049
        self.test.assertAlmostEquals(a, a + eps1, places=4)
        self.test.assertAlmostEquals(a, a - eps1, places=4)

        eps2 = 0.000051
        self.assertRaises(AssertionError,
            self.test.assertAlmostEquals,
            a, a + eps2, places=4)
        self.assertRaises(AssertionError,
            self.test.assertAlmostEquals,
            a, a - eps2, places=4)

    def testRaisesIfPlacesAndSigfigSpecified(self):
        self.assertRaisesWithMessage(ValueError,
            "assertAlmostEquals: "
                "cannot specify both 'places' and 'sigfig'",
            self.test.assertAlmostEquals,
            0, 0, places=4, sigfig=4)

    def assertSigFig(self, factor):
        self.assertRaises(AssertionError,
            self.test.assertAlmostEquals,
            1.23 * factor, 1.225 * factor, sigfig=3)
        self.assertAlmostEquals(1.23 * factor, 1.2251 * factor, sigfig=3)
        self.assertAlmostEquals(1.23 * factor, 1.2349 * factor, sigfig=3)
        self.assertRaises(AssertionError,
            self.test.assertAlmostEquals,
            1.23 * factor, 1.235 * factor, sigfig=3)

    def testSigfigNormal(self):
        self.assertSigFig(1)
        self.assertSigFig(1e-6)
        self.assertSigFig(1e+6)
        self.assertSigFig(1e+12)
        self.assertSigFig(1e-12)

    def testSigFigOfTwo(self):
        self.assertRaises(AssertionError,
            self.test.assertAlmostEquals,
            1.2, 1.15, sigfig=2)
        self.assertAlmostEquals(1.2, 1.151, sigfig=2)
        self.assertAlmostEquals(1.2, 1.249, sigfig=2)
        self.assertRaises(AssertionError,
            self.test.assertAlmostEquals,
            1.2, 1.25, sigfig=2)

    def testSigFigOfOne(self):
        self.assertRaises(AssertionError,
            self.test.assertAlmostEquals,
            1, 0.5, sigfig=1)
        self.assertAlmostEquals(1, 0.51, sigfig=1)
        self.assertAlmostEquals(1, 1.49, sigfig=1)
        self.assertRaises(AssertionError,
            self.test.assertAlmostEquals,
            1, 1.5, sigfig=1)

    def testRaisesIfSigFigZeroOrLess(self):
        self.assertRaisesWithMessage(ValueError,
            "assertAlmostEquals: 'sigfig' must be >=1",
            self.test.assertAlmostEquals,
            1, 1, sigfig=0.99)
        self.assertRaisesWithMessage(ValueError,
            "assertAlmostEquals: 'sigfig' must be >=1",
            self.test.assertAlmostEquals,
            1, 1, sigfig=0)
        self.assertRaisesWithMessage(ValueError,
            "assertAlmostEquals: 'sigfig' must be >=1",
            self.test.assertAlmostEquals,
            1, 1, sigfig=-1)

    def testHandlesArgsOfZero(self):
        self.assertAlmostEquals(0, 0)
        self.assertAlmostEquals(0, 0, places=4)
        self.assertAlmostEquals(0, 0, sigfig=4)

    def testHandlesIdenticalArgs(self):
        self.assertAlmostEquals(1.234, 1.234)
        self.assertAlmostEquals(1.234, 1.234, places=4)
        self.assertAlmostEquals(1.234, 1.234, sigfig=4)

if __name__ == "__main__":
    unittest.main()

A Science-Fiction Omnibus

a-science-fiction-omnibus

Editor: Brian Aldiss, 2007

A collection of short stories from 1941 to 2006. Most of my favourites in here are ones I've already read before: Asimov's Nightfall, Bear's Blood Music, Aldiss' own Poor Little Warrior!

Of the ones that are new to me, not many seemed to have much appeal. A few nicely crafted exceptions are worthy of special mention: Ward Moore's Lot, a tale of a family 's departure from home in the station wagon as civilisation ends around them; James Tiptree's And I Awoke and Found Me Here on the Cold Hill's Side, a tragic and visceral vision of humanity's emotional and charismatic failings amongst the sleek, sophisticated races of the galaxy.

Amongst the others, there are instances where the collection's vintage lets it down, ideas that were once exciting and cutting edge, which have been dulled by the passage of time and events, to expose the lackluster writing beneath. Multiple tales of supreme computers achieving godhood, for better or for worse. Tales of malfunctioning machinery that barely held my interest.

Imagine, then, my surprise, when the final story of the collection, John Crowley's Great Work of Time, of which I have never heard until now, turned out to be an absolute triumph. 75 pages of beautifully crafted prose, all by itself it is easily worth the cost of this collection, and a more than worthy reward for the time and effort of reading all the other stories in it.

This final story is yet-another-novel-take on time-travel - at once bold and understated, a nostalgic and wistful story of alternate histories and the motives of the people charged with affecting the critical junctures of human civilisation. An exquisitely crafted scene lies embedded within the story's amber perfection, in which Cecil Rhodes' 23 year-old would-be assassin is recruited for the job by none other than his elderly self, who persuades with a moving and cathartic retelling of how the events in question went for him. The reader at this point is already steeped in the bitter-sweet consequences that the outcome will have for each of their lives, for the mysterious organisation under who's auspices they act, for the British Empire's fading hopes of uniting the world under a single banner versus the looming spectre of future World Wars - it is so poignant and well constructed and performed that I immediately went back and read the whole story over again. Absolutely bloody marvellous.

Rating 9/10, if you only read the last story in the book. Diluted otherwise.

Update: A Great Work of Time is available as a stand-alone 75 page book on Amazon Kindle for £2.99, and Calibre with the DeDRM plugin will convert this into an epub which can be read on any device.

City at the End of Time

city-at-the-end-of-time

by Greg Bear, 2008

Smashing, but not life changing. Another great ripping yarn. I find myself with less and less patience for such a thing of late, and I'm not sure how to tell if that's due to the quality of the works in question, or due to a change in my own temprament.

I would, in years gone by, have listed Greg Bear as one of my favourite science-fiction authors, but my rating for this one is slipping dangerously close to the 'not sure it was worth my time' level. I felt the same way about The Algebraist, by Ian Banks, who I previously have also loved to death.

I guess authors have their good and bad seasons just like anyone else. But I'm tempted to go back and flick through Eon and Eternity again, to see if they really were as well-crafted as I remember them. Whether I love or loathe the exercise should decide the issue.

Rating: 6.5/10 Why all the X-point-five scores recently?

The Difference Engine

differenceengine

by William Gibson and Bruce Sterling, 1990

A quick re-read, inspired by a recent visit to the Science Museum, where we were enthralled by our meanderings around various astonishing artifacts:

  • The rickety-looking Apollo 10 capsule, charred from re-entry, having journeyed to within 9 miles of the moon and back again.

  • Stevenson's Rocket, not, as I had previously understood, the first steam-powered railway locomotive, just a revolutionary improvement in design, capable of the unheard-of speed of 29 miles an hour.

  • Watson and Crick's original 6-foot mechano model of the double-helix structure of a DNA molecule (or at least the original bits of it, re-assembled at Kings around 1990), constructed between LSD fuelled revelry and Sunday afternoons' quiet boozing pondering the meaning of life. Not a bad lifestyle, it has to be said.

  • Charles Babbage's Difference Engine number 1, or at least remnants of it. This is the arithmetic precursor to his subsequent, and more ambitious, analytical engine, the conception of which was a precient stroke of genius, the world's first programmable computer. Lacking electronics, it was implemented in a purely mechanical manner, a 40 year labour of which he pursued until his death.

I was surprised to learn that the failing of the device was not, as I had previously heard, due to the inadequate manufacturing tolerances of the time. Recent examinations have demonstrated these to be more than adequate. Instead, the lengthy and costly extensions to the project, leading to its eventual failure in an ocean of debts and soured relationships is nowadays blamed on the project suffering from 'inappropriate management'. Interesting to see the nascent software industry starting out as it was destined to continue.

It was, of course, this last item at the museum which inspired me to pick up The Difference Engine paperback again, an alternate history novel in which the authors indulge the fantasy that Babbage's project had been a success, and the British Empire, in the upheaval of a steam-powered information processing era, underwent a series of meritocratous social revolutions, and as a result was better equipped to continue its expansion across the globe.

Rating 7.5/10 - a splendid ripping yarn.