/*

Copyright 2010 James A. Mason, York University, Toronto, Canada
http://www.yorku.ca/jmason

Unless required by applicable law or agreed to in writing, this software
is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
ANY KIND, either express or implied.

You may use and modify it for research purposes, provided that
the copyright holder above is acknowledged as the original author.

 */

package cards1;

import java.io.*; // for testing, debugging, and reporting unexpected errors

import asd.*;
import java.util.*;
import java.awt.*;  // for Point
import java.awt.event.*;  // for MouseEvent
import javax.swing.*;  // for JTextArea

/*
   CardAgent instances can respond to English-language commands
   to perform actions on Card and CardPile instances on a CardTable.
 */
public class CardAgent1
{
   public CardAgent1(CardTable t)
   {
      table = t;
      semantics = new Cardgram1Semantics();
      parser = new ASDParser(semantics);
      semantics.setParser(parser);
      parser.useGrammar("Cardgram1.grm");
      effectors = new CardAgent1Effectors(this, table);
   }

// These functions are used by CardWorld to establish links to fields
// in the CardWorldFrame1 window, for linguistic communication with
// the user, and to initialize the state of the card table:

   public void setOutputPane(JTextArea out)
   {
      outputPane = out;
   }

   public void setUtteranceField(JTextField field)
   {
      utteranceField = field;
   }

   public void initialize()
   {  table.clear();
      deck = new PlayingCardDeck(2, "png"); // with two jokers
      deck.setAgent(this);
      deck.setCardTable(table);
      deck.setHomePosition(20, 40);
      deck.stackUp();
      referentPiles = table.getPiles();
        // needed to initialize currentPiles in CardTable instance ??
   }

// These functions are used to provide linguistic output to the user:

   private void echoInput(String input)
   {  if (outputPane != null)
         outputPane.append("You: " + input + "\n");
   }

   private void outputMessage(String m)
   {
      if (outputPane != null)
         outputPane.append("CardWorld: " + m + "\n");
   }


// These functions act as "sensory input" to the Card Agent,
// receiving messages from the current Card or CardTable instance:

   public void cardIndicated(Card givenCard)
      // invoked by a Card or CardTable instance
      // when the card is pointed to or moved.
   {
      newCardIndicated = true;
      lastCardAttendedTo = givenCard;
      lastPilesAttendedTo = null;
         // forget about piles previously attended to
   }

   public void cardsMoved(boolean value)
      // invoked by a CardTable when a card is moved
   {
      cardsMoved = value;
   }

   public void pointIndicated(Point mousePoint)
   {
      newPointIndicated = true;
      lastPointIndicated = mousePoint;
   }


// The interpret... funtions interpret input sentences from the user
// as commands to perform actions in the Card World:

   public void interpretCommand(String givenCommand)
      // interprets an entire input command string
      // in the context of any pointing since the last command.
   {
      // Prepare list of expected phrase types for ASDParser:
      ArrayList<String> expected = new ArrayList<String>(1);
      expected.add("SENTENCE");

      // Echo the input command to the output pane:
      outputPane.setText(null); // clear the output pane
      echoInput(givenCommand);

      // Reduce the command to all lower-case letters and remove
      // final punctuation, if any:
      String command = givenCommand.trim().toLowerCase();
      while (command.length() > 0 &&
         ! Character.isLetter(command.charAt(command.length()-1)) )
         // remove final non-letter character, if any:
         command = command.substring(0, command.length()-1);
      if (command.length()==0)
         return;  // Ignore empty or non-alphabetic command.

      // Check the command for possible unknown words:
      ASDGrammar lexicon = parser.lexicon();
      String[] words = command.split("\\s");
      boolean unknownWord = false;
      for (int n = 0; n < words.length; ++n)
      {
         if (lexicon.lookupWord(words[n]) == null)
         {  outputMessage("Unknown word \"" + words[n] + "\"");
            unknownWord = true;
         }
      }
      if (unknownWord)
         return;

      sentenceUsedExplicitDeictic = false;
         // keeps track of how long the last point indicated
         // should continue to be remembered.

      // Attempt to parse and interpret the command:
      parser.initialize(command, expected);
      boolean commandUnderstood = false;

      while(! commandUnderstood && parser.parse())
      {  // command has not been understood yet,
         // but a successful parse has been found.
         Object nodeValue = parser.phraseStructure().nextNode().value();
         ArrayList clauseValues = null;
         if (nodeValue instanceof ArrayList)
            clauseValues = (ArrayList) nodeValue;
            // each HashMap in the ArrayList holds
            // the semantics of a clause in the command.
         else
         {
            System.err.println(
               "CardAgent1 146: Semantic value of command is not "
               + "an ArrayList<HashMap> as expected.");
            commandUnderstood = false;
         }
         // Attempt to interpret the semantics of the clauses
         // in the current pragmatic context:
         commandUnderstood = this.interpretClauses(clauseValues);
      }

      // Adjust deictic and other memory for next command:
      newCardIndicated = false;
      newPointIndicated = false;

      // Remember what pile was last attended to
      // if that hasn't already been done:
      if ( lastCardAttendedTo != null
           && lastPilesAttendedTo == null)
      {
         lastPilesAttendedTo = new ArrayList<CardPile>(1);
         lastPilesAttendedTo.add(
            table.whichPileHasCard(lastCardAttendedTo));
      }

      // Forget the last card attended to,
      // if previous command was about pile(s):
      if (lastTypeReferredTo.equals("pile"))
         lastCardAttendedTo = null;

      // If the last command dealt with all the cards or piles
      // or all the cards in a particular pile,
      // forget about what card was last pointed to or manipulated:
      if (lastIncludedAllCardsExplicitly
          || inclusivity.equals("all")
          || aggregation.equals("each")
          || eachUsed)
         lastCardAttendedTo = null;

      // Similarly for memory of the last pile(s) attended to:
      if (lastIncludedAllCardsExplicitly
          && ! lastTypeReferredTo.equals("pile"))
         lastPilesAttendedTo = null;

      // Objects cannot continue to be referred to separately by "it" or
      // "that pile" after a command using "each" if explicit deixis was
      // used.  e.g. "spread out each pile",
      // [pointing] "turn that one over","stack it up".
      if (actualWhich.equals("deictic") )
         eachUsed = false;

      // For now, cards can't continue to be referred to by "it"
      // after a command using "each card", because memory
      // for more than lastCardAttendedTo is not kept:
      if (lastTypeReferredTo.equals("card") )
         eachUsed = false;

      // If the sentence didn't use explicit an deictic adverb,
      // forget the last place that was pointed to:
      if ( ! sentenceUsedExplicitDeictic )
         lastPointIndicated = null;

      if (commandUnderstood)
         outputMessage("OK");
      else
         outputMessage("Command not understood.");

      utteranceField.setText(""); // clear input line for next command

   } // end interpretCommand


