├── .gitignore ├── .travis.yml ├── README.MD ├── kyu2 └── Python │ └── src │ ├── sudoku_solver_hard_unique.py │ └── test_sudoku_solver_hard_unique.py ├── kyu3 ├── JavaScript │ ├── README.MD │ ├── src │ │ └── holdem.js │ └── test │ │ ├── holdem_test.js │ │ └── test_setup.html ├── Python │ ├── README.MD │ └── src │ │ ├── holdem.py │ │ ├── sudoku_solver_hard.py │ │ ├── sudoku_solver_hard_cw_submission.py │ │ ├── test_holdem.py │ │ ├── test_sudoku_solver_hard.py │ │ └── test_sudoku_solver_hard_cw_submission.py └── src │ ├── sudoku_solver_hard.py │ └── test_sudoku_solver_hard.py ├── kyu4 ├── Python │ ├── README.MD │ └── src │ │ ├── holdem.py │ │ ├── init.py │ │ ├── next_bigger.py │ │ ├── palindrome_one_liner.py │ │ ├── poker_rankings.py │ │ ├── sortable_poker_hands.py │ │ ├── sudoku_solver.py │ │ ├── sudoku_validator.py │ │ ├── test_holdem.py │ │ ├── test_next_bigger.py │ │ ├── test_palindrome_one_liner.py │ │ ├── test_poker_rankings.py │ │ ├── test_sortable_poker_hands.py │ │ ├── test_sudoku_solver.py │ │ ├── test_sudoku_validator.py │ │ ├── test_url_strip_params.py │ │ └── url_strip_params.py ├── dotnet │ └── Csharp │ │ ├── PokerHand.cs │ │ ├── PokerRankingsTests.cs │ │ ├── TupleList.cs │ │ └── src │ │ ├── PokerHand.cs │ │ └── SortablePokerHand.cs ├── sql │ ├── README.MD │ └── src │ │ ├── messy_data.sql │ │ └── two_actors_cast_the_most_together.sql └── src │ ├── init.py │ ├── palindrome_one_liner.py │ ├── poker_rankings.py │ ├── sortable_poker_hands.py │ ├── sudoku_validator.py │ ├── test_palindrome_one_liner.py │ ├── test_poker_rankings.py │ ├── test_sortable_poker_hands.py │ ├── test_sudoku_validator.py │ ├── test_url_strip_params.py │ └── url_strip_params.py ├── kyu5 ├── dotnet │ ├── Csharp │ │ └── best_travel.cs │ └── README.MD ├── python │ ├── README.MD │ └── src │ │ ├── base_neg_2.py │ │ ├── basic_excel.py │ │ ├── best_travel.py │ │ ├── camelcase.py │ │ ├── carpark.py │ │ ├── diceroller.py │ │ ├── digits_product.py │ │ ├── domain_name.py │ │ ├── even_digit_primes.py │ │ ├── fib_digit_occurence.py │ │ ├── find_unique_str.py │ │ ├── mod4regex.py │ │ ├── not_secure.py │ │ ├── perfect_power.py │ │ ├── prime_factorization.py │ │ ├── primes.py │ │ ├── sud_validator.py │ │ ├── test_base_neg_2.py │ │ ├── test_basic_excel.py │ │ ├── test_best_travel.py │ │ ├── test_camelcase.py │ │ ├── test_carpark.py │ │ ├── test_diceroller.py │ │ ├── test_digits_products.py │ │ ├── test_domain_name.py │ │ ├── test_even_digit_primes.py │ │ ├── test_fib_digit_occurence.py │ │ ├── test_find_unique_str.py │ │ ├── test_mod4regex.py │ │ ├── test_not_secure.py │ │ ├── test_prime_factorization.py │ │ ├── test_primes.py │ │ ├── test_sud_validator.py │ │ ├── test_valid_braces.py │ │ ├── test_valid_parentheses.py │ │ ├── valid_braces.py │ │ └── valid_parentheses.py └── sql │ ├── README.MD │ └── src │ ├── efficient_salary_increase.sql │ ├── group_by_day.sql │ ├── hierachical_structure.sql │ ├── min_median_max.sql │ ├── pivot.sql │ ├── relational_division_of_actors.sql │ ├── running_total.sql │ ├── simple_view.sql │ ├── sqlonacci.sql │ ├── time_clocked.sql │ ├── top_n_per_group_window.sql │ └── weekdays_function.sql └── kyu7 ├── Python └── src │ └── sort_arr_by_idx_val.py └── dotnet └── C# └── src ├── find_sum.cs ├── get_rankings.cs ├── product_of_arr_items.cs └── sort_arr_by_idx_val.cs /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | 27 | # PyInstaller 28 | # Usually these files are written by a python script from a template 29 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 30 | *.manifest 31 | *.spec 32 | 33 | # Installer logs 34 | pip-log.txt 35 | pip-delete-this-directory.txt 36 | 37 | # Unit test / coverage reports 38 | htmlcov/ 39 | .tox/ 40 | .coverage 41 | .coverage.* 42 | .cache 43 | nosetests.xml 44 | coverage.xml 45 | *,cover 46 | .hypothesis/ 47 | 48 | # Translations 49 | *.mo 50 | *.pot 51 | 52 | # Django stuff: 53 | *.log 54 | local_settings.py 55 | 56 | # Flask stuff: 57 | instance/ 58 | .webassets-cache 59 | 60 | # Scrapy stuff: 61 | .scrapy 62 | 63 | # Sphinx documentation 64 | docs/_build/ 65 | 66 | # PyBuilder 67 | target/ 68 | 69 | # IPython Notebook 70 | .ipynb_checkpoints 71 | 72 | # pyenv 73 | .python-version 74 | 75 | # celery beat schedule file 76 | celerybeat-schedule 77 | 78 | # dotenv 79 | .env 80 | 81 | # virtualenv 82 | venv/ 83 | ENV/ 84 | pip-selfcheck.json 85 | pyvenv.cfg 86 | bin/ 87 | include/ 88 | share/ 89 | python-wheels/ 90 | 91 | 92 | # Spyder project settings 93 | .spyderproject 94 | 95 | # Rope project settings 96 | .ropeproject 97 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | python: 3 | - "3.6" 4 | # which branch to build 5 | branches: 6 | only: 7 | - master 8 | # command to run tests 9 | script: 10 | - pytest ./kyu3/Python/src/test*.py 11 | - pytest ./kyu2/Python/src/test*.py 12 | -------------------------------------------------------------------------------- /kyu2/Python/src/test_sudoku_solver_hard_unique.py: -------------------------------------------------------------------------------- 1 | """Tests for https://www.codewars.com/kata/hard-sudoku-solver-1.""" 2 | 3 | import pytest 4 | from math import sqrt 5 | from operator import itemgetter 6 | 7 | 8 | unsolvable = [ 9 | [5, 2, 0, 7, 9, 4, 3, 0, 8], 10 | [4, 6, 0, 3, 1, 8, 5, 7, 0], 11 | [0, 0, 0, 5, 6, 2, 4, 0, 0], 12 | [0, 9, 4, 6, 8, 7, 1, 0, 0], 13 | [7, 1, 0, 9, 3, 5, 2, 4, 6], 14 | [6, 3, 5, 4, 2, 1, 9, 8, 7], 15 | [0, 0, 0, 1, 5, 6, 8, 0, 0], 16 | [0, 5, 0, 8, 4, 9, 7, 2, 0], 17 | [9, 8, 1, 2, 7, 3, 6, 0, 4] 18 | ] 19 | 20 | invalid = [ 21 | [1, 2, 3, 4, 5, 6, 7, 8, 'a'], 22 | [4, 0, 6, 7, 8, 9, 1, 2, 3], 23 | [7, 8, 9, 1, 2, 3, 4, 5, 6], 24 | [2, 3, 4, 5, 6, 7, 8, 9, 1], 25 | [5, 6, 7, 8, 9, 1, 2, 3, 4], 26 | [8, 9, 1, 2, 3, 4, 5, 6, 7], 27 | [3, 4, 5, 6, 7, 8, 9, 1, 2], 28 | [6, 7, 8, 9, 1, 2, 3, 4, 5], 29 | [9, 1, 2, 3, 4, 5, 6, 7, 8] 30 | ] 31 | 32 | not_unique = [ 33 | [0, 0, 3, 4, 5, 6, 7, 8, 9], 34 | [4, 5, 6, 7, 8, 9, 0, 0, 3], 35 | [7, 8, 9, 0, 0, 3, 4, 5, 6], 36 | [0, 3, 4, 5, 6, 7, 8, 9, 0], 37 | [5, 6, 7, 8, 9, 0, 0, 3, 4], 38 | [8, 9, 0, 0, 3, 4, 5, 6, 7], 39 | [3, 4, 5, 6, 7, 8, 9, 0, 0], 40 | [6, 7, 8, 9, 0, 0, 3, 4, 5], 41 | [9, 0, 0, 3, 4, 5, 6, 7, 8] 42 | ] 43 | 44 | hard_1 = [ 45 | [0, 0, 0, 2, 0, 0, 0, 6, 3], 46 | [3, 0, 0, 0, 0, 5, 4, 0, 1], 47 | [0, 0, 1, 0, 0, 3, 9, 8, 0], 48 | [0, 0, 0, 0, 0, 0, 0, 9, 0], 49 | [0, 0, 0, 5, 3, 8, 0, 0, 0], 50 | [0, 3, 0, 0, 0, 0, 0, 0, 0], 51 | [0, 2, 6, 3, 0, 0, 5, 0, 0], 52 | [5, 0, 3, 7, 0, 0, 0, 0, 8], 53 | [4, 7, 0, 0, 0, 1, 0, 0, 0], 54 | ] 55 | 56 | hard_2 = [ 57 | [0, 1, 0, 0, 0, 4, 0, 0, 0], 58 | [0, 0, 6, 8, 0, 5, 0, 0, 1], 59 | [5, 0, 3, 7, 0, 1, 9, 0, 0], 60 | [8, 0, 4, 0, 0, 7, 0, 0, 0], 61 | [0, 0, 0, 0, 0, 0, 0, 0, 0], 62 | [0, 0, 0, 3, 0, 0, 6, 0, 9], 63 | [0, 0, 1, 5, 0, 8, 2, 0, 4], 64 | [6, 0, 0, 4, 0, 3, 1, 0, 0], 65 | [0, 0, 0, 2, 0, 0, 0, 5, 0], 66 | ] 67 | 68 | expert = [ 69 | [0, 0, 6, 0, 0, 0, 0, 0, 4], 70 | [0, 0, 0, 8, 6, 0, 7, 3, 0], 71 | [0, 4, 0, 3, 5, 0, 0, 0, 2], 72 | [1, 7, 0, 4, 0, 0, 6, 0, 0], 73 | [0, 9, 0, 0, 0, 0, 0, 8, 0], 74 | [0, 0, 8, 0, 0, 6, 0, 1, 7], 75 | [2, 0, 0, 0, 8, 1, 0, 4, 0], 76 | [0, 6, 7, 0, 4, 3, 0, 0, 0], 77 | [8, 0, 0, 0, 0, 0, 3, 0, 0], 78 | ] 79 | 80 | 81 | diabolical = [ 82 | [3, 6, 0, 0, 0, 0, 1, 7, 0], 83 | [0, 1, 4, 7, 0, 0, 0, 0, 0], 84 | [0, 0, 7, 0, 8, 0, 0, 0, 0], 85 | [0, 0, 1, 2, 0, 6, 0, 0, 5], 86 | [9, 0, 0, 0, 0, 0, 0, 0, 4], 87 | [4, 0, 0, 5, 0, 8, 3, 0, 0], 88 | [0, 0, 0, 0, 5, 0, 2, 0, 0], 89 | [0, 0, 0, 0, 0, 7, 4, 3, 0], 90 | [0, 4, 9, 0, 0, 0, 0, 5, 6], 91 | ] 92 | 93 | 94 | TEST_SUDOKUS = [ 95 | (hard_1, True), 96 | (hard_2, True), 97 | (diabolical, True), 98 | ] 99 | 100 | def test_sudoku_solver_handles_garbage_input(): 101 | """Test that garbage input in the given sudoku is handled correctly by sudoku_solver.""" 102 | from sudoku_solver_hard_unique import setup 103 | with pytest.raises(Exception) as e_info: 104 | candidates, dicts, square_coords = setup(invalid) 105 | assert str(e_info.value) == "Garbage input: 'a' at coord (0, 8), not a valid Sudoku" 106 | 107 | def test_sudoku_solver_handles_non_unique(): 108 | """Test that an non-unique sudoku is handled correctly by sudoku_solver.""" 109 | from sudoku_solver_hard_unique import sudoku_solver 110 | with pytest.raises(Exception) as e_info: 111 | sudoku_solver(not_unique) 112 | assert str(e_info.value) == "Sudoku is not unique" 113 | 114 | @pytest.mark.parametrize('m, result', TEST_SUDOKUS) 115 | def test_solver_combo_approach(m, result): 116 | """ 117 | Test that function solver can solve a given sudoku of hard difficulty correctly using a mix of traditional solving techniques and 118 | brute force. 119 | """ 120 | from sudoku_solver_hard_unique import sudoku_solver, valid 121 | from copy import deepcopy 122 | brute_m = deepcopy(m) 123 | solved_sudoku = sudoku_solver(brute_m) 124 | assert valid(solved_sudoku) == result 125 | -------------------------------------------------------------------------------- /kyu3/JavaScript/README.MD: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | this repository includes all of my completed code-katas in JavaScript from the 4 | [CodeWars challenges](http://wwww.codewars.com) with a difficulty of 5 | kyu3. [History of the term kyu](https://en.wikipedia.org/wiki/Ky%C5%AB) 6 | 7 | # JS coding challenges: 8 | 9 | ## kyu3 10 | 11 | * Texas Hold'em Hands 12 | 13 | ## Details for each kata completed 14 | 15 | ### Texas Hold'em Hands 16 | * Description of Kata: Given hole cards and community cards, complete the function 17 | hand to return the type of hand and high cards. 18 | * Difficulty: kyu3 19 | * Module: holdem.js 20 | * [Link](https://www.codewars.com/kata/texas-holdem-hands) 21 | -------------------------------------------------------------------------------- /kyu3/JavaScript/src/holdem.js: -------------------------------------------------------------------------------- 1 | hands=["four-of-a-kind", "straight-flush", "straight", "flush", "nothing", 2 | "pair", "two pair", "straight-flush", "three-of-a-kind", "full house", "-Invalid-" ]; 3 | handRanks = [8,9,5,6,1,2,3,10,4,7,0]; 4 | var cardsLookup = { 5 | 2: '2', 6 | 3: '3', 7 | 4: '4', 8 | 5: '5', 9 | 6: '6', 10 | 7: '7', 11 | 8: '8', 12 | 9: '9', 13 | 10: '10', 14 | 11: 'J', 15 | 12: 'Q', 16 | 13: 'K', 17 | 14: 'A' 18 | }; 19 | // Algorithms to calculate the value of a hand taken from Poker Hand Evaluator by Pat Wilson ©2012 20 | function calcIndex(cs,ss) { 21 | var v,i,o,s; for (i=-1, v=o=0; i<5; i++, o=Math.pow(2,cs[i]*4)) {v += o*((v/o&15)+1);} 22 | if ((v%=15)!=5) {return v-1;} else {s = 1< 0) && (comb[i] >= n - k + 1 + i)) { --i; ++comb[i];} 43 | if (comb[0] > n - k) {return false;} // No more combinations can be generated 44 | for (i = i + 1; i < k; ++i) {comb[i] = comb[i-1] + 1;} 45 | return true; 46 | } 47 | while (next_comb(comb, k, n)) { result.push(comb.slice());} 48 | return result; 49 | } 50 | 51 | function getPokerScore(cs) { 52 | var a = cs.slice(), d={}, i; 53 | for (i=0; i<5; i++) {d[a[i]] = (d[a[i]] >= 1) ? d[a[i]] + 1 : 1;} 54 | a.sort(function(a,b){return (d[a] < d[b]) ? +1 : (d[a] > d[b]) ? -1 : (b - a);}); 55 | return a[0]<<16|a[1]<<12|a[2]<<8|a[3]<<4|a[4]; 56 | } 57 | 58 | function rankHand(str) { 59 | cards = getCards(str); 60 | suits = getSuits(str); 61 | var index = 10, winCardIndexes, i ,e, wci; 62 | var hand = cards; 63 | for (i=0;i maxRank) { 72 | maxRank = handRanks[index]; 73 | winIndex = index; 74 | wci = c[i].slice(); 75 | hand = cs; 76 | } else if (handRanks[index] == maxRank) { 77 | //If by chance we have a tie, find the best one 78 | var other_hand = [cards[wci[0]],cards[wci[1]],cards[wci[2]], cards[wci[3]],cards[wci[4]]]; 79 | var score1 = getPokerScore(cs); 80 | var score2 = getPokerScore(other_hand); 81 | if (score1 > score2) { 82 | hand = cs; 83 | wci = c[i].slice(); 84 | } 85 | } 86 | } 87 | return [ winIndex, hand ]; 88 | } 89 | 90 | function filter(index, hand, pairs) { 91 | var filtered = hand.filter(function(el) { 92 | switch (index) { 93 | case 6: 94 | case 9: 95 | return el != pairs[0] && el != pairs[1]; 96 | default: 97 | return el !== pairs[0]; 98 | } 99 | }); 100 | return filtered; 101 | }; 102 | 103 | function counter(array) { 104 | var count = {}; 105 | array.forEach(val => count[val] = (count[val] || 0) + 1); 106 | return count; 107 | } 108 | 109 | function hand(holeCards, board) { 110 | heroesHand = holeCards.concat(board).join(''); 111 | rankHandReturn = rankHand(heroesHand); 112 | var index = rankHandReturn[0]; 113 | var hand = rankHandReturn[1]; 114 | console.log('hand: ' + hand); 115 | var out = {}; 116 | var ranks = []; 117 | var pairs = hand.reduce(function(acc, el, i, arr) { 118 | if (arr.indexOf(el) !== i && acc.indexOf(el) < 0) acc.push(el); return acc; 119 | }, []); 120 | if ([0, 5, 6, 8, 9].includes(rankHandReturn[0])) { 121 | var filtered = filter(index, hand, pairs) 122 | filtered.sort((a, b) => (a - b)).reverse(); 123 | }; 124 | hand.sort((a, b) => (a - b)).reverse(); 125 | pairs.sort((a, b) => (a - b)).reverse(); 126 | if ([1, 2, 3, 4, 7].includes(index)) { var highCards = hand.slice(0, 5); } // straights&flush 127 | if (index == 8) { var highCards = pairs.concat(filtered.slice(0, 2)); } // three-of-a kind 128 | if (index == 5) { var highCards = pairs.concat(filtered.slice(0, 3)); } // pairs 129 | if (index == 6 || index == 0) { var highCards = pairs.concat(filtered.slice(0, 1)); } // 2pair & four-of-a-kind 130 | // full-house - we need to get a weighted sorted to display the set first and then the pair 131 | if (index == 9) { 132 | var fullHouse = counter(hand); 133 | var weightedFullHouse = []; 134 | for (var card in fullHouse) { 135 | weightedFullHouse.push([card, fullHouse[card]]); 136 | } 137 | weightedFullHouse.sort((a, b) => (a[1] - b[1])).reverse(); 138 | var highCards = []; 139 | weightedFullHouse.forEach(function(card){ 140 | highCards.push(card[0]); 141 | }); 142 | } 143 | for (var i=0;i 2 | 3 | 4 | 5 | 6 | Mocha Tests 7 | 8 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 27 | 28 | 29 |
30 | 31 | 32 | -------------------------------------------------------------------------------- /kyu3/Python/README.MD: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | this repository includes all of my completed code-katas in Python from the 4 | [CodeWars challenges](http://wwww.codewars.com) with a difficulty of 5 | kyu3. [History of the term kyu](https://en.wikipedia.org/wiki/Ky%C5%AB) 6 | 7 | ## kyu3 8 | 9 | * Texas Hold'em Hands 10 | 11 | ## Details for each kata completed 12 | 13 | ### Texas Hold'em Hands 14 | * Description of Kata: Given hole cards and community cards, complete the function 15 | hand to return the type of hand and high cards. 16 | * Difficulty: kyu3 17 | * Module: holdem.py 18 | * Tests: test_holdem.py 19 | * [Link](https://www.codewars.com/kata/texas-holdem-hands) 20 | -------------------------------------------------------------------------------- /kyu3/Python/src/holdem.py: -------------------------------------------------------------------------------- 1 | """Tests for codewars kata https://www.codewars.com/kata/texas-holdem-hands/.""" 2 | 3 | import re 4 | from collections import Counter 5 | from itertools import combinations 6 | from operator import itemgetter 7 | 8 | HANDS = [ 9 | "four-of-a-kind", "straight-flush", "straight", "flush", "nothing", 10 | "pair", "two pair", "straight-flush", "three-of-a-kind", "full house", "-Invalid-" 11 | ] 12 | 13 | LOOKUP = { 14 | 'A' : 14, 15 | 'K' : 13, 16 | 'Q' : 12, 17 | 'J' : 11, 18 | 'T' : 10, 19 | } 20 | 21 | HANDRANKS = [8, 9, 5, 6, 1, 2, 3, 10, 4, 7, 0] 22 | 23 | CARDSLOOKUP = { 24 | 2: '2', 25 | 3: '3', 26 | 4: '4', 27 | 5: '5', 28 | 6: '6', 29 | 7: '7', 30 | 8: '8', 31 | 9: '9', 32 | 10: '10', 33 | 11: 'J', 34 | 12: 'Q', 35 | 13: 'K', 36 | 14: 'A' 37 | } 38 | 39 | def get_cards(heroes_hand): 40 | """Return a list with string representation of a given 7 card hand.""" 41 | heroes_hand = re.sub(r"10", "T", heroes_hand) 42 | card_str_no_suits = ''.join([c if ord(c) < 128 else '' for c in heroes_hand]) 43 | cards = [LOOKUP[c] if not c.isnumeric() else int(c) for c in card_str_no_suits] 44 | return cards 45 | 46 | def get_suits(heroes_hand): 47 | """Return a list with the suits of the hand.""" 48 | pattern = re.compile('♠|♣|♥|♦') 49 | return list(re.findall(pattern, heroes_hand)) 50 | 51 | def get_pokerscore(heroes_hand): 52 | """Return a unique value representing overall hand strength.""" 53 | c = Counter(heroes_hand) 54 | a = sorted(heroes_hand, key=lambda x: (c[x], x), reverse=True) 55 | return a[0]<<16|a[1]<<12|a[2]<<8|a[3]<<4|a[4] 56 | 57 | def rank_hand(heroes_hand): 58 | """Return a tuple with index representing hand strength and final 5 card hand.""" 59 | cards = get_cards(heroes_hand) 60 | suits = get_suits(heroes_hand) 61 | for i, s in enumerate(suits): 62 | suits[i] = int(pow(2, (ord(s) % 9824))) 63 | heroes_hand = [(cards[i], suits[i]) for i in range(7)] 64 | c = combinations(heroes_hand, 5) 65 | max_rank = 0 66 | win_index = 10 67 | for combo in c: 68 | cs, ss = zip(*combo) 69 | index = calc_index(combo) 70 | if HANDRANKS[index] > max_rank: 71 | max_rank = HANDRANKS[index] 72 | win_index = index 73 | wci = cs 74 | heroes_hand = cs 75 | elif HANDRANKS[index] == max_rank: 76 | score_1 = get_pokerscore(cs) 77 | score_2 = get_pokerscore(wci) 78 | if (score_1 > score_2): 79 | heroes_hand = cs 80 | wci = cs 81 | return (win_index, heroes_hand) 82 | 83 | def calc_index(heroes_hand): 84 | """Return the integer index representing the strenght of the hand for a given 5 card hand and suits.""" 85 | cs, ss = zip(*heroes_hand) 86 | v = 0 87 | for card in cs: 88 | o = int(pow(2, card * 4)) 89 | v += o*((v // o & 15) + 1) 90 | v %= 15 91 | if v != 5: 92 | return v - 1 93 | else: 94 | s = 1< 1] 116 | filtered = [n for n in final_hand if n not in pairs] 117 | sorted_hand = sorted(final_hand, reverse=True) 118 | pairs.sort(reverse=True) 119 | filtered.sort(reverse=True) 120 | ordered_hand = get_ordered_hand(index, sorted_hand, pairs, filtered) 121 | return {'type': HANDS[index], 'ranks': [CARDSLOOKUP[card] for card in ordered_hand]} 122 | -------------------------------------------------------------------------------- /kyu3/Python/src/sudoku_solver_hard_cw_submission.py: -------------------------------------------------------------------------------- 1 | """ 2 | Solution for code-kata https://www.codewars.com/kata/hard-sudoku-solver 3 | This version contains less code than the original solution I came up with as implementing the 4 | naked pairs/set/quads strategy increased the run time slightly while adding more complexity so it won't be included 5 | in the final submission on codewars. 6 | For the CW submission, the pre-set version of Python is 2.7.6 so this code targets that version. 7 | """ 8 | 9 | from math import sqrt, ceil 10 | from itertools import islice, chain, permutations 11 | from operator import itemgetter 12 | from collections import defaultdict 13 | from functools import reduce 14 | from copy import deepcopy 15 | 16 | def initialize_dicts(m, square_sides): 17 | """Return a tuple of dicts for a given matrix and the size of the sudoku.""" 18 | rows_missing = defaultdict(list) 19 | rows_missing = initialize_d(rows_missing, square_sides) 20 | cols_missing = defaultdict(list) 21 | cols_missing = initialize_d(cols_missing, square_sides) 22 | squares_missing = defaultdict(list) 23 | squares_missing = initialize_d(cols_missing, square_sides, 1) 24 | return rows_missing, cols_missing, squares_missing 25 | 26 | def initialize_d(d, square_sides, offset=0): 27 | """Return an initialized dict so empty rows or columns in the Sudoku are 28 | correctly handled.""" 29 | return {key:[] for key in range(offset, square_sides ** 2 + offset)} 30 | 31 | def populate_dicts(m, square_sides, dicts): 32 | """Return dicts holding information about fills in given Sudoku.""" 33 | sq_nr = 0 34 | square_coords = {} 35 | for row in range(0, square_sides ** 2, square_sides): 36 | for col in range(0, square_sides ** 2, square_sides): 37 | sq_nr += 1 38 | square = [islice(m[i], col, square_sides + col) for i in range(row, row + square_sides)] 39 | dicts, square_coords = fill_given_numbers(square, row, col, sq_nr, dicts, square_coords) 40 | return dicts, square_coords 41 | 42 | def get_candidates(m, dicts, square_coords): 43 | """Return a dict of candidates for all starting_spots in the Sudoku.""" 44 | starting_spots = get_starting_spots(m, dicts, square_coords) 45 | starting_spots.sort(key=itemgetter(2)) 46 | rm, cm, sm = dicts 47 | c = {} 48 | for coordinate in starting_spots: 49 | row, col, missing = coordinate 50 | c[(row, col)] = [n for n in cm[col] if n in rm[row] and n in sm[square_coords[row, col]]] 51 | if not c[(row, col)]: 52 | raise ValueError 53 | return c 54 | 55 | def get_starting_spots(m, dicts, square_coords): 56 | """Return a list with coordinates as starting point for sudoku solver.""" 57 | rm, cm, sm = dicts 58 | starting_spots = [] 59 | row = 0 60 | col = 0 61 | max = 20 62 | start = -1 63 | for col in range(9): 64 | for row in range(9): 65 | if m[row][col] == 0: 66 | square = square_coords[(row, col)] - 1 67 | missing_numbers = len(cm[col]) + len(rm[row]) + len(sm[1 if square < 1 else square]) 68 | starting_spots.append((row, col, missing_numbers)) 69 | return starting_spots 70 | 71 | 72 | def setup(m): 73 | """ 74 | Return a list of candidates, helper_dicts and square_coords 75 | Store all starting numbers by looping through the Sudoku square by square 76 | Find all missing numbers by subtracting the 2 sets 77 | Generate all starting spots and find the one with the least amount of digits missing 78 | Generate candidates by finding all possible numbers for a given starting point 79 | """ 80 | square_sides = int(sqrt(len(m))) 81 | dicts = initialize_dicts(m, square_sides) 82 | dicts, square_coords = populate_dicts(m, square_sides, dicts) 83 | dicts = get_missing(dicts) 84 | try: 85 | candidates = get_candidates(m, dicts, square_coords) 86 | except ValueError as e: 87 | raise ValueError(e) 88 | return candidates, dicts, square_coords 89 | 90 | 91 | def get_missing(dicts): 92 | """Return dictionaries with swapped values from given numbers to missing numbers.""" 93 | for d in dicts: 94 | for k, v in d.items(): 95 | d[k] = set([1, 2, 3, 4, 5, 6, 7, 8, 9]) - set(v) 96 | return dicts 97 | 98 | 99 | def sudoku_solver(m, setup_return): 100 | """ 101 | Return a valid Sudoku for a given matrix, helper dicts, a list of candidates and a lookup for coords matching squares. 102 | Fill in simple numbers using scanning technique 103 | Find single candidates and fill in 104 | Look for naked pairs and eliminate from candidates 105 | Look for naked sets and eliminate from candidates 106 | """ 107 | candidates, dicts, square_coords = setup_return 108 | m = scan_sudoku(m, setup(m)) 109 | if candidates: 110 | single_candidates = single_candidate(setup(m)) 111 | else: 112 | return m 113 | m = fill_fit(m, get_candidates(m, dicts, square_coords), single_candidates=single_candidates) 114 | candidates = get_candidates(m, dicts, square_coords) 115 | return m 116 | 117 | 118 | def scan_sudoku(m, setup_return): 119 | """ 120 | Return an updated Sudoku and a list of candidates akin to the scanning technique where obvious fits are filled in. 121 | After each fit, list of candidates is rebuild until no further immediate fills are possible. 122 | """ 123 | candidates, dicts, square_coords = setup_return 124 | while True: 125 | if len(sorted(candidates.items(), key=lambda x: len(x[1])).pop(0)[1]) > 1: # no longer easily solvable 126 | break 127 | m = fill_fit(m, get_candidates(m, dicts, square_coords)) 128 | starting_spots = get_starting_spots(m, dicts, square_coords) 129 | starting_spots.sort(key=itemgetter(2)) 130 | candidates = get_candidates(m, dicts, square_coords) 131 | if not candidates: break 132 | return m 133 | 134 | 135 | def single_candidate(setup_return): 136 | """ 137 | Return a number which is a single candidate for a coordinate in the list of candidates: 138 | Build a dict with square as key and empty fields as value 139 | Go through every square and get all missing fields 140 | For every missing field in that square, get the possible numbers 141 | Look for a number which is only missing in one field in a square. 142 | """ 143 | candidates, dicts, square_coords = setup_return 144 | rm, cm, sm = dicts 145 | out = [] 146 | coords_missing_in_square = squares_to_missing(square_coords) 147 | for k, v in coords_missing_in_square.items(): 148 | single_candidates = defaultdict(list) 149 | seen = set() 150 | for coord in v: 151 | pn = set(candidates[coord]) 152 | if pn.issubset(seen): 153 | continue 154 | for n in pn: 155 | if n in seen: 156 | continue 157 | if single_candidates.get(n, 0): 158 | seen.add(n) 159 | continue 160 | single_candidates[n] = coord 161 | if len(seen) == len(sm[k]): 162 | continue 163 | out.append([(k, v) for k, v in single_candidates.items() if k not in seen]) 164 | return list(chain.from_iterable(out)) 165 | 166 | 167 | def squares_to_missing(square_coords): 168 | """Return a dict of square numbers as key and empty fields in the Sudoku as values.""" 169 | squares_missing = defaultdict(list) 170 | for k, v in square_coords.items(): 171 | squares_missing[v].append(k) 172 | return squares_missing 173 | 174 | 175 | def fill_given_numbers(square, row, col, sq_nr, dicts, sq): 176 | """Fill dicts with given numbers number for a given square.""" 177 | rm, cm, sm = dicts 178 | for row_idx, sr in enumerate(square): 179 | for col_idx, sv in enumerate(sr): 180 | coord = (row + row_idx, col + col_idx) 181 | if sv == 0: 182 | sq[coord] = sq_nr 183 | continue 184 | rm[coord[0]].append(sv) 185 | cm[coord[1]].append(sv) 186 | sm[sq_nr].append(sv) 187 | return dicts, sq 188 | 189 | 190 | def fill_fit(m, candidates, single_candidates=[]): 191 | """ 192 | Return an updated Sudoku by either finding a fit or taking a fit from a provided 193 | list of fits and filling it in as long as a fit is found. 194 | """ 195 | while True: 196 | try: 197 | if single_candidates: 198 | num, coord = single_candidates.pop() 199 | fit = (coord[0], coord[1], num) 200 | else: 201 | fit = find_fit(candidates) 202 | except IndexError: 203 | return m 204 | if fit: 205 | m = update_sudoku(fit, m) 206 | candidates, dicts, square_coords = setup(m) 207 | else: 208 | return m 209 | 210 | 211 | def find_fit(candidates): 212 | """Return a tuple with coordinate and value to update from a sorted 213 | representation of a dict. If no fit can be found, return None.""" 214 | fit = sorted(candidates.items(), key=lambda x: len(x[1])).pop(0) 215 | row, col = fit[0] 216 | n = fit[1].pop() 217 | if len(fit[1]) == 0: 218 | return row, col, n 219 | return None 220 | 221 | 222 | def update_sudoku(fit, m): 223 | """Return an updated Sudoku.""" 224 | row, col, n = fit 225 | m[row][col] = n 226 | return m 227 | 228 | 229 | def fill_square(brute_m, candidates, sq_p): 230 | """ 231 | Return True if all coordinates an be filled with a given permutation without invalidating the sudoku. 232 | Else return False. 233 | """ 234 | for fit in sq_p: 235 | coord, n = fit 236 | if n not in candidates[coord]: return False 237 | row, col = coord 238 | brute_m = update_sudoku((row, col, n), brute_m) 239 | return True 240 | 241 | 242 | def solver(board): 243 | """Return a solved Sudoku for a given Sudoku or raise a ValueError if not solvable.""" 244 | m = sudoku_solver(list(board), setup(board)) 245 | if valid(m): 246 | return m #Sudoku solved after the first run 247 | return rec_solver(m) 248 | 249 | 250 | def rec_solver(m): 251 | """Return a sudoku by recursively calling itself which triggers the next brute force of a square.""" 252 | candidates, dicts, sq = setup(m) 253 | rm, cm, sm = dicts 254 | square, coords = sorted(squares_to_missing(sq).items(), key = lambda x: len(x[1]), reverse=True).pop() 255 | missing = sm[square] 256 | for p in permutations(missing): #try all combinations of fields and missing numbers 257 | candidates, dicts, square_coords = setup(m) 258 | sq_p = tuple(zip(coords, p)) 259 | brute_m = deepcopy(m) 260 | if not fill_square(brute_m, candidates, sq_p): 261 | continue 262 | try: 263 | brute_m = sudoku_solver(list(brute_m), setup(m)) 264 | except ValueError as e: 265 | continue 266 | if not valid(brute_m): 267 | brute_m = rec_solver(brute_m) 268 | if not brute_m: 269 | continue 270 | return brute_m 271 | 272 | 273 | def valid(board): 274 | """Retrun True if the given matrix is a valid and solved sudoku.""" 275 | sq = int(sqrt(len(board))) 276 | size = sq * sq 277 | rows = board 278 | cols = [map(lambda x: x[i], board) for i in range(9)] 279 | squares = [reduce(lambda x, y: x + y, map(lambda x: x[i:(i + sq)], board[j:(j + sq)])) 280 | for j in range(0, size, sq) 281 | for i in range(0, size, sq)] 282 | 283 | for clusters in (rows, cols, squares): 284 | for cluster in clusters: 285 | if len(set(cluster)) != 9: 286 | return False 287 | return True -------------------------------------------------------------------------------- /kyu3/Python/src/test_holdem.py: -------------------------------------------------------------------------------- 1 | """Tests for codewars kata https://www.codewars.com/kata/texas-holdem-hands/.""" 2 | 3 | import pytest 4 | 5 | HAND_STRENGTH_INDEX = [ 6 | ([(14, 8), (5, 32), (5, 8), (13, 32), (13, 64)], 6), 7 | ([(11, 32), (5, 32), (10, 32), (12, 32), (3, 32)], 3), 8 | ([(14, 64), (14, 8), (14, 32), (14, 1), (13, 32)], 0), 9 | ] 10 | 11 | HAND_RANK = [ 12 | ('A♦A♣5♥5♣K♥Q♥K♦', 6), 13 | ('A♦A♣A♥A♠5♣K♥Q♥', 0), 14 | ('A♠K♦J♥5♥10♥Q♥3♥', 3), 15 | ] 16 | 17 | HAND_DICTS = [ 18 | (['K♠', 'Q♦'], ['J♣', 'Q♥', '9♥', '2♥', '3♦'], {'type': 'pair', 'ranks': ['Q', 'K', 'J', '9']}), 19 | (['K♠', 'A♦'], ['J♣', 'Q♥', '9♥', '2♥', '3♦'], {'type': 'nothing', 'ranks': ['A', 'K', 'Q', 'J', '9']}), 20 | (['K♠', 'J♦'], ['J♣', 'K♥', '9♥', '2♥', '3♦'], {'type': 'two pair', 'ranks': ['K', 'J', '9']}), 21 | (['4♠', '9♦'], ['J♣', 'Q♥', 'Q♠', '2♥', 'Q♦'], {'type': 'three-of-a-kind', 'ranks': ['Q', 'J', '9']}), 22 | (['Q♣', '9♦'], ['J♣', 'Q♥', 'Q♠', '2♥', 'Q♦'], {'type': 'four-of-a-kind', 'ranks': ['Q', 'J']}), 23 | (['8♠', '6♠'], ['7♠', '5♠', '9♠', 'J♠', '10♠'], {'type': 'straight-flush', 'ranks': ['J', '10', '9', '8', '7']}), 24 | (['2♠', '6♠'], ['7♠', '5♠', '9♠', 'J♠', '10♠'], {'type': 'flush', 'ranks': ['J', '10', '9', '7', '6']}), 25 | (['Q♣', 'J♦'], ['J♣', '2♥', 'Q♠', '2♥', 'Q♦'], {'type': 'full house', 'ranks': ['Q', 'J']}), 26 | (['10♠', '10♦'], ['K♣', 'K♥', '10♥', 'Q♥', '3♦'], {'type': 'full house', 'ranks': ['10', 'K']}), 27 | 28 | ] 29 | 30 | def test_get_cards(): 31 | """Test that get_cards returns a list of strings representing the numerical value of each card.""" 32 | from holdem import get_cards 33 | assert get_cards('A♦A♣5♥5♣K♥Q♥K♦') == [14, 14, 5, 5, 13 , 12, 13] 34 | 35 | def test_get_suits(): 36 | """Test that get_suits returns a list of strings representing the suits.""" 37 | from holdem import get_suits 38 | assert get_suits('A♦A♣5♥5♣K♥Q♥K♦') == ['♦', '♣', '♥', '♣', '♥', '♥', '♦'] 39 | 40 | def test_get_pokerscore(): 41 | """Test that getPokerScore returns correct integer score for given hand.""" 42 | from holdem import get_pokerscore 43 | assert get_pokerscore([14,14,5,5,12]) == 976220 44 | 45 | @pytest.mark.parametrize('hand, result', HAND_STRENGTH_INDEX) 46 | def test_calc_index(hand, result): 47 | """Test that calc_index returns the correct index for a given hand and suits.""" 48 | from holdem import calc_index 49 | assert calc_index(hand) == result 50 | 51 | @pytest.mark.parametrize('hand, result', HAND_RANK) 52 | def test_rank_hand(hand, result): 53 | """Test that rank_hand returns the correct index from a list of hand strenght for a given hand.""" 54 | from holdem import rank_hand 55 | assert rank_hand(hand)[0] == result 56 | 57 | @pytest.mark.parametrize('hole_cards, board, result', HAND_DICTS) 58 | def test_hand(hole_cards, board, result): 59 | """Test that hand returns a dict with type of hand and high cards in order.""" 60 | from holdem import hand 61 | assert hand(hole_cards, board) == result 62 | 63 | 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /kyu3/Python/src/test_sudoku_solver_hard_cw_submission.py: -------------------------------------------------------------------------------- 1 | """Tests for https://www.codewars.com/kata/hard-sudoku-solver.""" 2 | 3 | import pytest 4 | from math import sqrt 5 | from operator import itemgetter 6 | 7 | base = [ 8 | [5, 3, 4, 6, 7, 8, 9, 1, 2], 9 | [6, 7, 2, 1, 9, 5, 3, 4, 8], 10 | [1, 9, 8, 3, 4, 2, 5, 6, 7], 11 | [8, 5, 9, 7, 6, 1, 4, 2, 3], 12 | [4, 2, 6, 8, 5, 3, 7, 9, 1], 13 | [7, 1, 3, 9, 2, 4, 8, 5, 6], 14 | [9, 6, 1, 5, 3, 7, 2, 8, 4], 15 | [2, 8, 7, 4, 1, 9, 6, 3, 5], 16 | [3, 4, 5, 2, 8, 6, 1, 7, 9] 17 | ] 18 | 19 | brute_force = [ 20 | [5, 0, 0, 7, 9, 0, 0, 0, 8], 21 | [0, 6, 0, 0, 1, 0, 0, 7, 0], 22 | [0, 0, 0, 5, 6, 2, 0, 0, 0], 23 | [0, 9, 4, 6, 8, 7, 1, 0, 0], 24 | [7, 1, 0, 9, 3, 5, 0, 4, 6], 25 | [6, 0, 5, 4, 2, 1, 9, 0, 7], 26 | [0, 0, 0, 1, 5, 6, 0, 0, 0], 27 | [0, 5, 0, 0, 4, 9, 0, 2, 0], 28 | [9, 0, 0, 2, 7, 0, 0, 0, 4] 29 | ] 30 | 31 | fiendish = [ 32 | [3, 0, 5, 0, 2, 0, 7, 0, 1], 33 | [0, 0, 0, 8, 0, 3, 0, 0, 2], 34 | [0, 6, 0, 0, 0, 5, 0, 0, 0], 35 | [0, 7, 1, 0, 0, 0, 0, 5, 0], 36 | [0, 0, 6, 0, 0, 0, 9, 0, 0], 37 | [0, 9, 0, 0, 0, 0, 4, 2, 0], 38 | [0, 0, 0, 6, 0, 0, 0, 9, 0], 39 | [9, 0, 0, 0, 0, 4, 0, 0, 5], 40 | [6, 0, 8, 0, 3, 0, 2, 0, 4] 41 | ] 42 | 43 | unsolvable = [ 44 | [5, 2, 0, 7, 9, 4, 3, 0, 8], 45 | [4, 6, 0, 3, 1, 8, 5, 7, 0], 46 | [0, 0, 0, 5, 6, 2, 4, 0, 0], 47 | [0, 9, 4, 6, 8, 7, 1, 0, 0], 48 | [7, 1, 0, 9, 3, 5, 2, 4, 6], 49 | [6, 3, 5, 4, 2, 1, 9, 8, 7], 50 | [0, 0, 0, 1, 5, 6, 8, 0, 0], 51 | [0, 5, 0, 8, 4, 9, 7, 2, 0], 52 | [9, 8, 1, 2, 7, 3, 6, 0, 4] 53 | ] 54 | 55 | hard_1 = [ 56 | [0, 0, 0, 2, 0, 0, 0, 6, 3], 57 | [3, 0, 0, 0, 0, 5, 4, 0, 1], 58 | [0, 0, 1, 0, 0, 3, 9, 8, 0], 59 | [0, 0, 0, 0, 0, 0, 0, 9, 0], 60 | [0, 0, 0, 5, 3, 8, 0, 0, 0], 61 | [0, 3, 0, 0, 0, 0, 0, 0, 0], 62 | [0, 2, 6, 3, 0, 0, 5, 0, 0], 63 | [5, 0, 3, 7, 0, 0, 0, 0, 8], 64 | [4, 7, 0, 0, 0, 1, 0, 0, 0], 65 | ] 66 | 67 | hard_2 = [ 68 | [0, 1, 0, 0, 0, 4, 0, 0, 0], 69 | [0, 0, 6, 8, 0, 5, 0, 0, 1], 70 | [5, 0, 3, 7, 0, 1, 9, 0, 0], 71 | [8, 0, 4, 0, 0, 7, 0, 0, 0], 72 | [0, 0, 0, 0, 0, 0, 0, 0, 0], 73 | [0, 0, 0, 3, 0, 0, 6, 0, 9], 74 | [0, 0, 1, 5, 0, 8, 2, 0, 4], 75 | [6, 0, 0, 4, 0, 3, 1, 0, 0], 76 | [0, 0, 0, 2, 0, 0, 0, 5, 0], 77 | ] 78 | 79 | expert = [ 80 | [0, 0, 6, 0, 0, 0, 0, 0, 4], 81 | [0, 0, 0, 8, 6, 0, 7, 3, 0], 82 | [0, 4, 0, 3, 5, 0, 0, 0, 2], 83 | [1, 7, 0, 4, 0, 0, 6, 0, 0], 84 | [0, 9, 0, 0, 0, 0, 0, 8, 0], 85 | [0, 0, 8, 0, 0, 6, 0, 1, 7], 86 | [2, 0, 0, 0, 8, 1, 0, 4, 0], 87 | [0, 6, 7, 0, 4, 3, 0, 0, 0], 88 | [8, 0, 0, 0, 0, 0, 3, 0, 0], 89 | ] 90 | 91 | extreme = [ 92 | [0, 0, 9, 7, 4, 8, 0, 0, 0], 93 | [7, 0, 0, 0, 0, 0, 0, 0, 0], 94 | [0, 2, 0, 1, 0, 9, 0, 0, 0], 95 | [0, 0, 7, 0, 0, 0, 2, 4, 0], 96 | [0, 6, 4, 0, 1, 0, 5, 9, 0], 97 | [0, 9, 8, 0, 0, 0, 3, 0, 0], 98 | [0, 0, 0, 8, 0, 3, 0, 2, 0], 99 | [0, 0, 0, 0, 0, 0, 0, 0, 0], 100 | [0, 0, 0, 2, 7, 5, 9, 0, 0], 101 | ] 102 | 103 | diabolical = [ 104 | [3, 6, 0, 0, 0, 0, 1, 7, 0], 105 | [0, 1, 4, 7, 0, 0, 0, 0, 0], 106 | [0, 0, 7, 0, 8, 0, 0, 0, 0], 107 | [0, 0, 1, 2, 0, 6, 0, 0, 5], 108 | [9, 0, 0, 0, 0, 0, 0, 0, 4], 109 | [4, 0, 0, 5, 0, 8, 3, 0, 0], 110 | [0, 0, 0, 0, 5, 0, 2, 0, 0], 111 | [0, 0, 0, 0, 0, 7, 4, 3, 0], 112 | [0, 4, 9, 0, 0, 0, 0, 5, 6], 113 | ] 114 | 115 | tdd_cw = [ 116 | [9, 0, 0, 0, 8, 0, 0, 0, 1], 117 | [0, 0, 0, 4, 0, 6, 0, 0, 0], 118 | [0, 0, 5, 0, 7, 0, 3, 0, 0], 119 | [0, 6, 0, 0, 0, 0, 0, 4, 0], 120 | [4, 0, 1, 0, 6, 0, 5, 0, 8], 121 | [0, 9, 0, 0, 0, 0, 0, 2, 0], 122 | [0, 0, 7, 0, 3, 0, 2, 0, 0], 123 | [0, 0, 0, 7, 0, 5, 0, 0, 0], 124 | [1, 0, 0, 0, 4, 0, 0, 0, 7] 125 | ] 126 | 127 | 128 | TEST_SUDOKUS = [ 129 | (brute_force, True), 130 | (hard_1, True), 131 | (hard_2, True), 132 | (expert, True), 133 | (extreme, True), 134 | (fiendish, True), 135 | (diabolical, True), 136 | (tdd_cw, True), 137 | ] 138 | 139 | 140 | @pytest.mark.parametrize('m, result', TEST_SUDOKUS) 141 | def test_solver_combo_approach_benchmark(m, result): 142 | """ 143 | Test that function solver can solve a given sudoku of any difficulty correctly using a mix of traditional solving techniques and 144 | brute force. 145 | """ 146 | from sudoku_solver_hard_cw_submission import solver, valid 147 | from copy import deepcopy 148 | brute_m = deepcopy(m) 149 | solved_sudoku = solver(brute_m) 150 | assert valid(solved_sudoku) == result 151 | -------------------------------------------------------------------------------- /kyu3/src/sudoku_solver_hard.py: -------------------------------------------------------------------------------- 1 | """Module to solve the code-kata https://www.codewars.com/kata/sudoku-solver.""" 2 | 3 | from math import sqrt, ceil 4 | from itertools import islice, chain, groupby 5 | from operator import itemgetter 6 | from collections import defaultdict 7 | 8 | def sudoku_solver(m): 9 | """Return a valid Sudoku for a given matrix. 10 | Store all starting numbers by looping through the Sudoku square by square 11 | Find all missing numbers by subtracting the 2 sets 12 | Generate all starting spots and find the one with the least amount of digits missing 13 | Generate candidates by finding all possible numbers for a given starting point 14 | Pop the starting point from a sorted representation of the previously built list 15 | Update Sudoku if a fit is found 16 | Remove updated digit from all lists of missing number and as a possible candidates 17 | Repeat with next starting point 18 | If no immediate match is found, scrap candidated and rebuild until all digits have been 19 | inserted. 20 | """ 21 | square_sides = int(sqrt(len(m))) 22 | dicts = initialize_dicts(m, square_sides) 23 | dicts, square_coords = populate_dicts(m, square_sides, dicts) 24 | dicts = get_missing(dicts) 25 | candidates = get_candidates(m, dicts, square_coords) 26 | m, candidates = scan_sudoku(m, dicts, square_coords, candidates) 27 | single_candidates = single_candidate(candidates, square_coords, dicts) 28 | m, candidates = fill_fit(m, dicts, square_coords, single_candidates=single_candidates) 29 | candidates = get_candidates(m, dicts, square_coords) 30 | naked_sets_fields_row, naked_sets_fields_cols = find_naked_sets(candidates, dicts, setlength=2) 31 | candidates, naked_sets = remove_naked_sets_from_candidates(candidates, naked_sets_fields_row, naked_sets_fields_cols) 32 | candidates = get_candidates(m, dicts, square_coords, naked_sets) 33 | naked_sets_fields_row, naked_sets_fields_cols = find_naked_sets(candidates, dicts, setlength=3) 34 | return m 35 | 36 | def initialize_dicts(m, square_sides): 37 | """Return dicts to hold information about the Sudoku.""" 38 | rows_missing = defaultdict(list) 39 | rows_missing = initialize_d(rows_missing, square_sides) 40 | cols_missing = defaultdict(list) 41 | cols_missing = initialize_d(cols_missing, square_sides) 42 | squares_missing = defaultdict(list) 43 | squares_missing = initialize_d(cols_missing, square_sides, 1) 44 | return rows_missing, cols_missing, squares_missing 45 | 46 | 47 | def initialize_d(d, square_sides, offset=0): 48 | """Return an initialized dict so empty rows or columns in the Sudoku are 49 | correctly handled.""" 50 | return {key:[] for key in range(offset, square_sides ** 2 + offset)} 51 | 52 | 53 | def fill_given_numbers(square, row, col, sq_nr, dicts, squares_coords): 54 | """Fill dicts with given numbers number for a given square.""" 55 | rm, cm, sm = dicts 56 | sq = squares_coords 57 | for row_idx, sr in enumerate(square): 58 | for col_idx, sv in enumerate(sr): 59 | coord = (row + row_idx, col + col_idx) 60 | if sv == 0: 61 | sq[coord] = sq_nr 62 | continue 63 | rm[coord[0]].append(sv) 64 | cm[coord[1]].append(sv) 65 | sm[sq_nr].append(sv) 66 | return dicts, sq 67 | 68 | 69 | def populate_dicts(m, square_sides, dicts): 70 | """Return dicts holding information about fills in given Sudoku.""" 71 | sq_nr = 0 72 | squares_coords = {} 73 | for row in range(0, square_sides ** 2, square_sides): 74 | for col in range(0, square_sides ** 2, square_sides): 75 | sq_nr += 1 76 | square = [islice(m[i], col, square_sides + col) for i in range(row, row + square_sides)] 77 | dicts, square_coords = fill_given_numbers(square, row, col, sq_nr, dicts, squares_coords) 78 | return dicts, square_coords 79 | 80 | 81 | def get_missing(dicts): 82 | """Return dictionaries with swapped values from given numbers to missing numbers.""" 83 | for d in dicts: 84 | for k, v in d.items(): 85 | d[k] = set([1, 2, 3, 4, 5, 6, 7, 8, 9]) - set(v) 86 | return dicts 87 | 88 | 89 | def get_starting_spots(m, dicts, square_coords): 90 | """Return a list with coordinates as starting point for sudoku solver.""" 91 | rm, cm, sm = dicts 92 | starting_spots = [] 93 | row = 0 94 | col = 0 95 | max = 20 96 | start = -1 97 | for col in range(9): 98 | for row in range(9): 99 | if m[row][col] == 0: 100 | square = square_coords[(row, col)] - 1 101 | missing_numbers = len(cm[col]) + len(rm[row]) + len(sm[1 if square < 1 else square]) 102 | starting_spots.append((row, col, missing_numbers)) 103 | return starting_spots 104 | 105 | 106 | def get_candidates(m, dicts, square_coords, naked_sets=None): 107 | """Return a dict of candidates for all starting_spots in the Sudoko.""" 108 | starting_spots = get_starting_spots(m, dicts, square_coords) 109 | starting_spots.sort(key=itemgetter(2)) 110 | rm, cm, sm = dicts 111 | c = {} 112 | for coordinate in starting_spots: 113 | row, col, missing = coordinate 114 | c[(row, col)] = [n for n in cm[col] if n in rm[row] and n in sm[square_coords[row, col]]] 115 | try: 116 | c[(row, col)] = [n for n in c[(row, col)] if n not in naked_sets[(row, col)]] 117 | except (KeyError, TypeError): 118 | continue 119 | return c 120 | 121 | 122 | def find_fit(candidates): 123 | """Return a tuple with coordinate and value to update from a sorted 124 | representation of a dict. If no fit can be found, return None.""" 125 | try: 126 | fit = sorted(candidates.items(), key=lambda x: len(x[1])).pop(0) 127 | except AttributeError: 128 | return None 129 | row, col = fit[0] 130 | n = fit[1].pop() 131 | if len(fit[1]) == 0: 132 | return row, col, n 133 | return None 134 | 135 | 136 | def update_sudoku(fit, m): 137 | """Return an updated Sudoku.""" 138 | row, col, n = fit 139 | try: 140 | if m[row][col] != 0: 141 | raise ValueError 142 | m[row][col] = n 143 | except ValueError: 144 | raise ValueError('This coordinate has already been updated.') 145 | return m 146 | 147 | 148 | def remove_updated_from_dicts(fit, dicts, squares_coords): 149 | """Return dicts with updated digit removed from missing digits.""" 150 | row, col, n = fit 151 | rm, cm, sm = dicts 152 | sq = squares_coords 153 | rm[row].remove(n) 154 | cm[col].remove(n) 155 | sm[squares_coords[row, col]].remove(n) 156 | del sq[(row, col)] 157 | return dicts 158 | 159 | 160 | def remove_from_candidates(fit, candidates): 161 | """Return candidates with updated digit removed from all coordinates.""" 162 | if not candidates: return candidates 163 | row, col, n = fit 164 | del candidates[(row, col)] 165 | for k, v in candidates.items(): 166 | if k[0] == row or k[1] == col: 167 | try: 168 | v.remove(n) 169 | except: 170 | continue 171 | return candidates 172 | 173 | 174 | def fill_fit(m, dicts, squares_coords, candidates=[], single_candidates=[]): 175 | """Return an updated Sudoku by either finding a fit or taking a fit from a provided 176 | list of fits and filling it in as long as a fit is found.""" 177 | while True: 178 | try: 179 | if single_candidates: 180 | num, coord = single_candidates.pop() 181 | fit = (coord[0], coord[1], num) 182 | else: 183 | fit = find_fit(candidates) 184 | # print("fit: ", fit) 185 | except IndexError: 186 | return m, candidates 187 | if fit: 188 | m = update_sudoku(fit, m) 189 | dicts = remove_updated_from_dicts(fit, dicts, squares_coords) 190 | candidates = remove_from_candidates(fit, candidates) 191 | else: 192 | return m, candidates 193 | 194 | 195 | def scan_sudoku(m, dicts, square_coords, candidates): 196 | """Return an updated Sudoku by using the scanning technique to find fits and 197 | filling them in. After each scan, list of candidates is rebuild until no 198 | further immediate fills are possible.""" 199 | while True: 200 | if len(sorted(candidates.items(), key=lambda x: len(x[1])).pop(0)[1]) > 1: # no longer easily solvable 201 | break 202 | m, candidiates = fill_fit(m, dicts, square_coords, candidates=candidates) 203 | starting_spots = get_starting_spots(m, dicts, square_coords) 204 | starting_spots.sort(key=itemgetter(2)) 205 | candidates = get_candidates(m, dicts, square_coords) 206 | if not candidates: break 207 | return m, candidates 208 | 209 | 210 | def squares_to_missing(square_coords): 211 | """Return a dict of square numbers as key and empty fields in the Sudoku as values.""" 212 | squares_missing = defaultdict(list) 213 | for k, v in square_coords.items(): 214 | squares_missing[v].append(k) 215 | return squares_missing 216 | 217 | 218 | def single_candidate(candidates, square_coords, squares_missing): 219 | """Return a number which is a single candidate for a coordinate in the list of candidates: 220 | Build a dict with square as key and empty fields as value 221 | Go through every square and get all missing fields 222 | For every missing field in that square, get the possible numbers 223 | Look for a number which is only missing in one field in a square.""" 224 | out = [] 225 | coords_missing_in_square = squares_to_missing(square_coords) 226 | for k, v in coords_missing_in_square.items(): 227 | single_candidates = defaultdict(list) 228 | seen = set() 229 | for coord in v: 230 | pn = set(candidates[coord]) 231 | if pn.issubset(seen): 232 | continue 233 | for n in pn: 234 | if n in seen: 235 | continue 236 | if single_candidates.get(n, 0): 237 | seen.add(n) 238 | continue 239 | single_candidates[n] = coord 240 | if len(seen) == len(squares_missing[k]): 241 | continue 242 | out.append([(k, v) for k, v in single_candidates.items() if k not in seen]) 243 | return list(chain.from_iterable(out)) 244 | 245 | 246 | def find_naked_sets(candidates, dicts, setlength=2): 247 | """Return a dict of naked sets mapped to coordinates. A naked set is a set of numbers 248 | which are the only possible values for fields along a row or column.""" 249 | c = candidates 250 | ns = build_possible_naked_sets(c, setlength=setlength) 251 | cpns = build_coords_per_naked_set(ns) 252 | ns = update_naked_set(ns, cpns) 253 | rows = get_coords_naked_sets(ns, candidates, dicts, row_or_col=0, setlength=setlength) 254 | cols = get_coords_naked_sets(ns, candidates, dicts, row_or_col=1, setlength=setlength) 255 | return rows, cols 256 | 257 | 258 | def build_possible_naked_sets(c, setlength=2): 259 | """Return a dict with coordinates and possible values with length of setlength, 2 by default.""" 260 | ns = {} 261 | pairs = [p for p in c.values() if len(p) == setlength] 262 | for k, v in c.items(): 263 | if v in pairs: 264 | ns[k] = sorted(v) 265 | return ns 266 | 267 | 268 | def build_coords_per_naked_set(ns): 269 | """Return a new dict with inverted values from ns""" 270 | cpns = defaultdict(list) 271 | for pair in ns.values(): 272 | if cpns.get(tuple(pair), 0): continue 273 | for k, v in ns.items(): 274 | row, col = k 275 | if v == pair: 276 | cpns[tuple(pair)].append(k) 277 | return cpns 278 | 279 | 280 | def update_naked_set(ns, cpns): 281 | """Return an updated dict of naked set.""" 282 | for k, v in cpns.items(): 283 | if len(v) == 1: 284 | del ns[v.pop()] 285 | else: 286 | if len(set(v)) < 3: 287 | for coord in set(v): 288 | del ns[coord] 289 | return ns 290 | 291 | 292 | def get_coords_naked_sets(ns, candidates, dicts, row_or_col=0, setlength=2): 293 | """Return a list of coordinates where naked sets can be removed from.""" 294 | c = candidates 295 | rm, cm, sm = dicts 296 | group = [] 297 | out = {} 298 | ns_sorted = {el[0]:el[1] for el in sorted(ns.items(), key=lambda x: x[0])} 299 | for k, g in groupby(ns_sorted, lambda x: x[row_or_col]): 300 | coords = list(g) 301 | key = tuple(ns[coords[0]]) 302 | if len(coords) > 1: #if list has only one element, there are no naked sets for that key 303 | if len(cm[k] if row_or_col == 1 else rm[k]) > setlength: #check missing row or col dict to see if more than given setlength is missing 304 | out[key] = [coord for coord in c.keys() if coord[row_or_col] == k and coord not in coords] 305 | return out 306 | 307 | 308 | def remove_naked_sets_from_candidates(c, *args, naked_sets=defaultdict(list)): 309 | """Return an updated list of candidates and naked sets after removing possible numbers by looking at naked sets""" 310 | for d in args: 311 | for k, v in d.items(): 312 | for coord in v: 313 | c[coord] = [n for n in c[coord] if n not in k] 314 | naked_sets[coord].extend(list(k)) 315 | return c, dict(naked_sets) 316 | -------------------------------------------------------------------------------- /kyu4/Python/README.MD: -------------------------------------------------------------------------------- 1 | # Python coding challenges: 2 | 3 | ## kyu4 4 | 5 | * Validate Sudoku with size `NxN` 6 | * Valid Braces 7 | * Next Bigger Number 8 | * One Line Task: Palindrome String 9 | * Strip Url Params 10 | * Ranking Poker Hands 11 | * Sortable Poker Hands 12 | 13 | ## Details for each kata completed 14 | 15 | ### Validate Sudoku with size `NxN` 16 | * Description of Kata: Given a Sudoku data structure with size NxN, N > 0 and 17 | √N == integer, write a method to validate if it has been filled out correctly. 18 | * Difficulty: kyu4 19 | * Module: sudoku_validator.py 20 | * Tests: test_sudoku_validator.py 21 | * [Link](https://www.codewars.com/kata/validate-sudoku-with-size-nxn) 22 | 23 | ### Valid Braces 24 | * Description of Kata: Write a function called validBraces that takes a string of 25 | braces, and determines if the order of the braces is valid. validBraces should 26 | return true if the string is valid, and false if it's invalid. 27 | * Difficulty: kyu4 28 | * Module: valid_braces.py 29 | * Tests: test_valid_braces.py 30 | * [Link](https://www.codewars.com/kata/valid-braces) 31 | 32 | ### Next Bigger Number 33 | * Description of Kata: You have to create a function that takes a positive integer 34 | number and returns the next bigger number formed by the same digits: 35 | * Difficulty: kyu4 36 | * Module: next_bigger.py 37 | * Tests: test_next_bigger.py 38 | * [Link](https://www.codewars.com/kata/next-bigger-number-with-the-same-digits) 39 | * Comments about most interesting solution/best practice: 40 | Some of the solutions are short but are basically brute-forcing it. The below 41 | solution is 2nd highest voted but I like it better than the top voted one due to 42 | readability and speed. 43 | ```python 44 | import itertools 45 | def next_bigger(n): 46 | s = list(str(n)) 47 | for i in range(len(s)-2,-1,-1): 48 | if s[i] < s[i+1]: 49 | t = s[i:] 50 | m = min(filter(lambda x: x>t[0], t)) 51 | t.remove(m) 52 | t.sort() 53 | s[i:] = [m] + t 54 | return int("".join(s)) 55 | return -1 56 | ``` 57 | 58 | ### One Line Task: Palindrome String 59 | * Description of Kata: Your task is to generate a palindrome string, using the 60 | specified length n, the specified characters c(all characters in c must be used 61 | at least once), and the code length should less than 55 characters 62 | * Difficulty: kyu4 63 | * Module: palindrome_one_liner.py 64 | * Tests: test_palindrome_one_liner.py 65 | * [Link](https://www.codewars.com/kata/one-line-task-palindrome-string) 66 | * Comments about most interesting solution/best practice: Didn't know about method 67 | center, nice use here. 68 | ```python 69 | palindrome=lambda n,s:(s+s[-1-n%2::-1]).center(n,s[0]) 70 | ``` 71 | 72 | ### Strip Url Params 73 | * Description of Kata: Write a method that 74 | Removes any duplicate query string parameters from the url 75 | Removes any query string parameters specified within the 2nd argument (optional array) 76 | * Difficulty: kyu4 77 | * Module: url_strip_params.py 78 | * Tests: test_palindrome_one_liner.py 79 | * [Link](https://www.codewars.com/kata/strip-url-params) 80 | 81 | ### Ranking Poker Hands 82 | * Descripton of Kata: Create a poker hand that has a method to compare itself to another poker hand: 83 | compare_with(self, other_hand) 84 | A poker hand has a constructor that accepts a string containing 5 cards: 85 | PokerHand(hand) 86 | * Difficulty: kyu4 87 | * Module: poker_rankings.py 88 | * Tests: test_poker_rankings.py 89 | * [Link](https://www.codewars.com/kata/ranking-poker-hands/python) 90 | 91 | ### Sortable Poker Hands 92 | * Descripton of Kata: Write an algorithm which sorts Texas Hold'em poker hands. 93 | * Difficulty: kyu4 94 | * Module: sortable_poker_hands.py 95 | * Tests: test_sortable_poker_hands.py 96 | * [Link](https://www.codewars.com/kata/sortable-poker-hands/python) 97 | -------------------------------------------------------------------------------- /kyu4/Python/src/holdem.py: -------------------------------------------------------------------------------- 1 | """Solution for https://www.codewars.com/kata/texas-holdem-poker/python.""" 2 | 3 | from collections import Counter, defaultdict 4 | from operator import itemgetter 5 | 6 | CARDS = ['2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K', 'A'] 7 | 8 | class PokerHand(object): 9 | 10 | def __init__(self, hand): 11 | self.values = sorted([CARDS.index(c) for c in hand], reverse=True) 12 | 13 | self.val_cnt = defaultdict(int) 14 | for card in self.values: 15 | self.val_cnt[card] += 1 16 | 17 | self.groups = sorted(self.val_cnt.items(), key=itemgetter(1, 0), reverse=True) 18 | 19 | @property 20 | def score(self): 21 | points = 0.0 22 | if self._is_straight: 23 | points += 7 24 | cards = 0 25 | for k, v in self.groups: 26 | if cards + v > 5: continue 27 | cards += v 28 | points += v ** 2 29 | if cards == 5: return points 30 | 31 | @property 32 | def _is_straight(self): 33 | """Return True if combination of board and hole cards make a straight.""" 34 | cards = [k for k in self.val_cnt.keys()] 35 | for x in range(3): 36 | hand = sorted(cards[x:], reverse=True) 37 | if len(hand) < 5: return False 38 | straight = [hand[i] for i in range(4) if hand[i] - 1 == hand[i + 1]] 39 | if len(straight) == 4: 40 | last_card = hand.index(straight[-1]) + 1 41 | straight.append(hand[last_card]) 42 | self.values = straight 43 | self.val_cnt = defaultdict(int) 44 | for card in self.values: 45 | self.val_cnt[card] += 1 46 | self.groups = sorted(self.val_cnt.items(), key=itemgetter(1, 0), reverse=True) 47 | return True 48 | return False 49 | 50 | def compare_with(self, villain): 51 | """Return the winner given 2 pokerhand objects or return tie.""" 52 | if villain.score > self.score: 53 | return 'B' 54 | elif villain.score < self.score: 55 | return 'A' 56 | else: 57 | for i in range(0, len(self.groups)): 58 | if villain.groups[i][0] > self.groups[i][0]: 59 | return 'B' 60 | elif villain.groups[i][0] < self.groups[i][0]: 61 | return 'A' 62 | return 'AB' 63 | 64 | def texasHoldem(board, hole_cards_hero, hole_cards_villain): 65 | """Return the winning hand given board cards and 2 player's hands.""" 66 | hero = PokerHand(board + hole_cards_hero) 67 | villain = PokerHand(board + hole_cards_villain) 68 | return hero.compare_with(villain) 69 | -------------------------------------------------------------------------------- /kyu4/Python/src/init.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wenima/codewars/fc508af7ec2ad1b55eeba4e52b94b41f61cd75ae/kyu4/Python/src/init.py -------------------------------------------------------------------------------- /kyu4/Python/src/next_bigger.py: -------------------------------------------------------------------------------- 1 | """Solution for kata https://www.codewars.com/kata/next-bigger-number-with-the-same-digits.""" 2 | 3 | from itertools import permutations, islice 4 | 5 | def next_bigger(n): 6 | """Takes a positive integer and returns the next bigger number formed using 7 | the same digits by cutting the number at a certain index, creating sorted permutations 8 | and compare permutations until next bigger number is found. Checks before if a 9 | next bigger number can be formed by a simple swap of last 2 digits. If no 10 | bigger number can be formed, return -1.""" 11 | if is_biggest(n): 12 | return -1 13 | sn = str(n) 14 | idx = cut_number(n) 15 | slice_at = len(sn) - idx - 1 16 | if idx == 1: 17 | out = [sn[-1], sn[-2]] 18 | return int(''.join([d for d in islice(sn, slice_at)] + out)) 19 | slice = sn[slice_at:] 20 | n = int(''.join([d for d in slice])) 21 | next_bigger_num = n * 10 22 | for pn in sorted(permutations(slice), reverse=True): 23 | num_pn = int(''.join([d for d in pn])) 24 | if num_pn <= n: 25 | return int(''.join([d for d in islice(sn, slice_at)] + out)) 26 | if n < num_pn < next_bigger_num: 27 | next_bigger_num = num_pn 28 | out = [d for d in pn] 29 | 30 | 31 | def cut_number(n): 32 | """Return an index at which to cut the number to limit permutations to only 33 | those necessary.""" 34 | i = 0 35 | while True: 36 | cur = n % 10 37 | ahead = n // 10 % 10 38 | if cur > ahead: 39 | i += 1 40 | break 41 | i += 1 42 | n //= 10 43 | return i 44 | 45 | 46 | def is_biggest(n): 47 | """Return True if given n is already the biggest number possible to be formed 48 | of n's digits.""" 49 | return True if int(''.join(sorted([d for d in str(n)], reverse=True))) == n else False 50 | -------------------------------------------------------------------------------- /kyu4/Python/src/palindrome_one_liner.py: -------------------------------------------------------------------------------- 1 | """Solution for kata http://www.codewars.com/kata/one-line-task-palindrome-string/.""" 2 | 3 | palindrome=lambda n,s:(s*n)[:n//2]+(s*n)[~-n//2::-1] 4 | -------------------------------------------------------------------------------- /kyu4/Python/src/poker_rankings.py: -------------------------------------------------------------------------------- 1 | """Solution for https://www.codewars.com/kata/ranking-poker-hands/train/python.""" 2 | 3 | from operator import itemgetter 4 | from collections import defaultdict 5 | 6 | RESULT = ["Loss", "Tie", "Win"] 7 | 8 | SUITS = { 9 | 'S': ('Spades', 1), 10 | 'H': ('Hearts', 1), 11 | 'D': ('Diamonds', 1), 12 | 'C': ('Clubs', 1) 13 | } 14 | 15 | RANKS = { 16 | 'A': 14, 17 | '2': 2, 18 | '3': 3, 19 | '4': 4, 20 | '5': 5, 21 | '6': 6, 22 | '7': 7, 23 | '8': 8, 24 | '9': 9, 25 | 'T': 10, 26 | 'J': 11, 27 | 'Q': 12, 28 | 'K': 13 29 | } 30 | 31 | 32 | MADE_HANDS = { 33 | 'straight flush': 9, 34 | '4 of a kind': 8, 35 | 'full house': 7, 36 | 'flush': 6, 37 | 'straight': 5, 38 | 'set': 4, 39 | 'two pair': 3, 40 | 'one pair': 1, 41 | 'high card': 0 42 | } 43 | 44 | class PokerHand(object): 45 | """Create an object representing a Poker Hand based on an input of a 46 | string which represents the best 5 card combination from the player's hand 47 | and board cards. 48 | 49 | Attributes: 50 | vals: a list of card's values 51 | 52 | suits: a list of card's suits 53 | 54 | hand: a sorted list of tuples representing face value and card value ordered 55 | by highest card in descending order 56 | 57 | val_cnt: a dict containing each card and the number of times it appears in the hand 58 | 59 | hand_value: the hand value according to MADE_HANDS 60 | 61 | 62 | Methods: 63 | compare_with(self, villain): takes in Hero's Hand (self) and Villain's 64 | Hand (villain) and compares both hands according to rules of Texas Hold'em. 65 | Returns one of 3 strings (Win, Loss, Tie) based on wether Hero's hand 66 | is better than villain's hand. 67 | Helper Methods: 68 | _is_straight(self): returns True if hand is a straight 69 | _is_flush(self): returns True if hand is a flush 70 | """ 71 | 72 | def __init__(self, hand): 73 | """Initialize a poker hand based on a 10 character string input representing 74 | 5 cards. 75 | """ 76 | hand = hand.replace(' ', '') 77 | self.vals = [c for c in hand if c in RANKS.keys()] 78 | self.suits = [c for c in hand if c in SUITS.keys()] 79 | self.hand = sorted([RANKS[c] for c in self.vals], reverse=True) 80 | self.val_cnt = defaultdict(int) 81 | 82 | for card in self.vals: 83 | self.val_cnt[card] += 1 84 | self._total_value = self._total_value() 85 | 86 | def _total_value(self): 87 | """Return a tuple containing of an int representing hand value and a tuple 88 | of sorted high card values.""" 89 | if self._is_five_high_straight: 90 | del self.hand[0] 91 | self.hand.append(1) 92 | if self._hand_value in [1, 4]: 93 | sorted_d = sorted(self.val_cnt.items(), reverse=True) 94 | return (self._hand_value, sorted_d[0][0], tuple(self.hand)) 95 | return (self._hand_value, tuple(self.hand)) 96 | 97 | @property 98 | def _is_five_high_straight(self): 99 | if sum(self.hand) == 28: 100 | return True 101 | 102 | @property 103 | def _is_straight(self): 104 | """Return True if hand is a straight.""" 105 | if self._is_five_high_straight: return True 106 | previous_card = self.hand[-1] - 1 107 | for card in sorted(self.hand): 108 | if previous_card + 1 != card: return False 109 | previous_card = card 110 | return True 111 | 112 | @property 113 | def _is_flush(self): 114 | """Return True if hand is a flush.""" 115 | return True if len(set(self.suits)) == 1 else False 116 | 117 | @property 118 | def _hand_value(self): 119 | """Return a value based on hand strength.""" 120 | hand_value = 0 121 | """Check if hand is pair plus""" 122 | if len(set(self.val_cnt.values())) > 1: 123 | sorted_d = sorted(self.val_cnt.values(), reverse = True) 124 | if sorted_d[0] == 2 and sorted_d[1] == 2: return 3 125 | for pair_plus in sorted_d: 126 | if pair_plus == 1: return hand_value 127 | elif pair_plus == 4: 128 | return 8 129 | elif pair_plus == 3: 130 | hand_value = 4 131 | elif pair_plus == 2: 132 | if hand_value == 4: 133 | return 7 134 | else: 135 | return 1 136 | if self._is_straight: 137 | if self._is_flush: 138 | return 9 139 | else: 140 | return 5 141 | if self._is_flush: 142 | return 6 143 | if len(set(self.val_cnt.values())) == 1: return hand_value 144 | 145 | def compare_with(self, other): 146 | """Return one of 3 outcomes from result const.""" 147 | if self._total_value > other._total_value: 148 | return 'Win' 149 | elif self._total_value < other._total_value: 150 | return 'Loss' 151 | else: 152 | return 'Tie' 153 | -------------------------------------------------------------------------------- /kyu4/Python/src/sortable_poker_hands.py: -------------------------------------------------------------------------------- 1 | """Solution for https://www.codewars.com/kata/sortable-poker-hands/""" 2 | 3 | from operator import itemgetter 4 | from collections import defaultdict 5 | import functools 6 | 7 | SUITS = { 8 | 'S': ('Spades', 1), 9 | 'H': ('Hearts', 1), 10 | 'D': ('Diamonds', 1), 11 | 'C': ('Clubs', 1) 12 | } 13 | 14 | RANKS = { 15 | 'A': 14, 16 | '2': 2, 17 | '3': 3, 18 | '4': 4, 19 | '5': 5, 20 | '6': 6, 21 | '7': 7, 22 | '8': 8, 23 | '9': 9, 24 | 'T': 10, 25 | 'J': 11, 26 | 'Q': 12, 27 | 'K': 13 28 | } 29 | 30 | MADE_HANDS = { 31 | 'straight flush': 9, 32 | '4 of a kind': 8, 33 | 'full house': 7, 34 | 'flush': 6, 35 | 'straight': 5, 36 | 'set': 4, 37 | 'two pair': 3, 38 | 'one pair': 1, 39 | 'high card': 0 40 | } 41 | 42 | @functools.total_ordering 43 | class PokerHand(object): 44 | """Create an object representing a Poker Hand based on an input of a 45 | string which represents the best 5 card combination from the player's hand 46 | and board cards. 47 | 48 | Attributes: 49 | high_card_vals: a list of card values sorted highest to lowest 50 | 51 | vals: a list of card's values 52 | 53 | suits: a list of card's suits 54 | 55 | hand: a sorted list of tuples representing face value and card value ordered 56 | by highest card in descending order 57 | 58 | val_cnt: a dict containing each card and the number of times it appears in the hand 59 | 60 | has_two_pair: True if hand is a 2 pair hand, else False 61 | 62 | hand_value: the hand value according to MADE_HANDS 63 | 64 | total_value: a tuple of int representing the value of the hand and a tuple of high cards 65 | 66 | Methods: 67 | compare_with(self, villain): takes in Hero's Hand (self) and Villain's 68 | Hand (villain) and compares both hands according to rules of Texas Hold'em. 69 | Returns one of 3 strings (Win, Loss, Tie) based on wether Hero's hand 70 | is better than villain's hand. 71 | Helper Methods: 72 | _is_straight(self): returns True if hand is a straight 73 | _is_flush(self): returns True if hand is a flush 74 | """ 75 | 76 | def __repr__(self): return repr(self.passed_hand) 77 | 78 | def __lt__(self, other): 79 | return self._total_value < other._total_value 80 | 81 | def __eq__(self, other): 82 | return self.passed_hand == other 83 | 84 | def __init__(self, hand): 85 | """Initialize a poker hand based on a 10 character string input representing 86 | 5 cards. 87 | """ 88 | self.passed_hand = hand 89 | hand = hand.replace(' ', '') 90 | self.high_card_vals = [] 91 | self.vals = [c for c in hand if c in RANKS.keys()] 92 | self.suits = [c for c in hand if c in SUITS.keys()] 93 | self.hand = sorted([RANKS[c] for c in self.vals], reverse=True) 94 | self.val_cnt = defaultdict(int) 95 | 96 | for card in self.vals: 97 | self.val_cnt[card] += 1 98 | self._total_value = self._total_value() 99 | 100 | def _total_value(self): 101 | """Return a tuple containing of an int representing hand value and a tuple 102 | of sorted high card values.""" 103 | if self._is_five_high_straight: 104 | del self.hand[0] 105 | self.hand.append(1) 106 | return (self._hand_value, tuple(self.hand)) 107 | 108 | @property 109 | def _is_five_high_straight(self): 110 | if sum(self.hand) == 28: 111 | return True 112 | 113 | @property 114 | def _is_straight(self): 115 | """Return True if hand is a straight.""" 116 | if self._is_five_high_straight: return True 117 | previous_card = self.hand[-1] - 1 118 | for card in sorted(self.hand): 119 | if previous_card + 1 != card: return False 120 | previous_card = card 121 | return True 122 | 123 | @property 124 | def _is_flush(self): 125 | """Return True if hand is a flush.""" 126 | return True if len(set(self.suits)) == 1 else False 127 | 128 | @property 129 | def _hand_value(self): 130 | """Return a value based on hand strength.""" 131 | hand_value = 0 132 | """Check if hand is pair plus""" 133 | if len(set(self.val_cnt.values())) > 1: 134 | sorted_d = sorted(self.val_cnt.values(), reverse = True) 135 | if sorted_d[0] == 2 and sorted_d[1] == 2: return 3 136 | for pair_plus in sorted_d: 137 | if pair_plus == 1: return hand_value 138 | elif pair_plus == 4: 139 | return 8 140 | elif pair_plus == 3: 141 | hand_value = 4 142 | elif pair_plus == 2: 143 | if hand_value == 4: 144 | return 7 145 | else: 146 | return 1 147 | if self._is_straight: 148 | if self._is_flush: 149 | return 9 150 | else: 151 | return 5 152 | if self._is_flush: 153 | return 6 154 | if len(set(self.val_cnt.values())) == 1: return hand_value 155 | -------------------------------------------------------------------------------- /kyu4/Python/src/sudoku_solver.py: -------------------------------------------------------------------------------- 1 | """Module to solve the code-kata https://www.codewars.com/kata/sudoku-solver.""" 2 | 3 | from math import sqrt, ceil 4 | from itertools import islice 5 | from operator import itemgetter 6 | from collections import defaultdict 7 | 8 | def sudoku_solver(m): 9 | """Return a valid Sudoku for a given matrix. 10 | Store all starting numbers by looping through the Sudoku square by square 11 | Find all missing numbers by subtracting the 2 sets 12 | Generate all starting spots and find the one with the least amount of digits missing 13 | Generate candidates by finding all possible numbers for a given starting point 14 | Pop the starting point from a sorted representation of the previously built list 15 | Update Sudoku if a fit is found 16 | Remove updated digit from all lists of missing number and as a possible candidates 17 | Repeat with next starting point 18 | If no immediate match is found, scrap candidated and rebuild until all digits have been 19 | inserted. 20 | """ 21 | square_sides = int(sqrt(len(m))) 22 | rows_missing = defaultdict(list) 23 | rows_missing = initialize_d(rows_missing, square_sides) 24 | cols_missing = defaultdict(list) 25 | cols_missing = initialize_d(cols_missing, square_sides) 26 | squares_missing = defaultdict(list) 27 | squares_missing = {key:[] for key in range(1, square_sides ** 2 + 1)} 28 | squares_coords = {} 29 | dicts = rows_missing, cols_missing, squares_missing 30 | sq_nr = 0 31 | for row in range(0, square_sides ** 2, square_sides): 32 | for col in range(0, square_sides ** 2, square_sides): 33 | sq_nr += 1 34 | square = [islice(m[i], col, square_sides + col) for i in range(row, row + square_sides)] 35 | fill_given_numbers(square, row, col, sq_nr, dicts, squares_coords) 36 | for d in dicts: 37 | d = get_missing(d) 38 | m = fill_sudoku(m, dicts, squares_coords) 39 | return m 40 | 41 | 42 | def initialize_d(d, square_sides): 43 | """Return an initialized dict so empty rows or columns in the Sudoku are 44 | correctly handled.""" 45 | return {key:[] for key in range(square_sides ** 2)} 46 | 47 | 48 | def fill_given_numbers(square, row, col, sq_nr, dicts, squares_coords): 49 | """Fill dicts with given numbers number for a given square.""" 50 | rm, cm, sm = dicts 51 | for row_idx, sr in enumerate(square): 52 | for col_idx, sv in enumerate(sr): 53 | coord = (row + row_idx, col + col_idx) 54 | if sv == 0: 55 | squares_coords[coord] = sq_nr 56 | continue 57 | rm[coord[0]].append(sv) 58 | cm[coord[1]].append(sv) 59 | sm[sq_nr].append(sv) 60 | 61 | 62 | def get_missing(d): 63 | """Return a dictionary with swapped values from given numbers to missing numbers.""" 64 | for k, v in d.items(): 65 | d[k] = set([1, 2, 3, 4, 5, 6, 7, 8, 9]) - set(v) 66 | return d 67 | 68 | 69 | def get_starting_spots(m, dicts, squares_coords): 70 | """Return a list with coordinates as starting point for sudoku solver.""" 71 | rm, cm, sm = dicts 72 | starting_spots = [] 73 | row = 0 74 | col = 0 75 | max = 20 76 | start = -1 77 | for col in range(9): 78 | for row in range(9): 79 | if m[row][col] == 0: 80 | square = squares_coords[(row, col)] - 1 81 | missing_numbers = len(cm[col]) + len(rm[row]) + len(sm[1 if square < 1 else square]) 82 | starting_spots.append((row, col, missing_numbers)) 83 | return starting_spots 84 | 85 | 86 | def get_candidates(starting_spots, candidates, dicts, squares_coords): 87 | """Return a dict of candidates for all starting_spots in the Sudoko.""" 88 | for coordinate in starting_spots: 89 | row, col, missing = coordinate 90 | rm, cm, sm = dicts 91 | candidates[(row, col)] = [n for n in cm[col] if n in rm[row] and n in sm[squares_coords[row, col]]] 92 | return candidates 93 | 94 | 95 | def find_fit(candidates): 96 | """Return a tuple with coordinate and value to update from a sorted 97 | representation of a dict.""" 98 | fit = sorted(candidates.items(), key=lambda x: len(x[1])).pop(0) 99 | row, col = fit[0] 100 | n = fit[1].pop() 101 | if len(fit[1]) == 0: 102 | return row, col, n 103 | return None 104 | 105 | def update_sudoku(fit, m): 106 | """Return an updated Sudoku.""" 107 | row, col, n = fit 108 | try: 109 | if m[row][col] != 0: 110 | raise ValueError 111 | m[row][col] = n 112 | except ValueError: 113 | raise ValueError('This coordinate has already been updated.') 114 | return m 115 | 116 | 117 | def remove_updated_from_dicts(fit, dicts, squares_coords): 118 | """Return dicts with updated digit removed from missing digits.""" 119 | row, col, n = fit 120 | rm, cm, sm = dicts 121 | rm[row].remove(n) 122 | cm[col].remove(n) 123 | sm[squares_coords[row, col]].remove(n) 124 | return dicts 125 | 126 | 127 | def remove_from_candidates(fit, candidates): 128 | """Return candidates with updated digit removed from all coordinates.""" 129 | row, col, n = fit 130 | del candidates[(row, col)] 131 | for k, v in candidates.items(): 132 | if k[0] == row or k[1] == col: 133 | try: 134 | v.remove(n) 135 | except: 136 | continue 137 | return candidates 138 | 139 | 140 | def fill_sudoku(m, dicts, squares_coords): 141 | """Return the solved Sudoku by continously updating numbers from the list of 142 | candidates. If no immediate candidates, rebuild the starting spots and list 143 | of candidates and repeat updating process until no more candidates.""" 144 | candidates = {} 145 | starting_spots = get_starting_spots(m, dicts, squares_coords) 146 | starting_spots.sort(key=itemgetter(2)) 147 | candidates = get_candidates(starting_spots, candidates, dicts, squares_coords) 148 | if len(sorted(candidates.items(), key=lambda x: len(x[1])).pop(0)[1]) > 1: # no longer easily solvable 149 | return m 150 | while True: 151 | candidates_amt = len(candidates) 152 | try: 153 | fit = find_fit(candidates) 154 | except IndexError: 155 | return m 156 | if fit: 157 | m = update_sudoku(fit, m) 158 | dicts = remove_updated_from_dicts(fit, dicts, squares_coords) 159 | candidates = remove_from_candidates(fit, candidates) 160 | if not candidates: 161 | return m 162 | else: 163 | candidates = {} #we are no longer interested in current candidates 164 | starting_spots = [] 165 | m = fill_sudoku(m, dicts, squares_coords) 166 | -------------------------------------------------------------------------------- /kyu4/Python/src/sudoku_validator.py: -------------------------------------------------------------------------------- 1 | """Module to solve the code-kata https://www.codewars.com/kata/validate-sudoku-with-size-nxn.""" 2 | 3 | from math import sqrt 4 | from itertools import islice 5 | 6 | class Sudoku(object): 7 | 8 | """Create a Sudoku object. 9 | 10 | Methods: 11 | is_valid(): Check if the Sudoku object is valid 12 | 13 | Rules for validation are: 14 | Data structure dimension: NxN where N > 0 and √N == integer 15 | Rows may only contain integers: 1..N (N included) 16 | Columns may only contain integers: 1..N (N included) 17 | 'Little squares' (3x3) may also only contain integers: 1..N (N included). 18 | """ 19 | def __init__(self, matrix=None): 20 | self.m = None 21 | self.square_sides = 0 22 | self.size = 0 23 | if matrix: 24 | self.size = len(matrix) 25 | self.m = matrix 26 | self.square_sides = int(sqrt(len(matrix))) 27 | self.nums = {x for x in range(1, len(self.m))} 28 | 29 | def _extract_small_square(self, row, col): 30 | return [islice(self.m[i], col, self.square_sides + col) for i in range(row, row + self.square_sides)] 31 | 32 | def _check_square(self, row): 33 | for col in range(0, self.square_sides * 2, self.square_sides): 34 | square = self._extract_small_square(row, col) 35 | return self._validate_square(square) 36 | 37 | def _validate_square(self, square): 38 | sum_square = sum([n for l in square for n in l]) 39 | sum_expected = sum([n for n in range(1, self.size + 1)]) 40 | return sum_square == sum_expected 41 | 42 | def is_valid(self): 43 | """Validate a Sudoku puzzle of size NxN.""" 44 | for row in self.m: 45 | if self.nums - set(row): return False #check all rows for valid numbers 46 | for i in range(len(self.m)): # check all cols for valid numbers 47 | nums = set() 48 | for j in range(len(self.m)): 49 | nums.add(self.m[j][i]) 50 | if self.nums - set(nums): return False 51 | for l in self.m: #because True is mask for 1 52 | for n in l: 53 | if isinstance(n, bool): 54 | return False 55 | for i in range(0, 1 if self.square_sides == 1 else self.square_sides * 2, self.square_sides): 56 | try: 57 | self._check_square(i) 58 | except TypeError: 59 | return False 60 | return True 61 | -------------------------------------------------------------------------------- /kyu4/Python/src/test_holdem.py: -------------------------------------------------------------------------------- 1 | """Tests for codewars kata https://www.codewars.com/kata/texas-holdem-poker/python.""" 2 | 3 | import pytest 4 | 5 | BOARDS = [ 6 | (['2', '3', '5', '6', '7'], True), 7 | (['9','8','K','A','4'], False) 8 | ] 9 | 10 | TEST_STRAIGHT = [ 11 | (['2','2','3','3','4'],['5','6'], True), 12 | (['9','8','3','3','4'],['5','6'], False), 13 | (['9','8','K','Q','4'],['7','6'], False), 14 | (['2','6','3','7','A'],['5','4'], True), 15 | (['2', '3', '4', '5', '6'], ['K', 'Q'], True), 16 | (['8', 'J', 'Q', 'A', '2'], ['9', '10'], True), 17 | ] 18 | 19 | TEST_INPUT = [ 20 | (['6','3','3','5','2'],['3','3'],['4','7'], "A"), 21 | (['4','3','2','5','Q'],['7','8'],['5','7'], "B"), 22 | (['4','3','2','5','9'],['Q','A'],['A','Q'], "AB"), 23 | (['K', '3', '4', '5', '6'], ['7', '8'], ['7', '9'], "A"), 24 | (['2', '3', '4', '5', '6'], ['K', 'Q'], ['A', 'K'], "AB"), 25 | (['2', '3', '7', '8', 'Q'], ['3', '3'], ['7', '7'], "B"), 26 | (['8', 'J', 'Q', 'A', '2'], ['9', '10'], ['A', 'A'], "A"), 27 | (['2', '2', '3', '3', 'Q'], ['J', 'J'], ['Q', '3'], "B"), 28 | ] 29 | 30 | @pytest.mark.parametrize('board, hole_cards, result', TEST_STRAIGHT) 31 | def test_hand_is_straight(board, hole_cards, result): 32 | """Test that combination of board and hole_cards has a made hand value of straight.""" 33 | from holdem import PokerHand 34 | heros_hand = PokerHand(board + hole_cards) 35 | assert heros_hand._is_straight == result 36 | 37 | @pytest.mark.parametrize('board, hole_cards_hero, hole_cards_villain, result', TEST_INPUT) 38 | def test_texasHoldem(board, hole_cards_hero, hole_cards_villain, result): 39 | """Test which hand is better given hero's hole cards and villain's hole cards.""" 40 | from holdem import PokerHand, texasHoldem 41 | assert texasHoldem(board, hole_cards_hero, hole_cards_villain) == result 42 | -------------------------------------------------------------------------------- /kyu4/Python/src/test_next_bigger.py: -------------------------------------------------------------------------------- 1 | """Tests for codewars kata https://www.codewars.com/kata/next-bigger-number-with-the-same-digits.""" 2 | 3 | import pytest 4 | 5 | 6 | TEST_BIGGEST = [ 7 | (9, True), 8 | (111, True), 9 | (531, True), 10 | (123, False), 11 | (9998, True), 12 | (54321, True), 13 | ] 14 | 15 | TEST_INPUT = [ 16 | (999, -1), 17 | (12, 21), 18 | (513, 531), 19 | (2017, 2071), 20 | (12345, 12354), 21 | (54312, 54321), 22 | (414, 441), 23 | (144, 414), 24 | (1234567890, 1234567908), 25 | ] 26 | 27 | 28 | @pytest.mark.parametrize('n, result', TEST_INPUT) 29 | def test_next_bigger(n, result): 30 | """Test next_bigger returns correct next bigger number.""" 31 | from next_bigger import next_bigger 32 | assert next_bigger(n) == result 33 | 34 | @pytest.mark.parametrize('n, result', TEST_BIGGEST) 35 | def test_is_biggest(n, result): 36 | """Test is_biggest returns -1 .""" 37 | from next_bigger import is_biggest 38 | assert is_biggest(n) == result 39 | -------------------------------------------------------------------------------- /kyu4/Python/src/test_palindrome_one_liner.py: -------------------------------------------------------------------------------- 1 | """Tests for codewars kata http://www.codewars.com/kata/one-line-task-palindrome-string/.""" 2 | 3 | import pytest 4 | 5 | 6 | TEST = [ 7 | (10, 'abcd', 'abcdaadcba'), 8 | ] 9 | 10 | 11 | @pytest.mark.parametrize('n, s, result', TEST) 12 | def test_make_palindrome(n, s, result): 13 | """Test palindrome is returned with length n.""" 14 | from palindrome_one_liner import palindrome 15 | assert palindrome(n, s) == result 16 | -------------------------------------------------------------------------------- /kyu4/Python/src/test_poker_rankings.py: -------------------------------------------------------------------------------- 1 | """Tests for codewars kata https://www.codewars.com/kata/ranking-poker-hands/.""" 2 | 3 | import pytest 4 | 5 | 6 | TEST_INPUT = [ 7 | ("2H 3H 4H 5H 6H", "KS AS TS QS JS", 'Loss'), 8 | ("2H 3H 4H 5H 6H", "AS AD AC AH JD", 'Win'), 9 | ("AS AH 2H AD AC", "JS JD JC JH 3D", 'Win'), 10 | ("AS AH 3H AD AC", "AS AH 2H AD AC", 'Win'), 11 | ("2S AH 2H AS AC", "JS JD JC JH AD", 'Loss'), 12 | ("2S AH 2H AS AC", "2H 3H 5H 6H 7H", 'Win'), 13 | ("AS 3S 4S 8S 2S", "2H 3H 5H 6H 7H", 'Win'), 14 | ("2H 3H 5H 6H 7H", "2S 3H 4H 5S 6C", 'Win'), 15 | ("2S 3H 4H 5S 6C", "3D 4C 5H 6H 2S", 'Tie'), 16 | ("2S 3H 4H 5S 6C", "AH AC 5H 6H AS", 'Win'), 17 | ("2S 2H 4H 5S 4C", "AH AC 5H 6H AS", 'Loss'), 18 | ("2S 2H 4H 5S 4C", "AH AC 5H 6H 7S", 'Win'), 19 | ("6S AD 7H 4S AS", "AH AC 5H 6H 7S", 'Loss'), 20 | ("2S AH 4H 5S KC", "AH AC 5H 6H 7S", 'Loss'), 21 | ("2S 3H 6H 7S 9C", "7H 3C TH 6H 9S", 'Loss'), 22 | ("4S 5H 6H TS AC", "3S 5H 6H TS AC", 'Win'), 23 | ("2S AH 4H 5S 6C", "AD 4C 5H 6H 2C", 'Tie'), 24 | ("AH AC 5H 5C QS", "AH AC 5H 5C KS", 'Loss'), 25 | ("AH AC 5H 5C QS", "KH KC 5H 5C QS", 'Win'), 26 | ("7C 7S KH 2H 7H", "3C 3S AH 2H 3H", 'Win'), 27 | ("3C 3S AH 2H 3H", "7C 7S KH 2H 7H", 'Loss'), 28 | ("6H 5H 4H 3H 2H", "5H 4H 3H 2H AH", "Win"), 29 | ("5H 4H 3H 2H AH", "5H 4H 3H 2H AH", "Tie"), 30 | ("5H 4H 3H 2H AH", "6H 5H 4H 3H 2H", "Loss"), 31 | ("AS 3S 4S 8S 2S", "2H 3H 5H 6H 7H", "Win"), 32 | ("2S 3H 6H 7S 9C", "7H 3C TH 6H 9S", "Loss") 33 | ] 34 | 35 | TEST_STRAIGHT = [ 36 | ("2H 3H 4H 5H 6H", True), 37 | ("AS AH 2H AD AC", False), 38 | ("2H 3H 5H 6H 7H", False), 39 | ("KS AS TS QS JS", True), 40 | ("8H 9H QS JS TH", True), 41 | ] 42 | 43 | TEST_FLUSH = [ 44 | ("2H 3H 4H 5H 6H", True), 45 | ("AS AH 2H AD AC", False), 46 | ("2H 3H 5H 6H 7H", True), 47 | ("KS AS TS QS JS", True), 48 | ("8H 9H QS JS TH", False), 49 | ("AS 3S 4S 8S 2S", True), 50 | ] 51 | 52 | TEST_VALUES = [ 53 | ("2H 2S 4H 5H 6H", 1), 54 | ("AS AH 2H 2D KC", 3), 55 | ("AS AH AC 2D KC", 4), 56 | ("AH AC 6S 6H AS", 7), 57 | ("8H 9H QS JS KH", 0), 58 | ("2H 3H 4H 5H 6H", 9), 59 | ] 60 | 61 | @pytest.mark.parametrize('hand, result', TEST_STRAIGHT) 62 | def test_hand_is_straight(hand, result): 63 | """Test that hand has a made hand value of straight.""" 64 | from poker_rankings import PokerHand 65 | heros_hand = PokerHand(hand) 66 | assert heros_hand._is_straight == result 67 | 68 | @pytest.mark.parametrize('hand, result', TEST_FLUSH) 69 | def test_hand_is_flush(hand, result): 70 | """Test that hand has a made hand value of flush.""" 71 | from poker_rankings import PokerHand 72 | heros_hand = PokerHand(hand) 73 | assert heros_hand._is_flush == result 74 | 75 | def test_hand_is_straightflush(): 76 | """Test that hand has a made hand value of straight flush.""" 77 | from poker_rankings import PokerHand 78 | heroes_hand = PokerHand("5H 4H 3H 2H AH") 79 | assert heroes_hand._is_flush == True 80 | assert heroes_hand._is_straight == True 81 | assert heroes_hand._hand_value == 9 82 | 83 | def test_tie_when_both_hands_are_straightflush(): 84 | """Test that if both hands are 5 high straight flushes, result is Tie.""" 85 | from poker_rankings import PokerHand 86 | heroes_hand = PokerHand("5H 4H 3H 2H AH") 87 | villains_hand = PokerHand("5H 4H 3H 2H AH") 88 | heroes_hand.compare_with(villains_hand) == 'Tie' 89 | 90 | @pytest.mark.parametrize('hand, result', TEST_VALUES) 91 | def test_hand_values(hand, result): 92 | """Test that hand has a made hand value of flush.""" 93 | from poker_rankings import PokerHand 94 | from collections import defaultdict 95 | heroes_hand = PokerHand(hand) 96 | assert heroes_hand._hand_value == result 97 | 98 | def test_hand_has_correct_high_card(): 99 | from poker_rankings import PokerHand 100 | heroes_hand = PokerHand("8H 9H QS JS KH") 101 | from collections import defaultdict 102 | assert heroes_hand.hand.pop(0) == 13 103 | assert heroes_hand.hand.pop(0) == 12 104 | 105 | @pytest.mark.parametrize('hand, other, result', TEST_INPUT) 106 | def test_compare_hero_to_villain(hand, other, result): 107 | from poker_rankings import PokerHand 108 | from collections import defaultdict 109 | hero, villain = PokerHand(hand), PokerHand(other) 110 | print(hero._total_value) 111 | print(villain._total_value) 112 | assert hero.compare_with(villain) == result 113 | -------------------------------------------------------------------------------- /kyu4/Python/src/test_sortable_poker_hands.py: -------------------------------------------------------------------------------- 1 | """Tests for codewars kata https://www.codewars.com/kata/ranking-poker-hands/.""" 2 | 3 | import pytest 4 | from random import shuffle 5 | 6 | SORTED_POKER_HANDS = [ 7 | "KS AS TS QS JS", 8 | "KS AS TS QS JS", 9 | "2H 3H 4H 5H 6H", 10 | "AS AD AC AH JD", 11 | "JS JD JC JH 3D", 12 | "2S AH 2H AS AC", 13 | "KH KC 3S 3H 3D", 14 | "2H 2C 3S 3H 3D", 15 | "3D 2H 3H 2C 2D", 16 | "AS 3S 4S 8S 2S", 17 | "2H 3H 5H 6H 7H", 18 | "2S 3H 4H 5S 6C", 19 | "2D AC 3H 4H 5S", 20 | "AC KH QH AH AS", 21 | "AH AC 5H 6H AS", 22 | "7C 7S KH 2H 7H", 23 | "7C 7S QH 2S 7D", 24 | "7C 7S TH 2S 7D", 25 | "AH AC 5H 5C KS", 26 | "AH AC 5H 5C QS", 27 | "AH AC 4H 5S 4C", 28 | "2S 2H 4H 5S 4C", 29 | "AH AC 5H 6H 7S", 30 | "AH AC 4H 6H 7S", 31 | "2S AH 4H 5S KC", 32 | "2S 3H 6H 7S 9C", 33 | ] 34 | 35 | def test_custom_sort_5high_straight(): 36 | """Test that 5 high straights are compared correctly.""" 37 | from sortable_poker_hands import PokerHand 38 | pokerhands = [] 39 | pokerhands.append(PokerHand("2D AC 3H 4H 5S")) 40 | pokerhands.append(PokerHand("2S 3H 4H 5S 6C")) 41 | pokerhands.sort(reverse=True) 42 | assert pokerhands[0].passed_hand == "2S 3H 4H 5S 6C" 43 | 44 | def test_repr(): 45 | """Test that repr of PokerHand class returns a string.""" 46 | from sortable_poker_hands import PokerHand 47 | hand = PokerHand("2D AC 3H 4H 5S") 48 | assert hand == '2D AC 3H 4H 5S' 49 | 50 | def test_custom_sort(): 51 | """Test the output of sort matches list.""" 52 | from sortable_poker_hands import PokerHand 53 | pokerhands = [] 54 | for hand in SORTED_POKER_HANDS: 55 | pokerhands.append(PokerHand(hand)) 56 | shuffle(pokerhands) 57 | pokerhands.sort(reverse=True) 58 | for i, hand in enumerate(pokerhands): 59 | assert hand == SORTED_POKER_HANDS[i] 60 | -------------------------------------------------------------------------------- /kyu4/Python/src/test_sudoku_solver.py: -------------------------------------------------------------------------------- 1 | """Tests for https://www.codewars.com/kata/sudoku-solver.""" 2 | 3 | import pytest 4 | 5 | base = [ 6 | [5, 3, 4, 6, 7, 8, 9, 1, 2], 7 | [6, 7, 2, 1, 9, 5, 3, 4, 8], 8 | [1, 9, 8, 3, 4, 2, 5, 6, 7], 9 | [8, 5, 9, 7, 6, 1, 4, 2, 3], 10 | [4, 2, 6, 8, 5, 3, 7, 9, 1], 11 | [7, 1, 3, 9, 2, 4, 8, 5, 6], 12 | [9, 6, 1, 5, 3, 7, 2, 8, 4], 13 | [2, 8, 7, 4, 1, 9, 6, 3, 5], 14 | [3, 4, 5, 2, 8, 6, 1, 7, 9] 15 | ] 16 | 17 | kata = [ 18 | [5,3,0,0,7,0,0,0,0], 19 | [6,0,0,1,9,5,0,0,0], 20 | [0,9,8,0,0,0,0,6,0], 21 | [8,0,0,0,6,0,0,0,3], 22 | [4,0,0,8,0,3,0,0,1], 23 | [7,0,0,0,2,0,0,0,6], 24 | [0,6,0,0,0,0,2,8,0], 25 | [0,0,0,4,1,9,0,0,5], 26 | [0,0,0,0,8,0,0,7,9] 27 | ] 28 | 29 | kata4 = [ 30 | [0, 1, 9, 0, 6, 0, 5, 4, 0], 31 | [0, 0, 0, 0, 0, 0, 0, 0, 0], 32 | [8, 2, 0, 9, 7, 4, 0, 3, 6], 33 | [0, 0, 1, 5, 0, 3, 8, 0, 0], 34 | [0, 0, 0, 0, 0, 0, 0, 0, 0], 35 | [0, 0, 2, 7, 0, 1, 6, 0, 0], 36 | [7, 5, 0, 1, 3, 8, 0, 9, 2], 37 | [0, 0, 0, 0, 0, 0, 0, 0, 0], 38 | [0, 8, 3, 0, 4, 0, 7, 1, 0] 39 | ] 40 | 41 | medium = [ 42 | [3, 0, 5, 0, 2, 0, 7, 0, 1], 43 | [0, 0, 0, 8, 0, 3, 0, 0, 2], 44 | [0, 6, 0, 0, 0, 5, 0, 0, 0], 45 | [0, 7, 1, 0, 0, 0, 0, 5, 0], 46 | [0, 0, 6, 0, 0, 0, 9, 0, 0], 47 | [0, 9, 0, 0, 0, 0, 4, 2, 0], 48 | [0, 0, 0, 6, 0, 0, 0, 9, 0], 49 | [9, 0, 0, 5, 0, 4, 0, 0, 5], 50 | [6, 0, 8, 0, 3, 0, 2, 0, 4] 51 | ] 52 | 53 | 54 | @pytest.fixture 55 | def solved_sudoku(): 56 | from sudoku_solver import sudoku_solver 57 | new_sudoku = base 58 | return new_sudoku 59 | 60 | @pytest.fixture 61 | def kata_sudoku(): 62 | from sudoku_solver import sudoku_solver 63 | new_sudoku = sudoku_solver(kata) 64 | return new_sudoku 65 | 66 | @pytest.fixture 67 | def kata4_sudoku(): 68 | from sudoku_solver import sudoku_solver 69 | new_sudoku = sudoku_solver(kata4) 70 | return new_sudoku 71 | 72 | @pytest.fixture 73 | def medium_sudoku(): 74 | from sudoku_solver import sudoku_solver 75 | new_sudoku = sudoku_solver(medium) 76 | return new_sudoku 77 | 78 | def test_solved_sudoku_validator(solved_sudoku): 79 | """Test solved sudoku to make sure validtor works.""" 80 | from sudoku_validator import Sudoku 81 | solved_sudoku = Sudoku(solved_sudoku) 82 | assert solved_sudoku.is_valid() == True 83 | 84 | def test_kata_sudoku_validator(kata_sudoku): 85 | """Test sudoku_validator returns correct result.""" 86 | from sudoku_validator import Sudoku 87 | kata_sudoku = Sudoku(kata_sudoku) 88 | assert kata_sudoku.is_valid() == True 89 | 90 | def test_kata4_sudoku_validator(kata4_sudoku): 91 | """Test sudoku_validator returns correct result.""" 92 | from sudoku_validator import Sudoku 93 | kata4_sudoku = Sudoku(kata4_sudoku) 94 | assert kata4_sudoku.is_valid() == True 95 | 96 | def test_medium_sudoku_validator(medium_sudoku): 97 | """Test sudoku_validator returns correct result.""" 98 | from sudoku_validator import Sudoku 99 | medium_sudoku = Sudoku(medium_sudoku) 100 | assert medium_sudoku.is_valid() == False 101 | 102 | def test_update_non_empty_field_throws_exception(): 103 | """Test that update sudoku raises exception.""" 104 | from sudoku_solver import update_sudoku 105 | with pytest.raises(Exception) as e_info: 106 | update_sudoku((0, 0, 1), medium) 107 | -------------------------------------------------------------------------------- /kyu4/Python/src/test_sudoku_validator.py: -------------------------------------------------------------------------------- 1 | """Tests for https://www.codewars.com/kata/validate-sudoku-with-size-nxn.""" 2 | 3 | import pytest 4 | 5 | valid_m = [ 6 | [7,8,4, 1,5,9, 3,2,6], 7 | [5,3,9, 6,7,2, 8,4,1], 8 | [6,1,2, 4,3,8, 7,5,9], 9 | 10 | [9,2,8, 7,1,5, 4,6,3], 11 | [3,5,7, 8,4,6, 1,9,2], 12 | [4,6,1, 9,2,3, 5,8,7], 13 | 14 | [8,7,6, 3,9,4, 2,1,5], 15 | [2,4,3, 5,6,1, 9,7,8], 16 | [1,9,5, 2,8,7, 6,3,4] 17 | ] 18 | 19 | invalid_m_str = [ 20 | ['7',8,4, 1,5,9, 3,2,6], 21 | [5,3,9, 6,7,2, 8,4,1], 22 | [6,1,2, 4,3,8, 7,5,9], 23 | 24 | [9,2,8, 7,1,5, 4,6,3], 25 | [3,5,7, 8,4,6, 1,9,2], 26 | [4,6,1, 9,2,3, 5,8,7], 27 | 28 | [8,7,6, 3,9,4, 2,1,5], 29 | [2,4,3, 5,6,1, 9,7,8], 30 | [1,9,5, 2,8,7, 6,3,4] 31 | ] 32 | 33 | invalid_m_float = [ 34 | [7.9999,8,4, 1,5,9, 3,2,6], 35 | [5,3,9, 6,7,2, 8,4,1], 36 | [6,1,2, 4,3,8, 7,5,9], 37 | 38 | [9,2,8, 7,1,5, 4,6,3], 39 | [3,5,7, 8,4,6, 1,9,2], 40 | [4,6,1, 9,2,3, 5,8,7], 41 | 42 | [8,7,6, 3,9,4, 2,1,5], 43 | [2,4,3, 5,6,1, 9,7,8], 44 | [1,9,5, 2,8,7, 6,3,4] 45 | ] 46 | 47 | invalid_m_not_symmetric = [ 48 | [1,2,3,4,5], 49 | [1,2,3,4], 50 | [1,2,3,4], 51 | [1] 52 | ] 53 | 54 | four_by_four = [ 55 | [1,4, 2,3], 56 | [3,2, 4,1], 57 | [4,1, 3,2], 58 | [2,3, 1,4] 59 | ] 60 | 61 | one_by_one = [ 62 | [1] 63 | ] 64 | 65 | one_value_true = [ 66 | [True] 67 | ] 68 | 69 | valid_2 = [ 70 | [1, 3, 2, 5, 7, 9, 4, 6, 8], 71 | [4, 9, 8, 2, 6, 1, 3, 7, 5], 72 | [7, 5, 6, 3, 8, 4, 2, 1, 9], 73 | [6, 4, 3, 1, 5, 8, 7, 9, 2], 74 | [5, 2, 1, 7, 9, 3, 8, 4, 6], 75 | [9, 8, 7, 4, 2, 6, 5, 3, 1], 76 | [2, 1, 4, 9, 3, 5, 6, 8, 7], 77 | [3, 6, 5, 8, 1, 7, 9, 2, 4], 78 | [8, 7, 9, 6, 4, 2, 1, 5, 3] 79 | ] 80 | 81 | invalid_last_column = [ 82 | [1, 3, 2, 5, 7, 9, 4, 6, 8], 83 | [4, 9, 8, 2, 6, 1, 3, 7, 5], 84 | [7, 5, 6, 3, 8, 4, 2, 1, 9], 85 | 86 | [6, 4, 3, 1, 5, 8, 7, 9, 2], 87 | [5, 2, 1, 7, 9, 3, 8, 4, 6], 88 | [9, 8, 7, 4, 2, 6, 5, 3, 1], 89 | 90 | [2, 1, 4, 9, 3, 5, 6, 8, 7], 91 | [3, 6, 5, 8, 1, 7, 9, 2, 4], 92 | [8, 7, 9, 6, 4, 2, 1, 3, 5] 93 | ] 94 | 95 | 96 | @pytest.fixture 97 | def invalid_sudoku(): 98 | from sudoku_validator import Sudoku 99 | new_sudoku = Sudoku(invalid_m_str) 100 | return new_sudoku 101 | 102 | @pytest.fixture 103 | def invalid_sudoku_float(): 104 | from sudoku_validator import Sudoku 105 | new_sudoku = Sudoku(invalid_m_float) 106 | return new_sudoku 107 | 108 | @pytest.fixture 109 | def invalid_sudoku_not_symmetric(): 110 | from sudoku_validator import Sudoku 111 | new_sudoku = Sudoku(invalid_m_not_symmetric) 112 | return new_sudoku 113 | 114 | @pytest.fixture 115 | def valid_sudoku_4x4(): 116 | from sudoku_validator import Sudoku 117 | new_sudoku = Sudoku(four_by_four) 118 | return new_sudoku 119 | 120 | @pytest.fixture 121 | def valid_sudoku_1x1(): 122 | from sudoku_validator import Sudoku 123 | new_sudoku = Sudoku(one_by_one) 124 | return new_sudoku 125 | 126 | @pytest.fixture 127 | def ivalid_sudoku_true(): 128 | from sudoku_validator import Sudoku 129 | new_sudoku = Sudoku(one_value_true) 130 | return new_sudoku 131 | 132 | 133 | def test_sudoku_validator_4x4(valid_sudoku_4x4): 134 | """Test sudoku_validator returns correct result.""" 135 | assert valid_sudoku_4x4.is_valid() == True 136 | 137 | 138 | def test_sudoku_validator_1x1(valid_sudoku_1x1): 139 | """Test sudoku_validator returns correct result.""" 140 | assert valid_sudoku_1x1.is_valid() == True 141 | 142 | 143 | def test_sudoku_validator_false_on_string_element(invalid_sudoku): 144 | """Test sudoku_validator returns false if element in sudoku is not an integer.""" 145 | assert invalid_sudoku.is_valid() == False 146 | 147 | 148 | def test_sudoku_validator_false_on_float_element(invalid_sudoku_float): 149 | """Test sudoku_validator returns false if element in sudoku is not an integer.""" 150 | assert invalid_sudoku_float.is_valid() == False 151 | 152 | def test_sudoku_validator_false_not_symmetric(invalid_sudoku_not_symmetric): 153 | """Test sudoku_validator returns false if element in sudoku is not an integer.""" 154 | assert invalid_sudoku_not_symmetric.is_valid() == False 155 | 156 | def test_sudoku_validator_false_bool_as_value(ivalid_sudoku_true): 157 | """Test sudoku_validator returns false if element in sudoku is not an integer.""" 158 | assert ivalid_sudoku_true.is_valid() == False 159 | 160 | def test_sudoku_validator_valid_2(): 161 | """Test sudoku_validator returns correct result.""" 162 | from sudoku_validator import Sudoku 163 | s = Sudoku(valid_2) 164 | assert s.is_valid() == True 165 | 166 | def test_sudoku_validator(): 167 | """Test sudoku_validator returns correct result.""" 168 | from sudoku_validator import Sudoku 169 | s = Sudoku(invalid_last_column) 170 | assert s.is_valid() == False 171 | -------------------------------------------------------------------------------- /kyu4/Python/src/test_url_strip_params.py: -------------------------------------------------------------------------------- 1 | """Tests for codewars kata https://www.codewars.com/kata/51646de80fd67f442c000013.""" 2 | 3 | import pytest 4 | 5 | url1 = 'www.codewars.com?a=1&b=2' 6 | url2 = 'www.codewars.com?a=1&b=2&a=1&b=3' 7 | url3 = 'www.codewars.com?a=1' 8 | url4 = 'www.codewars.com' 9 | 10 | TEST = [ 11 | (url1, [], url1), 12 | (url2, [], url1), 13 | (url2, ['b'], url3), 14 | (url4, ['b'], url4), 15 | ] 16 | 17 | 18 | @pytest.mark.parametrize('u, p, result', TEST) 19 | def test_strip_url(u, p, result): 20 | """Test that function returns correct url.""" 21 | from url_strip_params import strip_url_params 22 | assert strip_url_params(u, p) == result 23 | -------------------------------------------------------------------------------- /kyu4/Python/src/url_strip_params.py: -------------------------------------------------------------------------------- 1 | """Module to solve the code-kata https://www.codewars.com/kata/51646de80fd67f442c000013.""" 2 | 3 | from urllib.parse import urlparse, parse_qs, urlencode 4 | 5 | def strip_url_params(url, strip=None): 6 | if not strip: strip = [] 7 | 8 | parse = urlparse(url) 9 | query = parse_qs(parse.query) 10 | 11 | query = {k: v[0] for k, v in query.items() if k not in strip} 12 | query = urlencode(query) 13 | new = parse._replace(query=query) 14 | 15 | return new.geturl() 16 | -------------------------------------------------------------------------------- /kyu4/dotnet/Csharp/PokerHand.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace PokerRankingsSolution 6 | { 7 | 8 | class Constants 9 | { 10 | private readonly Dictionary> Ranks = new Dictionary>() 11 | { 12 | { "A", new Tuple("Ace", 14) }, 13 | { "K", new Tuple("King", 13) }, 14 | { "Q", new Tuple("Queen", 12) }, 15 | { "J", new Tuple("Jack", 11) }, 16 | { "T", new Tuple("Ten", 10) }, 17 | { "9", new Tuple("Nine", 9) }, 18 | { "8", new Tuple("Eight", 8) }, 19 | { "7", new Tuple("Seven", 7) }, 20 | { "6", new Tuple("Six", 6) }, 21 | { "5", new Tuple("Five", 5) }, 22 | { "4", new Tuple("Four", 4) }, 23 | { "3", new Tuple("Three", 3) }, 24 | { "2", new Tuple("Two", 2) }, 25 | }; 26 | private readonly Dictionary> Suits = new Dictionary>() 27 | { 28 | { "S": ('Spades', 1) }, 29 | { "H": ('Hearts', 1) }, 30 | { "D": ('Diamonds', 1) }, 31 | { "C": ('Clubs') } 32 | }; 33 | private readonly Dictionary MadeHands = new Dictionary() 34 | { 35 | { "Straight Flush", 9 }, 36 | { "4 of a Kind", 8 }, 37 | { "Full House", 7 }, 38 | { "Flush", 6 }, 39 | { "Straight", 5 }, 40 | { "Set", 4 }, 41 | { "Two Pair", 3 }, 42 | { "One Pair", 1 }, 43 | { "High Car", 0 } 44 | }; 45 | } 46 | class PokerHand 47 | { 48 | public string Hand { get; set; } 49 | private TupleList HighCards { get; set; } 50 | public string[] Vals { get; set; } 51 | public string[] Suits {get; set; } 52 | 53 | 54 | public PokerHand(string hand) 55 | { 56 | Hand = Hand.Trim(); 57 | HighCards = new TupleList(); 58 | foreach (var Card in Hand) 59 | { 60 | if (Ranks.ContainsKey(Card)) 61 | { 62 | Vals.Add(Ranks[Card]); 63 | } 64 | } 65 | foreach (var Card in Hand) 66 | { 67 | if (Suits.ContainsKey(Card)) 68 | { 69 | Suits.Add(Suits[Card]) 70 | } 71 | } 72 | 73 | } 74 | 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /kyu4/dotnet/Csharp/PokerRankingsTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using NUnit.Framework; 7 | 8 | namespace PokerRankingsSolution 9 | { 10 | [TestFixture] 11 | public class KataTests 12 | { 13 | [Test] 14 | public void TestHandIsAStraight() 15 | { 16 | var HeroesHand = new PokerHand("2H 3H 4H 5H 6S"); 17 | Assert.That(delegate { HeroesHand.IsStraight(); }, Is.EqualTo(true)); 18 | } 19 | 20 | [Test] 21 | public void TestHandIsAFlush() 22 | { 23 | var HeroesHand = new PokerHand("AS 3S 4S 8S 2S"); 24 | Assert.That(delegate { HeroesHand.IsFlush(); }, Is.EqualTo(true)); 25 | } 26 | 27 | [Test] 28 | public void TestHandHasCorrectHighCard() 29 | { 30 | var HeroesHand = new PokerHand("8H 9H QS JS KH"); 31 | HeroesHand.setHighCards(); 32 | Assert.That(delegate { HeroesHand.HighCards.pop()[0]; }, Is.EqualTo("K")); 33 | Assert.That(delegate { HeroesHand.HighCards.pop()[0]; }, Is.EqualTo("Q")); 34 | } 35 | 36 | [Test] 37 | public void TestCompareHeroToVillain() 38 | { 39 | var HeroesHand = new PokerHand("2H 3H 4H 5H 6H"); 40 | var VillainsHand = new PokerHand("KS AS TS QS JS"); 41 | HeroesHand.getCardValues(); 42 | VillainsHand.getCardValues(); 43 | HeroesHand.getMadeHandValue(); 44 | VillainsHand.getMadeHandValue(); 45 | Assert.That(delegate { HeroesHand.compareWith(VillainsHand); }, Is.EqualTo(true)); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /kyu4/dotnet/Csharp/TupleList.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace PokerRankingsSolution 6 | { 7 | class TupleList : List> 8 | { 9 | public void Add( T1 item, T2 item2) 10 | { 11 | Add(new Tuple(item, item2)); 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /kyu4/dotnet/Csharp/src/PokerHand.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace PokerRankingsSolution 6 | { 7 | public static class Helper 8 | { 9 | public static IEnumerable> Enumerate(this IEnumerable collection, int startIndex=0) 10 | { 11 | foreach (var item in collection) { yield return new KeyValuePair(startIndex++, item); } 12 | } 13 | } 14 | 15 | class Constants 16 | { 17 | public static readonly Dictionary> Ranks = new Dictionary>() 18 | { 19 | { 'A', new Tuple("Ace", 14) }, 20 | { 'K', new Tuple("King", 13) }, 21 | { 'Q', new Tuple("Queen", 12) }, 22 | { 'J', new Tuple("Jack", 11) }, 23 | { 'T', new Tuple("Ten", 10) }, 24 | { '9', new Tuple("Nine", 9) }, 25 | { '8', new Tuple("Eight", 8) }, 26 | { '7', new Tuple("Seven", 7) }, 27 | { '6', new Tuple("Six", 6) }, 28 | { '5', new Tuple("Five", 5) }, 29 | { '4', new Tuple("Four", 4) }, 30 | { '3', new Tuple("Three", 3) }, 31 | { '2', new Tuple("Two", 2) }, 32 | }; 33 | public static readonly Dictionary> Suits = new Dictionary>() 34 | { 35 | { 'S', new Tuple("Spades", 1) }, 36 | { 'H', new Tuple("Hearts", 1) }, 37 | { 'D', new Tuple("Diamonds", 1) }, 38 | { 'C', new Tuple("Clubs", 1) } 39 | }; 40 | public static readonly Dictionary MadeHands = new Dictionary() 41 | { 42 | { "Straight Flush", 9 }, 43 | { "4 of a Kind", 8 }, 44 | { "Full House", 7 }, 45 | { "Flush", 6 }, 46 | { "Straight", 5 }, 47 | { "Set", 4 }, 48 | { "Two Pair", 3 }, 49 | { "One Pair", 1 }, 50 | { "High Car", 0 } 51 | }; 52 | } 53 | class PokerHand 54 | { 55 | private TupleList hand { get; } = new TupleList(); 56 | private List vals { get; } = new List(); 57 | private List suits {get; } = new List(); 58 | private Dictionary val_cnt { get; } 59 | private int handvalue { get; } 60 | private List twoPair { get; } 61 | private bool isFlush { get; } 62 | private bool isStraight { get; } = true; 63 | private int sumOfAllCardValues { get; } 64 | 65 | public Result CompareWith(PokerHand hand) 66 | { 67 | // special case to handle any comparison involving 5 high straights 68 | if (this.isStraight && other.isStraight && (this.sumOfAllCardValues == 28 || other.sumOfAllCardValues == 28)) 69 | { 70 | if (this.sumOfAllCardValues == 28) { return Result.Loss; } else { Result.Win; } 71 | } 72 | if (this.handvalue > hand.handvalue) { return Result.Win; } 73 | else if (this.handvalue < hand.handvalue) { return Result.Loss; } 74 | else 75 | { 76 | foreach (var idx_card in Helper.Enumerate(this.hand)) 77 | { 78 | int idx = idx_card.Key; 79 | if (idx_card.Value.Item2 > hand.hand[idx].Item2) { return Result.Win; } 80 | else if (idx_card.Value.Item2 < hand.hand[idx].Item2) { return Result.Loss; } 81 | } 82 | return Result.Tie; 83 | } 84 | } 85 | 86 | public PokerHand(string hand) 87 | { 88 | hand = hand.Trim(); 89 | this.handvalue = 0; 90 | foreach (var card in hand) 91 | { 92 | if (Constants.Ranks.ContainsKey(card)) 93 | { 94 | this.vals.Add(card); 95 | this.hand.Add(card.ToString(), Constants.Ranks[card].Item2); 96 | } 97 | } 98 | foreach (var card in hand) 99 | { 100 | if (Constants.Suits.ContainsKey(card)) 101 | { 102 | this.suits.Add(card); 103 | } 104 | } 105 | // initializing isFlush property 106 | this.isFlush = (this.suits.Distinct().Count() == 1) ? true : false; 107 | // initializing isStraight property; default value is true 108 | this.hand.Sort((x, y) => y.Item2.CompareTo(x.Item2)); 109 | int prev_card = this.hand[0].Item2 + 1; 110 | foreach (Tuplecard in this.hand) 111 | { 112 | if (prev_card - 1 != card.Item2) 113 | { 114 | this.isStraight = false; 115 | } 116 | prev_card = card.Item2; 117 | } 118 | // handle special case for 5-high straight 119 | List numericHandValue = new List(); 120 | foreach (char card in this.vals) 121 | { 122 | numericHandValue.Add(Constants.Ranks[card].Item2); 123 | } 124 | this.sumOfAllCardValues = numericHandValue.Sum(); 125 | // count how often a card appears to get all pairs, sets and quads 126 | var groups = vals.GroupBy(c => c ) 127 | .Select(c => new { Vals = c.Key, Count = c.Count() }); 128 | val_cnt = groups.ToDictionary( g => g.Vals, g => g.Count); 129 | // checking if hand contains 2pair 130 | this.twoPair = new List(); 131 | var ordered_val_cnt = val_cnt.OrderByDescending(x => x.Value); 132 | foreach (KeyValuePaircard in ordered_val_cnt) 133 | { 134 | if (card.Value == 2) 135 | { 136 | if (this.twoPair.Count == 1) 137 | { 138 | this.twoPair.Add(card.Key); 139 | this.handvalue += 3; 140 | break; 141 | } 142 | this.twoPair.Add(card.Key); 143 | } 144 | } 145 | // setting final handvalue 146 | if (this.handvalue != 3) 147 | { 148 | if (this.isStraight) 149 | { 150 | this.handvalue = (this.isFlush) ? 9 : 5; 151 | } 152 | if (this.handvalue == 0 && this.isFlush) 153 | { 154 | this.handvalue = 6; 155 | } 156 | foreach (KeyValuePaircard in ordered_val_cnt) 157 | { 158 | switch(card.Value) 159 | { 160 | case 4: 161 | this.handvalue = 8; 162 | break; 163 | case 3: 164 | this.handvalue = 4; 165 | break; 166 | case 2: 167 | this.handvalue = (this.handvalue == 4) ? 7 : 1; 168 | break; 169 | default: 170 | break; 171 | } 172 | } 173 | } 174 | 175 | } 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /kyu4/dotnet/Csharp/src/SortablePokerHand.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace PokerRankingsSolution 6 | { 7 | public static class Helper 8 | { 9 | public static IEnumerable> Enumerate(this IEnumerable collection, int startIndex=0) 10 | { 11 | foreach (var item in collection) { yield return new KeyValuePair(startIndex++, item); } 12 | } 13 | } 14 | 15 | class Constants 16 | { 17 | public static readonly Dictionary> Ranks = new Dictionary>() 18 | { 19 | { 'A', new Tuple("Ace", 14) }, 20 | { 'K', new Tuple("King", 13) }, 21 | { 'Q', new Tuple("Queen", 12) }, 22 | { 'J', new Tuple("Jack", 11) }, 23 | { 'T', new Tuple("Ten", 10) }, 24 | { '9', new Tuple("Nine", 9) }, 25 | { '8', new Tuple("Eight", 8) }, 26 | { '7', new Tuple("Seven", 7) }, 27 | { '6', new Tuple("Six", 6) }, 28 | { '5', new Tuple("Five", 5) }, 29 | { '4', new Tuple("Four", 4) }, 30 | { '3', new Tuple("Three", 3) }, 31 | { '2', new Tuple("Two", 2) }, 32 | }; 33 | public static readonly Dictionary> Suits = new Dictionary>() 34 | { 35 | { 'S', new Tuple("Spades", 1) }, 36 | { 'H', new Tuple("Hearts", 1) }, 37 | { 'D', new Tuple("Diamonds", 1) }, 38 | { 'C', new Tuple("Clubs", 1) } 39 | }; 40 | public static readonly Dictionary MadeHands = new Dictionary() 41 | { 42 | { "Straight Flush", 9 }, 43 | { "4 of a Kind", 8 }, 44 | { "Full House", 7 }, 45 | { "Flush", 6 }, 46 | { "Straight", 5 }, 47 | { "Set", 4 }, 48 | { "Two Pair", 3 }, 49 | { "One Pair", 1 }, 50 | { "High Car", 0 } 51 | }; 52 | } 53 | public class PokerHand : IComparable 54 | { 55 | private string hand { get; } 56 | private TupleList cards { get; } = new TupleList(); 57 | private List vals { get; } = new List(); 58 | private List suits {get; } = new List(); 59 | private Dictionary val_cnt { get; } 60 | private int handvalue { get; } 61 | private List twoPair { get; } = new List(); 62 | private bool isFlush { get; } 63 | private bool isStraight { get; } = true; 64 | private int sumOfAllCardValues { get; } 65 | 66 | public override string ToString() 67 | { 68 | return this.hand; 69 | } 70 | 71 | public PokerHand(string hand) 72 | { 73 | this.hand = hand; 74 | this.handvalue = 0; 75 | foreach (var card in this.hand.Trim()) 76 | { 77 | if (Constants.Ranks.ContainsKey(card)) 78 | { 79 | this.vals.Add(card); 80 | this.cards.Add(card, Constants.Ranks[card].Item2); 81 | } 82 | } 83 | foreach (var card in this.hand.Trim()) 84 | { 85 | if (Constants.Suits.ContainsKey(card)) 86 | { 87 | this.suits.Add(card); 88 | } 89 | } 90 | // initializing isFlush property 91 | this.isFlush = (this.suits.Distinct().Count() == 1) ? true : false; 92 | // initializing isStraight property; default value is true 93 | this.cards.Sort((x, y) => y.Item2.CompareTo(x.Item2)); 94 | int prev_card = this.cards[0].Item2 + 1; 95 | foreach (Tuplecard in this.cards) 96 | { 97 | if (prev_card - 1 != card.Item2) 98 | { 99 | this.isStraight = false; 100 | } 101 | prev_card = card.Item2; 102 | } 103 | // handle special case for 5-high straight 104 | List numericHandValue = new List(); 105 | foreach (char card in this.vals) 106 | { 107 | numericHandValue.Add(Constants.Ranks[card].Item2); 108 | } 109 | this.sumOfAllCardValues = numericHandValue.Sum(); 110 | if (this.sumOfAllCardValues == 28) { this.isStraight = true; }; 111 | // count how often a card appears to get all pairs, sets and quads 112 | var groups = vals.GroupBy(c => c ) 113 | .Select(c => new { Vals = c.Key, Count = c.Count() }); 114 | val_cnt = groups.ToDictionary( g => g.Vals, g => g.Count); 115 | // checking if hand contains 2pair 116 | var ordered_val_cnt = val_cnt.OrderByDescending(x => x.Value); 117 | foreach (KeyValuePaircard in ordered_val_cnt) 118 | { 119 | if (card.Value == 2) 120 | { 121 | if (this.twoPair.Count == 1) 122 | { 123 | this.twoPair.Add(card.Key); 124 | this.handvalue += 3; 125 | break; 126 | } 127 | this.twoPair.Add(card.Key); 128 | } 129 | } 130 | // setting final handvalue 131 | if (this.handvalue != 3) 132 | { 133 | if (this.isStraight) 134 | { 135 | this.handvalue = (this.isFlush) ? 9 : 5; 136 | } 137 | if (this.handvalue == 0 && this.isFlush) 138 | { 139 | this.handvalue = 6; 140 | } 141 | foreach (KeyValuePaircard in ordered_val_cnt) 142 | { 143 | switch(card.Value) 144 | { 145 | case 4: 146 | this.handvalue = 8; 147 | break; 148 | case 3: 149 | this.handvalue = 4; 150 | break; 151 | case 2: 152 | this.handvalue = (this.handvalue == 4) ? 7 : 1; 153 | break; 154 | default: 155 | break; 156 | } 157 | } 158 | } 159 | } 160 | 161 | int IComparable.CompareTo(PokerHand other) 162 | { 163 | // for same absolute handvalue, all cards need to be compared, starting with highest card in descending order 164 | if (this.handvalue == other.handvalue) 165 | { 166 | // special case to handle any comparison involving 5 high straights 167 | if (this.isStraight && other.isStraight && (this.sumOfAllCardValues == 28 || other.sumOfAllCardValues == 28)) 168 | { 169 | if (this.sumOfAllCardValues == 28) { return 1; } else { return -1; } 170 | } 171 | // compare quads, sets and one pair hands 172 | var ordered_val_cnt = this.val_cnt.OrderByDescending(x => x.Value); 173 | var other_ordered_val_cnt = other.val_cnt.OrderByDescending(x => x.Value); 174 | if (Constants.Ranks[ordered_val_cnt.ElementAt(0).Key].Item2 > Constants.Ranks[other_ordered_val_cnt.ElementAt(0).Key].Item2 && ordered_val_cnt.ElementAt(0).Value > 1 && this.twoPair.Count != 2) 175 | { 176 | return -1; 177 | } 178 | else 179 | if (Constants.Ranks[ordered_val_cnt.ElementAt(0).Key].Item2 < Constants.Ranks[other_ordered_val_cnt.ElementAt(0).Key].Item2 && ordered_val_cnt.ElementAt(0).Value > 1 && this.twoPair.Count != 2) 180 | { 181 | return 1; 182 | } 183 | // compare 2 pair hands 184 | if (this.twoPair.Count == 2) 185 | { 186 | // higher twopair wins 187 | if (Constants.Ranks[ordered_val_cnt.ElementAt(0).Key].Item2 > Constants.Ranks[other_ordered_val_cnt.ElementAt(0).Key].Item2) 188 | { 189 | return -1; 190 | } 191 | else if (Constants.Ranks[ordered_val_cnt.ElementAt(0).Key].Item2 < Constants.Ranks[other_ordered_val_cnt.ElementAt(0).Key].Item2) 192 | { 193 | return 1; 194 | } 195 | // higher pair of 2 pair is the same, comparing 2nd pair 196 | else if (Constants.Ranks[ordered_val_cnt.ElementAt(1).Key].Item2 > Constants.Ranks[other_ordered_val_cnt.ElementAt(1).Key].Item2) 197 | { 198 | return -1; 199 | } 200 | else if (Constants.Ranks[ordered_val_cnt.ElementAt(1).Key].Item2 < Constants.Ranks[other_ordered_val_cnt.ElementAt(1).Key].Item2) 201 | { 202 | return 1; 203 | } 204 | // still tied so we need to compare high cards 205 | } 206 | foreach (var idx_card in Helper.Enumerate(this.cards)) 207 | { 208 | int idx = idx_card.Key; 209 | if (idx_card.Value.Item2 > other.cards[idx].Item2) 210 | { 211 | return -1; 212 | } 213 | else if (idx_card.Value.Item2 < other.cards[idx].Item2) { return 1; } 214 | } 215 | return 0; 216 | } 217 | else if (this.handvalue > other.handvalue) 218 | { 219 | return -1; 220 | } 221 | else 222 | { 223 | return 1; 224 | } 225 | } 226 | } 227 | } 228 | -------------------------------------------------------------------------------- /kyu4/sql/README.MD: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | this repository includes all of my completed code-katas from the 4 | [CodeWars SQL challenges](http://wwww.codewars.com) with a difficulty of 5 | kyu4. [History of the term kyu](https://en.wikipedia.org/wiki/Ky%C5%AB) 6 | 7 | # SQL coding challenges: 8 | 9 | ## kyu4 10 | 11 | * Messy data 12 | * Challenge: Two actors who cast together the most 13 | 14 | ## Details for each kata completed 15 | 16 | ### Messy data 17 | * Description of Kata: For this task you are interested in the prospected 18 | customers that are already in your customer base and the prospected credit limit 19 | is higher than your internal estimate. When more than one agency prospected the 20 | same customer, chose the highest estimate. 21 | * Difficulty: kyu4 22 | * Module: messy_data.sql 23 | * [Link](https://www.codewars.com/kata/dealing-with-messy-data/) 24 | 25 | ### Challenge: Two actors who cast together the most 26 | * Description of Kata: Given the the schema presented below find two actors who 27 | cast together the most and list titles of only those movies they were casting 28 | together. Order the result set alphabetically by the movie title. 29 | * Difficulty: kyu4 30 | * Module: two_actors_cast_the_most_together.sql 31 | * [Link](http://www.codewars.com/kata/challenge-two-actors-who-cast-together-the-most) 32 | -------------------------------------------------------------------------------- /kyu4/sql/src/messy_data.sql: -------------------------------------------------------------------------------- 1 | -- Solution to codewars kata https://www.codewars.com/kata/dealing-with-messy-data/ 2 | 3 | CREATE EXTENSION pg_trgm; 4 | 5 | CREATE INDEX prospects_idx ON prospects USING GIN(full_name gin_trgm_ops); 6 | 7 | SELECT 8 | c.first_name, 9 | c.last_name, 10 | c.credit_limit AS "old_limit", 11 | max(p.credit_limit) AS "new_limit" 12 | FROM customers c, prospects p 13 | WHERE lower(p.full_name) LIKE '%' || lower(c.first_name) || '%' 14 | AND lower(p.full_name) LIKE '%' || lower(c.last_name) || '%' 15 | AND p.credit_limit > c.credit_limit 16 | GROUP BY c.first_name, c.last_name, c.credit_limit 17 | ORDER BY first_name; 18 | -------------------------------------------------------------------------------- /kyu4/sql/src/two_actors_cast_the_most_together.sql: -------------------------------------------------------------------------------- 1 | -- Solution to codewars kata http://www.codewars.com/kata/challenge-two-actors-who-cast-together-the-most 2 | 3 | -- get fist and second actor co-starring the most together 4 | -- get top entry and map to actor names 5 | -- join in film table to get all titles 6 | WITH top_team (actor1, actor2, starts) 7 | AS 8 | ( 9 | SELECT tp.actor_id, fa.actor_id AS costar, count(fa.actor_id) AS starts 10 | FROM film_actor fa, film_actor tp 11 | WHERE fa.film_id = tp.film_id 12 | AND fa.actor_id <> tp.actor_id 13 | GROUP BY tp.actor_id, fa.actor_id 14 | order by starts DESC, tp.actor_id 15 | LIMIT 1 16 | ) 17 | 18 | SELECT a1.first_name||' '||a1.last_name AS "first_actor", 19 | a2.first_name||' '||a2.last_name AS "second_actor", 20 | f.title 21 | FROM actor a1, actor a2, top_team tt, film f 22 | WHERE tt.actor1 = a1.actor_id 23 | AND tt.actor2 = a2.actor_id 24 | AND f.film_id IN ( 25 | SELECT fa1.film_id FROM film_actor fa1, film_actor fa2 26 | WHERE fa1.film_id = fa2.film_id 27 | AND fa1.actor_id = a1.actor_id 28 | AND fa2.actor_id = a2.actor_id 29 | ); 30 | -------------------------------------------------------------------------------- /kyu4/src/init.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wenima/codewars/fc508af7ec2ad1b55eeba4e52b94b41f61cd75ae/kyu4/src/init.py -------------------------------------------------------------------------------- /kyu4/src/palindrome_one_liner.py: -------------------------------------------------------------------------------- 1 | """Solution for kata http://www.codewars.com/kata/one-line-task-palindrome-string/.""" 2 | 3 | palindrome=lambda n,s:(s*n)[:n/2]+(s*n)[~-n/2::-1] 4 | -------------------------------------------------------------------------------- /kyu4/src/poker_rankings.py: -------------------------------------------------------------------------------- 1 | """Solution for https://www.codewars.com/kata/ranking-poker-hands/train/python.""" 2 | 3 | from operator import itemgetter 4 | from collections import defaultdict 5 | 6 | RESULT = ["Loss", "Tie", "Win"] 7 | 8 | SUITS = { 9 | 'S': ('Spades', 1), 10 | 'H': ('Hearts', 1), 11 | 'D': ('Diamonds', 1), 12 | 'C': ('Clubs', 1) 13 | } 14 | 15 | RANKS = { 16 | 'A': 14, 17 | '2': 2, 18 | '3': 3, 19 | '4': 4, 20 | '5': 5, 21 | '6': 6, 22 | '7': 7, 23 | '8': 8, 24 | '9': 9, 25 | 'T': 10, 26 | 'J': 11, 27 | 'Q': 12, 28 | 'K': 13 29 | } 30 | 31 | 32 | MADE_HANDS = { 33 | 'straight flush': 9, 34 | '4 of a kind': 8, 35 | 'full house': 7, 36 | 'flush': 6, 37 | 'straight': 5, 38 | 'set': 4, 39 | 'two pair': 3, 40 | 'one pair': 1, 41 | 'high card': 0 42 | } 43 | 44 | class PokerHand(object): 45 | """Create an object representing a Poker Hand based on an input of a 46 | string which represents the best 5 card combination from the player's hand 47 | and board cards. 48 | 49 | Attributes: 50 | vals: a list of card's values 51 | 52 | suits: a list of card's suits 53 | 54 | hand: a sorted list of tuples representing face value and card value ordered 55 | by highest card in descending order 56 | 57 | val_cnt: a dict containing each card and the number of times it appears in the hand 58 | 59 | hand_value: the hand value according to MADE_HANDS 60 | 61 | 62 | Methods: 63 | compare_with(self, villain): takes in Hero's Hand (self) and Villain's 64 | Hand (villain) and compares both hands according to rules of Texas Hold'em. 65 | Returns one of 3 strings (Win, Loss, Tie) based on wether Hero's hand 66 | is better than villain's hand. 67 | Helper Methods: 68 | _is_straight(self): returns True if hand is a straight 69 | _is_flush(self): returns True if hand is a flush 70 | """ 71 | 72 | def __init__(self, hand): 73 | """Initialize a poker hand based on a 10 character string input representing 74 | 5 cards. 75 | """ 76 | hand = hand.replace(' ', '') 77 | self.vals = [c for c in hand if c in RANKS.keys()] 78 | self.suits = [c for c in hand if c in SUITS.keys()] 79 | self.hand = sorted([RANKS[c] for c in self.vals], reverse=True) 80 | self.val_cnt = defaultdict(int) 81 | 82 | for card in self.vals: 83 | self.val_cnt[card] += 1 84 | self._total_value = self._total_value() 85 | 86 | def _total_value(self): 87 | """Return a tuple containing of an int representing hand value and a tuple 88 | of sorted high card values.""" 89 | if self._is_five_high_straight: 90 | del self.hand[0] 91 | self.hand.append(1) 92 | if self._hand_value in [1, 4]: 93 | sorted_d = sorted(self.val_cnt.items(), reverse=True) 94 | return (self._hand_value, sorted_d[0][0], tuple(self.hand)) 95 | return (self._hand_value, tuple(self.hand)) 96 | 97 | @property 98 | def _is_five_high_straight(self): 99 | if sum(self.hand) == 28: 100 | return True 101 | 102 | @property 103 | def _is_straight(self): 104 | """Return True if hand is a straight.""" 105 | if self._is_five_high_straight: return True 106 | previous_card = self.hand[-1] - 1 107 | for card in sorted(self.hand): 108 | if previous_card + 1 != card: return False 109 | previous_card = card 110 | return True 111 | 112 | @property 113 | def _is_flush(self): 114 | """Return True if hand is a flush.""" 115 | return True if len(set(self.suits)) == 1 else False 116 | 117 | @property 118 | def _hand_value(self): 119 | """Return a value based on hand strength.""" 120 | hand_value = 0 121 | """Check if hand is pair plus""" 122 | if len(set(self.val_cnt.values())) > 1: 123 | sorted_d = sorted(self.val_cnt.values(), reverse = True) 124 | if sorted_d[0] == 2 and sorted_d[1] == 2: return 3 125 | for pair_plus in sorted_d: 126 | if pair_plus == 1: return hand_value 127 | elif pair_plus == 4: 128 | return 8 129 | elif pair_plus == 3: 130 | hand_value = 4 131 | elif pair_plus == 2: 132 | if hand_value == 4: 133 | return 7 134 | else: 135 | return 1 136 | if self._is_straight: 137 | if self._is_flush: 138 | return 9 139 | else: 140 | return 5 141 | if self._is_flush: 142 | return 6 143 | if len(set(self.val_cnt.values())) == 1: return hand_value 144 | 145 | def compare_with(self, other): 146 | """Return one of 3 outcomes from result const.""" 147 | if self._total_value > other._total_value: 148 | return 'Win' 149 | elif self._total_value < other._total_value: 150 | return 'Loss' 151 | else: 152 | return 'Tie' 153 | -------------------------------------------------------------------------------- /kyu4/src/sortable_poker_hands.py: -------------------------------------------------------------------------------- 1 | """Solution for https://www.codewars.com/kata/sortable-poker-hands/""" 2 | 3 | from operator import itemgetter 4 | from collections import defaultdict 5 | import functools 6 | 7 | SUITS = { 8 | 'S': ('Spades', 1), 9 | 'H': ('Hearts', 1), 10 | 'D': ('Diamonds', 1), 11 | 'C': ('Clubs', 1) 12 | } 13 | 14 | RANKS = { 15 | 'A': 14, 16 | '2': 2, 17 | '3': 3, 18 | '4': 4, 19 | '5': 5, 20 | '6': 6, 21 | '7': 7, 22 | '8': 8, 23 | '9': 9, 24 | 'T': 10, 25 | 'J': 11, 26 | 'Q': 12, 27 | 'K': 13 28 | } 29 | 30 | MADE_HANDS = { 31 | 'straight flush': 9, 32 | '4 of a kind': 8, 33 | 'full house': 7, 34 | 'flush': 6, 35 | 'straight': 5, 36 | 'set': 4, 37 | 'two pair': 3, 38 | 'one pair': 1, 39 | 'high card': 0 40 | } 41 | 42 | @functools.total_ordering 43 | class PokerHand(object): 44 | """Create an object representing a Poker Hand based on an input of a 45 | string which represents the best 5 card combination from the player's hand 46 | and board cards. 47 | 48 | Attributes: 49 | high_card_vals: a list of card values sorted highest to lowest 50 | 51 | vals: a list of card's values 52 | 53 | suits: a list of card's suits 54 | 55 | hand: a sorted list of tuples representing face value and card value ordered 56 | by highest card in descending order 57 | 58 | val_cnt: a dict containing each card and the number of times it appears in the hand 59 | 60 | has_two_pair: True if hand is a 2 pair hand, else False 61 | 62 | hand_value: the hand value according to MADE_HANDS 63 | 64 | total_value: a tuple of int representing the value of the hand and a tuple of high cards 65 | 66 | Methods: 67 | compare_with(self, villain): takes in Hero's Hand (self) and Villain's 68 | Hand (villain) and compares both hands according to rules of Texas Hold'em. 69 | Returns one of 3 strings (Win, Loss, Tie) based on wether Hero's hand 70 | is better than villain's hand. 71 | Helper Methods: 72 | _is_straight(self): returns True if hand is a straight 73 | _is_flush(self): returns True if hand is a flush 74 | """ 75 | 76 | def __repr__(self): return repr(self.passed_hand) 77 | 78 | def __lt__(self, other): 79 | return self._total_value < other._total_value 80 | 81 | def __eq__(self, other): 82 | return self.passed_hand == other 83 | 84 | def __init__(self, hand): 85 | """Initialize a poker hand based on a 10 character string input representing 86 | 5 cards. 87 | """ 88 | self.passed_hand = hand 89 | hand = hand.replace(' ', '') 90 | self.high_card_vals = [] 91 | self.vals = [c for c in hand if c in RANKS.keys()] 92 | self.suits = [c for c in hand if c in SUITS.keys()] 93 | self.hand = sorted([RANKS[c] for c in self.vals], reverse=True) 94 | self.val_cnt = defaultdict(int) 95 | 96 | for card in self.vals: 97 | self.val_cnt[card] += 1 98 | self._total_value = self._total_value() 99 | 100 | def _total_value(self): 101 | """Return a tuple containing of an int representing hand value and a tuple 102 | of sorted high card values.""" 103 | if self._is_five_high_straight: 104 | del self.hand[0] 105 | self.hand.append(1) 106 | return (self._hand_value, tuple(self.hand)) 107 | 108 | @property 109 | def _is_five_high_straight(self): 110 | if sum(self.hand) == 28: 111 | return True 112 | 113 | @property 114 | def _is_straight(self): 115 | """Return True if hand is a straight.""" 116 | if self._is_five_high_straight: return True 117 | previous_card = self.hand[-1] - 1 118 | for card in sorted(self.hand): 119 | if previous_card + 1 != card: return False 120 | previous_card = card 121 | return True 122 | 123 | @property 124 | def _is_flush(self): 125 | """Return True if hand is a flush.""" 126 | return True if len(set(self.suits)) == 1 else False 127 | 128 | @property 129 | def _hand_value(self): 130 | """Return a value based on hand strength.""" 131 | hand_value = 0 132 | """Check if hand is pair plus""" 133 | if len(set(self.val_cnt.values())) > 1: 134 | sorted_d = sorted(self.val_cnt.values(), reverse = True) 135 | if sorted_d[0] == 2 and sorted_d[1] == 2: return 3 136 | for pair_plus in sorted_d: 137 | if pair_plus == 1: return hand_value 138 | elif pair_plus == 4: 139 | return 8 140 | elif pair_plus == 3: 141 | hand_value = 4 142 | elif pair_plus == 2: 143 | if hand_value == 4: 144 | return 7 145 | else: 146 | return 1 147 | if self._is_straight: 148 | if self._is_flush: 149 | return 9 150 | else: 151 | return 5 152 | if self._is_flush: 153 | return 6 154 | if len(set(self.val_cnt.values())) == 1: return hand_value 155 | -------------------------------------------------------------------------------- /kyu4/src/sudoku_validator.py: -------------------------------------------------------------------------------- 1 | """Module to solve the code-kata https://www.codewars.com/kata/validate-sudoku-with-size-nxn.""" 2 | 3 | from math import sqrt 4 | from itertools import islice 5 | 6 | class Sudoku(object): 7 | 8 | """Create a Sudoku object. 9 | 10 | Methods: 11 | is_valid(): Check if the Sudoku object is valid 12 | 13 | Rules for validation are: 14 | Data structure dimension: NxN where N > 0 and √N == integer 15 | Rows may only contain integers: 1..N (N included) 16 | Columns may only contain integers: 1..N (N included) 17 | 'Little squares' (3x3) may also only contain integers: 1..N (N included). 18 | """ 19 | def __init__(self, matrix=None): 20 | self.m = None 21 | self.square_sides = 0 22 | self.size = 0 23 | if matrix: 24 | self.size = len(matrix) 25 | self.m = matrix 26 | self.square_sides = int(sqrt(len(matrix))) 27 | self.nums = {x for x in range(1, len(self.m))} 28 | 29 | def _extract_small_square(self, row, col): 30 | return [islice(self.m[i], col, self.square_sides + col) for i in range(row, row + self.square_sides)] 31 | 32 | def _check_square(self, row): 33 | for col in range(0, self.square_sides * 2, self.square_sides): 34 | square = self._extract_small_square(row, col) 35 | return self._validate_square(square) 36 | 37 | def _validate_square(self, square): 38 | sum_square = sum([n for l in square for n in l]) 39 | sum_expected = sum([n for n in range(1, self.size + 1)]) 40 | return sum_square == sum_expected 41 | 42 | def is_valid(self): 43 | """Validate a Sudoku puzzle of size NxN.""" 44 | for row in self.m: 45 | if self.nums - set(row): return False #check all rows for valid numbers 46 | for i in range(len(self.m)): # check all cols for valid numbers 47 | nums = set() 48 | for j in range(len(self.m)): 49 | nums.add(self.m[j][i]) 50 | if self.nums - set(nums): return False 51 | for l in self.m: #because True is mask for 1 52 | for n in l: 53 | if isinstance(n, bool): 54 | return False 55 | for i in range(0, 1 if self.square_sides == 1 else self.square_sides * 2, self.square_sides): 56 | try: 57 | if not self._check_square(i): 58 | return False 59 | except TypeError: 60 | return False 61 | return True 62 | -------------------------------------------------------------------------------- /kyu4/src/test_palindrome_one_liner.py: -------------------------------------------------------------------------------- 1 | """Tests for codewars kata http://www.codewars.com/kata/one-line-task-palindrome-string/.""" 2 | 3 | import pytest 4 | 5 | 6 | TEST = [ 7 | (10, 'abcd'), 8 | ] 9 | 10 | 11 | @pytest.mark.parametrize('n, s', TEST) 12 | def test_make_palindrome(n, s, result): 13 | """Test palindrome is returned with length n.""" 14 | from palindrome_one_liner import palindrome 15 | assert make_palindrome(n, s) == result 16 | -------------------------------------------------------------------------------- /kyu4/src/test_poker_rankings.py: -------------------------------------------------------------------------------- 1 | """Tests for codewars kata https://www.codewars.com/kata/ranking-poker-hands/.""" 2 | 3 | import pytest 4 | 5 | 6 | TEST_INPUT = [ 7 | ("2H 3H 4H 5H 6H", "KS AS TS QS JS", 'Loss'), 8 | ("2H 3H 4H 5H 6H", "AS AD AC AH JD", 'Win'), 9 | ("AS AH 2H AD AC", "JS JD JC JH 3D", 'Win'), 10 | ("AS AH 3H AD AC", "AS AH 2H AD AC", 'Win'), 11 | ("2S AH 2H AS AC", "JS JD JC JH AD", 'Loss'), 12 | ("2S AH 2H AS AC", "2H 3H 5H 6H 7H", 'Win'), 13 | ("AS 3S 4S 8S 2S", "2H 3H 5H 6H 7H", 'Win'), 14 | ("2H 3H 5H 6H 7H", "2S 3H 4H 5S 6C", 'Win'), 15 | ("2S 3H 4H 5S 6C", "3D 4C 5H 6H 2S", 'Tie'), 16 | ("2S 3H 4H 5S 6C", "AH AC 5H 6H AS", 'Win'), 17 | ("2S 2H 4H 5S 4C", "AH AC 5H 6H AS", 'Loss'), 18 | ("2S 2H 4H 5S 4C", "AH AC 5H 6H 7S", 'Win'), 19 | ("6S AD 7H 4S AS", "AH AC 5H 6H 7S", 'Loss'), 20 | ("2S AH 4H 5S KC", "AH AC 5H 6H 7S", 'Loss'), 21 | ("2S 3H 6H 7S 9C", "7H 3C TH 6H 9S", 'Loss'), 22 | ("4S 5H 6H TS AC", "3S 5H 6H TS AC", 'Win'), 23 | ("2S AH 4H 5S 6C", "AD 4C 5H 6H 2C", 'Tie'), 24 | ("AH AC 5H 5C QS", "AH AC 5H 5C KS", 'Loss'), 25 | ("AH AC 5H 5C QS", "KH KC 5H 5C QS", 'Win'), 26 | ("7C 7S KH 2H 7H", "3C 3S AH 2H 3H", 'Win'), 27 | ("3C 3S AH 2H 3H", "7C 7S KH 2H 7H", 'Loss'), 28 | ("6H 5H 4H 3H 2H", "5H 4H 3H 2H AH", "Win"), 29 | ("5H 4H 3H 2H AH", "5H 4H 3H 2H AH", "Tie"), 30 | ("5H 4H 3H 2H AH", "6H 5H 4H 3H 2H", "Loss"), 31 | ("AS 3S 4S 8S 2S", "2H 3H 5H 6H 7H", "Win"), 32 | ("2S 3H 6H 7S 9C", "7H 3C TH 6H 9S", "Loss") 33 | ] 34 | 35 | TEST_STRAIGHT = [ 36 | ("2H 3H 4H 5H 6H", True), 37 | ("AS AH 2H AD AC", False), 38 | ("2H 3H 5H 6H 7H", False), 39 | ("KS AS TS QS JS", True), 40 | ("8H 9H QS JS TH", True), 41 | ] 42 | 43 | TEST_FLUSH = [ 44 | ("2H 3H 4H 5H 6H", True), 45 | ("AS AH 2H AD AC", False), 46 | ("2H 3H 5H 6H 7H", True), 47 | ("KS AS TS QS JS", True), 48 | ("8H 9H QS JS TH", False), 49 | ("AS 3S 4S 8S 2S", True), 50 | ] 51 | 52 | TEST_VALUES = [ 53 | ("2H 2S 4H 5H 6H", 1), 54 | ("AS AH 2H 2D KC", 3), 55 | ("AS AH AC 2D KC", 4), 56 | ("AH AC 6S 6H AS", 7), 57 | ("8H 9H QS JS KH", 0), 58 | ("2H 3H 4H 5H 6H", 9), 59 | ] 60 | 61 | @pytest.mark.parametrize('hand, result', TEST_STRAIGHT) 62 | def test_hand_is_straight(hand, result): 63 | """Test that hand has a made hand value of straight.""" 64 | from poker_rankings import PokerHand 65 | heros_hand = PokerHand(hand) 66 | assert heros_hand._is_straight == result 67 | 68 | @pytest.mark.parametrize('hand, result', TEST_FLUSH) 69 | def test_hand_is_flush(hand, result): 70 | """Test that hand has a made hand value of flush.""" 71 | from poker_rankings import PokerHand 72 | heros_hand = PokerHand(hand) 73 | assert heros_hand._is_flush == result 74 | 75 | def test_hand_is_straightflush(): 76 | """Test that hand has a made hand value of straight flush.""" 77 | from poker_rankings import PokerHand 78 | heroes_hand = PokerHand("5H 4H 3H 2H AH") 79 | assert heroes_hand._is_flush == True 80 | assert heroes_hand._is_straight == True 81 | assert heroes_hand._hand_value == 9 82 | 83 | def test_tie_when_both_hands_are_straightflush(): 84 | """Test that if both hands are 5 high straight flushes, result is Tie.""" 85 | from poker_rankings import PokerHand 86 | heroes_hand = PokerHand("5H 4H 3H 2H AH") 87 | villains_hand = PokerHand("5H 4H 3H 2H AH") 88 | heroes_hand.compare_with(villains_hand) == 'Tie' 89 | 90 | @pytest.mark.parametrize('hand, result', TEST_VALUES) 91 | def test_hand_values(hand, result): 92 | """Test that hand has a made hand value of flush.""" 93 | from poker_rankings import PokerHand 94 | from collections import defaultdict 95 | heroes_hand = PokerHand(hand) 96 | assert heroes_hand._hand_value == result 97 | 98 | def test_hand_has_correct_high_card(): 99 | from poker_rankings import PokerHand 100 | heroes_hand = PokerHand("8H 9H QS JS KH") 101 | from collections import defaultdict 102 | assert heroes_hand.hand.pop(0) == 13 103 | assert heroes_hand.hand.pop(0) == 12 104 | 105 | @pytest.mark.parametrize('hand, other, result', TEST_INPUT) 106 | def test_compare_hero_to_villain(hand, other, result): 107 | from poker_rankings import PokerHand 108 | from collections import defaultdict 109 | hero, villain = PokerHand(hand), PokerHand(other) 110 | print(hero._total_value) 111 | print(villain._total_value) 112 | assert hero.compare_with(villain) == result 113 | -------------------------------------------------------------------------------- /kyu4/src/test_sortable_poker_hands.py: -------------------------------------------------------------------------------- 1 | """Tests for codewars kata https://www.codewars.com/kata/ranking-poker-hands/.""" 2 | 3 | import pytest 4 | from random import shuffle 5 | 6 | SORTED_POKER_HANDS = [ 7 | "KS AS TS QS JS", 8 | "KS AS TS QS JS", 9 | "2H 3H 4H 5H 6H", 10 | "AS AD AC AH JD", 11 | "JS JD JC JH 3D", 12 | "2S AH 2H AS AC", 13 | "KH KC 3S 3H 3D", 14 | "2H 2C 3S 3H 3D", 15 | "3D 2H 3H 2C 2D", 16 | "AS 3S 4S 8S 2S", 17 | "2H 3H 5H 6H 7H", 18 | "2S 3H 4H 5S 6C", 19 | "2D AC 3H 4H 5S", 20 | "AC KH QH AH AS", 21 | "AH AC 5H 6H AS", 22 | "7C 7S KH 2H 7H", 23 | "7C 7S QH 2S 7D", 24 | "7C 7S TH 2S 7D", 25 | "AH AC 5H 5C KS", 26 | "AH AC 5H 5C QS", 27 | "AH AC 4H 5S 4C", 28 | "2S 2H 4H 5S 4C", 29 | "AH AC 5H 6H 7S", 30 | "AH AC 4H 6H 7S", 31 | "2S AH 4H 5S KC", 32 | "2S 3H 6H 7S 9C", 33 | ] 34 | 35 | def test_custom_sort_5high_straight(): 36 | """Test that 5 high straights are compared correctly.""" 37 | from sortable_poker_hands import PokerHand 38 | pokerhands = [] 39 | pokerhands.append(PokerHand("2D AC 3H 4H 5S")) 40 | pokerhands.append(PokerHand("2S 3H 4H 5S 6C")) 41 | pokerhands.sort(reverse=True) 42 | assert pokerhands[0].passed_hand == "2S 3H 4H 5S 6C" 43 | 44 | def test_repr(): 45 | """Test that repr of PokerHand class returns a string.""" 46 | from sortable_poker_hands import PokerHand 47 | hand = PokerHand("2D AC 3H 4H 5S") 48 | assert hand == '2D AC 3H 4H 5S' 49 | 50 | def test_custom_sort(): 51 | """Test the output of sort matches list.""" 52 | from sortable_poker_hands import PokerHand 53 | pokerhands = [] 54 | for hand in SORTED_POKER_HANDS: 55 | pokerhands.append(PokerHand(hand)) 56 | shuffle(pokerhands) 57 | pokerhands.sort(reverse=True) 58 | for i, hand in enumerate(pokerhands): 59 | assert hand == SORTED_POKER_HANDS[i] 60 | -------------------------------------------------------------------------------- /kyu4/src/test_sudoku_validator.py: -------------------------------------------------------------------------------- 1 | """Tests for https://www.codewars.com/kata/validate-sudoku-with-size-nxn.""" 2 | 3 | import pytest 4 | 5 | valid_m = [ 6 | [7,8,4, 1,5,9, 3,2,6], 7 | [5,3,9, 6,7,2, 8,4,1], 8 | [6,1,2, 4,3,8, 7,5,9], 9 | 10 | [9,2,8, 7,1,5, 4,6,3], 11 | [3,5,7, 8,4,6, 1,9,2], 12 | [4,6,1, 9,2,3, 5,8,7], 13 | 14 | [8,7,6, 3,9,4, 2,1,5], 15 | [2,4,3, 5,6,1, 9,7,8], 16 | [1,9,5, 2,8,7, 6,3,4] 17 | ] 18 | 19 | invalid_m_str = [ 20 | ['7',8,4, 1,5,9, 3,2,6], 21 | [5,3,9, 6,7,2, 8,4,1], 22 | [6,1,2, 4,3,8, 7,5,9], 23 | 24 | [9,2,8, 7,1,5, 4,6,3], 25 | [3,5,7, 8,4,6, 1,9,2], 26 | [4,6,1, 9,2,3, 5,8,7], 27 | 28 | [8,7,6, 3,9,4, 2,1,5], 29 | [2,4,3, 5,6,1, 9,7,8], 30 | [1,9,5, 2,8,7, 6,3,4] 31 | ] 32 | 33 | invalid_m_float = [ 34 | [7.9999,8,4, 1,5,9, 3,2,6], 35 | [5,3,9, 6,7,2, 8,4,1], 36 | [6,1,2, 4,3,8, 7,5,9], 37 | 38 | [9,2,8, 7,1,5, 4,6,3], 39 | [3,5,7, 8,4,6, 1,9,2], 40 | [4,6,1, 9,2,3, 5,8,7], 41 | 42 | [8,7,6, 3,9,4, 2,1,5], 43 | [2,4,3, 5,6,1, 9,7,8], 44 | [1,9,5, 2,8,7, 6,3,4] 45 | ] 46 | 47 | invalid_m_not_symmetric = [ 48 | [1,2,3,4,5], 49 | [1,2,3,4], 50 | [1,2,3,4], 51 | [1] 52 | ] 53 | 54 | four_by_four = [ 55 | [1,4, 2,3], 56 | [3,2, 4,1], 57 | [4,1, 3,2], 58 | [2,3, 1,4] 59 | ] 60 | 61 | one_by_one = [ 62 | [1] 63 | ] 64 | 65 | one_value_true = [ 66 | [True] 67 | ] 68 | 69 | valid_2 = [ 70 | [1, 3, 2, 5, 7, 9, 4, 6, 8], 71 | [4, 9, 8, 2, 6, 1, 3, 7, 5], 72 | [7, 5, 6, 3, 8, 4, 2, 1, 9], 73 | [6, 4, 3, 1, 5, 8, 7, 9, 2], 74 | [5, 2, 1, 7, 9, 3, 8, 4, 6], 75 | [9, 8, 7, 4, 2, 6, 5, 3, 1], 76 | [2, 1, 4, 9, 3, 5, 6, 8, 7], 77 | [3, 6, 5, 8, 1, 7, 9, 2, 4], 78 | [8, 7, 9, 6, 4, 2, 1, 5, 3] 79 | ] 80 | 81 | invalid_last_column = [ 82 | [1, 3, 2, 5, 7, 9, 4, 6, 8], 83 | [4, 9, 8, 2, 6, 1, 3, 7, 5], 84 | [7, 5, 6, 3, 8, 4, 2, 1, 9], 85 | 86 | [6, 4, 3, 1, 5, 8, 7, 9, 2], 87 | [5, 2, 1, 7, 9, 3, 8, 4, 6], 88 | [9, 8, 7, 4, 2, 6, 5, 3, 1], 89 | 90 | [2, 1, 4, 9, 3, 5, 6, 8, 7], 91 | [3, 6, 5, 8, 1, 7, 9, 2, 4], 92 | [8, 7, 9, 6, 4, 2, 1, 3, 5] 93 | ] 94 | 95 | 96 | @pytest.fixture 97 | def valid_sudoku(): 98 | from sudoku_validator import Sudoku 99 | new_sudoku = Sudoku(valid_m) 100 | return new_sudoku 101 | 102 | @pytest.fixture 103 | def invalid_sudoku(): 104 | from sudoku_validator import Sudoku 105 | new_sudoku = Sudoku(invalid_m_str) 106 | return new_sudoku 107 | 108 | @pytest.fixture 109 | def invalid_sudoku_float(): 110 | from sudoku_validator import Sudoku 111 | new_sudoku = Sudoku(invalid_m_float) 112 | return new_sudoku 113 | 114 | @pytest.fixture 115 | def invalid_sudoku_not_symmetric(): 116 | from sudoku_validator import Sudoku 117 | new_sudoku = Sudoku(invalid_m_not_symmetric) 118 | return new_sudoku 119 | 120 | @pytest.fixture 121 | def valid_sudoku_4x4(): 122 | from sudoku_validator import Sudoku 123 | new_sudoku = Sudoku(four_by_four) 124 | return new_sudoku 125 | 126 | @pytest.fixture 127 | def valid_sudoku_1x1(): 128 | from sudoku_validator import Sudoku 129 | new_sudoku = Sudoku(one_by_one) 130 | return new_sudoku 131 | 132 | @pytest.fixture 133 | def ivalid_sudoku_true(): 134 | from sudoku_validator import Sudoku 135 | new_sudoku = Sudoku(one_value_true) 136 | return new_sudoku 137 | 138 | 139 | def test_sudoku_validator(valid_sudoku): 140 | """Test sudoku_validator returns correct result.""" 141 | assert valid_sudoku.is_valid() == True 142 | 143 | 144 | def test_sudoku_validator_4x4(valid_sudoku_4x4): 145 | """Test sudoku_validator returns correct result.""" 146 | assert valid_sudoku_4x4.is_valid() == True 147 | 148 | 149 | def test_sudoku_validator_1x1(valid_sudoku_1x1): 150 | """Test sudoku_validator returns correct result.""" 151 | assert valid_sudoku_1x1.is_valid() == True 152 | 153 | 154 | def test_sudoku_validator_false_on_string_element(invalid_sudoku): 155 | """Test sudoku_validator returns false if element in sudoku is not an integer.""" 156 | assert invalid_sudoku.is_valid() == False 157 | 158 | 159 | def test_sudoku_validator_false_on_float_element(invalid_sudoku_float): 160 | """Test sudoku_validator returns false if element in sudoku is not an integer.""" 161 | assert invalid_sudoku_float.is_valid() == False 162 | 163 | def test_sudoku_validator_false_not_symmetric(invalid_sudoku_not_symmetric): 164 | """Test sudoku_validator returns false if element in sudoku is not an integer.""" 165 | assert invalid_sudoku_not_symmetric.is_valid() == False 166 | 167 | def test_sudoku_validator_false_bool_as_value(ivalid_sudoku_true): 168 | """Test sudoku_validator returns false if element in sudoku is not an integer.""" 169 | assert ivalid_sudoku_true.is_valid() == False 170 | 171 | def test_sudoku_validator(): 172 | """Test sudoku_validator returns correct result.""" 173 | from sudoku_validator import Sudoku 174 | valid_sudoku = Sudoku(valid_2) 175 | assert valid_sudoku.is_valid() == True 176 | 177 | def test_sudoku_validator(): 178 | """Test sudoku_validator returns correct result.""" 179 | from sudoku_validator import Sudoku 180 | valid_sudoku = Sudoku(invalid_last_column) 181 | assert valid_sudoku.is_valid() == False 182 | -------------------------------------------------------------------------------- /kyu4/src/test_url_strip_params.py: -------------------------------------------------------------------------------- 1 | """Tests for codewars kata https://www.codewars.com/kata/51646de80fd67f442c000013.""" 2 | 3 | import pytest 4 | 5 | url1 = 'www.codewars.com?a=1&b=2' 6 | url2 = 'www.codewars.com?a=1&b=2&a=1&b=3' 7 | url3 = 'www.codewars.com?a=1' 8 | url4 = 'www.codewars.com' 9 | 10 | TEST = [ 11 | (url1, [], url1), 12 | (url2, [], url1), 13 | (url2, ['b'], url3), 14 | (url4, ['b'], url4), 15 | ] 16 | 17 | 18 | @pytest.mark.parametrize('u, p, result', TEST) 19 | def test_strip_url(u, p, result): 20 | """Test that function returns correct url.""" 21 | from url_strip_params import strip_url_params 22 | assert strip_url_params(u, p) == result 23 | -------------------------------------------------------------------------------- /kyu4/src/url_strip_params.py: -------------------------------------------------------------------------------- 1 | """Module to solve the code-kata https://www.codewars.com/kata/51646de80fd67f442c000013.""" 2 | 3 | from urlparse import urlparse, parse_qs 4 | from urllib import urlencode 5 | 6 | def strip_url_params(url, strip=None): 7 | if not strip: strip = [] 8 | 9 | parse = urlparse(url) 10 | query = parse_qs(parse.query) 11 | 12 | query = {k: v[0] for k, v in query.iteritems() if k not in strip} 13 | query = urllib.urlencode(query) 14 | new = parse._replace(query=query) 15 | 16 | return new.geturl() 17 | -------------------------------------------------------------------------------- /kyu5/dotnet/Csharp/best_travel.cs: -------------------------------------------------------------------------------- 1 | // Solution for https://www.codewars.com/kata/best-travel/train/csharp 2 | 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | 7 | public static class IterTools 8 | { 9 | public static IEnumerable Combinations(this IList argList, int argSetSize) 10 | { 11 | if (argList == null) throw new ArgumentNullException("argList"); 12 | if (argSetSize <= 0) throw new ArgumentException("argSetSize must be great than 0", "argSetSize"); 13 | return combinationsImpl(argList, 0, argSetSize - 1); 14 | } 15 | 16 | private static IEnumerable combinationsImpl(IList argList, int argStart, int argIteration, List argIndices = null) 17 | { 18 | argIndices = argIndices ?? new List(); 19 | for (int i = argStart; i < argList.Count; i++) 20 | { 21 | argIndices.Add(i); 22 | if (argIteration > 0) 23 | { 24 | foreach (var array in combinationsImpl(argList, i + 1, argIteration - 1, argIndices)) 25 | { 26 | yield return array; 27 | } 28 | } 29 | else 30 | { 31 | var array = new T[argIndices.Count]; 32 | for (int j = 0; j < argIndices.Count; j++) 33 | { 34 | array[j] = argList[argIndices[j]]; 35 | } 36 | yield return array; 37 | } 38 | argIndices.RemoveAt(argIndices.Count - 1); 39 | } 40 | } 41 | } 42 | 43 | public static class SumOfK 44 | { 45 | public static int? chooseBestSum(int t, int k, List ls) { 46 | int best = 0; 47 | var combinations = IterTools.Combinations(ls, k); 48 | foreach (var c in combinations) 49 | { 50 | best = (c.Sum() > best && c.Sum() <= t) ? c.Sum() : best; 51 | } 52 | if (best > 0) 53 | { 54 | return best; 55 | } 56 | else 57 | { 58 | return null; 59 | } 60 | } 61 | } 62 | 63 | // Combinations using LINQ 64 | using System.Collections.Generic; 65 | using System.Linq; 66 | 67 | public static class SumOfK 68 | { 69 | public static int? chooseBestSum(int t, int k, List ls) => 70 | ls.Combinations(k) 71 | .Select(c => (int?) c.Sum()) 72 | .Where(sum => sum <= t) 73 | .DefaultIfEmpty() 74 | .Max(); 75 | 76 | // Inspired by http://stackoverflow.com/questions/127704/algorithm-to-return-all-combinations-of-k-elements-from-n 77 | public static IEnumerable> Combinations(this IEnumerable ls, int k) => 78 | k == 0 ? new[] { new int[0] } : 79 | ls.SelectMany((e, i) => 80 | ls.Skip(i + 1) 81 | .Combinations(k - 1) 82 | .Select(c => (new[] {e}).Concat(c))); 83 | } 84 | 85 | //Shortest solution 86 | using System; 87 | using System.Collections.Generic; 88 | using System.Linq; 89 | 90 | public static class SumOfK 91 | { 92 | public static int? chooseBestSum(int t, int k, List ls) 93 | { 94 | var _ls = ls.Where(x => x <= t); 95 | return _ls.Count() == 0 ? null : _ls.Select((x, i) => x + (k > 1 ? chooseBestSum(t-x, k-1, _ls.Skip(i+1).ToList()) : 0)).Max(); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /kyu5/dotnet/README.MD: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | this repository includes all of my completed code-katas from the 4 | [CodeWars Csharp challenges](http://wwww.codewars.com) with a difficulty of 5 | kyu5 or higher. [History of the term kyu](https://en.wikipedia.org/wiki/Ky%C5%AB) 6 | 7 | In addition, some of the challenges will be translated in VB.net 8 | 9 | # Csharp coding challenges: 10 | 11 | ## kyu5 12 | 13 | * Directions Reduction 14 | 15 | ## beta 16 | 17 | * Sortable Poker Hands 18 | 19 | ### Best Travel 20 | * Descripton of Kata: The function chooseBestSum (or choose_best_sum or ... 21 | depending on the language) will take as parameters t (maximum sum of distances, 22 | integer >= 0), k (number of towns to visit, k >= 1) and ls (list of distances, 23 | all distances are positive or null integers and this list has at least one element). 24 | The function returns the "best" sum ie the biggest possible sum of k distances 25 | less than or equal to the given limit t, if that sum exists, or otherwise null or None. 26 | * Difficulty: kyu5 27 | * Module: best_travel.cs 28 | * Tests: 29 | * Link: https://www.codewars.com/kata/best-travel/train/csharp 30 | 31 | ### Sortable Poker Hands 32 | * Descripton of Kata: A famous casino is suddenly faced with a sharp decline of 33 | their revenues. They decide to offer Texas hold'em also online. Can you help them 34 | by writing an algorithm that can rank poker hands? 35 | * Difficulty: beta 36 | * Module: sortable_poker_hands.cs 37 | * Tests: 38 | * Link: https://www.codewars.com/kata/sortable-poker-hands 39 | -------------------------------------------------------------------------------- /kyu5/python/src/base_neg_2.py: -------------------------------------------------------------------------------- 1 | """Module to solve the code-kata https://www.codewars.com/kata/base-2/train/python.""" 2 | 3 | 4 | def to_nega_binary(n): 5 | """Return a string representation of the negabinary value of given n.""" 6 | res = [] 7 | while i != 0: 8 | remainder = abs(i) % 2 9 | res.append(str(remainder)) 10 | if i < 0 and remainder: 11 | i -= 1 12 | i = math.trunc(i / -2) 13 | return ''.join(reversed(res)) if res else '0' 14 | 15 | 16 | def from_nega_binary(bin): 17 | """Return an integer for a given string representation of a binary in base-2.""" 18 | return sum([pow(-2, idx) for idx, bit in enumerate(reversed(s)) if bit == '1']) 19 | -------------------------------------------------------------------------------- /kyu5/python/src/basic_excel.py: -------------------------------------------------------------------------------- 1 | """Module to solve the code-kata https://www.codewars.com/kata/56055244356dc5c45c00001e. 2 | Per the description of the kata, we assume that if a string is given and it starts 3 | with anything but an alphanumeric character, it is an operator. 4 | 5 | Since the use of eval is not recommended for unknown input and apparently it's 6 | really hard to safeguard against malicious code using eval, we implement a 7 | dispatch/commann pattern. 8 | """ 9 | 10 | import re 11 | #commented out using fast_real from fastnumbers due to lib not available on codewars 12 | # from fastnumbers import fast_real 13 | import operator as op 14 | 15 | 16 | def compare(command, a, b): 17 | return(dispatch[command](a, b)) 18 | 19 | dispatch = { 20 | '<': op.lt, 21 | '>': op.gt, 22 | '<=': op.le, 23 | '>=': op.ge, 24 | '<>': op.ne, 25 | } 26 | 27 | 28 | def parse_input(criteria): 29 | """Return operator, comparator if operator exists in input.""" 30 | try: 31 | if criteria[:1].isalnum(): 32 | return '', criteria 33 | else: 34 | #since we can't use fastnumbers, we need to capture anyting after the decimal point 35 | m = re.match(r'([^\d\w]*)?(\d*\.?(\d*))$', criteria) 36 | operator, comparator, decimal = m.groups() 37 | comparator = float(comparator) if decimal else int(comparator) 38 | return operator, comparator 39 | except: 40 | return '', criteria 41 | 42 | 43 | def out_arr(values, criteria, operator, comparator): 44 | if len(operator) > 0: 45 | return [value for value in values if compare(operator, value, comparator)] 46 | else: 47 | return [value for value in values if value == criteria] 48 | 49 | 50 | def count_if(values, criteria): 51 | """Return number of occurences of criteria in list of values.""" 52 | operator, comparator = parse_input(criteria) 53 | return len(out_arr(values, criteria, operator, comparator)) 54 | 55 | 56 | def sum_if(values, criteria): 57 | """Return sum of values based on criteria.""" 58 | operator, comparator = parse_input(criteria) 59 | return sum(out_arr(values, criteria, operator, comparator)) 60 | 61 | 62 | def average_if(values,criteria): 63 | """Return average of values based on criteria.""" 64 | operator, comparator = parse_input(criteria) 65 | out = out_arr(values, criteria, operator, comparator) 66 | return op.truediv(sum(out), len(out)) 67 | -------------------------------------------------------------------------------- /kyu5/python/src/best_travel.py: -------------------------------------------------------------------------------- 1 | """Module to solve the code-kata https://www.codewars.com/kata/best-travel/train/python.""" 2 | 3 | from itertools import combinations 4 | 5 | def choose_best_sum(t, k, ls): 6 | """Return a value closest to, but not great than, t out of a combination of 7 | elements in ls with size k. 8 | """ 9 | best = 0 10 | for c in combinations(ls, k): 11 | if best < sum(c) <= t: 12 | best = sum(c) 13 | return best if best else None 14 | -------------------------------------------------------------------------------- /kyu5/python/src/camelcase.py: -------------------------------------------------------------------------------- 1 | """Module to solve the code-kata https://www.codewars.com/kata/convert-string-to-camel-case/python. 2 | """ 3 | 4 | import re 5 | 6 | 7 | def to_camel_case(s): 8 | """Return True if given s contains a number divisible by 4.""" 9 | pattern = r'(?:\_|\-)[a-zA-Z]' 10 | m = re.findall(pattern, s) 11 | new_s = s 12 | for find in m: 13 | new_s = re.sub(find, find[-1].upper(), new_s) 14 | return new_s 15 | -------------------------------------------------------------------------------- /kyu5/python/src/carpark.py: -------------------------------------------------------------------------------- 1 | """Module to solve the code-kata https://www.codewars.com/kata/car-park-escape/.""" 2 | 3 | def escape(carpark): 4 | """Return a list of directions to take to escape a given carpark.""" 5 | while True: 6 | try: 7 | carpark[0].index(2) 8 | break 9 | except: 10 | carpark = carpark[1:] 11 | if len(carpark) == 1: 12 | if carpark[0].index(2) + 1 == len(carpark[0]): return [] 13 | return ["R" + str(len(carpark[-1]) - 1 - carpark[0].index(2))] 14 | out = [] 15 | start = carpark[0].index(2) 16 | out, entry = find_exit(carpark[0], out, start) 17 | for row in carpark[1:-1]: 18 | out, entry = find_exit(row, out, entry) 19 | if entry + 1 != len(carpark[-1]): 20 | out.append("R" + str(len(carpark[-1]) - 1 - entry)) 21 | return out 22 | 23 | def find_exit(level, out, entry=0): 24 | """Return the escape route so far and the location of the stairs.""" 25 | if level[entry] == 1: 26 | last_stairs = out[-1] 27 | new_stairs = "D" + str(int(last_stairs[-1]) + 1) 28 | out[-1] = new_stairs 29 | stairs = entry 30 | else: 31 | stairs = level.index(1) 32 | if stairs < entry: 33 | out.append("L" + str(entry - level.index(1))) 34 | else: 35 | out.append("R" + str(stairs - entry)) 36 | out.append("D1") 37 | return out, stairs 38 | -------------------------------------------------------------------------------- /kyu5/python/src/diceroller.py: -------------------------------------------------------------------------------- 1 | """Module to solve the code-kata https://www.codewars.com/kata/rpg-dice-roller. 2 | """ 3 | 4 | import re 5 | from random import randint 6 | from collections import OrderedDict 7 | 8 | 9 | def normalize_input(s): 10 | try: 11 | s = s.replace(' ', '') 12 | except AttributeError: 13 | return False 14 | pattern = r'^[0-9]*d[1-9]+(?:[+-]?\d)*$' 15 | m = re.match(pattern, s) 16 | return m.group() if m else False 17 | 18 | 19 | def validate_input(s): 20 | """Return True if input follows the dice notation or False.""" 21 | try: 22 | m = normalize_input(s) 23 | except AttributeError: 24 | return False 25 | return True if m else False 26 | 27 | def get_modifiers(s): 28 | """Return a list of modifiers of the given dice notation.""" 29 | pattern = r'[+-]+[1-9][0-9]*' 30 | return sum([int(match) for match in re.findall(pattern, s)]) 31 | 32 | 33 | def roll(s, verbose=False): 34 | """Roll Dice defined in input string and apply modifieres if present. 35 | 36 | Keyword arguments: 37 | output -- takes either 'summed' or 'verbose' (default: 'summed') 38 | 'summed' will sum up the roll plus modifieres and return an integers 39 | 'verbose' returns an object with a list containing all rolls and the sum of 40 | all modifiers. If no modifiers are given, returns zero 41 | """ 42 | throw = OrderedDict() 43 | d = normalize_input(s) 44 | if validate_input(s): 45 | m = re.search(r'^([0-9])*d([1-9][0-9]*)', d) 46 | if m.group(1) == None: 47 | no_of_dies = 1 48 | else: 49 | no_of_dies = m.group(1) 50 | die = m.group(2) 51 | throw['dice'] = [randint(1, int(die)) for i in range(int(no_of_dies))] 52 | throw['modifier'] = get_modifiers(d) 53 | if verbose: 54 | return dict(throw) 55 | else: 56 | return sum(throw['dice']) + throw['modifier'] 57 | else: 58 | return False 59 | -------------------------------------------------------------------------------- /kyu5/python/src/digits_product.py: -------------------------------------------------------------------------------- 1 | """Solution for https://www.codewars.com/kata/simple-fun-number-81-digits-product/.""" 2 | 3 | def digits_product(n): 4 | """Return the smallest integer composed of products of n.""" 5 | out = [] 6 | if n == 1: return 11 7 | if n == 0: return 10 8 | while True: 9 | if n > 7 and isprime(n):return -1 10 | for i in range(9, 0, -1): 11 | if i == 1 and len(out) == 0: 12 | return -1 13 | if n % i: 14 | continue 15 | out.append(str(i)) 16 | n //= i 17 | break 18 | if n == 1 and len(out) == 1: 19 | out.append(str(n)) 20 | break 21 | elif n == 1:break 22 | return int(''.join(sorted(out))) 23 | 24 | 25 | def isprime(n): 26 | """Return True if n is a prime number for all n > 7.""" 27 | if n % 2 == 0:return False 28 | return all(n % i for i in range(3, int(n**0.5) + 1, 2)) 29 | -------------------------------------------------------------------------------- /kyu5/python/src/domain_name.py: -------------------------------------------------------------------------------- 1 | """Module to solve the code-kata 2 | https://www.codewars.com/kata/extract-the-domain-name-from-a-url-1/train/python. 3 | This is just a proof of concept/prototype. The correct solution covering all 4 | domain names as well as private domains is alot more complicated. It would 5 | involve getting the current viable top-level domains and parse the url looking 6 | up valid domains under the TLD. 7 | 8 | For example http://ag.utah.gov/ won't be found with the approach taken below. 9 | """ 10 | 11 | import re 12 | 13 | 14 | def domain_name(url): 15 | """Return the domain name of the url.""" 16 | pattern = r'^(?:http(?:s?)://)?(?:www\.)?(.*?)\.(?:[a-z.-])' 17 | m = re.match(pattern, url) 18 | return m.group(1) if m else None 19 | -------------------------------------------------------------------------------- /kyu5/python/src/even_digit_primes.py: -------------------------------------------------------------------------------- 1 | """Module for code-kata https://www.codewars.com/kata/primes-with-even-digits.""" 2 | 3 | 4 | def is_prime(n): 5 | """Return True if n is a prime.""" 6 | for x in range(3, int(n**0.5 + 1), 2): 7 | if n % x == 0: 8 | return False 9 | return True 10 | 11 | 12 | def get_range_even_digit_prime(n): 13 | """Return a value to get to the nearest range to find an even digit prime.""" 14 | sn = [n for n in str(n)] 15 | if sn[0] == '1': 16 | i = 1 17 | while True: 18 | if sn[i] != '0': 19 | break 20 | i += 1 21 | if i == len(sn) - 1: 22 | for j in range(1, len(sn) - 1): 23 | sn[j] = '8' 24 | sn[-1] = '9' 25 | del sn[0] 26 | break 27 | if int(sn[1]) % 2 != 0: 28 | sn[1] = str(int(sn[1]) - 1) 29 | for i in range(2, len(sn) - 1): 30 | sn[i] = '8' 31 | sn[-1] = '9' 32 | i = 0 33 | while True: 34 | if sn[i] != '1' and int(sn[i]) % 2 != 0: 35 | sn[i] = str(int(sn[i]) - 1) 36 | for i in range(i + 1, len(sn) - 1): 37 | sn[i] = '8' 38 | sn[-1] = '9' 39 | break 40 | if i == len(sn) - 1: 41 | break 42 | i += 1 43 | print(sn) 44 | return int(''.join(sn)) 45 | 46 | 47 | def step(n): 48 | """Yield all primes lower than n""" 49 | if n % 2 == 0: n -= 1 #need to see an odd number so we can step over known composites 50 | try: 51 | return next(x for x in range(n, 0, - 2) if is_prime(x)) 52 | except StopIteration: 53 | return None 54 | 55 | 56 | def f(n): 57 | """Return the closest prime number under a certain integer n that has the 58 | maximum possible amount of even digits.""" 59 | p = n - 1 60 | p = get_range_even_digit_prime(p) 61 | while True: 62 | p = step(p) 63 | if sum([str(p).count(str(i)) for i in range(0, 10, 2)]) == len(str(p)) - (2 if int(str(p)[0]) == 1 else 1): 64 | break 65 | p -= 2 66 | return p 67 | -------------------------------------------------------------------------------- /kyu5/python/src/fib_digit_occurence.py: -------------------------------------------------------------------------------- 1 | """Module to solve the code-kata 2 | https://www.codewars.com/kata/calculate-fibonacci-return-count-of-digit-occurrences.""" 3 | 4 | 5 | def fib_digits(n): 6 | """Return a list of integer pairs, containing number of occurrence for digit, 7 | sorted in descending order.""" 8 | fib = str(generate_fib(n)) 9 | found = {} 10 | for i in range(10): 11 | found[i] = fib.count(str(i)) 12 | return sorted([(v, k) for k, v in found.items() if v > 0], reverse = True) 13 | 14 | 15 | 16 | def generate_fib(n): 17 | """Return the nth fibonacci number.""" 18 | a, b = 0, 1 19 | for __ in range(n-1): 20 | a, b = b, a + b 21 | return b 22 | -------------------------------------------------------------------------------- /kyu5/python/src/find_unique_str.py: -------------------------------------------------------------------------------- 1 | """Module to solve the code-kata https://www.codewars.com/kata/find-the-unique-string.""" 2 | 3 | from collections import defaultdict 4 | 5 | def find_uniq(a): 6 | """Return the unique element from a list of strings.""" 7 | d = {} 8 | c = defaultdict(int) 9 | for e in a: 10 | t = frozenset(e.strip().lower()) 11 | d[t] = e 12 | c[t] += 1 13 | 14 | return d[next(filter(lambda k: c[k] == 1, c))] 15 | -------------------------------------------------------------------------------- /kyu5/python/src/mod4regex.py: -------------------------------------------------------------------------------- 1 | """Module to solve the code-kata https://www.codewars.com/kata/mod4-regex. 2 | """ 3 | 4 | import re 5 | 6 | 7 | def mod4(s): 8 | """Return True if given s contains a number divisible by 4.""" 9 | pattern = r'.*\[([+-]?0*\d*([02468][048]|[13579][26])]|[+-]?[048]\])' 10 | m = re.match(pattern, s) 11 | return True if m else False 12 | -------------------------------------------------------------------------------- /kyu5/python/src/not_secure.py: -------------------------------------------------------------------------------- 1 | """Module to solve the code-kata https://www.codewars.com/kata/not-very-secure/train/python. 2 | The kata seems to be broken as the description state a regular expression needs 3 | to be fixed but no regex is given. 4 | 5 | Thus, this module offers 2 solutions, the 'pythonic' one and one using regex. 6 | """ 7 | 8 | import re 9 | 10 | 11 | #pythonic 12 | 13 | def alphanumeric_py(string): 14 | """Return True if the given string is alphanumeric.""" 15 | return string.isalnum() 16 | 17 | 18 | #regex 19 | 20 | def alphanumeric(s): 21 | """Return True if given s is alphanumeric.""" 22 | if len(s) == 0: 23 | return False 24 | pattern = r'(?=([^A-Za-z0-9]))' #using lookahead for everything not alphanumeric 25 | m = re.search(pattern, s) 26 | return False if m else True 27 | -------------------------------------------------------------------------------- /kyu5/python/src/perfect_power.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wenima/codewars/fc508af7ec2ad1b55eeba4e52b94b41f61cd75ae/kyu5/python/src/perfect_power.py -------------------------------------------------------------------------------- /kyu5/python/src/prime_factorization.py: -------------------------------------------------------------------------------- 1 | """Module to solve https://www.codewars.com/kata/prime-factorization.""" 2 | 3 | from collections import defaultdict 4 | 5 | 6 | class PrimeFactorizer(int): 7 | 8 | def __init__(self, n): 9 | """Initialize a Prime object.""" 10 | 11 | 12 | def prime_factors(self): 13 | """Return a list of prime factors for a given number in ascending order.""" 14 | n = self 15 | factors = [] 16 | p = 2 17 | if n < 2: 18 | return factors 19 | while n >= (p * p): 20 | if n % p: 21 | p += 1 22 | else: 23 | n = n // p 24 | factors.append(p) 25 | factors.append(n) 26 | return factors 27 | 28 | @property 29 | def factor(self): 30 | """Return a dict where key is the prime and value the number of occurences 31 | in the prime factorization of n.""" 32 | prime_factors = PrimeFactorizer.prime_factors(self) 33 | d = defaultdict(int) 34 | for pf in prime_factors: 35 | d[pf] += 1 36 | return dict(d) 37 | -------------------------------------------------------------------------------- /kyu5/python/src/primes.py: -------------------------------------------------------------------------------- 1 | """Module to solve codewars kata https://www.codewars.com/kata/first-n-prime-numbers.""" 2 | 3 | from itertools import islice, count 4 | 5 | class Primes(object): 6 | """Create Primes object to store and return prime numbers. 7 | 8 | Methods: 9 | _eratosthenes(): generate primes infinitely 10 | _generate_primes: call _eratosthenes and fill up list until n elements 11 | first(n): will return a list of primes up to n. 12 | """ 13 | 14 | @staticmethod 15 | def _eratosthenes(): 16 | """Yield primes infinitively with a modified version of Eratosthenes. 17 | Since we know primes can't be even, we iterate in steps of 2.""" 18 | D = {} 19 | yield 2 20 | for q in islice(count(3), 0, None, 2): 21 | p = D.pop(q, None) 22 | if p is None: 23 | D[q*q] = q 24 | yield q 25 | else: 26 | x = q + 2*p 27 | while x in D: 28 | x += 2*p 29 | D[x] = p 30 | 31 | @staticmethod 32 | def _generate_primes(n): 33 | """Return a list of primes which is of length n by yielding next prime from 34 | eratosthenes function.""" 35 | prime_list = [] 36 | for p in Primes._eratosthenes(): 37 | prime_list.append(p) 38 | if len(prime_list) == n: 39 | break 40 | return prime_list 41 | 42 | @classmethod 43 | def first(self, n): 44 | """Return the list containing n primes.""" 45 | prime_list = Primes._generate_primes(n) 46 | return prime_list 47 | -------------------------------------------------------------------------------- /kyu5/python/src/sud_validator.py: -------------------------------------------------------------------------------- 1 | """Module to solve the code-kata https://www.codewars.com/kata/did-i-finish-my-sudoku/.""" 2 | 3 | def done_or_not(board): 4 | """Return a message to the user if the given Sudoku board is valid or not.""" 5 | for l in board + list(zip(*board)) + [sum((board[x+k][y:y+3] 6 | for k in (0,1,2)), []) for x in (0,3,6) for y in (0,3,6)]: 7 | if len(set(l)) < 9: return 'Try again!' 8 | return 'Finished!' 9 | -------------------------------------------------------------------------------- /kyu5/python/src/test_base_neg_2.py: -------------------------------------------------------------------------------- 1 | """Tests for codewars kata https://www.codewars.com/kata/base-2/train/python.""" 2 | 3 | import pytest 4 | 5 | TESTS = [ 6 | (0, '0') 7 | (6, '11010'), 8 | (-6, '1110'), 9 | (4, '100'), 10 | (18, '10110'), 11 | (-11, '110101'), 12 | ] 13 | 14 | 15 | @pytest.mark.parametrize('n, result', TESTS) 16 | def test_int_to_negabinary(n, result): 17 | """Test that given number n is being correctly returned as result.""" 18 | from base_neg_2.py import int_to_negabinary 19 | assert int_to_negabinary(n) == result 20 | -------------------------------------------------------------------------------- /kyu5/python/src/test_basic_excel.py: -------------------------------------------------------------------------------- 1 | """Tests for codewars kata https://www.codewars.com/kata/56055244356dc5c45c00001e.""" 2 | 3 | import pytest 4 | 5 | 6 | #try to use tuples for input var 7 | COUNTIF_INPUT = [ 8 | ([1, 3, 5, 7, 9], 3, 1), 9 | (["John","Steve","John"], "John", 2), 10 | ([1, 3, 5, 3],'>=3', 3), 11 | ([1.5, 3, 5, 3, 0, -1, -5],'<=1.5', 4), 12 | ([1, 3, 5, 3.5],'>3', 2), 13 | ([1, 3, 5, 3, 0, -1, -5],'<1', 3), 14 | ([1, 3, 5, 3, 0, -1, -5],'<>1', 6) 15 | ] 16 | 17 | SUMIF_INPUT = [ 18 | ([2, 4, 6, -1, 3, 1.5],">0", 16.5), 19 | ([1, 3, 5, 3], 3, 6), 20 | ([1, 3, 5, 3],'>=3', 11), 21 | ([1.5, 3, 5, 3, 0, -1, -5],'<=1.5', -4.5), 22 | ([1, 3, 5, 3.5],'>3', 8.5), 23 | ([1, 3, 5, 3, 0, -1, -5],'<1', -6), 24 | ([1, 3, 5, 3, 0, -1, -5],'<>1', 5), 25 | ] 26 | 27 | AVERAGEIF_INPUT = [ 28 | ([99, 95.5, 0, 83],"<>0", 92.5), 29 | ([1, 3, 5, 3], 3, 3), 30 | ([1, 3, 5, 3],'>=3', 11/3.0), 31 | ([1.5, 3, 5, 3, 0, -1, -5],'<=1.5', -1.125), 32 | ([1, 3, 5, 3.5],'>3', 4.25), 33 | ([1, 3, 5, 3, 0, -1, -5],'<1', -2), 34 | ([1, 3, 5, 3, 0, -1, -5],'<>1', 5/6.0), 35 | ] 36 | 37 | 38 | @pytest.mark.parametrize('values, criteria, result', COUNTIF_INPUT) 39 | def test_countif(values, criteria, result): 40 | """Test function returns number of occurences of comparator in elements.""" 41 | from basic_excel import count_if 42 | assert count_if(values, criteria) == result 43 | 44 | @pytest.mark.parametrize('values, criteria, result', SUMIF_INPUT) 45 | def test_sumif(values, criteria, result): 46 | """Test function returns sum of elements based on criteria.""" 47 | from basic_excel import sum_if 48 | assert sum_if(values, criteria) == result 49 | 50 | @pytest.mark.parametrize('values, criteria, result', AVERAGEIF_INPUT) 51 | def test_averageif(values, criteria, result): 52 | """Test function returns average of elements based on comparator.""" 53 | from basic_excel import average_if 54 | assert average_if(values, criteria) == result 55 | -------------------------------------------------------------------------------- /kyu5/python/src/test_best_travel.py: -------------------------------------------------------------------------------- 1 | """Tests for codewars kata https://www.codewars.com/kata/best-travel/.""" 2 | 3 | import pytest 4 | 5 | cities = [100, 76, 56, 44, 89, 73, 68, 56, 64, 123, 2333, 144, 50, 132, 123, 34, 89] 6 | 7 | TEST_INPUT = [ 8 | (163, 3, cities, 163), 9 | (230, 4, cities, 230), 10 | (430, 5, cities, 430), 11 | (430, 8, cities, None), 12 | ] 13 | 14 | 15 | @pytest.mark.parametrize('t, k, ls, result', TEST_INPUT) 16 | def test_choose_best_sum(t, k, ls, result): 17 | """Test that for a list, function returns correct result.""" 18 | from best_travel import choose_best_sum 19 | assert choose_best_sum(t, k, ls) == result 20 | -------------------------------------------------------------------------------- /kyu5/python/src/test_camelcase.py: -------------------------------------------------------------------------------- 1 | """Tests for codewars kata https://www.codewars.com/kata/convert-string-to-camel-case/python.""" 2 | 3 | import pytest 4 | 5 | 6 | TEST_INPUT = [ 7 | ('',''), 8 | ('the_stealth_warrior', 'theStealthWarrior'), 9 | ('The-Stealth-Warrior', 'TheStealthWarrior'), 10 | ] 11 | 12 | 13 | @pytest.mark.parametrize('s, result', TEST_INPUT) 14 | def test_to_camel_case(s, result): 15 | """Test to_camel_case converts correctly.""" 16 | from camelcase import to_camel_case 17 | assert to_camel_case(s) == result 18 | -------------------------------------------------------------------------------- /kyu5/python/src/test_carpark.py: -------------------------------------------------------------------------------- 1 | """Tests for codewars kata https://www.codewars.com/kata/car-park-escape/.""" 2 | 3 | import pytest 4 | 5 | 6 | TEST_INPUT = [ 7 | ([ 8 | [1, 0, 0, 0, 2], 9 | [0, 0, 0, 0, 0]], ["L4", "D1", "R4"]), 10 | ([ 11 | [2, 0, 0, 1, 0], 12 | [0, 0, 0, 1, 0], 13 | [0, 0, 0, 0, 0]], ["R3", "D2", "R1"]), 14 | ([ 15 | [0, 2, 0, 0, 1], 16 | [0, 0, 0, 0, 1], 17 | [0, 0, 0, 0, 1], 18 | [0, 0, 0, 0, 0]], ["R3", "D3"]), 19 | ([ 20 | [1, 0, 0, 0, 2], 21 | [0, 0, 0, 0, 1], 22 | [1, 0, 0, 0, 0], 23 | [0, 0, 0, 0, 0]], ["L4", "D1", "R4", "D1", "L4", "D1", "R4"]), 24 | ([ 25 | [0, 0, 0, 0, 2]], []), 26 | ([ 27 | [0, 0, 0, 1, 0], 28 | [2, 0, 0, 1, 0], 29 | [0, 0, 0, 0, 0]], ["R3", "D1", "R1"]), 30 | ] 31 | 32 | 33 | @pytest.mark.parametrize('carpark, route', TEST_INPUT) 34 | def test_alphanumeric(carpark, route): 35 | """Test that for a given carpark, the function returns a valid escape route.""" 36 | from carpark import escape 37 | assert escape(carpark) == route 38 | -------------------------------------------------------------------------------- /kyu5/python/src/test_diceroller.py: -------------------------------------------------------------------------------- 1 | """Tests for codewars kata https://www.codewars.com/kata/rpg-dice-roller.""" 2 | 3 | import pytest 4 | 5 | TEST_INPUT = [ 6 | ('', False), 7 | ({}, False), 8 | ('abc', False), 9 | ('2d6+3 abc', False), 10 | ('abc 2d6+3', False), 11 | ('2d6++4', False), 12 | ('d6', True), 13 | ('2d4', True), 14 | ('d3 + 4', True), 15 | ('3d7 + 3 -2', True), 16 | ('10d20', True), 17 | ] 18 | 19 | TEST_MODIFIERS = [ 20 | ('d3+4', 4), 21 | ('3d7+3-2', 1), 22 | ('d6+10-11', -1), 23 | ('d6', 0), 24 | ] 25 | 26 | TEST_OUTPUT_SUMMED = [ 27 | ('d6', 0, 7), 28 | ('2d4', 1, 9), 29 | ('d3 + 4', 4, 8), 30 | ('3d7 + 3 - 2', 3, 23), 31 | ] 32 | 33 | 34 | @pytest.mark.parametrize('d, result', TEST_INPUT) 35 | def test_dice_notation_input(d, result): 36 | """Test that dice notation is correct.""" 37 | from diceroller import validate_input, normalize_input 38 | assert validate_input(d) == result 39 | 40 | @pytest.mark.parametrize('d, result', TEST_MODIFIERS) 41 | def test_get_modifiers(d, result): 42 | """Test modifiers are correctly returned as an integer.""" 43 | from diceroller import get_modifiers 44 | assert get_modifiers(d) == result 45 | 46 | @pytest.mark.parametrize('d, lower, upper', TEST_OUTPUT_SUMMED) 47 | def test_dice_roll_summed(d, lower, upper): 48 | """Test that output with summed returns a number within bounds.""" 49 | from diceroller import roll 50 | assert lower < roll(d) < upper 51 | -------------------------------------------------------------------------------- /kyu5/python/src/test_digits_products.py: -------------------------------------------------------------------------------- 1 | """Test for https://www.codewars.com/kata/simple-fun-number-81-digits-product/.""" 2 | 3 | import pytest 4 | 5 | 6 | TEST_DIGITS = [ 7 | (12, 26), 8 | (19, -1), 9 | (450, 2559), 10 | (0, 10), 11 | (13, -1), 12 | (1, 11), 13 | (5, 15), 14 | (10, 25), 15 | ] 16 | 17 | 18 | @pytest.mark.parametrize('n, result', TEST_DIGITS) 19 | def test_digits_product(n, result): 20 | """Test fib_digits returns correct integer pairs.""" 21 | from digits_product import digits_product 22 | assert digits_product(n) == result 23 | -------------------------------------------------------------------------------- /kyu5/python/src/test_domain_name.py: -------------------------------------------------------------------------------- 1 | """Tests for codewars kata https://www.codewars.com/kata/extract-the-domain-name-from-a-url-1/train/python.""" 2 | 3 | import pytest 4 | 5 | 6 | TEST_INPUT = [ 7 | ('http://github.com/carbonfive/raygun', 'github'), 8 | ('http://www.zombie-bites.com', 'zombie-bites'), 9 | ('https://www.cnet.com', 'cnet'), 10 | ('www.xakep.ru', 'xakep'), 11 | ] 12 | 13 | 14 | @pytest.mark.parametrize('url, result', TEST_INPUT) 15 | def test_domain_name(url, result): 16 | """Test domain name is returned correctly from given url.""" 17 | from domain_name import domain_name 18 | assert domain_name(url) == result 19 | -------------------------------------------------------------------------------- /kyu5/python/src/test_even_digit_primes.py: -------------------------------------------------------------------------------- 1 | """Test for https://www.codewars.com/kata/primes-with-even-digits.""" 2 | 3 | import pytest 4 | 5 | 6 | TEST_DIGITS = [ 7 | (1000, 887), 8 | (1210, 1201), 9 | (10000, 8887), 10 | (500, 487), 11 | (487, 467), 12 | (90924, 88883), 13 | (1576, 1489), 14 | (91839, 88883), 15 | (639617, 628861), 16 | ] 17 | 18 | 19 | @pytest.mark.parametrize('n, result', TEST_DIGITS) 20 | def test_fib_digits(n, result): 21 | """Test fib_digits returns correct integer pairs.""" 22 | from even_digit_primes import f 23 | assert f(n) == result 24 | -------------------------------------------------------------------------------- /kyu5/python/src/test_fib_digit_occurence.py: -------------------------------------------------------------------------------- 1 | """Tests for https://www.codewars.com/kata/calculate-fibonacci-return-count-of-digit-occurrences.""" 2 | 3 | import pytest 4 | 5 | 6 | TEST_FIB = [ 7 | (1, 1), 8 | (3, 2), 9 | (20, 6765), 10 | (50, 12586269025), 11 | (99, 218922995834555169026), 12 | ] 13 | 14 | TEST_DIGITS = [ 15 | (10, [(2, 5)]), 16 | (10000, [ 17 | (254, 3),(228, 2),(217, 6),(217, 0),(202, 5),(199, 1),(198, 7),(197, 8), 18 | (194, 4),(184, 9) 19 | ]), 20 | (100000, [ 21 | (2149, 2), (2135, 1), (2131, 8), (2118, 9), (2109, 0), (2096, 3), (2053, 5), (2051, 6), (2034, 7), (2023, 4) 22 | ]), 23 | ] 24 | 25 | 26 | @pytest.mark.parametrize('n, result', TEST_FIB) 27 | def test_generate_fib(n, result): 28 | """Test generate_fib returns correct Fibonacci numbers.""" 29 | from fib_digit_occurence import generate_fib 30 | assert generate_fib(n) == result 31 | 32 | 33 | 34 | @pytest.mark.parametrize('n, result', TEST_DIGITS) 35 | def test_fib_digits(n, result): 36 | """Test fib_digits returns correct integer pairs.""" 37 | from fib_digit_occurence import fib_digits 38 | assert fib_digits(n) == result 39 | -------------------------------------------------------------------------------- /kyu5/python/src/test_find_unique_str.py: -------------------------------------------------------------------------------- 1 | """Tests for codewars kata https://www.codewars.com/kata/find-the-unique-string.""" 2 | 3 | import pytest 4 | 5 | 6 | TEST = [ 7 | ([ 'Aa', 'aaa', 'aaaaa', 'BbBb', 'Aaaa', 'AaAaAa', 'a' ], 'BbBb'), 8 | ([ 'abc', 'acb', 'bac', 'foo', 'bca', 'cab', 'cba' ], 'foo'), 9 | ([' ', ' ', ' ', 'a', ' ', ''], 'a'), 10 | (['', '', '', 'a', '', ''], 'a'), 11 | ] 12 | 13 | 14 | @pytest.mark.parametrize('arr, result', TEST) 15 | def test_find_uniq(arr, result): 16 | """Test that function returns unique element.""" 17 | from find_unique_str import find_uniq 18 | assert find_uniq(arr) == result 19 | -------------------------------------------------------------------------------- /kyu5/python/src/test_mod4regex.py: -------------------------------------------------------------------------------- 1 | """Tests for codewars kata https://www.codewars.com/kata/mod4-regex.""" 2 | 3 | import pytest 4 | 5 | 6 | TEST_INPUT = [ 7 | ('[+05620]', True), 8 | ('[+05621]', False), 9 | ('[-55622]', False), 10 | ('[005623]', False), 11 | ('[005624]', True), 12 | ('[-05628]', True), 13 | ('the beginning [0] ... [invalid] numb[3]rs ... the end', True), 14 | ("No, [2014] isn't a multiple of 4...", False), 15 | ('...may be [+002016] will be.', True), 16 | ('[555636]', True), 17 | ('[005600]', True), 18 | ('the beginning [-0] the end', True), 19 | ('~[4]', True), 20 | ('[+05640]', True), 21 | ('[32]', True), 22 | ('[~24]', False), 23 | ] 24 | 25 | 26 | @pytest.mark.parametrize('s, result', TEST_INPUT) 27 | def test_mod4regex(s, result): 28 | """Test mod4-regex returns mod4 correctly.""" 29 | from mod4regex import mod4 30 | assert mod4(s) == result 31 | -------------------------------------------------------------------------------- /kyu5/python/src/test_not_secure.py: -------------------------------------------------------------------------------- 1 | """Tests for codewars kata https://www.codewars.com/kata/not-very-secure/train/python.""" 2 | 3 | import pytest 4 | 5 | 6 | TEST_INPUT = [ 7 | ('hello world_', False), 8 | ('PassW0rd', True), 9 | ('', False), 10 | (' ', False), 11 | ] 12 | 13 | 14 | @pytest.mark.parametrize('s, result', TEST_INPUT) 15 | def test_alphanumeric(s, result): 16 | """Test alphanumeric returns True if input is alphanumeric else False.""" 17 | from not_secure import alphanumeric 18 | assert alphanumeric(s) == result 19 | 20 | @pytest.mark.parametrize('s, result', TEST_INPUT) 21 | def test_alphanumeric_py(s, result): 22 | """Test alphanumeric returns True if input is alphanumeric else False.""" 23 | from not_secure import alphanumeric_py 24 | assert alphanumeric_py(s) == result 25 | -------------------------------------------------------------------------------- /kyu5/python/src/test_prime_factorization.py: -------------------------------------------------------------------------------- 1 | """Tests for kata Prime factorization.""" 2 | 3 | import pytest 4 | 5 | INPUT_NUMS = [1, 24, 13] 6 | 7 | TESTS_PF = { 8 | 1: {}, 9 | 24: {2: 3, 3: 1}, 10 | 13: {13: 1}, 11 | } 12 | 13 | @pytest.fixture(scope='function', params=INPUT_NUMS) 14 | def PrimeFactorizer(request): 15 | from prime_factorization import PrimeFactorizer 16 | new_PrimeFactorizer = PrimeFactorizer(request.param) 17 | return new_PrimeFactorizer 18 | 19 | def test_prime_factorization(PrimeFactorizer): 20 | """Test that a dict with correct prime factors and count are returned.""" 21 | result = TESTS_PF[PrimeFactorizer] 22 | assert PrimeFactorizer.factor == result 23 | -------------------------------------------------------------------------------- /kyu5/python/src/test_primes.py: -------------------------------------------------------------------------------- 1 | """Test dbl_linked_list data structures.""" 2 | 3 | import pytest 4 | 5 | TESTS = [ 6 | (1, [2]), 7 | (2, [2, 3]), 8 | (5, [2, 3, 5, 7, 11]), 9 | ] 10 | 11 | 12 | @pytest.fixture 13 | def empty_Primes(): 14 | from primes import Primes 15 | new_primes = Primes() 16 | return new_primes 17 | 18 | 19 | @pytest.mark.parametrize('p, result', TESTS) 20 | def test_first_few_primes(p, result, empty_Primes): 21 | """Test method first returns results from TESTS.""" 22 | assert empty_Primes.first(p) == result 23 | 24 | def test_primes_slice(): 25 | """Test method first returns correct slice.""" 26 | from primes import Primes 27 | Primes = Primes() 28 | assert Primes.first(20)[-5:] == [53, 59, 61, 67, 71] 29 | 30 | def test_prime_100th_element(): 31 | """Test method first returns correct element.""" 32 | from primes import Primes 33 | Primes = Primes() 34 | assert Primes.first(100)[99] == 541 35 | 36 | def test_prime_80thth_element(): 37 | """Test method first returns correct element.""" 38 | from primes import Primes 39 | Primes = Primes() 40 | assert Primes.first(80)[79] == 409 41 | -------------------------------------------------------------------------------- /kyu5/python/src/test_sud_validator.py: -------------------------------------------------------------------------------- 1 | """Tests for https://www.codewars.com/kata/did-i-finish-my-sudoku/.""" 2 | 3 | import pytest 4 | 5 | valid = [ 6 | [1, 3, 2, 5, 7, 9, 4, 6, 8], 7 | [4, 9, 8, 2, 6, 1, 3, 7, 5], 8 | [7, 5, 6, 3, 8, 4, 2, 1, 9], 9 | [6, 4, 3, 1, 5, 8, 7, 9, 2], 10 | [5, 2, 1, 7, 9, 3, 8, 4, 6], 11 | [9, 8, 7, 4, 2, 6, 5, 3, 1], 12 | [2, 1, 4, 9, 3, 5, 6, 8, 7], 13 | [3, 6, 5, 8, 1, 7, 9, 2, 4], 14 | [8, 7, 9, 6, 4, 2, 1, 5, 3] 15 | ] 16 | 17 | invalid_last_column = [ 18 | [1, 3, 2, 5, 7, 9, 4, 6, 8], 19 | [4, 9, 8, 2, 6, 1, 3, 7, 5], 20 | [7, 5, 6, 3, 8, 4, 2, 1, 9], 21 | 22 | [6, 4, 3, 1, 5, 8, 7, 9, 2], 23 | [5, 2, 1, 7, 9, 3, 8, 4, 6], 24 | [9, 8, 7, 4, 2, 6, 5, 3, 1], 25 | 26 | [2, 1, 4, 9, 3, 5, 6, 8, 7], 27 | [3, 6, 5, 8, 1, 7, 9, 2, 4], 28 | [8, 7, 9, 6, 4, 2, 1, 3, 5] 29 | ] 30 | 31 | INPUTS = [ 32 | (valid, 'Finished!'), 33 | (invalid_last_column, 'Try again!'), 34 | ] 35 | @pytest.mark.parametrize('board, result', INPUTS) 36 | def test_valid_sudoku(board, result): 37 | """Test if function returns correct message.""" 38 | from sud_validator import done_or_not 39 | assert done_or_not(board) == result 40 | -------------------------------------------------------------------------------- /kyu5/python/src/test_valid_braces.py: -------------------------------------------------------------------------------- 1 | """Tests for codewars kata https://www.codewars.com/kata/valid-braces.""" 2 | 3 | import pytest 4 | 5 | 6 | TEST_PS = [ 7 | (')))(((', False), 8 | ('((()))(', False), 9 | ('((()))', True), 10 | ('()()()()()()', True), 11 | ('())))(((()', False), 12 | ('()()())()()()', False), 13 | ('(()(()(', False), 14 | ('(((((())))', False), 15 | ('())(((((', False), 16 | ] 17 | 18 | TEST_INPUT = [ 19 | ('(){}[]', True), 20 | ('(}', False), 21 | ('[(])', False), 22 | ('([{}])', True), 23 | ('', False), 24 | ('(', False), 25 | ] 26 | 27 | @pytest.mark.parametrize('s, result', TEST_PS) 28 | def test_valid_braces_parantheses(s, result): 29 | """Test valid braces for just parantheses.""" 30 | from valid_braces import valid_braces 31 | assert valid_braces(s) == result 32 | 33 | @pytest.mark.parametrize('s, result', TEST_INPUT) 34 | def test_valid_braces(s, result): 35 | """Test valid braces returns correct result.""" 36 | from valid_braces import valid_braces 37 | assert valid_braces(s) == result 38 | -------------------------------------------------------------------------------- /kyu5/python/src/test_valid_parentheses.py: -------------------------------------------------------------------------------- 1 | """Tests for codewars kata https://www.codewars.com/kata/valid-parentheses/python.""" 2 | 3 | import pytest 4 | 5 | 6 | TEST_PS = [ 7 | (')))(((', False), 8 | ('((()))(', False), 9 | ('((()))', True), 10 | ('()()()()()()', True), 11 | ('())))(((()', False), 12 | ('()()())()()()', False), 13 | ('(()(()(', False), 14 | ('(((((())))', False), 15 | ('())(((((', False), 16 | ('hi(hiasdfsfasfal2342342ogojia.?!!$%^sdjgosagj)()',True), 17 | ] 18 | 19 | 20 | @pytest.mark.parametrize('s, result', TEST_PS) 21 | def test_valid_parentheses(s, result): 22 | """Test valid braces for parantheses.""" 23 | from valid_parentheses import valid_parentheses 24 | assert valid_parentheses(s) == result 25 | -------------------------------------------------------------------------------- /kyu5/python/src/valid_braces.py: -------------------------------------------------------------------------------- 1 | """Solution for kata https://www.codewars.com/kata/valid-braces.""" 2 | 3 | import queue as q 4 | 5 | 6 | def valid_braces(s): 7 | """Return True if given string consisting of a mix of parens, brackets and 8 | braces is valid.""" 9 | queuesize = len(s) 10 | if queuesize < 1: 11 | return False 12 | parens = q.LifoQueue(queuesize) 13 | braces = q.LifoQueue(queuesize) 14 | brackets = q.LifoQueue(queuesize) 15 | last_updated = q.LifoQueue(queuesize) 16 | for p in s: 17 | # Tracer()() 18 | if p == '(': 19 | parens.put(p) 20 | last_updated.put(parens) 21 | elif p == '{': 22 | braces.put(p) 23 | last_updated.put(braces) 24 | elif p == '[': 25 | brackets.put(p) 26 | last_updated.put(brackets) 27 | else: 28 | try: 29 | if p == ')' and last_updated.get_nowait() == parens: 30 | parens.get_nowait() 31 | elif p == '}' and last_updated.get_nowait() == braces: 32 | braces.get_nowait() 33 | elif p == ']' and last_updated.get_nowait() == brackets: 34 | brackets.get_nowait() 35 | else: 36 | return False 37 | except q.Empty: 38 | return False 39 | if parens.empty() and braces.empty() and brackets.empty(): 40 | return True 41 | return False 42 | -------------------------------------------------------------------------------- /kyu5/python/src/valid_parentheses.py: -------------------------------------------------------------------------------- 1 | """Solution for kata https://www.codewars.com/kata/valid-parentheses/python.""" 2 | 3 | def valid_parentheses(s): 4 | """Return a boolean if given string has valid parentheses.""" 5 | stack = [] 6 | for c in s: 7 | if c not in ['(', ')']: 8 | continue 9 | if c == '(': 10 | stack.append(c) 11 | else: 12 | try: 13 | stack.pop() 14 | except IndexError: 15 | return False 16 | return len(stack) == 0 17 | -------------------------------------------------------------------------------- /kyu5/sql/README.MD: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | this repository includes all of my completed code-katas from the 4 | [CodeWars SQL challenges](http://wwww.codewars.com) with a difficulty of 5 | kyu5. [History of the term kyu](https://en.wikipedia.org/wiki/Ky%C5%AB) 6 | 7 | # SQL coding challenges: 8 | 9 | ## kyu5 10 | 11 | * SQL Tuning: Function Calls 12 | * Simple Hierarchical structure 13 | * Simple PIVOTING data 14 | * Simple View 15 | * Group By Day 16 | * Relational Division 17 | * Calculating Running Total 18 | * SQLonacci sequence 19 | 20 | ## beta 21 | 22 | * Using Window Functions To Get Top N per Group 23 | * SQL Data: Company Data - totals per day 24 | * Count Weekdays 25 | 26 | ## Details for each kata completed 27 | 28 | ### SQL Tuning: Function Calls 29 | * Description of Kata: Your objective in this kata is to optimize this query. 30 | The success criteria is to manage to run the query within the allowed kata 31 | solution time window (12 seconds for SQL katas). 32 | * Difficulty: kyu5 33 | * Module: efficient_salary_increase.sql 34 | * [Link](https://www.codewars.com/kata/sql-tuning-function-calls/) 35 | 36 | ### Simple Hierarchical structure 37 | * Description of Kata: For this challenge you need to create a RECURSIVE Hierarchical query 38 | * Difficulty: kyu5 39 | * Module: hierachical_structure.sql 40 | * [Link](https://www.codewars.com/kata/sql-basics-simple-hierarchical-structure/sql) 41 | 42 | ### Simple PIVOTING data 43 | * Description of Kata: For this challenge you need to PIVOT data using crosstab. 44 | * Difficulty: kyu5 45 | * Module: pivot.sql 46 | * [Link](https://www.codewars.com/kata/sql-basics-simple-pivoting-data/sql) 47 | 48 | ### Simple View 49 | * Description of Kata: Create a simple view 50 | * Difficulty: kyu5 51 | * Module: simple_view.sql 52 | * [Link](https://www.codewars.com/kata/sql-basics-simple-view/sql) 53 | 54 | ### SQL Statistics: MIN, MEDIAN, MAX 55 | * Description of Kata: SQL Statistics: Your task is to calculate the MIN, MEDIAN 56 | and MAX scores of the students from the results table. 57 | * Difficulty: kyu5 58 | * Module: min_median_max.sql 59 | * [Link](https://www.codewars.com/kata/sql-statistics-min-median-max/sql) 60 | 61 | ### Group By Day 62 | * Description of Kata: There is an events table used to track different key 63 | activities taken on a website. For this task you need to filter the name field 64 | to only show "trained" events. Events should be grouped by the day they happened 65 | and counted. The description field is used to distinguish which items the events 66 | happened on. 67 | * Difficulty: kyu5 68 | * Module: group_by_day.sql 69 | * [Link](https://www.codewars.com/kata/sql-basics-group-by-day/sql) 70 | 71 | ### Calculating Running Total 72 | * Description of Kata: Given a posts table that contains a created_at timestamp 73 | column write a query that returns date (without time component), a number of 74 | posts for a given date and a running (cumulative) total number of posts up until 75 | a given date. The resulting set should be ordered chronologically by date. 76 | * Difficulty: kyu5 77 | * Module: running_total.sql 78 | * [Link](https://www.codewars.com/kata/calculating-running-total) 79 | * Comments: 80 | 81 | ### Relational Divison 82 | * Description of Kata: Given film_actor and film tables from the DVD Rental sample 83 | database find all movies both Sidney Crowe (actor_id = 105) and Salma Nolte 84 | (actor_id = 122) cast in together and order the result set alphabetically. 85 | * Difficulty: kyu5 86 | * Module: relational_division_of_actors.sql 87 | * [Link](https://www.codewars.com/kata/relational-division-find-all-movies-two-actors-cast-in-together/sql) 88 | 89 | ### Calculating Running Total 90 | * Description of Kata: Given a posts table that contains a created_at timestamp 91 | column write a query that returns date (without time component), a number of 92 | posts for a given date and a running (cumulative) total number of posts up until 93 | a given date 94 | * Difficulty: kyu5 95 | * Module: running_total.sql 96 | * [Link](https://www.codewars.com/kata/calculating-running-total/) 97 | 98 | ### SQLonacci sequence 99 | * Description of Kata: You need to create a select statement which will produce 100 | first 90 Fibonnacci numbers. 101 | * Difficulty: kyu5 102 | * Module: sqlonacci.sql 103 | * [Link](https://www.codewars.com/kata/sqlonacci-sequence/) 104 | 105 | ### Using Window Functions To Get Top N per Group 106 | * Description of Kata: Given the schema presented below write a query, which 107 | uses a window function, that returns two most viewed posts for every category. 108 | * Difficulty: beta 109 | * Module: top_n_per_group_window.sql 110 | * [Link](https://www.codewars.com/kata/using-window-functions-to-get-top-n-per-group) 111 | 112 | ### SQL Data: Company Data - totals per day 113 | * Description of Kata: Your task is simple, given the data you must calculate 114 | on each day how many hours have been clocked up for each department. 115 | * Difficulty: beta 116 | * Module: time_clocked.sql 117 | * [Link](https://www.codewars.com/kata/sql-data-company-data-totals-per-day/) 118 | 119 | ### Count Weekdays 120 | * Description of Kata: You need to create a function that calculates the number 121 | of weekdays (Monday through Friday) between two dates inclusively. 122 | The function should be named weekdays accept two arguments of type DATE and return 123 | an INTEGER value. The order of arguments shouldn't matter. 124 | * Difficulty: beta 125 | * Module: weekdays_function.sql 126 | * [Link](https://www.codewars.com/kata/count-weekdays/sql) 127 | * Comments: using least/greatest to get the dates in the right order and generating 128 | a series of days and then filtering on days which are not in 0 and 6. 129 | -------------------------------------------------------------------------------- /kyu5/sql/src/efficient_salary_increase.sql: -------------------------------------------------------------------------------- 1 | -- Solution to codewars kata https://www.codewars.com/kata/sql-tuning-function-calls/ 2 | 3 | -- starting sql statement to be tuned: 4 | SELECT e.employee_id, 5 | e.first_name, 6 | e.last_name, 7 | d.department_name, 8 | e.salary AS old_salary, 9 | e.salary * (1 + pctIncrease(e.department_id)) AS new_salary 10 | FROM employees e, 11 | departments d 12 | WHERE e.department_id = d.department_id 13 | AND e.salary = 0 14 | ORDER BY 1 15 | 16 | 17 | 18 | -- starting sql statement to be tuned: 19 | 20 | -- create tmp_table for increase of salary per department so we don't call the function for every employee 21 | 22 | CREATE TEMP TABLE department ( 23 | department_id integer, 24 | department_name text, 25 | increase decimal 26 | ); 27 | 28 | INSERT INTO department SELECT 29 | department_id, department_name, pctIncrease(department_id) as increase 30 | FROM departments; 31 | 32 | SELECT e.employee_id, 33 | e.first_name, 34 | e.last_name, 35 | d.department_name, 36 | e.salary AS old_salary, 37 | e.salary * 1 + d.increase AS new_salary 38 | FROM employees e, 39 | department d 40 | WHERE e.department_id = d.department_id 41 | ORDER BY 1 42 | 43 | 44 | -- using subquery 45 | 46 | SELECT e.employee_id, 47 | e.first_name, 48 | e.last_name, 49 | d.department_name, 50 | e.salary AS old_salary, 51 | e.salary * d.pctinc AS new_salary 52 | FROM employees e, 53 | (SELECT department_id, 54 | department_name, 55 | 1 + pctIncrease(department_id) pctinc 56 | FROM departments) d 57 | WHERE e.department_id = d.department_id 58 | ORDER BY 1; 59 | 60 | 61 | -- using CTE 62 | 63 | WITH CTE AS (SELECT d.department_id 64 | ,d.department_name 65 | ,pctIncrease(d.department_id) as IncrecasePercentage 66 | FROM departments d) 67 | 68 | 69 | 70 | SELECT e.employee_id, 71 | e.first_name, 72 | e.last_name, 73 | d.department_name, 74 | e.salary AS old_salary, 75 | e.salary * (1 + d.IncrecasePercentage) AS new_salary 76 | FROM employees e 77 | INNER JOIN CTE d ON e.department_id = d.department_id 78 | ORDER BY 1; 79 | -------------------------------------------------------------------------------- /kyu5/sql/src/group_by_day.sql: -------------------------------------------------------------------------------- 1 | -- Solution to codewars kata https://www.codewars.com/kata/sql-basics-group-by-day/ 2 | 3 | SELECT created_at::date as day, description, count(name) 4 | FROM events 5 | WHERE name = 'trained' 6 | GROUP BY day, description 7 | ORDER BY day 8 | -------------------------------------------------------------------------------- /kyu5/sql/src/hierachical_structure.sql: -------------------------------------------------------------------------------- 1 | -- Solution for kata https://www.codewars.com/kata/sql-basics-simple-hierarchical-structure/ 2 | 3 | WITH RECURSIVE employee_levels(level, id, first_name, last_name, manager_id) 4 | AS ( 5 | SELECT 6 | 1 as level, 7 | id, first_name, last_name, manager_id 8 | FROM employees 9 | WHERE manager_id is NULL 10 | 11 | UNION ALL 12 | 13 | SELECT 14 | level + 1, 15 | e.id, e.first_name, e.last_name, e.manager_id 16 | FROM employees e, employee_levels el 17 | WHERE e.manager_id = el.id 18 | ) 19 | 20 | SELECT 21 | level, id, first_name, last_name, manager_id 22 | FROM employee_levels; 23 | -------------------------------------------------------------------------------- /kyu5/sql/src/min_median_max.sql: -------------------------------------------------------------------------------- 1 | -- Solution to codewars kata https://www.codewars.com/kata/sql-statistics-min-median-max/ 2 | 3 | SELECT 4 | MIN(score), 5 | ROUND(PERCENTILE_CONT(0.50) WITHIN GROUP (ORDER BY score)::numeric, 2)::float as median, 6 | MAX(score) 7 | FROM result; 8 | -------------------------------------------------------------------------------- /kyu5/sql/src/pivot.sql: -------------------------------------------------------------------------------- 1 | -- Solution for kata https://www.codewars.com/kata/sql-basics-simple-pivoting-data/sql 2 | 3 | CREATE EXTENSION tablefunc; 4 | 5 | CREATE TEMP TABLE product_details ( 6 | name text, 7 | detail text, 8 | ct integer 9 | ); 10 | 11 | INSERT INTO product_details 12 | SELECT p.name, d.detail, count(d.detail) as ct 13 | FROM products p, details d 14 | WHERE p.id = d.product_id 15 | GROUP BY p.name, d.detail; 16 | 17 | SELECT * 18 | FROM crosstab( 19 | 'SELECT name, detail, ct 20 | FROM product_details 21 | ORDER BY 1,2' 22 | ,$$VALUES ('good'::text), ('ok'::text), ('bad'::text)$$) 23 | AS ct("name" text, "good" int, "ok" int, "bad" int); 24 | 25 | 26 | -- most upvoted 27 | 28 | CREATE EXTENSION tablefunc; 29 | 30 | SELECT * 31 | FROM crosstab( 32 | 'SELECT p.name, detail, COUNT(d.id) 33 | FROM products p 34 | JOIN details d 35 | ON p.id = d.product_id 36 | GROUP BY p.name, d.detail 37 | ORDER BY 1,2') 38 | AS ct (name text, bad bigint, good bigint, ok bigint) 39 | -------------------------------------------------------------------------------- /kyu5/sql/src/relational_division_of_actors.sql: -------------------------------------------------------------------------------- 1 | -- Solution to codewars kata https://www.codewars.com/kata/relational-division-find-all-movies-two-actors-cast-in-together/sql 2 | 3 | SELECT F.title 4 | FROM 5 | (SELECT FA.film_id 6 | FROM film_actor FA 7 | WHERE FA.actor_id=105 8 | INTERSECT 9 | SELECT FA.film_id 10 | FROM film_actor FA 11 | WHERE FA.actor_id=122) R, film F 12 | WHERE F.film_id=R.film_id 13 | ORDER BY title 14 | -------------------------------------------------------------------------------- /kyu5/sql/src/running_total.sql: -------------------------------------------------------------------------------- 1 | -- Solution to codewars kata http://www.codewars.com/kata/calculating-running-total/ 2 | 3 | WITH cte AS (SELECT created_at::date as date FROM posts) 4 | 5 | SELECT date, count(date), SUM(count(date)) OVER (ORDER BY date)::integer as total 6 | FROM cte 7 | GROUP BY date; 8 | -------------------------------------------------------------------------------- /kyu5/sql/src/simple_view.sql: -------------------------------------------------------------------------------- 1 | -- Solution to codewars kata https://www.codewars.com/kata/sql-basics-simple-view/sql 2 | 3 | CREATE VIEW members_approved_for_voucher AS 4 | SELECT m.id, m.name, m.email, SUM(p.price) as total_spending 5 | FROM members m, sales s, products p 6 | WHERE m.id = s.member_id 7 | AND s.product_id = p.id 8 | AND s.department_id IN ( 9 | SELECT s.department_id 10 | FROM products p, sales s 11 | WHERE s.product_id = p.id 12 | GROUP BY s.department_id 13 | HAVING SUM(p.price) > 10000 14 | ) 15 | GROUP BY m.id 16 | HAVING SUM(p.price) > 1000 17 | ORDER BY m.id; 18 | 19 | SELECT * from members_approved_for_voucher; 20 | -------------------------------------------------------------------------------- /kyu5/sql/src/sqlonacci.sql: -------------------------------------------------------------------------------- 1 | --Solution for https://www.codewars.com/kata/sqlonacci-sequence/ 2 | 3 | WITH RECURSIVE fib(number,n2) AS ( 4 | SELECT 0::bigint,1::bigint 5 | UNION ALL 6 | SELECT n2::bigint,number+n2::bigint 7 | FROM fib 8 | ) 9 | SELECT number FROM fib LIMIT 90 10 | -------------------------------------------------------------------------------- /kyu5/sql/src/time_clocked.sql: -------------------------------------------------------------------------------- 1 | --- Solution https://www.codewars.com/kata/sql-data-company-data-totals-per-day/ 2 | -- simple solution assuming company doesn't have shift going into next day 3 | select 4 | sum(date_part('hour', age(t.logout, t.login))) as total_hours, 5 | d.name as department_name, 6 | t.login::date as day 7 | from timesheet t 8 | join department d 9 | on t.department_id = d.id 10 | group by 1, 2 11 | order by 1, 2 12 | -------------------------------------------------------------------------------- /kyu5/sql/src/top_n_per_group_window.sql: -------------------------------------------------------------------------------- 1 | --- Solution for example database dvdrental 2 | WITH film_categories AS (select f.title, c.name, count(r.rental_id) as cnt, 3 | dense_rank() OVER (PARTITION BY c.name ORDER BY count(r.rental_id) DESC) as rnk 4 | from film_category fc 5 | JOIN category c ON fc.category_id = c.category_id 6 | JOIN film f ON fc.film_id = f.film_id 7 | JOIN inventory i ON f.film_id = i.film_id 8 | JOIN rental r ON i.inventory_id = r.inventory_id 9 | GROUP BY f.title, c.name) 10 | 11 | SELECT * from film_categories where rnk <= 2 12 | ORDER BY cnt DESC; 13 | 14 | --- Solution for https://www.codewars.com/kata/using-window-functions-to-get-top-n-per-group 15 | WITH posts_per_category AS (select c.id as category_id, c.category, p.title, 16 | p.views as views, p.id as post_id, 17 | rank() OVER (PARTITION BY p.category_id ORDER BY p.views DESC, p.id) as rnk 18 | from categories c 19 | LEFT JOIN posts p ON c.id = p.category_id 20 | GROUP BY c.id, c.category, p.title, p.id) 21 | 22 | SELECT category_id, category, title, views, post_id 23 | FROM posts_per_category 24 | WHERE rnk <= 2 25 | ORDER BY category, views DESC, post_id; 26 | -------------------------------------------------------------------------------- /kyu5/sql/src/weekdays_function.sql: -------------------------------------------------------------------------------- 1 | -- Submitted solution for https://www.codewars.com/kata/count-weekdays/ 2 | CREATE OR REPLACE FUNCTION weekdays (date, date) RETURNS Integer 3 | AS 'WITH weekdays AS ( 4 | SELECT count(*) 5 | FROM generate_series(0, ($1::date - $2::date::date)) i 6 | WHERE date_part(''dow'', $2::date::date + i) NOT IN (0,6) 7 | 8 | UNION 9 | 10 | SELECT count(*) 11 | FROM generate_series(0, ($2::date - $1::date::date)) i 12 | WHERE date_part(''dow'', $1::date::date + i) NOT IN (0,6)) 13 | 14 | SELECT max(count)::int from weekdays;' 15 | LANGUAGE SQL 16 | IMMUTABLE; 17 | 18 | --Solution doing manual variable switching 19 | CREATE OR REPLACE FUNCTION weekdays (date, date) RETURNS INTEGER AS $$ 20 | DECLARE 21 | d1 date; 22 | d2 date; 23 | days int; 24 | BEGIN 25 | IF $1 > $2 THEN 26 | d1 := $1; 27 | d2 := $2; 28 | ELSE 29 | d1 := $2; 30 | d2 := $1; 31 | END IF; 32 | 33 | RETURN SELECT count(weekdays) 34 | FROM generate_series(0, (d1::date - d2::date::date)) i 35 | WHERE date_part('dow', d2::date::date + i) NOT IN (0,6); 36 | END; 37 | $$ 38 | LANGUAGE plpgsql; 39 | 40 | --better solution using least/greatest and extract instead of date_part 41 | CREATE FUNCTION weekdays(DATE, DATE) 42 | RETURNS INTEGER 43 | LANGUAGE sql AS 44 | $$ 45 | SELECT COUNT(days)::int 46 | FROM generate_series(LEAST($1, $2), GREATEST($1, $2), '1 day') as days 47 | WHERE EXTRACT(DOW FROM days) NOT IN(0, 6); 48 | $$; 49 | -------------------------------------------------------------------------------- /kyu7/Python/src/sort_arr_by_idx_val.py: -------------------------------------------------------------------------------- 1 | """Solution for https://www.codewars.com/kata/sort-an-array-by-value-and-index/""" 2 | 3 | from operator import itemgetter 4 | 5 | def sort_by_value_and_index(arr): 6 | return [n[0] for n in sorted([(n, (idx + 1) * n) for idx, n in enumerate(arr)], key=itemgetter(1))] 7 | 8 | # better way: 9 | def sort_by_value_and_index(arr): 10 | return [y[1] for y in sorted(enumerate(arr), key=lambda x:(x[0] + 1) * x[1])] 11 | 12 | # nice use of itertools 13 | import itertools 14 | def sort_by_value_and_index(nums): 15 | seq = itertools.count(1) 16 | return sorted(nums, key=lambda num:num*next(seq)) 17 | -------------------------------------------------------------------------------- /kyu7/dotnet/C#/src/find_sum.cs: -------------------------------------------------------------------------------- 1 | // Solution for kata https://www.codewars.com/kata/sum-of-all-the-multiples-of-3-or-5/ 2 | 3 | { 4 | public static class Program 5 | { 6 | public static int findSum(int n) 7 | { 8 | int sum = 0; 9 | for (int counter = 0; counter <= n; counter++) 10 | { 11 | sum = ((counter % 3 == 0) || (counter % 5 == 0)) ? sum += counter : sum; 12 | } 13 | return sum; 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /kyu7/dotnet/C#/src/get_rankings.cs: -------------------------------------------------------------------------------- 1 | // Solution for kata https://www.codewars.com/kata/climbers-rankings/ 2 | // Comment: the return type for this kata is set to be a dict when it should really be 3 | // an array as order is not guaranteed for a dict, OrderedDictionary in C# only sorts by value 4 | // which isn't what is required here. 5 | 6 | using System; 7 | using System.Collections.Generic; 8 | using System.Linq; 9 | 10 | namespace ClimbersRanking 11 | { 12 | public static class RankCalculator 13 | { 14 | public static Dictionary GetRankings(Dictionary> pointsByClimber) 15 | { 16 | var d = new Dictionary(); 17 | foreach (KeyValuePair> entry in pointsByClimber) 18 | { 19 | List all_points = new List(); 20 | all_points = entry.Value.ToList(); 21 | all_points.Sort((a, b) => -1 * a.CompareTo(b)); 22 | int points = 0; 23 | int end = all_points.Count() >= 6 ? 6 : all_points.Count(); 24 | for (int idx = 0; idx < end; idx++) 25 | { 26 | points += all_points[idx]; 27 | } 28 | d.Add(entry.Key, points); 29 | } 30 | var out_list = d.ToList(); 31 | out_list.Sort((x, y) => y.Value.CompareTo(x.Value)); 32 | d = out_list.ToDictionary(pair => pair.Key, pair => pair.Value); 33 | return d; 34 | } 35 | } 36 | } 37 | 38 | // more concise way: 39 | public static class RankCalculator 40 | { 41 | public static Dictionary GetRankings(Dictionary> pointsByClimber) 42 | { 43 | return pointsByClimber 44 | .ToDictionary(x => x.Key, x => x.Value.OrderByDescending(y => y).Take(6).Sum()) 45 | .OrderByDescending(x => x.Value) 46 | .ToDictionary(x => x.Key, x => x.Value); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /kyu7/dotnet/C#/src/product_of_arr_items.cs: -------------------------------------------------------------------------------- 1 | // Solution for kata https://www.codewars.com/kata/product-of-array-items/ 2 | 3 | public static int Product(int[] values) 4 | { 5 | return values.Aggregate((a, b) => a * b); 6 | } 7 | -------------------------------------------------------------------------------- /kyu7/dotnet/C#/src/sort_arr_by_idx_val.cs: -------------------------------------------------------------------------------- 1 | // Solution for kata https://www.codewars.com/kata/sort-an-array-by-value-and-index/ 2 | 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | 7 | public class Kata 8 | { 9 | public static int[] SortByValueAndIndex(int[] array) 10 | { 11 | arr.ToList().ForEach(Console.WriteLine); 12 | int i = 1; 13 | var tuple_lst = new List>(); 14 | foreach (int n in array) 15 | { 16 | string t_name = n.ToString(); 17 | var tuple = new Tuple(t_name, n, i * n); 18 | tuple_lst.Add(tuple); 19 | i++; 20 | } 21 | tuple_lst.Sort((x, y) => x.Item3 - y.Item3); 22 | var ret_lst = new List(); 23 | 24 | foreach (var n in tuple_lst) 25 | { 26 | ret_lst.Add(n.Item2); 27 | } 28 | return ret_lst.ToArray(); 29 | } 30 | } 31 | 32 | // highest voted 33 | public static int[] SortByValueAndIndex(int[] array) 34 | { 35 | int i = 1; 36 | return array.OrderBy(n => n * i++).ToArray(); 37 | } 38 | 39 | // using a for loop and index 40 | 41 | public static int[] SortByValueAndIndex(int[] array) 42 | { 43 | int[] keys = new int[array.Length]; 44 | for (int i = 0; i < array.Length; i++) keys[i] = array[i] * (i + 1); 45 | Array.Sort(keys, array); 46 | return array; 47 | } 48 | 49 | // using tuples and Linq 50 | 51 | public static int[] SortByValueAndIndex(int[] array) 52 | { 53 | var tuples = array.Select((t, i) => new Tuple(t, (i + 1) * t)).ToList(); 54 | return tuples.OrderBy(x => x.Item2).ToList().Select(x => x.Item1).ToArray(); 55 | } 56 | --------------------------------------------------------------------------------