Total votes: 0
Print: Print Article
Please login to rate or to leave a comment.
Published: 21 May 2010
Download Sample Code
In the two parts of series, we are going to develop another Silverlight 3 based Freecell game like the one shipped with Microsoft Windows. Our main purpose to write this game is to continue to explore the mouse related operations supported in Silverlight 3. And also, in constructing this application you will learn the Silverlight 3-supported behavior related topics.
The Developing Freecell Game Using Silverlight 3 Series
Developing Freecell Game Using Silverlight 3 Part 1 In the two parts of series, we are going to develop another Silverlight 3 based Freecell game like the one shipped with Microsoft Windows. Our main purpose to write this game is to continue to explore the mouse related operations supported in Silverlight 3. And also, in constructing this application you will learn the Silverlight 3-supported behavior related topics.
Developing Freecell Game Using Silverlight 3 Part 2 In this second part of the series, we will delve into the crucial part in the Freecell game. In detail, we are going to examine the two mouse events, i.e. MouseLeftButtonDown and MouseLeftButtonUp, related event handler programming.
FreeCell is an extremely addictive solitaire card game invented by Paul Alfille, which is fun and very skill-dependent. It's said in the help documents every game (approximately 99.99 %) of FreeCell solitaire can be won with perfect play. This makes FreeCell card game much more interesting and popular than any of the rest solitaire variations.
FreeCell is a one-deck solitaire card game. All cards are dealt into 8 tableau piles. Four Cells (in the top left corner of the screen) and four foundation piles (top right hand corner) are placed above the tableau piles. Figure 1 indicates the general layout of the FreeCell game.
Figure 1: The screen layout of the FreeCell game
Foundation (4 Piles)
There are four foundation piles in a standard FreeCell game. Base card for foundation cells is Ace, i.e. you have to start building from the Ace of each suits. For example, an a hearts foundation cell will start with Ace of hearts and build 2 to 10, then jack, queen and finally king of hearts.
Tableau (8 Columns)
In a standard FreeCell game there are 8 columns of cards. These are called tableau piles. When you create new games you can change this rule and specify your own values. Tableau card arranging order is down in alternating colors, i.e. you have to arrange cards down in order and you have to arrange it in alternating colors. For example, you can put a seven of clubs on top of an eight of hearts because both cards belongs to different suits and they are opposite in color and order by rank. Again, card arranging order in tableau also can be changed for new games.
Multi move is enabled in the tableau. That is a group of cards can be moved as a group if they are in proper order and there is enough free spaces in tableau or free cells. Multi move can be enabled or disabled for new games.
Space may be filled with any available card. Again, this also can be changed for new games in the game editor window.
Free Cells (4 Cells)
Free Cells or reserve cells are an integral part of FreeCell game and the name FreeCell itself came with this group of cells normally found on the top left of the main game window.
Free cells are used to store cards temporarily. If there is no possible move in the tableau then you can make use of free cells to store cards. Each cell can hold only one card at a time. Cards in these free cells can be played like any other free cards and can be moved to either foundation or tableau cells.
The object of the game is to build up all cards on foundations from Ace to King by following suit. You win when all 52 cards are moved there, 13 to a pile.
In detail, the rules include:
- Top cards of tableau piles and cards from Cells are available to play.
- You can build tableau piles down by alternating color.
- Only one card at a time can be moved.
- The top card of any tableau pile can also be moved to any Cell.
- Each Cell (or Reserve space) may contain only one card.
- Cards in the cells can be moved to the foundation piles or back to the tableau piles, if possible.
In addition, the rules state that you can move only one card at a time, but you can move group of cards in the proper sequence if you have enough free (empty) Cells and/or tableau piles.
To try to win the game, you can refer to the following tips:
- Evaluate the game before making any moves.
Look for Aces and other low cards that are deeply buried in the columns. Find all those cards and develop a plan to free them before you begin moving cards.
- Leave as many free (empty) Cells as possible.
- Try to create an empty tableau as soon as possible. Empty tableau is even better than empty Cell because you can use it to temporarily store (until you need it again) a legal sequence of cards instead of just a single card.
- Look for plays that organize cards in sequences.
In the next section, let's introduce the same or similar solutions to the previous Solitaire game in developing the FreeCell game application.
Same or Similar Solutions to the Solitaire Game
At first glance, as many readers think, the FreeCell game required techniques quite resemble those in developing the previous Solitaire game application. With a brown study, you may find there are still some knots required to be tackled. How can we support the double click operations (like that in the Windows counterpart) and, at the same time, remain the color inverse characteristic in the FreeCell game? What kind of double click solution are the most appropriate one for the FreeCell game? Having said that, many readers may recall some Adobe Flash based online FreeCell games or other desktop styled ones. Further studying, you found that most of them did not provide the perfect supports like in the Windows built-in version, didn't you? Indeed, I met the similar how-do-you-do in developing the FreeCell application.
On the other hand, I found that many of the techniques and tips used in my old Solitaire game could be transplanted to write the current FreeCell game. Let's give a brief introduction about them.
Dealing with the Card Coordinates
For simplicity, we still select a Canvas control to serve as the parent control of all the card controls. Therefore, we will still fall back on the following functions to deal with the card coordinates:
Listing 1: The important card coordinates related functions
Dealing with the Card zIndex
And also, we'll adopt the same policy as that introduced in the Solitaire game to deal with the z index of each card. I.e. we will continue to use the two methods,
SetZIndex to control the value of the
zIndex property of moved cards. For brevity, I want to simply remind readers to notice the following points:
- By default, all the sub controls in a parent control have the same
zIndex property value, i.e. 0. However, due to the characteristics of Silverlight design, the controls with the same
zIndex property value still be rendered on the screen in 'different layers'.
- Considering that there are totally 52 cards and the valid value range for the
zIndex property is enough for our game, we can take the action to add 1 to the
zIndex property value of current moved card.
Still Using the Silverlight Menu Control
In this FreeCell game, we continue to fall back upon the Codeplex supported Silverlight Menu Control to control the overall logic of the game. As pointed out before, to use this Menu control in our game application there are three points deserved your attention, as listed below:
- You should change the
zIndex property value to a large integer (1000 is enough in our case), so that the menu items seem to hover over the other controls on the game screen.
- When you click a menu item you have to click other areas out of the menu's scope, then you can start your real play.
- Thanks to the Silverlight menu control, all reset associated things get along as in your familiar desktop applications.
Mouse Button Related Events
As first glance, the mouse button handling in the FreeCell game is much simpler than that in the Solitaire game. No dragging and dropping; no mouse capturing. As you will find, what left for us to do is to track the two mouse events, i.e.
MouseLeftButtonUp, to decide which card is the current one and which is the previous one. In fact, however, things are not as easy as you will image. All these related programming will be left to be discussed in the second article of the series.
Non-Double Click Support
As is known, in the Windows desktop styled FreeCell game, the mouse double click is a good implementation, which accelerates the move of the cards and loved by players. In our case, however, it's pretty difficult to support this solution, which mainly results from the introduction of the
InverseColorClickBehavior behavior. By further digging into the how-to, you can easily find the answer - the inner working of the
InverseColorClickBehavior behavior has built in another
MouseLeftButtonDown event handler which in a large degree encumbers the double click implementation in the outer layer.
For the above reason, reluctantly, we decide to discard the double click support in the FreeCell game. If you cute readers find other proper double click solutions, please share with us all.
A Modified Method- GetCurrentCard
In the FreeCell game, it's also frequently required to locate the current card. So, we have also introduced a method
GetCurrentCard to meet this requirement. Please notice that we've adapted the initial version.
Listing 2: The modified version of the method GetCurrentCard
Obviously, the main code has not changed, still with the static method
FindElementsInHostCoordinates of the VisualTreeHelper class to traverse object relationships in the Silverlight visual tree. Note herein after we removed the parent node from the result set, the judgment condition changes to
collidedElements.Count() >1. Why? This is because before we added each Card into the parent Canvas control, we had added some Path objects and Rectangle controls to the parent Canvas control in advance. For this, I remind you of the fact that although the method
GetCurrentCard will be commonly-used in your future possible similar game applications like the FreeCell, it's sometime necessary for you to make some modifications to adapt to your new development environment.
Again, I remind you to pay attention to the argument passed to the method
null) and the second argument passed to the method
FindElementsInHostCoordinates is not
Defining a New Card Control
In the FreeCell game, we've also defined an independent user control- Card - since the general logics are nearly the same as that in the previous Solitaire game. In this case, we only need to define the rank and suit in the Card control since all the cards on the screen are face up. And, without much modification, we'll continue to adopt the similar method to deal with the card related images. That is, the control maintains an
Uri array, with each Uri pointing to the corresponding card image. Listing 3 below gives the definition of the Card control.
Listing 3: The Card control definition
Different from the Solitaire game, herein we defined just 52 images except for the red Joker and black Joker. We no more need special images to act as placeholders or for special use. To retrieve the related image, we should also resort to the index pointer. And also, to facilitate the instantiating the Card control, we set up another constructor. Altogether, the Card control in this case is much simpler.
Defining Custom Inverse-color Behavior
Ever since the release of the Silverlight 3, a lot of new cool features have been introduced. One of the cool features is the support of behaviors and triggers, with which you can declaratively associate an action with an event or property value.
Why introduce Behaviors?
From a fundamental point of view, behaviors are pieces of code that a designer can attach to an object simply by dragging and dropping. As you've known, Microsoft Expression Blend 3 has a handful behaviors built in, including the previously-mentioned
MouseDragElementBehavior, which you can attach to a UI element to allow it to be dragged with the mouse. They tend to be simple to write, and they do a lot to enhance the power of Expression Blend--and Silverlight, too, for that matter.
The motivation for adding behaviors in Silverlight 3 is twofold. First, the behaviors are somehow work-around for the missing triggers in Silverlight. They allow closing the gap between WPF and Silverlight. Second, they allow designers to add interactivity without needing to write any code.
All in all, behavior is nearly a MUST HAVE if you are going to learn Silverlight 3 or higher.
Developing the InverseColorClickBehavior Behavior
For simplicity, in this case we select to use the ready-to-use part of Windows Presentation Foundation Pixel Shader Effects Library. However, to adapt to the use of the FreeCell game, we have to make a little modification with the
Below is the complete code for the custom
Listing 4: The complete code for the custom InverseColorClickBehavior
First, as you may notice, the InverseColor typed property
inverseColor plays the main role with which we achieve the inverse-color effect of the card. Second, in defining a custom behavior, we usually need to overwrite the two methods,
OnDetaching. In the two methods, we usually change the properties or events of the
AssociatedObject object. And then, in the related methods you can implement your custom logic to achieve your initial target of developing this custom behavior.
In the above custom behavior, we are only interested in the
AssociatedObject_MouseLeftButtonDown event handler. As you'll know, the
AssociatedObject object will refer to the Card control. Based upon the request of the FreeCell game, we just need to control the value of the
Effect property. According to the times (odd or even) you click the card, clear or set its
Effect property value. In our game, when we set
null, the card is deprived of the inverse-color effect; when we set
inverseColor, the card wears inverse-color effect.
Using the InverseColorClickBehavior Behavior
There is an important characteristic of behavior that you should remember: one behavior can only be attached to one control at a time; one control can be attached to multiple behaviors.
Let's next look at the concrete leverage of the custom
First, since we have totally 52 cards we can define the following variable to be used later:
Second, let's look at the initialization (in the method
InitAndStartGame) of the behavior array:
Third, let's look at the use of these behaviors (within the method
When each card is created, we assign a related behavior attached to the card. Note in our application once the behaviors are bound to the cards we no more care about the related detachments. For more details, you can refer to the source code accompanying this article.
Designing the Game
With the game rules in mind, starting from this section we are going to explore all the fundamental work in developing the FreeCell game.
Defining Global Variables
Note all the names in Figure 3 refer to the original addressing of the Windows-versioned solitaire game. For simplicity, we only list the most important data structures used in our game, as follows.
Listing 5: Part of the global variables definitions in our game
On the whole, the above variables bear the following tasks:
nMaxMovingCards: used to limit the maximum number of the pile of cards at the bottom moved onto the matched card at another column at the bottom area.
timer: used to control the whole process of the game as well as give other corresponding prompt.
CurrentCard: used to hold the current card. Whenever you click any card at the top left or bottom area (except for the top right area) on the screen, the value of the variable is decided.
InverseColorCard: used to remember the current inverse-colored card.
SecondInverseColorCard: used to remember the most recently inverse-colored card at the bottom area.
topElementList: used to remember the possible valid series of cards at the bottom area that will be moved to another column at the bottom.
PreviousMatchedCardInSeryAtBottom: in combination with the variable
topElementList, used to mark the first card in the valid series at the bottom area.
Cells: a Card array used to remember the possible four cards at the top left cells.
FoundationPiles: a List<Card> array used to remember the four piles of cards at the top right target area.
TableauPiles: a List<Card> array used to remember the eight piles of cards at the bottom area.
InverseBehaviorArray: An InverseColorClickBehavior array used to be attached to the fifty-two cards to implement the inverse-color effect.
For more details about the variables,
iGlobalCellsColumn, you will find their respective use in the second article. Let's next say a few more words about the multiple cards moving dialog.
A Multiple Cards Moving Related Dialog
You may have already notice a variable named
MoveMultiCardsDlg in the above definitions, which is just relevant to the current topic. In fact, you will also find the related usage in the next article.
Note the dialog
MoveMultiCardsDlg will pop up when we want move one or a group of ordered cards from one column at the bottom area to another empty column. Figure 2 illustrates one of the related snapshots.
Figure 2: A dialog pops up prompting you to select whether to move one card or more
In Figure 2, the bottom column we previously clicked has a group of ordered cards while the currently-clicked column is empty when a related dialog will pop up prompting you to select whether to move one card or a group of ordered cards. And, of course, the fact number of cards to move will also be decided by empty cells at the top left area.
To facilitate measuring the positions of the cards, we've also introduced a List<Rect> list called
PlaceHolder. In this case, there are totally sixteen placeholders defined, which will be initialized in the helper method
InitPlaceHolder during the initialization of the game. Listing 6 gives the related code.
Listing 6: The card placeholders’ initialization
As you see from the above code, there are totally 16 card placeholders in the main game view, 4 for the cells at the top left, 4 for the foundation piles at the top right, and the rest 8 for the tableau piles at the bottom. Also, note the above eight placeholders have same size while the bottom eight placeholders have a second same size different from the first.
In the next section, let's look into how to generate and deal cards with the data structures defined above.
Generating and Dealing Cards
To start the game, you first have to find some algorithm to deal the card, which is done in the method
Generating 52 Cards in Random Order
Listing 7 below gives the first part of this method. First of all, you should generate 52 cards in random order.
Listing 7: Generating 52 cards in random order
First, we define a Card array
arrPoker to hold the final 52 cards in random order. Another Card array
OrderPoker is used to store the ordered cards. Second, with the help of the method
RandomKDiffer(0, 51, 52, RandomI), we can generate 52 different cards with the index numbers ranging from 0 to 51. Third, the 52 cards stored in array
arrPoker are all initialized with face up (in fact none face-down cases are used in the FreeCell game). Last, as soon as each random card is generated a related InverseColorClickBehavior behavior is attached to it. This is one of the important features in the FreeCell game worthy to be noticed.
Setting up Elementary Scene for the Game
Please think back to the Windows' FreeCell game--before you press the menu 'Start', there will be some intuitive prompt on the screen. For this, we are also going to ornament the screen with some rectangles. Figure 3 shows the related snapshot.
Figure 3: The initial state when the game just starts
As is seen, some rectangles are positioned at the top area to clearly mark the cells and foundation piles area. Of course, you can use whatever controls you image to decorate these areas. Anyway, you have to handle the extra payout. Let's delve into this.
In our case, there are two points in relation to the payout. One is you have to build up another version of the frequently-used method
GetCurrentCard which we've already discussed earlier. The other is when and how we can add those rectangles.
In fact, in this Silverlight game, to add those rectangles has led to some more trouble.
First, considering the features of starting and restarting the game, we decide to add those rectangles inside the method
GenerateAndDealCards. For this, we cannot gain the effect that before you press the menu 'Start' some intuitive prompt ornaments appear on the screen.
Second, we decide to load the rectangles related xaml code dynamically just before loading the cards. Then, how to dynamically load this xaml code? Is there anything to be noticed? All of this is finished in the method
LoadRectangleElementsFirst, as listed below.
Listing 8: The key parts of code of the method LoadRectangleElementsFirst
In the above code, there are the following key points deserving your attention:
- We create dynamic XAML elements with LINQ to XML technique.
- We should first add the assembly reference to the System.Xml.Linq.dll.
- We create many XElement objects and populate it with detailed XAML information.
- In each of the XElement object related string, you must declare the two default XML namespace; or else, you will see the following alike error: AG_E_PARSER_MISSING_DEFAULT_NAMESPACE.
In the next section, let's see how to add cards to the parent container.
Adding Cards to the Parent Container
At the very beginning of the game, there are only the tableau piles having cards, i.e. each of the first four columns has seven cards while each of the rest four columns has six cards.
In contrast to the previous Solitaire game, the case in the FreeCell game is much simpler. For brevity, let's directly look at the related code.
Listing 9: Adding cards to the parent container
Easily seen, two double
for loops bear two tasks respectively: the first sets up the first four columns; the second fills the second four columns. It's not necessary to give detailed explanation; you can refer to the previous Solitaire game related articles.
Starting the Game
Everything getting ready, let's look at how to launch the game. In fact, this is pretty simple, part of which owes to the introducing of the MenuControl control. Below indicates the related code.
Listing 10: Click Start menu item to launch the game
Next, let's look at another necessary work when the game is over - restore the environment and restart the game.
Replaying the Game
In this section, let's check out what are the differences between this FreeCell game and the previous Solitaire game in terms of replaying the game. In fact, they are nearly the same as far as the implementation logic is concerned.
As a complete game application, it should provide the support for replaying the game. As the name implies, before restarting the game we should reset all required data structures to their original values or states. It, in fact, is not as easy as it seems to have all data restored. On the one hand, the resetting work relies upon what kinds of data structures you introduced; on the other hand, it depends on where you launch the resetting action.
Listing 11 below shows the game restart related code in this FreeCell game.
Listing 11: Code for starting or restarting the game
By invoking the only helper method
InitAndStartGame, you get everything ready to play the game. Now, let's continue to follow up the scent to see the differences in the initialization programming.
Listing 12: Code for the helper method InitAndStartGame
First, compared with the initialization implementation in the Solitaire game, we initialized two important structures,
FoundationPiles. In this case, the data structure
TableauPiles is used to hold eight columns of cards at the bottom area, while another one
FoundationPiles is used to hold four columns of cards at the top right area.
Second, we set up fifty-two InverseColorClickBehavior typed behaviors, which are used to be attached to the initial fifty-two cards respectively. As a general rule, as is already pointed out above, in Silverlight programming field one behavior can only be attached to one UI element at a time.
Last, as done in the previous Solitaire game, we still have to subscribe to the mouse related events at the
Loaded event handler of the MainPage control.
Listing 13: Code for registering the mouse events
As emphasized before, we do not need to un-register the mouse events and register them again when we want to replay the game. And also, we have to subscribe to the two events herein rather than in the helper method
InitAndStartGame to avoid unexpected things happen.
In the current version of the FreeCell game, we have not programmed the
MouseMove event handler. In the counterpart of the Windows version, if you click some card and move mouse over the screen, then if there is a card matching the current clicked card at the right area the matched card is located at the mouse cursor will change accordingly to give a more intuitive guide. Due to various reasons, we've not accomplished this support. You can supplement this function yourselves.
In this first part of the series, we've introduced the main skills used to develop the Silverlight based FreeCell game. Note that since many of the techniques are similar to that used in the previous Solitaire game, we discarded the detailed discussion about them. Therefore, for more details about the related implementations, you can refer to the articles introducing the Solitaire game. In the next part of the series, we'll continue to explore the rest and core part of the FreeCell game, i.e. the two mouse events (
MouseLeftButtonUp) related programming.
I'm a college teacher and also a freelance developer and writer from WeiFang China, with more than fourteen years of experience in design, and development of various kinds of products and applications on Windows platform. My expertise is in Visual C++/Basic/C#, SQL Server 2000/2005/2008, PHP+MyS...
This author has published 81 articles on DotNetSlackers. View other articles or the complete profile here.
Please login to rate or to leave a comment.