Thursday, April 26, 2012
History of Greyhawk Wars PDF
Tuesday, April 3, 2012
Spell Progressions in D&D-variants
I've been reading some interesting stuff related to D&D-type roleplaying games recently. One thing I came across is spell progression: How do you translate from the wizard or cleric level to the number of spells of each spell level the character can cast? Apparently different incarnations of D&D used somewhat different tables over the years.
All of this reminded me that I never liked the spell progressions in D&D-type games: I never "got" them in the sense that they always seemed way too random to me. What was the unifying mechanic behind all these numbers? Hard to figure out, go ahead, try.
I use the following simple progression: 1 2 2 3 3 3 4 4 4 4 5 5 5 5 5 and so on and so forth. Read: You start spell level x with 1 spell, after one more level you gain a second spell, after two more levels you gain a third, after three more you gain a fourth, etc. And I use this for each spell level. Both wizards and clerics gain a new spell level every 2 class levels, and done: One simple rule explains it all.
Yes, there is some “fluctuation” in terms of which character levels get the most new spell levels, but it’s really not too bad to say “look, level 18 is really powerful because that’s where you realize the most about how the multiverse really works". And yes, there are certain levels where you gain absolutely nothing, especially as a cleric, but those are really high and I wouldn’t normally play there anyway.
Comments?
Monday, October 17, 2011
Browsing man pages in vim
.vimrc
lately, and this is one of the more useful things I've added. I have forgotten where it came from exactly, but here it is:let $GROFF_NO_SGR=1 source $VIMRUNTIME/ftplugin/man.vim nmap K :Man <cword><CR>
What's it good for? The default key binding for K (that's "shift-k" I guess) in
vim
is to look up the word under the cursor using the man
command. The sad thing about this process is that vim
gets replaced by less
(or whatever pager you happen to be using), and that once you're done reading the page, you have to press "Enter" one additional time to get back into vim
and back to whatever you were doing. Kinda breaks your flow, you know?Once you've added the three lines above to your
.vimrc
things are quite different. When you hit K, the man page opens as a new split window inside of vim
so you're staying in the same environment. All the usual binds for switching between windows work, so you can keep the man
page open while going back to your code. Better yet, the man
page will be "syntax highlighted" using different colors for headings, text, and (you guessed it) references to other man
pages. And the best thing? You can browse man
pages the same way you browse tags
: use "ctrl-]" to open another man
page and use "ctrl-t" to "go back" to the previous one.Now that's how
man
pages were supposed to be integrated with your editor. Very nice indeed... :-DThere's one small problem that I have not been able to work around yet: The original K could be preceded by the section number to look in, but this won't work in the replacement above. I am not enough of a
vim
hacker yet to add that capability. Shame on me?Update: Actually, I forced myself to learn just enough of
vimscript
to cobble together something ugly for section numbers:" experimental hack to get section numbers to work as well function ManWrapper(n, w) if a:n > 0 let cnt = a:n-line(".")+1 execute "Man" cnt a:w else execute "Man" a:w endif endfunction com -count=0 -nargs=+ CMan :call ManWrapper(<count>, <f-args>) nmap X :CMan <cword><cr>
Yes, I know, it's quite horrific! If you know this dreadful language better, please tell me how to rewrite this cleanly.
Tuesday, October 11, 2011
Random Design Patterns, Part 1
First pattern, simple as can be: Null Object. Say you have some operation that returns a
Sprite
object that you then do something to. What if there is no sprite? You could return NULL
or nil
or None
or whatever your language of choice calls the thing. But then you have to check the returned value:If instead you return asprite = some_operation() if sprite: sprite.do_something()
Sprite
instance that simply doesn't do anything, your code becomes a little more straightforward:Not exactly a big deal, but of course it could add up to something more significant if you were using this in a more complicated way.sprite = some_operation() sprite.do_something()
It's certainly not a good idea to always ignore the fact that you didn't find the sprite in question, for example you don't want to keep inserting
NullSprite
objects into a list over and over. So when you do care, you need a way of telling that it's a real sprite. One way is to guarantee that only a single NullSprite
is ever created and to make that one globally accessible. Then, in places where you care, you can say this:Whether Null Object is particularly useful therefore depends on how often you care versus how often you don't care (but still have to check if you use the language's builtin version of "no such object"). As with all design patterns: Think before you apply the pattern!sprite = some_operation() if sprite is not Sprite.NULL: sprite.do_something()
Monday, October 10, 2011
Sets of Dictionaries in Python
Of course this makes sense: Python implements sets as dictionaries and dictionaries as hash tables and you cannot use something mutable as a key in a hash table. (If you don't see why that's so, think harder.) But sensible or not, what I certainly don't want to do is search my list of dictionaries for duplicates before every single insertion! So I came up with a little hack to make dictionaries hashable:>>> set([{}]) Traceback (most recent call last): File "", line 1, in TypeError: unhashable type: 'dict'
Alright, so the dictionaries are not really hashable, instead I produce a hashable digest of a dictionary's contents. You can now give me a lecture on how this is not very efficient, and some part of me would agree. However, given a long enough list of dictionaries, the time I spend on computing these digests will be less than looking through the whole list for a duplicate.def hash_string_dict(obj): """ Return a unique-ish string for a dictionary mapping string-ish things to string-ish things. """ import hashlib k = "".join(sorted(str(obj.keys()))) v = "".join(sorted(str(obj.values()))) digest = hashlib.sha1(k+v).hexdigest() return digest
So instead of a list of dictionaries or a set of dictionaries, I end up with a dictionary of dictionaries: The key in the outer dictionary is the digest of the inner dictionary. If a duplicate comes along it'll produce an identical digest and I can forget about it after one (expected!) constant time lookup.
Of course all of this depends crucially on my dictionaries being immutable as far as my application is concerned. Python itself doesn't have the luxury of wondering about this, it has to "worst-case" it and assume all dictionaries are mutable, period. I wonder: Should I package this as a container class? :-D
Update: Note that it's sort of important that the keys and values you have in that dictionary produce "useful" string representations. In other words, don't apply this trick without thinking through the kind of data you're pushing around and whether the digest has a reasonable chance of being accurate enough for duplicate detection.
Sunday, October 9, 2011
And a new blog?
Anyway, apparently the students got frustrated enough to not re-open the blogging service under a different name, so for the past year or so I've not had a blog. Fast-forward to yesterday: I needed some information from one of my old blog posts, so I decided to request the raw data (thanks for the SQL dump Rich!) and to start over somewhere else. So here I am.
Not sure how long it'll take me to import the old posts, but I guess eventually you'll be able to read them here. Not that they were terribly interesting before, but hey, gotta have a blog, no?
Sunday, July 4, 2010
Self-Signed SSL Certificates
openssl genrsa 1024 > host.key
openssl req -new -key host.key -out host.csr
openssl x509 -req -days 730 -in host.csr -signkey host.key -out host.crt
Of course that's what everybody has, so why write about this? Three reasons:
- Make sure you
chmod 400 host.key
since you don't want anybody to see that. - Using lighttpd? Do a
cat host.key host.crt > host.pem
andchmod 400
that as well. - The "Common Name" you have to enter in step 2. If you have various subdomains like www.example.com and mail.example.com and so on, you don't want to enter "example.com" here. Instead you'd enter something globtastic like "*.example.com". But wait, that doesn't match just plain example.com anymore! Better use "*example.com" and wow, that actually works.
An Internet. Wow! It's so pretty... Who would've thunk? :-D