@ -16,12 +16,21 @@ class events(enum.Enum):
class limit_types ( enum . Enum ) :
points , rounds = range ( 2 )
Deck = namedtuple ( ' Deck ' , [ ' code ' , ' name ' , ' author ' , ' call_count ' , ' response_count ' , ' calls ' , ' responses ' ] )
# fqcode = fully qualified code = (namespace, code)
Deck = namedtuple ( ' Deck ' , [ ' fqcode ' , ' name ' , ' author ' , ' call_count ' , ' response_count ' , ' calls ' , ' responses ' ] )
Limit = namedtuple ( ' Limit ' , [ ' type ' , ' number ' ] )
Card = namedtuple ( ' Card ' , [ ' deck ' , ' text ' ] )
Namespace = namedtuple ( ' Namespace ' , [ ' url ' , ' supports_random ' ] )
deck_namespaces = {
' cardcast ' : Namespace ( None , True ) , # Use the library's default URL
' bslsk05 ' : Namespace ( ' https://dl.puckipedia.com/ ' , False )
}
class IRCFormattingState :
def __init__ ( self ) :
# 99 is the "client default colour"
@ -130,15 +139,11 @@ def game(send, notice, voice, devoice, get_event):
send ( ' , ' . join ( sorted ( players ) + sorted ( i . nick for i in bots . values ( ) ) ) )
def add_deck ( code ) :
def add_deck ( namespace , code ) :
nonlocal decks
assert code not in decks
assert ( namespace , code ) not in decks
# Colondeck and offtopiadeck deck live elsewhere
if code in ( ' colondeck ' , ' offtopiadeck ' ) :
base_url = ' https://dl.puckipedia.com/ '
else :
base_url = None
base_url = deck_namespaces [ namespace ] . url
# First get info for the deck we're adding
info = cardcast_api . info ( code , base_url = base_url )
@ -192,8 +197,8 @@ def game(send, notice, voice, devoice, get_event):
responses [ i ] = responses [ i ] [ : 159 ] + ' … '
# Add a new deck to list of decks
decks [ code ] = Deck (
code = code ,
decks [ ( namespace , code ) ] = Deck (
fq code = ( namespace , code ) ,
name = name ,
author = author ,
call_count = call_count ,
@ -202,33 +207,32 @@ def game(send, notice, voice, devoice, get_event):
responses = responses
)
def get_random_deck_code ( ) :
nonlocal cardcast _deck_count
def get_random_deck_code ( namespace ) :
nonlocal remote _deck_count
# Provide the count on subsequent calls
# First time around cardcast_deck_count will be None, so it
# gets requested from Cardcast, like if we didn't pass the
# `count` parameter
# This will update cardcast_deck_count for each call
# unnecessarily, but I think it simplifies the code and is not
# too bad
code , cardcast_deck_count = cardcast_api . random_code ( count = cardcast_deck_count )
base_url = deck_namespaces [ namespace ] . url
# Keep track of how many cards there are on the remote, so that we don't
# need to keep rerequesting that
if namespace not in remote_deck_count :
code , remote_deck_count [ namespace ] = cardcast_api . random_code ( base_url = base_url )
else :
code , remote_deck_count [ namespace ] = cardcast_api . random_code ( count = remote_deck_count [ namespace ] , base_url = base_url )
return code
def remove_deck ( code ) :
def remove_deck ( namespace , code ) :
nonlocal decks , round_call_card
# Purge all the cards from the deck from the game
for player_bot in players_bots ( ) :
for index , card in enumerate ( player_bot . hand ) :
if card is not None and card . deck . code == code :
if card is not None and card . deck . fq code == ( namespace , code ) :
player_bot . hand [ index ] = None
if round_call_card is not None and round_call_card . deck . code == code :
if round_call_card is not None and round_call_card . deck . fq code == ( namespace , code ) :
round_call_card = None
del decks [ code ]
del decks [ ( namespace , code ) ]
def list_decks ( ) :
nonlocal decks
@ -246,33 +250,45 @@ def game(send, notice, voice, devoice, get_event):
responses_left = len ( deck . responses )
responses = str ( response_count ) if response_count == responses_left else ' %i / %i ' % ( responses_left , response_count )
send ( ' %s ( %s , by %s , %s black, %s white) ' % (
namespace , code = deck . fqcode
send ( ' %s ( %s %s , by %s , %s black, %s white) ' % (
deck . name ,
deck . code ,
namespace ,
code ,
deck . author ,
calls ,
responses
) )
def deck_add_handler ( code ) :
def deck_add_handler ( namespace , code ) :
nonlocal decks
if code not in decks :
errwrapper ( ' Failure adding deck: %s ( %% s) ' % code , add_deck , code )
if namespace in deck_namespaces :
if ( namespace , code ) not in decks :
errwrapper ( ' Failure adding deck: %s %s ( %% s) ' % ( namespace , code ) , add_deck , namespace , code )
else :
send ( ' Deck already added ' )
else :
send ( ' Deck already added ' )
send ( ' Unknown deck namespace %s . Try one of: %s ' % ( namespace , ' , ' . join ( deck_namespaces . keys ( ) ) ) )
def deck_add_random_handler ( ) :
def deck_add_random_handler ( namespace ) :
nonlocal decks
# Let's hope this never bites us in the butt
while True :
code = errwrapper ( ' Failure getting random code for a deck. ( %s ) ' , get_random_deck_code )
if code is Error : return
if code not in decks : break
send ( ' That was weird, got %s randomly but it was already added ' % code )
errwrapper ( ' Failure adding deck: %s ( %% s) ' % code , add_deck , code )
send ( ' Added deck %s ( %s ) ' % ( decks [ code ] . name , code ) )
if namespace in deck_namespaces :
if deck_namespaces [ namespace ] . supports_random :
# Let's hope this never bites us in the butt
while True :
code = errwrapper ( ' Failure getting random code for a deck. ( %s ) ' , get_random_deck_code , namespace )
if code is Error : return
if ( namespace , code ) not in decks : break
send ( ' That was weird, got %s randomly but it was already added ' % code )
errwrapper ( ' Failure adding deck: %s %s ( %% s) ' % ( namespace , code ) , add_deck , namespace , code )
send ( ' Added deck %s ( %s %s ) ' % ( decks [ ( namespace , code ) ] . name , namespace , code ) )
else :
send ( ' Namespace %s does \' t support adding a random deck. Try one of: %s ' % ( namespace , ' , ' . join ( namespace for namespace in deck_namespaces . keys ( ) if deck_namespaces [ namespace ] . supports_random ) ) )
else :
send ( ' Unknown deck namespace %s . Try one of: %s ' % ( namespace , ' , ' . join ( deck_namespaces . keys ( ) ) ) )
def get_hand_origins ( player ) :
hand_origins = [ ]
@ -281,7 +297,7 @@ def game(send, notice, voice, devoice, get_event):
if card is None :
hand_origins . append ( ' <empty> ' )
else :
hand_origins . append ( card . deck . code )
hand_origins . append ( ' %s %s ' % card . deck . fq code)
return ' , ' . join ( ' %i : %s ' % ( index , i ) for index , i in enumerate ( hand_origins ) )
@ -348,18 +364,19 @@ def game(send, notice, voice, devoice, get_event):
send ( ' %s has been removed from the game ' % kickee )
elif event == events . deck_add :
cod e , = args
deck_add_handler ( code )
namespa ce, code = args
deck_add_handler ( namespace , code )
elif event == events . deck_add_random :
deck_add_random_handler ( )
namespace , = args
deck_add_random_handler ( namespace )
elif event == events . deck_remove :
cod e , = args
if code in decks :
errwrapper ( ' Failure removing deck %s ( %% s) ' % code , remove_deck , code )
namespa ce, code = args
if ( namespace , code ) in decks :
errwrapper ( ' Failure removing deck %s ( %% s) ' % code , remove_deck , namespace , code )
else :
send ( ' No such deck %s ' % code )
send ( ' No such deck %s %s ' % ( namespace , code ) )
elif event == events . deck_list :
list_decks ( )
@ -412,27 +429,26 @@ def game(send, notice, voice, devoice, get_event):
def start_game ( rest ) :
if len ( rest ) == 0 or rest [ 0 ] == ' default ' :
send ( ' Adding the default CAH deck (A5DCM) ' )
send ( ' Adding the default CAH deck (cardcast A5DCM) ' )
deck_add_handler ( ' A5DCM ' )
deck_add_handler ( ' cardcast ' , ' A5DCM' )
elif rest [ 0 ] == ' offtopia-random ' :
send ( ' Adding the default CAH deck (A5DCM), offtopia injoke deck (offtopiadeck), :Deck (colondeck) and three random decks ' )
send ( ' Adding the default CAH deck (cardcast A5DCM), offtopia injoke deck (bslsk05 offtopiadeck), :Deck (bslsk05 colondeck) and three random cardcast decks ' )
deck_add_handler ( ' A5DCM ' )
deck_add_handler ( ' offtopiadeck ' )
deck_add_handler ( ' colondeck ' )
deck_add_handler ( ' cardcast ' , ' A5DCM' )
deck_add_handler ( ' bslsk05 ' , ' offtopiadeck' )
deck_add_handler ( ' bslsk05 ' , ' colondeck' )
deck_add_random_handler ( )
deck_add_random_handler ( )
deck_add_random_handler ( )
for _ in range ( 3 ) :
deck_add_random_handler ( ' cardcast ' )
elif rest [ 0 ] == ' offtopia ' :
send ( ' Adding the default CAH deck (A5DCM), offtopia injoke deck (offtopiadeck), and :Deck (colondeck) ' )
send ( ' Adding the default CAH deck (cardcast A5DCM), offtopia injoke deck (bslsk05 offtopiadeck), and :Deck (bslsk05 colondeck) ' )
deck_add_handler ( ' A5DCM ' )
deck_add_handler ( ' offtopiadeck ' )
deck_add_handler ( ' colondeck ' )
deck_add_handler ( ' cardcast ' , ' A5DCM' )
deck_add_handler ( ' bslsk05 ' , ' offtopiadeck' )
deck_add_handler ( ' bslsk05 ' , ' colondeck' )
elif rest [ 0 ] != ' empty ' :
send ( ' Unknown preset %s ' % rest [ 0 ] )
@ -1067,10 +1083,10 @@ def game(send, notice, voice, devoice, get_event):
nick , = args
if nick not in players :
notice ( nick , ' call: %s ' % round_call_card . deck . code )
notice ( nick , ' call: %s %s ' % round_call_card . deck . fq code)
else :
notice ( nick , ' call: %s , %s ' % ( round_call_card . deck . code , get_hand_origins ( players [ nick ] ) ) )
notice ( nick , ' call: %s %s , %s ' % ( * round_call_card . deck . fq code, get_hand_origins ( players [ nick ] ) ) )
elif event == events . redeal :
nick , = args
@ -1204,15 +1220,15 @@ def game(send, notice, voice, devoice, get_event):
nick , = args
if nick not in players :
notice ( nick , ' call: %s ' % round_call_card . deck . code )
notice ( nick , ' call: %s %s ' % round_call_card . deck . fq code)
else :
answers_origins = [ ]
for index , player_bot in enumerate ( choosers ) :
answer_origins = [ i . deck . code for i in card_choices [ player_bot ] ]
answer_origins = [ ' %s %s ' % i . deck . fq code for i in card_choices [ player_bot ] ]
answers_origins . append ( ' %i : %s ' % ( index , ' , ' . join ( answer_origins ) ) )
notice ( nick , ' call: %s ; %s ' % ( round_call_card . deck . code , ' ; ' . join ( answers_origins ) ) )
notice ( nick , ' call: %s %s ; %s ' % ( * round_call_card . deck . fq code, ' ; ' . join ( answers_origins ) ) )
elif event == events . redeal :
nick , = args
@ -1314,7 +1330,7 @@ def game(send, notice, voice, devoice, get_event):
czar = None
card_choices = None
cardcast_deck_count = None
remote_deck_count = { }
state = no_game
while state != quit :
@ -1366,13 +1382,16 @@ if __name__ == '__main__':
kickee = input ( ' kickee> ' )
return ( events . kick , kicker , kickee )
elif t == ' deck add ' :
namespace = input ( ' namespace> ' )
code = input ( ' code> ' )
return ( events . deck_add , code )
return ( events . deck_add , namespace , code )
elif t == ' deck add random ' :
return ( events . deck_add_random , )
namespace = input ( ' namespace> ' )
return ( events . deck_add_random , namespace )
elif t == ' deck remove ' :
namespace = input ( ' namespace> ' )
code = input ( ' code> ' )
return ( events . deck_remove , code )
return ( events . deck_remove , namespace , code )
elif t == ' deck list ' :
return ( events . deck_list , )
elif t == ' bot add rando ' :