Teagan's Poet
Bonus lessons are optional.
Let's write a basic transaction processing system (TPS - not that one). A TPS tracks business events, like sales, inventory control... the regular stuff of business operations.
The sitch
You run a small comedy club, with 50 seats. Jeff Arcuri is performing. He's one of my fave comedians. You sell tickets over the phone, and at the club box office. People can also return tickets.
You want to keep track of how many tickets you've sold, and how many are still available. Let's see how it's done. You can download a solution.
I/O sample
Most programs we've written so far run through once, then stop. Not this one.
The program shows a main menu:
- Menu
- ----
- S: sale
- R: return
- T: sTatus report
- Q: quit
- Command?
The user enters a command, like S for sale. The program does whatever it needs to, the shows the main menu again. It keeps showing the menu until the user quits.
Here's an I/O session.
- Menu
- ----
- S: sale
- R: return
- T: sTatus report
- Q: quit
- Command? t
- Status report
- --------- ------ ------
- Ticket inventory: 50
- Tickets sold: 0
- Menu
- ----
- S: sale
- R: return
- T: sTatus report
- Q: quit
- Command? s
- How many sold? 4
- Cha-ching!
- Menu
- ----
- S: sale
- R: return
- T: sTatus report
- Q: quit
- Command? t
- Status report
- --------- ------ ------
- Ticket inventory: 46
- Tickets sold: 4
- Menu
- ----
- S: sale
- R: return
- T: sTatus report
- Q: quit
- Command? r
- How many returned? 6
- Sorry, we haven't sold that many tickets.
- Menu
- ----
- S: sale
- R: return
- T: sTatus report
- Q: quit
- Command? r
- How many returned? 2
- Menu
- ----
- S: sale
- R: return
- T: sTatus report
- Q: quit
- Command? t
- Status report
- --------- ------ ------
- Ticket inventory: 48
- Tickets sold: 2
- Menu
- ----
- S: sale
- R: return
- T: sTatus report
- Q: quit
- Command? s
- How many sold? 8
- Cha-ching!
- Menu
- ----
- S: sale
- R: return
- T: sTatus report
- Q: quit
- Command? s
- How many sold? 9
- Cha-ching!
- Menu
- ----
- S: sale
- R: return
- T: sTatus report
- Q: quit
- Command? s
- How many sold? 11
- Sorry, please enter a number from 1 to 10.
- How many sold? 9
- Cha-ching!
- Menu
- ----
- S: sale
- R: return
- T: sTatus report
- Q: quit
- Command? t
- Status report
- --------- ------ ------
- Ticket inventory: 22
- Tickets sold: 28
- Menu
- ----
- S: sale
- R: return
- T: sTatus report
- Q: quit
- Command? s
- How many sold? 9
- Cha-ching!
- Menu
- ----
- S: sale
- R: return
- T: sTatus report
- Q: quit
- Command? s
- How many sold? 10
- Cha-ching!
- Menu
- ----
- S: sale
- R: return
- T: sTatus report
- Q: quit
- Command? t
- Status report
- --------- ------ ------
- Ticket inventory: 3
- Tickets sold: 47
- Menu
- ----
- S: sale
- R: return
- T: sTatus report
- Q: quit
- Command? s
- How many sold? 4
- Sorry, we don't have that many tickets.
- Menu
- ----
- S: sale
- R: return
- T: sTatus report
- Q: quit
- Command? q
- OK, bye!
There's also the basic validation stuff we did before.
- Menu
- ----
- S: sale
- R: return
- T: sTatus report
- Q: quit
- Command? s
- How many sold? some
- Sorry, you must enter a number.
- How many sold? -1
- Sorry, please enter a number from 1 to 10.
- How many sold? 1
- Cha-ching!
- Menu
- ----
- S: sale
- R: return
- T: sTatus report
- Q: quit
- Command? r
- How many returned? some more
- Sorry, you must enter a number.
- How many returned? -44
- Sorry, please enter a number from 1 to 10.
- How many returned? 1
The main program
Let's write the program top-down. That is, start with the main program, before going into the deets.
Top-down helps identify the main chunks of an app. Each chunk often becomes a function, though not always.
Can anyone write some pseudocode for the main program?

Adela
Let's see. From the I/O, it's, like, show the menu, get a command, do it. Then show the menu, get a command, do it. Show the menu, get a command, do it. Keep going until the user quits.
That's a loop. So something like:
- while user hasn't quit
- show the menu
- get user command
- if command is sale
- do sale thing
- if command is return
- do return thing
- if command is status report
- show a report
Good!
Can anyone Pythonize some of it?

