1###
2# Copyright (c) 2003-2005, Jeremiah Fincher
3# Copyright (c) 2010, James McCoy
4# All rights reserved.
5#
6# Redistribution and use in source and binary forms, with or without
7# modification, are permitted provided that the following conditions are met:
8#
9#   * Redistributions of source code must retain the above copyright notice,
10#     this list of conditions, and the following disclaimer.
11#   * Redistributions in binary form must reproduce the above copyright notice,
12#     this list of conditions, and the following disclaimer in the
13#     documentation and/or other materials provided with the distribution.
14#   * Neither the name of the author of this software nor the name of
15#     contributors to this software may be used to endorse or promote products
16#     derived from this software without specific prior written consent.
17#
18# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21# ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
22# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28# POSSIBILITY OF SUCH DAMAGE.
29###
30
31import re
32import random
33
34
35import supybot.utils as utils
36from supybot.commands import *
37import supybot.ircmsgs as ircmsgs
38import supybot.ircutils as ircutils
39import supybot.callbacks as callbacks
40from supybot.i18n import PluginInternationalization, internationalizeDocstring
41_ = PluginInternationalization('Games')
42
43
44class Games(callbacks.Plugin):
45    """This plugin provides some small games like (Russian) roulette,
46    eightball, monologue, coin and dice."""
47    @internationalizeDocstring
48    def coin(self, irc, msg, args):
49        """takes no arguments
50
51        Flips a coin and returns the result.
52        """
53        if random.randrange(0, 2):
54            irc.reply(_('heads'))
55        else:
56            irc.reply(_('tails'))
57    coin = wrap(coin)
58
59    @internationalizeDocstring
60    def dice(self, irc, msg, args, m):
61        """<dice>d<sides>
62
63        Rolls a die with <sides> number of sides <dice> times.
64        For example, 2d6 will roll 2 six-sided dice; 10d10 will roll 10
65        ten-sided dice.
66        """
67        (dice, sides) = list(map(int, m.groups()))
68        if dice > 1000:
69            irc.error(_('You can\'t roll more than 1000 dice.'))
70        elif sides > 100:
71            irc.error(_('Dice can\'t have more than 100 sides.'))
72        elif sides < 3:
73            irc.error(_('Dice can\'t have fewer than 3 sides.'))
74        else:
75            L = [0] * dice
76            for i in range(dice):
77                L[i] = random.randrange(1, sides+1)
78            irc.reply(format('%L', [str(x) for x in L]))
79    _dicere = re.compile(r'^(\d+)d(\d+)$')
80    dice = wrap(dice, [('matches', _dicere,
81                        _('Dice must be of the form <dice>d<sides>'))])
82
83    # The list of words and algorithm are pulled straight the mozbot
84    # MagicEightBall.bm module: http://tinyurl.com/7ytg7
85    _positive = _('It is possible.|Yes!|Of course.|Naturally.|Obviously.|'
86                  'It shall be.|The outlook is good.|It is so.|'
87                  'One would be wise to think so.|'
88                  'The answer is certainly yes.')
89    _negative = _('In your dreams.|I doubt it very much.|No chance.|'
90                  'The outlook is poor.|Unlikely.|'
91                  'About as likely as pigs flying.|You\'re kidding, right?|'
92                  'NO!|NO.|No.|The answer is a resounding no.')
93    _unknown = _('Maybe...|No clue.|_I_ don\'t know.|'
94                 'The outlook is hazy, please ask again later.|'
95                 'What are you asking me for?|Come again?|'
96                 'You know the answer better than I.|'
97                 'The answer is def-- oooh! shiny thing!')
98
99    def _checkTheBall(self, questionLength):
100        if questionLength % 3 == 0:
101            catalog = self._positive
102        elif questionLength % 3 == 1:
103            catalog = self._negative
104        else:
105            catalog = self._unknown
106        return utils.iter.choice(catalog.split('|'))
107
108    @internationalizeDocstring
109    def eightball(self, irc, msg, args, text):
110        """[<question>]
111
112        Ask a question and the answer shall be provided.
113        """
114        if text:
115            irc.reply(self._checkTheBall(len(text)))
116        else:
117            irc.reply(self._checkTheBall(random.randint(0, 2)))
118    eightball = wrap(eightball, [additional('text')])
119
120    _rouletteChamber = random.randrange(0, 6)
121    _rouletteBullet = random.randrange(0, 6)
122    @internationalizeDocstring
123    def roulette(self, irc, msg, args, spin):
124        """[spin]
125
126        Fires the revolver.  If the bullet was in the chamber, you're dead.
127        Tell me to spin the chambers and I will.
128        """
129        if spin:
130            self._rouletteBullet = random.randrange(0, 6)
131            irc.reply(_('*SPIN* Are you feeling lucky?'), prefixNick=False)
132            return
133        channel = msg.args[0]
134        if self._rouletteChamber == self._rouletteBullet:
135            self._rouletteBullet = random.randrange(0, 6)
136            self._rouletteChamber = random.randrange(0, 6)
137            if irc.nick in irc.state.channels[channel].ops or \
138                    irc.nick in irc.state.channels[channel].halfops:
139                irc.queueMsg(ircmsgs.kick(channel, msg.nick, 'BANG!'))
140            else:
141                irc.reply(_('*BANG* Hey, who put a blank in here?!'),
142                          prefixNick=False)
143            irc.reply(_('reloads and spins the chambers.'), action=True)
144        else:
145            irc.reply(_('*click*'))
146            self._rouletteChamber += 1
147            self._rouletteChamber %= 6
148    roulette = wrap(roulette, ['public', additional(('literal', 'spin'))])
149
150    @internationalizeDocstring
151    def monologue(self, irc, msg, args, channel):
152        """[<channel>]
153
154        Returns the number of consecutive lines you've sent in <channel>
155        without being interrupted by someone else (i.e. how long your current
156        'monologue' is).  <channel> is only necessary if the message isn't sent
157        in the channel itself.
158        """
159        i = 0
160        for m in reversed(irc.state.history):
161            if m.command != 'PRIVMSG':
162                continue
163            if not m.prefix:
164                continue
165            if not ircutils.strEqual(m.args[0], channel):
166                continue
167            if msg.prefix == m.prefix:
168                i += 1
169            else:
170                break
171        irc.reply(format(_('Your current monologue is at least %n long.'),
172                         (i, _('line'))))
173    monologue = wrap(monologue, ['channel'])
174
175Class = Games
176
177
178# vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79:
179