// CS100M Spring 2001 Sample Solutions to P7 // instances of the FreeCell game // note: multiple instances are allowed public class FreeCell { // game has three "arenas": free cells, home cells, columns, in that order. final static int FREE = 0; final static int HOME = 1; final static int COL = 2; // name of each arena and one-letter abbrevation of each arena final static String[] NAMES = { "Free cell", "Home cell", "Column"}; final static String[] ABBREV = { "f", "h", "c" } ; // create the arenas and collect together into one array Pile[] free = new Pile[4]; Pile[] home = new Pile[4]; Pile[] column = new Pile[8]; Pile[][] pile = new Pile[][] { free, home, column }; // return 2-character bottom card for non-empty pile $p$ \ plus // return $"[]"$ for empty pile $p$ / padding space private String displayCells(Pile[] p) { String s = ""; for (int i = 0; i depth) { u += column[i].top(depth) + " "; go = true; } else u += depth == 0 ? "[] " : " "; System.out.println(u); } } // search for empty pile $s$ or for card $s$ at the bottom of a pile. // return ${ j, i }$ if $i$-th pile in arena $j$ is a match; // return $null$ otherwise public int[] find(String s) { for (int j = 0; j 0) { to = pile[destination[0]][destination[1]]; if (to.count() == 0) { if (destination[0] == HOME && from.bot(0).value() != Card.ACE) err += " first card on home cell must be an Ace\n"; } else if (destination[0] == HOME) { if (from.bot(0).suit() != to.bot(0).suit()) err += " suit does not match home cell suit\n"; if (from.bot(0).value() != 1+to.bot(0).value()) err += " value is not one more than card in home cell\n"; } else if (destination[0] == COL) { if (from.bot(0).isred() == to.bot(0).isred()) err += " colors are not different\n"; if (1+from.bot(0).value() != to.bot(0).value()) err += " value is not one less than card in column\n"; } else // destination is non-empty free cell err += " free cells can hold at most 1 card each\n"; } if (err.length() > 0) return "illegal move:\n" + err; from.deal(to); return "OK"; } // return "has the game been won?" (home cells hold all 52 cards?) public boolean win() { int count = 0; for (int i = 0; i0; i++) deck.deal(column[i % column.length]); } // play the game until the user wins or quits; // read moves from $in$ public void play(TokenReader in) { for (boolean go = true; go && !win(); ) { display(); System.out.print("What is your move? "); String src = in.readString().toLowerCase(); if (src.equals("quit") || src.equals("stop")) go = false; else if (src.equals("?") || src.equals("help")) { System.out.println ( "to quit: $quit$\n" + "to get help: $help$ or $?$\n" + "to move card $a$ atop card $b$: $a b$"); for (int i = 0; i0; i--) { int j = (int) (Math.random() * (i+1)); // swap cards at positions i, j Card c = cards[i]; cards[i] = cards[j]; cards[j] = c; } } // deal 1 card from bottom of self onto bottom of $destination$ public void deal(Pile destination) { size--; destination.cards[destination.size] = cards[size]; destination.size++; } } /* notes on class $Pile$ above: + $size$ is maintained by all the methods: constructor $Pile$ and method $move$ adjust size appropriately to reflect the number of cards in each pile ($size++;$, $size--;$, $destination.size++;$). + $scramble$ has been made private: if it were public, then it would be possible to (eventually) access any card in the pile: simply repeatedly scramble until the desired card appears on the bottom. probability theory says it will eventually happen. + since only method $deal$ can modify a pile, + to move a card from or to a pile, it must be moved from or to another pile, so all the "arenas" in free cell must be $Pile$s. + furthermore, this prevents others from: + re-arranging cards within a pile + putting the same card into multiple piles + losing a card (e.g. drop behind the couch) notes on how to prevent even more "cheating": + the code above allows others to create any number of decks of cards, so it is still possible for class $FreeCell$ to cheat by using cards from extra decks. + we could eliminate that problem if class $Pile$ were to supply only one deck of cards and not allow any others to be created: + include a static variable for the unique deck: $final static Pile DECK = new Pile(true);$ + make the old constructor $private$, so no one else can make a full deck. + include a nullary public constructor that calls the private constructor: $public Pile() { this(false); }$ here, $this$ is treated as a method (like how $super$ is used), which java understands to mean "a constructor for the current class" (as opposed to "a constructor for the super-class"). this is good style: if we modify the body of the private constructor, we can probably leave the public constructor unchanged. + note that this means only one card game can be run, since multiple instances of the game would have to share the same deck of cards. */