# Simple Poker Counter

It is a simple Poker balance/bid tracker to automate calculations for players' scores.

It is a project I made to practice the concepts of lists and indices. And thus heavily relies on indexing and list search.

I know I could have used dictionaries, but I chose not to get familiar with previously mentioned concepts.

I'm still learning using MIT online course, so I'm only familiar with a limited number of topics, including Branching and Iteration, String Manipulation / bisectional search / Guess And Check / Approximation, Decomposition / Abstraction / Function Definition, Sequences including lists, tuples, and Dictionaries.

Kindly see if this code can be improved in any way without using dictionaries.

import string

def new_line(n):
'''
prints (n) new lines
'''
for n in range(n):
print()

def x_player(list_secondary, string, list_primary):
'''
returns the item from a list(list_primary) at
the index of another string (string) in another list(list_secondary)
'''

return list_primary[list_secondary.index(string)]

def get_space(pos_list, pos_string, string, extra_length):
'''
returns the given string(string) argument multiplied by
the length of the longest string in a list(pos_list)
minus the length of another_string (pos_string)
plus any extra length determined by the user.
'''

lengths = [ len(string) for string in pos_list]
h_length = max(lengths) + extra_length
return ( string * ( h_length - len(pos_string)))

def list_players(player_names, player, list_):
'''
prints the player name + appropriate space + an item
at the index of : (player in player_names) from a different
list (list_)
'''
print ( player + get_space(player_names, player, ' ', 1) + ': '+ str(x_player(player_names, player, list_)))

def list_balances(player_names, balances):
'''
lists the player names and their balances.
'''
print('Current Balances:')
for player in player_names:
list_players(player_names, player, balances)

def list_bids(player_names, bids):
'''
lists the player names and their bids
'''
print('Current Bids:')
for player in player_names:
list_players(player_names, player, bids)

