2019-12-06 05:57:44 -05:00

374 lines
16 KiB
Python

###
# Copyright (c) 2014, KgBot
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice,
# this list of conditions, and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions, and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# * Neither the name of the author of this software nor the name of
# contributors to this software may be used to endorse or promote products
# derived from this software without specific prior written consent.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
###
import supybot.schedule as schedule
import json
import random
import time
import os
import supybot.utils as utils
from supybot.commands import *
import supybot.plugins as plugins
import supybot.ircutils as ircutils
import supybot.callbacks as callbacks
try:
from supybot.i18n import PluginInternationalization
_ = PluginInternationalization('BlackJack')
except:
# Placeholder that allows to run the plugin on a bot
# without the i18n module
_ = lambda x:x
class BlackJack(callbacks.Plugin):
"""Add the help for "@plugin help BlackJack" here
This should describe *how* to use this plugin."""
threaded = True
def __init__(self, irc):
self.__parent = super(BlackJack, self)
self.__parent.__init__(irc)
# Dictionary of players, holds important things about each player.
self.players = {}
# List of scheduled games
self.events = []
self.minStake = 10
self.maxStake = 50
def _isScheduled(self, name):
""" Checks to see if player is already playing game."""
if name in self.events:
return True
else:
return False
def _waitingPlayerAction(self, player):
""" Checks to see if player has got his first 2 cards and now we're waiting for him to hit/stand or double."""
if player in self.players.keys() and self.players[player]["waitingAction"] == True:
return True
else:
return False
def _deal(self, who, player):
_allowed_whos = ["player", "bank"]
if who in _allowed_whos:
cards = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, "J", "Q", "K"]
card = random.choice(cards)
# Because we J/Q/K are not numbers we must convert them to 10's, but this is not nice, and we must implement
# 2 types of cards here, one that will be answered to player and one that will be added to score
if card == "J" or card == "Q" or card == "K":
card = 10
if who == "player":
if card == 1:
# If 11 + score is bigger then 21 1 will be 1
if self.players[player]["score"] + 11 > 21:
card = 1
# If 1 + score is less then 21 1 will be 11
else:
card = 11
self.players[player]["score"] = self.players[player]["score"] + card
# Returns card because we might want to tell the player which card he's got.
return card
else:
if card == 1:
if self.players[player]["bankScore"] + 11 > 21:
card = 1
else:
card = 11
self.players[player]["bankScore"] = self.players[player]["bankScore"] + card
return card
def _finishGame(self, irc, player):
self.players[player]["waitingAction"] = False
playerScore = self.players[player]["score"]
bankScore = self.players[player]["bankScore"]
if playerScore == 21 and bankScore != 21:
self._playerWins(irc, player)
elif playerScore == 21 and bankScore == 21:
irc.reply("This is push, nobody wins.")
self._push(player)
elif bankScore == 21 and playerScore != 21:
self._bankWins(irc, player)
elif playerScore < 21 and playerScore > bankScore:
self._playerWins(irc, player)
elif bankScore < 21 and bankScore > playerScore:
self._bankWins(irc, player)
elif playerScore == bankScore:
irc.reply("This is push, nobody wins.")
self._push(player)
elif playerScore > 21 and bankScore < 21:
self._bankWins(irc, player)
else:
self._playerWins(irc, player)
def _removeScheduledGame(self, name):
try:
schedule.removeEvent(name)
self.events.remove(name)
except:
try:
self.events.remove(name)
except:
pass
def _push(self, player):
stake = self.players[player]["stake"]
chips = Chips()
chips._addChips(player, stake)
game_name = "game_%s" % player
self._removeScheduledGame(game_name)
def _playerWins(self, irc, player, blackjack=False):
game_name = "game_%s" % player
if blackjack:
amount = self._blackjackPrize(self.players[player]["stake"])
else:
amount = self.players[player]["stake"] * 2
chips = Chips()
chips._addChips(player, amount)
irc.reply("Congrats, you have won and you got %s chips." % amount)
self._removeScheduledGame(game_name)
def _bankWins(self, irc, player):
game_name = "game_%s" % player
irc.reply("Sorry but you've lost this time.")
self._removeScheduledGame(game_name)
def _playerChips(self, player):
""" Return number of chips that player has."""
chipsClass = Chips()
chips = chipsClass._getChips(player)
return chips
def _finishBank(self, irc, player):
if player in self.players.keys():
while(self.players[player]["bankScore"] < 17):
card = self._deal("bank", player)
irc.reply("Dealer has got \x02%s\x02, and his score is \x02%s\x02." % (card, self.players[player]["bankScore"]))
self._finishGame(irc, player)
def _addNewPlayer(self, player, stake):
""" Adds new player and needed things to self.players dictionary."""
self.players[player] = {}
# This is set because this is the best way to check if player is playing when he wants to hit/double/stand
self.players[player]["waitingAction"] = False
# We must keep track of players score after each card is dealt
self.players[player]["score"] = 0
# Also we must keep track of bank score
self.players[player]["bankScore"] = 0
# And we need to know how much the player wants to stake
self.players[player]["stake"] = stake
# We must also remove initial stake from player, just like he put it on table
chips = Chips()
chips._removeChips(player, stake)
def _scheduleNewGame(self, command, when, name, player, irc):
# Here we're adding game name to the list of games, from where we check who's playing and who's not
self.events.append(name)
# Here we schedule the command to run after 10s and to finish game
schedule.addEvent(command, time.time() + when, name, args=(irc, player))
def _checkBlackjack(self, irc, player):
""" Checking to see if enyone has blackjack after first two cards are dealt."""
if player in self.players.keys():
if self.players[player]["score"] == 21 and self.players[player]["bankScore"] != 21:
self._playerWins(irc, player, True)
self.players[player]["waitingAction"] = False
elif self.players[player]["bankScore"] == 21 and self.players[player]["score"] != 21:
self._bankWins(irc, player)
else:
return
def _blackjackPrize(self, stake):
# BlackJack prize is not same as regular prize. It is 3:1 so we must calculate it
halfStake = stake / 2
newStake = (stake * 2) + halfStake
return newStake
def blackjack(self, irc, msg, args, stake):
"""<stake> - amount of stake, between 10 and 50
Starts a new game."""
# Because nicks are case-insensitive we must turn nick to lower version.
player = str(msg.nick).lower()
# Nicks are uniqe and we will use nick for scheduling game.
game_name = "game_%s" % player
# Checks if player has any chips.
chips = self._playerChips(player)
chipsClass = Chips()
# If this is True player is already playing blackjack and only one game is allowed per player.
if self._isScheduled(game_name):
irc.reply("You can play only one instance of the game in same time.")
# If player does not have chips he can't play, logical.
if chips == "NoChipsFile" or chips == False or chips == None:
chipsClass._addChips(player, stake)
chips = stake
elif stake > chips:
chipsClass._addChips(player, stake - chips)
chips = stake
if stake >= self.minStake and stake <= self.maxStake and stake <= chips:
# Now is good time to add new player and actually start a game.
self._addNewPlayer(player, stake)
self._startNewGame(irc, player)
else:
irc.reply("Something is wrong with your stake, maybe it's too high or too low, or maybe you don't have enough chips.")
blackjack = wrap(blackjack, ["int"])
def _startNewGame(self, irc, player):
# We deal first card to the player
playerFirstCard = self._deal('player', player)
# And we must tell the player which card he got
irc.reply("Your first card is %s" % playerFirstCard)
# We must also deal first card to the bank
bankFirstCard = self._deal('bank', player)
# And player shouldn't know which is the dealers first card
irc.reply("Dealer has got his first card, face down.")
# Now we deal second card to the player
playerSecondCard = self._deal('player', player)
# And we have to tell the player what's his second card, and also what's his score
irc.reply("Your second card is %s, and you're score is %s" % (playerSecondCard, self.players[player]["score"]))
# Bank just got it's second card
bankSecondCard = self._deal('bank', player)
# Player needs to know which is dealer's second card
irc.reply("Dealer has got his second card %s, his score is now \x02%s\02.What are you gonna do, stand, hit or double. You have 20 seconds to decide." % (bankSecondCard, self.players[player]["bankScore"]))
# Now we wait for user to hit/double/stand and our bot must know that he's waiting for player action
self.players[player]["waitingAction"] = True
# Each game will wait 10s for user input and then it'll end the game if nothing happens, and we must schedule it
self._scheduleNewGame(self._finishBank, 20, "game_%s" % player, player, irc)
# We must check if somebody has got blackjack after first 2 cards
self._checkBlackjack(irc, player)
def hit(self, irc, msg, args):
"""Takes no arguments
Hit!!!"""
player = str(msg.nick).lower()
if self._waitingPlayerAction(player) == True:
game_name = "game_%s" % player
self._removeScheduledGame(game_name)
card = self._deal('player', player)
irc.reply("You've got %s, and your score is %s." % (card, self.players[player]["score"]))
if self.players[player]["score"] > 21:
irc.reply("You're busted.")
self._bankWins(irc, player)
else:
self._scheduleNewGame(self._finishBank, 20, game_name, player, irc)
else:
irc.reply("You're not playing blackjack at this moment. Start a new game with +blackjack 50")
hit = wrap(hit)
def double(self, irc, msg, args):
"""Takes no arguments
Double!!!!"""
player = str(msg.nick).lower()
if self._waitingPlayerAction(player) == True:
stake = self.players[player]["stake"]
chips = self._playerChips(player)
if chips != False and chips != None and chips != "NoChipsFile" and chips >= stake:
game_name = "game_%s" % player
self._removeScheduledGame(game_name)
chipsClass = Chips()
chipsClass._removeChips(player, stake)
self.players[player]["stake"] = stake * 2
irc.reply("Your stake was %s, and now it is %s." % (stake, self.players[player]["stake"]))
card = self._deal('player', player)
irc.reply("You've got %s, and your score is %s" % (card, self.players[player]["score"]))
if self.players[player]["score"] > 21:
irc.reply("You're busted.")
self._bankWins(irc, player)
else:
self._finishBank(irc, player)
else:
irc.reply("You don't have enough chips for double stake.")
else:
irc.reply("You're not playing blackjack at this moment. Start a new game with +blackjack 50")
double = wrap(double)
def stand(self, irc, msg, args):
"""Takes no arguments.
Stand!!!"""
player = str(msg.nick).lower()
if self._waitingPlayerAction(player) == True:
self._finishBank(irc, player)
else:
irc.reply("You're not playing blackjack at this moment. Start a new game with +blackjack 50")
stand = wrap(stand)
class Chips():
def __init__(self):
try:
with open("{0}/local/chips.json".format(os.path.dirname(os.path.abspath(__file__))), "r") as chipsFile:
self.players = json.load(chipsFile)
except:
self.players = False
def _getChips(self, player):
if self.players:
if player in self.players.keys():
try:
return self.players[player]["chips"]
except:
return False
else:
return None
else:
return "NoChipsFile"
def _addChips(self, player, amount):
if self.players:
if player in self.players.keys():
self.players[player]["chips"] = self.players[player]["chips"] + amount
self._saveChips()
else:
self.players[player] = {}
self.players[player]["chips"] = amount
self._saveChips()
else:
return "NoChipsFile"
def _removeChips(self, player, amount):
if self.players:
if player in self.players.keys():
self.players[player]["chips"] = self.players[player]["chips"] - amount
self._saveChips()
def _saveChips(self):
with open("{0}/local/chips.json".format(os.path.dirname(os.path.abspath(__file__))), "w") as chips:
json.dump(self.players, chips, indent=4)
Class = BlackJack
# vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: