├── README.md ├── degrees_test.py ├── generate_test.py ├── heredity_test.py ├── minesweeper_test.py ├── nim_test.py ├── pagerank_test.py ├── puzzle_test.py ├── shopping_test.py └── tictactoe_test.py /README.md: -------------------------------------------------------------------------------- 1 | # CS50's Introduction to Artificial Intelligence Test Scripts 2 | 3 | ## 🤷‍♂️ What's this? 🤷‍♀️ 4 | 5 | This repository contains Python scripts to automate tests for most of the [CS50’s Introduction to Artificial Intelligence with Python projects](https://github.com/jetkan-yk/cs50ai).
6 | 7 | It **does not** contain any project *solution*/*spoiler*, as per the course's [Academic Honesty policy](https://cs50.harvard.edu/ai/2020/honesty/). 8 | 9 | ## ⛔ Disclaimer 10 | 11 | This is a student-initiated project. Passing these test cases **does not** guarantee that you will receive a full grade from the official CS50 AI's teaching team. 12 | 13 | ## 📖 Table of Contents 14 | 15 | | Lecture | Concept | Project | Test Script | Description | 16 | | --------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------- | ------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | 17 | | [Search](https://cs50.harvard.edu/ai/2020/weeks/0/) | [Breadth First Search](https://cs50.harvard.edu/ai/2020/notes/0/#breadth-first-search) | [Degrees](https://cs50.harvard.edu/ai/2020/projects/0/degrees/) | [degrees_test.py](degrees_test.py) | Run test cases given by problem description and [this discussion](https://edstem.org/us/courses/176/discussion/226814?answer=546980) | 18 | | [Search](https://cs50.harvard.edu/ai/2020/weeks/0/) | [Minimax](https://cs50.harvard.edu/ai/2020/notes/0/#minimax) | [Tic-Tac-Toe](https://cs50.harvard.edu/ai/2020/projects/0/tictactoe/) | [tictactoe_test.py](tictactoe_test.py) | Let your AI play against itself for 10 rounds | 19 | | [Knowledge](https://cs50.harvard.edu/ai/2020/weeks/1/) | [Model Checking](https://cs50.harvard.edu/ai/2020/notes/1/#inference) | [Knights](https://cs50.harvard.edu/ai/2020/projects/1/knights/) | [puzzle_test.py](puzzle_test.py) | Check the correctness of the 4 puzzle results | 20 | | [Knowledge](https://cs50.harvard.edu/ai/2020/weeks/1/) | [Knowledge Engineering](https://cs50.harvard.edu/ai/2020/notes/1/#knowledge-engineering) | [Minesweeper](https://cs50.harvard.edu/ai/2020/projects/1/minesweeper/) | [minesweeper_test.py](minesweeper_test.py) | Check if your AI has ≈90% win rate over 1000 simulations | 21 | | [Uncertainty](https://cs50.harvard.edu/ai/2020/weeks/2/) | [Bayesian Networks](https://cs50.harvard.edu/ai/2020/notes/2/#bayesian-networks) | [Heredity](https://cs50.harvard.edu/ai/2020/projects/2/heredity/) | [heredity_test.py](heredity_test.py) | Run test cases given by problem description and [this discussion](https://edstem.org/us/courses/176/discussion/488564?answer=1263763) | 22 | | [Uncertainty](https://cs50.harvard.edu/ai/2020/weeks/2/) | [Markov Models](https://cs50.harvard.edu/ai/2020/notes/2/#markov-models) | [PageRank](https://cs50.harvard.edu/ai/2020/projects/2/pagerank/) | [pagerank_test.py](pagerank_test.py) | Compare the output of the 2 implemented functions | 23 | | [Optimization](https://cs50.harvard.edu/ai/2020/weeks/3/) | [Constraint Satisfaction](https://cs50.harvard.edu/ai/2020/notes/3/#constraint-satisfaction) | [Crossword](https://cs50.harvard.edu/ai/2020/projects/3/crossword/) | [generate_test.py](generate_test.py) | Generate crosswords using all 9 different structure + words combination and a special test case from [this discussion](https://edstem.org/us/courses/176/discussion/103609?answer=280445) | 24 | | [Learning](https://cs50.harvard.edu/ai/2020/weeks/4/) | [Nearest-Neighbor Classification](https://cs50.harvard.edu/ai/2020/notes/4/#nearest-neighbor-classification) | [Shopping](https://cs50.harvard.edu/ai/2020/projects/4/shopping/) | [shopping_test.py](shopping_test.py) | Check the information is parsed correctly and result is within a reasonable range | 25 | | [Learning](https://cs50.harvard.edu/ai/2020/weeks/4/) | [Reinforcement Learning](https://cs50.harvard.edu/ai/2020/notes/4/#reinforcement-learning) | [Nim](https://cs50.harvard.edu/ai/2020/projects/4/nim/) | [nim_test.py](nim_test.py) | Check if the AI which moves second has a 100% win rate | 26 | 27 | ## 🛠️ How to Run Tests 28 | 29 | ### Guide 30 | 31 | 1. Make sure you have [Python3](https://www.python.org/downloads/) installed in your machine. Anything above `Python 3.4+` should work. 32 | 2. Install `pytest` by running `pip install pytest` in a terminal. More information about `pip` [here](https://realpython.com/what-is-pip/). 33 | 3. Make a copy of the test file and paste it in the **same folder** as the project that you want to test. 34 | > For example, if you want to test your code for `degrees.py`, make a copy of `degrees_test.py` in the **same folder** as your `degrees.py` and other files that came along with the project, like `util.py`, `large/` and `small/`. 35 | 4. Navigate to the project folder and run `pytest` or `pytest _test.py` in a terminal. 36 | > For example, navigate to `degrees/` and run `pytest` or `pytest degrees_test.py`. 37 | 38 | ### Example 39 | 40 | ![example](https://user-images.githubusercontent.com/36299141/128583985-a56b4371-a092-430a-8c08-4483137367d6.png) 41 | 42 | ## 🚩 Useful pytest Flags 43 | 44 | - Run `pytest -s` to show print statements in the console 45 | - Run `pytest -vv` for verbose mode 46 | - Combine both flags `pytest -s -vv` for extra verbose mode 47 | - Run `pytest --durations=n` to see the `n` slowest execution time 48 | - Install `pytest-repeat` with `pip` and then run `pytest --count n` to repeat the test for *n* times 49 | 50 | ## 💻 My Setup 51 | 52 | Each test should take less than 30 seconds, depending on Python's I/O and your code efficiency. 53 | 54 | - Windows 10 Home Build 19042 55 | - Intel(R) Core(TM) i7-7700HQ CPU @ 2.80GHz 56 | - Python 3.9.5 64-bit 57 | - Visual Studio Code w/Pylance (latest release) 58 | 59 | ## 🏆 Acknowledgement 60 | 61 | Special thanks to these fellow CS50AI classmates who contributed some of the test cases on the [Ed discussion site](https://edstem.org/us/courses/176/discussion/)! 62 | 63 | - Ken Walker 64 | - Naveena A S 65 | - Ricardo L 66 | -------------------------------------------------------------------------------- /degrees_test.py: -------------------------------------------------------------------------------- 1 | """ 2 | Acceptance tests for degrees.py 3 | 4 | Make sure that this file is in the same directory as degrees.py! 5 | 6 | 'Why do we fall sir? So that we can learn to pick ourselves up.' 7 | - Batman Begins (2005) 8 | """ 9 | from degrees import load_data, person_id_for_name, shortest_path 10 | 11 | load_data("large") 12 | 13 | # Most test cases provided by Ken Walker. Thank you! 14 | # source: https://edstem.org/us/courses/176/discussion/226814?answer=546980 15 | 16 | 17 | def test0(): 18 | source = person_id_for_name("Jennifer Lawrence") 19 | target = person_id_for_name("Tom Hanks") 20 | assert len(shortest_path(source, target)) == 2 21 | 22 | 23 | def test1(): 24 | source = person_id_for_name("Emma Watson") 25 | target = person_id_for_name("Jennifer Lawrence") 26 | assert len(shortest_path(source, target)) == 3 27 | 28 | 29 | def test_zero_degree(): 30 | source = person_id_for_name("Tim Zinnemann") 31 | target = person_id_for_name("Lahcen Zinoun") 32 | assert shortest_path(source, target) is None 33 | 34 | 35 | def test_one_degree(): 36 | source = person_id_for_name("Tom Cruise") 37 | target = person_id_for_name("Lea Thompson") 38 | assert len(shortest_path(source, target)) == 1 39 | 40 | 41 | def test_two_degree(): 42 | source = person_id_for_name("Tom Cruise") 43 | target = person_id_for_name("Tom Hanks") 44 | assert len(shortest_path(source, target)) == 2 45 | 46 | 47 | def test_three_degree(): 48 | source = person_id_for_name("Emma Watson") 49 | target = person_id_for_name("Jennifer Lawrence") 50 | assert len(shortest_path(source, target)) == 3 51 | 52 | 53 | def test_four_degree(): 54 | source = person_id_for_name("Fred Astaire") 55 | target = person_id_for_name("Mohamed Zinet") 56 | assert len(shortest_path(source, target)) == 4 57 | 58 | 59 | def test_six_degree(): 60 | source = person_id_for_name("Juliane Banse") 61 | target = person_id_for_name("Bruce Davison") 62 | assert len(shortest_path(source, target)) == 6 63 | 64 | 65 | def test_eight_degree(): 66 | source = person_id_for_name("Juliane Banse") 67 | target = person_id_for_name("Julian Acosta") 68 | assert len(shortest_path(source, target)) == 8 69 | -------------------------------------------------------------------------------- /generate_test.py: -------------------------------------------------------------------------------- 1 | """ 2 | Acceptance tests for generate.py 3 | 4 | Make sure that this file is in the same directory as generate.py! 5 | 6 | 'Why do we fall sir? So that we can learn to pick ourselves up.' 7 | - Batman Begins (2005) 8 | """ 9 | import pytest 10 | 11 | from generate import Crossword, CrosswordCreator 12 | 13 | invalid_crossword = [(1, 0), (2, 0)] # These 2 combinations will not work 14 | 15 | # Pro tip: run `pytest --durations=10` to see the 10 slowest executions 16 | 17 | 18 | @pytest.mark.parametrize("execution", range(10)) 19 | @pytest.mark.parametrize("i", range(3)) 20 | @pytest.mark.parametrize("j", range(3)) 21 | def test(execution, i, j): 22 | crossword = generate_crossword(i, j) 23 | creator = CrosswordCreator(crossword) 24 | assignment = creator.solve() 25 | generate_output(creator, assignment, f"data/output{i}-{j}.png") 26 | 27 | if (i, j) in invalid_crossword: 28 | assert assignment is None 29 | else: 30 | assert len(assignment) == len(crossword.variables) 31 | 32 | 33 | # Special test case written by Ricardo L. Thank you! 34 | # If the backtracking search and inference algorithm are implemented correctly, 35 | # this crossword should be solved in 5 seconds. 36 | 37 | # source: https://edstem.org/us/courses/176/discussion/103609?answer=280445 38 | 39 | 40 | def test_optimization(): 41 | write_structure3() 42 | crossword = Crossword("data/structure3.txt", "data/words2.txt") 43 | creator = CrosswordCreator(crossword) 44 | assignment = creator.solve() 45 | generate_output(creator, assignment, f"data/output3-2.png") 46 | 47 | assert len(assignment) == len(crossword.variables) 48 | 49 | 50 | # helper function 51 | 52 | 53 | def generate_output(creator, assignment, file): 54 | if assignment is None: 55 | print("No solution") 56 | else: 57 | creator.print(assignment) 58 | creator.save(assignment, file) 59 | 60 | 61 | def generate_crossword(i, j): 62 | structure = f"data/structure{i}.txt" 63 | words = f"data/words{j}.txt" 64 | print(f"\nTesting structure{i} words{j}") 65 | return Crossword(structure, words) 66 | 67 | 68 | def write_structure3(): 69 | structure = """\ 70 | ######################### 71 | ######################### 72 | ######################### 73 | #_____________##_______## 74 | #_##_##_#_#_#_###_#_#_### 75 | #_##_##_#_#_#_##_______## 76 | #_____________###_#_#_### 77 | #_##_##_##_##_##_______## 78 | #_##_##_##_##_###_#_#_### 79 | #_____________##_______## 80 | #####__#####_###_######## 81 | #####__#####_____######## 82 | ####_##_######_########## 83 | ####____######_########## 84 | ####_##_######_########## 85 | ####_##_######_########## 86 | ####_##_####_____######## 87 | ######################### 88 | ######################### 89 | #########################""" 90 | f = open("data/structure3.txt", "w") 91 | f.write(structure) 92 | f.close 93 | -------------------------------------------------------------------------------- /heredity_test.py: -------------------------------------------------------------------------------- 1 | """ 2 | Acceptance tests for heredity.py 3 | 4 | Make sure that this file is in the same directory as heredity.py! 5 | 6 | 'Why do we fall sir? So that we can learn to pick ourselves up.' 7 | - Batman Begins (2005) 8 | """ 9 | from heredity import joint_probability, load_data, normalize, powerset, update 10 | 11 | PRECISION = 4 12 | 13 | 14 | # Test cases provided by Naveena A S. Thank you! 15 | # source: https://edstem.org/us/courses/176/discussion/488564?answer=1263763 16 | 17 | 18 | def test_family0(): 19 | expected = { 20 | "Harry": { 21 | "gene": {2: 0.0092, 1: 0.4557, 0: 0.5351}, 22 | "trait": {True: 0.2665, False: 0.7335}, 23 | }, 24 | "James": { 25 | "gene": {2: 0.1976, 1: 0.5106, 0: 0.2918}, 26 | "trait": {True: 1.0000, False: 0.0000}, 27 | }, 28 | "Lily": { 29 | "gene": {2: 0.0036, 1: 0.0136, 0: 0.9827}, 30 | "trait": {True: 0.0000, False: 1.0000}, 31 | }, 32 | } 33 | 34 | predicted = predict_family(0) 35 | 36 | return compare(predicted, expected) 37 | 38 | 39 | def test_family1(): 40 | expected = { 41 | "Arthur": { 42 | "gene": {2: 0.0329, 1: 0.1035, 0: 0.8636}, 43 | "trait": {True: 0.0000, False: 1.0000}, 44 | }, 45 | "Charlie": { 46 | "gene": {2: 0.0018, 1: 0.1331, 0: 0.8651}, 47 | "trait": {True: 0.0000, False: 1.0000}, 48 | }, 49 | "Fred": { 50 | "gene": {2: 0.0065, 1: 0.6486, 0: 0.3449}, 51 | "trait": {True: 1.0000, False: 0.0000}, 52 | }, 53 | "Ginny": { 54 | "gene": {2: 0.0027, 1: 0.1805, 0: 0.8168}, 55 | "trait": {True: 0.1110, False: 0.8890}, 56 | }, 57 | "Molly": { 58 | "gene": {2: 0.0329, 1: 0.1035, 0: 0.8636}, 59 | "trait": {True: 0.0000, False: 1.0000}, 60 | }, 61 | "Ron": { 62 | "gene": {2: 0.0027, 1: 0.1805, 0: 0.8168}, 63 | "trait": {True: 0.1110, False: 0.8890}, 64 | }, 65 | } 66 | 67 | predicted = predict_family(1) 68 | 69 | return compare(predicted, expected) 70 | 71 | 72 | def test_family2(): 73 | expected = { 74 | "Arthur": { 75 | "gene": {2: 0.0147, 1: 0.0344, 0: 0.9509}, 76 | "trait": {True: 0.0000, False: 1.0000}, 77 | }, 78 | "Hermione": { 79 | "gene": {2: 0.0608, 1: 0.1203, 0: 0.8189}, 80 | "trait": {True: 0.0000, False: 1.0000}, 81 | }, 82 | "Molly": { 83 | "gene": {2: 0.0404, 1: 0.0744, 0: 0.8852}, 84 | "trait": {True: 0.0768, False: 0.9232}, 85 | }, 86 | "Ron": { 87 | "gene": {2: 0.0043, 1: 0.2149, 0: 0.7808}, 88 | "trait": {True: 0.0000, False: 1.0000}, 89 | }, 90 | "Rose": { 91 | "gene": {2: 0.0088, 1: 0.7022, 0: 0.2890}, 92 | "trait": {True: 1.0000, False: 0.0000}, 93 | }, 94 | } 95 | 96 | predicted = predict_family(2) 97 | 98 | return compare(predicted, expected) 99 | 100 | 101 | # Helper functions 102 | 103 | 104 | def predict_family(n): 105 | people = load_data(f"data/family{n}.csv") 106 | 107 | probabilities = { 108 | person: {"gene": {2: 0, 1: 0, 0: 0}, "trait": {True: 0, False: 0}} 109 | for person in people 110 | } 111 | 112 | names = set(people) 113 | for have_trait in powerset(names): 114 | fails_evidence = any( 115 | ( 116 | people[person]["trait"] is not None 117 | and people[person]["trait"] != (person in have_trait) 118 | ) 119 | for person in names 120 | ) 121 | if fails_evidence: 122 | continue 123 | 124 | for one_gene in powerset(names): 125 | for two_genes in powerset(names - one_gene): 126 | 127 | p = joint_probability(people, one_gene, two_genes, have_trait) 128 | update(probabilities, one_gene, two_genes, have_trait, p) 129 | 130 | normalize(probabilities) 131 | 132 | return probabilities 133 | 134 | 135 | def compare(predicted, expected): 136 | for kPerson, vPerson in predicted.items(): 137 | for kVariable, vVariable in vPerson.items(): 138 | for kOutcome, vOutcome in vVariable.items(): 139 | assert ( 140 | round(vOutcome, PRECISION) == expected[kPerson][kVariable][kOutcome] 141 | ) 142 | -------------------------------------------------------------------------------- /minesweeper_test.py: -------------------------------------------------------------------------------- 1 | """ 2 | Acceptance tests for minesweeper.py 3 | 4 | Make sure that this file is in the same directory as minesweeper.py! 5 | 6 | 'Why do we fall sir? So that we can learn to pick ourselves up.' 7 | - Batman Begins (2005) 8 | """ 9 | import pytest as pt 10 | 11 | from minesweeper import Minesweeper, MinesweeperAI 12 | 13 | # Feel free to change these numbers for different expectation 14 | HEIGHT = 12 15 | WIDTH = 12 16 | MINES = 8 17 | expectedWinPercent = 85 18 | 19 | # Run the AI test for 10 times. Each test consists of letting the AI play 20 | # minesweeper for 1000 rounds. If the inference function is implemented correctly, 21 | # each test should have a very high win rate (≈90%) in most of the iterations. 22 | # Use `pytest -s` to see win rate. 23 | 24 | 25 | @pt.mark.parametrize("execution_number", range(10)) 26 | def test(execution_number): 27 | return play1000() 28 | 29 | 30 | # Helper functions 31 | 32 | 33 | def play1000(): 34 | totalWon = 0 35 | for _ in range(1000): 36 | totalWon += play() 37 | 38 | print(f"\nWin rate:{totalWon // 10}%") 39 | 40 | assert totalWon >= expectedWinPercent * 10 41 | 42 | 43 | def play(): 44 | game = Minesweeper(height=HEIGHT, width=WIDTH, mines=MINES) 45 | ai = MinesweeperAI(height=HEIGHT, width=WIDTH) 46 | 47 | won = lost = False 48 | while not (won or lost): 49 | # AI choose a move 50 | move = ai.make_safe_move() or ai.make_random_move() 51 | if move is None: 52 | won = True 53 | break 54 | 55 | # Make move and update AI 56 | if game.is_mine(move): 57 | lost = True 58 | else: 59 | nearby = game.nearby_mines(move) 60 | ai.add_knowledge(move, nearby) 61 | 62 | return won 63 | -------------------------------------------------------------------------------- /nim_test.py: -------------------------------------------------------------------------------- 1 | """ 2 | Acceptance tests for nim.py 3 | 4 | Make sure that this file is in the same directory as nim.py! 5 | 6 | 'Why do we fall sir? So that we can learn to pick ourselves up.' 7 | - Batman Begins (2005) 8 | """ 9 | import pytest 10 | 11 | from nim import Nim, train 12 | 13 | # Let the AI play against itself for 10 times. If the q function 14 | # is implemented correctly, the AI which moved second should always win. 15 | 16 | ai = train(10000) 17 | 18 | 19 | @pytest.mark.parametrize("execution", range(1000)) 20 | def test(execution): 21 | assert play_ai_vs_ai() == 1 22 | 23 | 24 | # helper function 25 | 26 | 27 | def play_ai_vs_ai(): 28 | game = Nim() 29 | while True: 30 | game.move(ai.choose_action(game.piles, epsilon=False)) 31 | 32 | if game.winner is not None: 33 | return game.winner 34 | -------------------------------------------------------------------------------- /pagerank_test.py: -------------------------------------------------------------------------------- 1 | """ 2 | Acceptance tests for pagerank.py 3 | 4 | Make sure that this file is in the same directory as pagerank.py! 5 | 6 | 'Why do we fall sir? So that we can learn to pick ourselves up.' 7 | - Batman Begins (2005) 8 | """ 9 | import random as rd 10 | 11 | import pytest as pt 12 | 13 | from pagerank import DAMPING, crawl, iterate_pagerank, sample_pagerank 14 | 15 | TOLERANCE = 1e-3 # Error tolerance = ±0.001 when comparing sample and iterate results 16 | SAMPLES = 10 ** 6 # More samples => better result 17 | 18 | corpus0 = crawl("corpus0") 19 | 20 | 21 | def test_crawl0(): 22 | assert len(corpus0) == 4 23 | 24 | 25 | def test_iterate0(): 26 | expected = {"1.html": 0.2202, "2.html": 0.4289, "3.html": 0.2202, "4.html": 0.1307} 27 | iterate = iterate_pagerank(corpus0, damping_factor=DAMPING) 28 | return compare(iterate, expected) 29 | 30 | 31 | @pt.mark.parametrize("execution_number", range(10)) 32 | def test_sample_vs_iterate(execution_number): 33 | return run_sample_vs_iterate() 34 | 35 | 36 | # helper function 37 | 38 | 39 | def checksum(probability): 40 | assert sum(probability.values()) == pt.approx(1, abs=TOLERANCE) 41 | 42 | 43 | def run_sample_vs_iterate(): 44 | corpus, _ = generate_random_data() 45 | 46 | sample = sample_pagerank(corpus, damping_factor=DAMPING, n=SAMPLES) 47 | iterate = iterate_pagerank(corpus, damping_factor=DAMPING) 48 | 49 | checksum(sample) 50 | checksum(iterate) 51 | 52 | return compare(sample, iterate) 53 | 54 | 55 | def compare(prob1, prob2): 56 | for page in prob1.keys(): 57 | assert prob1[page] == pt.approx(prob2[page], abs=TOLERANCE) 58 | 59 | 60 | def generate_random_data(): 61 | links = [f"{i}.html" for i in range(rd.randint(1, 10))] 62 | page = rd.choice(links) 63 | corpus = { 64 | link: set(rd.choices(links, k=rd.randint(0, len(links)))) - set([link]) 65 | for link in links 66 | } 67 | return corpus, page 68 | -------------------------------------------------------------------------------- /puzzle_test.py: -------------------------------------------------------------------------------- 1 | """ 2 | Acceptance tests for puzzle.py 3 | 4 | Make sure that this file is in the same directory as puzzle.py! 5 | 6 | 'Why do we fall sir? So that we can learn to pick ourselves up.' 7 | - Batman Begins (2005) 8 | """ 9 | from puzzle import * 10 | 11 | 12 | def test_puzzle0(): 13 | # AKnave 14 | assert model_check(knowledge0, AKnave) is True 15 | 16 | assert model_check(knowledge0, AKnight) is False 17 | assert model_check(knowledge0, BKnight) is False 18 | assert model_check(knowledge0, BKnave) is False 19 | assert model_check(knowledge0, CKnight) is False 20 | assert model_check(knowledge0, CKnave) is False 21 | 22 | 23 | def test_puzzle1(): 24 | # AKnave, BKnight 25 | assert model_check(knowledge1, AKnave) is True 26 | assert model_check(knowledge1, BKnight) is True 27 | 28 | assert model_check(knowledge1, AKnight) is False 29 | assert model_check(knowledge1, BKnave) is False 30 | assert model_check(knowledge1, CKnight) is False 31 | assert model_check(knowledge1, CKnave) is False 32 | 33 | 34 | def test_puzzle2(): 35 | # AKnave, BKnight 36 | assert model_check(knowledge2, AKnave) is True 37 | assert model_check(knowledge2, BKnight) is True 38 | 39 | assert model_check(knowledge2, AKnight) is False 40 | assert model_check(knowledge2, BKnave) is False 41 | assert model_check(knowledge2, CKnight) is False 42 | assert model_check(knowledge2, CKnave) is False 43 | 44 | 45 | def test_puzzle3(): 46 | # AKnight, BKnave, CKnight 47 | assert model_check(knowledge3, AKnight) is True 48 | assert model_check(knowledge3, BKnave) is True 49 | assert model_check(knowledge3, CKnight) is True 50 | 51 | assert model_check(knowledge3, AKnave) is False 52 | assert model_check(knowledge3, BKnight) is False 53 | assert model_check(knowledge3, CKnave) is False 54 | -------------------------------------------------------------------------------- /shopping_test.py: -------------------------------------------------------------------------------- 1 | """ 2 | Acceptance tests for shopping.py 3 | 4 | Make sure that this file is in the same directory as shopping.py! 5 | 6 | 'Why do we fall sir? So that we can learn to pick ourselves up.' 7 | - Batman Begins (2005) 8 | """ 9 | 10 | import csv 11 | 12 | import numpy as np 13 | 14 | from shopping import TEST_SIZE, evaluate, load_data, train_model, train_test_split 15 | 16 | FILENAME = "shopping.csv" 17 | 18 | expected0 = [0, 0.0, 0, 0.0, 1, 0.0, 0.2, 0.2, 0.0, 0.0, 1, 1, 1, 1, 1, 1, 0] 19 | 20 | 21 | def test(): 22 | with open(FILENAME) as f: 23 | reader = csv.reader(f) 24 | next(reader) 25 | 26 | N = p = f = 0 27 | for row in reader: 28 | N += 1 29 | if row[-1] == "TRUE": 30 | p += 1 31 | elif row[-1] == "FALSE": 32 | f += 1 33 | else: 34 | raise KeyError("DO NOT EDIT shopping.csv FILE!!!") 35 | 36 | evidence, labels = load_data(FILENAME) 37 | assert len(evidence) == len(labels) == N 38 | if type(evidence).__module__ == np.__name__: 39 | assert (evidence[0] == expected0).all() 40 | else: 41 | assert evidence[0] == expected0 42 | assert labels[0] == 0 43 | 44 | X_train, X_test, y_train, y_test = train_test_split( 45 | evidence, labels, test_size=TEST_SIZE 46 | ) 47 | model = train_model(X_train, y_train) 48 | predictions = model.predict(X_test) 49 | sensitivity, specificity = evaluate(y_test, predictions) 50 | 51 | assert y_test.size == N * TEST_SIZE 52 | assert 0.35 <= sensitivity <= 0.45 53 | assert 0.85 <= specificity <= 0.95 54 | -------------------------------------------------------------------------------- /tictactoe_test.py: -------------------------------------------------------------------------------- 1 | """ 2 | Acceptance tests for tictactoe.py 3 | 4 | Make sure that this file is in the same directory as tictactoe.py! 5 | 6 | 'Why do we fall sir? So that we can learn to pick ourselves up.' 7 | - Batman Begins (2005) 8 | """ 9 | import pytest as pt 10 | 11 | import tictactoe as ttt 12 | 13 | # Let the AI play against itself for 10 times. If the minimax function 14 | # is implemented correctly, every round should resolve in a tie. 15 | 16 | 17 | @pt.mark.parametrize("execution_number", range(10)) 18 | def test(execution_number): 19 | return play_ai_vs_ai() 20 | 21 | 22 | # Helper function 23 | 24 | 25 | def play_ai_vs_ai(): 26 | board = ttt.initial_state() 27 | game_over = False 28 | 29 | while not game_over: 30 | move = ttt.minimax(board) 31 | board = ttt.result(board, move) 32 | game_over = ttt.terminal(board) 33 | 34 | assert ttt.winner(board) is None 35 | --------------------------------------------------------------------------------