def poker():
'''
starts the poker score counter
'''
#   ask for the number of players and their names.
n_players = int(input('Number Of Players?... '))
player_names = []
for n in range(n_players):
player = input('Player # ' + str(n+1) + ' .... ')
player_names.append(player)
# iniate lists for balances, bids and list to determine if a player has folded or not.
balances = [ 90 for n in range(n_players)]
bids = [10 for n in range(len(player_names))]
fold_code = [ 0 for n in range(len(player_names)) ]
#balance_codes = [ 0 for n in range(n_players)]
# this variable will determine if a round is over or not.
this_round = ''
#   total bid:
total = sum(bids)
#   getting user's confirmation to start the counter.
#   this variable will not be used again.
stop = input('Press Enter To Continue...')
print('Starting Game...')
#   Intitial Balance / Bid listing.
list_balances(player_names, balances)
list_bids(player_names, bids)
while True:
#       to determine if a round is over.
if this_round == 'y':
print('Who Won?')
winner = input()
#           adding the total bid to the winner's balance.
balances[player_names.index(winner)] += total
new_line(1)
print(winner, 'Won This Round')
new_line(1)
list_balances(player_names, balances)
#           checking for players who are out.
#           a for loop that iterates n_player times. to avoid
#           indices being out of range after removing a player from the list.
for var in range(n_players):
if 0 in balances:
print (player_names[balances.index(0)].upper(), 'Is Out...')
player_names.pop(balances.index(0))
bids.pop(balances.index(0))
balances.pop(balances.index(0))
#           self-explanatory using the print statement.
for a player in player_names:
balances[player_names.index(player)] -= 10
#           re-initializing bids and fold code after each round.
#           fold code is used to determine if a player has folded for the round.
bids = [ 10 for n in range(len(player_names))]
fold_code = [0 for n in range(len(player_names))]
print('Deducting 10$from all players for the new round...') new_line(1) list_balances(player_names, balances) new_line(1) list_bids(player_names, bids) new_line(1) print('-' * 35) for player in player_names: # if a player folds for the round the value in the list ( fold-code) 'at the same index' # gets assigned to be '1' therefore the loop ends and moves on to the next player. if fold_code[player_names.index(player)] == 1: continue # asking for user input ( player action for the round. print('>' * 15, player.upper(), '>' * 15 ) action = input () # c i.e : 'call', which then evaluates the difference between the max bid and # the current player's bid and later on in the program subtracts it from the \ # player's balance and adds it to the total bid. if action == 'c': bid = max(bids) - x_player(player_names, player, bids) print(player, 'Called...') # # r i.e : 'raise', does the same process using the 'bid' variable but adds the raise value to it. elif action == 'r': print('Raise By?') r = int(input()) bid = max(bids) - x_player(player_names, player, bids) + r print(player, 'Raised By', r) # fold makes the 'bid' variable = 0 and makes the program skip the player's turn until the round is over. elif action == 'f' : fold_code[player_names.index(player)] = 1 bid = 0 print(player, 'Folded For the round') # similar to fold but doesn't skip the player's turn else: bid = 0 print(player, 'Checked...') # deducting the 'bid' variable from the player's balance and adding it to the respective value in the bids list. balances[player_names.index(player)] -= bid bids[player_names.index(player)] += bid total = sum(bids) new_line(1) list_balances(player_names, balances) new_line(1) list_bids(player_names, bids) new_line(1) print('Total Bid : ' + str(total)) new_line(2) print('-' * 35) # the decision to end a round is left to the user, plans to implement automatic round termination has been made. print('Round Over?') this_round = input() new_line(20) poker() $$`$$ ## 2 Answers # Docstrings You should always use triple double quotes for docstrings. ''' should be replaced with """ # Comments Don't fill your code with comments. The actual code is harder to read. Comments should be used only while explaining something which others can't understand by themselves. # Making the code shorter Functions other than poker • You can remove import string as you don't use it 1. You can use print() instead of creating a new function 2. You can use list_players directly, instead of list_bids or list_balances In the poker function • n_players = int(input('Number Of Players?... ')) player_names = [] for n in range(n_players): player = input('Player # ' + str(n+1) + ' .... ') player_names.append(player) can be rewritten as n_players = int(input('Number Of Players?... ')) player_names = [input(f'Player # {n+1} .... ') for n in range(n_players)] • While beginning, len(player_names) is the same as n_players. Therefore, we can write balances = [ 90 for n in range(n_players)] bids = [10 for n in range(len(player_names))] fold_code = [ 0 for n in range(len(player_names)) ] as balances = [90] * n_players bids = [10] * n_players fold_code = [0] * n_players Final code after implementing the above ideas and making some negligible changes: def get_space(pos_list, pos_string, string, extra_length): h_length = max(len(string) for string in pos_list) + extra_length return string * ( h_length - len(pos_string)) def list_players(player_names, n_players, list_): for i in range(n_players): print(player_names[i] + get_space(player_names, player_names[i], ' ', 1) + ': ' + str(list_[i])) def poker(): n_players = int(input('Number Of Players?... ')) player_names = [input(f'Player # {n+1} .... ') for n in range(n_players)] balances = [90] * n_players bids = [10] * n_players fold_code = [0] * n_players total = sum(bids) input('Press Enter To Continue...') print('Starting Game...') this_round = '' while True: print('Current Balances:') list_players(player_names, n_players, balances) print('Current Bids:') list_players(player_names, n_players, bids) if this_round == 'y': print('Who Won?') winner = input() balances[player_names.index(winner)] += total print() print(winner, 'Won This Round') print() print('Current Balances:') list_players(player_names, n_players, balances) for var in range(n_players): if 0 in balances: ind_0 = balances.index(0) print(player_names[ind_0].upper(), 'Is Out...') n_players -= 1 player_names.pop(ind_0) bids.pop(ind_0) balances.pop(ind_0) for player in player_names: balances[player_names.index(player)] -= 10 bids = [10] * n_players fold_code = [0] * n_players print('Deducting 10$ from all players for the new round...')
print()

print('Current Balances:')
list_players(player_names, n_players, balances)
print()

print('Current Bids:')
list_players(player_names, n_players, bids)
print()

print('-' * 35)

for player_index in range(n_players):
player = player_names[player_index]

if fold_code[player_names.index(player)] == 1:
continue

print('>' * 15, player.upper(), '>' * 15 )
action = input()

if action == 'c':
bid = max(bids) - bids[player_index]
print(player, 'Called...')

elif action == 'r':
print('Raise By?')
r = int(input())
bid = max(bids) - bids[player_index]
print(player, 'Raised By', r)

elif action == 'f':
fold_code[player_names.index(player)] = 1
bid = 0
print(player, 'Folded For the round')

else:
bid = 0
print(player, 'Checked...')

balances[player_names.index(player)] -= bid
bids[player_names.index(player)] += bid
total = sum(bids)

