/* The card game "Kings in the Corner", as a Java Applet * Written by Dan Thurston in the year 2003 */ package kings; import java.awt.*; import java.applet.*; import java.util.Random; import java.awt.image.*; import java.awt.MediaTracker; public class kings extends java.applet.Applet { int clicked = -1; /* Whether or not a card is selected, used in removal mode */ int current = 0; /* the card at the top of the deck */ int status = 0; /* the status of the game, placing mode, removing mode, "You Win" or "You Lose" */ int cardWidth = 43; /* Width of a Card, used to break up cards.gif into an array */ int cardHeight = 61; /* Height of a Card, used to break up cards.gif into an array */ int[] deck = new int[52]; /* The deck of cards, a stack of 52 integers, use % to determine which card is which */ int[] board = new int[16]; /* the playing board */ Graphics gScratch; /* extra Graphics object for double buffering */ Image winScratch; /* extra image object for double buffering */ Image cards[] = new Image[52]; /* deck of images, initially, they match up to the numbers in deck[] */ String helpString = ""; /* the help and error messages to be displayed. Almost constantly updated */ Font f = new Font("TimesRoman", Font.BOLD, 36); /* the font for "You Win!" and "You Lose!" */ Font normal = new Font("TimesRoman", Font.PLAIN, 12); /* the font used for the help messages */ /* addNotify() * I had to make my own addNotify() because of a bug in the Virtual Machine, which * can make the computer lock up if you declare a new Graphics object, which I had * to do to enable double buffering. Bascially, I needed to make sure that the * machine's main Graphics object was created before mine was. */ public void addNotify() { super.addNotify(); /* calls the real addNotify() */ winScratch = createImage(300,300); /* creates my image */ gScratch = winScratch.getGraphics(); /* initialized my new Graphics object */ } /* end addNotify() override */ /* allDown() * Tests to see if you've won, by checking all of the spaces where face cards * should go. Returns true if you've won, and false if you haven't */ public boolean allDown() { if(board[1]%13 == 11 && board[2]%13 == 11 && board[13]%13 == 11 && board[14]%13 == 11 && board[4]%13 == 12 && board[7]%13 == 12 && board[8]%13 == 12 && board[11]%13 == 12 && board[0]%13 == 0 && board[3]%13 == 0 && board[12]%13 == 0 && board[15]%13 == 0 && board[0] != 0 && board[3] != 0 && board[12] != 0 && board[15] != 0) { return true; } /* end if */ return false; } /* end alldown() */ /* blocked() * Checks to see if all of a face card's places are occupied. * If there are, then you lose, because there's no place to put it. * Returns true if blocked, false if not. */ public boolean blocked() { if(deck[current]%13 == 11 && board[1] != 0 && board[2] != 0 && board[13] != 0 && board[14] != 0) { /* if Jacks */ return true; } /* end if */ if(deck[current]%13 == 12 && board[4] != 0 && board[7] != 0 && board[8] != 0 && board[11] != 0) { /* if Queens */ return true; } /* end if */ if(deck[current]%13 == 0 && board[0] != 0 && board[3] != 0 && board[12] != 0 && board[15] != 0) { /* if Kings */ return true; } /* end if */ return false; } /* end blocked() */ /* init() * Sets up all of the vital variables. Splits up cards.gif into 52 individual * images, in the array deck[], and sets the background color. * I used one image, cards.gif, because it's faster to download one big image * than 52 small ones. It also uses MediaTracker to make sure that the images is * fully loaded before the player can start. */ public void init() { int cardPos; /* which offset in the image */ ImageFilter cardFilter; /* used to crop parts of the image */ ImageProducer cardProducer; /* produces new images from the cropped pieces */ Image deckOfCards; /* holds cards.gif */ MediaTracker tracker = new MediaTracker(this); /* creates a MediaTracker to make the program wait for the images to load */ deckOfCards = getImage(getCodeBase(), "cards.gif"); /* load the cards.gif file */ tracker.addImage(deckOfCards, 0); /* asks MediaTracker to keep track of deckOfCards */ try { tracker.waitForID(0); /* asks the program to wait for deckOfCards to be fully loaded */ } /* end try */ catch (InterruptedException e) { return; } /* end catch */ for(int j = 0; j < 52; j++) { /* cuts deckOfCards into 52 smaller images */ deck[j] = j+1; cardPos = (j * cardWidth); cardFilter = new CropImageFilter(cardPos, 0, cardWidth, cardHeight); cardProducer = new FilteredImageSource(deckOfCards.getSource(),cardFilter); cards[j] = createImage(cardProducer); } /* end for */ cardFilter = null; /* null for garbage collection */ cardProducer = null; /* null for garbage collection */ tracker = null; /* null for garbage collection */ setBackground(Color.green); /* set the background color */ shuffle(); /* shuffles the deck */ } /* end init() */ /* isFull() * Checks to see if all of the card slots are full, triggering the removal * phase of the game. Returns true if full, false if not. */ public boolean isFull() { for(int j = 0; j < 16; j++) { if(board[j] == 0) { return false; } /* end if */ } /* end for */ return true; } /* end isFull() */ /* mouseUp() * Most of the work is done here. Called every time the mouse is clicked. * Does different things based on the mode of play, and whether a card has * already been selected by the clicked integer. */ public boolean mouseUp(Event evt, int x, int y) { int place = whichClicked(x, y); /* tells where the mouse was clicked */ switch(status) { /* does different things based on what mode the game is in */ case 0: /* if the game's in card placing mode */ helpString = "Place the cards."; if (place == -1) { /* If you clicked outside of the playing area */ clicked = -1; repaint(); break; } /* end place if */ if(board[place] == 0) { /* if the place is empty */ if(deck[current]%13 == 11 && place != 1 && place != 2 && place != 13 && place != 14) { /* if you're trying to put a Jack where it doesn't go */ helpString = "Jacks go on the top and bottom."; break; } /* end if */ else if(deck[current]%13 == 12 && place != 4 && place != 7 && place != 8 && place != 11) { /* if you're trying to put a Queen where it doesn't go */ helpString = "Queens go on the sides."; break; } /* end if */ else if(deck[current]%13 == 0 && place != 0 && place != 3 && place != 12 && place != 15) { /* if you're trying to put a King where it doesn't go */ helpString = "Kings go in the corners."; break; } /* end if */ board[place] = deck[current]; /* puts the card in the place */ current++; /* moves down the stack to the next card on the deck */ } /* end if */ break; case 1: /* if the game's in card removal mode */ helpString = "Remove cards adding up to 10."; if (place == -1) { /* if you clicked outside of the playing area */ clicked = -1; repaint(); break; } /* end place if */ if (clicked < 0) { /* if you haven't selected another card yet */ if (board[place]%13 == 10) { /* if a 10 was selected, just remove it */ board[place] = 0; break; } /* end 10 if */ if(board[place] == 0) { /* if an empty place was selected, just ignore */ helpString = "Remove cards adding up to 10."; break; } /* end if */ else if(board[place]%13 > 10 || board[place]%13 == 0) { /* if it's a face card, write an error message and ignore it */ helpString = "You can't remove face cards."; break; } /* end if */ else { /* or, set that place to selected, and wait for the next card to be clicked */ clicked = whichClicked(x, y); break; } /* end if */ } /* end if */ if(board[clicked]%13 + board[place]%13 == 10 /* checks that the first selected card and the newly */ && clicked != place) { /* selected one are different cards and add up to 10 */ board[clicked] = 0; /* removes the first card */ board[place] = 0; /* removes the second card */ } /* end if */ if(!removesLeft()) { /* checks to see if more can be removed */ helpString = "Place the cards."; status = 0; /* sets the mode back to placing mode */ } /* end removesLeft if */ clicked = -1; /* resets clicked so that no cards are selected */ break; case 2: /* means that the game is over, and so clicking resets everything */ case 3: shuffle(); status = 0; break; } /* end status switch */ if(allDown()) { /* if you win */ helpString = "Congratulations! Click to replay."; status = 2; /* sets the game to "You Win" mode */ } /* end if */ if(isFull() && !removesLeft()) { /* if you can't remove cards, but the board is full */ helpString = "Sorry, no pairs add up to 10. Click to replay."; status = 3; /* sets the game to "You Lose" mode */ } /* end if */ else if(isFull()) { /* if the board is full, switches to remove mode */ helpString = "Remove cards adding up to 10."; status = 1; /* sets the game to removing mode */ } /* end empty if */ else if(current == 51) { /* if you made it to the bottom of the deck */ helpString = "Congratulations! Click to replay."; status = 2; /* sets it to "You Win" mode */ } /* end if */ else if(blocked() && status == 0) { /* if the face card is blocked, and you're not in removing mode */ helpString = "No place to put the face card. Click to replay."; status = 3; /* sets it to "You Lose" mode */ } /* end blocked if */ repaint(); /* redraws the screen */ return true; } /* end mouseUp() */ /* paint() * Draws the board and the cards. This is where the cards.gif images are used. * Uses double buffering, meaning that the program draws to a single image, gScratch, the same * size as the background before finally writing gScratch to the screen all at once. * This prevents flickering and makes everything look smooth. */ public void paint(java.awt.Graphics g) { gScratch.setColor(this.getBackground()); /* gets the color of the background */ gScratch.fillRect(0,0,getSize().width,getSize().height); /* removes the previous images by filling the background with the background color */ gScratch.setColor(Color.black); /* sets the drawing color to black */ for (int j = 0; j < 16; j++) { if (board[j] == 0) { /* if there's no card at that position */ gScratch.setColor(Color.darkGray); /* sets the color to dark gray */ gScratch.fillRoundRect( (j % 4 * cardWidth) + (j % 4 * 5), (j / 4 * cardHeight) + (j / 4 * 5), cardWidth, cardHeight, 10, 10); /* draws the rectangles where the cards go */ gScratch.setColor(Color.black); /* resets the color to black */ } /* end if */ else if (j == clicked) { /* if the player selected a card during removal phase. Uses XORMode to make it look different */ gScratch.setXORMode(Color.black); /* sets XORMode so that the card is drawn differently */ gScratch.drawImage(cards[board[j] - 1], (j % 4 * cardWidth) + (j % 4 * 5), (j / 4 * cardHeight) + (j / 4 * 5), this); gScratch.setPaintMode(); /* resets the paint mode, so that everything else is drawn normally */ } /* end else if */ else { gScratch.drawImage(cards[board[j] - 1], (j % 4 * cardWidth) + (j % 4 * 5), (j / 4 * cardHeight) + (j / 4 * 5), this); /* draws the cards */ } /* end else */ } /* end for */ gScratch.setColor(Color.white); /* sets the drawingcolor to white */ gScratch.fillRect(20,275,250,15); /* draws the white help rectangle */ gScratch.setColor(Color.black); /* sets the drawing color to black */ gScratch.drawString(helpString, 25, 287); /* draws the black help message */ gScratch.drawImage(cards[deck[current] - 1], 255, 150, this); /* draws the "on deck" card */ switch(status) { /* to tell if the game's over or not */ case 0: /* if placing mode */ case 1: /* if removing mode */ break; case 2: /* if you won */ gScratch.setFont(f); gScratch.drawString("You Win", 100, 150); gScratch.setFont(normal); break; case 3: /* if you lost */ gScratch.setFont(f); gScratch.drawString("You Lose", 100, 150); gScratch.setFont(normal); break; } /* end status switch */ g.drawImage(winScratch,0,0,this); /* draws the buffer to the screen all at once */ } /* end paint() */ /* update() * Simply calls paint() */ public final void update(Graphics g) { paint(g); } /* end update() */ /* removesLeft() * Checks to see if you can remove any more cards during the removal phase. * If you can't, then the game returns to the card placing phase. * Returns true if you can remove more, false if you can't */ public boolean removesLeft() { for(int j = 0; j < 16; j++) { switch(board[j]%13) { /* ignores face cards and empty spaces */ case 11: /* Jacks */ case 12: /* Queens */ case 0: /* Kings */ continue; case 10: /* if the card's a 10, which needs no partner for removal */ return true; default: for(int w = 0; w < 16; w++) { /* checks to see if this card, plus another equal 10 */ if(j == w) { /* skips checking against itself */ continue; } /* end if */ else if(board[j]%13 + board[w]%13 == 10) { /* checks to see if this card, plus another equal 10 */ return true; } /* end 10 else if */ } /* end w for */ } /* end switch */ } /* end j for */ return false; } /* end removesLeft() */ /* shuffle() * Shuffles the deck by swapping sets of two randomly chosen cards. * Also sets the card pointer to the top of the deck and clears the board. */ public void shuffle() { Random generator = new Random(); /* generates a new string of pseudorandom numbers */ for(int j = 0; j <= 50; j++) { int first = generator.nextInt(52); /* Selects the first card */ int second = generator.nextInt(52); /* Selects the second card */ int temp = deck[first]; /* swaps the two cards */ deck[first] = deck[second]; deck[second] = temp; } /* end for */ for (int w = 0; w < 16; w++) { /* clears the board by resetting all of the card places to 0 */ board[w] = 0; } /* end for */ helpString = "Place the cards"; /* sets the help string */ current = 0; /* sets the card pointer to the top of the deck */ } /* end shuffle() */ /* whichClicked() * Helps out mouseUp by telling it which, if any, card place was clicked. * Returns the place if there was one, and -1 if no place was clicked on. */ public int whichClicked(int x, int y) { if(x < 188 && y < 264) { int column = (x*4)/188; int row = (y*4)/264; return (4*row+1 * column+1) - 1; } /* end if */ return -1; } /* end whichClicked() */ } /* end kings class */