Boat Rental

The following problem was posed as the DMCommunity September 2024 challenge.

Rental Boats

Floataway tours has $420,000 that may be used to purchase new rental boats for hire during the summer. The boats can be purchased from two different manufacturers. Floataway tours would like to purchase at least 50 boats and would like to purchase the same number from Sleekboat as from Racer to maintain goodwill. Also, Floataway Tours wishes to have a total capacity of at least 200. Data about the boats is summarized below:

Boat Manufacturer Cost Seating Expected Daily Profit
Speedhawk Sleekboat 6000 3 70
Silverbird Sleekboat 7000 5 80
Catman Racer 5000 2 50
Classy Racer 9000 6 110

Can we solve this using cDMN? Let’s find out.

As always, we want to start by creating a glossary (See 3.1 Glossary). For this challenge, we can clearly identify two types: boats, and their manufacturer.

Type
Name Type Values
Boat String Speedhawk, Silverbird, Catman, Classy
Manufacturer String Sleekboat, Racer

We’ve also been given some information about the boats that we need to be able to represent: their manufacturer, their cost, their seating capacity, and their estimated profit. These properties are best represented using a function, i.e., a mapping from one type on another.

Additionally, we also want a way to represent the number of each boat type that we buy, and the total of each boat type bought per manufacturer.

Function
Name Type
manufacturer of Boat Manufacturer
cost of Boat Int
seating of Boat Int
profit of Boat Int
nb of Boat Int
total of Manufacturer Int

Finally, we need a way to represent the three requirements (total boats, total capacity, total price) and a way to represent the total profit. As these are just numerical values, we can represent them as constant variables. Note that constant here does not mean that their value always needs to be known up front, but rather that they have exactly one value in each solution.

Constant
Name Type
total boats Int
total capacity Int
total price Int
total profit Int

That concludes our glossary! We know have all the symbols that we need to start expressing rules. The first piece of logic that we’ll add is quite a simple one: we can only buy a positive number of boats. Because our function nb of Boat maps each boat on an integer number, it would be possible to have negative boats without such a constraint.

Non-neg constraint
E* Boat nb of Boat
1 -

This table can be read as “The number of each boat should be greater than or equal to zero”.

Next, we add the constraints on the total requirements:

  • minimum capacity of 200

  • at least 50 boats

  • max total price of 420000

  • total Sleekboat is equal to total Racer

Starting value
E* total capacity total boats total price total of Sleekboat
1 ≥ 200 ≥ 50 ≤ 420000 total of Racer

Of course, we still need to define how we calculate these values. The total profit, capacity and price are luckily quite easy to calculate using a C+ (count) table, as we just need to multiply the property of each boat with the number bought of each boat. Even better, we can count all three of them together in the same table.

Count parameters
C+ Boat total profit total capacity total price
1 - profit of Boat * nb of Boat seating of Boat * nb of Boat cost of Boat * nb of Boat

Additionally, we also need to calculate the number of boats per manufacturer. This table is similar to the previous one, but we will also need to “go over” (= quantify) each manufacturer as well.

Calculate number per manufacturer
C+ Boat Manufacturer total of Manufacturer
1 - manufacturer of Boat nb of Boat

In essence, this table will, for each manufacturer, count their number of boats. Note that the cell manufacturer of Boat is crucial here, as this one ensures that only the boats by that manufacturer are counted.

And that’s basically it for the business logic! All in all not that difficult. All that remains is specifying the concrete boat data using a data table, and adding a goal table to express the optimization.

Boat info
D Boat manufacturer of Boat cost of Boat seating of Boat profit of Boat
1 Speedhawk Sleekboat 6000 3 70
2 Silverbird Sleekboat 7000 5 80
3 Catman Racer 5000 2 50
4 Classy Racer 9000 6 110

Goal
Maximize total profit

We can now run our model using the cDMN solver or the online IDE. This results in the following output:

Model 1
==========
nb_of_Boat := {Speedhawk -> 28, Silverbird -> 0, Catman -> 0, Classy -> 28}.
total_of_Manufacturer := {Sleekboat -> 28, Racer -> 28}.
total_boats := 50.
total_capacity := 252.
total_price := 420000.
total_profit := 5040.

Elapsed Time:
0.499

The optimal distribution is to buy 28 Speedhawk en 28 Classy, for a total estimated profit of 5040 dollar.