# Copyright (C) 2002 mithrandi # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # mithrandi """blackjack - Blackjack bot for IIP, using Yodels""" from __future__ import generators import irclib import yodel import re import stats irclib.DEBUG = open('blackjack.log', 'w', 1) class Blackjack: maxBet = 0.20 yodelUsername = 'CENSORED' yodelPassword = 'CENSORED' shutdown_trigger = 'CENSORED' def __init__(self, nick, password, channel='#yodeljack', host='localhost', port=6667): self.s = {} self.shuttingDown = False self.stats = stats.BlackjackStats('stats.p') self.nick = nick self.yodel = yodel.Yodel(nick, password, host, port) self.irc = self.yodel.irc self.server = self.yodel.server self.irc.add_global_handler('privmsg', self.onMessage) self.irc.add_global_handler('join', self.onJoin) # self.irc.add_global_handler('error', self.onError) self.server.join(channel) def saveTerminate(self): raise SystemExit, "shutdown" # def onError(self, server, event): # raise SystemExit, "shutdown" def onMessage(self, server, event): text = event.arguments()[0] nick = irclib.nm_to_n(event.source()) if text == Blackjack.shutdown_trigger: self.shutdown() elif text[:4] == 'help': self.displayHelp(server, nick, text[5:]) elif text == 'stats': self.displayStats(server, nick) else: game = self.s.get(nick) if game is None: if self.shuttingDown: server.privmsg(nick, 'Sorry, yodeljack is shutting down. Please try again later.') return else: game = self.startGame(server, nick) game.processCommand(text) self.purgeGames() def displayStats(self, server, nick): server.privmsg(nick, self.stats.getStats()) def displayHelp(self, server, nick, text): try: assert text.find('/') == -1 if text == '': text = '_help' helpFile = open('help/' + text, 'r') for line in helpFile.xreadlines(): server.privmsg(nick, line) except: server.privmsg(nick, 'Unknown help topic.') def onJoin(self, server, event): nick = irclib.nm_to_n(event.source()) channel = event.target() if nick != self.nick and nick not in self.s.keys() and channel != '#yodelbots': server.privmsg(nick, 'Hello :) To start playing, /msg me with "help bet".') def startGame(self, server, nick): game = BlackjackGame(self.yodel, server, nick, self.stats) self.s[nick] = game return game def run_once(self, timeout=0): self.yodel.run_once(timeout) def run_forever(self, timeout=0.2): self.yodel.run_forever(timeout) def shutdown(self): self.shuttingDown = True def purgeGames(self): nicks = [] for (nick, game) in self.s.iteritems(): if not game.active: nicks.append(nick) for nick in nicks: del self.s[nick] if self.shuttingDown and len(self.s) == 0: self.yodel.quit() class CommandProc: def start(self): self.run() class BlackjackGame: commands = ('bet', 'hit', 'stand', 'double', 'split') def __init__(self, yodel, server, nick, stats): self.stats = stats self.yodel = yodel self.server = server self.nick = nick self.bet = [0, 0] self.active = False def processCommand(self, text): command = text.split(' ') cmd = command[0] arguments = command[1:] if cmd not in BlackjackGame.commands: self.msg('Invalid command.') else: getattr(self, 'cmd_' + cmd)(arguments) def deal(self): self.deck = Deck() self.dealerHand = BlackjackHand() self.playerHand = [BlackjackHand()] self.playerHand[0].deal(self.deck) self.dealerHand.deal(self.deck) self.playerHand[0].deal(self.deck) self.dealerHand.deal(self.deck) self.split = 0 def doSplit(self): self.playerHand.append(BlackjackHand()) self.playerHand[1].cards.append(self.playerHand[0].cards.pop()) self.bet[1] = float(self.bet[0]) self.playerHand[0].deal(self.deck) self.playerHand[1].deal(self.deck) def msg(self, message): self.server.privmsg(self.nick, message) def showPlayerHand(self): for hand in xrange(len(self.playerHand)): self.msg("Your hand %s: %s" % (hand == self.split and '(*)' or ' ', self.playerHand[hand])) def showHands(self): self.msg("Dealer's hand: ?? %s" % self.dealerHand.cards[1]) self.showPlayerHand() def showHandsFull(self): self.msg("Dealer's hand: %s" % self.dealerHand) self.showPlayerHand() def dealerPlay(self): while self.dealerHand.value() < 17: self.dealerHand.deal(self.deck) def isPayout(self, hand): if self.playerHand[hand].value() > 21: self.msg('Hand %d busted.' % (hand + 1)) return 0 elif self.dealerHand.value() > 21: if hand == 0: self.msg('Dealer busted.') return 2 elif self.playerHand[hand].value() == self.dealerHand.value(): self.msg('Hand %d tied.' % (hand + 1)) return 1 elif self.playerHand[hand].value() > self.dealerHand.value(): self.msg('Hand %d wins.' % (hand + 1)) return 2 else: self.msg('Hand %d loses.' % (hand + 1)) return 0 def doPayout(self, amount): self.stats.onPayout(amount) if amount > 0.00: event = self.yodel.issue(Blackjack.yodelUsername, Blackjack.yodelPassword, amount) if event.status == 'error': self.msg('PAYOUT ERROR! Please report this to mithrandi!') self.msg('Error: %s' % event.error) else: self.stats.onBalance(event.balance) self.msg('Payout: %s' % event.cert) self.msg('You may place another bet.') self.active = False def payout(self): payoutAmt = 0.00 for hand in xrange(len(self.playerHand)): payout = self.isPayout(hand) payoutAmt += self.bet[hand] * payout self.doPayout(payoutAmt) def stand(self): if self.split < len(self.playerHand) - 1: self.split += 1 self.showHandsFull() return False else: self.dealerPlay() self.showHandsFull() return True def hit(self): self.playerHand[self.split].deal(self.deck) self.showPlayerHand() if self.playerHand[self.split].value() > 21: self.msg('Busted.') if self.split < len(self.playerHand) - 1: self.split += 1 self.showPlayerHand() return True else: self.dealerPlay() self.showHandsFull() return False else: return True def cmd_bet(self, arguments): if len(arguments) != 1: self.msg('Invalid syntax.') return if self.active: self.msg('You are already playing a hand.') self.showHands() return try: cert = yodel.YodelCert(arguments[0]) except yodel.InvalidYodelCert: self.msg('Error: invalid cert.') self.active = False return if cert.amount > Blackjack.maxBet: self.msg('Maximum bet is %.2f.' % Blackjack.maxBet) return self.active = True event = self.yodel.deposit(Blackjack.yodelUsername, Blackjack.yodelPassword, cert.cert) if event.status == 'error': self.msg('Error: %s' % event.error) self.active = False return self.stats.onBalance(event.balance) self.bet[0] = event.amount self.deal() self.msg('Successfully placed %.2f bet.' % self.bet[0]) self.stats.onBet() self.stats.onWager(self.bet[0]) self.showHands() if self.dealerHand.blackjack() and self.playerHand[0].blackjack(): self.msg('You both have blackjack.') self.doPayout(self.bet[0]) elif self.dealerHand.blackjack(): self.msg('Dealer has blackjack, you lose!') self.msg('You may place another bet.') self.active = 0 elif self.playerHand[0].blackjack(): self.msg('You have blackjack!') self.doPayout(self.bet[0] * 3) def cmd_hit(self, arguments): if not self.active: self.msg('You must place a bet first.') return continueGame = self.hit() if not continueGame: self.payout() def cmd_stand(self, argument): if not self.active: self.msg('You must place a bet first.') return self.msg('You stand.') if self.stand(): self.payout() def cmd_double(self, arguments): if not self.active: self.msg('You must place a bet first.') return if len(arguments) != 1: self.msg('Invalid syntax.') return if not self.playerHand[self.split].canDouble(): self.msg('You can only double on two cards with a total of 9, 10, or 11 and no aces.') return try: cert = yodel.YodelCert(arguments[0]) except yodel.InvalidYodelCert: self.msg('Error: Invalid cert.') return if cert.amount != self.bet[self.split]: self.msg('Amount incorrect. You must pass a cert for %.2f in order to double.' % self.bet[self.split]) return event = self.yodel.deposit(Blackjack.yodelUsername, Blackjack.yodelPassword, cert.cert) if event.status == 'error': self.msg('Error: %s' % event.error) return self.stats.onBalance(event.balance) self.stats.onWager(self.bet[self.split]) self.bet[self.split] *= 2 self.msg('You double down.') if self.stand(): self.payout() def cmd_split(self, arguments): if not self.active: self.msg('You must place a bet first.') return if len(arguments) != 1: self.msg('Invalid syntax.') return if len(self.playerHand) > 1 or not self.playerHand[self.split].canSplit(): self.msg('You cannot split this hand.') return try: cert = yodel.YodelCert(arguments[0]) except yodel.InvalidYodelCert: self.msg('Error: Invalid cert.') return if cert.amount != self.bet[self.split]: self.msg('Amount incorrect. You must pass a cert for %.2f in order to split.' % self.bet[self.split]) return event = self.yodel.deposit(Blackjack.yodelUsername, Blackjack.yodelPassword, cert.cert) if event.status == 'error': self.msg('Error: %s' % event.error) return self.stats.onBalance(event.balance) self.stats.onWager(self.bet[self.split]) self.doSplit() import random class Deck: cards = {'A':1, '2':2, '3':3, '4':4, '5':5, '6':6, '7':7, '8':8, '9':9, 'T':10, 'J':10, 'Q':10, 'K':10} suites = ('H', 'D', 'C', 'S') deck = [] for n in xrange(4): for s in suites: for c in cards.iterkeys(): deck.append(c + s) def __init__(self): self.discard = [] self.deck = list(Deck.deck) self.shuffle() def shuffle(self): self.deck += self.discard self.discard = [] pass def deal(self): l = len(self.deck) - 1 n = random.randint(0, l) self.deck[n], self.deck[l] = self.deck[l], self.deck[n] card = self.deck.pop() self.discard.append(card) return card class BlackjackHand: def __init__(self): self.cards = [] def __str__(self): return '%s (%d)' % (' '.join(self.cards), self.value()) def deal(self, deck): self.cards.append(deck.deal()) def blackjack(self): return len(self.cards) == 2 and self.value() == 21 def canDouble(self): return len(self.cards) == 2 and self.value() in (9, 10, 11) and not self.cards[0][0] == 'A' and not self.cards[1][0] == 'A' def canSplit(self): return len(self.cards) == 2 and self.cards[0][0] == self.cards[1][0] def hardValue(self): total = 0 ace = False for c in self.cards: total += Deck.cards[c[0]] return total def value(self): total = 0 ace = False for c in self.cards: total += Deck.cards[c[0]] if c[0] == 'A': ace = True if ace and total + 10 <= 21: total += 10 return total