{"id":17749904,"url":"https://github.com/0nom4d/chooseyourbattles","last_synced_at":"2025-04-01T08:52:17.147Z","repository":{"id":104242948,"uuid":"571890457","full_name":"0Nom4D/ChooseYourBattles","owner":"0Nom4D","description":"4 algorithmic problems are facing you! Let's solve some! - Assignment from Fontys University of Applied Sciences (ALG1 Course)","archived":false,"fork":false,"pushed_at":"2023-01-25T22:41:28.000Z","size":74,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2023-03-04T01:41:31.481Z","etag":null,"topics":["algorithm","epitech","fontys","fontys-ict-students"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/0Nom4D.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-11-29T05:18:13.000Z","updated_at":"2022-12-12T18:52:45.000Z","dependencies_parsed_at":null,"dependency_job_id":"17833e8c-a889-4ea7-ad51-1337d53578db","html_url":"https://github.com/0Nom4D/ChooseYourBattles","commit_stats":null,"previous_names":[],"tags_count":0,"template":null,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/0Nom4D%2FChooseYourBattles","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/0Nom4D%2FChooseYourBattles/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/0Nom4D%2FChooseYourBattles/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/0Nom4D%2FChooseYourBattles/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/0Nom4D","download_url":"https://codeload.github.com/0Nom4D/ChooseYourBattles/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246612494,"owners_count":20805354,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["algorithm","epitech","fontys","fontys-ict-students"],"created_at":"2024-10-26T11:41:48.545Z","updated_at":"2025-04-01T08:52:17.115Z","avatar_url":"https://github.com/0Nom4D.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Assignment n°3: Pick You Battles\n\n## Problem A: The Way Out\n\nThis problem is to find the shortest path to the end of the maze, represented by a matrix of M x N.\n\nThe matrix also contains a character '2', representing the maze exit.\n\nI tried to solve this problem because I already have done it in the school I come from.\n\nTo solve this problem, I would use a Dijkstra algorithm on all directions (North, East, South, West).\n\nEach time we're checking a direction, we're storing it in a list. On the next iteration, we're processing once again from the already checked positions.\n\nYou can check the code I have already done by clicking this [link](https://github.com/0Nom4D/304pacman).\n\nThe way I would design it would be the same as the repository given just above. The time complexity of this code is 0(n^2), cause of the double loop used to compute the Dijkstra.\n\n## Problem B: SEND MORE MONEY!\n\nThis problem is a casual cryptarithmetic puzzle, where each letter represent a digit. Each digit must make an equation true, following this:\n\n```txt\nString 1 + String 2 = String 3\n```\n\nIn order to solve this problem, I would use the permutations object with Python that will help me.\n\nFirstly, we must get each letter in the phrase. If we have a higher number of letter than digits, we must have an error.\n\nThen, we will get all possible permutations using x of the 10 digits, where x is the number of letters in the equation. From there, we can get one case:\n\n- If the result of the equation is longer than both the first operands, the first digit of the result is 1. Though, we can exclude every permutation not having the number 1 at the right index.\n\nThen, we're computing each possible value for our letters. When it's done for each word, we can check if the equation is right.\n\nFinally, if the equation is true, we can print the result. If not, we can carry on the next permutation.\n\n```py\nfrom typing import List\nfrom itertools import permutations\nfrom sys import argv\n\n\nclass Solver:\n    def __init__(self, equationToSolve: str) -\u003e None:\n        self._equation = equationToSolve\n        self._wordList = []\n        self._letterList = []\n\n    @property\n    def equation(self) -\u003e str:\n        return self._equation\n\n    @property\n    def wordList(self):\n        return self._wordList\n\n    @property\n    def letterList(self):\n        return self._letterList\n\n    def getLettersFromEquation(self) -\u003e bool:\n        self._wordList = self._equation.split()\n\n        for word in self._wordList:\n            if not word.isalpha():\n                self._wordList.remove(word)\n        self._letterList = list(set(''.join(self._wordList)))\n        if len(self._letterList) \u003e 10:\n            return False\n        return True\n\n    def transformValue(self, values: List[int]) -\u003e int:\n        \"\"\"\n        Computes the list of integer for each value of a word, into an integer, using power of ten.\n        :param values: List of value for each word\n        :return: Computed value for the word\n        \"\"\"\n        tenPowersList = [10 ** i for i in range(len(values) - 1, -1, -1)]\n        value = 0\n\n        for i in range(len(values)):\n            value += values[i] * tenPowersList[i]\n        return value\n\n    def findCorrectWordValues(self) -\u003e int:\n        it = 0\n        resultBiggerLength = False\n\n        def getAllPossibleAssociation():\n            return list(permutations([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], len(self._letterList)))\n\n        def isResultLongerThanOperand():\n            \"\"\"\n            Checks if any word is longer than the crypt-arithmetic puzzle result\n            :return: True if the result is longer than every operand, False otherwise\n            \"\"\"\n            operationResult = []\n\n            for operand in self._wordList[:-1]:\n                operationResult.append(len(self._wordList[-1]) \u003e len(operand))\n            return all(operationResult)\n\n        associationList = getAllPossibleAssociation()\n        if isResultLongerThanOperand():\n            resultBiggerLength = True\n\n        for association in associationList:\n\n            ## Checks if the result is bigger than the both operands. If true, we're checking\n            ## if the element in the 'x' position of the permutation is not 1, we continue. Because that\n            ## permutation will not be the right one.\n            if resultBiggerLength and association[self._letterList.index(self._wordList[-1][0])] != 1:\n                continue\n\n            ## Creates all values for each letter of each word. When created, we loop over those\n            ## elements, and we're getting the indices of all letters in our main list and\n            ## we're getting its value in the permutation.\n            elementValues = [[0] * len(self._wordList[i]) for i in range(len(self._wordList))]\n            for it in range(len(self._letterList)):\n                for idx, wordValues in enumerate(elementValues):\n                    letterIndices = [_k for _k, _j in enumerate(self._wordList[idx]) if _j == self._letterList[it]]\n                    for index in letterIndices:\n                        wordValues[index] = association[it]\n\n            ## When done, we're computing the value for each word. Then, we're checking if the 2 firsts\n            ## are equal to the last one. Then, we're creating the equation with the transformed words.\n            resultList = [self.transformValue(values) for values in elementValues]\n            if sum(resultList[:-1]) == resultList[-1]:\n                result = ''\n                for idx, value in enumerate(resultList[:-1]):\n                    result += f'{value}{\" + \" if idx != len(resultList[:-1]) - 1 else \" = \"}'\n                result += f\"{resultList[-1]}\"\n                print(result)\n        return 0\n\n    def run(self) -\u003e int:\n        if not self.getLettersFromEquation():\n            return 1\n        return self.findCorrectWordValues()\n\n\ndef main() -\u003e int:\n    if len(argv) != 2:\n        return 1\n    equationSolver = Solver(argv[1])\n    return equationSolver.run()\n\n\nif __name__ == \"__main__\":\n    exit(main())\n```\n\nAll the code above is tested with this:\n\n```py\nfrom sources.main import Solver\n\n\nclass TestSolver:\n    def test_solverCreation(self):\n        solver = Solver('SEND + MORE = MONEY')\n        assert solver.equation == 'SEND + MORE = MONEY'\n        assert solver.wordList == []\n        assert solver.letterList == []\n\n    def test_getLettersFromEq(self):\n        solver = Solver('SEND + MORE = MONEY')\n        assert solver.getLettersFromEquation() == True\n        assert solver.equation == 'SEND + MORE = MONEY'\n        assert solver.wordList == ['SEND', 'MORE', 'MONEY']\n        assert set(solver.letterList) == set(['S', 'E', 'N', 'D', 'M', 'O', 'R', 'Y'])\n\n    def test_findCorrectEquation(self, capsys):\n        solver = Solver('SEND + MORE = MONEY')\n        assert solver.run() == 0\n        stdout = capsys.readouterr()[0]\n        assert stdout == \"9567 + 1085 = 10652\\n\"\n\n    def test_runWithTooMuchLetters(self):\n        solver = Solver('ALED + PEUR = SIGNAL')\n        assert solver.run() == 1\n\n    def test_getTooManyLetters(self):\n        solver = Solver('ALED + PEUR = SIGNAL')\n        assert solver.getLettersFromEquation() == False\n\n    def test_valueTransform(self):\n        _ = Solver('SEND + MORE = MONEY')\n        assert _.transformValue([9, 0, 5, 4]) == 9054\n```\n\nThe code I wrote here has 3 loops inside one loop. Therefore, the complexity would be O(n^4) because I wanted to make it generic for any number of operands.\n\nBy replacing those lines...:\n\n```py\nelementValues = [[0] * len(self._wordList[i]) for i in range(len(self._wordList))]\nfor it in range(len(self._letterList)):\n    for idx, wordValues in enumerate(elementValues):\n        letterIndices = [_k for _k, _j in enumerate(self._wordList[idx]) if _j == self._letterList[it]]\n        for index in letterIndices:\n            wordValues[index] = association[it]\n```\n\n... by creating and using 3 distincts variables instead of an array of elements, I would be able to lower the time complexity to O(n^2) once again.\n\n## Problem C: From X to Y\n\n***I am sorry I don't know how to explain it without showing an example.***\n\nThe problem is to transform X into Y only using 3 operations:\n\n- `X = min(X * m, Y * 2)`\n- `X = X - 2`\n- `X = X - 1`\n\nFor each iteration, I would look at:\n\n- X is lower than Y\n- The distance between the result of each operation and the goal Y until I will find Y\n\nAs an example:\n\n```txt\nLet's consider X = 4, Y = 12 and m = 25.\n\nX is lower than Y. There, we have X \u003c Y so we're going to use our first operation.\n\n4 * 25 has a higher distance from Y than Y * 2. So we're going to use our first operation.\n\nThen, we're lowering the value as we can (-2 until it's not possible anymore), checking until we're reaching Y.\n```\n\nAt first, to my mind, that would sometimes be the first operation and then the others operations.\n\nTo me the complexity of this algorithm would be O(n), using only one loop composed of conditional statements just looking for the conditions to trigger the operations.\n\n## Problem D: Shortest Paths\n\nWe're having a directed graph with a source vertex and a distance function on the arcs.\n\nTo it, I would use the Dijkstra algorithm once again. Since we are in a directed weighted graph, it's the most accurate algorithm I know.\n\nFrom the start vertex, we will try to get the shortest weight for each edge. For each edge, we will keep the vertex we just went to in memory.\n\nThen, we will iterate this for each know vertex. If a vertex is not the start vertex, we will add the weight already used to access this vertex to the other weights we're going to check.\n\nFinally, I will iterate until I find the end vertex, giving me the shortest path.\n\nThe way I look at it would be a O(n^2) too. It needs to iterate until our vertices list is empty and we need to iterate over our already checked vertices.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F0nom4d%2Fchooseyourbattles","html_url":"https://awesome.ecosyste.ms/projects/github.com%2F0nom4d%2Fchooseyourbattles","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F0nom4d%2Fchooseyourbattles/lists"}