This is a fully functional implementation of the game of mahjong, with competitive computer players, made for two reasons. The first being that I like mahjong and I wanted to be able to play it on a computer without having to install any software for that, and the second being that it's a great example by which to explain how to go about writing software that isn't trivial. We've all seen tutorials for writing a "todo app", or "a single web page", but those aren't things you'd actually want to write if you were to make something: they're simple toy exercises to show off "how easily you can make something". The problem is that tutorials like this gloss over the fact that actually making something rather than doing a simple exercise isn't easy. Writing software is hard.
And because it's hard, being told that it's easy also makes it really easy to forget to plan for hardship: once you find something you actually want to do, you soon discover that just following the same methodology of that easy, toy implementation makes you run into all kinds of obstacles, and if you're not careful, or you don't have someone to help you get on track, that thing you wanted to make just grinds to a halt before it gets abandoned altogether. Tutorials don't teach you how to plan for work you know is going to take more than a few days to get done, so what if there was a tutorial that did? A tutorial that covered the space between toy problems and product programming as a day job? That's where this comes in.
And not just because of the amount of code involved, it's also because you need to understand how much you can break up the work, when to recognise you're going too many different things, and when to abandon something that just doens't seem to want to work so that you can try a different approach. Programming is hard, you're going to fail at some point, and it would be super nice if there was a tutorial that didn't just tell you want to do, but also told you what failure looks like, so you can spot it, and maybe even avoid it.
The game you can play, above, took about two months to write in my spare time, and I'm going to assume you'll be doing the same, because you probably already have a job, or you're attending school, and you don't have the luxury of spending 16 hours a day on personal projects. So: if you want to learn to program something non-trivial, and you understand that this means you'll probably be working on it for a couple of weeks, or even a month or two: this might just be the tutorial for you.
Of course, if you just came here to play mahjong, then as an added bonus, you can! Keep reading for an explanation of how mahjong works, and specifically which rule set and scoring system this particular implementation of mahjong supports.
Mahjong is a four player tile game where players compete to form a winning "hand" of 14 tiles, arranged (in general) as four triplets (either consecutive tiles within the same suit, or genuine triplets with the same tile three times), and a pair (one tile, twice).
The traditional game uses 144 tiles, consisting of three numbered suits, seven types of "honour" tile split into "winds" and "dragons", and two sets of bonus tiles. Specifically:
Four sets of the numerical tiles 1 through 9 with "dots" ornamentation (traditionally called 筒, "coin"):
Four sets of the numerical tiles 1 through 9 with "bamboo"/"sticks" ornamentation (traditionally called 索, "coin string"):
Four sets of the numerical tiles 1 through 9 with "Chinese character" ornamentation (traditionally called 萬, "10,000"):
Four sets of the four cardinal "wind" directions:
Four sets of "dragon" tiles:
With eight bonus tiles (not part of play - they are set aside immediately when drawn, with a new tile drawn to replace it):
The four seasons (spring, summer, fall, winter, or 春, 夏, 秋, 冬)
The four seasonal flowers (orchid, plum blossom, bamboo, chrysanthenum, or 蘭, 梅, 竹, 菊)
Core game play is about as simple as it gets: everyone starts with 13 tiles, and players take turns in which they draw a tile from the set of undealt tiles, check to see if they can form a winning pattern, and if not, discard one of their tiles so they're back at 13, after which it is the next player's turn.
The "actual game" part, rather than "random chance" aspect comes in the form of "claims", where players may claim a tile as it is being discarded if they can use that tile to form certain sets. In this case, it immediately becomes their turn, irrespective of whose turn it "should" have been, and rather than drawing a tile from the undealt tiles, they take the discarded tile as their 14th tile.
If none of the above claims are made, then the player whose turn it is next is allowed to pick up the discard tile rather than drawing a tile from the undealt tiles, in order to form a set consisting of three consecutive numbers within the same suit, called a "chow". Note that these sets are typically worth no points, and will typically even negatively impact the hand score by ruling out scores based on true-set hand patterns.
In the case when a tile has claims by multiple players, a winning claims trump any other claims, and a real claim trumps a player wishing to form a set of consecutive tiles.
When honouring a claim, the receiving player must reveal all tiles in the set they formed, and put those tiles aside, face-up. These tiles are considered "locked", and can no longer be used for anything, with a single exception:
If a player has a locked, true set of three tiles already on the table, and they draw (not claim) the fourth tile, they are allowed to add that tile to their locked set of three, forming a locked set of four. If a player elects to do this, they receive a new tile from the undealt set of tiles to compensate for the "loss" of a tile. If a player forgets to do this, they they will not have enough tiles left to form a winning hand, and may even be penalized for playing a hand with the wrong number of tiles.
A player is not required to declare sets if they do not need to claim a tile for them. If a player has a set of three or four tiles in their hand, due to having started the game with them, or having drawn tiles over a number of turns to end up with them in hand, they are under no obligation to reveal that fact. Winning with unrevealed tiles typically incurs a bonus.
Note that there are special considerations when a player has four of the same tile in their hand. They may declare this and lock the four tiles as a set that is no longer in their hand, receiving an extra tile to prevent them from having too few tiles to form a winning hand (revealing the tiles depending on the exact rules being played. Some rules do not require this kind of set to be revealed, meaning other players will not know which tile has become unavailable, and so may try to win on a tile that cannot ever appear during that round of play). They may also elect to keep all four tiles in their hand so that three of the tiles can be used as one set, and the fourth tile can be used to form a numerical set. Note that because a winning hand has to consist of four sets and a pair, a player who wishes to count all four tiles as a single set will have to declare that set, or they will not have enough tiles with which to form the necessary number of sets and pair.
Scoring of hands is entirely dependent on the style of mahjong played, but can be split into two ways to count points:
Additionally, rules typically use one of two "score settling" methods:
The game on this page can be played using two different rulesets: Chinese Classical rules, and Cantonese rules.
Points are awarded to any player for the following (open/concealed):
The following points are awarded to the winner only:
The following doubles are awarded to any player:
The following doubles are awarded to the winner only:
The following limit hands (scoring a flat 1000 points) exist:
The player winds rotate counter-clockwise, so that (E,S,W,N) become (S,W,N,E), and the wind of the round rotates clockwise, starting at East, then South, then West, then North.
Points are awarded for for the following:
The following limit hands exist:
The conversion table for turning faan into a hand score is:
|5-6 (laak 1)||16|
|7+ (laak 2)||32|
Because of the payment rules explained above, a hand score leads to a total winnings of either 4x the points when winning off of a discard (discarding player pays double, the other two players pay the base score), or 6x the points when declaring a self-drawn win (all other players pay double).
This game can be played with either a mouse or keyboard (or combination of the two). The interface may also give you hints while playing:
After receiving a new tile either from the wall or from claiming a set:
While looking at discards from other players:
While in a modal dialog, click any button to select that option.
After receiving a new tile, and after claiming a set:
While looking at discards from other players:
While in a modal dialog: