|
| 1 | +import os |
| 2 | +import re |
| 3 | +import check50 |
| 4 | +import check50.py |
| 5 | + |
| 6 | +BRACKET2 = [ |
| 7 | + {"team": "Uruguay", "rating": 976}, |
| 8 | + {"team": "Portugal", "rating": 1306}, |
| 9 | +] |
| 10 | +BRACKET4 = [ |
| 11 | + {"team": "Uruguay", "rating": 976}, |
| 12 | + {"team": "Portugal", "rating": 1306}, |
| 13 | + {"team": "France", "rating": 1166}, |
| 14 | + {"team": "Argentina", "rating": 1254}, |
| 15 | +] |
| 16 | +BRACKET8 = [ |
| 17 | + {"team": "Uruguay", "rating": 976}, |
| 18 | + {"team": "Portugal", "rating": 1306}, |
| 19 | + {"team": "France", "rating": 1166}, |
| 20 | + {"team": "Argentina", "rating": 1254}, |
| 21 | + {"team": "Brazil", "rating": 1384}, |
| 22 | + {"team": "Mexico", "rating": 1008}, |
| 23 | + {"team": "Belgium", "rating": 1346}, |
| 24 | + {"team": "Japan", "rating": 528}, |
| 25 | +] |
| 26 | +BRACKET16 = [ |
| 27 | + {"team": "Uruguay", "rating": 976}, |
| 28 | + {"team": "Portugal", "rating": 1306}, |
| 29 | + {"team": "France", "rating": 1166}, |
| 30 | + {"team": "Argentina", "rating": 1254}, |
| 31 | + {"team": "Brazil", "rating": 1384}, |
| 32 | + {"team": "Mexico", "rating": 1008}, |
| 33 | + {"team": "Belgium", "rating": 1346}, |
| 34 | + {"team": "Japan", "rating": 528}, |
| 35 | + {"team": "Spain", "rating": 1162}, |
| 36 | + {"team": "Russia", "rating": 493}, |
| 37 | + {"team": "Croatia", "rating": 975}, |
| 38 | + {"team": "Denmark", "rating": 1054}, |
| 39 | + {"team": "Sweden", "rating": 889}, |
| 40 | + {"team": "Switzerland", "rating": 1179}, |
| 41 | + {"team": "Colombia", "rating": 989}, |
| 42 | + {"team": "England", "rating": 1040}, |
| 43 | +] |
| 44 | +QUESTIONS = [ |
| 45 | + "Which predictions, if any, proved incorrect as you increased the number of simulations?", |
| 46 | + 'Suppose you\'re charged a fee for each second of compute time your program uses.\nAfter how many simulations would you call the predictions "good enough"?', |
| 47 | +] |
| 48 | +SIMULATION_RUNS = [ |
| 49 | + "10", |
| 50 | + "100", |
| 51 | + "1000", |
| 52 | + "10000", |
| 53 | + "100000", |
| 54 | + "1000000", |
| 55 | +] |
| 56 | + |
| 57 | + |
| 58 | +@check50.check() |
| 59 | +def exists(): |
| 60 | + """tournament.py exists""" |
| 61 | + check50.exists("tournament.py", "answers.txt") |
| 62 | + check50.include("2018m.csv", "2019w.csv") |
| 63 | + |
| 64 | + |
| 65 | +@check50.check(exists) |
| 66 | +def imports(): |
| 67 | + """tournament.py imports""" |
| 68 | + check50.py.import_("tournament.py") |
| 69 | + |
| 70 | + |
| 71 | +@check50.check(imports) |
| 72 | +def sim_tournament_2(): |
| 73 | + """simulate_tournament handles a bracket of size 2""" |
| 74 | + check_tournament(BRACKET2) |
| 75 | + |
| 76 | + |
| 77 | +@check50.check(imports) |
| 78 | +def sim_tournament_4(): |
| 79 | + """simulate_tournament handles a bracket of size 4""" |
| 80 | + check_tournament(BRACKET4) |
| 81 | + |
| 82 | + |
| 83 | +@check50.check(imports) |
| 84 | +def sim_tournament_8(): |
| 85 | + """simulate_tournament handles a bracket of size 8""" |
| 86 | + check_tournament(BRACKET8) |
| 87 | + |
| 88 | + |
| 89 | +@check50.check(imports) |
| 90 | +def sim_tournament_16(): |
| 91 | + """simulate_tournament handles a bracket of size 16""" |
| 92 | + check_tournament(BRACKET16) |
| 93 | + |
| 94 | + |
| 95 | +@check50.check(imports) |
| 96 | +def counts(): |
| 97 | + """correctly keeps track of wins""" |
| 98 | + actual = check50.run("python3 tournament.py 2018m.csv").stdout() |
| 99 | + percents = re.findall("[0-9]*\.[0-9]", actual) |
| 100 | + percents = [float(x) for x in percents] |
| 101 | + if sum(percents) < 99 or sum(percents) > 101: |
| 102 | + raise check50.Failure("fails to keep track of wins") |
| 103 | + |
| 104 | + |
| 105 | +@check50.check(imports) |
| 106 | +def correct_teams1(): |
| 107 | + """correctly reports team information for Men's World Cup""" |
| 108 | + actual = check50.run("python3 tournament.py 2018m.csv").stdout() |
| 109 | + expected = ["Belgium", "Brazil", "Portugal", "Spain"] |
| 110 | + not_expected = ["Germany"] |
| 111 | + for team in expected: |
| 112 | + if team not in actual: |
| 113 | + raise check50.Failure(f"did not find team {team}") |
| 114 | + for team in not_expected: |
| 115 | + if team in actual: |
| 116 | + raise check50.Failure(f"incorrectly found team {team}") |
| 117 | + |
| 118 | + |
| 119 | +@check50.check(imports) |
| 120 | +def correct_teams2(): |
| 121 | + """correctly reports team information for Women's World Cup""" |
| 122 | + actual = check50.run("python3 tournament.py 2019w.csv").stdout() |
| 123 | + expected = ["Germany", "United States", "England"] |
| 124 | + not_expected = ["Belgium"] |
| 125 | + for team in expected: |
| 126 | + if team not in actual: |
| 127 | + raise check50.Failure(f"did not find team {team}") |
| 128 | + for team in not_expected: |
| 129 | + if team in actual: |
| 130 | + raise check50.Failure(f"incorrectly found team {team}") |
| 131 | + |
| 132 | + percents = re.findall("[0-9]*\.[0-9]", actual) |
| 133 | + percents = [float(x) for x in percents] |
| 134 | + if sum(percents) < 99 or sum(percents) > 101: |
| 135 | + raise check50.Failure("fails to keep track of wins") |
| 136 | + |
| 137 | + |
| 138 | +@check50.check(imports) |
| 139 | +def check_answers(): |
| 140 | + """answers.txt is complete""" |
| 141 | + with open("answers.txt") as f: |
| 142 | + contents = f.read() |
| 143 | + |
| 144 | + # Check timings |
| 145 | + for runs in SIMULATION_RUNS: |
| 146 | + match = re.search( |
| 147 | + rf"(?i){re.escape(runs)} simulations:\s*(\d+m\d+\.\d\d\ds)(?<!0m0\.000s)", |
| 148 | + contents, |
| 149 | + ) |
| 150 | + if not match: |
| 151 | + raise check50.Failure( |
| 152 | + "answers.txt does not include timings for each number of simulation runs", |
| 153 | + help="Did you put all of your answers in 0m0.000s format?", |
| 154 | + ) |
| 155 | + |
| 156 | + # Check free response |
| 157 | + num_questions = len(QUESTIONS) |
| 158 | + for i, question in enumerate(QUESTIONS): |
| 159 | + |
| 160 | + # Search for question, with at least 3 words afterwards |
| 161 | + if i + 1 < num_questions: |
| 162 | + |
| 163 | + # Regex includes question being asked, response, and following question |
| 164 | + regex = ( |
| 165 | + rf"(?i){re.escape(question)}" |
| 166 | + + r":\s*(\S+\s+){3,}" |
| 167 | + + rf"{re.escape(QUESTIONS[i + 1])}" |
| 168 | + ) |
| 169 | + else: |
| 170 | + |
| 171 | + # Last regex includes question being asked and response |
| 172 | + regex = rf"(?i){re.escape(question)}" + r":\s*(\S+\s+){3,}" |
| 173 | + |
| 174 | + match = re.search(regex, contents) |
| 175 | + if not match: |
| 176 | + raise check50.Failure( |
| 177 | + "answers.txt does not include answers to free response questions", |
| 178 | + help="Did you write a sufficient response to each question?", |
| 179 | + ) |
| 180 | + |
| 181 | + |
| 182 | +# Helpers |
| 183 | + |
| 184 | + |
| 185 | +def check_round(*args): |
| 186 | + tournament = check50.py.import_("tournament.py") |
| 187 | + actual = tournament.simulate_round(args[0]) |
| 188 | + |
| 189 | + for i in range(len(actual)): |
| 190 | + expected = [args[0][2 * i], args[0][2 * i + 1]] |
| 191 | + if not (actual[i] in expected): |
| 192 | + raise check50.Failure( |
| 193 | + "simulate_round fails to determine winners in a round" |
| 194 | + ) |
| 195 | + |
| 196 | + |
| 197 | +def check_tournament(*args): |
| 198 | + tournament = check50.py.import_("tournament.py") |
| 199 | + actual = tournament.simulate_tournament(args[0]) |
| 200 | + teams = [x["team"] for x in args[0]] |
| 201 | + |
| 202 | + if not actual in teams: |
| 203 | + raise check50.Failure( |
| 204 | + "simulate_tournament fails to return the name of 1 winning team" |
| 205 | + ) |
0 commit comments