mirror of
https://github.com/SpudGunMan/meshing-around.git
synced 2026-03-28 17:32:36 +01:00
426 lines
16 KiB
Python
426 lines
16 KiB
Python
# Port of https://github.com/devtronvarma/Video-Poker-Terminal-Game
|
||
# Adapted for Meshtastic mesh-bot by K7MHI Kelly Keeton 2024
|
||
import random
|
||
import time
|
||
from modules.log import *
|
||
|
||
vpStartingCash = 20
|
||
vpTracker= [{'nodeID': 0, 'cmd': 'new', 'time': time.time(), 'cash': vpStartingCash, 'player': None, 'deck': None, 'highScore': 0, 'drawCount': 0}]
|
||
|
||
# Define the Card class
|
||
class CardVP:
|
||
|
||
card_values = { # value of the ace is high until it needs to be low
|
||
2: 2,
|
||
3: 3,
|
||
4: 4,
|
||
5: 5,
|
||
6: 6,
|
||
7: 7,
|
||
8: 8,
|
||
9: 9,
|
||
10: 10,
|
||
'Jack': 11,
|
||
'Queen': 12,
|
||
'King': 13,
|
||
'Ace': 14
|
||
}
|
||
|
||
def __init__(self, suit, rank):
|
||
"""
|
||
:param suit: The face of the card, e.g. Spade or Diamond
|
||
:param rank: The value of the card, e.g 3 or King
|
||
"""
|
||
self.suit = suit.capitalize()
|
||
self.rank = rank
|
||
self.points = self.card_values[rank]
|
||
|
||
# Function to output ascii version of the cards in a hand in the terminal
|
||
def drawCardsVp(*cards, return_string=True):
|
||
"""
|
||
Instead of a boring text version of the card we render an ASCII image of the card.
|
||
:param cards: One or more card objects
|
||
:param return_string: By default we return the string version of the card, but the dealer hide the 1st card and we
|
||
keep it as a list so that the dealer can add a hidden card in front of the list
|
||
"""
|
||
# we will use this to prints the appropriate icons for each card
|
||
suits_name = ['Spades', 'Diamonds', 'Hearts', 'Clubs']
|
||
suits_symbols = ['♠️', '♦️', '♥️', '♣️']
|
||
|
||
# create an empty list of list, each sublist is a line 2 lines for the card
|
||
lines = [[] for i in range(1)]
|
||
|
||
for index, card in enumerate(cards):
|
||
# "King" should be "K" and "10" should still be "10"
|
||
if card.rank == 10: # ten is the only one who's rank is 2 char long
|
||
rank = str(card.rank)
|
||
space = '' # if we write "10" on the card that line will be 1 char to long
|
||
else:
|
||
rank = str(card.rank)[0] # some have a rank of 'King' this changes that to a simple 'K' ("King" doesn't fit)
|
||
space = ' ' # no "10", we use a blank space to will the void
|
||
# get the cards suit in two steps
|
||
suit = suits_name.index(card.suit)
|
||
suit = suits_symbols[suit]
|
||
|
||
# add the individual card on a line by line basis
|
||
lines[0].append('{}{} '.format(rank, suit))
|
||
|
||
result = []
|
||
#result.append('1 2 3 4 5') # add the index for the cards to top row
|
||
for index, line in enumerate(lines):
|
||
result.append(''.join(lines[index]))
|
||
|
||
# hidden cards do not use string
|
||
if return_string:
|
||
return '\n'.join(result)
|
||
else:
|
||
return result
|
||
|
||
# Define Deck class
|
||
class DeckVP:
|
||
|
||
def __init__(self):
|
||
self.cards = []
|
||
self.build()
|
||
|
||
# method for building the deck
|
||
def build(self):
|
||
for s in ['Spades', 'Diamonds', 'Hearts', 'Clubs']:
|
||
for v in range(2, 11):
|
||
self.cards.append(CardVP(s,v))
|
||
for c in ["Jack", "Queen", "King", "Ace"]:
|
||
self.cards.append(CardVP(s,c))
|
||
|
||
# method to show cards in deck
|
||
def display(self):
|
||
for c in self.cards:
|
||
print(drawCardsVp(c))
|
||
|
||
# method to shuffle cards in deck
|
||
def shuffle(self):
|
||
for i in range(len(self.cards) - 1, 0, -1):
|
||
r = random.randint(0, i)
|
||
self.cards[i], self.cards[r] = self.cards[r], self.cards[i]
|
||
|
||
# method to draw card from the deck
|
||
def draw_card(self):
|
||
return self.cards.pop()
|
||
|
||
# Define Player Class
|
||
class PlayerVP:
|
||
def __init__(self):
|
||
self.hand = []
|
||
self.bankroll = 20
|
||
|
||
# Method for initial five-card draw
|
||
def draw_cards(self, deck):
|
||
for i in range(5):
|
||
self.hand.append(deck.draw_card())
|
||
return self
|
||
|
||
# Method for displaying player's hand
|
||
def show_hand(self):
|
||
msg = (drawCardsVp(
|
||
self.hand[0],
|
||
self.hand[1],
|
||
self.hand[2],
|
||
self.hand[3],
|
||
self.hand[4]))
|
||
return msg
|
||
|
||
# Method for placing a bet
|
||
def bet(self, ammount=0):
|
||
bet = int(ammount)
|
||
self.bet_size = bet
|
||
self.bankroll -= self.bet_size
|
||
|
||
# Method for selecting cards to redraw
|
||
def redraw(self, deck, message):
|
||
# if message has single digit, then it is the card to redraw, else it is the list of cards to redraw with a comma
|
||
if len(message) == 1:
|
||
try:
|
||
# if single digit is the letter a redraw all cards
|
||
if message.lower() == "a":
|
||
for i in range(5):
|
||
self.hand[i] = deck.draw_card()
|
||
else:
|
||
# error trap for bad input
|
||
redraw_index = int(message) - 1
|
||
self.hand[redraw_index] = deck.draw_card()
|
||
|
||
return self.show_hand()
|
||
except Exception as e:
|
||
pass
|
||
else:
|
||
try:
|
||
# error trap for bad input
|
||
if "," in message:
|
||
redraw_list = [int(x) - 1 for x in message.split(',')]
|
||
if "." in message:
|
||
redraw_list = [int(x) - 1 for x in message.split('.')]
|
||
if " " in message:
|
||
redraw_list = [int(x) - 1 for x in message.split(' ')]
|
||
for i in redraw_list:
|
||
self.hand[i] = deck.draw_card()
|
||
return self.show_hand()
|
||
except Exception as e:
|
||
pass
|
||
|
||
return "Re-Draw/Deal ex:1,3,4 to hold cards 1,3 and 4, or (N)o to keep current (H)and"
|
||
|
||
# Method for scoring hand, calculating winnings, and outputting message
|
||
def score_hand(self, resetHand = True):
|
||
points = sorted([self.hand[i].points for i in range(5)])
|
||
suits = [self.hand[i].suit for i in range(5)]
|
||
points_repeat = [points.count(i) for i in points]
|
||
suits_repeat = [suits.count(i) for i in suits]
|
||
diff = max(points) - min(points)
|
||
hand_name = ""
|
||
msg = ""
|
||
payoff = {
|
||
"👑Royal Flush🚽": 10,
|
||
"🧻Straight Flush🚽": 9,
|
||
"Flush🚽": 8,
|
||
"Full House🏠": 7,
|
||
"Four of a Kind👯👯": 6,
|
||
"Three of a Kind☘️": 5,
|
||
"Two Pair👯👯": 4,
|
||
"Straight📏": 3,
|
||
"Pair👯": 2,
|
||
"Bad Hand 🙈": -1,
|
||
}
|
||
|
||
if 5 in suits_repeat:
|
||
if points == [10, 11, 12, 13, 14]: #find royal flush
|
||
hand_name = "👑Royal Flush🚽"
|
||
if resetHand:
|
||
self.bankroll += self.bet_size * payoff[hand_name]
|
||
elif diff == 4 and max(points_repeat) == 1: # find straight flush w/o ace low
|
||
hand_name = "🧻Straight Flush🚽"
|
||
if resetHand:
|
||
self.bankroll += self.bet_size * payoff[hand_name]
|
||
elif diff == 12 and points[4] == 14: # find straight flush w/ace low
|
||
check = 0
|
||
for i in range(1, 4):
|
||
check += points[i] - points[i - 1]
|
||
if check == 3:
|
||
hand_name = "🧻Straight Flush🚽"
|
||
if resetHand:
|
||
self.bankroll += self.bet_size * payoff[hand_name]
|
||
else:
|
||
hand_name = "Flush🚽"
|
||
if resetHand:
|
||
self.bankroll += self.bet_size * payoff[hand_name]
|
||
else:
|
||
hand_name = "Flush🚽"
|
||
if resetHand:
|
||
self.bankroll += self.bet_size * payoff[hand_name]
|
||
elif sorted(points_repeat) == [2,2,3,3,3]: # find full house
|
||
hand_name = "Full House🏠"
|
||
if resetHand:
|
||
self.bankroll += self.bet_size * payoff[hand_name]
|
||
elif 4 in points_repeat: # find four of a kind
|
||
hand_name = "Four of a Kind👯👯"
|
||
if resetHand:
|
||
self.bankroll += self.bet_size * payoff[hand_name]
|
||
elif 3 in points_repeat: # find three of a kind
|
||
hand_name = "Three of a Kind☘️"
|
||
if resetHand:
|
||
self.bankroll += self.bet_size * payoff[hand_name]
|
||
elif points_repeat.count(2) == 4: # find two-pair
|
||
hand_name = "Two Pair👯👯"
|
||
if resetHand:
|
||
self.bankroll += self.bet_size * payoff[hand_name]
|
||
elif 2 in points_repeat: # find pair
|
||
logger.debug(f"System: VideoPoker: 235 self.bankroll: {self.bankroll} bet_size: {self.bet_size}")
|
||
hand_name = "Pair👯"
|
||
if resetHand:
|
||
self.bankroll += self.bet_size * payoff[hand_name]
|
||
elif diff == 4 and max(points_repeat) == 1: # find straight w/o ace low
|
||
hand_name = "Straight📏"
|
||
if resetHand:
|
||
self.bankroll += self.bet_size * payoff[hand_name]
|
||
elif diff == 12 and points[4] == 14: # find straight w/ace low
|
||
check = 0
|
||
for i in range(1, 4):
|
||
check += points[i] - points[i - 1]
|
||
if check == 3:
|
||
hand_name = "Straight📏"
|
||
if resetHand:
|
||
self.bankroll += self.bet_size * payoff[hand_name]
|
||
else:
|
||
hand_name = "Bad Hand 🙈"
|
||
else: # for everything Hand
|
||
hand_name = "Bad Hand 🙈"
|
||
|
||
if resetHand:
|
||
self.hand = []
|
||
msg = f"\nYour hand, {hand_name}. Your bankroll is now {self.bankroll} coins."
|
||
else:
|
||
if hand_name != "":
|
||
msg = f"\nShowing:{hand_name}"
|
||
return msg
|
||
|
||
|
||
def getLastCmdVp(nodeID):
|
||
last_cmd = ""
|
||
for i in range(len(vpTracker)):
|
||
if vpTracker[i]['nodeID'] == nodeID:
|
||
last_cmd = vpTracker[i]['cmd']
|
||
return last_cmd
|
||
|
||
def setLastCmdVp(nodeID, cmd):
|
||
for i in range(len(vpTracker)):
|
||
if vpTracker[i]['nodeID'] == nodeID:
|
||
vpTracker[i]['cmd'] = cmd
|
||
|
||
def playVideoPoker(nodeID, message):
|
||
msg = ""
|
||
|
||
# Initialize the player
|
||
if getLastCmdVp(nodeID) is None or getLastCmdVp(nodeID=nodeID) == "":
|
||
# create new player if not in tracker
|
||
logger.debug(f"System: VideoPoker: New Player {nodeID}")
|
||
vpTracker.append({'nodeID': nodeID, 'cmd': 'new', 'time': time.time(), 'cash': vpStartingCash, 'player': None, 'deck': None, 'highScore': 0, 'drawCount': 0})
|
||
return f"Welcome to 🎰 VideoPoker! you have {vpStartingCash} coins, Whats your bet?"
|
||
|
||
# Gather the player's bet
|
||
if getLastCmdVp(nodeID) == "new" or getLastCmdVp(nodeID) == "gameOver":
|
||
# Initialize shuffled Deck and Player
|
||
player = PlayerVP()
|
||
deck = DeckVP()
|
||
deck.shuffle()
|
||
drawCount = 1
|
||
bet = 0
|
||
msg = ''
|
||
|
||
# load the player bankroll from tracker
|
||
for i in range(len(vpTracker)):
|
||
if vpTracker[i]['nodeID'] == nodeID:
|
||
player.bankroll = vpTracker[i]['cash']
|
||
vpTracker[i]['time'] = time.time()
|
||
|
||
# Detect if message is a bet
|
||
try:
|
||
bet = int(message)
|
||
except ValueError:
|
||
msg += "Please enter a valid bet amount. 1 to 5 coins."
|
||
|
||
# Check if bet is valid
|
||
if bet > player.bankroll:
|
||
msg += "You can only bet the money you have. No strip poker here..."
|
||
elif bet < 1:
|
||
msg += "You must bet at least 1 coin."
|
||
elif bet > 5:
|
||
msg += "You can only bet up to 5 coins."
|
||
|
||
# if msg contains an error, return it
|
||
if msg is not None and msg != '':
|
||
return msg
|
||
else:
|
||
# Take the bet
|
||
player.bet(str(message))
|
||
# Bet placed, start the game
|
||
setLastCmdVp(nodeID, "playing")
|
||
|
||
# save player and deck to tracker
|
||
for i in range(len(vpTracker)):
|
||
if vpTracker[i]['nodeID'] == nodeID:
|
||
vpTracker[i]['player'] = player
|
||
vpTracker[i]['deck'] = deck
|
||
vpTracker[i]['cash'] = player.bankroll
|
||
|
||
# Play the game
|
||
if getLastCmdVp(nodeID) == "playing":
|
||
msg = ''
|
||
|
||
player.draw_cards(deck)
|
||
msg += player.show_hand()
|
||
# give hint to player
|
||
msg += player.score_hand(resetHand=False)
|
||
|
||
# save player and deck to tracker
|
||
for i in range(len(vpTracker)):
|
||
if vpTracker[i]['nodeID'] == nodeID:
|
||
vpTracker[i]['player'] = player
|
||
vpTracker[i]['deck'] = deck
|
||
vpTracker[i]['drawCount'] = drawCount
|
||
|
||
msg += f"\nDeal new card? \nex: 1,3,4 or (N)o,(A)ll"
|
||
setLastCmdVp(nodeID, "redraw")
|
||
return msg
|
||
|
||
if getLastCmdVp(nodeID) == "redraw":
|
||
msg = ''
|
||
# load the player and deck from tracker
|
||
for i in range(len(vpTracker)):
|
||
if vpTracker[i]['nodeID'] == nodeID:
|
||
player = vpTracker[i]['player']
|
||
deck = vpTracker[i]['deck']
|
||
drawCount = vpTracker[i]['drawCount']
|
||
|
||
# if player wants to redraw cards, and not done already
|
||
if message.lower().startswith("n"):
|
||
setLastCmdVp(nodeID, "endGame")
|
||
if message.lower().startswith("h"):
|
||
msg = player.show_hand()
|
||
return msg
|
||
else:
|
||
if drawCount <= 1:
|
||
msg = player.redraw(deck, message)
|
||
if msg.startswith("Send Card"):
|
||
# if returned error message, return it
|
||
return msg
|
||
drawCount += 1
|
||
# save player and deck to tracker
|
||
for i in range(len(vpTracker)):
|
||
if vpTracker[i]['nodeID'] == nodeID:
|
||
vpTracker[i]['player'] = player
|
||
vpTracker[i]['deck'] = deck
|
||
vpTracker[i]['drawCount'] = drawCount
|
||
if drawCount == 2:
|
||
# this is the last draw will carry on to endGame for scoring
|
||
msg = player.redraw(deck, message) + f"\n"
|
||
if msg.startswith("Send Card"):
|
||
# if returned error message, return it
|
||
return msg
|
||
# redraw done
|
||
setLastCmdVp(nodeID, "endGame")
|
||
else:
|
||
# show redrawn hand
|
||
return msg
|
||
else:
|
||
# redraw already done
|
||
setLastCmdVp(nodeID, "endGame")
|
||
|
||
if getLastCmdVp(nodeID) == "endGame":
|
||
# load the player and deck from tracker
|
||
for i in range(len(vpTracker)):
|
||
if vpTracker[i]['nodeID'] == nodeID:
|
||
player = vpTracker[i]['player']
|
||
deck = vpTracker[i]['deck']
|
||
|
||
msg += player.score_hand()
|
||
|
||
if player.bankroll < 1:
|
||
player.bankroll = vpStartingCash
|
||
msg += "\nLooks 💸 like you're out of money. 💳 resetting ballance 🏧"
|
||
elif player.bankroll > vpTracker[i]['highScore']:
|
||
vpTracker[i]['highScore'] = player.bankroll
|
||
msg += " 🎉HighScore!"
|
||
|
||
msg += f"\nPlace your Bet, 'L' to leave the game."
|
||
|
||
setLastCmdVp(nodeID, "gameOver")
|
||
# reset player and deck in tracker
|
||
for i in range(len(vpTracker)):
|
||
if vpTracker[i]['nodeID'] == nodeID:
|
||
vpTracker[i]['player'] = None
|
||
vpTracker[i]['deck'] = None
|
||
vpTracker[i]['drawCount'] = 0
|
||
# save bankroll
|
||
vpTracker[i]['cash'] = player.bankroll
|
||
|
||
return msg
|
||
|
||
|