Problem Set 5 - Due Mon, Mar 6
(Files for task 2 and 3 due at 11:59 pm EST, paper copy of task 1 due Monday in lecture.)
Reading
- Think Python, Ch. 8: Iteration
- Think Python, Ch. 10: Lists
- Slides and notebooks from Lec 8 and Lec 9.
- Problems and solutions from Lab 06 Lists
About this Problem Set
This problem set will give you practice with lists and loops.
- In Task 1, you will will draw memory diagrams to capture changes in lists, practicing the concepts of mutability and aliasing. Take a picture or make a copy of your solution so you can compare it to our solution before grading is finished..
- In Task 2, you will write functions that create lists through accumulation. This is a continuation of the Word Play task from PS04.
-
In Task 3 you will write building block functions for a Tic Tac Toe games, using lists.
Use this shared Google Doc to find a pair programming partner. Remember that you can work with the same partner a maximum of TWICE this semester. We will deduct 2 points per day for any students who have not recorded their names on the pair programming google doc by Thursday, Mar 2 at 11:59pm.
- The CS111 Problem Set Guide gives an overview of psets, including a detailed description of individual and partner tasks.
- In Fall 2016, students spent in average 1.2 hours on Task 1 (min = 0.2 hours, max = 2 hours); 1.6 hours on Task 2 (min = 0.5 hours, max = 6 hours); and 4 hours on Task 3 (min = 0.5 hours, max = 10 hours).
- There is no quiz for this problem set, due to the scheduling of Exam 1. Task 1 will count in place of a quiz grade (25 points), while Tasks 2 and 3 count for the code grade (55 points).
All code for this assignment is available in the ps05
folder in
the cs111/download
directory within your cs
server account.
This assignment also uses the Otter Inspector program
to help you do a final check for Task 2, Task 3, and your Honor Code form before you submit.
Task 1: Memory Diagram
This is an individual problem which you must complete on your own, though you may ask for help from the CS111 staff.
Take a picture or make a copy of your solution so you can compare it to our solution before grading is finished..
Consider the following sequence of nine statements that create and manipulate nested lists in Python:
r = [[3,4,5], [6,7], [8]]
s = r[0]
s[1] = 9
r[0][2] = r[1]
r[1].append(r.pop())
s[2][1] = r[0][0] + r[0][1]
s[2][2][0] = s[2][0] + r[1][1]
r.insert(0, r[1][2])
r[2][2] = 17
Your task is to draw a sequence of nine memory diagrams that show the state of Python's memory after each of the nine statements.
Notes
-
Take a picture or make a copy of your solution so you can compare it to our solution before grading is finished..
-
An example of what such a sequence of diagrams looks like for the problem in Lecture 8 can be found in this document. You can choose to draw the diagrams by hand, or use drawing software -- whatever is more convenient for you.
-
Label each of your nine diagrams with the statement that is executed to produce it.
-
Carefully follow the conventions for drawing memory diagrams shown in the Lecture 8 slides. In particular:
- Variables should be drawn as named boxes.
- A list of length n should be drawn as a box containing n slots labeled with the indices 0 through n-1.
- If a variable or a list slot contains a "small value" (number, boolean, None), that value should be drawn inside the variable box or list slot box.
- If a variable or a list slot contains a "big value" (list, string, tuple, or object), that should be represented by drawing an arrow from the center of the box to the representation of the big value in the memory diagram. Such arrows are essential for reasoning about the sharing of mutable values.
- An arrow should never point to a variable or a list slot, since these are not Python values. It should always point to a big value (list, string, tuple, or object).
- You can use the Python interpreter to check that you're doing the right thing in your diagrams.
For example, if you sprinkle statements like
print r
orprint r, s
in the above statements and run them, you'll see the state of memory when each print statement is executed. However, we recomment you only try these print statements AFTER you have sketched out the diagrams. The whole purpose of the diagrams is to improve your ability to predict how Python programs will behave!
Task 2: Word Search
This task is an individual problem which you must complete on your own, though you may ask for help from the CS111 staff.
This task is a continuation of Task 2 from PS4.
In that task, you wrote the functions isPalindrome
and scrabbleScore
.
You will use these functions to write three new functions that use a list of English words to find and return words that fulfill a certain property, stored in lists and tuples.
listPalindromes
returns all palindromes found in the initial list.listGoodScrabbleWords
returns a list of pairs (2-tuples) of words and their Scrabble scores for all words whose Scrabble score reaches a given threshold.bestScrabbleWord
finds the best-scoring Scrabble word in the list and returns a single pair (a 2-tuple) of this word and its Scrabble score.
Additionally, you will write a function, reverseWords
, that takes
any sentence as a string of space-separated words, and returns a
string where every word is reversed.
Fill out the body of these functions in the file wordsearch.py
following their contracts.
Notice that the file imports the functions from wordplay.py
as well
as a list of words from vocabulary.py
. This list is called
englishwords
, and is the list that your functions should search
through.
Sample Execution (run otterInspect.py
for more test cases):
In [1]: listPalindromes()
Out[1]: ['aa', 'aha', 'anna', 'bib', 'bob', 'boob', 'cc',
'civic', 'dad', 'deed', 'deified', 'did', 'dud',
'ere', 'eve', 'ewe', 'eye', 'gag', 'gig', 'hrh',
'huh', 'kayak', 'level', 'll', 'madam', 'mam', 'marram',
'minim', 'mm', 'mom', 'mum', 'nan', 'nauruan', 'noon',
'nun', 'oho', 'otto', 'pap', 'peep', 'pep', 'pip', 'poop',
'pop', 'pp', 'pup', 'qq', 'radar', 'redder', 'rotor',
'sagas', 'sees', 'sexes', 'shahs', 'sis', 'solos', 'sos',
'ss', 'stets', 'tat', 'tenet', 'tit', 'tnt', 'toot',
'tot', 'tt', 'tut', 'wow']
In [2]: listGoodScrabbleWords(35)
Out[2]: [('acquaintanceships', 35), ('compartmentalized', 35),
('compartmentalizing', 36), ('cryptographically', 35),
('czechoslovak', 35), ('czechoslovakia', 37),
('czechoslovakian', 38), ('czechoslovakians', 39),
('czechoslovaks', 36), ('electroencephalograph', 36),
('electroencephalographs', 37), ('embezzlement', 36),
('embezzlements', 37), ('extemporization', 35),
('extemporizations', 36), ('jazzily', 35), ('mozambiquean', 36),
('mozambiqueans', 37), ('overcapitalizations', 35),
('psychoanalytically', 36), ('psychokinetically', 36),
('psychopathically', 36), ('quinquagesimas', 35),
('quizzed', 35), ('quizzical', 38), ('quizzically', 43),
('quizzing', 36), ('schizophrenically', 41),
('schizophrenics', 35), ('sympathizingly', 37)]
In [3]: bestScrabbleWord()
Out[3]: ('quizzically', 43)
In [4]: reverseWords('we are never ever getting back together')
Out[4]: 'ew era reven reve gnitteg kcab rehtegot'
Notes
-
We have provided the solution file for
wordplay.py
in theps05
folder. -
In the past we have written
import
declarations likefrom cs1graphics import *
, which means get everything from thecs1graphics
module. This time, we are only importing the particular named values that will be used inwordsearch.py
:from vocabulary import englishwords from wordplay import isPalindrome, scrabbleScore, reverse
-
The
listPalindromes
function implements the filter pattern introduced in Lecture 9, thelistGoodScrabbleWords
function combines the filter and map patterns from that lecture, thebestScrabbleWord
function uses the accumulating-a-value-from-a-list pattern, and thereverseWords
implements the map pattern. -
In
reverseWords
, use thesplit
and optionally thejoin
methods. They work like this:In [1]: word = 'i like apples' In [2]: word.split() Out[2]: ['i', 'like', 'apples'] In [3]: listOfWords = ['this', 'is', 'a', 'test'] In [4]: ' '.join(listOfWords) Out[4]: 'this is a test'
- For additional ungraded mapping entertainment (use a separate
file if you write this code), implement a function called
keyboardLeopard
that takes three arguments: (1) an arbitrarily long string containing sentences, paragraphs, etc.; (2) a "bad" word to be censored; and (3) a funny word with which to replace the "bad" word. The function should return a version of the first string where every occurrence of the "bad" word is replaced by the funny word. Extend the function to take a list of "bad"/funny word pairs and rewrite the original string to replace all occurrences of each "bad" word with its corresponding funny word in this list of pairs. See here for more inspiration.
Task 3: Tic Tac Toe
This task is a partner problem in which you are required to work with a partner as part of a two-person team.
You will write a program to help you and a friend play Tic Tac Toe.
Here is an example transcript of running this program.
Current board (turn 0):
_ _ _
_ _ _
_ _ _
Player X: Enter the row,column coordinates of your play, separated by comma: 1,3
Your move is outside the board, or in a filled cell. Please try again.
Player X: Enter the row,column coordinates of your play, separated by comma: 0,0
Current board (turn 1):
X _ _
_ _ _
_ _ _
Player O: Enter the row,column coordinates of your play, separated by comma: 1,2
Current board (turn 2):
X _ _
_ _ O
_ _ _
Player X: Enter the row,column coordinates of your play, separated by comma: 0,1
Current board (turn 3):
X X _
_ _ O
_ _ _
Player O: Enter the row,column coordinates of your play, separated by comma: 0,2
Current board (turn 4):
X X O
_ _ O
_ _ _
Player X: Enter the row,column coordinates of your play, separated by comma: 1,1
Current board (turn 5):
X X O
_ X O
_ _ _
Player O: Enter the row,column coordinates of your play, separated by comma: 2,2
Current board (turn 6):
X X O
_ X O
_ _ O
Player O wins!
X X O
_ X O
_ _ O
We have provided 3 functions in the tictactoe.py
file in
your ps05
folder:
-
play(n, players)
takes a board dimension,n
, a list of unique player symbols (typically['X', 'O']
) and implements the top-level game logic using several helper functions.play
works on any board dimension and any non-empty list of unique player symbols.playNormal
runs a traditional Tic Tac Toe game. -
initializeBoard(n)
returns a blank board of sizen x n
. A board is represented as a list of lists of strings. Each inner list is a row. Blank cells are represented by the string'_'
:In []: initializeBoard(3) Out[]: [['_', '_', '_'], ['_', '_', '_'], ['_', '_', '_']]
When a cell is marked by the X player, it will be represented by
'X'
, and by'O'
when it is marked by the O player. playMove(coordinates, player, board)
marks the player's symbol at in the board cell at the given coordinates, a (row, column) pair tuple.
Notice that play
is called within the body of if __name__ == '__main__'
, as you saw in lab. The code in this block will be
executed only when run directly from tictactoe.py
, and not when it
is imported elsewhere.
Understanding play
:
You are provided with the following play
function,
which runs the TicTacToe game, invoking various helper functions along the way.
You should carefully study this play
function so that you understand
how each of the helper functions you are defining will be used.
Do not change this definition of the play
function in any way!
def play(n, players):
"""Main function:
-- n should be the dimensions of the board
-- players should be a non-empty list of unique strings, serving
as the symbol of each player.
Typically, there are two players, but any number is supported.
Creates a new board of size n x n and alternates between the given
players, prompting for moves until game is won or tied
"""
# Create n x n game board
board = initializeBoard(n)
# Iterate over turns. Board has n*n cells, so there at most n*n turns.
for turn in range(n*n):
# Select player by turn.
player = players[turn % len(players)]
# Display board.
print 'Current board (turn ' + str(turn) + '):'
displayBoard(board)
# Prompt player for a move. Update the board.
playMove(getValidMove(player, board), player, board)
# Check for a win by player.
if won(player, board):
# Display winning player and final board
print 'Player', player, 'wins!'
displayBoard(board)
return # Exit from loop early since game is done
# Reaching here means the board is full after n*n moves,
# but no player has won.
print 'Game is tied.'
displayBoard(board)
return # Not necessary to say this, but indicates play function is done.
Your task:
In this problem, you will implement the following helper functions (plus several
more of your own design) to complete the implementation of play
.
Do not change play
itself.
-
displayBoard(board)
prints the board, with a single space between cells in each row, and each row on a separate line. As with all the helper functions you defined,displayBoard
must work on anyn x n
board, not just a3 x 3
board.In [2]: emptyBoard = initializeBoard(3) In [3]: displayBoard(emptyBoard) _ _ _ _ _ _ _ _ _ In [4]: testBoard = [['X', '_', '_'], ['X', 'O', '_'], ['O', 'X', '_']] In [5]: displayBoard(testBoard) X _ _ X O _ O X _ In [6]: displayBoard([['X', '_', '_', 'X'], ['X', 'O', '_' '_'], ['O', 'X', '_', '_'], ['O', '_', 'X', '_']]) X _ _ X X O _ _ O X _ _ O _ X _
-
requestMove(player)
takes one argument, the player's name (a string), prompts the user (using the prompt shown below) for an input specifying row and column indices of the cell to mark, and returns the indices as a pair (a 2-tuple):(row, col)
.- The user should enter the indices separated by a comma in the
form
row,col
. For example, typing0,1
would indicate row 0, column 1. -
Assume that the string is well-formed (i.e., it always contains two integers separated by a comma). If the user enters a malformed string such as
'a,0'
or'1, 0'
, it is fine for your program to crash.In [7]: requestMove('O') Player O: Enter the row,column coordinates of your play, separated by comma: 0,1 Out[7]: (0,1) In [8]: requestMove('X') Player X: Enter the row,column coordinates of your play, separated by comma: 3,-2 Out[8]: (3,-2) In [9]: requestMove('X') Player X: Enter the row,column coordinates of your play, separated by comma: -273,1729 Out[9]: (-273,1729)
- The user should enter the indices separated by a comma in the
form
-
isValidMove(coordinates, board)
takes a pair (a 2-tuple) of(row, col)
coordinates and a board and returns a boolean indicating whether (True
) or not (False
) the coordinates describe an empty cell within the bounds of the board.In [10]: isValidMove((0,2), testBoard) Out[10]: True In [11]: isValidMove((3,2), testBoard) Out[11]: False In [12]: isValidMove((1,-2), testBoard) Out[12]: False
-
getValidMove(player, board)
takes a player's name (a string) and a board and usesrequestMove
andisValidMove
to prompt the user repeatedly for the coordinates of a move, until the user enters valid coordinates.getValidMove
returns the first pair (2-tuple) of valid coordinates entered by the user.In [13]: coords = getValidMove('O', testBoard) Player O: Enter the row,column coordinates of your play, separated by comma: 0,3 Your move is outside the board, or in a filled cell. Please try again. Player O: Enter the row,column coordinates of your play, separated by comma: 0,0 Your move is outside the board, or in a filled cell. Please try again. Player O: Enter the row,column coordinates of your play, separated by comma: 0,2 In [14]: coords Out[14]: (0,2) In [15]: playMove(coords, 'O', testBoard) In [16]: displayBoard(testBoard) X _ O X O _ O X _
-
won(player, board)
checks if the player (typically 'X' or 'O') has won in this board. For example, X wins if there is a row, column, or diagonal in the board of all X's.won
must work on anyn x n
board, not just3 x 3
.In [1]: won('X', [['X', 'O', '_'], ['X', 'X', 'O'], ['X', '_', 'O']]) Out[1]: True In [2]: won('O', [['X', 'O', '_'], ['X', 'X', 'O'], ['X', '_', 'O']]) Out[2]: False In [3]: won('X', [['X', '_', '_', '_'], ['O', 'X', 'O', '_'], ['_', '_', 'X', '_'], ['_', '_', 'O', 'X']]) Out[3]: True
The
won
function is a bit more involved than the others. There are many possible ways to approach this function. You should break it down into separate small helper functions to check for four distinct types of wins. Use divide-conquer-glue and at least 4 additional helper functions to implementwon
. Our solution uses 6 small helper functions in addition towon
.Start by thinking of how to implement it for a 3x3 board, by drawing out some boards on paper and seeing what makes a winning Tic Tac Toe board, before you start coding. Then generalize to
n x n
, still on paper. Finally, when you have an understanding of how to break down the problem in a general way, begin writing code.Test your individual helper functions as you go. Once you have implemented a check for one type of win, test it before implementing checks for the next type of win. Glue together your checks for the separate types of wins as simply and elegantly as possible in
won
.
Notes
-
Use the
split
method inrequestMove
.split
with no arguments splits the string into a list along spaces. To split along commas, pass ',' as an argument.In [2]: word = 'ice cream,bananas'.split(',') Out[2]: ['ice cream', 'bananas']
- Use
Ctrl+C
(press both keys simultaneously) to break out of an endless loop.
Task 4: Honor Code Form and Final Checks
As in the previous problem sets, your honor code submission for this pset will involve
defining entering values for the variables in the honorcode.py
file.
This is a Python file, so your values must be valid Python code (strings or numbers).
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, or wrap them in a if __name__ == '__main__'
block.
Points will be deducted for isolated function invocations or superfluous print statements.
Remember to run otterInspect.py
one final time before you submit
to check that your wordsearch.py
and tictactoe.py
programs work as intended, and
that your honor code form is complete.
While running your code through otterInspect is not required,
it is highly recommended since it may catch errors that you haven't noticed.
If you have issues running otterInspect.py
, first check the following:
- Your code is written in the provided program files in the
ps05
folder, and you haven't deleted or moved any of the existing files, or created new files. - You have selected "Keep directory synced to editor" when running
otterInspect.py
in Canopy - You have followed the specifications here of how your code should be organized into functions.
- If
otterInspect
says you have failed test cases, read the problem set directions carefully to verify exactly what your functions are supposed to do. In particular, be careful about the distinction between print and return, and check that the returned values are of the appropriate types. Precision is important in CS! Points will be deducted for such mistakes.
If you're unable to resolve the issue after going through this checklist, seek help from any of the course staff.
How to turn in this Problem Set
Soft-Copy Submission
- Save your final
wordsearch.py
file in theps05
folder. - Each team member should save their
tictactoe.py
file in theirps05
folder. - Save your filled-up
honorcode.py
file inps05
as well. - Note: It is critical that the name of the folder you submit is
ps05
, and your submitted files arewordsearch.py
,tictactoe.py
, andhonorcode.py
. In other words, do not rename the folder that you downloaded, do not create new files or folders, and do not delete or re-name any of the existing files in this folder. Improperly named files or functions will incur penalties. - Drop your entire
ps05
folder in yourdrop
folder on thecs
server using Cyberduck by 11:59pm on Monday, Mar 6th, 2017. - Failure to submit your code before the deadline will result in zero credit for the code portion of PS5.
Hard-Copy Submission
Bring to class on Monday the paper-copy of your Task 1. Note your name and section on your submission. Take a picture or make a copy of your solution so you can compare it to our solution before grading is finished..