From c2fe77791f63bb638124da2f53b8398a3def167d Mon Sep 17 00:00:00 2001 From: Ishab Ahmed Date: Sun, 2 Apr 2023 00:14:40 +0100 Subject: [PATCH 1/5] Add Project Euler problem 79 solution 1 --- project_euler/problem_079/__init__.py | 0 project_euler/problem_079/p079_keylog.txt | 50 +++++++++++++++++++++ project_euler/problem_079/sol1.py | 53 +++++++++++++++++++++++ 3 files changed, 103 insertions(+) create mode 100644 project_euler/problem_079/__init__.py create mode 100644 project_euler/problem_079/p079_keylog.txt create mode 100644 project_euler/problem_079/sol1.py diff --git a/project_euler/problem_079/__init__.py b/project_euler/problem_079/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/project_euler/problem_079/p079_keylog.txt b/project_euler/problem_079/p079_keylog.txt new file mode 100644 index 000000000000..41f15673248d --- /dev/null +++ b/project_euler/problem_079/p079_keylog.txt @@ -0,0 +1,50 @@ +319 +680 +180 +690 +129 +620 +762 +689 +762 +318 +368 +710 +720 +710 +629 +168 +160 +689 +716 +731 +736 +729 +316 +729 +729 +710 +769 +290 +719 +680 +318 +389 +162 +289 +162 +718 +729 +319 +790 +680 +890 +362 +319 +760 +316 +729 +380 +319 +728 +716 diff --git a/project_euler/problem_079/sol1.py b/project_euler/problem_079/sol1.py new file mode 100644 index 000000000000..bedc0f1621f5 --- /dev/null +++ b/project_euler/problem_079/sol1.py @@ -0,0 +1,53 @@ +""" +Project Euler Problem 79: https://projecteuler.net/problem=79 + +Passcode derivation + +A common security method used for online banking is to ask the user for three +random characters from a passcode. For example, if the passcode was 531278, +they may ask for the 2nd, 3rd, and 5th characters; the expected reply would +be: 317. + +The text file, keylog.txt, contains fifty successful login attempts. + +Given that the three characters are always asked for in order, analyse the file +so as to determine the shortest possible secret passcode of unknown length. +... + +""" +import itertools +import os + + +def solution() -> int: + """ + Returns the shortest possible secret passcode of unknown length. + + >>> solution() + 73162890 + """ + with open(os.path.dirname(__file__) + "/p079_keylog.txt") as file: + logins = [tuple(line.strip()) for line in file] + + unique_chars = {char for login in logins for char in login} + + for permutation in itertools.permutations(unique_chars): + satisfied = True + for login in logins: + if not ( + permutation.index(login[0]) + < permutation.index(login[1]) + < permutation.index(login[2]) + ): + satisfied = False + break + + if satisfied: + return int("".join(permutation)) + break + + return 0 + + +if __name__ == "__main__": + print(solution()) From 4a1f7306aeca7939cb5fbae5f6e2b2d878beb1d9 Mon Sep 17 00:00:00 2001 From: Ishab Ahmed Date: Sun, 2 Apr 2023 12:03:37 +0100 Subject: [PATCH 2/5] Updated to follow repo standards --- .../{p079_keylog.txt => keylog.txt} | 0 project_euler/problem_079/sol1.py | 34 ++++++++++++++----- 2 files changed, 25 insertions(+), 9 deletions(-) rename project_euler/problem_079/{p079_keylog.txt => keylog.txt} (100%) diff --git a/project_euler/problem_079/p079_keylog.txt b/project_euler/problem_079/keylog.txt similarity index 100% rename from project_euler/problem_079/p079_keylog.txt rename to project_euler/problem_079/keylog.txt diff --git a/project_euler/problem_079/sol1.py b/project_euler/problem_079/sol1.py index bedc0f1621f5..4dd47a88d210 100644 --- a/project_euler/problem_079/sol1.py +++ b/project_euler/problem_079/sol1.py @@ -12,24 +12,29 @@ Given that the three characters are always asked for in order, analyse the file so as to determine the shortest possible secret passcode of unknown length. -... - """ import itertools import os -def solution() -> int: +def find_secret_passcode(logins: list[str]) -> int: """ Returns the shortest possible secret passcode of unknown length. - >>> solution() - 73162890 + >>> find_secret_passcode(["319", "680", "180", "690", "129", "620"]) + 6312980 + + >>> find_secret_passcode(["135", "259", "235", "189", "690", "168", "120"]) + 132568940 + + >>> find_secret_passcode(["426", "281", "061", "819" "268", "406", "420"]) + 4206819 """ - with open(os.path.dirname(__file__) + "/p079_keylog.txt") as file: - logins = [tuple(line.strip()) for line in file] - unique_chars = {char for login in logins for char in login} + # Split each login by character e.g. '319' -> ('3', '1', '9') + split_logins = [tuple(login) for login in logins] + + unique_chars = {char for login in split_logins for char in login} for permutation in itertools.permutations(unique_chars): satisfied = True @@ -46,7 +51,18 @@ def solution() -> int: return int("".join(permutation)) break - return 0 + raise Exception("Unable to find the secret passcode") + + +def solution() -> int: + """ + Returns the shortest possible secret passcode of unknown length + for successful login attempts given by keylog.txt. + """ + with open(os.path.dirname(__file__) + "/keylog.txt") as file: + logins = file.read().splitlines() + + return find_secret_passcode(logins) if __name__ == "__main__": From 716ff0caf7de637494350ec8cf8dc9c3e0966c30 Mon Sep 17 00:00:00 2001 From: Ishab Ahmed Date: Sun, 2 Apr 2023 12:32:20 +0100 Subject: [PATCH 3/5] Fixed test cases --- project_euler/problem_079/sol1.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/project_euler/problem_079/sol1.py b/project_euler/problem_079/sol1.py index 4dd47a88d210..ba7056ecc96c 100644 --- a/project_euler/problem_079/sol1.py +++ b/project_euler/problem_079/sol1.py @@ -21,13 +21,16 @@ def find_secret_passcode(logins: list[str]) -> int: """ Returns the shortest possible secret passcode of unknown length. - >>> find_secret_passcode(["319", "680", "180", "690", "129", "620"]) + >>> find_secret_passcode(["319", "680", "180", "690", "129", "620", "698", + ... "318", "328", "310", "320", "610", "629", "198", "190", "631"]) 6312980 - >>> find_secret_passcode(["135", "259", "235", "189", "690", "168", "120"]) - 132568940 + >>> find_secret_passcode(["135", "259", "235", "189", "690", "168", "120", + ... "136", "289", "589", "160", "165", "580", "369", "250", "280"]) + 12365890 - >>> find_secret_passcode(["426", "281", "061", "819" "268", "406", "420"]) + >>> find_secret_passcode(["426", "281", "061", "819" "268", "406", "420", + ... "428", "209", "689", "019", "421", "469", "261", "681", "201"]) 4206819 """ @@ -66,4 +69,8 @@ def solution() -> int: if __name__ == "__main__": + import doctest + + doctest.testmod() + print(solution()) From 9466b1013dc9535a06341e4aca5bbfc3b6e32af4 Mon Sep 17 00:00:00 2001 From: Dhruv Manilawala Date: Sun, 2 Apr 2023 17:18:51 +0530 Subject: [PATCH 4/5] Remove `doctest` invocation in main block --- project_euler/problem_079/sol1.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/project_euler/problem_079/sol1.py b/project_euler/problem_079/sol1.py index ba7056ecc96c..6cb3ca987108 100644 --- a/project_euler/problem_079/sol1.py +++ b/project_euler/problem_079/sol1.py @@ -69,8 +69,4 @@ def solution() -> int: if __name__ == "__main__": - import doctest - - doctest.testmod() - - print(solution()) + print(f"{solution() = }") From e982f92e7f99b3e2f6bd0815637cc092349cdd0a Mon Sep 17 00:00:00 2001 From: Dhruv Manilawala Date: Sun, 2 Apr 2023 17:31:02 +0530 Subject: [PATCH 5/5] Use `pathlib`, add `solution` doctest --- project_euler/problem_079/keylog_test.txt | 16 ++++++++++++++++ project_euler/problem_079/sol1.py | 17 +++++++---------- 2 files changed, 23 insertions(+), 10 deletions(-) create mode 100644 project_euler/problem_079/keylog_test.txt diff --git a/project_euler/problem_079/keylog_test.txt b/project_euler/problem_079/keylog_test.txt new file mode 100644 index 000000000000..2c7024bde948 --- /dev/null +++ b/project_euler/problem_079/keylog_test.txt @@ -0,0 +1,16 @@ +319 +680 +180 +690 +129 +620 +698 +318 +328 +310 +320 +610 +629 +198 +190 +631 diff --git a/project_euler/problem_079/sol1.py b/project_euler/problem_079/sol1.py index 6cb3ca987108..d34adcd243b0 100644 --- a/project_euler/problem_079/sol1.py +++ b/project_euler/problem_079/sol1.py @@ -14,17 +14,13 @@ so as to determine the shortest possible secret passcode of unknown length. """ import itertools -import os +from pathlib import Path def find_secret_passcode(logins: list[str]) -> int: """ Returns the shortest possible secret passcode of unknown length. - >>> find_secret_passcode(["319", "680", "180", "690", "129", "620", "698", - ... "318", "328", "310", "320", "610", "629", "198", "190", "631"]) - 6312980 - >>> find_secret_passcode(["135", "259", "235", "189", "690", "168", "120", ... "136", "289", "589", "160", "165", "580", "369", "250", "280"]) 12365890 @@ -52,18 +48,19 @@ def find_secret_passcode(logins: list[str]) -> int: if satisfied: return int("".join(permutation)) - break raise Exception("Unable to find the secret passcode") -def solution() -> int: +def solution(input_file: str = "keylog.txt") -> int: """ Returns the shortest possible secret passcode of unknown length - for successful login attempts given by keylog.txt. + for successful login attempts given by `input_file` text file. + + >>> solution("keylog_test.txt") + 6312980 """ - with open(os.path.dirname(__file__) + "/keylog.txt") as file: - logins = file.read().splitlines() + logins = Path(__file__).parent.joinpath(input_file).read_text().splitlines() return find_secret_passcode(logins)