Blackjack Game in Python 3/curses
$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.
python python-3.x playing-cards curses
$endgroup$
add a comment |
$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.
python python-3.x playing-cards curses
$endgroup$
add a comment |
$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.
python python-3.x playing-cards curses
$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
python python-3.x playing-cards curses
edited 7 mins ago
Jamal♦
30.3k11116226
30.3k11116226
asked 4 hours ago
some_guy632some_guy632
1086
1086
add a comment |
add a comment |
1 Answer
1
active
oldest
votes
$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 tois_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.
$endgroup$
add a comment |
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
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
$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 tois_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.
$endgroup$
add a comment |
$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 tois_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.
$endgroup$
add a comment |
$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 tois_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.
$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 tois_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.
answered 24 mins ago
vnpvnp
39k13099
39k13099
add a comment |
add a comment |
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.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
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