diff options
| author | Mohammad S Anwar <Mohammad.Anwar@yahoo.com> | 2019-12-16 00:35:59 +0000 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2019-12-16 00:35:59 +0000 |
| commit | 5ce75caf5ded27ab908b0c3209b9c972ed8a279c (patch) | |
| tree | 36a86889b4553d8f743b9f91a5368fcbd7c9f81b | |
| parent | 41db04c710820d98653eb179a2fef27066c9cafb (diff) | |
| parent | 1d360299a966c180d39c21151e0a52a97f59c42f (diff) | |
| download | perlweeklychallenge-club-5ce75caf5ded27ab908b0c3209b9c972ed8a279c.tar.gz perlweeklychallenge-club-5ce75caf5ded27ab908b0c3209b9c972ed8a279c.tar.bz2 perlweeklychallenge-club-5ce75caf5ded27ab908b0c3209b9c972ed8a279c.zip | |
Merge pull request #1041 from dcw803/master
imported my solutions for challenge 38
| -rw-r--r-- | challenge-038/duncan-c-white/README | 82 | ||||
| -rwxr-xr-x | challenge-038/duncan-c-white/perl5/ch-1.pl | 79 | ||||
| -rwxr-xr-x | challenge-038/duncan-c-white/perl5/ch-2.pl | 188 |
3 files changed, 304 insertions, 45 deletions
diff --git a/challenge-038/duncan-c-white/README b/challenge-038/duncan-c-white/README index c71e2a54c9..bbaa601b99 100644 --- a/challenge-038/duncan-c-white/README +++ b/challenge-038/duncan-c-white/README @@ -1,59 +1,51 @@ -Challenge 1: "Write a script to calculate the total number of weekdays (Mon-Fri) in each month of the year 2019. -Jan: 23 days -Feb: 20 days -Mar: 21 days -Apr: 22 days -May: 23 days -Jun: 20 days -Jul: 23 days -Aug: 22 days -Sep: 21 days -Oct: 23 days -Nov: 21 days -Dec: 22 days +Challenge 1: "Date Finder + +Create a script to accept a 7 digits number, where the first +number can only be 1 or 2. The second and third digits can be +anything 0-9. The fourth and fifth digits corresponds to the month +i.e. 01,02,03....11,12. And the last 2 digits respresents the days in +the month i.e. 01,02,03....29,30,31. Your script should validate if +the given number is valid as per the rule and then convert into human +readable format date. + +RULES + +1) If 1st digit is 1, then prepend 20 otherwise 19 to the 2nd and 3rd + digits to make it 4-digits year. + +2) The 4th and 5th digits together should be a valid month. + +3) The 6th and 7th digits together should be a valid day for the above month. + +For example, the given number is 2230120, it should print 1923-01-20. " My notes: sounds rather straightforward, with or without date manipulation -modules. Might even have a crack at this in an unconventional language -as well as Perl - how about Adobe Postscript? +modules. Reuse the "number of days in the month" code. -Afternotes: I did 3 versions of this: -ch-1-Date-Manip.pl: first version uses Date::Manip's Date_DaysInMonth(m,y) and - Date_DayOfWeek(m,d,y) functions. +Challenge 2: "Word Game -ch-1.pl: second version does it ourselves from scratch. +Lets assume we have tiles as listed below, with an alphabet (A..Z) +printed on them. Each tile has a value, e.g. A (1 point), B (4 points) +etc. You are allowed to draw 7 tiles from the lot randomly. Then try +to form a word using the 7 tiles with maximum points altogether. You +don't have to use all the 7 tiles to make a word. You should try to +use as many tiles as possible to get the maximum points. -ch-1-in-postscript.ps: translated ch-1.pl into Postscript. Yes, the language - mostly used for printer page layout, can be used as a - full-blown programming language. However, you have to - include functions to append strings and produce a - variety of debugging messages down the page, before - you start on the actual program logic. +For example, A (x8) means there are 8 tiles with letter A. +1 point: A (x8), G (x3), I (x5), S (x7), U (x5), X (x2), Z (x5) -Challenge 2: "Write a script to find out the DayLight gain/loss in -the month of December 2019 as compared to November 2019 in the city of -London. You can find out London sunrise and sunset data for November 2019 here: -https://www.timeanddate.com/sun/uk/london?month=11&year=2019 +2 points: E (x9), J (x3), L (x3), R (x3), V (x3), Y (x5) -and for December 2019 here: -https://www.timeanddate.com/sun/uk/london?month=12&year=2019 -" +3 points: F (x3), D (x3), P (x5), W (x5) -My notes: most of this problem is fetching the web pages and parsing the -information out of the first table in each page: that's a typical -HTML::Parser state machine, easy enough. But once we have that information, -eg. for each month a day->daylight duration mapping, what exactly does the -question mean us to do? +4 points: B (x5), N (x4) -I think this problem means +5 points: T (x5), O (x3), H (x3), M (x4), C (x4) -"calculate the difference between the amount of daylight on 1st Nov and on -30th Nov, do the same for December (1st and 31st), and find out which -"within month daylight duration" is smaller." +10 points: K (x2), Q (x2) +" -btw, if I'm right the smaller duration is obviously between 1st-31st Dec -than between 1st-30th Nov, because in November the days are getting shorter -throughout the whole month, whereas in December they get shorter from -1st-21st Dec, and then get longer again! +My notes: So not scrabble then:-) diff --git a/challenge-038/duncan-c-white/perl5/ch-1.pl b/challenge-038/duncan-c-white/perl5/ch-1.pl new file mode 100755 index 0000000000..4464df186d --- /dev/null +++ b/challenge-038/duncan-c-white/perl5/ch-1.pl @@ -0,0 +1,79 @@ +#!/usr/bin/perl +# +# Challenge 1: "Date Finder +# +# Create a script to accept a 7 digits number, where the first +# number can only be 1 or 2. The second and third digits can be +# anything 0-9. The fourth and fifth digits corresponds to the month +# i.e. 01,02,03....11,12. And the last 2 digits respresents the days in +# the month i.e. 01,02,03....29,30,31. Your script should validate if +# the given number is valid as per the rule and then convert into human +# readable format date. +# +# RULES +# +# 1) If 1st digit is 1, then prepend 20 otherwise 19 to the 2nd and 3rd +# digits to make it 4-digits year. +# +# 2) The 4th and 5th digits together should be a valid month. +# +# 3) The 6th and 7th digits together should be a valid day for the above month. +# +# For example, the given number is 2230120, it should print 1923-01-20. +# " +# +# My notes: sounds rather straightforward, with or without date manipulation +# modules. Uses Date::Manip's Date_DaysInMonth(m,y) function. +# + +use v5.10; # to get "say" +use strict; +use warnings; +use Function::Parameters; +use Date::Manip; + + +die "Usage: ch-1.pl [CODED_DATE]\n" if @ARGV>1; +my $date = shift // "2230120"; + + +# +# my $isodate = validate( $date ); +# Validate the strangely coded date, return undef if it's +# not valid, or an ISO format date string if it's valid. +# +fun validate( $date ) +{ + # must be 7 digits number + return undef unless $date =~ /^(\d)(\d\d)(\d\d)(\d\d)$/; + my( $first, $y, $m, $d ) = ( $1, $2, $3, $4 ); + + # where the first number can only be 1 or 2. + return undef unless $first eq '1' || $first eq '2'; + + # The second and third digits can be anything 0-9. + # If 1st digit is 1, then prepend 20 otherwise 19 to the year + $y = ($first eq '1' ? 20 : 19).$y; + + # The fourth and fifth digits corresponds to the month i.e. 01-12. + return undef unless $m >= 1 && $m <= 12; + + my $ndays = Date_DaysInMonth( $m, $y ); + say "debug: y=$y, m=$m, ndays=$ndays"; + + # The last 2 digits respresents the days in the month i.e. 01-31. + return undef unless $d >= 1 && $d <= $ndays; + + # Valid: convert into human readable format date. + return "$y-$m-$d"; +} + + +my $isodate = validate( $date ); +if( defined $isodate ) +{ + say "$date: valid $isodate"; +} else +{ + say "$date: invalid"; +} diff --git a/challenge-038/duncan-c-white/perl5/ch-2.pl b/challenge-038/duncan-c-white/perl5/ch-2.pl new file mode 100755 index 0000000000..eae5076dd9 --- /dev/null +++ b/challenge-038/duncan-c-white/perl5/ch-2.pl @@ -0,0 +1,188 @@ +#!/usr/bin/perl +# +# Challenge 2: "Word Game +# +# Lets assume we have tiles as listed below, with an alphabet (A..Z) +# printed on them. Each tile has a value, e.g. A (1 point), B (4 points) +# etc. You are allowed to draw 7 tiles from the lot randomly. Then try +# to form a word using the 7 tiles with maximum points altogether. You +# don't have to use all the 7 tiles to make a word. You should try to +# use as many tiles as possible to get the maximum points. +# +# For example, A (x8) means there are 8 tiles with letter A. +# +# 1 point: A (x8), G (x3), I (x5), S (x7), U (x5), X (x2), Z (x5) +# 2 points: E (x9), J (x3), L (x3), R (x3), V (x3), Y (x5) +# 3 points: F (x3), D (x3), P (x5), W (x5) +# 4 points: B (x5), N (x4) +# 5 points: T (x5), O (x3), H (x3), M (x4), C (x4) +# 10 points: K (x2), Q (x2) +# " +# +# My notes: Sounds rather like Scrabble without the board. +# + +use v5.10; # to get "say" +use strict; +use warnings; +use Function::Parameters; +use Data::Dumper; + +my %tilebag = ( + A => 8, G => 3, I => 5, S => 7, + U => 5, X => 2, Z => 5, E => 9, + J => 3, L => 3, R => 3, V => 3, + Y => 5, F => 3, D => 3, P => 5, + W => 5, B => 5, N => 4, T => 5, + O => 3, H => 3, M => 4, C => 4, + K => 2, Q => 2, +); + + +my $debug=0; + +#srand(1); + +# build alltiles, a weighted list of tiles to take at random +my $alltiles= join( '', map { $_ x $tilebag{$_} } sort keys %tilebag ); +#print "alltiles $alltiles\n"; + + +my %value = ( + # 1 point: + A => 1, G => 1, I => 1, S => 1, U => 1, X => 1, Z => 1, + + # 2 points: + E => 2, J => 2, L => 2, R => 2, V => 2, Y => 2, + + # 3 points: + F => 3, D => 3, P => 3, W => 3, + + # 4 points: + B => 4, N => 4, + + # 5 points: + T => 5, O => 5, H => 5, M => 5, C => 5, + + # 10 points: + K => 10, Q => 10, +); + +#die Dumper \%value; + + +# +# my $hand = randomtiles( $n ); +# Select $n random tiles from $alltiles. Return +# an $n-letter sorted sequence of those random tiles. +# +fun randomtiles( $n ) +{ + my $at = $alltiles; + my @result; + foreach my $i (1..$n) + { + my $pos = int(rand(length($at))); + push @result, substr( $at, $pos, 1 ); + substr( $at, $pos, 1 ) = ''; + } + return join( '', sort @result ); +} + + +# +# my @words = readdict( $filename ); +# Read the given wordlist $filename. Return the list of words. +# +fun readdict( $filename ) +{ + open( my $fh, '<', $filename ) || die; + my @result; + while( <$fh> ) + { + chomp; + next unless /^[A-Za-z]+$/; + $_ =~ tr/a-z/A-Z/; + push @result, $_; + } + close( $fh ); + return @result; +} + + +# +# my $score = score( $word ); +# Score the letters of $word using the %value. Return the score. +# +fun score( $word ) +{ + my $score = 0; + foreach my $letter (split(//,$word)) + { + $score += $value{$letter}; + } + return $score; +} + + +my @words = grep { length($_)<8 } readdict( '/usr/share/dict/words' ); +my %isword = map { $_ => 1 } @words; +#die Dumper \%isword; + +# find highest scoring word that is a sub-bag of $hand. +my $highscore=0; +my $highscore_word; + + +# +# findall( $prefix, $hand ); +# Find all sub-words of $hand (starting with $prefix) +# that are dictionary words, score each one, and find +# the highest scored word. +# +fun findall( $prefix, $hand ) +{ + #say "debug: prefix=$prefix, hand=$hand"; + my $l = length($hand); + foreach my $pos (0..$l-1) + { + my $letter = substr($hand,$pos,1); + my $w = $prefix.$letter; + if( $isword{$w} ) + { + my $score = score( $w ); + #say "debug: w=$w, score=$score"; + if( $score > $highscore ) + { + $highscore = $score; + $highscore_word = $w; + } + } + my $rest = $hand; + substr($rest,$pos,1) = ''; + findall( $w, $rest ); + } +} + + +die "Usage: ch-2.pl [NROUNDS]\n" if @ARGV>1; +my $nrounds = shift // 1; + +my $overall_highscore=0; +my $overall_highscore_word; +my $overall_highscore_hand; +foreach (1..$nrounds) +{ + $highscore=0; + my $hand = randomtiles( 7 ); + #my $score = score( $hand ); + findall( "", $hand ); + say "hand:$hand, highscore:$highscore, word:$highscore_word" if $debug; + if( $highscore > $overall_highscore ) + { + $overall_highscore = $highscore; + $overall_highscore_word = $highscore_word; + $overall_highscore_hand = $hand; + } +} +say "overall: hand:$overall_highscore_hand, highscore:$overall_highscore, word:$overall_highscore_word"; |
