//*********************** Copy right information ************************* // FAQ's // Q. Is this program copy right protected? // Ans. No. Even if it was you will copy it anyway so what is the point //************************************************************************ // Program: Solitaire card game // Programmer: H K Gupta Date: 25 Oct1997 // added lazy bone option and useless help Aug 16 1998 //************************************************************************ import java.awt.*; import java.awt.image.*; import java.applet.Applet; import java.lang.Math; public class Solitaire extends Applet implements Runnable{ Panel pTop; Button bNewGame; Button bBuyExtra; Button bLazyBones; Button bSingleCard; Button bHelp; Panel pLeft; Label[] lSolitaire; Panel pRight; Label[] lHridayesh; SolitairePlayArea playArea; Label results; HridayeshCardDeck deck; // card deck for playing the game boolean flagMoveSingleCard = false; boolean isLazyBones = false; Thread runThread = null; boolean firstTime = true; boolean pauseInEffect = false; public void init() { } public void start() { if (firstTime) { deck = new HridayeshCardDeck(); // get a new card deck setUpDisplay() ; results.setText("*S*O*L*I*T*A*I*R*E*"); firstTime = false; } if (runThread == null) { if(!pauseInEffect) { runThread = new Thread(this, "Solitaire"); runThread.start(); } } } public void run() { while (Thread.currentThread() == runThread) { if(playArea.moveInProgress) { playArea.move(); } else { if(playArea.autoInProgress) { playArea.moveAuto(); } } if(!gameOnDisplay) playArea.doGameOver(); try { Thread.sleep(40); } catch (InterruptedException e){ } } } public void stop() { runThread = null; } void setUpDisplay() { setLayout(new BorderLayout()); pTop = new Panel(); pTop.setLayout(new GridLayout(1,0)); add("North", pTop); bNewGame = new Button("New Game"); pTop.add(bNewGame); bBuyExtra = new Button("Buy turnOver for 25 pnts"); pTop.add(bBuyExtra); bLazyBones = new Button("Select Lazy Bones"); pTop.add(bLazyBones); bSingleCard = new Button("Move Multiple Cards"); pTop.add(bSingleCard); bHelp = new Button("HELP ME??"); pTop.add(bHelp); pLeft = new Panel(); pLeft.setLayout(new GridLayout(0, 1)); add("West", pLeft); lSolitaire = new Label[9]; for(int i = 0; i < 9; i++) { lSolitaire[i] = new Label("SOLITAIRE".substring(i, i+ 1), Label.CENTER); lSolitaire[i].setFont(new Font("TimesRoman",Font.BOLD,24)); lSolitaire[i].setForeground(Color.red); pLeft.add(lSolitaire[i]); } pRight = new Panel(); pRight.setLayout(new GridLayout(0, 1)); add("East", pRight); lHridayesh = new Label[9]; for(int i = 0; i < 9; i++) { lHridayesh[i] = new Label("HRIDAYESH".substring(i, i+ 1), Label.CENTER); lHridayesh[i].setFont(new Font("TimesRoman",Font.BOLD,24)); lHridayesh[i].setForeground(Color.red); pRight.add(lHridayesh[i]); } playArea = new SolitairePlayArea(this); add("Center", playArea); results = new Label("Loading Card Images", Label.CENTER); add("South", results); validate(); } private boolean newGame() { if(playArea.autoInProgress || playArea.moveInProgress) return true; playArea.flagMoveStored = false; gameOnDisplay = true; playArea.dealHand(); updateScore(1, -52); return true; } boolean gameOnDisplay = false; public boolean action(Event e, Object arg) { Object target = e.target; if(playArea.autoInProgress || playArea.moveInProgress) return true; if(target == bNewGame) return newGame(); if(target == bBuyExtra) { if(gameOnDisplay) { playArea.buyExtraTurnOver(); return true; } } if(target == bSingleCard) { if(flagMoveSingleCard) { bSingleCard.setLabel("Move Three cards"); bBuyExtra.setLabel("Buy turnOver for 25 points"); } else { bBuyExtra.setLabel("Buy turnOver for 75 points"); bSingleCard.setLabel("Move single card"); } flagMoveSingleCard = !flagMoveSingleCard; return newGame(); } if(target == bHelp) { processHelp(); return true;} if(target == bLazyBones) { if (isLazyBones) { isLazyBones = false; bLazyBones.setLabel("Select Lazy Bones"); } else { isLazyBones = true; bLazyBones.setLabel("I Am Lazy"); } } return false; } public boolean keyDown(Event e, int key) { if(playArea.autoInProgress || playArea.moveInProgress) return true; return false; } int gamesPlayed = 0; // statistics variables int score = 0; void updateScore(int games, int points) { gamesPlayed += games; score += points; dispScore(); } void dispScore() { results.setText("Games: " + gamesPlayed + " Score: " + score); } private void processHelp() { HridayeshHelp window = null; try { window = new HridayeshHelp(); } catch (Exception e) { bHelp.setLabel("Error"); bHelp.disable(); return; } window.setTitle("SolitaireHelp"); window.setText( "\n Solitaire Help \n\n" + "\nWe will not help you with the rules of the game. You better know them." + "\nHowever we will let you know about the features we have tried to push" + "\non unsuspecting public.\n"+ "\n1. Buy Turnover : After losing this game 273 straight times one of our" + "\nfan threatened to stop playing this game. Now we can not have that. So" + "\nto help out people who do not mind little bit of cheating, we allow you" + "\nto continue the game (after you have lost to it) by paying a small penalty." + "\nThe penalty is 25 points in three card at a time and 75 points in single" + "\ncard at a time version. (PLEASE do not ask us to explain this single/three" + "\ncard at a time business).\n\n" + "\n2. Lazy Bones: Human beings have two kind of bones. One kind is made from " + "\nCalcium and other unpronouncable materials. The other kind is made from" + "\nsheer laziness and its assorted compounds. To help these people we will " + "\nautomatically open any close card and save these souls from executing" + "\npainful mouse clicks just for opening a card.\n\n\n" + "\n GOOD LUCK"); window.pack(); window.show(); } } class SolitairePlayArea extends Canvas { private Solitaire controller; private HridayeshLoadCards deckImages; private int nPlayStacks = 7; int flagPaint = -1; SolitaireStack[] stack; final int typeClose = 0; final int typeDone = 1; final int typePlay = 2; final int typeOpen = 3; SolitaireStack closeStack; SolitaireStack openStack; SolitaireStack dragStack; HridayeshCardShow drawOver; public SolitairePlayArea(Solitaire controller) { super(); this.controller = controller; deckImages = new HridayeshLoadCards((Applet)controller); stack = new SolitaireStack[13]; for(int i = 0; i < 13; i++) stack[i] = new SolitaireStack(this, i, deckImages.getCardW(), deckImages.getCardH()); closeStack = stack[11]; openStack = stack[12]; dragStack = new SolitaireStack(this, 0, 0, 0); // we need a non drawing stack only moveArray = new SolitaireStack[100][2]; moveCount = new int[100]; drawOver = new HridayeshCardShow(deckImages); displayReset(); } private void displayReset() { for(int i = 0; i < 13; i++) stack[i].reset(); ssSelected = null; resetProgressFlags(); flagPaint = -1; } public void resetProgressFlags() { moveInProgress = false; autoInProgress = false; dragInProgress = false; } public void dealHand() { displayReset(); int card; closeStackTurnOverCount = 0; controller.deck.reset(); for(int k = 0; k < 7; k++) { for(int i = 0; i <= k; i++) { card = controller.deck.newCard(); if(k == i) { stack[k].addCard(card, true); } else { stack[k].addCard(card, false); } } } for(int i = 0; i < 24; i++) closeStack.addCard(controller.deck.newCard(), false); flagPaint = -1; repaint(); } private int closeStackTurnOverCount = 0; private boolean turnOversPending() { int max = 3; if(controller.flagMoveSingleCard) max = 1; if(closeStackTurnOverCount < max) return true; return false; } public void buyExtraTurnOver() { closeStackTurnOverCount--; if(controller.flagMoveSingleCard) { controller.updateScore(0, -75); } else { controller.updateScore(0, -25); } if(flagPaint != -1) return; flagPaint = 12; repaint(); } private boolean flagSetOffsets = true; Image iBuffer; // double buffer the playArea Graphics g; public void update(Graphics gPlayArea) { if(flagSetOffsets) { Dimension d = size(); iBuffer = createImage(d.width, d.height); g = iBuffer.getGraphics(); for(int i = 0; i < 13; i++) stack[i].setOffsets(g); flagSetOffsets = false; } if(flagPaint == 10) return; // some painting in progress int tmp = flagPaint; flagPaint = 10; // prevent others from issuing a repaint(); switch(tmp) { case -1: case 0: paintEveryThing(g); break; case 2: selectForDrag(g); break; case 4: paintMove(g); break; case 5: moveAutoCard(g); break; case 7: paintDragging(g); break; case 8: paintDragOver(g); break; case 11: drawOver.draw(g, this); break; case 12: closeStack.drawStack(g); break; default: } gPlayArea.drawImage(iBuffer, 0, 0, this); flagPaint = -1; } void moveAutoCard(Graphics g) { if(controller.isLazyBones) { // open any closed card if(!closeStack.isEmpty()) { if(openStack.isEmpty()) { putAndMoveCard(g, closeStack, null); return; } } for(int i = 0; i < 7; i++) { if(!stack[i].isEmpty()) { if(stack[i].isTopCardClosed()) { putAndMoveCard(g, stack[i], null); return; } } } } int lowSuitNo = 13; for(int i = 7; i < 11; i++) { if(suitNo(stack[i].topCard()) < lowSuitNo) lowSuitNo = suitNo(stack[i].topCard()); } lowSuitNo++; for(int i = 0; i < 13; i++) { int type = stack[i].getType(); if((type == typeOpen) ||(type == typePlay)) { int card = stack[i].topCard(); if((card != -1) && (!stack[i].isTopCardClosed())) { if(suitNo(card) == lowSuitNo) { for(int j = 7; j < 11; j++) { if(stack[j].isEmpty()) { putAndMoveCard(g, stack[i], stack[j]); return; } if((int)(card / 13) == (int)(stack[j].topCard() / 13)) { putAndMoveCard(g, stack[i], stack[j]); return; } } } else { if(suitNo(card) == (lowSuitNo + 1)) { for(int j = 7; j < 11; j++) { if(!stack[j].isEmpty()) { if((suitNo(card) == (suitNo(stack[j].topCard()) + 1)) && ((card / 13) == (stack[j].topCard() / 13))) { putAndMoveCard(g, stack[i], stack[j]); return; } } } } } } } } autoInProgress = false; moveIndex = totalMoves - 1; flagMoveStored = true; if(gameOver()) { controller.updateScore(1, 0); controller.gameOnDisplay = false; flagMoveStored = false; } } void putAndMoveCard(Graphics g, SolitaireStack ssSrc, SolitaireStack ssDst) { // single Card moveCardFromSrcToDst(g, ssSrc, ssDst, ssSrc.getNCards() - 1); moveArray[totalMoves][0] = ssSrc; moveArray[totalMoves][1] = ssDst; moveCount[totalMoves] = ssSrc.getNCards() - 1; // top card only totalMoves++; } private SolitaireStack[][] moveArray; private int[] moveCount; private int moveIndex; private int totalMoves; boolean flagMoveStored = false; void paintMove(Graphics g) { if(!moveInProgress) return; moveCardFromSrcToDst(g, moveArray[moveIndex][0], moveArray[moveIndex][1], moveCount[moveIndex]); moveIndex++; if(moveIndex >= totalMoves) { moveInProgress = false; autoInProgress = true; } } void moveCardFromSrcToDst(Graphics g, SolitaireStack ssSrc, SolitaireStack ssDst, int pos) { if(ssDst == null) { // open close card if(ssSrc.getType() == typePlay) { int card = ssSrc.removeCard(g); ssSrc.putCard(g, card, true); } if(ssSrc.getType() == typeClose) { if(ssSrc.isEmpty()) { if((!openStack.isEmpty()) && turnOversPending()) { // Move From Open stack all cards int n = openStack.getNCards(); for(int i = 0; i < n; i++) ssSrc.putCard(g, openStack.removeCard(g), false); openStack.setOpenIndex(0); } } else { int n = getCardsToMove(); openStack.eraseStack(g); openStack.setOpenIndex(0); for(int i = 0; i < n; i++) openStack.putCard(g, ssSrc.removeCard(g), true); if(closeStack.isEmpty()) { closeStackTurnOverCount++; closeStack.drawTopCard(g); } } } } else { for(int i = pos; i < ssSrc.getNCards(); i++) ssDst.putCard(g, ssSrc.getIthCard(i), true); for(int i = ssSrc.getNCards() - 1; i >= pos; i--) ssSrc.removeCard(g); if(ssSrc.getType() == typeDone) controller.updateScore(0, -5); if(ssDst.getType() == typeDone) controller.updateScore(0, 5); } } private int getCardsToMove() { int n = 1; if(!controller.flagMoveSingleCard) n = 3; if(n > closeStack.getNCards()) n = closeStack.getNCards(); return n; } void paintEveryThing(Graphics g) { Dimension d = size(); g.setColor(Color.gray); g.fillRect(0, 0, d.width, d.height); for(int i = 0; i < 13; i++) stack[i].drawStack(g); } void paintCard(Graphics g, boolean drawFlag, int x, int y, int cardNo, int type) { if(drawFlag && (cardNo >= 0)) { g.drawImage(deckImages.getCardImage(cardNo), x, y, this); } else { g.setColor(Color.gray); g.fillRect(x, y, deckImages.getCardW() + 1, deckImages.getCardH() + 1); if((type == typeClose) && (!turnOversPending())) { g.setColor(Color.yellow); g.fillRect(x, y, deckImages.getCardW() + 1, deckImages.getCardH() + 1); g.setColor(Color.black); g.drawString("XXXXXXX", x + 10, y + 50); } if(y == 2) { g.setColor(Color.black); g.drawRect(x, y, deckImages.getCardW() - 1, deckImages.getCardH() - 1); } } } private SolitaireStack ssSelected = null; private SolitaireStack ssDestination = null; boolean autoInProgress = false; boolean moveInProgress = false; boolean dragInProgress = false; private int cardToDrag; public boolean mouseDown(Event event, int x, int y) { if(!controller.gameOnDisplay) return false; if(autoInProgress || moveInProgress) return false; if(flagPaint != -1) return false; for(int i = 0; i < 13; i++) { int selPos = stack[i].getPositionSelected(x, y); switch(selPos) { case -1: break; // not inside case -2: if(stack[i] == closeStack) { return setMove(stack[i], null, 0); } break; default: if(stack[i].isTopCardClosed()) { return setMove(stack[i], null, 0); } ssSelected = stack[i]; dragInProgress = true; cardToDrag = selPos; flagPaint = 2; // selection dragOldX = stack[i].getXPos(selPos); dragOldY = stack[i].getYPos(selPos); dragXOff = x - dragOldX; dragYOff = y - dragOldY; repaint(); return true; } } return false; } void selectForDrag(Graphics g) { if(ssSelected == null) return; ssSelected.paintSelection(g, cardToDrag); } Image selImage; Graphics gSel; Image savedImage; Graphics gSaved; void drawSelectionImage(Graphics g, int x, int y, int w, int h) { selImage = createImage(w, h); gSel = selImage.getGraphics(); gSel.drawImage(iBuffer, - x, - y, this); dragStack.reset(); for(int i = cardToDrag; i < ssSelected.getNCards(); i++) dragStack.addCard(ssSelected.getIthCard(i), true); for(int i = ssSelected.getNCards() - 1; i >= cardToDrag; i--) ssSelected.removeCard(g); savedImage = createImage(w, h); gSaved = savedImage.getGraphics(); gSaved.drawImage(iBuffer, - x, - y, this); g.drawImage(selImage, x, y, this); } private int dragOldX, dragOldY; private int dragX, dragY; private int dragXOff, dragYOff; private void paintDragging(Graphics g) { g.drawImage(savedImage, dragOldX, dragOldY, this); gSaved.drawImage(iBuffer, - dragX, - dragY, this); dragOldX = dragX; dragOldY = dragY; g.drawImage(selImage, dragX, dragY, this); } private void paintDragOver(Graphics g) { for(int i = 0; i < dragStack.getNCards(); i++) ssSelected.addCard(dragStack.getIthCard(i), true); g.drawImage(savedImage, dragOldX, dragOldY, this); if((ssDestination != null) && (ssDestination != ssSelected)) { setMove(ssSelected, ssDestination, cardToDrag); } else { ssSelected.drawStack(g); } } public boolean mouseDrag(Event event, int x, int y) { if((!dragInProgress) || (flagPaint != -1)) return false; dragX = x - dragXOff; dragY = y - dragYOff; flagPaint = 7; repaint(); return false; } public boolean mouseUp(Event event, int x, int y) { if(!dragInProgress) return false; for(int i = 0; i < 13; i++) { if(stack[i].isInside(x, y)) { if(isMovePossible(dragStack, stack[i], 0)) { setDragOver(stack[i]); return false; } } } setDragOver(null); return false; } private void setDragOver(SolitaireStack ss) { ssDestination = ss; dragInProgress = false; flagPaint = 8; repaint(); } boolean setMove(SolitaireStack ssSrc, SolitaireStack ssDst, int pos) { flagMoveStored = false; autoInProgress = false; moveInProgress = true; totalMoves = 0; moveIndex = 0; addMove(ssSrc, ssDst, pos); ssSelected = null; return true; } void addMove(SolitaireStack ssSrc, SolitaireStack ssDst, int pos) { moveArray[totalMoves][0] = ssSrc; moveArray[totalMoves][1] = ssDst; moveCount[totalMoves] = pos; totalMoves++; } boolean isMovePossible(SolitaireStack ssSrc, SolitaireStack ssDst, int pos) { if(ssSrc == null) return false; if(ssSrc == ssDst) return false; // no move on self if(ssDst == openStack) return false; // no move to openStack if(ssSrc.isEmpty()) return false; if(ssSrc.isTopCardClosed()) return false; switch(ssDst.getType()) { case typeDone: if(pos != (ssSrc.getNCards() - 1)) return false; return isCardAceOrNext(ssSrc.topCard(), ssDst.topCard()); case typePlay: if(ssDst.isEmpty()) { if(suitNo(ssSrc.getIthCard(pos)) == 11) return true; } else { if(!ssDst.isTopCardClosed()) { return isCardAllowed(ssDst.topCard(), ssSrc.getIthCard(pos)); } } break; default: } return false; } boolean isCardAceOrNext(int src, int dst) { if(dst == -1) { if(suitNo(src) == -1) return true; } else { if(((src / 13) == (dst / 13)) && (suitNo(src) == (suitNo(dst) + 1))) return true; } return false; } boolean isCardAllowed(int high, int low) { if(suitColor(high) == suitColor(low)) return false; if(suitNo(high) == (suitNo(low) + 1)) return true; return false; } int suitNo(int card) { if(card < 0) return -2; int suitNum = card % 13; if(suitNum == 12) suitNum = -1; return suitNum; } int suitColor(int card) { int clr = card / 13; if((clr == 0) || (clr == 3)) return 0; return 1; } void move() { if(flagPaint != -1) return; flagPaint = 4; repaint(); } void moveAuto() { if(flagPaint != -1) return; flagPaint = 5; repaint(); } boolean gameOver() { for(int i = 7; i < 11; i++) { if(stack[i].getNCards() != 13) return false; } controller.gameOnDisplay = false; return true; } boolean gameLost() { return false; } public void paint(Graphics g) { if(flagPaint != -1) return; update(g); } public void doGameOver() { if(flagPaint != -1) return; flagPaint = 11; repaint(); } } class SolitaireStack { private int crdW, crdH; private int nCards; // No of cards on stack private int[] stack; private int closeCards; private int type; private int index; private int indexOpen; private SolitairePlayArea parent; private final int stackSize = 25; final int typeClose = 0; final int typeDone = 1; final int typePlay = 2; final int typeOpen = 3; public SolitaireStack(SolitairePlayArea parent, int index, int crdW, int crdH) { this.parent = parent; this.index = index; if(index < 7) { type = typePlay; } else { if(index < 11) { type = typeDone; } else { if(index == 11) { type = typeClose; } else { type = typeOpen; } } } this.crdW = crdW; this.crdH = crdH; stack = new int[stackSize]; reset(); } public void reset() { for(int i = 0; i < stackSize; i++) stack[i] = -1; closeCards = 0; nCards = 0; indexOpen = 0; } public void setOpenIndex(int n) { // index for open cards to be shift displayed sideways indexOpen = nCards - n; } public int topCard() { if(nCards < 1) return -1; return stack[nCards - 1]; } public boolean isEmpty() { if(nCards == 0) return true; return false; } private int xOff = 0; private int yOff = 0; public void setOffsets(Graphics g) { Dimension d = parent.size(); switch(type) { case typeClose: yOff = 2; xOff = 10; break; case typeOpen: yOff = 2; xOff = 10 + crdW + 20; break; case typeDone: yOff = 2; xOff = d.width - (4 - (index - 7)) * crdW; break; case typePlay: yOff = crdH + 9; int xGap = (d.width - 7 * crdW) / 8; xOff = xGap + index * (xGap + crdW); break; default: } } public boolean isInside(int x, int y) { if(type == typePlay) { if((x < xOff) || (x > (xOff + crdW)) || (y < yOff)) return false; return true; } if(type == typeDone) { if((x < xOff) || (x > (xOff + crdW)) || (y < yOff) || (y > (yOff + crdH))) return false; return true; } return false; } public int getPositionSelected(int x, int y) { if(nCards < 1) { if(type != typeClose) return -1; if((x < xOff) || (x > (xOff + crdW)) || (y < yOff) || (y > (yOff + crdH))) return -1; return -2; } if((nCards == closeCards) || (type != typePlay)) { // check top Card only if(isCardSelected(nCards - 1, x, y)) return nCards - 1; } else { for(int i = nCards - 1; i >= closeCards; i--) { if(isCardSelected(i, x, y)) return i; } } return -1; } private boolean isCardSelected(int i, int x, int y) { int xPos = getXPos(i); int yPos = getYPos(i); if((x < xPos) || (x > (xPos + crdW)) || (y < yPos) || (y > (yPos + crdH))) return false; return true; } public int getXPos(int i) { int xPos = xOff; switch(type) { case typeClose: xPos = xOff + ((int)(i / 8)) * 2; break; case typeOpen: if(i > indexOpen) xPos = xOff + (i - indexOpen) * xShow; break; default: } return xPos; } public int getYPos(int i) { int yPos = yOff; switch(type) { case typeClose: yPos = yOff + ((int)(i / 8)) * 2; break; case typeOpen: if(i > indexOpen) yPos = yOff + (i - indexOpen) * 2; break; case typePlay: if(nCards < 1) return yOff; if(closeCards == 0) return yOff + yShow * i; if(i < closeCards) return yOff + i * 2; yPos = yOff + closeCards * 2 + (i - closeCards) * yShow; break; default: } return yPos; } void addCard(int card, boolean openFlag) { nCards++; stack[nCards - 1] = card; if(!openFlag) closeCards = nCards; } void putCard(Graphics g, int card, boolean status) { addCard(card, status); drawTopCard(g); } int removeCard(Graphics g) { if(nCards == 0) return -1; int retCard = stack[nCards - 1]; drawCard(g, false, nCards - 1); nCards--; stack[nCards] = -1; if(closeCards > nCards) closeCards = nCards; drawTopCard(g); return retCard; } int getNCards() { return nCards; } int getIthCard(int i) { return stack[i]; } int getType() { return type; } private int yShow = 17; private int xShow = 20; void paintSelection(Graphics g, int i) { parent.drawSelectionImage(g, getXPos(i), getYPos(i), crdW, (nCards - i - 1) * yShow + crdH); } void drawCard(Graphics g, boolean drawFlag, int j) { if((j >= closeCards) && drawFlag) { parent.paintCard(g, drawFlag, getXPos(j), getYPos(j), stack[j], type); } else { parent.paintCard(g, drawFlag, getXPos(j), getYPos(j), 52, type); } } void drawTopCard(Graphics g) { if(isEmpty()) { drawCard(g, false, 0); } else { drawCard(g, true, nCards - 1); } } public void drawStack(Graphics g) { if(isEmpty()) { drawCard(g, false, 0); } else { for(int j = 0; j < nCards; j++) drawCard(g, true, j); } } public void eraseStack(Graphics g) { if(!isEmpty()) { for(int i = nCards -1; i >= 0; i--) drawCard(g, false, i); } } boolean isTopCardClosed() { if(nCards <= closeCards) return true; return false; } }