CSC 324 Assignment 4

For this assignment, you will construct and manipulate a simple blocks world. The blocks world is a simple dynamic example that is commonly used to study planning problems.

Part I

Defining the world

First we define the properties of the world. The initial state of the world should be as illustrated below. The table is represented by the horizontal lines, and the blocks by their names. There are two piles of blocks: block a itself, and a pile of blocks b, c and d. Note that one location on the table is clear.
		d
		c
	a	b	
	=================
There are two types of actions that can be taken. The state of the world is modified by taking the action. Only a single block can be moved at any time. If there are blocks on top of the the block we want to move, those blocks must be moved off the block before the block can be moved.

We will represent the state of the world with several relations.

We will represent an action (which may change the state of the world) with the following relation. Define these relations (possibly by defining helper relations or additional objects) subject to the world restrictions mentioned above. Since the place predicate can change the state of the world, we will need to assert and retract facts about the world. For example, if we move block a from the table to be on top of block b, we might have to retract(on(a,table)) and assert(on(a,b)), among other facts.

It might be useful to declare some of your relations as being dynamic, using something similar to the following clause:

    :- dynamic on/2.

Handling errors

The actions that can be taken at any time are resticted. For example, if block d is on top of block c, it would be incorrect to ask place(c,d) or place(c,table). Prolog should detect that your command cannot be satisfied and report "No", but that doesn't tell the user why it is wrong.

Write additional rules to detect errors and print a suitable error message. These rules should use a cut (!) to abandon any useless alternatives that may have been constructed, and always fail.

You may wish to use the built-ins write to print a string (always succeeds, no branching), nl to print a newline, and fail which always fails (which, if all alternatives are cut, will guarantee the computation fails). For example, the following might be the end of an error rule:

    write('No space available on the table!'), nl, !, fail.

Hint: Simplicity is key for this part. You will be extending your Part I code in A5, so make sure you test it thoroughly.

Part II

Initial state of the world

In this part we extend the basic world to allow some user customization. To allow the user to set up an initial state of the world, provide an initialize_world relation that takes a list of piles of blocks, each pile being a list of blocks from bottom to top, and initializes the world to that state.

For example, initialize_world([[a],[b,c,d],[]]). results in a world where block a is on the table with nothing on top of it, a pile of blocks with d at the top, c in the middle, and b at the bottom on the table, and one free spot on the table.

Notice that initialize_world gives us a great amount of flexibility: we can give our own names to the blocks, have various numbers of blocks, and specify the size of the table (how many different blocks can be directly on the table at once). This flexibility may require generalization of some of your rules.

Displaying the world

Write a relation print_world that uses write to print a representation of the world.

For example, print_world on the initial state mentioned above could look like this:


	d
	c
a	b	
=================

Hint: For this part, it might be useful to store the current state of the world as a list, in the format used by initialize_world, and derive on and print_world (and any other relations) from this state.