   private boolean interpretClauses(ArrayList clauses)
      // attempts to interpret the semantics of each clause in the command.
   {
      Iterator it = clauses.iterator();
      while (it.hasNext())
      {
         Object nextClauseSemantics = it.next();
         if (! (nextClauseSemantics instanceof HashMap) ||
             ! interpretClause((HashMap) nextClauseSemantics))
            return false;  // clause not understood
      }
      return true;  // all clauses understood
   } // end interpretClauses


   // This function takes semantic feature values representing the meaning
   // of a clause, together with the current situation on the CardTable, and
   // decides what pragmatic operation(s) to perform:
   private boolean interpretClause(HashMap clauseFeatures)
   {
      // Attempt to find the things on the CardTable
      // to which the clause refers, and unpack values of other
      // features of the clause meaning:
      if (! findReferents(clauseFeatures)) // null command, or
         return false;  // referents couldn't be found unambiguously

      // Apply the command according to referentType
      // and referentCards or referentPiles:
      if (commandNeedsMoreThanOneCard) // "shuffle", "spread", "stack"
      {  if ( ! interpretMultiCardCommand() )
            return false;   // interpretation failed
      }
      else if (command.equals("turn"))
      {  if ( ! interpretTurnCommand() )
            return false;   // interpretation failed
      }
      else // This case shouldn't happen.
      {
         outputMessage("Unexpected type of command.");
         return false;
      }

      // Interpretation of the clause has been successful.
      // Update relevant short-term memory items before the next clause:
      lastTypeReferredTo = referentType;
      if (eachUsed && number.equals("plural"))
         eachUsed = false;  // e.g. "them" or "the piles" after
                            // "spread out each pile"
      return true;  // clause was interpreted successfully

   }  // end interpretClause


   private boolean interpretMultiCardCommand()
      // interprets a command ("shuffle", "stack", or "spread")
      // that requires more than one card.
   {
      if (referentType.equals("--") || referentType.equals("pile"))
      {  // type of referent is unspecified or "pile"

         if (aggregation.equals("--") || aggregation.equals("each"))
         {
            // aggregation is not "together"; apply the command to
            // each refereent pile separately:
            if (aggregation.equals("each")
                && referentPiles.size() == 1)
               outputMessage("There is only one pile.");

            lastPilesAttendedTo = new ArrayList<CardPile>(4);
               // remembers the piles manipulated, for possible
               // anaphoric reference.subsequently.

            // Apply the command to each referent pile separately:
            Iterator it = referentPiles.iterator();
            while (it.hasNext())
            {
               CardPile pile = (CardPile) it.next();
               // Before shuffling, stacking or spreading the pile,
               // turn each card in the pile down, over, or up,
               // according to orientation, if specified:
               if (orientation != "--")
                  effectors.turnEachCardInPile(pile, orientation);
               if (command.equals("spread"))
               {
                  if (where.equals("--") || lastPointIndicated == null)
                     lastPilesAttendedTo.add(
                        effectors.spreadOutPile(pile));
                  else
                     lastPilesAttendedTo.add(
                        effectors.spreadOutPileAt(pile,
                                                  lastPointIndicated));
               }
               else if ( command.equals("shuffle"))
               {
                  lastPilesAttendedTo.add(effectors.shufflePile(pile));
                  // After shuffling, leave the pile arranged as it was,
                  // so the user must state explicitly if and how it
                  // should be rearranged.
               }
               else if (command.equals("stack"))
               {
                  if (where.equals("--") || lastPointIndicated == null)
                     lastPilesAttendedTo.add(
                        effectors.stackUpPile(pile));
                  else
                     lastPilesAttendedTo.add(
                        effectors.stackUpPileAt(pile,
                                                lastPointIndicated));
               }
            }
         }
         else if (aggregation.equals("together"))
         {
            // Apply the command to all of the referent piles together:
            if (referentType.equals("pile") && referentPiles.size() == 1)
               outputMessage("That's only one pile.");
            lastPilesAttendedTo = new ArrayList<CardPile>(4);
            Iterator it = referentPiles.iterator();
            if (where.equals("--") || lastPointIndicated == null)
            {  // no place indicated;
               // stack them at the home of the first pile
               lastPointIndicated
                  = referentPiles.get(0).getHomePosition();
                  // assumes at least one pile
            }
            while (it.hasNext())
            {
               CardPile pile = (CardPile) it.next();
               effectors.turnEachCardInPile(pile, orientation);
               effectors.stackUpPileAt(pile, lastPointIndicated);
            }
            table.findPiles();  // Ask table to re-partition the piles.
            CardPile newPile = null;
            if (lastPointIndicated != null)
               newPile = table.getPileAt(lastPointIndicated);
            if (newPile != null)
            {
               if (command.equals("shuffle"))
                  effectors.shufflePile(newPile);
               if (command.equals("spread"))
                  lastPilesAttendedTo.add(effectors.spreadOutPileAt(
                     newPile, lastPointIndicated));
               else // command is "shuffle" or "stack"
               {
                  effectors.stackUpPileAt(newPile, lastPointIndicated);
                  lastPilesAttendedTo.add(newPile);
               }
            }
            else // this case shouldn't happen
            {
               System.err.println("CardAgent1 367: No pile was found"
                                  + " at the point indicated.");
               return false;
            }
         }
      }
      else if (referentType.equals("card"))
      {  // e.g. "shuffle/spread/stack [all] the cards",
         // "shuffle/spread/stack those cards",
         // but not "shuffle/spread/stack each card"
         CardPile relevantPile = null;
         Point pointIndicated = null;
         if (referentCards.size() == 0)
         {  // Assume the cards were all in one pile:
            if (referentPiles.size() == 1)
            {
               relevantPile = referentPiles.get(0);
               // pointIndicated set here is provisional; the check for
               // deictic reference and pointing comes latet:
               pointIndicated = relevantPile.getHomePosition();
            }
            else // this case shouldn't happen
            {
               outputMessage("I don't know which cards you mean.");
               return false;
            }
         }
         else if (referentCards.size() <= 1)
         {
            outputMessage("I don't know how to " + command
                          + " single cards, only two or more.");
            return false;
         }
         else
         {  // cards referred to were not all in an existing pile
            relevantPile = new CardPile(referentCards.size());
            Iterator it = referentCards.iterator();
            while (it.hasNext())
               relevantPile.addCard((Card) it.next());
            relevantPile.setCardTable(table);
            Card firstCard = referentCards.get(0);
            pointIndicated = new Point((int)firstCard.getX(),
                                   (int)firstCard.getY());
            relevantPile.setHomePosition(pointIndicated);
         }
         effectors.turnEachCardInPile(relevantPile, orientation);
         if (command.equals("shuffle"))
            effectors.shufflePile(relevantPile);
            // needed for commands like
            // "shuffle all the cards and spread them out together"
         if ( ! where.equals("--") && lastPointIndicated != null)
            // deictic reference was used
            pointIndicated = lastPointIndicated;
         else
         // Note: lastPointIndicated mustn't be changed prematurely
         // if where == null, because the old value of
         // lastPointIndicated may be needed by a later clause
         // in the same sentence: e.g.
         // "shuffle the cards and spread them out here"
         {
            if (lastPointIndicated == null)
               lastPointIndicated = pointIndicated;
         }
         if (command.equals("spread"))
            effectors.spreadOutPileAt(relevantPile, pointIndicated);
         else if (command.equals("stack"))
            effectors.stackUpPileAt(relevantPile, pointIndicated);
         else if (command.equals("shuffle")
                  && aggregation.equals("together"))
            effectors.stackUpPileAt(relevantPile, pointIndicated);
         // If the command is "shuffle", but aggregation is not
         // "together", leave the pile arranged as it was, so the
         // user must specify explicitly if and how to rearrange it.
         lastPilesAttendedTo = new ArrayList<CardPile>(1);
         lastPilesAttendedTo.add(relevantPile);
      }

      return true;
   }  // end interpretMultiCommand


