This weekend (12-14th October), I attended this year’s university hackathon “Do You Have The GUTS”, organised by the university technology society. This was my third year attending, and was by far the most enjoyable.

Available challenges

  • Goldman Sachs challenge - Build a bot to do something
  • SAS - Audio analysis in “near real time”
  • JP Morgan - Write a web-based game to introduce 10-14 year old children to computing science
  • Craneware - Create a creative data-visualisation tool
  • Amazon - Make an Alexa skill
  • Morgan Stanley - Create a bot to participate in a multiplayer deathmatch game written for the event (I participated in this one)
  • Verint - Make a survival game

My team

My team was a relatively small team as we only ended up having three people, but we got on well. I worked with Kara and Ryan, which was very enjoyable since Ryan and I went to the same sixth form as me. They were very good teammates throughout the event, with good drive and we were all able to help each other when there were issues (and boy, were there issues!).

Our project

We worked on the Morgan Stanley tank-bot challenge because it had a fairly well defined scope and wasn’t too hard to get started with. The sever executable and some examples were hosted on GitHub (@NickMcCrea/MSTanks) and the wiki was pretty useful. Sadly, our bot was not particularly effective and scored 0 points in it’s heat along with two others but there were many lessons learned from building it. I decided to commentate over the final with Cameron and recorded it on YouTube here with some fairly positive responses.

Hack breakdown

The full project was hosted on Kara’s GitHub account (I also have a fork @blueish4/guts2018), but I’ll break down a few of the design decisions made, and some of the issues we had.

We decided to write our bot in Python since we all had some experience with it and I’m a bit of a Python fanboy. The exemplar Python bot supplied in the server repo were very minimal, having apparently created in just a few hours. They contained a couple of helpful classes for interfacing with the server application, such as a simple abstraction layer for the TCP socket and an enum class for the message codes the server sent in the first byte of the message (all subsequent bytes, if present, were an ASCII encoded json string). For ease of use, I broke this out into a separate file and re-named the variables to the standard lowercase_with_underscores that Python variables use. I also noticed that the readMessage method didn’t export the received message type, so I made it return a tuple of (message_type, message_payload). This made it very simple to edit existing code to use this new information.

 # old
 message = GameServer.readMessage()
 # new
 message_type, message = GameServer.readMessage()

I also decided fairly early on to model all of the potential game objects as Python objects. I stored this in tanks.py, and ended up being quite useful, especially the implementation on GameObject.distance_to_object(other) and GameObjcet.target_heading(other), which made the intent of calculations very clear.

I also used a handler system were the main event loop would invoke functions specified in a dictionary depending on the message from the server. This was originally because Python doesn’t have switch statements, but this design turns out to be more useful than this since functions can be remapped if required.

 handler_map = {ServerMessageTypes.OBJECTUPDATE: handle_object_update,
                ServerMessageTypes.KILL: handle_kill,
                ServerMessageTypes.ENTEREDGOAL: entered_goal,
                ServerMessageTypes.HITDETECTED: need,
                # ServerMessageTypes.GAMETIMEUPDATE: move,
                # ServerMessageTypes.SNITCHAPPEARED: snitch_app,
                ServerMessageTypes.SNITCHPICKUP: snitch_pickup,
                }
while True:
    time.sleep(0.02)
    message_type, message = GameServer.readMessage()
    try:
        handler_map.get(message_type)(message)
    except TypeError:  # I don't have a function to deal with these events yet, so errors happen
        print(ServerMessageTypes().to_string(message_type))

We did run into a problem where we suspected that we may have been running into an issue in sending commands to the server too frequently. I suggested that we implement a queue of information to send before we sent it, so it would be easier to rate-limit. In practice, this grew into a large, overly-complex beast since I wanted it to return to a state inside another function using callbacks. I realised this while thinking through the complexity of the job - in essence I wanted to re-create a queuing system that also had a more JavaScript style event listener system. This is clearly not possible given the time constraints, so I had to remove the partial implementation. In doing this, the bot was in a worse state than the morning, so Sunday morning consisted of rewinding commits until the bot was able to move and shoot accurately, but unable to reliably and quickly target pickups.

Final thoughts

This year’s hackathon was, despite the few t-shirt issues, the most enjoyable one I’ve attended to date. My thanks go out the the GUTS organisers for putting all of the organisational parts together, the sponsors, who put effort into helping people with issues and for creating and judging their challenges; the janitorial staff for dealing with the mess me made and to all of the participants who made a welcoming, fun environment to play around in.

PS: Please don’t put milk in a kettle. Why anyone would ever think that would be a good idea is beyond me.