// $Id: catflap.lsd,v 1.5 1997/12/16 18:37:37 ashley Exp $
// !@@! denotes bits that might need some more work
// !@@! This flap has no 4 way switch at the moment to make things simpler:
// the flap is there simply to stop heat getting out of the house and
// confuse the cat.
agent flap() {
// Observables that the agent owns
state
// Value is in degrees.
// 0: the flap is closed.
// +ve: the flap is protruding outside
// -ve: the flap is protruding inside
// Should be -90 <= flapAngle <= 90 at all times
(real) flapAngle = 0;
flapGap;
// State observables that are not subject to change
const
// Value is in centimetres (!@@! Sorry - better units than
// metres for this case!)
(real) flapLength = 30;
// Value in degrees. The flap can move at a rate of one of
// these units per time step if it chooses, or not at all.
// Therefore the flap position is a discrete variable.
(real) flapMoveDelta = 1;
// Observables to which the agent responds
oracle
catPushOut;
catPushIn;
catInFlap;
// Observables that may be under the agents control
handle
flapAngle;
// Indivisibly coupled stimulus - response relations
derivate
// The vertical gap between the floor and the end of the
// flap. If this is >= catHeight, then the cat can pass
// through unimpeded.
// Value is in centimetres.
// Need to take the absolute value to cope with -ve angles.
(real) flapGap =
flapLength - (flapLength * cos( abs(flapAngle) ));
// The actions that this agent may perform
protocol
// !@@! Locking of the flap is not currently implemented
catPushOut -> flapAngle = |flapAngle| + flapMoveDelta;
catPushIn -> flapAngle = |flapAngle| - flapMoveDelta;
// If the cat is not in the flap, then the flap will fall
// back to its original position.
(flapAngle < 0) ^ !catInFlap
-> flapAngle = |flapAngle| + flapMoveDelta;
(flapAngle > 0) ^ !catInFlap
-> flapAngle = |flapAngle| - flapMoveDelta;
// flapAngle is not allowed to exceed +/-90 degrees
(flapAngle > 90) -> flapAngle = 90
(flapAngle < -90) -> flapAngle = -90
}
// I'm modelling a 2D rectangular cat. It moves in a 1.5D world (the cat
// moves in 1D, the flap in another dimension - it rotates).
agent cat() {
// Observables that the agent owns
state
#define STAY_PUT 0
#define GO_OUT 1
#define GO_IN 2
// !@@! This could depend on whether the cat is hungry,
// or cold, or bored...
(int) catIntention = (STAY_PUT | GO_OUT | GO_IN);
// The position of the mid point of the cat, relative
// to the flap. Value is in centimetres.
// 0: the cat is halfway through the flap
// +ve: the cat is outside
// -ve: the cat is inside
(real) catMidPos = 100;
// The cat can decide to push the flap in a certain direction
// if the flap is obstructing it.
(bool) catPushOut = false;
(bool) catPushIn = false;
// Whether the cat is currently positioned in the flap or not.
(bool) catInFlap = false;
// State observables that are not subject to change
const
// Values are in centimetres.
// !@@! These might change if the cat can contort itself.
// !@@! What happens if the cat is bigger than the flap?
(real) catHeight = 20;
(real) catLength = 50;
// Value in centimetres - the cat can move at a rate of one
// of these units per time step if it chooses.
(real) catMoveDelta = 1;
// Observables to which the agent responds
oracle
flapLength;
flapAngle;
// Observables that may be under the agents control
handle
catIntention;
catMidPos;
catPushOut;
catPushIn;
// !@@! This one is a derivate - is it therefore a handle?
catInFlap;
// Indivisibly coupled stimulus - response relations
derivate
// !@@! There are a few ways we could model the flap/cat
// interaction.
//
// 1) The flap simply rests on top of the cat - it does
// not hinder or slow down the cat in any way.
// 2) If cat has moved so that it is protruding through the
// flap, then the flap moves.
// 3) When the cat gets close to the flap, it pushes on the
// flap until it is fully open. Then the cat can move
// through the flap - pushing and moving are separated.
// 4) If the cat is obstructed by the flap, it pushes on the
// flap to move it up. It then moves forward as much as it
// can. If we are modelling this in discrete time intervals
// (although maybe LSD is not supposed to assume this?) then
// we can calculate where the cat will be in the next time
// frame. If the flap is in the space between where the cat
// currently is and where it will be, then the cat is
// obstructed and must push the flap. If the flap is not in
// this space, then the cat can move. This is the approach
// taken by Simon Yung.
//
// All of these are unrealistic to some extent - we should
// really model the force that the cat exterts on the flap
// and also visa versa. If there is nothing opposing the cat,
// then it moves forwards at increasing acceleration. If the
// flap produces an opposing force, then the forward force
// of the cat will be transferred into moving the flap as
// well. This is complex, however so I'll use alternative 4.
//
(bool) catCanMoveOut =
(flapGap >= catHeight) \/
(catNextOutEndPos <= flapPosAtCatHeight);
// If the cat is moving outwards, this is the position of
// the end of the cat nearest to the flap.
(real) catCurrentOutEndPos = catMidPoint + (catLength / 2);
// This is the position of the end of the cat after the
// next time step, if the cat is moving outwards.
(real) catNextOutEndPos = catCurrentOutEndPos + catMoveDelta;
// If we draw a line along the top of the cat (at "ear
// height"), it will intersect the flap at some point.
// Here we calculate the position of this point.
(real) flapPosAtCatHeight =
(if catHeight <= flapLength) -> tan(flapAngle) * (flapLength - catHeight)
(else) -> 0;
(bool) catCanMoveIn =
(flapGap >= catHeight) \/
(catNextInEndPos >= flapPosAtCatHeight);
// If the cat is moving inwards, this is the position of
// the end of the cat nearest to the flap.
(real) catCurrentInEndPos = catMidPoint - (catLength / 2);
// This is the position of the end of the cat after the
// next time step, if the cat is moving outwards.
(real) catNextInEndPos = catCurrentInEndPos - catMoveDelta;
// Is the cat currently engaged in the flap or not?
catInFlap = abs(catMidPos) < (catLength / 2);
// The actions that this agent may perform
protocol
catIntention==GO_OUT ^ catCanMoveOut
-> catMidPos = |catMidPos| + catMoveDelta;
catIntention==GO_IN ^ catCanMoveIn
-> catMidPos = |catMidPos| - catMoveDelta;
// !@@! The original spec had these as derivates. However
// I think these are really actions, and should therefore
// go under protocol. This means I have to have the rules
// below to set the Push values back to false.
catIntention==GO_OUT ^ !catCanMoveOut
-> catPushOut = true;
catIntention==GO_IN ^ !catCanMoveIn
-> catPushIn = true;
catCanMoveOut -> catPushOut = false;
catCanMoveIn -> catPushIn = false;
}