   private boolean interpretTurnCommand()
   {
      // direction should be "down", "over", or "up";
      // if it is null, use orientation "down" or "up", instead,
      // or use "over" as the default.  Note: This is redundant
      // with logic in Cardgram1Semantics cardgram_$$_3.
      if (direction.equals("--"))
      {
         if (orientation.equals("--"))
            direction = "over"; // default is "over"
         else // clause had phrase "face down" or "face up"
            direction = orientation;
      }
      else if ( ! ( direction.equals("down") || direction.equals("over")
                  || direction.equals("up") ) )
         return false;

      if (referentType.equals("--"))
      {
         outputMessage("I don't know whether you want "
            + "to turn the entire pile or just that one card.");
         return false;
      }
      else if (referentType.equals("card"))
      {  // The command is to turn card(s).
         if (number.equals("singular") && quantity.equals("one"))
         {
            if (referentCards.size() == 1)
            {  // Turn just the one card:
               Card c = (Card) referentCards.get(0);
               if ( ! where.equals("--") && lastPointIndicated != null)
                  // "turn [down/over/up] that card" with pointing
                  effectors.turnCard(c, direction, lastPointIndicated);
               else
                  effectors.turnCard(c, direction);
            }
            else
            {
               outputMessage("I'm not sure which of those cards"
                             + " you want to turn.");
               return false;
            }
         }
         else if (number.equals("plural") || quantity.equals("several"))
         {
            // e.g. "the cards", "each card"
            if (referentCards != null && referentCards.size() > 0)
            {
               // Turn all of the cards referred to:
               Card c = null;
               Iterator it = referentCards.iterator();
               while (it.hasNext())
               {
                  c = (Card) it.next();
                  effectors.turnCard(c, direction);
               }
               lastPilesAttendedTo = null;

               // If all of the cards were in the same pile,
               // remember that pile:
               boolean samePile = false;
               CardPile p = null;
               it = referentCards.iterator();
               if (it.hasNext())
               {
                  c = (Card) it.next();
                  p = table.whichPileHasCard(c);
                  samePile = true;
               }
               while (samePile && it.hasNext())
               {
                  c = (Card) it.next();
                  if ( p != table.whichPileHasCard(c))
                     samePile = false;
               }
               if (samePile)
               {
                  lastPilesAttendedTo = new ArrayList<CardPile>(1);
                  lastPilesAttendedTo.add(p);
               }
            }
            else if (referentPiles != null)
            {
               // Turn all of the cards in the piles referred to:
               Iterator it = referentPiles.iterator();
               while (it.hasNext())
               {
                  CardPile p = (CardPile) it.next();
                  // Assume each card is to be turned individually:
                  effectors.turnEachCardInPile(p, direction);
               }
               lastPilesAttendedTo = referentPiles;
            }
         }
      }
      else if (referentType.equals("pile"))
      {
         // The command is to turn pile(s).
         if ( ! aggregation.equals("together") )
         {
            if (referentPiles == null) // This shouldn't occur.
            {
               outputMessage("I don't know which pile(s) "
                  + "you want me to turn.");
               return false;
            }

            // Turn each specified pile separately:
            lastPilesAttendedTo = new ArrayList<CardPile>(4);
            Iterator it = referentPiles.iterator();
            while (it.hasNext())
            {
               CardPile pile = (CardPile) it.next();
               ArrayList<CardPile> newPiles
                  = effectors.turnPile(pile, direction);
               CardPile newPile = newPiles.get(0);
               if ( ! where.equals("--") && lastPointIndicated != null )
                  effectors.stackUpPileAt(newPile, lastPointIndicated);
               else
                  lastPilesAttendedTo.add(newPile);
            }

            if ( ! where.equals("--") && lastPointIndicated != null )
            {  // the piles have been put together
               // Ask the table to re-partition the piles:
               table.findPiles();
               CardPile newPile = table.getPileAt(lastPointIndicated);
               if (newPile != null)
               {
                  // Re-stack the consolidated pile
                  effectors.stackUpPileAt(newPile, lastPointIndicated);
                  lastPilesAttendedTo.add(newPile);
               }
               else // this case shouldn't happen
               {
                  System.err.println(
                     "CardAgent1 583: No pile was found"
                     + " at the point indicated.");
                  return false;
               }
            }
         }
         else // aggregation.equals("together")
         {
            // Turn each of the specified piles at the same place:
            if (referentPiles.size() == 1)
               outputMessage("That's only one pile.");

            lastPilesAttendedTo = new ArrayList<CardPile>(4);
            if (where.equals("--")) // no place specified
            {  // stack them at the home of the first pile
               lastPointIndicated
                  = referentPiles.get(0).getHomePosition();
                  // assumes at least one pile
            }
            else if (lastPointIndicated == null)
            {
               outputMessage("I don't know where you mean.");
               return false;
            }
            Iterator it = referentPiles.iterator();
            while (it.hasNext())
            {
               CardPile pile = (CardPile) it.next();
               effectors.turnPile(pile, direction);
               effectors.stackUpPileAt(pile, lastPointIndicated);
            }
            CardPile newPile = table.getPileAt(lastPointIndicated);
            if (newPile != null)
               lastPilesAttendedTo.add(newPile);
            else // this case shouldn't happen
            {
               System.err.println(
                  "CardAgent1 620: No pile was found"
                  + " at the point indicated.");
               return false;
            }
         }
      }

      return true;
   } // end interpretTurnCommand


// The findReferents... functions find the referents
// for the noun/pronoun phrase and
// (optional) deictic adverb in a clause:

