Design Objectives
The main objectives of the design for the Chipless Android app are grouped into three main categories: Inputs, Processes, and Outputs to ensure technical accuracy and measurability.
Inputs
- Player Data Input: Allow users to enter player names, starting chip counts, and roles.
- Game Configuration: Users can specify the number of players, small and big blind amounts.
- Player Actions: Users can interact with the game via fold, check, call, raise buttons.
- Save & Load Feature: Users can save and load game sessions.
Processes
- Game Logic: Implement and enforce Texas Hold’em rules.
- Turn Management: Ensure each player acts in the correct order, skipping inactive players.
- Bet Validation & Deduction: Validate that players can only bet within their chip balance.
- Data Persistence: Store and retrieve game states reliably to ensure session continuity.
Outputs
- User Feedback: Display error messages, confirmations, and game state updates.
- Chip Tracking: Update and show player balances after each action.
- Pot Updates: Reflect real-time changes to the pot as bets are placed.
- Winner Declaration: Display the winner and update chip redistribution accordingly.
Decomposition / Step-Wise Refinement / Top-Down Design
The system is designed using a Top-Down approach, breaking down the problem into smaller modules to simplify development and improve maintainability. Each element of decomposition is necessary to address specific functionalities effectively.
Game Configuration
Reason for Breakdown: Setting up a poker game involves multiple independent inputs that need validation before gameplay begins. Handling this separately ensures a smooth start to each game.
- Player input collection
- Validation of names, chip counts, and blind values
- Assignment of dealer, small blind, and big blind roles
Game Progression
Reason for Breakdown: Poker involves sequential decision-making, so tracking turns, bets, and player actions requires a structured approach to avoid logic errors.
- Manage betting rounds ( pre-flop, flop, turn, river)
- Track player actions ( bet, fold, check, call, raise)
- Ensure turn-based progression skipping folded players
Betting System
Reason for Breakdown: Bets directly affect the game state, so accurate tracking of chip deductions and pot updates is crucial to avoid inconsistencies.
- Validate bets ( within available chips, minimum raise rule enforcement)
- Deduct chip amounts and update the player’s balance
- Update pot total and maintain fairness in chip distribution
Winner Determination & Chip Redistribution
Reason for Breakdown: Resolving the game outcome correctly ensures fairness and logical integrity.
- Enable manual selection of winner(s)
- Distribute chips according to game rules
- Reset game state for the next round
Data Persistence
Reason for Breakdown: Saving and loading functionality ensures players can resume games at a later time without losing progress.
- Save game state including chip counts, player positions, and pot total
- Load and validate stored data for accuracy
User Interface & Error Handling
Reason for Breakdown: Ensuring a smooth user experience requires UI elements to provide clear visual feedback.
- Display player information, pot size, and turn order
- Show validation messages when errors occur
- Implement undo functionality for correcting mistakes
Overview of System’s Processes
The app follows a series of steps to ensure smooth game management:
-
Setup:
- Players input their names, starting chips, and roles ( e.g. dealer, small blind, big blind ).
- Validate the input data, ensuring there are no duplicates or invalid entries.
-
Game Start:
- The table configuration ( including blind amounts and the number of players ) is set up.
- Players are assigned roles based on the dealer position.
-
Gameplay:
- Players take turns making bets, checking, folding, or raising.
- Each player’s bet is tracked and added to the pot.
- The game progresses through rounds ( pre-flop, post-flop, turn, river ).
- The pot is updated, and players’ chip counts are adjusted accordingly.
-
End of Game:
- At the end of each game or round, the winner is determined based on hand strength.
- Chips are redistributed, and the game resets for the next round.
-
Data Persistence:
- The app automatically saves the game state ( players, bets, chip counts ) so that the user can resume later.
Interface Designs
Inputs Focus
The app will accept several inputs from the user. These include:
- Player Names: Text field popups for entering new player names.
- Starting Chips: Integer input field to specify the starting chip count for each player.
- Small and Big Blind: Integer input fields for configuring blind amounts.
- Player Actions: Buttons for actions ( fold, check, call, raise ) during each round.
- Game Configuration: Options for setting up the number of players, table size, and starting roles.
The user interface will provide immediate feedback on any invalid inputs ( e.g. invalid characters in names, invalid bet amounts ).
Outputs Focus
Outputs will include the following:
- Player Information: Display names, balances, statuses, and current bets.
- Game Status: Display current round ( e.g. flop, river, etc. ), player turn, pot total, and status messages ( e.g. player folded, met the bet ).
- Game Results: Display the winner at the end of the game or round, and update chip counts.
- Error Messages: Display error indications for validation failures ( e.g. invalid bet amount, player name duplicates ).
Variables / Data Structures / File Structures to be Used
- Player Objects: Each player is represented by a
Playerobject, containing variables for each player:name,balance,statusandcurrentBet. - Players Object: Stores a
List<Player>and provides functions to manage thePlayerobjects using aplayerIDor perform mass operations such asresetAllForNewRound(), which iterates through allPlayerobjects and adjusts variables accordingly for the next betting round. This object also manages thefocusPlayerand thedealerPlayer. - TableDataViewModel: This ViewModel manages the
Playersobject,Potsobject and MutableState Variables, including thecurrentTableBet,bettingRound,startingChips,bigBlindandsmallBlind. The ViewModel is used to ensure that game progression is protected against app configuration changes and can persist accross screens. - Game State Persistence: The game state ( players, table configuration, pot total, etc. ) will be saved using local storage ( e.g. a local database ).
- Database Integration: Using a local database ( e.g. SQLite or Room ) to store saved games and retrieve data for game resumption.
Validations Required
- Player Name Validation: Ensure that player names are not empty, contain no invalid characters, and are unique.
- Chip Count Validation: Ensure that the starting chip count is a positive integer for each player.
- Blind Validation: Ensure the small blind is less than the big blind and both are positive values.
- Bet Validation: Ensure the bet amount is within the player’s available chip count and is a valid number.
- Turn Validation: Ensure players act in the correct order, and no actions are taken out of turn.
- Game State Validation: Ensure that the game has sufficient players to start and that all required actions ( e.g. fold, call ) are available at the correct stage.
Classes And Variable Tables
TableDataViewModel Variables
| Variable | Data Type | Description | Validation |
|---|---|---|---|
players | Players | Manages players: List<Player> with helper functions to perform mass operations on all players or operations on individual players using a playerID. | This is immutable and therefore will never change, although there are variables held within this object that are mutable. |
tableConfig | TableConfig | Manages TableConfig.startingChips, TableConfig.bigBlind and TableConfig.smallBlind with helper functions. | This is immutable and therefore will never change, although there are variables held within this object that are mutable. |
currentTableBet | Int | Stores the current bet that all players must match. This variable observes the state of Players.highestBet, mirroring its current value. | This variable cannot be reassigned and only changes if Players.highestBet changes. |
tablePots | TablePots | Manages a TablePots.mainPot and TablePots.sidePots with helper functions, and calculates the TablePots.currentPot. | This is immutable and therefore will never change, although there are variables held within this object that are mutable. |
_bettingRound | MutableState<BettingRound> | Available only within TableConfig, stores the current betting round (e.g. Flop, River, Turn). | The only changes that can occur to this variable are managed by validated helper functions within the TableDataViewModel, to ensure that only the correct data type is given. |
bettingRound | State<BettingRound> | Mirrors the state of _bettingRound, but in an immutable form. | This variable cannot be reassigned and only changes when _bettingRound changes, which can only be changed by functions within the TableDataViewModel. |
Players Variables
| Variable | Data Type | Description | Validation |
|---|---|---|---|
_players | List<Player> | Available only within Players, stores a list of Player objects that is interacted with using helper functions. | The only changes that can occur to this variable are managed by validated helper functions within Players, to ensure that only the correct data type is given. |
participatingIDs | List<Int> | Stores a list of existing players that are not sitting out. | This uses derivedStateOf { } to calculate the value whenever dependent variables change. Therefore this variable only ever changes when one of the variables it’s dependent on, changes. |
activeIDs | List<Int> | Stores a list of existing players that are not sitting out, have folded or have gone all in. | This uses derivedStateOf { } to calculate the value whenever dependent variables change. Therefore this variable only ever changes when one of the variables it’s dependent on, changes. |
highestBet | State<Int> | Stores the highest bet amount of any player in _players. | This uses derivedStateOf { } to calculate the value whenever dependent variables change. Therefore this variable only ever changes when one of the variables it’s dependent on, changes. |
_focusID | MutableIntState | Available only within Players, stores the playerID of the current focus player. | The only changes that can occur to this variable are managed by validated helper functions within Players, to ensure that only the correct data type is given. |
focusID | Int | Observes the state of _focusID, mirroring its current value. | This variable cannot be reassigned and only changes when _focusID changes, which can only be changed by validated helper functions within Players. |
focusPlayer | Player | Stores the Player object of the current focus player. | This uses derivedStateOf { } to calculate the value whenever dependent variables change. Therefore this variable only ever changes when one of the variables it’s dependent on, changes. |
_dealerID | MutableIntState | Available only within Players, stores the playerID of the current dealer player. | The only changes that can occur to this variable are managed by validated helper functions within Players, to ensure that only the correct data type is given. |
dealerID | Int | Observes the state of _dealerID, mirroring its current value. | This variable cannot be reassigned and only changes when _dealerID changes, which can only be changed by validated helper functions within Players. |
dealerPlayer | Player | Stores the Player object of the current dealer player. | This uses derivedStateOf { } to calculate the value whenever dependent variables change. Therefore this variable only ever changes when one of the variables it’s dependent on, changes. |
smallBlindPlayerID | Int | Observes the state of _smallBlindPlayerID, mirroring its current value. | This variable cannot be reassigned and only changes when _smallBlindPlayerID changes, which can only be changed by validated helper functions within Players. |
smallBlindPlayer | Player | Stores the Player object of the current small blind player. | This uses derivedStateOf { } to calculate the value whenever dependent variables change. Therefore this variable only ever changes when one of the variables it’s dependent on, changes. |
bigBlindPlayerID | Int | Observes the state of _bigBlindPlayerID, mirroring its current value. | This variable cannot be reassigned and only changes when _bigBlindPlayerID changes, which can only be changed by validated helper functions within Players. |
bigBlindPlayer | Player | Stores the Player object of the current big blind player. | This uses derivedStateOf { } to calculate the value whenever dependent variables change. Therefore this variable only ever changes when one of the variables it’s dependent on, changes. |
Player Variables
| Variable | Data Type | Description | Validation |
|---|---|---|---|
name | String | Stores the name of the player to be displayed on the table. | This is only changed using a validated user input, which ensures it’s a String, that it isn’t a duplicate name and that it’s not too long (rather than a character limit, this is based on the length of the characters to ensure the entire name can be displayed on screen). |
status | PlayerStatus | Stores the current status of the player (e.g. IDLE, BET_MATCHED, FOLDED). | The only changes that can occur to this variable are managed by validated helper functions within Player, Players and the TableDataViewModel, to ensure that only the correct data type is given. |
balance | Int | Stores the current chip count in the player’s possession. | The only changes that can occur to this variable are managed by validated helper functions within Player and Players, to ensure that only the correct data type is given. |
currentBet | Int | Stores the current bet amount made by the player. | The only changes that can occur to this variable are managed by validated helper functions within Player, to ensure that only the correct data type is given. |
TableConfig Variables
| Variable | Data Type | Description | Validation |
|---|---|---|---|
_startingChips | MutableIntState | Available only within TableConfig, manages the number of chips each player starts with when calling TableDataViewModel.initialiseNewTable(). | The only changes that can occur to this variable are managed by validated helper functions within TableConfig, to ensure that only the correct data type is given. |
startingChips | State<Int> | Mirrors the state of _startingChips, but in an immutable form. | This variable cannot be reassigned and only changes when _startingChips changes, which can only be changed by functions within TableConfig. |
_bigBlind | MutableIntState | Available only within TableConfig, manages the big blind bet that is paid by the Players.bigBlindPlayer when calling TableDataViewModel.initialiseNewMatch() or TableDataViewModel.initialiseNewTable(). | The only changes that can occur to this variable are managed by validated helper functions within TableConfig, to ensure that only the correct data type is given. |
bigBlind | State<Int> | Mirrors the state of _bigBlind, but in an immutable form. | This variable cannot be reassigned and only changes when _bigBlind changes, which can only be changed by functions within TableConfig. |
_smallBlind | MutableIntState | Available only within TableConfig, manages the small blind bet that is paid by the Players.smallBlindPlayer when calling TableDataViewModel.initialiseNewMatch() or TableDataViewModel.initialiseNewTable(). | The only changes that can occur to this variable are managed by validated helper functions within TableConfig, to ensure that only the correct data type is given. |
smallBlind | State<Int> | Mirrors the state of _smallBlind, but in an immutable form. | This variable cannot be reassigned and only changes when _smallBlind changes, which can only be changed by functions within TableConfig. |
TablePots Variable
todo {Update Table}
| Variable | Data Type | Description | Validation |
|---|---|---|---|
mainPot | Pot | Stores the Pot object to be used as the main pot. This is the initial pot used to store bets that players make. If a player goes ALL_IN, then this pot is no longer used and a side pot is created and added to sidePots. In the BettingRound.SHOWDOWN, this pot is split amongst all players. | |
sidePots | MutableList<Pot> | Starts empty but when a player is forced to go PlayerStatus.ALL_IN, because they are unable to contribute to the Pot any longer, a side Pot is created in this list. A side Pot, once created, will be contributed to instead of the mainPot. In the BettingRound.SHOWDOWN, each side Pot is split amongst all players that contributed to it. | |
currentPot | Pot |
Pot Variables
todo {Update Table}
| Variable | Data Type | Description | Validation |
|---|---|---|---|
balance | MutableIntState | Stores the chip count for the Pot. |
Major Algorithms which Have Been Tested and Justified Against Design Objectives
- Bet Calculation Algorithm: Validates the amount of chips to bet, updates the pot total, and adjusts the player’s chip count.
- Turn Management Algorithm: Ensures that the game progresses in the correct order, skipping folded players and correctly determining the next player’s turn.
- Winner Determination Algorithm: Compares players’ poker hands to determine the winner, based on Texas Hold’em rules.
- Game Save/Load Algorithm: Retrieves and saves the game state, ensuring data integrity and the ability to resume the game from where it left off.
todo {Further elaboration on specific algorithms}
Test Strategy / Test Plan
- Unit Testing: Each function will be individually tested to ensure expected behaviour.
- Integration Testing: Ensure UI interactions, game logic, and data storage work together seamlessly.
- UI Testing: Verify interface responsiveness across various screen sizes.
- Validation Tests: Ensure all user inputs trigger the correct validation messages.
- Load Testing: Test performance with maximum players and active game sessions.