/*  DRAW.PL  */
 
 
:- module draw.
 
 
:- public draw_bricks/0.
 
 
/*
SPECIFICATION
-------------
 
This module is not part of the Tutor as distributed, but is something I
once tried in some experiments. The idea was to have the student enter
certain facts, and then have the Tutor draw a picture of them. This
would help train the student to use logic properly, because missing or
incorrect facts would be apparent from the picture.
 
Given our limited - VT100 - graphics, this isn't something I've carried
further. However, you may be interested to see what I have done so far.
 
 
This module depends on BLOCKS.PL, which defines predicates for building
simple pictures from rectangular arrays of characters. The idea is that
the teacher give the student a picture depicting coloured bricks of
various sizes on a table. The student then has to express this picture
in Logic. When finished, he can get the Tutor to call the predicate
draw_bricks, which produces a picture from these facts.
 
The predicates to be used by the student are
    on( A, B )      - brick A is on brick B.
    colour( A, C )  - brick A has colour C.
    size( A, S )    - brick A has size S.
In this, the bricks can be named by any atom. Colours can also be any
atom. A size is one of { tiny, small, medium, big }. draw_bricks will
depict a brick as a rectangle made up of the first letter of its colour.
Its size will depend on S. Each brick will be pointed to by an arrow
giving its name. The whole set of bricks will be drawn as a number of
piles on a table-top.
 
 
PUBLIC draw_bricks:
 
Draw the bricks as described above.
 
 
Example:
 
Given these facts,
    on(a,b).
    on(b,c).
    on(c,d).
    on(e,f).
    colour(a,green).
    colour(b,green).
    colour(c,green).
    colour(d,green).
    colour(e,red).
    colour(f,red).
    size(a,tiny).
    size(b,big).
    size(c,medium).
    size(d,small).
    size(e,big).
    size(f,small).
then draw_bricks produces this picture,
     g <-- a
 
    gggg <-- b
    gggg
    gggg                                rrrr <-- e
    ggg <-- c                           rrrr
    ggg                                 rrrr
     gg <-- d                            rr <-- f
 
    +-------------------------------------------------------------+
    !                  THIS IS THE TABLE                          !
    +-------------------------------------------------------------+
 
 
Terminology note: "blocks" = picture-representations as defined by
BLOCKS.PL. "bricks" = rectangular coloured things to be drawn. This is
more likely to worry Americans than English.
*/
 
 
/*
IMPLEMENTATION
--------------
 
draw_bricks works as follows:
 
1) Get the names of all the bricks, and all those bricks not on other
bricks. The latter determines how many separate piles to draw. Calculate
the spacing between piles so they are equally spaced.
 
2) Start with a block depicting the table-top. For each pile P,
construct a block representing that, and overlay it with the picture so
far.
 
3) Draw the result.
 
 
There is one non-portability. For making lists of bricks and bottoms, I
use fast_setof. This is a Poplog predicate which is like setof, but
where you don't have to existentially quantify unbound variables. I
could have used findall (which I supply), but I needed duplicates
removed.
*/
 
 
:- needs
    block_height/2,
    colour/2,
    draw_block/1,
    fast_setof/3,
    is_block/2,
    length/2,
    member/2,
    on/2,
    overlay/7,
    size/2.              
 
 
draw_bricks :-
    objects_and_bases( _, Bottoms ),
    piles_from( Bottoms, Piles ),
    length( Piles, NoOfPiles ),
    Spacing is 72 div NoOfPiles,
    Table is_block
[ '+-------------------------------------------------------------+',
  '!                  THIS IS THE TABLE                          !',
  '+-------------------------------------------------------------+'
],
    pile_piles_on_table( 1, Piles, Table, Spacing, Pic ),
    draw_block( Pic ).
 
 
