07 December 2012

Dominion's ecosystem

Without going into too many details, Dominion is a medieval-themed deck-building card game designed by Donald Vaccarino. The base set was released in 2008 and received a bunch of awards. A total of 8 expansions have been following, released every 6 months. Most of the cards of the 8 expansions were already designed and bundled in sets since 2008.

In Dominion, the optimal strategy depends on which cards are already in your deck, what the other players do, what cards are available to add to your deck, and a small amount of (somewhat controllable) luck. The game has been so carefully tuned and playtested that some reviewers consider it too safe and overbalanced, overdesigned and overdeveloped. Dominion's balance is so interesting that it deserves an article in itself.

Looking at the complex interactions between cards, Dominion reminds of Magic the Gathering. For example, expert players talk about engines, a concept similar to Magic's deck types, but (I'd say) deeper. In fact, the MtG analogy is not surprising, given that Vaccarino is a veteran MtG player who pitched several design ideas to the MtG designers in the late 1990s.

After having played around 100 games, Dominion also reminds of Chess: it has traditional openings, annotated games, and a vocabulary very game-specific (e.g. greening or stalling). Dominion is a very deep game.

Expansions

Of the 8 Dominion expansions, some have been received more favorably than others. For example, Alchemy is the third expansion. It adds potions, a new currency, and not too many people enjoy playing just Alchemy games. On the other, the fourth expansion is called Prosperity, and it adds a lot of over-the-top cards: compare, for example, Market from the basic game to Grand Market in Prosperity. This expansion is certainly the most well-received.

The lesson here seems to be: make more awesome versions of what exists already, and adding extra currencies like Potions makes the game more complicated, but not necessarily more fun.

Dominion's unofficial ecosystem

A lot of unofficial projects emerged around Dominion. For example, a card set constructor helps you determine which cards to pick for your non-digital game(s) with friends. An AI simulator enables experts to polish their strategies by scripting basic AIs and running thousands of games between those AIs. The AI is provided from external files, and the returned graphs plot the average number of points earned per turn against the number of turns. As an example, this technique was used on the basic set of the first Dominion. There are also fan-made expansions, a zombie retheme, and a stand-alone program to play Dominion by yourself against AIs.

Since at least 2010, an online server called isotropic has been running Dominion games online. Its interface is very simple, and a lot of players have been enjoying the fact that it is free and of relatively good quality. Vaccarino has even been using it to playtest his expansions with select players. Along isotropic, there used to be councilroom, a website used to measure various statistics about Dominion cards from isotropic game logs. A lot of expert players seem to have enjoyed the stats reported by councilroom. And there used to be a free iPhone app that allowed people to play Dominion online with other people, but it was discontinued in April 2012.

Dominion generated dozens of projects run by motivated fans. How could a game publisher channel these fans? And how much control should be exerted?

27 October 2012

Game modes and MVC, part 2/2

This article follows a high-level architecture overview of two pygame projects. In this article, I show why it is difficult to engineer a game in modes when following MVC, and suggest two ways of solving the problem, detailing their pros and cons.

MVC and modes: cross-cutting concerns
- Menu mode Game mode
Models None World, Inventory
Views MenuWidget WorldRenderer, HUD
Controllers MenuController CameraController, HUDController

MVC vs modes

In the previous article, I showed two veteran pygame developers using two different techniques to implement modes in their game. Shandy Brown uses a main controller (view) that relays events to its sub-controller(s) (sub-view(s)), so that when the mode changes, only the sub-components inside the main controller (view) change. The event manager is still sending events to the same main controller (view). This approach starts with MVC as a basis, but mode switching has to be implemented in both the controller, the view, and possibly even in the model. In short, the logic for switching between modes is scattered in each of the MVC components. This is called a cross-cutting concern.

On the other hand, Joe Wreschnig first assumes a state machine to handle the game modes. Then, in each mode, there may be an MVC. This is just another way of looking at the previous cross-cutting concern: each mode has to implement an MVC. There is an easy object-oriented answer: a Mode abstract class, with a list of models, views, and controllers as attributes, such that when a mode is created, it directly starts with an MVC structure. But now we have coupled the mode logic to the MVC logic, and this tangling is typical of cross-cutting concerns.

And this is how we end up with two ways to look at the same problem: either per MVC component (horizontally in the table nearby), or per mode (vertically in the table).

Any silver bullet?

I'm not completely sure, but I think object-oriented programming can not solve this problem by itself. Aspect-oriented programming aims at answering those problems, and there are some aspect libraries in Python. However, some Python programmers argue that Python is a dynamic language with powerful introspection mechanisms, and this allows for decorators and monkey patching to do the work just fine. I have not tried any of these solutions (although decorators seem quite interesting). Rather, I want to keep my code simple and readable, and I try to mix the previous two approaches in one, aware that there is no silver bullet. Let us look at the pros and cons of each approach.

The first approach involves a central event manager, main model, main view, and main controller. The single event manager makes debugging relatively easy, since all events have to go through a central point. However, since the main view and the main controller forward messages to their sub-components, there is no single point of control in a given mode anymore. There is another advantage of keeping the MVC at a higher level than the mode management: the view implementation can switch at any time in development from pygame to pyglet or panda3d.

There are also disadvantages. The first happens in an event manager with pubsub: let's say the HUD widgets in the view subscribe to the main view for ScoreUpdateEvent. The main view itself must subscribe to ScoreUpdateEvent from the central event manager. When the view changes mode, it should unsubscribe from ScoreUpdateEvent from the event manager, but should still subscribe to tick events. This means unsubscribing happens per event, and not per component. Thus, the view should memorize which events it subscribed to in the previous mode, and unsubscribe from each of them. This is awkward. Another architectural problem shows up when, say, a model component tries to create a controller component. The new controller component will have to subscribe to the model event broker. This is bad: there is a now controller in the model.

In the second approach, the mode state machine is central, and each mode implements its own event manager and MVC. Unlike the first approach, there is a single entity managing mode transitions, which is a good point. Another advantage is that sub-components do not need to unsubscribe from the central event manager: they stay subscribed to their mode's event manager until the mode is killed. But this has the drawback that the mode state machine must subscribe to each mode's event manager to be able to receive events for mode switching. Moreover, each mode now has its own model. For simple menu modes, there is no model, so it's not a big problem. But the config menu may change some values that the game may need to know about. It seems that data can be exchanged between modes only through 1) events, 2) init arguments, or 3) config files. A last disadvantage is the fact that there is no central place where the view is: each mode has a view, so switching from pygame to pyglet will require updating all the modes.

Conclusion

After giving a try to the first approach, I did not like having to unsubscribe to the event manager. So I went with the second approach: each mode has its own event manager and clock. I think that each mode having its own context is actually a good thing: a game instance should not need anything that is not in a config file or too big to fit as an argument or attribute of an event. For example, if the character selection mode returns the character name as a string, it passes it as an attribute of a GameStartEvent, so that the game instance can determine which character to play with. And finally, to fix the problem of changing from pygame to pyglet, my views exist in their own files, not with the mode's controller or model.

Links