Georgina
We need a variable for the command. Let's make it... er, command
.
The program loops while command
isn't q
. So maybe...
- while command != 'q':

Ethan
Gotta initialize command
first?

Georgina
Oh, right, thanks.
- command = ''
- while command != 'q':
Nice.
What's the first thing in the loop?

Ray
The first part of the pseudocode is:
- while user hasn't quit
- show the menu
- get user command
- ...
We're gonna show the menu, ask the user for a command, and... test it's valid?
Maybe print()
s for the menu, then an input()
, then validation. That's another loop for validation, like we've done before.
This is gonna be a long program.
Right. It's easy to get lost in the weeds. It's best to think in big chunks first, and add the deets later.

Adela
Oh! We should make a function to... well, get a valid command from the user, whatever that involves. So:
- command = ''
- while command != 'q':
- command = get_main_menu_choice()
Are we on the right track?
Yes! That's great!
Here's the pseudocode again, with some real Python.
- command = ''
- while command != 'q':
- command = get_main_menu_choice()
- if command is sale
- do sale thing
- if command is return
- do return thing
- if command is status report
- show a report

Georgina
So, command
is a single letter. We can use that in more of the code.
- command = ''
- while command != 'q':
- command = get_main_menu_choice()
- if command == 's':
- do sale thing
- if command == 'r':
- do return thing
- if command == 't':
- show a report
- print()
- print('OK, bye!')

Georgina
I added the last two lines, too.

Ethan
Hey, Georgina, there's nothing like command == 'q'
.

Georgina
Doesn't need to be.
Here's the code again.
- command = ''
- while command != 'q':
- command = get_main_menu_choice()
- if command == 's':
- do sale thing
- if command == 'r':
- do return thing
- if command == 't':
- show a report
- print()
- print('OK, bye!')
When the user gives the quit command (line 3), a q
will be in command
.
command
doesn't match in the if
s on lines 4, 6, or 8. So, the program loops back up to line 2.
command
is q
. Since the loop runs while command
is not q
, the loop exits.

Ethan
OK, I see it now. Thanks.

Ray
I'll add some comments for the missing code.
- command = ''
- while command != 'q':
- command = get_main_menu_choice()
- if command == 's':
- # Sale
- # Get number tickets sold
- # Reduce inventory
- if command == 'r':
- # Return
- # Get number tickets returned
- # Add to inventory
- if command == 't':
- # Show a report
- print()
- print('OK, bye!')
Drilling down
Ray is doing it right. The command loop is done(ish). He can drill down on the pieces inside it, thinking about them one at a time.
Part of being an effective programmer is managing your own cognitive load, that is, the number of things you need to think about at one time.

Adela
OK, I see what you did. Nice.
Should we have a function to ask the user how many tickets sold?
The function will return something to the main program. Maybe:
num_sold = ticket_sale()

Georgina
Ooo! Then...
- command = ''
- while command != 'q':
- command = get_main_menu_choice()
- if command == 's':
- # Sale
- # Get number tickets sold
- num_sold = ticket_sale()
- # Reduce inventory
- current_inv -= num_sold
- if command == 'r':
- # Return
- # Get number tickets returned
- # Add to inventory
- if command == 't':
- # Show a report
- print()
- print('OK, bye!')

Georgina
I just made up the variable name. It could be something else.

Ethan
current_inv
needs a value, though, or crashy crashy.
Hmm, not sure what to do... Oh! There are 50 seats in the club.
I'll add stuff for the other commands, too.
- current_inv = 50
- command = ''
- while command != 'q':
- command = get_main_menu_choice()
- if command == 's':
- # Sale
- # Get number tickets sold
- num_sold = ticket_sale()
- # Reduce inventory
- current_inv -= num_sold
- if command == 'r':
- # Return
- # Get number tickets returned
- num_returned = ticket_return(total_sold)
- # Add to inventory
- current_inv += num_returned
- if command == 't':
- # Show a report
- show_status_report(current_inv)
- print()
- print('OK, bye!')
What is a function's signature?

Ray
It's the name, params, and return type of a function. Kinda tells you what it takes, and what it returns. The name should hint at what it does.
Top-down FTW!
The Scoobies wrote the main program first, adding calls to functions that made sense to them. They've specified the purpose and signature of each function. Now, when the Scoobies write the functions, the functions will fit with what the main program wants them to do.

