HiveBrain v1.2.0
Get Started
← Back to all entries
patternpythonMinor

Rock paper scissors

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
scissorspaperrock

Problem

I've started learning Python recently and I wanted to test myself. So, can you tell me how many I would get out of 10 and how I could improve my future coding?

```
import random
import time
print('Welcome to Rock, Paper, Scissors')
print(' '*25)
wins = 0
loses = 0
draws = 0
point= int(input('How many rounds do you want to play?'))
list = ['r','p','s']
for x in range (1,(point+1)):
computer = random.choice(list)
human=str(input('Choose (R)ock, (P)aper, or (S)cissors?'))
if human == computer:
draws = draws + 1
print('''Human: {} Computer: {} A draw
Wins = {} Loses = {} Draws = {}'''.format(human,computer,wins,loses,draws))
print(' '*25)
elif human == 'r' and computer == 'p':
loses = loses + 1
print('''Human: ROCK Computer: PAPER Computer wins
Wins = {} Loses = {} Draws = {}'''.format(wins,loses,draws))
print(' '*25)
elif human == 'r' and computer == 's':
wins = wins + 1
print('''Human: ROCK Computer: SCISSORS Human wins
Wins = {} Loses = {} Draws = {}'''.format(wins,loses,draws))
print(' '*25)
elif human == 'p' and computer == 's':
loses = loses + 1
print('''Human: PAPER Computer: SCISSORS Computer wins
Wins = {} Loses = {} Draws = {}'''.format(wins,loses,draws))
print(' '*25)
elif human == 'p' and computer == 'r':
wins = wins + 1
print('''Human: PAPER Computer: ROCK Human wins
Wins = {} Loses = {} Draws = {}'''.format(wins,loses,draws))
print(' '*25)
elif human == 's' and computer == 'p':
wins = wins + 1
print('''Human: SCISSORS Computer: PAPER Human wins
Wins = {} Loses = {} Draws = {}'''.format(wins,loses,draws))
print(' '*25)
elif human == 's' and computer == 'r':
loses = loses + 1
print('''Human: SCISSORS Computer: ROCK Compu

Solution

Your program is adequate, but it doesn't "scale". Each player can choose from 3 moves; of those 9 possibilities, the 3 ties can be handled in common, for a total of 7 code branches. Handling all those cases with cut-and-paste code violates the "Don't Repeat Yourself" principle, and it makes the code hard to maintain.

I would start by introducing a class to represent the moves, and define how they relate to each other:

from collections import namedtuple

class RPSMove(namedtuple('RPSMove', ['name', 'short_name', 'beats'])):
    def __gt__(self, other):
        return self.beats == other.short_name

    def __lt__(self, other):
        return not(self == other or self > other)

    def __str__(self):
        return self.name

    all = dict()

RPSMove.all['r'] = RPSMove('rock',     short_name='r', beats='s')
RPSMove.all['p'] = RPSMove('paper',    short_name='p', beats='r')
RPSMove.all['s'] = RPSMove('scissors', short_name='s', beats='p')


Next, I would extract the way the computer and human take their turns into functions:

import random
from sys import exit

def computer_play():
    return random.choice(list(RPSMove.all.values()))

def human_play():
    while True:
        try:
            choice = input('Choose (R)ock, (P)aper, or (S)cissors? ')
            return RPSMove.all[choice[0].lower()]
        except EOFError:
            print('')
            exit(0)
        except:
            print('Error')


The human_play() function is more complex than your original due to handling of invalid choices and end-of-file (Control+D in Unix or Control+Z in Windows).

With those preliminaries out of the way, the heart of the game can look quite nice.

random.seed()
human_wins, computer_wins, draws = 0, 0, 0

print('Welcome to Rock, Paper, Scissors')
print('')
rounds = int(input('How many rounds do you want to play? '))
for _ in range(rounds):
    human_move, computer_move = human_play(), computer_play()
    if human_move > computer_move:
        result = 'Human wins'
        human_wins += 1
    elif human_move < computer_move:
        result = 'Computer wins'
        computer_wins += 1
    else:
        result = 'A draw'
        draws += 1

    print('Human: %-17s Computer: %-17s %s' %
          (human_move, computer_move, result))
    print('Human wins = %-11d Computer wins = %-11d Draws = %-11d' %
          (human_wins, computer_wins, draws))
    print('')


I've renamed wins and loses to human_wins and computer_wins to avoid anthropocentrism.

Code Snippets

from collections import namedtuple

class RPSMove(namedtuple('RPSMove', ['name', 'short_name', 'beats'])):
    def __gt__(self, other):
        return self.beats == other.short_name

    def __lt__(self, other):
        return not(self == other or self > other)

    def __str__(self):
        return self.name

    all = dict()

RPSMove.all['r'] = RPSMove('rock',     short_name='r', beats='s')
RPSMove.all['p'] = RPSMove('paper',    short_name='p', beats='r')
RPSMove.all['s'] = RPSMove('scissors', short_name='s', beats='p')
import random
from sys import exit

def computer_play():
    return random.choice(list(RPSMove.all.values()))

def human_play():
    while True:
        try:
            choice = input('Choose (R)ock, (P)aper, or (S)cissors? ')
            return RPSMove.all[choice[0].lower()]
        except EOFError:
            print('')
            exit(0)
        except:
            print('Error')
random.seed()
human_wins, computer_wins, draws = 0, 0, 0

print('Welcome to Rock, Paper, Scissors')
print('')
rounds = int(input('How many rounds do you want to play? '))
for _ in range(rounds):
    human_move, computer_move = human_play(), computer_play()
    if human_move > computer_move:
        result = 'Human wins'
        human_wins += 1
    elif human_move < computer_move:
        result = 'Computer wins'
        computer_wins += 1
    else:
        result = 'A draw'
        draws += 1

    print('Human: %-17s Computer: %-17s %s' %
          (human_move, computer_move, result))
    print('Human wins = %-11d Computer wins = %-11d Draws = %-11d' %
          (human_wins, computer_wins, draws))
    print('')

Context

StackExchange Code Review Q#36368, answer score: 4

Revisions (0)

No revisions yet.