Building a Monopoly app in Salesforce - part 13: Building houses and hotels

Building houses is an important part of the game, so it definitely needs to be included in our version in Salesforce. Fortunately, Flow Builder offers great features to create an appealing user experience for this. Let’s not waste too many words on the introduction and get straight to work.

To determine whether a player can buy houses, we first need to know whether that player owns all the Assets in an Asset Group and whether more houses can still be built within that Asset Group.

So we’re going to define the following on the Asset Group:

  • the total number of Assets in the Asset Group
  • the Purchase Price for houses in each Asset Group
  • The maximum number of houses that may be built on the combined Assets (streets) of the Group (city).

For this, we’ll create the fields Number of Assets in Group (number), Property Purchase Price (currency), and Maximum Number of Houses (number). Access to these fields will be managed not through profiles, but through Permission Sets.

To enter the values for these new fields on the Asset Group records, we go to the Asset Groups tab. There, we select the “all” list view and add the new fields via Fields to Display.

We can now use this list view to fill in how many Assets are in each Group and how much a house costs on the Assets within that Group.

For the Stations and Utilities groups, we leave the Property Purchase Price field blank because houses can’t be purchased there.

An object that exists solely for use inside this flow

While running the flow, we also want to know how many buildings have already been constructed for each Asset Group, and how many can still be purchased. We need this information, along with a few other details, to determine the following:

  • whether the player is allowed to buy buildings in that city (Asset Group)
  • How many buildings are already built in that city
  • how many more buildings the player can buy
  • the total cost for purchasing the desired number of buildings
  • whether the player has enough cash to make the purchase

This involves quite a bit of data, and we’d prefer to keep it all together in a single object. However, I’d rather not place all of it on the Asset Group itself, in order to keep that object clean and compact. Besides, most of this data isn’t needed anywhere else except during the flow where we perform the checks mentioned above.

That’s why we’re creating a dedicated object to store this data together: Building Transaction.

The fields we will surely need are:

  • Asset Group
  • Number of Assets in Group
  • Property Purchase Price
  • Asset Group Max number of Buildings
    • 5 x the number of Assets in the Asset Group
  • Asset Group Number of Buildings Built before
  • Transaction Quantity
  • Asset Group Number of Buildings Built after
  • Transaction Amount

What is the screen flow going to look like

In designing this flow, we let the user interface we want to achieve guide our approach.

In a Repeater Component, the player should see a tile on the screen for every city where they own all the streets and where not every street yet has a hotel.

In each tile, the player can use a slider to choose how many buildings they want to buy. The slider’s maximum for each tile is calculated as the maximum number of buildings allowed (a hotel is basically just five houses) minus the number of houses already built on that city’s streets.

After the player has indicated where and how many houses they want to buy, the new number of Buildings on the Assets in that Asset Group must be updated, and the purchase amount must be subtracted from the player’s Cash Balance.

Of course, we’ll also include a check to verify whether the player has enough Cash, and if not, they won’t be able to buy that number of houses.

Now that we’ve broadly defined what we want to deliver, we can analyze what we need in order to make it happen.

Determining in which cities the player can buy houses

We can treat this process as a standalone process that doesn’t require any screen input. Through the inputPlayer, we have access to all the data we need. That’s why we’ll build an Autolaunched Flow for this.

By first retrieving all the Assets owned by the inputPlayer, we can then identify the Asset Groups in which the player has Assets.

We'll do this in three stages:

Get Assets where Owned_by_Player__c equals inputPlayer.Id. And of course, the Assets must not be mortgaged, since according to the rules you’re not allowed to build houses on them.

We then use a Transform element to create a Text Collection Variable containing the IDs of the Asset Groups to which the player’s Assets belong. We’ll use this collection variable afterward to retrieve the Asset Groups.

On the left side, we select the collection from Get Assets owned by Player. On the right side, for the output, we choose Type Text and check the option for Multiple values.

Now we drag the Connector from Asset Group to the output. Thanks to this functionality of the Transform element, we don’t need a Loop here.

The third step is to retrieve the Asset Groups. In the criteria of this Get Records element, we select Record Id, use the Operator “In,” and set the value to AssetGroupIds, which is the result of the Transform element. By filtering out Asset Groups where the Property Purchase Price is empty (null), we exclude those for stations and utilities, since houses can’t be built there anyway.

At this point, we still don’t know which of these Asset Groups the player actually owns in full.

We can quickly test that with a double, or “nested,” loop. In the first, outer loop, we go through the Asset Groups we’ve retrieved one by one.

The first step within this loop is to filter the player’s Assets so that only those belonging to the current group remain.

On the Asset Group—the city—it’s already defined how many Assets (streets) belong to that group. If the number of Assets we’ve just filtered matches that value, we know the player owns all the streets in that city and is therefore allowed to buy houses there.

We need an Assignment element to count how many Assets come out of our filter.

We create a new Number variable with a default value of 0 and 0 decimals. We name it numberOfAssetsOwnedInCurrentGroup. For the Operator in this Assignment, we select Equals Count. As the value, we choose the filtered Assets from this Asset Group.

Next, we add a Decision element. What we’re testing is whether the number of Assets the player owns in this Asset Group equals the number specified on the Asset Group record. This check confirms whether the player owns all the streets in that city.

Only if the player owns all the Assets in the group—all the streets in the city—do the following elements matter. So we continue along the Yes path of the decision.

We’re still inside the loop over the retrieved Asset Groups. Under the Yes path of the decision, we start the inner loop. For this, we use the streets (Assets) of the city (Asset Group) that we had already filtered out.

Within this loop over the streets of the city we’re currently examining, we count the number of houses that have already been built. We’ll store that number in a Building Transaction record. To do this, we take a small step back: before entering this Asset loop, we first prepare the Building Transaction.

Here’s what we can already determine for the Building Transaction before we start counting the houses that have been built.

In the Assets loop we'll add the number of houses that is already built on each street to:

  • Number of Buildings built on de current Asset Group (of the outer loop)
  • Asset Group Buildings Built in the Building Transaction
  • Asset Group Buildings Built after in the Building Transaction

And will subtract this number of built houses from:

  • Number of Buildings Saleable in the Building Transaction
  • Asset Group Buildings available in the Building Transaction

Now we close the Asset loop and then, still within the Asset Groups loop, add the Building Transaction to the collection of Building Transaction records.

Now we’ll actually save these Building Transactions in the database. In the screen flow where we call the subflow we’ve just built, we’ll retrieve them by filtering on the Flow Interview Id.

We will now save and activate this flow.

The Screenflow

While designing this flow, I kept running into the limitations of the Repeater Component, which made my original plan for the flow unworkable. I had actually wanted to create a single flow for buying and selling houses and never intended to store the Building Transactions in the database.

When that doesn’t work, you start experimenting with small adjustments until you arrive at a solution that works well enough—and that’s what we’re going to build together now. At the beginning of this episode, you already saw a preview of the User Interface we’re aiming for.

The first step in the screen flow is the subflow we built at the beginning of this episode.

This subflow receives the familiar inputPlayer variable as input. The subflow actually saves the Building Transactions. We retrieve them with a Get Records element, and at the very end of the flow, once they’ve served their purpose, we’ll delete the Building Transactions again.

Now you can see what the Flow Interview Id on the Building Transaction object is for. Although the subflow is technically a different flow, the user’s interaction with it is still part of the same flow interview as the screen flow from which the subflow is launched. That’s why we can use the Flow Interview Id to make sure we only retrieve Building Transactions for the current player at this moment.

If the subflow shows that the player can’t buy any houses, then the rest of the flow should of course be skipped. So now we add a decision: if Building Transactions are found, it means the player can buy houses, and we continue.

Als er ten minste één Building Transaction is, dan krijgt de speler het volgende scherm te zien.

First, we make sure the player can always see how much money they have. That way, as the player, you can keep track and make sure you’re not planning to spend too much.

