oblarghto http://blog.harto.org Most recent posts at oblarghto posterous.com Wed, 24 Aug 2011 20:23:00 -0700 Pac-Man http://blog.harto.org/pac-man http://blog.harto.org/pac-man

I've published my Pac-Man clone! It's not 100% complete, but this was just a learning exercise, and I think I've gotten enough out of it.

The bits that I didn't do:

  • Music: I wanted to record some simple background music in a particular key, and record the sound effects in the same key. They would combine produce a randomly generated soundtrack. I recently attended a talk at Freeplay by a guy called Stephan Schütze, who had lots of interesting things to say about procedural music generation. I may revisit this idea in a future project.
  • Artwork: There are some elements that I didn't bother doing (mainly the bonus symbols, which are represented as plain white squares). The ghost artwork is pretty inconsistent, too - ghosts look totally different when frightened. I would probably have fixed this, but my trial Photoshop license expired. (Do you know how much that thing costs nowadays?)
  • I skipped some behavioural details, like the way ghosts jostle around in the house when they're waiting to be released.

Other than those, it's a pretty faithful implementation of the game.

There are some technical decisions that I would make differently in retrospect. The following (non-exhaustive!) list outlines some choices that might have overcomplicated things:

  • Code structure: Programmers have a tendency to look for patterns and generalisations in code that can be re-used. In my case, I wanted to keep the engine code as simple as possible, and pushed all the behavioural bits and pieces (particularly the ghost releasing and mode switching behaviour) down into objects (i.e. DotCounter, ElroyCounter, ModeSwitcher and ReleaseTimer). I don't think I hit a particularly sweet spot of abstraction by doing that – it would probably have been nicer to keep it all nearer the surface.
  • Movement: I got overly concerned about actors moving in sub-pixel increments. That is, the ghosts and Pac-Man can have speeds of e.g. 1.2 pixels/frame, but you can't draw inbetween pixels on the screen. I came up with some rather complicated code to accumulate fractional pixel amounts between frames, but it would've been much easier to store the raw values and round to the nearest pixel when drawing. Check out Actor to see what I mean.

There are plenty of other warts in the code, but it works OK.

The playable version is at http://www.harto.org/wheel/pacman. It works in recent versions of Firefox, Chrome and Safari (without sound). The source is at https://github.com/harto/pacman.

Let me know what you think!

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/149311/space-invaders.gif http://posterous.com/users/4akVZTBfQjAJ Stuart Campbell harto Stuart Campbell
Tue, 02 Aug 2011 17:27:00 -0700 Simple JavaScript dependency analysis http://blog.harto.org/simple-javascript-dependency-analysis http://blog.harto.org/simple-javascript-dependency-analysis

One of the most glorious forms of procrastination for a programmer is developing tools that automate menial and repetitive tasks. I've wasted lots of time on little Python scripts that generate Java classes, remove unused rules from CSS stylesheets, and all kinds of other unnecessary stuff.

My latest invention is a program that does a simple analysis of JavaScript files to determines the order in which they should be concatenated.

Concatenating multiple JS files is a simple optimisation that reduces the number of HTTP requests between a browser and a server. However, they need to be concatenated in the right order so that objects and functions are defined before they are referenced.

The program works by scanning JSLint /*global ...*/ declarations to determine the JavaScript variables referenced by each script. It then looks in all other scripts to figure out which one defines each variable. This results in a dependency graph that maps each file to each of its dependencies.

Once the dependencies are known, the program determines the concatenation order by inserting files into a list, one at a time. Each file is inserted after all its dependencies. If any of the previously inserted files references the newly inserted file, they are removed and reinserted in the same way.

The insertion algorithm seems very inefficient, and possibly isn't even correct! (I don't remember anything about graph theory.) But it seems to work for my Pac-Man project, which is split into 32 interdependent files.

Here's the program, and a Makefile that shows how it's used:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
"""
Determines the order in which JavaScript files should be concatenated by
scanning JSLint /*global ... */ declarations.
"""

import re
import sys

### Graph construction

def slurp(filename):
    with open(filename) as f: return f.read()

DEPS_RE = re.compile(r'/\*global ([^*]+)\*/', re.M)

def js_dependencies(filename):
    "Finds a script's dependencies as declared in a /*global...*/ block."
    match = DEPS_RE.search(slurp(filename))
    return match and [dep.strip().split(':')[0] for dep in match.group(1).split(',')]

def declaration_re(js_identifiers):
    "Constructs a regexp that matches declarations of JavaScript identifiers."
    return re.compile(r'^var [^;]%(names)s|^function %(names)s' %
                        {'names': r'\b(%s)\b' % '|'.join(js_identifiers)}, re.M)

def declaring_scripts(js_identifiers, filenames):
    "Finds the scripts that declare one or more of `js_identifiers'."
    if not js_identifiers: return []
    decl_re = declaration_re(js_identifiers)
    decls = {}
    for f in filenames:
        matches = decl_re.findall(slurp(f))
        if matches: decls[f] = [m[1] for m in matches]
    return decls

def dependency_graph(filenames):
    "Returns a map of filenames to their dependencies."
    graph = {}
    for f in filenames:
        others = filenames[:]
        others.remove(f)
        graph[f] = set(declaring_scripts(js_dependencies(f), others))
    return graph

### Graph utilities

