/* 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.