Georgina
Hey, that feels pretty good!

Ethan
Yeah, it's like writing the main program has given us a plan for the functions.

Adela
So cool!
Work on the menu function next?
The menu

Ray
We've already done string validation loops a few times. Ask the user for input, if it's invalid, show an error message, and ask again.
Here's some code for a Y/N input, from the past.
- choice = ''
- while choice != 'y' and choice != 'n':
- choice = input('Something (Y=yes, N=no)? ')
- choice = choice.strip().lower()
- if choice != 'y' and choice != 'n':
- print('Please enter Y or N.')

Ethan
I once had trouble understanding that. Seems easy now.
It's good to see that. Helps you remind yourself you're learning Good Stuff.

Adela
Let's replace some of that.
As you do, let's add another goal: make it easy to add other commands to the menu.

Adela
OK. Hmm. If we're gonna do that, maybe think about the things that would change if we added a new menu entry?
There'd be another option in the menu, and the validity test could change, the thing that checks for Y and N. It would have to check for the new option as well.
Oh, the error message would change.
- choice = ''
- while Change validity check. Not choice != 'y' and choice != 'n' anymore:
- choice = input('Change the prompt Something (Y=yes, N=no)? ')
- choice = choice.strip().lower()
- if Change validity check again choice != 'y' and choice != 'n':
- print('Please enter Options.')

Adela
Before, when we had repeated code, we made a function for it. Maybe put the validity check in a function.

Ethan
Yeah, makes sense! Like is_menu_choice_valid(choice)
.
I don't know what the return value would be. A string? A float?

Adela
Hey! The user's choice is either valid, or not, right? That's a boolean! A value that's either true or false, and nothing else.
We could even call the function directly in the while
.
- choice = ''
- while not is_menu_choice_valid(choice):
- choice = input('Prompt')
- choice = choice.strip().lower()
- if not is_menu_choice_valid(choice):
- print('Error message')
Good! Putting the validity check in a function eliminates duplicate code, but it does something else, too.
How does having the function is_menu_choice_valid(choice)
help make it easier to add new menu items?

Ray
I think I know. We add the new validity check in one place.
Before, when we had this:
- choice = ''
- while Change validity check choice != 'y' and choice != 'n':
- choice = input('Something (Y=yes, N=no)? ')
- choice = choice.strip().lower()
- if Change validity check choice != 'y' and choice != 'n':
- print('Please enter Y or N.')
If we wanted to add an option, we'd have to change the check twice. More chance of error.
Now we have:
- choice = ''
- while not is_menu_choice_valid(choice):
- choice = input('Prompt')
- choice = choice.strip().lower()
- if not is_menu_choice_valid(choice):
- print('Error message')
The validity check is in one place, is_menu_choice_valid
. Change that, one and done.
Right! Good thinking, Ray! Less chance of Buggy McBugFace.
OK, we have:
- choice = ''
- while not is_menu_choice_valid(choice):
- choice = input('Prompt')
- choice = choice.strip().lower()
- if not is_menu_choice_valid(choice):
- print('Error message')

Ethan
Should we add the function stuff, to remind ourselves what we're working on?
Maybe we should have done this before.
Ethan's right
When you're writing a function, type in the signature, including return value, first. Helps you keep the goal in mind.
- def get_main_menu_choice():
- choice = ''
- while not is_menu_choice_valid(choice):
- choice = input('Prompt')
- choice = choice.strip().lower()
- if not is_menu_choice_valid(choice):
- print('Error message')
- return choice

Georgina
Ya know... it kinda feels right to make another function that shows the menu and gets the user's choice. It's, like, one chunk of logic, that does one thing. We can give it a name that makes sense, telling you what it does.
Puts the change-the-options-shown in one place.
Maybe this? Oh, I'll make the error message more generic, too.
- def get_main_menu_choice():
- choice = ''
- while not is_menu_choice_valid(choice):
- choice = input_menu_choice()
- if not is_menu_choice_valid(choice):
- print('Please enter a valid command.')
- return choice

Ethan
We could put the normalization in there, too! Keep the deets together.
Note
The Scoobies are developing good programming instincts.
"Here's some functionality that does one thing, we can give it a name, and wrap the deets inside it."
The more you look at good code, the better you'll be at packaging functionality.
Here's the program so far. I've removed some comments to take less screen space, but we'll put them back later.
- def get_main_menu_choice():
- choice = ''
- while not is_menu_choice_valid(choice):
- choice = input_menu_choice()
- if not is_menu_choice_valid(choice):
- print('Please enter a valid command.')
- return choice
- command = ''
- while command != 'q':
- command = get_main_menu_choice()
- if command == 's':
- num_sold = ticket_sale()
- current_inv -= num_sold
- if command == 'r':
- # Return
- # Get number tickets returned
- # Add to inventory
- if command == 't':
- # Show a report
- print()
- print('OK, bye!')
Functions for get_main_menu_choice()

Adela
We need two functions for get_main_menu_choice()
:
is_menu_choice_valid(choice)
input_menu_choice()
The second might be easier to think about.

Ethan
OK, how about this?
- def input_menu_choice():
- print()
- print('Menu')
- print('----')
- print(' S: sale')
- print(' R: return')
- print(' T: sTatus report')
- print(' Q: quit')
- choice = input('Command? ')
- choice = choice.strip().lower()
- return choice
Note
Ethan included the signature right at the start this time. Makes things easier.

Ray
Oh, dude, that's, like... easy to understand. You took the I/O we saw before, printed the menu, an input, and normalization.
Note
The best programs are made of small functions that are easy to write, test, and debug.
To know what the functions should do, you need to design the program's structure first. Top-down FTW!

Georgina
I'll try the other one, is_menu_choice_valid(choice)
. It returns true if choice
is valid, false if not.
- def is_menu_choice_valid(choice):
- if choice == 's' or choice == 'r' or choice == 't' or choice == 'q':
- return True
- else:
- return False

Georgina
It's a bit hard to follow, though. What if we used the backslash thing to break up the long line?
- def is_menu_choice_valid(choice):
- if choice == 's' \
- or choice == 'r' \
- or choice == 't' \
- or choice == 'q':
- return True
- else:
- return False
Note
Human eyes are better at scanning down, than across. That's what makes this easier to understand.

Georgina
That's OK, but something's bugging me. It's, like, if the logical expression (the stuff after if
) is true, return true. If it's false, return false.
Could we... I dunno... return the value of the logical expression itself? Do we even need the if
? Kieran?
Good thinking! You don't need the if
. This thing...
- choice == 's' \
- or choice == 'r' \
- or choice == 't' \
- or choice == 'q'
... is either true or false already. It returns a boolean, and that's what the function should return.

Georgina
Oh, I see! How about this?
- def is_menu_choice_valid(choice):
- valid_choice = \
- choice == 's' \
- or choice == 'r' \
- or choice == 't' \
- or choice == 'q'
- return valid_choice
Right! If choice
is one of the allowed values, valid_choice
will be true, or else it will be false. Just return it! That fits with what the function is supposed to do.

Ethan
Wow, good thinking, Georgina!

Ray
Yeah, so elegant!

Adela
Yup, that's primo thunking!
The Scoobies make a good team.
The program so far:
- def get_main_menu_choice():
- choice = ''
- while not is_menu_choice_valid(choice):
- choice = input_menu_choice()
- if not is_menu_choice_valid(choice):
- print('Please enter a valid command.')
- return choice
- def input_menu_choice():
- print()
- print('Menu')
- print('----')
- print(' S: sale')
- print(' R: return')
- print(' T: sTatus report')
- print(' Q: quit')
- choice = input('Command? ')
- choice = choice.strip().lower()
- return choice
- def is_menu_choice_valid(choice):
- valid_choice = \
- choice == 's' \
- or choice == 'r' \
- or choice == 't' \
- or choice == 'q'
- return valid_choice
- command = ''
- while command != 'q':
- command = get_main_menu_choice()
- if command == 's':
- num_sold = ticket_sale()
- current_inv -= num_sold
- if command == 'r':
- # Return
- # Get number tickets returned
- # Add to inventory
- if command == 't':
- # Show a report
- print()
- print('OK, bye!')
Testy testy

Adela
We've got an entire... not sure what to call it... big chunk now. Let's test it.
I'd call it a subsystem. A menu subsystem. Testing now is a good idea.

Ray
OK, I put the whole thing into the Spyder IDE. Now run it...
I get an error on these lines:
- num_sold = ticket_sale()
- current_inv -= num_sold
Oh, that makes sense! We haven't written that stuff yet. I'll just comment them out for now.
- # num_sold = ticket_sale()
- # current_inv -= num_sold
Commenting out
Adding #
s to the lines tells Python to ignore them, but the lines are still there. That called commenting out.

