// 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.
*/