[ Jocelyn Ireson-Paine's Home Page | Try Traveller | Traveller source | Traveller PHP script | About the design ]

# The game of Traveller

The basic predicates are `square`, `building`, `joins`, `in`, `loop`, `sells_fuel`, `buys` and `sells`, plus three ones derived from them: `next`, `clockwise` and `distance`.

Together, these describe a simple street layout which you can view here. It has squares containing shops and fuel stations. Unlike Monopoly, the squares are not confined to the outside of the board, but can form streets, some looped back on themselves, wandering over its interior.

The design of the game is discussed here.

More specifically, the predicates have the meanings given below:

• `square(S,X,Y)`: S is the number of a square, lying at co-ordinate X,Y. Each square has a unique number; these are integers (whole numbers), and work upwards from 1. Adjacent squares do not necessarily have adjacent numbers.

• `building(S,B)`: B is the name of the building in square number S.

• `joins(S1,S2)`: whereas `square` defines where squares lie and what they contain, `joins` defines how they are connected. There is one clause, `joins(S1,S2)` for every pair of connected squares. To make things simple, squares can only be connected by vertical, horizontal, or 45-degree diagonal lines.

• `in(S,L)`: The board contains several streets that curve back on themselves, forming closed loops. Each loop has a name (you will see why later). There is one clause, `in(S,L)` for every square S that lies in a loop called L. You can think of the loops as being distinct regions of a city, such as the City in London or the central part of Oxford, except that to keep things simple, they do not contain any roads apart from the loop itself.

• `loop(L)`: This defines the names of the loops. There is one clause for each loop.

• `next(S1,S2)`: This is true if S1 joins S2. Note that it is defined in terms of `joins`: S1 is next to S2 if S1 joins S2 or S2 joins S1. The import of this is that if you want to find out about connected squares, you should use `next`, not `joins`.

• `clockwise(S1,S2)`: This is true if square S1 and S2 are both in the same loop, and S2 is immediately next to, and clockwise of, S1. Its purpose is to give you a simple way of finding your way round loops. It is defined in terms of `joins`, and only works because I took care, when generating the board definition, to make sure that the `joins` facts always had their arguments in an order which would make it work.

• `distance(S1,S2,D)`, This sets D to the distance between S1 and S2. It uses `is`, an operator for doing arithmetic. The formula is the standard one for working out distance in terms of X and Y co-ordinates: `X^2` means X squared, and `sqrt(...)` means the square root of.

If you care to look at the diagram of the board, it should become clear what these facts say.

## The buildings

In this era of recession - I wrote that nine months ago, and it is still true! - it is important that we all be good consumers and keep the economy moving. So, bearing this in mind, the buildings all concern buying and selling, as you may have guessed from their names.

There are three kinds of building: wholesalers, retailers, and fuel-stations. Each retailer sells one (and one only) of five types of good to the public: coal, diamonds, glasses (tableware, not eyewear), peaches, and televisions. The retailers buy their stocks from traders, who in turn buy from wholesalers. By buying cheap at a wholesaler and selling dear to a retailer, a smart trader can make a hefty profit. Wholesalers, retailers, and fuel-stations are described by the facts below:

• `sells(B,Good,Price)`: B names a wholesaler, and will be one of the names given as the first argument of `building`. Good is the type of product sold. Like retailers, each wholesaler deals in only one kind of product. Price is the price at which the wholesaler sells one unit of the product to a trader (i.e. the price at which the trader buys it).

• `buys(B,Good,Price)`: B names a retailer. As before, Good is the product type. This time, Price is the price at which the retailer buys from the trader, i.e. the price at which the trader sells it.

• `sells_fuel(B,Price)`: This tells you which buildings are fuel stations. Price is the price per unit of fuel, and B is the name of the building.

To play Traveller, you have to move around the board, making as much profit as possible. It takes some skill to keep going: for example, you must not allow yourself to run out of fuel. But there is a twist. You yourself are not playing Traveller - or not directly. It is your task to program an automatic trader in Prolog, and to give it rules that determine when and where to buy, sell, or move. Once you start your trader off, it must run without any help from you. The only thing you can do is to watch the output: sympathise if it runs out of fuel or money; applaud if it makes a huge profit.

Before showing you how to program a trader, I need to say some more about prices. Goods are bought and sold in ``units''. So a trader may tell Prolog that it wants to buy 20 units of coal, or to sell 100 units of diamonds. To make Traveller more realistic, you can think of these units as being sacks (of coal); individual boxes (for diamonds and televisions); boxes of six (glasses); boxes of twenty-four (peaches). However, it doesn't matter how many things are inside; you can only buy in whole units.

The buying and selling prices given by `buys` and `sells` are all quoted as the price per unit.

Products have volumes, measured in cubic feet. These are given by the only fact I haven't yet mentioned: `unit_volume(Good,V)`. V is the volume occupied by one unit of Good (all goods of the same type have the same unit volume - containerisation). This matters because as a trader, you drive a lorry whose size is strictly limited. It has a capacity of 1000 cubic feet. You can always fill it up to this amount, provided you have the money, but you cannot cram in any more above the limit.

When you start off, you are given £ 5000 in cash, to spend as you will. There is no upper limit on your cash - you can accumulate indefinitely - but you are not allowed to go below zero. There are no credit facilities in this game.

Your lorry has a fuel tank whose capacity is twenty units. When you start off, it is full. At each stage in the game, you are allowed to do one of four things:

• Buy some stock from a wholesaler.

• Sell some of your stock to a retailer.

• Move one unit along any road.

Thus, when you move, you can only move to an adjacent square, and then only if it is connected to your current square. Each move costs one unit of fuel, so after twenty moves you will be out of the game unless you remember to refuel.

To buy or sell, you must be on the same square as the person you're dealing with. You can only buy (fuel or tradeable goods) if you have enough money; otherwise you will be thrown out of the game for fraud. If you try to buy more fuel than you have room for in your tank, you will certainly spend the money; your tank will be filled, and any excess will just be spilt. However, if you try to buy more of a good than you have room for, the transaction will be forbidden for safety reasons, and you will be kicked out as an unsafe driver.

You can only sell a good if you have enough of it in your lorry. If you haven't, you will again be thrown out for fraud.

Each trader begins with a full tank. Initial cash is £ 5000, and initial loads are all zero. The total load is displayed in cubic feet, and the stock of each good is displayed both in units and in cubic feet, using `unit_volume` to provide conversion factors.

```trader is on square 67.
Fuel 20 in tank size 20.
Cash 5000.
Total load 0 cu ft in lorry size 1000.
Stock of televisions = 0 units (0 cu ft).
Stock of peaches = 0 units (0 cu ft).
Stock of glasses = 0 units (0 cu ft).
Stock of diamonds = 0 units (0.0 cu ft).
Stock of coal = 0 units (0 cu ft).
```

As the game continues, you will see a summary of the actions the trader takes at each turn, and the result on his fuel, cash, position, and stocks. Wait until the trader makes his way to The Hub, and has gone through a few cycles of buying and selling; when you have seen enough, you can interrupt the game.

Now, how can you write a trader of your own? Well, when you call `run`, it starts by asserting some clauses which describe the trader's state. These are:

• `at(T,Square)`: T is the name of the trader, and Square is the number of the square he is on. This always starts off as the second argument to `run`.

• `carries(T,Good,Qty)`: T is the name of the trader, Good is the name of one of his goods (`coal`, `diamonds`, `glasses`, `peaches` or `televisions`) and Qty is the number of units he has. This predicate was originally called `load`, but I have changed it to avoid clashes with the `load` command.

• `cash(T,Cash)`: T is the name of the trader, and Cash is the amount of cash he has.

• `fuel(T,Qty)`: T is the name of the trader, and Qty is the number of units of fuel he has.

• `max_load(T,Vol)`: T is the name of the trader, and Vol is the maximum volume his lorry can hold. In this version of Traveller, it is 1000 cubic feet.

• `tank_size(T,Qty)`: T is the name of the trader, and Qty is the maximum number of units he can get into his tank. In this version of Traveller, it's 20.

• `total_load(T,Vol)`: T is the name of the trader, and Vol is the total load in cubic feet.

The name T of the trader is the first argument to `run`. So for `run(trader,67)`, you get clauses

```    at(trader,67).
Note that, apart from `carries`, there is only one clause of each kind.
Having done this, `run` then tries to find out what the trader's first action is to be. Will he move to a new square, buy something, or sell something? It does so by calling (i.e. by asking itself the question) `act( T, Action, Arg1, Arg2 )` where T is the name of the trader. The file TRADER contained clauses I have written for `act`: to play Traveller, you must write some for your trader.
When `act` is called, it must set Action to one of `move`, `buy`, or `sell`. If Action is `move`, Arg1 must become the next square to move to, and Arg2 must become the atom `dummy`. If Action is `buy` or `sell`, Arg1 must become the name of a good, and Arg2 must become the quantity in units that is to be bought or sold. Finally, if Action is `buy`, Arg1 can also be `fuel`. In this case, the trader is buying fuel, not a good for resale.