├── .gitignore ├── Readme ├── homework ├── hw1.py ├── hw2.py ├── hw3.py ├── hw3_fa13.py ├── hw4.py ├── hw5.py ├── hw6.py └── hw7.scm ├── project ├── proj1 │ └── hog │ │ ├── dice.py │ │ ├── hog.py │ │ ├── hog_gui.py │ │ ├── images │ │ ├── die1.gif │ │ ├── die2.gif │ │ ├── die3.gif │ │ ├── die4.gif │ │ ├── die5.gif │ │ └── die6.gif │ │ ├── ok │ │ ├── tests │ │ ├── info.py │ │ ├── q00.py │ │ ├── q01.py │ │ ├── q02.py │ │ ├── q03.py │ │ ├── q04.py │ │ ├── q05.py │ │ ├── q06.py │ │ ├── q07.py │ │ ├── q08.py │ │ ├── q09.py │ │ └── q10.py │ │ └── ucb.py ├── proj2 │ └── trends │ │ ├── data.py │ │ ├── data │ │ ├── cali_tweets2014.txt │ │ ├── family_tweets2014.txt │ │ ├── football_tweets2014.txt │ │ ├── high_school_tweets2014.txt │ │ ├── movie_tweets2014.txt │ │ ├── sentiments.csv │ │ ├── shopping_tweets2014.txt │ │ ├── snow_tweets2014.txt │ │ ├── states.json │ │ ├── texas_tweets2014.txt │ │ └── weekend_tweets2014.txt │ │ ├── geo.py │ │ ├── graphics.py │ │ ├── images │ │ ├── cali.png │ │ ├── cali_no_dots.png │ │ ├── family.png │ │ ├── football.png │ │ ├── high_school.png │ │ ├── movie.png │ │ ├── shopping.png │ │ ├── snow.png │ │ ├── texas.png │ │ └── weekend.png │ │ ├── maps.py │ │ ├── tests │ │ ├── info.py │ │ ├── q1.py │ │ ├── q2.py │ │ ├── q3.py │ │ ├── q4.py │ │ ├── q5.py │ │ ├── q6.py │ │ ├── q7.py │ │ ├── q8.py │ │ └── test_functions.py │ │ ├── trends.py │ │ └── ucb.py └── proj3 │ └── ants │ ├── ants.py │ ├── ants_gui.py │ ├── graphics.py │ ├── img │ ├── ant_fire.gif │ ├── ant_freeze.gif │ ├── ant_harvester.gif │ ├── ant_hungry.gif │ ├── ant_longthrower.gif │ ├── ant_ninja.gif │ ├── ant_queen.gif │ ├── ant_scuba.gif │ ├── ant_shortthrower.gif │ ├── ant_stun.gif │ ├── ant_thrower.gif │ ├── ant_wall.gif │ ├── ant_weeds.gif │ ├── ants_vs_bees.png │ ├── bee.gif │ ├── gui_explanation.png │ ├── remover.gif │ └── tunnel.gif │ ├── ok │ ├── tests │ ├── info.py │ ├── q1.py │ ├── q2.py │ ├── q3.py │ ├── q4A.py │ ├── q4B.py │ ├── q5A.py │ ├── q5B.py │ ├── q6A.py │ ├── q6B.py │ ├── q7A.py │ ├── q7B.py │ ├── q8.py │ ├── q9.py │ └── qEC.py │ └── ucb.py └── quiz └── quiz1 └── quiz1.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | *.pyc 3 | .ropeproject/ 4 | code2html.egg-info/ 5 | dist/ 6 | build/ 7 | __pycache__/ 8 | .ok_refresh 9 | -------------------------------------------------------------------------------- /Readme: -------------------------------------------------------------------------------- 1 | UC Berkeley CS61A, Fall 2014 2 | 3 | Homeworks, projects, exams, etc. 4 | -------------------------------------------------------------------------------- /homework/hw1.py: -------------------------------------------------------------------------------- 1 | # CS 61A Fall 2014 2 | # Name: kfei 3 | # Login: 4 | 5 | from operator import add, sub 6 | 7 | def a_plus_abs_b(a, b): 8 | """Return a+abs(b), but without calling abs. 9 | 10 | >>> a_plus_abs_b(2, 3) 11 | 5 12 | >>> a_plus_abs_b(2, -3) 13 | 5 14 | """ 15 | if b < 0: 16 | f = sub 17 | else: 18 | f = add 19 | return f(a, b) 20 | 21 | def two_of_three(a, b, c): 22 | """Return x*x + y*y, where x and y are the two largest members of the 23 | positive numbers a, b, and c. 24 | 25 | >>> two_of_three(1, 2, 3) 26 | 13 27 | >>> two_of_three(5, 3, 1) 28 | 34 29 | >>> two_of_three(10, 2, 8) 30 | 164 31 | >>> two_of_three(5, 5, 5) 32 | 50 33 | """ 34 | return sum([i*i for i in sorted([a, b, c])[1:3]]) 35 | 36 | def if_function(condition, true_result, false_result): 37 | """Return true_result if condition is a true value, and 38 | false_result otherwise. 39 | 40 | >>> if_function(True, 2, 3) 41 | 2 42 | >>> if_function(False, 2, 3) 43 | 3 44 | >>> if_function(3==2, 3+2, 3-2) 45 | 1 46 | >>> if_function(3>2, 3+2, 3-2) 47 | 5 48 | """ 49 | if condition: 50 | return true_result 51 | else: 52 | return false_result 53 | 54 | def with_if_statement(): 55 | """ 56 | >>> with_if_statement() 57 | 1 58 | """ 59 | if c(): 60 | return t() 61 | else: 62 | return f() 63 | 64 | def with_if_function(): 65 | return if_function(c(), t(), f()) 66 | 67 | def c(): 68 | return True 69 | 70 | def t(): 71 | return 1 72 | 73 | def f(): 74 | raise RuntimeError 75 | 76 | def hailstone(n): 77 | """Print the hailstone sequence starting at n and return its 78 | length. 79 | 80 | >>> a = hailstone(10) 81 | 10 82 | 5 83 | 16 84 | 8 85 | 4 86 | 2 87 | 1 88 | >>> a 89 | 7 90 | """ 91 | len = 1 92 | while n >= 1: 93 | print(n) 94 | 95 | if n == 1: 96 | return len 97 | elif n % 2 == 0: 98 | n = n / 2 99 | else: 100 | n = 3 * n + 1 101 | 102 | len = len + 1 103 | 104 | challenge_question_program = """ 105 | s = 'print("s = " + repr(s) + "; eval(s)")'; eval(s) 106 | """ 107 | # Reference for last question: 108 | # http://www.madore.org/~david/computers/quine.html 109 | # http://switchb.org/kpreid/quines 110 | # https://github.com/mame/quine-relay 111 | -------------------------------------------------------------------------------- /homework/hw2.py: -------------------------------------------------------------------------------- 1 | # CS 61A Fall 2014 2 | # Name: 3 | # Login: 4 | 5 | def square(x): 6 | return x * x 7 | 8 | def triple(x): 9 | return 3 * x 10 | 11 | def identity(x): 12 | return x 13 | 14 | def increment(x): 15 | return x + 1 16 | 17 | def piecewise(f, g, b): 18 | """Returns the piecewise function h where: 19 | 20 | h(x) = f(x) if x < b, 21 | g(x) otherwise 22 | 23 | >>> def negate(x): 24 | ... return -x 25 | >>> abs_value = piecewise(negate, identity, 0) 26 | >>> abs_value(6) 27 | 6 28 | >>> abs_value(-1) 29 | 1 30 | """ 31 | def ret(x): 32 | if x < 0: 33 | return f(x) 34 | else: 35 | return g(x) 36 | return ret 37 | 38 | def intersects(f, x): 39 | """Returns a function that returns whether f intersects g at x. 40 | 41 | >>> at_three = intersects(square, 3) 42 | >>> at_three(triple) # triple(3) == square(3) 43 | True 44 | >>> at_three(increment) 45 | False 46 | >>> at_one = intersects(identity, 1) 47 | >>> at_one(square) 48 | True 49 | >>> at_one(triple) 50 | False 51 | """ 52 | def ret(g): 53 | if g(x) == f(x): 54 | return True 55 | return False 56 | return ret 57 | 58 | def repeated(f, n): 59 | """Return the function that computes the nth application of f. 60 | 61 | >>> add_three = repeated(increment, 3) 62 | >>> add_three(5) 63 | 8 64 | >>> repeated(triple, 5)(1) # 3 * 3 * 3 * 3 * 3 * 1 65 | 243 66 | >>> repeated(square, 2)(5) # square(square(5)) 67 | 625 68 | >>> repeated(square, 4)(5) # square(square(square(square(5)))) 69 | 152587890625 70 | """ 71 | def nth(x): 72 | i = n # Or specify a nonlocal 73 | while i > 0: 74 | x = f(x) 75 | i -= 1 76 | return x 77 | return nth 78 | 79 | ################### 80 | # Church Numerals # 81 | ################### 82 | 83 | def zero(f): 84 | return lambda x: x 85 | 86 | def successor(n): 87 | return lambda f: lambda x: f(n(f)(x)) 88 | 89 | def one(f): 90 | """Church numeral 1: same as successor(zero)""" 91 | def ret(x): 92 | return f(x) 93 | return ret 94 | 95 | def two(f): 96 | """Church numeral 2: same as successor(successor(zero))""" 97 | def ret(x): 98 | return f(f(x)) 99 | return ret 100 | 101 | three = successor(two) 102 | 103 | def church_to_int(n): 104 | """Convert the Church numeral n to a Python integer. 105 | 106 | >>> church_to_int(zero) 107 | 0 108 | >>> church_to_int(one) 109 | 1 110 | >>> church_to_int(two) 111 | 2 112 | >>> church_to_int(three) 113 | 3 114 | """ 115 | def increase(x): 116 | assert x >= 0 117 | if x == 0: 118 | return 1 119 | else: 120 | return x + 1 121 | 122 | return n(increase)(0) 123 | 124 | def add_church(m, n): 125 | """Return the Church numeral for m + n, for Church numerals m and n. 126 | 127 | >>> church_to_int(add_church(two, three)) 128 | 5 129 | """ 130 | def ret(f): 131 | def _add(x): 132 | return m(f)(x) + n(f)(x) 133 | return _add 134 | return ret 135 | 136 | def mul_church(m, n): 137 | """Return the Church numeral for m * n, for Church numerals m and n. 138 | 139 | >>> four = successor(three) 140 | >>> church_to_int(mul_church(two, three)) 141 | 6 142 | >>> church_to_int(mul_church(three, four)) 143 | 12 144 | """ 145 | def ret(f): 146 | return m(n(f)) 147 | return ret 148 | 149 | def pow_church(m, n): 150 | """Return the Church numeral m ** n, for Church numerals m and n. 151 | 152 | >>> church_to_int(pow_church(two, three)) 153 | 8 154 | >>> church_to_int(pow_church(three, two)) 155 | 9 156 | """ 157 | return n(m) 158 | -------------------------------------------------------------------------------- /homework/hw3.py: -------------------------------------------------------------------------------- 1 | # There is only one different question from hw3 (Fall 2013) 2 | 3 | 4 | def towers_of_hanoi(n, start, end): 5 | """Print the moves required to solve the towers of hanoi game, starting 6 | with n disks on the start pole and finishing on the end pole. 7 | 8 | The game is to assumed to have 3 poles. 9 | 10 | >>> towers_of_hanoi(1, 1, 3) 11 | Move the top disk from rod 1 to rod 3 12 | >>> towers_of_hanoi(2, 1, 3) 13 | Move the top disk from rod 1 to rod 2 14 | Move the top disk from rod 1 to rod 3 15 | Move the top disk from rod 2 to rod 3 16 | >>> towers_of_hanoi(3, 1, 3) 17 | Move the top disk from rod 1 to rod 3 18 | Move the top disk from rod 1 to rod 2 19 | Move the top disk from rod 3 to rod 2 20 | Move the top disk from rod 1 to rod 3 21 | Move the top disk from rod 2 to rod 1 22 | Move the top disk from rod 2 to rod 3 23 | Move the top disk from rod 1 to rod 3 24 | """ 25 | assert 0 < start <= 3 and 0 < end <= 3 and start != end, "Bad start/end" 26 | 27 | def print_step(from_pole, to_pole): 28 | print("Move the top disk from rod %s to rod %s" % (from_pole, to_pole)) 29 | 30 | def helper(n, from_pole, by_pole, to_pole): 31 | if n == 1: 32 | print_step(from_pole, to_pole) 33 | # Assume that we already have the solution for disk number n - 1 34 | else: 35 | helper(n - 1, from_pole, to_pole, by_pole) 36 | print_step(from_pole, to_pole) 37 | helper(n - 1, by_pole, from_pole, to_pole) 38 | 39 | return helper(n, start, start + 1, end) 40 | -------------------------------------------------------------------------------- /homework/hw3_fa13.py: -------------------------------------------------------------------------------- 1 | # Homework url: 2 | # http://inst.eecs.berkeley.edu/~cs61a/fa13/hw/hw3.html 3 | 4 | # Q1. 5 | 6 | def g(n): 7 | """Return the value of G(n), computed recursively. 8 | 9 | >>> g(1) 10 | 1 11 | >>> g(2) 12 | 2 13 | >>> g(3) 14 | 3 15 | >>> g(4) 16 | 10 17 | >>> g(5) 18 | 22 19 | """ 20 | if n <= 3: 21 | return n 22 | else: 23 | return g(n - 1) + 2 * g(n - 2) + 3 * g(n - 3) 24 | 25 | def g_iter(n): 26 | """Return the value of G(n), computed iteratively. 27 | 28 | >>> g_iter(1) 29 | 1 30 | >>> g_iter(2) 31 | 2 32 | >>> g_iter(3) 33 | 3 34 | >>> g_iter(4) 35 | 10 36 | >>> g_iter(5) 37 | 22 38 | """ 39 | if n == 1: 40 | return 1 41 | elif n == 2: 42 | return 2 43 | elif n == 3: 44 | return 3 45 | 46 | p1, p2, p3 = 3, 2, 1 47 | i, current = 4, 0 48 | while i <= n: 49 | current = p1 + 2 * p2 + 3 * p3 50 | p1, p2, p3 = current, p1, p2 51 | i += 1 52 | 53 | return current 54 | 55 | 56 | # Q2. 57 | 58 | def has_seven(k): 59 | """Has a has_seven 60 | >>> has_seven(3) 61 | False 62 | >>> has_seven(7) 63 | True 64 | >>> has_seven(2734) 65 | True 66 | >>> has_seven(2634) 67 | False 68 | >>> has_seven(734) 69 | True 70 | >>> has_seven(7777) 71 | True 72 | """ 73 | if k % 10 == 7: 74 | return True 75 | elif k < 10: 76 | return False 77 | else: 78 | return has_seven(k // 10) 79 | 80 | 81 | # Q3. 82 | 83 | "1 2 3 4 5 6 [7] 6 5 4 3 2 1 [0] 1 2 [3] 2 1 0 [-1] 0 1 2 3 4 [5] [4] 5 6" 84 | 85 | def pingpong(n): 86 | """Return the nth element of the ping-pong sequence. 87 | 88 | >>> pingpong(7) 89 | 7 90 | >>> pingpong(8) 91 | 6 92 | >>> pingpong(15) 93 | 1 94 | >>> pingpong(21) 95 | -1 96 | >>> pingpong(22) 97 | 0 98 | >>> pingpong(30) 99 | 6 100 | >>> pingpong(68) 101 | 2 102 | >>> pingpong(69) 103 | 1 104 | >>> pingpong(70) 105 | 0 106 | >>> pingpong(71) 107 | 1 108 | >>> pingpong(72) 109 | 0 110 | >>> pingpong(100) 111 | 2 112 | """ 113 | # Following is a tree-recursion (2-way) version, 114 | # which takes too much time when n is large. 115 | # 116 | # if n == 1: 117 | # return 1 118 | # elif n == 2: 119 | # return 2 120 | # elif (n - 1) % 7 == 0 or has_seven(n - 1): 121 | # return pingpong(n - 2) 122 | # else: 123 | # return 2 * pingpong(n - 1) - pingpong(n - 2) 124 | 125 | # So re-implement a from-bottom-to-top recursion, using a helper fuction. 126 | def helper(k, direction, ret): 127 | if k == n: 128 | return ret + direction 129 | elif k % 7 == 0 or has_seven(k): 130 | return helper(k + 1, -direction, ret + direction) 131 | else: 132 | return helper(k + 1, direction, ret + direction) 133 | 134 | return helper(1, 1, 0) 135 | 136 | 137 | # Q4. 138 | 139 | def ten_pairs(n): 140 | """Return the number of ten-pairs within positive integer n. 141 | 142 | >>> ten_pairs(7823952) 143 | 3 144 | >>> ten_pairs(55055) 145 | 6 146 | >>> ten_pairs(9641469) 147 | 6 148 | """ 149 | def count_digit(digit, number): 150 | if number < 10 and number == digit: 151 | return 1 152 | elif number < 10 and number != digit: 153 | return 0 154 | elif number % 10 == digit: 155 | return 1 + count_digit(digit, number // 10) 156 | else: 157 | return count_digit(digit, number // 10) 158 | 159 | def helper(n): 160 | if n < 10: 161 | return 0 162 | else: 163 | return count_digit(10 - n % 10, n // 10) + helper(n // 10) 164 | 165 | return helper(n) 166 | 167 | 168 | # Q5. 169 | 170 | def count_change(amount): 171 | """Return the number of ways to make change for amount. 172 | 173 | >>> count_change(7) 174 | 6 175 | >>> count_change(10) 176 | 14 177 | >>> count_change(20) 178 | 60 179 | >>> count_change(100) 180 | 9828 181 | """ 182 | def count_partitions(number, at_most): 183 | if number < 0: 184 | # There is no way to represent a negative number 185 | return 0 186 | elif number == 0: 187 | # There is only one way to represent zero 188 | return 1 189 | elif at_most == 0: 190 | # There is only one way to represent a number using one (2^0) 191 | return 1 192 | else: 193 | # The representation may contains 2^at_most or not 194 | contains = count_partitions(number - pow(2, at_most), at_most) 195 | not_contains = count_partitions(number, at_most - 1) 196 | return contains + not_contains 197 | 198 | def find_at_most(number): 199 | k = 0 200 | while pow(2, k) <= number: 201 | k += 1 202 | return k - 1 203 | 204 | at_most = find_at_most(amount) 205 | 206 | return count_partitions(amount, at_most) 207 | 208 | 209 | # Q6. 210 | 211 | from operator import sub, mul 212 | 213 | def make_anonymous_factorial(): 214 | """Return the value of an expression that computes factorial. 215 | 216 | >>> make_anonymous_factorial()(5) 217 | 120 218 | """ 219 | # Idea: Pass lambda function to function F as an argument, 220 | # so that it can be refered within the scope of F in the future. 221 | return lambda n: (lambda f, v: f(f, v))(lambda f, v: 1 if v == 1 else mul(v, f(f, sub(v, 1))), n) 222 | -------------------------------------------------------------------------------- /homework/hw4.py: -------------------------------------------------------------------------------- 1 | # CS 61A Fall 2014 2 | # Name: 3 | # Login: 4 | 5 | def interval(a, b): 6 | """Construct an interval from a to b.""" 7 | return [a, b] 8 | 9 | def lower_bound(x): 10 | """Return the lower bound of interval x.""" 11 | return x[0] 12 | 13 | def upper_bound(x): 14 | """Return the upper bound of interval x.""" 15 | return x[1] 16 | 17 | def str_interval(x): 18 | """Return a string representation of interval x. 19 | 20 | >>> str_interval(interval(-1, 2)) 21 | '-1 to 2' 22 | """ 23 | return '{0} to {1}'.format(lower_bound(x), upper_bound(x)) 24 | 25 | def add_interval(x, y): 26 | """Return an interval that contains the sum of any value in interval x and 27 | any value in interval y. 28 | 29 | >>> str_interval(add_interval(interval(-1, 2), interval(4, 8))) 30 | '3 to 10' 31 | """ 32 | lower = lower_bound(x) + lower_bound(y) 33 | upper = upper_bound(x) + upper_bound(y) 34 | return interval(lower, upper) 35 | 36 | def mul_interval(x, y): 37 | """Return the interval that contains the product of any value in x and any 38 | value in y. 39 | 40 | >>> str_interval(mul_interval(interval(-1, 2), interval(4, 8))) 41 | '-8 to 16' 42 | """ 43 | p1 = lower_bound(x) * lower_bound(y) 44 | p2 = lower_bound(x) * upper_bound(y) 45 | p3 = upper_bound(x) * lower_bound(y) 46 | p4 = upper_bound(x) * upper_bound(y) 47 | return interval(min(p1, p2, p3, p4), max(p1, p2, p3, p4)) 48 | 49 | def div_interval(x, y): 50 | """Return the interval that contains the quotient of any value in x divided 51 | by any value in y. 52 | 53 | Division is implemented as the multiplication of x by the reciprocal of y. 54 | 55 | >>> str_interval(div_interval(interval(-1, 2), interval(4, 8))) 56 | '-0.25 to 0.5' 57 | """ 58 | assert 0 < lower_bound(y) or uppder_bound(y) < 0 59 | reciprocal_y = interval(1/upper_bound(y), 1/lower_bound(y)) 60 | return mul_interval(x, reciprocal_y) 61 | 62 | def sub_interval(x, y): 63 | """Return the interval that contains the difference between any value in x 64 | and any value in y. 65 | 66 | >>> str_interval(sub_interval(interval(-1, 2), interval(4, 8))) 67 | '-9 to -2' 68 | """ 69 | lower = lower_bound(x) - upper_bound(y) 70 | upper = upper_bound(x) - lower_bound(y) 71 | return interval(lower, upper) 72 | 73 | def par1(r1, r2): 74 | return div_interval(mul_interval(r1, r2), add_interval(r1, r2)) 75 | 76 | def par2(r1, r2): 77 | one = interval(1, 1) 78 | rep_r1 = div_interval(one, r1) 79 | rep_r2 = div_interval(one, r2) 80 | return div_interval(one, add_interval(rep_r1, rep_r2)) 81 | 82 | # These two intervals give different results for parallel resistors: 83 | """ a, b = interval(99, 101), interval(99, 101) """ 84 | 85 | def multiple_references_explanation(): 86 | exp = """ Yes. Since interval is an uncertain value, the more times it 87 | has been referenced, the larger error bound it will produced. """ 88 | return exp 89 | 90 | def quadratic(x, a, b, c): 91 | """Return the interval that is the range of the quadratic defined by 92 | coefficients a, b, and c, for domain interval x. 93 | 94 | >>> str_interval(quadratic(interval(0, 2), -2, 3, -1)) 95 | '-3 to 0.125' 96 | >>> str_interval(quadratic(interval(1, 3), 2, -3, 1)) 97 | '0 to 10' 98 | """ 99 | def f(t): 100 | return a * t * t + b * t + c 101 | 102 | extreme_point = -b / (2 * a) 103 | extreme_f = f(extreme_point) 104 | lower_f = f(lower_bound(x)) 105 | upper_f = f(upper_bound(x)) 106 | 107 | if lower_bound(x) <= extreme_point <= upper_bound(x): 108 | return interval(min(lower_f, upper_f, extreme_f), 109 | max(lower_f, upper_f, extreme_f)) 110 | else: 111 | return interval(min(lower_f, upper_f), max(lower_f, upper_f)) 112 | 113 | def polynomial(x, c): 114 | """Return the interval that is the range of the polynomial defined by 115 | coefficients c, for domain interval x. 116 | 117 | >>> str_interval(polynomial(interval(0, 2), [-1, 3, -2])) 118 | '-3 to 0.125' 119 | >>> str_interval(polynomial(interval(1, 3), [1, -3, 2])) 120 | '0 to 10' 121 | >>> str_interval(polynomial(interval(0.5, 2.25), [10, 24, -6, -8, 3])) 122 | '18.0 to 23.0' 123 | """ 124 | 125 | # Define f, df and ddf with coefficients c 126 | def f(t): 127 | order, ret = len(c), c[0] 128 | while order > 1: 129 | ret += c[order - 1] * pow(t, order - 1) 130 | order -= 1 131 | return ret 132 | 133 | def df(t): 134 | order, ret = len(c) - 1, c[1] 135 | while order > 1: 136 | ret += order * c[order] * pow(t, order - 1) 137 | order -= 1 138 | return ret 139 | 140 | def ddf(t): 141 | order, ret = len(c) - 2, 2 * c[2] 142 | while order > 1: 143 | ret += order * (order + 1) * c[order + 1] * pow(t, order - 1) 144 | order -= 1 145 | return ret 146 | 147 | # Define Newton's Method 148 | def improve(update, close, guess): 149 | while not close(guess): 150 | guess = update(guess) 151 | return guess 152 | 153 | def newton_update(f, df): 154 | def update(x): 155 | return x - f(x) / df(x) 156 | return update 157 | 158 | def approx_eq(x, tolerance=1e-5): 159 | return abs(df(x)) < tolerance 160 | 161 | def middle(x): 162 | return (lower_bound(x) + upper_bound(x)) / 2 163 | 164 | # Define a function which returns zero of g within interval x 165 | def find_zero(g, x): 166 | lower = lower_bound(x) 167 | upper = upper_bound(x) 168 | if g(lower) * g(upper) <= 0: 169 | # Now call Newton's Method to compute the zero 170 | zero = improve(newton_update(df, ddf), approx_eq, middle(x)) 171 | return [round(zero, 5)] 172 | else: 173 | return [] 174 | 175 | # Slice the interval x into len(c) sub-intervals 176 | step = (upper_bound(x) - lower_bound(x)) / len(c) 177 | extreme_points = [] 178 | for i in range(len(c)): 179 | lower = lower_bound(x) + i * step 180 | upper = lower + step 181 | extreme_points += find_zero(df, interval(lower, upper)) 182 | 183 | extremas = [f(t) for t in extreme_points] 184 | boundary_values = [f(lower_bound(x)), f(upper_bound(x))] 185 | all_possibles = extremas + boundary_values 186 | 187 | return interval(min(all_possibles), max(all_possibles)) 188 | 189 | """ Note that I'm not sure this approximation works for all polynomials 190 | over all intervals (though I think it will), just an implementation that 191 | passes the doctests. Still looking for a mathematically better solution. 192 | """ 193 | -------------------------------------------------------------------------------- /homework/hw6.py: -------------------------------------------------------------------------------- 1 | # CS 61A Fall 2014 2 | # Name: 3 | # Login: 4 | 5 | class VendingMachine(object): 6 | """A vending machine that vends some product for some price. 7 | 8 | >>> v = VendingMachine('candy', 10) 9 | >>> v.vend() 10 | 'Machine is out of stock.' 11 | >>> v.restock(2) 12 | 'Current candy stock: 2' 13 | >>> v.vend() 14 | 'You must deposit $10 more.' 15 | >>> v.deposit(7) 16 | 'Current balance: $7' 17 | >>> v.vend() 18 | 'You must deposit $3 more.' 19 | >>> v.deposit(5) 20 | 'Current balance: $12' 21 | >>> v.vend() 22 | 'Here is your candy and $2 change.' 23 | >>> v.deposit(10) 24 | 'Current balance: $10' 25 | >>> v.vend() 26 | 'Here is your candy.' 27 | >>> v.deposit(15) 28 | 'Machine is out of stock. Here is your $15.' 29 | """ 30 | def __init__(self, product, price): 31 | self.product = product 32 | self.price = price 33 | self.balance = 0 34 | self.stock = 0 35 | 36 | def restock(self, amount): 37 | self.stock += amount 38 | return 'Current {0} stock: {1}'.format(self.product, self.stock) 39 | 40 | def deposit(self, amount): 41 | if self.stock == 0: 42 | return 'Machine is out of stock. Here is your ${0}.'.format(amount) 43 | 44 | self.balance += amount 45 | return 'Current balance: ${0}'.format(self.balance) 46 | 47 | def vend(self): 48 | if self.stock == 0 and self.balance == 0: 49 | return 'Machine is out of stock.' 50 | 51 | change = self.balance - self.price 52 | if change < 0: 53 | return 'You must deposit ${0} more.'.format(-change) 54 | elif change > 0: 55 | self.balance = 0 56 | self.stock -= 1 57 | return 'Here is your {0} and ${1} change.'.format(self.product, change) 58 | else: 59 | self.balance = 0 60 | self.stock -= 1 61 | return 'Here is your {0}.'.format(self.product) 62 | 63 | class MissManners(object): 64 | """A container class that only forward messages that say please. 65 | 66 | >>> v = VendingMachine('teaspoon', 10) 67 | >>> v.restock(2) 68 | 'Current teaspoon stock: 2' 69 | >>> m = MissManners(v) 70 | >>> m.ask('vend') 71 | 'You must learn to say please first.' 72 | >>> m.ask('please vend') 73 | 'You must deposit $10 more.' 74 | >>> m.ask('please deposit', 20) 75 | 'Current balance: $20' 76 | >>> m.ask('now will you vend?') 77 | 'You must learn to say please first.' 78 | >>> m.ask('please hand over a teaspoon') 79 | 'Thanks for asking, but I know not how to hand over a teaspoon' 80 | >>> m.ask('please vend') 81 | 'Here is your teaspoon and $10 change.' 82 | >>> really_fussy = MissManners(m) 83 | >>> really_fussy.ask('deposit', 10) 84 | 'You must learn to say please first.' 85 | >>> really_fussy.ask('please deposit', 10) 86 | 'Thanks for asking, but I know not how to deposit' 87 | >>> really_fussy.ask('please please deposit', 10) 88 | 'Thanks for asking, but I know not how to please deposit' 89 | >>> really_fussy.ask('please ask', 'please deposit', 10) 90 | 'Current balance: $10' 91 | >>> fussy_three = MissManners(3) 92 | >>> fussy_three.ask('add', 4) 93 | 'You must learn to say please first.' 94 | >>> fussy_three.ask('please add', 4) 95 | 'Thanks for asking, but I know not how to add' 96 | >>> fussy_three.ask('please __add__', 4) 97 | 7 98 | """ 99 | def __init__(self, to): 100 | self.to = to 101 | 102 | def ask(self, sentence, *args): 103 | if sentence[:6] != 'please': 104 | return 'You must learn to say please first.' 105 | 106 | method = sentence[7:] 107 | if hasattr(self.to, method): 108 | return getattr(self.to, method)(*args) 109 | else: 110 | return 'Thanks for asking, but I know not how to {0}'.format(method) 111 | 112 | 113 | ########################################## 114 | # Challenge Problem # 115 | # (You can delete this part if you want) # 116 | ########################################## 117 | 118 | def make_instance(some_class): 119 | """Return a new object instance of some_class.""" 120 | def get_value(name): 121 | if name in attributes: 122 | return attributes[name] 123 | else: 124 | value = some_class['get'](name) 125 | return bind_method(value, instance) 126 | 127 | def set_value(name, value): 128 | attributes[name] = value 129 | 130 | attributes = {} 131 | instance = {'get': get_value, 'set': set_value} 132 | return instance 133 | 134 | def bind_method(value, instance): 135 | """Return value or a bound method if value is callable.""" 136 | if callable(value): 137 | def method(*args): 138 | return value(instance, *args) 139 | return method 140 | else: 141 | return value 142 | 143 | def make_class(attributes, base_classes=()): 144 | """Return a new class with attributes. 145 | 146 | attributes -- class attributes 147 | base_classes -- a sequence of classes 148 | """ 149 | def get_value(name): 150 | if name in attributes: 151 | return attributes[name] 152 | 153 | if len(base_classes) == 1: 154 | return base_classes[0]['get'](name) 155 | 156 | if len(base_classes) > 1: 157 | for b in mro()[1:]: 158 | if b['hasattr'](name): 159 | return b['get'](name) 160 | 161 | def set_value(name, value): 162 | attributes[name] = value 163 | 164 | def new(*args): 165 | return init_instance(cls, *args) 166 | 167 | def hasattr(name): 168 | if name in attributes: 169 | return True 170 | return False 171 | 172 | def mro_helper(c): 173 | ret = [] 174 | def search(c, level=0): 175 | ret.append((c, level)) 176 | if len(c['get']('bases')) > 0: 177 | for b in c['get']('bases'): 178 | search(b, level + 1) 179 | search(c, 0) 180 | return ret 181 | 182 | def mro(): 183 | ret = [] 184 | mro_seq = mro_helper(cls) 185 | depth = max([c[1] for c in mro_seq]) 186 | for level in range(depth): 187 | for c in mro_seq: 188 | if c[1] == level and c[0] not in ret: 189 | ret.append(c[0]) 190 | return ret 191 | 192 | attributes['bases'] = base_classes 193 | cls = {'get': get_value, 'set': set_value, 'new': new, 194 | 'mro': mro, 'hasattr': hasattr} 195 | return cls 196 | 197 | def init_instance(some_class, *args): 198 | """Return a new instance of some_class, initialized with args.""" 199 | instance = make_instance(some_class) 200 | init = some_class['get']('__init__') 201 | if init: 202 | init(instance, *args) 203 | return instance 204 | 205 | # AsSeenOnTVAccount example from lecture. 206 | 207 | def make_account_class(): 208 | """Return the Account class, which has deposit and withdraw methods.""" 209 | 210 | interest = 0.02 211 | 212 | def __init__(self, account_holder): 213 | self['set']('holder', account_holder) 214 | self['set']('balance', 0) 215 | 216 | def deposit(self, amount): 217 | """Increase the account balance by amount and return the new balance.""" 218 | new_balance = self['get']('balance') + amount 219 | self['set']('balance', new_balance) 220 | return self['get']('balance') 221 | 222 | def withdraw(self, amount): 223 | """Decrease the account balance by amount and return the new balance.""" 224 | balance = self['get']('balance') 225 | if amount > balance: 226 | return 'Insufficient funds' 227 | self['set']('balance', balance - amount) 228 | return self['get']('balance') 229 | 230 | return make_class(locals()) 231 | 232 | Account = make_account_class() 233 | 234 | def make_checking_account_class(): 235 | """Return the CheckingAccount class, which imposes a $1 withdrawal fee. 236 | 237 | >>> checking = CheckingAccount['new']('Jack') 238 | >>> checking['get']('interest') 239 | 0.01 240 | >>> checking['get']('deposit')(20) 241 | 20 242 | >>> checking['get']('withdraw')(5) 243 | 14 244 | """ 245 | interest = 0.01 246 | withdraw_fee = 1 247 | 248 | def withdraw(self, amount): 249 | fee = self['get']('withdraw_fee') 250 | return Account['get']('withdraw')(self, amount + fee) 251 | 252 | return make_class(locals(), [Account]) 253 | 254 | CheckingAccount = make_checking_account_class() 255 | 256 | def make_savings_account_class(): 257 | """Return the SavingsAccount class, which imposes a $2 deposit fee. 258 | 259 | >>> savings = SavingsAccount['new']('Jack') 260 | >>> savings['get']('interest') 261 | 0.02 262 | >>> savings['get']('deposit')(20) 263 | 18 264 | >>> savings['get']('withdraw')(5) 265 | 13 266 | """ 267 | deposit_fee = 2 268 | 269 | def deposit(self, amount): 270 | fee = self['get']('deposit_fee') 271 | return Account['get']('deposit')(self, amount - fee) 272 | 273 | return make_class(locals(), [Account]) 274 | 275 | SavingsAccount = make_savings_account_class() 276 | 277 | def make_as_seen_on_tv_account_class(): 278 | """Return an account with lots of fees and a free dollar. 279 | 280 | >>> such_a_deal = AsSeenOnTVAccount['new']('Jack') 281 | >>> such_a_deal['get']('balance') 282 | 1 283 | >>> such_a_deal['get']('interest') 284 | 0.01 285 | >>> such_a_deal['get']('deposit')(20) 286 | 19 287 | >>> such_a_deal['get']('withdraw')(5) 288 | 13 289 | """ 290 | def __init__(self, account_holder): 291 | self['set']('holder', account_holder) 292 | self['set']('balance', 1) 293 | 294 | return make_class(locals(), [CheckingAccount, SavingsAccount]) 295 | 296 | AsSeenOnTVAccount = make_as_seen_on_tv_account_class() 297 | -------------------------------------------------------------------------------- /homework/hw7.scm: -------------------------------------------------------------------------------- 1 | ; CS 61A Fall 2014 2 | ; Name: 3 | ; Login: 4 | 5 | (define (assert-equal value expression) 6 | (if (equal? value (eval expression)) 7 | (print 'ok) 8 | (print (list 'for expression ': 9 | 'expected value 10 | 'but 'got (eval expression))))) 11 | 12 | (define (cddr s) 13 | (cdr (cdr s))) 14 | 15 | (define (cadr s) 16 | (car (cdr s))) 17 | 18 | (define (caddr s) 19 | (car (cddr s))) 20 | 21 | (define (test-car-cadr) 22 | (assert-equal (list 3 4) '(cddr '(1 2 3 4))) 23 | (assert-equal 2 '(cadr '(1 2 3 4))) 24 | (assert-equal 3 '(caddr '(1 2 3 4)))) 25 | 26 | (test-car-cadr) 27 | 28 | (define (sign x) 29 | (cond 30 | ((zero? x) 0) 31 | ((< x 0) (- 1)) 32 | (else 1))) 33 | 34 | (define (test-sign) 35 | (assert-equal -1 '(sign -42)) 36 | (assert-equal 0 '(sign 0)) 37 | (assert-equal 1 '(sign 42))) 38 | 39 | (test-sign) 40 | 41 | (define (gcd m n) 42 | (cond 43 | ((= m n) n) 44 | ((> m n) (gcd (- m n) n)) 45 | ((< m n) (gcd (- n m) m)))) 46 | 47 | (define (test-gcd) 48 | (assert-equal 4 '(gcd 12 8)) 49 | (assert-equal 4 '(gcd 12 16)) 50 | (assert-equal 8 '(gcd 16 8)) 51 | (assert-equal 6 '(gcd 24 42)) 52 | (assert-equal 5 '(gcd 5 5))) 53 | 54 | (test-gcd) 55 | 56 | (define (square x) (* x x)) 57 | 58 | (define (pow b n) 59 | (cond 60 | ((zero? n) 1) 61 | ((even? n) (square (pow b (/ n 2)))) 62 | ((odd? n) (* b (pow b (- n 1)))))) 63 | 64 | (define (test-pow) 65 | (assert-equal 1024 '(pow 2 10)) 66 | (assert-equal 1000 '(pow 10 3)) 67 | (assert-equal 243 '(pow 3 5))) 68 | 69 | (test-pow) 70 | 71 | (define (ordered? lst) 72 | (cond 73 | ((null? lst) true) 74 | ((null? (cdr lst)) true) 75 | ((< (car lst) (cadr lst)) (ordered? (cdr lst))) 76 | (else false))) 77 | 78 | (define (test-ordered?) 79 | (assert-equal true '(ordered? '(1 2 3 4 5))) 80 | (assert-equal false '(ordered? '(1 5 2 4 3)))) 81 | 82 | (test-ordered?) 83 | 84 | ; Sets as sorted lists 85 | 86 | (define (empty? s) (null? s)) 87 | 88 | (define (contains? s v) 89 | (cond ((empty? s) false) 90 | ((> (car s) v) false) 91 | ((< (car s) v) (contains? (cdr s) v)) 92 | (else true))) 93 | 94 | (define odds (list 3 5 7 9)) 95 | 96 | (define (test-contains) 97 | (assert-equal true '(contains? odds 3)) 98 | (assert-equal true '(contains? odds 9)) 99 | (assert-equal false '(contains? odds 6))) 100 | 101 | (test-contains) 102 | 103 | (define (append s v) 104 | (cond ((empty? s) (list v)) 105 | ((contains? s v) s) 106 | ((< v (car s)) (cons v s)) 107 | (else (cons (car s) (append (cdr s) v))))) 108 | 109 | (define (test-append) 110 | (assert-equal '(2 3 5 7 9) '(append odds 2)) 111 | (assert-equal '(3 5 7 9) '(append odds 5)) 112 | (assert-equal '(3 5 6 7 9) '(append odds 6)) 113 | (assert-equal '(3 5 7 9 10) '(append odds 10))) 114 | 115 | (test-append) 116 | 117 | (define (intersect s t) 118 | (cond ((or (empty? s) (empty? t)) nil) 119 | ((= (car s) (car t)) (cons (car s) (intersect (cdr s) (cdr t)))) 120 | ((< (car s) (car t)) (intersect (cdr s) t)) 121 | ((> (car s) (car t)) (intersect s (cdr t))))) 122 | 123 | (define eight (list 1 2 3 4 5 6 7 8)) 124 | 125 | (define (test-intersect) 126 | (assert-equal '(3 5) '(intersect odds (list 2 3 4 5))) 127 | (assert-equal '() '(intersect odds (list 2 4 6 8))) 128 | (assert-equal '(3 5 7) '(intersect odds eight))) 129 | 130 | (define (union s t) 131 | (cond ((empty? s) t) 132 | ((empty? t) s) 133 | (else (union (append s (car t)) (cdr t))))) 134 | 135 | (define (test-union) 136 | (assert-equal '(2 3 4 5 7 9) '(union odds (list 2 3 4 5))) 137 | (assert-equal '(2 3 4 5 6 7 8 9) '(union odds (list 2 4 6 8))) 138 | (assert-equal '(1 2 3 4 5 6 7 8 9) '(union odds eight))) 139 | 140 | (test-intersect) 141 | (test-union) 142 | 143 | ; Binary search trees 144 | 145 | ; A data abstraction for binary trees where nil represents the empty tree 146 | (define (tree entry left right) (list entry left right)) 147 | (define (entry t) (car t)) 148 | (define (left t) (cadr t)) 149 | (define (right t) (caddr t)) 150 | (define (empty? s) (null? s)) 151 | (define (leaf entry) (tree entry nil nil)) 152 | 153 | (define (in? t v) 154 | (cond ((empty? t) false) 155 | ((= v (entry t)) true) 156 | ((< v (entry t)) (in? (left t) v)) 157 | (else (in? (right t) v)))) 158 | 159 | (define odd-tree (tree 3 (leaf 1) 160 | (tree 7 (leaf 5) 161 | (tree 9 nil (leaf 11))))) 162 | 163 | (define (test-in?) 164 | (assert-equal true '(in? odd-tree 1)) 165 | (assert-equal false '(in? odd-tree 2)) 166 | (assert-equal true '(in? odd-tree 3)) 167 | (assert-equal false '(in? odd-tree 4)) 168 | (assert-equal true '(in? odd-tree 5))) 169 | 170 | (test-in?) 171 | 172 | (define (as-list t) 173 | (define (flatten t e) 174 | (if (empty? t) 175 | e 176 | (flatten (left t) (cons (entry t) (flatten (right t) s))) 177 | (flatten t nil)))) 178 | 179 | (define (test-as-list) 180 | (assert-equal '(5 7 9 11) '(as-list (right odd-tree))) 181 | (assert-equal '(1 3 5 7 9 11) '(as-list odd-tree))) 182 | 183 | (test-as-list) 184 | -------------------------------------------------------------------------------- /project/proj1/hog/dice.py: -------------------------------------------------------------------------------- 1 | """Functions that simulate dice rolls. 2 | 3 | A dice function takes no arguments and returns a number from 1 to n 4 | (inclusive), where n is the number of sides on the dice. 5 | 6 | Types of dice: 7 | 8 | - Dice can be fair, meaning that they produce each possible outcome with equal 9 | probability. Examples: four_sided, six_sided 10 | 11 | - For testing functions that use dice, deterministic test dice always cycle 12 | through a fixed sequence of values that are passed as arguments to the 13 | make_test_dice function. 14 | """ 15 | 16 | from random import randint 17 | 18 | def make_fair_dice(sides): 19 | """Return a die that returns 1 to SIDES with equal chance.""" 20 | assert type(sides) == int and sides >= 1, 'Illegal value for sides' 21 | def dice(): 22 | return randint(1,sides) 23 | return dice 24 | 25 | four_sided = make_fair_dice(4) 26 | six_sided = make_fair_dice(6) 27 | 28 | def make_test_dice(*outcomes): 29 | """Return a die that cycles deterministically through OUTCOMES. 30 | 31 | >>> dice = make_test_dice(1, 2, 3) 32 | >>> dice() 33 | 1 34 | >>> dice() 35 | 2 36 | >>> dice() 37 | 3 38 | >>> dice() 39 | 1 40 | >>> dice() 41 | 2 42 | 43 | This function uses Python syntax/techniques not yet covered in this course. 44 | The best way to understand it is by reading the documentation and examples. 45 | """ 46 | assert len(outcomes) > 0, 'You must supply outcomes to make_test_dice' 47 | for o in outcomes: 48 | assert type(o) == int and o >= 1, 'Outcome is not a positive integer' 49 | index = len(outcomes) - 1 50 | def dice(): 51 | nonlocal index 52 | index = (index + 1) % len(outcomes) 53 | return outcomes[index] 54 | return dice 55 | -------------------------------------------------------------------------------- /project/proj1/hog/hog.py: -------------------------------------------------------------------------------- 1 | """The Game of Hog.""" 2 | 3 | from dice import four_sided, six_sided, make_test_dice 4 | from ucb import main, trace, log_current_line, interact 5 | 6 | GOAL_SCORE = 100 # The goal of Hog is to score 100 points. 7 | 8 | ###################### 9 | # Phase 1: Simulator # 10 | ###################### 11 | 12 | def roll_dice(num_rolls, dice=six_sided): 13 | """Roll DICE for NUM_ROLLS times. Return either the sum of the outcomes, 14 | or 1 if a 1 is rolled (Pig out). This calls DICE exactly NUM_ROLLS times. 15 | 16 | num_rolls: The number of dice rolls that will be made; at least 1. 17 | dice: A zero-argument function that returns an integer outcome. 18 | """ 19 | # These assert statements ensure that num_rolls is a positive integer. 20 | assert type(num_rolls) == int, 'num_rolls must be an integer.' 21 | assert num_rolls > 0, 'Must roll at least once.' 22 | i, sum, pigout = 0, 0, False 23 | while i < num_rolls: 24 | k = dice() 25 | if k == 1: 26 | pigout = True 27 | else: 28 | sum += k 29 | i += 1 30 | 31 | return (pigout and 1) or sum 32 | 33 | def take_turn(num_rolls, opponent_score, dice=six_sided): 34 | """Simulate a turn rolling NUM_ROLLS dice, which may be 0 (Free bacon). 35 | 36 | num_rolls: The number of dice rolls that will be made. 37 | opponent_score: The total score of the opponent. 38 | dice: A function of no args that returns an integer outcome. 39 | """ 40 | assert type(num_rolls) == int, 'num_rolls must be an integer.' 41 | assert num_rolls >= 0, 'Cannot roll a negative number of dice.' 42 | assert num_rolls <= 10, 'Cannot roll more than 10 dice.' 43 | assert opponent_score < 100, 'The game should be over.' 44 | 45 | if num_rolls == 0: 46 | return abs(opponent_score // 10 - opponent_score % 10) + 1 47 | else: 48 | sum = roll_dice(num_rolls, dice) 49 | 50 | return sum 51 | 52 | def select_dice(score, opponent_score): 53 | """Select six-sided dice unless the sum of SCORE and OPPONENT_SCORE is a 54 | multiple of 7, in which case select four-sided dice (Hog wild). 55 | """ 56 | if (score + opponent_score) % 7 == 0: 57 | return four_sided 58 | else: 59 | return six_sided 60 | 61 | def bid_for_start(bid0, bid1, goal=GOAL_SCORE): 62 | """Given the bids BID0 and BID1 of each player, returns three values: 63 | 64 | - the starting score of player 0 65 | - the starting score of player 1 66 | - the number of the player who rolls first (0 or 1) 67 | """ 68 | assert bid0 >= 0 and bid1 >= 0, "Bids should be non-negative!" 69 | assert type(bid0) == int and type(bid1) == int, "Bids should be integers!" 70 | 71 | if bid0 == bid1: 72 | return goal, goal, 0 73 | if bid0 == bid1 - 5: 74 | return 0, 10, 1 75 | if bid1 == bid0 - 5: 76 | return 10, 0, 0 77 | if bid1 > bid0: 78 | return bid1, bid0, 1 79 | else: 80 | return bid1, bid0, 0 81 | 82 | def other(who): 83 | """Return the other player, for a player WHO numbered 0 or 1. 84 | 85 | >>> other(0) 86 | 1 87 | >>> other(1) 88 | 0 89 | """ 90 | return 1 - who 91 | 92 | def play(strategy0, strategy1, score0=0, score1=0, goal=GOAL_SCORE): 93 | """Simulate a game and return the final scores of both players, with 94 | Player 0's score first, and Player 1's score second. 95 | 96 | A strategy is a function that takes two total scores as arguments 97 | (the current player's score, and the opponent's score), and returns a 98 | number of dice that the current player will roll this turn. 99 | 100 | strategy0: The strategy function for Player 0, who plays first 101 | strategy1: The strategy function for Player 1, who plays second 102 | score0 : The starting score for Player 0 103 | score1 : The starting score for Player 1 104 | """ 105 | who = 0 # Which player is about to take a turn, 0 (first) or 1 (second) 106 | 107 | def update_score(who, score): 108 | nonlocal score0, score1 109 | if who == 0: 110 | score0 += score 111 | else: 112 | score1 += score 113 | 114 | def get_score(who): 115 | if who == 0: 116 | return score0 117 | else: 118 | return score1 119 | 120 | def get_strategy(who): 121 | if who == 0: 122 | return strategy0 123 | else: 124 | return strategy1 125 | 126 | def swine(): 127 | nonlocal score0, score1 128 | if score1 == 2 * score0 or score0 == 2 * score1: 129 | score0, score1 = score1, score0 130 | 131 | # Main loop 132 | while score0 < 100 and score1 < 100: 133 | # Get the current scores of both players 134 | who_score = get_score(who) 135 | opponent_score = get_score(other(who)) 136 | # Get the strategy provided by current player 137 | strategy = get_strategy(who)(who_score, opponent_score) 138 | # Use above information to select correct dice 139 | dice = select_dice(who_score, opponent_score) 140 | # Then roll the dice and update current player's score 141 | update_score(who, take_turn(strategy, opponent_score, dice)) 142 | # Swine swap if condition holds 143 | swine() 144 | # Now it's time for the other player 145 | who = other(who) 146 | 147 | return score0, score1 148 | 149 | ####################### 150 | # Phase 2: Strategies # 151 | ####################### 152 | 153 | def always_roll(n): 154 | """Return a strategy that always rolls N dice. 155 | 156 | A strategy is a function that takes two total scores as arguments 157 | (the current player's score, and the opponent's score), and returns a 158 | number of dice that the current player will roll this turn. 159 | 160 | >>> strategy = always_roll(5) 161 | >>> strategy(0, 0) 162 | 5 163 | >>> strategy(99, 99) 164 | 5 165 | """ 166 | def strategy(score, opponent_score): 167 | return n 168 | return strategy 169 | 170 | # Experiments 171 | 172 | def make_averaged(fn, num_samples=30000): 173 | """Return a function that returns the average_value of FN when called. 174 | 175 | To implement this function, you will have to use *args syntax, a new Python 176 | feature introduced in this project. See the project description. 177 | 178 | >>> dice = make_test_dice(3, 1, 5, 6) 179 | >>> averaged_dice = make_averaged(dice, 1000) 180 | >>> averaged_dice() 181 | 3.75 182 | >>> make_averaged(roll_dice, 1000)(2, dice) 183 | 6.0 184 | 185 | In this last example, two different turn scenarios are averaged. 186 | - In the first, the player rolls a 3 then a 1, receiving a score of 1. 187 | - In the other, the player rolls a 5 and 6, scoring 11. 188 | Thus, the average value is 6.0. 189 | """ 190 | def ret(*args): 191 | sum, i = 0, 0 192 | while i < num_samples: 193 | sum, i = sum + fn(*args), i + 1 194 | return sum / num_samples 195 | 196 | return ret 197 | 198 | 199 | def max_scoring_num_rolls(dice=six_sided): 200 | """Return the number of dice (1 to 10) that gives the highest average turn 201 | score by calling roll_dice with the provided DICE. Assume that dice always 202 | return positive outcomes. 203 | 204 | >>> dice = make_test_dice(3) 205 | >>> max_scoring_num_rolls(dice) 206 | 10 207 | """ 208 | _max, number_of_dice, ret = 0, 10, 0 209 | while number_of_dice > 0: 210 | avg = make_averaged(roll_dice)(number_of_dice, dice) 211 | _max = max(_max, avg) 212 | if avg >= _max: 213 | ret = number_of_dice 214 | number_of_dice -= 1 215 | 216 | return ret 217 | 218 | def winner(strategy0, strategy1): 219 | """Return 0 if strategy0 wins against strategy1, and 1 otherwise.""" 220 | score0, score1 = play(strategy0, strategy1) 221 | if score0 > score1: 222 | return 0 223 | else: 224 | return 1 225 | 226 | def average_win_rate(strategy, baseline=always_roll(5)): 227 | """Return the average win rate (0 to 1) of STRATEGY against BASELINE.""" 228 | win_rate_as_player_0 = 1 - make_averaged(winner)(strategy, baseline) 229 | win_rate_as_player_1 = make_averaged(winner)(baseline, strategy) 230 | return (win_rate_as_player_0 + win_rate_as_player_1) / 2 # Average results 231 | 232 | def run_experiments(): 233 | """Run a series of strategy experiments and report results.""" 234 | if False: # Change to False when done finding max_scoring_num_rolls 235 | six_sided_max = max_scoring_num_rolls(six_sided) 236 | print('Max scoring num rolls for six-sided dice:', six_sided_max) 237 | four_sided_max = max_scoring_num_rolls(four_sided) 238 | print('Max scoring num rolls for four-sided dice:', four_sided_max) 239 | 240 | if False: # Change to True to test always_roll(8) 241 | print('always_roll(8) win rate:', average_win_rate(always_roll(8))) 242 | 243 | if False: # Change to True to test bacon_strategy 244 | print('bacon_strategy win rate:', average_win_rate(bacon_strategy)) 245 | 246 | if False: # Change to True to test swap_strategy 247 | print('swap_strategy win rate:', average_win_rate(swap_strategy)) 248 | 249 | if True: # Change to True to test final_strategy 250 | print('final_strategy win rate:', average_win_rate(final_strategy)) 251 | 252 | "*** You may add additional experiments as you wish ***" 253 | 254 | # Strategies 255 | 256 | def bacon_strategy(score, opponent_score, margin=8, num_rolls=5): 257 | """This strategy rolls 0 dice if that gives at least MARGIN points, 258 | and rolls NUM_ROLLS otherwise. 259 | """ 260 | bacon_points = abs(opponent_score // 10 - opponent_score % 10) + 1 261 | 262 | if bacon_points >= margin: 263 | return 0 264 | else: 265 | return num_rolls 266 | 267 | def swap_strategy(score, opponent_score, margin=8, num_rolls=5): 268 | """This strategy rolls 0 dice when it would result in a beneficial swap and 269 | rolls NUM_ROLLS if it would result in a harmful swap. It also rolls 270 | 0 dice if that gives at least MARGIN points and rolls 271 | NUM_ROLLS otherwise. 272 | """ 273 | bacon_points = abs(opponent_score // 10 - opponent_score % 10) + 1 274 | 275 | if opponent_score == 2 * (score + bacon_points): 276 | return 0 277 | elif (score + bacon_points) == 2 * opponent_score: 278 | return num_rolls 279 | else: 280 | return bacon_strategy(score, opponent_score, margin, num_rolls) 281 | 282 | def final_strategy(score, opponent_score): 283 | """Write a brief description of your final strategy. 284 | 285 | See the per-line comments. 286 | PS. The expected score of rolling a 4-sided dice is about 3.797. 287 | PSS. This strategy get an average win rate greater than 0.55, is 0.56 possible? 288 | """ 289 | bacon_points = abs(opponent_score // 10 - opponent_score % 10) + 1 290 | 291 | # If bacon_points is greater than 3.797, leave opponent with 4-sided dice 292 | if (score + bacon_points + opponent_score) % 7 == 0 and bacon_points >= 4: 293 | return 0 294 | # Try to trigger a beneficial swine swap by trying score only one point 295 | elif 2 * (score + 1) == opponent_score: 296 | return swap_strategy(score, opponent_score, 10, 10) 297 | # Decrease num_rolls to 3 and margin to 4, more suitable for a 4-sided dice 298 | elif (score + opponent_score) % 7 == 0: 299 | return swap_strategy(score, opponent_score, 4, 3) 300 | # More offensive when losing 301 | elif score < opponent_score: 302 | return swap_strategy(score, opponent_score, 9, 6) 303 | # More defensive when leading 304 | else: 305 | return swap_strategy(score, opponent_score, 7, 4) 306 | 307 | 308 | ########################## 309 | # Command Line Interface # 310 | ########################## 311 | 312 | # Note: Functions in this section do not need to be changed. They use features 313 | # of Python not yet covered in the course. 314 | 315 | 316 | @main 317 | def run(*args): 318 | """Read in the command-line argument and calls corresponding functions. 319 | 320 | This function uses Python syntax/techniques not yet covered in this course. 321 | """ 322 | import argparse 323 | parser = argparse.ArgumentParser(description="Play Hog") 324 | parser.add_argument('--run_experiments', '-r', action='store_true', 325 | help='Runs strategy experiments') 326 | args = parser.parse_args() 327 | 328 | if args.run_experiments: 329 | run_experiments() 330 | -------------------------------------------------------------------------------- /project/proj1/hog/hog_gui.py: -------------------------------------------------------------------------------- 1 | """A graphical user interface (GUI) for the game of Hog. 2 | 3 | This file uses many features of Python not yet covered in the course. A lab 4 | later in the semester will review its implementation and let you extend it. 5 | """ 6 | 7 | import hog 8 | import dice 9 | from ucb import main 10 | 11 | import tkinter as tk 12 | from tkinter import * 13 | import argparse 14 | 15 | ############# 16 | # Utilities # 17 | ############# 18 | 19 | class BetterWidget(object): 20 | """A BetterWidget returns itself on pack and config for call chaining.""" 21 | def pack(self, **kwargs): 22 | super().pack(**kwargs) 23 | return self 24 | 25 | def config(self, **kwargs): 26 | super().config(**kwargs) 27 | return self 28 | 29 | class TextWidget(BetterWidget): 30 | """A TextWidget contains a mutable line of text.""" 31 | def __init__(self, **kwargs): 32 | self.textvar = kwargs.get('textvariable', tk.StringVar()) 33 | self.config(textvariable=self.textvar) 34 | if 'text' in kwargs: 35 | self.textvar.set(kwargs['text']) 36 | 37 | @property 38 | def text(self): 39 | return self.textvar.get() 40 | 41 | @text.setter 42 | def text(self, value): 43 | return self.textvar.set(str(value)) 44 | 45 | class Label(TextWidget, tk.Label): 46 | """A Label is a text label.""" 47 | def __init__(self, parent, **kwargs): 48 | kwargs.update(label_theme) 49 | tk.Label.__init__(self, parent, **kwargs) 50 | TextWidget.__init__(self, **kwargs) 51 | 52 | class Button(BetterWidget, tk.Button): 53 | """A Button is an interactive button.""" 54 | def __init__(self, *args, **kwargs): 55 | kwargs.update(button_theme) 56 | tk.Button.__init__(self, *args, **kwargs) 57 | 58 | class Entry(TextWidget, tk.Entry): 59 | """An Entry widget accepts text entry.""" 60 | def __init__(self, parent, **kwargs): 61 | kwargs.update(entry_theme) 62 | tk.Entry.__init__(self, parent, **kwargs) 63 | TextWidget.__init__(self, **kwargs) 64 | 65 | class Frame(BetterWidget, tk.Frame): 66 | """A Frame contains other widgets.""" 67 | def __init__(self, *args, **kwargs): 68 | kwargs.update(frame_theme) 69 | tk.Frame.__init__(self, *args, **kwargs) 70 | 71 | def name(who): 72 | """Return the name of a player.""" 73 | return "Player {0}".format(who) 74 | 75 | ####### 76 | # GUI # 77 | ####### 78 | 79 | class HogGUIException(BaseException): 80 | """HogGUI-specific Exception. Used to exit a game prematurely.""" 81 | pass 82 | 83 | class HogGUI(Frame): 84 | """Tkinter GUI for Hog.""" 85 | 86 | KILL = -9 # kill signal to stop a game 87 | 88 | ######################### 89 | # Widget Initialization # 90 | ######################### 91 | 92 | def __init__(self, parent, computer=False): 93 | """Replace hog module's dice with hooks to GUI and start a game. 94 | 95 | parent -- parent widget (should be root) 96 | computer -- True if playing against a computer 97 | """ 98 | super().__init__(parent) 99 | self.pack(fill=BOTH) 100 | self.parent = parent 101 | self.who = 0 102 | 103 | self.init_scores() 104 | self.init_rolls() 105 | self.init_dice() 106 | self.init_status() 107 | self.init_restart() 108 | 109 | hog.six_sided = self.make_dice(6) 110 | hog.four_sided = self.make_dice(4) 111 | self.computer, self.turn = computer, 0 112 | self.play() 113 | 114 | def init_scores(self): 115 | """Creates child widgets associated with scoring. 116 | 117 | Each player has a score Label that is updated each turn. Scores can be 118 | accessed and modified through Tkinter variables in self.score_vars. 119 | """ 120 | self.score_frame = Frame(self).pack() 121 | 122 | self.p_frames = [None, None] 123 | self.p_labels = [None, None] 124 | self.s_labels = [None, None] 125 | for i in (0, 1): 126 | self.p_frames[i] = Frame(self.score_frame, padx=25).pack(side=LEFT) 127 | self.p_labels[i] = Label(self.p_frames[i], 128 | text=name(i) + ':').pack() 129 | self.s_labels[i] = Label(self.p_frames[i]).pack() 130 | 131 | def init_rolls(self): 132 | """Creates child widgets associated with the number of rolls. 133 | 134 | The primary widget is an Entry that accepts user input. An intermediate 135 | Tkinter variable, self.roll_verified, is set to the final number of 136 | rolls. Once it is updated, the player immediately takes a turn based on 137 | its value. 138 | """ 139 | self.roll_frame = Frame(self).pack() 140 | 141 | self.roll_label = Label(self.roll_frame).pack() 142 | self.roll_entry = Entry(self.roll_frame, 143 | justify=CENTER).pack() 144 | self.roll_entry.bind('', 145 | lambda event: self.roll_button.invoke()) 146 | self.roll_verified = IntVar() 147 | self.roll_button = Button(self.roll_frame, 148 | text='Roll!', 149 | command=self.roll).pack() 150 | 151 | def init_dice(self): 152 | """Creates child widgets associated with dice. Each dice is stored in a 153 | Label. Dice Labels will be packed or unpacked depending on how many dice 154 | are rolled. 155 | """ 156 | self.dice_frames = [ 157 | Frame(self).pack(), 158 | Frame(self).pack() 159 | ] 160 | self.dice = { 161 | i: Label(self.dice_frames[i//5]). 162 | config(image=HogGUI.IMAGES[6]). 163 | pack(side=LEFT) 164 | for i in range(10) 165 | } 166 | 167 | def init_status(self): 168 | """Creates child widgets associated with the game status. For example, 169 | Hog Wild is displayed here.""" 170 | self.status_label = Label(self).pack() 171 | 172 | def init_restart(self): 173 | """Creates child widgets associated with restarting the game.""" 174 | self.restart_button = Button(self, text='Restart', 175 | command=self.restart).pack() 176 | 177 | ############## 178 | # Game Logic # 179 | ############## 180 | 181 | def make_dice(self, sides): 182 | """Creates a dice function that hooks to the GUI and wraps 183 | dice.make_fair_dice. 184 | 185 | sides -- number of sides for the die 186 | """ 187 | fair_dice = dice.make_fair_dice(sides) 188 | def gui_dice(): 189 | """Roll fair_dice and add a corresponding image to self.dice.""" 190 | result = fair_dice() 191 | img = HogGUI.IMAGES[result] 192 | self.dice[self.dice_count].config(image=img).pack(side=LEFT) 193 | self.dice_count += 1 194 | return result 195 | return gui_dice 196 | 197 | def clear_dice(self): 198 | """Unpacks (hides) all dice Labels.""" 199 | for i in range(10): 200 | self.dice[i].pack_forget() 201 | 202 | def roll(self): 203 | """Verify and set the number of rolls based on user input. As 204 | per game rules, a valid number of rolls must be an integer 205 | greater than or equal to 0. 206 | """ 207 | result = self.roll_entry.text 208 | if result.isnumeric() and 10 >= int(result) >= 0: 209 | self.roll_verified.set(int(result)) 210 | 211 | def switch(self, who=None): 212 | """Switches players. self.who is either 0 or 1.""" 213 | self.p_frames[self.who].config(bg=bg) 214 | self.p_labels[self.who].config(bg=bg) 215 | self.s_labels[self.who].config(bg=bg) 216 | self.who = 1 - self.who if who is None else who 217 | self.p_frames[self.who].config(bg=select_bg) 218 | self.p_labels[self.who].config(bg=select_bg) 219 | self.s_labels[self.who].config(bg=select_bg) 220 | 221 | def strategy(self, score, opp_score): 222 | """A strategy with a hook to the GUI. This strategy gets 223 | passed into the PLAY function from the HOG module. At its 224 | core, the strategy waits until a number of rolls has been 225 | verified, then returns that number. Game information is 226 | updated as well. 227 | 228 | score -- player's score 229 | opp_score -- opponent's score 230 | """ 231 | s0 = score if self.who == 0 else opp_score 232 | s1 = opp_score if self.who == 0 else score 233 | self.s_labels[0].text = s0 234 | self.s_labels[1].text = s1 235 | self.roll_label.text = name(self.who) +' will roll:' 236 | status = self.status_label.text 237 | if hog.select_dice(score, opp_score) == hog.four_sided: 238 | status += ' Hog Wild!' 239 | self.status_label.text = status 240 | 241 | if self.computer and self.who == self.turn: 242 | self.update() 243 | self.after(DELAY) 244 | result = hog.final_strategy(score, opp_score) 245 | else: 246 | self.roll_entry.focus_set() 247 | self.wait_variable(self.roll_verified) 248 | result = self.roll_verified.get() 249 | self.roll_entry.text = '' 250 | if result == HogGUI.KILL: 251 | raise HogGUIException 252 | 253 | self.clear_dice() 254 | self.dice_count = 0 255 | self.status_label.text = '{} chose to roll {}.'.format(name(self.who), 256 | result) 257 | self.switch() 258 | return result 259 | 260 | def play(self): 261 | """Simulates a game of Hog by calling hog.play with the GUI strategies. 262 | 263 | If the player destroys the window prematurely (i.e. in the 264 | middle of a game), a HogGUIException is raised to exit out 265 | of play's loop. Otherwise, the widget will be destroyed, 266 | but the strategy will continue waiting. 267 | """ 268 | self.turn = 1 - self.turn 269 | self.switch(0) 270 | self.s_labels[0].text = '0' 271 | self.s_labels[1].text = '0' 272 | self.status_label.text = '' 273 | try: 274 | score, opponent_score = hog.play(self.strategy, 275 | self.strategy) 276 | except HogGUIException: 277 | pass 278 | else: 279 | self.s_labels[0].text = score 280 | self.s_labels[1].text = opponent_score 281 | winner = 0 if score > opponent_score else 1 282 | self.status_label.text = 'Game over! {} wins!'.format( 283 | name(winner)) 284 | 285 | def restart(self): 286 | """Kills the current game and begins another game.""" 287 | self.roll_verified.set(HogGUI.KILL) 288 | self.status_label.text = '' 289 | self.clear_dice() 290 | self.play() 291 | 292 | def destroy(self): 293 | """Overrides the destroy method to end the current game.""" 294 | self.roll_verified.set(HogGUI.KILL) 295 | super().destroy() 296 | 297 | def run_GUI(computer=False): 298 | """Start the GUI. 299 | 300 | computer -- True if playing against computer 301 | """ 302 | root = Tk() 303 | root.title('The Game of Hog') 304 | root.minsize(520, 400) 305 | root.geometry("520x400") 306 | 307 | # Tkinter only works with GIFs 308 | HogGUI.IMAGES = { 309 | 1: PhotoImage(file='images/die1.gif'), 310 | 2: PhotoImage(file='images/die2.gif'), 311 | 3: PhotoImage(file='images/die3.gif'), 312 | 4: PhotoImage(file='images/die4.gif'), 313 | 5: PhotoImage(file='images/die5.gif'), 314 | 6: PhotoImage(file='images/die6.gif'), 315 | } 316 | 317 | app = HogGUI(root, computer) 318 | root.mainloop() 319 | 320 | ########## 321 | # THEMES # 322 | ########## 323 | 324 | select_bg = '#a6d785' 325 | bg='#ffffff' 326 | fg='#000000' 327 | font=('Arial', 14) 328 | 329 | frame_theme = { 330 | 'bg': bg, 331 | } 332 | 333 | label_theme = { 334 | 'font': font, 335 | 'bg': bg, 336 | 'fg': fg, 337 | } 338 | 339 | button_theme = { 340 | 'font': font, 341 | 'activebackground': select_bg, 342 | 'bg': bg, 343 | 'fg': fg, 344 | } 345 | 346 | entry_theme = { 347 | 'fg': fg, 348 | 'bg': bg, 349 | 'font': font, 350 | 'insertbackground': fg, 351 | } 352 | 353 | ########################## 354 | # Command Line Interface # 355 | ########################## 356 | 357 | DELAY=2000 358 | 359 | @main 360 | def run(*args): 361 | parser = argparse.ArgumentParser(description='Hog GUI') 362 | parser.add_argument('-f', '--final', 363 | help='play against the final strategy in hog.py. ' 364 | 'Computer alternates playing as player 0 and 1.', 365 | action='store_true') 366 | parser.add_argument('-d', '--delay', 367 | help='time delay for computer, in seconds', type=int, 368 | default=2) 369 | args = parser.parse_args() 370 | global DELAY 371 | DELAY = args.delay * 1000 372 | run_GUI(computer=args.final) 373 | -------------------------------------------------------------------------------- /project/proj1/hog/images/die1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kfei/cs61a/d3821f53a9808fce68bd8f9e42e69f87881c2634/project/proj1/hog/images/die1.gif -------------------------------------------------------------------------------- /project/proj1/hog/images/die2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kfei/cs61a/d3821f53a9808fce68bd8f9e42e69f87881c2634/project/proj1/hog/images/die2.gif -------------------------------------------------------------------------------- /project/proj1/hog/images/die3.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kfei/cs61a/d3821f53a9808fce68bd8f9e42e69f87881c2634/project/proj1/hog/images/die3.gif -------------------------------------------------------------------------------- /project/proj1/hog/images/die4.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kfei/cs61a/d3821f53a9808fce68bd8f9e42e69f87881c2634/project/proj1/hog/images/die4.gif -------------------------------------------------------------------------------- /project/proj1/hog/images/die5.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kfei/cs61a/d3821f53a9808fce68bd8f9e42e69f87881c2634/project/proj1/hog/images/die5.gif -------------------------------------------------------------------------------- /project/proj1/hog/images/die6.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kfei/cs61a/d3821f53a9808fce68bd8f9e42e69f87881c2634/project/proj1/hog/images/die6.gif -------------------------------------------------------------------------------- /project/proj1/hog/ok: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kfei/cs61a/d3821f53a9808fce68bd8f9e42e69f87881c2634/project/proj1/hog/ok -------------------------------------------------------------------------------- /project/proj1/hog/tests/info.py: -------------------------------------------------------------------------------- 1 | info = { 2 | 'hash_key': 'jrg6xr8melsozsx5je8rvuvambt8vb6ix4vs30yr8ek9p5qd2cdr3l0o4irf15sqn26is658gcli7hiflm1svwpiogjae84eksx3oc62ur5ncvswjd4vumbvrm7ln4oh', 3 | 'name': 'proj1', 4 | 'params': { 5 | 'doctest': { 6 | 'cache': """ 7 | import hog 8 | from hog import * 9 | """ 10 | } 11 | }, 12 | 'src_files': [ 13 | 'hog.py' 14 | ], 15 | 'version': '1.0' 16 | } -------------------------------------------------------------------------------- /project/proj1/hog/tests/q00.py: -------------------------------------------------------------------------------- 1 | test = { 2 | 'names': [ 3 | 'q00', 4 | '0', 5 | 'q0' 6 | ], 7 | 'points': 0, 8 | 'suites': [ 9 | [ 10 | { 11 | 'test': """ 12 | >>> test_dice = make_test_dice(4, 1, 2) 13 | >>> test_dice() 14 | 4 15 | >>> test_dice() # Second call 16 | 1 17 | >>> test_dice() # Third call 18 | 2 19 | >>> test_dice() # Fourth call 20 | 4 21 | """, 22 | 'type': 'doctest' 23 | } 24 | ] 25 | ] 26 | } -------------------------------------------------------------------------------- /project/proj1/hog/tests/q01.py: -------------------------------------------------------------------------------- 1 | test = { 2 | 'names': [ 3 | 'q01', 4 | '1', 5 | 'q1' 6 | ], 7 | 'points': 1, 8 | 'suites': [ 9 | [ 10 | { 11 | 'test': """ 12 | >>> roll_dice(2, make_test_dice(4, 6, 1)) 13 | 10 14 | """, 15 | 'type': 'doctest' 16 | }, 17 | { 18 | 'test': """ 19 | >>> roll_dice(3, make_test_dice(4, 6, 1)) 20 | 1 21 | """, 22 | 'type': 'doctest' 23 | }, 24 | { 25 | 'test': """ 26 | >>> roll_dice(3, make_test_dice(1, 2, 3)) 27 | 1 28 | """, 29 | 'type': 'doctest' 30 | }, 31 | { 32 | 'test': """ 33 | >>> counted_dice = make_test_dice(4, 1, 2, 6) 34 | >>> roll_dice(3, counted_dice) 35 | 1 36 | >>> roll_dice(1, counted_dice) # Make sure you call dice exactly num_rolls times! 37 | 6 38 | """, 39 | 'type': 'doctest' 40 | } 41 | ] 42 | ] 43 | } -------------------------------------------------------------------------------- /project/proj1/hog/tests/q02.py: -------------------------------------------------------------------------------- 1 | test = { 2 | 'names': [ 3 | 'q02', 4 | '2', 5 | 'q2' 6 | ], 7 | 'points': 1, 8 | 'suites': [ 9 | [ 10 | { 11 | 'test': """ 12 | >>> take_turn(2, 0, make_test_dice(4, 6, 1)) 13 | 10 14 | """, 15 | 'type': 'doctest' 16 | }, 17 | { 18 | 'test': """ 19 | >>> take_turn(3, 20, make_test_dice(4, 6, 1)) 20 | 1 21 | """, 22 | 'type': 'doctest' 23 | }, 24 | { 25 | 'test': """ 26 | >>> take_turn(0, 35) 27 | 3 28 | """, 29 | 'type': 'doctest' 30 | }, 31 | { 32 | 'test': """ 33 | >>> take_turn(0, 71) 34 | 7 35 | """, 36 | 'type': 'doctest' 37 | }, 38 | { 39 | 'test': """ 40 | >>> take_turn(0, 7) 41 | 8 42 | """, 43 | 'type': 'doctest' 44 | } 45 | ] 46 | ] 47 | } -------------------------------------------------------------------------------- /project/proj1/hog/tests/q03.py: -------------------------------------------------------------------------------- 1 | test = { 2 | 'names': [ 3 | 'q03', 4 | '3', 5 | 'q3' 6 | ], 7 | 'points': 1, 8 | 'suites': [ 9 | [ 10 | { 11 | 'test': """ 12 | >>> select_dice(4, 24) == six_sided 13 | False 14 | """, 15 | 'type': 'doctest' 16 | }, 17 | { 18 | 'test': """ 19 | >>> select_dice(16, 64) == six_sided 20 | True 21 | """, 22 | 'type': 'doctest' 23 | }, 24 | { 25 | 'test': """ 26 | >>> select_dice(0, 0) == six_sided 27 | False 28 | """, 29 | 'type': 'doctest' 30 | }, 31 | { 32 | 'test': """ 33 | >>> select_dice(50, 80) == six_sided 34 | True 35 | """, 36 | 'type': 'doctest' 37 | } 38 | ] 39 | ] 40 | } -------------------------------------------------------------------------------- /project/proj1/hog/tests/q04.py: -------------------------------------------------------------------------------- 1 | test = { 2 | 'names': [ 3 | 'q04', 4 | '4', 5 | 'q4' 6 | ], 7 | 'points': 1, 8 | 'suites': [ 9 | [ 10 | { 11 | 'test': """ 12 | >>> score0, score1, start = bid_for_start(1, 1, goal=100) # start can be 0 or 1 13 | >>> score0 14 | 100 15 | >>> score1 16 | 100 17 | """, 18 | 'type': 'doctest' 19 | }, 20 | { 21 | 'test': """ 22 | >>> score0, score1, start = bid_for_start(2, 7, goal=100) 23 | >>> score0 24 | 0 25 | >>> score1 26 | 10 27 | >>> start 28 | 1 29 | """, 30 | 'type': 'doctest' 31 | }, 32 | { 33 | 'test': """ 34 | >>> score0, score1, start = bid_for_start(8, 3, goal=100) 35 | >>> score0 36 | 10 37 | >>> score1 38 | 0 39 | >>> start 40 | 0 41 | """, 42 | 'type': 'doctest' 43 | }, 44 | { 45 | 'test': """ 46 | >>> score0, score1, start = bid_for_start(4, 3, goal=100) 47 | >>> score0 48 | 3 49 | >>> score1 50 | 4 51 | >>> start 52 | 0 53 | """, 54 | 'type': 'doctest' 55 | }, 56 | { 57 | 'test': """ 58 | >>> score0, score1, start = bid_for_start(3, 4, goal=100) 59 | >>> score0 60 | 4 61 | >>> score1 62 | 3 63 | >>> start 64 | 1 65 | """, 66 | 'type': 'doctest' 67 | } 68 | ] 69 | ] 70 | } -------------------------------------------------------------------------------- /project/proj1/hog/tests/q05.py: -------------------------------------------------------------------------------- 1 | test = { 2 | 'names': [ 3 | 'q05', 4 | '5', 5 | 'q5' 6 | ], 7 | 'params': { 8 | 'doctest': { 9 | 'cache': """ 10 | always = hog.always_roll 11 | def weird_strat(score, opponent): 12 | return opponent // 10 13 | """, 14 | 'setup': """ 15 | hog.four_sided = make_test_dice(1) 16 | hog.six_sided = make_test_dice(3) 17 | """ 18 | } 19 | }, 20 | 'points': 3, 21 | 'suites': [ 22 | [ 23 | { 24 | 'answer': 'While score0 and score1 are both less than goal', 25 | 'choices': [ 26 | 'While score0 and score1 are both less than goal', 27 | 'While at least one of score0 or score1 is less than goal', 28 | 'While score0 is less than goal', 29 | 'While score1 is less than goal' 30 | ], 31 | 'question': """ 32 | The variables score0 and score1 are the scores for both 33 | players. Under what conditions should the game continue? 34 | """, 35 | 'type': 'concept' 36 | }, 37 | { 38 | 'answer': 'strategy1(score1, score0)', 39 | 'choices': [ 40 | 'strategy1(score1, score0)', 41 | 'strategy1(score0, score1)', 42 | 'strategy1(score1)', 43 | 'strategy1(score0)' 44 | ], 45 | 'question': """ 46 | If strategy1 is Player 1's strategy function, score0 is 47 | Player 0's current score, and score1 is Player 1's current 48 | score, then which of the following demonstrates correct 49 | usage of strategy1? 50 | """, 51 | 'type': 'concept' 52 | }, 53 | { 54 | 'answer': """ 55 | After the current player takes her turn, and if either 56 | player's score is double the other player's score 57 | """, 58 | 'choices': [ 59 | """ 60 | After the current player takes her turn, and if either 61 | player's score is double the other player's score 62 | """, 63 | """ 64 | After the current player takes her turn, and if the 65 | current player's score is double her opponent's score 66 | """, 67 | """ 68 | Before the current player takes her turn, and if either 69 | player's score is double the other player's score 70 | """, 71 | """ 72 | Before the current player takes her turn, and if the 73 | current player's score is double her opponent's score 74 | """ 75 | ], 76 | 'question': 'Recall the "swine swap" rule. When does this rule apply?', 77 | 'type': 'concept' 78 | }, 79 | { 80 | 'never_lock': True, 81 | 'test': """ 82 | >>> hog.play(always(5), always(5)) 83 | (92, 106) 84 | """, 85 | 'type': 'doctest' 86 | }, 87 | { 88 | 'never_lock': True, 89 | 'test': """ 90 | >>> hog.play(always(2), always(2)) 91 | (17, 102) 92 | """, 93 | 'type': 'doctest' 94 | }, 95 | { 96 | 'never_lock': True, 97 | 'test': """ 98 | >>> hog.play(always(2), always(10)) 99 | (19, 120) 100 | """, 101 | 'type': 'doctest' 102 | }, 103 | { 104 | 'never_lock': True, 105 | 'test': """ 106 | >>> hog.play(always(0), always(0)) 107 | (101, 97) 108 | """, 109 | 'type': 'doctest' 110 | }, 111 | { 112 | 'never_lock': True, 113 | 'test': """ 114 | >>> hog.play(always(0), always(2)) 115 | (100, 95) 116 | """, 117 | 'type': 'doctest' 118 | }, 119 | { 120 | 'never_lock': True, 121 | 'test': """ 122 | >>> hog.play(always(0), weird_strat) 123 | (64, 109) 124 | """, 125 | 'type': 'doctest' 126 | }, 127 | { 128 | 'never_lock': True, 129 | 'test': """ 130 | >>> hog.play(weird_strat, weird_strat) 131 | (108, 93) 132 | """, 133 | 'type': 'doctest' 134 | } 135 | ] 136 | ] 137 | } -------------------------------------------------------------------------------- /project/proj1/hog/tests/q06.py: -------------------------------------------------------------------------------- 1 | test = { 2 | 'names': [ 3 | 'q06', 4 | 'q6', 5 | '6' 6 | ], 7 | 'params': { 8 | 'doctest': { 9 | 'setup': """ 10 | 11 | """, 12 | 'teardown': """ 13 | 14 | """ 15 | } 16 | }, 17 | 'points': 2, 18 | 'suites': [ 19 | [ 20 | { 21 | 'test': """ 22 | >>> dice = make_test_dice(3, 1, 5, 6) 23 | >>> averaged_dice = make_averaged(dice, 1000) 24 | >>> averaged_dice() # average of calling dice 1000 times 25 | 3.75 26 | """, 27 | 'type': 'doctest' 28 | }, 29 | { 30 | 'never_lock': True, 31 | 'test': """ 32 | >>> dice = make_test_dice(3, 1, 5, 6) 33 | >>> averaged_roll_dice = make_averaged(roll_dice, 1000) 34 | >>> averaged_roll_dice(2, dice) 35 | 6.0 36 | """, 37 | 'type': 'doctest' 38 | } 39 | ] 40 | ] 41 | } -------------------------------------------------------------------------------- /project/proj1/hog/tests/q07.py: -------------------------------------------------------------------------------- 1 | test = { 2 | 'names': [ 3 | 'q07', 4 | '7', 5 | 'q7' 6 | ], 7 | 'points': 2, 8 | 'suites': [ 9 | [ 10 | { 11 | 'test': """ 12 | >>> dice = make_test_dice(3) # dice always returns 3 13 | >>> max_scoring_num_rolls(dice) 14 | 10 15 | """, 16 | 'type': 'doctest' 17 | } 18 | ], 19 | [ 20 | { 21 | 'answer': 'The lowest num_rolls', 22 | 'choices': [ 23 | 'The lowest num_rolls', 24 | 'The highest num_rolls', 25 | 'A random num_rolls' 26 | ], 27 | 'question': """ 28 | If multiple num_rolls are tied for the highest scoring 29 | average, which should you return? 30 | """, 31 | 'type': 'concept' 32 | }, 33 | { 34 | 'test': """ 35 | >>> dice = make_test_dice(2) # dice always rolls 2 36 | >>> max_scoring_num_rolls(dice) 37 | 10 38 | """, 39 | 'type': 'doctest' 40 | }, 41 | { 42 | 'test': """ 43 | >>> dice = make_test_dice(1, 2) # dice alternates 1 and 2 44 | >>> max_scoring_num_rolls(dice) 45 | 1 46 | """, 47 | 'type': 'doctest' 48 | } 49 | ] 50 | ] 51 | } -------------------------------------------------------------------------------- /project/proj1/hog/tests/q08.py: -------------------------------------------------------------------------------- 1 | test = { 2 | 'names': [ 3 | 'q08', 4 | 'q8', 5 | '8' 6 | ], 7 | 'points': 1, 8 | 'suites': [ 9 | [ 10 | { 11 | 'test': """ 12 | >>> bacon_strategy(0, 0) 13 | 5 14 | """, 15 | 'type': 'doctest' 16 | }, 17 | { 18 | 'test': """ 19 | >>> bacon_strategy(70, 50) 20 | 5 21 | """, 22 | 'type': 'doctest' 23 | }, 24 | { 25 | 'test': """ 26 | >>> bacon_strategy(50, 70) 27 | 0 28 | """, 29 | 'type': 'doctest' 30 | }, 31 | { 32 | 'test': """ 33 | >>> bacon_strategy(32, 4, 5, 4) 34 | 0 35 | """, 36 | 'type': 'doctest' 37 | }, 38 | { 39 | 'test': """ 40 | >>> bacon_strategy(20, 25, 5, 4) 41 | 4 42 | """, 43 | 'type': 'doctest' 44 | } 45 | ] 46 | ] 47 | } -------------------------------------------------------------------------------- /project/proj1/hog/tests/q09.py: -------------------------------------------------------------------------------- 1 | test = { 2 | 'names': [ 3 | 'q09', 4 | '9', 5 | 'q9' 6 | ], 7 | 'points': 2, 8 | 'suites': [ 9 | [ 10 | { 11 | 'test': """ 12 | >>> swap_strategy(23, 60) # 23 + (1 + abs(6 - 0)) = 30 13 | 0 14 | """, 15 | 'type': 'doctest' 16 | }, 17 | { 18 | 'test': """ 19 | >>> swap_strategy(27, 17) # 27 + (1 + abs(1 - 7)) = 34 20 | 5 21 | """, 22 | 'type': 'doctest' 23 | }, 24 | { 25 | 'test': """ 26 | >>> swap_strategy(50, 80) # 1 + abs(8 - 0) = 9 27 | 0 28 | """, 29 | 'type': 'doctest' 30 | }, 31 | { 32 | 'test': """ 33 | >>> swap_strategy(12, 12) # Baseline 34 | 5 35 | """, 36 | 'type': 'doctest' 37 | } 38 | ], 39 | [ 40 | { 41 | 'test': """ 42 | >>> swap_strategy(15, 34, 5, 4) # beneficial swap 43 | 0 44 | """, 45 | 'type': 'doctest' 46 | }, 47 | { 48 | 'test': """ 49 | >>> swap_strategy(8, 9, 5, 4) # harmful swap 50 | 4 51 | """, 52 | 'type': 'doctest' 53 | } 54 | ] 55 | ] 56 | } -------------------------------------------------------------------------------- /project/proj1/hog/tests/q10.py: -------------------------------------------------------------------------------- 1 | test = { 2 | 'names': [ 3 | 'q10', 4 | '10' 5 | ], 6 | 'note': """ 7 | Tests for Q10 are not included with ok. 8 | Submit your project to receive results by email. 9 | """, 10 | 'points': 3 11 | } -------------------------------------------------------------------------------- /project/proj1/hog/ucb.py: -------------------------------------------------------------------------------- 1 | """The UCB module contains functions specific to 61A projects at UC Berkeley.""" 2 | 3 | import code 4 | import functools 5 | import inspect 6 | import re 7 | import signal 8 | import sys 9 | 10 | 11 | def main(fn): 12 | """Call fn with command line arguments. Used as a decorator. 13 | 14 | The main decorator marks the function that starts a program. For example, 15 | 16 | @main 17 | def my_run_function(): 18 | # function body 19 | 20 | Use this instead of the typical __name__ == "__main__" predicate. 21 | """ 22 | if inspect.stack()[1][0].f_locals['__name__'] == '__main__': 23 | args = sys.argv[1:] # Discard the script name from command line 24 | fn(*args) # Call the main function 25 | return fn 26 | 27 | _PREFIX = '' 28 | def trace(fn): 29 | """A decorator that prints a function's name, its arguments, and its return 30 | values each time the function is called. For example, 31 | 32 | @trace 33 | def compute_something(x, y): 34 | # function body 35 | """ 36 | @functools.wraps(fn) 37 | def wrapped(*args, **kwds): 38 | global _PREFIX 39 | reprs = [repr(e) for e in args] 40 | reprs += [repr(k) + '=' + repr(v) for k, v in kwds.items()] 41 | log('{0}({1})'.format(fn.__name__, ', '.join(reprs)) + ':') 42 | _PREFIX += ' ' 43 | try: 44 | result = fn(*args, **kwds) 45 | _PREFIX = _PREFIX[:-4] 46 | except Exception as e: 47 | log(fn.__name__ + ' exited via exception') 48 | _PREFIX = _PREFIX[:-4] 49 | raise 50 | # Here, print out the return value. 51 | log('{0}({1}) -> {2}'.format(fn.__name__, ', '.join(reprs), result)) 52 | return result 53 | return wrapped 54 | 55 | 56 | def log(message): 57 | """Print an indented message (used with trace).""" 58 | if type(message) is not str: 59 | message = str(message) 60 | print(_PREFIX + re.sub('\n', '\n' + _PREFIX, message)) 61 | 62 | 63 | def log_current_line(): 64 | """Print information about the current line of code.""" 65 | frame = inspect.stack()[1] 66 | log('Current line: File "{f[1]}", line {f[2]}, in {f[3]}'.format(f=frame)) 67 | 68 | 69 | def interact(msg=None): 70 | """Start an interactive interpreter session in the current environment. 71 | 72 | On Unix: 73 | -D exits the interactive session and returns to normal execution. 74 | In Windows: 75 | -Z exists the interactive session and returns to normal 76 | execution. 77 | """ 78 | # use exception trick to pick up the current frame 79 | try: 80 | raise None 81 | except: 82 | frame = sys.exc_info()[2].tb_frame.f_back 83 | 84 | # evaluate commands in current namespace 85 | namespace = frame.f_globals.copy() 86 | namespace.update(frame.f_locals) 87 | 88 | # exit on interrupt 89 | def handler(signum, frame): 90 | print() 91 | exit(0) 92 | signal.signal(signal.SIGINT, handler) 93 | 94 | if not msg: 95 | _, filename, line, _, _, _ = inspect.stack()[1] 96 | msg = 'Interacting at File "{0}", line {1} \n'.format(filename, line) 97 | msg += ' Unix: -D continues the program; \n' 98 | msg += ' Windows: -Z continues the program; \n' 99 | msg += ' exit() or -C exits the program' 100 | 101 | code.interact(msg, None, namespace) 102 | -------------------------------------------------------------------------------- /project/proj2/trends/data.py: -------------------------------------------------------------------------------- 1 | """Functions for reading data from the sentiment dictionary and tweet files.""" 2 | 3 | import os 4 | import re 5 | import string 6 | import sys 7 | from datetime import datetime 8 | from ucb import main, interact 9 | 10 | # Look for data directory 11 | PY_PATH = sys.argv[0] 12 | if PY_PATH.endswith('doctest.py') and len(sys.argv) > 1: 13 | PY_PATH = sys.argv[1] 14 | DATA_PATH = os.path.join(os.path.dirname(PY_PATH), 'data') + os.sep 15 | if not os.path.exists(DATA_PATH): 16 | DATA_PATH = 'data' + os.sep 17 | 18 | def load_sentiments(path=DATA_PATH + "sentiments.csv"): 19 | """Read the sentiment file and return a dictionary containing the sentiment 20 | score of each word, a value from -1 to +1. 21 | """ 22 | with open(path, encoding='utf8') as sentiment_file: 23 | scores = [line.split(',') for line in sentiment_file] 24 | return {word: float(score.strip()) for word, score in scores} 25 | 26 | word_sentiments = load_sentiments() 27 | 28 | def file_name_for_term(term, unfiltered_name): 29 | """Return a valid file name for an arbitrary term string.""" 30 | valid_characters = '-_' + string.ascii_letters + string.digits 31 | no_space_lower = term.lower().replace(' ', '_') 32 | valid_only = [c for c in no_space_lower if c in valid_characters] 33 | return ''.join(valid_only) + '_' + unfiltered_name 34 | 35 | def generate_filtered_file(unfiltered_name, term): 36 | """Return the path to a file containing tweets that match term, generating 37 | that file if necessary. 38 | """ 39 | filtered_path = DATA_PATH + file_name_for_term(term, unfiltered_name) 40 | if not os.path.exists(filtered_path): 41 | msg = 'Generating filtered tweets file for "{0}" using tweets from {1}' 42 | print(msg.format(term, unfiltered_name)) 43 | r = re.compile('\W' + term + '\W', flags=re.IGNORECASE) 44 | with open(filtered_path, mode='w', encoding='utf8') as out: 45 | with open(DATA_PATH + unfiltered_name, encoding='utf8') as full: 46 | matches = [line for line in full if term in line.lower()] 47 | for line in matches: 48 | if r.search(line): 49 | out.write(line) 50 | print('Wrote {}.'.format(filtered_path)) 51 | return filtered_path 52 | 53 | def tweet_from_line(line, make_tweet): 54 | """Parse a line and call make_tweet on its contents.""" 55 | loc, _, time_text, text = line.strip().split("\t") 56 | time = datetime.strptime(time_text, '%Y-%m-%d %H:%M:%S') 57 | lat, lon = [float(x) for x in loc[1:-1].split(',')] 58 | return make_tweet(text.lower(), time, lat, lon) 59 | 60 | def load_tweets(make_tweet, term='cali', file_name='tweets2014.txt'): 61 | """Return the list of tweets in file_name that contain term. 62 | 63 | Arguments: 64 | make_tweet -- a constructor function that takes four arguments: 65 | 1) a string containing the words in the tweet 66 | 2) a datetime.datetime object representing the time of the tweet 67 | 3) a longitude coordinate 68 | 4) a latitude coordinate 69 | """ 70 | filtered_path = generate_filtered_file(file_name, term) 71 | with open(filtered_path, encoding='utf8') as tweets: 72 | return [tweet_from_line(line, make_tweet) for line in tweets 73 | if len(line.strip().split("\t")) >= 4] 74 | -------------------------------------------------------------------------------- /project/proj2/trends/geo.py: -------------------------------------------------------------------------------- 1 | """Geography and projection utilities.""" 2 | 3 | from data import DATA_PATH 4 | from math import sin, cos, atan2, radians, sqrt 5 | from json import JSONDecoder 6 | 7 | ############################# 8 | # Position Data Abstraction # 9 | ############################# 10 | 11 | def make_position(lat, lon): 12 | """Return a geographic position, which has a latitude and longitude.""" 13 | return [lat, lon] 14 | 15 | def latitude(position): 16 | """Return the latitudinal coordinate of a geographic position.""" 17 | return position[0] 18 | 19 | def longitude(position): 20 | """Return the longitudinal coordinate of a geographic position.""" 21 | return position[1] 22 | 23 | ### === +++ ABSTRACTION BARRIER +++ === ### 24 | 25 | def geo_distance(position0, position1): 26 | """Return the great circle distance (in miles) between two 27 | geographic positions. 28 | 29 | Uses the "haversine" formula. 30 | http://en.wikipedia.org/wiki/Haversine_formula 31 | 32 | >>> round(geo_distance(make_position(50, 5), make_position(58, 3)), 1) 33 | 559.2 34 | """ 35 | earth_radius = 3963.2 # miles 36 | positions = [position0, position1] 37 | lat0, lat1 = [radians(latitude(p)) for p in positions] 38 | lon0, lon1 = [radians(longitude(p)) for p in positions] 39 | dlat, dlon = lat1-lat0, lon1-lon0 40 | a = sin(dlat/2) ** 2 + sin(dlon/2) ** 2 * cos(lat0) * cos(lat1) 41 | c = 2 * atan2(sqrt(a), sqrt(1-a)); 42 | return earth_radius * c; 43 | 44 | def position_to_xy(position): 45 | """Convert a geographic position within the US to a planar x-y point.""" 46 | lat = latitude(position) 47 | lon = longitude(position) 48 | if lat < 25: 49 | return _hawaii(position) 50 | elif lat > 52: 51 | return _alaska(position) 52 | else: 53 | return _lower48(position) 54 | 55 | def albers(origin, parallels, translate, scale): 56 | """Return an Albers projection from geographic positions to x-y positions. 57 | 58 | Derived from Mike Bostock's Albers javascript implementation for D3 59 | http://mbostock.github.com/d3 60 | http://mathworld.wolfram.com/AlbersEqual-AreaConicProjection.html 61 | 62 | Arguments: 63 | origin -- a geographic position 64 | parallels -- bounding latitudes 65 | translate -- x-y translation to place the projection within a larger map 66 | scale -- scaling factor 67 | """ 68 | phi1, phi2 = [radians(p) for p in parallels] 69 | s, c = sin(phi1), cos(phi1) 70 | base_lat = radians(latitude(origin)) 71 | base_lon = radians(longitude(origin)) 72 | n = 0.5 * (s + sin(phi2)) 73 | C = c*c + 2*n*s 74 | p0 = sqrt(C - 2*n*sin(base_lat))/n 75 | 76 | def project(position): 77 | lat, lon = radians(latitude(position)), radians(longitude(position)) 78 | t = n * (lon - base_lon) 79 | p = sqrt(C - 2*n*sin(lat))/n 80 | x = scale * p * sin(t) + translate[0] 81 | y = scale * (p * cos(t) - p0) + translate[1] 82 | return [x, y] 83 | return project 84 | 85 | _lower48 = albers(make_position(38, -98), [29.5, 45.5], [480, 250], 1000) 86 | _alaska = albers(make_position(60, -160), [55, 65], [150, 440], 400) 87 | _hawaii = albers(make_position(20, -160), [8, 18], [300, 450], 1000) 88 | 89 | def format_shapes(shapes): 90 | """Format a shape as a list of lists of positions, one for each shape. 91 | The states.json file has an odd format, that the get function normalizes. 92 | 93 | >>> single = [] 94 | """ 95 | def get(shape): 96 | """Get the shape from one of the elements of shapes.""" 97 | if type(shape[0][0]) == list: # the shape is a single polygon 98 | assert len(shape) == 1, 'Multi-polygon shape' 99 | return shape[0] 100 | else: 101 | return shape 102 | 103 | return [[make_position(lat, lon) for lon, lat in get(s)] for s in shapes] 104 | 105 | def load_states(): 106 | """Return a dictionary from state names to lists of polygons. 107 | 108 | >>> len(load_states()['HI']) # Hawaii has 5 islands 109 | 5 110 | """ 111 | with open(DATA_PATH + 'states.json', encoding='utf8') as json_data_file: 112 | states = JSONDecoder().decode(json_data_file.read()) 113 | return {state: format_shapes(shapes) for state, shapes in states.items()} 114 | 115 | us_states = load_states() 116 | -------------------------------------------------------------------------------- /project/proj2/trends/graphics.py: -------------------------------------------------------------------------------- 1 | """The graphics module implements a simple GUI library.""" 2 | 3 | import sys 4 | import math 5 | 6 | try: 7 | import tkinter 8 | except ImportError as e: 9 | print('Could not load tkinter: ' + str(e)) 10 | 11 | FRAME_TIME = 1/30 12 | 13 | class Canvas(object): 14 | """A Canvas object supports drawing and animation primitives. 15 | 16 | draw_* methods return the id number of a shape object in the underlying Tk 17 | object. This id can be passed to move_* and edit_* methods. 18 | 19 | Canvas is a singleton; only one Canvas instance can be created. 20 | """ 21 | _instance = None 22 | 23 | def __init__(self, width=1024, height=768, title='', color='White', tk=None): 24 | # Singleton enforcement 25 | if Canvas._instance is not None: 26 | raise Exception('Only one canvas can be instantiated.') 27 | Canvas._instance = self 28 | 29 | # Attributes 30 | self.color = color 31 | self.width = width 32 | self.height = height 33 | 34 | # Root window 35 | self._tk = tk or tkinter.Tk() 36 | self._tk.protocol('WM_DELETE_WINDOW', sys.exit) 37 | self._tk.title(title or 'Graphics Window') 38 | self._tk.bind('', self._click) 39 | self._click_pos = None 40 | 41 | # Canvas object 42 | self._canvas = tkinter.Canvas(self._tk, width=width, height=height) 43 | self._canvas.pack() 44 | self._draw_background() 45 | self._canvas.update() 46 | self._images = dict() 47 | 48 | def clear(self, shape='all'): 49 | """Clear all shapes, text, and images.""" 50 | self._canvas.delete(shape) 51 | if shape == 'all': 52 | self._draw_background() 53 | self._canvas.update() 54 | 55 | def draw_polygon(self, points, color='Black', fill_color=None, filled=1, smooth=0, width=1): 56 | """Draw a polygon and return its tkinter id. 57 | 58 | points -- a list of (x, y) pairs encoding pixel positions 59 | """ 60 | if fill_color == None: 61 | fill_color = color 62 | if filled == 0: 63 | fill_color = "" 64 | return self._canvas.create_polygon(flattened(points), outline=color, fill=fill_color, 65 | smooth=smooth, width=width) 66 | 67 | def draw_circle(self, center, radius, color='Black', fill_color=None, filled=1, width=1): 68 | """Draw a cirlce and return its tkinter id. 69 | 70 | center -- an (x, y) pair encoding a pixel position 71 | """ 72 | if fill_color == None: 73 | fill_color = color 74 | if filled == 0: 75 | fill_color = "" 76 | x0, y0 = [c - radius for c in center] 77 | x1, y1 = [c + radius for c in center] 78 | return self._canvas.create_oval(x0, y0, x1, y1, outline=color, fill=fill_color, width=width) 79 | 80 | def draw_image(self, pos, image_file=None, scale=1, anchor=tkinter.NW): 81 | """Draw an image from a file and return its tkinter id.""" 82 | key = (image_file, scale) 83 | if key not in self._images: 84 | image = tkinter.PhotoImage(file=image_file) 85 | if scale >= 1: 86 | image = image.zoom(int(scale)) 87 | else: 88 | image = image.subsample(int(1/scale)) 89 | self._images[key] = image 90 | 91 | image = self._images[key] 92 | x, y = pos 93 | return self._canvas.create_image(x, y, image=image, anchor=anchor) 94 | 95 | def draw_text(self, text, pos, color='Black', font='Arial', 96 | size=12, style='normal', anchor=tkinter.NW): 97 | """Draw text and return its tkinter id.""" 98 | x, y = pos 99 | font = (font, str(size), style) 100 | return self._canvas.create_text(x, y, fill=color, text=text, font=font, anchor=anchor) 101 | 102 | def edit_text(self, id, text=None, color=None, font=None, size=12, 103 | style='normal'): 104 | """Edit the text, color, or font of an existing text object.""" 105 | if color is not None: 106 | self._canvas.itemconfigure(id, fill=color) 107 | if text is not None: 108 | self._canvas.itemconfigure(id, text=text) 109 | if font is not None: 110 | self._canvas.itemconfigure(id, font=(font, str(size), style)) 111 | 112 | def animate_shape(self, id, duration, points_fn, frame_count=0): 113 | """Animate an existing shape over points.""" 114 | max_frames = duration // FRAME_TIME 115 | points = points_fn(frame_count) 116 | self._canvas.coords(id, flattened(points)) 117 | if frame_count < max_frames: 118 | def tail(): 119 | """Continues the animation at the next frame.""" 120 | self.animate_shape(id, duration, points_fn, frame_count + 1) 121 | self._tk.after(int(FRAME_TIME * 1000), tail) 122 | 123 | def slide_shape(self, id, end_pos, duration, elapsed=0): 124 | """Slide an existing shape to end_pos.""" 125 | points = paired(self._canvas.coords(id)) 126 | start_pos = points[0] 127 | max_frames = duration // FRAME_TIME 128 | def points_fn(frame_count): 129 | completed = frame_count / max_frames 130 | offset = [(e - s) * completed for s, e in zip(start_pos, end_pos)] 131 | return [shift_point(p, offset) for p in points] 132 | self.animate_shape(id, duration, points_fn) 133 | 134 | def wait_for_click(self, seconds=0): 135 | """Return (position, elapsed) pair of click position and elapsed time. 136 | 137 | position: (x,y) pixel position of click 138 | elapsed: milliseconds elapsed since call 139 | seconds: maximum number of seconds to wait for a click 140 | 141 | If there is still no click after the given time, return (None, seconds). 142 | 143 | """ 144 | elapsed = 0 145 | while elapsed < seconds or seconds == 0: 146 | if self._click_pos is not None and self._click_pos[1]>0: 147 | pos = self._click_pos 148 | self._click_pos = None 149 | return pos, elapsed 150 | self._sleep(FRAME_TIME) 151 | elapsed += FRAME_TIME 152 | return None, elapsed 153 | 154 | def _draw_background(self): 155 | w, h = self.width - 1, self.height - 1 156 | corners = [(0,0), (0, h), (w, h), (w, 0)] 157 | self.draw_polygon(corners, self.color, fill_color=self.color, filled=True, smooth=False) 158 | 159 | def _click(self, event): 160 | self._click_pos = (event.x, event.y) 161 | 162 | def _sleep(self, seconds): 163 | self._tk.update_idletasks() 164 | self._tk.after(int(1000 * seconds), self._tk.quit) 165 | self._tk.mainloop() 166 | 167 | def flattened(points): 168 | """Return a flat list of coordinates from a list of pairs.""" 169 | coords = list() 170 | [coords.extend(p) for p in points] 171 | return tuple(coords) 172 | 173 | def paired(coords): 174 | """Return a list of pairs from a flat list of coordinates.""" 175 | assert len(coords) % 2 == 0, 'Coordinates are not paired.' 176 | points = [] 177 | x = None 178 | for elem in coords: 179 | if x is None: 180 | x = elem 181 | else: 182 | points.append((x, elem)) 183 | x = None 184 | return points 185 | 186 | def translate_point(point, angle, distance): 187 | """Translate a point a distance in a direction (angle).""" 188 | x, y = point 189 | return (x + math.cos(angle) * distance, y + math.sin(angle) * distance) 190 | 191 | def shift_point(point, offset): 192 | """Shift a point by an offset.""" 193 | x, y = point 194 | dx, dy = offset 195 | return (x + dx, y + dy) 196 | 197 | def rectangle_points(pos, width, height): 198 | """Return the points of a rectangle starting at pos.""" 199 | x1, y1 = pos 200 | x2, y2 = width + x1, height + y1 201 | return [(x1, y1), (x1, y2), (x2, y2), (x2, y1)] 202 | 203 | def format_color(r, g, b): 204 | """Format a color as a string. 205 | 206 | r, g, b -- integers from 0 to 255 207 | """ 208 | return '#{0:02x}{1:02x}{2:02x}'.format(int(r * 255), int(g * 255), int(b * 255)) 209 | -------------------------------------------------------------------------------- /project/proj2/trends/images/cali.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kfei/cs61a/d3821f53a9808fce68bd8f9e42e69f87881c2634/project/proj2/trends/images/cali.png -------------------------------------------------------------------------------- /project/proj2/trends/images/cali_no_dots.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kfei/cs61a/d3821f53a9808fce68bd8f9e42e69f87881c2634/project/proj2/trends/images/cali_no_dots.png -------------------------------------------------------------------------------- /project/proj2/trends/images/family.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kfei/cs61a/d3821f53a9808fce68bd8f9e42e69f87881c2634/project/proj2/trends/images/family.png -------------------------------------------------------------------------------- /project/proj2/trends/images/football.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kfei/cs61a/d3821f53a9808fce68bd8f9e42e69f87881c2634/project/proj2/trends/images/football.png -------------------------------------------------------------------------------- /project/proj2/trends/images/high_school.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kfei/cs61a/d3821f53a9808fce68bd8f9e42e69f87881c2634/project/proj2/trends/images/high_school.png -------------------------------------------------------------------------------- /project/proj2/trends/images/movie.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kfei/cs61a/d3821f53a9808fce68bd8f9e42e69f87881c2634/project/proj2/trends/images/movie.png -------------------------------------------------------------------------------- /project/proj2/trends/images/shopping.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kfei/cs61a/d3821f53a9808fce68bd8f9e42e69f87881c2634/project/proj2/trends/images/shopping.png -------------------------------------------------------------------------------- /project/proj2/trends/images/snow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kfei/cs61a/d3821f53a9808fce68bd8f9e42e69f87881c2634/project/proj2/trends/images/snow.png -------------------------------------------------------------------------------- /project/proj2/trends/images/texas.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kfei/cs61a/d3821f53a9808fce68bd8f9e42e69f87881c2634/project/proj2/trends/images/texas.png -------------------------------------------------------------------------------- /project/proj2/trends/images/weekend.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kfei/cs61a/d3821f53a9808fce68bd8f9e42e69f87881c2634/project/proj2/trends/images/weekend.png -------------------------------------------------------------------------------- /project/proj2/trends/maps.py: -------------------------------------------------------------------------------- 1 | """Map drawing utilities for U.S. sentiment data.""" 2 | 3 | from graphics import Canvas 4 | from geo import position_to_xy, us_states 5 | 6 | # A fixed gradient of sentiment colors from negative (blue) to positive (red) 7 | # Colors chosen via Cynthia Brewer's Color Brewer (colorbrewer2.com) 8 | SENTIMENT_COLORS = ["#313695", "#4575B4", "#74ADD1", "#ABD9E9", "#E0F3F8", 9 | "#FFFFFF", "#FEE090", "#FDAE61", "#F46D43", "#D73027", 10 | "#A50026"] 11 | SENTIMENT_COLORS = ["#22366A", "#3B596A", "#427676", "#3F9A82", "#77BFFF", 12 | "#FFFFFF", 13 | #"#ECDB60", "#F1B82D", "#FFDD22", "#FFAA11", "#FF8800" 14 | "#ECDB60", "#FFDD22", "#FFAA11", "#FF8800", "#FF5500" 15 | ] 16 | GRAY = "#AAAAAA" 17 | 18 | def get_sentiment_color(sentiment, sentiment_scale=4): 19 | """Returns a color corresponding to the sentiment value. 20 | 21 | Arguments: 22 | sentiment -- a number between -1 (negative) and +1 (positive) 23 | """ 24 | if sentiment is None: 25 | return GRAY 26 | scaled = (sentiment_scale * sentiment + 1)/2 27 | index = int( scaled * len(SENTIMENT_COLORS) ) # Rounds down 28 | if index < 0: 29 | index = 0 30 | if index >= len(SENTIMENT_COLORS): 31 | index = len(SENTIMENT_COLORS) - 1 32 | return SENTIMENT_COLORS[index] 33 | 34 | def draw_state(shapes, sentiment_value=None): 35 | """Draw the named state in the given color on the canvas. 36 | 37 | Arguments: 38 | state -- a list of list of polygons (which are lists of positions) 39 | sentiment_value -- a number between -1 (negative) and 1 (positive) 40 | canvas -- the graphics.Canvas object 41 | """ 42 | for polygon in shapes: 43 | vertices = [position_to_xy(position) for position in polygon] 44 | color = get_sentiment_color(sentiment_value) 45 | get_canvas().draw_polygon(vertices, fill_color=color) 46 | 47 | def draw_name(name, location): 48 | """Draw the two-letter postal code at the center of the state. 49 | 50 | Arguments: 51 | location -- a position 52 | """ 53 | center = position_to_xy(location) 54 | get_canvas().draw_text(name.upper(), center, anchor='center', style='bold') 55 | 56 | def draw_dot(location, sentiment_value=None, radius=3): 57 | """Draw a small dot at location. 58 | 59 | Arguments: 60 | location -- a position 61 | sentiment_value -- a number between -1 (negative) and 1 (positive) 62 | """ 63 | center = position_to_xy(location) 64 | color = get_sentiment_color(sentiment_value) 65 | get_canvas().draw_circle(center, radius, fill_color=color) 66 | 67 | def memoize(fn): 68 | """A decorator for caching the results of the decorated function.""" 69 | cache = {} 70 | def memoized(*args): 71 | if args not in cache: 72 | cache[args] = fn(*args) 73 | return cache[args] 74 | return memoized 75 | 76 | @memoize 77 | def get_canvas(): 78 | """Return a Canvas, which is a drawing window.""" 79 | return Canvas(width=960, height=500) 80 | 81 | def wait(secs=0): 82 | """Wait for mouse click.""" 83 | get_canvas().wait_for_click(secs) 84 | 85 | def message(s): 86 | """Display a message.""" 87 | c = get_canvas() 88 | c.draw_text(s, (c.width//2, c.height//2), size=36, anchor='center') 89 | -------------------------------------------------------------------------------- /project/proj2/trends/tests/info.py: -------------------------------------------------------------------------------- 1 | info = { 2 | 'name': 'cal/61A/fa14/proj2', 3 | 'params': { 4 | 'doctest': { 5 | 'cache': """ 6 | import sys 7 | sys.path.append('.') 8 | import trends 9 | from maps import us_states 10 | import geo 11 | import datetime 12 | from trends import * 13 | import test_functions 14 | from test_functions import Tweet, Sentiment, Position 15 | """ 16 | } 17 | }, 18 | 'src_files': [ 19 | 'trends.py' 20 | ], 21 | 'version': '1.0' 22 | } -------------------------------------------------------------------------------- /project/proj2/trends/tests/q1.py: -------------------------------------------------------------------------------- 1 | test = { 2 | 'names': [ 3 | 'q1', 4 | '1' 5 | ], 6 | 'params': { 7 | 'doctest': { 8 | 'cache': """ 9 | # Storing original implementation of ADTs 10 | trends.position_adt = (make_position, latitude, longitude) 11 | """ 12 | } 13 | }, 14 | 'points': 1, 15 | 'suites': [ 16 | [ 17 | { 18 | 'test': """ 19 | >>> t = make_tweet('just ate lunch', datetime(2014, 9, 29, 13), 122, 37) 20 | >>> tweet_text(t) 21 | 'just ate lunch' 22 | # choice: datetime(2014, 9, 29, 13, 0) 23 | # choice: 'just ate lunch' 24 | # choice: 122 25 | # choice: '"just ate lunch" @ (122, 37)' 26 | >>> tweet_time(t) 27 | datetime(2014, 9, 29, 13, 0) 28 | # choice: datetime(2014, 9, 29, 13, 0) 29 | # choice: 'just ate lunch' 30 | # choice: 122 31 | # choice: '"just ate lunch" @ (122, 37)' 32 | >>> latitude(tweet_location(t)) 33 | 122 34 | # choice: datetime(2014, 9, 29, 13, 0) 35 | # choice: 'just ate lunch' 36 | # choice: 122 37 | # choice: '"just ate lunch" @ (122, 37)' 38 | >>> tweet_string(t) 39 | '"just ate lunch" @ (122, 37)' 40 | # choice: datetime(2014, 9, 29, 13, 0) 41 | # choice: 'just ate lunch' 42 | # choice: 122 43 | # choice: '"just ate lunch" @ (122, 37)' 44 | """, 45 | 'type': 'doctest' 46 | }, 47 | { 48 | 'never_lock': True, 49 | 'test': """ 50 | >>> s = make_tweet_fn('just ate lunch', datetime(2014, 9, 29, 13), 122, 37) 51 | >>> tweet_text_fn(s) 52 | 'just ate lunch' 53 | >>> tweet_time_fn(s) 54 | datetime(2014, 9, 29, 13, 0) 55 | >>> latitude(tweet_location_fn(s)) 56 | 122 57 | """, 58 | 'type': 'doctest' 59 | }, 60 | { 61 | 'never_lock': True, 62 | 'teardown': """ 63 | # restore original position ADT 64 | trends.make_position, trends.latitude, trends.longitude = trends.position_adt 65 | """, 66 | 'test': """ 67 | >>> # Testing for abstraction violations 68 | >>> trends.make_position = Position 69 | >>> trends.latitude = Position.latitude 70 | >>> trends.longitude = Position.longitude 71 | >>> f = make_tweet('first!', datetime(2014, 9, 20, 13), 122, 37) 72 | >>> tweet_text(f) 73 | 'first!' 74 | >>> tweet_time(f) 75 | datetime(2014, 9, 20, 13, 0) 76 | >>> trends.latitude(tweet_location(f)) 77 | 122 78 | """, 79 | 'type': 'doctest' 80 | } 81 | ] 82 | ] 83 | } -------------------------------------------------------------------------------- /project/proj2/trends/tests/q2.py: -------------------------------------------------------------------------------- 1 | test = { 2 | 'names': [ 3 | 'q2', 4 | '2' 5 | ], 6 | 'points': 2, 7 | 'suites': [ 8 | [ 9 | { 10 | 'test': """ 11 | >>> extract_words("anything else.....not my job") 12 | ['anything', 'else', 'not', 'my', 'job'] 13 | # choice: ['anything', 'else', 'not', 'my', 'job'] 14 | # choice: ['anything', 'else', '.....', 'not', 'my', 'job'] 15 | # choice: ['anything', 'else.....not', 'my', 'job'] 16 | # choice: ['anything', 'else', '.', '.', '.', '.', '.', 'not', 'my', 'job'] 17 | """, 18 | 'type': 'doctest' 19 | }, 20 | { 21 | 'test': """ 22 | >>> extract_words('i love my job. #winning') 23 | ['i', 'love', 'my', 'job', 'winning'] 24 | # choice: ['i', 'love', 'my', 'job', 'winning'] 25 | # choice: ['i', 'love', 'my', 'job.', '#winning'] 26 | # choice: ['i', 'love', 'my', 'job', '.', '#', 'winning'] 27 | # choice: ['i', 'love', 'my', 'job'] 28 | """, 29 | 'type': 'doctest' 30 | }, 31 | { 32 | 'test': """ 33 | >>> extract_words('make justin # 1 by tweeting #vma #justinbieber :)') 34 | ['make', 'justin', 'by', 'tweeting', 'vma', 'justinbieber'] 35 | # choice: ['make', 'justin', 'by', 'tweeting', 'vma', 'justinbieber'] 36 | # choice: ['make', 'justin', '#', '1', 'by', 'tweeting', '#', 'vma', '#', 'justinbieber'] 37 | # choice: ['make', 'justin', '#', '1', 'by', 'tweeting', '#vma', '#justinbieber'] 38 | # choice: ['make', 'justin', '1', 'by', 'tweeting'] 39 | """, 40 | 'type': 'doctest' 41 | }, 42 | { 43 | 'test': """ 44 | >>> extract_words("paperclips! they're so awesome, cool, & useful!") 45 | ['paperclips', 'they', 're', 'so', 'awesome', 'cool', 'useful'] 46 | # choice: ['paperclips', 'they', 're', 'so', 'awesome', 'cool', 'useful'] 47 | # choice: ['paperclips!', "they're", 'so', 'awesome,', 'cool,', 'useful!'] 48 | # choice: ['paperclips!', "they're", 'so', 'awesome', 'cool', '&', 'useful'] 49 | # choice: ['paperclips!', 'they', 'so', 'awesome', 'cool', 'and', 'useful'] 50 | """, 51 | 'type': 'doctest' 52 | }, 53 | { 54 | 'test': """ 55 | >>> extract_words('@(cat$.on^#$my&@keyboard***@#*') 56 | ['cat', 'on', 'my', 'keyboard'] 57 | # choice: ['cat', 'on', 'my', 'keyboard'] 58 | # choice: ['@', '(', 'cat', '$', '.', 'on', '^', '#', '$', 'my', '&', '@', 'keyboard', '***', '@', '#', '*'] 59 | # choice: ['catonmykeyboard'] 60 | """, 61 | 'type': 'doctest' 62 | } 63 | ], 64 | [ 65 | { 66 | 'never_lock': True, 67 | 'test': """ 68 | >>> extract_words("This.is separated!by@only#non$letter%characters^so&you*need(to)use-white+listing{instead}of black/listing:or'elseget~the wrong answer") 69 | ['This', 'is', 'separated', 'by', 'only', 'non', 'letter', 'characters', 'so', 'you', 'need', 'to', 'use', 'white', 'listing', 'instead', 'of', 'black', 'listing', 'or', 'else', 'you', 'll', 'get', 'the', 'wrong', 'answer'] 70 | """, 71 | 'type': 'doctest' 72 | } 73 | ] 74 | ] 75 | } -------------------------------------------------------------------------------- /project/proj2/trends/tests/q3.py: -------------------------------------------------------------------------------- 1 | test = { 2 | 'names': [ 3 | 'q3', 4 | '3' 5 | ], 6 | 'points': 1, 7 | 'suites': [ 8 | [ 9 | { 10 | 'test': """ 11 | >>> positive = make_sentiment(0.2) 12 | >>> neutral = make_sentiment(0) 13 | >>> unknown = make_sentiment(None) 14 | >>> has_sentiment(positive) 15 | True 16 | >>> has_sentiment(neutral) 17 | True 18 | >>> has_sentiment(unknown) 19 | False 20 | >>> sentiment_value(positive) 21 | 0.2 22 | >>> sentiment_value(neutral) 23 | 0 24 | """, 25 | 'type': 'doctest' 26 | } 27 | ] 28 | ] 29 | } -------------------------------------------------------------------------------- /project/proj2/trends/tests/q4.py: -------------------------------------------------------------------------------- 1 | test = { 2 | 'names': [ 3 | 'q4', 4 | '4' 5 | ], 6 | 'params': { 7 | 'doctest': { 8 | 'cache': """ 9 | # Storing original implementations of ADTs 10 | trends.tweet_adt = (make_tweet, tweet_text, tweet_time, tweet_location) 11 | trends.tweet_fn_adt = (make_tweet_fn, tweet_text_fn, tweet_time_fn, tweet_location_fn) 12 | trends.sentiment_adt = (make_sentiment, has_sentiment, sentiment_value) 13 | round5 = lambda args: tuple(round(arg, 5) for arg in args) 14 | """ 15 | } 16 | }, 17 | 'points': 1, 18 | 'suites': [ 19 | [ 20 | { 21 | 'answer': 'get_word_sentiment', 22 | 'choices': [ 23 | 'get_word_sentiment', 24 | 'make_sentiment', 25 | 'sentiment_value', 26 | 'extract_words', 27 | 'make_tweet' 28 | ], 29 | 'question': """ 30 | What function will take a string (a word) and return 31 | the associated sentiment? 32 | """, 33 | 'type': 'concept' 34 | }, 35 | { 36 | 'answer': 'A sentiment object', 37 | 'choices': [ 38 | 'A sentiment object', 39 | 'A number from -1 to 1', 40 | 'A number from -1 to 1, or None if no sentiment', 41 | 'A tweet' 42 | ], 43 | 'question': 'What type of object does analyze_tweet_sentiment return?', 44 | 'type': 'concept' 45 | }, 46 | { 47 | 'answer': 'make_sentiment(None)', 48 | 'choices': [ 49 | 'make_sentiment(None)', 50 | '0', 51 | 'sentiment_value(None)' 52 | ], 53 | 'question': """ 54 | If a tweet has no words with sentiments, what should 55 | analyze_tweet_sentiment return? 56 | """, 57 | 'type': 'concept' 58 | }, 59 | { 60 | 'never_lock': True, 61 | 'test': """ 62 | >>> positive = make_tweet('i love my job. #winning', None, 0, 0) 63 | >>> negative = make_tweet("saying, 'i hate my job'", None, 0, 0) 64 | >>> no_sentiment = make_tweet("berkeley golden bears!", None, 0, 0) 65 | >>> round(sentiment_value(analyze_tweet_sentiment(positive)), 5) 66 | 0.29167 67 | >>> sentiment_value(analyze_tweet_sentiment(negative)) 68 | -0.25 69 | >>> has_sentiment(analyze_tweet_sentiment(no_sentiment)) 70 | False 71 | """, 72 | 'type': 'doctest' 73 | }, 74 | { 75 | 'never_lock': True, 76 | 'test': """ 77 | >>> trends.make_sentiment = Sentiment 78 | >>> trends.sentiment_value = Sentiment.sentiment_value 79 | >>> trends.has_sentiment = Sentiment.has_sentiment 80 | >>> t1 = trends.make_tweet("Help, I'm trapped in an autograder factory and I can't get out!".lower(), None, 0, 0) 81 | >>> t2 = trends.make_tweet('The thing that I love about hating things that I love is that I hate loving that I hate doing it.'.lower(), None, 0, 0) 82 | >>> t3 = trends.make_tweet('Peter Piper picked a peck of pickled peppers'.lower(), None, 0, 0) 83 | >>> round(trends.sentiment_value(analyze_tweet_sentiment(t1)), 5) 84 | -0.41667 85 | >>> trends.sentiment_value(analyze_tweet_sentiment(t2)) 86 | 0.075 87 | >>> trends.has_sentiment(analyze_tweet_sentiment(t3)) 88 | False 89 | >>> # restore original sentiment adt 90 | >>> trends.make_sentiment, trends.has_sentiment, trends.sentiment_value = trends.sentiment_adt 91 | """, 92 | 'type': 'doctest' 93 | }, 94 | { 95 | 'never_lock': True, 96 | 'test': """ 97 | >>> trends.make_tweet = Tweet 98 | >>> trends.tweet_text = Tweet.text 99 | >>> trends.tweet_time = Tweet.time 100 | >>> trends.tweet_location = Tweet.location 101 | >>> t1 = trends.make_tweet("Help, I'm trapped in an autograder factory and I can't get out!".lower(), None, 0, 0) 102 | >>> t2 = trends.make_tweet('The thing that I love about hating things that I love is that I hate loving that I hate doing it.'.lower(), None, 0, 0) 103 | >>> t3 = trends.make_tweet('Peter Piper picked a peck of pickled peppers'.lower(), None, 0, 0) 104 | >>> round(trends.sentiment_value(analyze_tweet_sentiment(t1)), 5) 105 | -0.41667 106 | >>> trends.sentiment_value(analyze_tweet_sentiment(t2)) 107 | 0.075 108 | >>> trends.has_sentiment(analyze_tweet_sentiment(t3)) 109 | False 110 | >>> # restore original tweet adt 111 | >>> trends.make_tweet, trends.tweet_text, trends.tweet_time, trends.tweet_location = trends.tweet_adt 112 | """, 113 | 'type': 'doctest' 114 | } 115 | ] 116 | ] 117 | } -------------------------------------------------------------------------------- /project/proj2/trends/tests/q5.py: -------------------------------------------------------------------------------- 1 | test = { 2 | 'names': [ 3 | 'q5', 4 | '5' 5 | ], 6 | 'params': { 7 | 'doctest': { 8 | 'cache': """ 9 | # Storing original implementations of ADTs 10 | trends.position_adt = (make_position, latitude, longitude) 11 | """ 12 | } 13 | }, 14 | 'points': 2, 15 | 'suites': [ 16 | [ 17 | { 18 | 'answer': 'The latitude, longitude, and area', 19 | 'choices': [ 20 | 'The latitude, longitude, and area', 21 | 'The area of the polygon', 22 | 'A position object with the calculated latitude and longitude', 23 | 'latitude and longitude' 24 | ], 25 | 'question': 'What should find_centroid return?', 26 | 'type': 'concept' 27 | }, 28 | { 29 | 'answer': 'x is the latitude, y is the longitude', 30 | 'choices': [ 31 | 'x is the latitude, y is the longitude', 32 | 'x is the longitude, y is the latitude' 33 | ], 34 | 'question': 'The formula on Wikipedia uses variables x and y. What do these represent?', 35 | 'type': 'concept' 36 | }, 37 | { 38 | 'answer': 'They should be the latitude and longitude of the first position', 39 | 'choices': [ 40 | 'They should be the latitude and longitude of the first position', 41 | 'They should both be 0', 42 | 'They should be the latitude and longitude calculated by the formula' 43 | ], 44 | 'question': """ 45 | If the area of the polygon is 0, what should the 46 | latitude and longitude be? 47 | """, 48 | 'type': 'concept' 49 | }, 50 | { 51 | 'answer': 'After calculating latitude and longitude with the negative area, take the absolute value of the area', 52 | 'choices': [ 53 | 'After calculating latitude and longitude with the negative area, take the absolute value of the area', 54 | 'Before calculating latitude and longitude with the negative area, take the absolute value of the area', 55 | 'Leave the area as a negative number', 56 | 'This will never happen given the formula' 57 | ], 58 | 'question': 'How would you handle a negative area?', 59 | 'type': 'concept' 60 | }, 61 | { 62 | 'never_lock': True, 63 | 'test': """ 64 | >>> p1, p2, p3 = make_position(1, 2), make_position(3, 4), make_position(5, 0) 65 | >>> triangle = [p1, p2, p3, p1] # First vertex is also the last vertex 66 | >>> round5 = lambda x: round(x, 5) # Rounds floats to 5 digits 67 | >>> list(map(round5, find_centroid(triangle))) 68 | [3.0, 2.0, 6.0] 69 | >>> list(map(round5, find_centroid([p1, p3, p2, p1]))) 70 | [3.0, 2.0, 6.0] 71 | >>> list(map(float, find_centroid([p1, p2, p1]))) # A zero-area polygon 72 | [1.0, 2.0, 0.0] 73 | """, 74 | 'type': 'doctest' 75 | }, 76 | { 77 | 'never_lock': True, 78 | 'teardown': """ 79 | # restore original position adt 80 | trends.make_position, trends.latitude, trends.longitude = trends.position_adt 81 | """, 82 | 'test': """ 83 | >>> # Testing for abstraction violations 84 | >>> make_posiion = trends.make_position = Position 85 | >>> trends.latitude = Position.latitude 86 | >>> trends.longitude = Position.longitude 87 | >>> find_centroid = trends.find_centroid 88 | >>> make_position = trends.make_position 89 | >>> p1, p2, p3 = make_position(1, 2), make_position(3, 4), make_position(5, 0) 90 | >>> triangle = [p1, p2, p3, p1] # First vertex is also the last vertex 91 | >>> round5 = lambda x: round(x, 5) # Rounds floats to 5 digits 92 | >>> list(map(round5, find_centroid(triangle))) 93 | [3.0, 2.0, 6.0] 94 | >>> list(map(round5, find_centroid([p1, p3, p2, p1]))) 95 | [3.0, 2.0, 6.0] 96 | >>> list(map(float, find_centroid([p1, p2, p1]))) # A zero-area polygon 97 | [1.0, 2.0, 0.0] 98 | """, 99 | 'type': 'doctest' 100 | } 101 | ] 102 | ] 103 | } -------------------------------------------------------------------------------- /project/proj2/trends/tests/q6.py: -------------------------------------------------------------------------------- 1 | test = { 2 | 'names': [ 3 | 'q6', 4 | '6' 5 | ], 6 | 'params': { 7 | 'doctest': { 8 | 'cache': """ 9 | # Storing original implementation of ADTs 10 | trends.position_adt = (make_position, latitude, longitude) 11 | """ 12 | } 13 | }, 14 | 'points': 1, 15 | 'suites': [ 16 | [ 17 | { 18 | 'answer': 'A position object', 19 | 'choices': [ 20 | 'A position object', 21 | 'The latitude and longitude', 22 | 'A list of polygons' 23 | ], 24 | 'question': 'What type of object does find_state_center return?', 25 | 'type': 'concept' 26 | }, 27 | { 28 | 'never_lock': True, 29 | 'test': """ 30 | >>> ca = find_state_center(us_states['CA']) # California 31 | >>> round(latitude(ca), 5) 32 | 37.25389 33 | >>> round(longitude(ca), 5) 34 | -119.61439 35 | >>> hi = find_state_center(us_states['HI']) # Hawaii 36 | >>> round(latitude(hi), 5) 37 | 20.1489 38 | >>> round(longitude(hi), 5) 39 | -156.21763 40 | """, 41 | 'type': 'doctest' 42 | }, 43 | { 44 | 'never_lock': True, 45 | 'teardown': """ 46 | # restore original position ADT 47 | trends.make_position, trends.latitude, trends.longitude = trends.position_adt 48 | geo.make_position, geo.latitude, geo.longitude = trends.position_adt 49 | """, 50 | 'test': """ 51 | >>> # Testing for abstraction violations 52 | >>> other = Position, Position.latitude, Position.longitude 53 | >>> trends.make_position, trends.latitude, trends.longitude = other 54 | >>> geo.make_position, geo.latitude, geo.longitude = other 55 | >>> us_states = geo.load_states() 56 | >>> ca = find_state_center(us_states['CA']) # California 57 | >>> round(trends.latitude(ca), 5) 58 | 37.25389 59 | >>> round(trends.longitude(ca), 5) 60 | -119.61439 61 | >>> hi = find_state_center(us_states['HI']) # Hawaii 62 | >>> round(trends.latitude(hi), 5) 63 | 20.1489 64 | >>> round(trends.longitude(hi), 5) 65 | -156.21763 66 | """, 67 | 'type': 'doctest' 68 | } 69 | ] 70 | ] 71 | } -------------------------------------------------------------------------------- /project/proj2/trends/tests/q7.py: -------------------------------------------------------------------------------- 1 | test = { 2 | 'names': [ 3 | 'q7', 4 | '7' 5 | ], 6 | 'params': { 7 | 'doctest': { 8 | 'cache': """ 9 | # Storing original implementations of ADTs 10 | trends.tweet_adt = (make_tweet, tweet_text, tweet_time, tweet_location) 11 | trends.tweet_fn_adt = (make_tweet_fn, tweet_text_fn, tweet_time_fn, tweet_location_fn) 12 | trends.sentiment_adt = (make_sentiment, has_sentiment, sentiment_value) 13 | trends.position_adt = (make_position, latitude, longitude) 14 | round5 = lambda args: tuple(round(arg, 5) for arg in args) 15 | geo.us_states_adt = us_states 16 | """ 17 | } 18 | }, 19 | 'points': 2, 20 | 'suites': [ 21 | [ 22 | { 23 | 'answer': 'geo_distance', 24 | 'choices': [ 25 | 'geo_distance', 26 | 'find_state_center', 27 | 'make_position' 28 | ], 29 | 'question': 'What function calculates the distance between two positions?', 30 | 'type': 'concept' 31 | }, 32 | { 33 | 'answer': 'A list of tweet objects', 34 | 'choices': [ 35 | 'A list of tweet objects', 36 | 'A list of position objects', 37 | 'A dictionary of tweet objects', 38 | 'A dictionary of position objects' 39 | ], 40 | 'question': 'What type of object is the parameter tweets?', 41 | 'type': 'concept' 42 | }, 43 | { 44 | 'answer': 'A dictionary whose keys are strings (state names) and values are lists of tweet objects', 45 | 'choices': [ 46 | 'A dictionary whose keys are strings (state names) and values are lists of tweet objects', 47 | 'A dictionary whose keys are strings (state names) and values are tweet objects', 48 | 'A dictionary whose keys are tweet objects and values are strings (state names)', 49 | 'A dictionary whose keys are tweet objects and values are position objects' 50 | ], 51 | 'question': 'What type of object does group_tweets_by_state return?', 52 | 'type': 'concept' 53 | }, 54 | { 55 | 'never_lock': True, 56 | 'test': """ 57 | >>> sf = trends.make_tweet("welcome to san francisco", None, 38, -122) 58 | >>> ny = trends.make_tweet("welcome to new york", None, 41, -74) 59 | >>> two_tweets_by_state = group_tweets_by_state([sf, ny]) 60 | >>> len(two_tweets_by_state) 61 | 2 62 | >>> california_tweets = two_tweets_by_state['CA'] 63 | >>> len(california_tweets) 64 | 1 65 | >>> tweet_string(california_tweets[0]) 66 | '"welcome to san francisco" @ (38, -122)' 67 | """, 68 | 'type': 'doctest' 69 | }, 70 | { 71 | 'never_lock': True, 72 | 'teardown': """ 73 | # restore original tweet adt 74 | trends.make_tweet, trends.tweet_text, trends.tweet_time, trends.tweet_location = trends.tweet_adt 75 | """, 76 | 'test': """ 77 | >>> # Testing for abstraction violations 78 | >>> trends.make_tweet = Tweet 79 | >>> trends.tweet_text = Tweet.text 80 | >>> trends.tweet_time = Tweet.time 81 | >>> trends.tweet_location = Tweet.location 82 | >>> group_tweets_by_state = trends.group_tweets_by_state 83 | >>> sf = trends.make_tweet("welcome to san francisco", None, 38, -122) 84 | >>> ny = trends.make_tweet("welcome to new york", None, 41, -74) 85 | >>> # Begin tests 86 | >>> two_tweets_by_state = group_tweets_by_state([sf, ny]) 87 | >>> len(two_tweets_by_state) 88 | 2 89 | >>> california_tweets = two_tweets_by_state['CA'] 90 | >>> len(california_tweets) 91 | 1 92 | >>> ak_1 = trends.make_tweet("came to find my rubber ducky *o*", None, 100, 8) 93 | >>> ak_2 = trends.make_tweet("couldn't find one :((((( such sadness", None, 90, 10) 94 | >>> me_1 = trends.make_tweet("i heard that rubber duckies were made here!", None, 50, -74) 95 | >>> me_2 = trends.make_tweet("they put ducks in clam chowder! >.<", None, 55, -73) 96 | >>> two_tweets_by_state = group_tweets_by_state([ak_1, ak_2, me_1, me_2]) 97 | >>> len(two_tweets_by_state) 98 | 2 99 | >>> alaska_tweets = two_tweets_by_state['AK'] 100 | >>> len(alaska_tweets) 101 | 2 102 | >>> maine_tweets = two_tweets_by_state['ME'] 103 | >>> len(maine_tweets) 104 | 2 105 | """, 106 | 'type': 'doctest' 107 | } 108 | ] 109 | ] 110 | } -------------------------------------------------------------------------------- /project/proj2/trends/tests/q8.py: -------------------------------------------------------------------------------- 1 | test = { 2 | 'names': [ 3 | 'q8', 4 | '8' 5 | ], 6 | 'params': { 7 | 'doctest': { 8 | 'cache': """ 9 | # Storing original implementations of ADTs 10 | trends.tweet_adt = (make_tweet, tweet_text, tweet_time, tweet_location) 11 | trends.tweet_fn_adt = (make_tweet_fn, tweet_text_fn, tweet_time_fn, tweet_location_fn) 12 | trends.sentiment_adt = (make_sentiment, has_sentiment, sentiment_value) 13 | trends.position_adt = (make_position, latitude, longitude) 14 | round5 = lambda args: tuple(round(arg, 5) for arg in args) 15 | geo.us_states_adt = us_states 16 | """ 17 | } 18 | }, 19 | 'points': 2, 20 | 'suites': [ 21 | [ 22 | { 23 | 'answer': 'keys are strings (state names), values are numbers (average sentiment values)', 24 | 'choices': [ 25 | 'keys are strings (state names), values are numbers (average sentiment values)', 26 | 'keys are strings (state names), values are sentiment objects', 27 | 'keys are strings (state names), values are lists of sentiment objects', 28 | 'keys are tweet objects, values are sentiment objects', 29 | 'keys are tweet objects, values are numbers (average sentiment values)' 30 | ], 31 | 'question': """ 32 | average_sentiments returns a dictionary. What are the keys 33 | and values of this dictionary? 34 | """, 35 | 'type': 'concept' 36 | }, 37 | { 38 | 'answer': 'the state should not be included in the dictionary', 39 | 'choices': [ 40 | 'the state should not be included in the dictionary', 41 | 'the state should be included in the dictionary with a value of 0', 42 | 'the state should be included in the dictionary with a value of None', 43 | 'the state should be included in the dictionary with an empty list' 44 | ], 45 | 'question': 'What should average_sentiments do if a state has no tweets with sentiments?', 46 | 'type': 'concept' 47 | }, 48 | { 49 | 'answer': 'analyze_tweet_sentiment, which returns a sentiment object', 50 | 'choices': [ 51 | 'analyze_tweet_sentiment, which returns a sentiment object', 52 | 'analyze_tweet_sentiment, which returns a number', 53 | 'tweet_words, which returns a list of words', 54 | 'get_word_sentiment, which returns a number', 55 | 'get_word_sentiment, which returns a sentiment object' 56 | ], 57 | 'question': """ 58 | What function computes the sentiment of a tweet, and what type of 59 | object does it return? 60 | """, 61 | 'type': 'concept' 62 | }, 63 | { 64 | 'never_lock': True, 65 | 'test': """ 66 | >>> # Begin tests 67 | >>> tweets_by_state = test_functions.make_average_sentiments_tests(make_tweet) 68 | >>> groups = average_sentiments(tweets_by_state) 69 | >>> groups['MT'] 70 | -0.08333333333333333 71 | >>> groups['MI'] 72 | 0.325 73 | >>> groups['FL'] 74 | 0.5 75 | >>> groups['ND'] 76 | 0.020833333333333332 77 | >>> len(groups) 78 | 4 79 | """, 80 | 'type': 'doctest' 81 | }, 82 | { 83 | 'never_lock': True, 84 | 'teardown': """ 85 | # restore original sentiment adt 86 | trends.make_sentiment, trends.has_sentiment, trends.sentiment_value = trends.sentiment_adt 87 | trends.make_tweet, trends.tweet_text, trends.tweet_time, trends.tweet_location = trends.tweet_adt 88 | """, 89 | 'test': """ 90 | >>> # Testing for abstraction violations 91 | >>> make_tweet = trends.make_tweet = Tweet 92 | >>> trends.tweet_text = Tweet.text 93 | >>> trends.tweet_time = Tweet.time 94 | >>> trends.tweet_location = Tweet.location 95 | >>> trends.make_sentiment = Sentiment 96 | >>> trends.has_sentiment = Sentiment.has_sentiment 97 | >>> trends.sentiment_value = Sentiment.sentiment_value 98 | >>> group_tweets_by_state = trends.group_tweets_by_state 99 | >>> # Begin tests 100 | >>> tweets_by_state = test_functions.make_average_sentiments_tests(make_tweet) 101 | >>> groups = average_sentiments(tweets_by_state) 102 | >>> groups['MT'] 103 | -0.08333333333333333 104 | >>> groups['MI'] 105 | 0.325 106 | >>> groups['FL'] 107 | 0.5 108 | >>> groups['ND'] 109 | 0.020833333333333332 110 | >>> len(groups) 111 | 4 112 | """, 113 | 'type': 'doctest' 114 | } 115 | ] 116 | ] 117 | } -------------------------------------------------------------------------------- /project/proj2/trends/tests/test_functions.py: -------------------------------------------------------------------------------- 1 | def pirate_tweets(make_tweet): 2 | return [ 3 | make_tweet('I am the very model of a modern Major-General'.lower(), None, 43, -84), 4 | make_tweet("I've information vegetable, animal, and mineral".lower(), None, 58, -112), 5 | make_tweet('I know the kings of England, and I quote the fights historical'.lower(), None, 49, -104), 6 | make_tweet('From Marathon to Waterloo, in order categorical'.lower(), None, 19, -87), 7 | make_tweet("I'm very well acquainted, too, with matters mathematical".lower(), None, 44, -85), 8 | make_tweet('I understand equations, both the simple and quadratical'.lower(), None, 59, -110), 9 | make_tweet("About binomial theorem I'm teeming with a lot o' news".lower(), None, 50, -100), 10 | make_tweet('With many cheerful facts about the square of the hypotenuse'.lower(), None, 15, -87), 11 | ] 12 | 13 | def make_average_sentiments_tests(make_tweet): 14 | tweets = pirate_tweets(make_tweet) \ 15 | + [make_tweet('This tweet is without a sentiment', None, None, None)] \ 16 | + [make_tweet('This tweet is also without a sentiment', None, None, None) ] 17 | tweets_by_state = {} 18 | tweets_by_state['MT'] = [tweets[1], tweets[5]] 19 | tweets_by_state['MI'] = [tweets[0], tweets[4]] 20 | tweets_by_state['FL'] = [tweets[3], tweets[7]] 21 | tweets_by_state['ND'] = [tweets[2], tweets[6]] 22 | tweets_by_state['AA'] = [tweets[8], tweets[9]] 23 | return tweets_by_state 24 | 25 | # These classes test abstraction violations 26 | class AbstractionViolation(Exception): 27 | pass 28 | 29 | def datatype(obj): 30 | return type(obj).__name__ 31 | 32 | # Generic abstract data type 33 | class Abstract(object): 34 | def __add__(self, other): 35 | raise AbstractionViolation("Can't add {} object to {}".format(datatype(self), datatype(other))) 36 | 37 | def __radd__(self, other): 38 | raise AbstractionViolation("Can't add {} object to {}".format(datatype(self), datatype(other))) 39 | 40 | def __eq__(self, other): 41 | if isinstance(other, type(self)): 42 | return other is self 43 | raise AbstractionViolation("Can't use == on {} object and {}".format(datatype(self), datatype(other))) 44 | 45 | def __ne__(self, other): 46 | if isinstance(other, type(self)): 47 | return other is not self 48 | raise AbstractionViolation("Can't use != on {} object and {}".format(datatype(self), datatype(other))) 49 | 50 | def __bool__(self): 51 | raise AbstractionViolation("Can't use {} object as a boolean".format(datatype(self))) 52 | 53 | def __getitem__(self, index): 54 | raise AbstractionViolation("Can't use [] notation on {} object".format(datatype(self))) 55 | 56 | def __contains__(self, other): 57 | raise AbstractionViolation("Can't use contains notation on {} object".format(datatype(self))) 58 | 59 | def __delitem__(self, other): 60 | raise AbstractionViolation("Can't use del notation on {} object".format(datatype(self))) 61 | 62 | def __iter__(self): 63 | raise AbstractionViolation("Can't iterate on {} object".format(datatype(self))) 64 | 65 | def __len__(self): 66 | raise AbstractionViolation("Can't use len notation on {} object".format(datatype(self))) 67 | 68 | def __setitem__(self, key, item): 69 | raise AbstractionViolation("Can't use setitem notation on {} object".format(datatype(self))) 70 | 71 | class Tweet(Abstract): 72 | def __init__(self, text, time, lat, lon): 73 | self._text = text 74 | self._time = time 75 | self._lat = lat 76 | self._lon = lon 77 | 78 | def text(self): 79 | return self._text 80 | 81 | def time(self): 82 | return self._time 83 | 84 | def location(self): 85 | import trends 86 | return trends.make_position(self._lat, self._lon) 87 | 88 | class Sentiment(Abstract): 89 | def __init__(self, value): 90 | assert value is None or type(value) in (float, int), 'invalid value to sentiment constructor: {}'.format(datatype(value)) 91 | self._value = value 92 | 93 | def sentiment_value(self): 94 | if type(self) != Sentiment: 95 | raise AbstractionViolation("Can't call sentiment_value on {}".format(self)) 96 | assert self.has_sentiment(), 'No sentiment value' 97 | return self._value 98 | 99 | def has_sentiment(self): 100 | if type(self) != Sentiment: 101 | raise AbstractionViolation("Can't call has_sentiment on {}".format(self)) 102 | return self._value != None 103 | 104 | class Position(Abstract): 105 | def __init__(self, lat, lon): 106 | self._lat = lat 107 | self._lon = lon 108 | 109 | def latitude(self): 110 | if type(self) != Position: 111 | raise AbstractionViolation("Can't call latitude on {}".format(self)) 112 | return self._lat 113 | 114 | def longitude(self): 115 | if type(self) != Position: 116 | raise AbstractionViolation("Can't call longitude on {}".format(self)) 117 | return self._lon 118 | -------------------------------------------------------------------------------- /project/proj2/trends/ucb.py: -------------------------------------------------------------------------------- 1 | """The UCB module contains functions specific to 61A projects at UC Berkeley.""" 2 | 3 | import code 4 | import functools 5 | import inspect 6 | import re 7 | import signal 8 | import sys 9 | 10 | 11 | def main(fn): 12 | """Call fn with command line arguments. Used as a decorator. 13 | 14 | The main decorator marks the function that starts a program. For example, 15 | 16 | @main 17 | def my_run_function(): 18 | # function body 19 | 20 | Use this instead of the typical __name__ == "__main__" predicate. 21 | """ 22 | if inspect.stack()[1][0].f_locals['__name__'] == '__main__': 23 | args = sys.argv[1:] # Discard the script name from command line 24 | fn(*args) # Call the main function 25 | return fn 26 | 27 | _PREFIX = '' 28 | def trace(fn): 29 | """A decorator that prints a function's name, its arguments, and its return 30 | values each time the function is called. For example, 31 | 32 | @trace 33 | def compute_something(x, y): 34 | # function body 35 | """ 36 | @functools.wraps(fn) 37 | def wrapped(*args, **kwds): 38 | global _PREFIX 39 | reprs = [repr(e) for e in args] 40 | reprs += [repr(k) + '=' + repr(v) for k, v in kwds.items()] 41 | log('{0}({1})'.format(fn.__name__, ', '.join(reprs)) + ':') 42 | _PREFIX += ' ' 43 | try: 44 | result = fn(*args, **kwds) 45 | _PREFIX = _PREFIX[:-4] 46 | except Exception as e: 47 | log(fn.__name__ + ' exited via exception') 48 | _PREFIX = _PREFIX[:-4] 49 | raise 50 | # Here, print out the return value. 51 | log('{0}({1}) -> {2}'.format(fn.__name__, ', '.join(reprs), result)) 52 | return result 53 | return wrapped 54 | 55 | 56 | def log(message): 57 | """Print an indented message (used with trace).""" 58 | if type(message) is not str: 59 | message = str(message) 60 | print(_PREFIX + re.sub('\n', '\n' + _PREFIX, message)) 61 | 62 | 63 | def log_current_line(): 64 | """Print information about the current line of code.""" 65 | frame = inspect.stack()[1] 66 | log('Current line: File "{f[1]}", line {f[2]}, in {f[3]}'.format(f=frame)) 67 | 68 | 69 | def interact(msg=None): 70 | """Start an interactive interpreter session in the current environment. 71 | 72 | On Unix: 73 | -D exits the interactive session and returns to normal execution. 74 | In Windows: 75 | -Z exists the interactive session and returns to normal 76 | execution. 77 | """ 78 | # use exception trick to pick up the current frame 79 | try: 80 | raise None 81 | except: 82 | frame = sys.exc_info()[2].tb_frame.f_back 83 | 84 | # evaluate commands in current namespace 85 | namespace = frame.f_globals.copy() 86 | namespace.update(frame.f_locals) 87 | 88 | # exit on interrupt 89 | def handler(signum, frame): 90 | print() 91 | exit(0) 92 | signal.signal(signal.SIGINT, handler) 93 | 94 | if not msg: 95 | _, filename, line, _, _, _ = inspect.stack()[1] 96 | msg = 'Interacting at File "{0}", line {1} \n'.format(filename, line) 97 | msg += ' Unix: -D continues the program; \n' 98 | msg += ' Windows: -Z continues the program; \n' 99 | msg += ' exit() or -C exits the program' 100 | 101 | code.interact(msg, None, namespace) 102 | -------------------------------------------------------------------------------- /project/proj3/ants/graphics.py: -------------------------------------------------------------------------------- 1 | """The graphics module implements a simple GUI library.""" 2 | 3 | import sys 4 | import math 5 | 6 | try: 7 | import tkinter 8 | except Exception as e: 9 | print('Could not load tkinter: ' + str(e)) 10 | 11 | FRAME_TIME = 1/30 12 | 13 | class Canvas(object): 14 | """A Canvas object supports drawing and animation primitives. 15 | 16 | draw_* methods return the id number of a shape object in the underlying Tk 17 | object. This id can be passed to move_* and edit_* methods. 18 | 19 | Canvas is a singleton; only one Canvas instance can be created. 20 | 21 | """ 22 | 23 | _instance = None 24 | 25 | def __init__(self, width=1024, height=768, title='', color='White', tk=None): 26 | # Singleton enforcement 27 | if Canvas._instance is not None: 28 | raise Exception('Only one canvas can be instantiated.') 29 | Canvas._instance = self 30 | 31 | # Attributes 32 | self.color = color 33 | self.width = width 34 | self.height = height 35 | 36 | # Root window 37 | self._tk = tk or tkinter.Tk() 38 | self._tk.protocol('WM_DELETE_WINDOW', sys.exit) 39 | self._tk.title(title or 'Graphics Window') 40 | self._tk.bind('', self._click) 41 | self._click_pos = None 42 | 43 | # Canvas object 44 | self._canvas = tkinter.Canvas(self._tk, width=width, height=height) 45 | self._canvas.pack() 46 | self._draw_background() 47 | self._canvas.update() 48 | self._images = dict() 49 | 50 | def clear(self, shape='all'): 51 | """Clear all shapes, text, and images.""" 52 | self._canvas.delete(shape) 53 | if shape == 'all': 54 | self._draw_background() 55 | self._canvas.update() 56 | 57 | def draw_polygon(self, points, color='Black', fill_color=None, filled=1, smooth=0, width=1): 58 | """Draw a polygon and return its tkinter id. 59 | 60 | points -- a list of (x, y) pairs encoding pixel positions 61 | """ 62 | if fill_color == None: 63 | fill_color = color 64 | if filled == 0: 65 | fill_color = "" 66 | return self._canvas.create_polygon(flattened(points), outline=color, fill=fill_color, 67 | smooth=smooth, width=width) 68 | 69 | def draw_circle(self, center, radius, color='Black', fill_color=None, filled=1, width=1): 70 | """Draw a cirlce and return its tkinter id. 71 | 72 | center -- an (x, y) pair encoding a pixel position 73 | """ 74 | if fill_color == None: 75 | fill_color = color 76 | if filled == 0: 77 | fill_color = "" 78 | x0, y0 = [c - radius for c in center] 79 | x1, y1 = [c + radius for c in center] 80 | return self._canvas.create_oval(x0, y0, x1, y1, outline=color, fill=fill_color, width=width) 81 | 82 | def draw_image(self, pos, image_file=None, scale=1, anchor=tkinter.NW, behind=0): 83 | """Draw an image from a file and return its tkinter id.""" 84 | key = (image_file, scale) 85 | if key not in self._images: 86 | image = tkinter.PhotoImage(file=image_file) 87 | if scale >= 1: 88 | image = image.zoom(int(scale)) 89 | else: 90 | image = image.subsample(int(1/scale)) 91 | self._images[key] = image 92 | 93 | image = self._images[key] 94 | x, y = pos 95 | id = self._canvas.create_image(x, y, image=image, anchor=anchor) 96 | if behind > 0: 97 | self._canvas.tag_lower(id, behind) 98 | return id 99 | 100 | def draw_text(self, text, pos, color='Black', font='Arial', 101 | size=12, style='normal', anchor=tkinter.NW): 102 | """Draw text and return its tkinter id.""" 103 | x, y = pos 104 | font = (font, str(size), style) 105 | return self._canvas.create_text(x, y, fill=color, text=text, font=font, anchor=anchor) 106 | 107 | def edit_text(self, id, text=None, color=None, font=None, size=12, 108 | style='normal'): 109 | """Edit the text, color, or font of an existing text object.""" 110 | if color is not None: 111 | self._canvas.itemconfigure(id, fill=color) 112 | if text is not None: 113 | self._canvas.itemconfigure(id, text=text) 114 | if font is not None: 115 | self._canvas.itemconfigure(id, font=(font, str(size), style)) 116 | 117 | def animate_shape(self, id, duration, points_fn, frame_count=0): 118 | """Animate an existing shape over points.""" 119 | max_frames = duration // FRAME_TIME 120 | points = points_fn(frame_count) 121 | self._canvas.coords(id, flattened(points)) 122 | if frame_count < max_frames: 123 | def tail(): 124 | """Continues the animation at the next frame.""" 125 | self.animate_shape(id, duration, points_fn, frame_count + 1) 126 | self._tk.after(int(FRAME_TIME * 1000), tail) 127 | 128 | def slide_shape(self, id, end_pos, duration, elapsed=0): 129 | """Slide an existing shape to end_pos.""" 130 | points = paired(self._canvas.coords(id)) 131 | start_pos = points[0] 132 | max_frames = duration // FRAME_TIME 133 | def points_fn(frame_count): 134 | completed = frame_count / max_frames 135 | offset = [(e - s) * completed for s, e in zip(start_pos, end_pos)] 136 | return [shift_point(p, offset) for p in points] 137 | self.animate_shape(id, duration, points_fn) 138 | 139 | def wait_for_click(self, seconds=0): 140 | """Return (position, elapsed) pair of click position and elapsed time. 141 | 142 | position: (x,y) pixel position of click 143 | elapsed: milliseconds elapsed since call 144 | seconds: maximum number of seconds to wait for a click 145 | 146 | If there is still no click after the given time, return (None, seconds). 147 | 148 | """ 149 | elapsed = 0 150 | while elapsed < seconds or seconds == 0: 151 | if self._click_pos is not None: 152 | pos = self._click_pos 153 | self._click_pos = None 154 | return pos, elapsed 155 | self._sleep(FRAME_TIME) 156 | elapsed += FRAME_TIME 157 | return None, elapsed 158 | 159 | def _draw_background(self): 160 | w, h = self.width - 1, self.height - 1 161 | corners = [(0,0), (0, h), (w, h), (w, 0)] 162 | self.draw_polygon(corners, self.color, fill_color=self.color, filled=True, smooth=False) 163 | 164 | def _click(self, event): 165 | self._click_pos = (event.x, event.y) 166 | 167 | def _sleep(self, seconds): 168 | self._tk.update_idletasks() 169 | self._tk.after(int(1000 * seconds), self._tk.quit) 170 | self._tk.mainloop() 171 | 172 | def flattened(points): 173 | """Return a flat list of coordinates from a list of pairs.""" 174 | coords = list() 175 | [coords.extend(p) for p in points] 176 | return tuple(coords) 177 | 178 | def paired(coords): 179 | """Return a list of pairs from a flat list of coordinates.""" 180 | assert len(coords) % 2 == 0, 'Coordinates are not paired.' 181 | points = [] 182 | x = None 183 | for elem in coords: 184 | if x is None: 185 | x = elem 186 | else: 187 | points.append((x, elem)) 188 | x = None 189 | return points 190 | 191 | def translate_point(point, angle, distance): 192 | """Translate a point a distance in a direction (angle).""" 193 | x, y = point 194 | return (x + math.cos(angle) * distance, y + math.sin(angle) * distance) 195 | 196 | def shift_point(point, offset): 197 | """Shift a point by an offset.""" 198 | x, y = point 199 | dx, dy = offset 200 | return (x + dx, y + dy) 201 | 202 | def rectangle_points(pos, width, height): 203 | """Return the points of a rectangle starting at pos.""" 204 | x1, y1 = pos 205 | x2, y2 = width + x1, height + y1 206 | return [(x1, y1), (x1, y2), (x2, y2), (x2, y1)] 207 | 208 | def format_color(r, g, b): 209 | """Format a color as a string. 210 | 211 | r, g, b -- integers from 0 to 255 212 | """ 213 | return '#{0:02x}{1:02x}{2:02x}'.format(int(r * 255), int(g * 255), int(b * 255)) 214 | -------------------------------------------------------------------------------- /project/proj3/ants/img/ant_fire.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kfei/cs61a/d3821f53a9808fce68bd8f9e42e69f87881c2634/project/proj3/ants/img/ant_fire.gif -------------------------------------------------------------------------------- /project/proj3/ants/img/ant_freeze.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kfei/cs61a/d3821f53a9808fce68bd8f9e42e69f87881c2634/project/proj3/ants/img/ant_freeze.gif -------------------------------------------------------------------------------- /project/proj3/ants/img/ant_harvester.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kfei/cs61a/d3821f53a9808fce68bd8f9e42e69f87881c2634/project/proj3/ants/img/ant_harvester.gif -------------------------------------------------------------------------------- /project/proj3/ants/img/ant_hungry.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kfei/cs61a/d3821f53a9808fce68bd8f9e42e69f87881c2634/project/proj3/ants/img/ant_hungry.gif -------------------------------------------------------------------------------- /project/proj3/ants/img/ant_longthrower.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kfei/cs61a/d3821f53a9808fce68bd8f9e42e69f87881c2634/project/proj3/ants/img/ant_longthrower.gif -------------------------------------------------------------------------------- /project/proj3/ants/img/ant_ninja.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kfei/cs61a/d3821f53a9808fce68bd8f9e42e69f87881c2634/project/proj3/ants/img/ant_ninja.gif -------------------------------------------------------------------------------- /project/proj3/ants/img/ant_queen.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kfei/cs61a/d3821f53a9808fce68bd8f9e42e69f87881c2634/project/proj3/ants/img/ant_queen.gif -------------------------------------------------------------------------------- /project/proj3/ants/img/ant_scuba.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kfei/cs61a/d3821f53a9808fce68bd8f9e42e69f87881c2634/project/proj3/ants/img/ant_scuba.gif -------------------------------------------------------------------------------- /project/proj3/ants/img/ant_shortthrower.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kfei/cs61a/d3821f53a9808fce68bd8f9e42e69f87881c2634/project/proj3/ants/img/ant_shortthrower.gif -------------------------------------------------------------------------------- /project/proj3/ants/img/ant_stun.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kfei/cs61a/d3821f53a9808fce68bd8f9e42e69f87881c2634/project/proj3/ants/img/ant_stun.gif -------------------------------------------------------------------------------- /project/proj3/ants/img/ant_thrower.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kfei/cs61a/d3821f53a9808fce68bd8f9e42e69f87881c2634/project/proj3/ants/img/ant_thrower.gif -------------------------------------------------------------------------------- /project/proj3/ants/img/ant_wall.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kfei/cs61a/d3821f53a9808fce68bd8f9e42e69f87881c2634/project/proj3/ants/img/ant_wall.gif -------------------------------------------------------------------------------- /project/proj3/ants/img/ant_weeds.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kfei/cs61a/d3821f53a9808fce68bd8f9e42e69f87881c2634/project/proj3/ants/img/ant_weeds.gif -------------------------------------------------------------------------------- /project/proj3/ants/img/ants_vs_bees.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kfei/cs61a/d3821f53a9808fce68bd8f9e42e69f87881c2634/project/proj3/ants/img/ants_vs_bees.png -------------------------------------------------------------------------------- /project/proj3/ants/img/bee.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kfei/cs61a/d3821f53a9808fce68bd8f9e42e69f87881c2634/project/proj3/ants/img/bee.gif -------------------------------------------------------------------------------- /project/proj3/ants/img/gui_explanation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kfei/cs61a/d3821f53a9808fce68bd8f9e42e69f87881c2634/project/proj3/ants/img/gui_explanation.png -------------------------------------------------------------------------------- /project/proj3/ants/img/remover.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kfei/cs61a/d3821f53a9808fce68bd8f9e42e69f87881c2634/project/proj3/ants/img/remover.gif -------------------------------------------------------------------------------- /project/proj3/ants/img/tunnel.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kfei/cs61a/d3821f53a9808fce68bd8f9e42e69f87881c2634/project/proj3/ants/img/tunnel.gif -------------------------------------------------------------------------------- /project/proj3/ants/ok: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kfei/cs61a/d3821f53a9808fce68bd8f9e42e69f87881c2634/project/proj3/ants/ok -------------------------------------------------------------------------------- /project/proj3/ants/tests/info.py: -------------------------------------------------------------------------------- 1 | info = { 2 | 'name': 'cal/61A/fa14/proj3', 3 | 'params': { 4 | 'doctest': { 5 | 'cache': r""" 6 | from ants import * 7 | import ants 8 | import imp 9 | """, 10 | 'setup': r""" 11 | hive, layout = Hive(make_test_assault_plan()), test_layout 12 | colony = AntColony(None, hive, ant_types(), layout) 13 | """ 14 | } 15 | }, 16 | 'src_files': [ 17 | 'ants.py' 18 | ], 19 | 'version': '1.0' 20 | } -------------------------------------------------------------------------------- /project/proj3/ants/tests/q1.py: -------------------------------------------------------------------------------- 1 | test = { 2 | 'names': [ 3 | 'q1', 4 | '1' 5 | ], 6 | 'points': 0, 7 | 'suites': [ 8 | [ 9 | { 10 | 'answer': 'It represents the amount of health remaining, so the insect is eliminated when it reaches 0.', 11 | 'choices': [ 12 | 'It represents the size of the insect, so the insect gets really small when it reaches 0.', 13 | 'It represents the amount of health remaining, so the insect is eliminated when it reaches 0.', 14 | "It represents the speed of the insect, so that the insect doesn't move when it reaches 0." 15 | ], 16 | 'question': "What is the significance of an Insect's armor attribute? What happens when it reaches 0?", 17 | 'type': 'concept' 18 | }, 19 | { 20 | 'answer': 'damage', 21 | 'choices': [ 22 | 'damage', 23 | 'armor', 24 | 'Insect', 25 | 'Ant' 26 | ], 27 | 'question': 'Which of the following is a class attribute of the Ant class?', 28 | 'type': 'concept' 29 | }, 30 | { 31 | 'answer': 'instance: each Ant instance needs its own armor value', 32 | 'choices': [ 33 | 'instance: each Ant instance needs its own armor value', 34 | 'instance: Ants all have the same amount of starting armor', 35 | 'class: Ants all have the same amount of starting armor', 36 | 'class: each Ant instance needs its own armor value' 37 | ], 38 | 'question': 'Is the armor attribute of the Ant class an instance or class attribute? Why?', 39 | 'type': 'concept' 40 | }, 41 | { 42 | 'answer': 'class: All ants of the same type deal the same damage', 43 | 'choices': [ 44 | 'instance: Ants do damage to bees at different rates', 45 | 'instance: All ants of the same type deal the same damage', 46 | 'class: Ants do damage to bees at different rates', 47 | 'class: All ants of the same type deal the same damage' 48 | ], 49 | 'question': 'Is the damage attribute of the Ant class an instance or class attribute? Why?', 50 | 'type': 'concept' 51 | }, 52 | { 53 | 'answer': 'Insect', 54 | 'choices': [ 55 | 'Insect', 56 | 'Place', 57 | 'Bee', 58 | 'Ant' 59 | ], 60 | 'question': 'Which class do both Ant and Bee inherit from?', 61 | 'type': 'concept' 62 | }, 63 | { 64 | 'answer': 'armor, place', 65 | 'choices': [ 66 | 'armor', 67 | 'armor, place', 68 | 'armor, place, implemented' 69 | ], 70 | 'question': 'What attribute(s) do Ant and Bee instances inherit from the Insect class?', 71 | 'type': 'concept' 72 | } 73 | ] 74 | ] 75 | } -------------------------------------------------------------------------------- /project/proj3/ants/tests/q2.py: -------------------------------------------------------------------------------- 1 | test = { 2 | 'names': [ 3 | 'q2', 4 | '2' 5 | ], 6 | 'points': 2, 7 | 'suites': [ 8 | [ 9 | { 10 | 'test': r""" 11 | >>> HarvesterAnt.food_cost 12 | 2 13 | """, 14 | 'type': 'doctest' 15 | }, 16 | { 17 | 'test': r""" 18 | >>> ThrowerAnt.food_cost 19 | 4 20 | """, 21 | 'type': 'doctest' 22 | } 23 | ], 24 | [ 25 | { 26 | 'test': r""" 27 | >>> # Testing HarvesterAnt action 28 | >>> colony.food = 4 29 | >>> HarvesterAnt().action(colony) 30 | >>> colony.food 31 | 5 32 | >>> HarvesterAnt().action(colony) 33 | >>> colony.food 34 | 6 35 | """, 36 | 'type': 'doctest' 37 | } 38 | ] 39 | ] 40 | } -------------------------------------------------------------------------------- /project/proj3/ants/tests/q3.py: -------------------------------------------------------------------------------- 1 | test = { 2 | 'names': [ 3 | 'q3', 4 | '3' 5 | ], 6 | 'points': 1, 7 | 'suites': [ 8 | [ 9 | { 10 | 'test': r""" 11 | >>> # Simple test for Place 12 | >>> exit = Place('Test Exit') 13 | >>> exit.exit 14 | None 15 | # choice: None 16 | # choice: exit 17 | # choice: hive 18 | # choice: colony.queen 19 | >>> exit.entrance 20 | None 21 | # choice: None 22 | # choice: exit 23 | # choice: hive 24 | # choice: colony.queen 25 | >>> place = Place('Test Place', exit) 26 | >>> place.exit 27 | exit 28 | # choice: None 29 | # choice: exit 30 | # choice: hive 31 | # choice: colony.queen 32 | # choice: place 33 | >>> exit.entrance 34 | place 35 | # choice: None 36 | # choice: exit 37 | # choice: hive 38 | # choice: colony.queen 39 | # choice: place 40 | """, 41 | 'type': 'doctest' 42 | }, 43 | { 44 | 'never_lock': True, 45 | 'test': r""" 46 | >>> # Testing if entrances are properly initialized 47 | >>> passed = True 48 | >>> for entrance in colony.bee_entrances: 49 | ... place = entrance 50 | ... while place: 51 | ... passed = passed and (place.entrance is not None) 52 | ... place = place.exit 53 | >>> passed 54 | True 55 | """, 56 | 'type': 'doctest' 57 | }, 58 | { 59 | 'never_lock': True, 60 | 'test': r""" 61 | >>> # Testing if exits and entrances are different 62 | >>> passed = True 63 | >>> for place in colony.places.values(): 64 | ... passed = passed and \ 65 | ... (place is not place.exit) and \ 66 | ... (place is not place.entrance) 67 | ... if place.exit and place.entrance: 68 | ... passed = passed and (place.exit is not place.entrance) 69 | >>> passed 70 | True 71 | """, 72 | 'type': 'doctest' 73 | } 74 | ] 75 | ] 76 | } -------------------------------------------------------------------------------- /project/proj3/ants/tests/q4A.py: -------------------------------------------------------------------------------- 1 | test = { 2 | 'names': [ 3 | 'q4A', 4 | 'qA4', 5 | '4A', 6 | 'A4' 7 | ], 8 | 'points': 2, 9 | 'suites': [ 10 | [ 11 | { 12 | 'answer': 'Always; after adding the insect, reduce its armor to 0 if it is not watersafe', 13 | 'choices': [ 14 | 'Always; after adding the insect, reduce its armor to 0 if it is not watersafe', 15 | 'Only if the insect is watersafe; if it is not watersafe, reduce its armor to 0', 16 | 'Only if the insect is watersafe; if it is not watersafe, remove the insect from the place', 17 | 'Never; no insect can be placed in a Water Place' 18 | ], 19 | 'question': 'When should an insect be added to a Water Place?', 20 | 'type': 'concept' 21 | }, 22 | { 23 | 'answer': 'class attribute', 24 | 'choices': [ 25 | 'class attribute', 26 | 'instance attribute', 27 | 'local attribute', 28 | 'global attribute' 29 | ], 30 | 'question': 'What type of attribute should "watersafe" be?', 31 | 'type': 'concept' 32 | }, 33 | { 34 | 'answer': 'reduce_armor, in the Insect class', 35 | 'choices': [ 36 | 'reduce_armor, in the Insect class', 37 | 'remove_insect, in the Place class', 38 | 'sting, in the Bee class', 39 | 'action, in the Insect class', 40 | 'remove_ant, in the AntColony class' 41 | ], 42 | 'question': r""" 43 | What method deals damage to an Insect and removes it from 44 | its Place? (You should use this method in your code.) 45 | """, 46 | 'type': 'concept' 47 | }, 48 | { 49 | 'never_lock': True, 50 | 'test': r""" 51 | >>> # Testing water with soggy (non-watersafe) bees 52 | >>> test_ants = [Bee(1000000), HarvesterAnt(), Ant(), ThrowerAnt()] 53 | >>> test_ants[0].watersafe = False # Make Bee non-watersafe 54 | >>> test_water = Water('Water Test1') 55 | >>> passed = True 56 | >>> for test_ant in test_ants: 57 | ... test_water.add_insect(test_ant) 58 | ... passed = passed and \ 59 | ... test_ant is not test_water.ant and \ 60 | ... test_ant.armor == 0 61 | >>> passed 62 | True 63 | """, 64 | 'type': 'doctest' 65 | }, 66 | { 67 | 'never_lock': True, 68 | 'test': r""" 69 | >>> # Testing water with watersafe bees 70 | >>> test_bee = Bee(1) 71 | >>> test_water = Water('Water Test2') 72 | >>> test_water.add_insect(test_bee) 73 | >>> test_bee.armor 74 | 1 75 | >>> test_bee in test_water.bees 76 | True 77 | """, 78 | 'type': 'doctest' 79 | } 80 | ], 81 | [ 82 | { 83 | 'never_lock': True, 84 | 'teardown': 'Place.add_insect = old_add_insect', 85 | 'test': r""" 86 | >>> # Testing water inheritance 87 | >>> old_add_insect = Place.add_insect 88 | >>> def new_add_insect(self, insect): 89 | ... raise NotImplementedError() 90 | >>> Place.add_insect = new_add_insect 91 | >>> test_bee = Bee(1) 92 | >>> test_water = Water('Water Test3') 93 | >>> passed = False 94 | >>> try: 95 | ... test_water.add_insect(test_bee) 96 | ... except NotImplementedError: 97 | ... passed = True 98 | >>> passed 99 | True 100 | # explanation: Make sure to use inheritance! 101 | """, 102 | 'type': 'doctest' 103 | }, 104 | { 105 | 'never_lock': True, 106 | 'teardown': 'Place.add_insect = old_add_insect', 107 | 'test': r""" 108 | >>> ### Make sure to place the ant before watering it! 109 | >>> old_add_insect = Place.add_insect 110 | >>> def new_add_insect(self, insect): 111 | ... raise NotImplementedError() 112 | >>> Place.add_insect = new_add_insect 113 | >>> test_ant = HarvesterAnt() 114 | >>> test_water = Water('Water Test4') 115 | >>> passed = False 116 | >>> try: 117 | ... test_water.add_insect(test_ant) 118 | ... except NotImplementedError: 119 | ... passed = True 120 | >>> passed 121 | True 122 | """, 123 | 'type': 'doctest' 124 | } 125 | ] 126 | ] 127 | } -------------------------------------------------------------------------------- /project/proj3/ants/tests/q4B.py: -------------------------------------------------------------------------------- 1 | test = { 2 | 'names': [ 3 | 'q4B', 4 | 'qB4', 5 | 'B4', 6 | '4B' 7 | ], 8 | 'points': 2, 9 | 'suites': [ 10 | [ 11 | { 12 | 'answer': 'random_or_none, defined in ant.py', 13 | 'choices': [ 14 | 'random_or_none, defined in ant.py', 15 | 'random.random(), defined in the "random" module', 16 | 'getitem, defined in the "operators" module' 17 | ], 18 | 'question': 'What function selects a random bee from a list of bees?', 19 | 'type': 'concept' 20 | }, 21 | { 22 | 'test': r""" 23 | >>> # Testing nearest_bee 24 | >>> thrower = ThrowerAnt() 25 | >>> colony.places['tunnel_0_0'].add_insect(thrower) 26 | >>> place = colony.places['tunnel_0_0'] 27 | >>> near_bee = Bee(2) 28 | >>> far_bee = Bee(2) 29 | >>> colony.places["tunnel_0_3"].add_insect(near_bee) 30 | >>> colony.places["tunnel_0_6"].add_insect(far_bee) 31 | >>> hive = colony.hive 32 | >>> thrower.nearest_bee(hive) 33 | near_bee 34 | # choice: near_bee 35 | # choice: far_bee 36 | >>> thrower.action(colony) # Attack! 37 | >>> near_bee.armor # Should do 1 damage 38 | 1 39 | >>> thrower.place # Don't change self.place! 40 | place 41 | # choice: place 42 | # choice: hive 43 | # choice: colony.queen 44 | # choice: None 45 | """, 46 | 'type': 'doctest' 47 | }, 48 | { 49 | 'test': r""" 50 | >>> # Testing Nearest bee not in the hive 51 | >>> thrower = ThrowerAnt() 52 | >>> colony.places["tunnel_0_0"].add_insect(thrower) 53 | >>> hive = colony.hive 54 | >>> bee = Bee(2) 55 | >>> hive.add_insect(bee) # adding a bee to the hive 56 | >>> thrower.nearest_bee(hive) # bee or None ? 57 | None 58 | # choice: None 59 | # choice: bee 60 | # choice: thrower 61 | # choice: hive 62 | """, 63 | 'type': 'doctest' 64 | } 65 | ], 66 | [ 67 | { 68 | 'test': r""" 69 | >>> # Test that ThrowerAnt attacks bees on its own square 70 | >>> thrower = ThrowerAnt() 71 | >>> colony.places['tunnel_0_0'].add_insect(thrower) 72 | >>> near_bee = Bee(2) 73 | >>> colony.places["tunnel_0_0"].add_insect(near_bee) 74 | >>> thrower.nearest_bee(colony.hive) # near_bee or None ? 75 | near_bee 76 | # choice: near_bee 77 | # choice: None 78 | # choice: thrower 79 | # choice: hive 80 | >>> thrower.action(colony) # Attack! 81 | >>> near_bee.armor # should do 1 damage 82 | 1 83 | """, 84 | 'type': 'doctest' 85 | }, 86 | { 87 | 'never_lock': True, 88 | 'test': r""" 89 | >>> # Testing ThrowerAnt chooses a random target 90 | >>> thrower = ThrowerAnt() 91 | >>> colony.places["tunnel_0_0"].add_insect(thrower) 92 | >>> bee1 = Bee(1001) 93 | >>> bee2 = Bee(1001) 94 | >>> colony.places["tunnel_0_3"].add_insect(bee1) 95 | >>> colony.places["tunnel_0_3"].add_insect(bee2) 96 | >>> # Throw 1000 times. The first bee should take ~1000*1/2 = ~500 damage, 97 | >>> # and have ~501 remaining. 98 | >>> for _ in range(1000): 99 | ... thrower.action(colony) 100 | >>> # Test if damage to bee1 is within 6 standard deviations (~95 damage) 101 | >>> # If bees are chosen uniformly, this is true 99.9999998% of the time. 102 | >>> def dmg_within_tolerance(): 103 | ... return abs(bee1.armor-501) < 95 104 | >>> dmg_within_tolerance() 105 | True 106 | """, 107 | 'type': 'doctest' 108 | } 109 | ] 110 | ] 111 | } -------------------------------------------------------------------------------- /project/proj3/ants/tests/q5A.py: -------------------------------------------------------------------------------- 1 | test = { 2 | 'names': [ 3 | 'q5A', 4 | 'qA5', 5 | '5A', 6 | 'A5' 7 | ], 8 | 'points': 3, 9 | 'suites': [ 10 | [ 11 | { 12 | 'test': r""" 13 | >>> # Testing fire parameters 14 | >>> fire = FireAnt() 15 | >>> FireAnt.food_cost 16 | 4 17 | >>> fire.armor 18 | 1 19 | """, 20 | 'type': 'doctest' 21 | }, 22 | { 23 | 'test': r""" 24 | >>> # Testing fire damage 25 | >>> place = Place('Fire Test1') 26 | >>> bee = Bee(5) 27 | >>> place.add_insect(bee) 28 | >>> place.add_insect(FireAnt()) 29 | >>> bee.action(colony) # attack the FireAnt 30 | >>> bee.armor 31 | 2 32 | """, 33 | 'type': 'doctest' 34 | } 35 | ], 36 | [ 37 | { 38 | 'test': r""" 39 | >>> # Testing fire does damage to all Bees in its Place 40 | >>> place = Place('Fire Test2') 41 | >>> bee = Bee(3) 42 | >>> place.add_insect(bee) 43 | >>> place.add_insect(Bee(3)) 44 | >>> place.add_insect(FireAnt()) 45 | >>> bee.action(colony) # attack the FireAnt 46 | >>> len(place.bees) # How many bees are left? 47 | 0 48 | """, 49 | 'type': 'doctest' 50 | }, 51 | { 52 | 'test': r""" 53 | >>> # Testing FireAnt dies 54 | >>> place = Place('Fire Test3') 55 | >>> bee = Bee(1) 56 | >>> ant = FireAnt() 57 | >>> place.add_insect(bee) 58 | >>> place.add_insect(ant) 59 | >>> bee.action(colony) # attack the FireAnt 60 | >>> ant.armor 61 | 0 62 | """, 63 | 'type': 'doctest' 64 | } 65 | ], 66 | [ 67 | { 68 | 'test': r""" 69 | >>> # Testing fire damage is instance attribute 70 | >>> place = Place('Fire Test4') 71 | >>> bee = Bee(900) 72 | >>> buffAnt = ants.FireAnt() 73 | >>> buffAnt.damage = 500 # Feel the burn! 74 | >>> place.add_insect(bee) 75 | >>> place.add_insect(buffAnt) 76 | >>> bee.action(colony) # attack the FireAnt 77 | >>> bee.armor # is damage an instance attribute? 78 | 400 79 | """, 80 | 'type': 'doctest' 81 | } 82 | ] 83 | ] 84 | } -------------------------------------------------------------------------------- /project/proj3/ants/tests/q5B.py: -------------------------------------------------------------------------------- 1 | test = { 2 | 'names': [ 3 | 'q5B', 4 | 'qB5', 5 | '5B', 6 | 'B5' 7 | ], 8 | 'points': 3, 9 | 'suites': [ 10 | [ 11 | { 12 | 'test': r""" 13 | >>> # Testing Long/ShortThrower paramters 14 | >>> short_t = ShortThrower() 15 | >>> long_t = LongThrower() 16 | >>> ShortThrower.food_cost 17 | 3 18 | >>> LongThrower.food_cost 19 | 3 20 | >>> short_t.armor 21 | 1 22 | >>> long_t.armor 23 | 1 24 | """, 25 | 'type': 'doctest' 26 | }, 27 | { 28 | 'never_lock': True, 29 | 'teardown': r""" 30 | ThrowerAnt.action = old_thrower_action 31 | ThrowerAnt.throw_at = old_throw_at 32 | """, 33 | 'test': r""" 34 | >>> # Testing LongThrower Inheritance from ThrowerAnt 35 | >>> def new_action(self, colony): 36 | ... raise NotImplementedError() 37 | >>> def new_throw_at(self, target): 38 | ... raise NotImplementedError() 39 | >>> old_thrower_action = ThrowerAnt.action 40 | >>> old_throw_at = ThrowerAnt.throw_at 41 | >>> ThrowerAnt.action = new_action 42 | >>> test_long = LongThrower() 43 | >>> passed = 0 44 | >>> try: 45 | ... test_long.action(colony) 46 | ... except NotImplementedError: 47 | ... passed += 1 48 | >>> ThrowerAnt.action = old_thrower_action 49 | >>> ThrowerAnt.throw_at = new_throw_at 50 | >>> test_long = LongThrower() 51 | >>> try: 52 | ... test_long.throw_at(Bee(1)) 53 | ... except NotImplementedError: 54 | ... passed += 1 55 | >>> ThrowerAnt.throw_at = old_throw_at 56 | >>> passed 57 | 2 58 | """, 59 | 'type': 'doctest' 60 | }, 61 | { 62 | 'never_lock': True, 63 | 'teardown': r""" 64 | ThrowerAnt.action = old_thrower_action 65 | ThrowerAnt.throw_at = old_throw_at 66 | """, 67 | 'test': r""" 68 | >>> # Testing ShortThrower Inheritance from ThrowerAnt 69 | >>> def new_action(self, colony): 70 | ... raise NotImplementedError() 71 | >>> def new_throw_at(self, target): 72 | ... raise NotImplementedError() 73 | >>> old_thrower_action = ThrowerAnt.action 74 | >>> old_throw_at = ThrowerAnt.throw_at 75 | >>> ThrowerAnt.action = new_action 76 | >>> test_short = ShortThrower() 77 | >>> passed = 0 78 | >>> try: 79 | ... test_short.action(colony) 80 | ... except NotImplementedError: 81 | ... passed += 1 82 | >>> ThrowerAnt.action = old_thrower_action 83 | >>> ThrowerAnt.throw_at = new_throw_at 84 | >>> test_short = ShortThrower() 85 | >>> try: 86 | ... test_short.throw_at(Bee(1)) 87 | ... except NotImplementedError: 88 | ... passed += 1 89 | >>> ThrowerAnt.throw_at = old_throw_at 90 | >>> passed 91 | 2 92 | """, 93 | 'type': 'doctest' 94 | } 95 | ], 96 | [ 97 | { 98 | 'test': r""" 99 | >>> # Test LongThrower Hit 100 | >>> ant = LongThrower() 101 | >>> in_range = Bee(2) 102 | >>> colony.places['tunnel_0_0'].add_insect(ant) 103 | >>> colony.places["tunnel_0_4"].add_insect(in_range) 104 | >>> ant.action(colony) 105 | >>> in_range.armor 106 | 1 107 | """, 108 | 'type': 'doctest' 109 | }, 110 | { 111 | 'test': r""" 112 | >>> # Testing LongThrower miss 113 | >>> ant = LongThrower() 114 | >>> out_of_range = Bee(2) 115 | >>> colony.places["tunnel_0_0"].add_insect(ant) 116 | >>> colony.places["tunnel_0_3"].add_insect(out_of_range) 117 | >>> ant.action(colony) 118 | >>> out_of_range.armor 119 | 2 120 | """, 121 | 'type': 'doctest' 122 | } 123 | ], 124 | [ 125 | { 126 | 'test': r""" 127 | >>> # Test ShortThrower hit 128 | >>> ant = ShortThrower() 129 | >>> in_range = Bee(2) 130 | >>> colony.places['tunnel_0_0'].add_insect(ant) 131 | >>> colony.places["tunnel_0_2"].add_insect(in_range) 132 | >>> ant.action(colony) 133 | >>> in_range.armor 134 | 1 135 | """, 136 | 'type': 'doctest' 137 | }, 138 | { 139 | 'test': r""" 140 | >>> # Testing ShortThrower miss 141 | >>> ant = ShortThrower() 142 | >>> out_of_range = Bee(2) 143 | >>> colony.places["tunnel_0_0"].add_insect(ant) 144 | >>> colony.places["tunnel_0_3"].add_insect(out_of_range) 145 | >>> ant.action(colony) 146 | >>> out_of_range.armor 147 | 2 148 | """, 149 | 'type': 'doctest' 150 | }, 151 | { 152 | 'test': r""" 153 | >>> # Testing if max_range is looked up in the instance 154 | >>> ant = ShortThrower() 155 | >>> ant.max_range = 10 # Buff the ant's range 156 | >>> colony.places["tunnel_0_0"].add_insect(ant) 157 | >>> bee = Bee(2) 158 | >>> colony.places["tunnel_0_6"].add_insect(bee) 159 | >>> ant.action(colony) 160 | >>> bee.armor 161 | 1 162 | """, 163 | 'type': 'doctest' 164 | } 165 | ] 166 | ] 167 | } -------------------------------------------------------------------------------- /project/proj3/ants/tests/q6A.py: -------------------------------------------------------------------------------- 1 | test = { 2 | 'names': [ 3 | 'q6A', 4 | 'qA6', 5 | '6A', 6 | 'A6' 7 | ], 8 | 'points': 1, 9 | 'suites': [ 10 | [ 11 | { 12 | 'test': r""" 13 | >>> # Testing WallAnt parameters 14 | >>> wall = WallAnt() 15 | >>> wall.armor 16 | 4 17 | >>> WallAnt.food_cost 18 | 4 19 | """, 20 | 'type': 'doctest' 21 | } 22 | ] 23 | ] 24 | } -------------------------------------------------------------------------------- /project/proj3/ants/tests/q6B.py: -------------------------------------------------------------------------------- 1 | test = { 2 | 'names': [ 3 | 'q6B', 4 | 'qB6', 5 | '6B', 6 | 'B6' 7 | ], 8 | 'points': 1, 9 | 'suites': [ 10 | [ 11 | { 12 | 'test': r""" 13 | >>> # Testing ScubaThrower parameters 14 | >>> scuba = ScubaThrower() 15 | >>> ScubaThrower.food_cost 16 | 5 17 | >>> scuba.armor 18 | 1 19 | """, 20 | 'type': 'doctest' 21 | }, 22 | { 23 | 'never_lock': True, 24 | 'teardown': r""" 25 | ThrowerAnt.action = old_thrower_action 26 | ThrowerAnt.throw_at = old_throw_at 27 | """, 28 | 'test': r""" 29 | >>> # Testing ScubaThrower Inheritance from ThrowerAnt 30 | >>> def new_action(self, colony): 31 | ... raise NotImplementedError() 32 | >>> def new_throw_at(self, target): 33 | ... raise NotImplementedError() 34 | >>> old_thrower_action = ThrowerAnt.action 35 | >>> old_throw_at = ThrowerAnt.throw_at 36 | >>> ThrowerAnt.action = new_action 37 | >>> test_scuba = ScubaThrower() 38 | >>> passed = 0 39 | >>> try: 40 | ... test_scuba.action(colony) 41 | ... except NotImplementedError: 42 | ... passed += 1 43 | >>> ThrowerAnt.action = old_thrower_action 44 | >>> ThrowerAnt.throw_at = new_throw_at 45 | >>> test_scuba = ScubaThrower() 46 | >>> try: 47 | ... test_scuba.throw_at(Bee(1)) 48 | ... except NotImplementedError: 49 | ... passed += 1 50 | >>> ThrowerAnt.throw_at = old_throw_at 51 | >>> passed 52 | 2 53 | """, 54 | 'type': 'doctest' 55 | }, 56 | { 57 | 'test': r""" 58 | >>> # Testing if ScubaThrower is watersafe 59 | >>> water = Water('Water') 60 | >>> ant = ScubaThrower() 61 | >>> water.add_insect(ant) 62 | >>> ant.place 63 | water 64 | # choice: water 65 | # choice: None 66 | # choice: colony.hive 67 | # choice: colony.queen 68 | >>> ant.armor 69 | 1 70 | """, 71 | 'type': 'doctest' 72 | } 73 | ], 74 | [ 75 | { 76 | 'test': r""" 77 | >>> # Testing ScubaThrower on land 78 | >>> place1 = colony.places["tunnel_0_0"] 79 | >>> place2 = colony.places["tunnel_0_4"] 80 | >>> ant = ScubaThrower() 81 | >>> bee = Bee(3) 82 | >>> place1.add_insect(ant) 83 | >>> place2.add_insect(bee) 84 | >>> ant.action(colony) 85 | >>> bee.armor # ScubaThrower can throw on land 86 | 2 87 | """, 88 | 'type': 'doctest' 89 | }, 90 | { 91 | 'test': r""" 92 | >>> # Testing ScubaThrower in the water 93 | >>> water = Water("water") 94 | >>> water.entrance = colony.places["tunnel_0_1"] 95 | >>> target = colony.places["tunnel_0_4"] 96 | >>> ant = ScubaThrower() 97 | >>> bee = Bee(3) 98 | >>> water.add_insect(ant) 99 | >>> target.add_insect(bee) 100 | >>> ant.action(colony) 101 | >>> bee.armor # ScubaThrower can throw in water 102 | 2 103 | """, 104 | 'type': 'doctest' 105 | } 106 | ] 107 | ] 108 | } -------------------------------------------------------------------------------- /project/proj3/ants/tests/q7A.py: -------------------------------------------------------------------------------- 1 | test = { 2 | 'names': [ 3 | 'q7A', 4 | '7A', 5 | 'qA7', 6 | 'A7' 7 | ], 8 | 'points': 3, 9 | 'suites': [ 10 | [ 11 | { 12 | 'test': r""" 13 | >>> # Testing NinjaAnt parameters 14 | >>> ninja = NinjaAnt() 15 | >>> ninja.armor 16 | 1 17 | >>> NinjaAnt.food_cost 18 | 6 19 | """, 20 | 'type': 'doctest' 21 | }, 22 | { 23 | 'test': r""" 24 | >>> # Testing non-NinjaAnts still block bees 25 | >>> p0 = colony.places["tunnel_0_0"] 26 | >>> p1 = colony.places["tunnel_0_1"] 27 | >>> bee = Bee(2) 28 | >>> p1.add_insect(bee) 29 | >>> p1.add_insect(ThrowerAnt()) 30 | >>> bee.action(colony) # attack ant, don't move past it 31 | >>> bee.place 32 | p1 33 | # choice: p1 34 | # choice: p0 35 | # choice: None 36 | """, 37 | 'type': 'doctest' 38 | } 39 | ], 40 | [ 41 | { 42 | 'test': r""" 43 | >>> # Testing NinjaAnts do not block bees 44 | >>> p0 = colony.places["tunnel_0_0"] 45 | >>> p1 = colony.places["tunnel_0_1"] 46 | >>> bee = Bee(2) 47 | >>> p1.add_insect(bee) 48 | >>> p1.add_insect(NinjaAnt()) 49 | >>> bee.action(colony) # shouldn't attack ant, move past it 50 | >>> bee.place 51 | p0 52 | # choice: p0 53 | # choice: p1 54 | # choice: None 55 | """, 56 | 'type': 'doctest' 57 | }, 58 | { 59 | 'test': r""" 60 | >>> # Testing NinjaAnt strikes all bees in its place 61 | >>> test_place = colony.places["tunnel_0_0"] 62 | >>> for _ in range(3): 63 | ... test_place.add_insect(Bee(1)) 64 | >>> ninja = NinjaAnt() 65 | >>> test_place.add_insect(ninja) 66 | >>> ninja.action(colony) # should strike all bees in place 67 | >>> len(test_place.bees) 68 | 0 69 | """, 70 | 'type': 'doctest' 71 | } 72 | ], 73 | [ 74 | { 75 | 'test': r""" 76 | >>> # Testing damage is looked up on the instance 77 | >>> place = colony.places["tunnel_0_0"] 78 | >>> bee = Bee(900) 79 | >>> place.add_insect(bee) 80 | >>> buffNinja = NinjaAnt() 81 | >>> buffNinja.damage = 500 # Sharpen the sword 82 | >>> place.add_insect(buffNinja) 83 | >>> buffNinja.action(colony) 84 | >>> bee.armor 85 | 400 86 | """, 87 | 'type': 'doctest' 88 | } 89 | ] 90 | ] 91 | } -------------------------------------------------------------------------------- /project/proj3/ants/tests/q7B.py: -------------------------------------------------------------------------------- 1 | test = { 2 | 'names': [ 3 | 'q7B', 4 | '7B', 5 | 'qB7', 6 | 'B7' 7 | ], 8 | 'points': 3, 9 | 'suites': [ 10 | [ 11 | { 12 | 'test': r""" 13 | >>> # Testing HungryAnt parameters 14 | >>> hungry = HungryAnt() 15 | >>> HungryAnt.food_cost 16 | 4 17 | >>> hungry.armor 18 | 1 19 | """, 20 | 'type': 'doctest' 21 | }, 22 | { 23 | 'test': r""" 24 | >>> # Testing HungryAnt eats and digests 25 | >>> hungry = HungryAnt() 26 | >>> super_bee, wimpy_bee = Bee(1000), Bee(1) 27 | >>> place = colony.places["tunnel_0_0"] 28 | >>> place.add_insect(hungry) 29 | >>> place.add_insect(super_bee) 30 | >>> hungry.action(colony) # super_bee is no match for HungryAnt! 31 | >>> super_bee.armor 32 | 0 33 | >>> place.add_insect(wimpy_bee) 34 | >>> for _ in range(3): 35 | ... hungry.action(colony) # digesting...not eating 36 | >>> wimpy_bee.armor 37 | 1 38 | >>> hungry.action(colony) # back to eating! 39 | >>> wimpy_bee.armor 40 | 0 41 | """, 42 | 'type': 'doctest' 43 | } 44 | ], 45 | [ 46 | { 47 | 'test': r""" 48 | >>> # Testing HungryAnt only waits when digesting 49 | >>> hungry = HungryAnt() 50 | >>> place = colony.places["tunnel_0_0"] 51 | >>> place.add_insect(hungry) 52 | >>> # Wait a few turns before adding Bee 53 | >>> for _ in range(5): 54 | ... hungry.action(colony) # shouldn't be digesting 55 | >>> bee = Bee(3) 56 | >>> place.add_insect(bee) 57 | >>> hungry.action(colony) # Eating time! 58 | >>> bee.armor 59 | 0 60 | """, 61 | 'type': 'doctest' 62 | } 63 | ], 64 | [ 65 | { 66 | 'test': r""" 67 | >>> # Testing HungryAnt digest time looked up on instance 68 | >>> very_hungry = HungryAnt() # Add very hungry caterpi- um, ant 69 | >>> very_hungry.time_to_digest = 0 70 | >>> place = colony.places["tunnel_0_0"] 71 | >>> place.add_insect(very_hungry) 72 | >>> for _ in range(100): 73 | ... place.add_insect(ants.Bee(3)) 74 | >>> for _ in range(100): 75 | ... very_hungry.action(colony) # Eat all the bees! 76 | >>> len(place.bees) 77 | 0 78 | """, 79 | 'type': 'doctest' 80 | } 81 | ] 82 | ] 83 | } -------------------------------------------------------------------------------- /project/proj3/ants/tests/q8.py: -------------------------------------------------------------------------------- 1 | test = { 2 | 'names': [ 3 | 'q8', 4 | '8' 5 | ], 6 | 'params': { 7 | 'doctest': { 8 | 'setup': r""" 9 | place = Place("TestProblem8") 10 | bodyguard = BodyguardAnt() 11 | bodyguard2 = BodyguardAnt() 12 | test_ant = Ant() 13 | test_ant2 = Ant() 14 | harvester = HarvesterAnt() 15 | """ 16 | } 17 | }, 18 | 'points': 5, 19 | 'suites': [ 20 | [ 21 | { 22 | 'test': r""" 23 | >>> # Testing BodyguardAnt parameters 24 | >>> bodyguard = BodyguardAnt() 25 | >>> BodyguardAnt.food_cost 26 | 4 27 | >>> bodyguard.armor 28 | 2 29 | """, 30 | 'type': 'doctest' 31 | }, 32 | { 33 | 'test': r""" 34 | >>> # Testing BodyguardAnt starts off empty 35 | >>> bodyguard.ant 36 | None 37 | >>> bodyguard.action(colony) 38 | """, 39 | 'type': 'doctest' 40 | }, 41 | { 42 | 'test': r""" 43 | >>> # Testing BodyguardAnt contain_ant 44 | >>> bodyguard.contain_ant(test_ant) 45 | >>> bodyguard.ant 46 | test_ant 47 | # choice: test_ant 48 | # choice: bodyguard 49 | # choice: None 50 | """, 51 | 'type': 'doctest' 52 | }, 53 | { 54 | 'test': r""" 55 | >>> # Testing BodyguardAnt container 56 | >>> bodyguard.container 57 | True 58 | """, 59 | 'type': 'doctest' 60 | }, 61 | { 62 | 'test': r""" 63 | >>> # Testing normal Ant container is false 64 | >>> test_ant.container 65 | False 66 | """, 67 | 'type': 'doctest' 68 | } 69 | ], 70 | [ 71 | { 72 | 'test': r""" 73 | >>> # Testing bodyguard.can_contain returns True on non-containers 74 | >>> bodyguard.can_contain(test_ant) 75 | True 76 | """, 77 | 'type': 'doctest' 78 | }, 79 | { 80 | 'test': r""" 81 | >>> # Testing normal_ant.can_contain returns False 82 | >>> test_ant.can_contain(test_ant2) 83 | False 84 | """, 85 | 'type': 'doctest' 86 | }, 87 | { 88 | 'test': r""" 89 | >>> # Testing bodyguard.can_contain returns False on otherbodyguards 90 | >>> bodyguard.can_contain(bodyguard2) 91 | False 92 | """, 93 | 'type': 'doctest' 94 | }, 95 | { 96 | 'test': r""" 97 | >>> # Testing bodyguard.can_contain returns False once it is already containing 98 | >>> bodyguard.contain_ant(test_ant) 99 | >>> bodyguard.can_contain(test_ant2) 100 | False 101 | """, 102 | 'type': 'doctest' 103 | } 104 | ], 105 | [ 106 | { 107 | 'test': r""" 108 | >>> # Testing modified add_insect test 1 109 | >>> place.add_insect(bodyguard) 110 | >>> place.add_insect(test_ant) 111 | >>> bodyguard.ant is test_ant 112 | True 113 | >>> place.ant is bodyguard 114 | True 115 | """, 116 | 'type': 'doctest' 117 | }, 118 | { 119 | 'test': r""" 120 | >>> # Testing modified add_insect test 2 121 | >>> place.add_insect(test_ant) 122 | >>> place.add_insect(bodyguard) 123 | >>> bodyguard.ant is test_ant 124 | True 125 | >>> place.ant is bodyguard 126 | True 127 | """, 128 | 'type': 'doctest' 129 | }, 130 | { 131 | 'never_lock': True, 132 | 'test': r""" 133 | >>> # Testing modified add_insect test 3 134 | >>> place.add_insect(bodyguard) 135 | >>> place is bodyguard.place 136 | True 137 | >>> passed = False 138 | >>> try: 139 | ... place.add_insect(bodyguard2) # can't add bodyguard in another bodyguard 140 | ... except AssertionError: 141 | ... passed = True 142 | >>> passed 143 | True 144 | """, 145 | 'type': 'doctest' 146 | }, 147 | { 148 | 'never_lock': True, 149 | 'test': r""" 150 | >>> # Testing modified add_insect test 4 151 | >>> place.add_insect(bodyguard) 152 | >>> place.add_insect(test_ant) 153 | >>> passed = False 154 | >>> try: 155 | ... place.add_insect(test_ant2) # can't add third ant 156 | ... except AssertionError: 157 | ... passed = True 158 | >>> passed 159 | True 160 | """, 161 | 'type': 'doctest' 162 | } 163 | ], 164 | [ 165 | { 166 | 'test': r""" 167 | >>> # Testing what happens if bodyguard ant perishes 168 | >>> place.add_insect(bodyguard) 169 | >>> place.add_insect(test_ant) 170 | >>> bodyguard.reduce_armor(bodyguard.armor) 171 | >>> place.ant is test_ant 172 | True 173 | """, 174 | 'type': 'doctest' 175 | }, 176 | { 177 | 'test': r""" 178 | >>> # Testing bodyguard performs contained ant's action 179 | >>> food = colony.food 180 | >>> bodyguard.contain_ant(harvester) 181 | >>> bodyguard.action(colony) # should do harvester's action 182 | >>> colony.food 183 | food + 1 184 | # choice: food + 1 185 | # choice: food 186 | # choice: 0 187 | # choice: 1 188 | """, 189 | 'type': 'doctest' 190 | }, 191 | { 192 | 'test': r""" 193 | >>> # Testing bodyguard performs thrower's action 194 | >>> ant = ThrowerAnt() 195 | >>> bee = ants.Bee(2) 196 | >>> colony.places["tunnel_0_0"].add_insect(bodyguard) 197 | >>> colony.places["tunnel_0_0"].add_insect(ant) 198 | >>> colony.places["tunnel_0_3"].add_insect(bee) 199 | >>> bodyguard.action(colony) 200 | >>> bee.armor 201 | 1 202 | """, 203 | 'type': 'doctest' 204 | }, 205 | { 206 | 'test': r""" 207 | >>> # Testing removing a bodyguard doesn't remove contained ant 208 | >>> place = colony.places['tunnel_0_0'] 209 | >>> bodyguard = BodyguardAnt() 210 | >>> test_ant = Ant(1) 211 | >>> place.add_insect(bodyguard) 212 | >>> place.add_insect(test_ant) 213 | >>> colony.remove_ant('tunnel_0_0') 214 | >>> place.ant is test_ant 215 | True 216 | """, 217 | 'type': 'doctest' 218 | }, 219 | { 220 | 'test': r""" 221 | >>> # Testing bodyguarded ant keeps instance attributes 222 | >>> test_ant = Ant() 223 | >>> def new_action( colony): 224 | ... test_ant.armor += 9000 225 | >>> test_ant.action = new_action 226 | >>> place = colony.places['tunnel_0_0'] 227 | >>> bodyguard = BodyguardAnt() 228 | >>> place.add_insect(test_ant) 229 | >>> place.add_insect(bodyguard) 230 | >>> place.ant.action(colony) 231 | >>> place.ant.ant.armor 232 | 9001 233 | """, 234 | 'type': 'doctest' 235 | } 236 | ], 237 | [ 238 | { 239 | 'test': r""" 240 | >>> # Testing if we can construct a container besides BodyGuard 241 | >>> ant = ThrowerAnt() 242 | >>> ant.container = True 243 | >>> ant.ant = None 244 | >>> ant.can_contain(ThrowerAnt()) 245 | True 246 | """, 247 | 'type': 'doctest' 248 | }, 249 | { 250 | 'test': r""" 251 | >>> # Testing container can contain a special non-container bodyguard 252 | >>> bodyguard = BodyguardAnt() 253 | >>> mod_guard = BodyguardAnt() 254 | >>> mod_guard.container = False 255 | >>> bodyguard.can_contain(mod_guard) 256 | True 257 | """, 258 | 'type': 'doctest' 259 | } 260 | ] 261 | ] 262 | } -------------------------------------------------------------------------------- /project/proj3/ants/tests/q9.py: -------------------------------------------------------------------------------- 1 | test = { 2 | 'names': [ 3 | 'q9', 4 | '9' 5 | ], 6 | 'params': { 7 | 'doctest': { 8 | 'setup': r""" 9 | def queen_layout(queen, register_place, steps=5): 10 | "Create a two-tunnel layout with water in the middle of 5 steps." 11 | for tunnel in range(2): 12 | exit = queen 13 | for step in range(steps): 14 | place = ants.Water if step == steps//2 else ants.Place 15 | exit = place('tunnel_{0}_{1}'.format(tunnel, step), exit) 16 | register_place(exit, step == steps-1) 17 | 18 | imp.reload(ants) 19 | hive = ants.Hive(ants.make_test_assault_plan()) 20 | colony = ants.AntColony(None, hive, ants.ant_types(), queen_layout) 21 | queen = ants.QueenAnt() 22 | imposter = ants.QueenAnt() 23 | """ 24 | } 25 | }, 26 | 'points': 5, 27 | 'suites': [ 28 | [ 29 | { 30 | 'test': r""" 31 | >>> # Testing queen place 32 | >>> colony_queen = ants.Place('Original Queen Location of the Colony') 33 | >>> ant_queen = ants.Place('Place given to the QueenAnt') 34 | >>> queen_place = ants.QueenPlace(colony_queen, ant_queen) 35 | >>> colony_queen.bees = [ants.Bee(1, colony_queen) for _ in range(3)] 36 | >>> ant_queen.bees = [ants.Bee(2, colony_queen) for _ in range(4)] 37 | >>> len(queen_place.bees) 38 | 7 39 | >>> bee_armor = sum(bee.armor for bee in queen_place.bees) 40 | >>> bee_armor 41 | 11 42 | """, 43 | 'type': 'doctest' 44 | } 45 | ], 46 | [ 47 | { 48 | 'never_lock': True, 49 | 'test': r""" 50 | >>> # Testing double damage 51 | >>> back = ants.ThrowerAnt() 52 | >>> thrower_damage = back.damage 53 | >>> front = ants.FireAnt() 54 | >>> fire_damage = front.damage 55 | >>> side_back = ants.ThrowerAnt() 56 | >>> side_front = ants.ThrowerAnt() 57 | >>> armor, side_armor = 20, 10 58 | >>> bee, side_bee = ants.Bee(armor), ants.Bee(side_armor) 59 | >>> colony.places['tunnel_0_0'].add_insect(back) 60 | >>> colony.places['tunnel_0_2'].add_insect(queen) 61 | >>> colony.places['tunnel_0_4'].add_insect(bee) 62 | >>> colony.places['tunnel_1_1'].add_insect(side_back) 63 | >>> colony.places['tunnel_1_3'].add_insect(side_front) 64 | >>> colony.places['tunnel_1_4'].add_insect(side_bee) 65 | >>> # Simulate a battle in Tunnel 0 (contains Queen) 66 | >>> back.action(colony) 67 | >>> armor -= thrower_damage # No doubling until queen's action 68 | >>> bee.armor # if failed, damage doubled too early 69 | armor 70 | >>> queen.action(colony) 71 | >>> armor -= thrower_damage # Queen should always deal normal damage 72 | >>> bee.armor # if failed, Queen damage incorrect 73 | armor 74 | >>> bee.action(colony) # Bee moves forward 75 | >>> colony.places['tunnel_0_3'].add_insect(front) # Fire ant added 76 | >>> back.action(colony) 77 | >>> armor -= 2 * thrower_damage # Damage doubled in back 78 | >>> bee.armor # if failed, back damage incorrect 79 | armor 80 | >>> queen.action(colony) 81 | >>> armor -= thrower_damage # Queen should always deal normal damage 82 | >>> bee.armor # If failed, Queen damage incorrect (2) 83 | armor 84 | >>> back.action(colony) 85 | >>> armor -= 2 * thrower_damage # Thrower damage still doubled 86 | >>> bee.armor # Back damage incorrect 87 | armor 88 | >>> bee.action(colony) 89 | >>> armor -= 2 * fire_damage # Fire damage doubled 90 | >>> bee.armor # if failed, Fire damage incorrect 91 | armor 92 | >>> # Simulate a battle in Tunnel 1 (no Queen) 93 | >>> side_bee.armor # if failed, side bee took damage when it shouldn't have 94 | side_armor 95 | >>> side_back.action(colony) 96 | >>> side_armor -= thrower_damage # Ant in another tunnel: normal damage 97 | >>> side_bee.armor # If failed, side back damage is incorrect 98 | side_armor 99 | >>> side_front.action(colony) 100 | >>> side_armor -= thrower_damage # Ant in another tunnel: normal damage 101 | >>> side_bee.armor # If failed, side front damage is incorrect 102 | side_armor 103 | """, 104 | 'type': 'doctest' 105 | } 106 | ], 107 | [ 108 | { 109 | 'test': r""" 110 | >>> # Testing Game ends when Queen place is infiltrated 111 | >>> bee = ants.Bee(3) 112 | >>> colony.places['tunnel_0_1'].add_insect(queen) 113 | >>> colony.places['tunnel_0_2'].add_insect(bee) 114 | >>> queen.action(colony) 115 | >>> len(colony.queen.bees) <= 0 # If failed, Game ended before it should have 116 | True 117 | >>> bee.action(colony) 118 | >>> len(colony.queen.bees) > 0 # Game should have ended 119 | True 120 | """, 121 | 'type': 'doctest' 122 | }, 123 | { 124 | 'test': r""" 125 | >>> # Testing Imposter Queen 126 | >>> ant = ants.ThrowerAnt() 127 | >>> bee = ants.Bee(10) 128 | >>> colony.places['tunnel_0_0'].add_insect(queen) 129 | >>> colony.places['tunnel_0_1'].add_insect(imposter) 130 | >>> colony.places['tunnel_0_3'].add_insect(ant) 131 | >>> colony.places['tunnel_0_4'].add_insect(bee) 132 | >>> imposter.action(colony) 133 | >>> bee.armor # Imposter should not damage bee 134 | 10 135 | >>> ant.damage # Imposter should not double damage 136 | 1 137 | >>> queen.action(colony) 138 | >>> bee.armor # Queen should damage bee 139 | 9 140 | >>> ant.damage # Queen should double damage 141 | 2 142 | >>> ant.action(colony) 143 | >>> bee.armor # If failed, ThrowerAnt has incorrect damage 144 | 7 145 | >>> queen.armor # Long live the Queen 146 | 1 147 | >>> imposter.armor # Short-lived imposter 148 | 0 149 | """, 150 | 'type': 'doctest' 151 | } 152 | ], 153 | [ 154 | { 155 | 'never_lock': True, 156 | 'test': r""" 157 | >>> # Testing bodyguard doubling 158 | >>> bee = ants.Bee(3) 159 | >>> guard = ants.BodyguardAnt() 160 | >>> guard.damage, doubled = 5, 10 161 | >>> colony.places['tunnel_0_1'].add_insect(queen) 162 | >>> colony.places['tunnel_0_1'].add_insect(guard) 163 | >>> colony.places['tunnel_0_2'].add_insect(bee) 164 | >>> queen.action(colony) 165 | >>> guard.damage # Bodyguard should be buffed 166 | doubled 167 | >>> queen.action(colony) 168 | >>> bee.armor # QueenAnt should not have been buffed 169 | 1 170 | >>> guard.damage # Bodyguard should not be buffed twice 171 | doubled 172 | >>> len(colony.queen.bees) <= 0 # Game should not have ended 173 | True 174 | >>> bee.action(colony) 175 | >>> len(colony.queen.bees) > 0 # Game should have ended 176 | True 177 | """, 178 | 'type': 'doctest' 179 | }, 180 | { 181 | 'never_lock': True, 182 | 'test': r""" 183 | >>> # Testing bodyguard contain doubling 184 | >>> guard = ants.BodyguardAnt() 185 | >>> guard.damage, doubled = 5, 10 186 | >>> ant = ants.ThrowerAnt() 187 | >>> ant_doubled = 2 * ant.damage 188 | >>> colony.places['tunnel_0_1'].add_insect(queen) 189 | >>> colony.places['tunnel_0_3'].add_insect(guard) 190 | >>> colony.places['tunnel_0_3'].add_insect(ant) 191 | >>> queen.action(colony) 192 | >>> guard.damage # Bodyguard damage should have doubled 193 | doubled 194 | >>> ant.damage # Contained ant should be buffed 195 | ant_doubled 196 | >>> queen.action(colony) 197 | >>> guard.damage # Bodyguard should not be buffed twice 198 | doubled 199 | >>> ant.damage # contained ant should not be buffed twice 200 | ant_doubled 201 | """, 202 | 'type': 'doctest' 203 | } 204 | ], 205 | [ 206 | { 207 | 'test': r""" 208 | >>> # Testing Remove 209 | >>> p0 = colony.places['tunnel_0_0'] 210 | >>> p1 = colony.places['tunnel_0_1'] 211 | >>> p0.add_insect(queen) 212 | >>> p1.add_insect(imposter) 213 | >>> p0.remove_insect(queen) 214 | >>> p1.remove_insect(imposter) 215 | >>> queen == p0.ant # Queen can't be removed 216 | True 217 | >>> p1.ant # Imposter should have been removed 218 | None 219 | >>> queen.action(colony) 220 | """, 221 | 'type': 'doctest' 222 | }, 223 | { 224 | 'test': r""" 225 | >>> # Testing that game still ends the old-fashioned way 226 | >>> bee = ants.Bee(3) 227 | >>> # The bee has an uninterrupted path to the heart of the colony 228 | >>> colony.places['tunnel_0_1'].add_insect(bee) 229 | >>> colony.places['tunnel_0_2'].add_insect(queen) 230 | >>> queen.action(colony) 231 | >>> bee.action(colony) 232 | >>> len(colony.queen.bees) <= 0 # Game should not be over 233 | True 234 | >>> queen.action(colony) 235 | >>> bee.action(colony) 236 | >>> len(colony.queen.bees) > 0 # Game should have ended 237 | True 238 | """, 239 | 'type': 'doctest' 240 | }, 241 | { 242 | 'test': r""" 243 | >>> # Testing if queen will buff newly added ants 244 | >>> colony.places['tunnel_0_0'].add_insect(ants.ThrowerAnt()) 245 | >>> colony.places['tunnel_0_2'].add_insect(queen) 246 | >>> queen.action(colony) 247 | >>> # Add ant and buff 248 | >>> ant = ants.ThrowerAnt() 249 | >>> colony.places['tunnel_0_1'].add_insect(ant) 250 | >>> queen.action(colony) 251 | >>> # Attack a bee 252 | >>> bee = ants.Bee(3) 253 | >>> colony.places['tunnel_0_4'].add_insect(bee) 254 | >>> ant.action(colony) 255 | >>> bee.armor # Queen should buff new ants 256 | 1 257 | """, 258 | 'type': 'doctest' 259 | } 260 | ] 261 | ] 262 | } -------------------------------------------------------------------------------- /project/proj3/ants/tests/qEC.py: -------------------------------------------------------------------------------- 1 | test = { 2 | 'extra': True, 3 | 'names': [ 4 | 'qEC', 5 | 'EC', 6 | 'ec', 7 | 'extra' 8 | ], 9 | 'points': 2, 10 | 'suites': [ 11 | [ 12 | { 13 | 'locked': True, 14 | 'test': r""" 15 | >>> # Testing status parameters 16 | >>> slow = SlowThrower() 17 | >>> stun = StunThrower() 18 | >>> SlowThrower.food_cost 19 | f4b3281120d40117b023d6c1a373fde6 20 | # locked 21 | >>> StunThrower.food_cost 22 | 6e64cd41ecdfe7fd3b99f4395dfd7c25 23 | # locked 24 | >>> slow.armor 25 | d051d778cc59e30ceee412e76d1fdbc4 26 | # locked 27 | >>> stun.armor 28 | d051d778cc59e30ceee412e76d1fdbc4 29 | # locked 30 | """, 31 | 'type': 'doctest' 32 | }, 33 | { 34 | 'never_lock': True, 35 | 'test': r""" 36 | >>> # Testing Slow 37 | >>> slow = SlowThrower() 38 | >>> bee = Bee(3) 39 | >>> colony.places["tunnel_0_0"].add_insect(slow) 40 | >>> colony.places["tunnel_0_4"].add_insect(bee) 41 | >>> slow.action(colony) 42 | >>> colony.time = 1 43 | >>> bee.action(colony) 44 | >>> bee.place.name # SlowThrower should cause slowness on odd turns 45 | 'tunnel_0_4' 46 | >>> colony.time += 1 47 | >>> bee.action(colony) 48 | >>> bee.place.name # SlowThrower should cause slowness on odd turns 49 | 'tunnel_0_3' 50 | >>> for _ in range(3): 51 | ... colony.time += 1 52 | ... bee.action(colony) 53 | >>> bee.place.name 54 | 'tunnel_0_1' 55 | """, 56 | 'type': 'doctest' 57 | }, 58 | { 59 | 'never_lock': True, 60 | 'test': r""" 61 | >>> # Testing Stun 62 | >>> error_msg = "StunThrower doesn't stun for exactly one turn." 63 | >>> stun = StunThrower() 64 | >>> bee = Bee(3) 65 | >>> colony.places["tunnel_0_0"].add_insect(stun) 66 | >>> colony.places["tunnel_0_4"].add_insect(bee) 67 | >>> stun.action(colony) 68 | >>> bee.action(colony) 69 | >>> bee.place.name # StunThrower should stun for exactly one turn 70 | 'tunnel_0_4' 71 | >>> bee.action(colony) 72 | >>> bee.place.name # StunThrower should stun for exactly one turn 73 | 'tunnel_0_3' 74 | """, 75 | 'type': 'doctest' 76 | } 77 | ], 78 | [ 79 | { 80 | 'locked': True, 81 | 'test': r""" 82 | >>> # Testing if effects stack 83 | >>> stun = StunThrower() 84 | >>> bee = Bee(3) 85 | >>> stun_place = colony.places["tunnel_0_0"] 86 | >>> bee_place = colony.places["tunnel_0_4"] 87 | >>> stun_place.add_insect(stun) 88 | >>> bee_place.add_insect(bee) 89 | >>> for _ in range(4): # stun bee four times 90 | ... stun.action(colony) 91 | >>> passed = True 92 | >>> for _ in range(4): 93 | ... bee.action(colony) 94 | ... if bee.place.name != 'tunnel_0_4': 95 | ... passed = False 96 | >>> passed 97 | 818d43c4eb49bce28d693d249148409c 98 | # locked 99 | """, 100 | 'type': 'doctest' 101 | }, 102 | { 103 | 'locked': True, 104 | 'test': r""" 105 | >>> # Testing multiple stuns 106 | >>> stun1 = StunThrower() 107 | >>> stun2 = StunThrower() 108 | >>> bee1 = Bee(3) 109 | >>> bee2 = Bee(3) 110 | >>> colony.places["tunnel_0_0"].add_insect(stun1) 111 | >>> colony.places["tunnel_0_1"].add_insect(bee1) 112 | >>> colony.places["tunnel_0_2"].add_insect(stun2) 113 | >>> colony.places["tunnel_0_3"].add_insect(bee2) 114 | >>> stun1.action(colony) 115 | >>> stun2.action(colony) 116 | >>> bee1.action(colony) 117 | >>> bee2.action(colony) 118 | >>> bee1.place.name 119 | 922cc8e76e4df721c3123a518f16b467 120 | # locked 121 | >>> bee2.place.name 122 | 6ee2dac456a484e0c52d92382c675744 123 | # locked 124 | >>> bee1.action(colony) 125 | >>> bee2.action(colony) 126 | >>> bee1.place.name 127 | ca02366e00a5ed9c798ad31c3bb8a2cb 128 | # locked 129 | >>> bee2.place.name 130 | 359065a3eb11a7b754157e3fde96fc93 131 | # locked 132 | """, 133 | 'type': 'doctest' 134 | }, 135 | { 136 | 'locked': True, 137 | 'test': r""" 138 | >>> # Testing long effect stack 139 | >>> stun = StunThrower() 140 | >>> slow = SlowThrower() 141 | >>> bee = Bee(3) 142 | >>> colony.places["tunnel_0_0"].add_insect(stun) 143 | >>> colony.places["tunnel_0_1"].add_insect(slow) 144 | >>> colony.places["tunnel_0_4"].add_insect(bee) 145 | >>> for _ in range(3): # slow bee three times 146 | ... slow.action(colony) 147 | >>> stun.action(colony) # stun bee once 148 | >>> colony.time = 0 149 | >>> bee.action(colony) # stunned 150 | >>> bee.place.name 151 | 0743f00a386a9fdb01a8d03f6cafc604 152 | # locked 153 | >>> colony.time = 1 154 | >>> bee.action(colony) # slowed thrice 155 | >>> bee.place.name 156 | 0743f00a386a9fdb01a8d03f6cafc604 157 | # locked 158 | >>> colony.time = 2 159 | >>> bee.action(colony) # slowed thrice 160 | >>> bee.place.name 161 | 6ee2dac456a484e0c52d92382c675744 162 | # locked 163 | >>> colony.time = 3 164 | >>> bee.action(colony) # slowed thrice 165 | >>> bee.place.name 166 | 6ee2dac456a484e0c52d92382c675744 167 | # locked 168 | >>> colony.time = 4 169 | >>> bee.action(colony) # slowed twice 170 | >>> bee.place.name 171 | 359065a3eb11a7b754157e3fde96fc93 172 | # locked 173 | >>> colony.time = 5 174 | >>> bee.action(colony) # slowed twice 175 | >>> bee.place.name 176 | 359065a3eb11a7b754157e3fde96fc93 177 | # locked 178 | >>> colony.time = 6 179 | >>> bee.action(colony) # slowed once 180 | >>> bee.place.name 181 | 922cc8e76e4df721c3123a518f16b467 182 | # locked 183 | >>> colony.time = 7 184 | >>> bee.action(colony) # no effects 185 | >>> slow.armor 186 | 11862fc8ebde17878dbcfc9a133b7094 187 | # locked 188 | """, 189 | 'type': 'doctest' 190 | } 191 | ] 192 | ] 193 | } -------------------------------------------------------------------------------- /project/proj3/ants/ucb.py: -------------------------------------------------------------------------------- 1 | """The UCB module contains functions specific to 61A projects at UC Berkeley.""" 2 | 3 | import code 4 | import functools 5 | import inspect 6 | import re 7 | import signal 8 | import sys 9 | 10 | 11 | def main(fn): 12 | """Call fn with command line arguments. Used as a decorator. 13 | 14 | The main decorator marks the function that starts a program. For example, 15 | 16 | @main 17 | def my_run_function(): 18 | # function body 19 | 20 | Use this instead of the typical __name__ == "__main__" predicate. 21 | """ 22 | if inspect.stack()[1][0].f_locals['__name__'] == '__main__': 23 | args = sys.argv[1:] # Discard the script name from command line 24 | fn(*args) # Call the main function 25 | return fn 26 | 27 | _PREFIX = '' 28 | def trace(fn): 29 | """A decorator that prints a function's name, its arguments, and its return 30 | values each time the function is called. For example, 31 | 32 | @trace 33 | def compute_something(x, y): 34 | # function body 35 | """ 36 | @functools.wraps(fn) 37 | def wrapped(*args, **kwds): 38 | global _PREFIX 39 | reprs = [repr(e) for e in args] 40 | reprs += [repr(k) + '=' + repr(v) for k, v in kwds.items()] 41 | log('{0}({1})'.format(fn.__name__, ', '.join(reprs)) + ':') 42 | _PREFIX += ' ' 43 | try: 44 | result = fn(*args, **kwds) 45 | _PREFIX = _PREFIX[:-4] 46 | except Exception as e: 47 | log(fn.__name__ + ' exited via exception') 48 | _PREFIX = _PREFIX[:-4] 49 | raise 50 | # Here, print out the return value. 51 | log('{0}({1}) -> {2}'.format(fn.__name__, ', '.join(reprs), result)) 52 | return result 53 | return wrapped 54 | 55 | 56 | def log(message): 57 | """Print an indented message (used with trace).""" 58 | if type(message) is not str: 59 | message = str(message) 60 | print(_PREFIX + re.sub('\n', '\n' + _PREFIX, message)) 61 | 62 | 63 | def log_current_line(): 64 | """Print information about the current line of code.""" 65 | frame = inspect.stack()[1] 66 | log('Current line: File "{f[1]}", line {f[2]}, in {f[3]}'.format(f=frame)) 67 | 68 | 69 | def interact(msg=None): 70 | """Start an interactive interpreter session in the current environment. 71 | 72 | On Unix: 73 | -D exits the interactive session and returns to normal execution. 74 | In Windows: 75 | -Z exists the interactive session and returns to normal 76 | execution. 77 | """ 78 | # use exception trick to pick up the current frame 79 | try: 80 | raise None 81 | except: 82 | frame = sys.exc_info()[2].tb_frame.f_back 83 | 84 | # evaluate commands in current namespace 85 | namespace = frame.f_globals.copy() 86 | namespace.update(frame.f_locals) 87 | 88 | # exit on interrupt 89 | def handler(signum, frame): 90 | print() 91 | exit(0) 92 | signal.signal(signal.SIGINT, handler) 93 | 94 | if not msg: 95 | _, filename, line, _, _, _ = inspect.stack()[1] 96 | msg = 'Interacting at File "{0}", line {1} \n'.format(filename, line) 97 | msg += ' Unix: -D continues the program; \n' 98 | msg += ' Windows: -Z continues the program; \n' 99 | msg += ' exit() or -C exits the program' 100 | 101 | code.interact(msg, None, namespace) 102 | -------------------------------------------------------------------------------- /quiz/quiz1/quiz1.py: -------------------------------------------------------------------------------- 1 | # CS 61A Fall 2014 2 | # Name: 3 | # Login: 4 | 5 | 6 | def two_equal(a, b, c): 7 | """Return whether exactly two of the arguments are equal and the 8 | third is not. 9 | 10 | >>> two_equal(1, 2, 3) 11 | False 12 | >>> two_equal(1, 2, 1) 13 | True 14 | >>> two_equal(1, 1, 1) 15 | False 16 | >>> result = two_equal(5, -1, -1) # return, don't print 17 | >>> result 18 | True 19 | 20 | """ 21 | if a == b and b == c: 22 | return False 23 | if a == b or b == c or c == a: 24 | return True 25 | else: 26 | return False 27 | 28 | 29 | def same_hailstone(a, b): 30 | """Return whether a and b are both members of the same hailstone 31 | sequence. 32 | 33 | >>> same_hailstone(10, 16) # 10, 5, 16, 8, 4, 2, 1 34 | True 35 | >>> same_hailstone(16, 10) # order doesn't matter 36 | True 37 | >>> result = same_hailstone(3, 19) # return, don't print 38 | >>> result 39 | False 40 | 41 | """ 42 | def hailstone(n, match): 43 | if n == match: 44 | return True 45 | if n == 1: 46 | return False 47 | elif n % 2 == 0: 48 | return hailstone(n / 2, match) 49 | else: 50 | return hailstone(3 * n + 1, match) 51 | 52 | return hailstone(a, b) or hailstone(b, a) 53 | 54 | 55 | def near_golden(perimeter): 56 | """Return the integer height of a near-golden rectangle with PERIMETER. 57 | 58 | >>> near_golden(42) # 8 x 13 rectangle has perimeter 42 59 | 8 60 | >>> near_golden(68) # 13 x 21 rectangle has perimeter 68 61 | 13 62 | >>> result = near_golden(100) # return, don't print 63 | >>> result 64 | 19 65 | 66 | """ 67 | assert perimeter % 2 == 0, "Perimeter is not even!" 68 | 69 | def difference(h, w): 70 | return abs((h / w) - (w / h - 1)) 71 | 72 | h = 1 73 | ret, min_difference = h, difference(h, perimeter / 2 - h) 74 | while h < perimeter / 4: 75 | w = perimeter / 2 - h 76 | if difference(h, w) < min_difference: 77 | ret, min_difference = h, difference(h, w) 78 | h += 1 79 | 80 | return ret 81 | --------------------------------------------------------------------------------