   private boolean findReferents(HashMap clauseFeatures)
   {
      // Unpack the semantic features and set default values:
      command = (String) clauseFeatures.get("command");
      if (command == null)
         return false;
      direction = (String) clauseFeatures.get("direction");
         // null, "down", "over", or "up"
      if (direction == null) direction = "--";
      orientation = (String) clauseFeatures.get("orientation");
         // null, "down", or "up"
      if (orientation == null) orientation = "--";
      where = (String) clauseFeatures.get("where");
      if (where == null)
         where = "--";
      else
      {  // a deictic adverb "here" or "there" has been used
         if ( where.equals("here") )
         {
            if ( ! newPointIndicated && ! newCardIndicated )
            {  // "here" shouldn't be used anaphorically
               outputMessage("I don't know where you are referring to.");
               return false;
            }
            else if ( ! newPointIndicated && lastCardAttendedTo != null )
               // If no place has been indicated, use the location of
               // the last card indicated or moved, if any:
               lastPointIndicated = lastCardAttendedTo.getLocation();
         }
         else // where.equals("there") // could be deictic or anaphoric
         if ( lastPointIndicated == null && lastCardAttendedTo != null )
            // If no place has been indicated, use the location of
            // the last card indicated or moved, if any:
            lastPointIndicated = lastCardAttendedTo.getLocation();
         // Also, remember that an explicit deictic adverb was
         // used, so that memory to the last point indicated can be
         // continued to the next sentence, if necessary:
         sentenceUsedExplicitDeictic = true;
      }
      what = (HashMap) clauseFeatures.get("what");
      type = (String) what.get("type");
      if (type == null) type = "--";
      aggregation = (String) clauseFeatures.get("aggregation");
         // null, "each", or "together"
      String nounPhraseAggregation = (String) what.get("aggregation");
      if (aggregation == null)
         aggregation = nounPhraseAggregation;
      if (aggregation == null) aggregation = "--";
      // Both number and quantity are needed, because of phrases like
      // "each card" or "each one", which yield number "singular"
      // but quantity "several"
      number = (String) what.get("number"); // "singular" or "plural"
System.out.println("CardAgent1 705: number = " + number);
      quantity = (String) what.get("quantity"); // "one" or "several"
      which = (String) what.get("which"); // null, "anaphoric" or "deictic"
      if (which == null) which = "--";
System.out.println("CardAgent1 710: which = " + which);
      inclusivity = (String) what.get("inclusivity"); // null or "all"
      if (inclusivity == null) inclusivity = "--";

      // variables to keep track of the things actually referred to
      // and their type:
      actualWhich = "quantified";  // "anaphoric", "deictic" or "quantified",
         // "quantified" being the default

      // These two variables keep track of which reference possibilities
      // have been tried for the current clause, to prevent unending
      //  mutualrecursion between findReferentsAnaphoricCard and
      // findReferentsQuantified:
      anaphoricCardReferenceTried = false;
      quantifiedReferenceTried = false;

      if (eachUsed) // "each", "every", or "separately" was used in
      {             // the previous command
         if (! type.equals("--") && ! lastTypeReferredTo.equals("--")
             && ! type.equals(lastTypeReferredTo)
             || aggregation.equals("together")
             || which.equals("deictic") && newCardIndicated
            )
            // The previous command used it with respect to a different
            // type, or the current one cancels the separation.
            eachUsed = false;
      }
      eachUsed = eachUsed ||
                 aggregation.equals("each") && number.equals("singular");
         // "each" or "every" was used explicitly in this clause,
         // or has just been used to indicate
         // more than one item with a singular noun phrase.
         // This is needed to interpret "it" in a command like
         // "shuffle each pile and spread it out" correctly.
         // Note: The grammar gives a clause like
         // "spread out each of the piles" a singular number.

      referentType = type;
      if (referentType.equals("--"))
         referentType = lastTypeReferredTo;
         // type of actual referent: unspecified, "card", or "pile"
      referentCards = null;  // referent(s) if referentType is "card"
      referentPiles = null; // referent(s) if referentType is "pile"

      commandNeedsMoreThanOneCard
         = command.equals("shuffle") || command.equals("spread")
           || command.equals("stack");

      // Find the referent and actual referentType ("card" or "pile")
      // for the command:

      // Get ready to accumulate either cards or piles as referents:
      referentCards = new ArrayList<Card>(13);
      referentPiles = new ArrayList<CardPile>(4);

      if (newCardIndicated)
         // user has pointed to or manipulated a card or pile;
         // treat the command as deictic (i.e., reference by pointing),
         // if possible
      {
         if (findReferentsWithPointing())
            return true;
         outputMessage("I don't know what you are referring to.");
         return false;
      }

      // otherwise no card (or pile) has been pointed to

      else if (which.equals("deictic"))
      {  // e.g. "this", "that", "these", "those"
         // the command called for pointing to or manipulating
         // a card or pile, but none was done; so see if it
         // can be interpreted as anaphoric
         actualWhich = "anaphoric";
      }
      else if (which.equals("anaphoric")
               || which.equals("quantified"))
         actualWhich = which;


      if (actualWhich.equals("anaphoric"))
         // e.g. "it" or "them", "that pile", "those cards"
         // without pointing
         return findReferentsAnaphoric();

      else if (actualWhich.equals("quantified"))
         return findReferentsQuantified();
            // actualWhich may be changed to "anaphoric" if
            // quantified reference proved to be ambiguous

      outputMessage("I don't know what you are referring to.");
      return false;

   } // end findReferents

