What this is about...

This blog is about how I spend some of my time with my computers.

100GOTO100

Various topics on Computing, Operating Systems, Programming Languages, Computer Games, Internet and Communications

Tuesday, August 3, 2010

Ubuntu and my favourite keyboard IBM SK-8835 (.xmodmap & KEYSYMNAME)

Let me start with this: I'm a trackpoint fan and I only use a touchpad if I absolutely MUST. Given a choice, I would go for the trackpoint , then the mouse, and finally the dreaded touchpad. The reason is that using the pad moves my index fingers away from the typing position. So much for pad gestures...

Anyhow, for quite some time now I have been ordering keyboards from IBM (Ultranavs) in various configurations, from full keyboards with numeric keypads (for my desktops) to space saving versions for some of my old laptops.

My present keyboard model is an old USB IBM Ultranav (an SK-8835 model) which I use with my primary Ubuntu server machine. It’s quite non-standard as far as keyboards go (see picture), so I needed to make some changes to accommodate the various key locations.

Since it’s Ubuntu I am using, I had to find a way to change the keyboard layouts so that the keys match exactly.



After some googling around I found references to the .xmodmap file. After reading some more, I decided that this was the simplest way of doing it so here is what I did:

This file is read by the X software upon login and modifies the keyboard according to the instructions in the file. The instructions consist of lines starting with a keyword: keycode followed by a number (i.e. 18), an equal sign (=) and then a series KEYSYMNAME definitions.

You only need to put in the file the keycodes that need changing.

This is the format:

!Put a comment here

keycode 9 = Escape Escape Escape Escape
keycode 20 = apostrophe question apostrophe degree 


the keycode is a number generated each time a key is pressed. The keycode number can be generated/discovered using the xev command in any terminal window. By opening an editor, anyone can create a list of the keycodes generated by their keyboard. Just press the key, find the number, and record it in the editor file along with some detailed description of the characters on the key. Save the file under a name that describes its function i.e. KeycodesIBMSK8835.

The  KEYSYMNAME definitions can be found in "the header file (without the XK_ prefix) or the keysym database /lib/X11/XKeysymDB, where refers to the root of the X11 install tree. Note that if the same keysym is bound to multiple keys, the expression is executed for each matching keycode." See here for more details.

There is a maximum of 4 KEYSYMNAME definitions for each of my keys, the first two for the GBr (UK) keyboard layout, and two for the Gre (Greek) keyboard layout.

So, here are the contents for my layout:

keycode 49 = backslash bar onehalf plusminus
keycode 10 = 1 exclam 1 exclam
keycode 11 = 2 quotedbl 2 quotedbl
keycode 12 = 3 sterling 3 numbersign
keycode 13 = 4 dollar 4 dollar
keycode 14 = 5 percent 5 percent
keycode 15 = 6 ampersand 6 notsign
keycode 16 = 7 slash 7 slash
keycode 17 = 8 parenleft 8 parenleft
keycode 18 = 9 parenright 9 parenright
keycode 19 = 0 equal 0 equal
keycode 20 = apostrophe question apostrophe degree
keycode 21 = plus asterisk plus asterisk
keycode 34 = bracketleft braceleft bracketleft leftdoublequotemark
keycode 35 = bracketright braceright bracketright rightdoublequotemark
keycode 48 = rightsinglequotemark leftsinglequotemark caret asciitilde
keycode 51 = acute at twosuperior threesuperior
keycode 94 = less greater paragraph copyright
keycode 59 = comma semicolon comma semicolon
keycode 60 = period colon period colon
keycode 61 = minus underscore minus Greek_horizbar

And that did it! When I saved the file under the name .xmodmap, after a logout/login gnome asked me to read the file. I said yes, and now, everykey is at the right place.

Backup/Clonezilla and we are safe!




Tuesday, March 2, 2010

Learning Java (& SWT & Eclipse) by example: A simple game of patience (solitaire)

As an exercise in Eclipse, Java, SWT and Model-View-Controller, I programmed the following patience (or solitaire, if you prefer) card game that was shown to me by a high-school friend back in 1977-78!
First, let me say that I did not “design” the program and then built it based on some formal design specs. I tried to use an “unfolding” process (like the method Christopher Alexander uses in his timeless way to build); I started by some general decision on concepts like Model-View-Controller, or the auto-play mode. I also made some software decisions i.e. to use Java, Eclipse and the SWT toolkit. Then, with the knowledge of how the game worked, I started to write the classes and the glue code. My programming method consisted of changing the classes in an iterative process, adding functionality, refining them and many times altering them substantially, until they “stabilized” to this working version. It is also natural that the data elements and methods either reflect some physical element or process, or they are there to support one, because they reflect a “real'” game as I play it. The classes are not “perfectly coded,” nor do they have support for “future possibilities;” they were not created to support multiple future card games (although with some minor alterations they could). I specifically created them with this program in mind. I have not “factored out” reusable parts etc. this can always happen later on.
This process has made the “feel” of the program more organic. I tried some things, refined them, repaired them, created & deleted stuff, and so on. This version is not “final” in any sense, and its structure is based too strongly on the MVC pattern.
I decided to post the code in this blog for everyone to see and comment so that I can investigate options and  improve the program. Ultimately, I want to move on to Scala with actors, traits, and functional programming. I also want to investigate C++ programming under Ubuntu.
Since I created this program (and, in a way, the game), if you decide to use any parts of it, or recreate the game (or part of it) again please mention this blog and my efforts. I will gradually add/change the program to use resources (instead of external .gif files) and, perhaps, re-do it in C++ and some cross-platform IDE.
Before I describe the game itself, I present a list of the things I wanted the application to do and how I wanted it to do them (basic UI/functionality):
  • It should work in a manual, user-action mode but with as little ”mousing around” as possible. I though of implementing card dragging, but decided to keep that as an exercise for the next version. Most of the user actions are done by double clicking on widgets and buttons.
  • It should have an auto-play mode, where it could run any number of games for statistical purposes. The game window should update during autoplay so the user can see the moves, and the speed of the game should be changeable (using a slider control) by the user. 
  • It should have a window with the rules of the game, that the user can activate by pressing a button.
  • It should show the game’s statistics in a separate window floating over or under the game window.
  • If possible, it should not use threads, as this was my first exercise and I wanted the code to be as simple as possible.
  • It should not pause to shuffle the cards at the beginning of each game. With the exception of when the game runs for the first time, I shuffle while the user (or the system) plays the game without using threads, as follows: I use 2 decks and swap them in the beginning of a game. While the game progresses, in the main play loop, I send a shuffle message to the second (off-line) deck asking it to swap 2 random card locations n number of times (where n is presently between 1 and 3, but that can change). After the game is over, I have sent the shuffle message to the off-line deck a minimum of 52 times (if in autoplay mode and no triplets are removed from the columns), each time asking it to swap from 1 to 3 pairs of cards. The worst case scenario (in autoplay mode) is 52 card swaps, and the best is many thousands (as the game loops waiting for user input, or as the game removes triplets and deals again and again). Before the game starts, I make the off-line deck the playing deck and so on… I think this way I always start a game with a sufficiently shuffled deck without making the user wait.
Future additions:
  • Code to automatically detect a “stuck” game and recover.
  • Code to implement the dragging and dropping  of cards 
  • Some kind of animation when a card moves from one location to another.
So, the game itself is quite simple, here are the rules:
  1. Shuffle the deck.
  2. Start dealing cards in 7 columns, row by row: i.e. C1R1, C2R1, C3R1, … C7R1, C1R2, C2R2, … C7R2
  3. From the 3rd row and above, and for every dealt column, if the sum of the bottom 3 cards equals 10, 20 or 30, remove the cards and put them back to the bottom of the deck. Do the same if the bottom 2 cards and the top card also sum up to 10, 20 or 30. Do the same if the bottom 1 card and the top 2 cards add up. You must always remove 3 cards, never more or less, and from the locations listed above. Let’s call the combos B3, T1B2, and T2B1. No other combination is allowed. Not the top 3 cards, not more that 3 cards, even if they sum up to the 10/20/30 amount.
  4. If, after removing a triplet, there are still other valid triplets in that column, you can remove them too (if you want, I always do) at any time before dealing again to that column. If you missed a triplet and after dealing you realize that you should have moved the cards to the deck, sorry, you cannot take that back!
  5. If, after removing a triplet, the column has no cards left, that column is not dealt from the deck again until the end of the game. This way the number of columns can decrease during the game, and you get closer to winning!
  6. If you can find no more valid triplets to remove (from any column), you deal to the next (non-empty) column.
  7. You win if you manage to migrate all cards from all columns back to the deck.
  8. You lose if you have no more cards to deal from the deck and no triplets can be removed anymore from any column (or the game is “stuck”, see Note 1 below).
  9. The cards score points according to the following numeric values (for the addition): Ace is 1, two is 2, three is 3, etc, ten is 10, Jack is 10, Queen is 10, King is 10.
  10. That’s it.
Note 1: it is possible for this game to reach a point where the player can repeat the same moves but the game cannot produce an outcome. This is what I call a “stuck game”, and is equivalent to losing because the cards cannot return to the deck (game lasts indefinitely, unless you stop it).
Here is a screenshot of the main screen at the beginning of a game under the Windows OS:
Main Game Screen
and again under Ubuntu:
Ubuntu Game Window
Note 2: I downnoaded the card images from the web. Specifically, from here:
As things are now, you will need the card images as external files in a subdirectory named resources. It should be located under the directory where the compiled files are located.

The deck is called the Doods deck by Katzmiff (2005). It can be used under the GNU License (it was in the rar file). Same license holds for my program, as well.
Let me explain a bit the graphical format of the window: On the left side of the screen I deal the cards on 7 columns. Right below those you can see 3 buttons in each column representing the 3 possible card-removal combinations: B3 (bottom 3 cards), T2B1 (top 2 bottom 1 card), and T1B2 (top 1 bottom 2 cards). The user can press any one of these 21 buttons to indicate to the program his/her wish. If the cards can be removed, they will be put back to the bottom of the deck (which stands to the right of the seventh –last- column). If not, nothing happens no matter how much you click!
On the right side of the window, there are some additional buttons and controls:
  1. A deal button. If pressed, a card is dealt to the next appropriate column. Obviously, empty columns will be skipped, and the column that will be dealt next is indicated by a small multi-colored square at the bottom of the window under the buttons.
    The player can also use the following other methods to deal a card from the deck: double-click anywhere on the green area of the table where no other object is located, or, double-click on the deck (which is between the last column and the deal button). Under the deck, the player can see the remaining number of cards.
  2. An auto-play checkbox. If selected, the program will go into auto-play mode where it will play consecutive games endlessly while updating the statistics window (more on that below).
    There are 2 additional controls that influence the auto-play behaviour: a slider with which the user can slow down the updating of the window, and a text entry in which the user can input a specific number of games to be played before checking auto-play. The game will gray-out the text box and count down the number.
  3. A New Game button to start a new game in mid-play, and
  4. An Exit Game Button to close the application (similar to the close button of the window).
There is another window floating on the screen:
Before I go on, please go to the source code post. Copy and paste the Java code in Eclipse and have it open while reading the explanations below, so that you can see the details. For best results, use the editor facilities to collapse and expand the code (or use multiple editors to view different parts of the file).
Some preliminaries:
  1. All classes in this application are prefixed with P7_.
  2. There is only one application thread. I deliberately tried to avoid any additional sync logic in order to keep the program as simple as possible for this initial version. Of course, the SWT and other Java system threads are still operating behind the scenes.
  3. I consciously tried to limit as much as possible object proliferation. I did this because I wanted as much speed as possible and, in my mind, since most identifiers in Java are references to objects, the more one uses the new keyword, the more work for the garbage collection thread, loading the JVM with work. 
  4. The program reads the card and other images from external .gif files that must be located in the subdirectory held in the following String variables in the P7_FixedElements class:
    (i) PileIndicatorFileNameWindows
    (ii) CardImageFilePrefixWindows[] 
    (iii) PileIndicatorFileNameUnix
    (iv) CardImageFilePrefixUnix[]
    One of the future changes will be to create a package that will contain the “resources” so that there will be no need for separate files and/or directories (like netbeans does).
  5. In the program, I use the term Pile to refer to a column of cards. The two terms are interchangeable in this text.
What follows is a list of the classes and explanations on their function.
  • There are two global enums, P7_Suit, and P7_Rank for the obvious usage.
  • I have defined a class called P7_FixedElements as a central repository to create & hold the following global-like identifiers:

    (i) the SWT elements of the UI,
    (II) the game related constants & variables like the 2 decks, the statistics object, various strings, etc.,
    (iii) references to the created Model, View & Controller instances, and
    (iv) some debugging methods like a simple abortApplication(String S) which ends the program with a user message, or the pressAnyKey() which waits for the user to press a key.

    This class was created to solve a repeating problem I had while writing the code: where to define, create and store the various utility parts needed by the application, small and large. For example, the UI parts (graphics constants, and buttons, and windows, and dialogs, and fonts etc), the strings, which I wanted all in one place, (but neither the Model nor the View or the Controller were proper places in my mind), and the decks (which, in reality, exist in a drawer even when no-one is playing). Finally, what evolved was a smaller View class, containing the game related methods and through those, we get to this class which holds an abundance of the more elemental parts, a brick-a-brak, if you like. This way, the game code is more explicit, but this class is like a storehouse.
  • I have created a P7_GameView class to express the View part of the Model-View-Controller paradigm. Here I put the methods that reflect the results of user or program actions in the program windows. This class manipulates the UI fixed elements defined and created in the previous class. Its methods are called from the controller class primarily after some action (user or program based) has taken place.  The methods are:
    (i) the constructor. It takes the Game Model object as a parameter, as it needs the data in the objects contained there in order to display them to the player.
    (ii) updateGraphicalElements() shows the latest statistics, the deck and all the piles. It is called before & after a game to display the final state of the table to the player.
    (iii) showDeckAndPile() takes a deck and a column as parameters and shows them to the player. It is usually called after a user event on either the deck or some column.
    (iv) showTheWindows() send the open message to the Shell objects each time a game starts and after the creation of all the graphical elements.
    (v) then we have all the show methods: showCard(), showPile(), showStatistics(), and showAllPiles(). These do what their names suggest, i.e. they manipulate the SWT graphics objects to show the progressing of the game.
  • After the view class BUT before the controller class, I have defined four classes that extend Adapters (listeners) to handle the user events:
    (i) P7_DealButtonSelectionAdapter extends SelectionAdapter listens for user clicks on the deal button.
    Deal ButtonWhen this happens, the message processDealButton()is sent to the Controller. There, the Controller sends the deal() message to the Model object in order to update the Model data and the showDeck() and showPile() messages to the View object to update the UI.
    (ii) P7_PileButtonAction extends SelectionAdapter handles the input from buttons at the bottom of the window.
    Triplet ButtonsThere are 21 such buttons (3 rows x 7 columns) created by the P7_TripletButtonGrid class in the P7_FixedElements class. The user can either use these buttons to describe which triplet should be moved to the deck (B3=Bottom 3, T2B1=Top 2 Bottom 1, etc), or he can double-click on a (valid) card that describes (to the program) the triplet (more of that below).
    (iii) P7_DeckCardMouseAdapter extends MouseAdapter listens for double-clicks on the deck object and, as explained before sends the processDealButton() message to the Controller object.
    (iv) P7_PileCardMouseAdapter extends MouseAdapter listens for double-clicks on cards located in the columns. Specifically, if the user double-clicks on the first, second or third card from the bottom of any column, or on the top or second from the top card of any column, the processPileClick() message is sent to the Controller object along with the card (as a widget parameter) that was selected by the player. In that method, the Controller object (by asking the card, as will be explained in the Model description onwards) determines the column and row of the card, and sends messages to the Model object’s Pile objects to check if the triplet indicated by the card has a valid sum (again, we will have to get to the Model’s data objects for additional explanations). If not, nothing happens, otherwise, the cards are moved to the deck (messages send to the Model object) and the View is updated (again) with the showDeck() and showPile() messages. Please note this: as I was learning SWT by reading various code snippets, I started defining the event handlers and adapters as anonymous classes (parameters of the various add…Listener methods). As I learned more, I created the separate adapter classes listed above. However, some of the event handlers, I left where the widget was created in the P7_FixedElements class. Specifically, I did not put handlers in separate classes for the following controls:

    (i) the auto-play checkbox and the other related controls.
    (ii) the double-click in an empty area anywhere in the window that defaults to dealing a card.
    (iii) all those pop-up dialogs and confirmations. I will explain how all this works in the Model part further on.

    For now, all I can say is that the selection of these controls affects boolean and other variables in the Model with minimal other code so I thought that if I defined more classes it would obfuscate the program needlessly. However, for orthogonality, I am considering doing that in the future.
  • The Controller class is called P7_GameController and it has 9 methods and 1 constructor.
    The constructor has as parameters the P7_FixedElements object, the P7_GameView object and the P7_GameModel object because the controller needs to send messages to control both the Model of the game and its View. To do that it needs to know about the global elements, and its Model and View partners. I cannot use the FixedElements references to these entities yet, because when the FixedElements object is created, the MVC objects do not yet exist. The values at FixedElements construction time are null. Only after all the instances of MVC are created in the main() method (in the exact MVC order), I update the MVC references in FixedElements with an explicit message and from then on I can use them safely; there is only one instance of each.
    The Controller methods correspond to the processing of player events, or of automated play. Specifically, there is a process method for every major user event:
    (i) dealing a card event (button, double-click on deck or on the table -processDealButton()),
    (ii) double-clicking on a card to select a triplet (processPileClick()),
    (iii) exiting the program (window close, button - processUserExitRequest()).

    The play() method contains the main loop of the program. If autoplay is enabled, it evokes the runIt() method which deals and checks the column for triplets using the processPiles() method. If in manual mode, the loop evokes the SWT display sleep() method. The event listeners do all the work in manual mode.

    Finally, there are 3 other methods:
    (i) initialSetup() which shuffles the deck when the game runs for the first time and deals the first two rows of cards for every game afterwards;
    (ii) returnAllCardsToTheDeck() returns all cards to the deck at the end of a game;
    (iii) locateCard() returns the card that the user double-clicked on and is needed to identify the column/triplet.

    The methods were created as I “unfolded” the program logic.
  • Finally we get to the basic classes used by the Model:
    (The Card (P7_PlayingCard), the Pile/Column (P7_Pile/P7_PileIterator), the Deck (P7_PlayingDeck extends P7_Pile), the GameState (P7_GameState), and the GameModel (P7_GameModel). After those, there is one very simple class named PatienJava containing the main() method that just creates the basic game objects (FixedElements, Model, View, Controller) and then sends the play() message to the Controller continuously until it comes back with an exit flag raised.
    The P7_PlayingCard class is quite big. It evolved from being a simple Suit/Rank combo to a sophisticated object as I worked on the program. The main reason for this growth is that I store quite a bit of information for each card in the card itself. Most of this information if about the graphical appearance of the card: its SWT widget (a label), its coordinates (location) in the window, 2 image references (Face, Back), a reference to behavioral handlers which define the behavior of double-clicks depending on the location of the card (the deck or a column), flags to indicate if it is visible or if it is showing its face or its back to the player. I also use methods to set or reset these values. There are also two types of constructors: one to be used for creating “hollow” cards (for when the values are not yet known and will be created/assigned later), and one used to create “deep” cards when all the card properties are known.
    There is an assignment method:
    (assign(P7_PlayingCard Other))
    where a card’s values are replaced by the parameter’s values. However, this method will NOT do a deep copy, i.e. it will not create copies of heap created objects like the face & back images or the widgets. It will copy the references to these as per the Java paradigm. For this program, this is as designed, because it gains speed and there is no need to change the card’s face or back or widget type. Even if in future versions there is an option to use different decks (visual difference), the various graphic images will all be created when the game runs for the first time and, afterwards, the program will deal with references to these created objects. This approach also mirrors a real-life card’s attributes: a real card has a face & a back, both with specific graphics, has a location during play, is either visible or not, is “face-up” or not, can participate in various calculations & game procedures based on the location, or some rule or characteristic (this is implemented as the Adapter reference which the GameController and the GameModel can change/reset). The program deals with cards as if they were real, i.e. does not create and destroy them as virtual objects but rather passes their reference around just like we pass cards in a real game. In the end it returns all the cards from the columns to the deck. There are more methods to accommodate the needs of the game like switchCard() which swaps the values of the receiver with the playing card in the parameter.

    The next needed class is the column/pile class (P7_Pile). This class contains a P7_PlayingCard linked list with the column’s cards, a window location indicating its top/left (x,y) position, the column’s number (0..6), and the methods of the object.
    (i) The constructor takes the location and the index as parameters and creates an empty column.
    It can process these messages (methods):
    (ii) appendCard() adds a card at the bottom,
    (iii) getCardFromPosition(int Position) removes the card from the indicated position starting from the head down,
    (iv) referenceCard(int Pos) returns a reference to the cards but does NOT remove it from the column,
    (v) checkForT1B2Sum() or checkForT2B1Sum() or checkForB3Sum() all return a flag indicating if the triplet can be removed,
    (vi) outputPile() was used in debugging,
    (vii) get/setLocation(Point P) changes the values of the x,y coordinates of the column,
    (viii) lookUp() has 2 versions: one with a widget as a parameter and one with the card’s rank & suit as parameters. It returns a reference to the card that matches the parameters, and null if it cannot find it.
    (ix) getIndex() returns the column’s index (0..6)
    (x) swap(int I, int J) swaps the cards located at I & J respectively (from the top of the column).
    (xi) correctYValues() is a peculiar method; It is used to adjust the x,y location values in the cards of the column after a changes happens (i.e. the removal of a triplet). At some point during programming, I had to decide what would happen if a column was too long to fit in the game window. Instead of changing the window’s size, I chose to collapse some of the middle cards. Those cards are not part of any triplet so the player is not interested in them (other than the “visual” appearance of the “table.” I chose to create “mini-decks” when that happens, i.e. those cards all have the same location (the middle of the column) and they are face down, that is, the user cannot see their values but knows that the column has now “overflowed.” All these column will have that face-down card in their middle. Here is an screen shot:
    Main Game Window (Overflowed Column vs Just before)
    In this screen shot, you can see 2 columns that appear to be the same height, but only one of them has actually “overflowed” the space allocated to columns in the window; it is the one with the face-down card in the middle.

    I also created a P7_PileIterator class as a utility. It is like a regular int but, wraps around at the 0 and 6 boundaries. It does not “jump over” the empty columns though; the other classes check for that condition and act accordingly.

    A specialized case of the P7_Pile class is the P7_PlayingDeck.
    The constructor takes 2 arguments: the location (for the parent class), and a boolean that tells the class to construct a “hollow” deck to be used for copying. This is because in this constructor that the program creates the cards & reads their graphical images from the .gif files. If the deck will only copy another, there is no need to create new duplicate images in memory.
    It extends its parent class by adding the following methods:
    (i) the copy() method creates and returns a copy of the current deck,
    (ii) resetCardsLocation() visits all the cards in the deck and sets their x,y location to the deck’s x,y location. It also turns them face-down.
    (iii) shuffle(int Times) is the methods that shuffles the deck. It does that in two ways: the first is when its argument is less than 0. When that is the case, it will shuffle the deck for about 4 seconds before starting the game. If the argument is greater than 0, it will perform the indicated number of random card swaps.
    and it does
    (iv) P7_PlayingCard deal() returns the top card from the deck. It also changes the behavior of the card in the following ways: it instructs it to be open i.e. to show it’s “face,” and it also changes the cards double-click handler to handle column,instead of deck, logic.
    (v) boolean deckIsHealthy() checks a deck for completeness i.e. if all the cards for the game are in it. Returns true or false.
    (vi) static int lookupValue(P7_Rank R) is used when cards are being created to get the respective point values for the triplet sums.

    The GameModel class needs a small utility class called P7_GameState. In this class, which is used by the GameModel I store some needed variables for the present game:
    (i) StopGame is a boolean that indicates to the program if it should stop the game. It is used to exit loops.
    (ii) GameOver is a boolean that indicates to the program to exit completely. It gets this value from the various dialog boxes asking for player confirmation when the close window or the exit game buttons are pressed.
    (iii) NumberOfGamesToPlay holds the number of games to play in auto-play mode. –1 means play indefinitely. It is used when the player wants to see the success rate of the game.
    (iv) AutoPlayDelay gets its value from the slider to slow down execution during auto-play.
    (v) finally, CurrentPile is an interator that holds the current column in the game.

    At last, we got to the P7_GameModel class. In this class I create the columns and the game state objects. The 2 decks for the game are created in the P7_FixedElements class and they exist for the duration of the play.
    Its constructor takes a P7_GameModel class as an argument, because it will need access to the model in order to display the game’s progress.
    The rest of the class consists of methods for the manipulation of the cards & the statistics during the game:
    (i) numberOfCardsInDeck() is self explanatory; it returns the number of cards remaining in the deck.
    (ii) initializeGame() resets the flags that stop or end the game, returns any leftover cards from the columns back to the deck, and resets the current column to the first.
    (iii) subtractGameCounter() is used to decrease the game counter in autoplay and to update the value in the text field so that the user can see how many games are still to be played.
    (iv) deck() returns a reference to the current deck.
    (v) currentPile(),currentPileNo() returns a reference to or the number of the current column/pile.
    (vi) setStopGame(boolean B), getStopGame() set and get the boolean var that signifies that the game must or has stopped.
    (vii) areAllPilesEmpty() returns true if they are, and false if not.
    (viii) processSuccess() increments the total games played variable and the total successes variable in the statistics class object.
    (ix) processFailure() increments the total number of games played variable and the total failures variable in the statistics class object.
    (x) fromPile2Deck(P7_PlayingCard PC, boolean MoveCardsYLocation) changes the card in 2 ways: (a) makes it hidden again, and (b) sets its double-click handler to that of a deck card before it moves it from the column back to the bottom of the deck.
    (xi) the removeB3(…), removeT1B2(), & removeT2B1() methods move the corresponding triplet back to the deck.
    (xii) deal2Pile() deals the top card to the column indicated by GameState object. Returns true if a card was dealt or false if not, meaning the deck had no more cards to deal , so the game was lost.
    (xiii) the findNextValidPile() is used to locate the next column to deal to. It skips over any empty columns and returns true if a valid column was found, else it returns false: the game is won. The swapDecks() is used at the beginning of a game to switch to the offline shuffled deck.
  • We are now at the main class called PatienJava which only contains the public static void main(String[] args) method where execution of the program begins, the args are not used. Here 2 things happen: (a) the instances of the P7_FixedElements and of the P7_GameModel, P7_GameView, and P7_GameController classes are created, and (b) we loop while the player wants to keep playing games.
    Each loop is a game returning a continuation yes/no boolean.
    The last dispose method returns the resources to the OS before exiting the program.
  • In the end, there is one class that I copied (as is seen in the code) from the net to implement the evocation of the browser to show this blog.
  • And that’s it!

Nestor Louizidis

Create your badge

Monday, February 22, 2010

Source Code

import java.lang.reflect.Method;
import javax.swing.JOptionPane;
import java.util.Arrays;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.MessageBox;
import org.eclipse.swt.widgets.Display; 
import org.eclipse.swt.widgets.Label; 
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Slider;
import org.eclipse.swt.widgets.Text;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.FontData;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;

enum P7_Suit { Diamonds, Clubs, Hearts, Spades  }
enum P7_Rank { Ace, Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten, Jack, Queen, King }

class P7_Statistics {
    protected long GamesPlayed;
    protected long Successes;
    protected long Failures;
    protected long NumberOfGamesToPlay=-1;
    protected float pctSuccess() {
        if (GamesPlayed>0) {
            float F=(float)Successes/(float)GamesPlayed;
            return(F);
        } else
            return((float)0);
    }
    protected float pctFailures() {
        if (GamesPlayed>0) {
            float F=(float)Failures/(float)GamesPlayed; 
            return(F);
        } else
            return((float)0); }
    protected P7_Statistics() {
        GamesPlayed=0;
        Successes=0;
        Failures=0;
    } // end method constructor
} // End class P7_Statistics

class P7_ButtonData {
    int Pile;
    String Action;
    P7_ButtonData(int P, String A) {
        Pile=P;
        Action=A;
    } // end method constructor
} // end class P7_ButtonData

class P7_FixedElements {
    private static P7_GameController GCtl;
    private static P7_GameModel GModel;
    private static P7_GameView GView;
    protected P7_FixedElements setGameMVC(P7_GameModel M, P7_GameView V, P7_GameController C) {
        GCtl=C; GModel=M; GView=V; return(this);
    } // end method set MVC
    protected static P7_GameModel getModel() { return(GModel); }
    protected static P7_GameView getView() { return(GView); }
    protected static P7_GameController getCtl() { return(GCtl); }

/*
* Graphical elements & Constants-----------------------------------------------------------
*/
    class P7_TripletButtonGrid { // Creates the bottom button grid.
        Button[][] Choices= new Button[P7_FixedElements.SUMITEMS][P7_FixedElements.NUMBEROFPILES];
        P7_TripletButtonGrid(Shell s, P7_GameModel GM, P7_GameView GV) {
            int X=P7_FixedElements.PILEORIGINX,
                Y=P7_FixedElements.WINDOWHEIGHT-(P7_FixedElements.BUTTONHEIGHT*P7_FixedElements.SUMITEMS)-(4*P7_FixedElements.BORDERSIZE);
            for (int R=0; R<P7_FixedElements.SUMITEMS; ++R) {
                for (int C=0; C<P7_FixedElements.NUMBEROFPILES; ++C) {
//                    System.out.println("X->"+X+", Y->"+Y);
                    Choices[R][C]= new Button(s, SWT.CENTER|SWT.PUSH);
                    Choices[R][C].setLocation(new Point(X,Y));
                    Choices[R][C].setText(P7_FixedElements.ChoicesText[R]);
                    Choices[R][C].setSize(P7_FixedElements.CARDWIDTH, P7_FixedElements.BUTTONHEIGHT);
                    Choices[R][C].moveAbove(null);
                    Choices[R][C].setData(new P7_ButtonData(C, P7_FixedElements.ChoicesText[R]));
//                    Choices[R][C].setBackground(GameBackgoundColor);
                    X=X+P7_FixedElements.CARDWIDTH+P7_FixedElements.BORDERSIZE;
//                    final Button Bb=Choices[R][C];
                    Choices[R][C].addSelectionListener(new P7_PileButtonAction(Choices[R][C]));
                } // end for C
                X=P7_FixedElements.PILEORIGINX;
                Y=Y+P7_FixedElements.BUTTONHEIGHT; //+P7_FixedElements.BORDERSIZE;
            } // end for R
        } // end method constructor
    } // end class P7_TripletButtonGrid
    protected static Image PileIndicatorImage;
    protected static Color GameBackgoundColor, TextForegroundColor;
    protected static final int WINDOWWIDTH=800; //620
    protected static final int WINDOWHEIGHT=650; //450
    protected static final int CARDWIDTH=71;
    protected static final int CARDHEIGHT=96;
    protected static final int PILEDISTANCE=80;
    protected static final int PILEORIGINX=5; // Coordinate X of the drawing location of the 1st pile
    protected static final int PILEORIGINY=5; // Coordinate Y of the drawing location of the 1st pile
    protected static final int BORDERSIZE=10;
    protected static final int CARDVISIBILITYDIVISORY=4;
    protected static final int CARDVISIBILITYDIVISORX=3;
    protected static final int VISIBLECARDPARTY= CARDHEIGHT/CARDVISIBILITYDIVISORY;
    protected static final int NUMBEROFPILES= 7;
    protected static final int DECKXLOCATION=(BORDERSIZE+CARDWIDTH)*(NUMBEROFPILES)+BORDERSIZE;
    protected static final int MAINWINDOWINITIALX=50;
    protected static final int MAINWINDOWINITIALY=50;
    protected static final int BUTTONWIDTH=100;
    protected static final int BUTTONHEIGHT= 30;
    protected static final int BUTTONFONTSIZE= 10;
    protected static final int BUTTONSXLOCATION=WINDOWWIDTH-BUTTONWIDTH-BORDERSIZE;
    protected static final int BUTTONSYORIGIN=5;
    protected static final int MINIMUMSCREENHEIGHT= 480;
    protected static final int MINIMUMSCREENWIDTH=640;
    protected static final int MAXNONSTACKEDCARDS= (WINDOWHEIGHT-P7_FixedElements.BORDERSIZE-3*P7_FixedElements.BUTTONHEIGHT-(P7_FixedElements.CARDHEIGHT-P7_FixedElements.VISIBLECARDPARTY))/VISIBLECARDPARTY;
    protected static Display D;
    private static Shell shell, info, rulesW;
    private static Font font; // the larger button font & other uses...
    protected static Button DealB, ExitGameB, AutoPlayB, NewGameB, RulesB, BlogB;
    private static Label ClearPileArea;
    protected static Label PileIndicator, NumberOfCardsInDeck;
    protected static Slider Speed;
    protected static Label[] A= new Label[4];
    protected static Label[] T= new Label[4];
    protected static Text NumberOfGamesToPlayT;
    protected static P7_TripletButtonGrid Sel;
/*
* Game related Constants & Objects----------------------------------------------------------------
*/
    protected static Random Generator = new Random();
    protected static P7_PlayingDeck PlDk, Deck01, Deck02, Shfl;
    protected static P7_Statistics Stats;
    protected static int CardPoints[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10, 10, 10 };
    protected static boolean Autoplay=false;
    protected static boolean FirstTime=true;
    protected static final String CARDFILENAMEEXTENSION= ".gif";
    protected static final int MAXDIGITSINMAXNUMBEROFGAMES=7;
    protected static final int SUMITEMS= 3;
    protected static final int NUMBEROFCARDSINSUIT=13;
    protected static final int NUMBEROFCARDSINDECK = 52;
    protected static final int NUMBEROFALLCARDSINDECK = 54;
    protected static final int SUFFLINGTIME = 4000;
    protected static final int MAXTIMEDELAYFORAUTOPLAY=1500;
    protected static final int NUMBEROFSUITS=4;
    protected static final int TOPCARDSTODISPLAY= (MAXNONSTACKEDCARDS/2)-1;
    protected static final int BOTTOMCARDSTODISPLAY=MAXNONSTACKEDCARDS/2;
    protected static final boolean KEEPYPOSITION=false;
    protected static final boolean ADVANCEYPOSITION=true;
    protected static final Point Ll=new Point(DECKXLOCATION, PILEORIGINY);
    private static final String Commentary[]= {
         "Written during 1Q2010 by NPL (
http://100goto100.blogspot.com/)",
         "I wrote this game to learn the Eclipse IDE, Java, and SWT.",
         "Please go to my blog to discuss the program, ways to improve it",
         "and alternative ways to do it. i.e using threads for a better UI."
    };
    protected static final String GameName= "7 Columns Solitaire";
    protected static final String ChoicesText[]= { "B3", "T2B1", "T1B2" };
    protected static String Platform;
    protected static Boolean WindowsOS= true;
    protected static String PileIndicatorFileName, CardImageFilePrefix[]= {" ", " " };
    protected static final String PileIndicatorFileNameWindows="bin\\resources\\PileIndicator.gif"; 
    protected static String CardImageFilePrefixWindows[]= { "bin\\resources\\10", "bin\\resources\\1" };
    protected static final String PileIndicatorFileNameUnix="./resources/PileIndicator.gif";
    protected static String CardImageFilePrefixUnix[]= { "./resources/10", "./resources//1" };

    private static final String BlogAddress= "http://100GOTO100.blogspot.com";
    private static final String Rules=
    "Player shuffles the deck. (Done automatcally in the game) \n"+
    "Player deals one by one cards in 7 vertical piles from the Deck.\n"+
    "While dealing the third row, for any 3 combo (as per below) that adds up to 10 or 20 or 30,\n"+
    "the player is free to move the three cards from the pile to the bottom of the deck.\n"+
    "There can be more than one combinations of valid triplets, and the player must choose\n"+
    "which triplet to remove. If after removing the cards there are still valid triplets in\n"+
    "the pile, the player is free to move them to the deck too, until no valid triplet exists anymore.\n"+
    "To deal a card from the deck, the player can hit the deal button, or double click on the deck,\n"+
    "or double click inside the window in any free area.\n\n"+
    "Once a card is dealt, the next one will be dealt to the next non-empty pile.\n"+
    "This is indicated with a small three-coloured box beneath the bottom row of buttons.\n"+
    "For any (non-empty, of course) pile, the player can double click on a card to select:\n\n"+
    " a) the 3 bottom cards (double click on the third card from the bottom), or\n"+
    " b) the 2 top cards and the 1 bottom card (double click on the bottom card), or\n"+
    " c) the top 1 card and the 2 bottom cards (double click on the second card from the bottom).\n\n"+
    "The sum of these cards must be 10, or 20 or 30. Nothing will happen otherwise.\n"+
    "If, after dealing a card, a pile contains only 3 cards with the correct sum (10, 20, or 30)\n"+
    "then removing the cards will leave the pile empty (which moves the player closer to winning),\n"+
    "and will be skipped (will not be dealt to) for the rest of the game.\n\n"+
    "Player wins when all the cards return to the deck.\n"+
    "Player loses when the deck becomes empty (all cards are dealt to the piles, and\n"+
    "no eligible triplets can be lifted from any pile).\n"+
    "If autoplay is checked, dealing will become automatic, and the table will update\n"+
    "at the speed selected by the slider. While in normal mode, the user can enter a number of games\n"+
    "in the text field indected as such. When Autoplay is selected, the game will try to play this\n"+
    "number of games, while updating the statistics. The textbox becomes inactive but counts down the\n"+
    "number of games still left to be played. Please be aware that the game can become 'stuck', i.e.\n"+
    "it can play the same moves forever (for now, until I can figure a way to detect this), and so never\n"+
    "terminate. If this happens, the user can interfere by going into normal (manual) mode and dealing\n"+
    "the cards without removing them from the piles. When the deck is out of cards, the games will be\n"+
    "marked as lost. At that point the user can re-check the autoplay and resume the statistics.\n";
/*
* Constructors ------------------------------------------------------------------------
*/
    protected P7_FixedElements() { // constructor
        D= new Display();
        shell= new Shell(P7_FixedElements.D, SWT.CLOSE | SWT.TITLE | SWT.MIN | SWT.MAX);
        info= new Shell(P7_FixedElements.D, SWT.TITLE);
        rulesW= new Shell(info, SWT.CLOSE | SWT.TITLE | SWT.MIN | SWT.MAX);
        GameBackgoundColor= P7_FixedElements.D.getSystemColor(SWT.COLOR_DARK_GREEN);
        TextForegroundColor= P7_FixedElements.D.getSystemColor(SWT.COLOR_WHITE);
//         Create a larger font for the buttons
        FontData[] fd = shell.getFont().getFontData();
        for (int i = 0; i < fd.length; i++) {
            fd[i].setHeight(P7_FixedElements.BUTTONFONTSIZE);
        } // end for i
        font = new Font(P7_FixedElements.D,fd);
        try {
            Platform= SWT.getPlatform();
            if (Platform.startsWith("win")) {
                WindowsOS=true;
                P7_FixedElements.PileIndicatorFileName=P7_FixedElements.PileIndicatorFileNameWindows;
            } else {
                WindowsOS=false;
                P7_FixedElements.PileIndicatorFileName=P7_FixedElements.PileIndicatorFileNameUnix;
            } // end else
            FileInputStream fis = new FileInputStream(PileIndicatorFileName);
            PileIndicatorImage= new Image(D, PileIndicatorFileName);
        }
        catch (FileNotFoundException ex) {
            abortApplication("Indicator Image File: "+PileIndicatorFileName+" not found. Abrupt end of program!");
        } // end catch
        Deck01=new P7_PlayingDeck(Ll, false);
        Deck02=Deck01.copy();
        PlDk= Deck01; // Start Playing with deck01
        Shfl= Deck02;  // Assign Deck02 to be shuffled while playing
        Stats= new P7_Statistics();
        setupGraphicalElements();
        // call the D.getBounds or the D.getClientArea to determine if the screen has enough room to show the game
    } //end constructor P7_FixedElements
    protected static Shell getMainWindow() { return(shell); }
    protected static Shell getInfoWindow() { return(info); }
    protected static Shell getRulesWindow() { return(rulesW); }
    protected void cleanUp() { // cleanup/dispose of SWT Objects
//        rulesW.dispose();
        info.dispose();
        shell.dispose();
        D.dispose();
    } // end of View.cleanUp
    private void setUpMainWindowAttributesAndListeners() {
        shell.setText("NPL's PatienJava (V0.1) "+GameName+" - Running under "+SWT.getPlatform()+", and SWT Version "+SWT.getVersion());
        shell.setLocation(P7_FixedElements.MAINWINDOWINITIALX, P7_FixedElements.MAINWINDOWINITIALY);
        shell.setBounds(P7_FixedElements.MAINWINDOWINITIALX, P7_FixedElements.MAINWINDOWINITIALY, P7_FixedElements.WINDOWWIDTH, P7_FixedElements.WINDOWHEIGHT);
        shell.setBackground(GameBackgoundColor);
        shell.addMouseListener(new MouseListener() { // Double clicking anywhere in the window deals a card
                public void mouseDown(MouseEvent e) {}
                public void mouseUp(MouseEvent e) {} // Label l = new Label(s, SWT.FLAT); l.setText("Mouse Button up at:" + e.x + " " + e.y); l.setBounds(e.x, e.y, 150, 15);
                public void mouseDoubleClick(MouseEvent e) {
                        (P7_FixedElements.getCtl()).processDealButton();
                }
              });
        shell.addListener(SWT.Close, new Listener() {
            public void handleEvent(Event event) {
                if (areUSure()) {
                    (P7_FixedElements.getCtl()).processUserExitRequest();
                    event.doit=true;
                    cleanUp();
                } // end if
                else event.doit=false; // System.out.println("returning to game...");
            }
          });
    } // end method setUp MainWindowListeners
    private void checkScreenSize() {
        // Get screen size info
        Rectangle R= P7_FixedElements.D.getBounds();
        int ScreenHeight= R.height;
        int ScreenWidth= R.width;
        if (ScreenHeight<P7_FixedElements.MINIMUMSCREENHEIGHT)
            popUpAbortDialog("Screen height is too small for this game.");
        if (ScreenWidth<P7_FixedElements.MINIMUMSCREENWIDTH)
            popUpAbortDialog("Screen width is too small for this game.");
    } // end method check screen size
    private void popUpAbortDialog(String Message) {
        MessageBox messageBox = new MessageBox(getMainWindow(), SWT.OK);
        messageBox.setMessage(Message);
        messageBox.setText("Bummer!");
        messageBox.open();
        cleanUp();
        System.exit(1);
    } // end method popUpAbortDialog
    protected static boolean betterLuckNextTime() {
        MessageBox messageBox = new MessageBox(getMainWindow(), SWT.OK|SWT.CANCEL);
        messageBox.setMessage("Sorry! Press [OK] to play again or [Cancel] to exit.");
        messageBox.setText("Bummer!");
        if (messageBox.open() == SWT.OK) { return(false); } //endif
//        else P7_FixedElements.abortApplication("Goodbye! Thanks for playing...");
        return(true);
    } //betterLuckNextTime
    protected static boolean congrats() {
        MessageBox messageBox = new MessageBox(getMainWindow(), SWT.OK|SWT.CANCEL);
        messageBox.setText("U r a winner!");
        messageBox.setMessage("Bravo! This time it worked! Press [OK] to play again or [Cancel] to exit.");
        if (messageBox.open() == SWT.OK) return(false); //endif
        return(true);
    } //congrats
    private boolean areUSure() {
        MessageBox messageBox = new MessageBox(getMainWindow(), SWT.OK|SWT.CANCEL);
        messageBox.setText("Confirmation of Exit");
        messageBox.setMessage("Are you sure you want to exit the game?\n Press OK to exit or Cancel to go back.");
        if (messageBox.open() == SWT.OK) {
            messageBox.setMessage("Goodbye! Thanks for playing...");
            return(true);
        } // endif
        else {
            messageBox.setMessage("Returning...");
            return(false);
        } // end else
    }// end method areUSure
    private Button createDealButton() {
        Point Location= new Point(BUTTONSXLOCATION,BUTTONSYORIGIN);
        Button DealButton= new Button(getMainWindow(), SWT.CENTER| SWT.PUSH);
        DealButton.setFont(font);
        DealButton.setLocation(Location);
        DealButton.setText("Deal");
        DealButton.setSize(BUTTONWIDTH, BUTTONHEIGHT);
        P7_DealButtonSelectionAdapter AS= new P7_DealButtonSelectionAdapter();
        DealButton.addSelectionListener(AS);
        return(DealButton);
    } // end method createDealButton
    private Button createAutoplayCheck() {
        int Y=BUTTONSYORIGIN+2*(BUTTONHEIGHT+(BORDERSIZE/2));
        Point Location= new Point(BUTTONSXLOCATION,Y);
        Button AutoPlay= new Button(getMainWindow(), SWT.LEFT| SWT.CHECK);
        AutoPlay.setFont(font);
        AutoPlay.setLocation(Location);
        AutoPlay.setText("Autoplay");
        AutoPlay.setSize(BUTTONWIDTH, BUTTONHEIGHT);
        AutoPlay.setBackground(GameBackgoundColor);
        AutoPlay.setForeground(TextForegroundColor);
        AutoPlay.setEnabled(true);
        AutoPlay.setSelection(Autoplay);
        AutoPlay.addSelectionListener(new SelectionAdapter() {
              public void widgetSelected(SelectionEvent event) {
                  boolean Selected=false;
                  Button X= (Button) event.widget;
                  if (X==null) abortApplication("Check box null!");
                  X.setRedraw(true);
                  Selected=X.getSelection();
                  Autoplay=Selected;
                  if (Autoplay) {
                      DealB.setEnabled(false); // disable deal button
                      NewGameB.setEnabled(false); // disable  new game button
                      NumberOfGamesToPlayT.setEnabled(false);
                      Speed.setEnabled(true);
                  } else {
                      DealB.setEnabled(true); // enable deal button
                      NewGameB.setEnabled(true); // enable  new game button
                      NumberOfGamesToPlayT.setEnabled(true);
                      Speed.setEnabled(false);
                  } // end else
                  try {
                       String S= NumberOfGamesToPlayT.getText();
                       long lo = Long.parseLong(S.trim());
//                       System.out.println("long l = " + lo);
                       P7_FixedElements.Stats.NumberOfGamesToPlay=lo;
                      } catch (NumberFormatException nfe) {
//                         System.out.println("NumberFormatException: " + nfe.getMessage());
                          NumberOfGamesToPlayT.setText("-1");
                      }
                  event.doit=true;
                  // GM.setStopGame();
              }
            });
        return(AutoPlay);
    } // end method createAutoplayCheck
    private Slider createSpeedSlider() {
        int Y=BUTTONSYORIGIN+SUMITEMS*(BUTTONHEIGHT+(BORDERSIZE/2));
        Point Location= new Point(BUTTONSXLOCATION,Y);
        Slider Speed= new Slider(getMainWindow(), SWT.HORIZONTAL);
        Speed.setLocation(Location);
        Speed.setSize(BUTTONWIDTH, BUTTONHEIGHT);
        Speed.setEnabled(true);
        Speed.setMinimum(0); Speed.setMaximum(MAXTIMEDELAYFORAUTOPLAY);
        Speed.setSelection(0);
        Speed.setBackground(GameBackgoundColor);
        Speed.addSelectionListener(new SelectionAdapter() {
              public void widgetSelected(SelectionEvent event) { // When the button is clicked, exit app
                  getModel().GS.AutoPlayDelay= ((Slider)(event.widget)).getSelection();
              }
            });
        return(Speed);
    } // end method createSpeedSlider
    private void createLabelNumberOfGames() {
        int Y=BUTTONSYORIGIN+5*(BUTTONHEIGHT+(BORDERSIZE/2));
        Point Location= new Point(BUTTONSXLOCATION,Y);
        Label NoOfGames= new Label(getMainWindow(), SWT.LEFT|SWT.HORIZONTAL|SWT.SHADOW_IN);
        NoOfGames.setFont(font);
        NoOfGames.setLocation(Location);
        NoOfGames.setText("# of games");
        NoOfGames.setSize(BUTTONWIDTH, BUTTONHEIGHT);   
        NoOfGames.setBackground(GameBackgoundColor);
        NoOfGames.setForeground(TextForegroundColor);
    } // end method createLabelNumberOfGames
    private Text createTextInputNumberOfGames() {
        int Y=BUTTONSYORIGIN+6*(BUTTONHEIGHT+(BORDERSIZE/2));
        Point Location= new Point(BUTTONSXLOCATION,Y);
        Text NoOfGames= new Text(getMainWindow(), SWT.SINGLE|SWT.BORDER|SWT.LEFT);
        NoOfGames.setFont(font);
        NoOfGames.setLocation(Location);
        NoOfGames.setSize(BUTTONWIDTH, BUTTONHEIGHT);
//        NoOfGames.setBackground(GameBackgoundColor);
        NoOfGames.setForeground(TextForegroundColor);
        NoOfGames.addSelectionListener(new SelectionAdapter() {
              public void widgetSelected(SelectionEvent event) {
                  Text T=((Text)(event.widget));
                  String S= T.getText();
                  try {
                       long lo = Long.parseLong(S.trim());
//                       System.out.println("long l = " + lo);
                       P7_FixedElements.Stats.NumberOfGamesToPlay=lo;
                      } catch (NumberFormatException nfe) {
//                         System.out.println("NumberFormatException: " + nfe.getMessage());
                         T.setText("-1");
                      }
                      event.doit=true;
              }
            });

        return(NoOfGames);
    } // end method createTextInputNumberOfGames
    private Button createNewGameButton() {
        int Y=BUTTONSYORIGIN+7*(BUTTONHEIGHT+(BORDERSIZE/2));
        Point Location= new Point(BUTTONSXLOCATION, Y);
        Button NewGameButton= new Button(getMainWindow(), SWT.CENTER| SWT.PUSH);
        NewGameButton.setFont(font);
        NewGameButton.setLocation(Location);
        NewGameButton.setText("New Game");
        NewGameButton.setSize(BUTTONWIDTH, BUTTONHEIGHT);
        NewGameButton.addSelectionListener(new SelectionAdapter() {
              public void widgetSelected(SelectionEvent event) { // When the button is clicked, exit app
                  P7_FixedElements.getModel().GS.StopGame=true;
              }
            });
        return(NewGameButton);
    } // end method createNewGameButton
    private Button createExitGameButton() {
        int Y= WINDOWHEIGHT-BUTTONHEIGHT-4*BORDERSIZE;
        Point Location= new Point(BUTTONSXLOCATION, Y);
        Button ExitGameButton= new Button(getMainWindow(), SWT.CENTER| SWT.PUSH);
        ExitGameButton.setFont(font);
        ExitGameButton.setLocation(Location);
        ExitGameButton.setText("Exit Game");
        ExitGameButton.setSize(BUTTONWIDTH, BUTTONHEIGHT);
        ExitGameButton.addSelectionListener(new SelectionAdapter() {
              public void widgetSelected(SelectionEvent event) { // When the button is clicked, exit app
                  boolean Confirmation=areUSure();
                  if (Confirmation) {
                     getCtl().processUserExitRequest();
                     event.doit=true;
                     cleanUp();
                    } // end if
              }
            });
        return(ExitGameButton);
    } // end createExitGameButton
    private Label createNumberOfCardsInDeck() {
        int Y=BUTTONSYORIGIN+3*(BUTTONHEIGHT+(BORDERSIZE/2));
        Point L= new Point(DECKXLOCATION, Y);
        Label NOC= new Label(getMainWindow(), SWT.CENTER);
        NOC.setFont(font);
        NOC.setText("Deck:");
        NOC.setSize(CARDWIDTH, CARDHEIGHT/2);
        NOC.setBackground(GameBackgoundColor);
        NOC.setForeground(TextForegroundColor);
        NOC.setLocation(L);
        return(NOC);
    } // end method create number of cards in deck label
    private void createInfoWindow() {
        getInfoWindow().setText("Statistics for PatientzaJ");
        getInfoWindow().setLocation(MAINWINDOWINITIALX+850, MAINWINDOWINITIALY+100);
        FillLayout L= new FillLayout(SWT.VERTICAL);
        getInfoWindow().setLayout(L);
        String Losses= "Games Lost: "+Long.toString(Stats.Failures);
        A[0]= new Label(getInfoWindow(), SWT.CENTER|SWT.HORIZONTAL|SWT.SHADOW_IN);
        A[0].setText(Losses.toString());
        String Wins= "Games Won :"+Long.toString(Stats.Successes);
        A[1]= new Label(getInfoWindow(), SWT.CENTER|SWT.HORIZONTAL|SWT.SHADOW_IN);
        A[1].setText(Wins);
        String TotalGames= "Total Games Played: "+Long.toString(Stats.GamesPlayed);
        A[2]= new Label(getInfoWindow(), SWT.CENTER|SWT.HORIZONTAL|SWT.SHADOW_IN);
        A[2].setText(TotalGames);
        String Pctgs= "Percentages: Successes("+Float.toString(Stats.pctSuccess())+"%, Failures("+Float.toString(Stats.pctFailures())+")";
        A[3]= new Label(getInfoWindow(), SWT.CENTER|SWT.HORIZONTAL|SWT.SHADOW_IN);
        A[3].setText(Pctgs);
        T[0]= new Label(getInfoWindow(), SWT.CENTER|SWT.HORIZONTAL|SWT.SHADOW_IN);
//        T[0].setFont(font);
        T[0].setText(P7_FixedElements.Commentary[0]);
        T[1]= new Label(getInfoWindow(), SWT.CENTER|SWT.HORIZONTAL|SWT.SHADOW_IN);
        T[1].setText(P7_FixedElements.Commentary[1]);
        T[2]= new Label(getInfoWindow(), SWT.CENTER|SWT.HORIZONTAL|SWT.SHADOW_IN);
        T[2].setText(P7_FixedElements.Commentary[2]);
        T[3]= new Label(getInfoWindow(), SWT.CENTER|SWT.HORIZONTAL|SWT.SHADOW_IN);
        T[3].setText(P7_FixedElements.Commentary[3]);
        getInfoWindow().pack();
    } // end method createInfoWindow
    private Button createRulesOfTheGameButton() {
        final Button RulesButton= new Button(getInfoWindow(), SWT.CENTER| SWT.PUSH);
        RulesB= RulesButton;
        RulesButton.setText("So, what are the rules?");
        RulesButton.addSelectionListener(new SelectionAdapter() {
            public void widgetSelected(SelectionEvent event) {
                rulesW= new Shell(getInfoWindow(), SWT.CLOSE | SWT.TITLE | SWT.MIN | SWT.MAX);
                rulesW.setText("PatientzaJ Rules");
                rulesW.setLocation(MAINWINDOWINITIALX, MAINWINDOWINITIALY);
                rulesW.setLayout(new GridLayout());
                Label Descr= new Label(getRulesWindow(), SWT.LEFT | SWT.HORIZONTAL | SWT.SHADOW_IN);
                Descr.setText(Rules);
                RulesB.setEnabled(false);
                rulesW.pack();
                rulesW.open();
                rulesW.addDisposeListener(new DisposeListener() {
                      public void widgetDisposed(DisposeEvent event) {
                         RulesB.setEnabled(true);
                      } // end method widgetDisposed
                });
              }
            });
        return(RulesButton);
    } // end method createRulesOfTheGameButton
    private Button createGotoBlogButton() {
        Button GotoNPLBlog= new Button(getInfoWindow(), SWT.CENTER| SWT.PUSH);
        GotoNPLBlog.setText("Visit my blog for the source code!");
        GotoNPLBlog.setSize(BUTTONWIDTH, BUTTONHEIGHT);       
        final String url= "
http://100goto100.blogspot.com/";
        GotoNPLBlog.addSelectionListener(new SelectionAdapter() {
            public void widgetSelected(SelectionEvent event) {
                     BareBonesBrowserLaunch.openURL(url);
            }
        });
        return(GotoNPLBlog);
    } // end method createGotoBlogButton
    private void createPileIndicator() {
        Rectangle R=PileIndicatorImage.getBounds();
        PileIndicator= new Label(getMainWindow(), SWT.CENTER);
        PileIndicator.setImage(PileIndicatorImage);
        PileIndicator.setSize(CARDWIDTH, R.height);
        PileIndicator.setBackground(GameBackgoundColor);
    }
    private P7_FixedElements transferCardsToMainWindow() {
        int Index;
        int DeckSize=PlDk.depth();
        for (Index=0; Index<DeckSize; ++Index) {
            Label Temp=PlDk.referenceCard(Index).getLabel();
            Temp.setParent(getMainWindow());
            Temp.redraw();
        } // end for Index
        return(this);
    } // end method transfer cards to main window
    protected P7_FixedElements createPileGraphics() {
        ClearPileArea=new Label(getMainWindow(), SWT.CENTER);
        ClearPileArea.setText("");
        return(this);
    } // end method createPileGraphics
    private P7_FixedElements setupGraphicalElements() {
        checkScreenSize();    //  Game can abort at this point if screen is not sufficient. If it is, next line is executed.
        // Setup window title, location and layout. Put the buttons
        setUpMainWindowAttributesAndListeners();
        DealB=createDealButton();
        AutoPlayB=createAutoplayCheck();
        Speed=createSpeedSlider();
        createLabelNumberOfGames();
        NumberOfGamesToPlayT=createTextInputNumberOfGames();
        NumberOfGamesToPlayT.setTextLimit(MAXDIGITSINMAXNUMBEROFGAMES);
        NumberOfGamesToPlayT.setText("-1");
        NewGameB=createNewGameButton();
        ExitGameB=createExitGameButton();
        NumberOfCardsInDeck=createNumberOfCardsInDeck();
        createInfoWindow();
        RulesB=createRulesOfTheGameButton();
        BlogB=createGotoBlogButton();
        Sel= new P7_TripletButtonGrid(getMainWindow(), getModel(), getView());
        createPileIndicator();
        transferCardsToMainWindow();
        P7_FixedElements.PlDk.resetCardsLocation();
        P7_FixedElements.Shfl.resetCardsLocation();
        // getMainWindow().open();
        // getInfoWindow().open();
        return(this);
    } //end method setUpgraphicalElemets
// Various debug methods
    protected static void abortApplication(String S) {
        System.out.println(S);
        System.exit(1);
    } //end method abortApplication
    protected static void outputPoint(Point P) {
        System.out.println("X("+P.x+") Y("+P.y+")");
    } // end method outputPoint
    protected static void pressAnyKey() {
        try
        {
            BufferedReader stdin = new BufferedReader(new InputStreamReader(System.in));
            int ch = stdin.read();
            if(ch ==-1)    return;
        } // end try
        catch(IOException io) {
            System.out.println("ERROR" + io);
        } // end catch
    } // end method pressanykey

} // end class P7_FixedElements

/*
* VIEW Classes----------------------------------------------------------------
*/

class P7_GameView {
    private P7_GameModel GM;
    protected P7_GameView(P7_GameModel G) { GM=G; } //end view constructor
    protected void updateGraphicalElements() {
        showStats(P7_FixedElements.A, P7_FixedElements.Stats);
        showDeck(GM.deck());
        showAllPiles();
        } //end method updategraphicalElemets
    protected P7_GameView showDeckAndPile(P7_PlayingDeck Dk, P7_Pile Pl) { showPile(Pl); showDeck(Dk); return(this);}
    protected P7_GameView showTheWindows() {
        P7_FixedElements.getMainWindow().open();
        P7_FixedElements.getInfoWindow().open();
        P7_FixedElements.getInfoWindow().pack();
        return(this);
    } // end method initial setup
    protected P7_GameView showCard(P7_PlayingCard PC) {
        if (PC.isVisible())
            PC.Lb.moveAbove(null);
        P7_FixedElements.D.update();
        return(this);
    } //end View.showCard
    protected P7_GameView showPile(P7_Pile P) {
        int I= P.depth();
        P.correctYValues();
        for (int J=0; J<I; ++J) showCard(P.referenceCard(J));
        return(this);
    } // end method show pile
    protected P7_GameView showStats(Label[] Aa, P7_Statistics Stats) {
        String Losses= "Games Lost: "+ Long.toString(Stats.Failures);
        if (Aa[0]==null) P7_FixedElements.abortApplication("Aa[0] is null!, Losses string is: "+Losses);
            Aa[0].setText(Losses);
        String Wins= "Games Won :"+ Long.toString(Stats.Successes);
        Aa[1].setText(Wins);
        String TotalGames= "Total Games Played: "+Long.toString(Stats.GamesPlayed);
        Aa[2].setText(TotalGames);
        String Pctgs= "Successes: "+Float.toString(Stats.pctSuccess()*100)+
                       "%, Failures: "+Float.toString(Stats.pctFailures()*100)+"%";
        Aa[3].setText(Pctgs);
        P7_FixedElements.getInfoWindow().pack();
        return(this);
    } // End showStats   
    protected P7_GameView showAllPiles() {
        for (int I=0; I<P7_FixedElements.NUMBEROFPILES; ++I)
            showPile(GM.Piles[I]);
        return(this);
    } // end method showAllPiles
    protected void showCurrentPileIndicator() {
        Button K=P7_FixedElements.Sel.Choices[2][1];
        Point Pp= GM.currentPile().getLocation();
        P7_FixedElements.PileIndicator.setLocation(Pp.x, K.getLocation().y+P7_FixedElements.BUTTONHEIGHT);
        P7_FixedElements.PileIndicator.moveAbove(null);
    } // end method showCurrentPileIndicator
    protected void showDeck(P7_PlayingDeck D) {
        int I= D.depth();
        P7_FixedElements.NumberOfCardsInDeck.setText("Left: "+Integer.toString(I));
        P7_FixedElements.NumberOfCardsInDeck.moveAbove(null);
        if (I==0) return;
        showCard(D.referenceCard(0));
        showCurrentPileIndicator();
    } // end method showDeck
} // End class View
/*
* CONTROLLER classes-----------------------------------------------------------------
*
*/
class P7_DealButtonSelectionAdapter extends SelectionAdapter {
    public void widgetSelected(SelectionEvent event) {
        P7_FixedElements.getCtl().processDealButton();
    } // widgetSelected
} // end class SelAdpt

class P7_PileButtonAction extends SelectionAdapter {
    Button Bb;
    P7_PileButtonAction(Button X) {
        Bb=X;
    } // end method constructor
    public void widgetSelected(SelectionEvent event) {
        whatAndWhere();
        P7_ButtonData X=(P7_ButtonData)Bb.getData();
        P7_FixedElements.getView().showDeckAndPile(P7_FixedElements.PlDk, P7_FixedElements.getModel().Piles[X.Pile]);
      } // end method widget selected
    protected void whatAndWhere() {
        P7_ButtonData BD= (P7_ButtonData) Bb.getData();
        int ButtonPile= BD.Pile;
        P7_Pile TempPile= P7_FixedElements.getModel().Piles[ButtonPile];
        if (ButtonPile<0 || ButtonPile>=P7_FixedElements.NUMBEROFPILES) { P7_FixedElements.abortApplication("Invalid Pile Number! Aborting"); }
        if (BD.Action.equals(P7_FixedElements.ChoicesText[0])) { // B3 cards
            if (TempPile.checkForB3Sum()) P7_FixedElements.getModel().removeB3(ButtonPile);
        } // end if
        else if (BD.Action.equals(P7_FixedElements.ChoicesText[1])) { // T2B1 cards
            if (TempPile.checkForT2B1Sum()) P7_FixedElements.getModel().removeT2B1(ButtonPile);
        }
        else if (BD.Action.equals(P7_FixedElements.ChoicesText[2])) { // T1B2 cards
            if (TempPile.checkForT1B2Sum()) P7_FixedElements.getModel().removeT1B2(ButtonPile);
        }
        else P7_FixedElements.abortApplication("Error. Action String other than accepted values ("+BD.Action+")");
        // Decide if the Pile will be avoided from now on
//        if (TempPile.depth()==0)
//            TempPile.avoid();
//        float Pile= L.x/X;
//        System.out.println("Calculated pile: "+Float.toString(Pile)+" Stored Pile: "+BD.Pile+" Action: "+BD.Action);
    } // end method what and where
} // end class P7_PileButtonAction

class P7_DeckCardMouseAdapter extends MouseAdapter {
      public void mouseDown(MouseEvent e) {}
      public void mouseUp(MouseEvent e) {} /* Label l = new Label(s, SWT.FLAT); l.setText("Mouse Button up at:" + e.x + " " + e.y); l.setBounds(e.x, e.y, 150, 15); */
      public void mouseDoubleClick(MouseEvent e) {
//             System.out.println("Got double-click on deck!"); // DEBUG
             (P7_FixedElements.getCtl()).processDealButton();
      } // end method mouseDoubleClick
} // end class P7_DeckCardMouseAdapter

class P7_PileCardMouseAdapter extends MouseAdapter {
      public void mouseDown(MouseEvent e) {}
      public void mouseUp(MouseEvent e) {} /* Label l = new Label(s, SWT.FLAT); l.setText("Mouse Button up at:" + e.x + " " + e.y); l.setBounds(e.x, e.y, 150, 15); */
      public void mouseDoubleClick(MouseEvent e) {
             (P7_FixedElements.getCtl()).processPileClick((Label) e.widget);
      } // end method mouseDoubleClick   
} // end class P7_PileCardMouseAdapter

class P7_GameController {
    private P7_GameView GV;
    private P7_GameModel GM;
    protected P7_GameController(P7_FixedElements F, P7_GameView GView, P7_GameModel GModel) { GV=GView; GM=GModel; }
    protected void initialSetup() {
        if (P7_FixedElements.FirstTime) {
            P7_FixedElements.FirstTime=false;
            P7_FixedElements.PlDk.shuffle(-1);
        }
        // Deal 2 rows by 7 columns before the main processing of the triplets begins
        for (int C=0; C<2; ++C) { //Rows
            for (int C2=0; C2<P7_FixedElements.NUMBEROFPILES; ++C2) { //Columns
                if (P7_FixedElements.PlDk.isEmpty()==false) {
                    P7_PlayingCard T= P7_FixedElements.PlDk.deal();
                    T.setOpen();
                    T.pileMouseAdapter();
                    GM.Piles[C2].appendCard(T, P7_FixedElements.ADVANCEYPOSITION);
                } //if
                else P7_FixedElements.abortApplication("Deck run out during initialization phase, something is terribly wrong!");
//                System.out.println("["+C2+"] ");
            } // endfor C2
        } //endfor C
    } //initialSetup
    protected P7_PlayingCard locateCard(Label Source) {
        P7_PlayingCard Result= P7_FixedElements.PlDk.lookUp(Source); // look in the Deck first
        if (Result==null) { // card not in the deck
            int PileIndex;
            // Now look in the piles
            for (PileIndex=0; PileIndex<P7_FixedElements.NUMBEROFPILES; ++PileIndex) {
                Result= GM.Piles[PileIndex].lookUp(Source);
                if (Result!=null)
                    return(Result); // Card found, return result
            } // end for
        } // end card not in deck   
        return(Result); // return either null or Card from deck
    } // end method locate card from Label reference
    protected P7_GameController processDealButton() {
//        System.out.println("Gotta deal!"); // DEBUG       
        P7_Pile Temp=GM.currentPile();
        boolean Goon= GM.deal2Pile();
        GV.showDeckAndPile(GM.deck(), Temp);
        if (Goon)
            return(this); // we are done, game is proceeding
        else {
                GM.GS.StopGame=true;
                if (P7_FixedElements.Autoplay) /*RETURN HERE */ ;
                else if (GM.areAllPilesEmpty()) GM.GS.GameOver=P7_FixedElements.congrats();
                     else GM.GS.GameOver=P7_FixedElements.betterLuckNextTime();
        }
        return(this);
    } // end method processDealButton
    protected P7_GameController processPiles(P7_Pile PreviousPile) {
        Random FiftyFifty= new Random();
        int Toss=0, NoOfPossibles=0, Pile2CheckIndex;
        boolean B3, T1B2, T2B1;
        P7_Pile PileToCheck= PreviousPile;
        Pile2CheckIndex=PileToCheck.getIndex();
        LinkedList<Integer> Choser = new LinkedList<Integer>();
        do {
              B3=   PileToCheck.checkForB3Sum();
              T1B2= PileToCheck.checkForT1B2Sum();
              T2B1= PileToCheck.checkForT2B1Sum();
            Choser.clear();
             NoOfPossibles=0;
              if (B3)   { ++NoOfPossibles; Choser.add(1); } else Choser.add(0);
              if (T1B2) { ++NoOfPossibles; Choser.add(1); } else Choser.add(0);
             if (T2B1) { ++NoOfPossibles; Choser.add(1); } else Choser.add(0);
//              System.out.println("Pile is: "+Pile2CheckIndex+" size("+PileToCheck.depth()+")"+" Deck size: "+GM.numberOfCardsInDeck()+", Hits:"+NoOfPossibles); // DEBUG
              switch (NoOfPossibles) {
              case 0: break; // Nothing to remove, return
              case 1: if (B3) GM.removeB3(Pile2CheckIndex);
                      else if (T1B2) GM.removeT1B2(Pile2CheckIndex);
                           else if (T2B1) GM.removeT2B1(Pile2CheckIndex);
                           else P7_FixedElements.abortApplication("Impossible: you said one would be true!"+ NoOfPossibles + T1B2 + T2B1+ B3);
                      break;
             case 2: Toss= FiftyFifty.nextInt(NoOfPossibles-1);
                      if (Toss==0) {
                          if (Choser.get(2)==1) GM.removeT2B1(Pile2CheckIndex);
                          else GM.removeT1B2(Pile2CheckIndex);
                      } //if
                     else {
                         if (Choser.get(0)==1) GM.removeB3(Pile2CheckIndex);
                         else GM.removeT1B2(Pile2CheckIndex);
                     } //else
                     break;
             case 3: Toss= FiftyFifty.nextInt(NoOfPossibles-1);
                     switch (Toss) {
                             case 0: GM.removeB3(Pile2CheckIndex); break;
                             case 1: GM.removeT1B2(Pile2CheckIndex); break;
                             case 2: GM.removeT2B1(Pile2CheckIndex); break;
                             default: P7_FixedElements.abortApplication("Impossible: Toss="+ NoOfPossibles + T1B2 + T2B1+ B3);
                     } // switch Toss
                    break;
                default: P7_FixedElements.abortApplication("Impossible: "+ NoOfPossibles+ " should be up to 3");
              } //switch NoOfPossibles
              if (NoOfPossibles>0)
                  PileToCheck.correctYValues();
        } while (B3 || T1B2 || T2B1); // end do
        P7_FixedElements.D.update();
        return(this);
    } // end method processPiles
    protected P7_GameController processPileClick(Label Source) {
        P7_PlayingCard PC= this.locateCard(Source);
        if (PC==null)
            P7_FixedElements.abortApplication("Could not identify the card from its label...");
        else {
//            System.out.print("Found Card: "); PC.outputP7_PlayingCard(); // DEBUG
            int Row=PC.getRow();
            int Column= PC.getColumn();
            if (Column<0)
                P7_FixedElements.abortApplication("Trying to handle pile click on deck object");
            P7_Pile TempPile= GM.Piles[Column];
            int PileDepth=TempPile.depth();
            if (PileDepth<P7_FixedElements.SUMITEMS) // Pile has less than 3 cards, return
                ;//return(this);
            else if (Row<(PileDepth-3) && Row>1) // do nothing user clicked on card that cannot be part of a calculation
                  ; //return(this);
                 else if (PileDepth==P7_FixedElements.SUMITEMS) { // Pile has exactly 3 cards, so use B3 for any card double click
//                           System.out.println(" Checking B3");
                           if (TempPile.checkForB3Sum()) GM.removeB3(Column); }
                       else if (Row==(PileDepth-3)) {
//                                 System.out.println(" Checking B3");
                                 if (TempPile.checkForB3Sum()) GM.removeB3(Column); }
                              else if (Row==(PileDepth-1) || Row==1) { // user wants to check option T2B1
//                                         System.out.println(" Checking T2B1");
                                         if (TempPile.checkForT2B1Sum()) GM.removeT2B1(Column); }
                                      else if (Row==(PileDepth-2) || Row==0) { // user wants to check option T1B2
//                                              System.out.println(" Checking T1B2");
                                              if (TempPile.checkForT1B2Sum()) GM.removeT1B2(Column);
                                           }
            GV.showDeckAndPile(GM.deck(), TempPile);
        } // end else
        return(this);
    } // end method process pile click
    protected P7_GameController processUserExitRequest() {
        GM.setStopGame(true);
        GM.GS.GameOver=true;
        System.out.println("Goodbye!"); //DEBUG
        return(this);
    } // end method processUserExitRequest
    protected P7_GameController runIt() {   
//        System.out.println("Automated run: "+R); // DEBUG
        if (P7_FixedElements.Autoplay && !GM.GS.StopGame) {
//            System.out.println("Automated dealing ("+R+")"); // DEBUG
            P7_Pile Prev=GM.currentPile();
            this.processDealButton();
            try {
                Thread.sleep(P7_FixedElements.getModel().GS.AutoPlayDelay);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
//                e.printStackTrace();
                GM.GS.StopGame=true;
                return(this);
            }
            this.processPiles(Prev);
            try {
                Thread.sleep(P7_FixedElements.getModel().GS.AutoPlayDelay);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
//                e.printStackTrace();
                GM.GS.StopGame=true;
                return(this);
            }
            int Sh=P7_FixedElements.Generator.nextInt(P7_FixedElements.SUMITEMS+1);
            P7_FixedElements.Shfl.shuffle(Sh);
//            ++R; // DEBUG
        } // end if
        return(this);
    } // end method runIt (for Autoplay)
    protected boolean play() {
        initialSetup();
        GV.showTheWindows();
        GV.updateGraphicalElements();

        boolean Dispatched=false;
        while (GM.getStopGame()==false) {
             Dispatched=P7_FixedElements.D.readAndDispatch();
             if (!Dispatched) {
                 if (P7_FixedElements.Autoplay)
                     this.runIt();
                 else P7_FixedElements.D.sleep();
             }
        } //  end while
        if (P7_FixedElements.D!=null && GM.GS.GameOver!=true) {
            P7_FixedElements.getView().updateGraphicalElements();
            GM.initializeGame();
            P7_FixedElements.getView().updateGraphicalElements();
            GM.setStopGame(false);
            GM.swapDecks();
            if (P7_FixedElements.Autoplay) {
                if (P7_FixedElements.Stats.NumberOfGamesToPlay>0) {
                    GM.subtractGameCounter();
                    if (P7_FixedElements.Stats.NumberOfGamesToPlay==0) {
                        P7_FixedElements.Autoplay=false;
                        P7_FixedElements.AutoPlayB.setSelection(false);
                        P7_FixedElements.Speed.setEnabled(false);
                        P7_FixedElements.NumberOfGamesToPlayT.setText("-1");
                        P7_FixedElements.NewGameB.setEnabled(true);
                        P7_FixedElements.DealB.setEnabled(true);
                        P7_FixedElements.NumberOfGamesToPlayT.setEnabled(true);
                    }
                } // end if
            }
        } // end if
        return(GM.GS.GameOver);
    } // end method play       
} // endclass Controller

/*
* MODEL ---------------------------------------------------------------------------
*
* Every Game has the following objects: 1 Deck , 7 Piles, A set of statistics
* */

class P7_PlayingCard {
    private int Column, Row;
    private MouseAdapter MA;
    private int Points;
    protected static Shell ParentWindow= new Shell();
    protected static MouseAdapter DefaultMouseAdapter= new P7_DeckCardMouseAdapter(); // Set the mouse listener to the default deck handling (i.e. double click deals card)
    protected static MouseAdapter PileMouseAdapter= new P7_PileCardMouseAdapter();
    protected static P7_PlayingCard TempHold= new P7_PlayingCard();
    protected P7_Suit S;
    protected P7_Rank V;
    protected Image Face, Back;
    protected Label Lb;
    protected Point Location;
    protected boolean Visible, Hidden;
    protected P7_PlayingCard() {
            Face=null; Back=null; Lb=null; MA=null;
            S=P7_Suit.Hearts; V=P7_Rank.Ace; Points=0;
            Visible=false; Hidden=false;
            Location= new Point(0,0);
            Column=-1; // -1 signifies the deck, actual column number can be set later
            Row=0; // First row
    } // end default constructor
    protected P7_PlayingCard(P7_Suit Ss, P7_Rank Rr, int PointsValue, Image Front, Image BackDesign) {
        S=Ss;
        V=Rr;
        Points=PointsValue;
        Face= Front;
        Back= BackDesign;
        Location= new Point(0,0);
        Column= -1;  // -1 signifies the deck, actual column number can be set later
        Row=0; // First row
        Visible=true;
        Hidden=false;
        MA= DefaultMouseAdapter;
        Lb= new Label(ParentWindow, SWT.CENTER);
        Lb.setSize(P7_FixedElements.CARDWIDTH, P7_FixedElements.CARDHEIGHT);
        Lb.addMouseListener(MA);
        Lb.setImage(Face);
        Lb.setBackground(P7_FixedElements.GameBackgoundColor);
//        if (hidden()) Lb.setImage(getBack());
//        else Lb.setImage(getFace());
    } // end constructor
    protected P7_PlayingCard assign(P7_PlayingCard Other) { // does not do a deep copy of the images!
        this.S=Other.S;
        this.V=Other.V;
        this.Points=Other.Points;
        this.Face=Other.Face;
        this.Back=Other.Back;
        this.Visible=Other.Visible;
        this.Hidden=Other.Hidden;
        this.Location.x=Other.Location.x;
        this.Location.y=Other.Location.y;
        this.Column=Other.Column;
        this.Row=Other.Row;
        this.Lb=Other.Lb;
        this.MA= Other.MA;
        return(this);
    } // end method assign
    protected P7_PlayingCard copy() {
        P7_PlayingCard New= new P7_PlayingCard();
        New.assign(this);
        return(New);
    } // end method copy
    protected void setSuit(P7_Suit NewS) { S=NewS; }
    protected P7_Suit getSuit() { return(S); }
    protected void setRank(P7_Rank NewV) { V=NewV; }
    protected P7_Rank getRank() { return(V); }
    protected void setPoints(int P) { Points=P; }
    protected int getPoints() { return(Points); }
    protected void setLocation(Point P) { Location.x= P.x; Location.y=P.y;  Lb.setLocation(P); }
    protected Point getLocation() { return(Location); }
    protected P7_PlayingCard setRow(int R) { this.Row=R; return(this); }
    protected int getRow() { return(this.Row); }
    protected P7_PlayingCard setColumn(int C) { this.Column=C; return(this); }
    protected int getColumn() { return(this.Column); }
    protected void setVisible() { Visible=true; }
    protected void setInvisible() { Visible=false; }
    protected boolean isVisible() { return(Visible); }
    protected void setHidden() { Hidden=true; Lb.setImage(Back); }
    protected void setOpen() { Hidden=false; Lb.setImage(Face); }
    protected boolean isHidden() { return(Hidden); }
    protected Image getFace() { return(Face); }
    protected Image getBack() { return(Back); }
    protected Label getLabel() { return(Lb); }
    P7_PlayingCard setLabel(Label L) { Lb=L; return(this); }
    protected P7_PlayingCard lookUpLabel(Label L) {
        if (L==Lb) return(this);
        else return(null);
    } // end method lookUp Label
    protected boolean switchCard(P7_PlayingCard Other) {
        if (this==null || Other == null) return(false);
        TempHold.assign(this);
        this.assign(Other);
        Other.assign(TempHold);
        return(true);
    } // end method switchCard
    P7_PlayingCard setMouseAdapter(MouseAdapter ML) { MA=ML; return(this); } // set what to do on double click
    protected P7_PlayingCard resetMouseAdapter() {
        Lb.removeMouseListener(MA);
        MA=DefaultMouseAdapter;
        Lb.addMouseListener(MA);
        return(this);
    } // set default behaviour i.e. deal card
    protected P7_PlayingCard pileMouseAdapter() {
        Lb.removeMouseListener(MA);
        MA=PileMouseAdapter;
        Lb.addMouseListener(MA);
        return(this);
    } // end method pileMouseAdapter
    protected void outputP7_PlayingCard() {
        System.out.print("["+S+", "+V+"]("+Location.x+", "+Location.y+"), Visible("+
                         Visible+"), Hidden("+Hidden+"), Pile: "+Column+", Row: "+Row+" W:"+ParentWindow+
                         " Face:"+Face+" Back:"+Back);
    } //outputP7_PlayingCard
} // end class P7_PlayingCard

class P7_Pile {
    private int Index=0;
    protected Point Location= new Point(0,0);
    protected LinkedList<P7_PlayingCard> LL = new LinkedList<P7_PlayingCard>();
    protected P7_Pile(Point L, int I) {
        Location.x=L.x; Location.y=L.y;
        Index=I;
    } // end method constructor P7_Pile
    protected void copyListInto(P7_Pile Duplicate, boolean AdvanceY) { // deep copy of cards!
        if (this.isEmpty())
            return;
        else {
            int D=this.depth();
            for (int K=0; K<D; ++K) {
                P7_PlayingCard TPC=(this.LL.get(K)).copy();
                Duplicate.appendCard(TPC, AdvanceY);
            } // end for
        } // end else
    } // end method copyListInto
    protected boolean isEmpty() {    return(LL.isEmpty()); } // has no cards
    protected int depth() { return(LL.size()); }
//    P7_Pile avoid() { Avoid=true; return(this); }
//    P7_Pile engaged() { Avoid=false; return(this); }
    protected void appendCard(P7_PlayingCard PC, boolean AdvanceYPosition) {
/* appending a card to a P7_Pile can either change the cards location or not
* depending on whether we are stacking (i.e. a deck) or spreading the cards to see their faces.
* the AdvanceYPosition flag is used for this descrimination
*/
        Point Pp= new Point(0,0);
        Pp.x=Location.x;
        if (AdvanceYPosition) Pp.y=Location.y+depth()*P7_FixedElements.VISIBLECARDPARTY;
        else Pp.y= Location.y;
        PC.setLocation(Pp);        // Store the card's location in the window
        PC.setRow(LL.size());     // Store the card's Row
        PC.setColumn(this.getIndex());    // Store the card's P7_Pile
        LL.addLast(PC);
    }
    protected P7_PlayingCard getCardFromPosition(int Position) {
        /*
         * Removes the card from the list and returns its reference to the caller.
         * Used when we want to move the card from one cardlist to another.
         */
        return(LL.remove(Position));
    } // removeCard
    protected P7_PlayingCard referenceCard(int Pos) {
        /*
         * Returns a reference of the card to the caller WITHOUT removing it from the list.
         * Used when we want to change some aspect of the P7_PlayingCard in place.
         */
        return(LL.get(Pos));
    } // end method referenceCard
    protected boolean checkForT1B2Sum() {
        if (LL.size()<P7_FixedElements.SUMITEMS) return(false);
        else {
            int C= LL.getFirst().getPoints()+ LL.getLast().getPoints()+ LL.get(LL.size()-2).getPoints();
            if (C==10 || C==20 || C==30) return(true);
            else return(false);
        } //else
    } //checkForT2B1Sum
    protected boolean checkForT2B1Sum() {
        if (LL.size()<P7_FixedElements.SUMITEMS) return(false);
        else {
            int C= LL.getFirst().getPoints()+ LL.getLast().getPoints()+ LL.get(1).getPoints();
            if (C==10 || C==20 || C==30) return(true);
            else return(false);
        }
    } // checkForT2B1Sum
    protected boolean checkForB3Sum() {
            if (LL.size()<P7_FixedElements.SUMITEMS) return(false);
            else {
                int C= LL.getLast().getPoints()+ LL.get(LL.size()-2).getPoints()+ LL.get(LL.size()-3).getPoints();
                if (C==10 || C==20 || C==30) return(true);
                else return(false);
            } //if
    } // checkForB3Sum
    protected void outputPile() {
        System.out.print("Pile: ");
        for (P7_PlayingCard T : LL)
            T.outputP7_PlayingCard();
        System.out.println();
        System.out.println("------");
    } //outputPile
    protected void setLocation(Point P) { Location.x= P.x; Location.y=P.y; }
    protected Point getLocation() { return(Location); }
    protected P7_PlayingCard lookUp(Label Source) {
        int Max=LL.size();
        for (int I=0; I<Max; ++I) {
            P7_PlayingCard Current=LL.get(I);
            if (Current.lookUpLabel(Source)!=null)
                return(Current);
        } // end for
        return(null);
    } // end method lookUp
    protected P7_PlayingCard lookUp(P7_Rank R, P7_Suit S) {
        int I=0; int D=this.depth();
        for (I=0; I<D; ++I) {
            P7_PlayingCard TPC= this.referenceCard(I);
            if (TPC.S==S)
                if (TPC.V==R)
                    return(TPC);
        } // end for
        return(null);
    } // end lookup find card based on values
    protected int getIndex() { return(Index); }
    protected void swap(int I, int J) {
        P7_PlayingCard T1= LL.get(I);
        P7_PlayingCard T2= LL.get(J);
        T1.switchCard(T2);
    } // end method swap list locations
    protected void correctYValues() { // After a change in a pile, put the proper x,y to the cards and the correct row.
        int I=this.depth();
        if (I==0) return;
        P7_PlayingCard CrRf;
        int J;
        if (I<P7_FixedElements.MAXNONSTACKEDCARDS) {
            Point T= new Point(this.Location.x,this.Location.y);
            for (J=0; J<I; ++J) {
                CrRf=referenceCard(J);   
                CrRf.setRow(J); // set proper row values
                T.y=this.Location.y+P7_FixedElements.VISIBLECARDPARTY*J;
                CrRf.setLocation(T);
                CrRf.setOpen();
            } // for
        } // end if no need to stack
        else {
            Point T= new Point(this.Location.x, this.Location.y);
            //+P7_FixedElements.VISIBLECARDPARTY*(P7_FixedElements.TOPCARDSTODISPLAY+1));
            for (J=0; J<P7_FixedElements.TOPCARDSTODISPLAY; ++J) {
                CrRf=referenceCard(J);
                CrRf.setRow(J);
                T.y=this.Location.y+P7_FixedElements.VISIBLECARDPARTY*J;
                CrRf.setOpen();
                CrRf.setLocation(T);
            } // end for top cards
            T.y=this.Location.y+P7_FixedElements.VISIBLECARDPARTY*(P7_FixedElements.TOPCARDSTODISPLAY);
            for (J=P7_FixedElements.TOPCARDSTODISPLAY; J<I-P7_FixedElements.BOTTOMCARDSTODISPLAY; ++J) {
                CrRf=referenceCard(J);
                CrRf.setRow(J);
                CrRf.setHidden();
                CrRf.setLocation(T);
            } // end for J
            T.y=T.y+P7_FixedElements.VISIBLECARDPARTY;
            for (J=I-P7_FixedElements.BOTTOMCARDSTODISPLAY;
                 /* T.y=this.Location.y+P7_FixedElements.VISIBLECARDPARTY*(Rebase+P7_FixedElements.TOPCARDSTODISPLAY) */
                 J<I;
                 ++J, T.y=T.y+P7_FixedElements.VISIBLECARDPARTY) {
                CrRf=referenceCard(J);
                CrRf.setRow(J);
                CrRf.setOpen();
                CrRf.setLocation(T);
            } // end for the low cards
//            System.out.println("Deck ("+Location.x+", "+Location.y+") CardY("+T.y+") "); // DEBUG
        } // end need to stack
    } // end method correctPileYValues
} // end class Pile

class P7_PileIterator {
    private int CurrentPile=0;
    private int equate(P7_PileIterator A) { CurrentPile=A.CurrentPile; return(CurrentPile); }
    private int equate(int A){
        if (A<0) CurrentPile=0;
        else if (A>=P7_FixedElements.NUMBEROFPILES)
            CurrentPile=P7_FixedElements.NUMBEROFPILES-1;
        else CurrentPile=A;
        return(CurrentPile);
    }
    protected P7_PileIterator() { CurrentPile=0; }
    protected P7_PileIterator(P7_PileIterator A) { CurrentPile=A.CurrentPile; }
    protected int reset() { CurrentPile=0;  return(CurrentPile); }
    protected int first() { return(reset()); }
    protected int last() { CurrentPile=P7_FixedElements.NUMBEROFPILES-1; return(CurrentPile); }
    protected int current() { return(CurrentPile); }
    protected int next() {
        int T= CurrentPile+1;
        if (T==P7_FixedElements.NUMBEROFPILES) return(0);
        else return(T);
    } // end method next
    protected int previous() {
        int T=CurrentPile-1;
        if ((T)<0) return(P7_FixedElements.NUMBEROFPILES-1);
        else return(T);
    } // previous
    protected void gotoNextPile() {
        ++CurrentPile;
        if (CurrentPile==P7_FixedElements.NUMBEROFPILES)
            CurrentPile=0;
    } // end method gotoNextPile
    protected void gotoPreviousPile() {
        --CurrentPile;
        if (CurrentPile<0)
            CurrentPile=P7_FixedElements.NUMBEROFPILES-1;
    } // end method gotoPreviousPile
} // end class P7_PileIterator

class P7_PlayingDeck extends P7_Pile {
    protected P7_PlayingDeck(Point L, boolean ForCopy) { // constructor
        super(L, -1);
        super.Location= new Point(0,0);
        Location.x=L.x; Location.y=L.y;
        if (ForCopy)
            return;
        String FileName=null, FinalPrefix, CardBackFileName;
        FileInputStream fis;
//        String Prefix[]= { "bin\\resources\\10", "bin\\resources\\1" };
        System.out.println("Loading card images."); // DEBUG
        Point CardLocation= new Point(0,0);
        Image Face, CardBack;
        if (P7_FixedElements.WindowsOS) {
            P7_FixedElements.CardImageFilePrefix[0]=P7_FixedElements.CardImageFilePrefixWindows[0];
            P7_FixedElements.CardImageFilePrefix[1]=P7_FixedElements.CardImageFilePrefixWindows[1];
        } else {
            P7_FixedElements.CardImageFilePrefix[0]=P7_FixedElements.CardImageFilePrefixUnix[0];
            P7_FixedElements.CardImageFilePrefix[1]=P7_FixedElements.CardImageFilePrefixUnix[1];
        } // end else
        CardBackFileName=P7_FixedElements.CardImageFilePrefix[1]+"55"+P7_FixedElements.CARDFILENAMEEXTENSION;
        int II=0;
        for (P7_Suit suit : P7_Suit.values())
             for (P7_Rank rank : P7_Rank.values()) {
                 if (II<9) FinalPrefix=P7_FixedElements.CardImageFilePrefix[0];
                 else FinalPrefix=P7_FixedElements.CardImageFilePrefix[1];
                 FileName= FinalPrefix+Integer.toString(II+1)+P7_FixedElements.CARDFILENAMEEXTENSION;
                 try {
//                     System.out.println("Working Directory is: "+System.getProperty("user.dir")+" Card Filename: "+FileName); //DEBUG
                     fis = new FileInputStream(FileName);
                     Face= new Image(P7_FixedElements.D, FileName);
                    fis = new FileInputStream(CardBackFileName);
                    CardBack= new Image(P7_FixedElements.D, CardBackFileName); // for double click to work, I have to have a listener!
                    P7_PlayingCard TempCard=new P7_PlayingCard(suit, rank, lookupValue(rank), Face, CardBack);
                    CardLocation.x=Location.x;
                    CardLocation.y=P7_FixedElements.PILEORIGINY; // Location.y+P7_FixedElements.VISIBLECARDPARTY*II;
//                    System.out.println("Card location is:"+CardLocation.x+":"+CardLocation.y); 
                    TempCard.setLocation(CardLocation);
                    TempCard.setHidden();
                    this.appendCard(TempCard, P7_FixedElements.KEEPYPOSITION);
                } // end try
                 catch (FileNotFoundException ex) {
                     P7_FixedElements.abortApplication("Card Image File: "+FileName+" not found. Abrupt end of program!");
                 } // end catch
                ++II;
             } // for Rank
            System.out.println("Done Loading card images."); // DEBUG
//            P7_FixedElements.pressAnyKey(); //DEBUG
    } //end constructor
    protected P7_PlayingDeck copy() {
        P7_PlayingDeck TPD=new P7_PlayingDeck(this.Location, true);
        copyListInto(TPD, P7_FixedElements.KEEPYPOSITION);
        return(TPD);
    } // end method copy deck
    protected P7_PlayingDeck resetCardsLocation() {
        Point P= new Point(Location.x, Location.y);
        P7_PlayingCard TempC;
        int D=this.depth();
        for (int Index=0; Index<D; ++Index) {
            TempC=this.referenceCard(Index);
            TempC.setHidden();
            TempC.setLocation(P);
        } // end for
        return(this);
    }
    protected P7_PlayingDeck shuffle(int Times) {
        int FirstCardLocation, SecondCardLocation, I;
        if (Times<0) {
            long StartTime= System.currentTimeMillis();
            System.out.println("First Time Shuffling..."); // DEBUG
            while (System.currentTimeMillis()< (StartTime+P7_FixedElements.SUFFLINGTIME)) {
                FirstCardLocation= (P7_FixedElements.Generator.nextInt(P7_FixedElements.NUMBEROFCARDSINDECK));
                SecondCardLocation= (P7_FixedElements.Generator.nextInt(P7_FixedElements.NUMBEROFCARDSINDECK));
                swap(FirstCardLocation, SecondCardLocation);
//                P7_FixedElements.getView().displayCards();
            } //while
    /*        System.out.println("Finished Shuffling");
            Print the shuffled Deck
            for (int i=0; i<P7_FixedElements.NUMBEROFCARDSINDECK; ++i) {
                LL.get(i).outputP7_PlayingCard();
                System.out.println("["+i+"], Deck Depth: "+LL.size());
            } // for
*/        } else {
            for (I=0; I<Times; ++I) {
                FirstCardLocation= (P7_FixedElements.Generator.nextInt(P7_FixedElements.NUMBEROFCARDSINDECK));
                SecondCardLocation= (P7_FixedElements.Generator.nextInt(P7_FixedElements.NUMBEROFCARDSINDECK));
                swap(FirstCardLocation, SecondCardLocation);
//                if (Ret<30) { ++Ret; System.out.print(">"); }  // DEBUG
//                else { Ret=0; System.out.println("."); } // DEBUG
//                P7_FixedElements.getView().displayCards();
            } // end for
        } // end else
        return(this);
    } //shuffle
    protected P7_PlayingCard deal() {
        if (LL.isEmpty()) {
            System.out.println("cannot deal, deck is empty... check for deck size before calling.. Aborting Application");
            System.exit(0);
        } // if
        P7_PlayingCard X=LL.pop();
        return(X);
    } //deal
    protected boolean deckIsHealthy() {
        for (P7_Suit suit : P7_Suit.values())
             for (P7_Rank rank : P7_Rank.values()) {
                 P7_PlayingCard Tt= lookUp(rank, suit);
                 if (Tt==null) return(false);
             }
        return(true);
    } // end method deckIsHealthy
    protected static int lookupValue(P7_Rank R)    {
        switch (R) {
        case Ace: return(P7_FixedElements.CardPoints[0]);
        case Two: return(P7_FixedElements.CardPoints[1]);
        case Three: return(P7_FixedElements.CardPoints[2]);
        case Four: return(P7_FixedElements.CardPoints[3]);
        case Five: return(P7_FixedElements.CardPoints[4]);
        case Six: return(P7_FixedElements.CardPoints[5]);
        case Seven: return(P7_FixedElements.CardPoints[6]);
        case Eight: return(P7_FixedElements.CardPoints[7]);
        case Nine: return(P7_FixedElements.CardPoints[8]);
        case Ten: return(P7_FixedElements.CardPoints[9]);
        case Jack:  return(P7_FixedElements.CardPoints[10]);
        case Queen: return(P7_FixedElements.CardPoints[11]);
        case King: return(P7_FixedElements.CardPoints[12]);
        default: P7_FixedElements.abortApplication("Could not locate rank in point lookup... Serious error!");
        } //switch
        return(0);
    } //lookup

} //class P7_PlayingDeck

class P7_GameState {
    protected boolean StopGame;
    protected boolean GameOver;
    protected int AutoPlayDelay;
    protected P7_PileIterator CurrentPile;
    protected P7_GameState() {
        StopGame=false;
        GameOver=false;
//        NumberOfGamesToPlay=-1;
        CurrentPile= new P7_PileIterator();
        AutoPlayDelay=0; // max speed
    } // end constructor P7_GameState
} //end class P7_GameState

class P7_GameModel {
    protected P7_GameState GS;
    protected P7_Pile[] Piles;
    protected P7_GameModel(P7_FixedElements FEs) {
        GS= new P7_GameState();
//        FEls=FEs;
        Piles= new P7_Pile[P7_FixedElements.NUMBEROFPILES];
        int X=P7_FixedElements.PILEORIGINX, Y=P7_FixedElements.PILEORIGINY;
        for (int C=0; C<P7_FixedElements.NUMBEROFPILES; ++C) {
            Point L=new Point(X, Y);
            Piles[C]= new P7_Pile(L, C);
            X=X+P7_FixedElements.BORDERSIZE+P7_FixedElements.CARDWIDTH;
        } // end for
    } // end constructor Game
    protected int numberOfCardsInDeck() { return(P7_FixedElements.PlDk.depth()); }
    private P7_GameModel returnAllCardsToTheDeck() {
//        System.out.println("Start: Return cards to the deck --> "); // DEBUG
        P7_PlayingCard TempCard;
        for (int P=0; P<P7_FixedElements.NUMBEROFPILES; ++P) {
//            System.out.println("Pile is ["+P+"]"); // DEBUG
            while (!Piles[P].isEmpty()) {
                TempCard=Piles[P].getCardFromPosition(0);
                TempCard.setLocation(P7_FixedElements.Ll);
                TempCard.setHidden();
                TempCard.resetMouseAdapter();
                P7_FixedElements.PlDk.appendCard(TempCard, false);
            } // end while
        } // end for P
//        System.out.println("All cards back in the playing deck. Size is again: "+P7_FixedElements.PlDk.depth()+
//                           " Shadow Deck ["+P7_FixedElements.Shfl.depth()+"]"); // DEBUG
//        System.out.println("Playing deck health: "+P7_FixedElements.PlDk.deckIsHealthy()); //P7_FixedElements.PlDk.outputPile();
//        System.out.println("Shuffle deck health: "+P7_FixedElements.Shfl.deckIsHealthy()); //P7_FixedElements.Shfl.outputPile();
        return(this);
    } // end method returnAllCardsToTheDeck
    protected P7_GameModel initializeGame() {
        returnAllCardsToTheDeck();
        GS.StopGame=false;
        GS.CurrentPile.reset();
        GS.GameOver=false;
        return(this);
    }
    protected P7_GameModel subtractGameCounter() {
        --P7_FixedElements.Stats.NumberOfGamesToPlay;
        P7_FixedElements.NumberOfGamesToPlayT.setText(Long.toString(P7_FixedElements.Stats.NumberOfGamesToPlay));
        return(this);
    }
    protected P7_PlayingDeck deck() { return(P7_FixedElements.PlDk); }
    protected P7_Pile currentPile() { return(Piles[GS.CurrentPile.current()]); }
    protected int currentPileNo() { return(GS.CurrentPile.current()); }
    protected void setStopGame(boolean B) { GS.StopGame=B;    } // end method setStopGame
    protected boolean getStopGame() {    return(GS.StopGame); } // end method getStopGame
    protected boolean areAllPilesEmpty() { // Check for win
        for (int C=0; C<P7_FixedElements.NUMBEROFPILES; ++C) {
            if (Piles[C].isEmpty()==false)
                return(false);
        } //endfor
        this.processSuccess();
        return(true);
    } //end of areAllPilesEmpty
    protected P7_GameModel processSuccess() { ++P7_FixedElements.Stats.Successes; ++P7_FixedElements.Stats.GamesPlayed; return(this); }
    protected P7_GameModel processFailure() { ++P7_FixedElements.Stats.Failures; ++P7_FixedElements.Stats.GamesPlayed; return(this); }
    protected P7_GameModel fromPile2Deck(P7_PlayingCard PC, boolean MoveCardsYLocation) {
        PC.setHidden();
        PC.resetMouseAdapter();        // going back to the deck so set the double click to deck handling
        P7_FixedElements.PlDk.appendCard(PC, MoveCardsYLocation);
        return(this);
    } // end method fromPile2Deck
    protected P7_GameModel removeB3(int PileI) { // Case of 3 bottom cards
        P7_Pile CurrentPile=Piles[PileI];
        P7_PlayingCard PC=CurrentPile.getCardFromPosition(CurrentPile.depth()-3);
        fromPile2Deck(PC, P7_FixedElements.KEEPYPOSITION);
        PC=CurrentPile.getCardFromPosition(CurrentPile.depth()-2);
        fromPile2Deck(PC, P7_FixedElements.KEEPYPOSITION);
        PC=CurrentPile.getCardFromPosition(CurrentPile.depth()-1);
        fromPile2Deck(PC, P7_FixedElements.KEEPYPOSITION);
        return(this);
    } //removeB3
    protected P7_GameModel removeB3(P7_PileIterator Pi) { // Case of 3 bottom cards
        int J=Pi.current();
        return(removeB3(J));
    } //removeB3
    protected P7_GameModel removeT1B2(int PileI) { // Case of 1 top and 2 bottom cards
        P7_Pile CurrentPile=Piles[PileI];

        P7_PlayingCard PC=CurrentPile.getCardFromPosition(CurrentPile.depth()-2);       
        fromPile2Deck(PC, P7_FixedElements.KEEPYPOSITION);
        PC=CurrentPile.getCardFromPosition(CurrentPile.depth()-1);
        fromPile2Deck(PC, P7_FixedElements.KEEPYPOSITION);
        PC=CurrentPile.getCardFromPosition(0);
        fromPile2Deck(PC, P7_FixedElements.KEEPYPOSITION);
        return(this);
    } //removeT1B2
    protected P7_GameModel removeT1B2(P7_PileIterator Pi) { // Case of 1 top and 2 bottom cards
        int J=Pi.current();
        return(removeT1B2(J));
    } //removeT1B2
    protected P7_GameModel removeT2B1(int PileI) { // Case of 2 top and 1 bottom card
        P7_Pile CurrentPile=Piles[PileI];

        P7_PlayingCard PC=CurrentPile.getCardFromPosition(CurrentPile.depth()-1);
        fromPile2Deck(PC, P7_FixedElements.KEEPYPOSITION);
        PC=CurrentPile.getCardFromPosition(0);
        fromPile2Deck(PC, P7_FixedElements.KEEPYPOSITION);
        PC=CurrentPile.getCardFromPosition(0);
        fromPile2Deck(PC, P7_FixedElements.KEEPYPOSITION);
        return(this);
    } //removeT2B1
    protected P7_GameModel removeT2B1(P7_PileIterator Pi) { // Case of 2 top and 1 bottom card
        int J=Pi.current();
        return(removeT2B1(J));
    } //removeT2B1
    protected boolean deal2Pile() {
        if (areAllPilesEmpty()) { // Do not deal anything, game is over, user has won
//            this.processSuccess(); this is done in areAllPilesEmpty, no need to do it here
            return(false); // game is over
        }
        // There are still cards in piles continue with dealing
        if (P7_FixedElements.PlDk.depth()>0) { // Still have cards to deal, game is continuing
            P7_PlayingCard Card= P7_FixedElements.PlDk.deal();
            Card.setOpen();            // Make sure card is face up
            Card.pileMouseAdapter();    // Set the mouse adapter of the card to handle pile double clicks instead of deck double clicks
            Piles[GS.CurrentPile.current()].appendCard(Card, P7_FixedElements.ADVANCEYPOSITION);
            // move deal focus to next non empty pile
            if (findNextValidPile())
                return(true); // everything OK, move dealing point to the next valid pile
            else {
                P7_FixedElements.abortApplication("Should never have reached this! method(deal) class(P7_GameModel)");
                return(false);
            } // end else
        } //endif
        else  { // No more cards, everything is on the piles, user has lost
            this.processFailure();
            return(false);
        } // end else game over, user lost
    } // end method deal
    protected boolean findNextValidPile() {
        GS.CurrentPile.gotoNextPile();
        int Count=0;
        while (Piles[GS.CurrentPile.current()].isEmpty() && Count<P7_FixedElements.NUMBEROFPILES) {
            ++Count;
            GS.CurrentPile.gotoNextPile();
        } // end while
        if (Count>=P7_FixedElements.NUMBEROFPILES) return(false);
        return(true);
    } // end method findNextValidPile
    protected P7_GameModel swapDecks() {
        P7_PlayingDeck TPD;
        TPD=P7_FixedElements.PlDk;
        P7_FixedElements.PlDk=P7_FixedElements.Shfl;
        P7_FixedElements.Shfl=TPD;
        return(this);
    } // end method swapDecks
} // endclass P7_GameModel

public class PatienJava {
    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        P7_FixedElements Konsts= new P7_FixedElements(); // must create this in order to initialize stuff before anything else
        P7_GameModel M= new P7_GameModel(Konsts);
        P7_GameView V= new P7_GameView(M);
        P7_GameController C= new P7_GameController(Konsts, V, M);
        Konsts.setGameMVC(M, V, C);
        boolean Continue=true;
        while (Continue) {
            Continue=!C.play(); // returns GameOver flag if true the user wants to exit, false if he wants to continue
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        } // end while       
        P7_FixedElements.D.dispose();
    } // end main()
} ///endclass PatientzaJ

/////////////////////////////////////////////////////////
// Bare Bones Browser Launch //
// Version 2.0 (May 26, 2009) //
// By Dem Pilafian //
// Supports: //
// Mac OS X, GNU/Linux, Unix, Windows XP/Vista //
// Example Usage: //
// String url = "
http://www.centerkey.com/"; //
// BareBonesBrowserLaunch.openURL(url); //
// Public Domain Software -- Free to Use as You Like //
/////////////////////////////////////////////////////////
class BareBonesBrowserLaunch {
    protected static final String[] browsers = { "firefox", "opera", "konqueror", "epiphany", "seamonkey", "galeon", "kazehakase", "mozilla", "netscape" };
    protected static void openURL(String url) {
        String osName = System.getProperty("os.name");
        try {
            if (osName.startsWith("Mac OS")) {
                Class<?> fileMgr = Class.forName("com.apple.eio.FileManager");
                Method openURL = fileMgr.getDeclaredMethod("openURL", new Class[] {String.class});
                openURL.invoke(null, new Object[] {url});
            } else if (osName.startsWith("Windows"))
                    Runtime.getRuntime().exec("rundll32 url.dll,FileProtocolHandler " + url);
                    else { //assume Unix or Linux
                            boolean found = false;
                            for (String browser : browsers)
                                if (!found) {
                                    found = Runtime.getRuntime().exec( new String[] {
                                            "which", browser}).waitFor() == 0;
                                            if (found) Runtime.getRuntime().exec(new String[] {
                                                    browser, url});
                                }
                            if (!found) throw new Exception(Arrays.toString(browsers));
                         } // end else
        } //end try
        catch (Exception e) {
            JOptionPane.showMessageDialog(null, "Error attempting to launch web browser\n" + e.toString());
        } // end catch
    } // end openURL
}  // end BareBonesBrowserLaunch