def circular_ref(node, graph, path=None):
    deps = graph[node]
    if not deps:
        # resolved all dependencies
        return None

    path = [] if path is None else path[:]
    path.append(node)

    for dep in deps:
        if dep in path:
            circular = path[path.index(dep):]
            circular.append(dep)
            return circular
        circular = circular_ref(dep, graph, path)
        if circular:
            return circular

def check_circular_refs(graph):
    "Returns true if a graph contains any direct or indirect circular references."
    for node in graph:
        path = circular_ref(node, graph)
        if path:
            raise Exception('circular dependency: %s' % ' -> '.join(path))

### Determine script output order

def insertion_index(filename, graph, order):
    deps = set(order).intersection(graph[filename])
    return 0 if not deps else max(order.index(dep) for dep in deps) + 1

# XXX: this seems very inefficient - there's probably a better way to do it
def order_insert(filename, graph, order):
    index = insertion_index(filename, graph, order)
    order.insert(index, filename)
    # did this invalidate any indirect dependencies?
    invalidated = [f for f in order
                   if order.index(f) < insertion_index(f, graph, order)]
    for invalid in invalidated:
        order.remove(invalid)
        order_insert(invalid, graph, order)

def concatenation_order(graph):
    """
Determines the order in which files must be concatenated to satisfy all
dependencies. Each file is inserted after all its dependencies. If this
invalidates the order, the dependencies of the inserted file are recursively
reinserted.
"""
    check_circular_refs(graph)
    order = []
    for f in graph:
        order_insert(f, graph, order)
    return order

if __name__ == '__main__':
    print ' '.join(concatenation_order(dependency_graph(sys.argv[1:])))

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/149311/space-invaders.gif http://posterous.com/users/4akVZTBfQjAJ Stuart Campbell harto Stuart Campbell
Thu, 21 Jul 2011 21:12:00 -0700 State of play http://blog.harto.org/state-of-play http://blog.harto.org/state-of-play

So much for my regular blogging habit.

Since my last post, I've been continuing my self-education in games development. I'm going to write about the state of my current project, which is a Pac-Man clone.

But first, briefly, some exciting personal news!

In March, 7 years after we starting going out, I proposed to my partner Lucy. And she said yes! We're getting married in October here in Melbourne.

We also went to Europe for a short holiday - one week in the UK catching up with relatives, then two weeks in a Parisian apartment. It was an amazing time - what an incredible city!

So, as is often the way with Life, I haven't been able to spend as much time as I expected on Pac-Man. Aside from the aforementioned events, it's still taken longer than expected.

There are a few reasons for this:

  • As my understanding of the game evolved, I would think of different ways of doing things. This resulted in me reworking large chunks of code. This is really the whole point of the exercise: an opportunity to learn how a program like this hangs together.
  • While fleshing out "secondary" parts of the game, I would realise that some earlier assumption I had made was no longer valid, and would have to do some rework. For example, when Pac-Man eats a ghost, everything pauses for about half a second. This meant implementing a global wait function. I could've glossed over these kinds of details, but I learned more by just spending the time and doing it.
  • I decided to implement some rudimentary graphics and sound effects. This has been a lot of fun so far, and is a big part of why I'm excited by games development - lots of different creative techniques involved.
  • The game logic is more complicated than I realised. Luckily, I could refer to The Pac-Man Dossier whenever I needed help - an excellent resource.

I'm not going to implement every last feature of the game, but I do still want to implement a couple more things. I think it's only the following:

  • Background music. (In the original Pac-Man, this is a weird wobbling sound that speeds up towards the end of each level.)
  • Artwork for the bonus symbols (cherry, key, etc).
  • "Cruise Elroy" mode (see Pac-Man Dossier)
  • Maze flashing on level-up

The code will be available on my GitHub account shortly. Once I publish it, I'll write a follow-up post detailing some of the interesting technicalities of my implementation.

Blinky

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/149311/space-invaders.gif http://posterous.com/users/4akVZTBfQjAJ Stuart Campbell harto Stuart Campbell
Thu, 03 Feb 2011 17:43:00 -0800 Reinventing the wheel http://blog.harto.org/reinventing-the-wheel http://blog.harto.org/reinventing-the-wheel

To get really skilful at something, you got to do it a lot. An obvious way to learn games development is by building clones of existing games.

My brother showed me an article that prescribes a list of arcade classics to be developed by the aspiring programmer. Once you remake each of these games, you'll theoretically know all the fundamentals of game development. (This would also be a really good way to learn a new programming language.)

I started on a Tetris clone a long time back then forgot about it. But I recently remembered that I kind of, you know, need to learn how to make games. It's implemented in JavaScript and basically works, although the rotation algorithm is a bit smelly (i.e. wrong). I've pushed it to Github and you can find the source here.

Feel free to use the code in whatever way you like. It's obviously not perfect. I publish it to shame myself into improving it. Hopefully I'll find the time to do so.

Other people's software seems simple until you discover all the little details that accumulate over the life of the program. I won't aim for 100% of the functionality, since it's only a learning exercise. But I will endeavour to add a bit more here and there, possibly in parallel with the next game in the list - Breakout.

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/149311/space-invaders.gif http://posterous.com/users/4akVZTBfQjAJ Stuart Campbell harto Stuart Campbell
Wed, 30 Sep 2009 15:58:15 -0700 Hello, world! http://blog.harto.org/hello-world-1587 http://blog.harto.org/hello-world-1587
Lisplogo_alien_256

Yay!

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/149311/space-invaders.gif http://posterous.com/users/4akVZTBfQjAJ Stuart Campbell harto Stuart Campbell