Smooth coastline!
For the island game I’m creating using App Engine, I need a tiled map. I wasn’t expecting this to be difficult, but the sheer number of different tiles required is daunting. If I limit roads to only entering a tile from the north, south, east, or west, there are still 15 ways for roads to cross a tile. Since I don’t want blocky, square islands with abrupt land/ocean transitions, I need a set of 46 coastline tiles. Start multiplying by different types of base tile (forests, fields, mountains, tiny villages, big cities, etc.) and the situation quickly gets out of hand.
Blocky coastline
The only sane solution appears to be transparent overlays which are composited on demand. I don’t see a way to do server-side compositing with App Engine (plus I don’t think it would scale well), so that leaves client-side compositing. I did a simple mock-up with transparent PNG images, using absolute positioning to stack them on top of each other. It seems workable. Traditionally, transparent PNG images weren’t supported well by IE6, but since only 10% of the visitors to my site are using IE6, I might just forget about supporting it.
So that’s the long-term plan. In the spirit of getting a working prototype as quickly as possible, though, I’m going to start with a really simple tileset where I can pre-generate all the combinations and just serve static images. Here are the 6 tiles I’m starting with:
Ocean
Land
Mountains
City
Field
Road
I’m not going to allow roads over mountains for now, so there are 50 possible tiles (16 road combinations * (city, field, land) + ocean + mountains). Pre-generating all the combinations is a snap using PIL.
#!/usr/bin/env python2.5
import os
from PIL import Image
def composite(images):
"""Composite a stack of images, [0] on top, [-1] on bottom."""
top = images[0]
for lower in images[1:]:
top = Image.composite(top, lower, top)
return top
def combinations(list):
"""Generate all combinations of items in list."""
if list:
for c in combinations(list[:-1]):
yield c
yield c + [list[-1]]
else:
yield []
def load(name):
return Image.open(os.path.join('sources', '%s.png' % name))
def save(name, image):
image.save(os.path.join('img', '%s.png' % name))
def make_roads(name, above, below):
"""Save tiles with roads. Images in above will be composited above
the roads. Images in below will be composited below the roads."""
roads = dict(w=load('road_overlay'),
s=load('road_overlay').transpose(Image.ROTATE_90),
e=load('road_overlay').transpose(Image.ROTATE_180),
n=load('road_overlay').transpose(Image.ROTATE_270))
above = [load(filename) for filename in above]
below = [load(filename) for filename in below]
for directions in combinations(sorted(roads.keys())):
out = composite(above + [roads[x] for x in directions] + below)
if directions:
save('%s_road_%s' % (name, ''.join(directions)), out)
else:
save(name, out)
def main():
save('mountains', load('mountains'))
save('ocean', load('ocean'))
make_roads('land', [], ['land'])
make_roads('city', ['city_overlay'], ['land'])
make_roads('field', [], ['field_overlay', 'land'])
if __name__ == '__main__':
main()
PIL also comes in handy to paste together the first proof-of-concept map using the new tiles:
#!/usr/bin/env python2.5
import os
import sys
from PIL import Image
TILE_SIZE = 50 # Width/height of a tile in pixels.
in_file, out_file = sys.argv[1:3]
data = [line.split() for line in open(in_file).readlines()]
map = Image.new('RGB', (TILE_SIZE * len(data[0]), TILE_SIZE * len(data)))
for y, row in enumerate(data):
for x, name in enumerate(row):
image = Image.open(os.path.join('img', '%s.png' % name))
map.paste(image, (x * TILE_SIZE, y * TILE_SIZE))
map.save(out_file)
And here it is: The first example map!
Next on the agenda: Getting Django running on App Engine and serving this example map using HTML.
A couple weekends ago I wanted to play around with some game ideas, to see if they were super-awesome or boring. I needed a simple framework to prototype them on, so I whipped one out using pygame. Then I sketched up some art. And made an installer.
And totally forgot to play around my original game ideas.
Damn. Maybe next time.
Anyway, here it is: a simple scrolling demo made with python and pygame. It has no real purpose (unless you want to do scrolling in pygame).
OSX
Windows
Source (Linux)
Organizing bookmarks is too hard. Does Backcountry Maps go under Maps or Backpacking? Does Hot Library Smut go under Libraries or Pornography? And how am I supposed to keep my bookmarks synched across 5 computers? I’m a busy guy, I don’t have time to figure this stuff out.
That’s why I’ve wanted to use del.icio.us for a long time. I was really excited when they announced their import tool. A few minutes later I was staring at 1000 poorly tagged bookmarks. Oops, should’ve cleaned them up before importing. After failing to find any way to batch process the mess, I dejectedly went back to my hierarchy.
I checked again today and there’s still no “Delete All” (the FAQ helpfully suggests canceling & remaking my account). Time to do it myself. I looked at the API docs (oh, I see, I’m not supposed to flood the server…) and grabbed pydelicious. Fired up ipython and had my bookmarks deleted in no time. Sharing all of my bookmarks turned out to be simple too: Since pydelicious doesn’t seem to understand private bookmarks [1] it shares everything. Changing my mind and deciding that I really should have tagged all my new bookmarks “import” (oops, again) was similarly easy. Hooray!
[1] It is unfortunate that the del.icio.us team doesn’t provide up-to-date libraries in a few languages. In a lot of ways I think that officially maintained libraries are an ideal answer to the SOAP vs. REST arguments
I hate it when a poorly-behaved SDL app (usually mine) grabs the mouse, crashes, and doesn’t give the mouse back. Great, now I’m stuck in X with no mouse. Here’s a handy python script to get it back:
#!/usr/bin/env python
# get the mouse back after an SDL app crashes
import pygame
pygame.init()
pygame.mouse.set_visible(False)
pygame.event.set_grab(True)
pygame.quit()
Some resources for using XCode with Python. A few introductory sites plus the ones I find most useful on a day-to-day basis:
Here are a few hints about how I set up XCode for Python development.
Run your program from XCode
Some versions of PyObjC have a bug so when you make new projects, XCode doesn’t know how to run them. Make a new custom executable & point it build/Development/myProgram.app.
No, don’t quit!
Having the keyboard shortcut to quit XCode be the same shortcut as quitting your application (cmd-Q) is a recipe for annoyance when you accidently hit it twice. Instead, go into XCode’s preferences and change the key binding to quit XCode to something like cmd-option-Q.
Faster builds
You can shave a second or two off the build-test-fix cycle if you set up a dummy “No Build” target. The Development build target will run every time but behind the scenes it isn’t copying files, it is just making aliases (py2app’s –alias option). Set up a new shell script target which runs /usr/bin/true and you can save the effort.
You have no debugger, but at least use PDB
If you set the USE_PDB environment variable, PyObjC will dump you at a PDB prompt when there is an unhandled exception. You can set this on the “Arguments” tab of the custom executable.
Introduction
XCode is the IDE that Apple ships with OS X. Although primarily targeted towards C++, Objective C, and Java it also plays well with Python. It is excellent for writing OS X applications in Python thanks to good integration with Apple’s Interface Builder and py2app (the OS X distutils packager).
If you want to write OSX applications using python, XCode is the tool you want. This is thanks to solid integration with Apple’s Cocoa application framework and Interface Builder.
Debugger
First, the bad news: no debugger. XCode has a debugger but it doesn’t work with Python. You’ll have to use another tool like PDB instead.
Environment
Except for the debugger, the rest of the environment is nice. The UI feels like a proper OSX app, with all the standard keyboard shortcuts and controls, so you’ll feel right at home.
The UI is very flexible, and can accommodate a single fullscreen window or multiple separate windows for each file.
Editor
Standard features here. Syntax highlighting, split panes, menus to jump straight to a class or function. Content assist exists, but leaves something to be desired. I have yet to see a content assist for python that is able to keep up with the dynamic nature of Python classes, however.
Source Control
XCode knows about Subversion, Perforce, and CVS. File comparisons are done using Apple’s File Merge tool so you get nice side-by-side views.
Finding Stuff
“Find in Project” lets you search your entire project. Bookmarks let you quickly jump to common sections of code
Making OSX Apps
XCode is a great environment for Cocoa development. PyObjC provides the Cocoa bindings for Python, and once it is installed XCode knows how to make a new Python project. The distutils setup.py script is handled for you and you can create your .app bundle by clicking Build
Drag-and-Drop your interface…
Interface Builder lets you set up your widgets. Helpful guides pop up to help you position your widgets according to the Aqua guidelines.
…and then Drag-and-Connect your buttons
Hooking up buttons to actions in your Python objects is just a matter of ctrl-dragging from the button to the object.
Conclusion
If you want a Python IDE for your Mac, XCode is a good choice. If you also want to develop OSX applications in Python, XCode is a very good choice.
Attended my first Super Happy Dev House on Saturday night and had a great time. The house was packed with energetic people devoted to getting stuff done. Even though most of the people were strangers working on their own projects, it was really motivating to be surrounded by so much activity.
My project was getting boost.python working on my iBook. I wasn’t able to stay all night and didn’t reach my goal*, but I did learn about iPython from the people sitting next to me. Someone (not sure who) had designed a great poster for the event and they had printed up stickers & pins so there was even some schwag. I’m looking forward to the next one.
* My problem was I was trying to use the boost packages from fink. Turns out they delete a bunch of the files you need to use bjam. The answer? Download the source directly from boost.org (not sure why I didn’t think of that right away, actually). Mission accomplished.
Last month, TR bought a Perplex City starter kit for my wife and I. What a cool game! There are so many levels you can play at. You can solve puzzle cards by yourself. You can join up with others online and solve cards together (including some really hard cards requiring group efforts). The city itself has a lot of depth, with record companies, pharmaceutical companies, schools, blogs, and newspapers. Some of these “companies” run ads in London newspapers, and puzzles from the game have shown up in newspapers and magazines. Finally, you can hunt for the stolen cube and try to get the $200K reward.
Many of the puzzles make fun programming challenges. I’ve been doing them in python, since that is such a great language for rapid programming. Sweet Dreams (#85) makes an obvious candidate:
#!/usr/bin/env python
a = 1
b = 1
while a < 400000:
c = a + b
a = b
b = c
print c
My wife solved Rickety Old Bridge (#106) much faster than I was able to write the program (although the program found that there are two similar solutions, not just one). This was a good one to learn generators with:
#!/usr/bin/env python
class bridge:
people = { "kurt":2, "scarlett":5, "violet":1, "sente":10 }
states = [ {"near":people.keys(), "far":[], "moves":[]} ]
def move(self, source, dest):
""" yield all new states formed by moving
a person from source -> dest """
for state in self.states:
for person in state[source]:
yield {source: [s for s in state[source] if s != person],
dest: state[dest] + [person],
"moves": state["moves"] + [person] }
def to_far(self):
self.states = [s for s in self.move("near", "far")]
def to_near(self):
self.states = [s for s in self.move("far", "near")]
def score(self, state):
times = [self.people[name] for name in state["moves"]]
total_time = max(times[0], times[1]) + times[2] + \\
max(times[3], times[4]) + times[5] + \\
max(times[6], times[7])
return total_time, state["moves"]
def print_scores(self):
scores = [self.score(s) for s in self.states]
scores.sort()
scores.reverse()
for s in scores:
print s[0], s[1]
def run(self):
self.to_far()
self.to_far()
self.to_near()
self.to_far()
self.to_far()
self.to_near()
self.to_far()
self.to_far()
self.print_scores()
if __name__ == "__main__":
b = bridge()
b.run()
The program I came up with for solving Magic Square (98) takes a long time to run but eventually starts finding candidate solutions about 2.2 million squares into its search:
#!/usr/bin/env python
def comb(nums, n):
if n == 0: yield [], nums
else:
for i in range(len(nums)):
for tail, remaining in comb( nums[:i] + nums[i+1:], n-1):
yield [nums[i]] + tail, remaining
def rows(nums):
if not nums: yield []
else:
for row, remaining in comb(nums, 4):
if sum(row) == 34:
for other_rows in rows(remaining):
yield [row] + other_rows
def check(grid):
magic = True
for i in range(4):
if sum([row[i] for row in grid]) != 34:
magic = False
if sum([grid[i][i] for i in range(4)]) != 34 or \\
sum([grid[i][3-i] for i in range(4)]) != 34:
magic = False
return magic
count = 0
for grid in rows(range(1, 17)):
count += 1
if count % 100000 == 0:
print count / 1000, "K"
if check(grid):
print grid