diff options
| author | drbaggy <js5@sanger.ac.uk> | 2022-04-25 08:56:26 +0100 |
|---|---|---|
| committer | drbaggy <js5@sanger.ac.uk> | 2022-04-25 08:56:26 +0100 |
| commit | 30bb1d6595b66cc29338db192f04128dbbde5af8 (patch) | |
| tree | 7c3397c0c8de7cbb61d45eb91493107d503d5f15 | |
| parent | 5fd7b3b85e632fa3c9368f58d86d95f569dd70ca (diff) | |
| download | perlweeklychallenge-club-30bb1d6595b66cc29338db192f04128dbbde5af8.tar.gz perlweeklychallenge-club-30bb1d6595b66cc29338db192f04128dbbde5af8.tar.bz2 perlweeklychallenge-club-30bb1d6595b66cc29338db192f04128dbbde5af8.zip | |
pushing wtuff
| -rw-r--r-- | challenge-162/james-smith/README.md | 198 |
1 files changed, 34 insertions, 164 deletions
diff --git a/challenge-162/james-smith/README.md b/challenge-162/james-smith/README.md index bd2de9087b..106cb78373 100644 --- a/challenge-162/james-smith/README.md +++ b/challenge-162/james-smith/README.md @@ -1,6 +1,6 @@ -[< Previous 160](https://github.com/drbaggy/perlweeklychallenge-club/tree/master/challenge-160/james-smith) | -[Next 162 >](https://github.com/drbaggy/perlweeklychallenge-club/tree/master/challenge-162/james-smith) -# The Weekly Challenge 161 +[< Previous 161](https://github.com/drbaggy/perlweeklychallenge-club/tree/master/challenge-161/james-smith) | +[Next 163 >](https://github.com/drbaggy/perlweeklychallenge-club/tree/master/challenge-163/james-smith) +# The Weekly Challenge 162 You can find more information about this weeks, and previous weeks challenges at: @@ -12,183 +12,53 @@ submit solutions in whichever language you feel comfortable with. You can find the solutions here on github at: -https://github.com/drbaggy/perlweeklychallenge-club/tree/master/challenge-161/james-smith +https://github.com/drbaggy/perlweeklychallenge-club/tree/master/challenge-162/james-smith -# Challenge 1 - Abecedarian Words +# Challenge 1 - Validate ISBN-13 -***An abecedarian word is a word whose letters are arranged in alphabetical order. For example, “knotty” is an abecedarian word, but “knots” is not. Output or return a list of all abecedarian words in the dictionary, sorted in decreasing order of length. Optionally, using only abecedarian words, leave a short comment in your code to make your reviewer smile.*** +***Write a script to generate the check digit of given ISBN-13 code. Checksum is generated by summing the numbers in the odd positions with 3 times the sum of the numbers in the even positions. The checksum digit is the number you would add to get a total of 0*** ## The solution +Rather than computing the checksum - as we have ISBN numbers with the checksum we will validate the numbers {checking the checksum calculated is equal to the last digit in the ISNB number]. ```perl -open my $d, q(<), 'dictionary.txt'; - -my @abcde; - -O: while( my $f='', chomp(my $w =<$d>) ) { - $f gt $_ ? (next O) : ($f=$_) for split //, $w; - push @abcde, $w; +sub validate_isbn13 { + my( $s, @p ) = ( 0, grep {/\d/} split //, $_[0] ); + $s -= shift(@p) + 3*shift @p for 0..5; + $p[0] == $s%10; } - -$a=$_, say "$a: ", join q(, ), grep { $a == length $_ } @abcde for 1..6; -``` - -The list of words are: -``` -1: a, i, m, x -2: ad, ah, am, an, as, at, ax, be, by, cc, cs, do, eh, em, go, hi, ho, ii, in, is, it, - iv, ix, ms, mu, my, no, or, ox, qt, xx -3: ace, act, add, ado, ads, ago, ail, aim, air, all, amp, ant, any, apt, art, ass, bee, - beg, bet, bin, bit, boo, bop, bow, box, boy, buy, chi, coo, cop, cot, cow, cox, coy, - cry, den, dew, dim, din, dip, dos, dot, dry, eel, egg, ego, elm, err, fin, fir, fit, - fix, flu, fly, for, fox, fry, gin, gnu, goo, got, guy, him, hip, his, hit, hop, hot, - how, iii, ill, imp, inn, ins, ivy, jot, joy, lop, lot, low, moo, mop, mow, nor, not, - now, opt, pry, xxx -4: abet, ably, aces, adds, ahoy, ails, aims, airs, airy, ally, alms, amps, beef, been, - beer, bees, beet, begs, bell, belt, bent, best, bill, bins, blot, blow, boor, boos, - boot, boss, buzz, cell, cent, chin, chip, chop, chow, city, clot, coop, coos, cops, - copy, cost, crux, deem, deep, deer, deft, defy, dens, dent, deny, dill, dims, dins, - dips, dirt, door, eels, eggs, egos, elms, envy, errs, fill, film, fins, firs, fist, - fizz, flop, flow, flux, foot, fort, foxy, fuzz, gill, gilt, gins, gist, glow, gory, - hill, hilt, hims, hint, hips, hiss, hoop, hoot, hops, host, ills, imps, inns, knot, - know, loop, loot, lops, loss, lost, moor, moos, moot, mops, moss, most, nosy -5: abbey, abbot, abhor, abort, adept, adopt, affix, afoot, aglow, allot, allow, alloy, - annoy, beefs, beefy, beers, befit, begin, bells, belly, below, berry, bills, boors, - boost, booty, bossy, cello, cells, chill, chimp, chins, chips, chops, coops, deems, - deeps, deity, dills, dirty, ditty, doors, empty, fills, filly, films, filmy, first, - floor, flops, floss, forty, ghost, gills, glory, gloss, hills, hilly, hippy, hoops, - loops, lorry, moors, mossy -6: abhors, accent, accept, access, accost, almost, begins, bellow, billow, cellos, - chills, chilly, chimps, chintz, choosy, choppy, effort, floors, floppy, glossy, - knotty ``` -# Challenge 2 - Pangrams - -***A pangram is a sentence or phrase that uses every letter in the English alphabet at least once. For example, perhaps the most well known pangram is: "the quick brown fox jumps over the lazy dog"***. - -```perl -sub generate_a_pangram_random { - my ($list,$c,%letters,@pangram) = (shift,0,map { $_ => 0 } 'a'..'z'); - O: while( $c < 26 ) { - my($f,@l) = (1,split //, my $word = $list->[rand @{$list}]); - $f *= $letters{$_} foreach @l; - next if $f; - push @pangram, $word; - $letters{$_} || ( $letters{$_}=1, $c++ ) for @l; - } - @pangram; -} -``` +# Challenge 2 - Wheatstone-Playfair -## Bonus challenge #1 *"cabbed fad high jackal moping ventriloquist waxy fez"* +***Implement encryption and decryption using the Wheatstone-Playfair cipher.*** -I set myself the following challenge: choose a pangram where - for each word we add we can only add the -next consecutive letters in the alphabet. *e.g.* - * the first word could be `cab` as it contains `a`, `b`, and `c`; - * the second word could be `bead` as it contains `e` and `d`. -We then look for the shortest word which introduces the most letters. +I'm not going to try and explain this here - but refer you to wikipedia https://en.wikipedia.org/wiki/Playfair_cipher -``` -sub generate_a_pangram_alpha_order { - my ($list,$next,%letters,@pangram) = (shift,'a',map { $_ => 0 } 'a'..'z'); - O: until( 'aa' eq $next ) { ## If we get to "aa" we know that we have finished - my($best,$best_length,$best_word) = (0,0,''); - W: foreach my $word ( @{$list} ) { - my %t = map { $_=>1 } split //, $word; - my ($score,$ch) = (0,$next); - ($_ gt $ch) ? next W : ($score++,$ch++) for sort grep { $_ ge $next } keys %t; - ## Get a list of letters in alphabetical order which are greater that the ones - ## we already have in the pangram, and find out how many we have that are - ## consecutive as $next. If they aren't all consecutive or start with $next - ## we jump out of the loop and look at the next word - next if $score < $best; - if( $score > $best ) { - ($best,$best_word,$best_length) = ($score,$word,length $word); - ## Update best word {and reset associated "metadata") - } elsif( $best_length > length $word ) { - ($best_word,$best_length) = ($word,length $word); - ## score is equal to best update if shorter word - } - } - push @pangram, $best_word; - $next++ foreach 1..$best; ## Find next letter to add - } - @pangram; -} -``` - -### The solution *"cabbed fad high jackal moping ventriloquist waxy fez"* - -I like that the solution contains very few words - and a total of only 45 characters - the star being *ventriloquist*: - - | Word | Extra letters | Letters | - | ------------- | ------------- | ------: | - | cabbed | 5 - *abcde* | 6 | - | fad | 1 - *f* | 3 | - | high | 3 - *ghi* | 4 | - | jackal | 3 - *jkl* | 6 | - | moping | 4 - *mnop* | 6 | - | ventriloquist | 6 - *qrstuv* | 13 | - | waxy | 3 - *wxy* | 4 | - | fez | 1 - *fez* | 3 | - | TOTAL | | 45 | - -***Note:** This isn't the shortest pangram - e.g. you can replace **cabbed fad** with **cab fed** to reduce the length to 42 characters. **cab fed high jackal moping ventriloquist waxy fez**.* - -## Bonus challenge #2 *"a baa cc ad be fad age ah i jab eke ail m an do pa qi or as at mu iv we x by fez"* - -***Note:** to achieve this we have added 3 words to the dictionary "baa", "i" and "qi" which give a solution* - -This is a pangram which a word which includes the same letter as previous words plus the next letter in the alphabet. This is similar (but slightly simplified) version of the previous one: +First we note the only difference between encryption and decryption is the direction we move left->right up->down of *vv*, so we can implement these with a single "function" and two wrappers "emcrypt" and "decrypt". Often two way ciphers have this feature. +We first create a hash and array which map the letter to their position and their position to the letter. This makes the future calculations easier ```perl -sub generate_one_letter_at_time { - my ($list,$next,%letters,@pangram) = (shift,'a',map { $_ => 0 } 'a'..'z'); - O: until( 'aa' eq $next ) { - my($best,$best_length,$best_word) = (0,1e6,''); - W: foreach my $word ( @{$list} ) { - my %t = map { $_=>1 } split //, $word; - my @T = grep { $_ ge $next } keys %t; - ($best_word,$best_length) = ($word,length $word) if @T == 1 && $T[0] eq $next && $best_length > length $word; - } - push @pangram, $best_word; - $next++; +sub encrypt { return _crypt( 1,@_); } +sub decrypt { return _crypt(-1,@_); } + +sub _crypt { + my($off,$key,$p,$out,@r,%l) = (shift,shift,0,''); ## Initialise variables and get mapping... + ($_ eq 'j' && ($_='i')), exists $l{$_} || ($l{$_}=[int $p/5,($p++)%5]) for grep { /[a-z]/ } split(//,$key),'a'..'i','j'..'z'; + $r[$l{$_}[0]][$l{$_}[1]]=$_ for keys %l; + + my @seq = grep {/[a-z]/} split //, shift =~ s{j}{j}gr; ## Prep sequence + + while(my($m,$n)=splice @seq,0,2) { ## Loop through letter pairs + unshift(@seq,$n), $n='x' if $n && $n eq $m and $n ne 'x'; ## Deal with case when both letters the same + $n ||= 'x'; ## Pad if required... + $out.= $l{$m}[0] eq $l{$n}[0] ? $r[ $l{$m}[0] ][($l{$m}[1]+$off)%5] . $r[ $l{$n}[0] ][($l{$n}[1]+$off)%5] + : $l{$m}[1] eq $l{$n}[1] ? $r[($l{$m}[0]+$off)%5][ $l{$m}[1] ] . $r[($l{$n}[0]+$off)%5][ $l{$n}[1] ] + : $r[ $l{$m}[0] ][ $l{$n}[1] ] . $r[ $l{$n}[0] ][ $l{$m}[1] ] + ; } - @pangram; + $out; } ``` -If we change the inequality from "`>`" to "`<`" in the inner loop we get the longest word that can be made with only adding that letter - then we get the following: - -***a baa cab add acceded defaced baggage beheaded caddied jabbed feedback blackjacked blackmailed blackjacking blindfolding handicapping qi archaeological incomprehensible misrepresentations telecommunications oversimplification counterclockwise extraterrestrials characteristically institutionalizing*** - -## Bonus challenge #3 *"oversimplification ladybug hawk jazz equinox"* - -A different one now - choose the sentance by finding a word that is as short as possible but contains as many different letters as possible, and repeat for all 26 letters.... - -```perl -sub most_letters { - my ($list,$c,%letters,@pangram) = (shift,0,map{$_=>1}'a'..'z'); - while($c<26) { - my( $most, $length, $best )=(0,1000,''); - I: foreach my $word (@{$list}) { - my %t = map { $_ => 1 } grep { $letters{$_} } split //, $word; - if( $most < scalar keys %t ) { - ($length,$best,$most) = (length $word,$word,scalar keys %t); - } elsif( scalar keys %t == $most && $length > length $word ) { - $length = length ($best=$word); - } - } - $c+=$most; - $letters{$_}=0 foreach split//,$best; - push @pangram, $best; - } - @pangram; -} -``` - -This gives us *"**oversimpl**i**f**i**cat**io**n** la**dybug** **h**a**wk** **j**a**z**z e**q**uino**x**"* - -If we stick to the words from challenge 1 - we get *"abhors deity flux know chimp go iv qt jot buzz"*. |
