Problem Set 11 - Due Tuesday, Dec 11 at 4:30pm

Reading

  1. Lecture slides and notebook: Lec 22
  2. Lecture slides and notebook: Lec 23 Objects, Classes, and Inheritance
  3. Lecture slides and code folder: Lec 24 Animation
  4. Lab notes and exercises: Lab 13 Objects and Animation

About this Problem Set

This problem set is intended to give you practice with file traversal, classes, objects and inheritance in the context of animation.

This assignment will not have a reflection or a quiz. 40 points on this assignment are allocated to the code in Task 1, 40 points are allocated to the code in Task 2, and 20 points are allocated to filling out a survey form on resources for help in CS111 in Task 3:

Notes:



Task 1: File Tree Sizes

This task is a partner-recommended problem in which it is strongly recommended that you work with a partner as part of a two-person team. If you work with a partner on both Tasks 1 and 2, it must be the same partner. Use this shared Google Doc to find a partner for this pset.

This task involves defining fruitful recursive functions that return information from a file tree. Before starting this task, you should review terminology and examples from Lec 22, Recursive File Traversal.

Background

Review the background information from ps10 task 4.

Recall that in this task, you should ignore the size of bookkeeping information associated with a directory, and you should ignore hidden files.

All code for this task should be written in the provided fileTreeSizes.py file in the ps11 folder.

IMPORTANT: All parts of this task are independent of one another. It is possible to solve each part without having solved the previous parts.

Subtask 1a: fileTreeSize

Define a function fileTreeSize that takes as its single argument the pathname (a string) of a file or directory relative to the current working directory.

For nondirectory files, fileTreeSize returns the same size as os.path.getsize (unless the file is a hidden file, in which case it returns 0):

In [1]: fileTreeSize('testfiles/html/img/100.png')
Out[1]: 995

In [2]: fileTreeSize('testfiles/txt/hello.txt')
Out[2]: 180

In [3]: fileTreeSize('testfiles/hollow/.hidden')
Out[3]: 0

For directories, fileTreeSize returns the sum of the sizes of the nonhidden nondirectory files in the tree rooted at the directory:

In [4]: fileTreeSize('testfiles/txt')
Out[4]: 300 # 180 + 120

Note that the size of the bookkeeping information for testfiles/txt is ignored by fileTreeSize, as is the size of any hidden files it encounters.

As another example, consider the size of the whole testfiles directory:

In [5]: os.listdir('testfiles')
Out[5]:
['.secret', # hidden file; ignore
'ballast.txt', 'html', 'hollow', 'py', 'txt']

In [6]: [fileTreeSize(os.path.join('testfiles', f)) for f in
   ...:   ['ballast.txt', 'html', 'hollow', 'py','txt']]
Out[6]: [250, 2300, 0, 150, 300] # sizes of 5 nonhidden 
                                  # children of testfiles

In [7]: fileTreeSize('testfiles')
Out[7]: 3000 # 250 + 2300 + 0 + 150 + 300

Flesh out the skeleton definition of fileTreeSize in fileTreeSizes.py.

Notes


Subtask 1b: fileTreeList

Define a function fileTreeList that takes as its single argument the pathname (a string) of a file or directory relative to the current working directory. It should return a list of all the pathnames (relative to the current working directory) of the nonhidden files and directories in the file tree rooted at the given pathname. The order of these pathnames should be similar to the order in which the files are printed by printFileTree in slide 22-17 in the Lec 22 slides except that a directory should come right after all its descendants rather than right before all its descendants. For example:

In [8]: fileTreeList('testfiles/txt/hello.txt')
Out[8]: ['testfiles/txt/hello.txt']

In [9]: fileTreeList('testfiles/txt')
Out[9]:
['testfiles/txt/hello.txt',
'testfiles/txt/world.txt',
'testfiles/txt']

In [10]: fileTreeList('testfiles')
Out[10]:
['testfiles/ballast.txt',
'testfiles/hollow',
'testfiles/html/img/100.png',
'testfiles/html/img/200.png',
'testfiles/html/img/balance.txt',
'testfiles/html/img',
'testfiles/html/small.html',
'testfiles/html/tiny.html',
'testfiles/html',
'testfiles/py/small.py',
'testfiles/py/tiny.py',
'testfiles/py',
'testfiles/txt/hello.txt',
'testfiles/txt/world.txt',
'testfiles/txt',
'testfiles']

Notes:


Subtask 1c: fileTreeListWithSizes

