This project is part of my assignment while studying Python in Monash University.
All right reserved. This project uses a module called main.py as a controller to play the game, and a controller class named notfreecell.py to control the interface. As part of our assignment, we were given a task to create a game that similar to FreeCell game but in different flavours according to the specifications. Application will take several inputs as follows: A minimum value of a card, a maximum value of a card, and the number of suit that are going to be played. However, due to the nature of the card themselves, there are several restrictions when a player wants to play this game. They are as follows:
- The value of a card can be in the domain of number 1 to 13, in which number 1 will be represent an ace and 11, 12, 13 will be Jack, Queen, and King of a suit respectively.
- Maximum value cannot be smaller than minimum value.
- Due to maintain the nature of this game, for every number of suit that less than 4, the number of foundations will still be 4. Whereas, the number of foundations will the same with the number of suits for every number of suits more than 4.
- This game will end by itself, and give the user information provided all of the cards have been placed gracefully into every available foundation.
Application will have to have a series of validation features according to https://en.wikipedia.org/wiki/FreeCell, some of them are as follows:
- Foundation: only an Ace of every suit can be placed for the first time or at the bottom of the foundation. Moreover, only the sequential number can be placed accordingly until reach the end of appropriate suit.
- Cells: Every card can be placed in the free cell, however once a card has been placed from either foundation or tableau, it cannot have another card being pushed. Because, a free cell can only contain a card at a time.
- Tableaus: Every tableau has the characteristics of a stack ADT object which uses Last in First Out concept. They will validate for every movement from others such as other tableaus, foundations, or even cells. For every card that will be placed, it cannot have the same card colour from its stack and must have a different number of one from its stack.
However, there are some general assumptions for using this game, they are as follows:
- No time limit constraint;
- A player cannot create a new game while the game is still in play;
- The movement of a card can only use the source and destination input.
There are 12 files with an extension of .py, and a package folder in this project. From those files, developer has created 10 classes and 2 modules. Some of the classes are base class and others are derived class from base class or an enum class.
This application has 3 Enum classes, CardColor, CardSuit, and Color. They create a relationship by using other object as a field in another. For example, CardSuit has a property called CLUBS, which has a property from CardColor called BLACK, and that particular CardColor property has a property binary UTF-8 value of '\033[30m'. This allows developer to use existing value and can make the changes easier and become more agile in development stage.
In addition to that, another set of classes that heavily use inheritance concept are Stack, Deck, Tableau and Foundation. Stack will become the base class among them, in terms that every child class can re-use or extend the usage of their parent’s method, making every change become more precise and verbose. For example, one of the methods inside Stack class is push() method, it allows a card object being stacked on top of the list of cards inside of its object. However, there are different logic or validation sets for every child classes implementation, one class might have to check its color and number, another will have to check only its suit and number. Eventually, for every successful validation will result a card be pushed on top of the object. Hence, a child class will invoke parent’s class method only after a card has been successfully validated.
To give more abstraction concept of every implementation, here are the classes inside this project.
- Color Class This class uses enum as its base class, for constructing properties inside of its class that allows for any implementation to avoid any unnecessarily mistakes, or any code repetition. For example, to print a card, it will use a string value to represent a colour. Other usage from other classes such as display boards of matrix can also use these properties values.
- CardColor Class It will only have 2 properties comprises of BLACK and RED, both use Color Class enum as one of its value and an integer value to represent mathematical value of a colour. The latter allows mathematical operation by comparing the integer value of a colour inside of any cards. This integer value comprise of the number of 0 or 1 which represent the colour of RED and BLACK respectively.
- CardSuit Class This class uses 4 properties with 2 values each, to determine a face suit of a card. It also has four UTF symbols to denote a card in its manner. The goal is to give a better representation and user experience when the user wants to play the game. This class has 3 accessor methods to get some of its values such as colour name, colour value and the name of its instance. This class will be one of the arguments for another class such as Deck and Card class.
- Card Class This class is the core object of this game, every card object in this game will be instantiated distinctly with its number, face or suit, and colour. Inside a card, a number will be assigned for each card from 1 (Ace) to 13 (King). Every card has a face or suit that comprises of four types: diamonds, hearts, spades, and clubs. Therefore, there will be only 52-card given, ignoring the jokers. In addition, this class also has 3 arguments, which 2 of them are mandatory to determine a card’s number and suit. Its colour will be determined based on its suit since its suit will have only a colour.
- Stack Class
Based on the class diagram provided above, It is clear, this application heavily uses object oriented programming concept that uses Stack class as the base class implementation for Deck, Tableau and Foundation class. They will employ pre-existing method inside the base class to avoid redundancy and unified the same concept across implementation. Some of the original code of Stack have been modified to gain consistency across implementation such as variable naming convention and maintain robustness.
Stack class has 12 methods (a function inside a class is called a method) comprises of 4 special methods in which allows to override existing method, and 8 standard methods allows implementation object to use the benefit of a list object whilst maintaining its LIFO concept. - Deck Class This class is a representation of a collection of cards, which can be created specifically by user’s request. This deck must have the ability to create cards from a minimum value of 1 which will be represent by an Ace and a maximum value of 13 which will be represent by a king. Furthermore, it is essential to a deck which it can create multiple cards depends on how many suit it’s being given. For example, a deck can create 2 suits of cards, each suit will consist of card from 1 to 5. Hence, its code will be like this d = Deck(1,5,2). It also have some validation algorithms to prevent errors, such as maximum value of a card must have larger number than minimum value, also they must have a greater than 0 value. This class also must exhibit another function based on the specifications such as shuffle cards, and draw card. It creates a factory pattern method for creating card in a static method. Generally, there are 2 special methods (constructor and str function), a static method and a shuffle method. However, due to inheritance implementation using Stack as its base class, this class also have all its parent methods. This diagram will depict Deck class in class diagram.
- Foundation Class This class is sometimes called as winner deck, because it will hold stack of cards that will be moved from either Tableau or Cells. All cards must be placed per its suit and number sequentially. The number of foundation object will depend on the number of suits that will be created in the beginning, except for the number of suits is less than 4, the number of foundation will still be 4. Moreover, this class uses Stack as its base class, to re-use pre-existing methods. However, one of its derived methods will be overridden to add logic such as add_card method, another will be raise into NotImplementedError to avoid unnecessarily invoked from caller. Finally, if the first appropriate card successfully placed in the foundation, it will set the value for _type variable to lock only that particular type of card suit can be accepted. This class has 2 magic methods, a property set for location (get and set methods) and 2 overridden methods. This diagram will illustrate the foundation’s design.
- Tableau Class This class is designed to contain all cards after being distributed from the deck evenly from left to the right and then go back to the left until it empty. This model allows every card being distributed evenly regardless how many they are. This class also use Stack as its base class, even though it has the same model for push the card, it has 2 distinct models in which the first distribution from the deck will invalidate any rules, whereas any card movement from other place will have to be validated by its rules. The rule itself is part of the specification in which has to be in different colour and the number can only have 1 number difference.
- Cell Class This class is the free cell class which can contain any card from any subset, however only a card can be placed at a time. Without the possibility to override or stack the card on top of it. This cell will use the same convention standard name from stack, however it does not have behaviour list. It will guarantee that only a card can be placed at a time. It also uses magic function to overwrite to string method from object to ensure the same user experience. It has relatively simple and straightforward class model since it does not use any base class.
- NotFreeCell Class
This class is the brain of this game, its responsible for creating and constructing game board and move the card from deck to tableaus, and from tableaus to cells or foundations as well as across tableaus. It enables user for interacting to game domain (cards, tableaus, cells and foundation). Even though the game board is displayed in matrix manner, it does not emphasise on use matrix for interacting to cards to any other objects.
First, this game will be constructed when users instantiate this class, it passes the arguments to deck class and creates all of the required objects such as tableaus, foundations and cells accordingly. Next, it will shuffle the cards within the deck and distribute them evenly to the tableaus.
Second, main module will invoke display_board method in which construct the matrix from all of the available objects accordingly divided into 2 sections; upper section contains cells and foundations and lower section contains only tableaus. The number of rows will adjust according to the maximum length of a tableau.
Third, main module may invoke move_card method that receives input from the user using notation provided in the label such as T1 to denote Tableau object number 1 or C2 to denote cell object number 2. The movement of the card will pass on to the object which holds the card objects and will be validate accordingly. Last, for every movement, it will be checked whether all of the cards are no longer exist outside of foundations object, hence a user will be notified that he/she has won the game. - Main module This main module is the starting point of this application, it only has 3 functions which are input_validator, play_game and main function. First, main function will be invoked, then it will ask the user to input minimum value, maximum value and how many suit user want to play. Next, input_validator function will use recursive function to validate user input to avoid any unnecessarily typo or mistake character other than numbers. Furthermore, play_game function will take into account every source and destination user input such that suppose the user do not give proper input or failed in execute command from the user, it will ask user to input again using recursive function. Finally, play_game function will get the result from move_card method from NotFreeCell class to determine whether the game has already finished and stopped the application.