Welcome to Part 3 of the series! In Part 2 we discussed the process we’re going to use to design our system. Now we’re going to use that process to start designing.
The Context
Before we start designing, let’s talk through a bit about why we want to build this system in the first place. What are the basic use cases that are motivating us to build this?
- I am in my house and want to play board games with family and friends, but I don’t want to physically set it up. We are located in the same room and are close enough together that we could play the game physically, but it would simply be more convenient to play virtually.
- I am in my house and want to play board games with someone who is in a different room or upstairs. Maybe one or both of us is sick, or we are feeling particularly lazy. So we need a virtual substitute to enable us to play together.
- I want to play a game with friends or family who are in a physically different location.
- I want to play a particular game right now, and I don’t care who my opponent is.
- I want to play any of my favorite games, but I want a particular flavor of gaming experience. For example, it needs to be short, because I only have 15 minutes to play. Or I want a deeply competitive match where I have more time to think through my moves. Or I want to play against opponents with a particular game play style.
- I want to analyze my past games so I can improve.
- I want to make my own games that I can play with others.
There are some insights we can draw from these use cases that may inform our system design. The first is that we are looking for a virtual substitute for the physical board gaming experience. This means it might be sensible for players to share a single device to play. Maybe both players are sitting on the couch, and casting to the TV (or playing on a game console, or even playing on the TV itself), and they can pass the ‘controller’ when the player turn changes. When playing on a single device, the game session can be entirely handled on that device. There is no need for a server or a P2P protocol to manage game state across devices. On a single screen, we may need a mechanism for hiding and showing information private to a player. For example, if they are playing a card game, they may need to be able to ‘peek’ at their cards while their real life opponent looks away.
If players are virtualizing their physical gaming experience, they would want to arrange their own game sessions. In other words, they control who is allowed to enter the game session instead of a matchmaking service. This has security implications. Playing with unknown players who could be anywhere in the world requires certain safeguards. We may not need those safeguards if players control who they interact with and must manually invite other players to games.
This mode of playing may be more social than competitive. Players who know each other are sharing an activity together. This has design implications. We may want the ambience of the system to feel more relaxed and playful. We may want subsystems that support chat or reactions or other kinds of communication layered on top of the game. They aren’t there just to play a game, they are there to have fun together.
A few of the use cases suggest a more competitive nature. The focus is more on the game itself, and less on the people you are playing with. To support many of the functions that go along with that, we might need to track more information about players’ gaming history. A simple one is player skill level, such as an ELO rating. If players are looking for a competitive experience, they will likely get the best experience playing against someone with a similar skill level. Time is also a concern here. In competitive gaming, turns need to have time limits. We will need to build a system for enforcing time limits.
In a competitive system where players don’t know their opponents, we will need a matchmaking system. Anyone looking for a game joins the system, and it matches opponents together. This could use a simple first come, first served algorithm. It could use a more complex algorithm that prioritizes making good matches (e.g. similar skill levels) over time spent waiting for a match.
Another implication of players not knowing their opponents is that we need to handle bad actors. One common case is when a player joins a game but then doesn’t play, and their opponent gets a lousy gaming experience. Another is when players try to game the rating or matchmaking system. Another is players or bots who are using the system for something other than its intended uses, such as advertising, stealing information, or performing other shady activities. We may need additional monitoring systems to spot this bad behavior. We may need a system to temporarily or permanently ban users. We may want to limit the ways in which players can interact with each other. For example, instead of a chat system, we may have a fixed set of reactions players can use to communicate, such as Great move
, Good game!
, and so forth.
Reviewing past games to improve suggests we need a rich navigational interface on top of the game history. We also need to be able to replay the game in the game session UI. We will also want some kind of AI assistant which can help analyze moves and tell the player why a move is bad or suggest alternatives to explore. This may dovetail into the need for AI players, so that the human player can interactively explore how the game might play out if they had made different moves.
AI players could tie into other use cases as well. They could be used to help shorten matchmaking times, by supplying an AI player when a suitable human player cannot be found. They could be used to provide very specific types of opponents if a player is looking for a particular kind of gaming experience.
The ability to create my own games suggests the need for a content creation system and plugin architecture. We need to define what assets a user needs to supply in order for the system to be able to use the custom game. We may need to provide an interface to support creation, or extensive documentation that describes the process. The system needs to be able to dynamically load custom games.
Two Systems?
After thinking through the different use cases, it feels like there could be two categories of experience that could have two different audiences. One is social, one is competitive. Do we actually have two separate systems here?
That is a fair question. Let’s look at what those two experiences would have in common.
- The games. Regardless of your motivation for playing the games, they are still the same games. A social Scrabble experience where players chat about what good moves could be and a cut-throat Scrabble match are both using the same board, same pieces, same game mechanics.
- The game sessions. Once you are playing a game, the UI for displaying the game is the same regardless of why you are there. The board needs to be rendered the same. There may be additional components on top of that depending on the situation (e.g. chat view), but the core game UI is the same. The state for the game session is the same. You need to track whose turn it is, validate moves, etc. There may be additional bits of state on top of that, such as a timer in timed games.
- Communication systems. Although the social and competitive experiences may not support all the same communication systems, some may be shared, such as a reaction system.
- Game histories. Even when playing socially, you may want to store your game history so you can look back at previous games and find a great move someone made or a crazy situation that came up.
Since there are shared components, that suggests we might benefit from a layered architecture, where the core components of the system are in one layer, and we can build other layers on top of that with additional functionality. Perhaps something like this diagram:
Ordering our Development Activities
At this point, we are looking at a pretty big system to handle our different use cases. There are many possible components and many technologies we need to incorporate. We may need some components to handle high scale. If we try to build that all up front we are doomed to failure. Developing all of this takes time, and the more time it takes, the more uncertainty the project will have. We will not have perfect knowledge about the technologies we are going to use, and we will learn new things along the way. We need to take an iterative approach that allows us to build usable parts of the system and progressively add more to it. This reduces the risk of project failure and allows us to deliver a usable system that we can start deriving value from.
The possible layering suggests a particular order in which we might want to develop the system components. Social and Competitive gaming systems could be two different products. They could be developed one after the other. They both depend on the Core Gaming Platform, so we need to start developing that first. Within the Core Gaming Platform, games and their rulesets and game sessions are critical components that we need in order to use the system in any context. Those should be developed first. Game History depends on those components and cannot be developed until they are completed. Communication is a loosely coupled subsystem that could be developed at any time.
Within the Social gaming system, the game lobby / invite system needs to come first so players can create games. The chat / friends system can be built next. We may want a second iteration on the game lobby / invite system after we implement the friends system.
Within the Competitive gaming system, the matchmaking system needs to come first so players can create games. Player analytics depends on the Game History component. More advanced matchmaking depends on Player analytics. So we will likely need to develop matchmaking in stages, where a first iteration is a basic first come, first served system, then develop analytics, then iterate on matchmaking to add more advanced algorithms that use the analytics. Security often needs to be considered across all aspects of a system, so it should probably be developed continually, and iteratively, alongside other development within the Competitive gaming system.
Custom Games depends on the core gameplay systems. You need to be able to start a game session and render the board and make moves to be able to use a custom game. It could be used within either social or competitive gaming. The game selection interface could be built in such a way that those systems do not even to know that custom games even exist – they are just treated as another possible game to choose from. We will need to build this after we get some of the core gameplay systems in place, but we don’t clearly need to order it beyond that yet. There is also a potential two-way dependency here. The game system needs to be able to load custom games, so it needs to know something about where to find custom games, and how to load them. There is a shared contract both components need to know about in order to work together properly. This likely means some joint iteration between the core gameplay systems and the custom games functionality.
AI players also depend on the core gameplay systems. The AI player needs a game to play. The core gameplay system may need to know about AI players as well. If AI players are used in some circumstances but not others, or if human players control when an AI player is used, that concept needs to be part of the system somewhere. That may be in the core layer, or it may be part of the social or competitive layers. If the game history interface uses an AI assistant, it will need to know about AI players. We’ll need to be conscious of how that dependency structure plays out.
Another thing to consider is the deployment environment and infrastructure needs at various stages in the process. If we build a local couch gaming experience on a single device, that is a single application that does not need any client/server or P2P architecture. If we are targeting a social experience that people deploy themselves, the system may not need to handle scale. If we are not storing gaming history, we may not need any persistence systems. If games are locally run or invite only, we may not need user authentication. Each of those technology needs requires a decision. Do we persist with flat files or a database (and which one)? Which authentication provider do we use? Do we need a message queue? Do we do a monolithic deployment or microservices? That’s a lot to think about and analyze. We don’t want to do that all at once, and we’d like to defer those decisions as much as possible so that we can gain a better understanding of the system we are building and what it truly needs.
Let’s gather some insights before we proceed to making a plan:
- Social gaming seems less complex than Competitive gaming.
- Everything needs the core game rules and game sessions components.
- Some components have possible two way dependencies that should be dealt with early to avoid large rewrites.
A Rough Plan
We have enough at this point to lay out a basic plan for development. We will mostly certainly need to revisit this later, but we can start developing components and learning more as we go. Here is the basic order we will go in:
- Game Rules and Game Sessions
- Local (one device) social gaming
- Custom games
- AI players
- Game History
- Remote (multiple device) social gaming
- Communication
- Friends / Chat in social gaming
- Matchmaking for competitive gaming
- Player analytics for competitive gaming
Conclusion
That’s it for part 3. Now that we have a rough development plan, we will proceed to a more detailed design of our first set of components, the Game Rules and Game Sessions, in part 4.