adelie.cx...
LCA 2005 Hackfest notes
Random notes about the linux.conf.au
2005 Hackfest, by
Russell Steicke,
russells@adelie.cx.
The hackfest was based around the game spellcast. Two parts of the hackfest were to:
- To develop an AI program which can play a game without human assistance.
- To develop a GUI program which allows a human to play the game
I only entered the GUI section.
Here is the man page from the original X spellcast game, on which the hackfest competition is modelled, and lacking original thought, my entry resembles it quite a lot. The game itself is older than this, which is detailed in the man page.
My entry
Screenshot of Ecar (my spellcast gui entry) playing against the test AI. Note the amazingly crappy gesture icons. I got used to them, and only remembered how bad they were about 30 minutes before closing. And here's the code
There are lots more things that need doing and cleaning up, but the time for the hackfest ran out, so that's what got submitted.
Things to do, in no particular order:
- Make icon themes work, then make much better icon sets than the ones I have now.
- Pop up suggestions for spells when you hover the mouse over the gesture list.
- Sound, probably via pygame.
- Rearrange the code in spellcast_gui.py to put related things together. Most of the code has evolved with similar things like event handlers or message type handlers grouped together. They should be grouped differently, so all the code to handle MSG_ASK_SPELL_DIRECTIONS (ie the message handler for that message and the handler for the button that sends the reply to that message) is together, for example.
- spellcast_gui.py is a little large, ~1500 lines of python. Try to split some things out of it.
- Make the gesture select window grab focus as soon as it pops up. At the moment you have to press the mouse button on an image to start selecting a gesture, release the mouse, then click again on the popup. It would be much nicer to just press once and release on the gesture you select, like the original spellcast. Also, when the gesture select window is up, you can't dismiss it without clicking on a gesture.
The curious among you can get a current copy of the source from my arch archive:
Archive: russells@adelie.cx--hackfest-2005
URL: http://adelie.cx/archives/russells@adelie.cx--hackfest-2005
You'll need these steps to get a working copy that works (until I set up an arch config or something):
- tla get russells@adelie.cx--hackfest-2005/ecar--trunk--0 ecar
- cd ecar
- tla get russells@adelie.cx--hackfest-2005/hflib--trunk--0 hflib
Results
OMG, I won the GUI section! Stephen Thorne won the AI section. Many thanks to IBM and Terra Soft Solutions.
Protocol
- MSG_ASK_WELCOME (1, 0x1)
- MSG_RCV_CLIENT_DETAILS (2, 0x2)
- MSG_ASK_FOR_GESTURES (3, 0x3)
- MSG_RCV_GESTURES_USED (4, 0x4)
- MSG_ASK_FOR_SPELLS_CAST (5, 0x5)
- MSG_RCV_SPELLS_CAST (6, 0x6)
- MSG_SEND_GESTURES_SEEN (7, 0x7)
- MSG_ASK_SPELL_DIRECTIONS (8, 0x8)
- MSG_RCV_SPELL_DIRECTIONS (9, 0x9)
- MSG_SEND_SPELL_CAST (10, 0xa)
- MSG_SEND_CREATURE_STATE (11, 0xb)
- MSG_SEND_END_GAME (12, 0xc)
- MSG_SEND_NEWPLAYER_INFO (13, 0xd)
- MSG_ASK_MONSTER_DIRECTIONS (15, 0xf)
- MSG_RCV_MONSTER_DIRECTIONS (16, 0x10)
- MSG_SEND_NEW_MONSTER_INFO (17, 0x11)
- MSG_SEND_MONSTER_ATTACK_INFO (18, 0x12)
- MSG_SEND_EVENT_INFO (19, 0x13)
- MSG_SEND_START_GAME (20, 0x14)
- MSG_ASK_CHARM_PERSON_CTRL_HAND (21, 0x15)
- MSG_ASK_PARALYSIS_CTRL_HAND (22, 0x16
- MSG_ASK_CHARM_PERSON_CTRL_GESTURE (23, 0x17)
- MSG_RCV_CHARM_PERSON_CTRL_HAND (24, 0x18)
- MSG_RCV_PARALYSIS_CTRL_HAND (25, 0x19)
- MSG_RCV_CHARM_PERSON_CTRL_GESTURE (26, 0x1a)
- MSG_USERNAME_IN_USE_ALREADY (27, 0x1b)
- MSG_SEND_ROUND_BEGIN (28, 0x1c)
Message-based. Messages have this format:
- Int: start marker, 0xDEADBEEF.
- Int: message length, including the markers.
- Int, the message type.
- Message content, dependent on message type.
- Int: end marker, 0xABCDFEDA.
- Strings inside messages are null-terminated. Sometimes a string is preceded by an int that indicates its length.
- Ints inside messages are 32 bits in network order.
So, look for the start marker. Read the length. Read that many bytes (including markers). Check the end marker. Interpret the message.
Some details follow about the different message types. Each of these is preceded by the start marker, message length and message type, and followed by the end marker.
In here I've randomly confused "player" and "wizard". They mostly mean the same thing.
MSG_ASK_WELCOME
(1, 0x1, server -> player)
Protocol version and welcome message.
- Int: Protocol version
- String: Message.
MSG_RCV_CLIENT_DETAILS
(2, 0x2, player -> server)
Reply to MSG_ASK_WELCOME
MSG_SEND_NEWPLAYER_INFO
(13, 0xd, server -> player)
Sent to all connected players when a new player joins. Sent once for each player(?). Sent to new player once for each existing player. Each player gets message once for himself as well as once each for all the other players.
A warning... this message is sent in an the same order to each player, so the player has to remember his name, and figure out from the name in each message which player he is. This also means that each player must have a unique name for the game to work properly.
- Int: player number.
- Int: length of player name, including null.
- String: player name.
MSG_SEND_CREATURE_STATE
(11, 0xb, server -> player)
Sent to all connected players to tell them about the health of a creature (player or monster).
- Int: creature (player or monster) number. If this is zero, this is the end of the creature state information, and the next two fields are not present.
- Int: health of creature, ie number of hit points.
- Int: bit field of creature states (normal==0, no bits set, other bits indicate blinded, confused, dead etc).
MSG_ASK_FOR_GESTURES
(3, 0x3, server -> player)
Ask the player what gestures will be used this round. No data.
MSG_RCV_GESTURES_USED
(4, 0x4, player -> server)
Tells the server what gestures we are using this round. Don't assume that the gestures you send are the ones that your wizard will actually use, as the wizard may be paralysed, confused, charmed etc.
- Int: Left hand gesture.
- Int: Right hand gesture.
MSG_SEND_GESTURES_SEEN
(7, 0x7, player -> server)
Tells us what gestures were used by a player. Our own gestures come back to us in a message as well.
- Int: Player number. This can be 0 to indicate the end of the gesture information.
- Int: Left hand gesture.
- Int: Right hand gesture.
MSG_ASK_FOR_SPELLS_CAST
(5, 0x5, server -> player)
Ask the player what spells are to be cast with each hand.
- Int: Number of possible spells for left hand.
- Ints: A series of integers, enumerating the possible left hand spells.
- Int: Number of possible spells for right hand.
- Ints: A series of integers, enumerating the possible right hand spells.
MSG_RCV_SPELLS_CAST
(6, 0x6, player -> server)
Tell the server what spells are to be cast with each hand.
- Int: Left hand spell.
- Int: Right hand spell.
MSG_ASK_SPELL_DIRECTIONS
(8, 0x8, server -> player)
Ask the player where the spells are to be cast.
- Int: Left hand spell.
- Int: Right hand spell.
- Int: Number of valid targets.
- Ints: A series of integers, enumerating the possible spell targets.
MSG_RCV_SPELL_DIRECTIONS
(9, 0x9, player -> server)
Tell the server where we're casting our spells.
- Int: Left hand spell target.
- Int: Right hand spell target.
MSG_SEND_SPELL_CAST
(10, 0xa, server -> player)
Tell the player about a spell that has been cast.
- Int: Source.
- Int: Target.
- Int: Spell number.
- Int: Spell worked? 1 if it worked, 0 if not.
- Int: Length of message stringt.
- String: A message about the spell. We can replace %S in this string with the name of the source, and %T with the name of the target.
MSG_SEND_EVENT_INFO
(19, 0x13, server -> player)
Tell us about something that happened. Text message, no significance in game.
- Int: Event type.
- Int: Message source.
- Int: Source.
- Int: Target.
- Int: Message length.
- String: Message. Replace %S in here with source name, and %T with target name.
MSG_SEND_START_GAME
(20, 0x14, server -> player)
Tell the player that the game has started, and the turn timeout. This message is sent before the MSG_SEND_NEWPLAYER_INFO messages.
- Int: My own player number.
- Int: Turn timeout (in seconds?).
MSG_SEND_END_GAME
(12, 0xc, server -> player)
Tell the player that the game has ended, and who won.
- Int: Number of winners.
- Ints: Player numbers of winners.
MSG_SEND_NEW_MONSTER_INFO
(17, 0x11, server -> player)
Tell us about a new monster.
- Int: Monster ID.
- Int: Monster type.
- Int: Owner ID.
- Int: Name length.
- String: Name
MSG_SEND_MONSTER_ATTACK_INFO
(18, 0x12, server -> player)
Tell who a monster attacks.
- Int: Source (Monster ID).
- Int: Target.
- Int: Damage.
- Int: Message length.
- String: Message. %S==source %T==target %D==damage.
MSG_ASK_MONSTER_DIRECTIONS
(15, 0xf, server -> player)
Ask a player what he wants to do with his monsters.
- Int: Number of monsters.
- Ints: Monster numbers.
- Int: Number of targets.
- Ints: Target numbers.
MSG_RCV_MONSTER_DIRECTIONS
(16, 0x10, player -> server)
Tell the server what our monsters are to do.
- Int: Number of monsters.
- Ints: (Number of monsters * 2) ints, in pairs of (monster number, target number).
MSG_ASK_CHARM_PERSON_CTRL_HAND
(21, 0x15, server -> player)
- Int: Target ID. Player whose hand we're controlling.
MSG_ASK_PARALYSIS_CTRL_HAND
(22, 0x16, server -> player)
- Int: Target ID. Player whose hand we're paralysing.
MSG_RCV_CHARM_PERSON_CTRL_GESTURE
(23, 0x17, server -> player)
- Int: Target ID.
- Int: Hand whose gesture we will set.
MSG_RCV_CHARM_PERSON_CTRL_HAND
(24, 0x18, player -> server)
- Int: Target ID.
- Int: Hand to control. Left hand == 1, right hand == 2.
MSG_RCV_PARALYSIS_CTRL_HAND
(25, 0x19, player -> server)
- Int: Target ID.
- Int: Hand to paralyse. Left hand == 1, right hand == 2.
MSG_RCV_CHARM_PERSON_CTRL_GESTURE
(26, 0x1a, player -> server)
- Int: Target ID.
- Int: Gesture.
MSG_USERNAME_IN_USE_ALREADY
(27, 0x1b, server -> player)
No data.
MSG_SEND_ROUND_BEGIN
(28, 0x1c, server -> player)
Running multiple player games
Here's how to run multiple player games on one Debian box and one MS Windows
box:
Here's a screenshot of the windowsbox desktop with a four-player game going.
Notes
- Magic mirror stops finger of death.
- Magic mirror reflects lightning bolt.
- Can't cast certain pairs of spells at the same target at the same time, they fizzle each other out.
- Finger of death is unstoppable by anything exept magic mirror. But you can disrupt it in the middle of its gesture sequence with anti-spell.
- It takes 5 rounds for disease to kill you.
- Finger of death is thwarted by blindess, because you can't see the target.
- Blindness makes you miss targeted spells.
- Raise dead can raise wizards, but only one person/monster at a time.
- Haste lasts three rounds. You get two turns for everyone else's one.
- Giants are good, they cause (at least?) four points damage.
- Lightning bolt does 5 points.
Helpers
Everyone needs a secret weapon. I'm learning tactics by watching my children play. :)