Reward vs. Fun

Most modern games don’t ask the player to interpret the wry smile on another character’s face. The narratives are built on enjoyment and, as games are chiefly meant for recreation rather than as a test of emotional intelligence, there’s nothing wrong with that. Old-time RPGs did not adhere to that idea: back then, “fun” was what you made it and you were not guaranteed to have any.

Does it matter if people don’t finish games any more?

I found Pillars of Eternity to be a great game. Like Baldur's Gate, it's a game of people. And Gods, yes, but mostly people. I find stories have greater depth where they concern the interactions of people; Gods and demons allow for simple narratives -- God/Demon wants to take over the world and must be stopped! -- that are less satisfying.

Far more so than Baldur's Gate, Pillars of Eternity is a game of unclear moral choices: the characters driving quests in either direction have motives that are often identifiable with. Rather than being driven by obvious hero/villain semantics, your choices are driven more by things like one's own bent towards revolutionary firebrands or stable authority.

I ended up avoiding either of the completion paths of one of PoE's story line, as I couldn't decide which party to side with. It turned out even leaving the quest line incomplete led to a note in the end story about the results of the my leaving the status quo.

I'm currently playing Dragon Age: Origins. While it's undeniably a great game, its one-dimensional monsters and obvious villains felt like a step backwards from PoE. Reading parts of walkthroughs, there is subtlety residing in side quests but the main quest appears more morally straight-forward.

While clearly PoE is not difficult moral choice after difficult moral choice, I enjoyed a slightly deeper experience. It was as much reward as fun.

One to Watch update submitted to Apple

In One to Watch's film querying at risk I mentioned that the Freebase API that One to Watch relies on was going offline on June 30th.

I've submitted an update to Apple which migrates the application to use TMdB's API. I've been using this for the past couple of months without issue, and it was getting close to June 30th, so I decided it needed to be submitted.

Please accept this update :)

Setting up git-clang-format

I am draconian when it comes to code formatting. Combined with an "I’ll know it when I see it" attitude, this can become irritating. In exasperation, a colleague suggested using clang-format to automatically format our Objective C codebase. Their proposal:

I’ll get the tool's formatting specification as close to what you like as possible. In return, you agree that you’ll accept what the tool produces.

So, quite some time ago, we introduced a clang-formatting file to our repository.

Huge, horrible whitespace monsters

Originally we suggested using a plugin for Xcode to format code. This is a pretty great tool, but we had a problem: it was hard to format just the lines that had been changed, leading to frustration picking apart "real" changes from white space changes.

The other day I came across a script to solve this problem, from the LLVM team themselves, git-clang-format. It’s a python script that hooks into git to give you a git clang-format command, which runs clang-format over the changes staged for commit.

Surprisingly --- to me at least --- it’s really easy to install a git extension:

  1. Call the script something starting with git-.
  2. Put it in your path.
  3. Make it executable.

So for git-clang-format, the process runs something like this:

  1. Install the standalone clang-format tool: brew install clang-format.
  2. Download git-clang-format from here. Read it to check for nastiness.
  3. Move the script somewhere in your path, for me mv git-clang-format ~/bin/git-clang-format.
  4. Make the script executable: chmod +x ~/bin/git-clang-format.
  5. Check it’s been picked up by git: git clang-format -h.
  6. Try it out with: git clang-format --diff.

You can now run clang-format, which will modify your working copy, rather than your staged files. This allows for easy backing out of the changes it makes.

> git status
On branch 45845-reusable-fetcher
Your branch is up-to-date with 'origin/45845-reusable-fetcher'.

Changes to be committed:
  (use "git reset HEAD ..." to unstage)

    modified:   Classes/common/CDTFetchChanges.h

> git clang-format
changed files:
    Classes/common/CDTFetchChanges.h

Running on every commit

The final step is to make sure clang-format is run before each commit. Some may want to just run the tool on every commit, but I’m happier with a simple reminder.

Following some instructions from Atlassian, I wrote a simple git pre-commit hook which runs git clang-format --diff, aborting the commit if something other than no modified files to format or clang-format did not modify any files is returned. Rather fragile, but as long as I don’t update my git-clang-format script, it should continue working.

#!/usr/bin/env python

import subprocess
output = subprocess.check_output(["git", "clang-format", "--diff"])

if output not in ['no modified files to format\n', 'clang-format did not modify any files\n']:
    print "Run git clang-format, then commit.\n"
    exit(1)
else:
    exit(0)

Git stores commit hooks on a per-repository basis, inside the .git folder of the repository. So we need to add this script there, and make it executable:

  1. cp check_formatting.sh .git/hooks/pre-commit
  2. chmod -x .git/hooks/pre-commit

Once all this is in place, if you try to make a commit with which clang-format finds an issue the commit will be aborted. Of course, you could be more or less terse in your message:

> git commit -m "Documentation updates"
Run git clang-format, then commit.

Given how lax I am at keeping to my own standards, this is invaluable.

Facebook's presumably a more successful AOL

Back in the late 1990s, AOL tried to create an alternative internet. It was roundly derided, and everyone instead used its sign-up CDs as drink coasters.

As far as I can see, Facebook is now trying very hard to create an alternative internet for its users, one which threatens to be more successful.

Facebook has said publicly that it wants to make the experience of consuming content online more seamless. News articles on Facebook are currently linked to the publisher’s own website, and open in a web browser[...]

In addition to hosting content directly on Facebook, the company is talking with publishers about other technical ways to hasten delivery of their articles.

(nyt)

Facebook has a lot of users, and it's worrying how many organisations are willing to give up their own identities -- online and off -- for a perceived share of those. Any company that believes Facebook has anyone's interests but Facebook's at heart is a fool. Otherwise its way of making "consuming content online more seamless" would probably not involve putting it into Facebook.

Instead I'm inclined to agree with Dave Pell:

Here’s what tech people are really good at:

Tech.

Because tech happens to be at the center of a financial and cultural revolution, the people who have been talented and fortunate enough to make to the apex of the industry are perceived as oracles of industry; every industry.

And therefore are no more qualified to tell other industries what to do than the people who tell technology companies what to do from the outside.

Though that coming from someone who's ceded their writing to Medium is a little comic.

Using Instruments to track down memory leaks

I was prototyping using TMDb for film search in One to Watch to solve the problem of Freebase's API going away. While doing this I noticed that the memory usage of the app was going up significantly every time I opened a film's details view.

Using the Allocations tool in Instruments, I could see that the memory was increasing in a staircase like manner. Looking at the memory listing shown in Instruments, it was clear that this stepping was caused by decoded PNG images, used to display the cover art. Each time the details view was shown, another 3MB PNG image appeared in Instruments.

The way displaying a film's detail works is incredibly simple and standard: the details view, SavedItemDetailsView is pushed onto a UINavigationContoller object's stack. It's popped off the stack when the view is dismissed. Why wasn't the SavedItemDetailsView object getting freed when the details view was popped from the stack maintained by the UINavigationController?

At first I had a classic "blame the compiler" moment: "the UINavigationController must be retaining my view for too long!". StackOverflow, bless its socks, had a bunch of people having similar issues and leaping to blame UINavigationController too. Of course, it turned out to be my fault. As I assume it did for most of those SO posts.

Thankfully a friend pointed me toward solving the issue by pointing out how to see retain/release calls during the running of the program:

  1. Start a new Allocations session in Instruments.
  2. Before starting your run:
    1. Select the Allocations tool in the top segment of the screen.
    2. Show the right hand side bar (View -> Inspectors -> Show Record Settings).
    3. Enable "Record reference counts".

Now, start the application running using the "record" button in the top left. Do the thing which is causing memory to increase, in my case showing and hiding the details view. As described above, at first I just saw a lot of PNG memory usage. However, this PNG image just got allocated via ImageIO so didn't give much away. So my first port of call was the parent view controller; it just seemed a good place to start. So I needed to see the retain/release process for the view controller:

  1. Put the name of the class (SavedItemDetailsView in my case) into the search box next to the inspector sidebar.
  2. Hit the tiny right-arrow that appears when you mouseover the class name for one of the instances.
  3. Hit the tiny right-arrow appearing when hovering over the memory address.

You should get a (potentially very long) list of retain/release calls for your class. In my listing was an obvious unbalanced retain call. Once I found this, I easily tracked down to a delegate that was strong rather than weak. Correcting the property declaration quickly fixed my problem.