The basic predicates are
sells, plus three ones derived from them:
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.
squaredefines where squares lie and what they contain,
joinsdefines 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
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
joinsfacts 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^2means 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.
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.
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
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:
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:
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
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
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
carries(T,Good,Qty): T is the name of the trader, Good is the name of one of his goods (
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
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). cash(trader,5000). fuel(trader,20). carries(trader,coal,0). carries(trader,diamonds,0). carries(trader,glasses,0). carries(trader,peaches,0). carries(trader,televisions,0). max_load(trader,1000). tank_size(trader,20). total_load(trader,0).Note that, apart from
carries, there is only one clause of each kind.
Having done this,
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
have written for
act: to play Traveller, you must write some for
act is called,
it must set Action to one of
sell. If Action is
move, Arg1 must become the next square to move to, and Arg2 must
become the atom
dummy. If Action is
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.
1st November 2008.