   private boolean findReferentsAnaphoric()
   {
      // e.g. "it" or "them", "that pile", "those cards"
      // find anaphoric referent using lastCardAttendedTo
      // or lastPilesAttendedTo
      if (referentType.equals("--"))
         referentType = lastTypeReferredTo;
      if (referentType.equals("--"))
      {  // ambiguous whether reference is to a card or a pile
         // even anaphorically
         if (number.equals("singular")) // "it"
         {
            lastIncludedAllCardsExplicitly = false;
            if (lastCardAttendedTo != null)
            {  // D1 or A1 Assume reference to last card indicated
               referentCards.add(lastCardAttendedTo);
               // may have to use its pile, depending on the command
               if ( commandNeedsMoreThanOneCard )
               { // D2 or A2 e.g. "shuffle", "spread", "stack"
                  referentType = "pile";
                  referentPiles.add(
                     table.whichPileHasCard(lastCardAttendedTo));
               }
               return true; // referent has been found
            }
            else
            {  // no last card indicated;
               // see if there is only one possibility
               if (lastPilesAttendedTo != null &&
                   lastPilesAttendedTo.size() == 1)
               { // A2
                   referentType = "pile";
                   referentPiles = lastPilesAttendedTo;
                   return true; // referent has been found
               }
               else if (table.numberOfPiles() == 1)
               {
                  if (commandNeedsMoreThanOneCard)
                  { // Q6
                     lastIncludedAllCardsExplicitly = false;
                     referentType = "pile";
                     referentPiles = table.getPiles(); // only one pile
                     return true; // referent has been found
                  }
                  else
                  { // Q5 or Q6
                     outputMessage("I don't know whether you are "
                       + "referring to the pile or the card.");
                     return false;
                  }
               }

               outputMessage(
                  "I don't know what you are referring to.");
               return false;
            }
         }
         else if (number.equals("plural")) // "them"
         {
            if (lastIncludedAllCardsExplicitly)
            {  // A3 or A5, Q1 or Q2
               referentType = "card";
               referentCards = table.getCards();
            }
            else
            {
               referentPiles = table.getPiles(); // all of the piles
               if (referentPiles.size() > 1)
                  referentType = "pile";  // Q3 or Q4
               else // Q1 or Q2 there is only one pile;
               {    // so "them" refers to cards
                  referentCards = table.getCards();
                  referentType = "card";
               }
            }
            return true; // referent has been found
         }
         else
         {
            outputMessage("I don't know what you are referring to.");
            return false;
         }
      }
      else if (referentType.equals("card"))
      {
         return findReferentsAnaphoricCard();
      }
      else if (referentType.equals("pile"))
      {
         return findReferentsAnaphoricPile();
      }

      outputMessage("I don't know what you are referring to.");
      return false;
   } // end findReferentsAnaphoric

   private boolean findReferentsAnaphoricCard()
   {
      anaphoricCardReferenceTried = true;
         // to prevent unending mutual recursion with
         // findReferentsQuantified
      if (number.equals("singular") && quantity.equals("one"))
      {  // "that card", or "it" where lastTypeReferredTo was "card"
         lastIncludedAllCardsExplicitly = false;
         if (lastCardAttendedTo != null)
         {  // A1
            referentCards.add(lastCardAttendedTo);
            return true; // referent has been found
         }

         // no memory of last card attended to
         if (type.equals("--")) // "it" or "that one" was used when the
            // preceding command referred to a card or cards;
            // so "it" may refer to a pile
         {
            CardPile p = null;
            referentPiles = new ArrayList<CardPile>(1);
            if ( lastPilesAttendedTo != null
                 && lastPilesAttendedTo.size() == 1)
            {  // See if reference can be to the last pile attended to
               p = (CardPile) lastPilesAttendedTo.get(0);
            }

            if ( p != null &&  // There was a suitable last pile attended to
                 (p.size() > 1 || ! commandNeedsMoreThanOneCard) )
            {  // A2 It is a multi-card pile or doesn't need to be.
               referentPiles.add(p);
               referentType = "pile";
                  // changed in case of subsequent anaphoric reference
               return true; // referent found
            }

            // No memory of a last pile attended to, or
            // the last pile attended to has only one card
            // while the command needs more than one card.
            // So look for a lone pile or lone multi-card pile:
            String message = findReferentsUniquelyQuantifiedPile();
            if (message != null)
            {  // Q6
               referentType = "pile";
                  // changed in case of subsequent anaphoric reference
               if (message.equals("the only pile with more than one card"))
                  outputMessage("I assume you are referring to " + message + ".");
               return true;  // referent found
            }

            outputMessage("I'm not sure what you are referring to.");
            return false;
         }

         if (type.equals("card")) // "that card" was used
         {  // See if the phrase can be interpreted as uuiquely quantified,
            // if that hasn't been tried alraady.
            referentType = "card";  // redundant, but best to be explicit here
            if ( ! quantifiedReferenceTried )
            {
               actualWhich = "quantified";
               return findReferentsQuantified();
            }
         }

         outputMessage(
            "I don't know which you are referring to.");
         return false;
      }

      // number is "plural" ("those cards", or "them"
      // when "card" was last type referred to)
      // or quantity is "several" ("each card")

      if (lastIncludedAllCardsExplicitly)
      {  // A3 or A5, Q1 or Q2
         referentCards = table.getCards();
         return true; // referent has been found
      }

      if ( lastPilesAttendedTo != null
          && lastPilesAttendedTo.size() == 1)
      {
         // See if it makes sense for the referent to be
         // the cards in the last pile(s) manipulated:
         CardPile p = (CardPile) lastPilesAttendedTo.get(0);
         if ( p.size() > 1)
         {  // A3 or A5; A2
            referentPiles = lastPilesAttendedTo;
            return true; // referent has been found
         }
      }

      if ( lastCardAttendedTo != null )
      {  // assume reference is to all cards in its pile
// If referentType is changed to "pile" here, a subsequent "them" may be
// misinterpreted:
//       referentType = "pile";

         CardPile p = table.whichPileHasCard(lastCardAttendedTo);
         if (p.size() > 1)
         {  // A1, A2
            outputMessage("I assume you are referring to " +
               "the pile containing the card(s) previously referred to.");
            referentPiles.add(p);
            return true;  // referent has been found
         }
      }

      // Q1 or Q2 or Q3 or Q4
      // Assume reference is to all cards or all piles on the table
      outputMessage("I assume you are referring to " +
         "all the cards and/or piles.");
      referentCards = table.getCards();
      referentPiles = table.getPiles();
      lastIncludedAllCardsExplicitly = true;

      lastCardAttendedTo = null; // Since reference is now to
          // more than one card, "it" cannot be used subsequently
          // to refer to a single card that was attended to.\
      return true;  // referent has been found
   } // end findReferentsAnaphoricCard


   private boolean findReferentsAnaphoricPile()
   {
      lastIncludedAllCardsExplicitly = false;
      CardPile pile = null;
      if (number.equals("singular") && !eachUsed)
      {  // e.g. "that pile" or "it" but not "each pile"

         if (lastCardAttendedTo != null
//                        && which != null && which.equals("anaphoric")
             )
         {  // A1, A2 reference is to the pile
            // containing the last card pointed to
            pile = table.whichPileHasCard(lastCardAttendedTo);
         }
         else if (lastPilesAttendedTo != null
            && lastPilesAttendedTo.size() == 1)
         {  // A2 assume reference is to the last pile manipulated
            pile = lastPilesAttendedTo.get(0);
         }

         if (pile != null)
         {
            if (commandNeedsMoreThanOneCard && pile.size() == 1)
               // A one-card pile was referred to.
               // e.g. move a card alone, "turn it over" "spread it out"
               // when the last type referred to was "pile";
               // or "spread that pile out" when last pile referred to has only one card
            outputMessage("Warning: That pile has only one card.");
            referentPiles.add(pile);
            return true;  // referent has been found
         }

         String message = findReferentsUniquelyQuantifiedPile();
         if (message == null) // no uniquely quantified referent pile was found.
         {
            outputMessage("I don't know what you are referring to.");
            return false;
         }
         // Q6 There is only one sensible referent pile.
         if (message.equals("the only pile with more than one card"))
            outputMessage("I assume you are referring to " + message + ".");
         return true;  // referent has been found

      }
      else // number is "plural" -- "those piles", or "them"
           // or "each" has been used -- "each [pile]"
           // where "pile" was the last type referred to
           // if "pile" was not stated explicitly
      {
         if (lastPilesAttendedTo != null &&
             lastPilesAttendedTo.size() > 1)
         {  // A4 or A6
            referentPiles = lastPilesAttendedTo;
            return true; // referent has been found
         }
         else
         {  // Q3 or Q4
            actualWhich = "quantified";
            referentPiles = table.getPiles();
            outputMessage("I assume you are referring to " +
               "all of the piles.");
            if (referentPiles.size() == 1)
               outputMessage("(Right now there is only one pile.)");
            lastIncludedAllCardsExplicitly = true;
            return true; // referent has been found
         }
         // Note: Don't set lastCardAttendedTo = null yet;
         // it may be needed for a second clause in the same command.
      }

   } // end findReferentsAnaphoricPile

   private boolean findReferentsQuantified()
   {
      // actualWhich may be changed to "anaphoric" if
      // quantified reference proves to be ambiguous
      quantifiedReferenceTried = true;
         // to prevent unending mutual recursion with
         // findReferentsAnaphoricCard
      if (referentType.equals("--"))
      {      // no explicit type specified (e.g. "shuffle all" or
             // "shuffle them" with no prior reference)
             // referent type depends on command
         if (commandNeedsMoreThanOneCard)
         {  // Q3 or Q4
            referentPiles = table.getPiles();
            if (referentPiles.size() > 1)
            {
               referentType = "pile";
               lastIncludedAllCardsExplicitly = false;
            }
            else
            {
               referentType = "card";
               lastIncludedAllCardsExplicitly
                  = number.equals("plural");
            }

            return true; // referent has been found
         }
         else
            referentType = lastTypeReferredTo;
      }
      if (referentType.equals("--")) // type still not known
      {
         outputMessage("I don't know what you're referring to.");
         return false;
      }
      else if (referentType.equals("card"))
      {
         if (number.equals("singular") && quantity.equals("one"))
         {  // "the card"
            lastIncludedAllCardsExplicitly = false;
            // See if there is only one card by itself,
            // not in a pile with others:
            Card c = table.findOnlySingletonCard();
            if (c != null)
            {  // Q5
               outputMessage("I assume you are referring to " +
                  "the only card that is not in a pile with other cards.");
               referentCards.add(c);
               lastCardAttendedTo = c;
               lastPilesAttendedTo = new ArrayList<CardPile>(1);
               lastPilesAttendedTo.add(table.whichPileHasCard(c));
               return true; // referent has been found
            }
            else
            {  // Q5
               // See if there is only one pile, stacked, with
               // a single top card:
               ArrayList<CardPile> piles = table.getPiles();
               if (piles.size() == 1)
               {
                  CardPile p = piles.get(0);
                  if (p.isStacked())
                  {
                     c = p.getCard(p.size()-1);
                     referentCards.add(c);
                     lastPilesAttendedTo = new ArrayList<CardPile>(1);
                     lastPilesAttendedTo.add(p);
                     return true;
                  }
               }
               // Otherwise, assume reference to the last card
               // referred to, if any, if not already tried:
               if ( ! anaphoricCardReferenceTried )
               {
                  actualWhich = "anaphoric";
                  return findReferentsAnaphoricCard();
               }
               outputMessage("I don't know what you are referring to.");
               return false;
            }
         }
         else // Q1 or Q2 number is "plural" ("the cards")
              // or quantity is "several" ("each card")
              // assume reference is to all the cards
         {
            referentCards = table.getCards();
            lastIncludedAllCardsExplicitly = true;

            return true; // referent has been found
         }
      }
      else if (referentType.equals("pile"))
      {
         lastIncludedAllCardsExplicitly = false;
         if (number.equals("singular") && quantity.equals("one"))
         {  // "the pile"

            String message = findReferentsUniquelyQuantifiedPile();
            if (message != null) // unique pile referent found
            {  // Q6
               if (message.equals("the only pile with more than one card"))
                  outputMessage("I assume you are referring to " + message + ".");
               return true;  // referent has been found
            }

            if ( ! which.equals("anaphoric") ) // if not already tried,
            {  // assume reference to the last pile referred to
               actualWhich = "anaphoric";
               return findReferentsAnaphoricPile();
            }
         }
         else // Q3 or Q4 number is "plural" -- "the piles", or
              // quantity is "several" -- e.g. "each pile"
              // assume reference is to all the piles
         {
            outputMessage("I assume you are referring to " +
               "all the piles on the table.");
            referentPiles = table.getPiles();
            if (referentPiles.size() == 1)
               outputMessage("(Right now there is only one pile.)");
            lastIncludedAllCardsExplicitly = true;

            return true; // referent has been found
         }
      }

      outputMessage("I don't know what you are referring to.");
      return false;
   } // end findReferentsQuantified