Define a function fileTreeListWithSizes that takes as its single argument the pathname (a string) of a file or directory relative to the current working directory. It should return a list of pairs where each pair is a tuple of

  1. a pathname (relative to the current working directory); and
  2. the file tree size of the file or directory with that pathname.

The pairs should have the same order as the pathnames returned by fileTreeList in Subtask 3b That is, a pair whose pathname is a directory should come right after the pairs for all its descendants.

For example:

In [11]: fileTreeListWithSizes('testfiles/txt/hello.txt')
Out[11]: [('testfiles/txt/hello.txt', 180)]

In [12]: fileTreeListWithSizes('testfiles/txt')
Out[12]:
[('testfiles/txt/hello.txt', 180),
('testfiles/txt/world.txt', 120),
('testfiles/txt', 300)]

In [13]: fileTreeListWithSizes('testfiles')
Out[13]:
[('testfiles/ballast.txt', 250),
('testfiles/hollow', 0),
('testfiles/html/img/100.png', 995),
('testfiles/html/img/200.png', 995),
('testfiles/html/img/balance.txt', 10),
('testfiles/html/img', 2000),
('testfiles/html/small.html', 200),
('testfiles/html/tiny.html', 100),
('testfiles/html', 2300),
('testfiles/py/small.py', 100),
('testfiles/py/tiny.py', 50),
('testfiles/py', 150),
('testfiles/txt/hello.txt', 180),
('testfiles/txt/world.txt', 120),
('testfiles/txt', 300),
('testfiles', 3000)]

Notes:



Task 2: Spinner Animations

This task is a partner-recommended problem in which it is strongly recommended that you work with a partner as part of a two-person team. If you work with a partner on both Tasks 1 and 2, it must be the same partner. Use this shared Google Doc to find a partner for this pset.

This task involves the animation system you have seen in lecture and lab. In this system, you will define three classes of animation sprites related by inheritance.

Subtask 1a: Spinner

The first class, called Spinner, specifies sprites that look like circular disks with a color on each side that appear to be spinning around a vertical axis, as in this video:

In this subtask, your goal is to flesh out the declaration of the Spinner class (a subclass of Sprite) in the file Spinner.py so that it describes the behavior of spinning two-colored disks. Instances of Spinner are created via the following constructor function call:

    Spinner(centerX, centerY, radius, deltaRadius, color1, color2)

This should create a spinning disk with radius radius whose center is at the position (centerX, centerY) on the animation's canvas. The disk displays two "sides", one of which is colored color1, and the other of which is colored color2. Initially a circle of color1 should be displayed.

The disk is actually an ellipse that initially has a width and height that are twice the radius. The "spinning" motion is simulated by changing the width of the ellipse by twice deltaRadius on every invocation of the step method for this sprite. Initially, the width of the ellipse decreases by twice deltaRadius on every step until the next width would be less than or equal to zero; at this point the ellipse width is set to 1, the color changes to the other color, and the width of the ellipse begins to increases by twice deltaRadius on every step. This continues until the next width would be greater than twice the radius, at which point the radius is set to twice radius, and the ellipse goes into shrinking mode again.

The height of the ellipse is twice the radius and never changes. The center point (centerX, centerY) of the ellipse also does not change.

Once you have implemented the Spinner class, you can execute the animation in the file spinnerAnimation.py, which should give the behavior shown in the above video.

"""A sample animation with four Spinner sprites"""

from Animation import *
from Spinner import *

spinners = Animation(800, 600, 'white', 'Spinners')

spinners.addSprite(Spinner(150, 250, 100, 3, 'blue', 'yellow'))
spinners.addSprite(Spinner(400, 300, 275, 2, 'red', 'green'))
spinners.addSprite(Spinner(550, 200, 50, 5, 'pink', 'gray'))
spinners.addSprite(Spinner(650, 450, 125, 1, 'cyan', 'magenta'))

# spinners.setDebug(False) # Uncomment this to stop printing debugging trace
spinners.start()

Notes:

  Spinners[#sprites=4; step#=0; Sprites:
    Spinner<col=blue, rad=100, dRad=-3, wid=200.0;>
    Spinner<col=red, rad=275, dRad=-2, wid=550.0;>
    Spinner<col=pink, rad=50, dRad=-5, wid=100.0;>
    Spinner<col=cyan, rad=125, dRad=-1, wid=250.0;>]

  Spinners[#sprites=4; step#=1; Sprites:
    Spinner<col=blue, rad=100, dRad=-3, wid=194.0;>
    Spinner<col=red, rad=275, dRad=-2, wid=546.0;>
    Spinner<col=pink, rad=50, dRad=-5, wid=90.0;>
    Spinner<col=cyan, rad=125, dRad=-1, wid=248.0;>]

  Spinners[#sprites=4; step#=2; Sprites:
    Spinner<col=blue, rad=100, dRad=-3, wid=188.0;>
    Spinner<col=red, rad=275, dRad=-2, wid=542.0;>
    Spinner<col=pink, rad=50, dRad=-5, wid=80.0;>
    Spinner<col=cyan, rad=125, dRad=-1, wid=246.0;>]

Subtask 1b: BouncingSpinner

In this subtask, you will define a BouncingSpinner class whose instances are spinners that move up and down vertically. Whenever the y-coordinate of the sprite's center point goes off the top or bottom of the canvas, the sprite "bounces" by changing direction, as illustrated in the following video.

The file BouncingSpinner.py contains the skeleton of a BouncingSpinner class that inherits from the Spinner class. You should flesh out this class definition so that instances of the class behave as shown in the video.

Instance of BouncingSpinner are created via the following constructor function call:

    BouncingSpinner(centerX, centerY, radius, deltaRadius, 
                    color1, color2, deltaY)

The first six parameters have the same meaning as for the Spinner constructor. The seventh parameter, deltaY, specifies how many vertical units the center of the sprite should move at each step.

Once you have implemented the BouncingSpinner class, you can execute the animation in the file bouncingSpinnerAnimation.py, which should give the behavior shown in the above video.

"""A sample animation with four BouncingSpinner sprites"""

from Animation import *
from BouncingSpinner import *

bouncingSpinners = Animation(800, 600, 'white', 'Bouncing Spinners')

bouncingSpinners.addSprite( \
    BouncingSpinner(150, 250, 100, 3, 'blue', 'yellow', 5))
bouncingSpinners.addSprite( \
    BouncingSpinner(400, 300, 275, 2, 'red', 'green', -1))
bouncingSpinners.addSprite( \
    BouncingSpinner(550, 200, 50, 5, 'pink', 'gray', 2))
bouncingSpinners.addSprite( \
    BouncingSpinner(650, 450, 125, 1, 'cyan', 'magenta', -3))

# bouncingSpinners.setDebug(False) # Uncomment this to stop printing debugging trace      
bouncingSpinners.start()

Notes:

Bouncing Spinners[#sprites=4; step#=0; Sprites:
  BouncingSpinner<col=blue, rad=100, dRad=-3, wid=200.0; dY=5, maxY=600, y=250.0;>
  BouncingSpinner<col=red, rad=275, dRad=-2, wid=550.0; dY=-1, maxY=600, y=300.0;>
  BouncingSpinner<col=pink, rad=50, dRad=-5, wid=100.0; dY=2, maxY=600, y=200.0;>
  BouncingSpinner<col=cyan, rad=125, dRad=-1, wid=250.0; dY=-3, maxY=600, y=450.0;>]

Bouncing Spinners[#sprites=4; step#=1; Sprites:
  BouncingSpinner<col=blue, rad=100, dRad=-3, wid=194.0; dY=5, maxY=600, y=255.0;>
  BouncingSpinner<col=red, rad=275, dRad=-2, wid=546.0; dY=-1, maxY=600, y=299.0;>
  BouncingSpinner<col=pink, rad=50, dRad=-5, wid=90.0; dY=2, maxY=600, y=202.0;>
  BouncingSpinner<col=cyan, rad=125, dRad=-1, wid=248.0; dY=-3, maxY=600, y=447.0;>]

Bouncing Spinners[#sprites=4; step#=2; Sprites:
  BouncingSpinner<col=blue, rad=100, dRad=-3, wid=188.0; dY=5, maxY=600, y=260.0;>
  BouncingSpinner<col=red, rad=275, dRad=-2, wid=542.0; dY=-1, maxY=600, y=298.0;>
  BouncingSpinner<col=pink, rad=50, dRad=-5, wid=80.0; dY=2, maxY=600, y=204.0;>
  BouncingSpinner<col=cyan, rad=125, dRad=-1, wid=246.0; dY=-3, maxY=600, y=444.0;>]

Subtask 1c: RotatingBouncingSpinner

In this subtask, you will define a RotatingBouncingSpinner class whose instances are bouncing spinners that also rotate about their center points, as illustrated in the following video. (This animation is slower than the others because the .getAngle() method is costly.)

The file RotatingBouncingSpinner.py contains the skeleton of a RotatingBouncingSpinner class that inherits from the BouncingSpinner class. You should flesh out this class definition so that instances of the class behave as shown in the video.

