Blackjack Game in Python 3/curses












0












$begingroup$


You were all so helpful with my Tc Tac Toe games in C. I was encouraged to put it up on GitHub and two very helpful users refactored it in ways that really taught me a thing or two. While I have switched focus to Python in anticipation of the upcoming 7DRL I was so happy about the way my C projects were reviewed that this has become one of my favorite forums. So now I want your help with something else:



Over the last week or so I've created a Blackjack game with Python 3.5.3. The logic was easy to do, especially with Python, but making a clean curses interface was more challenging. What I wound up doing was creating a blackjack game without curses to get the logic straight and then implementing curses in the form of a draw_screen() function that turned out to be a real monster. It is messy and repetitive compared to the rest of the code, and the function wound up being well over 100 lines long. In addition to that I'm still struggling to do things in the most "Pythonic" way, and even the behind-the-screen logic may not be implemented in a way that is ideal.



What I want help from you guys with is to help me refactor the draw_screen() function into something more manageable. Should I split it up into multiple functions? How best to do that? I would also appreciate any and all feedback regarding how I could make the non-curses logic of the game more "Pythonic."



I intend to make a Roguelike for the 7DRL event this year and I intend to do it with Python 3.5.3 and the curses library. It'll be harder than what I usually do but I'm looking forward to it. By helping me hammer out my fundamentals here you will help me to be better prepared for making a small Roguelike in the near future.



Note: the GREEN_TEXT and RED_TEXT color pair definitions were originally
going to be used for the player's funds and for busting prompts, respectively. I haven't removed them because I intend to add that functionality myself very soon, if not today.



Here is the code itself:



"""
Project: Simple 21/Blackjack
File: twenty-one-curses.py
Date: 24 JAN 2019
Author: sgibber2018
Description: A simple implementation of 21/Blackjack using the terminal and python.
Uses the curses library for character cell graphics.
"""

import random
import curses

# init curses
stdscr = curses.initscr()
curses.cbreak()
curses.noecho()
curses.curs_set(False)
curses.start_color()

# init curses colors
curses.init_color(curses.COLOR_RED, 900, 0, 0)
curses.init_color(curses.COLOR_BLACK, 0, 0, 0)
curses.init_color(curses.COLOR_GREEN, 0, 900, 0)
curses.init_color(curses.COLOR_WHITE, 1000, 1000, 1000)
colors_dict = {"RED_CARD":1,
"BLACK_CARD":2,
"GREEN_TEXT":3,
"RED_TEXT":4}
curses.init_pair(colors_dict.get("RED_CARD"), curses.COLOR_RED, curses.COLOR_WHITE)
curses.init_pair(colors_dict.get("BLACK_CARD"), curses.COLOR_BLACK, curses.COLOR_WHITE)
curses.init_pair(colors_dict.get("GREEN_TEXT"), curses.COLOR_GREEN, curses.COLOR_BLACK)
curses.init_pair(colors_dict.get("RED_TEXT"), curses.COLOR_RED, curses.COLOR_BLACK)


class Card:
def __init__(self, suit, value):
self.suit = suit
self.value = value


def generate_deck():
"""
Generate a list of Card objects to be used as a deck
and return it
"""
deck =
card_suits = ["D", "H", "S", "C"]
card_nums_range = range(2, 11)
card_faces = ["J", "Q", "K", "A"]
for suits in range(len(card_suits)):
for card_nums in card_nums_range:
deck.append(Card(card_suits[suits], str(card_nums)))
for card in range(len(card_faces)):
deck.append(Card(card_suits[suits], card_faces[card]))
random.shuffle(deck)
return deck

def draw(hand, num_to_draw, deck):
"""
takes a hand list and a number and draws that number
of cards from the deck and places them in the
desired hand
"""
for num_cards in range(num_to_draw):
card = deck[-1]
hand.append(card)
deck.remove(card)

def count_hand(hand):
"""
Evaluates a hand and returns the value
of its cards
"""
card_values = {"2": 2, "3": 3, "4": 4, "5": 5,
"6": 6, "7": 7, "8":8, "9": 9,
"10":10, "J":10, "Q":10, "K":10}
hand_sum = 0
hand_suits =
for cards in hand:
hand_suits.append(cards.value)
if "A" in hand_suits:
num_aces = 0
for card_value in hand_suits:
if card_value == "A":
num_aces += 1
for card in hand:
if card.value != "A":
hand_sum += card_values.get(card.value)
if num_aces == 1:
if hand_sum + 11 > 21:
hand_sum += 1
elif hand_sum + 11 <= 21:
hand_sum += 11
elif num_aces == 2:
if hand_sum + 12 > 21:
hand_sum += 2
elif hand_sum + 12 <= 21:
hand_sum += 12
elif num_aces == 3:
if hand_sum + 13 > 21:
hand_sum += 3
elif hand_sum + 13 <= 21:
hand_sum += 13
elif num_aces == 4:
if hand_sum + 14 > 21:
hand_sum += 4
elif hand_sum + 14 <= 21:
hand_sum += 14
elif "A" not in hand_suits:
for card in hand:
hand_sum += card_values.get(card.value)
return hand_sum

def player_hits(player_hand, stdscr):
"""
Asks if player wants to hit
"""
# get dimensions
wsize = stdscr.getmaxyx()
prompt_line = 16
# lay out the strings
prompt = "(H)it or (S)tay"
prompt_hit = "Player has chosen to hit!"
prompt_stay = "Player has chosen to stay!"
prompt_wrong = "Invalid input! Try again..."
# center the prompts
prompt_x = wsize[1] // 2 - len(prompt) // 2
prompt_hit_x = wsize[1] // 2 - len(prompt_hit) // 2
prompt_stay_x = wsize[1] // 2 - len(prompt_stay) // 2
prompt_wrong_x = wsize[1] // 2 - len(prompt_wrong) // 2
# clear the entire prompt line
clear_str = ""
for char_cell in range(wsize[1]):
clear_str += " "
stdscr.addstr(prompt_line, 0, clear_str)
# display the prompt
stdscr.addstr(prompt_line, prompt_x, prompt)
# get the input
uinput = stdscr.getch()
if uinput == 104 or uinput == 72:
# print("Player has chosen to hit!")
stdscr.addstr(prompt_line, 0, clear_str)
stdscr.addstr(prompt_line, prompt_hit_x, prompt_hit)
stdscr.getch()
return True
elif uinput == 83 or uinput == 115:
# print("Player has chosen to stay!")
stdscr.addstr(prompt_line, 0, clear_str)
stdscr.addstr(prompt_line, prompt_stay_x, prompt_stay)
stdscr.getch()
return False
else:
# print("Invalid input! Try again...")
stdscr.addstr(prompt_line, 0, clear_str)
stdscr.addstr(prompt_line, prompt_wrong_x, prompt_wrong)
stdscr.getch()
player_hits(player_hand)

def prompt(string, stdscr):
"""
Takes a string, clears the prompt line, and places the
string on the prompt line
"""
wsize = stdscr.getmaxyx()
prompt_line = 16
prompt_clear = ""
for char_cell in range(wsize[1]):
prompt_clear += " "
stdscr.addstr(prompt_line, 0, prompt_clear)
centered_x = wsize[1] // 2 - len(string) // 2
stdscr.addstr(prompt_line, centered_x, string)
stdscr.getch()

def is_busted(hand):
"""
Checks a hand and if it is busted, returns True
"""
card_values = {"2": 2, "3": 3, "4": 4, "5": 5,
"6": 6, "7": 7, "8":8, "9": 9,
"10":10, "J":10, "Q":10, "K":10}
hand_sum = 0
hand_suits =
for cards in hand:
hand_suits.append(cards.value)
if "A" in hand_suits:
num_aces = 0
for card_value in hand_suits:
if card_value == "A":
num_aces += 1
for card in hand:
if card.value != "A":
hand_sum += card_values.get(card.value)
if num_aces == 1:
if hand_sum + 11 > 21:
hand_sum += 1
elif hand_sum + 11 <= 21:
hand_sum += 11
elif num_aces == 2:
if hand_sum + 12 > 21:
hand_sum += 2
elif hand_sum + 12 <= 21:
hand_sum += 12
elif num_aces == 3:
if hand_sum + 13 > 21:
hand_sum += 3
elif hand_sum + 13 <= 21:
hand_sum += 13
elif num_aces == 4:
if hand_sum + 14 > 21:
hand_sum += 4
elif hand_sum + 14 <= 21:
hand_sum += 14
elif "A" not in hand_suits:
for card in hand:
hand_sum += card_values.get(card.value)
if hand_sum > 21:
return True
else:
return False


def game_not_over(player_funds, turn_num):
"""
Checks to see if the game is over.
Returns True if game not over.
Prints game over if game is over, then returns False
"""
if player_funds <= 0:
prompt("Player loses in " + str(turn_num) + " turns!", stdscr)
return False
elif player_funds > 0:
return True

def compare_hands(dealer_hand, player_hand):
"""
Checks to see which hand is the winner
returns "dealer" or "player" as a result
In case of tie, returns "dealer"
"""
player_score = count_hand(player_hand)
dealer_score = count_hand(dealer_hand)
if player_score > dealer_score:
return "player"
elif dealer_score >= player_score:
return "dealer"

def dealer_hits(dealer_hand):
"""
Counts the dealer hand and returns true
if under 17
"""
count = count_hand(dealer_hand)
if count < 17:
prompt("Dealer hits!", stdscr)
return True
else:
prompt("Dealer Stays!", stdscr)
return False

def draw_screen(stdscr, dealer_hand, player_hand, turn_num, player_funds, dealer_flipped=False):
"""
Draws the entire game status on to the screen
including a visual representation of the cards in play.
Will be centered in final version.
"""
# clear screen
stdscr.clear()
# get dimensions
wsize = stdscr.getmaxyx()
display_height = 17
display_width = 36
# get the strings
funds_str = str("Player Funds: " + str(player_funds))
turn_str = str("Turn Number: " + str(turn_num))
player_score_str = str("Player: " + str(count_hand(player_hand)))
# dealer score string depends on whether dealer_flipped is flagged
if dealer_flipped:
dealer_score_str = str("Dealer: " + str(count_hand(dealer_hand)))
if not dealer_flipped:
flipped_dealer_hand =
for card in range(len(dealer_hand)):
if card != 0:
flipped_dealer_hand.append(dealer_hand[card])
dealer_score_str = str("Dealer Visible: " + str(count_hand(flipped_dealer_hand)))
# place the strings in their appropriate places
dealer_str_coords = (0, 1)
player_str_coords = (8, 1)
funds_str_coords = (15, 1)
turn_str_coords = (15, 20)
stdscr.addstr(dealer_str_coords[0], dealer_str_coords[1], dealer_score_str)
stdscr.addstr(player_str_coords[0], player_str_coords[1], player_score_str)
stdscr.addstr(funds_str_coords[0], funds_str_coords[1], funds_str)
stdscr.addstr(turn_str_coords[0], turn_str_coords[1], turn_str)
# place the cards:
# create lists of tuples with the x and y coords or each symbol on each card
# List of lists of tuples:
# Outer list = hand.
# Inner list = card
# Sets = (top-left suit, central value, bottom-right suit)
# called with something like sym = dealer_hand_coords[0][0] for top-left symbol of first card in hand
# first tuple doubles as a top-left coordinate for the blank card rects
dealer_hand_coords = [[(2, 1), (4, 2), (6, 3)],
[(2, 5), (4, 6), (6, 7)],
[(2, 9), (4, 10), (6, 11)],
[(2, 13), (4, 14), (6, 15)],
[(2, 17), (4, 18), (6, 19)],
[(2, 21), (4, 22), (6, 23)],
[(2, 25), (4, 26), (6, 27)],
[(2, 29), (4, 30), (6, 31)],
[(2, 33), (4, 34), (6, 35)]]