   private boolean findReferentsWithPointing()
   {
      actualWhich = "deictic";
      newCardIndicated = false; // Use deictic reference
         // for only one clause; use anaphoric references thereafter.
         // Note: If it turns out that the deictic reference
         // is not really used for this clause,
         // newCardIndicated is reset to true later on below.
      if (referentType.equals("--")) // Referent type was not stated
      {  // explicitly and there was no memory for last referent type';
         // so try to infer it from command type
         if (commandNeedsMoreThanOneCard)
            // "shuffle", "spread", or "stack" implies "pile"
            referentType = "pile";
         else // referent type could still be "card" or "pile"
         {
            CardPile p
               = table.whichPileHasCard(lastCardAttendedTo);
            if (p.size() > 1)
            {
               outputMessage(
                  "I'm not sure whether you mean"
                  + " the card or the pile.");
               return false;
            }
            // If the last card pointed to or moved is by
            // itself, assume the reference is to the
            // card, not to its one-card pile:
            referentType = "card";
         }
      }

      if (referentType.equals("--"))
      {  // no explicit type specified
         // -- e.g. pointing to initial pile "turn it over"
         outputMessage(
            "I don't know whether you are referring to card or pile.");
         return false;
      }

      if (referentType.equals("card"))
      {
         if (eachUsed)
         {
            if (commandNeedsMoreThanOneCard)
            {
               outputMessage("I don't know how to " + command +
                  " individual cards, only piles or multiple cards.");
               return false;
            }
            if (which.equals("deictic"))
            {
               // D3 e.g. "each of those cards"
               // Note: This logic may be changed in a future version
               // that keeps track of several cards separately
               // from their piles and operates on those referent cards
               // separately from other cards in their piles:
               CardPile p
                  = table.whichPileHasCard(lastCardAttendedTo);
               Iterator it = p.getIterator();
               while(it.hasNext())
                  referentCards.add((Card) it.next());
               lastPilesAttendedTo = new ArrayList<CardPile>(1);
               lastPilesAttendedTo.add(p);
               lastIncludedAllCardsExplicitly = false;
               return true;  // referent has been found
            }
            else // Q1 e.g. "each of the cards" or "each card"
            {
               referentCards = table.getCards();
               lastIncludedAllCardsExplicitly = true;
               actualWhich = "each"; // to change it from "deictic"
               outputMessage("I assume you are referring to every "
                  + "card on the table.");
               return true;  // referent has been found
            }
         }
         else if (number.equals("singular"))
         {
            // D1 "that card", "the card",
            // or "it" with previous reference type "card",
            // Reference is to the card itself
            if (commandNeedsMoreThanOneCard)
            {
               outputMessage("I don't know how to " + command +
                  " individual cards, only piles or multiple cards.");
               return false;
            }
            else
            {
               referentCards.add(lastCardAttendedTo);
               lastPilesAttendedTo = new ArrayList<CardPile>(1);
               lastPilesAttendedTo.add(table.whichPileHasCard(
                  lastCardAttendedTo));
               lastIncludedAllCardsExplicitly = false;
               return true;  // referent has been found
            }
         }
         else if (number.equals("plural"))
         {
            // "cards" or "them" when previous referentType was "card"
            if (which.equals("quantified") && inclusivity.equals("all"))
            {
               // Q1 "all the cards"
               referentPiles = table.getPiles();
                  // so there will be a home position in cases like
                  // "shuffle all the cards and spread them out together"
               referentCards = table.getCards();
               lastIncludedAllCardsExplicitly = true;
               actualWhich = "quantified"; // to change it from "deictic"
               return true;  // referent has been found
            }
            else
            {  // D4 reference is to the cards in the pile
               // containing the given card.

               lastIncludedAllCardsExplicitly = false;

               // "shuffle", "spread" or "stack" should have
               // a pile of more than one card:
               if ( commandNeedsMoreThanOneCard
                    && ! table.isCardInAMultiCardPile(lastCardAttendedTo) )
                  outputMessage(
                     "Warning: There is only one card in that pile.");

               referentPiles.add(
                  table.whichPileHasCard(lastCardAttendedTo));

               if (aggregation.equals("--") && command.equals("turn"))
                  aggregation = "each"; // D3
               return true;  // referent has been found
            }
         }
      }
      else if (referentType.equals("pile"))
      {
         lastIncludedAllCardsExplicitly = false;

         if (eachUsed)
         {
            if (! which.equals("deictic"))
            {
               // e.g. "shuffle each pile" or "stack each of them"
               actualWhich = "each";
                  // to cancel earlier actualWhich = "deictic"
               newCardIndicated = true; // restoring its value
                  // for use in a possible second clause.
                  // e.g. "shuffle each pile and stack this one"
               if (lastPilesAttendedTo != null
                   && lastPilesAttendedTo.size() > 1) // A4
                  referentPiles = lastPilesAttendedTo;
               else
               {  // Q4 Assume referent is all piles on the table:
                  referentPiles = table.getPiles();
                  outputMessage("I assume you are referring to "
                     + "all the piles on the table.");

               // Resetting lastCardAttendedTo = null here would be
               // premature.  It may be needed for
               // a second clause in the same command:
               }
               return true;  // referent has been found
            }
            else if (quantity.equals("several"))
            {  // which is "deictic" here
               // e.g. "shuffle each of those piles"
               // Only one pile has been pointed to,
               // but reference is to more than one pile.
               // Assume reference is not to the single pile last
               // indicated, but to piles previously referred to or
               // to all piles on the table:
               actualWhich = "anaphoric";
               return findReferentsAnaphoric();
            }
         }

         else if (number.equals("singular"))
         {
            // "that pile", "the pile", or "it" used when the most recent
            // reference type is "pile"
            CardPile pile = null;
            if (lastCardAttendedTo != null)
               // D2
               pile = table.whichPileHasCard(lastCardAttendedTo);
            else if (lastPilesAttendedTo != null &&
                     lastPilesAttendedTo.size() == 1)
               // A2
               pile = lastPilesAttendedTo.get(0);

            if ( pile != null && pile.size() == 1
                 && commandNeedsMoreThanOneCard
                 && which.equals("deictic") && referentType.equals("pile")
               )
            {
               // e.g.. "shuffle that pile", "shuffle this pile",
               // or "stack that one" with previous referent type "pile"
               // where the last card indicated is in a one-card pile
               outputMessage("Warning: That pile has only one card.");
               referentPiles.add(pile);
               return true;
            }

            if (pile == null
               || commandNeedsMoreThanOneCard && pile.size() == 1)
            {
               // e.g. move a card alone, "turn it over" "spread it out"
               // when the most recent referent type was "pile";
               // or "spread that pile out"
               // when last pile referred to has only one card
               String message = findReferentsUniquelyQuantifiedPile();
               if (message == null)
               {
                  outputMessage("I don't know what you are referring to.");
                  return false;
               }
               // Q6 There is only one sensible referent pile.
               if (message.equals("the only pile with more than one card"))
                  outputMessage(
                     "I assume you are referring to " + message + ".");
               // No output message is needed if there is only one pile.
               return true;  // referent has been found
            }

            referentPiles.add(pile);
            return true;  // referent has been found
         }

         else if (number.equals("plural"))  // "piles"
         {
            if ( ! (which.equals("anaphoric") || which.equals("deictic") ) )
            {
               // Assume the pointing to a specific card or pile
               // is for a later clause in the sentence.
               newCardIndicated = true;
               actualWhich = "quantified";
               return findReferentsQuantified();
            }
            else if (which.equals("deictic")) // "those [piles]"
            {
               // Note:  Because of the pointing, "those" can refer
               // to cards here even if the previous referentType is "pile".
               CardPile pile = table.whichPileHasCard(lastCardAttendedTo);
               if ( pile.size() == 1 ) // assume anaphoric reference
               {
                  // Assume the reference is anaphoric and the pointing to a
                  // specific card or pile is for a later clause
                  // in the sentence.
                  newCardIndicated = true;
                  actualWhich = "anaphoric";
                  return findReferentsAnaphoric();
               }
               else
               {  // Assume reference is to the cards in the pile
                  // just pointed to.
                  actualWhich = "deictic";
                  if (type.equals("--")) // "these" or "those"
                  {
                     // D4 Change reference type from piles to cards:
                     referentType = "card";
                     referentPiles.add(pile);
                     referentCards = new ArrayList<Card>(pile.size());
                     Iterator it = pile.getIterator();
                     while (it.hasNext())
                     {
                        referentCards.add((Card) it.next());
                     }
                     outputMessage("I assume you are referring to " +
                        "the cards in that pile.");
                     return true; // referent has been found
                  }
                  // Here type is explicitly "piles", but only one plle
                  // has been pointed to:
                  if (table.numberOfPiles() == 1)
                  {
                     // D2
                     outputMessage("There is only one pile.");
                     referentPiles.add(pile);
                     return true; // referent has been found
                  }
                  outputMessage("You only pointed to one pile; " +
                     "so I don't know if you\n are referring to " +
                     "that one pile or all the piles.");
                  return false;
               }

            }
            else
            {
               actualWhich = which;  // "anaphoric" or "quantified"
                                     // to be interepreted below
               newCardIndicated = true;
               if (actualWhich.equals("anaphoric"))
                  return findReferentsAnaphoric();
               else if (actualWhich.equals("quantified"))
                  return findReferentsQuantified();
            }
         }
      }

      return false;

   } // end findReferentsWithPointing


