Advertisement
MolSno

unobot.py

Dec 31st, 2014
316
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 17.15 KB | None | 0 0
  1. """
  2. Copyright 2010 Tamas Marki. All rights reserved.
  3.  
  4. Redistribution and use in source and binary forms, with or without modification, are
  5. permitted provided that the following conditions are met:
  6.  
  7.   1. Redistributions of source code must retain the above copyright notice, this list of
  8.      conditions and the following disclaimer.
  9.  
  10.   2. Redistributions in binary form must reproduce the above copyright notice, this list
  11.      of conditions and the following disclaimer in the documentation and/or other materials
  12.      provided with the distribution.
  13.  
  14. THIS SOFTWARE IS PROVIDED BY TAMAS MARKI ``AS IS'' AND ANY EXPRESS OR IMPLIED
  15. WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
  16. FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL TAMAS MARKI OR
  17. CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  18. CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  19. SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
  20. ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  21. NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
  22. ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  23.  
  24.  
  25. [18:03] <Lako> .play w 3
  26. [18:03] <unobot> TopMobil's turn. Top Card: *]
  27. [18:03] [Notice] -unobot- Your cards: [4][9][4][8][D2][D2]
  28. [18:03] [Notice] -unobot- Next: hatcher (5 cards) - Lako (2 cards)
  29. [18:03] <TopMobil> :O
  30. [18:03] <Lako> :O
  31.  
  32. """
  33.  
  34. import random
  35. from datetime import datetime, timedelta
  36.  
  37. CHANNEL = '#radbusiness'
  38. SCOREFILE = "/root/phenny/unoscores.txt"
  39.  
  40. STRINGS = {
  41.     'ALREADY_STARTED' : '\x0300,01Game already started by %s! Type join to join!',
  42.     'GAME_STARTED' : '\x0300,01IRC-UNO started by %s - Type join to join!',
  43.     'GAME_STOPPED' : '\x0300,01Game stopped.',
  44.     'CANT_STOP' : '\x0300,01%s is the game owner, you can\'t stop it!',
  45.     'DEALING_IN' : '\x0300,01Dealing %s into the game as player #%s!',
  46.     'JOINED' : '\x0300,01Dealing %s into the game as player #%s!',
  47.     'ENOUGH' : '\x0300,01There are enough players, type .deal to start!',
  48.     'NOT_STARTED' : '\x0300,01Game not started, type .uno to start!',
  49.     'NOT_ENOUGH' : '\x0300,01Not enough players to deal yet.',    
  50.     'NEEDS_TO_DEAL' : '\x0300,01%s needs to deal.',
  51.     'ALREADY_DEALT' : '\x0300,01Already dealt.',
  52.     'ON_TURN' : '\x0300,01It\'s %s\'s turn.',
  53.     'DONT_HAVE' : '\x0300,01You don\'t have that card, %s',
  54.     'DOESNT_PLAY' : '\x0300,01That card does not play, %s',
  55.     'UNO' : '\x0300,01UNO! %s has ONE card left!',
  56.     'WIN' : '\x0300,01We have a winner! %s!!!! This game took %s',
  57.     'DRAWN_ALREADY' : '\x0300,01You\'ve already drawn, either .pass or .play!',
  58.     'DRAWS' : '\x0300,01%s draws a card',
  59.     'DRAWN_CARD' : '\x0300,01Drawn card: %s',
  60.     'DRAW_FIRST' : '\x0300,01%s, you need to draw first!',
  61.     'PASSED' : '\x0300,01%s passed!',
  62.     'NO_SCORES' : '\x0300,01No scores yet',
  63.     'SCORE_ROW' : '\x0300,01#%s %s (%s points %s games, %s won, %s wasted)',
  64.     'TOP_CARD' : '\x0300,01%s\'s turn. Top Card: %s',
  65.     'YOUR_CARDS' : '\x0300,01Your cards: %s',
  66.     'NEXT_START' : '\x0300,01Next: ',
  67.     'NEXT_PLAYER' : '\x0300,01%s (%s cards)',
  68.     'D2' : '\x0300,01%s draws two and is skipped!',
  69.     'CARDS' : '\x0300,01Cards: %s',
  70.     'WD4' : '\x0300,01%s draws four and is skipped!',
  71.     'SKIPPED' : '\x0300,01%s is skipped!',
  72.     'REVERSED' : '\x0300,01Order reversed!',
  73.     'GAINS' : '\x0300,01%s gains %s points!',
  74. }
  75.  
  76. class UnoBot:
  77.     def __init__ (self):
  78.         self.colored_card_nums = [ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'R', 'S', 'D2' ]
  79.         self.special_scores = { 'R' : 20, 'S' : 20, 'D2' : 20, 'WD4' : 50, 'W' : 50 }
  80.         self.colors = 'RGBY'
  81.         self.special_cards = [ 'W', 'WD4' ]
  82.         self.players = { }
  83.         self.playerOrder = [ ]
  84.         self.game_on = False
  85.         self.currentPlayer = 0
  86.         self.topCard = None
  87.         self.way = 1
  88.         self.drawn = False
  89.         self.scoreFile = SCOREFILE
  90.         self.deck = [ ]
  91.    
  92.     def start(self, phenny, owner):
  93.         if self.game_on:
  94.             phenny.msg (CHANNEL, STRINGS['ALREADY_STARTED'] % self.game_on)
  95.         else:
  96.             self.game_on = owner
  97.             self.deck = [ ]
  98.             phenny.msg (CHANNEL, STRINGS['GAME_STARTED'] % owner)
  99.             self.players = { }
  100.             self.players[owner] = [ ]
  101.             self.playerOrder = [ owner ]
  102.    
  103.     def stop (self, phenny, input):
  104.         if input.nick == self.game_on:
  105.             phenny.msg (CHANNEL, STRINGS['GAME_STOPPED'])
  106.             self.game_on = False
  107.         elif self.game_on:
  108.             phenny.msg (CHANNEL, STRINGS['CANT_STOP'] % self.game_on)
  109.            
  110.     def join (self, phenny, input):
  111.         #print dir (phenny.bot)
  112.         #print dir (input)
  113.         if self.game_on:
  114.             if input.nick not in self.players:
  115.                 self.players[input.nick] = [ ]
  116.                 # put new player in a random index
  117.                 self.playerOrder.insert (random.randrange (len (self.playerOrder) + 1), input.nick)
  118.                 if self.deck:
  119.                     for i in xrange (0, 7):
  120.                         self.players[input.nick].append (self.getCard ())
  121.                     phenny.msg (CHANNEL, STRINGS['DEALING_IN'] % (input.nick, self.playerOrder.index (input.nick) + 1))
  122.                 else:
  123.                     phenny.msg (CHANNEL, STRINGS['JOINED'] % (input.nick, self.playerOrder.index (input.nick) + 1))
  124.                     if len (self.players) > 1:
  125.                         phenny.msg (CHANNEL, STRINGS['ENOUGH'])
  126.         else:
  127.             phenny.msg (CHANNEL, STRINGS['NOT_STARTED'])
  128.    
  129.     def deal (self, phenny, input):
  130.         if not self.game_on:
  131.             phenny.msg (CHANNEL, STRINGS['NOT_STARTED'])
  132.             return
  133.         if len (self.players) < 2:
  134.             phenny.msg (CHANNEL, STRINGS['NOT_ENOUGH'])
  135.             return
  136.         if input.nick != self.game_on:
  137.             phenny.msg (CHANNEL, STRINGS['NEEDS_TO_DEAL'] % self.game_on)
  138.             return
  139.         if len (self.deck):
  140.             phenny.msg (CHANNEL, STRINGS['ALREADY_DEALT'])
  141.             return
  142.         self.startTime = datetime.now ()
  143.         self.deck = self.createnewdeck ()
  144.         for i in xrange (0, 7):
  145.             for p in self.players:
  146.                 self.players[p].append (self.getCard ())
  147.         self.topCard = self.getCard ()
  148.         while self.topCard in ['W', 'WD4']: self.topCard = self.getCard ()
  149.         random.shuffle (self.playerOrder)
  150.         self.currentPlayer = 1
  151.         self.cardPlayed (phenny, self.topCard)
  152.         self.showOnTurn (phenny)
  153.    
  154.     def play (self, phenny, input):
  155.         if not self.game_on or not self.deck:
  156.             return
  157.         if input.nick != self.playerOrder[self.currentPlayer]:
  158.             phenny.msg (CHANNEL, STRINGS['ON_TURN'] % self.playerOrder[self.currentPlayer])
  159.             return
  160.         tok = [z.strip () for z in str (input).upper ().split (' ')]
  161.         if len (tok) != 3:
  162.             return
  163.         searchcard = ''
  164.         if tok[1] in self.special_cards:
  165.             searchcard = tok[1]
  166.         else: searchcard = (tok[1] + tok[2])
  167.         if searchcard not in self.players[self.playerOrder[self.currentPlayer]]:
  168.             phenny.msg (CHANNEL, STRINGS['DONT_HAVE'] % self.playerOrder[self.currentPlayer])
  169.             return
  170.         playcard = (tok[1] + tok[2])
  171.         if not self.cardPlayable (playcard):
  172.             phenny.msg (CHANNEL, STRINGS['DOESNT_PLAY'] % self.playerOrder[self.currentPlayer])
  173.             return
  174.        
  175.         self.drawn = False
  176.         self.players[self.playerOrder[self.currentPlayer]].remove (searchcard)
  177.        
  178.         pl = self.currentPlayer
  179.        
  180.         self.incPlayer ()
  181.         self.cardPlayed (phenny, playcard)
  182.  
  183.         if len (self.players[self.playerOrder[pl]]) == 1:
  184.             phenny.msg (CHANNEL, STRINGS['UNO'] % self.playerOrder[pl])
  185.         elif len (self.players[self.playerOrder[pl]]) == 0:
  186.             phenny.msg (CHANNEL, STRINGS['WIN'] % (self.playerOrder[pl], (datetime.now () - self.startTime)))
  187.             self.gameEnded (phenny, self.playerOrder[pl])
  188.             return
  189.            
  190.         self.showOnTurn (phenny)
  191.  
  192.     def draw (self, phenny, input):
  193.         if not self.game_on or not self.deck:
  194.             return
  195.         if input.nick != self.playerOrder[self.currentPlayer]:
  196.             phenny.msg (CHANNEL, STRINGS['ON_TURN'] % self.playerOrder[self.currentPlayer])
  197.             return
  198.         if self.drawn:
  199.             phenny.msg (CHANNEL, STRINGS['DRAWN_ALREADY'])
  200.             return
  201.         self.drawn = True
  202.         phenny.msg (CHANNEL, STRINGS['DRAWS'] % self.playerOrder[self.currentPlayer])
  203.         c = self.getCard ()
  204.         self.players[self.playerOrder[self.currentPlayer]].append (c)
  205.         phenny.notice (input.nick, STRINGS['DRAWN_CARD'] % self.renderCards ([c]))
  206.  
  207.     # this is not a typo, avoiding collision with Python's pass keyword
  208.     def passs (self, phenny, input):
  209.         if not self.game_on or not self.deck:
  210.             return
  211.         if input.nick != self.playerOrder[self.currentPlayer]:
  212.             phenny.msg (CHANNEL, STRINGS['ON_TURN'] % self.playerOrder[self.currentPlayer])
  213.             return
  214.         if not self.drawn:
  215.             phenny.msg (CHANNEL, STRINGS['DRAW_FIRST'] % self.playerOrder[self.currentPlayer])
  216.             return
  217.         self.drawn = False
  218.         phenny.msg (CHANNEL, STRINGS['PASSED'] % self.playerOrder[self.currentPlayer])
  219.         self.incPlayer ()
  220.         self.showOnTurn (phenny)
  221.    
  222.     def top10 (self, phenny, input):
  223.         from copy import copy
  224.         prescores = [ ]
  225.         try:
  226.             f = open (self.scoreFile, 'r')
  227.             for l in f:
  228.                 t = l.replace ('\n', '').split (' ')
  229.                 if len (t) < 4: continue
  230.                 prescores.append (copy (t))
  231.                 if len (t) == 4: t.append (0)
  232.             f.close ()
  233.         except: pass
  234.         prescores = sorted (prescores, lambda x, y: cmp ((y[1] != '0') and (float (y[3]) / int (y[1])) or 0, (x[1] != '0') and (float (x[3]) / int (x[1])) or 0))
  235.         if not prescores:
  236.             phenny.notice (input.nick, STRINGS['NO_SCORES'])
  237.         i = 1
  238.         for z in prescores[:10]:
  239.             phenny.notice (input.nick, STRINGS['SCORE_ROW'] % (i, z[0], z[3], z[1], z[2], timedelta (seconds = int (z[4]))))
  240.             i += 1
  241.  
  242.    
  243.     def createnewdeck (self):
  244.         ret = [ ]
  245.         for a in self.colored_card_nums:
  246.             for b in self.colors:
  247.                 ret.append (b + a)
  248.         for a in self.special_cards:
  249.             ret.append (a)
  250.             ret.append (a)
  251.        
  252.         ret *= 2
  253.         random.shuffle (ret)
  254.         return ret
  255.    
  256.     def getCard(self):
  257.         ret = self.deck[0]
  258.         self.deck.pop (0)
  259.         if not self.deck:
  260.             self.deck = self.createnewdeck ()        
  261.         return ret
  262.    
  263.     def showOnTurn (self, phenny):
  264.         phenny.msg (CHANNEL, STRINGS['TOP_CARD'] % (self.playerOrder[self.currentPlayer], self.renderCards ([self.topCard])))
  265.         phenny.notice (self.playerOrder[self.currentPlayer], STRINGS['YOUR_CARDS'] % self.renderCards (self.players[self.playerOrder[self.currentPlayer]]))
  266.         msg = STRINGS['NEXT_START']
  267.         tmp = self.currentPlayer + self.way
  268.         if tmp == len (self.players):
  269.             tmp = 0
  270.         if tmp < 0:
  271.             tmp = len (self.players) - 1
  272.         arr = [ ]
  273.         while tmp != self.currentPlayer:
  274.             arr.append (STRINGS['NEXT_PLAYER'] % (self.playerOrder[tmp], len (self.players[self.playerOrder[tmp]])))
  275.             tmp = tmp + self.way
  276.             if tmp == len (self.players):
  277.                 tmp = 0
  278.             if tmp < 0:
  279.                 tmp = len (self.players) - 1
  280.         msg += ' - '.join (arr)
  281.         phenny.notice (self.playerOrder[self.currentPlayer], msg)
  282.        
  283.     def renderCards (self, cards):
  284.         ret = [ ]
  285.         for c in sorted (cards):
  286.             if c in ['W', 'WD4']:
  287.                 ret.append ('\x0300,01[' + c + ']')
  288.                 continue
  289.             if c[0] == 'W':
  290.                 c = c[-1] + '*'
  291.             t = '\x0300,01\x03'
  292.             if c[0] == 'B':
  293.                 t += '02,01['
  294.             if c[0] == 'Y':
  295.                 t += '08,01['
  296.             if c[0] == 'G':
  297.                 t += '09,01['
  298.             if c[0] == 'R':
  299.                 t += '04,01['
  300.             t += c[1:] + ']\x0300,01'
  301.             ret.append (t)
  302.         return ''.join (ret)
  303.    
  304.     def cardPlayable (self, card):
  305.         if card[0] == 'W' and card[-1] in self.colors:
  306.             return True
  307.         if self.topCard[0] == 'W':
  308.             return card[0] == self.topCard[-1]
  309.         return ((card[0] == self.topCard[0]) or (card[1] == self.topCard[1])) and (card[0] not in ['W', 'WD4'])
  310.    
  311.     def cardPlayed (self, phenny, card):
  312.         if card[1:] == 'D2':
  313.             phenny.msg (CHANNEL, STRINGS['D2'] % self.playerOrder[self.currentPlayer])
  314.             z = [self.getCard (), self.getCard ()]
  315.             phenny.notice(self.playerOrder[self.currentPlayer], STRINGS['CARDS'] % self.renderCards (z))
  316.             self.players[self.playerOrder[self.currentPlayer]].extend (z)
  317.             self.incPlayer ()
  318.         elif card[:2] == 'WD':
  319.             phenny.msg (CHANNEL, STRINGS['WD4'] % self.playerOrder[self.currentPlayer])
  320.             z = [self.getCard (), self.getCard (), self.getCard (), self.getCard ()]
  321.             phenny.notice(self.playerOrder[self.currentPlayer], STRINGS['CARDS'] % self.renderCards (z))
  322.             self.players[self.playerOrder[self.currentPlayer]].extend (z)
  323.             self.incPlayer ()
  324.         elif card[1] == 'S':
  325.             phenny.msg (CHANNEL, STRINGS['SKIPPED'] % self.playerOrder[self.currentPlayer])
  326.             self.incPlayer ()
  327.         elif card[1] == 'R' and card[0] != 'W':
  328.             phenny.msg (CHANNEL, STRINGS['REVERSED'])
  329.             self.way = -self.way
  330.             self.incPlayer ()
  331.             self.incPlayer ()
  332.         self.topCard = card
  333.    
  334.     def gameEnded (self, phenny, winner):
  335.         try:
  336.             score = 0
  337.             for p in self.players:
  338.                 for c in self.players[p]:
  339.                     if c[0] == 'W':
  340.                         score += self.special_scores[c]
  341.                     elif c[1] in [ 'S', 'R', 'D' ]:
  342.                         score += self.special_scores[c[1:]]
  343.                     else:
  344.                         score += int (c[1])
  345.             phenny.msg (CHANNEL, STRINGS['GAINS'] % (winner, score))
  346.             self.saveScores (self.players.keys (), winner, score, (datetime.now () - self.startTime).seconds)
  347.         except Exception, e:
  348.             print 'Score error: %s' % e
  349.         self.players = { }
  350.         self.playerOrder = [ ]
  351.         self.game_on = False
  352.         self.currentPlayer = 0
  353.         self.topCard = None
  354.         self.way = 1
  355.        
  356.    
  357.     def incPlayer (self):
  358.         self.currentPlayer = self.currentPlayer + self.way
  359.         if self.currentPlayer == len (self.players):
  360.             self.currentPlayer = 0
  361.         if self.currentPlayer < 0:
  362.             self.currentPlayer = len (self.players) - 1
  363.    
  364.     def saveScores (self, players, winner, score, time):
  365.         from copy import copy
  366.         prescores = { }
  367.         try:
  368.             f = open (self.scoreFile, 'r')
  369.             for l in f:
  370.                 t = l.replace ('\n', '').split (' ')
  371.                 if len (t) < 4: continue
  372.                 if len (t) == 4: t.append (0)
  373.                 prescores[t[0]] = [t[0], int (t[1]), int (t[2]), int (t[3]), int (t[4])]
  374.             f.close ()
  375.         except: pass
  376.         for p in players:
  377.             if p not in prescores:
  378.                 prescores[p] = [ p, 0, 0, 0, 0 ]
  379.             prescores[p][1] += 1
  380.             prescores[p][4] += time
  381.         prescores[winner][2] += 1
  382.         prescores[winner][3] += score
  383.         try:
  384.             f = open (self.scoreFile, 'w')
  385.             for p in prescores:
  386.                 f.write (' '.join ([str (s) for s in prescores[p]]) + '\n')
  387.             f.close ()
  388.         except Exception, e:
  389.             print 'Failed to write score file %s' % e
  390.  
  391. unobot = UnoBot ()
  392.  
  393. def uno(phenny, input):
  394.     unobot.start (phenny, input.nick)
  395. uno.commands = ['uno']
  396. uno.priority = 'high'
  397.  
  398. def unostop(phenny, input):
  399.     unobot.stop (phenny, input)
  400. unostop.commands = ['unostop']
  401. unostop.priority = 'high'
  402.  
  403. def join(phenny, input):
  404.     unobot.join (phenny, input)
  405. join.rule = '^join$'
  406. join.priority = 'high'
  407.  
  408. def deal(phenny, input):
  409.     unobot.deal (phenny, input)
  410. deal.commands = ['deal']
  411. deal.priority = 'high'
  412.  
  413. def play(phenny, input):
  414.     unobot.play (phenny, input)
  415. play.commands = ['play']
  416. play.priority = 'high'
  417.  
  418. def draw(phenny, input):
  419.     unobot.draw (phenny, input)
  420. draw.commands = ['draw']
  421. draw.priority = 'high'
  422.  
  423. def passs(phenny, input):
  424.     unobot.passs (phenny, input)
  425. passs.commands = ['pass']
  426. passs.priority = 'high'
  427.  
  428. def unotop10 (phenny, input):
  429.     unobot.top10 (phenny, input)
  430. unotop10.commands = ['unotop10']
  431. unotop10.priority = 'high'
  432.  
  433. if __name__ == '__main__':
  434.        print __doc__.strip()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement