I don't think it's quite that simple but you can look at the Ruby code yourself here:
https://github.com/igrigorik/vimgolf/tree/master/lib/vimgolf
I see a class for keylogging, for one.
Here's the command that is used to launch vim in lib/vimgolf/cli.rb:
vimcmd = GOLFVIM.shellsplit + %W{-Z -n --noplugin --nofork -i NONE +0
-u #{challenge.vimrc_path} -U NONE -W #{challenge.log_path} #{challenge.work_path}}
@statox was (very nearly) right about -w outfile ... -W outfile, actually, which overwrites the file to which the typed characters are sent rather than appending to it. But as I said, not "quite that simple"...compare the contents of one of those files and what gets generated by the vimgolf tool. This is from an actual vimgolf problem...
-W output:
^VGI0. ^[gvg^AZZ
vimgolf displayed/uploaded output:
<C-V>GI0. <Esc>gvg<C-A>ZZ
Update: I spent a few minutes looking a bit closer at things. Here's how the raw form gets converted to the more readable Vimgolf format...
The keylogger class keylog.rb that I mention above is indeed a major piece of the puzzle but it's actually fairly straightforward. The keycodes that Vim saves to the -W log file are either one byte or three bytes long. The latter always start with the value 0x80 to distinguish them from one-byte codes.
To decode the one-byte keycodes a simple array lookup is done.
Example: If you press G the byte 0x47 is saved to the -W file. (Hex 47 is the ASCII code for the letter G.) In the lookup array the element at position 0x47 contains, naturally, G. For printable characters this is an obvious conversion but it's necessary for non-printable ASCII characters that we want to have a nice name like <Esc> and <CR>.
For multi-byte keycodes there is a hash table that maps bytes two and three to a human readable string.
Example: If you press Home the three-byte sequence 80 6B 68 is saved to the -W file. The last two bytes are converted to ASCII and concatenated: kh. In the hash table that string is mapped to the string <Home>.
There are some exceptions/corner-cases that are handled differently but most of the keycodes are resolved by one of the two methods described above.