player_hand_coords = [[(9, 1), (11, 2), (13, 3)],
[(9, 5), (11, 6), (13, 7)],
[(9, 9), (11, 10), (13, 11)],
[(9, 13), (11, 14), (13, 15)],
[(9, 17), (11, 18), (13, 19)],
[(9, 21), (11, 22), (13, 23)],
[(9, 25), (11, 26), (13, 27)],
[(9, 29), (11, 30), (13, 31)],
[(9, 33), (11, 34), (13, 35)]]

# NOTE: Re-Factor this into some more DRY-compliant code
# NOTE: Re-Factor into multiple smaller functions that are easier for others
# to follow along with!
# player hand
for card in range(len(player_hand)):
# for each card in the hand
value = player_hand[card].value
suit = player_hand[card].suit
if suit == "H" or suit == "D":
color = colors_dict.get("RED_CARD")
elif suit == "C" or suit == "S":
color = colors_dict.get("BLACK_CARD")

# place the blank card rect
card_height = 5
card_width = 3
for cell_y in range(player_hand_coords[card][0][0], player_hand_coords[card][0][0] + card_height):
for cell_x in range(player_hand_coords[card][0][1], player_hand_coords[card][0][1] + card_width):
stdscr.addstr(cell_y, cell_x, " ", curses.color_pair(color))

# place the symbols
# place two suit symbols and a value symbol
stdscr.addstr(player_hand_coords[card][0][0], player_hand_coords[card][0][1], suit, curses.color_pair(color))
stdscr.addstr(player_hand_coords[card][1][0], player_hand_coords[card][1][1], value, curses.color_pair(color))
stdscr.addstr(player_hand_coords[card][2][0], player_hand_coords[card][2][1], suit, curses.color_pair(color))

# dealer hand
if dealer_flipped:
for card in range(len(dealer_hand)):
# for each card in the hand
value = dealer_hand[card].value
suit = dealer_hand[card].suit
if suit == "H" or suit == "D":
color = colors_dict.get("RED_CARD")
elif suit == "C" or suit == "S":
color = colors_dict.get("BLACK_CARD")

# place the blank card rect
card_height = 5
card_width = 3
for cell_y in range(dealer_hand_coords[card][0][0], dealer_hand_coords[card][0][0] + card_height):
for cell_x in range(dealer_hand_coords[card][0][1], dealer_hand_coords[card][0][1] + card_width):
stdscr.addstr(cell_y, cell_x, " ", curses.color_pair(color))

# place the symbols
# place two suit symbols and a value symbol
stdscr.addstr(dealer_hand_coords[card][0][0], dealer_hand_coords[card][0][1], suit, curses.color_pair(color))
stdscr.addstr(dealer_hand_coords[card][1][0], dealer_hand_coords[card][1][1], value, curses.color_pair(color))
stdscr.addstr(dealer_hand_coords[card][2][0], dealer_hand_coords[card][2][1], suit, curses.color_pair(color))

if not dealer_flipped:
for card in range(len(dealer_hand)):
# for each card in the hand
value = dealer_hand[card].value
suit = dealer_hand[card].suit
if suit == "H" or suit == "D":
color = colors_dict.get("RED_CARD")
elif suit == "C" or suit == "S":
color = colors_dict.get("BLACK_CARD")

# place the blank card rect
card_height = 5
card_width = 3
for cell_y in range(dealer_hand_coords[card][0][0], dealer_hand_coords[card][0][0] + card_height):
for cell_x in range(dealer_hand_coords[card][0][1], dealer_hand_coords[card][0][1] + card_width):
stdscr.addstr(cell_y, cell_x, " ", curses.color_pair(color))

# place the symbols
# place two suit symbols and a value symbol
if card != 0:
stdscr.addstr(dealer_hand_coords[card][0][0], dealer_hand_coords[card][0][1], suit, curses.color_pair(color))
stdscr.addstr(dealer_hand_coords[card][1][0], dealer_hand_coords[card][1][1], value, curses.color_pair(color))
stdscr.addstr(dealer_hand_coords[card][2][0], dealer_hand_coords[card][2][1], suit, curses.color_pair(color))
stdscr.refresh()

def main(stdscr):
try:
# bet amount
bet = 100
# starting funds
player_funds = 1000
turn_num = 1
while game_not_over(player_funds, turn_num):
# while the player has funds left to bet
# generate a new deck
deck = generate_deck()
dealer_hand =
player_hand =
# draw two cards for each player
draw(dealer_hand, 2, deck)
draw(player_hand, 2, deck)
# take the player's bet
player_funds -= bet
winner = None
player_hitting = True
while player_hitting:
# while the player is deciding to hit or stay:
# draw the screen with curses
draw_screen(stdscr, dealer_hand, player_hand, turn_num, player_funds)
if player_hits(player_hand, stdscr):
# if the player chooses to hit:
# draw a card
draw(player_hand, 1, deck)
if is_busted(player_hand):
# If the player busts:
# draw the screen again
draw_screen(stdscr, dealer_hand, player_hand, turn_num, player_funds)
# prompt that the player has busted
prompt("Player Busted!", stdscr)
player_hitting = False
winner = "dealer"
else:
# end the loop if the player chooses to stay
player_hitting = False
if not is_busted(player_hand):
# If the player has stayed and the player has not busted:
dealer_hitting = True
while dealer_hitting:
# while the dealer is choosing to hit or stay:
# draw the screen with curses
draw_screen(stdscr, dealer_hand, player_hand, turn_num, player_funds, dealer_flipped=True)
if dealer_hits(dealer_hand):
# If the dealer chooses to hit:
# dealer draws a card
draw(dealer_hand, 1, deck)
if is_busted(dealer_hand):
# If the dealer busts:
# draw the screen with curses
draw_screen(stdscr, dealer_hand, player_hand, turn_num, player_funds, dealer_flipped=True)
# prompt that the dealer has busted
prompt("Dealer Busted!", stdscr)
dealer_hitting = False
winner = "player"
# reward the player with double their bet
player_funds += bet * 2
else:
# if the dealer busts, break the loop
dealer_hitting = False
if not is_busted(dealer_hand):
if not is_busted(player_hand):
# If neither player has busted and both have stayed:
# draw the screen with curses
draw_screen(stdscr, dealer_hand, player_hand, turn_num, player_funds, dealer_flipped=True)
# get the winning hand
winner = compare_hands(dealer_hand, player_hand)
# prompt the winner
prompt(str(winner + " Wins!"), stdscr)
if winner == "player":
# if the player wins, reward them with double their bet
player_funds += bet * 2
# increase turn num
turn_num += 1
finally:
# end curses window on error or exit
curses.endwin()


if __name__ == "__main__":
main(stdscr)


And here is a link to the GitHub page, for those who want to contribute more directly.



As ever, anyone who finds this useful is more than welcome to do whatever they want with it. I worked hard on it, but it was just a practice project. If it helps someone else, by all means go for it.










share|improve this question











