Do not use sets, dictionaries, try/except, classes, or recursion this week.
The autograder (or a manual CA review later) will reject your submission entirely if you do.
Like in the previous assignment, we will grade your code based on whether
it follows the 15-112 style guide. We
may deduct up to 10 points from your overall grade for style errors. We highly
recommend that you try to write clean code with good style all along rather
than fixing your style issues at the end. Good style helps you code faster and
with fewer bugs. It is totally worth it. In any case, style grading already
started, so please use good style from now on!
You will notice that the skeleton file only includes testcases for some of the functions you are writing. You should write testcases for the others. (You can find some nice ways to test in the write-up below, but you will need to translate those to actual testcases.)
-
nondestructiveRemoveRepeats(L) [10 pts]
Important Note: to receive any credit for this problem,
you may not simply create a copy of L and then call the
destructiveRemoveRepeats function below. Instead, you must
create the resulting list from scratch here. Also, note
that the autograder has no way to check this, so our CA's will
check this manually after the hw deadline.
Write the function nondestructiveRemoveRepeats(L), which
takes a list L and nondestructively returns a new list in
which any repeating elements in L are removed. For example:
assert(nondestructiveRemoveRepeats([1, 3, 5, 3, 3, 2, 1, 7, 5]) ==
[1, 3, 5, 2, 7])
Also:
L = [1, 3, 5, 3, 3, 2, 1, 7, 5]
assert(nondestructiveRemoveRepeats(L) == [1, 3, 5, 2, 7])
assert(L == [1, 3, 5, 3, 3, 2, 1, 7, 5]) # nondestructive!
Note that the values in the resulting list occur in the order they
appear in the original list, but each occurs only once in the result.
Also, since this is a nondestructive function, it returns the
resulting list.
-
destructiveRemoveRepeats(L) [10 pts]
Important Note: this is the analog of the previous
important note. Here, to receive any credit for this problem,
you may not simply call nondestructiveRemoveRepeats(L)
and then somehow remove all the elements in L and then
append the elements from that call. Instead, you must
destructively modify the list as you go. Also, again, note
that the autograder has no way to check this, so our TA's will
check this manually after the hw deadline.
Write the function destructiveRemoveRepeats(L), which implements the
same function destructively. Thus, this function should directly modify
the provided list to not have any repeating elements. Since this
is a destructive function, it should not return any value at all
(so, implicitly, it should return None).
For example:
L = [1, 3, 5, 3, 3, 2, 1, 7, 5]
assert(destructiveRemoveRepeats(L) == None)
assert(L == [1, 3, 5, 2, 7]) # destructive!
-
lookAndSay(a) [15 pts]
First, read about look-and-say numbers
here. Then, write the function lookAndSay(a) that takes a list of numbers and returns a list of numbers that results from "reading off" the initial list using the look-and-say method, using tuples for each (count, value) pair. For example:
lookAndSay([]) == []
lookAndSay([1,1,1]) == [(3,1)]
lookAndSay([-1,2,7]) == [(1,-1),(1,2),(1,7)]
lookAndSay([3,3,8,-10,-10,-10]) == [(2,3),(1,8),(3,-10)]
Hint: you'll want to keep track of the current number and how many times it has been seen.
-
inverseLookAndSay(a) [15 pts]
Write the function inverseLookAndSay(a) that does the inverse of the previous problem, so that, in general:
inverseLookAndSay(lookAndSay(a)) == a
Or, in particular:
inverseLookAndSay([(2,3),(1,8),(3,-10)]) == [3,3,8,-10,-10,-10]
-
maxProfit(prices) [15 pts]
Write the function maxProfit(prices)
which
takes a list prices
where prices[i]
is the price of a given stock on the ith day.
You want to maximize your profit by choosing a single day to buy one stock and
choosing a different day in the future to sell it.
The function should return the maximum profit you can be achieved from any
buy-sell trade. If no profit can be achieved, the function should return 0.
For example, maxProfit([7,1,5,3,6,4])
returns 5 because the best
possible trade is to buy on day 1 (price = 1) and sell on day 4 (price = 6),
thus the maximum profit is 6-1 = 5. Note that buying on day 1 and selling on day
0 is not allowed because you must buy before you sell.
maxProfit([4,3,2,1])
should return 0 because the stock prices are
declining and any buy-sell trade would generate losses.
-
bestScrabbleScore(dictionary, letterScores, hand) [15 pts]
Background: In a Scrabble-like game, players each have a hand, which is a list
of tiles that represent lowercase letters plus one or more blank tiles. There is
also a dictionary, which is a list of legal words (all in lowercase letters).
And there is a list of letterScores, which is length 26, where letterScores[i]
contains the point value for the tile that represents the ith character in the
alphabet (so letterScores[0] contains the point value for tile 'a'). Players can
use some or all of the tiles in their hand and arrange them in any order to form
words. In addition, the game has two blank tiles that are unmarked and carry no
point value. The blank tiles can stand in to be any letter. The point value for
a word is 0 if it is not in the dictionary, otherwise it is the sum of the point
values of each tile used to form the word, according to the letterScores list
(pretty much as it works in actual Scrabble).
In case you are interested, here is a list of the actual letterScores for Scrabble:
letterScores = [
# a, b, c, d, e, f, g, h, i, j, k, l, m
1, 3, 3, 2, 1, 4, 2, 4, 1, 8, 5, 1, 3,
# n, o, p, q, r, s, t, u, v, w, x, y, z
1, 1, 3,10, 1, 1, 1, 1, 4, 4, 8, 4,10
]
Note that your function must work for any list of letterScores as is provided by the caller.
With this in mind, write the function bestScrabbleScore(dictionary, letterScores, hand) that takes 3 lists -- dictionary (a list of lowercase words), letterScores (a list of 26 integers), and hand (a list of lowercase characters) -- and returns a tuple of the highest-scoring word in the dictionary that can be formed by some arrangement of some subset of letters in the hand, followed by its score. In the case of a tie, the first element of the tuple should instead be a list of all such words in the order they appear in the dictionary. If no such words exist, return None.
Here's one example using the actual letter scores for Scrabble and a custom dictionary:
myDict = ['told', 'led', 'old', 'oldest', 'do', 'let', 'toledo']
myHand = ['l','d','e','o','t']
bestScrabbleScore(myDict, letterScores, myHand) == ('told', 5)
Here's the same example but now the hand includes one blank tile (denoted as '*'), which acts as a wildcard and allows to form longer (and higher score) words.
myDict = ['told', 'led', 'old', 'oldest', 'do', 'let', 'toledo']
myHand = ['*', 'l','d','e','o','t']
bestScrabbleScore(myDict, letterScores, myHand) == (['oldest', 'toledo'], 6)
Hint: you should definitely write helper functions for this problem! In fact, try to think of at least two helper functions you could use before writing any code at all.
Note: there is no fixed dictionary here. Each time we call the function, we may provide a different dictionary! It may contain 100 words or perhaps 100,000 words.
Another Hint: You may not use itertools for this problem! In fact, do not create permutations of the letters at all -- that is, do not try to generate all the possible ways to arrange the hand! If you do, your solution will take too long and the autograder will time out (hence, fail). There's a much simpler way to find all the legal words you can create...
Yet one more hint: Consider: if you had a single word w, and you have a single hand h, could you write a function f(w,h) (perhaps with a better name) that tells you whether or not that word could be constructed using that hand? And how might you use that function to help solve this problem?
-
Code-Writing: Sudoku Logic [20 pts]
This problem involves the game Sudoku, a game which is played on a 32x32 grid,
though we will generalize it to the N2xN2 case, where N is a positive integer.
First, read the top part (up to History) of the Wikipedia page on Sudoku
so we can agree on the rules. For terminology, we will refer to each of the N2 different N-by-N sub-regions as "blocks".
The following figure shows each of the 4 blocks in a 22x22 completed puzzle highlighted in a different color:
While the next example shows the blocks of a 32x32 incomplete puzzle:
For our purposes, we will number the blocks from 0 to N2-1 (hence, 0 to 8 in the figure), with block 0 in the top-left (red), moving across and then down (so, in the figure, block 1 is orange, block 2 is yellow, block 3 is green, block 4 is blue, block 5 is purple, block 6 is gray, block 7 is brown, and block 8 is tan). We will refer to the top row as row 0, the bottom row as row (N2-1), the left column as column 0, and the right column as column (N2-1).
A Sudoku is in a legal state if all N4 cells are either blank (0) or contain a single integer from 1 to N2 (inclusive), and if each integer from 1 to N2 occurs at most once in each row, each column, and each block. A Sudoku is solved if it is in a legal state and contains no blanks.
We will represent a Sudoku board as an N2xN2 2d list of integers, where 0 indicates that a given cell is blank. For example, here is how we would represent the 32x32 Sudoku board in the figure above:
[
[ 5, 3, 0, 0, 7, 0, 0, 0, 0 ],
[ 6, 0, 0, 1, 9, 5, 0, 0, 0 ],
[ 0, 9, 8, 0, 0, 0, 0, 6, 0 ],
[ 8, 0, 0, 0, 6, 0, 0, 0, 3 ],
[ 4, 0, 0, 8, 0, 3, 0, 0, 1 ],
[ 7, 0, 0, 0, 2, 0, 0, 0, 6 ],
[ 0, 6, 0, 0, 0, 0, 2, 8, 0 ],
[ 0, 0, 0, 4, 1, 9, 0, 0, 5 ],
[ 0, 0, 0, 0, 8, 0, 0, 7, 9 ]
]
With this description in mind, your task is to write some functions to indicate if a given Sudoku board is legal. To make this problem more approachable, we are providing a specific design for you to follow. And to make the problem more gradeable, we are requiring that you follow the design! You should solve the problem by writing the following functions in the order given:
- areLegalValues(values) [4 pts]
This function takes a 1d list of values, which you should verify is of length N2 for some positive integer N and contains only integers in the range 0 to N2 (inclusive). These values may be extracted from any given row, column, or block in a Sudoku board (and, in fact, that is exactly what the next few functions will do -- they will each call this helper function). The function returns True if the values are legal: that is, if every value is an integer between 0 and N2, inclusive, and if each integer from 1 to N2 occurs at most once in the given list (0 may be repeated, of course). Note that this function does not take a 2d Sudoku board, but only a 1d list of values that may or may not have been extracted from some Sudoku board. Also, note that this function must be non-destructive.
- isLegalRow(board, row) [4 pts]
This function takes a Sudoku board and a row number. The function returns True if the given row in the given board is legal (where row 0 is the top row and row (N2-1) is the bottom row), and False otherwise. To do this, your function must create a 1d list of length N2 holding the values from the given row, and then provide these values to the areLegalValues function you previously wrote. (Actually, because areLegalValues is non-destructive, you do not have to copy the row; you may use an alias.)
- isLegalCol(board, col) [4 pts]
This function works just like the isLegalRow function, only for columns, where column 0 is the leftmost column and column (N2-1) is the rightmost column. Similarly to isLegalRow, this function must create a 1d list of length N2 holding the values from the given column, and then provide these values to the areLegalValues function you previously wrote.
- isLegalBlock(board, block) [4 pts]
This function works just like the isLegalRow function, only for blocks, where block 0 is the left-top block, and block numbers proceed across and then down, as described earlier. Similarly to isLegalRow and isLegalCol, this function must create a 1d list of length N2 holding the values from the given block, and then provide these values to the areLegalValues function you previously wrote. Note that getting the values from a block is much harder than getting the values from a row or col. You'll need to do a bit of math to find the starting row and col for each block based on the block's number.
- isLegalSudoku(board) [4 pts]
This function takes a Sudoku board (which you may assume is a N2xN2 2d list of integers), and returns True if the board is legal, as described above. To do this, your function must call isLegalRow over every row, isLegalCol over every column, and isLegalBlock over every block. See how helpful those helper functions are?