Voting

Category

real language

Bookmarking

Del.icio.us Digg Diigo DZone Earthlink Google Kick.ie
Windows Live LookLater Ma.gnolia Reddit Rojo StumbleUpon Technorati

Language Python

(Advanced, extensible beer/wall framework)

Date:05/17/06
Author:Jamie Turner
URL:http://jamwt.com
Comments:7
Info:http://python.org
Score: (3.03 in 191 votes)
'''An extensible framework for simulated bottle buying, 
putting away, taking down, and drinking.
'''

import sys

def vernacularFilter(f):
    '''Let's say "no more" instead of "zero"
    '''
    def _f(*args, **kw):
        word = f(*args, **kw)
        if word == 'zero':
            return 'no more'
        return word
    return _f

class LanguageTranslationError(Exception): pass

class NumToLanguageTranslator:
    '''Translate integers to spoken words, for various locales.
    '''
    DEFAULT_LOCALE = 'en-US'

    def __init__(self, locale=DEFAULT_LOCALE):
        self.translate = getattr(self, 'translate_%s' % 
        locale.lower().replace('-','_'))
        if not self.translate:
            raise LanguageTranslationError, \
            "Missing locale translation tables for numeric values"

    # en-US section.  More to follow
    EN_US_SINGLETON = {
        0: 'zero',
        1: 'one',
        2: 'two',
        3: 'three',
        4: 'four',
        5: 'five',
        6: 'six',
        7: 'seven',
        8: 'eight',
        9: 'nine',
        10: 'ten',
        11: 'eleven',
        12: 'twelve',

        20: 'twenty',
        30: 'thirty',
        40: 'fourty',
        50: 'fifty',
        60: 'sixty',
        70: 'seventy',
        80: 'eighty',
        90: 'ninety',
    }

    EN_US_TEENS = (13,20)
    EN_US_TEEN_SPECIAL_PFX = {
        3: 'thir', 
        4: 'four', 
        5: 'fif',
        8: 'eigh',
    }
    EN_US_RANGE = (0, 100)
    EN_US_TEEN_SFX = 'teen'

    @vernacularFilter
    def translate_en_us(self, num):
        '''Translations for en-US; currently only 0-99
        are supported.  That should be enough bottles
        for anyone.
        '''
        if num not in range(*self.EN_US_RANGE):
            raise LanguageTranslationError, \
            "Cannot tranlsate: number out of range"
        if num in self.EN_US_SINGLETON:
            return self.EN_US_SINGLETON[num]
        remainder = num % 10
        if num in range(*self.EN_US_TEENS):
            return '%s%s' % ( 
                self.EN_US_TEEN_SPECIAL_PFX.get(remainder,
                    self.EN_US_SINGLETON[remainder]),
                self.EN_US_TEEN_SFX)
        return '%s-%s' % (self.EN_US_SINGLETON[num / 10 * 10], 
                          self.EN_US_SINGLETON[remainder])

def joinStringList(strings):
    '''Take care of those pesky contractions.
    '''
    if len(strings) == 1: 
        return strings[0]
    return '%s and %s' % (','.join(strings[:-1]), strings[-1]) 

class Collection:
    '''A collection of items, complete with
    a few common actions (taking down, passing around,
    putting away) and extensive logging.
    '''
    def __init__(self, itemClass, emptyClass,
    translator=NumToLanguageTranslator(), logfd=sys.stdout):
        self.itemClass = itemClass
        self.emptyClass = emptyClass
        self.items = []
        self.location = None
        self.numTranslator = translator
        self.logfd = logfd

    def _get_description(self):
        return '%s %s' % (self.numTranslator.translate(len(self.items)),
                          self.itemClass.name(plural=len(self.items) != 1))
    description = property(_get_description)

    def _get_longDescription(self):
        return '%s %s' % (self.description, self.location)
    longDescription = property(_get_longDescription)

    def putAway(self, items, location):
        self.items.extend(items)
        self.location = location

    (EVENT_TAKE_ONE_DOWN,
     EVENT_PASS_IT_AROUND) = range(2)
    def takeOneDown(self, actor):
        '''Get an item and log its removal and distribution.
        '''
        try:
            item_to_discard = self.items.pop(0)
        except IndexError:
            raise self.emptyClass
        self.logEvents(
            [self.EVENT_TAKE_ONE_DOWN,
             self.EVENT_PASS_IT_AROUND,
            ], actor)
        return item_to_discard

    LOG_MESSAGES = {
        EVENT_TAKE_ONE_DOWN: 'take one down',
        EVENT_PASS_IT_AROUND: 'pass it around',
    }

    def logEvents(self, events, actor):
        self.logfd.write('%s%s, ' % (
            actor and (str(actor) + ' ') or '',
            joinStringList(map(lambda x: self.LOG_MESSAGES[x], events))
        ))
        
class Item:
    '''Any damn thing.
    '''
    @staticmethod
    def name(plural=False):
        raise NotImplementedError, "Override me!"

class Person:
    '''Someone, but specifically someone with a name.
    '''
    def __init__(self, name):
        self.name = name

    def __str__(self):
        return self.name

class TheStore:
    '''The source of many things.  At the moment, only beer.

    TODO: pretzels.
    '''
    @staticmethod
    def buyBeer(howMuch):
        return [BeerBottle() for x in range(howMuch)]

## Our application: Separate module??

class OutOfBeer(Exception): 
    '''For resonable handling of a terrible situation.
    '''
    def __str__(self):
        return "Go to the store and buy some more"


class BeerBottle(Item):
    '''A potential item for collecting, passing around, etc.
    '''
    @staticmethod
    def name(plural=False):
        return 'bottle%s of beer' % (plural and 's' or '')

    def drink(self):
        pass # Mmm. Beer.


def letsDrink(who, howMuch, whereToKeep):
    '''A "sample" application, if you will, of this framework in use.
    
    Scales easily up to 99 bottles.
    '''
    assert 1 <= howMuch <= 99
    actor = Person(who)
    collection = Collection(BeerBottle, OutOfBeer)
    def refill():
        collection.putAway(TheStore.buyBeer(howMuch), whereToKeep)
    refill()
    try:
        while True:
            print collection.longDescription.capitalize() + ', ' + collection.description + '.'
            bottle = collection.takeOneDown(actor)
            print collection.longDescription + '.'
            bottle.drink()
            print ''
    except OutOfBeer, e:
        sys.stdout.write(str(e))
        # Lets be polite and leave the wall full!
        refill()
        print ',', collection.longDescription + '.'
        # --oh wait, GC.  damn

if __name__ == "__main__":
    # test
    letsDrink('You', 99, 'on the wall')

Download Source | Write Comment

Alternative Versions

VersionAuthorDateCommentsRate
This example demonstrates the simplicityGerold Penz07/23/0515
Creative versionSchizo11/06/0516
minimal versionOliver Xymoron04/20/055
using lambda in LISP styleJ Adrian Zimmer11/14/062
Exception basedMichael Galpin02/08/080
functional, w/o variables or proceduresIvan Tkatchev07/14/052
minimal version with singularEmlyn Jones06/13/053
Fully compliant versionRicardo Garcia Gonzalez01/15/067
Using a iterator classEric Moritz01/20/062
New conditional expressions in 2.5Ezequiel Pochiero12/18/061

Comments

>>  Auders said on 10/29/06 15:51:56

Auders Now this was cool!
Keep up the good work!

>>  Isaac said on 12/08/06 17:12:45

Isaac So wrong, but sooo right. This should be in text books under the heading "The problems of over-design in OOP".

Hilarious.

>>  Criptych said on 05/14/08 20:57:51

Criptych Complete and total overkill... programming at its best. Have you considered making this into a text-adventure engine? You have all the basics. :)

>>  DND said on 11/30/08 17:03:58

DND Wall used 'Bottle of beer'
Critical Hit!

>>  anon said on 11/19/09 13:15:35

anon Absolute overgeekness...

>>  lucyisssilo said on 06/01/10 05:18:03

lucyisssilo Total Screen Recorder, a great tool for making video tutorials:http://www.totalscreenrecorder.com

>>  roosieslo said on 06/28/10 05:00:55

roosieslo GodswMobile Software dedicated to providing a better experience life for the people who use and rely upon Microsoft Windows Mobile devices for their personal and commercial needs. With thousands of users over 50 countries worldwide, GodswMobile has become the popular, trusted and convenient choice to backup and restore the valuable information which stored in mobile phones.
http://www.godswmobile.com/

Download Source | Write Comment

Add Comment

Please provide a value for the fields Name, Comment and Security Code.
This is a gravatar-friendly website.
E-mail addresses will never be shown.
Enter your e-mail address to use your gravatar.

Please don't post large portions of code here! Use the form to submit new examples or updates instead!

Name:

eMail:

URL:

Security Code:
  
Comment: