diff --git a/LICENSE b/LICENSE
index 277f2f3..e9edad2 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,6 +1,6 @@
MIT License
-Copyright (c) 2024 Shrey Agarwal
+Copyright (c) 2024 Gaurav Narula
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/README.md b/README.md
index 12cc647..e69de29 100644
--- a/README.md
+++ b/README.md
@@ -1,20 +0,0 @@
-# Bets Manager ( Teen patti / Poker)
-
-I just wrote a small script to maintain the bets and balances for each player while in gambling games like Teen Patti or Poker so that you can play even without any chips/ cash.
-
-Currently it does not take balance in account during bets. If the balance is over it takes it in negative ( Does not fold the player or considers all-in ).
-
-You can run this script and it will keep track of players and their bets in each round and save the overall loss/ profit in a file.
-
-To use this, go to the terminal and type
- `git clone https://github.com/shreyagarwal13/teen_patti.git`
- `cd teen_patti`
- `python3 bet_manager.py`
-
-Input y/n for yes and no to start or end each round.
-
-Entering -1 in the middle of a round will end the round.
-
-Enter the player no. who won after each round to add the pot to their balance.
-
-You can more players at the end of each round.
\ No newline at end of file
diff --git a/bets_manager.py b/bets_manager.py
index b80ac06..75fec8d 100644
--- a/bets_manager.py
+++ b/bets_manager.py
@@ -1,125 +1,507 @@
-#This script is used to track the bets in each round of a teen patti game
-import json
+class colors:
+ """Colors class:reset all colors with colors.reset; two
+ sub classes fg for foreground
+ and bg for background; use as colors.subclass.colorname.
+ i.e. colors.fg.red or colors.bg.green
+ Also, the generic bold, disable,
+ underline, reverse, strike through,
+ and invisible work with the main class i.e. colors.bold"""
+
+ reset = "\033[0m"
+ bold = "\033[01m"
+ disable = "\033[02m"
+ underline = "\033[04m"
+ reverse = "\033[07m"
+ strikethrough = "\033[09m"
+ invisible = "\033[08m"
+
+ class fg:
+ black = "\033[30m"
+ red = "\033[31m"
+ green = "\033[32m"
+ orange = "\033[33m"
+ blue = "\033[34m"
+ purple = "\033[35m"
+ cyan = "\033[36m"
+ lightgrey = "\033[37m"
+ darkgrey = "\033[90m"
+ lightred = "\033[91m"
+ lightgreen = "\033[92m"
+ yellow = "\033[93m"
+ lightblue = "\033[94m"
+ pink = "\033[95m"
+ lightcyan = "\033[96m"
+
+ class bg:
+ black = "\033[40m"
+ red = "\033[41m"
+ green = "\033[42m"
+ orange = "\033[43m"
+ blue = "\033[44m"
+ purple = "\033[45m"
+ cyan = "\033[46m"
+ lightgrey = "\033[47m"
+
+
+# This script is used to track the bets in each round of a teen patti game
+import json
import os.path
+import sys
+from prettytable import PrettyTable
-players = {}
+# --- Constants ---
+FOLD = 0
+END_BETTING = -1
+SHOW = -2
+WINNING_HISTORY_PATH = "winning_history.json"
-winning_history_path = 'winning_history.json'
+players = {}
+print(
+ colors.fg.lightred
+ + "Don't Fiddle with the game else the game will fiddle with you!! "
+ + colors.reset
+)
+winning_history_path = "winning_history.json"
check_file = os.path.isfile(winning_history_path)
-load_game = "n"
+load_game_input = "n"
if check_file:
- load_game = input("We found saved balances, do you want to load them? (y/n) ")
+ load_game_input = input("We found saved balances, do you want to load them? (y/n) ")
+last_winner_id = None # Keep track of the last winner
+
+
+def check_return_int_conversion(string):
+ if string == "-1":
+ return -1
+ if string == "-2": # Adding Show command
+ return -2
+ if string.isnumeric():
+ return int(string)
+ else:
+ print(
+ colors.bold + colors.fg.red + "Invalid Input!!, Ending Game" + colors.reset
+ )
+ exit(0)
+
def check_yes_no(yes_no_ip):
+ """Checks if the input string is 'y' (case-insensitive)."""
if yes_no_ip.lower() == "y":
return True
else:
return False
-def save_winnings():
- with open(winning_history_path, 'w', encoding='utf-8') as json_file:
+
+def save_data():
+ with open(winning_history_path, "w", encoding="utf-8") as json_file:
json.dump(players, json_file)
- print('\nPlayer balances saved!\n')
+ print(colors.fg.green + "\nPlayer Data saved!\n" + colors.reset)
+
+
+def get_colored_input(
+ prompt, prompt_color=colors.fg.lightcyan, input_color=colors.fg.lightgreen
+):
+ """Gets input from the user with specified colors."""
+ print(prompt_color + prompt + input_color, end="")
+ return input()
+
+
+def print_colored(message, color=colors.reset):
+ """Prints a message with the specified color."""
+ print(color + message + colors.reset)
+
def input_players():
- num = int(input("Enter number of players "))
+ """Gets initial player information from the user."""
+ global players # Modifies the global players dict
+ num = 0
+ while num < 2:
+ # Pass the prompt string directly to get_colored_input
+ num_str = get_colored_input("Enter number of players: ")
+ num = check_return_int_conversion(num_str)
+ if num < 2:
+ # Use the print_colored helper
+ print_colored(
+ "No no, do it again, this time atleast 2 players.", colors.fg.lightred
+ )
for i in range(num):
- player_name = input("\nEnter name of Player number "+str(i+1)+" ")
- player_balance = int(input("Enter balance of "+player_name+" "))
- players[i+1] = {
- "id":i+1,
- "balance":player_balance,
- "name":player_name
- }
+ player_name = get_colored_input(f"\nEnter name of Player number {i + 1} ")
+ while True:
+ balance_str = get_colored_input(f"Enter balance of {player_name.title()} ")
+ player_balance = check_return_int_conversion(balance_str)
+ if player_balance is not None and player_balance >= 0: # Basic validation
+ break
+ print_colored(
+ "Invalid balance, please enter a non-negative number.", colors.fg.red
+ )
+ players[i + 1] = {"id": i + 1, "balance": player_balance, "name": player_name}
print_player_balances()
+
def get_new_player_number():
- new_player_number = 1
- for k, v in players.items():
- if v.get("id")>=new_player_number:
- new_player_number = v.get("id")+1
- return new_player_number
+ """Calculates the next available player ID."""
+ return list(players)[-1] + 1
-def add_new_player():
- add_new_player_check = input("\nDo you want to add new player? (y/n) ")
- while check_yes_no(add_new_player_check):
+
+def add_remove_player():
+ """Handles adding or removing players between rounds."""
+ add_remove_player_check = check_return_int_conversion(
+ input(
+ colors.fg.lightred
+ + "\nDo you want to add(1)/remove(2) player?\nEnter any other number to not take any action: "
+ + colors.fg.lightgreen
+ )
+ )
+ while add_remove_player_check == 1:
player_number = get_new_player_number()
- player_name = input("\nEnter name of Player number "+str(player_number)+" ")
- player_balance = int(input("Enter balance of "+player_name+" "))
+ player_name = get_colored_input(
+ f"\nEnter name of Player number {player_number} "
+ )
+ while True:
+ balance_str = get_colored_input(f"Enter balance of {player_name.title()} ")
+ player_balance = check_return_int_conversion(balance_str)
+ if player_balance is not None and player_balance >= 0:
+ break
+ print_colored(
+ "Invalid balance, please enter a non-negative number.", colors.fg.red
+ )
players[player_number] = {
- "id":player_number,
- "balance":player_balance,
- "name":player_name,
+ "id": player_number,
+ "balance": player_balance,
+ "name": player_name,
}
- add_new_player_check = input("\nDo you want to add another player? (y/n) ")
+ print_colored(
+ f"\nPlayer {player_name.title()} added with ID {player_number}.",
+ colors.fg.green,
+ )
+ add_remove_player_check = check_return_int_conversion(
+ input(
+ colors.fg.lightred
+ + "\nDo you want to add another player? \nEnter 1 for yes, 2 for player deletion, any other number for quitting "
+ + colors.fg.lightgreen
+ )
+ )
+
+ while add_remove_player_check == 2:
+ global last_winner_id # Needed to potentially reset last_winner_id
+ if len(list(players)) == 0:
+ print(
+ colors.bold
+ + colors.fg.lightred
+ + "Noooo I won't let you do this. Haha!"
+ + colors.reset
+ )
+ print_players()
+ player_number = check_return_int_conversion(
+ get_colored_input("Enter the player number you want to remove: ")
+ )
+ if not players.get(player_number):
+ print(
+ colors.bold
+ + colors.fg.red
+ + "Do you think I am Dumb?? I am not but you definitely are!!"
+ + colors.reset
+ )
+ else:
+ removed_player_name = players[player_number]["name"].title()
+ players.pop(player_number)
+ print_colored(f"Player {removed_player_name} removed.", colors.fg.yellow)
+ # Reset last winner if the removed player was the last winner
+ if last_winner_id == player_number:
+ last_winner_id = None
+ print_colored(
+ "Last winner was removed, starting player for the next round will be chosen manually.",
+ colors.fg.yellow,
+ )
+ add_remove_player_check = check_return_int_conversion(
+ get_colored_input(
+ "\nDo you want to remove another player (Enter 2 for yes, any other number for quitting)? "
+ )
+ )
+ save_data()
+
def print_player_balances():
- print("\nBalances of each player are")
- print("\n No. \t Name \t Balance")
+ table = PrettyTable(["No.", "Name", "Balance"], title="Player Balances")
+ for k, v in players.items():
+ table.add_row([k, v.get("name").title(), "Rs. " + str(v.get("balance"))])
+ print(f"\n{colors.fg.yellow}{table}{colors.reset}\n")
+
+
+def print_players():
+ table = PrettyTable(["No.", "Name"], title="Players")
for k, v in players.items():
- print(" "+str(k)+"\t"+v.get("name")+"\t: Rs. "+str(v.get("balance")))
+ table.add_row([k, v["name"].title()])
+ print(table)
+
+
+def find_preceding_active_player(current_player_id, current_player_ids, folded_players):
+ """Finds the active player immediately preceding the current player in turn order."""
+ active_players_list = [p for p in current_player_ids if p not in folded_players]
+ if len(active_players_list) < 2:
+ return None # Cannot have a show with less than 2 active players
+
+ try:
+ current_player_index_in_full_list = current_player_ids.index(current_player_id)
+ except ValueError:
+ return None # Should not happen
+
+ num_players = len(current_player_ids)
+ search_index = (current_player_index_in_full_list - 1 + num_players) % num_players
+
+ # Iterate backwards through the original list order
+ for _ in range(num_players):
+ potential_preceding_id = current_player_ids[search_index]
+ if (
+ potential_preceding_id != current_player_id
+ and potential_preceding_id not in folded_players
+ ):
+ return potential_preceding_id # Found the preceding active player
+ search_index = (search_index - 1 + num_players) % num_players
+
+ return None # Should only happen if only one player is active
+
def generate_round():
- print("\nStarting new round! ")
- starting_player_id = int(input("Enter the player number who starts this round "))
- print(players)
- print("\nThe bets will start from "+players.get(starting_player_id).get("name"))
- num_of_players = len(players)
- print("\nEnter bets of players")
- print(" 0 : Fold")
- print(" -1 : End Betting\n")
+ global last_winner_id # Use the global variable
+ global players # Reads and modifies global players dict
+
+ print_colored("\nStarting new round!", colors.fg.cyan)
+
+ player_ids = sorted(players.keys())
+ if not player_ids:
+ print_colored("No players left in the game!", colors.fg.red)
+ sys.exit(0)
+
+ if last_winner_id is None or last_winner_id not in players:
+ print_players()
+ # Get input using the helper first, then convert
+ start_player_str = get_colored_input(
+ "Enter the player number who starts this round: "
+ )
+ starting_player_id = check_return_int_conversion(start_player_str)
+ else:
+ try:
+ winner_index = player_ids.index(last_winner_id)
+ start_index = (winner_index + 1) % len(player_ids)
+ starting_player_id = player_ids[start_index]
+ print(
+ f"{colors.fg.lightcyan}Last winner was {players[last_winner_id]['name'].title()}. Next turn starts with {players[starting_player_id]['name'].title()}."
+ )
+ except (
+ ValueError
+ ): # Should not happen if last_winner_id is in players, but safety first
+ print(
+ colors.fg.red
+ + "Error determining next player. Please select manually."
+ + colors.reset
+ )
+ print_players()
+ starting_player_id = check_return_int_conversion(
+ get_colored_input("Enter the player number who starts this round: ")
+ )
+
+ if not players.get(starting_player_id):
+ print(
+ colors.bold
+ + colors.fg.red
+ + "Do you think I am Dumb!! , Now go ahead do everything again!"
+ + colors.reset
+ )
+ sys.exit(0)
+ print(
+ colors.fg.lightcyan
+ + "\nThe bets will start from "
+ + players.get(starting_player_id).get("name").title()
+ )
+
+ boot_amount_str = get_colored_input(
+ "Enter the initial Boot amount for this round: "
+ )
+ boot_amount = check_return_int_conversion(boot_amount_str)
+ # Add validation for boot amount if needed (e.g., must be positive)
+ current_stake = boot_amount # The current bet level required
+
+ print(colors.fg.blue + "\nEnter bets of players")
+ print(f" {FOLD} : Fold")
+ print(" -1 : End Betting (Declare Winner)")
+ print(" -2 : Show (with preceding player)\n" + colors.fg.lightcyan)
+
player_bet = 0
winning_amount = 0
+ winning_player_id = None
folded_players = {}
- player_id = starting_player_id
- round_history = { k: 0 for k in players.keys()}
- while(player_bet!=-1):
- player_name = players.get(player_id).get("name")
+ round_history = {k: 0 for k in players.keys()}
+
+ current_player_ids = sorted(players.keys()) # Get ordered list for cycling
+ current_index = current_player_ids.index(starting_player_id)
+
+ while player_bet != -1:
+ player_id = current_player_ids[current_index]
+
+ # Skip folded players
if folded_players.get(player_id):
- player_id = player_id%num_of_players + 1
+ current_index = (current_index + 1) % len(current_player_ids)
continue
- player_bet = int(input(player_name+" Rs. "))
- if player_bet==-1:
+ player_name = players.get(player_id).get("name").title()
+ player_bet = check_return_int_conversion(
+ get_colored_input(f"{player_name} Bets Rs. ")
+ )
+
+ # Process bet
+ if player_bet == END_BETTING:
break
- elif player_bet == 0:
- print("\n"+player_name+" Folds тип\n")
+ elif player_bet == FOLD:
+ print(colors.fg.lightcyan + "\n" + player_name + " Folds \n")
folded_players[player_id] = True
- players[player_id]["balance"]-=player_bet
- round_history[player_id] -= player_bet
- winning_amount+=player_bet
- player_id = player_id%num_of_players + 1
- winning_player_id = int(input("Enter the player number who won this round "))
+ # --- Check if only one player remains after fold ---
+ active_players = [p for p in current_player_ids if p not in folded_players]
+ if len(active_players) == 1:
+ winning_player_id = active_players[0]
+ print_colored(
+ f"\n{players[winning_player_id]['name'].title()} is the last player remaining and wins!",
+ colors.fg.lightgreen,
+ )
+ break # Exit the betting loop
+ elif player_bet == SHOW: # Handle Show
+ print(colors.fg.lightcyan + f"\n{player_name} requests a Show.")
+ preceding_player_id = find_preceding_active_player(
+ player_id, current_player_ids, folded_players
+ )
+
+ if preceding_player_id is None:
+ print(
+ colors.fg.yellow
+ + "Cannot perform Show. Not enough active players or no preceding active player found."
+ + colors.reset
+ )
+ continue # Ask the current player for input again
+
+ preceding_player_name = players[preceding_player_id]["name"].title()
+ show_cost = current_stake # Cost to initiate the show is the current stake
+
+ # Allow negative balance - Deduct cost regardless of current balance
+ print(f"{colors.fg.lightcyan}Show cost for {player_name}: Rs. {show_cost}")
+ players[player_id]["balance"] -= show_cost
+ round_history[player_id] -= show_cost
+ winning_amount += show_cost
+ print(
+ f"{colors.fg.yellow}Show between {player_name} ({player_id}) and {preceding_player_name} ({preceding_player_id})."
+ )
+ while True:
+ loser_input = get_colored_input(
+ f"Enter the player number ({player_id} or {preceding_player_id}) who FOLDS: "
+ )
+ loser_id = check_return_int_conversion(loser_input)
+ if loser_id == player_id or loser_id == preceding_player_id:
+ folded_players[loser_id] = True
+ print(
+ f"{colors.fg.lightcyan}{players[loser_id]['name'].title()} folds after the Show.\n"
+ )
+ # --- Check if only one player remains after show ---
+ active_players_after_show = [p for p in current_player_ids if p not in folded_players]
+ if len(active_players_after_show) == 1:
+ winning_player_id = active_players_after_show[0]
+ print_colored(
+ f"\n{players[winning_player_id]['name'].title()} wins as the last player after the show!",
+ colors.fg.lightgreen
+ )
+ # Signal the outer loop to break after this inner loop finishes
+ player_bet = END_BETTING
+ break
+ else:
+ print(
+ colors.fg.red
+ + "Invalid input. Please enter either the requester's number or the preceding player's number."
+ + colors.reset
+ )
+ # If the show resulted in a winner, break the outer loop now
+ if player_bet == END_BETTING:
+ break
+
+ elif player_bet > 0: # Handle regular bet (Chaal)
+ if player_bet < current_stake:
+ print(
+ colors.fg.yellow
+ + f"Bet must be at least the current stake of Rs. {current_stake}. Try again."
+ + colors.reset
+ )
+ continue # Ask the current player for input again
+ elif player_bet > current_stake:
+ print(
+ colors.fg.lightcyan
+ + f"Stake increased to Rs. {player_bet} by {player_name}."
+ + colors.reset
+ )
+ current_stake = player_bet # Update stake if bet is higher
+
+ # Process the valid bet
+ # Allow negative balance - Deduct bet regardless of current balance
+ players[player_id]["balance"] -= player_bet
+ round_history[player_id] -= player_bet
+ winning_amount += player_bet
+ else: # Handle invalid negative numbers other than -1, -2
+ print(
+ colors.fg.red
+ + "Invalid input. Use 0 (Fold), -1 (End Betting), -2 (Show), or a positive bet amount."
+ + colors.reset
+ )
+ continue # Ask the current player for input again
+
+ # Move to the next player
+ current_index = (current_index + 1) % len(current_player_ids)
+
+ # --- Winner Determination ---
+ if not winning_player_id:
+ print_players()
+ winning_player_id = check_return_int_conversion(
+ get_colored_input("Enter the player number who won this round: ")
+ )
+
+ if not players.get(winning_player_id):
+ print(
+ colors.bold
+ + colors.fg.red
+ + "Do you think I am Dumb!! , Now go ahead do everything again!"
+ + colors.reset
+ )
+ sys.exit(0)
+
winning_player_name = players.get(winning_player_id).get("name")
- print("\n Congratulations! "+winning_player_name+" won the pot of Rs. "+str(winning_amount))
+ print(
+ colors.fg.lightgreen
+ + "\nCongratulations! "
+ + winning_player_name.title()
+ + " won the pot of Rs. "
+ + str(winning_amount)
+ )
players[winning_player_id]["balance"] += winning_amount
+ last_winner_id = winning_player_id # Update the last winner for the next round
round_history[winning_player_id] += winning_amount
print_player_balances()
- save_winnings()
- add_new_player()
-
-if check_yes_no(load_game):
- print("Loading saved game ....")
- with open(winning_history_path, 'r', encoding='utf-8') as json_file:
+ save_data()
+ add_remove_player()
+
+
+if check_yes_no(load_game_input):
+ print(colors.fg.lightgreen + "Loading saved game ....")
+ with open(winning_history_path, "r", encoding="utf-8") as json_file:
try:
players = json.load(json_file)
- players = {int(k):v for k,v in players.items()}
+ players = {int(k): v for k, v in players.items()}
print_player_balances()
- add_new_player()
+ add_remove_player()
except:
- print("\n Sorry, no data found. Starting new game... \n")
+ print_colored(
+ colors.fg.lightred + "\n Sorry, no data found. Starting new game... \n"
+ )
input_players()
-
else:
input_players()
game_status = "y"
-while(check_yes_no(game_status)):
+while check_yes_no(game_status):
generate_round()
- game_status = input("Do you want to start to next round ? (y/n) ")
-
-
-
-
-
-
+ game_status = get_colored_input(
+ "Do you want to start to next round ? (y/n) ", input_color=colors.fg.lightgreen
+ )