MODULE--------------------------------------------------------------- control ---; IMPORT RefIntTbl, Word, RefSeq, Solve; IMPORT Thread, Fmt (*, Wr, Stdio*); Solve2 
TYPE
  Status = { Success, Failure, Aborted, GiveUp, Exhausted, Solved };
 The interpretation of Success and Failure are context dependent.
        The others have fixed uses:
            Aborted   => the user interrupted the search
            GiveUp    => the depth-first search reached its limit
            Exhausted => the total search reached its limit
            Solved    => a solution was found.
CONST
  StopName = ARRAY Status OF TEXT {
    "",
    "Game is not winnable",
    "Aborted",
    "Couldn't find winning move.  Middle click Hint for deep search",
    "Couldn't find winning move.  Middle click Hint for deep search",
    "Solved"
  };
CONST
  StopReason = ARRAY Status OF Solve.WhyStop {
    Solve.WhyStop.Solution,
    Solve.WhyStop.NoSolution,
    Solve.WhyStop.Aborted,
    Solve.WhyStop.GiveUp,
    Solve.WhyStop.Exhausted,
    Solve.WhyStop.Solution
  };
----------------------------------------------------------------- cards ---
TYPE
  Card = [0..52];
  (*Suit = Solve.Suit;*)  (* Spade, Heart, Diamond, Club *)
  Rank = [0..13];
CONST
  NoCard = 52;
CONST
  RankOf = ARRAY Card OF Rank {
     1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
     1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
     1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
     1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
     0
  };
CONST
  CardName = ARRAY Card OF TEXT {
     "Ace of Spades",  "2 of Spades",   "3 of Spades",     "4 of Spades",
     "5 of Spades",    "6 of Spades",   "7 of Spades",     "8 of Spades",
     "9 of Spades",    "10 of Spades",  "Jack of Spades",  "Queen of Spades",
     "King of Spades",
     "Ace of Hearts",  "2 of Hearts",   "3 of Hearts",     "4 of Hearts",
     "5 of Hearts",    "6 of Hearts",   "7 of Hearts",     "8 of Hearts",
     "9 of Hearts",    "10 of Hearts",  "Jack of Hearts",  "Queen of Hearts",
     "King of Hearts",
     "Ace of Diamonds","2 of Diamonds", "3 of Diamonds",   "4 of Diamonds",
     "5 of Diamonds",  "6 of Diamonds", "7 of Diamonds",   "8 of Diamonds",
     "9 of Diamonds",  "10 of Diamonds","Jack of Diamonds","Queen of Diamonds",
     "King of Diamonds",
     "Ace of Clubs",    "2 of Clubs",   "3 of Clubs",      "4 of Clubs",
     "5 of Clubs",      "6 of Clubs",   "7 of Clubs",      "8 of Clubs",
     "9 of Clubs",      "10 of Clubs",  "Jack of Clubs",   "Queen of Clubs",
     "King of Clubs",
     ""
  };
CONST (* "Below[x]" is the card that "fits" below "x" *)
  Below = ARRAY Card OF Card {
    NoCard,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11,
    NoCard, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
    NoCard, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37,
    NoCard, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50,
    NoCard
  };
------------------------------------------------------- table locations ---
TYPE
  Location = [0..17];
  Group = Solve.Group; (* {Foundation, Tableau, Talon} *)
CONST
  First_Foundation = 0;   Last_Foundation = 3;
  First_Tableau    = 4;   Last_Tableau    = 13;
  First_Talon      = 14;  Last_Talon      = 17;
CONST
  GroupOf = ARRAY Location OF Group {
    Group.Foundation, Group.Foundation, Group.Foundation, Group.Foundation,
    Group.Tableau,    Group.Tableau,    Group.Tableau,    Group.Tableau,
    Group.Tableau,    Group.Tableau,    Group.Tableau,    Group.Tableau,
    Group.Tableau,    Group.Tableau,
    Group.Talon,      Group.Talon,      Group.Talon,      Group.Talon
  };
CONST
  GroupName = ARRAY Group OF TEXT {
    "Foundation", "Tableau", "Talon"
  };
--------------------------------------------------------------- layouts ---
TYPE
  Layout = RECORD (* an arrangement of cards *)
    head : ARRAY Location OF Card;
    next : ARRAY Card OF Card;
  END;
 return the sum of ranks of the foundation cards 
PROCEDUREreturn card on 'top' of pile 'loc' in layout 'x'NumFnd (READONLY layout: Layout): CARDINAL = VAR fndSize: CARDINAL := 0; BEGIN FOR i := First_Foundation TO Last_Foundation DO INC(fndSize, RankOf [layout.head[i]]); END; RETURN (fndSize); END NumFnd;
PROCEDUREcompute the new layout resulting from the move src -> destTop (READONLY x: Layout; loc: Location): Card = VAR prev: Card := x.head[loc]; cur := x.next[prev]; BEGIN WHILE cur # NoCard DO prev := cur; cur := x.next[cur]; END; RETURN prev; END Top;
PROCEDURE---------------------------------------------------- cannonical layouts ---NewLayout (READONLY layout : Layout; READONLY src, dest : Location; VAR(*OUT*) result : Tree): Status = VAR card: Card; src_group := GroupOf [src]; dest_group := GroupOf [dest]; tmp := layout; BEGIN (* remove card from 'src' *) card := tmp.head[src]; tmp.head[src] := tmp.next[card]; (* add card to 'dest' *) tmp.next[card] := tmp.head[dest]; tmp.head[dest] := card; (* deal with non-uniqueness of layouts by sorting *) IF (src_group = Group.Talon) OR (dest_group = Group.Talon) THEN SortTalon(tmp); END; IF (src_group = Group.Tableau) AND (tmp.head[src] = NoCard) THEN CollapseTableau(tmp, src); END; IF (dest_group = Group.Tableau) AND (RankOf[card] = 13) THEN MoveKing(tmp, dest); END; RETURN NewTree (tmp, result); END NewLayout;
move king into place. Know that positions >'start' are empty
PROCEDURE------------------------------------------------------------- game tree ---MoveKing (VAR x: Layout; start: Location) = VAR i : Location := start - 1; card := x.head[start]; BEGIN WHILE (i >= First_Tableau) AND (card < Top (x, i)) DO x.head[i + 1] := x.head[i]; DEC (i); END; x.head[i + 1] := card; END MoveKing; PROCEDURECollapseTableau (VAR x: Layout; start: Location) = BEGIN FOR i := start TO Last_Tableau-1 DO x.head[i] := x.head[i + 1]; END; x.head[Last_Tableau] := NoCard; END CollapseTableau; PROCEDURESort (VAR layout: Layout) = BEGIN SortTableau (layout); SortTalon (layout); END Sort; PROCEDURESortTableau (VAR x: Layout) = (* insertion sort *) VAR j: Location; save, key: Card; BEGIN FOR i := First_Tableau + 1 TO Last_Tableau DO save := x.head[i]; key := Top (x, i); j := i - 1; WHILE (j >= First_Tableau) AND (key < Top (x, j)) DO x.head[j+1] := x.head[j]; DEC (j); END; x.head[j+1] := save; END; END SortTableau; PROCEDURESortTalon (VAR x: Layout) = (* insertion sort *) VAR j: Location; key: Card; BEGIN FOR i := First_Talon + 1 TO Last_Talon DO key := x.head[i]; j := i - 1; WHILE (j >= First_Talon) AND (key < x.head[j]) DO x.head[j+1] := x.head[j]; DEC (j); END; x.head[j+1] := key; END; END SortTalon;
TYPE
  Tree = REF RECORD (* tree of all possible layouts *)
    layout  : Layout;
    parent  : Tree := NIL;
    children: Tree := NIL;
    sibling : Tree := NIL;
    hash    : INTEGER := 0;
  END;
 add 'child' as a child of 'parent' in the tree 
PROCEDURE------------------------------------------------------------ hash table ---AddToTree (parent, child: Tree) = BEGIN <*ASSERT child.sibling = NIL AND child.parent=NIL *> child.parent := parent; child.sibling := parent.children; parent.children := child; END AddToTree;
TYPE
  Key = REFANY;
TYPE
  LayoutTable = RefIntTbl.Default OBJECT OVERRIDES
    keyEqual := EqualProc;
    keyHash  := HashProc;
  END;
VAR
  tbl: LayoutTable;
VAR
  numLayouts1: CARDINAL;  (* number of layouts in this depth-first probe *)
  numLayouts : CARDINAL;  (* total number of layouts evaluated *)
  sizeHTable : CARDINAL;
  hashCnt    : CARDINAL;  (* # of times a layout was hashed *)
  eqCnt      : CARDINAL;  (* # of times EqualProc was called *)
  fastCnt    : CARDINAL;  (* # of times took the fast path through EqualProc *)
VAR (* a standby REF value used during table lookups *)
  layoutPool := NEW (Tree);
PROCEDURE NewTree  (READONLY layout: Layout;  VAR(*OUT*) result: Tree): Status =
  BEGIN
    INC(numLayouts);
    INC(numLayouts1);
    IF numLayouts MOD 512 = 0 THEN
      IF Thread.TestAlert() THEN RETURN Status.Aborted; END;
      IF callback # NIL     THEN callback(numLayouts);   END;
    END;
    IF numLayouts1 >= depthLim THEN RETURN Status.GiveUp;    END;
    IF numLayouts >= lim       THEN RETURN Status.Exhausted; END;
    layoutPool.layout := layout;
    layoutPool.hash   := 0;
    IF tbl.put(layoutPool, 0) THEN RETURN Status.Failure; END;
    (* a new entry! *)
    INC(sizeHTable);
    result := layoutPool;
    layoutPool := NEW(Tree); (* allocate a new spare *)
    RETURN Status.Success;
  END NewTree;
PROCEDURE HashProc  (<*UNUSED*>tbl: LayoutTable;  READONLY key: Key): Word.T =
  CONST konst = 1234567;
  VAR h: Tree := key;  sum := h.hash;
  BEGIN
    INC(hashCnt);
    IF (sum = 0) THEN
      WITH x = h.layout DO
        FOR i := FIRST (x.head) TO LAST (x.head) DO
          sum := Word.Plus (Word.Times (konst, sum), x.head[i]);
        END;
        FOR i := FIRST (x.next) TO LAST (x.next) DO
          sum := Word.Plus (Word.Times (konst, sum), x.next[i]);
        END;
      END;
      h.hash := sum;
    END;
    RETURN sum;
  END HashProc;
PROCEDURE EqualProc  (<*UNUSED*>tbl: LayoutTable; READONLY a, b: Key): BOOLEAN =
  VAR h1: Tree := a;  h2: Tree := b;
  BEGIN
    INC(eqCnt);
    IF h1.hash # h2.hash THEN INC(fastCnt); RETURN FALSE; END;
    RETURN h1.layout = h2.layout;
  END EqualProc;
------------------------------------------------------------- debugging ---
******************* PROCEDURE Put (a, b, c: TEXT := NIL) = <*FATAL ANY*> BEGIN IF (a # NIL) THEN Wr.PutText (Stdio.stderr, a); END; IF (b # NIL) THEN Wr.PutText (Stdio.stderr, b); END; IF (c # NIL) THEN Wr.PutText (Stdio.stderr, c); END; END Put;
PROCEDURE PutLayout (READONLY x: Layout) =
  BEGIN
    FOR i := First_Foundation TO Last_Foundation DO
      PutList (x, x.head[i]);
      Put ( );
    END;
    Put ( -- );
    FOR i := First_Talon TO Last_Talon DO
      PutList (x, x.head[i]);
      Put ( );
    END;
    Put ( -- );
    FOR i := First_Tableau TO Last_Tableau DO
      PutList (x, x.head[i]);
      Put ( );
    END;
  END PutLayout;
CONST
  CName = ARRAY Card OF TEXT {
   As,2s,3s,4s,5s,6s,7s,8s,9s,Xs,Js,Qs,Ks,
   Ah,2h,3h,4h,5h,6h,7h,8h,9h,Xh,Jh,Qh,Kh,
   Ad,2d,3d,4d,5d,6d,7d,8d,9d,Xd,Jd,Qd,Kd,
   Ac,2c,3c,4c,5c,6c,7c,8c,9c,Xc,Jc,Qc,Kc,
   *
  };
PROCEDURE PutList (READONLY x: Layout;  c: Card) =
  BEGIN
    IF (c = NoCard) THEN
      Put ([*]);
    ELSE
      Put ([);
      WHILE (c # NoCard) DO
        Put (CName[c]);
        c := x.next[c];
      END;
      Put (]);
    END;
  END PutList;
PROCEDURE DumpResult () =
  VAR n := NUMBER (resultArr^);
  BEGIN
    Put (----- new path --- , Fmt.Int (n),  moves ---\n);
    FOR i := 0 TO MIN (20, n-1) DO
      PutLayout (resultArr[i].layout);
      Put (\n);
    END;
  END DumpResult;
*****************************
-------------------------------------------------------- solution cache ---
VAR resultArr : REF ARRAY OF Tree := NIL; resultInd : CARDINAL; PROCEDUREKnownResult (READONLY layout: Layout): TEXT =
If 'layout' is a known layout in the cached solution, return a description of the next move. Otherwise, return NIL.
  BEGIN
    IF resultArr = NIL THEN RETURN NIL END;
    FOR i := MAX (resultInd-3, 0) TO MIN(resultInd+25, LAST (resultArr^)-1) DO
      WITH x = resultArr[i].layout DO
        IF layout = x THEN
          resultInd := i + 1;
          RETURN DescribeMove (x, resultArr[resultInd].layout);
        END;
      END;
    END;
    IF NumFnd (layout) = 52 THEN RETURN "Game is already won" END;
    RETURN NIL;
  END KnownResult;
* RecordResult records the winning moves in resultArr.
PROCEDUREdetermine the single move that gets from 'layout1' to 'layout2'RecordResult (tree: Tree) = VAR n: INTEGER; t: Tree; BEGIN (* first, find the path length *) t := tree; n := 0; WHILE (t # NIL) DO INC (n); t := t.parent; END; (* allocate space *) resultArr := NEW (REF ARRAY OF Tree, n); (* finally, copy the values into the cache *) t := tree; WHILE (t # NIL) DO DEC (n); resultArr[n] := t; t := t.parent; END; resultInd := 0; END RecordResult; PROCEDUREDescribeMove (READONLY a, b: Layout): TEXT = VAR card: Card; src, dest: Location; BEGIN ComputeMove(a, b, card, src, dest); RETURN Fmt.F("%s: %s -> %s", CardName[card], GroupName[GroupOf[src]], GroupName[GroupOf[dest]]); END DescribeMove; TYPE CardMap = ARRAY Card OF Location;
PROCEDURE------------------------------------------------------- move generation ---ComputeMove (READONLY layout1, layout2: Layout; VAR (* out*) card : Card; VAR (* out*) src, dest : Location ) = VAR before, after: CardMap; VAR top1, top2: ARRAY Location OF Card; VAR canon1, canon2: Layout; BEGIN FindLocations (layout1, before); FindLocations (layout2, after); (* find the card that changed groups or was restacked *) FOR i := FIRST (before) TO LAST (before) DO IF (GroupOf[before[i]] # GroupOf[after[i]]) OR (layout1.next[i] # layout2.next[i]) THEN card := i; src := before[i]; dest := after[i]; RETURN; END; END; (* we're dead... *) FOR i := FIRST (top1) TO LAST (top1) DO top1[i] := Top(layout1, i) END; FOR i := FIRST (top2) TO LAST (top2) DO top2[i] := Top(layout2, i) END; canon1 := layout1; Sort (canon1); canon2 := layout2; Sort (canon2); <*ASSERT FALSE*> END ComputeMove; PROCEDUREFindLocations (READONLY layout: Layout; VAR loc: CardMap) = VAR c: Card; BEGIN loc [NoCard] := LAST (Location); FOR i := FIRST (layout.head) TO LAST (layout.head) DO c := layout.head[i]; WHILE (c # NoCard) DO loc[c] := i; c := layout.next[c]; END; END; END FindLocations;
* Returns Status.Success if the card at 'loc' has a place to move to, * returns Status.Failure if it doesn't. * * If a move is found, it is used to generate a new layout, which is * returned in result. * * The only case in which there are two spots to move to is if the Talon * and Tableau are both possible. FindSpot returns Talon first. Then * a child of this move will contain the other possibility, namely the * move to the Tableau.
PROCEDUREFindFoundationSpot (READONLY layout : Layout; READONLY loc : Location; VAR(*OUT*) result : Tree): Status = VAR card := layout.head [loc]; dest : Location := First_Foundation + (card DIV 13); BEGIN IF layout.head[dest] = Below [card] THEN RETURN NewLayout(layout, loc, dest, result); ELSE RETURN Status.Failure; END; END FindFoundationSpot; PROCEDUREFindTalonSpot (READONLY layout : Layout; READONLY loc : Location; VAR(*OUT*) result : Tree): Status = VAR card := layout.head [loc]; dest : Location; BEGIN (* Don't move a single king to talon. *) IF (RankOf [card] = 13) AND (layout.next[card] = NoCard) THEN RETURN Status.Failure; END; (* consider moving the card to the talon *) IF (layout.head[Last_Talon] = NoCard) THEN (* there's a free spot *) dest := Last_Talon-1; WHILE (dest >= First_Talon) AND (layout.head[dest] = NoCard) DO DEC (dest); END; RETURN NewLayout (layout, loc, dest+1, result); END; RETURN Status.Failure; END FindTalonSpot; PROCEDUREFindTableauSpot (READONLY layout : Layout; READONLY loc : Location; VAR(*OUT*) result : Tree): Status = VAR card := layout.head [loc]; rank := RankOf [card]; status : Status; c : Card; BEGIN (* Don't move a single king from tableau to tableau. *) IF (RankOf [card] = 13) AND (GroupOf[loc] = Group.Tableau) AND (layout.next[card] = NoCard) THEN RETURN Status.Failure; END; (* finally, try moving the card to the tableau *) FOR i := First_Tableau TO Last_Tableau DO IF (i # loc) THEN c := layout.head[i]; IF (c # NoCard AND card = Below[c]) OR (c = NoCard AND rank = 13) THEN status := NewLayout(layout, loc, i, result); IF (status # Status.Failure) THEN RETURN status END; END; END; END; RETURN Status.Failure; END FindTableauSpot;
* Identify children and add them to 'tree'. * Status.Success => found zero or more children * Status.Failure => ** is never returned ** * otherwise, bailing out.
PROCEDUREFindChildren (tree: Tree): Status = VAR result: Tree; status: Status; BEGIN WITH layout = tree.layout DO FOR i := First_Tableau TO Last_Talon DO IF layout.head[i] # NoCard THEN status := FindFoundationSpot(layout, i, result); IF (status # Status.Failure) THEN IF (status = Status.Success) THEN AddToTree(tree, result); END; RETURN status; END; END; END; FOR i := First_Tableau TO Last_Tableau DO IF layout.head[i] # NoCard THEN status := FindTalonSpot(layout, i, result); IF (status = Status.Failure) THEN (* can't move this card *) ELSIF (status = Status.Success) THEN AddToTree(tree, result); ELSE RETURN status; END; END; END; FOR i := First_Tableau TO Last_Talon DO IF layout.head[i] # NoCard THEN status := FindTableauSpot(layout, i, result); IF (status = Status.Failure) THEN (* can't move this card *) ELSIF (status = Status.Success) THEN AddToTree(tree, result); ELSE RETURN status; END; END; END; END; RETURN Status.Success; END FindChildren;
* Recursively generate all possible layouts reachable from 'tree'. * Status.Failure => no winning position is reachable * Status.Success => ** is never returned ** * otherwise, bailing out.
PROCEDUREdo depth-first search of each position on queueGenerate (tree: Tree): Status = VAR status: Status; BEGIN (* if there is a move to foundation, generate only that move *) status := FindChildren(tree); IF (status # Status.Success) THEN RETURN status; END; IF tree.children = NIL THEN (* no legal moves from this point *) IF NumFnd(tree.layout) = 52 THEN RecordResult(tree); RETURN Status.Solved; END; RETURN Status.Failure; END; (* now, call Generate on children *) tree := tree.children; WHILE (tree # NIL) DO status := Generate(tree); IF (status # Status.Failure) THEN RETURN status; END; tree := tree.sibling; END; RETURN Status.Failure; END Generate;
PROCEDUREGenerateDepth (queue: RefSeq.T): Status= VAR max := 0; min := LAST(INTEGER); result := Status.Failure; status : Status; n : INTEGER; tmp : Tree; BEGIN (* find the range of foundation values *) FOR i := 0 TO queue.size () - 1 DO tmp := queue.get (i); n := NumFnd(tmp.layout); max := MAX(max, n); min := MIN(min, n); END; (* search the positions in decreasing foundation order *) FOR i := max TO min BY -1 DO FOR j := 0 TO queue.size () - 1 DO tmp := queue.get (j); IF NumFnd (tmp.layout) = i THEN numLayouts1 := 0; (* reset the depth-first limit *) status := Generate (tmp); IF (status = Status.Failure) THEN (* nothing found *) ELSIF (status = Status.GiveUp) THEN (* bottomed-out *) result := Status.GiveUp; ELSE (* solved it or hit some hard limit *) RETURN status; END; END; END; END; RETURN result; END GenerateDepth;
* Generate tree of all possible layouts in level order, then * call GenerateDepth on each leaf. * Status.Failure => game is not winnable * Status.Success => ** is never returned **
PROCEDURE------------------------------------------------------------------ main ---GenerateBreadth (tree: Tree): Status = VAR queue := NEW(RefSeq.T).init(MIN (cutOver, 1000)); status : Status; n : INTEGER; BEGIN (* expand the queue with a breadth-first search until it contains at least 'cutOver' elements *) queue.addhi (tree); n := 1; WHILE (n > 0) AND (n < cutOver) DO tree := queue.remlo (); DEC (n); status := FindChildren (tree); IF (status # Status.Success) THEN RETURN status; END; IF tree.children = NIL THEN (* no legal moves from this point *) IF NumFnd(tree.layout) = 52 THEN RecordResult(tree); RETURN Status.Solved; END; END; (* now, add the children *) tree := tree.children; WHILE (tree # NIL) DO queue.addhi (tree); INC (n); tree := tree.sibling; END; END; depthLim := userDepthLim; RETURN GenerateDepth (queue); END GenerateBreadth;
VAR cutOver : CARDINAL; userDepthLim : CARDINAL; lim : CARDINAL; depthLim : CARDINAL; callback : Solve.Callback; PROCEDURENextMove (READONLY initial : Solve.Layout; VAR whyStop : Solve.WhyStop; depth : CARDINAL; breadth : CARDINAL; total : CARDINAL; <*UNUSED*> vbose : BOOLEAN; callbck : Solve.Callback): TEXT = VAR layout : Layout; txt : TEXT; tree : Tree; status : Status; BEGIN Encode (initial, layout); Sort(layout); txt := KnownResult (layout); IF (txt # NIL) THEN whyStop := Solve.WhyStop.Solution; RETURN txt; END; (* make args global *) lim := total; userDepthLim := depth; cutOver := breadth; callback := callbck; (* initialize the globals *) depthLim := LAST(INTEGER); tbl := NEW (LayoutTable).init (20000); numLayouts1 := 0; numLayouts := 0; sizeHTable := 0; eqCnt := 0; hashCnt := 0; fastCnt := 0; status := NewTree(layout, tree); <*ASSERT status = Status.Success*> status := GenerateBreadth(tree); IF (status = Status.Solved) THEN txt := KnownResult (layout); ELSE txt := StopName [status]; END; whyStop := StopReason [status]; (* give the garbage collector a chance *) EVAL tbl.init (0); tbl := NIL; IF resultArr # NIL THEN FOR i := FIRST (resultArr^) TO LAST (resultArr^) DO IF resultArr[i] # NIL THEN resultArr[i].children := NIL; resultArr[i].parent := NIL; resultArr[i].sibling := NIL; END; END; END; RETURN txt; END NextMove; PROCEDUREEncode (READONLY x: Solve.Layout; VAR(*OUT*) y: Layout) = (* convert a Solve layout to our format *) VAR l, m: Solve.CardList; c, d: Card; BEGIN FOR i := FIRST (y.head) TO LAST (y.head) DO y.head[i] := NoCard END; FOR i := FIRST (y.next) TO LAST (y.next) DO y.next[i] := NoCard END; (* build the foundation chains *) FOR i := 1 TO 4 DO c := CvtCard (x.fnd[i]); IF (c # NoCard) THEN y.head [First_Foundation + (c DIV 13)] := c; d := Below[c]; WHILE (d # NoCard) DO y.next[c] := d; c := d; d := Below[c]; END; END; END; (* build the talon *) FOR i := 1 TO 4 DO y.head[i+(First_Talon-1)] := CvtCard (x.tal[i]); END; (* and finally, rebuild the tableau *) FOR i := 1 TO 10 DO l := x.tab[i]; IF (l # NIL) THEN c := CvtCard (l.card); y.head[i+(First_Tableau-1)] := c; WHILE (l # NIL) DO m := l.nxt; IF (m # NIL) THEN d := CvtCard (m.card); y.next[c] := d; c := d; END; l := m; END; END; END; END Encode; PROCEDURECvtCard (READONLY c: Solve.CardType): Card = BEGIN IF (c.val = 0) THEN RETURN NoCard; ELSE RETURN 13 * ORD (c.suit) + c.val - 1; END; END CvtCard; BEGIN END Solve2.