The red text isn’t visible the first time the player visits the screen. It can become visible if, after this screen, the check shows that the player plans to spend more on new houses than they have available in cash. We’ll take a closer look at that further on.

Next, you see the Repeater Component. For each city where the player can buy houses, they’ll see one of these tiles.

When configuring a Repeater, the first step is to select the Collection Variable that contains the source data to display. In this case, that’s the set of Building Transactions we’ve retrieved.

Under Configure Display Options, we make sure the player can’t add or remove elements themselves. The slider we place in each tile is used to indicate how many houses the player wants to buy for that city.

We set the slider’s maximum based on the number of houses that can still be purchased for that city, according to the data in the Building Transaction.

This way the maximum of the slider can be different for each city.

The option to display two Repeater tiles side by side can be configured under the Style tab of the Repeater Component itself.

Before we carry out the purchase of new houses, we first need to check whether the player has enough money for all the houses they want to buy. I would have loved to do this with a Screen Action that immediately calculates the total based on the player’s slider adjustments, but a Screen Action currently can’t work with data from the Repeater component. So we perform this check after the player clicks the Next button. Volgende knop klikt.

Broadly speaking, we do the following:

  • For each city we multiply the purchse price of a house in this city by the number of houses that the player wants to buy.
  • We add those numbers up.
  • We check if the total amount is higher than the player's Cash Balance.
  • Does the player stay within budget, that we can continue with the rest of the flow.
  • If the player wants to spend more money than they have, we immediately redirect them back to the Buy Building screen, where they’ll now also see the red warning text.

Since the player might once again try to spend too much money on a later attempt, it’s important to reset TotalTransactionAmount to 0 before we start counting.

Next we evaluate each element of the Repeater output.

Within the loop, we first use a Decision element to check whether the player wants to buy buildings for that city. If not, we can immediately move on to the next city.

Next, we retrieve the full Building Transaction record associated with that element in the Repeater, since the Repeater’s output doesn’t include all the data from the original input record.

We use this filtered collection, which should contain only one element, for the inner loop. We still need a Loop here, because even though the filtered collection has just one element, it’s still a collection—so we need a loop to be able to look at its elements.

within there we calculate how much the player wants to spend on houses in this city.

The formula transactionAmount calculates the number selected in the Repeater’s slider multiplied by the Property Purchase Price stored in the Building Transaction.

Now we update the Building Transaction, add the calculated purchase amount to the total, and compile a collection of the IDs of the Asset Groups where houses need to be built.

After finishing the Loop over the Repeater elements, we compare the total amount to be spent with the Player’s Cash Balance. If the player is trying to spend too much money, we send them back to the screen.

The conditional visibility of the red warning text is based on this decision.

The Player does have enough cash

When the Player's Cash Balance is sufficient to buy the houses the player chose by using the sliders, then we can process this transaction.

Since we’ll eventually be updating the number of houses on the different Assets, we now need to retrieve those Assets. Which Assets we need and will retrieve is determined by the AssetGroupIds collection variable, which we filled with only the IDs of the Asset Groups where the player is allowed and intends to buy houses.

After that, we use a nested loop. We already did this with the Repeater output and then a loop inside it for the related Building Transactions.

For each Building Transaction, we need to add the correct number of purchased houses to the correct streets. So the outer loop processes each Building Transaction.

Inside that, we first filter out only the Assets that belong to this Transaction. We can do this with the Filter element, using an attribute shared by both the Building Transactions and the Assets: the Asset Group Id. Practically speaking: if we’re now looking at the houses we bought for Arnhem (a single Building Transaction), then to build those houses we only need Arnhem’s streets (Assets).

More technically speaking: by filtering which records are used in that inner Loop, we avoid unnecessary iterations and wasted processing time.

Limits
A few years ago, Salesforce had a limit stating that within a Flow Transaction a maximum of 2,000 elements could be touched. With loops, the same elements are visited multiple times, so that number can add up quickly.

