So apparently there's a rumour that the word on the street is that I put my "free time" to "good use". Poppycock! I haven't done a productive thing in years. In fact my life ambition is just to watch all 500 of Empire's list of greatest films*. Not convinced?
To quench these fiendish lies I stumbled upon an idea so pointless, so time-consuming, that no one could possibly believe I was anything other than stark-raving mad.
Monopoly! Widely agreed to be unnecessarily drawn out and infuriating for all the players. In fact its very creation was to convince people of the unjust inequalities of capitalism. This lengthy, turn-based boardgame was nothing short of perfect for an online, Post-Off-Chess-style neverending multiplayer browser-based game. Rapture!
The benefit, of course, is that there's no pressure to fold up the game and put it away. It just keeps going and going until every last player gets to taste their bankruptcy.
Yes: Online Monopoly, my genius creation that probably already exists and to which I have no legal attachment. Unlike what I presume other online games represent however, my version aimed to mirror the traditional boarded version to every last detail. To that extent, here's my legal disclaimer:
To play this game, each player should have their own copy of the official Monopoly board game, and re-enact each move on their physical copy. The online game is simply the medium by which to synchronize boards.
Not only does this intensify the tedium, it adds another layer of Post-Off-Chess-esque prestige, a luxury previously denied to monopoly players due to their dependence on dice.
Dying to play? First off you should know that this work was never completed and a number of features are missing or unfinished. Having said that, the game is perfectly playable.
What it has:
• Buying property
• Buying and selling houses and hotels
• A chat system (that supports unicode!)
• Trading between players
• A 3D projected virtual board
• Multiple themes
What it has not:
• Community chest and Chance cards
• Tax, Jail and Free Parking
The "has not" list is pretty much the reason I got distracted from this project. The fact is, auctions are integral to the game, but they break the turn-based system for a realtime one. My hope was that each player could log in, play their move, and log out so that no one need necessarily be logged in at the same time. So do we wait for all players to be alerted to the auction? Wait for everyone to be logged in at once? This conundrum was deferred and eventually forgotten.
In addition, I dreaded the community chest and chance cards doubly so as I'd decided on multiple theme support - of course different themes have different forfeits and the whole thing would take an awful long time to manifest, even by my standards.
You'll need friends for this. Choose amongst them an identifier for your game session and if everyone quotes it upon starting/logging in, you should all happily be connected together and see the setup screen. The game will begin once everyone has hit the 'ready to play' box - don't hit it unless you're happy with the settings. From then on, everything should be pretty self-explanatory.
For the version uploaded to this shared webserver I have disabled the long-polling ajax calls. It's running apache and I'm not entirely sure long-polling is a legitimate thing to do (I believe apache has a limited number of threads and they can all get tied up very quickly like that). So the game may seem a little more laggy than it would otherwise.
The most notable feature is the 3D board. I created this using just elementary trigonometry and it works pretty well - this was well before I was aware of anything like WebGL. The players are sprites, of course, but the houses and hotels are genuine bonafide 3D models.
It's a hassle to go out and find other people to interact with when all you want is to play around with the 3D board. I understand! Here's a semipopulated demo board that you can rotate, zoom, and experience the monopolization without any silly log-ins or rolling dice:
The board is set to wireframe-while-moving by default but you can disable that in the little dropdown box, if you have a zippy machine/browser. You can also translate / zoom by holding control or shift while you drag.
You'll notice the wireframe doesn't quite line up with the image. This is again a side-effect of supporting multiple themes, their board images were laid out slightly differently and the wireframe position was a compromise. One of the forgotten to-do items was adding an aspect-ratio compensation factor to the theme parameters.
Drawing the board image is resource-heavy because there's no native way of doing non-affine transformations. The way I did it, like with my raycasters' floors, was to draw to an intermediate surface.
The client polls a stack on the server. When something happens in the game, it is copied into each player's stack so that the next poll will receive all the updates that have happened. Given that the clients are continuously polling (or long polling) there are usually only one or two updates to deliver. Obviously when a client logs in it needs to send the whole game data, which is actually sent by the same mechanism. The stack is actually maintained in JSON on the server and so gets sent directly without any processing. PHP's native JSON handling is a bit shaky but in the end this system worked out very well and is particularly low-bandwidth. The system can resync if necessary (although special care is needed for cumulative data, like the chat log, so as not to repeat messages already displayed -- laziness is the reason I didn't assign each message a unique ID) and can also send updates to individual players only. The client has a very simple task of merging the received JSON data with the game data object. The whole thing gave me an idea to extend this and make a generic PHP-Ajax game sync library, but I seem to have gotten distracted. Also notice that unicode is supported throughout. This wasn't originally intended but turned out to be necessary as at one point, a single unescaped unicode character managed to cause the whole system to crash.
There's actually a surprising amount of info to store on any given game. There are 40 locations, 28 of which can be owned, 22 can be built upon. That's 28 booleans per player, and per game at least 22 house-status cells containing a number up to 6: up to four houses, or hotel, or mortgaged. Given the nature of MySQL data types, the obvious solution by far is to go bitwise. (To be fair I probably would have done this regardless of possible data types.) We can use 3 bits per status and although there are 40 locations, less than 32 need to be tracked, so we can use standard 4-byte integers to hold all the data on what's owned, and three 4-byte integers to contain everything about the status. For some people this would be a nightmare but I rather enjoyed it. Bitwise logic is fast and the database is many orders of magnitude smaller than it otherwise would have been.
Rules and redundancy
Absolutely everything about the client (that's you) is untrusted. This means the rules have to take place on the server. But having them only take place on the server would give an awfully poor user experience. This means they have to enact in your browser too. Did I mention the two are programmed in different languages? But I enjoy it. I enjoy checking everything twice.
It surprised even me the number of obscure rules that appear in monopoly. For instance, there is a hard limit on the number of houses that can be on the board. Pretty easy to implement in real life but a chore when it comes to virtualization... you know, one moment, every house is on the board and then somebody sells a hotel - they can't downgrade to houses because there aren't any. I chose to give them the cash value of what the houses would be. There exists an apparently endless supply of inconvenient complexities which go totally unnoticed in the physical game. Even I, zen master, began to smirk at the situation.
Transactions, hopefully, are completely functional. This again includes silly things about mortgaged properties requiring a 10% payment to the bank if traded but the unmortgaging fee is waived if it's only just been traded. A sense of 'immediately' is blurred in what started out as a perfectly turn-based game. Actual playing with this system soon showed that again, for decent gameplay, all players need to be logged in at the same time.
The Programming Style
One marvellous thing about working from scratch and completely alone is not having to conform to what can only be called programmatic-social-pressures. This leads to an enormous amount of freedom. There are numerous examples in the source code of habits, shortcuts and tricks which border on obfuscation. But the optimizations are consistent and make sense to me. At the end of the day, I am writing this for fun and I don't give a toss if other people can read it. The bitwise data storage is one example. Another is the use of operator shortcuts all over the place, which I love. Confusing callbacks are often nested. I even committed the cardinal sin of placing an assignment in a comparison on purpose. It always makes me chuckle how much people hate that.
Other optimizations were made by replacing the JSON object names and statuses with single characters. Given that it's continuously polled this has a huge effect on overall bandwidth. I also investigated other bandwidth sinks, peculiarly finding that the little house and hotel icons had their smallest filesizes in GIF format (not PNG). I've yet to fully investigate the reason for this but I suspect it may have something to do with the dithering offsetting the RLE compression.
I should have mentioned this sooner. There's no end-game. It never ends. Given the lack of tax and forfeits there isn't really anywhere for the money to go. If the players are conservative, they can just keep going around, and around, and around. Collect £200 as you pass go. Go on, and again. The fun just never ends.