/*  objects_and_bases( Objects-, Bottoms- ):
        Objects is a list of all the bricks mentioned by the student -
        those appearing in either argument of an 'on'. Bottoms is a list
        of all the bricks which are not standing on other bricks (i.e.
        which are directly on the table).
*/
objects_and_bases( Objects, Bottoms ) :-
    fast_setof( X, (on(X,_);on(_,X)), Objects ),
    fast_setof( X, (member(X,Objects),not(on(X,_))), Bottoms ).
    /*  Non-portable. The first call gives all the X's in arguments of
        'on'; the second gives all those not on something else.
    */
 
 
/*  pile_piles_on_table( N+, Piles+, Table+, Spacing+, Picture- ):
        Table is a block depicting the table-top and the first to N-1
        piles of bricks on it.
        Piles is a list, being the Nth to final pile of
        Picture is a block representing the table with all its
        piles of bricks, where the piles are to be spaced Spacing
        characters apart when drawn.
*/
pile_piles_on_table( _, [], Table, _, Table ) :-
    !.
 
pile_piles_on_table( PileNo, [Pile|OtherPiles], Table, Spacing, Pic ) :-
    Distance is Spacing * (PileNo - 1),
    block_height( Pile, PileBase ),
    block_height( Table, TableBase ),
    TableTop is TableBase - 3,
    overlay( Table, Distance,TableTop, Pile, 0,PileBase, NewTable ),
    PileNoAdd1 is PileNo + 1,
    pile_piles_on_table( PileNoAdd1, OtherPiles, NewTable, Spacing, Pic ).
 
 
/*  piles_from( Bottoms+, Piles- ):
        Bottoms is a list of bricks. Piles is a corresponding list
        of blocks: the Nth element of Piles depicts the pile
        of bricks starting at the Nth element of Bottoms.
*/
piles_from( [], [] ) :- !.
 
piles_from( [Bottom|OtherBases], [Pile|OtherPiles] ) :-
    pile_from( Bottom, Pile ),
    piles_from( OtherBases, OtherPiles ).
 
 
/*  pile_from( Bottom+, Pile- ):
        Pile is a block which depicts the pile of bricks whose bottom-
        most brick is Bottom.
*/
pile_from( Bottom, Pile ) :-
    brick_to_block( Bottom, BottomPic ),
    (
        on( Next, Bottom )
    ->
        pile_from( Next, RestOfPic ),
        Pile is_block RestOfPic # BottomPic
    ;
        Pile = BottomPic
    ).
 
 
/*  brick_to_block( B+, Bl- ):
        Bl is a block which depicts brick B.
*/
brick_to_block( Brick, Pic ) :-
    ( colour( Brick, Colour ) ; Colour = '?' ),
    name( Colour, [Ci|_] ),
    name( C, [Ci] ),
    ( size( Brick, Size ) ; Size = unknown ),
    make_brick( C, Size, BrickPic ),
    Label is_block ( ' <-- ' + Brick ) # '',
    (
        Colour = '?'
    ->
        Z is_block 'Colour?'
    ;
        Z is_block ''
    ),
    (
        Size = unknown
    ->
        Z_ is_block 'Size?'
    ;
        Z_ is_block ''
    ),
    Z__ is_block Z + Z_,
    overlay( Label, 1,0, Z__, 0,0, P ),
    Pic is_block BrickPic + P.
 
 
/*  make_brick( C+, Size+, Bl- ):
        C is a single-character atom to be used when drawing a brick.
        Size is the brick's size - 'unknown' if not given by the
        student.
        Bl is a block representing this brick. Its size depends on Size,
        and it is made up entirely of C's.
*/
make_brick( I, tiny, I ) :- !.
 
make_brick( I, small, [[I,I]] ) :- !.
 
make_brick( I, medium, [ [I,I,I],[I,I,I] ] ) :- !.
 
make_brick( I, big, [ [I,I,I,I], [I,I,I,I], [I,I,I,I] ] ) :- !.
 
make_brick( I, unknown, I ) :- !.
 
make_brick( I, _, I ) :- !.
 
 
:- endmodule.
