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

MCS 260 Fall 2021 Worksheet 14 Solutions

  • Course instructor: David Dumas

Topics

This worksheet focuses on tkinter and working with dates and times.

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. Tuesdays

Write a program that accepts two command line arguments, each of which is expected to be a date in the format YYYY-MM-DD, and which then prints the date of every Tuesday that occurs between the given dates.

I recommend doing this with a loop that starts at the first date and steps forward, one day at a time, checking each time whether the day of week is a Tuesday. There is a convenient method .weekday() of datetime.datetime objects that returns an integer, where 0 means Monday, 1 means Tuesday, etc..

Also, you could do this by parsing strings into datetime.datetime objects and then stepping forward by 24 hours at a time, but I suggest you convert the datetime.datetime objects to datetime.date objects so that the time of day is ignored. Each datetime.datetime object has a method .date() that converts it to a date, discarding the time information.

Sample usage:

PS C:\Users> python3 tuesdays.py 2021-08-23 2021-12-03
2021-08-24
2021-08-31
2021-09-07
2021-09-14
2021-09-21
2021-09-28
2021-10-05
2021-10-12
2021-10-19
2021-10-26
2021-11-02
2021-11-09
2021-11-16
2021-11-23
2021-11-30
PS C:\Users>

Solution

Shown below, and also available at samplecode/dates_and_times/tuesdays.py

In [ ]:
# Contents of tuesdays.py
"""
Tuesdays between two dates
"""

import datetime
import sys

def usage():
    "Show usage message and quit"
    print("USAGE: {} DATE1 DATE2".format(sys.argv[0]))
    exit(1)

if __name__=="__main__":
    if len(sys.argv) < 3:
        usage()
    dt1 = datetime.datetime.strptime(sys.argv[1],"%Y-%m-%d")
    dt2 = datetime.datetime.strptime(sys.argv[2],"%Y-%m-%d")

    date1 = dt1.date()
    date2 = dt2.date()

    day = datetime.timedelta(days=1)

    while date1 <= date2:
        if date1.weekday() == 1:
            print(date1.strftime("%Y-%m-%d"))
        date1 += day

2. Whack-a-mole

You might be familiar with a popular carnival game called "Whack-a-mole" or "Mogura Taiji", where a player must use a mallet to strike small objects that pop up from an array of holes.

Make a tkinter GUI version of this game, where the holes are replaced by buttons, one of which is labeled "Mole" while the others say nothing.

Clicking on the mole button should make the mole move to a different location (at random). Clicking on any other button should end the game immediately.

If the user hits the mole 10 times in a row, the game should end and print a message congratulating the user, and telling them how many seconds it took to complete the game. The timing should begin when they press a button for the first time (rather than when the window opens).

A template program has been provided which takes care of some basic GUI setup:

It is recommended that you complete this problem by editing that program. The starter program also demonstrates the correct way to end a tkinter GUI application, which is to call the .quit() method on the tkinter.Tk object.

Solution

Shown below, and also available at samplecode/guimole.py

In [ ]:
# content of guimole.py
"""GUI demo: whack-a-mole"""
# MCS 260 Fall 2021
# David Dumas
import random
import time
import tkinter
import tkinter.ttk

class MoleGame(tkinter.Tk):
    "Whack-a-mole game"
    def __init__(self):
        "Create main window and buttons"
        # Call the tkinter.Tk constructor
        super().__init__()
        # Now add the GUI elements we want
        self.title("Hit the mole")

        self.b0 = tkinter.ttk.Button(self,text="Mole",command=self.click0)
        self.b0.grid(row=0,column=0,padx=5,pady=5,ipadx=10,ipady=10,sticky="nsew")

        self.b1 = tkinter.ttk.Button(self,text="Mole",command=self.click1)
        self.b1.grid(row=0,column=1,padx=5,pady=5,ipadx=10,ipady=10,sticky="nsew")

        self.b2 = tkinter.ttk.Button(self,text="Mole",command=self.click2)
        self.b2.grid(row=0,column=2,padx=5,pady=5,ipadx=10,ipady=10,sticky="nsew")

        self.b3 = tkinter.ttk.Button(self,text="Mole",command=self.click3)
        self.b3.grid(row=0,column=3,padx=5,pady=5,ipadx=10,ipady=10,sticky="nsew")

        self.buttons = [self.b0, self.b1, self.b2, self.b3]

        self.n = 0
        self.molepos = 0
        self.move_mole()

    def click0(self):
        if self.molepos == 0:
            self.move_mole()
        else:
            self.fail()

    def click1(self):
        if self.molepos == 1:
            self.move_mole()
        else:
            self.fail()

    def click2(self):
        if self.molepos == 2:
            self.move_mole()
        else:
            self.fail()

    def click3(self):
        if self.molepos == 3:
            self.move_mole()
        else:
            self.fail()

    def move_mole(self):
        if self.n == 0:
            self.t0 = time.time()
        elif self.n == 10:
            print("YOU WIN!  Time={:.1f}".format(time.time()-self.t0))
            self.quit()
        self.n += 1
        self.molepos = (self.molepos + random.randrange(1,3)) % 4
        for i,b in enumerate(self.buttons):
            if i == self.molepos:
                btn_text = "Mole"
            else:
                btn_text = ""
            b.config(text=btn_text)

    def fail(self):
        print("Sorry, better luck next time.")
        self.quit()

g = MoleGame()
g.mainloop()

Revision history

  • 2021-11-21 Initial release