This limit no longer exists, but it’s still a good indicator of how much time a flow transaction will take. If a transaction runs longer than 10 seconds, the platform will cut it off and the flow interview will fail.

By making sure during development that each transaction stays well below 2,000 touched elements, you can practically avoid such “Timeouts.”

One way to prevent this is by filtering before entering a loop, so you avoid unnecessary iterations.

With this filtered collection containing only the Assets of a single city, we then enter the inner loop—if needed…

Because there’s a chance we may not even need a Loop—thanks to the Transform element.

What we need to do for each Asset is assign the correct number of houses. With the Transform element, you can create or update an entire record collection variable in a single step—without needing a Loop.

Let’s take a step back to the calculation we need to perform here—and we’ll walk through it with an example. Suppose we’re adding the purchased houses to the streets of Arnhem. For the calculation, we need three numbers:

  • How many houses are already bauilt on the streets of Arnhem
  • Ho many more houses can we add
  • How many streets does Arnhem have

The number of houses on each street after we build, must be (number of old houses + number of new houses) / number of streets.

Suppose we already had 6 houses, we’re adding 3 more, and Arnhem has 3 streets. The calculation would be: (6 + 3 = 9) / 3 = 3.

If this calculation results in a whole number, we can use the Transform element to assign the correct number of houses only to the streets of Arnhem.

This is how we will assign the new number of houses to each Asset.

To make sure we only assign whole numbers, we use rounding down in the formula. For example, if after construction we should have 10 houses, then the number of houses per street that we can assign using the Transform element is 10 ÷ 3 = 3.33, rounded down to 3.

With the FLOOR formula we round down to a whole number.

After that, we need to check whether there are still more houses to build. If we have 10 houses to distribute over 3 streets, then with the Transform element we’ve just assigned 3 houses to each street. That means 1 extra house still needs to be added to one of the streets. To determine if there are any leftover houses to build, we need this calculation:

Total number of houses that should exist in the whole city after construction – (number of houses per street we’ve already built * number of streets).

In this example that comes down to 10 - 3 x 3 = 10 - 9 = 1.

In the example, Asset Group Buildings Built after is 10, and buildingsBuiltInTransform is 3 \* 3 = 9. So the value of numberOfBuildingsYetToBuild is 1.

We then use a Decision element to check if there are more houses to build.

If so, we still need to assign an extra house to one (or two) of the three Assets—or to one of the two Assets for cities with only two streets. In this example, it’s one of three. For cases where we still have two houses left to distribute, we do need to use a Loop.

Within this Loop, we therefore also need to check—after assigning the first extra house—whether a second house still needs to be placed on the next street.

Therefore we must also check if there are any houses left to build inside the Loop. For this reason we move the Decision element that we put before the loop to inside it.

In the example of 10 houses in Arnhem, the outcome of this decision is Y for the first street and N for the second and third.

We therefore only need to assign a fouth house to the first street. Ten we will have 4 + 3 + 3 = 10 houses in the city.

Each city has one street where you can charge slightly higher rent. Naturally, that’s the street where you’d prefer to place the largest number of houses. We achieve this by sorting descending on Base_Rent_Price__c in the Get Assets element from a few steps back.

In the Assignment element, we don’t just assign the correct number of houses to the street—we also subtract one from the number of houses still to be built. That’s the number we check in the decision at the start of each loop iteration. This way, we prevent building more houses than allowed.

Whether we added any more houses to the street inside this loop or not, we always add this street to the updateAssets collection. After we've done that, we can exit the inner and outer Loop.

Now we need to commit our changes to the Asset records. We perform the following updates.

  • We subtract the total amount spent on purchasing the houses from the Player's Cash Balance and update the Player record.
  • We update the Assets to save the new number of houses on each street.
  • We delete the Building Transactions because outside this flow we don't need then for anything.

This was quite a complex flow to build—and especially to design. What we still need to do now is insert it in the right places within the SCR – Roll and Move flow. We’ll tackle that in the next episode.