print()
print('Current Balances:')
list_players(player_names, n_players, balances)

print()
print('Current Biddings:')
list_players(player_names, n_players, bids)

print()
print('Total Bid : ' + str(total))

print()
print()

print('-' * 35)

print('Round Over?')
this_round = input()

print('\n' * 20)
poker()

If you have any suggestions or if you find any mistakes, please point them out in the comments.

Hope this helps!

• Instead of calling print() / print(whatever), couldn't one just do print(\nwhatever)? – BruceWayne Nov 9 at 2:59
• @BruceWayne I did think of that, but don't you think it looks a bit ugly? print('\nwhatever') I totes agree with you if you have to make the code shorter. – Srivaths Nov 9 at 4:00
• Can you please refer me to the Python Doc that states that docstrings should be written using " " " instead of ' ' ' – Elbasel Nov 9 at 9:31
• Of course! Here it is! It says For consistency, always use """triple double quotes""" around docstrings. – Srivaths Nov 9 at 9:35
• @Srivaths yeah perhaps, it's OP's call, I just wanted to mention another option instead of doing empty prints. – BruceWayne Nov 9 at 15:13

There is a lot of room for improvement but I will stick to the index stuff.

# Naming

def x_player(list_secondary, string, list_primary):
'''
returns the item from a list(list_primary) at
the index of another string (string) in another list(list_secondary)
'''

return list_primary[list_secondary.index(string)]
• has bad names for function and parameters. x_player suggest that it is about players but in fact it is an absolutely generic function. Even if used with players the qualifier x_ does not give any hint what is happening or returned. also what is list_primary and what is list_secondary? The parameter name string is without any semantic hint. From the perspective of the function it even worse as the function is not limited to string type keys.
• has a bad docstring. the docstring is just a lengthy read of the code line below. A reader not familiar with your code has a hard time to understand the usage.

What if we change the names and the docstring to

def get_value(keys, key, values):
'''
fakes a dict()
'''
return values[keys.index(key)]

All of a sudden it is absolutely clear, that this is a completely generic function that looks up values in a parallel list and may be used for any key type.

# Consistency

You use your fake dict x_player for bids and balances, but you do not use it for fold_code where you do fold_code[player_names.index(player)] explicitly. Also there are explicit usages balances[player_names.index(player)] and bids[player_names.index(player)]. Either you think fold_code[player_names.index(player)] is readable, then avoid to create a function. Or you think that is somewhat hard to read, then create the function and use it throughout the code.

For your dict-less implementation you have several lists of the same length. You first fill the key list player_names then you create the value lists inconsistently

balances = [ 90 for n in range(n_players)]
bids = [10 for n in range(len(player_names))]
fold_code = [ 0 for n in range(len(player_names)) ]

The first line I like the least, n_players is a helper for the input loop only. The real truth is player_names, the list lengths must match. So in terms of dependency to the real stuff your other lines are better. However thy are not pythonic in two ways. In python you nearly never loop over range(len(x)). So

bids = [10 for n in range(len(player_names))]

bids = [10 for name in player_names]

which is less error prone. Also in python you use _ when you do not need the variables you somehow get.

bids = [10 for name in player_names]

bids = [10 for _ in player_names]

# Loops over player

You have a loop over player_names where you have multiple fake dict lookups for player attributes:

for player in player_names:
# [...]
if fold_code[player_names.index(player)] == 1:
# [...]
if action == 'c':
bid = max(bids) - x_player(player_names, player, bids)

For this case your fake dict is not very efficient. Instead of retrieving player name and have many lookups you could also lookup the player index once

for player_idx, player in enumerate(player_names):
# [...]
if fold_code[player_idx] == 1:
# [...]
if action == 'c':
bid = max(bids) - bids[player_idx]

That makes the code more readable.

• Can you please elaborate why range(len(x)) is more prone to error? – Elbasel Nov 9 at 9:28
• Off by 1 is a typical error on loops. Of course it is hard to get your simple loop wrong. But if you access an element you type the container name twice for i in range(len(container)) you access container[i]. When copy-pasting it is easy to get one name wrong. If you do for i, e in enumerate(container): you index/value tuple always matches. Or you may iterate over slices for i, e in enumerate(container[5:-5:2]):. enumerate() also works on generators, where len() is not available. Get used to use enumerate(). – stefan Nov 11 at 18:57