[comp.sources.x] v11i061: seahaven - solitaire card game, Part01/02

weissman%pianoforte.esd.sgi.com@SGI.COM (Terry Weissman) (02/13/91)

Submitted-by: Terry Weissman <weissman%pianoforte.esd.sgi.com@SGI.COM>
Posting-number: Volume 11, Issue 61
Archive-name: seahaven/part01

#!/bin/sh
# to extract, remove the header and type "sh filename"
if `test ! -s ./README`
then
echo "writing ./README"
cat > ./README << '\Rogue\Monster\'
This is Seahaven Towers, a solitaire card game written for X11R4 and
C++.  If you don't have X11R4 and C++, you might as well give up now.

One of the files (the one containing the bitmaps for the cards) is so
huge that it won't conveniently fit in a 64K message.  However, it
compresses real well, so I compressed and uuencoded it before putting
it in the shar file.  (Don't worry, you're not missing much by not
being able to read it directly; it's just a bunch of numbers.)  So,
after unsharing, you need to execute the following three commands:

uudecode < cardbitmaps.uue
uncompress cardbitmaps.C.Z
rm cardbitmaps.uue

After that, you should be able to just run xmkmf and make.  (If you
don't have imake, rename Makefile.noimake to be Makefile, and run
make.)  Of course, I can't guarantee that my C++ code is portable; I
haven't been able to try it on a lot of platforms.  But it shouldn't
be too hard to fix if it doesn't compile.

Terry Weissman
weissman@sgi.com

\Rogue\Monster\
else
  echo "will not over write ./README"
fi
if `test ! -s ./seahaven.6`
then
echo "writing ./seahaven.6"
cat > ./seahaven.6 << '\Rogue\Monster\'
.\"Man page for seahaven, by Terry Weissman.
.TH seahaven 6 "07 Feb 1991"
.SH NAME
\fISeahaven Towers\fR - A solitaire game
.SH SYNOPSIS
.B seahaven
[-display display:number] [-speedup num]
.SH DESCRIPTION
.I seahaven
is an X implementation of a solitaire game sometimes known as 
.I Seahaven Towers,
which I originally saw as a shareware game for the Macintosh.  
.I seahaven
is a fairly blatent rip-off of that game.

.SH RULES FOR SEAHAVEN TOWERS

The game is played using an ordinary deck of cards.  The cards are all
face-up; you always know where all of the cards are.  At any time,
each card is in one of three kinds of stacks:

- Playing stacks.  There are ten of these, each initially having five
cards.

- Working stacks.  There are four of these, two of them initially
having a card in them.  Each working stack is allowed to contain at
most one card.

- Ace stacks.  There are four of these, one for each suit.  They are
initially empty.  Cards must be placed in these stacks in ascending
order, starting with the ace.  The object of the game is to get all
the cards in the ace stacks.

The rules are simple.  You may only move one card at a time; only a
card in a working stack or on the top of a playing stack may be moved.
A card may be moved to the top of a playing stack only if it is the
same suit that was on top there and the next lower card.  (In other
words, you may only place the seven of spades on top of the eight of
spades.)  A card may be moved to any empty working stack.  And a card
may be moved to an ace stack if it is an ace or if it is the next
higher card than the one that is already there.


.SH PLAYING SEAHAVEN

To move a card, just drag it with the left mouse button.  When you let
go, it will be placed on the stack that the card was moved closest to,
if such a move is legal.  If the move is not legal, the card will
spring back to its original location.

Since it is always to your advantage to move cards to the ace stacks
as soon as possible, cards will be automatically moved there for you.

There is also a convenient shortcut: you may move several cards at
once from one playing stack to another, providing that such a move
would be possible using available empty work stacks.

To help you locate cards, if you press the middle button on a card,
the next lower card of the same suit will be highlighted.  If you
press the right button, the next higher card will be highlighted.

Since there is no hidden information in the game, it's not quite
cheating to provide undo commands.  There are Undo and Redo buttons at
the bottom of the window; you may also use the U and R keys.  The
Restart button will restore you back to the original set-up.

If you give up, you can press the Autoplay button.  This counts as a
loss (unless you've already won the hand).  The computer will figure
out whether there's a solution.  If there is, you can review the
computer's solution by using the Restart and Redo buttons.

.SH SCORING

If you get all the cards into the ace piles, you will be scored a win.
If you press the New Game button or the Autoplay button without having
won, you will be scored a loss.  Your wins and losses will be
remembered across invocations of 
.I seahaven.

.SH COMMAND LINE OPTIONS

The -speedup flag changes the speed of the animation.  The higher the
number, the faster cards will move when automatically transferred to
the ace piles.  The default value is 6.

.SH BUGS

Needs an icon.

Does not look at any resources at all.  That is, your .Xdefaults file
will be ignored.

Lots of the code is horrid.

This man page is poorly written.


.SH COPYRIGHT


Copyright 1991 by Terry Weissman and Charles Haynes.

Permission to use, copy, modify, and distribute this software and its
documentation for any purpose and without fee is hereby granted,
provided that the above copyright notice appear in all copies and that
both that copyright notice and this permission notice appear in
supporting documentation, and that the names of	Terry Weissman or
Charles Haynes or their employers not be used in advertising or
publicity pertaining to distribution of the software without specific,
written prior permission.  Terry Weissman and Charles Haynes make no
representations about the suitability of this software for any
purpose.  It is provided "as is" without express or implied warranty.

TERRY WEISSMAN AND CHARLES HAYNES AND THEIR EMPLOYERS DISCLAIM ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL THEY BE
LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
SOFTWARE.

So there.


.SH AUTHORS


Terry Weissman
.br
Silicon Graphics, Incorporated
.br
weissman@sgi.com

Auto-play code by

Charles Haynes
.br
Western Software Laboratory
.br
Digital Equipment Corporation
.br
haynes@wsl.dec.com
\Rogue\Monster\
else
  echo "will not over write ./seahaven.6"
fi
if `test ! -s ./Imakefile`
then
echo "writing ./Imakefile"
cat > ./Imakefile << '\Rogue\Monster\'
OBJS = main.o util.o card.o cardbitmaps.o stack.o score.o auto.o 
SRCS = main.C util.C card.C cardbitmaps.C stack.C score.C auto.C 
LDLIBS = $(XLIB)

/* Make C++ work correctly.  This works for AT&T cfront; it will need to be */
/* tweaked to work with g++. */
CPLUSPLUS = CC
CC = $(CPLUSPLUS)		/* So that linking works right */
INCLUDES = -I/usr/include/CC	/* So that make depend works right */
.SUFFIXES: $(.SUFFIXES) .C
.C.o: $*.C
	$(CPLUSPLUS) -c $(CFLAGS) $*.C

ComplexProgramTarget(seahaven)
\Rogue\Monster\
else
  echo "will not over write ./Imakefile"
fi
if `test ! -s ./Makefile.noimake`
then
echo "writing ./Makefile.noimake"
cat > ./Makefile.noimake << '\Rogue\Monster\'
# This is a simple Makefile that should work if you can't get imake to work.

INCLUDES =
LIBX = -lX11
DEFINES =

CPLUSPLUS = CC

OBJS = main.o util.o card.o cardbitmaps.o stack.o score.o auto.o 

.SUFFIXES: $(.SUFFIXES) .C

.C.o: $*.C
	$(CPLUSPLUS) -c -g $(INCLUDES) $(DEFINES) $*.C


all: cleanup seahaven

cleanup:
	-rm -f *.c

seahaven: $(OBJS)
	rm -f seahaven
	$(CPLUSPLUS) -o seahaven $(OBJS) $(LIBX)
\Rogue\Monster\
else
  echo "will not over write ./Makefile.noimake"
fi
if `test ! -s ./auto.C`
then
echo "writing ./auto.C"
cat > ./auto.C << '\Rogue\Monster\'
#include <stream.h>


#include <strings.h>
#include <stdlib.h>

#include <sys/types.h>
#include <sys/time.h>

#include <fcntl.h>
//#include <osfcn.h>

//#include <stdlib.h>		// for getenv()


#include "auto.h"

#undef BATCH
#undef INTERACTIVE
#undef VERBOSE
#undef DYNAMIC
#undef AUTO

#define AUTO

#ifdef BATCH
int solved_count = 0;
int failed_count = 0;
int solved_moves = 0;
int solved_moves_max = 0;
int failed_moves = 0;
int failed_moves_max = 0;
int solved_time = 0;
int failed_time = 0;
#endif


struct timezone tz;
struct timeval start_time;
struct timeval end_time;

typedef int Boolean;

typedef enum Suit {
    SuitFirst, Hearts=SuitFirst, Diamonds, Clubs, Spades, SuitLast=Spades
    } Suit;

const int NumSuits = SuitLast+1;

typedef enum Rank {
    RankFirst, Ace=RankFirst, Two, Three, Four, Five, Six, Seven,
    Eight, Nine, Ten, Jack, Queen, King, RankLast=King
    } Rank;

const int NumRanks = RankLast+1;

void Error(char *s1 = "", char *s2 = "", char *s3 = "") {
    cerr << "\n" << s1 << s2 << s3 << "\n";
    exit(-1);
}



class Tableaux;

inline unsigned short Fold(long l) { return ((l>>16)^(l&0xffff)); }

class Hash {
    long l1;
    long l2;
    long l3;
  public:
    Hash(Tableaux&);
    Hash() { l1 = l2 = l3 = 0; }
    Hash(int i) { l1 = l2 = l3 = i; } // semi-private
    Boolean operator== (Hash& h) {
	return ((l1 == h.l1) && (l2 == h.l2) && (l3 == h.l3));
    }
    unsigned short Short()  { return Fold(l1)^Fold(l2)^Fold(l3); }
    Boolean EmptyP() { return (l1 == 0) && (l2 == 0) && (l3 == 0); }
    Boolean BadP() { return (l3 < 0); }
    void Print(ostream &os) { os << hex(l1, 8) << hex(l2, 8) << hex(l3,8); }
};

ostream& operator<< (ostream &os, Hash& h) { h.Print(os); return os; }

class HashList;

class HashTable {
    int size;
    int count;
    int hits;
    int maxDepth;
    HashList *ht;
  public:
    HashTable(int n);
    ~HashTable();
    int Count() { return count; }
    void Clear();
    Boolean CheckAndAdd(Hash& h);
/*
 {
	const unsigned short s = h.Short();
	Boolean ret = ht[s].InP(h);
	if (!ret) {
	    int depth = ht[s].Add(h);
	    if (depth > maxDepth) maxDepth = depth;
	    if (depth) hits++;
	    count++;
//	    if (count%1000 == 0) PrintStats();
#ifdef INTERACTIVE
	    if (count%1000 == 0) cerr << ".";
#endif
	}
	return ret;
    }
*/
    void Print(ostream &os);
    void PrintStats(ostream& os) {
#ifdef INTERACTIVE
	os << "\nHash table stats - "
	    << "size " << size
		<< " count " << count
		    << " hits " << hits
			<< " max depth " << maxDepth;
#endif
    }
};

class HashList {
    HashList *next;
    Hash hash;
    int depth;
    friend void HashTable::Clear();
  public:
    HashList(Hash& h, HashList* n) { hash = h; next = n; depth++; }
    HashList() { hash = Hash(); next = NULL; depth = 0; }
    ~HashList();
    Boolean InP(Hash& h);
    int Add(Hash &h); // returns depth
    Hash HashVal() { return hash; }
    int Depth() { return depth; }
    void Print(ostream& os);
    Boolean EmptyP() { return hash.EmptyP(); }
};

HashTable::HashTable(int n) {
    ht = new HashList[n]; size = n; count = maxDepth = hits = 0;
}

HashTable::~HashTable() {
    delete[size] ht;
}

HashList::~HashList() {
    if (next) delete next;

}

void HashList::Print(ostream &os) {
    if (EmptyP()) os << "NULL";
    else {
	char *sep = "";
	for (HashList* hlt = this; hlt != NULL; hlt = hlt->next) {
	    os << sep << hlt->hash;
	    sep = " ";
	}
    }
}

ostream& operator<< (ostream &os, HashList& hl) { hl.Print(os); return os; }

Boolean HashList::InP(Hash& h) {
    for (HashList *hlt = this; hlt != NULL; hlt = hlt->next) {
	if (hlt->HashVal() == h) return 1;
    }
    return 0;
}

int HashList::Add(Hash& h) {
    if (!this) Error("Add to NULL HashList.");
    if (HashVal().EmptyP()) hash = h;
    else next = new HashList(h, next);
    return depth++;
}

ostream& operator<< (ostream& os, HashTable& ht) { ht.Print(os); return os; }

void HashTable::Clear() {
    for (int i=0; i<size; i++) {
	if (ht[i].next) delete ht[i].next;
	ht[i] = HashList();
    }
    count = maxDepth = hits = 0;
}

Boolean HashTable::CheckAndAdd(Hash& h) {
    const unsigned short s = h.Short();
    Boolean ret = ht[s].InP(h);
    if (!ret) {
	int depth = ht[s].Add(h);
	if (depth > maxDepth) maxDepth = depth;
	if (depth) hits++;
	count++;
#ifdef INTERACTIVE
	if (count%1000 == 0) cerr << ".";
#endif
    }
    return ret;
}

void HashTable::Print(ostream &os) {
    HashList* hll = &ht[size];
    for (HashList* hlt = ht; hlt != hll; hlt++)
	if (! hlt->EmptyP())
	    os << "\n" << dec(hlt-ht,5) << ": " << *hlt;
}

static HashTable hashTable(65536);

// typedef unsigned char CardVal;
typedef int CardVal;

class Card;
class CardRange;

class Dest {
  public:
    void MoveTo(Card&);
    void MoveTo(CardRange&);
};

void Dest::MoveTo(Card&) { Error("Dest::MoveTo(Card&)"); }
void Dest::MoveTo(CardRange&) { Error("Dest::MoveTo(CardRange&)"); }

typedef void (Dest::* MoveCardToMemberFunction)(Card&);
typedef void (Dest::* MoveCardRangeToMemberFunction)(CardRange&);

class Card : public Dest {
    Suit thisSuit;
    Rank thisRank;
    Boolean empty;
  public:
    Card(Suit suit, Rank rank) {
	if ((suit < SuitFirst) || (suit > SuitLast))
	    Error("Bad suit ", dec(suit), " in Card constructor.");
	if ((rank < RankFirst) || (rank > RankLast))
	    Error("Bad rank ", dec(rank), " in Card constructor.");
	thisSuit = suit; thisRank = rank; empty = 0;
    }
    Card(char *st);
    Card() { // create an "empty" card
	thisSuit = SuitFirst; thisRank = RankFirst; empty = 1;
    } 
    Suit suit() const { return thisSuit; }
    Rank rank() const { return thisRank; }
    Card Next(int delta = 1) const { return Card(suit(), Rank(rank()+delta)); }
    Card Prev(int delta = 1) const { return Card(suit(), Rank(rank()-delta)); }
    Boolean NextP(Card c) const {
	return ((suit() == c.suit()) && rank()+1 == c.rank());
    }
    Boolean operator== (Card &c) {
	return (EmptyP()
		? c.EmptyP()
		: ((! c.EmptyP())
		   && (suit() == c.suit())
		   && (rank() == c.rank())));
    }
    Boolean operator!= (Card &c) { return (! operator==(c)); }
    Boolean EmptyP() const { return empty; }
    CardVal Value() const { return thisSuit*NumRanks+thisRank; }//semi-private.
    void Print(ostream& os) const;
};

static char *rankName = "A23456789TJQK";
static char *suitName = "HDCS";

void Card::Print(ostream &os) const {
    if (EmptyP()) os << "--";
    else os << chr(rankName[rank()]) << chr(suitName[suit()]);
}

Card::Card(char* st) {
    char *c;
    c = index(suitName, st[1]);
    if (! c) Error("Bad suit ", chr(st[1]), " in Card string constructor.");
    thisSuit = Suit(c-suitName);
    c = index(rankName, st[0]);
    if (! c) Error("Bad rank ", chr(st[0]), " in Card string constructor.");
    thisRank = Rank(c - rankName);
    empty = 0;
}

ostream& operator<< (ostream& os, Card c) { c.Print(os); return os; }

typedef Card Deck[NumSuits*NumRanks];

// typedef unsigned char Count;
typedef int Count;

class CardRange : public Dest {
    Card thisCard;
    Count thisCount;
    void Init(Card c, Count count) { thisCard = c; thisCount = count; }
  public:
    CardRange(Suit s, Rank rank1, Rank rank2) {
	if (rank2 < rank1) Error("rank2 < rank1 in CardRange constructor.");
	Init(Card(s, rank1), rank2-rank1+1);
    }
    CardRange(Suit s, Rank rank, Count count = 1) {
	Init(Card(s, rank), count);
    }
    CardRange(Card c, Count count = 1) { Init(c, count); }
    CardRange() { Init(Card(), 0); }
//    CardRange(char *st) { Init(Card(st), 1); }
    Suit suit() const { return thisCard.suit(); }
    Card First() const { return thisCard; }
    Card Last() const { return thisCard.Next(thisCount-1); }
    Card Next() const { return thisCard.Next(thisCount); }
    Count Count() const { return thisCount; }
    void Prepend(const CardRange& cr) {
	if (!cr.NextP(First())) Error("CardRange::Prepend, not next.");
	thisCount += cr.Count();
	thisCard = cr.First();
    }
    Boolean NextP(Card c) const {
	return ((suit() == c.suit()) && (First().rank()+Count() == c.rank()));
    }
    Boolean EmptyP() const { return thisCard.EmptyP(); }
    void Print(ostream& os) const;
};

void CardRange::Print(ostream &os) const {
    if (EmptyP()) os << "--   ";
    else {
	os << First();
	if (Count() == 1) os << "   ";
	else os << "-" << Last();
    }
}

ostream& operator<< (ostream& os, CardRange c) { c.Print(os); return os; }


#ifdef AUTO
SolutionLog solutionhead = NULL;

overload LogSolution();

static void LogOneSolution(Card card, int dest) {
    SolutionLog temp = new SolutionLogRec;
    temp->rank = card.rank();
    temp->suit = card.suit();
    temp->dest = dest;
    temp->next = solutionhead;
    solutionhead = temp;
}

static void LogBoundary() {
    SolutionLog temp = new SolutionLogRec;
    temp->rank = 0;
    temp->suit = 0;
    temp->dest = -999;
    temp->next = solutionhead;
    solutionhead = temp;
}    

static void LogSolution(Card card, int dest) {
    LogBoundary();
    LogOneSolution(card, dest);
}

static void LogSolution(CardRange range, int dest) {
    if (range.EmptyP()) return;
    LogBoundary();
    Suit suit = range.suit();
    Rank first = range.First().rank();
    Rank last = range.Last().rank();
    Rank i;
    if (dest != -99) {
	for (i=first ; i<last ; i++) LogOneSolution(Card(suit, i), dest);
    }
    LogOneSolution(Card(suit, last), dest);
    for (i=Rank(last-1) ; i>=first ; i--) LogOneSolution(Card(suit, i), -99);
}
#endif


const int NumCardRangesInPile = 5;

class Pile : public Dest {
    CardRange data[NumCardRangesInPile];
    int ti;
  public:
    Pile() { ti = 4; }
    CardRange& operator[] (int i) { return data[i]; }
    void Set(int i, const CardRange cr) { data[i] = cr; }
    CardRange& top() { return data[ti]; }
    int topIndex() { return ti; }
    CardRange& pop() { return data[ti--]; }
    Boolean EmptyP() { return (ti < 0); }
    void Test(CardRange& from);
    void MoveToPile(CardRange& from) {
	top().Prepend(from);
	from = CardRange();
    }
    void MoveToPile(Card& from) {
	top().Prepend(from);
	from = Card();
    }
    void MoveCardRangeToPile(CardRange& from);
    void MoveTo(CardRange& from);
    void MoveTo(Card& from);
};

void Pile::MoveTo(CardRange& from) { MoveToPile(from); }

void Pile::MoveCardRangeToPile(CardRange& from) { MoveToPile(from); }

void (Pile::* foo)(CardRange&) = &Pile::MoveCardRangeToPile;
//MoveCardRangeToMemberFunction foo = &Pile::MoveCardRangeToPile;

const int NumSpaces = 4;

class Spaces : public Dest {
    Card data[NumSpaces];
  public:
    Card& operator[] (int i) { return data[i]; }
    Card* FirstFree() {
	return (data[0].EmptyP() ? &data[0] :
		data[1].EmptyP() ? &data[1] :
		data[2].EmptyP() ? &data[2] :
		data[3].EmptyP() ? &data[3] :
		NULL);
    }
    void Spaces::MoveToSpace(CardRange& from) {
	// move all of the cards in range to spaces
	for (int i = 0 ; i < from.Count(); i++) {
	    *FirstFree() = from.First().Next(i);
	}
    from = CardRange();
    }
    void Spaces::MoveTo(CardRange& from);
    void Spaces::MoveTo(Card&);
};

void Spaces::MoveTo(CardRange& from) { MoveToSpace(from); }
void Spaces::MoveTo(Card&) { Error("Spaces::MoveTo(Card&)"); };

const int NumPiles = 10;

class Aces : public Dest {
    Card data[NumSuits];
  public:
    Card& operator[] (int i) { return data[i]; }
    void MoveToAce(Card& from) {
	data[from.suit()] = Card(from);
	from = Card();
    }
    void MoveToAce(CardRange& from) {
	data[from.suit()] = from.Last();
	from = CardRange();
    }
    void MoveTo(Card& from) { MoveToAce(from); }
    void MoveTo(CardRange& from) { MoveToAce(from); }
};

class Kings : public Dest {
    Card data[NumSuits];
  public:
    Card& operator[] (int i) { return data[i]; }
    void MoveToKing(Card& from) {
	data[from.suit()] = Card(from);
	from = Card();
    }
    void MoveToKing(CardRange& from) {
	data[from.suit()] = from.First();
	from = CardRange();
    }
    void MoveTo(Card& from) { MoveToKing(from); }
    void MoveTo(CardRange& from) { MoveToKing(from); }
};

class Tableaux {
  private:
    Hash hashVal;
    void CanonicalForm();
  public:
    Pile piles[NumPiles];
    Spaces spaces;
    Aces aces;
    Kings kings;
    Boolean Solve();
    Hash HashVal() { return hashVal; }
    Boolean WonP();
    void Print(ostream& os);
    void Dump();
    void Read(istream& is);
    Tableaux(Card *deck);
    Tableaux(char *st);
    Tableaux(istream& is) { Read(is); }
    Boolean CheckAces(Card &c) {
	return (((c.rank() == Ace)
		|| (c.rank() == aces[c.suit()].Next().rank()))
		? FillAces()
		: 0);
    }
    Boolean FillAces(); // move all cards to aces that can be
};

class Move {
  protected:
    Dest& dest;
  public:
    Move(Dest& d) : dest(d) { }
    virtual void DoIt() { Error("Move::DoIt()"); };
};

class MoveCard : public Move {
    Card& from;
    MoveCardToMemberFunction mcmf;
  public:
    MoveCard(Card& f, Dest& d, MoveCardToMemberFunction mf)
	: (d), from(f), mcmf(mf) { }
    void MoveCard::DoIt() { dest.MoveTo(from); }
//    void MoveCard::DoIt() { dest.*mcmf(from); }
};

class MoveCardRange : public Move {
    CardRange& from;
  public:
    MoveCardRange(CardRange& f, Dest& d) : (d), from(f) { }
    void DoIt() {dest.MoveTo(from);} 
};

ostream& operator<< (ostream& os, Tableaux &t) { t.Print(os); return os; }
istream& operator>> (istream& is, Tableaux &t) { t.Read(is); return is; }

void Tableaux::Dump() { Print(cout); }

Hash::Hash(Tableaux& t) {

    // there are 5 possible tops, and 13 possible values for the number of
    // cards in the top "group". These two values uniquely determine what's in
    // that pile. Each pile starts with five groups, that number never grows.
    // You can only move a group by either removing it (to a space or an ace)
    // or moving it to the end of another group (which merely lengthens that
    // group). So the number of groups in a pile is monotone non-increasing.
    // An implication of this is that the *suit* of each position in the pile
    // stays fixed, and so is derivable from the initial position (i.e.  adds
    // no information) thus we can ignore the suit in computing the hash. This
    // means that there are 66 possible hash values for each pile. 5*13
    // possible top+count values + empty.
    // 66^5 < 2^32 so we can store the hash for 10 piles in two 32 bit numbers
    // We also need to store information about the kings. I'm lazy so I just
    // store it in one long.

    // Note: Hash 0 would imply all empty piles, which will never happen!

    l3 = (((((t.kings[0].Value() << 8)
	     + t.kings[1].Value()) << 8)
	   + t.kings[2].Value()) << 8)
	+ t.kings[3].Value();

    l1 = 0;
    Pile *p;
    for (p=t.piles; p < t.piles+5; p++) {
	const int count = p->top().Count();
	if (count >= 6) {
	    const Suit s = p->top().suit();
	    const Rank r = p->top().First().rank();
	    for (CardRange* cr = &(p->top()); cr >= &((*p)[0]); cr--) {
		if (cr->suit() != s) continue;
		if (cr->First().rank() < r) {
		    l1 = l2 = l3 = -1;
		    return;
		}
	    }
	}
	l1 = l1*66 + (p->EmptyP() ? 0 : (p->topIndex()*13+count-1)+1);
    }

    l2 = 0;
    for (p = t.piles+5 ; p < t.piles+NumPiles; p++) {
	const int count = p->top().Count();
	if (count >= 6) {
	    const Suit s = p->top().suit();
	    const Rank r = p->top().First().rank();
	    for (CardRange* cr = &(p->top()); cr >= &((*p)[0]); cr--) {
		if (cr->suit() != s) continue;
		if (cr->First().rank() < r) {
		    l1 = l2 = l3 = -1;
		    return;
		}
	    }
	}
	l2 = l2*66 + (p->EmptyP() ? 0 : (p->topIndex()*13+count-1)+1);
    }

    if ((l1==0)&&(l2==0)&&(l3==0))
	Error("Tableaux hashes to NULL.");
}

Boolean Tableaux::WonP() {
    return ((aces[0].rank() == King)
	    && (aces[1].rank() == King)
	    && (aces[2].rank() == King)
	    && (aces[3].rank() == King));
}

Boolean Tableaux::FillAces() {
    int found;
    do {
	found = 0;
	int dests[NumSuits];

	int numFull = 0;
	for (Suit s = SuitFirst; s <= SuitLast; s++) {
	    if (aces[s].EmptyP()) dests[s] = Ace;
	    else {
		numFull += (aces[s].rank() == King);
		dests[s] = aces[s].rank()+1;
	    }
	}
	if (numFull == NumSuits) return 1;

	// check piles
	for (Pile* p = piles; p < piles+NumPiles; p++) {
	    if (p->EmptyP()) continue;
	    Card& first = p->top().First();
	    if (dests[first.suit()] == first.rank()) {
		aces.MoveToAce(p->pop());
		found = 1;
#ifdef VERBOSE
		cerr << "+";
#endif
	    }
	}
	
	// check spaces
	for (Card *c = &spaces[0]; c < &spaces[NumSpaces]; c++) {
	    if (c->EmptyP()) continue;
	    if (dests[c->suit()] == c->rank()) {
		aces.MoveToAce(*c);
		found = 1;
#ifdef VERBOSE
		cerr << "+";
#endif
	    }
	}
	
	// check kings
	for (Suit s1 = SuitFirst; s1 <= SuitLast; s1++) {
	    if (kings[s1].EmptyP()) continue;
	    if (dests[s1] == kings[s1].rank()) {
		Card tempKing = Card(s1, King);
		aces.MoveToAce(tempKing);
		kings[s1] = Card();
		found = 1;
#ifdef VERBOSE
		cerr << "!";
#endif
	    }
	}
    } while (found);
    return 0;
}

Boolean Tableaux::Solve() {

    hashVal = Hash(*this);

    if (hashTable.CheckAndAdd(HashVal())) return 0;

    Tableaux savedTab = *this;

#ifdef VERBOSE
    cerr << ".";
#endif
#ifdef DYNAMIC
    cout << "[;H" << *this;
#endif

    // pre-calculate useful bits of info - how many empty spaces are there
    // how many empty piles there are

    int empty_space_count = 0;
    Card *c;
    for (c = &spaces[0]; c < &spaces[NumSpaces]; c++)
	empty_space_count += c->EmptyP();

    int empty_pile_count = 0;
    Pile* p;
    for (p = piles; p < piles+NumPiles; p++) empty_pile_count += p->EmptyP();

    Suit s;
    for (s = SuitFirst; s <= SuitLast; s++)
	empty_pile_count -= !kings[s].EmptyP();

    // generate legal moves, check each one
    // all legal moves are either
    // 1) an entire top sequence onto the top of a pile (shortcut 3+2)
    // 2) a card from a space onto the top of a pile
    // 3) an entire top sequence onto a space or spaces
    // 4) an entire king based sequence onto an empty pile (shortcut 3+5+2)
    // 5) a king from a space onto an empty pile

    // another way of looking at it is that all moves are onto one of
    // tops of piles
    // empty piles (kings)
    // spaces

    int destTable[NumSuits*NumRanks];

    memset((char*)destTable,0,sizeof(destTable));

    // enumerate destination piles
    for (p = piles; p < piles+NumPiles; p++) {
	if (p->EmptyP()) continue;
	destTable[p->top().First().Prev().Value()] = (p-piles)+1;
    }

    // enumerate destination kings
    for (s = SuitFirst; s <= SuitLast; s++) {
	if (kings[s].EmptyP()) {
	    if (empty_pile_count) destTable[Card(s,King).Value()] = s+11;
	} else {
	    destTable[kings[s].Prev().Value()] = s+11;
	}
    }

    Move* movesMakingEmptyPiles[10];
    int movesMakingEmptyPilesCount = 0;

    Move* movesMakingUnmovableRange[10];
    int movesMakingUnMovableRange= 0;

    // look for moves from spaces
    for (Card *from = &spaces[0]; from < &spaces[NumSpaces]; from++) {
	if (from->EmptyP()) continue;
	const int dest = destTable[from->Value()];
	if (!dest) continue;
	if (dest < 11) piles[dest-1].MoveToPile(*from);
	else kings.MoveToKing(*from);
	if (Solve()) {
	    *this = savedTab;
#ifdef AUTO
	    LogSolution(*from, dest);
#endif
#ifdef INTERACTIVE
	    cout << "\n" << *from << "(S) -> ";
	    if (dest < 11) cout << piles[dest-1].top();
	    else cout << "(K)" << kings[dest-11];
#endif
	    return 1;
	} else {
	    *this = savedTab;
	    if (dest < 11) return 0; // It never costs to move something from
				     // a space to a pile (unless it's a king)
	}
    }

    // look for moves from piles
    for (p = piles; p < piles+NumPiles; p++) {
	if (p->EmptyP()) continue;
	CardRange& from = p->top();
	if (from.Count()-1 > empty_space_count) continue;
	int const dest = destTable[from.Last().Value()];
	if (!dest) continue;
	// legal move!
/*
	if (p->topIndex() == 0) {
	    if (dest < 11) {
		movesMakingEmptyPiles[movesMakingEmptyPilesCount++] =
		    new Move();
	    } else {
	    }
	} else {
*/
	    if (dest < 11) {
		piles[dest-1].MoveToPile(p->pop());
		// did we uncover a king?
		if ((p->topIndex() == 0) && (p->top().Last().rank() == King))
		    kings.MoveToKing(p->pop());
	    } else {
		kings.MoveToKing(p->pop());
	    }
/*
	}
*/
	if ((!p->EmptyP()) && CheckAces(p->top().First()) || Solve()) {
#ifdef AUTO
	    *this = savedTab;
	    LogSolution(from, dest);
#endif
#ifdef INTERACTIVE
	    *this = savedTab;
	    cout << "\n" << from << " -> ";
	    if (dest < 11) cout << piles[dest-1].top();
	    else cout << "(K)" << kings[dest-11];
#endif
	    return 1;
	}
	*this = savedTab;
    }

    // look for moves into spaces
    for (p = piles; p < piles+NumPiles; p++) {
	if (p->EmptyP()) continue;
	CardRange& tp = p->top();
	if (tp.Count() > empty_space_count) continue;
	spaces.MoveToSpace(p->pop());
	if (((!p->EmptyP()) && CheckAces(p->top().First())) || Solve()) {
#ifdef AUTO
	    *this = savedTab;
	    LogSolution(tp, -99);
#endif 
#ifdef INTERACTIVE
	    *this = savedTab;
	    cout << "\n" << tp << " -> space";
#endif
	    return 1;
	}
	*this = savedTab;
    }

    return 0;
};

void Tableaux::CanonicalForm() {
    // find all sequences and compress them
    for (int i = 0; i < NumPiles; i++) {
	Pile& p = piles[i];
	int k = 0;
	for (int j = 1; j < NumCardRangesInPile; j++)
	    if (p[j].NextP(p[k].Last())) p[k].Prepend(p[j].First());
	    else p.Set(++k, p[j]);

	for (k++; k < NumCardRangesInPile; k++) {
	    p.Set(k,CardRange());
	    p.pop();
	}
    }

/*
    for (Pile* p = piles; p < piles+NumPiles; p++) {
	CardRange *nextPos = &(*p)[0];
	for (CardRange *c = &(*p)[1]; c < &(*p)[NumCardRangesInPile]; c++) {
	    if (c->NextP(nextPos->Last())) nextPos->Prepend(*c);
	    else *(++nextPos) = *c;
	}
	for (nextPos++; nextPos < &(*p)[NumCardRangesInPile]; nextPos++)
	    *nextPos = CardRange();
    }
*/

    FillAces();
}

Tableaux::Tableaux(char* st) {
    static int deck[NumSuits][NumRanks];

    Card c;

    c = st; deck[c.suit()][c.rank()]++;
    spaces[1] = c;
    st += 2;
    c = st; deck[c.suit()][c.rank()]++;
    spaces[2] = c;
    st += 2;


    for (Pile* p = piles; p < piles+NumPiles; p++)
	for (CardRange* cr = &(*p)[0]; cr < &(*p)[NumCardRangesInPile]; cr++) {
	    c = st; deck[c.suit()][c.rank()]++;
	    *cr = c;
	    st += 2;
	}

    for (Suit s = SuitFirst; s <= SuitLast; s++)
	for (Rank r = RankFirst; r <= RankLast; r++)
	    if (deck[s][r] != 1) {
		cerr << "\n" << Card(s, r) << " count is " << deck[s][r];
		Error();
	    }

    CanonicalForm();

}

void Tableaux::Print(ostream& os) {
    os << "\n";
    os << aces[0]   << "    " << aces[1]   << "          "
       << spaces[0] << "    " << spaces[1] << "    "
       << spaces[2] << "    " << spaces[3] << "          "
       << aces[2]   << "    " << aces[3];
    os << "\n";
    for (int j = 0; j < NumCardRangesInPile; j++) {
	char *sep = "\n";
	for (int i = 0; i < NumPiles; i++) {
	    os << sep << piles[i][j];
	    sep = " ";
	}
    }
    os << "\n";
    os << kings[0] << " " << kings[1] << " " << kings[2] << " " << kings[3];
    os << "\n";
}

void Tableaux::Read(istream& is) {
    int won, lost, streak, longest_won, longest_lost, dummy;
    is >> won >> lost >> streak >> longest_won >> longest_lost >> dummy;
    for (int i = 0; i < NumCardRangesInPile; i++)
	for (int j = 0; j < NumPiles; j++) {
	    int s, r;
	    is >> s >> r;
	    piles[j].Set(i, CardRange(Card(Suit(s),Rank(r))));
	}
    int s, r;
    is >> s >> r;
    spaces[1] = Card(Suit(s),Rank(r));
    is >> s >> r;
    spaces[2] = Card(Suit(s),Rank(r));

    CanonicalForm();

}

Tableaux::Tableaux(Card *deck) {
    for (int i = 0; i < NumCardRangesInPile; i++)
	for (int j = 0; j < NumPiles; j++) {
	    piles[j].Set(i, CardRange(*(deck++)));
	}
    spaces[1] = *(deck++);
    spaces[2] = *(deck++);

    CanonicalForm();

}


#ifdef AUTO
int AutoPlay() {

    char savefile[500];

    sprintf(savefile, "%s/.seahavensave", getenv("HOME"));

    istream in = istream(open(savefile, O_RDONLY));
    Tableaux t(in);

    solutionhead = NULL;
    int result = t.Solve();

    hashTable.Clear();
    return result;
}
#endif
\Rogue\Monster\
else
  echo "will not over write ./auto.C"
fi
echo "Finished archive 1 of 2"
exit





--
Dan Heller
------------------------------------------------
O'Reilly && Associates 		      Zyrcom Inc
Senior Writer			       President
argv@ora.com			argv@zipcode.com