ActiveState Code

Recipe 181064: Using a Dictionary in place of a 'switch' statement


Description: Rock, Scissors, Paper Game. Shows a clean way of implementing a 'switch' statement in Python via a dictionary container. The dictionary is made up of known 'named states' that are tested in sequence for their current 'state'.

Python
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
#! /usr/local/bin/python

# /////////////////////////////////////////////////////////////////////////
# /**
# * Title: rochambeau.py
# *
# * Description: Rock, Scissors, Paper Game. 
# *              Shows a clean way of implementing a 'switch'
# *              statement in Python via a dictionary container. 
# *              The dictionary is made up of known 'named states' that
# *              are tested in sequence for their current 'state'.
# *
# * Copyright: Copyright (c) 2003
# *            This file is distributed as EXAMPLE SOURCE CODE ONLY!
# *            The following code is considered 'Freeware' and can be 
# *            freely copied/reused/distributed as needed. 
# *
# * Company: None
# * @author: Alan Haffner
# * @version 1.0
# */
# 
# /////////////////////////////////////////////////////////////////////////
# Date: 02/16/03


import os, sys
import string, random
import types

def cli():

   c = '?'
   while c not in 'rps':

      try:
         print
         # tailing index '[0]' picks only first char from input
         c = raw_input('\tPlease enter (r)ock, (p)aper or (s)cissors to play... ')[0]
      except IndexError:
         # bad input, so like get another...
         pass

      c = c.lower()

      #   x, q...   --> quit
      if c in ('x', 'q' ):
         raise 'USER_QUIT_ERROR'

   return c

if __name__=='__main__':

   errorCode = 0

   stateList = ['r', 'p', 's']

   validStates = { 'User Wins'      : (('p','r'), ('r','s'), ('s','p')),
                   'No One Wins'    : (('p','p'), ('r','r'), ('s','s')),
                   'Computer Wins'  : (('r','p'), ('s','r'), ('p','s')),
   }

   try:
      while 1:
         testTuple     = (None, None)
         userInput     =        None
         computerInput =       '?'

         userInput     = cli()
         computerInput = ( stateList[random.randint(0,2)] )
   
         testTuple = (userInput, computerInput)

         for select in validStates:
            if testTuple in validStates[select]:
               print
               print "You chose:         ", userInput
               print "The computer chose:", computerInput
               print " ****", select, " ****" 
               print

   # Note: By convention, all local exception 'constants' end 
   # in '_ERROR' regaurdless of their intended use. 
   except KeyboardInterrupt:
      print '\n' * 3
      print '[interrupted by user]'
      print '\n' * 3
   except 'USER_QUIT_ERROR':
      print '\n' * 3
      print '[interrupted by user]'
      print '\n' * 3
   except:
      # unexpected error
      print '\n' * 3
      traceback.print_exc()
      print '\n' * 3

      errorCode = 2

   sys.exit(errorCode)

Discussion

Shows a nice clean way of working around Python's lack of a 'switch' or 'case' statement. Dictionaries turn out to be a very clean way of doing what you only thought a true 'switch' statement could do.

Cheers,

Yaipa

Comments

  1. 1. At 11:52 a.m. on 11 may 2003, Ville Tirronen said:

    Doing this Right. This recipe does not in any way use dictionary, just lists. For doing this as it should be, one could do this:

       stateList = ['r', 'p', 's']
    
    #   validStates = { 'User Wins'      : (('p','r'), ('r','s'), ('s','p')),
    #                   'No One Wins'    : (('p','p'), ('r','r'), ('s','s')),
    #                   'Computer Wins'  : (('r','p'), ('s','r'), ('p','s')),
    #   }
    # This is not wise. Just the same as using a list.
    
    # The following is better:
    
    # First some handy aliases to save typing.
    win = "User wins"
    draw ="No one wins"
    lose = " Computer wins"
    validStates = { ('p','r') : win,
                ('r','s') : win,
                ('s','p'): win,
                            ('p','p'): draw,
                ('r','r'):  draw,
                ('s','s'): draw,
                            ('r','p'): lose,
                ('s','r'): lose,
                ('p','s'): lose
       }
    
       try:
          while 1:
     #        testTuple     = (None, None)
     #        userInput     =        None
     #        computerInput =       '?'
    # These can safely be commented out. Pythons power is conciseness after all
    
             userInput     = cli()
             computerInput = ( stateList[random.randint(0,2)] )
    
             testTuple = (userInput, computerInput)
    
     #        for select in validStates:
     #           if testTuple in validStates[select]:
     #              print
     #              print "You chose:         ", userInput
     #              print "The computer chose:", computerInput
     #              print " ****", select, " ****"
     #              print
    # Iterating over a dictionary makes really no sense. After all, it IS a dictionary
    # Instead we can:
    
    print validStates[testTuple]
    
    # For the exact same effects.
    

    Less to type, faster to execute, easier to extend.

  2. 2. At 3:30 p.m. on 17 feb 2004, Runsun Pan said:

    A more concise way.

    import random
    userPick=''
    while userPick not in ['r', 'p', 's']:
         userPick = raw_input('\tPlease enter (r)ock, (p)aper or (s)cissors to play... ')[0]
    
    computerPick= random.choice(['r','p','s'])
    pair = (userPick, computerPick)
    
    result= { pair==('r','r') or pair==('p','p') or pair==('s','s'): 'draw!',
              pair==('p','r') or pair==('s','p') or pair==('r','s'): 'You won!',
              pair==('r','p') or pair==('p','s') or pair==('s','r'): 'Computer won!'}[1]
    
    print 'You entered: ', userPick, ', Computer entered: ', computerPick
    print result
    
  3. 3. At 3:30 p.m. on 17 feb 2004, Runsun Pan said:

    A more concise way.

    import random
    userPick=''
    while userPick not in ['r', 'p', 's']:
         userPick = raw_input('\tPlease enter (r)ock, (p)aper or (s)cissors to play... ')[0]
    
    computerPick= random.choice(['r','p','s'])
    pair = (userPick, computerPick)
    
    result= { pair==('r','r') or pair==('p','p') or pair==('s','s'): 'draw!',
              pair==('p','r') or pair==('s','p') or pair==('r','s'): 'You won!',
              pair==('r','p') or pair==('p','s') or pair==('s','r'): 'Computer won!'}[1]
    
    print 'You entered: ', userPick, ', Computer entered: ', computerPick
    print result
    
  4. 4. At 9:35 p.m. on 25 apr 2005, Josiah Carlson said:

    Your example is more concise than the original poster, but not significantly more concise than Ville Tirronen's.

    Also, your method is slower over the course of long-term execution because the dictionary needs to be computed in every pass. Ville Tirronen's example mechanism is far faster in practice.

Sign in to comment