xenocara/app/xlockmore/modes/solitare.cc
2006-11-26 11:07:42 +00:00

1411 lines
34 KiB
C++

/* -*- Mode: C; tab-width: 4 -*- */
/* solitare --- Shows a pointless game, used in the Mandarin Candidate */
#if !defined( lint ) && !defined( SABER )
static const char sccsid[] = "@(#)solitare.cc 5.00 2000/11/01 xlockmore";
#endif
/* Copyright (c) D. Bagley, 1999. */
/*
* 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.
*
* This file is provided AS IS with no warranties of any kind. The author
* shall have no liability with respect to the infringement of copyrights,
* trade secrets or any patents by this file or any part thereof. In no
* event will the author be liable for any lost revenue or profits or
* other special, indirect and consequential damages.
*
* This module is based on Eric Lassauge's text3d and Timothy Budd's
* C++ program from 1990 contained in the book "An Introduction to
* Object Oriented Programming" Addison-Wesley Publishing 1991.
*
* Albert H. Morehead and Geoffrey Mott-Smith, The Complete Book of
* Solitaire and Patience Games, Grosset & Dunlap, New York, 1949.
*
* After I started this I found out about some nice related sites:
* http://www.delorite.com/store/ace
* http://wildsav.idv.uni-linz.ac.at/mfx/psol-cardsets/index.html
*
* Revision History:
* 01-Nov-2000: Allocation checks
* 17-Jan-2000: autoplay, trackmouse, resizing, release
* 10-Dec-1999: wanted to do some C++, may end up using GL but not for now.
*
*/
#ifdef STANDALONE
#define MODE_solitare
#define PROGCLASS "Solitare"
#define HACK_INIT init_solitare
#define HACK_DRAW draw_solitare
#define solitare_opts xlockmore_opts
#define DEFAULTS "*delay: 2000000 \n" \
"*ncolors: 64 \n" \
"*mouse: False \n"
extern "C"
{
#include "xlockmore.h" /* from the xscreensaver distribution */
}
#else /* !STANDALONE */
#include "xlock.h" /* in xlockmore distribution */
#endif /* !STANDALONE */
#ifdef MODE_solitare
/* Methods overridden in subclasses of class cardPile
Suit Alternate Deck Discard Deal
initialize X X
cleanup X
addCard X X X
display X
select X X X
canTake X X
*/
#define DEF_TRACKMOUSE "False"
static Bool trackmouse;
#define MAXCOLORS 2
#define MAXSUITS 4
#define MAXRANK 13
#define MAXALTERNATEPILES 7
#define MAXPILES (MAXALTERNATEPILES+MAXSUITS+2)
#define OVERLAP (0.25)
enum Colors {black, red};
enum Suits {spade, diamond, club, heart};
#ifdef FR
// Ace => As, Jack => Valet,
// Queen = Reine (Dame), King = Roi, Joker = Joker
static const char *RankNames[] = {"", "A",
"2", "3", "4", "5", "6", "7", "8", "9", "10",
"V", "D", "R"};
#else
#ifdef NL
// Ace => Aas, Jack => Boer (Farmer),
// Queen = Vrouw (Lady), King = Heer (Gentleman), Joker = Joker
static const char *RankNames[] = {"", "A",
"2", "3", "4", "5", "6", "7", "8", "9", "10",
"B", "V", "H"};
#else
#ifdef RU
// Some may be approximations to Cyrillic
static const char *RankNames[] = {"", "T",
"2", "3", "4", "5", "6", "7", "8", "9", "10",
"B", "Q", "K"};
#else
static const char *RankNames[] = {"", "A",
"2", "3", "4", "5", "6", "7", "8", "9", "10",
"J", "Q", "K"};
#endif
#endif
#endif
static inline int int_round(double x) { return int(x + 0.5); }
#ifdef DOFONT
// Does this really add anything?
extern XFontStruct *getFont(Display * display);
static XFontStruct *mode_font = None;
static int char_width[256];
static int
font_width(XFontStruct * font, char ch)
{
int dummy;
XCharStruct xcs;
(void) XTextExtents(font, &ch, 1, &dummy, &dummy, &dummy, &xcs);
return xcs.width;
}
#endif
// I am probably not thinking object oriented here...
void
drawSuit(ModeInfo * mi, Suits suit, int x, int y, int width, int height)
{
Display *display = MI_DISPLAY(mi);
Window window = MI_WINDOW(mi);
GC gc = MI_GC(mi);
XPoint polygon[4];
switch (suit) {
case spade:
polygon[0].x = x + width / 2;
polygon[0].y = y;
polygon[1].x = short(float(-width) / 3.0);
polygon[1].y = height / 3;
polygon[2].x = short(float(width) / 3.0);
polygon[2].y = height / 3;
polygon[3].x = short(float(width) / 3.0) + 1;
polygon[3].y = -height / 3;
XFillPolygon(display, window, gc,
polygon, 4, Convex, CoordModePrevious);
XFillArc(display, window, gc,
x, y + height / 3, width / 2, height / 2, 0, 23040);
XFillArc(display, window, gc,
x + width / 2, y + height / 3, width / 2, height / 2, 0, 23040);
polygon[0].x = x + width / 2;
polygon[0].y = y + height / 2;
polygon[1].x = -width / 8;
polygon[1].y = height / 2;
polygon[2].x = width / 4;
polygon[2].y = 0;
XFillPolygon(display, window, gc,
polygon, 3, Convex, CoordModePrevious);
break;
case diamond:
polygon[0].x = x + width / 2;
polygon[0].y = y;
polygon[1].x = -width / 2;
polygon[1].y = height / 2;
polygon[2].x = width / 2;
polygon[2].y = height / 2;
polygon[3].x = width / 2;
polygon[3].y = -height / 2;
XFillPolygon(display, window, gc,
polygon, 4, Convex, CoordModePrevious);
break;
case club:
XFillArc(display, window, gc,
x, y + short(float(height) / 3.6), width / 2, height / 2, 0, 23040);
XFillArc(display, window, gc,
x + width / 2, y + short(float(height) / 3.6), width / 2, height / 2, 0, 23040);
XFillArc(display, window, gc,
x + width / 4, y, width / 2, height / 2, 0, 23040);
polygon[0].x = x + width / 2;
polygon[0].y = y + height / 2 - 1;
polygon[1].x = -width / 8;
polygon[1].y = height / 2 + 1;
polygon[2].x = width / 4;
polygon[2].y = 0;
XFillPolygon(display, window, gc,
polygon, 3, Convex, CoordModePrevious);
break;
case heart:
XFillArc(display, window, gc,
x + 1, y, width / 2, height / 2, 0, 23040);
XFillArc(display, window, gc,
x + width / 2 - 1, y, width / 2, height / 2, 0, 23040);
polygon[0].x = x + width / 2;
polygon[0].y = y + short(float(height) / 4.1);
polygon[1].x = short(float(-width) / 2.7);
polygon[1].y = short(float(height) / 4.1);
polygon[2].x = short(float(width) / 2.7);
polygon[2].y = short(float(height) / 2.05);
polygon[3].x = short(float(width) / 2.7);
polygon[3].y = short(float(-height) / 2.05);
XFillPolygon(display, window, gc,
polygon, 4, Convex, CoordModePrevious);
break;
}
}
//card.h
class Card
{
private:
Suits suit;
int rank;
public:
Card(Suits suit, int rank);
Suits whichSuit();
int whichRank();
Colors whichColor();
};
inline Card::Card(Suits a_suit, int a_rank) { this->suit = a_suit; this->rank = a_rank;}
inline Suits Card::whichSuit() { return this->suit;}
inline int Card::whichRank() { return this->rank;}
inline Colors Card::whichColor() { return ((whichSuit() % MAXCOLORS) ? red : black);}
//cardview.h
class CardView
{
private:
ModeInfo *mi;
Card * card;
Bool faceUp;
int locationX, locationY;
public:
CardView(ModeInfo * mi, Card * card);
CardView(ModeInfo *mi, Suits suit, int rank);
Card * thisCard();
void draw();
void erase();
Bool isFaceUp();
void flip();
Bool includes(int, int); // mouse interaction
int x();
int y();
int width();
int height();
void moveTo(int, int);
};
inline Card* CardView::thisCard() { return card; }
inline Bool CardView::isFaceUp() { return faceUp; }
inline void CardView::flip() { faceUp = !faceUp; }
inline int CardView::x() { return locationX; }
inline int CardView::y() { return locationY; }
inline void CardView::moveTo(int lx, int ly) { locationX = lx; locationY = ly; }
//cardview.cc
CardView::CardView(ModeInfo * a_mi, Card * a_card)
{
this->mi = a_mi;
this->card = a_card;
faceUp = False;
locationX = locationY = 0;
}
CardView::CardView(ModeInfo * a_mi, Suits suit, int rank)
{
this->mi = a_mi;
if ((this->card = new Card(suit, rank)) == NULL) {
return;
}
faceUp = False;
locationX = locationY = 0;
}
void CardView::draw()
{
Display * display = MI_DISPLAY(mi);
Window window = MI_WINDOW(mi);
GC gc = MI_GC(mi);
unsigned long red_pixel = (MI_NPIXELS(mi) > 2) ? MI_PIXEL(mi,0) : MI_BLACK_PIXEL(mi);
unsigned long cyan_pixel = (MI_NPIXELS(mi) > 2) ? MI_PIXEL(mi,MI_NPIXELS(mi) / 2) : MI_WHITE_PIXEL(mi);
if (isFaceUp()) {
XSetForeground(display, gc, MI_WHITE_PIXEL(mi));
XFillRectangle(display, window, gc, x(), y(), width(), height());
if (card->whichColor() == red) {
XSetForeground(display, gc, red_pixel);
} else {
XSetForeground(display, gc, MI_BLACK_PIXEL(mi));
}
if (width() > 16 && height() > 34) {
#ifdef CENTERLABEL
XDrawString(display, window, gc,
x() + width() / 2 - strlen(RankNames[card->whichRank()]) * 8,
y() + int_round(height() * 0.40),
RankNames[card->whichRank()], strlen(RankNames[card->whichRank()]));
#else
XDrawString(display, window, gc,
x() + 8 - strlen(RankNames[card->whichRank()]) * 8 + width() / 8,
y() + 20,
RankNames[card->whichRank()], strlen(RankNames[card->whichRank()]));
#endif
}
#ifdef SUITNAMES
char *SuitNames[] = {"Spade", "Diamond", "Club", "Heart"};
XDrawString(display, window, gc, x() + 0.2, y() + int_round(height() * 0.8),
SuitNames[card->whichSuit()], strlen(SuitNames[card->whichSuit()]));
#endif
#ifdef CENTERLABEL
drawSuit(mi, card->whichSuit(),
x() + int_round(width() * 0.35), y() + int_round(height() * 0.55),
int_round(0.3 * width()), int_round(0.3 * height()));
#else
if (width() > 16 && height() > 34) {
drawSuit(mi, card->whichSuit(),
x() + 5, y() + 25,
int_round(0.3 * width()), int_round(0.3 * height()));
} else {
drawSuit(mi, card->whichSuit(),
x() + 2, y() + 2,
int_round(0.3 * width()), int_round(0.3 * height()));
}
#endif
XSetForeground(display, gc, MI_BLACK_PIXEL(mi));
XDrawLine(display, window, gc,
x() + 1, y() + 1, x() + width() - 3, y() + 1);
XDrawLine(display, window, gc,
x() + 1, y() + height() - 2, x() + width() - 3, y() + height() - 2);
XDrawLine(display, window, gc,
x() + 1, y() + 1, x() + 1, y() + height() - 3);
XDrawLine(display, window, gc,
x() + width() - 2, y() + 1, x() + width() - 2, y() + height() - 3);
} else {
int n;
XSetForeground(display, gc, cyan_pixel);
XFillRectangle(display, window, gc,
x(), y(), width(), height());
XSetForeground(display, gc, MI_BLACK_PIXEL(mi));
n = x() + int_round(int_round(width() * 0.3));
XDrawLine(display, window, gc, n, y() + int_round(height() * 0.1),
n, y() + int_round(height() * 0.9));
n = x() + int_round(width() * 0.7);
XDrawLine(display, window, gc, n, y() + int_round(height() * 0.1),
n, y() + int_round(height() * 0.9));
n = y() + int_round(height() * 0.3);
XDrawLine(display, window, gc, x() + int_round(height() * 0.1), n,
x() + int_round(width() * 0.9), n);
n = y() + int_round(height() * 0.7);
XDrawLine(display, window, gc, x() + int_round(width() * 0.1), n,
x() + int(width() * 0.9), n);
}
// Shadow
#if 1
XDrawLine(display, window, gc,
x() - 1, y() - 1 , x() + width(), y() - 1);
XDrawLine(display, window, gc,
x() - 1, y() - 1, x() - 1, y() + height());
#else
XDrawLine(display, window, gc,
x() + 1, y() + height(), x() + width(), y() + height());
XDrawLine(display, window, gc,
x() + width(), y() + 1, x() + width(), y() + height());
#endif
}
void CardView::erase()
{
Display * display = MI_DISPLAY(mi);
Window window = MI_WINDOW(mi);
GC gc = MI_GC(mi);
XSetForeground(display, gc, MI_BLACK_PIXEL(mi));
XFillRectangle(display, window, gc,
x(), y(), width(), height());
}
//pile.h
Bool CardView::includes(int a, int b)
{
return (a >= x() && (a <= x() + width()) &&
b >= y() && (b <= y() + height()));
}
class CardLink : public CardView
{
public:
CardLink(ModeInfo * mi, Suits suit, int rank);
CardLink(ModeInfo * mi, Card * card);
CardLink * nextCard();
void setLink(CardLink * card);
private:
CardLink * link;
};
inline CardLink::CardLink(ModeInfo *a_mi, Suits suit, int rank) : CardView(a_mi, suit, rank) { ; }
inline CardLink::CardLink(ModeInfo *a_mi, Card * a_card) : CardView(a_mi, a_card) { ; }
inline CardLink * CardLink::nextCard() { return link; }
inline void CardLink::setLink(CardLink * a_card) { link = a_card; }
class CardPile
{
public:
CardPile(ModeInfo *);
virtual void updateLocation(ModeInfo *);
virtual void addCard(CardLink *);
virtual Bool canTake(Card *);
Bool contains(int, int); // mouse interaction
virtual void displayPile();
virtual Bool initialize();
virtual void cleanup();
CardLink * removeCard();
virtual Bool select(int, int); // mouse interaction
virtual Bool select(Bool); // generator
protected:
int x, y;
ModeInfo * mi;
CardLink *top;
};
#define nilLink (CardLink *) 0
inline CardPile::CardPile(ModeInfo * a_mi) { this->mi = a_mi; top = nilLink; }
class DealPile : public CardPile
{
public:
DealPile(ModeInfo *);
virtual void cleanup();
Bool shuffle(CardPile *);
};
inline DealPile::DealPile(ModeInfo * a_mi) : CardPile(a_mi) { ; }
class SuitPile : public CardPile
{
public:
SuitPile(ModeInfo * a_mi, int s) : CardPile(a_mi) { suit = s; }
virtual void updateLocation(ModeInfo *);
virtual Bool canTake(Card *);
private:
int suit;
};
class AlternatePile : public CardPile
{
public:
AlternatePile(ModeInfo * a_mi, int p) : CardPile(a_mi) { pile = p; }
virtual void updateLocation(ModeInfo *);
virtual void addCard(CardLink *);
virtual Bool canTake(Card *);
void copyBuild(CardLink *, CardPile *);
virtual void displayPile();
virtual Bool select(int, int);
virtual Bool select(Bool);
virtual Bool initialize();
private:
int pile;
};
class DeckPile : public CardPile
{
public:
DeckPile(ModeInfo * a_mi) : CardPile(a_mi) { ; }
virtual void updateLocation(ModeInfo *);
virtual void addCard(CardLink *);
virtual Bool initialize();
virtual Bool select(int, int);
virtual Bool select(Bool);
};
class DiscardPile : public CardPile
{
public:
DiscardPile(ModeInfo * a_mi) : CardPile(a_mi) { ; }
virtual void updateLocation(ModeInfo *);
virtual void addCard(CardLink *);
virtual Bool select(int, int);
virtual Bool select(Bool);
};
//game.h
class GameTable
{
public:
GameTable(ModeInfo *);
~GameTable();
Bool newGame(ModeInfo *);
Bool suitCanAdd(Card *);
CardPile * suitAddPile(Card *);
Bool alternateCanAdd(Card *);
CardPile * alternateAddPile(Card *);
void Resize(ModeInfo *);
void Redraw(ModeInfo *);
Bool HandleMouse(ModeInfo *);
Bool HandleGenerate();
DealPile *dealPile;
CardPile *deckPile;
CardPile *discardPile;
protected:
CardPile * allPiles[MAXALTERNATEPILES + MAXSUITS + 2];
CardPile * suitPiles[MAXSUITS];
CardPile * alternatePiles[MAXALTERNATEPILES];
};
// pile.cc
typedef struct {
Bool painted;
GameTable * game;
int width, height, cardwidth, cardheight;
int showend;
#ifdef DOFONT
int fontascent, fontheight;
#endif
} solitarestruct;
static solitarestruct *solitare = (solitarestruct *) NULL;
int CardView::width()
{
solitarestruct *bp = &solitare[MI_SCREEN(mi)];
return bp->cardwidth;
}
int CardView::height()
{
solitarestruct *bp = &solitare[MI_SCREEN(mi)];
return bp->cardheight;
}
void CardPile::addCard(CardLink * card)
{
if (card != nilLink) {
card->setLink(top);
top = card;
top->moveTo(x, y);
}
}
void CardPile::updateLocation(ModeInfo * a_mi)
{
;
}
Bool CardPile::canTake(Card * card)
{
return False; // from virtual, card unused
}
Bool CardPile::contains(int a, int b)
{
for (CardLink *p = top; p!= nilLink; p = p->nextCard())
if (p->includes(a, b))
return True;
return False;
}
void CardPile::displayPile()
{
solitarestruct *bp = &solitare[MI_SCREEN(mi)];
Display * display = MI_DISPLAY(mi);
Window window = MI_WINDOW(mi);
GC gc = MI_GC(mi);
if (top == nilLink) {
XSetForeground(display, gc, MI_BLACK_PIXEL(mi));
XFillRectangle(display, window, gc,
x, y, bp->cardwidth, bp->cardheight);
} else {
top->draw();
}
}
Bool CardPile::initialize()
{
top = nilLink;
return True;
}
void CardPile::cleanup()
{
CardLink *p;
while (top != nilLink) {
p = top;
top = top->nextCard();
delete p;
}
}
CardLink * CardPile::removeCard()
{
CardLink *p;
if (top == nilLink)
return nilLink;
p = top;
top = top->nextCard();
return p;
}
Bool CardPile::select(int a_x, int a_y)
{
// from virtual, a_x & a_y unused
return select(True);
}
Bool CardPile::select(Bool first)
{
// from virtual, first unused
return False;
}
void DealPile::cleanup()
{
CardLink *p;
while (top != nilLink) {
p = top;
top = top->nextCard();
delete p->thisCard();
delete p;
}
}
Bool DealPile::shuffle(CardPile * pile)
{
CardLink *p, *q;
int max, limit, i;
// first see how many cards we have
for (max = 0, p = top; p != nilLink; p = p->nextCard()) {
max++;
if (p->isFaceUp())
p->flip();
}
// then pull them out, randomly, one at a time
for (; max > 0; max--) {
limit = (int) ((LRAND() >> 3) % max) + 1;
for ( i= 1, p = top; i <= limit; ) {
while (p->isFaceUp())
p = p->nextCard();
i++;
if (i <= limit)
p = p->nextCard();
}
if ((q = new CardLink(mi, p->thisCard())) == NULL) {
return False;
}
pile->addCard(q);
p->flip();
}
return True;
}
void DeckPile::updateLocation(ModeInfo * a_mi)
{
solitarestruct *bp = &solitare[MI_SCREEN(a_mi)];
//set pile
x = int_round(((bp->cardwidth + 2.0 * MAXSUITS * bp->cardwidth *
(MAXALTERNATEPILES + 1.0)) / (2.0 * MAXALTERNATEPILES)) +
3 * bp->cardwidth / 4.0);
y = int_round(0.25 * bp->cardheight);
// set card
for (CardLink *p = top; p!= nilLink; p = p->nextCard()) {
p->moveTo(x, y);
}
}
Bool DeckPile::initialize()
{
solitarestruct *bp = &solitare[MI_SCREEN(mi)];
CardPile::initialize();
if (!bp->game->dealPile->shuffle(this))
return False;
return True;
}
void DeckPile::addCard(CardLink *c)
{
if (c->isFaceUp())
c->flip();
CardPile::addCard(c);
}
Bool DeckPile::select(int a_x, int a_y)
{
// from virtual, a_x & a_y unused
return select(True);
}
// turn over a new card
Bool DeckPile::select(Bool first)
{
// from virtual, first unused
solitarestruct *bp = &solitare[MI_SCREEN(mi)];
CardLink *c;
Bool didSomething = False;
if (top != nilLink) {
c = removeCard(); // should not this be 3 at a time
if (c != nilLink)
(bp->game->discardPile)->addCard(c);
didSomething = True;
}
displayPile();
(bp->game->discardPile)->displayPile();
return didSomething;
}
void DiscardPile::updateLocation(ModeInfo * a_mi)
{
solitarestruct *bp = &solitare[MI_SCREEN(a_mi)];
//set pile
x = int_round(((bp->cardwidth + 2.0 * MAXSUITS * bp->cardwidth *
(MAXALTERNATEPILES + 1.0)) / (2.0 * MAXALTERNATEPILES)) +
2 * bp->cardwidth);
y = int_round(0.25 * bp->cardheight);
// set card
for (CardLink *p = top; p!= nilLink; p = p->nextCard()) {
p->moveTo(x, y);
}
}
void DiscardPile::addCard(CardLink *c)
{
if (!(c->isFaceUp()))
c->flip();
CardPile::addCard(c);
}
// play the current face card
Bool DiscardPile::select(int a_x, int a_y)
{
// from virtual, a_x & a_y unused
return select(True);
}
// play the current face card
Bool DiscardPile::select(Bool first)
{
// from virtual, first unused
solitarestruct *bp = &solitare[MI_SCREEN(mi)];
CardPile * pile;
if (top == nilLink)
return False;
// see if we can move it to a suit pile
if (bp->game->suitCanAdd(top->thisCard())) {
pile = bp->game->suitAddPile(top->thisCard());
pile->addCard(removeCard());
displayPile();
pile->displayPile();
return True;
}
if (bp->game->alternateCanAdd(top->thisCard())) {
pile = bp->game->alternateAddPile(top->thisCard());
pile->addCard(removeCard());
displayPile();
pile->displayPile();
return True;
}
return False;
}
void SuitPile::updateLocation(ModeInfo * a_mi)
{
solitarestruct *bp = &solitare[MI_SCREEN(a_mi)];
//set pile
x = int_round(((bp->cardwidth + 2.0 * suit * bp->cardwidth *
(MAXALTERNATEPILES + 1.0)) / (2.0 * MAXALTERNATEPILES)) +
bp->cardwidth / 2.0);
y = int_round(0.25 * bp->cardheight);
// set card
for (CardLink *p = top; p!= nilLink; p = p->nextCard()) {
p->moveTo(x, y);
}
}
Bool SuitPile::canTake(Card * card)
{
if (top == nilLink) { // empty so can take ace
if (card->whichRank() == 1)
return True;
return False;
}
if ((top->thisCard())->whichSuit() != card->whichSuit())
return False;
if (((top->thisCard())->whichRank() + 1) == card->whichRank())
return True;
return False;
}
void AlternatePile::updateLocation(ModeInfo * a_mi)
{
solitarestruct *bp = &solitare[MI_SCREEN(a_mi)];
int ty;
//set pile
x = (int_round((bp->cardwidth + 2.0 * pile * bp->cardwidth *
(MAXALTERNATEPILES + 1.0)) / (2.0 * MAXALTERNATEPILES)));
y = int_round(1.5 * bp->cardheight);
CardLink *bf = nilLink;
// set card
for (CardLink *p = top; p!= nilLink; p = p->nextCard()) {
p->moveTo(x, y);
// find bottom faceup card
// not setup to work with STRANGE_LAYOUT
if (p->isFaceUp())
bf = p;
}
if (bf != nilLink) {
// ok but this is wrong. Since we only have a singly link
// we will do this slightly inefficiently...
ty = y;
while (bf != top) {
CardLink *sp;
for (sp = top; sp->nextCard() != bf; sp = sp->nextCard());
bf = sp;
ty += int_round(bp->cardheight * OVERLAP);
sp->moveTo(x, ty);
}
}
}
Bool AlternatePile::initialize()
{
solitarestruct *bp = &solitare[MI_SCREEN(mi)];
int a_pile;
//put the right number of cards on the alternate
(void) CardPile::initialize();
for (a_pile = 0; a_pile <= this->pile; a_pile++)
addCard((bp->game->deckPile)->removeCard());
// flip the last one
if (top != nilLink) {
top->flip();
}
return True;
}
void AlternatePile::addCard(CardLink * card)
{
solitarestruct *bp = &solitare[MI_SCREEN(mi)];
int tx, ty;
if (top == nilLink)
CardPile::addCard(card);
else {
tx = top->x();
ty = top->y();
// figure out where to place the card
#ifdef STRANGE_LAYOUT
// This was the original logic... does not look right to me.
if (!(top->isFaceUp() && top->nextCard() != nilLink &&
(top->nextCard())->isFaceUp()))
#else
if (top->isFaceUp())
#endif
ty += int_round(bp->cardheight * OVERLAP);
CardPile::addCard(card);
top->moveTo(tx, ty);
}
}
Bool AlternatePile::canTake(Card *card)
{
if (top == nilLink) { // can take only kings on an empty pile
if (card->whichRank() == MAXRANK) {
return True;
}
return False;
}
// see if it is face up, colors are different, and the number is legal
if ((top->thisCard())->whichColor() != card->whichColor() &&
((top->thisCard())->whichRank() - 1) == card->whichRank()) {
return True;
}
return False;
}
void AlternatePile::copyBuild(CardLink *card, CardPile * a_pile)
{
CardLink *tempCard;
top->erase();
tempCard = removeCard();
displayPile();
if (card != tempCard)
copyBuild(card, a_pile);
a_pile->addCard(tempCard);
a_pile->displayPile();
}
static void stackDisplay(CardLink *p)
{
if (p->nextCard())
stackDisplay(p->nextCard());
p->draw();
}
void AlternatePile::displayPile()
{
// Zero or one cards, can not do any better
if (top == nilLink)
CardPile::displayPile();
else { // otherwise half display all the covered cards
stackDisplay(top);
}
}
Bool AlternatePile::select(int a_x, int a_y)
{
solitarestruct *bp = &solitare[MI_SCREEN(mi)];
CardLink *c;
// no cards, do nothing
if (top == nilLink)
return False;
// if top card is not flipped, flip it now
if (!top->isFaceUp()) {
top->flip();
top->draw();
return True;
}
// if it was to top card, see if we can move it
if (top->includes(a_x, a_y)) {
// see if we ca move it to a suit pile
if (bp->game->suitCanAdd(top->thisCard())) {
copyBuild(top, bp->game->suitAddPile(top->thisCard()));
return True;
}
// else see if we can move a alternate pile but only if it is not part of pile
if (((top->nextCard() == nilLink) || !((top->nextCard())->isFaceUp())) &&
bp->game->alternateCanAdd(top->thisCard())) {
copyBuild(top, bp->game->alternateAddPile(top->thisCard()));
return True;
}
}
// else see if we can move a pile
for (c = top->nextCard(); c!= nilLink; c = c->nextCard())
if (c->isFaceUp() && c->includes(a_x, a_y)) {
if (bp->game->alternateCanAdd(c->thisCard())) {
copyBuild(c, bp->game->alternateAddPile(c->thisCard()));
}
return True;
}
return False;
}
Bool AlternatePile::select(Bool first)
{
solitarestruct *bp = &solitare[MI_SCREEN(mi)];
CardLink *c;
// no cards, do nothing
if (top == nilLink)
return False;
// if top card is not flipped, flip it now
if (!top->isFaceUp()) {
top->flip();
top->draw();
return True;
}
// see if we ca move it to a suit pile
if (bp->game->suitCanAdd(top->thisCard())) {
copyBuild(top, bp->game->suitAddPile(top->thisCard()));
return True;
}
// If all cards are not flipped it could lead to problems...
if (first)
return False;
// Other moves may be better but this is stupid
// Bouncing may result if one allows to take away moves
// find highest faceup card
c = top;
while (c->nextCard() != nilLink && (c->nextCard())->isFaceUp())
c = c->nextCard();
// if not king see if it can be moved to leave a space (else it will bounce)
if (((c->nextCard() != nilLink) ||
((c->thisCard())->whichRank() != MAXRANK)) &&
bp->game->alternateCanAdd(c->thisCard())) {
copyBuild(c, bp->game->alternateAddPile(c->thisCard()));
return True;
}
return False;
}
GameTable::GameTable(ModeInfo *mi)
{
solitarestruct *bp = &solitare[MI_SCREEN(mi)];
int suit, rank, pile;
bp->width = MI_WIDTH(mi);
bp->height = MI_HEIGHT(mi);
bp->showend = 0;
bp->cardwidth = bp->width / (MAXALTERNATEPILES + 1);
bp->cardheight = bp->height / 5;
// Create the original (unshuffled) deck (with no cards) ... dah daaah!
if ((dealPile = new DealPile(mi)) == NULL) {
return;
}
for (suit = 0; suit < MAXSUITS; suit++)
for (rank = 1; rank <= MAXRANK; rank++) {
CardLink * deal;
// 52 pickup, creating new cards and creating links
if ((deal = new CardLink(mi, Suits(suit), rank)) == NULL) {
dealPile->cleanup();
delete dealPile;
return;
}
dealPile->addCard(deal);
}
// create 2 piles the deck and discard piles
if ((allPiles[0] = deckPile = new DeckPile(mi)) == NULL) {
dealPile->cleanup();
delete dealPile;
return;
}
deckPile->updateLocation(mi);
if ((allPiles[1] = discardPile = new DiscardPile(mi)) == NULL) {
dealPile->cleanup();
delete dealPile;
delete deckPile;
return;
}
discardPile->updateLocation(mi);
// create suit piles
for (suit = 0; suit < MAXSUITS; suit++) {
if ((allPiles[suit + 2] = suitPiles[Suits(suit)] = new SuitPile(mi, suit)) == NULL) {
delete this;
return;
}
(suitPiles[Suits(suit)])->updateLocation(mi);
}
// create alternate piles
for (pile = 0; pile < MAXALTERNATEPILES; pile++) {
if ((allPiles[pile + 2 + MAXSUITS] = alternatePiles[pile] =
new AlternatePile(mi, pile)) == NULL) {
delete this;
return;
}
(alternatePiles[pile])->updateLocation(mi);
}
}
GameTable::~GameTable()
{
for (int pile = 0; pile < MAXPILES; pile++)
if (allPiles[pile]) {
allPiles[pile]->cleanup();
delete allPiles[pile];
}
if (dealPile) {
dealPile->cleanup();
delete dealPile;
}
}
void GameTable::Resize(ModeInfo *mi)
{
solitarestruct *bp = &solitare[MI_SCREEN(mi)];
int suit, pile;
bp->width = MI_WIDTH(mi);
bp->height = MI_HEIGHT(mi);
bp->cardwidth = bp->width / (MAXALTERNATEPILES + 1);
bp->cardheight = bp->height / 5;
deckPile->updateLocation(mi);
discardPile->updateLocation(mi);
// move suit piles
for (suit = 0; suit < MAXSUITS; suit++) {
(suitPiles[Suits(suit)])->updateLocation(mi);
}
// move alternate piles
for (pile = 0; pile < MAXALTERNATEPILES; pile++) {
(alternatePiles[pile])->updateLocation(mi);
}
}
Bool GameTable::newGame(ModeInfo * mi)
{
int pile;
// initialize all the piles
for (pile = 0; pile < MAXPILES; pile++)
if (!allPiles[pile]->initialize())
return False;
// redraw the game window
Redraw(mi);
return True;
}
void GameTable::Redraw(ModeInfo * mi)
{
int pile;
//first clear the entire playing area
MI_CLEARWINDOW(mi);
// then display the piles
for (pile = 0; pile < MAXPILES; pile++)
allPiles[pile]->displayPile();
}
// this is where the smarts is/is not
Bool GameTable::HandleMouse(ModeInfo * mi)
{
Window r, c;
int cx, cy, rx, ry;
unsigned int m;
int pile;
(void) XQueryPointer(MI_DISPLAY(mi), MI_WINDOW(mi),
&r, &c, &rx, &ry, &cx, &cy, &m);
if (cx <= 0 || cy <= 0 ||
cx >= MI_WIDTH(mi) - 1 || cy >= MI_HEIGHT(mi) -1) {
return HandleGenerate();
}
for (pile = 0; pile < MAXPILES; pile++)
if (allPiles[pile]->contains(cx, cy)) {
(void) allPiles[pile]->select(cx, cy);
return True;
}
return True;
}
// if done right it looks smart...
Bool GameTable::HandleGenerate()
{
int pile;
// Start with the biggest stacks... usually the last ones
for (pile = MAXALTERNATEPILES - 1; pile >= 0; pile--)
if (alternatePiles[pile]->select(True))
return True;
for (pile = MAXALTERNATEPILES - 1; pile >= 0; pile--)
if (alternatePiles[pile]->select(False))
return True;
// Look for something to do in discard pile
if (discardPile->select(True))
return True;
// Get a new card or end it
return (deckPile->select(True));
}
// see if any of the suit piles can add a specific card
Bool GameTable::suitCanAdd(Card * card)
{
int suit;
for (suit = 0; suit < MAXSUITS; suit++)
if (suitPiles[Suits(suit)]->canTake(card))
return True;
return False;
}
// see if any of the alternate piles can add a specific card
Bool GameTable::alternateCanAdd(Card * card)
{
int pile;
for (pile = 0; pile < MAXALTERNATEPILES; pile++)
if (alternatePiles[pile]->canTake(card))
return True;
return False;
}
// return which of the suit piles can add a card
CardPile * GameTable::suitAddPile(Card * card)
{
int suit;
for (suit = 0; suit < MAXSUITS; suit++)
if (suitPiles[Suits(suit)]->canTake(card))
return suitPiles[Suits(suit)];
(void) printf("suitAddPile\n");
return (CardPile *) NULL; // Hopefully we can not get here
}
// return which of the alternate piles can add a card
CardPile * GameTable::alternateAddPile(Card * card)
{
int pile;
for (pile = 0; pile < MAXPILES; pile++)
if (alternatePiles[pile]->canTake(card))
return alternatePiles[pile];
(void) printf("alternateAddPile\n");
return (CardPile *) NULL; // Hopefully we can not get here
}
/* Yes, it's an ugly mix of 'C' and 'C++' functions */
extern "C" { void init_solitare(ModeInfo * mi); }
extern "C" { void draw_solitare(ModeInfo * mi); }
extern "C" { void change_solitare(ModeInfo * mi); }
extern "C" { void release_solitare(ModeInfo * mi); }
extern "C" { void refresh_solitare(ModeInfo * mi); }
#ifndef DISABLE_INTERACTIVE
static XrmOptionDescRec opts[] =
{
{(char *) "-trackmouse", (char *) ".solitare.trackmouse", XrmoptionNoArg, (caddr_t) "on"},
{(char *) "+trackmouse", (char *) ".solitare.trackmouse", XrmoptionNoArg, (caddr_t) "off"}
};
static argtype vars[] =
{
{(void *) & trackmouse, (char *) "trackmouse", (char *) "TrackMouse", (char *) DEF_TRACKMOUSE, t_Bool}
};
static OptionStruct desc[] =
{
{(char *) "-/+trackmouse", (char *) "turn on/off the tracking of the mouse"}
};
ModeSpecOpt solitare_opts =
{sizeof opts / sizeof opts[0], opts, sizeof vars / sizeof vars[0], vars, desc};
#else
ModeSpecOpt solitare_opts =
{0, (XrmOptionDescRec *) NULL, 0, (argtype *) NULL, (OptionStruct *) NULL};
#endif
#ifdef USE_MODULES
ModStruct solitare_description =
{(char *) "solitare", (char *) "init_solitare",
(char *) "draw_solitare", (char *) "release_solitare",
(char *) "refresh_solitare", (char *) "init_solitare",
(char *) NULL, &solitare_opts,
2000000, 1, 1, 1, 64, 1.0, (char *) "",
(char *) "Shows Klondike's game of solitare", 0, NULL};
#endif
/*
*-----------------------------------------------------------------------------
*-----------------------------------------------------------------------------
* Xlock hooks.
*-----------------------------------------------------------------------------
*-----------------------------------------------------------------------------
*/
/*
*-----------------------------------------------------------------------------
* Initialize solitare. Called each time the window changes.
*-----------------------------------------------------------------------------
*/
void
init_solitare(ModeInfo * mi)
{
solitarestruct *bp;
if (solitare == NULL) {
if ((solitare = (solitarestruct *) calloc(MI_NUM_SCREENS(mi),
sizeof(solitarestruct))) == NULL)
return;
}
bp = &solitare[MI_SCREEN(mi)];
MI_CLEARWINDOW(mi);
#ifdef DOFONT
Display *display = MI_DISPLAY(mi);
XGCValues gcv;
if (mode_font == None)
mode_font = getFont(display);
if (mode_font != None) {
gcv.font = mode_font->fid;
XSetFont(display, MI_GC(mi), mode_font->fid);
gcv.graphics_exposures = False;
gcv.foreground = MI_WHITE_PIXEL(mi);
gcv.background = MI_BLACK_PIXEL(mi);
if ((mp->gc = XCreateGC(display, MI_WINDOW(mi),
GCForeground | GCBackground | GCGraphicsExposures | GCFont,
&gcv)) == None) {
return;
}
mp->ascent = mode_font->ascent;
mp->height = font_height(mode_font);
for (i = 0; i < 256; i++)
char_width[i] = 8;
}
#endif
if (bp->game) {
if (bp->width != MI_WIDTH(mi) || bp->height != MI_HEIGHT(mi)) {
/* Let us not be creative then... */
bp->game->Resize(mi);
bp->painted = True;
refresh_solitare(mi);
return;
}
delete bp->game;
}
bp->painted = False;
if ((bp->game = new GameTable(mi)) == NULL) {
return;
}
if (!bp->game->newGame(mi)) {
delete bp->game;
}
}
/*
*-----------------------------------------------------------------------------
* Called by the mainline code periodically to update the display.
*-----------------------------------------------------------------------------
*/
void
draw_solitare(ModeInfo * mi)
{
solitarestruct *bp;
if (solitare == NULL)
return;
bp = &solitare[MI_SCREEN(mi)];
if (!bp->game)
return;
MI_IS_DRAWN(mi) = True;
bp->painted = True;
if (bp->showend) {
bp->showend++;
if (bp->showend >= 10)
init_solitare(mi);
} else if (trackmouse) {
if (!bp->game->HandleMouse(mi)) {
bp->showend++;
}
} else if (!bp->game->HandleGenerate()) {
bp->showend++;
}
}
/*
*-----------------------------------------------------------------------------
* The display is being taken away from us. Free up malloc'ed
* memory and X resources that we've alloc'ed. Only called
* once, we must zap everything for every screen.
*-----------------------------------------------------------------------------
*/
void
release_solitare(ModeInfo * mi)
{
if (solitare != NULL) {
for (int screen = 0; screen < MI_NUM_SCREENS(mi); screen++) {
solitarestruct *bp = &solitare[screen];
if (bp->game)
delete bp->game;
}
free(solitare);
solitare = (solitarestruct *) NULL;
}
}
void
refresh_solitare(ModeInfo * mi)
{
solitarestruct *bp;
if (solitare == NULL)
return;
bp = &solitare[MI_SCREEN(mi)];
if (!bp->game)
return;
if (bp->painted) {
bp->game->Redraw(mi);
bp->painted = False;
}
}
#endif /* MODE_solitare */