Ethan
Maybe add another line to show what the user's choice was?

Ray
OK, good idea.
- command = ''
- while command != 'q':
- command = get_main_menu_choice()
- print('Command: ' + command)
- if command == 's':
- # num_sold = ticket_sale()
- # current_inv -= num_sold
- if command == 'r':
- # Return
- # Get number tickets returned
- # Add to inventory
- if command == 't':
- # Show a report
- print()
- print('OK, bye!')

Ray
Now, let's give it a whirl.
Here's the I/O:
- Menu
- ----
- S: sale
- R: return
- T: sTatus report
- Q: quit
- Command? S
- Command: s
- Menu
- ----
- S: sale
- R: return
- T: sTatus report
- Q: quit
- Command? s
- Command: s
- Menu
- ----
- S: sale
- R: return
- T: sTatus report
- Q: quit
- Command? llamas
- Plese enter a valid command.
- Menu
- ----
- S: sale
- R: return
- T: sTatus report
- Q: quit
- Command? T
- Command: t
- Menu
- ----
- S: sale
- R: return
- T: sTatus report
- Q: quit
- Command? q
- Command: q
- OK, bye!

Adela
That's perfect! Yeehah!
Yeehah indeed.
Sale!
We have:
- command = ''
- while command != 'q':
- command = get_main_menu_choice()
- if command == 's':
- num_sold = ticket_sale()
- current_inv -= num_sold
- if command == 'r':
- # Return
- # Get number tickets returned
- # Add to inventory
- if command == 't':
- # Show a report
- print()
- print('OK, bye!')

Ethan
So ticket_sale()
just gets a number, right? Just a validation loop?

Adela
What is someone wants to buy more tickets than we have?

Ray
That's a problem. This is from the I/O from before.
- Menu
- ----
- S: sale
- R: return
- T: sTatus report
- Q: quit
- Command? s
- How many sold? 4
- Sorry, we don't have that many tickets.
- Menu
- ----
- S: sale
- R: return
- T: sTatus report
- Q: quit
- Command?

Georgina
It might be easier if we changed the main program, tested inventory there?
Maybe:
- if command == 's':
- num_sold = ticket_sale()
- if num_sold > current_inv:
- print("Sorry, we don't have that many tickets.")
- else:
- current_inv -= num_sold
- print('Cha-ching!')
Note
It's common to go back and revise your work, when you discover something you haven't allowed for. I do that all the time.

Georgina
I like it!
Hey, we have the ticket returns, too. I bet that's similar code. Maybe:
- elif command == 'r':
- num_returned = ticket_return()
- if num_returned > number of tickets sold:
- print("Sorry, we haven't sold that many tickets.")
- else:
- current_inv += num_returned

Georgina
I don't know what the missing thing in line 3 is, though.

Adela
Idea! We need a new variable!
We've got current_inv
. We subtract from that when people buy tickets, and add to it when they return tickets.
How about total_sold
, or something? Add to it when someone buys tickets, subtract when they return tickets.
- elif command == 'r':
- num_returned = ticket_return()
- if num_returned > total_sold:
- print("Sorry, we haven't sold that many tickets.")
- else:
- current_inv += num_returned

Ethan
That works! Although... we'd need to add the add/subtract code. I think?
- if command == 's':
- num_sold = ticket_sale()
- if num_sold > current_inv:
- print("Sorry, we don't have that many tickets.")
- else:
- current_inv -= num_sold
- total_sold += num_sold
- print('Cha-ching!')
- elif command == 'r':
- num_returned = ticket_return()
- if num_returned > total_sold:
- print("Sorry, we haven't sold that many tickets.")
- else:
- current_inv += num_returned
- total_sold -= num_returned
Good work!
One thing missing, though. A tiny thing, but the program won't work without it.
Hint: what does the program do with current_inv
, first thing?
What's missing?

Ethan
Oh! I got it.
Need to initialize the new variable. The top of the main program will be...
- current_inv = 50
- total_sold = 0
Right!
Numeric input

Georgina
Hey, I was thinking about ticket_sale()
and ticket_return()
. They ask the user how many tickets were sold and returned.
Here's stuff from the I/O. I've marked some things.
- Menu
- ----
- S: sale
- R: return
- T: sTatus report
- Q: quit
- Command? s
- How many sold? some
- Sorry, you must enter a number.
- How many sold? -1
- Sorry, please enter a number from 1 to 10.
- How many sold? 1
- Cha-ching!
- Menu
- ----
- S: sale
- R: return
- T: sTatus report
- Q: quit
- Command? r
- How many returned? some more
- Sorry, you must enter a number.
- How many returned? -44
- Sorry, please enter a number from 1 to 10.
- How many returned? 1

