diff --git a/docs/source/src.ch07.rst b/docs/source/src.ch07.rst index 6885a5b..3801c32 100644 --- a/docs/source/src.ch07.rst +++ b/docs/source/src.ch07.rst @@ -12,6 +12,14 @@ src.ch07.c1\_breed\_rats module :undoc-members: :show-inheritance: +src.ch07.c2\_safe\_cracker module +--------------------------------- + +.. automodule:: src.ch07.c2_safe_cracker + :members: + :undoc-members: + :show-inheritance: + Module contents --------------- diff --git a/src/ch07/c2_safe_cracker.py b/src/ch07/c2_safe_cracker.py new file mode 100644 index 0000000..d1ec9a3 --- /dev/null +++ b/src/ch07/c2_safe_cracker.py @@ -0,0 +1,99 @@ +"""Use hill-climbing algorithm to solve a lock combination. + +Solve a lock combination by randomly changing a tumbler's values one +by one and noting whether the safe had a response. If so, lock the +tumbler at that value and continue randomly changing tumbler values. + +Previously, a locked tumbler can still be changed, but the safe wouldn't +respond, so the change would be discarded. This improves upon the algorithm by +removing the locked tumbler from the pool of tumblers to randomly change. + +""" +import time +import random + + +def compare(combo: list, attempt: list) -> int: + """Compare items in two lists and count number of matches. + + Compare each tumbler in **combo** with **attempt** and return + the number of matches. + + Args: + combo (list): Integers of safe combination. + attempt (list): Integers of guessed safe combination. + + Returns: + Number of tumbler matches between **combo** and **attempt**. + + """ + return sum(1 for i, j in zip(combo, attempt) if i == j) + + +def crack_safe(combo: str) -> tuple: + """Crack a safe combination with a hill-climbing algorithm. + + Solve a lock combination by randomly changing a tumbler's values one + by one and noting whether the safe had a response. If so, lock the + tumbler at that value, remove it from the pool of tumblers, and + continue randomly changing tumbler values. + + Args: + combo (str): String of numbers representing combination of safe. + + Returns: + Tuple with string of solved combination and number of attempts. + + """ + # Convert combo to list. + combo = [int(i) for i in combo] + + # Make initial guess and compare. + best_guess = [0] * len(combo) + best_guess_match = compare(combo, best_guess) + + count = 0 + tumblers = list(range(len(combo))) + + # Evolve guess. + while best_guess != combo: + # Crossover. + guess = best_guess.copy() + + # Mutate. + lock_tumbler = random.choice(tumblers) + guess[lock_tumbler] = random.randint(0, len(combo) - 1) + + # Compare and select. + guess_match = compare(combo, guess) + if guess_match > best_guess_match: + best_guess = guess.copy() + best_guess_match = guess_match + tumblers.remove(lock_tumbler) + print(guess, best_guess) + count += 1 + return ''.join([str(i) for i in best_guess]), count + + +def main(): + """Demonstrate safe cracker. + + Use default combination to demonstrate :func:`crack_safe` and display time + (in seconds) it took to run. + + """ + start_time = time.time() + + combination = '6822858902' + print(f'Combination: {combination}') + guess, count = crack_safe(combination) + print(f'\nCracked! {guess} ') + print(f'in {count} tries!') + + end_time = time.time() + duration = end_time - start_time + print(f'\nRuntime for this program was {duration:.5f} seconds.') + + +if __name__ == '__main__': + main() diff --git a/tests/data/ch07/main/safe_cracker.txt b/tests/data/ch07/main/safe_cracker.txt new file mode 100644 index 0000000..01a80a1 --- /dev/null +++ b/tests/data/ch07/main/safe_cracker.txt @@ -0,0 +1,127 @@ +Combination: 6822858902 +[0, 0, 0, 5, 0, 0, 0, 0, 0, 0] [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] +[0, 0, 0, 0, 0, 0, 0, 3, 0, 0] [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] +[0, 0, 0, 0, 0, 0, 6, 0, 0, 0] [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] +[0, 0, 0, 0, 0, 0, 0, 0, 0, 2] [0, 0, 0, 0, 0, 0, 0, 0, 0, 2] +[0, 0, 0, 6, 0, 0, 0, 0, 0, 2] [0, 0, 0, 0, 0, 0, 0, 0, 0, 2] +[0, 0, 0, 0, 0, 0, 0, 0, 3, 2] [0, 0, 0, 0, 0, 0, 0, 0, 0, 2] +[0, 0, 7, 0, 0, 0, 0, 0, 0, 2] [0, 0, 0, 0, 0, 0, 0, 0, 0, 2] +[0, 0, 0, 0, 0, 0, 6, 0, 0, 2] [0, 0, 0, 0, 0, 0, 0, 0, 0, 2] +[0, 0, 0, 0, 6, 0, 0, 0, 0, 2] [0, 0, 0, 0, 0, 0, 0, 0, 0, 2] +[0, 0, 0, 5, 0, 0, 0, 0, 0, 2] [0, 0, 0, 0, 0, 0, 0, 0, 0, 2] +[0, 0, 0, 0, 0, 0, 0, 0, 0, 2] [0, 0, 0, 0, 0, 0, 0, 0, 0, 2] +[0, 0, 0, 0, 0, 3, 0, 0, 0, 2] [0, 0, 0, 0, 0, 0, 0, 0, 0, 2] +[0, 0, 0, 0, 0, 0, 0, 6, 0, 2] [0, 0, 0, 0, 0, 0, 0, 0, 0, 2] +[0, 6, 0, 0, 0, 0, 0, 0, 0, 2] [0, 0, 0, 0, 0, 0, 0, 0, 0, 2] +[0, 0, 0, 0, 0, 0, 0, 0, 1, 2] [0, 0, 0, 0, 0, 0, 0, 0, 0, 2] +[0, 0, 0, 0, 1, 0, 0, 0, 0, 2] [0, 0, 0, 0, 0, 0, 0, 0, 0, 2] +[0, 0, 0, 0, 0, 7, 0, 0, 0, 2] [0, 0, 0, 0, 0, 0, 0, 0, 0, 2] +[0, 0, 0, 5, 0, 0, 0, 0, 0, 2] [0, 0, 0, 0, 0, 0, 0, 0, 0, 2] +[0, 0, 0, 8, 0, 0, 0, 0, 0, 2] [0, 0, 0, 0, 0, 0, 0, 0, 0, 2] +[0, 0, 0, 0, 0, 0, 0, 0, 0, 2] [0, 0, 0, 0, 0, 0, 0, 0, 0, 2] +[0, 0, 0, 0, 0, 0, 8, 0, 0, 2] [0, 0, 0, 0, 0, 0, 8, 0, 0, 2] +[0, 0, 0, 0, 7, 0, 8, 0, 0, 2] [0, 0, 0, 0, 0, 0, 8, 0, 0, 2] +[0, 0, 0, 0, 0, 0, 8, 0, 7, 2] [0, 0, 0, 0, 0, 0, 8, 0, 0, 2] +[0, 9, 0, 0, 0, 0, 8, 0, 0, 2] [0, 0, 0, 0, 0, 0, 8, 0, 0, 2] +[0, 0, 0, 0, 0, 0, 8, 0, 0, 2] [0, 0, 0, 0, 0, 0, 8, 0, 0, 2] +[0, 0, 7, 0, 0, 0, 8, 0, 0, 2] [0, 0, 0, 0, 0, 0, 8, 0, 0, 2] +[0, 0, 0, 0, 0, 0, 8, 0, 8, 2] [0, 0, 0, 0, 0, 0, 8, 0, 0, 2] +[5, 0, 0, 0, 0, 0, 8, 0, 0, 2] [0, 0, 0, 0, 0, 0, 8, 0, 0, 2] +[0, 0, 0, 7, 0, 0, 8, 0, 0, 2] [0, 0, 0, 0, 0, 0, 8, 0, 0, 2] +[0, 0, 2, 0, 0, 0, 8, 0, 0, 2] [0, 0, 2, 0, 0, 0, 8, 0, 0, 2] +[0, 0, 2, 0, 0, 8, 8, 0, 0, 2] [0, 0, 2, 0, 0, 0, 8, 0, 0, 2] +[0, 0, 2, 0, 9, 0, 8, 0, 0, 2] [0, 0, 2, 0, 0, 0, 8, 0, 0, 2] +[0, 0, 2, 0, 0, 0, 8, 9, 0, 2] [0, 0, 2, 0, 0, 0, 8, 9, 0, 2] +[0, 0, 2, 0, 0, 0, 8, 9, 0, 2] [0, 0, 2, 0, 0, 0, 8, 9, 0, 2] +[0, 0, 2, 0, 0, 9, 8, 9, 0, 2] [0, 0, 2, 0, 0, 0, 8, 9, 0, 2] +[0, 0, 2, 0, 0, 0, 8, 9, 3, 2] [0, 0, 2, 0, 0, 0, 8, 9, 0, 2] +[0, 2, 2, 0, 0, 0, 8, 9, 0, 2] [0, 0, 2, 0, 0, 0, 8, 9, 0, 2] +[0, 0, 2, 0, 0, 9, 8, 9, 0, 2] [0, 0, 2, 0, 0, 0, 8, 9, 0, 2] +[0, 0, 2, 0, 0, 0, 8, 9, 4, 2] [0, 0, 2, 0, 0, 0, 8, 9, 0, 2] +[0, 0, 2, 0, 0, 0, 8, 9, 0, 2] [0, 0, 2, 0, 0, 0, 8, 9, 0, 2] +[0, 0, 2, 0, 0, 0, 8, 9, 0, 2] [0, 0, 2, 0, 0, 0, 8, 9, 0, 2] +[0, 0, 2, 1, 0, 0, 8, 9, 0, 2] [0, 0, 2, 0, 0, 0, 8, 9, 0, 2] +[0, 0, 2, 0, 0, 1, 8, 9, 0, 2] [0, 0, 2, 0, 0, 0, 8, 9, 0, 2] +[0, 0, 2, 0, 5, 0, 8, 9, 0, 2] [0, 0, 2, 0, 0, 0, 8, 9, 0, 2] +[0, 3, 2, 0, 0, 0, 8, 9, 0, 2] [0, 0, 2, 0, 0, 0, 8, 9, 0, 2] +[0, 0, 2, 0, 0, 9, 8, 9, 0, 2] [0, 0, 2, 0, 0, 0, 8, 9, 0, 2] +[0, 0, 2, 0, 1, 0, 8, 9, 0, 2] [0, 0, 2, 0, 0, 0, 8, 9, 0, 2] +[0, 0, 2, 0, 0, 6, 8, 9, 0, 2] [0, 0, 2, 0, 0, 0, 8, 9, 0, 2] +[0, 0, 2, 0, 3, 0, 8, 9, 0, 2] [0, 0, 2, 0, 0, 0, 8, 9, 0, 2] +[0, 0, 2, 0, 0, 0, 8, 9, 1, 2] [0, 0, 2, 0, 0, 0, 8, 9, 0, 2] +[0, 0, 2, 4, 0, 0, 8, 9, 0, 2] [0, 0, 2, 0, 0, 0, 8, 9, 0, 2] +[6, 0, 2, 0, 0, 0, 8, 9, 0, 2] [6, 0, 2, 0, 0, 0, 8, 9, 0, 2] +[6, 0, 2, 3, 0, 0, 8, 9, 0, 2] [6, 0, 2, 0, 0, 0, 8, 9, 0, 2] +[6, 7, 2, 0, 0, 0, 8, 9, 0, 2] [6, 0, 2, 0, 0, 0, 8, 9, 0, 2] +[6, 0, 2, 0, 0, 4, 8, 9, 0, 2] [6, 0, 2, 0, 0, 0, 8, 9, 0, 2] +[6, 0, 2, 0, 0, 0, 8, 9, 0, 2] [6, 0, 2, 0, 0, 0, 8, 9, 0, 2] +[6, 0, 2, 3, 0, 0, 8, 9, 0, 2] [6, 0, 2, 0, 0, 0, 8, 9, 0, 2] +[6, 0, 2, 0, 0, 9, 8, 9, 0, 2] [6, 0, 2, 0, 0, 0, 8, 9, 0, 2] +[6, 0, 2, 0, 0, 0, 8, 9, 0, 2] [6, 0, 2, 0, 0, 0, 8, 9, 0, 2] +[6, 8, 2, 0, 0, 0, 8, 9, 0, 2] [6, 8, 2, 0, 0, 0, 8, 9, 0, 2] +[6, 8, 2, 0, 0, 4, 8, 9, 0, 2] [6, 8, 2, 0, 0, 0, 8, 9, 0, 2] +[6, 8, 2, 0, 0, 3, 8, 9, 0, 2] [6, 8, 2, 0, 0, 0, 8, 9, 0, 2] +[6, 8, 2, 0, 0, 5, 8, 9, 0, 2] [6, 8, 2, 0, 0, 5, 8, 9, 0, 2] +[6, 8, 2, 6, 0, 5, 8, 9, 0, 2] [6, 8, 2, 0, 0, 5, 8, 9, 0, 2] +[6, 8, 2, 0, 4, 5, 8, 9, 0, 2] [6, 8, 2, 0, 0, 5, 8, 9, 0, 2] +[6, 8, 2, 0, 0, 5, 8, 9, 8, 2] [6, 8, 2, 0, 0, 5, 8, 9, 0, 2] +[6, 8, 2, 6, 0, 5, 8, 9, 0, 2] [6, 8, 2, 0, 0, 5, 8, 9, 0, 2] +[6, 8, 2, 0, 0, 5, 8, 9, 9, 2] [6, 8, 2, 0, 0, 5, 8, 9, 0, 2] +[6, 8, 2, 0, 0, 5, 8, 9, 5, 2] [6, 8, 2, 0, 0, 5, 8, 9, 0, 2] +[6, 8, 2, 0, 0, 5, 8, 9, 6, 2] [6, 8, 2, 0, 0, 5, 8, 9, 0, 2] +[6, 8, 2, 6, 0, 5, 8, 9, 0, 2] [6, 8, 2, 0, 0, 5, 8, 9, 0, 2] +[6, 8, 2, 0, 0, 5, 8, 9, 8, 2] [6, 8, 2, 0, 0, 5, 8, 9, 0, 2] +[6, 8, 2, 9, 0, 5, 8, 9, 0, 2] [6, 8, 2, 0, 0, 5, 8, 9, 0, 2] +[6, 8, 2, 0, 0, 5, 8, 9, 4, 2] [6, 8, 2, 0, 0, 5, 8, 9, 0, 2] +[6, 8, 2, 0, 0, 5, 8, 9, 0, 2] [6, 8, 2, 0, 0, 5, 8, 9, 0, 2] +[6, 8, 2, 0, 0, 5, 8, 9, 3, 2] [6, 8, 2, 0, 0, 5, 8, 9, 0, 2] +[6, 8, 2, 0, 0, 5, 8, 9, 7, 2] [6, 8, 2, 0, 0, 5, 8, 9, 0, 2] +[6, 8, 2, 0, 0, 5, 8, 9, 1, 2] [6, 8, 2, 0, 0, 5, 8, 9, 0, 2] +[6, 8, 2, 0, 0, 5, 8, 9, 9, 2] [6, 8, 2, 0, 0, 5, 8, 9, 0, 2] +[6, 8, 2, 0, 4, 5, 8, 9, 0, 2] [6, 8, 2, 0, 0, 5, 8, 9, 0, 2] +[6, 8, 2, 0, 0, 5, 8, 9, 9, 2] [6, 8, 2, 0, 0, 5, 8, 9, 0, 2] +[6, 8, 2, 0, 0, 5, 8, 9, 3, 2] [6, 8, 2, 0, 0, 5, 8, 9, 0, 2] +[6, 8, 2, 5, 0, 5, 8, 9, 0, 2] [6, 8, 2, 0, 0, 5, 8, 9, 0, 2] +[6, 8, 2, 0, 0, 5, 8, 9, 0, 2] [6, 8, 2, 0, 0, 5, 8, 9, 0, 2] +[6, 8, 2, 0, 2, 5, 8, 9, 0, 2] [6, 8, 2, 0, 0, 5, 8, 9, 0, 2] +[6, 8, 2, 0, 0, 5, 8, 9, 4, 2] [6, 8, 2, 0, 0, 5, 8, 9, 0, 2] +[6, 8, 2, 0, 0, 5, 8, 9, 8, 2] [6, 8, 2, 0, 0, 5, 8, 9, 0, 2] +[6, 8, 2, 0, 0, 5, 8, 9, 1, 2] [6, 8, 2, 0, 0, 5, 8, 9, 0, 2] +[6, 8, 2, 0, 0, 5, 8, 9, 9, 2] [6, 8, 2, 0, 0, 5, 8, 9, 0, 2] +[6, 8, 2, 0, 0, 5, 8, 9, 0, 2] [6, 8, 2, 0, 0, 5, 8, 9, 0, 2] +[6, 8, 2, 0, 5, 5, 8, 9, 0, 2] [6, 8, 2, 0, 0, 5, 8, 9, 0, 2] +[6, 8, 2, 0, 4, 5, 8, 9, 0, 2] [6, 8, 2, 0, 0, 5, 8, 9, 0, 2] +[6, 8, 2, 0, 4, 5, 8, 9, 0, 2] [6, 8, 2, 0, 0, 5, 8, 9, 0, 2] +[6, 8, 2, 0, 0, 5, 8, 9, 1, 2] [6, 8, 2, 0, 0, 5, 8, 9, 0, 2] +[6, 8, 2, 0, 0, 5, 8, 9, 0, 2] [6, 8, 2, 0, 0, 5, 8, 9, 0, 2] +[6, 8, 2, 0, 8, 5, 8, 9, 0, 2] [6, 8, 2, 0, 8, 5, 8, 9, 0, 2] +[6, 8, 2, 3, 8, 5, 8, 9, 0, 2] [6, 8, 2, 0, 8, 5, 8, 9, 0, 2] +[6, 8, 2, 0, 8, 5, 8, 9, 8, 2] [6, 8, 2, 0, 8, 5, 8, 9, 0, 2] +[6, 8, 2, 0, 8, 5, 8, 9, 9, 2] [6, 8, 2, 0, 8, 5, 8, 9, 0, 2] +[6, 8, 2, 4, 8, 5, 8, 9, 0, 2] [6, 8, 2, 0, 8, 5, 8, 9, 0, 2] +[6, 8, 2, 0, 8, 5, 8, 9, 0, 2] [6, 8, 2, 0, 8, 5, 8, 9, 0, 2] +[6, 8, 2, 8, 8, 5, 8, 9, 0, 2] [6, 8, 2, 0, 8, 5, 8, 9, 0, 2] +[6, 8, 2, 6, 8, 5, 8, 9, 0, 2] [6, 8, 2, 0, 8, 5, 8, 9, 0, 2] +[6, 8, 2, 0, 8, 5, 8, 9, 3, 2] [6, 8, 2, 0, 8, 5, 8, 9, 0, 2] +[6, 8, 2, 4, 8, 5, 8, 9, 0, 2] [6, 8, 2, 0, 8, 5, 8, 9, 0, 2] +[6, 8, 2, 6, 8, 5, 8, 9, 0, 2] [6, 8, 2, 0, 8, 5, 8, 9, 0, 2] +[6, 8, 2, 0, 8, 5, 8, 9, 6, 2] [6, 8, 2, 0, 8, 5, 8, 9, 0, 2] +[6, 8, 2, 8, 8, 5, 8, 9, 0, 2] [6, 8, 2, 0, 8, 5, 8, 9, 0, 2] +[6, 8, 2, 0, 8, 5, 8, 9, 7, 2] [6, 8, 2, 0, 8, 5, 8, 9, 0, 2] +[6, 8, 2, 8, 8, 5, 8, 9, 0, 2] [6, 8, 2, 0, 8, 5, 8, 9, 0, 2] +[6, 8, 2, 0, 8, 5, 8, 9, 7, 2] [6, 8, 2, 0, 8, 5, 8, 9, 0, 2] +[6, 8, 2, 0, 8, 5, 8, 9, 7, 2] [6, 8, 2, 0, 8, 5, 8, 9, 0, 2] +[6, 8, 2, 0, 8, 5, 8, 9, 3, 2] [6, 8, 2, 0, 8, 5, 8, 9, 0, 2] +[6, 8, 2, 7, 8, 5, 8, 9, 0, 2] [6, 8, 2, 0, 8, 5, 8, 9, 0, 2] +[6, 8, 2, 7, 8, 5, 8, 9, 0, 2] [6, 8, 2, 0, 8, 5, 8, 9, 0, 2] +[6, 8, 2, 0, 8, 5, 8, 9, 7, 2] [6, 8, 2, 0, 8, 5, 8, 9, 0, 2] +[6, 8, 2, 4, 8, 5, 8, 9, 0, 2] [6, 8, 2, 0, 8, 5, 8, 9, 0, 2] +[6, 8, 2, 0, 8, 5, 8, 9, 4, 2] [6, 8, 2, 0, 8, 5, 8, 9, 0, 2] +[6, 8, 2, 0, 8, 5, 8, 9, 6, 2] [6, 8, 2, 0, 8, 5, 8, 9, 0, 2] +[6, 8, 2, 0, 8, 5, 8, 9, 6, 2] [6, 8, 2, 0, 8, 5, 8, 9, 0, 2] +[6, 8, 2, 2, 8, 5, 8, 9, 0, 2] [6, 8, 2, 2, 8, 5, 8, 9, 0, 2] + +Cracked! 6822858902 +in 121 tries! + +Runtime for this program was 55545.00000 seconds. diff --git a/tests/data/ch07/safe_cracker.txt b/tests/data/ch07/safe_cracker.txt new file mode 100644 index 0000000..fe5fcd3 --- /dev/null +++ b/tests/data/ch07/safe_cracker.txt @@ -0,0 +1,110 @@ +[0, 0, 0, 0, 0, 0, 0, 0, 0, 5] [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] +[0, 0, 0, 0, 0, 0, 0, 0, 0, 0] [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] +[0, 0, 0, 0, 0, 8, 0, 0, 0, 0] [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] +[0, 0, 0, 0, 0, 0, 0, 0, 0, 5] [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] +[0, 0, 0, 0, 0, 0, 0, 0, 0, 9] [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] +[0, 0, 6, 0, 0, 0, 0, 0, 0, 0] [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] +[0, 0, 0, 0, 0, 0, 9, 0, 0, 0] [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] +[0, 0, 0, 0, 0, 0, 0, 0, 0, 2] [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] +[0, 0, 0, 0, 0, 2, 0, 0, 0, 0] [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] +[0, 0, 0, 0, 0, 0, 0, 0, 3, 0] [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] +[0, 0, 0, 0, 0, 8, 0, 0, 0, 0] [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] +[0, 0, 0, 7, 0, 0, 0, 0, 0, 0] [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] +[0, 0, 9, 0, 0, 0, 0, 0, 0, 0] [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] +[0, 0, 0, 2, 0, 0, 0, 0, 0, 0] [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] +[0, 0, 0, 0, 0, 0, 0, 8, 0, 0] [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] +[0, 0, 0, 0, 0, 0, 0, 0, 0, 1] [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] +[0, 0, 0, 0, 0, 0, 5, 0, 0, 0] [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] +[0, 0, 0, 1, 0, 0, 0, 0, 0, 0] [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] +[0, 0, 0, 6, 0, 0, 0, 0, 0, 0] [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] +[0, 0, 0, 0, 0, 0, 0, 6, 0, 0] [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] +[0, 4, 0, 0, 0, 0, 0, 0, 0, 0] [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] +[0, 0, 0, 0, 0, 0, 0, 0, 0, 0] [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] +[0, 0, 0, 0, 8, 0, 0, 0, 0, 0] [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] +[0, 0, 0, 4, 0, 0, 0, 0, 0, 0] [0, 0, 0, 4, 0, 0, 0, 0, 0, 0] +[0, 0, 0, 4, 9, 0, 0, 0, 0, 0] [0, 0, 0, 4, 0, 0, 0, 0, 0, 0] +[0, 0, 0, 4, 0, 0, 0, 4, 0, 0] [0, 0, 0, 4, 0, 0, 0, 0, 0, 0] +[0, 0, 0, 4, 0, 0, 0, 0, 0, 0] [0, 0, 0, 4, 0, 0, 0, 0, 0, 0] +[0, 0, 0, 4, 0, 8, 0, 0, 0, 0] [0, 0, 0, 4, 0, 0, 0, 0, 0, 0] +[0, 0, 0, 4, 0, 0, 0, 5, 0, 0] [0, 0, 0, 4, 0, 0, 0, 0, 0, 0] +[0, 0, 0, 4, 0, 0, 0, 0, 3, 0] [0, 0, 0, 4, 0, 0, 0, 0, 0, 0] +[0, 0, 0, 4, 0, 4, 0, 0, 0, 0] [0, 0, 0, 4, 0, 0, 0, 0, 0, 0] +[0, 0, 5, 4, 0, 0, 0, 0, 0, 0] [0, 0, 0, 4, 0, 0, 0, 0, 0, 0] +[0, 0, 0, 4, 0, 0, 0, 0, 6, 0] [0, 0, 0, 4, 0, 0, 0, 0, 0, 0] +[0, 7, 0, 4, 0, 0, 0, 0, 0, 0] [0, 0, 0, 4, 0, 0, 0, 0, 0, 0] +[0, 0, 0, 4, 9, 0, 0, 0, 0, 0] [0, 0, 0, 4, 0, 0, 0, 0, 0, 0] +[0, 0, 0, 4, 0, 0, 0, 0, 0, 3] [0, 0, 0, 4, 0, 0, 0, 0, 0, 3] +[0, 0, 0, 4, 0, 0, 1, 0, 0, 3] [0, 0, 0, 4, 0, 0, 0, 0, 0, 3] +[0, 0, 0, 4, 0, 0, 0, 0, 0, 3] [0, 0, 0, 4, 0, 0, 0, 0, 0, 3] +[0, 0, 0, 4, 0, 0, 6, 0, 0, 3] [0, 0, 0, 4, 0, 0, 0, 0, 0, 3] +[0, 0, 0, 4, 0, 0, 8, 0, 0, 3] [0, 0, 0, 4, 0, 0, 0, 0, 0, 3] +[0, 0, 0, 4, 0, 3, 0, 0, 0, 3] [0, 0, 0, 4, 0, 0, 0, 0, 0, 3] +[0, 0, 0, 4, 0, 0, 9, 0, 0, 3] [0, 0, 0, 4, 0, 0, 0, 0, 0, 3] +[0, 0, 0, 4, 0, 1, 0, 0, 0, 3] [0, 0, 0, 4, 0, 0, 0, 0, 0, 3] +[0, 0, 0, 4, 0, 0, 0, 0, 0, 3] [0, 0, 0, 4, 0, 0, 0, 0, 0, 3] +[8, 0, 0, 4, 0, 0, 0, 0, 0, 3] [8, 0, 0, 4, 0, 0, 0, 0, 0, 3] +[8, 0, 0, 4, 0, 0, 0, 3, 0, 3] [8, 0, 0, 4, 0, 0, 0, 0, 0, 3] +[8, 0, 0, 4, 0, 9, 0, 0, 0, 3] [8, 0, 0, 4, 0, 9, 0, 0, 0, 3] +[8, 0, 0, 4, 0, 9, 4, 0, 0, 3] [8, 0, 0, 4, 0, 9, 0, 0, 0, 3] +[8, 0, 4, 4, 0, 9, 0, 0, 0, 3] [8, 0, 0, 4, 0, 9, 0, 0, 0, 3] +[8, 0, 0, 4, 0, 9, 0, 0, 0, 3] [8, 0, 0, 4, 0, 9, 0, 0, 0, 3] +[8, 2, 0, 4, 0, 9, 0, 0, 0, 3] [8, 0, 0, 4, 0, 9, 0, 0, 0, 3] +[8, 0, 3, 4, 0, 9, 0, 0, 0, 3] [8, 0, 0, 4, 0, 9, 0, 0, 0, 3] +[8, 0, 8, 4, 0, 9, 0, 0, 0, 3] [8, 0, 0, 4, 0, 9, 0, 0, 0, 3] +[8, 0, 0, 4, 0, 9, 2, 0, 0, 3] [8, 0, 0, 4, 0, 9, 0, 0, 0, 3] +[8, 0, 0, 4, 0, 9, 8, 0, 0, 3] [8, 0, 0, 4, 0, 9, 0, 0, 0, 3] +[8, 0, 0, 4, 8, 9, 0, 0, 0, 3] [8, 0, 0, 4, 0, 9, 0, 0, 0, 3] +[8, 0, 0, 4, 0, 9, 5, 0, 0, 3] [8, 0, 0, 4, 0, 9, 0, 0, 0, 3] +[8, 0, 0, 4, 9, 9, 0, 0, 0, 3] [8, 0, 0, 4, 0, 9, 0, 0, 0, 3] +[8, 0, 0, 4, 8, 9, 0, 0, 0, 3] [8, 0, 0, 4, 0, 9, 0, 0, 0, 3] +[8, 0, 0, 4, 0, 9, 0, 0, 0, 3] [8, 0, 0, 4, 0, 9, 0, 0, 0, 3] +[8, 0, 0, 4, 0, 9, 0, 0, 8, 3] [8, 0, 0, 4, 0, 9, 0, 0, 0, 3] +[8, 2, 0, 4, 0, 9, 0, 0, 0, 3] [8, 0, 0, 4, 0, 9, 0, 0, 0, 3] +[8, 0, 0, 4, 6, 9, 0, 0, 0, 3] [8, 0, 0, 4, 0, 9, 0, 0, 0, 3] +[8, 0, 0, 4, 4, 9, 0, 0, 0, 3] [8, 0, 0, 4, 0, 9, 0, 0, 0, 3] +[8, 4, 0, 4, 0, 9, 0, 0, 0, 3] [8, 0, 0, 4, 0, 9, 0, 0, 0, 3] +[8, 0, 0, 4, 0, 9, 9, 0, 0, 3] [8, 0, 0, 4, 0, 9, 0, 0, 0, 3] +[8, 0, 7, 4, 0, 9, 0, 0, 0, 3] [8, 0, 7, 4, 0, 9, 0, 0, 0, 3] +[8, 0, 7, 4, 0, 9, 0, 0, 7, 3] [8, 0, 7, 4, 0, 9, 0, 0, 0, 3] +[8, 0, 7, 4, 2, 9, 0, 0, 0, 3] [8, 0, 7, 4, 0, 9, 0, 0, 0, 3] +[8, 0, 7, 4, 0, 9, 0, 0, 7, 3] [8, 0, 7, 4, 0, 9, 0, 0, 0, 3] +[8, 0, 7, 4, 0, 9, 0, 9, 0, 3] [8, 0, 7, 4, 0, 9, 0, 0, 0, 3] +[8, 0, 7, 4, 0, 9, 2, 0, 0, 3] [8, 0, 7, 4, 0, 9, 0, 0, 0, 3] +[8, 0, 7, 4, 0, 9, 0, 0, 0, 3] [8, 0, 7, 4, 0, 9, 0, 0, 0, 3] +[8, 0, 7, 4, 0, 9, 4, 0, 0, 3] [8, 0, 7, 4, 0, 9, 0, 0, 0, 3] +[8, 0, 7, 4, 0, 9, 0, 2, 0, 3] [8, 0, 7, 4, 0, 9, 0, 2, 0, 3] +[8, 2, 7, 4, 0, 9, 0, 2, 0, 3] [8, 0, 7, 4, 0, 9, 0, 2, 0, 3] +[8, 4, 7, 4, 0, 9, 0, 2, 0, 3] [8, 0, 7, 4, 0, 9, 0, 2, 0, 3] +[8, 0, 7, 4, 0, 9, 8, 2, 0, 3] [8, 0, 7, 4, 0, 9, 0, 2, 0, 3] +[8, 2, 7, 4, 0, 9, 0, 2, 0, 3] [8, 0, 7, 4, 0, 9, 0, 2, 0, 3] +[8, 9, 7, 4, 0, 9, 0, 2, 0, 3] [8, 9, 7, 4, 0, 9, 0, 2, 0, 3] +[8, 9, 7, 4, 7, 9, 0, 2, 0, 3] [8, 9, 7, 4, 0, 9, 0, 2, 0, 3] +[8, 9, 7, 4, 7, 9, 0, 2, 0, 3] [8, 9, 7, 4, 0, 9, 0, 2, 0, 3] +[8, 9, 7, 4, 2, 9, 0, 2, 0, 3] [8, 9, 7, 4, 0, 9, 0, 2, 0, 3] +[8, 9, 7, 4, 0, 9, 0, 2, 7, 3] [8, 9, 7, 4, 0, 9, 0, 2, 0, 3] +[8, 9, 7, 4, 0, 9, 3, 2, 0, 3] [8, 9, 7, 4, 0, 9, 0, 2, 0, 3] +[8, 9, 7, 4, 0, 9, 0, 2, 4, 3] [8, 9, 7, 4, 0, 9, 0, 2, 0, 3] +[8, 9, 7, 4, 3, 9, 0, 2, 0, 3] [8, 9, 7, 4, 0, 9, 0, 2, 0, 3] +[8, 9, 7, 4, 0, 9, 7, 2, 0, 3] [8, 9, 7, 4, 0, 9, 0, 2, 0, 3] +[8, 9, 7, 4, 0, 9, 0, 2, 6, 3] [8, 9, 7, 4, 0, 9, 0, 2, 0, 3] +[8, 9, 7, 4, 0, 9, 0, 2, 0, 3] [8, 9, 7, 4, 0, 9, 0, 2, 0, 3] +[8, 9, 7, 4, 0, 9, 0, 2, 0, 3] [8, 9, 7, 4, 0, 9, 0, 2, 0, 3] +[8, 9, 7, 4, 0, 9, 7, 2, 0, 3] [8, 9, 7, 4, 0, 9, 0, 2, 0, 3] +[8, 9, 7, 4, 0, 9, 0, 2, 9, 3] [8, 9, 7, 4, 0, 9, 0, 2, 0, 3] +[8, 9, 7, 4, 0, 9, 0, 2, 0, 3] [8, 9, 7, 4, 0, 9, 0, 2, 0, 3] +[8, 9, 7, 4, 0, 9, 3, 2, 0, 3] [8, 9, 7, 4, 0, 9, 0, 2, 0, 3] +[8, 9, 7, 4, 0, 9, 0, 2, 0, 3] [8, 9, 7, 4, 0, 9, 0, 2, 0, 3] +[8, 9, 7, 4, 0, 9, 0, 2, 0, 3] [8, 9, 7, 4, 0, 9, 0, 2, 0, 3] +[8, 9, 7, 4, 0, 9, 0, 2, 3, 3] [8, 9, 7, 4, 0, 9, 0, 2, 0, 3] +[8, 9, 7, 4, 8, 9, 0, 2, 0, 3] [8, 9, 7, 4, 0, 9, 0, 2, 0, 3] +[8, 9, 7, 4, 0, 9, 0, 2, 1, 3] [8, 9, 7, 4, 0, 9, 0, 2, 1, 3] +[8, 9, 7, 4, 9, 9, 0, 2, 1, 3] [8, 9, 7, 4, 0, 9, 0, 2, 1, 3] +[8, 9, 7, 4, 0, 9, 9, 2, 1, 3] [8, 9, 7, 4, 0, 9, 0, 2, 1, 3] +[8, 9, 7, 4, 0, 9, 5, 2, 1, 3] [8, 9, 7, 4, 0, 9, 0, 2, 1, 3] +[8, 9, 7, 4, 3, 9, 0, 2, 1, 3] [8, 9, 7, 4, 0, 9, 0, 2, 1, 3] +[8, 9, 7, 4, 7, 9, 0, 2, 1, 3] [8, 9, 7, 4, 0, 9, 0, 2, 1, 3] +[8, 9, 7, 4, 4, 9, 0, 2, 1, 3] [8, 9, 7, 4, 0, 9, 0, 2, 1, 3] +[8, 9, 7, 4, 0, 9, 6, 2, 1, 3] [8, 9, 7, 4, 0, 9, 0, 2, 1, 3] +[8, 9, 7, 4, 0, 9, 0, 2, 1, 3] [8, 9, 7, 4, 0, 9, 0, 2, 1, 3] +[8, 9, 7, 4, 0, 9, 4, 2, 1, 3] [8, 9, 7, 4, 0, 9, 0, 2, 1, 3] +[8, 9, 7, 4, 5, 9, 0, 2, 1, 3] [8, 9, 7, 4, 5, 9, 0, 2, 1, 3] diff --git a/tests/test_chapter07.py b/tests/test_chapter07.py index ed6466c..169476a 100644 --- a/tests/test_chapter07.py +++ b/tests/test_chapter07.py @@ -5,6 +5,7 @@ from io import StringIO import src.ch07.c1_breed_rats as breed_rats +import src.ch07.c2_safe_cracker as safe_cracker class TestBreedRats(unittest.TestCase): @@ -321,5 +322,80 @@ def test_main(self, mock_stdout, mock_random, mock_time): self.assertEqual(mock_stdout.getvalue(), file_data) +class TestSafeCracker(unittest.TestCase): + """Test Safe Cracker.""" + + @classmethod + def setUpClass(cls): + """Configure attributes for use in this class only.""" + cls.random = Random() + + def test_compare(self): + """Test compare.""" + list1 = [8] + list2 = [8] + test = safe_cracker.compare(list1, list2) + self.assertEqual(test, 1) + list1 = [8, 9] + list2 = [8] + test = safe_cracker.compare(list1, list2) + self.assertEqual(test, 1) + list1 = [8, 9] + list2 = [8, 8] + test = safe_cracker.compare(list1, list2) + self.assertEqual(test, 1) + list1 = [8, 9] + list2 = [8, 9] + test = safe_cracker.compare(list1, list2) + self.assertEqual(test, 2) + list1 = [8, 9, 7, 4, 5, 9, 0] + list2 = [8, 8, 6, 3, 5, 8, 1] + test = safe_cracker.compare(list1, list2) + self.assertEqual(test, 2) + list1 = [8, 9, 7, 4, 5, 9, 0] + list2 = [8, 9, 7, 4, 5, 9, 0] + test = safe_cracker.compare(list1, list2) + self.assertEqual(test, 7) + + @unittest.mock.patch('src.ch07.c2_safe_cracker.random') + @unittest.mock.patch('sys.stdout', new_callable=StringIO) + def test_crack_safe(self, mock_stdout, mock_random): + """Test crack_safe.""" + # Patch random to use non-random seed. + self.random.seed(211) + mock_random.choice._mock_side_effect = self.random.choice + mock_random.randint._mock_side_effect = self.random.randint + + combo = '8974590213' + test, count = safe_cracker.crack_safe(combo) + self.assertEqual(count, 110) + self.assertEqual(combo, test) + + # Test sys.stdout output. + with open(os.path.normpath('tests/data/ch07/safe_cracker.txt'), + 'r') as file: + file_data = ''.join(file.readlines()) + self.assertEqual(mock_stdout.getvalue(), file_data) + + @unittest.mock.patch('src.ch07.c2_safe_cracker.time') + @unittest.mock.patch('src.ch07.c2_safe_cracker.random') + @unittest.mock.patch('sys.stdout', new_callable=StringIO) + def test_main(self, mock_stdout, mock_random, mock_time): + """Test main.""" + # Patch out variances. + self.random.seed(111) + mock_random.choice._mock_side_effect = self.random.choice + mock_random.randint._mock_side_effect = self.random.randint + mock_time.time.side_effect = [12345, 67890] + + safe_cracker.main() + + # Test sys.stdout output. + with open(os.path.normpath('tests/data/ch07/main/safe_cracker.txt'), + 'r') as file: + file_data = ''.join(file.readlines()) + self.assertEqual(mock_stdout.getvalue(), file_data) + + if __name__ == '__main__': unittest.main()