$endgroup$

















    0












    $begingroup$


    You were all so helpful with my Tc Tac Toe games in C. I was encouraged to put it up on GitHub and two very helpful users refactored it in ways that really taught me a thing or two. While I have switched focus to Python in anticipation of the upcoming 7DRL I was so happy about the way my C projects were reviewed that this has become one of my favorite forums. So now I want your help with something else:



    Over the last week or so I've created a Blackjack game with Python 3.5.3. The logic was easy to do, especially with Python, but making a clean curses interface was more challenging. What I wound up doing was creating a blackjack game without curses to get the logic straight and then implementing curses in the form of a draw_screen() function that turned out to be a real monster. It is messy and repetitive compared to the rest of the code, and the function wound up being well over 100 lines long. In addition to that I'm still struggling to do things in the most "Pythonic" way, and even the behind-the-screen logic may not be implemented in a way that is ideal.



    What I want help from you guys with is to help me refactor the draw_screen() function into something more manageable. Should I split it up into multiple functions? How best to do that? I would also appreciate any and all feedback regarding how I could make the non-curses logic of the game more "Pythonic."



    I intend to make a Roguelike for the 7DRL event this year and I intend to do it with Python 3.5.3 and the curses library. It'll be harder than what I usually do but I'm looking forward to it. By helping me hammer out my fundamentals here you will help me to be better prepared for making a small Roguelike in the near future.



    Note: the GREEN_TEXT and RED_TEXT color pair definitions were originally
    going to be used for the player's funds and for busting prompts, respectively. I haven't removed them because I intend to add that functionality myself very soon, if not today.



    Here is the code itself:



    """
    Project: Simple 21/Blackjack
    File: twenty-one-curses.py
    Date: 24 JAN 2019
    Author: sgibber2018
    Description: A simple implementation of 21/Blackjack using the terminal and python.
    Uses the curses library for character cell graphics.
    """

    import random
    import curses

    # init curses
    stdscr = curses.initscr()
    curses.cbreak()
    curses.noecho()
    curses.curs_set(False)
    curses.start_color()

    # init curses colors
    curses.init_color(curses.COLOR_RED, 900, 0, 0)
    curses.init_color(curses.COLOR_BLACK, 0, 0, 0)
    curses.init_color(curses.COLOR_GREEN, 0, 900, 0)
    curses.init_color(curses.COLOR_WHITE, 1000, 1000, 1000)
    colors_dict = {"RED_CARD":1,
    "BLACK_CARD":2,
    "GREEN_TEXT":3,
    "RED_TEXT":4}
    curses.init_pair(colors_dict.get("RED_CARD"), curses.COLOR_RED, curses.COLOR_WHITE)
    curses.init_pair(colors_dict.get("BLACK_CARD"), curses.COLOR_BLACK, curses.COLOR_WHITE)
    curses.init_pair(colors_dict.get("GREEN_TEXT"), curses.COLOR_GREEN, curses.COLOR_BLACK)
    curses.init_pair(colors_dict.get("RED_TEXT"), curses.COLOR_RED, curses.COLOR_BLACK)


    class Card:
    def __init__(self, suit, value):
    self.suit = suit
    self.value = value


    def generate_deck():
    """
    Generate a list of Card objects to be used as a deck
    and return it
    """
    deck =
    card_suits = ["D", "H", "S", "C"]
    card_nums_range = range(2, 11)
    card_faces = ["J", "Q", "K", "A"]
    for suits in range(len(card_suits)):
    for card_nums in card_nums_range:
    deck.append(Card(card_suits[suits], str(card_nums)))
    for card in range(len(card_faces)):
    deck.append(Card(card_suits[suits], card_faces[card]))
    random.shuffle(deck)
    return deck

    def draw(hand, num_to_draw, deck):
    """
    takes a hand list and a number and draws that number
    of cards from the deck and places them in the
    desired hand
    """
    for num_cards in range(num_to_draw):
    card = deck[-1]
    hand.append(card)
    deck.remove(card)

    def count_hand(hand):
    """
    Evaluates a hand and returns the value
    of its cards
    """
    card_values = {"2": 2, "3": 3, "4": 4, "5": 5,
    "6": 6, "7": 7, "8":8, "9": 9,
    "10":10, "J":10, "Q":10, "K":10}
    hand_sum = 0
    hand_suits =
    for cards in hand:
    hand_suits.append(cards.value)
    if "A" in hand_suits:
    num_aces = 0
    for card_value in hand_suits:
    if card_value == "A":
    num_aces += 1
    for card in hand:
    if card.value != "A":
    hand_sum += card_values.get(card.value)
    if num_aces == 1:
    if hand_sum + 11 > 21:
    hand_sum += 1
    elif hand_sum + 11 <= 21:
    hand_sum += 11
    elif num_aces == 2:
    if hand_sum + 12 > 21:
    hand_sum += 2
    elif hand_sum + 12 <= 21:
    hand_sum += 12
    elif num_aces == 3:
    if hand_sum + 13 > 21:
    hand_sum += 3
    elif hand_sum + 13 <= 21:
    hand_sum += 13
    elif num_aces == 4:
    if hand_sum + 14 > 21:
    hand_sum += 4
    elif hand_sum + 14 <= 21:
    hand_sum += 14
    elif "A" not in hand_suits:
    for card in hand:
    hand_sum += card_values.get(card.value)
    return hand_sum

    def player_hits(player_hand, stdscr):
    """
    Asks if player wants to hit
    """
    # get dimensions
    wsize = stdscr.getmaxyx()
    prompt_line = 16
    # lay out the strings
    prompt = "(H)it or (S)tay"
    prompt_hit = "Player has chosen to hit!"
    prompt_stay = "Player has chosen to stay!"
    prompt_wrong = "Invalid input! Try again..."
    # center the prompts
    prompt_x = wsize[1] // 2 - len(prompt) // 2
    prompt_hit_x = wsize[1] // 2 - len(prompt_hit) // 2
    prompt_stay_x = wsize[1] // 2 - len(prompt_stay) // 2
    prompt_wrong_x = wsize[1] // 2 - len(prompt_wrong) // 2
    # clear the entire prompt line
    clear_str = ""
    for char_cell in range(wsize[1]):
    clear_str += " "
    stdscr.addstr(prompt_line, 0, clear_str)
    # display the prompt
    stdscr.addstr(prompt_line, prompt_x, prompt)
    # get the input
    uinput = stdscr.getch()
    if uinput == 104 or uinput == 72:
    # print("Player has chosen to hit!")
    stdscr.addstr(prompt_line, 0, clear_str)
    stdscr.addstr(prompt_line, prompt_hit_x, prompt_hit)
    stdscr.getch()
    return True
    elif uinput == 83 or uinput == 115:
    # print("Player has chosen to stay!")
    stdscr.addstr(prompt_line, 0, clear_str)
    stdscr.addstr(prompt_line, prompt_stay_x, prompt_stay)
    stdscr.getch()
    return False
    else:
    # print("Invalid input! Try again...")
    stdscr.addstr(prompt_line, 0, clear_str)
    stdscr.addstr(prompt_line, prompt_wrong_x, prompt_wrong)
    stdscr.getch()
    player_hits(player_hand)

    def prompt(string, stdscr):
    """
    Takes a string, clears the prompt line, and places the
    string on the prompt line
    """
    wsize = stdscr.getmaxyx()
    prompt_line = 16
    prompt_clear = ""
    for char_cell in range(wsize[1]):
    prompt_clear += " "
    stdscr.addstr(prompt_line, 0, prompt_clear)
    centered_x = wsize[1] // 2 - len(string) // 2
    stdscr.addstr(prompt_line, centered_x, string)
    stdscr.getch()

    def is_busted(hand):
    """
    Checks a hand and if it is busted, returns True
    """
    card_values = {"2": 2, "3": 3, "4": 4, "5": 5,
    "6": 6, "7": 7, "8":8, "9": 9,
    "10":10, "J":10, "Q":10, "K":10}
    hand_sum = 0
    hand_suits =
    for cards in hand:
    hand_suits.append(cards.value)
    if "A" in hand_suits:
    num_aces = 0
    for card_value in hand_suits:
    if card_value == "A":
    num_aces += 1
    for card in hand:
    if card.value != "A":
    hand_sum += card_values.get(card.value)
    if num_aces == 1:
    if hand_sum + 11 > 21:
    hand_sum += 1
    elif hand_sum + 11 <= 21:
    hand_sum += 11
    elif num_aces == 2:
    if hand_sum + 12 > 21:
    hand_sum += 2
    elif hand_sum + 12 <= 21:
    hand_sum += 12
    elif num_aces == 3:
    if hand_sum + 13 > 21:
    hand_sum += 3
    elif hand_sum + 13 <= 21:
    hand_sum += 13
    elif num_aces == 4:
    if hand_sum + 14 > 21:
    hand_sum += 4
    elif hand_sum + 14 <= 21:
    hand_sum += 14
    elif "A" not in hand_suits:
    for card in hand:
    hand_sum += card_values.get(card.value)
    if hand_sum > 21:
    return True
    else:
    return False


    def game_not_over(player_funds, turn_num):
    """
    Checks to see if the game is over.
    Returns True if game not over.
    Prints game over if game is over, then returns False
    """
    if player_funds <= 0:
    prompt("Player loses in " + str(turn_num) + " turns!", stdscr)
    return False
    elif player_funds > 0:
    return True

    def compare_hands(dealer_hand, player_hand):
    """
    Checks to see which hand is the winner
    returns "dealer" or "player" as a result
    In case of tie, returns "dealer"
    """
    player_score = count_hand(player_hand)
    dealer_score = count_hand(dealer_hand)
    if player_score > dealer_score:
    return "player"
    elif dealer_score >= player_score:
    return "dealer"

    def dealer_hits(dealer_hand):
    """
    Counts the dealer hand and returns true
    if under 17
    """
    count = count_hand(dealer_hand)
    if count < 17:
    prompt("Dealer hits!", stdscr)
    return True
    else:
    prompt("Dealer Stays!", stdscr)
    return False

    def draw_screen(stdscr, dealer_hand, player_hand, turn_num, player_funds, dealer_flipped=False):
    """
    Draws the entire game status on to the screen
    including a visual representation of the cards in play.
    Will be centered in final version.
    """
    # clear screen
    stdscr.clear()
    # get dimensions
    wsize = stdscr.getmaxyx()
    display_height = 17
    display_width = 36
    # get the strings
    funds_str = str("Player Funds: " + str(player_funds))
    turn_str = str("Turn Number: " + str(turn_num))
    player_score_str = str("Player: " + str(count_hand(player_hand)))
    # dealer score string depends on whether dealer_flipped is flagged
    if dealer_flipped:
    dealer_score_str = str("Dealer: " + str(count_hand(dealer_hand)))
    if not dealer_flipped:
    flipped_dealer_hand =
    for card in range(len(dealer_hand)):
    if card != 0:
    flipped_dealer_hand.append(dealer_hand[card])
    dealer_score_str = str("Dealer Visible: " + str(count_hand(flipped_dealer_hand)))
    # place the strings in their appropriate places
    dealer_str_coords = (0, 1)
    player_str_coords = (8, 1)
    funds_str_coords = (15, 1)
    turn_str_coords = (15, 20)
    stdscr.addstr(dealer_str_coords[0], dealer_str_coords[1], dealer_score_str)
    stdscr.addstr(player_str_coords[0], player_str_coords[1], player_score_str)
    stdscr.addstr(funds_str_coords[0], funds_str_coords[1], funds_str)
    stdscr.addstr(turn_str_coords[0], turn_str_coords[1], turn_str)
    # place the cards:
    # create lists of tuples with the x and y coords or each symbol on each card
    # List of lists of tuples:
    # Outer list = hand.
    # Inner list = card
    # Sets = (top-left suit, central value, bottom-right suit)
    # called with something like sym = dealer_hand_coords[0][0] for top-left symbol of first card in hand
    # first tuple doubles as a top-left coordinate for the blank card rects
    dealer_hand_coords = [[(2, 1), (4, 2), (6, 3)],
    [(2, 5), (4, 6), (6, 7)],
    [(2, 9), (4, 10), (6, 11)],
    [(2, 13), (4, 14), (6, 15)],
    [(2, 17), (4, 18), (6, 19)],
    [(2, 21), (4, 22), (6, 23)],
    [(2, 25), (4, 26), (6, 27)],
    [(2, 29), (4, 30), (6, 31)],
    [(2, 33), (4, 34), (6, 35)]]

    player_hand_coords = [[(9, 1), (11, 2), (13, 3)],
    [(9, 5), (11, 6), (13, 7)],
    [(9, 9), (11, 10), (13, 11)],
    [(9, 13), (11, 14), (13, 15)],
    [(9, 17), (11, 18), (13, 19)],
    [(9, 21), (11, 22), (13, 23)],
    [(9, 25), (11, 26), (13, 27)],
    [(9, 29), (11, 30), (13, 31)],
    [(9, 33), (11, 34), (13, 35)]]

    # NOTE: Re-Factor this into some more DRY-compliant code
    # NOTE: Re-Factor into multiple smaller functions that are easier for others
    # to follow along with!
    # player hand
    for card in range(len(player_hand)):
    # for each card in the hand
    value = player_hand[card].value
    suit = player_hand[card].suit
    if suit == "H" or suit == "D":
    color = colors_dict.get("RED_CARD")
    elif suit == "C" or suit == "S":
    color = colors_dict.get("BLACK_CARD")

    # place the blank card rect
    card_height = 5
    card_width = 3
    for cell_y in range(player_hand_coords[card][0][0], player_hand_coords[card][0][0] + card_height):
    for cell_x in range(player_hand_coords[card][0][1], player_hand_coords[card][0][1] + card_width):
    stdscr.addstr(cell_y, cell_x, " ", curses.color_pair(color))

    # place the symbols
    # place two suit symbols and a value symbol
    stdscr.addstr(player_hand_coords[card][0][0], player_hand_coords[card][0][1], suit, curses.color_pair(color))
    stdscr.addstr(player_hand_coords[card][1][0], player_hand_coords[card][1][1], value, curses.color_pair(color))
    stdscr.addstr(player_hand_coords[card][2][0], player_hand_coords[card][2][1], suit, curses.color_pair(color))

    # dealer hand
    if dealer_flipped:
    for card in range(len(dealer_hand)):
    # for each card in the hand
    value = dealer_hand[card].value
    suit = dealer_hand[card].suit
    if suit == "H" or suit == "D":
    color = colors_dict.get("RED_CARD")
    elif suit == "C" or suit == "S":
    color = colors_dict.get("BLACK_CARD")

    # place the blank card rect
    card_height = 5
    card_width = 3
    for cell_y in range(dealer_hand_coords[card][0][0], dealer_hand_coords[card][0][0] + card_height):
    for cell_x in range(dealer_hand_coords[card][0][1], dealer_hand_coords[card][0][1] + card_width):
    stdscr.addstr(cell_y, cell_x, " ", curses.color_pair(color))

    # place the symbols
    # place two suit symbols and a value symbol
    stdscr.addstr(dealer_hand_coords[card][0][0], dealer_hand_coords[card][0][1], suit, curses.color_pair(color))
    stdscr.addstr(dealer_hand_coords[card][1][0], dealer_hand_coords[card][1][1], value, curses.color_pair(color))
    stdscr.addstr(dealer_hand_coords[card][2][0], dealer_hand_coords[card][2][1], suit, curses.color_pair(color))

    if not dealer_flipped:
    for card in range(len(dealer_hand)):
    # for each card in the hand
    value = dealer_hand[card].value
    suit = dealer_hand[card].suit
    if suit == "H" or suit == "D":
    color = colors_dict.get("RED_CARD")
    elif suit == "C" or suit == "S":
    color = colors_dict.get("BLACK_CARD")

    # place the blank card rect
    card_height = 5
    card_width = 3
    for cell_y in range(dealer_hand_coords[card][0][0], dealer_hand_coords[card][0][0] + card_height):
    for cell_x in range(dealer_hand_coords[card][0][1], dealer_hand_coords[card][0][1] + card_width):
    stdscr.addstr(cell_y, cell_x, " ", curses.color_pair(color))

    # place the symbols
    # place two suit symbols and a value symbol
    if card != 0:
    stdscr.addstr(dealer_hand_coords[card][0][0], dealer_hand_coords[card][0][1], suit, curses.color_pair(color))
    stdscr.addstr(dealer_hand_coords[card][1][0], dealer_hand_coords[card][1][1], value, curses.color_pair(color))
    stdscr.addstr(dealer_hand_coords[card][2][0], dealer_hand_coords[card][2][1], suit, curses.color_pair(color))
    stdscr.refresh()

    def main(stdscr):
    try:
    # bet amount
    bet = 100
    # starting funds
    player_funds = 1000
    turn_num = 1
    while game_not_over(player_funds, turn_num):
    # while the player has funds left to bet
    # generate a new deck
    deck = generate_deck()
    dealer_hand =
    player_hand =
    # draw two cards for each player
    draw(dealer_hand, 2, deck)
    draw(player_hand, 2, deck)
    # take the player's bet
    player_funds -= bet
    winner = None
    player_hitting = True
    while player_hitting:
    # while the player is deciding to hit or stay:
    # draw the screen with curses
    draw_screen(stdscr, dealer_hand, player_hand, turn_num, player_funds)
    if player_hits(player_hand, stdscr):
    # if the player chooses to hit:
    # draw a card
    draw(player_hand, 1, deck)
    if is_busted(player_hand):
    # If the player busts:
    # draw the screen again
    draw_screen(stdscr, dealer_hand, player_hand, turn_num, player_funds)
    # prompt that the player has busted
    prompt("Player Busted!", stdscr)
    player_hitting = False
    winner = "dealer"
    else:
    # end the loop if the player chooses to stay
    player_hitting = False
    if not is_busted(player_hand):
    # If the player has stayed and the player has not busted:
    dealer_hitting = True
    while dealer_hitting:
    # while the dealer is choosing to hit or stay:
    # draw the screen with curses
    draw_screen(stdscr, dealer_hand, player_hand, turn_num, player_funds, dealer_flipped=True)
    if dealer_hits(dealer_hand):
    # If the dealer chooses to hit:
    # dealer draws a card
    draw(dealer_hand, 1, deck)
    if is_busted(dealer_hand):
    # If the dealer busts:
    # draw the screen with curses
    draw_screen(stdscr, dealer_hand, player_hand, turn_num, player_funds, dealer_flipped=True)
    # prompt that the dealer has busted
    prompt("Dealer Busted!", stdscr)
    dealer_hitting = False
    winner = "player"
    # reward the player with double their bet
    player_funds += bet * 2
    else:
    # if the dealer busts, break the loop
    dealer_hitting = False
    if not is_busted(dealer_hand):
    if not is_busted(player_hand):
    # If neither player has busted and both have stayed:
    # draw the screen with curses
    draw_screen(stdscr, dealer_hand, player_hand, turn_num, player_funds, dealer_flipped=True)
    # get the winning hand
    winner = compare_hands(dealer_hand, player_hand)
    # prompt the winner
    prompt(str(winner + " Wins!"), stdscr)
    if winner == "player":
    # if the player wins, reward them with double their bet
    player_funds += bet * 2
    # increase turn num
    turn_num += 1
    finally:
    # end curses window on error or exit
    curses.endwin()


    if __name__ == "__main__":
    main(stdscr)


    And here is a link to the GitHub page, for those who want to contribute more directly.



    As ever, anyone who finds this useful is more than welcome to do whatever they want with it. I worked hard on it, but it was just a practice project. If it helps someone else, by all means go for it.










    share|improve this question











    $endgroup$















      0












      0








      0





      $begingroup$


      You were all so helpful with my Tc Tac Toe games in C. I was encouraged to put it up on GitHub and two very helpful users refactored it in ways that really taught me a thing or two. While I have switched focus to Python in anticipation of the upcoming 7DRL I was so happy about the way my C projects were reviewed that this has become one of my favorite forums. So now I want your help with something else:



      Over the last week or so I've created a Blackjack game with Python 3.5.3. The logic was easy to do, especially with Python, but making a clean curses interface was more challenging. What I wound up doing was creating a blackjack game without curses to get the logic straight and then implementing curses in the form of a draw_screen() function that turned out to be a real monster. It is messy and repetitive compared to the rest of the code, and the function wound up being well over 100 lines long. In addition to that I'm still struggling to do things in the most "Pythonic" way, and even the behind-the-screen logic may not be implemented in a way that is ideal.



      What I want help from you guys with is to help me refactor the draw_screen() function into something more manageable. Should I split it up into multiple functions? How best to do that? I would also appreciate any and all feedback regarding how I could make the non-curses logic of the game more "Pythonic."



      I intend to make a Roguelike for the 7DRL event this year and I intend to do it with Python 3.5.3 and the curses library. It'll be harder than what I usually do but I'm looking forward to it. By helping me hammer out my fundamentals here you will help me to be better prepared for making a small Roguelike in the near future.



      Note: the GREEN_TEXT and RED_TEXT color pair definitions were originally
      going to be used for the player's funds and for busting prompts, respectively. I haven't removed them because I intend to add that functionality myself very soon, if not today.



      Here is the code itself:



      """
      Project: Simple 21/Blackjack
      File: twenty-one-curses.py
      Date: 24 JAN 2019
      Author: sgibber2018
      Description: A simple implementation of 21/Blackjack using the terminal and python.
      Uses the curses library for character cell graphics.
      """

      import random
      import curses

      # init curses
      stdscr = curses.initscr()
      curses.cbreak()
      curses.noecho()
      curses.curs_set(False)
      curses.start_color()

      # init curses colors
      curses.init_color(curses.COLOR_RED, 900, 0, 0)
      curses.init_color(curses.COLOR_BLACK, 0, 0, 0)
      curses.init_color(curses.COLOR_GREEN, 0, 900, 0)
      curses.init_color(curses.COLOR_WHITE, 1000, 1000, 1000)
      colors_dict = {"RED_CARD":1,
      "BLACK_CARD":2,
      "GREEN_TEXT":3,
      "RED_TEXT":4}
      curses.init_pair(colors_dict.get("RED_CARD"), curses.COLOR_RED, curses.COLOR_WHITE)
      curses.init_pair(colors_dict.get("BLACK_CARD"), curses.COLOR_BLACK, curses.COLOR_WHITE)
      curses.init_pair(colors_dict.get("GREEN_TEXT"), curses.COLOR_GREEN, curses.COLOR_BLACK)
      curses.init_pair(colors_dict.get("RED_TEXT"), curses.COLOR_RED, curses.COLOR_BLACK)


      class Card:
      def __init__(self, suit, value):
      self.suit = suit
      self.value = value


      def generate_deck():
      """
      Generate a list of Card objects to be used as a deck
      and return it
      """
      deck =
      card_suits = ["D", "H", "S", "C"]
      card_nums_range = range(2, 11)
      card_faces = ["J", "Q", "K", "A"]
      for suits in range(len(card_suits)):
      for card_nums in card_nums_range:
      deck.append(Card(card_suits[suits], str(card_nums)))
      for card in range(len(card_faces)):
      deck.append(Card(card_suits[suits], card_faces[card]))
      random.shuffle(deck)
      return deck

      def draw(hand, num_to_draw, deck):
      """
      takes a hand list and a number and draws that number
      of cards from the deck and places them in the
      desired hand
      """
      for num_cards in range(num_to_draw):
      card = deck[-1]
      hand.append(card)
      deck.remove(card)

      def count_hand(hand):
      """
      Evaluates a hand and returns the value
      of its cards
      """
      card_values = {"2": 2, "3": 3, "4": 4, "5": 5,
      "6": 6, "7": 7, "8":8, "9": 9,
      "10":10, "J":10, "Q":10, "K":10}
      hand_sum = 0
      hand_suits =
      for cards in hand:
      hand_suits.append(cards.value)
      if "A" in hand_suits:
      num_aces = 0
      for card_value in hand_suits:
      if card_value == "A":
      num_aces += 1
      for card in hand:
      if card.value != "A":
      hand_sum += card_values.get(card.value)
      if num_aces == 1:
      if hand_sum + 11 > 21:
      hand_sum += 1
      elif hand_sum + 11 <= 21:
      hand_sum += 11
      elif num_aces == 2:
      if hand_sum + 12 > 21:
      hand_sum += 2
      elif hand_sum + 12 <= 21:
      hand_sum += 12
      elif num_aces == 3:
      if hand_sum + 13 > 21:
      hand_sum += 3
      elif hand_sum + 13 <= 21:
      hand_sum += 13
      elif num_aces == 4:
      if hand_sum + 14 > 21:
      hand_sum += 4
      elif hand_sum + 14 <= 21:
      hand_sum += 14
      elif "A" not in hand_suits:
      for card in hand:
      hand_sum += card_values.get(card.value)
      return hand_sum

      def player_hits(player_hand, stdscr):
      """
      Asks if player wants to hit
      """
      # get dimensions
      wsize = stdscr.getmaxyx()
      prompt_line = 16
      # lay out the strings
      prompt = "(H)it or (S)tay"
      prompt_hit = "Player has chosen to hit!"
      prompt_stay = "Player has chosen to stay!"
      prompt_wrong = "Invalid input! Try again..."
      # center the prompts
      prompt_x = wsize[1] // 2 - len(prompt) // 2
      prompt_hit_x = wsize[1] // 2 - len(prompt_hit) // 2
      prompt_stay_x = wsize[1] // 2 - len(prompt_stay) // 2
      prompt_wrong_x = wsize[1] // 2 - len(prompt_wrong) // 2
      # clear the entire prompt line
      clear_str = ""
      for char_cell in range(wsize[1]):
      clear_str += " "
      stdscr.addstr(prompt_line, 0, clear_str)
      # display the prompt
      stdscr.addstr(prompt_line, prompt_x, prompt)
      # get the input
      uinput = stdscr.getch()
      if uinput == 104 or uinput == 72:
      # print("Player has chosen to hit!")
      stdscr.addstr(prompt_line, 0, clear_str)
      stdscr.addstr(prompt_line, prompt_hit_x, prompt_hit)
      stdscr.getch()
      return True
      elif uinput == 83 or uinput == 115:
      # print("Player has chosen to stay!")
      stdscr.addstr(prompt_line, 0, clear_str)
      stdscr.addstr(prompt_line, prompt_stay_x, prompt_stay)
      stdscr.getch()
      return False
      else:
      # print("Invalid input! Try again...")
      stdscr.addstr(prompt_line, 0, clear_str)
      stdscr.addstr(prompt_line, prompt_wrong_x, prompt_wrong)
      stdscr.getch()
      player_hits(player_hand)

      def prompt(string, stdscr):
      """
      Takes a string, clears the prompt line, and places the
      string on the prompt line
      """
      wsize = stdscr.getmaxyx()
      prompt_line = 16
      prompt_clear = ""
      for char_cell in range(wsize[1]):
      prompt_clear += " "
      stdscr.addstr(prompt_line, 0, prompt_clear)
      centered_x = wsize[1] // 2 - len(string) // 2
      stdscr.addstr(prompt_line, centered_x, string)
      stdscr.getch()

      def is_busted(hand):
      """
      Checks a hand and if it is busted, returns True
      """
      card_values = {"2": 2, "3": 3, "4": 4, "5": 5,
      "6": 6, "7": 7, "8":8, "9": 9,
      "10":10, "J":10, "Q":10, "K":10}
      hand_sum = 0
      hand_suits =
      for cards in hand:
      hand_suits.append(cards.value)
      if "A" in hand_suits:
      num_aces = 0
      for card_value in hand_suits:
      if card_value == "A":
      num_aces += 1
      for card in hand:
      if card.value != "A":
      hand_sum += card_values.get(card.value)
      if num_aces == 1:
      if hand_sum + 11 > 21:
      hand_sum += 1
      elif hand_sum + 11 <= 21:
      hand_sum += 11
      elif num_aces == 2:
      if hand_sum + 12 > 21:
      hand_sum += 2
      elif hand_sum + 12 <= 21:
      hand_sum += 12
      elif num_aces == 3:
      if hand_sum + 13 > 21:
      hand_sum += 3
      elif hand_sum + 13 <= 21:
      hand_sum += 13
      elif num_aces == 4:
      if hand_sum + 14 > 21:
      hand_sum += 4
      elif hand_sum + 14 <= 21:
      hand_sum += 14
      elif "A" not in hand_suits:
      for card in hand:
      hand_sum += card_values.get(card.value)
      if hand_sum > 21:
      return True
      else:
      return False


      def game_not_over(player_funds, turn_num):
      """
      Checks to see if the game is over.
      Returns True if game not over.
      Prints game over if game is over, then returns False
      """
      if player_funds <= 0:
      prompt("Player loses in " + str(turn_num) + " turns!", stdscr)
      return False
      elif player_funds > 0:
      return True

      def compare_hands(dealer_hand, player_hand):
      """
      Checks to see which hand is the winner
      returns "dealer" or "player" as a result
      In case of tie, returns "dealer"
      """
      player_score = count_hand(player_hand)
      dealer_score = count_hand(dealer_hand)
      if player_score > dealer_score:
      return "player"
      elif dealer_score >= player_score:
      return "dealer"

      def dealer_hits(dealer_hand):
      """
      Counts the dealer hand and returns true
      if under 17
      """
      count = count_hand(dealer_hand)
      if count < 17:
      prompt("Dealer hits!", stdscr)
      return True
      else:
      prompt("Dealer Stays!", stdscr)
      return False

      def draw_screen(stdscr, dealer_hand, player_hand, turn_num, player_funds, dealer_flipped=False):
      """
      Draws the entire game status on to the screen
      including a visual representation of the cards in play.
      Will be centered in final version.
      """
      # clear screen
      stdscr.clear()
      # get dimensions
      wsize = stdscr.getmaxyx()
      display_height = 17
      display_width = 36
      # get the strings
      funds_str = str("Player Funds: " + str(player_funds))
      turn_str = str("Turn Number: " + str(turn_num))
      player_score_str = str("Player: " + str(count_hand(player_hand)))
      # dealer score string depends on whether dealer_flipped is flagged
      if dealer_flipped:
      dealer_score_str = str("Dealer: " + str(count_hand(dealer_hand)))
      if not dealer_flipped:
      flipped_dealer_hand =
      for card in range(len(dealer_hand)):
      if card != 0:
      flipped_dealer_hand.append(dealer_hand[card])
      dealer_score_str = str("Dealer Visible: " + str(count_hand(flipped_dealer_hand)))
      # place the strings in their appropriate places
      dealer_str_coords = (0, 1)
      player_str_coords = (8, 1)
      funds_str_coords = (15, 1)
      turn_str_coords = (15, 20)
      stdscr.addstr(dealer_str_coords[0], dealer_str_coords[1], dealer_score_str)
      stdscr.addstr(player_str_coords[0], player_str_coords[1], player_score_str)
      stdscr.addstr(funds_str_coords[0], funds_str_coords[1], funds_str)
      stdscr.addstr(turn_str_coords[0], turn_str_coords[1], turn_str)
      # place the cards:
      # create lists of tuples with the x and y coords or each symbol on each card
      # List of lists of tuples:
      # Outer list = hand.
      # Inner list = card
      # Sets = (top-left suit, central value, bottom-right suit)
      # called with something like sym = dealer_hand_coords[0][0] for top-left symbol of first card in hand
      # first tuple doubles as a top-left coordinate for the blank card rects
      dealer_hand_coords = [[(2, 1), (4, 2), (6, 3)],
      [(2, 5), (4, 6), (6, 7)],
      [(2, 9), (4, 10), (6, 11)],
      [(2, 13), (4, 14), (6, 15)],
      [(2, 17), (4, 18), (6, 19)],
      [(2, 21), (4, 22), (6, 23)],
      [(2, 25), (4, 26), (6, 27)],
      [(2, 29), (4, 30), (6, 31)],
      [(2, 33), (4, 34), (6, 35)]]

      player_hand_coords = [[(9, 1), (11, 2), (13, 3)],
      [(9, 5), (11, 6), (13, 7)],
      [(9, 9), (11, 10), (13, 11)],
      [(9, 13), (11, 14), (13, 15)],
      [(9, 17), (11, 18), (13, 19)],
      [(9, 21), (11, 22), (13, 23)],
      [(9, 25), (11, 26), (13, 27)],
      [(9, 29), (11, 30), (13, 31)],
      [(9, 33), (11, 34), (13, 35)]]

      # NOTE: Re-Factor this into some more DRY-compliant code
      # NOTE: Re-Factor into multiple smaller functions that are easier for others
      # to follow along with!
      # player hand
      for card in range(len(player_hand)):
      # for each card in the hand
      value = player_hand[card].value
      suit = player_hand[card].suit
      if suit == "H" or suit == "D":
      color = colors_dict.get("RED_CARD")
      elif suit == "C" or suit == "S":
      color = colors_dict.get("BLACK_CARD")

      # place the blank card rect
      card_height = 5
      card_width = 3
      for cell_y in range(player_hand_coords[card][0][0], player_hand_coords[card][0][0] + card_height):
      for cell_x in range(player_hand_coords[card][0][1], player_hand_coords[card][0][1] + card_width):
      stdscr.addstr(cell_y, cell_x, " ", curses.color_pair(color))

      # place the symbols
      # place two suit symbols and a value symbol
      stdscr.addstr(player_hand_coords[card][0][0], player_hand_coords[card][0][1], suit, curses.color_pair(color))
      stdscr.addstr(player_hand_coords[card][1][0], player_hand_coords[card][1][1], value, curses.color_pair(color))
      stdscr.addstr(player_hand_coords[card][2][0], player_hand_coords[card][2][1], suit, curses.color_pair(color))

      # dealer hand
      if dealer_flipped:
      for card in range(len(dealer_hand)):
      # for each card in the hand
      value = dealer_hand[card].value
      suit = dealer_hand[card].suit
      if suit == "H" or suit == "D":
      color = colors_dict.get("RED_CARD")
      elif suit == "C" or suit == "S":
      color = colors_dict.get("BLACK_CARD")

      # place the blank card rect
      card_height = 5
      card_width = 3
      for cell_y in range(dealer_hand_coords[card][0][0], dealer_hand_coords[card][0][0] + card_height):
      for cell_x in range(dealer_hand_coords[card][0][1], dealer_hand_coords[card][0][1] + card_width):
      stdscr.addstr(cell_y, cell_x, " ", curses.color_pair(color))

      # place the symbols
      # place two suit symbols and a value symbol
      stdscr.addstr(dealer_hand_coords[card][0][0], dealer_hand_coords[card][0][1], suit, curses.color_pair(color))
      stdscr.addstr(dealer_hand_coords[card][1][0], dealer_hand_coords[card][1][1], value, curses.color_pair(color))
      stdscr.addstr(dealer_hand_coords[card][2][0], dealer_hand_coords[card][2][1], suit, curses.color_pair(color))

      if not dealer_flipped:
      for card in range(len(dealer_hand)):
      # for each card in the hand
      value = dealer_hand[card].value
      suit = dealer_hand[card].suit
      if suit == "H" or suit == "D":
      color = colors_dict.get("RED_CARD")
      elif suit == "C" or suit == "S":
      color = colors_dict.get("BLACK_CARD")

      # place the blank card rect
      card_height = 5
      card_width = 3
      for cell_y in range(dealer_hand_coords[card][0][0], dealer_hand_coords[card][0][0] + card_height):
      for cell_x in range(dealer_hand_coords[card][0][1], dealer_hand_coords[card][0][1] + card_width):
      stdscr.addstr(cell_y, cell_x, " ", curses.color_pair(color))

      # place the symbols
      # place two suit symbols and a value symbol
      if card != 0:
      stdscr.addstr(dealer_hand_coords[card][0][0], dealer_hand_coords[card][0][1], suit, curses.color_pair(color))
      stdscr.addstr(dealer_hand_coords[card][1][0], dealer_hand_coords[card][1][1], value, curses.color_pair(color))
      stdscr.addstr(dealer_hand_coords[card][2][0], dealer_hand_coords[card][2][1], suit, curses.color_pair(color))
      stdscr.refresh()

      def main(stdscr):
      try:
      # bet amount
      bet = 100
      # starting funds
      player_funds = 1000
      turn_num = 1
      while game_not_over(player_funds, turn_num):
      # while the player has funds left to bet
      # generate a new deck
      deck = generate_deck()
      dealer_hand =
      player_hand =
      # draw two cards for each player
      draw(dealer_hand, 2, deck)
      draw(player_hand, 2, deck)
      # take the player's bet
      player_funds -= bet
      winner = None
      player_hitting = True
      while player_hitting:
      # while the player is deciding to hit or stay:
      # draw the screen with curses
      draw_screen(stdscr, dealer_hand, player_hand, turn_num, player_funds)
      if player_hits(player_hand, stdscr):
      # if the player chooses to hit:
      # draw a card
      draw(player_hand, 1, deck)
      if is_busted(player_hand):
      # If the player busts:
      # draw the screen again
      draw_screen(stdscr, dealer_hand, player_hand, turn_num, player_funds)
      # prompt that the player has busted
      prompt("Player Busted!", stdscr)
      player_hitting = False
      winner = "dealer"
      else:
      # end the loop if the player chooses to stay
      player_hitting = False
      if not is_busted(player_hand):
      # If the player has stayed and the player has not busted:
      dealer_hitting = True
      while dealer_hitting:
      # while the dealer is choosing to hit or stay:
      # draw the screen with curses
      draw_screen(stdscr, dealer_hand, player_hand, turn_num, player_funds, dealer_flipped=True)
      if dealer_hits(dealer_hand):
      # If the dealer chooses to hit:
      # dealer draws a card
      draw(dealer_hand, 1, deck)
      if is_busted(dealer_hand):
      # If the dealer busts:
      # draw the screen with curses
      draw_screen(stdscr, dealer_hand, player_hand, turn_num, player_funds, dealer_flipped=True)
      # prompt that the dealer has busted
      prompt("Dealer Busted!", stdscr)
      dealer_hitting = False
      winner = "player"
      # reward the player with double their bet
      player_funds += bet * 2
      else:
      # if the dealer busts, break the loop
      dealer_hitting = False
      if not is_busted(dealer_hand):
      if not is_busted(player_hand):
      # If neither player has busted and both have stayed:
      # draw the screen with curses
      draw_screen(stdscr, dealer_hand, player_hand, turn_num, player_funds, dealer_flipped=True)
      # get the winning hand
      winner = compare_hands(dealer_hand, player_hand)
      # prompt the winner
      prompt(str(winner + " Wins!"), stdscr)
      if winner == "player":
      # if the player wins, reward them with double their bet
      player_funds += bet * 2
      # increase turn num
      turn_num += 1
      finally:
      # end curses window on error or exit
      curses.endwin()


      if __name__ == "__main__":
      main(stdscr)


      And here is a link to the GitHub page, for those who want to contribute more directly.



      As ever, anyone who finds this useful is more than welcome to do whatever they want with it. I worked hard on it, but it was just a practice project. If it helps someone else, by all means go for it.










      share|improve this question











      $endgroup$




      You were all so helpful with my Tc Tac Toe games in C. I was encouraged to put it up on GitHub and two very helpful users refactored it in ways that really taught me a thing or two. While I have switched focus to Python in anticipation of the upcoming 7DRL I was so happy about the way my C projects were reviewed that this has become one of my favorite forums. So now I want your help with something else:



      Over the last week or so I've created a Blackjack game with Python 3.5.3. The logic was easy to do, especially with Python, but making a clean curses interface was more challenging. What I wound up doing was creating a blackjack game without curses to get the logic straight and then implementing curses in the form of a draw_screen() function that turned out to be a real monster. It is messy and repetitive compared to the rest of the code, and the function wound up being well over 100 lines long. In addition to that I'm still struggling to do things in the most "Pythonic" way, and even the behind-the-screen logic may not be implemented in a way that is ideal.



      What I want help from you guys with is to help me refactor the draw_screen() function into something more manageable. Should I split it up into multiple functions? How best to do that? I would also appreciate any and all feedback regarding how I could make the non-curses logic of the game more "Pythonic."



      I intend to make a Roguelike for the 7DRL event this year and I intend to do it with Python 3.5.3 and the curses library. It'll be harder than what I usually do but I'm looking forward to it. By helping me hammer out my fundamentals here you will help me to be better prepared for making a small Roguelike in the near future.



      Note: the GREEN_TEXT and RED_TEXT color pair definitions were originally
      going to be used for the player's funds and for busting prompts, respectively. I haven't removed them because I intend to add that functionality myself very soon, if not today.



      Here is the code itself:



      """
      Project: Simple 21/Blackjack
      File: twenty-one-curses.py
      Date: 24 JAN 2019
      Author: sgibber2018
      Description: A simple implementation of 21/Blackjack using the terminal and python.
      Uses the curses library for character cell graphics.
      """

      import random
      import curses

      # init curses
      stdscr = curses.initscr()
      curses.cbreak()
      curses.noecho()
      curses.curs_set(False)
      curses.start_color()

      # init curses colors
      curses.init_color(curses.COLOR_RED, 900, 0, 0)
      curses.init_color(curses.COLOR_BLACK, 0, 0, 0)
      curses.init_color(curses.COLOR_GREEN, 0, 900, 0)
      curses.init_color(curses.COLOR_WHITE, 1000, 1000, 1000)
      colors_dict = {"RED_CARD":1,
      "BLACK_CARD":2,
      "GREEN_TEXT":3,
      "RED_TEXT":4}
      curses.init_pair(colors_dict.get("RED_CARD"), curses.COLOR_RED, curses.COLOR_WHITE)
      curses.init_pair(colors_dict.get("BLACK_CARD"), curses.COLOR_BLACK, curses.COLOR_WHITE)
      curses.init_pair(colors_dict.get("GREEN_TEXT"), curses.COLOR_GREEN, curses.COLOR_BLACK)
      curses.init_pair(colors_dict.get("RED_TEXT"), curses.COLOR_RED, curses.COLOR_BLACK)


      class Card:
      def __init__(self, suit, value):
      self.suit = suit
      self.value = value


      def generate_deck():
      """
      Generate a list of Card objects to be used as a deck
      and return it
      """
      deck =
      card_suits = ["D", "H", "S", "C"]
      card_nums_range = range(2, 11)
      card_faces = ["J", "Q", "K", "A"]
      for suits in range(len(card_suits)):
      for card_nums in card_nums_range:
      deck.append(Card(card_suits[suits], str(card_nums)))
      for card in range(len(card_faces)):
      deck.append(Card(card_suits[suits], card_faces[card]))
      random.shuffle(deck)
      return deck

      def draw(hand, num_to_draw, deck):
      """
      takes a hand list and a number and draws that number
      of cards from the deck and places them in the
      desired hand
      """
      for num_cards in range(num_to_draw):
      card = deck[-1]
      hand.append(card)
      deck.remove(card)

      def count_hand(hand):
      """
      Evaluates a hand and returns the value
      of its cards
      """
      card_values = {"2": 2, "3": 3, "4": 4, "5": 5,
      "6": 6, "7": 7, "8":8, "9": 9,
      "10":10, "J":10, "Q":10, "K":10}
      hand_sum = 0
      hand_suits =
      for cards in hand:
      hand_suits.append(cards.value)
      if "A" in hand_suits:
      num_aces = 0
      for card_value in hand_suits:
      if card_value == "A":
      num_aces += 1
      for card in hand:
      if card.value != "A":
      hand_sum += card_values.get(card.value)
      if num_aces == 1:
      if hand_sum + 11 > 21:
      hand_sum += 1
      elif hand_sum + 11 <= 21:
      hand_sum += 11
      elif num_aces == 2:
      if hand_sum + 12 > 21:
      hand_sum += 2
      elif hand_sum + 12 <= 21:
      hand_sum += 12
      elif num_aces == 3:
      if hand_sum + 13 > 21:
      hand_sum += 3
      elif hand_sum + 13 <= 21:
      hand_sum += 13
      elif num_aces == 4:
      if hand_sum + 14 > 21:
      hand_sum += 4
      elif hand_sum + 14 <= 21:
      hand_sum += 14
      elif "A" not in hand_suits:
      for card in hand:
      hand_sum += card_values.get(card.value)
      return hand_sum

      def player_hits(player_hand, stdscr):
      """
      Asks if player wants to hit
      """
      # get dimensions
      wsize = stdscr.getmaxyx()
      prompt_line = 16
      # lay out the strings
      prompt = "(H)it or (S)tay"
      prompt_hit = "Player has chosen to hit!"
      prompt_stay = "Player has chosen to stay!"
      prompt_wrong = "Invalid input! Try again..."
      # center the prompts
      prompt_x = wsize[1] // 2 - len(prompt) // 2
      prompt_hit_x = wsize[1] // 2 - len(prompt_hit) // 2
      prompt_stay_x = wsize[1] // 2 - len(prompt_stay) // 2
      prompt_wrong_x = wsize[1] // 2 - len(prompt_wrong) // 2
      # clear the entire prompt line
      clear_str = ""
      for char_cell in range(wsize[1]):
      clear_str += " "
      stdscr.addstr(prompt_line, 0, clear_str)
      # display the prompt
      stdscr.addstr(prompt_line, prompt_x, prompt)
      # get the input
      uinput = stdscr.getch()
      if uinput == 104 or uinput == 72:
      # print("Player has chosen to hit!")
      stdscr.addstr(prompt_line, 0, clear_str)
      stdscr.addstr(prompt_line, prompt_hit_x, prompt_hit)
      stdscr.getch()
      return True
      elif uinput == 83 or uinput == 115:
      # print("Player has chosen to stay!")
      stdscr.addstr(prompt_line, 0, clear_str)
      stdscr.addstr(prompt_line, prompt_stay_x, prompt_stay)
      stdscr.getch()
      return False
      else:
      # print("Invalid input! Try again...")
      stdscr.addstr(prompt_line, 0, clear_str)
      stdscr.addstr(prompt_line, prompt_wrong_x, prompt_wrong)
      stdscr.getch()
      player_hits(player_hand)

      def prompt(string, stdscr):
      """
      Takes a string, clears the prompt line, and places the
      string on the prompt line
      """
      wsize = stdscr.getmaxyx()
      prompt_line = 16
      prompt_clear = ""
      for char_cell in range(wsize[1]):
      prompt_clear += " "
      stdscr.addstr(prompt_line, 0, prompt_clear)
      centered_x = wsize[1] // 2 - len(string) // 2
      stdscr.addstr(prompt_line, centered_x, string)
      stdscr.getch()

      def is_busted(hand):
      """
      Checks a hand and if it is busted, returns True
      """
      card_values = {"2": 2, "3": 3, "4": 4, "5": 5,
      "6": 6, "7": 7, "8":8, "9": 9,
      "10":10, "J":10, "Q":10, "K":10}
      hand_sum = 0
      hand_suits =
      for cards in hand:
      hand_suits.append(cards.value)
      if "A" in hand_suits:
      num_aces = 0
      for card_value in hand_suits:
      if card_value == "A":
      num_aces += 1
      for card in hand:
      if card.value != "A":
      hand_sum += card_values.get(card.value)
      if num_aces == 1:
      if hand_sum + 11 > 21:
      hand_sum += 1
      elif hand_sum + 11 <= 21:
      hand_sum += 11
      elif num_aces == 2:
      if hand_sum + 12 > 21:
      hand_sum += 2
      elif hand_sum + 12 <= 21:
      hand_sum += 12
      elif num_aces == 3:
      if hand_sum + 13 > 21:
      hand_sum += 3
      elif hand_sum + 13 <= 21:
      hand_sum += 13
      elif num_aces == 4:
      if hand_sum + 14 > 21:
      hand_sum += 4
      elif hand_sum + 14 <= 21:
      hand_sum += 14
      elif "A" not in hand_suits:
      for card in hand:
      hand_sum += card_values.get(card.value)
      if hand_sum > 21:
      return True
      else:
      return False


      def game_not_over(player_funds, turn_num):
      """
      Checks to see if the game is over.
      Returns True if game not over.
      Prints game over if game is over, then returns False
      """
      if player_funds <= 0:
      prompt("Player loses in " + str(turn_num) + " turns!", stdscr)
      return False
      elif player_funds > 0:
      return True

      def compare_hands(dealer_hand, player_hand):
      """
      Checks to see which hand is the winner
      returns "dealer" or "player" as a result
      In case of tie, returns "dealer"
      """
      player_score = count_hand(player_hand)
      dealer_score = count_hand(dealer_hand)
      if player_score > dealer_score:
      return "player"
      elif dealer_score >= player_score:
      return "dealer"

      def dealer_hits(dealer_hand):
      """
      Counts the dealer hand and returns true
      if under 17
      """
      count = count_hand(dealer_hand)
      if count < 17:
      prompt("Dealer hits!", stdscr)
      return True
      else:
      prompt("Dealer Stays!", stdscr)
      return False

      def draw_screen(stdscr, dealer_hand, player_hand, turn_num, player_funds, dealer_flipped=False):
      """
      Draws the entire game status on to the screen
      including a visual representation of the cards in play.
      Will be centered in final version.
      """
      # clear screen
      stdscr.clear()
      # get dimensions
      wsize = stdscr.getmaxyx()
      display_height = 17
      display_width = 36
      # get the strings
      funds_str = str("Player Funds: " + str(player_funds))
      turn_str = str("Turn Number: " + str(turn_num))
      player_score_str = str("Player: " + str(count_hand(player_hand)))
      # dealer score string depends on whether dealer_flipped is flagged
      if dealer_flipped:
      dealer_score_str = str("Dealer: " + str(count_hand(dealer_hand)))
      if not dealer_flipped:
      flipped_dealer_hand =
      for card in range(len(dealer_hand)):
      if card != 0:
      flipped_dealer_hand.append(dealer_hand[card])
      dealer_score_str = str("Dealer Visible: " + str(count_hand(flipped_dealer_hand)))
      # place the strings in their appropriate places
      dealer_str_coords = (0, 1)
      player_str_coords = (8, 1)
      funds_str_coords = (15, 1)
      turn_str_coords = (15, 20)
      stdscr.addstr(dealer_str_coords[0], dealer_str_coords[1], dealer_score_str)
      stdscr.addstr(player_str_coords[0], player_str_coords[1], player_score_str)
      stdscr.addstr(funds_str_coords[0], funds_str_coords[1], funds_str)
      stdscr.addstr(turn_str_coords[0], turn_str_coords[1], turn_str)
      # place the cards:
      # create lists of tuples with the x and y coords or each symbol on each card
      # List of lists of tuples:
      # Outer list = hand.
      # Inner list = card
      # Sets = (top-left suit, central value, bottom-right suit)
      # called with something like sym = dealer_hand_coords[0][0] for top-left symbol of first card in hand
      # first tuple doubles as a top-left coordinate for the blank card rects
      dealer_hand_coords = [[(2, 1), (4, 2), (6, 3)],
      [(2, 5), (4, 6), (6, 7)],
      [(2, 9), (4, 10), (6, 11)],
      [(2, 13), (4, 14), (6, 15)],
      [(2, 17), (4, 18), (6, 19)],
      [(2, 21), (4, 22), (6, 23)],
      [(2, 25), (4, 26), (6, 27)],
      [(2, 29), (4, 30), (6, 31)],
      [(2, 33), (4, 34), (6, 35)]]

      player_hand_coords = [[(9, 1), (11, 2), (13, 3)],
      [(9, 5), (11, 6), (13, 7)],
      [(9, 9), (11, 10), (13, 11)],
      [(9, 13), (11, 14), (13, 15)],
      [(9, 17), (11, 18), (13, 19)],
      [(9, 21), (11, 22), (13, 23)],
      [(9, 25), (11, 26), (13, 27)],
      [(9, 29), (11, 30), (13, 31)],
      [(9, 33), (11, 34), (13, 35)]]

      # NOTE: Re-Factor this into some more DRY-compliant code
      # NOTE: Re-Factor into multiple smaller functions that are easier for others
      # to follow along with!
      # player hand
      for card in range(len(player_hand)):
      # for each card in the hand
      value = player_hand[card].value
      suit = player_hand[card].suit
      if suit == "H" or suit == "D":
      color = colors_dict.get("RED_CARD")
      elif suit == "C" or suit == "S":
      color = colors_dict.get("BLACK_CARD")

      # place the blank card rect
      card_height = 5
      card_width = 3
      for cell_y in range(player_hand_coords[card][0][0], player_hand_coords[card][0][0] + card_height):
      for cell_x in range(player_hand_coords[card][0][1], player_hand_coords[card][0][1] + card_width):
      stdscr.addstr(cell_y, cell_x, " ", curses.color_pair(color))

      # place the symbols
      # place two suit symbols and a value symbol
      stdscr.addstr(player_hand_coords[card][0][0], player_hand_coords[card][0][1], suit, curses.color_pair(color))
      stdscr.addstr(player_hand_coords[card][1][0], player_hand_coords[card][1][1], value, curses.color_pair(color))
      stdscr.addstr(player_hand_coords[card][2][0], player_hand_coords[card][2][1], suit, curses.color_pair(color))

      # dealer hand
      if dealer_flipped:
      for card in range(len(dealer_hand)):
      # for each card in the hand
      value = dealer_hand[card].value
      suit = dealer_hand[card].suit
      if suit == "H" or suit == "D":
      color = colors_dict.get("RED_CARD")
      elif suit == "C" or suit == "S":
      color = colors_dict.get("BLACK_CARD")

      # place the blank card rect
      card_height = 5
      card_width = 3
      for cell_y in range(dealer_hand_coords[card][0][0], dealer_hand_coords[card][0][0] + card_height):
      for cell_x in range(dealer_hand_coords[card][0][1], dealer_hand_coords[card][0][1] + card_width):
      stdscr.addstr(cell_y, cell_x, " ", curses.color_pair(color))

      # place the symbols
      # place two suit symbols and a value symbol
      stdscr.addstr(dealer_hand_coords[card][0][0], dealer_hand_coords[card][0][1], suit, curses.color_pair(color))
      stdscr.addstr(dealer_hand_coords[card][1][0], dealer_hand_coords[card][1][1], value, curses.color_pair(color))
      stdscr.addstr(dealer_hand_coords[card][2][0], dealer_hand_coords[card][2][1], suit, curses.color_pair(color))

      if not dealer_flipped:
      for card in range(len(dealer_hand)):
      # for each card in the hand
      value = dealer_hand[card].value
      suit = dealer_hand[card].suit
      if suit == "H" or suit == "D":
      color = colors_dict.get("RED_CARD")
      elif suit == "C" or suit == "S":
      color = colors_dict.get("BLACK_CARD")

      # place the blank card rect
      card_height = 5
      card_width = 3
      for cell_y in range(dealer_hand_coords[card][0][0], dealer_hand_coords[card][0][0] + card_height):
      for cell_x in range(dealer_hand_coords[card][0][1], dealer_hand_coords[card][0][1] + card_width):
      stdscr.addstr(cell_y, cell_x, " ", curses.color_pair(color))

      # place the symbols
      # place two suit symbols and a value symbol
      if card != 0:
      stdscr.addstr(dealer_hand_coords[card][0][0], dealer_hand_coords[card][0][1], suit, curses.color_pair(color))
      stdscr.addstr(dealer_hand_coords[card][1][0], dealer_hand_coords[card][1][1], value, curses.color_pair(color))
      stdscr.addstr(dealer_hand_coords[card][2][0], dealer_hand_coords[card][2][1], suit, curses.color_pair(color))
      stdscr.refresh()

      def main(stdscr):
      try:
      # bet amount
      bet = 100
      # starting funds
      player_funds = 1000
      turn_num = 1
      while game_not_over(player_funds, turn_num):
      # while the player has funds left to bet
      # generate a new deck
      deck = generate_deck()
      dealer_hand =
      player_hand =
      # draw two cards for each player
      draw(dealer_hand, 2, deck)
      draw(player_hand, 2, deck)
      # take the player's bet
      player_funds -= bet
      winner = None
      player_hitting = True
      while player_hitting:
      # while the player is deciding to hit or stay:
      # draw the screen with curses
      draw_screen(stdscr, dealer_hand, player_hand, turn_num, player_funds)
      if player_hits(player_hand, stdscr):
      # if the player chooses to hit:
      # draw a card
      draw(player_hand, 1, deck)
      if is_busted(player_hand):
      # If the player busts:
      # draw the screen again
      draw_screen(stdscr, dealer_hand, player_hand, turn_num, player_funds)
      # prompt that the player has busted
      prompt("Player Busted!", stdscr)
      player_hitting = False
      winner = "dealer"
      else:
      # end the loop if the player chooses to stay
      player_hitting = False
      if not is_busted(player_hand):
      # If the player has stayed and the player has not busted:
      dealer_hitting = True
      while dealer_hitting:
      # while the dealer is choosing to hit or stay:
      # draw the screen with curses
      draw_screen(stdscr, dealer_hand, player_hand, turn_num, player_funds, dealer_flipped=True)
      if dealer_hits(dealer_hand):
      # If the dealer chooses to hit:
      # dealer draws a card
      draw(dealer_hand, 1, deck)
      if is_busted(dealer_hand):
      # If the dealer busts:
      # draw the screen with curses
      draw_screen(stdscr, dealer_hand, player_hand, turn_num, player_funds, dealer_flipped=True)
      # prompt that the dealer has busted
      prompt("Dealer Busted!", stdscr)
      dealer_hitting = False
      winner = "player"
      # reward the player with double their bet
      player_funds += bet * 2
      else:
      # if the dealer busts, break the loop
      dealer_hitting = False
      if not is_busted(dealer_hand):
      if not is_busted(player_hand):
      # If neither player has busted and both have stayed:
      # draw the screen with curses
      draw_screen(stdscr, dealer_hand, player_hand, turn_num, player_funds, dealer_flipped=True)
      # get the winning hand
      winner = compare_hands(dealer_hand, player_hand)
      # prompt the winner
      prompt(str(winner + " Wins!"), stdscr)
      if winner == "player":
      # if the player wins, reward them with double their bet
      player_funds += bet * 2
      # increase turn num
      turn_num += 1
      finally:
      # end curses window on error or exit
      curses.endwin()


      if __name__ == "__main__":
      main(stdscr)


      And here is a link to the GitHub page, for those who want to contribute more directly.



      As ever, anyone who finds this useful is more than welcome to do whatever they want with it. I worked hard on it, but it was just a practice project. If it helps someone else, by all means go for it.







      python python-3.x playing-cards curses






      share|improve this question















      share|improve this question













      share|improve this question




      share|improve this question








      edited 9 mins ago









      Jamal

      30.3k11116226




      30.3k11116226










      asked 4 hours ago









      some_guy632some_guy632

      1086




      1086






















          1 Answer
          1






          active

          oldest

          votes


















          0












          $begingroup$


          • I are not implementing standard (aka Las Vegas) rules. According to the standard rules, a player's hand of an Ace and a honeur forms a blackjack, and must be disclosed immediately. I am not even talking about the split and insurance rules.


          • You don't give the player an opportunity to wrap up, collect her fortune and go home. The game continues until she is stripped off completely.



          • I am afraid there are more monsters than you are aware of. To begin with, I don't see a clean MVC separation. A litmus test is to evaluate efforts required to port this code from curses to, say, Tk.



            Besides, draw_screen has no business to count hands' values, or to be concerned with the dealers' open card. This information shall be computed by the model, and passed to view in an appropriate form.




          • count_hand is suspiciously similar to is_busted. I expect



            def is_busted(....):
            return count_hand(....) > 21



          • count_hand is also overcomplicated. Consider



            def count_hand(hand):
            aces = 0
            hand_sum = 0
            for card in hand:
            if card.is_ace():
            aces += 1 # Initially count all aces as 1
            hand_sum += card.value
            # Now try to assign come aces an extra 10 points
            while aces > 0 and hand_sum <= 11:
            aces -= 1
            hand_sum += 10
            return hand_sum


            Notice how having card itself an instance of the class helps.








          share|improve this answer









          $endgroup$













            Your Answer





            StackExchange.ifUsing("editor", function () {
            return StackExchange.using("mathjaxEditing", function () {
            StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix) {
            StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
            });
            });
            }, "mathjax-editing");

            StackExchange.ifUsing("editor", function () {
            StackExchange.using("externalEditor", function () {
            StackExchange.using("snippets", function () {
            StackExchange.snippets.init();
            });
            });
            }, "code-snippets");

            StackExchange.ready(function() {
            var channelOptions = {
            tags: "".split(" "),
            id: "196"
            };
            initTagRenderer("".split(" "), "".split(" "), channelOptions);

            StackExchange.using("externalEditor", function() {
            // Have to fire editor after snippets, if snippets enabled
            if (StackExchange.settings.snippets.snippetsEnabled) {
            StackExchange.using("snippets", function() {
            createEditor();
            });
            }
            else {
            createEditor();
            }
            });

            function createEditor() {
            StackExchange.prepareEditor({
            heartbeatType: 'answer',
            autoActivateHeartbeat: false,
            convertImagesToLinks: false,
            noModals: true,
            showLowRepImageUploadWarning: true,
            reputationToPostImages: null,
            bindNavPrevention: true,
            postfix: "",
            imageUploader: {
            brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
            contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
            allowUrls: true
            },
            onDemand: true,
            discardSelector: ".discard-answer"
            ,immediatelyShowMarkdownHelp:true
            });


            }
            });














            draft saved

            draft discarded


















            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f212249%2fblackjack-game-in-python-3-curses%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown

























            1 Answer
            1






            active

            oldest

            votes








            1 Answer
            1






            active

            oldest

            votes









            active

            oldest

            votes






            active

            oldest

            votes









            0












            $begingroup$


            • I are not implementing standard (aka Las Vegas) rules. According to the standard rules, a player's hand of an Ace and a honeur forms a blackjack, and must be disclosed immediately. I am not even talking about the split and insurance rules.


            • You don't give the player an opportunity to wrap up, collect her fortune and go home. The game continues until she is stripped off completely.



            • I am afraid there are more monsters than you are aware of. To begin with, I don't see a clean MVC separation. A litmus test is to evaluate efforts required to port this code from curses to, say, Tk.



              Besides, draw_screen has no business to count hands' values, or to be concerned with the dealers' open card. This information shall be computed by the model, and passed to view in an appropriate form.




            • count_hand is suspiciously similar to is_busted. I expect



              def is_busted(....):
              return count_hand(....) > 21



            • count_hand is also overcomplicated. Consider



              def count_hand(hand):
              aces = 0
              hand_sum = 0
              for card in hand:
              if card.is_ace():
              aces += 1 # Initially count all aces as 1
              hand_sum += card.value
              # Now try to assign come aces an extra 10 points
              while aces > 0 and hand_sum <= 11:
              aces -= 1
              hand_sum += 10
              return hand_sum


              Notice how having card itself an instance of the class helps.








            share|improve this answer









            $endgroup$


















              0












              $begingroup$


              • I are not implementing standard (aka Las Vegas) rules. According to the standard rules, a player's hand of an Ace and a honeur forms a blackjack, and must be disclosed immediately. I am not even talking about the split and insurance rules.


              • You don't give the player an opportunity to wrap up, collect her fortune and go home. The game continues until she is stripped off completely.



              • I am afraid there are more monsters than you are aware of. To begin with, I don't see a clean MVC separation. A litmus test is to evaluate efforts required to port this code from curses to, say, Tk.



                Besides, draw_screen has no business to count hands' values, or to be concerned with the dealers' open card. This information shall be computed by the model, and passed to view in an appropriate form.




              • count_hand is suspiciously similar to is_busted. I expect



                def is_busted(....):
                return count_hand(....) > 21



              • count_hand is also overcomplicated. Consider



                def count_hand(hand):
                aces = 0
                hand_sum = 0
                for card in hand:
                if card.is_ace():
                aces += 1 # Initially count all aces as 1
                hand_sum += card.value
                # Now try to assign come aces an extra 10 points
                while aces > 0 and hand_sum <= 11:
                aces -= 1
                hand_sum += 10
                return hand_sum


                Notice how having card itself an instance of the class helps.








              share|improve this answer









              $endgroup$
















                0












                0








                0





                $begingroup$


                • I are not implementing standard (aka Las Vegas) rules. According to the standard rules, a player's hand of an Ace and a honeur forms a blackjack, and must be disclosed immediately. I am not even talking about the split and insurance rules.


                • You don't give the player an opportunity to wrap up, collect her fortune and go home. The game continues until she is stripped off completely.



                • I am afraid there are more monsters than you are aware of. To begin with, I don't see a clean MVC separation. A litmus test is to evaluate efforts required to port this code from curses to, say, Tk.



                  Besides, draw_screen has no business to count hands' values, or to be concerned with the dealers' open card. This information shall be computed by the model, and passed to view in an appropriate form.




                • count_hand is suspiciously similar to is_busted. I expect



                  def is_busted(....):
                  return count_hand(....) > 21



                • count_hand is also overcomplicated. Consider



                  def count_hand(hand):
                  aces = 0
                  hand_sum = 0
                  for card in hand:
                  if card.is_ace():
                  aces += 1 # Initially count all aces as 1
                  hand_sum += card.value
                  # Now try to assign come aces an extra 10 points
                  while aces > 0 and hand_sum <= 11:
                  aces -= 1
                  hand_sum += 10
                  return hand_sum


                  Notice how having card itself an instance of the class helps.








                share|improve this answer









                $endgroup$




                • I are not implementing standard (aka Las Vegas) rules. According to the standard rules, a player's hand of an Ace and a honeur forms a blackjack, and must be disclosed immediately. I am not even talking about the split and insurance rules.


                • You don't give the player an opportunity to wrap up, collect her fortune and go home. The game continues until she is stripped off completely.



                • I am afraid there are more monsters than you are aware of. To begin with, I don't see a clean MVC separation. A litmus test is to evaluate efforts required to port this code from curses to, say, Tk.



                  Besides, draw_screen has no business to count hands' values, or to be concerned with the dealers' open card. This information shall be computed by the model, and passed to view in an appropriate form.




                • count_hand is suspiciously similar to is_busted. I expect



                  def is_busted(....):
                  return count_hand(....) > 21



                • count_hand is also overcomplicated. Consider



                  def count_hand(hand):
                  aces = 0
                  hand_sum = 0
                  for card in hand:
                  if card.is_ace():
                  aces += 1 # Initially count all aces as 1
                  hand_sum += card.value
                  # Now try to assign come aces an extra 10 points
                  while aces > 0 and hand_sum <= 11:
                  aces -= 1
                  hand_sum += 10
                  return hand_sum


                  Notice how having card itself an instance of the class helps.









                share|improve this answer












                share|improve this answer



                share|improve this answer










                answered 26 mins ago









                vnpvnp

                39k13099




                39k13099






























                    draft saved

                    draft discarded




















































                    Thanks for contributing an answer to Code Review Stack Exchange!


                    • Please be sure to answer the question. Provide details and share your research!

                    But avoid



                    • Asking for help, clarification, or responding to other answers.

                    • Making statements based on opinion; back them up with references or personal experience.


                    Use MathJax to format equations. MathJax reference.


                    To learn more, see our tips on writing great answers.




                    draft saved


                    draft discarded














                    StackExchange.ready(
                    function () {
                    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f212249%2fblackjack-game-in-python-3-curses%23new-answer', 'question_page');
                    }
                    );

                    Post as a guest















                    Required, but never shown





















































                    Required, but never shown














                    Required, but never shown












                    Required, but never shown







                    Required, but never shown

































                    Required, but never shown














                    Required, but never shown












                    Required, but never shown







                    Required, but never shown







                    Popular posts from this blog

                    How to reconfigure Docker Trusted Registry 2.x.x to use CEPH FS mount instead of NFS and other traditional...

                    is 'sed' thread safe

                    How to make a Squid Proxy server?