Instances of RotatingBouncingSpinner are created via the following constructor function call:

    RotatingBouncingSpinner(centerX, centerY, radius, deltaRadius, 
                            color1, color2, deltaY, deltaAngle):

The first seven parameters have the same meaning as for the BouncingSpinner constructor. The eighth parameter, deltaAngle, specifies the angle by which the spinner ellipse should rotate clockwise on each step.

Once you have implemented the RotatingBouncingSpinner class, you can execute the animation in the file rotatingBouncingSpinnerAnimation.py, which should give the behavior shown in the above video.

"""A sample animation with four RotatingBouncingSpinner sprites"""

from Animation import *
from RotatingBouncingSpinner import *

rotatingBouncingSpinners = Animation(800, 600, 'white', 'Rotating Bouncing Spinners')

rotatingBouncingSpinners.addSprite( \
    RotatingBouncingSpinner(150, 250, 100, 3, 'blue', 'yellow', 5, -3))
rotatingBouncingSpinners.addSprite( \
    RotatingBouncingSpinner(400, 300, 275, 2, 'red', 'green', -1, 2))
rotatingBouncingSpinners.addSprite( \
    RotatingBouncingSpinner(550, 200, 50, 5, 'pink', 'gray', 2, -4))
rotatingBouncingSpinners.addSprite( \
    RotatingBouncingSpinner(650, 450, 125, 1, 'cyan', 'magenta', -3, 1))

# rotatingBouncingSpinners.setDebug(False) # Uncomment this to stop printing debugging trace                                                      
rotatingBouncingSpinners.start()

Notes:

Rotating Bouncing Spinners[#sprites=4; step#=0; Sprites:
  RotatingBouncingSpinner<col=blue, rad=100, dRad=-3, wid=200.0; dY=5, maxY=600, y=250.0; dAng=-3, ang=0.0;>
  RotatingBouncingSpinner<col=red, rad=275, dRad=-2, wid=550.0; dY=-1, maxY=600, y=300.0; dAng=2, ang=0.0;>
  RotatingBouncingSpinner<col=pink, rad=50, dRad=-5, wid=100.0; dY=2, maxY=600, y=200.0; dAng=-4, ang=0.0;>
  RotatingBouncingSpinner<col=cyan, rad=125, dRad=-1, wid=250.0; dY=-3, maxY=600, y=450.0; dAng=1, ang=0.0;>]

Rotating Bouncing Spinners[#sprites=4; step#=1; Sprites:
  RotatingBouncingSpinner<col=blue, rad=100, dRad=-3, wid=194.0; dY=5, maxY=600, y=255.0; dAng=-3, ang=-3.0;>
  RotatingBouncingSpinner<col=red, rad=275, dRad=-2, wid=546.0; dY=-1, maxY=600, y=299.0; dAng=2, ang=2.0;>
  RotatingBouncingSpinner<col=pink, rad=50, dRad=-5, wid=90.0; dY=2, maxY=600, y=202.0; dAng=-4, ang=-4.0;>
  RotatingBouncingSpinner<col=cyan, rad=125, dRad=-1, wid=248.0; dY=-3, maxY=600, y=447.0; dAng=1, ang=1.0;>]

Rotating Bouncing Spinners[#sprites=4; step#=2; Sprites:
  RotatingBouncingSpinner<col=blue, rad=100, dRad=-3, wid=188.0; dY=5, maxY=600, y=260.0; dAng=-3, ang=-6.0;>
  RotatingBouncingSpinner<col=red, rad=275, dRad=-2, wid=542.0; dY=-1, maxY=600, y=298.0; dAng=2, ang=4.0;>
  RotatingBouncingSpinner<col=pink, rad=50, dRad=-5, wid=80.0; dY=2, maxY=600, y=204.0; dAng=-4, ang=-8.0;>
  RotatingBouncingSpinner<col=cyan, rad=125, dRad=-1, wid=246.0; dY=-3, maxY=600, y=444.0; dAng=1, ang=2.0;>]

Task 3: Resources for Help in CS111 Questionnaire

In this task we ask you to fill out this questionnaire about resources for help in CS111.

Completely filling out this form is worth 20 out of 100 points on this pset.


Task 4: Honor Code File

As in the previous psets, your honor code submission for this pset will involve defining entering values for the variables in the honorcode.py file.

If you wrote any function invocations or print statements in your Python files to test your code, please remove them, comment them out before you submit. Points will be deducted for superfluous print statements.


How to turn in this Problem Set

Soft-copy submission

Hard-copy submission

There is no hard copy submission. Problem set submission is entirely electronic.