A UNIX console based implementation of the card game Cheat built in Python3 using Firebase as a backend.
The only requirements for play are a UNIX system with Python3 installed.
To download the program, either clone the repository using
git clone https://github.com/aparande/PythonCheat.git
or download it from here.
Upon cloning/downloading the repository, please run install all the necessary packages with pip.
pip3 install -r requirements.txt
python3 cheat.py
The objective of Cheat is very simple: to get rid of all your cards. It is best played with 3 to 5 people.
First, the deck of cards is divided as evenly as possible among all of the players.
The first round begins with the card of rank 2
. The first player places down a card and declares its rank to be 2
. If they have a card of rank 2
, they may play it, but they may also lie. All other players then have an opportunity to either call them on their bluff or let gameplay continue. When players call the bluff, if the initial player was indeed bluffing, they take the entire middle pile. If they were not bluffing, then all players who were wrong divide the pile in the middle among themselves.
The next player commences the subsequent round with a card of rank 3
. Gameplay continues until someone runs out of cards. When the round's designated rank reaches A
, the next round will wrap around back to rank 2
.
In Cheat, it is imperative that other players have absolutely no knowledge about the other players' cards. To enforce this requirement, each user plays the game on a different computer. The program coordinates the gameplay between different computers through Firebase's Realtime Database. This provides a layer of separation between players and removes the complex, unintuitive gameplay experience which workarounds such as passwords would necessitate.
I chose Firebase Realtime Database in particular because it is NoSQL, so it is much easier to store the unstructured data required to manage the game state than with a relational database. Moreover, because it is real-time, the game can be played in real-time with constant updates. Firebase also has well designed APIs to interact with the database as if it was a regular REST API.
As a scripting language, Python is incredily versatile in the programs it can create. Because I was using Firebase, I needed a language which made it easy to handle concurrency issues and act on events triggered in real-time. Python has these capabilities readily available and it still has the object-oriented aspects which allow for clean but complex functionality.
Card
: A model class to represent cardsCardGroup
: A group of cards which can model a deck, a hand, or the center pile. Uses a HashMap-like implementation to provide O(1) retrieval operationsCheatGame
: The game client which coordinates between theEngine
and Firebase to show information and take input from the userEngine
: The game engine which each user runs. It manages game rules and controls game flowPlayer
: A model object which represents a PlayerStream
: A listener class which listens to updates from Firebase
firebaseutils.py
: Firebase utility functions to abstract away the Database implementationutils.py
: Minor utility functions
By structuring the code in this way, I reduced the amount of data I needed to store in Firebase and made it easier to handle concurrency. Since each client maintains its own game state through the Engine
, I do not need to store the entire game (each player and their hands, the pile, etc) in Firebase. Instead I can just update the last card played and have every client update its internal state accordingly.
The Stream
class also makes it easier to handle concurrency because I can pass it a method which gets called every time an update is made to Firebase. This allows the game to be asynchronous and update in real time.
I chose to create Player
, Card
, and CardGroup
model classes because having these abstractions which reflect real objects makes the code easier to read and the logic easier to understand. It also groups together related functionality, reducing code repetition.
I chose to make CardGroup
a modified HashMap because the O(1) lookup times make it easy to use when checking if a player is making a valid move. Adding an ordering
to the HashMap also enables easy distribution of the cards in the group to different players via the distributeTo
function because each removal is also O(1), enabling me to use the CardGroup
as a deck.
- If two people with the same name try to enter a room, the second one is barred from entering
- People cannot enter a room once the game has begun
- A game can't start with fewer than 2 people, and more than 6 people can't enter a room
- Pyrebase: A Firebase API wrapper [link]