   private String findReferentsUniquelyQuantifiedPile()
      // Attempt to find the only pile that could reasonably
      // be a referent.  If not null, the returned value
      // describes the result; if null, it indicates that
      // a unique referent pile wasn't found.
   {
      if ( table.numberOfPiles() == 1 )
      {
         referentPiles = table.getPiles();
         return "the only pile on the table";
      }
      if (commandNeedsMoreThanOneCard)
      {
         CardPile p = table.findOnlyMultiCardPile();
         if (p != null)
         {
            referentPiles = new ArrayList<CardPile>(1);
            referentPiles.add(p);
            return "the only pile with more than one card";
         }
      }
      return null; // no unique referent pile found

   } // end findReferebtsUniquelyQuantifiedPile


// *** instance variables:

// These variables provide links to the agent's "world" and to other
// modules of its "mind":
   private CardTable table = null;
   private PlayingCardDeck deck = null;
   private ASDParser parser = null;
   private Cardgram1Semantics semantics = null;
      // semantic functions for Cardgram1
   private CardAgent1Effectors effectors = null;
      // functions that operate on cards and piles on the CardTable
   private JTextField utteranceField = null;
      // for input sentences from the user
   private JTextArea outputPane = null;
      // for messages to the user

// These variables represent semantic feature values for the current
// command being processed:
   private String command = null;
   private String aggregation = "--";  // "--", "each", or "together"
   private String direction = "--";   // "--", "down", "over", or "up"
   private String orientation = "--";  // "--", "down", or "up"
   private String where = "--";
   private HashMap what = null;
   private String type = "--";
   // Both number and quantity are needed, because of phrases like
   // "each card" or "each one", which yield number "singular"
   // but quantity "several"
   private String number = "--"; // "singular" or "plural"
   private String quantity = "--"; // "one" or "several"
   private String which = "--"; // null, "anaphoric" or "deictic"
   private String inclusivity = "--"; // null or "all"

// These variables keep track of the things actually referred to
// and their type:
   private String actualWhich = "quantified";
      // "anaphoric", "deictic" or "quantified"
   private String referentType = "--";
      // type of actual referent, "card" or "pile"
   // The following two variables might ultimately be replaced by
   // a more general ArrayList<Object> referentObjects.
   private ArrayList<Card> referentCards = null;
      // referent(s) if referentType is "card"
   private ArrayList<CardPile> referentPiles = null;
      // referent(s) if referentType is "pile"

// These variables represent part of the "short term memory" of the agent:
   private boolean commandNeedsMoreThanOneCard = false;
      // true for "shuffle", "spread", "stack"
   private boolean newCardIndicated = false;
      // Has a new card been pointed to or moved?
   private boolean cardIsInAPile = false;
      // Is that card in a pile of two or more cards?
   private boolean newPointIndicated = false;
      // Has a new place on the table top been pointed to?
   private boolean lastIncludedAllCardsExplicitly = false;
      // Indicates whether the last operation was applied to all the cards
      // rather than to all of the piles or just some of the cards.
      // This is necessary to handle commands like
      // "turn over each card and stack them together" or
      // "shuffle all the cards and turn each over"
   private Point lastPointIndicated = null;  // last location pointed to,
      // or where the last operation was done.
   private boolean sentenceUsedExplicitDeictic = false;
      // Indicates whether the current sentence used an explicit deictic
      // adverb ("here" or "there").  If not, the lastPointIndicated
      // can be forgotten for the next sentence, unless a new point
      // is indicated for that sentence.
   private boolean cardsMoved = false; // indicates whether any cards
   // or piles have been moved since the last English input command
   private boolean eachUsed = false; // indicates whether the current
   // clause or the previous one in the same sentence used "each", as in
   // "shuffle each pile and spread it out", so that the singular "it"
   // can correctly be interpreted as referring to more than one thing.

   private String lastTypeReferredTo = "--"; //  "--", "card" or "pile"

   // These two variables are used to prevent unending mutual
   // recursion between findReferentsAnaphoricCard and
   // findReferentsQuantified:
   private boolean anaphoricCardReferenceTried = false;
   private boolean quantifiedReferenceTried = false;

// The following variable can be declared for future use in making
// the agent more general in its handling of referents:
//   private ArrayList<Object> lastThingsAttendedTo = null;

// The following two variables should be replaceable
// ultimately by use of lastThingsAttendedTo:
   private Card lastCardAttendedTo = null;
   private ArrayList<CardPile> lastPilesAttendedTo = null;
}
