class DummyContract: def __init__(self, server, talon): self._name = "DummyContract" self._server = server self._talon = talon self._pileMenus = [] self.DistributeTalon() ## written for TeamContract, then copied here def DistributeTalon(self): cards = list(self._talon.values()) shuffle(cards) Npile = self.Npiles() ## create a pile menu to choose which cards to pick up self._talonMenu = Menu() self._talonMenu.Deactivate() for i in range(Npile): tag = 'pile{}'.format(i) desc = '' ## description of cards in pile ## create a menu for each pile of cards menu = Menu(info=True) ## distribute cards to the piles ## build the description for the pile menu for card in cards[i * self._num:(i + 1) * self._num]: menu.AddEntry(card.ShortName(), card.LongName()) ## entry for each card desc = desc + ('/' if (len(desc) > 0) else '') + '{}'.format( card.LongName()) self._talonMenu.AddEntry(tag, desc) ## entry for picking piles self._pileMenus.append(menu) def Npiles(self): return 1 def ValidPlay(self, hand, play, lead): return True def CardValue(self, card): return 0 def TrickWinner(self, trick, leadPlayer): return list(trick.keys())[0] def GetMenu(self, name, tag): if tag == 'talon': return self._talonMenu elif tag[:4] == 'pile': return self._pileMenus[0] def ProcessMenuEntry(self, name, tag, req): if tag == 'talon' and req[:4] == 'pile': n = int(req[-1]) self._server._gameControl._bidders['active'].DeactivateMenu( 'talon') self._server.BroadcastMessage("{} picked {}".format(name, req)) raise ValueError("ProcessMenuEntry") def SetKing(self, king): raise ValueError("{} not a valid contract for calling a king".format( self._name))
def BuildDefaultMenu(self): self._menus = {} ## keep multiple menus self._menus['default'] = Menu() self._menus['default'].AddEntry( 'quit', "Exit the program") self._menus['default'].AddEntry( 'master', "Request master action", True) self._menus['default'].AddEntry( 'exception', "Throw intentional exception", not( _debug)) self._menus['default'].AddEntry( '', "Ready", True) self._menus['default'].AddEntry( 'end', "End the game", True) self._menus['default'].AddEntry( '2p', "Start two player game", True) ## debugging purposes self._menus['messages'] = Menu( info=True)
def DistributeTalon(self): cards = list(self._talon.values()) shuffle(cards) Npile = self.Npiles() ## create a pile menu to choose which cards to pick up self._talonMenu = Menu() self._talonMenu.Deactivate() for i in range(Npile): tag = 'pile{}'.format(i) desc = '' ## description of cards in pile ## create a menu for each pile of cards menu = Menu(info=True) ## distribute cards to the piles ## build the description for the pile menu for card in cards[i * self._num:(i + 1) * self._num]: menu.AddEntry(card.ShortName(), card.LongName()) ## entry for each card desc = desc + ('/' if (len(desc) > 0) else '') + '{}'.format( card.LongName()) self._talonMenu.AddEntry(tag, desc) ## entry for picking piles self._pileMenus.append(menu)
def InitializeMenus(self): self._bidMenu = Menu() self._bidMenu.AddEntry('pass', 'No bid') self._bidMenu.AddEntry('K', 'Klop') self._bidMenu.AddEntry('3', 'Three w/ partner') self._bidMenu.AddEntry('2', 'Two w/ partner') self._bidMenu.AddEntry('1', 'One w/ partner') self._bidMenu.AddEntry('S3', 'Solo Three') self._bidMenu.AddEntry('S2', 'Solo Two') self._bidMenu.AddEntry('S1', 'Solo One') self._bidMenu.AddEntry('B', 'Beggar') self._bidMenu.AddEntry('S0', 'Solo Without') self._bidMenu.AddEntry('OB', 'Open Beggar') self._bidMenu.AddEntry('CV', 'Color Valat Without') self._bidMenu.AddEntry('V', 'Valat Without') self._bidMenu.Deactivate() self._kingMenu = Menu() self._kingMenu.AddEntry('diamond', 'King of Diamonds') self._kingMenu.AddEntry('spade', 'King of Spades') self._kingMenu.AddEntry('heart', 'King of Hearts') self._kingMenu.AddEntry('club', 'King of Clubs') self._kingMenu.Deactivate() self._announcementMenu = Menu() self._announcementMenu.AddEntry('pass', 'No announcement') self._announcementMenu.AddEntry('kontra', 'Kontra game (x2)') self._announcementMenu.AddEntry('rekontra', 'Rekontra game (x4)') self._announcementMenu.AddEntry('subkontra', 'Subkontra game (x8)') self._announcementMenu.AddEntry('mordkontra', 'Mordkontra game (x16)') self._announcementMenu.AddEntry('trula', 'End game in possession of trula') self._announcementMenu.AddEntry('kings', 'End game in possession of all kings') self._announcementMenu.AddEntry('king_ult', 'Win called king on last trick') self._announcementMenu.AddEntry('pagat_ult', 'Win last trick with pagat') self._announcementMenu.AddEntry('valat', 'Win all tricks') self._announcementMenu.Deactivate()
class TeamContract: def __init__(self, server, num, talon): self._name = "TeamContract" self._server = server self._num = num self._king = None self._talon = talon self._talonMenu = None self._pileMenus = [] self.DistributeTalon() def DistributeTalon(self): cards = list(self._talon.values()) shuffle(cards) Npile = len(cards) // self._num ## create a pile menu to choose which cards to pick up self._talonMenu = Menu() self._talonMenu.Deactivate() for i in range(Npile): tag = 'pile{}'.format(i) desc = '' ## description of cards in pile ## create a menu for each pile of cards menu = Menu(info=True) #menu.Deactivate() ## menus broadcast right before display ## distribute cards to the piles ## build the description for the pile menu for card in cards[i * self._num:(i + 1) * self._num]: menu.AddEntry(card.ShortName(), card.LongName()) ## entry for each card desc = desc + ('/' if (len(desc) > 0) else '') + '{}'.format( card.LongName()) self._talonMenu.AddEntry(tag, desc) ## entry for picking piles self._pileMenus.append(menu) def Npiles(self): return len(self._pileMenus) def ValidPlay(self, hand, play, lead): return True def CardValue(self, card): return 0 def TrickWinner(self, trick, leadPlayer): return list(trick.keys())[0] def GetMenu(self, name, tag): if tag == 'talon': return self._talonMenu elif tag[:4] == 'pile': n = int(tag[-1]) return self._pileMenus[n] def ProcessMenuEntry(self, name, tag, req): if tag == 'talon' and req[:4] == 'pile': n = int(req[-1]) self._server.BidderHook('leading').DeactivateMenu('talon') self._server.BroadcastMessage("{} picked {}".format(name, req)) for shortName, longName in self._pileMenus[n].items(): card = self._talon[shortName] self._server.BroadcastMessage("{} picked up {}".format( name, longName)) self._server.PlayerHook(name).AddToHand(card, fromTalon=True) ## refresh the menu self._server.PlayerHook(name).HandToMenu() self._server.ClientHook(name).PrintMessage( "YOUR TURN: choose {} cards to lay down".format(self._num)) elif tag == 'discard': Ndiscard = self._server.PlayerHook(name).DiscardFromHand(req) if Ndiscard == self._num: ## go to announcements self._server.PlayerHook(name).HandToInfo() self._server._gameControl._bidding.MakeAnnouncements() else: self._server.ClientHook(name).PrintMessage( "YOUR TURN: choose {} cards to lay down".format(self._num - Ndiscard)) def SetKing(self, king): self._king = king
class Bidding: def __init__(self, server): self._server = server self._contracts = { 'K': 0, '3': 10, '2': 20, '1': 30, 'S3': 40, 'S2': 50, 'S1': 60, 'B': 70, 'S0': 80, 'OB': 90, 'CV': 125, 'V': 250 } self._announcements = { 'kontra': None, 'rekontra': None, 'subkontra': None, 'mordkontra': None, 'trula': None, 'kings': None, 'king_ult': None, 'pagat_ult': None, 'valat': None } self._playerNames = [] self._bidders = {} ## key is bidder condition, value is name of bidder self.Cleanup() self.InitializeMenus() def Cleanup(self): self._talon = {} ## dictionary to hold talon cards temporarily self._leadingBid = None def InitializeMenus(self): self._bidMenu = Menu() self._bidMenu.AddEntry('pass', 'No bid') self._bidMenu.AddEntry('K', 'Klop') self._bidMenu.AddEntry('3', 'Three w/ partner') self._bidMenu.AddEntry('2', 'Two w/ partner') self._bidMenu.AddEntry('1', 'One w/ partner') self._bidMenu.AddEntry('S3', 'Solo Three') self._bidMenu.AddEntry('S2', 'Solo Two') self._bidMenu.AddEntry('S1', 'Solo One') self._bidMenu.AddEntry('B', 'Beggar') self._bidMenu.AddEntry('S0', 'Solo Without') self._bidMenu.AddEntry('OB', 'Open Beggar') self._bidMenu.AddEntry('CV', 'Color Valat Without') self._bidMenu.AddEntry('V', 'Valat Without') self._bidMenu.Deactivate() self._kingMenu = Menu() self._kingMenu.AddEntry('diamond', 'King of Diamonds') self._kingMenu.AddEntry('spade', 'King of Spades') self._kingMenu.AddEntry('heart', 'King of Hearts') self._kingMenu.AddEntry('club', 'King of Clubs') self._kingMenu.Deactivate() self._announcementMenu = Menu() self._announcementMenu.AddEntry('pass', 'No announcement') self._announcementMenu.AddEntry('kontra', 'Kontra game (x2)') self._announcementMenu.AddEntry('rekontra', 'Rekontra game (x4)') self._announcementMenu.AddEntry('subkontra', 'Subkontra game (x8)') self._announcementMenu.AddEntry('mordkontra', 'Mordkontra game (x16)') self._announcementMenu.AddEntry('trula', 'End game in possession of trula') self._announcementMenu.AddEntry('kings', 'End game in possession of all kings') self._announcementMenu.AddEntry('king_ult', 'Win called king on last trick') self._announcementMenu.AddEntry('pagat_ult', 'Win last trick with pagat') self._announcementMenu.AddEntry('valat', 'Win all tricks') self._announcementMenu.Deactivate() def MenuMask(self, name, tag): if tag == 'bidding': # Make 3 and Klop available only to final bidder after three passes mask = set(['K', '3']) if self._leadingBid is None: if self._bidders['last'] == name: mask.discard('3') mask.discard('K') # Mask off any bids lower than current bid else: val = 10 for ct, v in self._contracts.items(): if ct == self._leadingBid: val = v #TODO: Update to allow for precendence out = dict( (c, v) for c, v in self._contracts.items() if v <= val) for c in out: mask.add(c) return mask elif tag == 'announcement': mask = set(self._announcementMenu.keys()) mask.discard('pass') if self._announcements['valat'] is None: mask.discard('valat') mask.discard('kontra') if not (self._announcements['kontra'] is None): mask.add('kontra') mask.discard('rekontra') if not (self._announcements['rekontra'] is None): mask.add('rekontra') mask.discard('subkontra') if not (self._announcements['subkontra'] is None): mask.add('subkontra') mask.discard('mordkontra') if not (self._announcements['mordkontra'] is None): mask.add('mordkontra') if self._announcements['trula'] is None: mask.discard('trula') if self._announcements['kings'] is None: mask.discard('kings') if self._announcements['king_ult'] is None: mask.discard('king_ult') if self._announcements['pagat_ult'] is None: mask.discard('pagat_ult') return mask return set([]) def GetMenu(self, name, tag): if tag == 'bidding': return self._bidMenu if tag == 'kings': return self._kingMenu if tag == 'announcement': return self._announcementMenu def StartBidding(self): self._bidders['passed'] = [] self._bidders['active'] = self._bidders['first'] i = self._playerNames.index(self._bidders['first']) Nplayer = len(self._playerNames) self._bidders['last'] = self._playerNames[(Nplayer + i - 1) % Nplayer] self._bidders['leading'] = None self._leadingBid = None ## distribute bidding menu self._server.BroadcastMenu('bidding') self.BidderHook('active').ActivateMenu('bidding') def EndBidding(self): self.BidderHook('active').DeactivateMenu('bidding') self._server.BroadcastMessage('{0} is Declarer with Bid {1}.'.format( self._bidders['leading'], self._leadingBid)) self._server._gameControl.SetContract(self._leadingBid, self._talon) self.DeclareKing() ## return a hook to the client def BidderHook(self, tag): return self._server._clientHooks[self._bidders[tag]] ## get the name of the bidder def Bidder(self, tag): return self._bidders[tag] def DeclareKing(self): kingcontracts = ["3", "2", "1"] if self._leadingBid in kingcontracts: self._server.BroadcastMenu('kings') self._bidders['active'] = self._bidders['leading'] self.BidderHook('active').ActivateMenu('kings') else: ## skip to talon self.HandleTalon() def HandleTalon(self): taloncontracts = ["3", "2", "1", "S3", "S2", "S1"] if self._leadingBid in taloncontracts: self._server.BroadcastMenu('talon') for i in range(self._server._gameControl._contract.Npiles()): self._server.BroadcastMenu('pile{}'.format(i)) self._bidders['active'] = self._bidders['leading'] self.BidderHook('active').ActivateMenu('talon') else: ## skip to announcements self.MakeAnnouncements() def DeactivatePileMenus(self): taloncontracts = ["3", "2", "1", "S3", "S2", "S1"] if self._leadingBid in taloncontracts: for name in self._playerNames: for i in range(self._server._gameControl._contract.Npiles()): self._server.ClientHook(name).DeactivateMenu( 'pile{}'.format(i)) def MakeAnnouncements(self): self._server.BroadcastMessage("starting announcements") self._bidders['passed'] = [] self._bidders['active'] = self._bidders['first'] i = self._playerNames.index(self._bidders['first']) ## distribute bidding menu self._server.BroadcastMenu('announcement') self.BidderHook('active').ActivateMenu('announcement') def EndAnnouncements(self): for name in self._playerNames: self._server.ClientHook(name).DeactivateMenu('announcement') self._server.BroadcastMessage('ending announcements') self._server._gameControl.StartTricks() def ContractValue(self): return self._contracts[self._leadingBid] ## handle message forwarding to appropriate class object def ProcessMenuEntry(self, name, tag, req): if name == self._bidders['active'] and tag == 'bidding': if req != 'pass': self._leadingBid = req self._bidders['leading'] = name ## in this case, the incoming bid was the final bid if len(self._bidders['passed']) == len(self._playerNames) - 1: self.EndBidding() return ## don't allow a NextActivePlayer call ## if the bid was 'pass' else: self._bidders['passed'].append(name) if len(self._bidders['passed']) == len(self._playerNames) - 1: self._server.BroadcastMessage('Final Bid') elif len(self._bidders['passed']) == len(self._playerNames): self.EndBidding() self.NextActivePlayer('bidding') elif name == self._bidders['active'] and tag == 'kings': self._server._gameControl._contract.SetKing(req) self.BidderHook('active').DeactivateMenu('kings') self.HandleTalon() elif name == self._bidders['active'] and tag == 'talon': self._server._gameControl._contract.ProcessMenuEntry( name, tag, req) elif tag == 'announcement': if req != 'pass': ## TODO: change so more than one announcement can be made by same person at once ## need to have at least two rounds of passing to complete announcement process self._announcements[req] = name ## if the bid was 'pass' else: self._bidders['passed'].append(name) if len(self._bidders['passed']) == len(self._playerNames): self.EndAnnouncements() return self.NextActivePlayer('announcement') def NextActivePlayer(self, tag): i = self._playerNames.index(self._bidders['active']) Nplayer = len(self._playerNames) self.BidderHook('active').DeactivateMenu(tag) for k in range(1, Nplayer + 1): if (self._playerNames[(Nplayer + i - k) % Nplayer] not in self._bidders['passed']): self._bidders['active'] = self._playerNames[(Nplayer + i - k) % Nplayer] self.BidderHook('active').ActivateMenu(tag) return def NextFirstPlayer(self): Nplayer = len(self._playerNames) i = (self._playerNames.index(self._bidders['first']) + 1) % Nplayer self._bidders['first'] = self._playerNames[i] self._server.BroadcastMessage('{} to lead next round'.format( self._bidders['first'])) def SetPlayers(self, playerNames): self._playerNames = playerNames if self._bidders.get('first', None) is None: self._bidders['first'] = self._playerNames[0] def ActiveBidder(self): return self._bidders['active'] ## add cards to talon when dealing ## just save here for now; will be handled by contract later def AddToTalon(self, card): self._talon[card.ShortName()] = card
class Player: def __init__(self, name, server): self._name = name self._server = server self._score = 0 self.Cleanup() def SetScore(self, score): self._score = score def GetScore(self): return self._score def Cleanup(self): self._handMenu = Menu(info=True) self._hand = {} ## Card class objects, keys are Card.ShortName() self._tricks = {} ## Card class objects won in tricks self._talonCards = [] ## track cards that came from talon ## add Card objects to hand when dealing or handling talon def AddToHand(self, card, fromTalon=False): self._handMenu.AddEntry(card.ShortName(), card.LongName()) self._hand[card.ShortName()] = card if fromTalon: ## keep track of which cards come from talon self._talonCards.append(card.ShortName()) ## lay down cards after taking from talon def DiscardFromHand(self, req): card = self._hand[req] self._handMenu.MaskEntry(card.ShortName()) self._tricks[req] = card self._server.ClientHook(self._name).PrintMessage( "you laid down {}".format(card.LongName())) self._server.BroadcastMessage("{} laid down a card".format(self._name)) return len( self._tricks.keys()) ## used to determine change to announcements def MenuMask(self, tag): if tag == 'hand': return self._handMenu._mask def GetMenu(self, tag): if tag == 'hand': return self._handMenu ## convert the hand from info-only to menu def HandToMenu(self): self._handMenu.SetToMenu() self._server.BuildMenu(self._name, 'hand') ## convert the hand from menu to info-only def HandToInfo(self): self._handMenu.SetToInfo() self._server.BuildMenu(self._name, 'hand') ## add cards to hand when dealing or handling talon def TakeTrick(self, trick): for card in trick.values(): self._tricks[card.ShortName()] = card def CardsInHand(self): return len(self._handMenu.keys()) def ScoreTricks(self, contract): cardValueSum = 0 for card in self._tricks.values(): cardValueSum += contract.CardValue(card) return cardValueSum
def Cleanup(self): self._handMenu = Menu(info=True) self._hand = {} ## Card class objects, keys are Card.ShortName() self._tricks = {} ## Card class objects won in tricks self._talonCards = [] ## track cards that came from talon