/* BOARDTEX.PL */
/*
This is the board-to-Latex converter. It outputs Latex picture
directives which draw an approximate copy of the board. I have not
however made it intelligent enough to place names so as to avoid
overlaps. To run it, call 'run'.
*/
/*
Load the code. See MASTER.PL for purpose of the directives below.
*/
:- library(macro).
:- killmac("prolog").
:- killmac("teach").
:- killmac("ref").
:- killmac("help").
:- killmac("lib").
:- quietspy(on).
sp :- (spy), (nospy prolog_expand_term),
(nospy prolog_user_goal),
(nospy prolog_expand_goal).
:- reconsult( '[popx.book.disc.source]lib.pl' ).
:- lib( chars ).
:- lib( control ).
:- lib( findall ).
:- lib( useful ).
:- lib( lists ).
:- lib( output ).
:- lib( output_english ).
/*
IMPLEMENTATION
--------------
The program runs through the following stages:
1) Place the squares:
For all square(N,X,Y),
place square around X, Y.
2) Place the roads:
For all joins(N1,N2),
joins X1,Y1 to X2,Y2.
3) Place the names and numbers:
For all square(N,X,Y),
write N a little above it;
write name a little under it.
4) Write about buyers:
For all square(N,X,Y) which contain a buyer,
write BUYS Good Price.
5) + 6) Write about sellers and fuel-sellers:
As 4).
*/
/*
Main predicates.
----------------
'generate' emits some Latex setting-up directives, then calls the
individual stages, then emits directives to end the document. I have
chosen the coordinates and lengths so that the picture fits on an A4
page.
*/
:- reconsult('board.pl').
run :-
tell( 'board.tex' ),
generate,
nl,
told.
generate :-
output( '\\documentstyle{article}'~ ),
output( '\\begin{document}'~ ),
output( '\\topmargin 0 pt'~ ),
output( '\\textwidth 11.5 in'~ ),
output( '\\oddsidemargin -0.5 in'~),
output( '\\evensidemargin -0.5 in'~),
output( '\\setlength{\\unitlength}{0.60in}'~ ),
output( '\\begin{flushleft}'~ ),
output( '\\begin{picture}(17,16)(0,0)'~ ),
place_squares,
place_roads,
place_names,
place_buyers,
place_sellers,
place_fuel_sellers,
output( '\\end{picture}'~ ),
output( '\\end{flushleft}'~ ),
output( '\\end{document}'~ ).
/*
The stages themselves.
----------------------
draw_straight and draw_arc are dummies: this version doesn't actually
draw the roads. This is because of the difficulty, in Latex, of making
sure they don't extend inside the squares. My real-time board generator
for ESL Prolog-2 with Windows draws the board in a graphics window.
There is no difficulty with roads here, because one can draw the roads
first, and then draw the squares in fill mode, flooding over anything
inside them.
As mentioned above, no intelligence is used in placing names. On my
Prolog-2 generator, I have a predicate
name_start( N+, X-, Y- )
which, for square N, returns in X and Y, the coordinates of where the
name should start. This works by looking to see where the neighbouring
squares are, and choosing the clearest side - i.e. top for squares with
no neighbour above, right for those with none to the right, and so on.
This still does not take into account the effect of very long names on
neigbouring squares: i.e. it avoids the squares themselves, but not
their names. In practice, the font can just be made small enough that
this isn't a problem (though I did have to shorten two names, even so).
*/
place_squares :-
forall(
square(N,X,Y)
,
place_square_and_number(N,X,Y)
).
place_square_and_number(N,X,Y) :-
place_at( X-boxwidth/2, Y-boxheight/2, framebox_(boxwidth,boxheight,N) ).
place_roads :-
forall(
joins(N1,N2)
,
(
square(N1,X1,Y1),
square(N2,X2,Y2),
place_road_between(X1,Y1,X2,Y2)
)
).
place_road_between(X,Y1,X,Y2) :-
!,
draw_straight( X, Y1, X, Y2 ).
place_road_between(X1,Y,X2,Y) :-
!,
draw_straight( X1, Y, X2, Y ).
place_road_between(X1,Y1,X2,Y2) :-
draw_arc( X1, Y1, X2, Y2 ).
draw_straight( X, Y1, X, Y2 ).
draw_straight( X1, Y, X2, Y ).
draw_arc( X1, Y1, X2, Y2 ).
place_names :-
forall(
square(N,X,Y)
,
place_name_of_square(N,X,Y)
).
place_name_of_square(N,X,Y) :-
building( N, Name ),
!,
place_at( X-boxwidth/2, Y-boxheight, tiny_(Name) ).
place_name_of_square(_,_,_).
place_buyers :-
forall(
square(N,X,Y)
,
write_buyer_details(N,X,Y)
).
write_buyer_details(N,X,Y) :-
building( N, Name ),
buys( Name, Good, Price ),
!,
write_info( X, Y, 'BUY', good_(Good)...Price ).
write_buyer_details(_,_,_).
place_sellers :-
forall(
square(N,X,Y)
,
write_seller_details(N,X,Y)
).
write_seller_details(N,X,Y) :-
building( N, Name ),
sells( Name, Good, Price ),
!,
write_info( X, Y, 'SELL', good_(Good)...Price ).
write_seller_details(_,_,_).
place_fuel_sellers :-
forall(
square(N,X,Y)
,
write_fuel_seller_details(N,X,Y)
).
write_fuel_seller_details(N,X,Y) :-
building( N, Name ),
sells_fuel( Name, Price ),
!,
write_info( X, Y, 'FUEL', Price ).
write_fuel_seller_details(_,_,_).
/*
Utilities.
----------
place_at(X,Y,Thing) generates a Latex command to place Thing at X,Y. All
three arguments can be arithmetic expressions, possibly containing
constants, e.g.
place_at( X-boxwidth/2, Y-2*boxheight, tiny_(L2) )
These constants must be defined by a clause such as
define( boxwidth, 0.3 ).
place_at calls eval(Expr,N) which evaluates the expressions as far as it
can, looking up constants in 'define'.
Thing may be a structure such as tiny_(L2). These are written specially
by additional clauses for 'output'.
*/
place_at( X, Y, What ) :-
eval( X, X_ ),
eval( Y, Y_ ),
eval( What, What_ ),
output( '\\put('<>X_<>','<>Y_<>'){'<>What_<>'}'~ ).
/* write_info( X+, Y+, L1+, L2+ ):
Write information about the square at X,Y. This comes in two
lines (L1 and L2) in "tiny" font.
*/
write_info( X, Y, L1, L2 ) :-
place_at( X-boxwidth/2, Y-1.5*boxheight, tiny_(L1) ),
place_at( X-boxwidth/2, Y-2*boxheight, tiny_(L2) ).
define( boxwidth, 0.3 ).
define( boxheight, 0.3 ).
/*
Output.
-------
In place_buyers and place_sellers, we wrapped the names of goods in
the functor good_. This is caught by one of the clauses below, which
replaces "televisions" by "t'visions". Otherwise, it won't fit.
*/
?- add_user_output( out ).
out( good_(televisions) ) :-
output( 't\'visions' ).
out( good_(G) ) :-
output( G ).
out( framebox_(X,Y,F) ) :-
!,
output( '\\framebox('<>X<>','<>Y<>'){'<>F<>'}' ).
out( small_(T) ) :-
!,
output( '{\\small '<>T<>'}' ).
out( footnotesize_(T) ) :-
!,
output( '{\\footnotesize '<>T<>'}' ).
out( scriptsize_(T) ) :-
!,
output( '{\\scriptsize '<>T<>'}' ).
out( tiny_(T) ) :-
!,
output( '{\\tiny '<>T<>'}' ).
/*
The expression evaluator.
-------------------------
It evaluates structures by evaluating their arguments. This allows
us to use third arguments to place_at of the form
framebox_(boxwidth,boxheight,N)
.
*/
eval( A, V ) :-
define( A, V ),
!.
eval( X, X ) :-
( atomic(X) ; var(X) ),
!.
eval( A+B, C ) :-
!,
eval( A, A_ ),
eval( B, B_ ),
C is A_ + B_.
eval( A-B, C ) :-
eval( A, A_ ),
eval( B, B_ ),
C is A_ - B_.
eval( A*B, C ) :-
!,
eval( A, A_ ),
eval( B, B_ ),
C is A_ * B_.
eval( A/B, C ) :-
!,
eval( A, A_ ),
eval( B, B_ ),
C is A_ / B_.
eval( S, S_ ) :-
S =.. [ P | Args ],
eval_list( Args, Args_ ),
S_ =.. [ P | Args_ ].
eval_list( [], [] ) :- !.
eval_list( [H|T], [H_|T_] ) :-
eval( H, H_ ),
eval_list( T, T_ ).