My Pet Project Monopoly 2

My Pet Project Monopoly 2

By Nigel Eke

Overload, 8(35):, January 2000


This is the second, of a series of three articles. In the previous article I gave a rundown of stages that a 'pet project' (Monopoly®) has been through over the many years I've been learning C, C++, Windows programming, OOP etc.

This time we take a closer look at one of the completed C++ programs, what classes were involved, how they worked and some the consequences behind the design.

UML diagrams have been used to show the hierarchies and interaction of the classes. These have been created for this article and UML itself wasn't used during the design process.

Introduction

Let us start with a couple of caveats. Firstly, I don't intend to describe each and every class here, but restrict things to some of the classes which are of interest to the article. Secondly, this isn't intended to be a perfect design by any means.

Because of the nature of the project, i.e. just for myself, I just dived-in and 'hacked' things (something I don't recommend and certainly don't practice at work!). But this approach raises issues quickly and, one hopes that because they are raised through the process of "doing", the consequences are better understood.

I've subsequently found out many of the points I came up against have been discussed in various OO design and Pattern books - which at the time of coding were not known to me. Everyday practitioners of C++ will look at these, probably recognise them, sigh deeply (and know solutions instantly)

Basic Classes

The main text of this article flows around the following classes:

Board Class:

This (misnamed) class managed the game. It provided methods for moving players around the board, picking cards, quitting players from the game and other miscellaneous tasks.

Player Class:

Each instantiation of this class represented each of the players taking part in the game. (Also see implementation note 1)

Site Class:

This formed the top class in a hierarchy of different types of site one finds while touring the Monopoly board. (Also see implementation note 2). As implemented the full class hierarchy is shown in figure 1.

Card Class:

This formed the top class in a hierarchy of different types of Chance and Community Chest cards. The full class hierarchy is shown in figure 2.

Site class hierarchy

Figure 1. Site class hierarchy

Card class hierarchy

Figure 2. Card class hierarchy

Implementation

The board (an array of Site* ), the participants (an array of Player* ) and the chance/community chest cards (arrays of Card* ) were all global variables (see implementation note 3).

One of the Site 's virtual methods was called Action (see implementation note 4). This is called when a player lands on a site in order to perform a site-dependent action, i.e. collect tax, collect rent, pick a chance or community chest card etc.

Similarly one of Card 's virtual methods (also called Action ) was called when a card is picked after a player lands on a chance or community chest site. This performed the card-dependent action, i.e. collecting fees, moving to other locations on the board, celebrating birthdays etc.

Figure 3 shows what happens when a player rolls the dice and lands on a chance card site, and the card picked up is a CashCard to collect doctor's fees for example.

A player’s move

Figure 3. A player's move

Similarly figure 4 depicts the sequence of events when a player buys a site.

A player buys a site

Figure 4. A player buys a site

Implementation Notes

  1. The intention was to have Player as an abstract class with inherited classes of HumanPlayer and ComputerPlayer . In fact the game was completed for human players only and Player remained as a concrete class.

  2. The only " Site " missing from this hierarchy is the JustVisitingSite . The " Action " for this site is to do nothing, which was the default Action provided by Site . One could argue that Site should have been an abstract class, with Action as a pure virtual method. A concrete JustVisitingSite class would then be required to implement the do nothing action.

  3. Global variables humph! Yes, I know now good design eliminates global variables, but I must have been overly familiar in using them when this program was first written. These are probably better encapsulated in a class such as MonopolyDocument .

  4. " Action " could be better named but it achieved its purpose.

Some Design Points

On the face of it the above might look all right, but there are issues here. Some of these can be deduced from the diagrams, others need a little more elaboration.

Copy semantics

The Site and Player are reference, rather than value, objects, i.e. it does not make sense to take a copy of the 'Car' player, debit £50 cash from the copy, and expect that the 'Car' player itself is £50 worse off (although that probably would have been the intention had it been coded that way). It must be confessed that this implementation did not declare a private copy constructor, and assignment operator. Rather it relied on the programmer instead of the compiler to enforce the correct copy rules.

Class dependencies

The following relationships exist between Players and Sites.

  • A Player is located at a Site .

  • A Site , potentially, has visitors - i.e. a number of Player s.

  • An AssetSite may have an owner - i.e. a Player .

  • A Player , will have assets - i.e. a number of Site s.

Bearing the required copy semantics in mind, it seems quite natural to want to write:

Site& aSite (aPlayer.Location ());  
  // or…
Player& aPlayer (anAssetSite.Owner ());

This means, though, that neither Site nor Player is a standalone class; each is dependent on the other. I wouldn't want that dependency if I could help it, but in this case I haven't really come across a better solution. References [1] and [2] address this issue in depth - perhaps I should go back and re-read them)

More Class dependencies

Another interesting dependency involved the AssetSites and the colour group to which they belong. In this implementation ColourGroup and Site were referred to in two ways each(!), (yes, I did say two!). First they both were enumerated types. The ColourGroup enumerated type was passed to an AssetSite when the object was created.

Secondly, they were in global (again!!) arrays. The Site* array, already mentioned, representing the board, and a ColourGroup* array indexed by the enumerated type for a given colour group, for each set of the Site 's enumerated types.

If one were to add new sites or colour groups then two places in the code would need changing, one - the addition of the appropriate enumerated type and two - the creation of the object itself. Although this works it clearly isn't good practise - it would be easy to forget (or overlook) changes one of those places.

Colour groups need to know about the sites that make up the colour group. It's debatable if sites need to know about their colour group, but (ignoring necessary casts) it seems natural to want to write:

AssetSite& anAssetSite 
                (aPlayer.Location ());
ColourGroup& colourGroup
          (anAssetSite.ColourGroup ());

But we cannot have the following constructors:

class AssetSite {
public:
  AssetSite (ColourGroup&, etc…);
};
class ColourGroup {
public:
  ColourGroup (AssetSite&, AssetSite&);
// Utility, Mayfair & OldKentRd
  ColourGroup (AssetSite&, AssetSite&, AssetSite&); // Most BuildingSites
  ColourGroup (AssetSite&, AssetSite&, AssetSite&, AssetSite&); // Stations
};

because an asset site cannot be created until its colour group exists, and vice versa. So do we have a default constructor for a colour group, then add the sites later, or ignore the colour group when the site is constructed and assign it later? Which, if any, of these is appropriate should come out of the design process and this gets discussed in the next article.

What are Sites?

In this implementation it can been seen that, when we look at the hierarchy under AssetSite , that the AssetSite s represent two things. One is the Site itself - the player lands on the site and something happens. The other is they represent an Asset that is owned by a player, i.e. the TitleDeed cards. The distinction between these representations was not made in this implementation.

Model-View-Controller

This application lends itself naturally to the Model-View-Controller pattern. However this design and implementation wasn't based on the pattern and any similarity was pure happenstance.

The purpose of the pattern is to separate the data from the user control and views of the data. More details on the pattern in general can be found in references [3] and [4]. It is also discussed, with respect to this project, in the next article.

This implementation described here partly failed in this respect.

As you can see from the diagrams opposite, whenever a user issued a command, the controller ( theApplicationWindow ) had an internal knowledge of the data (by direct access to those global variables), and manipulated them accordingly.

The viewing part of the system was slightly better organised. The main view onto the status of the game was known as the BoardView . This had it's own knowledge of the bitmaps representing the sites, and the icons representing each player, which means there's no dependency of the game objects (Site, Player etc) on the view, but the view (a window) still directly accessed the appropriate global variables. The same was also true of player status and auction dialog windows.

A thought on STL

The standard template library wasn't around at the time this version of the program was implemented, but it would have been useful to use the container classes. I.e. all the current players could be stored in std::list<Player> , the assets belonging to a player referenced through std::set<AssetSite> etc.

But wait… we've already mentioned that Player and Site are reference based objects and not value based objects required by the STL. How we get round this and a different look at the game's design will be given in the final part of this article. Also we have a first stab at applying some Patterns.

References

Analysis Patterns - Reusable Object Models Martin Fowler; Addison Wesley; ISBN 0-201-89542-0

Designing Object Oriented C++ Applications Using the Booch Method Robert Martin; Prentice Hall; ISBN 0-13-203837-4

Design Patterns - Elements of ReusableSsoftware Gamma et al; Addison Wesley; ISBN 0-201-63361-2

A System of Patterns (Pattern - Oriented Software Architecture) Buschmann et al; Wiley; ISBN 0-471-95869-7






Your Privacy

By clicking "Accept Non-Essential Cookies" you agree ACCU can store non-essential cookies on your device and disclose information in accordance with our Privacy Policy and Cookie Policy.

Current Setting: Non-Essential Cookies REJECTED


By clicking "Include Third Party Content" you agree ACCU can forward your IP address to third-party sites (such as YouTube) to enhance the information presented on this site, and that third-party sites may store cookies on your device.

Current Setting: Third Party Content EXCLUDED



Settings can be changed at any time from the Cookie Policy page.