Adela
Ooo! I see what you mean, Georgina. Most everything is the same. The error messages, the 1 to 10 range.
The only stuff that's different is the prompts.
- Menu
- ----
- S: sale
- R: return
- T: sTatus report
- Q: quit
- Command? s
- How many sold? some
- Sorry, you must enter a number.
- How many sold? -1
- Sorry, please enter a number from 1 to 10.
- How many sold? 1
- Cha-ching!
- Menu
- ----
- S: sale
- R: return
- T: sTatus report
- Q: quit
- Command? r
- How many returned? some more
- Sorry, you must enter a number.
- How many returned? -44
- Sorry, please enter a number from 1 to 10.
- How many returned? 1

Ray
Oh, snap, dawg! We write one function for sales and returns! The only thing different is the prompt, so we pass that in as a parameter!
Let me try it. I'm gonna use what we did before.
Oh, I'll change the main program, too.
- def get_tickets_amount(prompt):
- is_input_ok = False
- while not is_input_ok:
- is_input_ok = True
- user_input = input(prompt)
- try:
- amount = int(user_input)
- except ValueError:
- print('Sorry, you must enter a number.')
- is_input_ok = False
- if is_input_ok:
- if amount < 1 or amount > 10:
- print('Sorry, please enter a number from 1 to 10.')
- is_input_ok = False
- return amount
- ...
- num_sold = get_tickets_amount('How many sold? ')
- ...
- num_returned = get_tickets_amount('How many returned? ')

Georgina
Ooo, some spicy code, Ray, nom nom nom.
Status report
The only thing left is the status report.
- Menu
- ----
- S: sale
- R: return
- T: sTatus report
- Q: quit
- Command? t
- Status report
- --------- ------ ------
- Ticket inventory: 48
- Tickets sold: 2

Adela
The main program will need some more stuff. A call to a new function. Like:
- ...
- elif command == 't':
- show_status_report()
- ...
What's missing from the function call?

Georgina
The function will need to show inventory and sales.

Adela
Oh, right.
- ...
- elif command == 't':
- show_status_report(current_inv, total_sold)
- ...
Try writing the function show_status_report
. Type your code in here.

Ethan
Maybe... like this?
- def show_status_report(inv, sold):
- print()
- print('Status report')
- print('------ ------')
- print('Ticket inventory:' + str(inv))
- print('Tickets sold:' + str(sold))
Looks good!
More testy testy
The Scoobies tested and tested and tested. And...
Yeeha! It worked!
The Scoobies were a team so bright,
Their code was a pure delight.
With logic so clear,
No bugs would appear,
And their programs ran smooth day and night!
Summary
- Top-down is good. Start with an overall plan of the program. That will tell you what the functions will do.
- The best programs are made of small functions that are easy to write, test, and debug.
- Use the \ to break up long code lines, to make them easier to scan.
- Commenting out: Adding
#
s to the lines tells Python to ignore them, but the lines are still there. - The Scoobies made one function to handle both numeric inputs. It takes a prompt as a parameter.
Exercise
Rock, paper, scissors cheater
Write a program to play the game rock, paper, scissors. Make sure the game wins every turn.
Here's some I/O:
Rock, paper, scissors
=====================
Rock (r), paper (p), or scissors (s) (Q to quit)? something
Please type r, p, s, or q.
Rock (r), paper (p), or scissors (s) (Q to quit)?
Please type r, p, s, or q.
Rock (r), paper (p), or scissors (s) (Q to quit)? r
My move: paper
I win!
Your points: 0
My points: 1
---------------
Rock (r), paper (p), or scissors (s) (Q to quit)? P
My move: scissors
I win!
Your points: 0
My points: 2
---------------
Rock (r), paper (p), or scissors (s) (Q to quit)? S
My move: rock
I win!
Your points: 0
My points: 3
---------------
Rock (r), paper (p), or scissors (s) (Q to quit)? q
OK, bye!
The program validates user input, as shown. It loops until the user exits. It reports the scores after each turn.
Use at least two functions. Use more if you want.
Upload a zip of your project folder. The usual coding standards apply.