/* LEARNING BY PLAYING WITH THE RIVER MICROWORLD An 'unscripted' tutorial Presenter: Aaron Sloman School of Computer Science, University of Birmingham http://www.cs.bham.ac.uk/research/projects/poplog/cas-ai/video-tutorials.html Recorded: 9 Jan 2012 Based on a program originally by Max Clowes, University of Sussex, around 1978 Available as part of Poplog and here: http://www.cs.bham.ac.uk/research/projects/poplog/teach/riverworld A Youtube video tutorial based on this will be available. A previous tutorial explained how a teacher could set up a microworld based on the 'River Crossing' puzzle: the RiverWorld, as described in TEACH RIVERWORLD This tutorial, also for teachers, shows how a learner can make use of the microworld after it has been created, using a Pop11 library that is included in in Poplog, and a teach file to go with it: TEACH RIVER This microworld deals with a world in which processes occur, like a boat crossing a river. We shall not simulate the detailed processes of crossing the river, which would be required for a system to demonstrate fine-grained "online" control of actions. Some programming teaching environments (e.g. the MIT Scratch system) emphasise such control of motion. So this is not about how to generate impressive or entertaining interactive displays. Instead we focus on how an intelligent individual works out a plan of action without considering all the details -- only the major transitions. Learning how to divide up continuous changes into sequences of discrete states is a major aspect of human intelligence, and probably also intelligence in some animals. This is a small step towards 'Thinky Programming', defined in more detail in this web site: http://www.cs.bham.ac.uk/research/projects/poplog/examples */ /* CONTENTS -- Introduction: Setting up a microworld for teaching -- Procedure start is used to initialise everything. -- Procedure view() shows the current state diagrammatically -- Test start(); -- Action procedures which check preconditions then change the world -- putin(item) -- LIB rIVER removes the need to type word quotes -- Procedure takeout(item) -- Procedure getin() -- Procedures getout(), crossriver(), takeout(item) -- Test a sequence of commands -- Things a student can go on to learn -- Defining a procedure that solves the puzzle -- The long solution can be shortened by defining procedures -- Defining new sub-procedures -- Testing cross -- Exercise: shorten the solve procedure -- Moving up to a higher level of structure -- Procedure opposite(side) -- Further Reading -- Introduction: Setting up a microworld for teaching ----------------- This tutorial (with video on Youtube) explains how to use the Pop11 language including its pattern matcher and simple database mechanism to create a micro-world for teaching some aspect of general programming, or AI (artificial intelligence) programming. An earlier tutorial TEACH RIVER http://www.cs.bham.ac.uk/research/projects/poplog/teach/river with Youtube video showing how it works: http://youtu.be/xfGv9xfqDxU made use of a microworld of the sort described here. The original program providing the microworld (available as LIB RIVER in Pop11) is several decades old. The version presented below makes less use of general programming constructs (e.g. using global variables) and more use of a database in which knowledge about the world, including both its general features and its current state, can be stored. It is useful to distinguish: o Internal semantics of the program. Which entities and operations of the computer, or the virtual machine running on the computer are referred to by structures and commands in the program code. E.g. the program can refer to lists, strings, number representations, variables, their values, procedures, and operations on those items. o External semantics of the program. Which entities, states, events and processes in the world being modelled (e.g. a boat, a man, a river, and processes of getting in and out of the boat, or moving the boat from one side of the river to the other) are referred to by structures and commands in the program code. The external semantics can also include causal relationships (what causes what, and what constraints what) in the world represented. The external semantics may or may not include some portion of the actual world. In this tutorial it is a mythical rather simple world, which is an abstraction from things that can exist in our actual world, containing real rivers, boats, animals, eatings, river crossings, and so on. The external causal relationships will be represented by constraints on what the program allows, or what side effects running portions of the program can have. In this tutorial, intended for teachers learning to set up a microworld for students to use, we employ the pop11 database and associated mechanisms, in addition to conventional programming constructs, e.g. procedures, variables, conditionals, loops, etc. The pop11 database is one of a number of related libraries of varying sophistication available in pop11 based in the Pop11 pattern matcher, introduced in TEACH MATCHES and summarised in HELP MATCHES with an enhanced version described in HELP DOESMATCH The database is explained in these files TEACH DATABASE HELP DATABASE TEACH FOREACH HELP FOREVERY HELP WHICH and other files referenced in those. TEACH Previous video tutorials in this collection introduced the use of the matcher. This one shows how to use the matcher with Pop11's most elementary database mechanisms to design a microworld. The microworld is the puzzle world of man, fox, chicken, grain, boat and river, illustrated in TEACH RIVER. */ ;;; We use the pop11 river package, described in TEACH RIVER uses river /* -- Procedure start is used to initialise everything. ------------------ */ start(); database ==> /* -- Procedure view() shows the current state diagrammatically ---------- view(); As the databse representing the state of the world changes, the view() procedure can inspect the current contents of the database, and show states of the world in one-dimensional diagrams like these: ** [man grain chicken fox ---\ \_ _/ _________________ /---] ** [man grain chicken fox ---\ _________________ \_ _/ /---] ** [man grain chicken fox ---\ \_ _/ _________________ /---] ** [man grain chicken ---\ \_ fox _/ _________________ /---] ** [grain chicken ---\ \_ man fox _/ _________________ /---] ** [grain chicken ---\ _________________ \_ man fox _/ /---] ** [grain chicken ---\ _________________ \_ fox _/ /--- man] ** [grain chicken ---\ _________________ \_ _/ /--- fox man] ** [chicken ---\ _________________ \_ _/ /--- fox man] NB: the laws of our microworld should not allow some of these states to exist! */ /* -- Test start(); ------------------------------------------------------ start(); ;;; Print out the database: database ==> ;;; That should print out ** [[boat isat left] [chicken isat left] [fox isat left] [grain isat left] [man isat left]] ;;; display the world using text-graphics (for now) view(); ;;; That should print out: ** [man grain chicken fox ---\ \_ _/ _________________ /---] remove([boat isat left]); add([boat isat right]); ;;; the database has now changed -- boat now at right: database ==> ;;; should print out ** [[boat isat right] [chicken isat left] [fox isat left] [grain isat left] [man isat left]] ;;; How does that look graphically view(); ;;; the boat is now shown at the right (moved by magic): ** [man grain chicken fox ---\ _________________ \_ _/ /---] ;;; We can create a simulated world in which things happen that would be ;;; impossible in the real world, or in the world defined by the puzzle. */ /* /* -- Action procedures which check preconditions then change the world -- -- putin(item) -------------------------------------------------------- */ /* ;;; test putin start(); view(); putin("elephant"); putin("man"); getin(); view(); database ==> /* -- LIB rIVER removes the need to type word quotes --------------------- The river library defines several global variables to refer to the words "man" "fox" etc., so that we don't need to type the word quotes in al our commands. This is how it is done: vars man, fox, chicken, grain, boat; "man" -> man; "fox" -> fox; "chicken" -> chicken; "grain"-> grain; "boat" -> boat; The TEACH RIVER file, intended for beginners, hides this detail, and just tells learners to type: putin(fox); not putin("fox"); */ /* -- Procedure takeout(item) -------------------------------------------- */ start(); view(); putin(fox); view(); database ==> takeout(fox); takeout(chicken); takeout(grain); takeout(man); takeout(boat); */ /* -- Procedure getin() -------------------------------------------------- */ start(); view(); getin(); view(); Notice the disaster! */ /* -- Procedures getout(), crossriver(), takeout(item) ------------------- */ start(); view(); putin(chicken); getin(); view(); crossriver(); view(); getout(); view(); ;;; we can now use 'takeout' takeout(chicken); view(); database ==> /* -- Test a sequence of commands ---------------------------------------- start(); getin(); checkeat(); crossriver(); getout(); */ /* -- Things a student can go on to learn -------------------------------- */ A full solution is quite long and repetitive start(); putin(chicken); getin(); crossriver(); getout(); takeout(chicken); getin(); crossriver(); getout(); putin(fox); getin(); crossriver(); getout(); takeout(fox); putin(chicken); getin(); crossriver(); getout(); takeout(chicken); putin(grain); getin(); crossriver(); getout(); takeout(grain); getin(); crossriver(); getout(); putin(chicken); getin(); crossriver(); getout(); takeout(chicken); -- Defining a procedure that solves the puzzle ------------------------ You can now combine all your instructions into one big procedure called "solve" by putting the following at the beginning of the set of commands in your file solve.p define solve(); and putting the following at the end enddefine; define solve(); start(); putin(chicken); getin(); crossriver(); getout(); takeout(chicken); getin(); crossriver(); getout(); putin(fox); getin(); crossriver(); getout(); takeout(fox); putin(chicken); getin(); crossriver(); getout(); takeout(chicken); putin(grain); getin(); crossriver(); getout(); takeout(grain); getin(); crossriver(); getout(); putin(chicken); getin(); crossriver(); getout(); takeout(chicken); enddefine; ;;; then instead of running all those instructions, we can run ;;; one instruction solve(); /* -- The long solution can be shortened by defining procedures The long solution is very clumsy, and has much repetition. We can shorten it by defining sub-procedures. -- Defining new sub-procedures ---------------------------------------- The sequence of successful commands in the definition of "solve" is long and repetitive. You can abbreviate it by replacing some of the repeated sub-sequences by commands to obey your own procedures. ;;; One of the repeated sub-sequences consists of the sequence of actions getin(); crossriver(); getout(); This sequence can be replaced by a single command cross(); PROVIDED that you first define a procedure called "cross". E.g. */ define cross(); getin(); crossriver(); getout(); enddefine; /* -- Testing cross ------------------------------------------------------ */ start(); putin(chicken); cross(); database ==> view(); cross(); /* -- Exercise: shorten the solve procedure ------------------------------ */ define shortsolve(); start(); putin(chicken); ;;; previous instructions: ;;; getin(); ;;; crossriver(); ;;; getout(); cross(); takeout(chicken); cross(); putin(fox); getin(); crossriver(); getout(); takeout(fox); putin(chicken); getin(); crossriver(); getout(); takeout(chicken); putin(grain); getin(); crossriver(); getout(); takeout(grain); getin(); crossriver(); getout(); putin(chicken); getin(); crossriver(); getout(); takeout(chicken); enddefine; ;;; test shortsolve shortsolve(); /* Of course -- Moving up to a higher level of structure --------------------------- There is still another repetition of more or less the same form - putin(thing); cross(); takeout(thing); Define a new procedure called move(thing); */ define move (thing); putin(thing); cross(); takeout(thing); enddefine; /* This a procedure with an "input variable", namely "thing". It is called a variable because it can be given different values, e.g. fox, river, or grain, when the procedure MOVE is run. Sometimes the name "argument" is used instead of "input variable". NOTE: Pop11 also allows procedures to have output variables, if the create something to be printed out or used by other procedures. See TEACH DEFINE Now test the procedure: */ start(); move(fox); ;;; or move(chicken); ;;; or move(grain); /* TEACH RIVER introduces some relatively small fragments. There is lots more to be said about how we can talk about procedures, algorithms, functions, complete programs, and large systems assembled from many programs. But not now. */ /* -- Procedure opposite(side) ------------------------------------------- There are many more details in the library that have not been shown here. A learner might invent them spontaneously to save repetition or might be given a hint. For example the procedure for crossing the river has to go from left to right, or from right to left. By defining an auxilliary procedure (or subroutine) called opposite, it can complex a clumsy conditional command into a single elegant command that covers both cases. */ opposite("left") => opposite("right") => ;;; This can be used in a sequence of commands like: lookup([boat isat ?place]); opposite(place) -> newplace; remove([boat isat ^place]); add([boat isat ^newplace]); ;;; That is nice because the same code will move things from left to right ;;; and from right to left. ;;; There's lots more that can be learnt in this microworld. /* -- Further Reading ---------------------------------------------------- TEACH RIVER TEACH DATABASE TEACH LISTS TEACH FOREACH TEACH RIVERCHAT TEACH PRBRIVER Shows how a program using poprulebase can search for a plan to cross the river, using either depth first or breadth first searching. And there's lots, lots more. */