├── .gitignore ├── LICENSE ├── README.md ├── __init__.py ├── solutions ├── ASCII85_Encoding_And_Decoding.py ├── All_that_is_open_must_be_closed.py ├── Battleship_field_validator.py ├── Bowling_score_calculator.py ├── Breadcrumb_Generator.py ├── Calculator.py ├── Complementary_DNA.py ├── Consecutive_strings.py ├── Convert_number_to_reversed_array_of_digits.py ├── Conway_s_Game_of_Life_Unlimited_Edition.py ├── Counting_power_sets.py ├── Luck_check.py ├── Maximum_subarray_sum.py ├── Merged_String_Checker.py ├── Metaclasses_Simple_Django_Models.py ├── Miles_per_gallon_to_kilometers_per_liter.py ├── Mony_mony_mony.py ├── Morse_encoding.py ├── Multiples_of_3_and_5.py ├── Pascal_s_Triangle_2.py ├── Permutations.py ├── Persistent_Bugger.py ├── Playing_with_digits.py ├── Prefill_an_Array.py ├── Prime_number_decompositions.py ├── Square_into_Squares_Protect_trees.py ├── String_Shortener_shrink.py ├── Strip_Comments.py ├── Sum_of_odd_numbers.py ├── The_Millionth_Fibonacci_Kata.py ├── Validate_Sudoku_with_size_NxN.py ├── Vigenere_Autokey_Cipher_Helper.py ├── Where_my_anagrams_at.py ├── __init__.py └── text_align_justify.py └── tests ├── All_that_is_open_must_be_closed.py └── __init__.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | 5 | # C extensions 6 | *.so 7 | 8 | # Distribution / packaging 9 | .Python 10 | env/ 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | *.egg-info/ 23 | .installed.cfg 24 | *.egg 25 | 26 | # PyInstaller 27 | # Usually these files are written by a python script from a template 28 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 29 | *.manifest 30 | *.spec 31 | 32 | # Installer logs 33 | pip-log.txt 34 | pip-delete-this-directory.txt 35 | 36 | # Unit test / coverage reports 37 | htmlcov/ 38 | .tox/ 39 | .coverage 40 | .coverage.* 41 | .cache 42 | nosetests.xml 43 | coverage.xml 44 | *,cover 45 | 46 | # Translations 47 | *.mo 48 | *.pot 49 | 50 | # Django stuff: 51 | *.log 52 | 53 | # Sphinx documentation 54 | docs/_build/ 55 | 56 | # PyBuilder 57 | target/ 58 | .idea/ 59 | .vscode/ 60 | main.py 61 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Liangyaozhi 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This is my Python3 solutions on [Codewars](https://www.codewars.com/). 2 | 3 | ### [My profile](https://www.codewars.com/users/Peter-Liang) 4 | ![Codewars Rank](https://www.codewars.com/users/Peter-Liang/badges/large) 5 | -------------------------------------------------------------------------------- /__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Peter-Liang/CodeWars-Python/80628427ed1b5bb3c8ecd2a494495ae7c81e2063/__init__.py -------------------------------------------------------------------------------- /solutions/ASCII85_Encoding_And_Decoding.py: -------------------------------------------------------------------------------- 1 | """ 2 | ASCII85 Encoding & Decoding 3 | http://www.codewars.com/kata/5277dc3ff4bfbd9a36000c1c/train/python 4 | """ 5 | 6 | 7 | def toAscii85(data): 8 | hex_str = '' 9 | result = '' 10 | for c in data: 11 | hex_str += format(ord(c), '02x') 12 | index = 0 13 | while index < len(hex_str): 14 | padding = max(((index + 8) - len(hex_str)) / 2, 0) 15 | encode_block = hex_str[index:index + 8] if padding == 0 else hex_str[index:] + '00' * padding 16 | if encode_block == '0' * 8 and padding == 0: 17 | result += 'z' 18 | else: 19 | encode_block_int = int(encode_block, 16) / (85 ** padding) 20 | encode_block_result = '' 21 | for _ in range(5 - padding): 22 | encode_block_int, remainder = divmod(encode_block_int, 85) 23 | encode_block_result = chr(remainder + 33) + encode_block_result 24 | result += encode_block_result 25 | index += 8 26 | return '<~' + result + '~>' 27 | 28 | 29 | def fromAscii85(data): 30 | result = '' 31 | illegal_character = ['\n', ' ', '\0', '\t'] 32 | for c in illegal_character: 33 | data = data.replace(c, '') 34 | data = data[2:-2] 35 | 36 | index = 0 37 | while index < len(data): 38 | if data[index] == 'z': 39 | result += '\0' * 4 40 | index += 1 41 | else: 42 | padding = max(index + 5 - len(data), 0) 43 | encoded_block = data[index:index + 5] if padding == 0 else data[index:] + 'u' * padding 44 | encoded_int = 0 45 | for i, c in enumerate(encoded_block[::-1]): 46 | encoded_int += (ord(c) - 33) * (85 ** i) 47 | encoded_byte = format(encoded_int, '08x') 48 | if padding > 0: 49 | encoded_byte = encoded_byte[:-padding * 2] 50 | index += 5 51 | result += ''.join([chr(int(encoded_byte[i:i + 2], 16)) for i in range(0, len(encoded_byte), 2)]) 52 | return result 53 | -------------------------------------------------------------------------------- /solutions/All_that_is_open_must_be_closed.py: -------------------------------------------------------------------------------- 1 | """ 2 | All that is open must be closed... 3 | http://www.codewars.com/kata/55679d644c58e2df2a00009c/train/python 4 | """ 5 | def is_balanced(source, caps): 6 | count = {} 7 | stack = [] 8 | for c in source: 9 | if c in caps: 10 | i = caps.index(c) 11 | if i % 2 == 0: 12 | if caps[i] == caps[i + 1]: 13 | if caps[i] in count: 14 | count[caps[i]] += 1 15 | else: 16 | count[caps[i]] = 1 17 | else: 18 | stack.append(c) 19 | else: 20 | if caps[i - 1] == caps[i]: 21 | if caps[i] in count: 22 | count[caps[i]] += 1 23 | else: 24 | count[caps[i]] = 1 25 | else: 26 | if len(stack) == 0 or stack.pop() != caps[i - 1]: 27 | return False 28 | return (len(stack) == 0) and ((sum([v for k, v in count.items()])) % 2 == 0) 29 | 30 | print(is_balanced("(Sensei says yes!)", "()") == True) 31 | print(is_balanced("(Sensei says no!", "()") == False) 32 | 33 | print(is_balanced("(Sensei [says] yes!)", "()[]") == True) 34 | print(is_balanced("(Sensei [says) no!]", "()[]") == False) 35 | 36 | print(is_balanced("Sensei says -yes-!", "--") == True) 37 | print(is_balanced("Sensei -says no!", "--") == False) 38 | -------------------------------------------------------------------------------- /solutions/Battleship_field_validator.py: -------------------------------------------------------------------------------- 1 | """ 2 | Battleship field validator 3 | http://www.codewars.com/kata/52bb6539a4cf1b12d90005b7/train/python 4 | """ 5 | 6 | 7 | def validateBattlefield(field): 8 | ships = {} 9 | for row in range(len(field[0])): 10 | for col in range(len(field)): 11 | if field[row][col] == 1: 12 | try: 13 | result = getShipSize(row, col, field) 14 | ships[result] = ships.get(result, 0) + 1 15 | except ValueError: 16 | return False 17 | return ships.get(4, 0) == 1 and ships.get(3, 0) == 2 and ships.get(2, 0) == 3 and ships.get(1, 0) == 4 18 | 19 | 20 | def isCornerValid(row, col, field): 21 | if row == len(field) - 1: 22 | return True 23 | if col == 0: 24 | return field[row + 1][col + 1] != 1 25 | if col == len(field[0]) - 1: 26 | return field[row + 1][col - 1] != 1 27 | return field[row + 1][col + 1] != 1 and field[row + 1][col - 1] != 1 28 | 29 | 30 | def isSideValid(row, col, field): 31 | if row == len(field) - 1 or col == len(field[0]) - 1: 32 | return True 33 | return not (field[row + 1][col] != 0 and field[row][col + 1] != 0) 34 | 35 | 36 | def isValidPoint(row, col, field): 37 | return isCornerValid(row, col, field) and isSideValid(row, col, field) 38 | 39 | 40 | def getShipSize(row, col, field): 41 | if not isValidPoint(row, col, field): 42 | raise ValueError('Invalid disposition') 43 | field[row][col] = -1 44 | if row != len(field) and field[row + 1][col] == 1: 45 | return 1 + getShipSize(row + 1, col, field) 46 | if col != len(field[0]) and field[row][col + 1] == 1: 47 | return 1 + getShipSize(row, col + 1, field) 48 | return 1 -------------------------------------------------------------------------------- /solutions/Bowling_score_calculator.py: -------------------------------------------------------------------------------- 1 | """ 2 | Bowling score calculator 3 | http://www.codewars.com/kata/5427db696f30afd74b0006a3/train/python 4 | """ 5 | 6 | 7 | def bowling_score(rolls): 8 | """Compute the total score for a player's game of bowling.""" 9 | 10 | total = 0 11 | frame = 0 12 | newframe = True 13 | for index, roll in enumerate(rolls): 14 | if frame == 10: 15 | break 16 | if roll == 10: 17 | total += rolls[index + 1] 18 | total += rolls[index + 2] 19 | frame += 1 20 | elif not newframe: 21 | if rolls[index - 1] + roll == 10: 22 | total += rolls[index + 1] 23 | frame += 1 24 | newframe = True 25 | else: 26 | newframe = False 27 | total += roll 28 | 29 | return total -------------------------------------------------------------------------------- /solutions/Breadcrumb_Generator.py: -------------------------------------------------------------------------------- 1 | """ 2 | 4 kyu: Breadcrumb Generator 3 | http://www.codewars.com/kata/breadcrumb-generator/train/python 4 | 5 | 6 | As breadcrumb menùs are quite popular today, I won't digress much on explaining them, leaving the wiki link to do all the dirty work in my place. 7 | 8 | What might not be so trivial is instead to get a decent breadcrumb from your current url. For this kata, your purpose is to create a function that takes a url, strips the first part (labelling it always HOME) and then builds it making each element but the last a element linking to the relevant path; last has to be a element getting the active class. 9 | 10 | All elements need to be turned to uppercase and separated by a separator, given as the second parameter of the function; the last element can terminate in some common extension like .html, .htm, .php or .asp; if the name of the last element is index.something, you treat it as if it wasn't there, sending users automatically to the upper level folder. 11 | 12 | A few examples can be more helpful than thousands of words of explanation, so here you have them: 13 | 14 | generate_bc("mysite.com/pictures/holidays.html", " : ") == 'HOME : PICTURES : HOLIDAYS' 15 | generate_bc("www.codewars.com/users/GiacomoSorbi", " / ") == 'HOME / USERS / GIACOMOSORBI' 16 | generate_bc("www.microsoft.com/docs/index.htm", " * ") == 'HOME * DOCS' 17 | Seems easy enough? 18 | 19 | Well, probably not so much, but we have one last extra rule: if one element (other than the root/home) is longer than 30 characters, you have to shorten it, acronymizing it (i.e.: taking just the initials of every word); url will be always given in the format this-is-an-element-of-the-url and you should ignore words in this array while acronymizing: ["the","of","in","from","by","with","and", "or", "for", "to", "at", "a"]; a url composed of more words separated by - and equal or less than 30 characters long needs to be just uppercased with hyphens replaced by spaces. 20 | 21 | Ignore anchors (www.url.com#lameAnchorExample) and parameters (www.url.com?codewars=rocks&pippi=rocksToo) when present. 22 | 23 | Examples: 24 | 25 | generate_bc("mysite.com/very-long-url-to-make-a-silly-yet-meaningful-example/example.htm", " > ") == 'HOME > VLUMSYME > EXAMPLE' 26 | generate_bc("www.very-long-site_name-to-make-a-silly-yet-meaningful-example.com/users/giacomo-sorbi", " + ") == 'HOME + USERS + GIACOMO SORBI' 27 | You will always be provided valid url to webpages in common formats, so you probably shouldn't bother validating them. 28 | 29 | If you like to test yourself with actual work/interview related kata, please also consider this one about building a string filter for Angular.js. 30 | 31 | Special thanks to the colleague that, seeing my code and commenting that I worked on that as if it was I was on CodeWars, made me realize that it could be indeed a good idea for a kata :) 32 | """ 33 | 34 | 35 | def generate_bc(url, separator): 36 | if '//' in url: 37 | url = url[url.index('//') + 2:] 38 | 39 | url = url.rstrip('/') 40 | 41 | try: 42 | for i, c in enumerate(url): 43 | if c in ['?', '#']: 44 | url = url[0:i] 45 | break 46 | 47 | menus = url.split('/')[1:] 48 | if menus and 'index.' == menus[-1][0:6]: 49 | menus = menus[:-1] 50 | if not menus: 51 | return 'HOME' 52 | 53 | breadcrumb = 'HOME' 54 | 55 | for i, e in enumerate(menus[:-1]): 56 | breadcrumb += separator + '{}'.format('/'.join(menus[:i + 1]), get_element_name(e)) 57 | 58 | breadcrumb += separator + '{}'.format(get_element_name(menus[-1])) 59 | return breadcrumb 60 | except: 61 | return url 62 | 63 | 64 | ignore_words = ["the", "of", "in", "from", "by", "with", "and", "or", "for", "to", "at", "a"] 65 | 66 | 67 | def get_element_name(element): 68 | acronyms = element.split('-') 69 | for i, c in enumerate(acronyms[-1]): 70 | if c == '.': 71 | acronyms[-1] = acronyms[-1][:i] 72 | break 73 | 74 | if len(element) > 30: 75 | for i, c in reversed(list(enumerate(acronyms))): 76 | if c in ignore_words: 77 | acronyms.pop(i) 78 | return ''.join([s[0].upper() for s in acronyms]) 79 | 80 | return ' '.join([s.upper() for s in acronyms]) 81 | 82 | 83 | 84 | assert generate_bc("mysite.com/pictures/holidays.html", 85 | " : ") == 'HOME : PICTURES : HOLIDAYS' 86 | assert generate_bc("www.codewars.com/users/GiacomoSorbi?ref=CodeWars", 87 | " / ") == 'HOME / USERS / GIACOMOSORBI' 88 | assert generate_bc("www.microsoft.com/important/confidential/docs/index.htm#top", 89 | " * ") == 'HOME * IMPORTANT * CONFIDENTIAL * DOCS' 90 | assert generate_bc("mysite.com/very-long-url-to-make-a-silly-yet-meaningful-example/example.asp", 91 | " > ") == 'HOME > VLUMSYME > EXAMPLE' 92 | assert generate_bc("www.very-long-site_name-to-make-a-silly-yet-meaningful-example.com/users/giacomo-sorbi", 93 | " + ") == 'HOME + USERS + GIACOMO SORBI' 94 | 95 | # print("https://www.linkedin.com/in/giacomosorbi".index('//')) 96 | # print(generate_bc("https://www.linkedin.com/in/giacomosorbi", " * ")) 97 | assert generate_bc("https://www.linkedin.com/in/giacomosorbi", 98 | " * ") == 'HOME * IN * GIACOMOSORBI' 99 | print(generate_bc("www.agcpartners.co.uk", " * ")) 100 | assert generate_bc("www.agcpartners.co.uk", " * ") == 'HOME' 101 | assert generate_bc("www.agcpartners.co.uk/", " * ") == 'HOME' 102 | assert generate_bc("www.agcpartners.co.uk/index.html", " * ") == 'HOME' 103 | assert generate_bc("www.google.ca/index.php", " * ") == 'HOME' 104 | -------------------------------------------------------------------------------- /solutions/Calculator.py: -------------------------------------------------------------------------------- 1 | """ 2 | Calculator 3 | http://www.codewars.com/kata/5235c913397cbf2508000048/train/python 4 | """ 5 | import operator 6 | 7 | 8 | class Calculator(object): 9 | operands = {'+': operator.add, '-': operator.sub, '*': operator.mul, '/': operator.div} 10 | 11 | def __init__(self): 12 | self.result = [] 13 | 14 | def evaluate(self, string): 15 | self.result = string.split(' ') 16 | self._loop('*/') 17 | self._loop('+-') 18 | return float(self.result[0]) 19 | 20 | def _loop(self, operators): 21 | i = 1 22 | while i < len(self.result) - 1: 23 | if self.result[i] in operators: 24 | self.result[i - 1] = str(self.__class__.operands[self.result[i]](float(self.result[i - 1]), float(self.result[i + 1]))) 25 | self.result.pop(i + 1) 26 | self.result.pop(i) 27 | continue 28 | i += 1 29 | -------------------------------------------------------------------------------- /solutions/Complementary_DNA.py: -------------------------------------------------------------------------------- 1 | """ 2 | 7 kyu: Complementary DNA 3 | http://www.codewars.com/kata/554e4a2f232cdd87d9000038/train/python 4 | """ 5 | def DNA_strand(dna): 6 | complements = { 7 | 'A': 'T', 8 | 'T': 'A', 9 | 'G': 'C', 10 | 'C': 'G', 11 | } 12 | return ''.join([complements[c] for c in dna]) 13 | 14 | 15 | print(DNA_strand("AAAA")) 16 | print(DNA_strand("ATTGC")) 17 | print(DNA_strand("GTAT")) -------------------------------------------------------------------------------- /solutions/Consecutive_strings.py: -------------------------------------------------------------------------------- 1 | """ 2 | 6 kyu: Consecutive strings 3 | http://www.codewars.com/kata/consecutive-strings/train/python 4 | 5 | You are given an array strarr of strings and an integer k. 6 | Your task is to return the first longest string consisting of k consecutive strings taken in the array. 7 | 8 | #Example: longest_consec(["zone", "abigail", "theta", "form", "libe", "zas", "theta", "abigail"], 2) --> "abigailtheta" 9 | 10 | n being the length of the string array, if n = 0 or k > n or k <= 0 return "". 11 | """ 12 | 13 | 14 | def longest_consec(strarr, k): 15 | n = len(strarr) 16 | if n == 0 or k > n or k <= 0: 17 | return '' 18 | 19 | longest = index = 0 20 | for i in range(n - k + 1): 21 | length = sum([len(s) for s in strarr[i: i + k]]) 22 | if length > longest: 23 | longest = length 24 | index = i 25 | 26 | return ''.join(strarr[index: index + k]) 27 | 28 | 29 | assert longest_consec(["zone", "abigail", "theta", "form", "libe", "zas"], 2) == "abigailtheta" 30 | -------------------------------------------------------------------------------- /solutions/Convert_number_to_reversed_array_of_digits.py: -------------------------------------------------------------------------------- 1 | """ 2 | 8 kyu: Convert number to reversed array of digits 3 | 4 | Convert number to reversed array of digits 5 | 6 | Given a random number: 7 | 8 | C#: long; 9 | C++: unsigned long; 10 | You have to return the digits of this number within an array in reverse order. 11 | 12 | Example: 13 | 14 | 348597 => [7,9,5,8,4,3] 15 | """ 16 | 17 | 18 | def digitize(n): 19 | return [int(s) for s in str(n)][::-1] 20 | 21 | 22 | print(digitize(35231)) 23 | -------------------------------------------------------------------------------- /solutions/Conway_s_Game_of_Life_Unlimited_Edition.py: -------------------------------------------------------------------------------- 1 | """ 2 | Conway's Game of Life - Unlimited Edition 3 | http://www.codewars.com/kata/52423db9add6f6fc39000354/train/python 4 | """ 5 | 6 | from copy import deepcopy 7 | 8 | 9 | def get_generation(cells, generations): 10 | origin = deepcopy(cells) 11 | if generations == 0: 12 | return origin 13 | if generations > 1: 14 | origin = get_generation(origin, generations - 1) 15 | 16 | for row in origin: 17 | row.insert(0, 0) 18 | row.append(0) 19 | origin.insert(0, [0] * len(origin[0])) 20 | origin.append([0] * len(origin[0])) 21 | 22 | result = deepcopy(origin) 23 | for r in range(len(origin)): 24 | for c in range(len(origin[0])): 25 | neighbours = get_living_neighbours(origin, r, c) 26 | if neighbours > 3 or neighbours < 2: 27 | result[r][c] = 0 28 | elif neighbours == 3: 29 | result[r][c] = 1 30 | 31 | trim_result(result) 32 | 33 | return result 34 | 35 | 36 | def trim_result(result): 37 | while is_row_all_empty(result[0]): 38 | result.pop(0) 39 | while is_row_all_empty(result[-1]): 40 | result.pop() 41 | start_empty, end_empty = True, True 42 | while start_empty or end_empty: 43 | for r in result: 44 | if r[0] != 0: 45 | start_empty = False 46 | if r[-1] != 0: 47 | end_empty = False 48 | for r in result: 49 | if start_empty: 50 | r.pop(0) 51 | if end_empty: 52 | r.pop() 53 | 54 | 55 | def is_row_all_empty(row): 56 | return sum(row) == 0 57 | 58 | 59 | def get_living_neighbours(cells, row, col): 60 | livings = 0 61 | for r in [-1, 0, 1]: 62 | if 0 <= row + r <= len(cells) - 1: 63 | for c in [-1, 0, 1]: 64 | if 0 <= col + c <= len(cells[0]) - 1: 65 | if c == 0 and r == 0: 66 | continue 67 | livings += cells[row + r][col + c] 68 | return livings -------------------------------------------------------------------------------- /solutions/Counting_power_sets.py: -------------------------------------------------------------------------------- 1 | """ 2 | Counting power sets 3 | http://www.codewars.com/kata/54381f0b6f032f933c000108/train/python 4 | """ 5 | 6 | def powers(lst): 7 | return 2 ** len(lst) -------------------------------------------------------------------------------- /solutions/Luck_check.py: -------------------------------------------------------------------------------- 1 | """ 2 | Luck check 3 | http://www.codewars.com/kata/5314b3c6bb244a48ab00076c/train/python 4 | """ 5 | 6 | 7 | def luck_check(string): 8 | i, j, total = 0, len(string) - 1, 0 9 | while i != j and i < j: 10 | total += (int(string[i]) - int(string[j])) 11 | i += 1 12 | j -= 1 13 | return total == 0 -------------------------------------------------------------------------------- /solutions/Maximum_subarray_sum.py: -------------------------------------------------------------------------------- 1 | """ 2 | 5 kyu: Maximum subarray sum 3 | http://www.codewars.com/kata/maximum-subarray-sum/train/python 4 | 5 | The maximum sum subarray problem consists in finding the maximum sum of a contiguous subsequence in an array or list of integers: 6 | 7 | maxSequence([-2, 1, -3, 4, -1, 2, 1, -5, 4]) 8 | # should be 6: [4, -1, 2, 1] 9 | Easy case is when the list is made up of only positive numbers and the maximum sum is the sum of the whole array. If the list is made up of only negative numbers, return 0 instead. 10 | 11 | Empty list is considered to have zero greatest sum. Note that the empty list or array is also a valid sublist/subarray. 12 | """ 13 | 14 | 15 | def maxSequence(arr): 16 | maximum = 0 17 | local_maximum = 0 18 | for i in arr: 19 | if local_maximum > 0: 20 | local_maximum += i 21 | if local_maximum < 0: 22 | local_maximum = 0 23 | elif local_maximum > maximum: 24 | maximum = local_maximum 25 | elif i > 0: 26 | local_maximum += i 27 | 28 | return maximum 29 | 30 | 31 | assert maxSequence([-2, 1, -3, 4, -1, 2, 1, -5, 4]) == 6 32 | -------------------------------------------------------------------------------- /solutions/Merged_String_Checker.py: -------------------------------------------------------------------------------- 1 | """ 2 | Merged String Checker 3 | 4 | http://www.codewars.com/kata/54c9fcad28ec4c6e680011aa/train/python 5 | """ 6 | 7 | 8 | def is_merge(s, part1, part2): 9 | result = list(s) 10 | 11 | def findall(part): 12 | pointer = 0 13 | for c in part: 14 | found = False 15 | for i in range(pointer, len(result)): 16 | if result[i] == c: 17 | pointer = i + 1 18 | found = True 19 | break 20 | if not found: 21 | return False 22 | return True 23 | 24 | def removechar(part): 25 | for c in part: 26 | if c in result: 27 | result.remove(c) 28 | else: 29 | return False 30 | return True 31 | 32 | return findall(part1) and findall(part2) and removechar(part1 + part2) and len(result) == 0 -------------------------------------------------------------------------------- /solutions/Metaclasses_Simple_Django_Models.py: -------------------------------------------------------------------------------- 1 | """ 2 | 3 kyu Metaclasses - Simple Django Models 3 | https://www.codewars.com/kata/54b26b130786c9f7ed000555/train/python 4 | """ 5 | 6 | import datetime 7 | import re 8 | from typing import Any 9 | 10 | 11 | class ValidationError(Exception): 12 | pass 13 | 14 | 15 | class ValidationField: 16 | def __init__(self, default=None, blank=False, **kwargs): 17 | self.default = default 18 | self.blank = blank 19 | self.__dict__.update(kwargs) 20 | 21 | def validate(self, val): 22 | if val is None and self.blank is False: 23 | raise ValidationError 24 | 25 | 26 | class CharField(ValidationField): 27 | def __init__(self, min_length=0, max_length=None, **kwargs): 28 | super().__init__(**kwargs) 29 | self.min_length = min_length 30 | self.max_length = max_length 31 | 32 | def validate(self, val): 33 | super().validate(val) 34 | 35 | if val is None: 36 | return 37 | 38 | if not isinstance(val, str): 39 | raise ValidationError 40 | 41 | if self.min_length is not None and len(val) < self.min_length: 42 | raise ValidationError 43 | 44 | if self.max_length is not None and self.max_length < len(val): 45 | raise ValidationError 46 | 47 | 48 | class IntegerField(ValidationField): 49 | def __init__(self, min_value=None, max_value=None, **kwargs): 50 | super().__init__(**kwargs) 51 | self.min_value = min_value 52 | self.max_value = max_value 53 | 54 | def validate(self, val): 55 | super().validate(val) 56 | 57 | if val is None: 58 | return 59 | 60 | if isinstance(val, bool): 61 | raise ValidationError 62 | 63 | if not isinstance(val, int): 64 | raise ValidationError 65 | 66 | if self.min_value is not None and val < self.min_value: 67 | raise ValidationError 68 | 69 | if self.max_value is not None and self.max_value < val: 70 | raise ValidationError 71 | 72 | 73 | class BooleanField(ValidationField): 74 | def __init__(self, **kwargs): 75 | super().__init__(**kwargs) 76 | 77 | def validate(self, val): 78 | super().validate(val) 79 | 80 | if val is None: 81 | return 82 | 83 | if not isinstance(val, bool): 84 | raise ValidationError 85 | 86 | 87 | class DateTimeField(ValidationField): 88 | def __init__(self, default=None, auto_now=False, **kwargs): 89 | super().__init__(**kwargs) 90 | self.auto_now = auto_now 91 | self._default = default 92 | 93 | def __getattribute__(self, name: str) -> Any: 94 | if name == 'default': 95 | if self._default is None and self.auto_now is True: 96 | return datetime.datetime.now() 97 | else: 98 | return self._default 99 | return super().__getattribute__(name) 100 | 101 | def validate(self, val): 102 | super().validate(val) 103 | 104 | if val is None: 105 | return 106 | 107 | if not isinstance(val, datetime.datetime): 108 | raise ValidationError 109 | 110 | 111 | class EmailField(ValidationField): 112 | def __init__(self, min_length=0, max_length=None, **kwargs): 113 | super().__init__(**kwargs) 114 | self.min_length = min_length 115 | self.max_length = max_length 116 | 117 | def validate(self, val): 118 | super().validate(val) 119 | 120 | if val is None: 121 | return 122 | 123 | if not isinstance(val, str): 124 | raise ValidationError 125 | 126 | if self.min_length is not None and len(val) < self.min_length: 127 | raise ValidationError 128 | 129 | if self.max_length is not None and self.max_length < len(val): 130 | raise ValidationError 131 | 132 | if not re.match(r"[^@]+@[^@]+\.[^@]+", val): 133 | raise ValidationError 134 | 135 | 136 | class Model: 137 | def __init__(self, **kwargs): 138 | for d in [d for d in dir(self.__class__) if d.startswith('_f_')]: 139 | attr = getattr(self.__class__, d) 140 | setattr(self, d[3:], attr.default) 141 | 142 | for k, v in kwargs.items(): 143 | setattr(self, k, v) 144 | 145 | def __init_subclass__(cls) -> None: 146 | for d in [d for d in dir(cls) if not d.startswith('_')]: 147 | attr = getattr(cls, d) 148 | if isinstance(attr, (CharField, EmailField, 149 | IntegerField, BooleanField, DateTimeField)): 150 | delattr(cls, d) 151 | setattr(cls, '_f_' + d, attr) 152 | super().__init_subclass__() 153 | 154 | def validate(self): 155 | for d in [d for d in dir(self.__class__) if d.startswith('_f_') and hasattr(self, d[3:])]: 156 | class_attr = getattr(self.__class__, d) 157 | instance_attr = getattr(self, d[3:]) 158 | class_attr.validate(instance_attr) 159 | -------------------------------------------------------------------------------- /solutions/Miles_per_gallon_to_kilometers_per_liter.py: -------------------------------------------------------------------------------- 1 | """ 2 | Miles per gallon to kilometers per liter 3 | http://www.codewars.com/kata/557b5e0bddf29d861400005d/train/python 4 | """ 5 | 6 | 7 | def converter(mpg): 8 | return float("{0:.2f}".format(mpg / 4.54609188 * 1.609344)) -------------------------------------------------------------------------------- /solutions/Mony_mony_mony.py: -------------------------------------------------------------------------------- 1 | """ 2 | 7 kyu: Money, Money, Money 3 | http://www.codewars.com/kata/563f037412e5ada593000114/train/python 4 | """ 5 | 6 | 7 | def calculate_years(principal, interest, tax, desired): 8 | if principal >= desired: 9 | return 0 10 | result = principal * interest * (1 - tax) + principal 11 | return 1 if result >= desired else 1 + calculate_years(result, interest, tax, desired) 12 | -------------------------------------------------------------------------------- /solutions/Morse_encoding.py: -------------------------------------------------------------------------------- 1 | """ 2 | 3 kyu: Morse Encoding 3 | https://www.codewars.com/kata/morse-encoding/train/python 4 | """ 5 | class Morse: 6 | 7 | @classmethod 8 | def encode(self, message): 9 | words = message.split(' ') 10 | code = ('0' * 7).join(['000'.join([Morse.alpha[c.upper()] 11 | for c in word]) for word in words]) 12 | result = [] 13 | for i in range(0, len(code), 32): 14 | num = code[i:i + 32].ljust(32, '0') 15 | result.append(int(num[1:], 2) - 16 | (0 if num[0] == '0' else Morse.neg)) 17 | return result 18 | 19 | @classmethod 20 | def decode(self, array): 21 | code = '' 22 | for num in array: 23 | if num < 0: 24 | num += Morse.neg 25 | code += '1' + bin(num)[2:].rjust(31, '0') 26 | else: 27 | code += bin(num)[2:].rjust(32, '0') 28 | code = code.rstrip('0') 29 | result = ' '.join([''.join([next(k for k, v in Morse.alpha.items() if v == char) 30 | for char in words.split('000')]) for words in code.split('0' * 7)]) 31 | 32 | return result 33 | 34 | neg = 1 << 31 35 | 36 | alpha = { 37 | 'A': '10111', 38 | 'B': '111010101', 39 | 'C': '11101011101', 40 | 'D': '1110101', 41 | 'E': '1', 42 | 'F': '101011101', 43 | 'G': '111011101', 44 | 'H': '1010101', 45 | 'I': '101', 46 | 'J': '1011101110111', 47 | 'K': '111010111', 48 | 'L': '101110101', 49 | 'M': '1110111', 50 | 'N': '11101', 51 | 'O': '11101110111', 52 | 'P': '10111011101', 53 | 'Q': '1110111010111', 54 | 'R': '1011101', 55 | 'S': '10101', 56 | 'T': '111', 57 | 'U': '1010111', 58 | 'V': '101010111', 59 | 'W': '101110111', 60 | 'X': '11101010111', 61 | 'Y': '1110101110111', 62 | 'Z': '11101110101', 63 | '0': '1110111011101110111', 64 | '1': '10111011101110111', 65 | '2': '101011101110111', 66 | '3': '1010101110111', 67 | '4': '10101010111', 68 | '5': '101010101', 69 | '6': '11101010101', 70 | '7': '1110111010101', 71 | '8': '111011101110101', 72 | '9': '11101110111011101', 73 | '.': '10111010111010111', 74 | ',': '1110111010101110111', 75 | '?': '101011101110101', 76 | "'": '1011101110111011101', 77 | '!': '1110101110101110111', 78 | '/': '1110101011101', 79 | '(': '111010111011101', 80 | ')': '1110101110111010111', 81 | '&': '10111010101', 82 | ':': '11101110111010101', 83 | ';': '11101011101011101', 84 | '=': '1110101010111', 85 | '+': '1011101011101', 86 | '-': '111010101010111', 87 | '_': '10101110111010111', 88 | '"': '101110101011101', 89 | '$': '10101011101010111', 90 | '@': '10111011101011101', 91 | ' ': '0'} 92 | 93 | if __name__ == '__main__': 94 | print(Morse.encode('hello world')) 95 | # print(Morse.encode('EEEEEEEIE')) 96 | # print(Morse.decode([-2004318070, 536870912])) 97 | print(Morse.decode([-1440552402, -1547992901, -1896993141, -1461059584])) 98 | -------------------------------------------------------------------------------- /solutions/Multiples_of_3_and_5.py: -------------------------------------------------------------------------------- 1 | """ 2 | Multiples of 3 and 5 3 | 4 | If we list all the natural numbers below 10 that are multiples of 3 or 5, we get 3, 5, 6 and 9. The sum of these multiples is 23. 5 | Finish the solution so that it returns the sum of all the multiples of 3 or 5 below the number passed in. 6 | Courtesy of ProjectEuler.net 7 | """ 8 | 9 | 10 | def solution(number): 11 | result = [] 12 | for i in range(1, number): 13 | if i % 3 == 0 or i % 5 == 0: 14 | result.append(i) 15 | 16 | return sum(result) -------------------------------------------------------------------------------- /solutions/Pascal_s_Triangle_2.py: -------------------------------------------------------------------------------- 1 | def pascal(p): 2 | result = [] 3 | for i in range(p): 4 | if i == 0: 5 | result.append([1]) 6 | else: 7 | result.append([1] + [result[i - 1][j] + result[i - 1][j + 1] for j in range(len(result[i - 1]) - 1)] + [1]) 8 | return result -------------------------------------------------------------------------------- /solutions/Permutations.py: -------------------------------------------------------------------------------- 1 | """ 2 | http://www.codewars.com/kata/5254ca2719453dcc0b00027d/train/python 3 | 4 | 5 | In this kata you have to create all permutations of an input string and remove duplicates, if present. 6 | This means, you have to shuffle all letters from the input in all possible orders. 7 | 8 | Examples: 9 | 10 | permutations('a'); # ['a'] 11 | permutations('ab'); # ['ab', 'ba'] 12 | permutations('aabb'); # ['aabb', 'abab', 'abba', 'baab', 'baba', 'bbaa'] 13 | 14 | """ 15 | 16 | 17 | def permutations(string): 18 | result = set([string]) 19 | if len(string) == 2: 20 | result.add(string[1] + string[0]) 21 | 22 | elif len(string) > 2: 23 | for i, c in enumerate(string): 24 | for s in permutations(string[:i] + string[i + 1:]): 25 | result.add(c + s) 26 | 27 | return list(result) -------------------------------------------------------------------------------- /solutions/Persistent_Bugger.py: -------------------------------------------------------------------------------- 1 | """ 2 | 6 kyu: Persistent Bugger. 3 | Write a function, persistence, 4 | that takes in a positive parameter num and returns its multiplicative persistence, 5 | which is the number of times you must multiply the digits in num until you reach a single digit. 6 | 7 | For example: 8 | 9 | persistence(39) => 3 # Because 3*9 = 27, 2*7 = 14, 1*4=4 10 | # and 4 has only one digit. 11 | 12 | persistence(999) => 4 # Because 9*9*9 = 729, 7*2*9 = 126, 13 | # 1*2*6 = 12, and finally 1*2 = 2. 14 | 15 | persistence(4) => 0 # Because 4 is already a one-digit number. 16 | """ 17 | 18 | import functools 19 | 20 | 21 | def persistence(n): 22 | times = 0 23 | 24 | while n > 9: 25 | n = functools.reduce(lambda x, y: x * y, [int(i) for i in str(n)]) 26 | times += 1 27 | 28 | return times 29 | -------------------------------------------------------------------------------- /solutions/Playing_with_digits.py: -------------------------------------------------------------------------------- 1 | """ 2 | Playing with digits 3 | http://www.codewars.com/kata/playing-with-digits/train/python 4 | """ 5 | 6 | 7 | def dig_pow(n, p): 8 | num = str(n) 9 | total = sum([int(num[i]) ** (p + i) for i in range(len(num))]) 10 | return total / n if (total % n) == 0 else -1 11 | -------------------------------------------------------------------------------- /solutions/Prefill_an_Array.py: -------------------------------------------------------------------------------- 1 | """ 2 | Prefill an Array 3 | 4 | http://www.codewars.com/kata/54129112fb7c188740000162/train/python 5 | """ 6 | 7 | 8 | def prefill(n, v='undefined'): 9 | if n is None: 10 | raise TypeError("None is invalid") 11 | try: 12 | n = int(n) 13 | except ValueError: 14 | raise TypeError(n + " is invalid") 15 | return [v] * n -------------------------------------------------------------------------------- /solutions/Prime_number_decompositions.py: -------------------------------------------------------------------------------- 1 | """ 2 | Prime number decompositions 3 | http://www.codewars.com/kata/prime-number-decompositions/train/python 4 | """ 5 | 6 | 7 | def getAllPrimeFactors(n): 8 | if n == 1: 9 | return [1] 10 | 11 | result = [] 12 | if isvalidparameter(n): 13 | factor = 2 14 | while n > 1: 15 | while n % factor == 0: 16 | n /= factor 17 | result.append(factor) 18 | factor += 1 19 | return result 20 | 21 | 22 | def getUniquePrimeFactorsWithCount(n): 23 | result = [[], []] 24 | if isvalidparameter(n): 25 | factors = getAllPrimeFactors(n) 26 | for f in factors: 27 | if f in result[0]: 28 | result[1][-1] += 1 29 | else: 30 | result[0].append(f) 31 | result[1].append(1) 32 | return result 33 | 34 | 35 | def getUniquePrimeFactorsWithProducts(n): 36 | result = [] 37 | if isvalidparameter(n): 38 | factors = getUniquePrimeFactorsWithCount(n) 39 | result = map(lambda x: x[0] ** x[1], zip(factors[0], factors[1])) 40 | return result 41 | 42 | 43 | def isvalidparameter(n): 44 | return isinstance(n, int) and n > 0 -------------------------------------------------------------------------------- /solutions/Square_into_Squares_Protect_trees.py: -------------------------------------------------------------------------------- 1 | """ 2 | Square into Squares. Protect trees! 3 | 4 | http://www.codewars.com/kata/54eb33e5bc1a25440d000891/train/python 5 | 6 | """ 7 | 8 | 9 | def decompose(n): 10 | goal = 0 11 | result = [n] 12 | while result: 13 | current = result.pop() 14 | goal += current ** 2 15 | for i in range(current - 1, 0, -1): 16 | if goal - (i ** 2) >= 0: 17 | goal -= i ** 2 18 | result.append(i) 19 | if goal == 0: 20 | result.sort() 21 | return result 22 | return None 23 | -------------------------------------------------------------------------------- /solutions/String_Shortener_shrink.py: -------------------------------------------------------------------------------- 1 | """ 2 | String Shortener (shrink) 3 | http://www.codewars.com/kata/557d18803802e873170000a0/train/python 4 | """ 5 | 6 | 7 | def shorten(string, length, glue="..."): 8 | if length >= len(string): 9 | return string 10 | if length == len(glue) + 1 or len(glue) > length: 11 | return string[:length] 12 | shortened = length - len(glue) 13 | return string[:int(shortened / 2)] + glue + string[-(int(shortened / 2) + (shortened % 2)):] -------------------------------------------------------------------------------- /solutions/Strip_Comments.py: -------------------------------------------------------------------------------- 1 | """ 2 | Strip Comments 3 | 4 | http://www.codewars.com/kata/51c8e37cee245da6b40000bd/train/python 5 | """ 6 | 7 | 8 | def solution(string, markers): 9 | lines = string.split('\n') 10 | for i, line in enumerate(lines): 11 | for marker in markers: 12 | index = line.find(marker) 13 | if index != -1: 14 | line = line[:index] 15 | lines[i] = line.rstrip(' ') 16 | return '\n'.join(lines) 17 | -------------------------------------------------------------------------------- /solutions/Sum_of_odd_numbers.py: -------------------------------------------------------------------------------- 1 | """ 2 | 7 kyu: Sum of odd numbers 3 | http://www.codewars.com/kata/sum-of-odd-numbers/train/python 4 | """ 5 | 6 | 7 | def row_sum_odd_numbers(n): 8 | start = n ** 2 - (n - 1) 9 | return sum(range(start, start + n * 2, 2)) 10 | 11 | 12 | assert row_sum_odd_numbers(1) == 1 13 | assert row_sum_odd_numbers(2) == 8 14 | assert row_sum_odd_numbers(41) == 68921 15 | -------------------------------------------------------------------------------- /solutions/The_Millionth_Fibonacci_Kata.py: -------------------------------------------------------------------------------- 1 | """ 2 | The Millionth Fibonacci Kata 3 | http://www.codewars.com/kata/53d40c1e2f13e331fc000c26/train/python 4 | """ 5 | 6 | 7 | def fib(n): 8 | """Calculates the nth Fibonacci number""" 9 | if n >= 0: 10 | return fibiter(1, 0, 0, 1, n) 11 | if n < 0: 12 | a, b = 0, 1 13 | for _ in xrange(0, n, -1): 14 | a, b = b - a, a 15 | return a 16 | 17 | 18 | def fibiter(a, b, p, q, count): 19 | if count == 0: 20 | return b 21 | if count % 2 == 0: 22 | return fibiter(a, b, p * p + q * q, q * q + 2 * p * q, count / 2) 23 | else: 24 | return fibiter(b * q + a * q + a * p, b * p + a * q, p, q, count - 1) -------------------------------------------------------------------------------- /solutions/Validate_Sudoku_with_size_NxN.py: -------------------------------------------------------------------------------- 1 | """ 2 | Validate Sudoku with size `NxN` 3 | http://www.codewars.com/kata/540afbe2dc9f615d5e000425/train/python 4 | """ 5 | from math import sqrt 6 | 7 | 8 | class Sudoku(object): 9 | def __init__(self, sudoku): 10 | self.sudoku = sudoku 11 | self.n = 0 12 | self.block_len = 0 13 | 14 | def is_valid(self): 15 | self.n = len(self.sudoku) 16 | try: 17 | self.block_len = int(sqrt(self.n)) 18 | except ValueError: 19 | return False 20 | if any([len(row) != self.n for row in self.sudoku]): 21 | return False 22 | return all([self._is_valid_row(i) 23 | and self._is_valid_column(i) 24 | and self._is_valid_block(i) 25 | for i in range(self.n)]) 26 | 27 | def _is_valid_row(self, row_num): 28 | return self._is_valid_set(set(self.sudoku[row_num])) 29 | 30 | def _is_valid_column(self, column_num): 31 | return self._is_valid_set(set([self.sudoku[r][column_num] for r in range(self.n)])) 32 | 33 | def _is_valid_block(self, block_num): 34 | block_row_num, block_column_num = divmod(block_num, self.block_len) 35 | block_row, block_column = block_row_num * self.block_len, block_column_num * self.block_len 36 | block_set = set() 37 | for r in range(block_row, block_row + self.block_len): 38 | for c in range(block_column, block_column + self.block_len): 39 | block_set.add(self.sudoku[r][c]) 40 | return self._is_valid_set(block_set) 41 | 42 | def _is_valid_set(self, num_set): 43 | return len(num_set) == self.n \ 44 | and max(num_set) == self.n \ 45 | and min(num_set) == 1 \ 46 | and all(type(i) is int for i in num_set) -------------------------------------------------------------------------------- /solutions/Vigenere_Autokey_Cipher_Helper.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | """ 3 | Vigenère Autokey Cipher Helper 4 | http://www.codewars.com/kata/52d2e2be94d26fc622000735/train/python 5 | """ 6 | 7 | 8 | class VigenereAutokeyCipher: 9 | def __init__(self, key, abc): 10 | self.key = key 11 | self.abc = abc 12 | 13 | def encode(self, text): 14 | result = [] 15 | key = self.key + ''.join([t for t in text if t in self.abc]) 16 | index = 0 17 | for c in text: 18 | if c in self.abc: 19 | offset = self.abc.index(key[index]) 20 | result.append(self.abc[(self.abc.index(c) + offset) % len(self.abc)]) 21 | index += 1 22 | else: 23 | result.append(c) 24 | return ''.join(result) 25 | 26 | def decode(self, text): 27 | result = [] 28 | key = self.key 29 | index = 0 30 | for c in text: 31 | if c in self.abc: 32 | offset = self.abc.index(key[index]) 33 | decoded = self.abc[(self.abc.index(c) - offset) % len(self.abc)] 34 | result.append(decoded) 35 | key += decoded 36 | index += 1 37 | else: 38 | result.append(c) 39 | return ''.join(result) -------------------------------------------------------------------------------- /solutions/Where_my_anagrams_at.py: -------------------------------------------------------------------------------- 1 | """ 2 | Where my anagrams at? 3 | http://www.codewars.com/kata/523a86aa4230ebb5420001e1/train/python 4 | """ 5 | 6 | 7 | def anagrams(word, words): 8 | return [w for w in words if sorted(w) == sorted(word)] -------------------------------------------------------------------------------- /solutions/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Peter-Liang/CodeWars-Python/80628427ed1b5bb3c8ecd2a494495ae7c81e2063/solutions/__init__.py -------------------------------------------------------------------------------- /solutions/text_align_justify.py: -------------------------------------------------------------------------------- 1 | """ 2 | Text align justify 3 | 4 | http://www.codewars.com/kata/537e18b6147aa838f600001b/train/python 5 | """ 6 | 7 | 8 | def justify(text, width): 9 | words = text.split() 10 | current_len = 0 11 | line_words = [] 12 | lines = [line_words] 13 | for word in words: 14 | if current_len + len(word) > width: 15 | line_words = [word] 16 | lines.append(line_words) 17 | current_len = len(word) + 1 18 | else: 19 | line_words.append(word) 20 | current_len += len(word) + 1 21 | for i in range(len(lines) - 1): 22 | line_words = lines[i] 23 | space_need = width - sum(len(word) for word in line_words) 24 | while space_need: 25 | for index in range(len(line_words) - 1): 26 | if space_need == 0: 27 | break 28 | line_words[index] += ' ' 29 | space_need -= 1 30 | lines[i] = ''.join(line_words) + '\n' 31 | lines[-1] = ' '.join(lines[-1]) 32 | return ''.join(lines) 33 | -------------------------------------------------------------------------------- /tests/All_that_is_open_must_be_closed.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | class TestIsBalanced(unittest.TestCase): 4 | 5 | 6 | def __init__(self): 7 | unittest.Te.__init__(self) 8 | 9 | import('../solutions/All_that_is_open_must_be_closed.py') 10 | 11 | self.is_balanced = is_balanced 12 | 13 | 14 | def TestTrue(self): 15 | self.assertTrue(self.is_balanced()) 16 | 17 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Peter-Liang/CodeWars-Python/80628427ed1b5bb3c8ecd2a494495ae7c81e2063/tests/__init__.py --------------------------------------------------------------------------------