A document from MCS 260 Fall 2021, instructor David Dumas. You can also get the notebook file.

MCS 260 Fall 2021 Worksheet 15

  • Course instructor: David Dumas

Topics

This worksheet focuses on threads and tkinter.

Resources

The main resources to refer to for this worksheet are:

(Lecture videos are not linked on worksheets, but are also useful to review while working on worksheets. Video links can be found in the course course Blackboard site.)

1. Red Light Green Light

The playground game

Red Light Green Light (RLGL), also known as Statues, is a playground game in which players attempt to cross from one side of a field to the other. However, to avoid disqualification, players must follow rules that limit their progress. The game is supervised by another participant, the "caller" or "curator", who stands at the end of the field that players are trying to reach.

When the caller yells out "Green Light" and turns away from field, players are allowed to move as they like (e.g. running toward the side where the caller stands). But when the caller yells "Red Light" and turns toward the field, every player must stand completely still. If the caller sees any player moving (e.g. due to failure to come to a complete stop before the caller turns), that player is disqualified from the round.

The caller alternates between "Red Light" and "Green Light" on whatever schedule they like, usually choosing a somewhat random sequence of delays so that players moving forward will be forced to stop unexpectedly. Players who reach the caller's side of the field without being disqualified win that round. The caller is informally evaluated on the basis of how many players are disqualified (with higher numbers meaning a better performance).

Your task

Make a Python program that plays RLGL, with the program functioning as the caller and the user being the only player.

Each time the user presses Enter, it corresponds to taking one step forward. Their current position is indicated by a simple text graphics depiction of the field, e.g.

The user (represented by @) is close to the start:

start|-----@------------------------|goal

The user is near the goal:

start|---------------------------@--|goal

The line of text graphics representing the field is printed anew after each step. You can make the field as wide as you prefer, but I recommend something between 20 and 72 characters.

At random times, the program will print "RED LIGHT!" or "GREEN LIGHT!" on the screen, and the user must follow these cues. The time between messages will always be between 0.5 and 2.5 seconds. If the user presses Enter while red light is active, the game ends with a message indicating the user lost.

If the player reaches the goal, the program instead prints a congratulatory message.

How to do this

Make a program with two threads. The main thread handles the user input (the function input() will wait for the user to press enter) as well as printing the board. The other thread runs in the background and is only responsible for timing and printing the RL/GL messages.

The background thread object should be created so that it has an attribute .greenlight that is a Boolean value, set to True whenever "Green Light" is announced, and to False when "Red Light" is announced. Its main .run() method should just loop forever, flipping this attribute and printing messages with random delays.

The main thread should check the value of the .greenlight attribute of the thread after each time the user presses Enter, and respond accordingly.

Improvements

If time allows, add a feature where each announcement of a Red Light has a 0.25-second grace period before movement causes disqualification. If Enter is pressed during the grace period, the user does not move forward and does not lose the game. Green Light announcements should have no such grace period.

Sample gameplay

Here's a screencast showing a brief session of a game, including the optional grace period. (You'll notice the user presses enter after "Red Light" in one case, but the game allows it because it happened during the grace period.)

2. Decimal to other base converter

Make a tkinter GUI program that lets the user enter a decimal integer (e.g. 1546) in a Entry widget, and which displays the same number in a different base in another Entry widget. The base to use should be specified using a slider (Scale widget) which allows the selection of any base between 2 and 36.

You can find a mockup of the GUI below, but you can use a more spartan interface if you like. For example, the labels on either side of the slider are optional.

Starter code: base conversion function

Since base conversions are not the main point of this worksheet, I've provided a function below that takes an integer n and base b and returns a string that represents n in base b. (If you finish both exercises on this worksheet early, it would be a good idea to try to write your own function that does the same thing, without referring to this one while doing so.)

In [30]:
def decimal_to_base(n,b):
    """
    Convert integer `n` to string in base `b`, where base-`b` digits
    beyond the first 10 are given by uppercase letters a-z.
    """
    DIGITS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    # If n is a string or float, convert to int first.
    if not isinstance(n,int):
        n = int(n)
    if n < 0:
        raise ValueError("n must be greater than or equal to 0")
    if b < 2 or b > 36:
        raise ValueError("b must be between 2 and 36")
    L = []
    while (not L) or n > 0:
        L.append(DIGITS[n%b])
        n = n//b
    return "".join(L[::-1])

Desired interface

Here's an animation showing usage of a finished GUI meeting the specs given above. In your solution, the labels and indicators next to the sliders are optional. Only a slider and two text entry boxes are required.

Revision history

  • 2021-11-29 Initial release