//------------------------------------------------------- // // This Applet was created by David Heine // // dlheine@leland.stanford.edu // November 6, 1995 // // Feel free to borrow and use the code as you like // and send me pointers to your newer, improved versions. // or derivative stuff. // // Modified November 7, 1995 // Give each card an front and back image. // Give each pile a default image // Give each pile a highlight type. // Move all location info to the Solitaire class. // // Modified January 8, 1995 // Beta2 broke Dimension Cloning. // // Version: 0.2 //-------------------------------------------------------- import java.awt.*; import java.applet.Applet; class Card { private int num; private int suit; private Card next_card; private Card prev_card; private boolean faceUp; private String faceStr[] = {" A", " 2", " 3", " 4", " 5", " 6", " 7", " 8", " 9", "10", " J", " Q", " K"}; private String suitStr[] = {" S", " H", " D", " C"}; private Image back_image; private Image card_image; public Card(int n, int s, Applet app, Image BackImage) { String filename; num = n; suit = s; next_card = null; prev_card = null; faceUp = false; back_image = BackImage; filename = "image/card_" +faceStr[num].trim() + "_" + suitStr[suit].trim() + ".GIF"; card_image = app.getImage(app.getCodeBase(), filename); } public void set(int n, int s) { num = n; suit = s; } public Card NextCard() { return(next_card); } public Card PrevCard() { return(prev_card); } public boolean FaceUp() { return(faceUp); } public void setFaceUp() { faceUp = true; } public void unsetFaceUp() { faceUp = false; } public int CardNum() { return(num); } public int CardSuit() { return(suit); } public String PrintStr() { String faceS; String suitS; if (this == null) { return(" ---- "); } if (!faceUp) { return(" Down "); } return(" " + faceStr[num] + suitStr[suit] + " "); } public Image GetImage() { if (faceUp) { return(card_image); } return(back_image); } public void Print() { System.out.print(PrintStr()); } public String PrintAllStr() { Card nowCard; String theString; theString = PrintStr(); nowCard = this; if (nowCard != null) { nowCard = nowCard.next_card; } while (nowCard != null) { theString += nowCard.PrintStr(); nowCard = nowCard.next_card; } return(theString); } public void PrintAll() { System.out.print(PrintAllStr()); } // unlink from a doubly linked list public void Unlink() { Card prev; Card next; prev = prev_card; next = next_card; if (prev != null) { // not first card in pile prev.next_card = next_card; } prev_card = null; if (next != null) { // not last card in pile next.prev_card = prev; } next_card = null; } // this card must point nowhere public void AddBefore(Card next) { Card tmp_prev; tmp_prev = next.prev_card; if (tmp_prev != null) { tmp_prev.next_card = this; prev_card = tmp_prev; } next_card = next; next.prev_card = this; } // this card must point nowhere public void AddAfter(Card prev) { Card tmp_next; tmp_next = prev.next_card; if (tmp_next != null) { tmp_next.prev_card = this; next_card = tmp_next; } prev_card = prev; prev.next_card = this; } // recursive definition. public Card GetCardNum(int slot) { Card card; card = this; while (--slot >= 0) { card = card.next_card; } return(card); } }; class Pile { private Card top_card; private Card bottom_card; private int num_cards; private boolean needs_update; private boolean isHand; private Rectangle d; private Point hand_loc; private int pile_type; private int pile_value; private int pile_highlight; private Image blank_image; private static Applet app; final static int NO_HIGHLIGHT = 0; public Pile(Rectangle rect, Point handLoc, int PileType, int PileValue, Image BlankImage, Applet applet) { app = applet; top_card = null; bottom_card = null; num_cards = 0; needs_update = true; isHand = false; d = rect; hand_loc = handLoc; pile_type = PileType; pile_value = PileValue; pile_highlight = NO_HIGHLIGHT; blank_image = BlankImage; } public void setLoc(int x, int y) { d.x = x; d.y = y; } public Card TopCard() { return(top_card); } public int PileType() { return(pile_type); } public int PileValue() { return(pile_value); } public boolean IsHand() { return(isHand); } public void setIsHand() { isHand = true; } public void unsetIsHand() { isHand = false; } public String PrintStr() { if (top_card == null) { return(" ---- "); } // print it all if (isHand) { return(top_card.PrintAllStr()); } else { return(top_card.PrintStr()); } } public void Print() { System.out.print(PrintStr()); } public int NumCards() { return(num_cards); } // Card must be alone public void AddCard(Card card) { needs_update = true; if (top_card == null) { top_card = card; bottom_card = card; num_cards++; return; } card.AddBefore(top_card); top_card = card; num_cards++; } // Card must be alone public void AddCardAfter(Card card, Card prev_card) { needs_update = true; if (top_card == null) { top_card = card; bottom_card = card; num_cards++; return; } card.AddAfter(prev_card); num_cards++; } public void RemoveCard(Card card) { if (card == top_card) { needs_update = true; if (card == bottom_card) { top_card = null; bottom_card = null; // No need to "unlink it" } top_card = card.NextCard(); } card.Unlink(); num_cards--; } public void MoveCardTo(Card card, Pile toPile) { RemoveCard(card); toPile.AddCard(card); } public void MoveCardAfter(Card card, Card prev_card, Pile toPile) { RemoveCard(card); toPile.AddCardAfter(card, prev_card); } public void Shuffle() { Card nowCard; int nCards = NumCards(); int count = nCards * 3; while (--count >= 0) { // Assume Math.random never returns 1.0? int slot1 = (int) (Math.random() * nCards); nowCard = top_card.GetCardNum(slot1); // move it to the top MoveCardTo(nowCard, this); } } public void paintOne(Graphics g, Card card, boolean IsInHand, Rectangle loc) { String name; Image image = null; g.clearRect(loc.x, loc.y, loc.width-1, loc.height-1); g.drawRect(loc.x, loc.y, loc.width-1, loc.height-1); if (IsInHand) { image = blank_image; name = " ==== "; } else if (card == null) { image = blank_image; name = " ---- "; } else { image = card.GetImage(); name = card.PrintStr(); } if (image != null) { g.drawImage(image, loc.x, loc.y, loc.width, loc.height, app); } else { g.drawString(name, loc.x, loc.y+g.getFont().getSize()); } } public void paint(Graphics g) { paintOne(g, TopCard(), isHand, d); if (isHand) { Dimension loc; Rectangle my_rect; Card card = TopCard(); int col = 0; int row = 0; while(card != null) { // Beta2 broke this // my_rect = (Rectangle) d.clone(); // my_rect.move(hand_loc.x + col*d.width, // hand_loc.y + row*d.height); my_rect = new Rectangle(hand_loc.x + col*d.width, hand_loc.y + row*d.height, d.width, d.height); paintOne(g, card, false, my_rect); col++; if (col == 7) { col = 0; row++; } card = card.NextCard(); } } } // card is null if it is not this pile // return true and set the play or // return false and set nothing public boolean isThisPile(CardPlay play, Point point) { if (isHand) { Rectangle my_rect; Card card = TopCard(); int col = 0; int row = 0; while(card != null) { // Beta2 broke this // my_rect = (Rectangle) d.clone(); // my_rect.move(hand_loc.x + col*d.width, // hand_loc.y + row*d.height); my_rect = new Rectangle(hand_loc.x + col*d.width, hand_loc.y + row*d.height, d.width, d.height); if (my_rect.inside(point.x, point.y)) { play.card = card; play.fromPile = this; play.toPile = this; return(true); } col++; if (col == 7) { col = 0; row++; } card = card.NextCard(); } return(false); } if (d.inside(point.x, point.y)) { play.card = TopCard(); play.fromPile = this; play.toPile = this; return(true); } return(false); } }; class Deck { // Deck of cards. private Card cards[] = new Card[52]; private final static int NNUMS = 13; private final static int NSUITS = 4; public Deck(Pile pile, Dimension card_size, Image cards_image, Image back_image, Applet app) { int num, suit; java.awt.image.CropImageFilter crop_image; Card card; // initialize cards for (suit = 0; suit < NSUITS; suit++) { for (num = 0; num < NNUMS; num++) { card = new Card(num, suit, app, back_image); pile.AddCard(card); cards[num + NNUMS*suit] = card; } } } }; class CardPlay { public Card card; public Pile fromPile; public Pile toPile; public CardPlay() { } }; class Solitaire { public final static int ACE_CARD=0; public final static int KING_CARD=12; public final static int NUM_PILE=0; public final static int ACES_PILE=1; public final static int KINGS_PILE=2; public final static int PLAY_PILE=3; public final static int ORIG_PILE=4; public final static int FLOAT_PILE=5; private Pile AcesPile[] = new Pile[4]; private Pile KingsPile[] = new Pile[4]; private Pile NumPile[] = new Pile[13]; private Pile PlayPile; private Pile OrigPile; private Pile HandPile; // points to the NumPile in the hand private Deck deck1; private Deck deck2; private Image SuitImage[] = new Image[4]; private Image CardsImage; public Image BackImage; public Image BlankImage; private String SuitImageFile[] = {"image/spade.GIF", "image/heart.GIF", "image/diamond.GIF", "image/club.GIF"}; private String BackImageFile = "image/back.GIF"; private String BlankImageFile = "image/blank.GIF"; private Applet app; final static Point base = new Point(50, 75); public Dimension CardSize; private static int next_pile_num = 0; // creates a location from a simpler x, y coordinate space public Point createLoc(double row, double col) { Point point = new Point(base.x + (int)(col*CardSize.width), base.y + (int)(row*CardSize.height)); return(point); } public Solitaire(Applet applet, Dimension card_size, Image cards_image) { app = applet; CardSize = card_size; CardsImage = cards_image; // Initialize the other images for (int i=0; i< 4; i++) { SuitImage[i] = app.getImage(app.getCodeBase(), SuitImageFile[i]); } BackImage = app.getImage(app.getCodeBase(), BackImageFile); BlankImage = app.getImage(app.getCodeBase(), BlankImageFile); Point handLoc = createLoc(6,0); System.out.println(handLoc); HandPile = null; PlayPile = new Pile(new Rectangle(createLoc(4.5, 4.5), CardSize), handLoc, PLAY_PILE, 0, BlankImage, app); OrigPile = new Pile(new Rectangle(createLoc(4.5, 3.5), CardSize), handLoc, ORIG_PILE, 0, BlankImage, app); Pile pile; for (int i = 0; i<4; i++) { pile = new Pile(new Rectangle(createLoc(0, i), CardSize), handLoc, ACES_PILE, i, SuitImage[i], app); AcesPile[i] = pile; } for (int i = 0; i<4; i++) { pile = new Pile(new Rectangle(createLoc(1+i, 6), CardSize), handLoc, KINGS_PILE, i, SuitImage[i], app); KingsPile[i] = pile; } for (int i = 0; i<5; i++) { pile = new Pile(new Rectangle(createLoc(2,i), CardSize), handLoc, NUM_PILE, i, BlankImage, app); NumPile[i] = pile; } for (int i=5; i<10; i++) { pile = new Pile(new Rectangle(createLoc(3,i-5), CardSize), handLoc, NUM_PILE, i, BlankImage, app); NumPile[i] = pile; } for (int i=10; i<13; i++) { pile = new Pile(new Rectangle(createLoc(4,i-10), CardSize), handLoc, NUM_PILE, i, BlankImage, app); NumPile[i] = pile; } deck1 = new Deck(OrigPile, CardSize, CardsImage, BackImage, applet); deck2 = new Deck(OrigPile, CardSize, CardsImage, BackImage, applet); } public void Shuffle() { // Shuffle OrigPile.Shuffle(); } public void MakePlay(CardPlay play) { play.fromPile.MoveCardTo(play.card, play.toPile); } public boolean getNextPlay(CardPlay play) { // walk through each of the face ups, then the hand to // find a play // int i; Card next_card; int suit; int num; for (i = ACE_CARD; i <= KING_CARD; i++) { next_card = NumPile[i].TopCard(); while (next_card != null) { suit = next_card.CardSuit(); num = next_card.CardNum(); play.card = next_card; play.fromPile = NumPile[i]; play.toPile = KingsPile[suit]; if (isValidPlay(play)) { return(true); } play.toPile = AcesPile[suit]; if (isValidPlay(play)) { return(true); } if (NumPile[i].IsHand()) { next_card = next_card.NextCard(); } else { next_card = null; } } } return(false); } public boolean isValidPlay(CardPlay play) { int num = play.card.CardNum(); int first_card, modifier; Card top_card = play.toPile.TopCard(); // Check for moving something from the PLAY_PILE // to a numpile. // switch(play.fromPile.PileType()) { case PLAY_PILE: if ((play.toPile.PileType() == NUM_PILE) && (play.toPile.PileValue() == play.card.CardNum())) { return(true); } else { return(false); } case NUM_PILE: break; default: return(false); } switch(play.toPile.PileType()) { case ACES_PILE: first_card = ACE_CARD; modifier = +1; break; case KINGS_PILE: first_card = KING_CARD; modifier = -1; break; default: return(false); } // Check for the right suit. if (play.card.CardSuit() != play.toPile.PileValue()) { return(false); } // Moving to the aces or kings pile if (top_card == null) { if (first_card == num) { return(true); } return(false); } if (num == top_card.CardNum()+modifier) { return(true); } return(false); } public void Deal() { // Deal 'em while (OrigPile.NumCards() > 0) { Deal1(); } } public void Deal1() { Card card; int extraCards; int num = next_pile_num; int cardNum; // Deal a card if (OrigPile.NumCards() > 0) { extraCards = 0; card = OrigPile.TopCard(); card.setFaceUp(); cardNum = card.CardNum(); if (cardNum == num) { extraCards += 3; } if (cardNum == ACE_CARD) { extraCards += 2; } if (cardNum == KING_CARD) { extraCards += 1; } next_pile_num = num+1; if (num == KING_CARD) { extraCards+=3; next_pile_num = ACE_CARD; } OrigPile.MoveCardTo(card, NumPile[num]); num = next_pile_num; while (extraCards > 0) { card = OrigPile.TopCard(); if (card != null) { OrigPile.MoveCardTo(card, PlayPile); } extraCards--; } } } public int numUpCards() { return(PlayPile.NumCards()); } public boolean MovesLeft() { // Return true if there are more moves CardPlay nextPlay = new CardPlay(); return(getNextPlay(nextPlay)); } public boolean Play1Card() { // Play Cards CardPlay nextPlay = new CardPlay(); if (getNextPlay(nextPlay)) { MakePlay(nextPlay); return(true); } return(false); } public void PlayCards() { // Play Cards while (Play1Card()) { } } public boolean TurnPlayPile() { // unset the handpile if (HandPile != null) { HandPile.unsetIsHand(); HandPile = null; } // Play Cards Card card = PlayPile.TopCard(); if (card == null) { return(false); } card.setFaceUp(); PlayPile.MoveCardTo(card, NumPile[card.CardNum()]); // Move the target pile to the HAND HandPile = NumPile[card.CardNum()]; HandPile.setIsHand(); return(true); } public int numPlayedCards() { int numCards = 0; int i; for (i = 0; i< 4; i++) { numCards += KingsPile[i].NumCards(); } for (i = 0; i< 4; i++) { numCards += AcesPile[i].NumCards(); } return(numCards); } public boolean CheckWin() { if (numPlayedCards() == 104) { return(true); } return(false); } public String PrintStr() { int i=0; String theString = new String(""); for (i = 0; i< 4; i++) { theString += AcesPile[i].PrintStr(); } theString += " "; theString += KingsPile[0].PrintStr(); theString += "\n"; for (i = 0; i< 5; i++) { if (NumPile[i].IsHand()) { theString += " Hand "; } else { theString += NumPile[i].PrintStr(); } } theString += KingsPile[1].PrintStr(); theString += "\n"; for (; i< 10; i++) { if (NumPile[i].IsHand()) { theString += " Hand "; } else { theString += NumPile[i].PrintStr(); } } theString += KingsPile[2].PrintStr(); theString += "\n"; for (; i< 13; i++) { if (NumPile[i].IsHand()) { theString += " Hand "; } else { theString += NumPile[i].PrintStr(); } } theString += OrigPile.PrintStr(); theString += PlayPile.PrintStr(); theString += KingsPile[3].PrintStr(); theString += "\n"; theString += "\n"; if (HandPile != null) { theString += HandPile.PrintStr(); } theString += "\n"; theString += "\n"; return(theString); } public void Print() { System.out.print(PrintStr()); } public void CleanUp() { int i; Card card; for (i = 0; i < 4; i++) { // Kings while ((card = KingsPile[i].TopCard()) != null) { card.unsetFaceUp(); KingsPile[i].MoveCardTo(card, OrigPile); } } for (i = 0; i < 4; i++) { // Aces while ((card = AcesPile[i].TopCard()) != null) { card.unsetFaceUp(); AcesPile[i].MoveCardTo(card, OrigPile); } } for (i = 0; i< 13; i++) { // Num piles if (NumPile[i].IsHand()) { NumPile[i].unsetIsHand(); HandPile = null; } while ((card = NumPile[i].TopCard()) != null) { card.unsetFaceUp(); NumPile[i].MoveCardTo(card, OrigPile); } } // up-pile better be empty } public void paint(Graphics g) { int i; for (i = 0; i< 4; i++) { AcesPile[i].paint(g); } for (i = 0; i< 4; i++) { KingsPile[i].paint(g); } for (i = 0; i< 13; i++) { NumPile[i].paint(g); } PlayPile.paint(g); OrigPile.paint(g); } // play is allocated outside public boolean findFromCardFromLoc(CardPlay play, Point point) { int i; Card card; Pile pile; for (i = 0; i< 13; i++) { pile = NumPile[i]; if (pile.isThisPile(play, point)) { if (play.card != null) { return(true); } } } pile = PlayPile; if (pile.isThisPile(play, point)) { if (play.card != null) { return(true); } } pile = OrigPile; if (pile.isThisPile(play, point)) { if (play.card != null) { return(true); } } return(false); } public boolean findToPileFromLoc(CardPlay play, Point point) { int i; Pile pile; for (i = 0; i< 4; i++) { pile = AcesPile[i]; if (pile.isThisPile(play, point)) { return(true); } } for (i = 0; i< 4; i++) { pile = KingsPile[i]; if (pile.isThisPile(play, point)) { return(true); } } pile = PlayPile; if (pile.isThisPile(play, point)) { return(true); } pile = OrigPile; if (pile.isThisPile(play, point)) { return(true); } return(false); } }; public class SolitaireApplet extends java.applet.Applet { int win = 0; int lose = 0; final String SHUFFLE_BUTTON = "Shuffle"; final String DEAL1_BUTTON = "Deal1"; final String DEAL_BUTTON = "Deal"; final String TURN_BUTTON = "Turn"; final String PLAY1_BUTTON = "Play1"; final String PLAY_BUTTON = "Play"; final String PLAYALL_BUTTON = "PlayAll"; final String RESTART_BUTTON = "Restart"; String infoStr = null; Button shuffleB = new Button(SHUFFLE_BUTTON); Button deal1B = new Button(DEAL1_BUTTON); Button dealB = new Button(DEAL_BUTTON); Button turnB = new Button(TURN_BUTTON); Button play1B = new Button(PLAY1_BUTTON); Button playB = new Button(PLAY_BUTTON); Button playAllB = new Button(PLAYALL_BUTTON); Button restartB = new Button(RESTART_BUTTON); Solitaire sol; CardPlay fromPlay; Card prev_card_in_hand_pile; Dimension CardSize; Image CardsImage; public void init() { // Read in the cardfile CardsImage = getImage(getCodeBase(), "cards.GIF"); CardSize = new Dimension(50, 100); StartGame(); } private void StartGame() { //resize(600, 600); resize(CardSize.width*9, CardSize.height*9); //add(shuffleB); //add(deal1B); //add(dealB); add(turnB); add(play1B); add(playB); add(playAllB); add(restartB); sol = new Solitaire(this, CardSize, CardsImage); sol.Shuffle(); sol.Deal(); repaint(); } private void setCardSize(int width, int height) { CardSize = new Dimension((width+3)/13-4, (height+3)/4-4); } public void setDimensions(int width, int height) { setCardSize(width, height); } public void imageComplete(int status) { StartGame(); } // OK, so I need to use a smarter algorithm for redraws // that only redraws the effective area. public void paint(Graphics g) { String solString; String subs; int cnt = 2; Dimension d = size(); g.clearRect(0, 0, d.width - 1, d.height - 1); g.drawRect(0, 0, d.width - 1, d.height - 1); if (sol != null) { g.drawString("Win = "+win + " --- Lose = "+lose + "; Down Cards = "+ sol.numUpCards() + "; Played Cards = "+ sol.numPlayedCards() , 0, 25*cnt ); cnt++; // Paint each of the piles sol.paint(g); // Paint the flying card also if (fromPlay != null) { fromPlay.toPile.paint(g); } } if (infoStr != null) { g.drawString(infoStr, 0, 25*cnt); } } public boolean action(Event evt, Object obj) { if (evt.target instanceof Button) { infoStr = null; String label = (String)obj; if (label.equals(SHUFFLE_BUTTON)) { sol.Shuffle(); repaint(); return(true); } if (label.equals(DEAL_BUTTON)) { sol.Deal(); repaint(); return(true); } if (label.equals(DEAL1_BUTTON)) { sol.Deal1(); repaint(); return(true); } if (label.equals(TURN_BUTTON)) { if (sol.MovesLeft()) { infoStr = "There are more moves left"; } else { if (sol.TurnPlayPile() == false) { if (sol.CheckWin()) { infoStr = "WIN !!"; repaint(); win++; } else { infoStr = "LOSE !!"; repaint(); lose++; } } } repaint(); return(true); } if (label.equals(PLAY1_BUTTON)) { if (sol.Play1Card() == false) { infoStr = "No more cards to play. Turn over another"; } repaint(); return(true); } if (label.equals(PLAY_BUTTON)) { sol.PlayCards(); repaint(); return(true); } if (label.equals(PLAYALL_BUTTON)) { sol.PlayCards(); while (sol.TurnPlayPile()) { sol.PlayCards(); } //if (sol.CheckWin()) { //infoStr = "WIN !!"; //win++; //} else { //infoStr = "LOSE !!"; //lose++; //} repaint(); return(true); } if (label.equals(RESTART_BUTTON)) { if (sol.CheckWin()) { win++; } else { lose++; } sol.CleanUp(); sol.Shuffle(); sol.Deal(); repaint(); return(true); } } repaint(); return(false); } // public boolean mouseDown(Event evt, int x, int y) { infoStr = ""; Point eventPoint = new Point(x, y); fromPlay = new CardPlay(); if (sol.findFromCardFromLoc(fromPlay, eventPoint)) { // Check for turning a card if ((fromPlay.fromPile.PileType() == Solitaire.PLAY_PILE) || (fromPlay.fromPile.PileType() == Solitaire.PLAY_PILE)) { infoStr = "Turn pile " + fromPlay.card.PrintStr(); fromPlay.card.setFaceUp(); } infoStr += "Pick up card " + fromPlay.card.PrintStr(); // When we pick up a card, we place a clone of it // in a temporary buffer and put the card face down. // If it comes form the hand, place the old card face down // otherwise, pick off the old card // The card will be placed in the toPile. fromPlay.toPile = new Pile(new Rectangle( new Point(x-CardSize.width/2, y-CardSize.height/2), CardSize), new Point(0,0), Solitaire.FLOAT_PILE, 0, sol.BlankImage, this); prev_card_in_hand_pile = fromPlay.card.PrevCard(); fromPlay.fromPile.MoveCardTo(fromPlay.card, fromPlay.toPile); repaint(); return(true); } infoStr = "MOUSE DOWN ignored"; fromPlay = null; repaint(); return(false); } public boolean mouseDrag(Event evt, int x, int y) { if (fromPlay != null) { fromPlay.toPile.setLoc(x-CardSize.width/2, y-CardSize.height/2); repaint(); return(true); } return(false); } private void replaceFromPlay() { if (prev_card_in_hand_pile == null) { fromPlay.toPile.MoveCardTo(fromPlay.card, fromPlay.fromPile); } else { fromPlay.toPile.MoveCardAfter(fromPlay.card, prev_card_in_hand_pile, fromPlay.fromPile); } prev_card_in_hand_pile = null; fromPlay.toPile = null; } public boolean mouseUp(Event evt, int x, int y) { CardPlay toPlay; infoStr = ""; Point eventPoint = new Point(x, y); if (fromPlay == null) { return(false); } // Put that card back replaceFromPlay(); toPlay = new CardPlay(); if (! sol.findToPileFromLoc(toPlay, eventPoint)) { infoStr = "Invalid Move"; } else { // Check for turning a card if ((fromPlay.fromPile.PileType() == Solitaire.PLAY_PILE) || (fromPlay.fromPile.PileType() == Solitaire.PLAY_PILE)) { if (fromPlay.fromPile == toPlay.toPile) { sol.TurnPlayPile(); } } else { CardPlay thePlay = new CardPlay(); thePlay.card = fromPlay.card; thePlay.fromPile = fromPlay.fromPile; thePlay.toPile = toPlay.toPile; if (sol.isValidPlay(thePlay)) { sol.MakePlay(thePlay); infoStr = "Played card " + thePlay.card.PrintStr(); } else { infoStr = "Can't move to this pile"; } } } fromPlay = null; if (sol.CheckWin()) { infoStr += " ___Win___"; } repaint(); return(true); } }