This is my response to The Weekly Challenge #376.
8 W B W B W B W B
7 B W B W B W B W
6 W B W B W B W B
5 B W B W B W B W
4 W B W B W B W B
3 B W B W B W B W
2 W B W B W B W B
1 B W B W B W B W
a b c d e f g h
Example 1:
Input: $c1 = "a7", $c2 = "f4"
Output: true
Example 2:
Input: $c1 = "c1", $c2 = "e8"
Output: false
Example 3:
Input: $c1 = "b5", $c2 = "h2"
Output: false
Example 4:
Input: $c1 = "f3", $c2 = "h1"
Output: true
Example 5:
Input: $c1 = "a1", $c2 = "g8"
Output: false
#! /usr/bin/env raku
subset CHESSPOS where /^ <[a..h]> <[1..8]> $/;
unit sub MAIN (CHESSPOS $c1, CHESSPOS $c2, :v(:$verbose));
my $colour_c1 = colour($c1);
my $colour_c2 = colour($c2);
say ":C1: $colour_c1\n:C2: $colour_c2" if $verbose;
say $colour_c1 eq $colour_c2;
sub colour (CHESSPOS $pos)
{
my ($col, $row) = $pos.comb;
my $magic = ($col.ord - 'a'.ord + $row) % 2;
return $magic ?? "B" !! "W";
}
[3] A custom type (with subset) to ensure a legal position on the board;
a letter a-h followed by a digit 1-8.
See docs.raku.org/language/typesystem#subset for more information about subset.
[5] Apply that custom type to the two positions.
[7,8] Get the colour of the two positions.
[12] Are the two colours the same?
[14] Procedure getting the colour.
[16] Extract the two characters in the position.
[18] Reduse the column letter to an integer from 0 and up (by subtracting 'a'.ord
from the ascii order of the letter itself - in case the character set is not ascii
(and unicode) compatible). It should not really matter for the result, as both colours
would be wrong in that case - giving a correct comparison result.
[20] Modulo 2 (in [18]) told us if the number is even or odd. Then return the correct colour.
Running it:
$ ./chessboard-squares a7 f4
True
$ ./chessboard-squares c1 e8
False
$ ./chessboard-squares b5 h2
False
$ ./chessboard-squares f3 h1
True
$ ./chessboard-squares a1 g8
False
Looking good.
With verbose mode:
$ ./chessboard-squares -v a7 f4
:C1: B
:C2: B
True
$ ./chessboard-squares -v c1 e8
:C1: B
:C2: W
False
$ ./chessboard-squares -v b5 h2
:C1: W
:C2: B
False
$ ./chessboard-squares -v f3 h1
:C1: W
:C2: W
True
$ ./chessboard-squares -v a1 g8
:C1: B
:C2: W
False
Input: $str = "you're given the job of checking the pages on a\nweb server for doubled words (such as 'this this'), a common problem\nwith documents subject to heavy editing."
Output: "web server for doubled words (such as '[this] [this]'), a common problem"
Example 2:
Input: $str = "Find doubled words despite capitalization differences, such as with 'The\nthe...', as well as allow differing amounts of whitespace (spaces,\ntabs, newlines, and the like) to lie between the words."
Output: "Find doubled words despite capitalization differences, such as with '[The]\n[the]...', as well as allow differing amounts of whitespace (spaces,"
Example 3:
Input: $str = "to make a word bold: '...it is <B>very</B> very important...'."
Output: "to make a word bold: '...it is <B>[very]</B> [very] important...'."
Example 4:
Input: $str = "Perl officially stands for Practical Extraction and Report Language, except when it doesn't."
Output: ""
Example 5:
Input: $str = "There's more than one one way to do it.\nEasy things should be easy and hard things should be possible."
Output: "There's more than [one] [one] way to do it."
The «look ahead» implied by the first bullet point («Work across lines») is problematic. It is better to do a «look behind», i.e. to change the previous line when we get to the repeated word on the next line.
The trick is to store each line on a parsed format that makes it possible to change the doubleness afterwards (i.e. when we parse the next line). That parsed format is actually a class, and it takes care of punctuation, html markup, case insensitivity, and constructing the printable output.
File: doubled-words#! /usr/bin/env raku
unit sub MAIN ($str is copy, :v(:$verbose));
class mushroom
{
has $.word is required;
has $.pre = "";
has $.post = "";
has $.doubled is rw = False;
method insensitive
{
return $.word.lc;
}
method double
{
$.doubled = True;
}
method Str
{
my $output = "";
$output ~= $.pre if $.pre;
$output ~= "[" if $.doubled;
$output ~= $.word;
$output ~= "]" if $.doubled;
$output ~= $.post if $.post;
return $output;
}
}
$str ~~ s:g/\\n/\n/;
my @lines = $str.lines;
my $last_line_token;
my @output-rows;
for @lines -> $line
{
say ": Line: $line" if $verbose;
my @chunks = $line.words;
my @a;
my $t;
for @chunks -> $chunk is copy
{
my $pre = "";
my $post = "";
while $chunk ~~ /^ (<['"(]>) (.*) / ## ' ##
{
$pre ~= $0.Str; $chunk = $1.Str;
}
while $chunk ~~ /^ ("<" <[a..z A..Z]> ">") (.*) /
{
$pre ~= $0.Str; $chunk = $1.Str;
}
while $chunk ~~ / (.*) (<[,.;:!)'"]>) $/ ## ' ##
{
$post = $1.Str ~ $post; $chunk = $0.Str;
}
while $chunk ~~ / (.*) ("<" "/" <[a..z A..Z]> ">") $/
{
$post = $1.Str ~ $post; $chunk = $0.Str;
}
$t = mushroom.new( word => $chunk,
pre => $pre,
post => $post );
if @a.elems && @a[*-1].insensitive eq $t.insensitive
{
@a[*-1].double;
$t.double;
}
@a.push: $t;
}
if $last_line_token && $last_line_token eq @a[0].insensitive
{
say ": - doubled the last chunk on the previous line"
if $verbose;
@a[0].double;
@output-rows[*-1].[*-1].double;
}
$last_line_token = $t.insensitive;
say ": Content: { @a.raku }" if $verbose;
@output-rows.push: @a;
}
for @output-rows -> @row
{
say @row.join(" ") if any @row>>.doubled;
}
[3] The is copy is there so that we can change the value (in [34]).
[5] The class used to store each chunk, a word with punctuation and/or html tag(s).
[7] The text (or word) is mandatory.
[8,9] The pre and post parts (punctuation and/or html tag(s)) are optional.
[10] Is the current word doubled? The default is "no", and note the is rw so that
we can change the value after object construction.
[12] This method return a case insensitive version of the word, for easier comparison.
I have chosen lowercase (lc), but note that upper and lowercasing does not always
round trip:
> say "Straße".lc; # -> straße
> say "Straße".uc; # -> STRASSE
> say "STRASSE".lc; # -> strasse
[17] Use this method to mark a word for doubleness, after the fact.
[22] This method is used when we stringify the object. It will mark a doubled word in brackets, and reattach the original punctuation and/or html tags.
[34] The string \n" on the command line is not a newline, but the
two characters \ and n. So we replace them with an actual newline
with the substitution operator s///.
See docs.raku.org/syntax/s/ / /
for mor information about the substitution operator s///.
[36] Split the input into lines.
[38] The last input token, used on a new line to check what word we ended the previous one with.
[40] The output will be built up from this array of arrays.
[42] Iterate over the lines.
[46] Split the line into words (with words), or chunks as I have
called them - as they can contain more than just the word.
[47] The chunks for the row will end up here.
[48] The chunk object, with a funny name.
[50] Iterate over the chunks. Note the is copy as we update the value itself
(in e.g. [57]).
[52,53] The pre and post parts are initially empty.
[55] Extract some common non-letter prefixes, in a loop.
[60] Extract a starting html tag, in a loop.
[65] Extract some common non-letter postfixes, in a loop.
[70] Extract an ending html tag, in a loop.
[75] Construct the new mushroom object.
[79] If we have a previous word (in @a), and that word is the same as the current one,
mark the previous one [81] - and the current one [82] - as doubled.
[85] Add the new mushroom object to the line.
[88] If the last line token variable has been set, we have a previous line. If that token variable is the same as the first word on this line, we mark the first word on this line [93], and the last word on the last line in the output buffer [94] as doubled.
[97] Update the last line token.
[101] Add the newly parsed line to the output.
[104] Iterate over the rows.
[106] Print the row, but only if it has one or more doubled words. The Str method
(in [22]) takes care of the stringification
Running it:
$ ./doubled-words "you're given the job of checking the pages on a\nweb \
server for doubled words (such as 'this this'), a common problem\nwith \
documents subject to heavy editing."
web server for doubled words (such as '[this] [this]'), a common problem
$ ./doubled-words "Find doubled words despite capitalization differences, \
such as with 'The\nthe...', as well as allow differing amounts of \
whitespace (spaces,\ntabs, newlines, and the like) to lie between the words."
Find doubled words despite capitalization differences, such as with '[The]
[the]...', as well as allow differing amounts of whitespace (spaces,
$ ./doubled-words "to make a word bold: '...it is <B>very</B> very important...'."
to make a word bold: '...it is <B>[very]</B> [very] important...'.
$ ./doubled-words "Perl officially stands for Practical Extraction and \
Report Language, except when it doesn't."
$ ./doubled-words "There's more than one one way to do it.\nEasy things \
should be easy and hard things should be possible."
There's more than [one] [one] way to do it.
Looking good.
With verbose mode:
$ ./doubled-words -v "you're given the job of checking the pages on a\nweb \
server for doubled words (such as 'this this'), a common problem\nwith \
documents subject to heavy editing."
: Line: you're given the job of checking the pages on a
: Content: [
mushroom.new(text => "you're", pre => "", post => "", doubled => Bool::False),
mushroom.new(text => "given", pre => "", post => "", doubled => Bool::False),
mushroom.new(text => "the", pre => "", post => "", doubled => Bool::False),
mushroom.new(text => "job", pre => "", post => "", doubled => Bool::False),
mushroom.new(text => "of", pre => "", post => "", doubled => Bool::False),
mushroom.new(text => "checking", pre => "", post => "", doubled => Bool::False),
mushroom.new(text => "the", pre => "", post => "", doubled => Bool::False),
mushroom.new(text => "pages", pre => "", post => "", doubled => Bool::False),
mushroom.new(text => "on", pre => "", post => "", doubled => Bool::False),
mushroom.new(text => "a", pre => "", post => "", doubled => Bool::False)
]
: Line: web server for doubled words (such as 'this this'), a common problem
: Content: [
mushroom.new(text => "web", pre => "", post => "", doubled => Bool::False),
mushroom.new(text => "server", pre => "", post => "", doubled => Bool::False),
mushroom.new(text => "for", pre => "", post => "", doubled => Bool::False),
mushroom.new(text => "doubled", pre => "", post => "", doubled => Bool::False),
mushroom.new(text => "words", pre => "", post => "", doubled => Bool::False),
mushroom.new(text => "such", pre => "(", post => "", doubled => Bool::False),
mushroom.new(text => "as", pre => "", post => "", doubled => Bool::False),
mushroom.new(text => "this", pre => "'", post => "", doubled => Bool::True),
mushroom.new(text => "this", pre => "", post => "'),", doubled => Bool::True),
mushroom.new(text => "a", pre => "", post => "", doubled => Bool::False),
mushroom.new(text => "common", pre => "", post => "", doubled => Bool::False),
mushroom.new(text => "problem", pre => "", post => "", doubled => Bool::False)
]
: Line: with documents subject to heavy editing.
: Content: [
mushroom.new(text => "with", pre => "", post => "", doubled => Bool::False),
mushroom.new(text => "documents", pre => "", post => "", doubled => Bool::False),
mushroom.new(text => "subject", pre => "", post => "", doubled => Bool::False),
mushroom.new(text => "to", pre => "", post => "", doubled => Bool::False),
mushroom.new(text => "heavy", pre => "", post => "", doubled => Bool::False),
mushroom.new(text => "editing", pre => "", post => ".", doubled => Bool::False)]
web server for doubled words (such as '[this] [this]'), a common problem
$ ./doubled-words -v "Find doubled words despite capitalization differences, \
such as with 'The\nthe...', as well as allow differing amounts of \
whitespace (spaces,\ntabs, newlines, and the like) to lie between the words."
: Line: Find doubled words despite capitalization differences, such as with 'The
: Content: [
mushroom.new(text => "Find", pre => "", post => "", doubled => Bool::False),
mushroom.new(text => "doubled", pre => "", post => "", doubled => Bool::False),
mushroom.new(text => "words", pre => "", post => "", doubled => Bool::False),
mushroom.new(text => "despite", pre => "", post => "", doubled => Bool::False),
mushroom.new(text => "capitalization", pre => "", post => "", doubled => Bool::False),
mushroom.new(text => "differences", pre => "", post => ",", doubled => Bool::False),
mushroom.new(text => "such", pre => "", post => "", doubled => Bool::False),
mushroom.new(text => "as", pre => "", post => "", doubled => Bool::False),
mushroom.new(text => "with", pre => "", post => "", doubled => Bool::False),
mushroom.new(text => "The", pre => "'", post => "", doubled => Bool::False)
]
: Line: the...', as well as allow differing amounts of whitespace (spaces,
: - doubled the last chunk on the previous line
: Content: [
mushroom.new(text => "the", pre => "", post => "...',", doubled => Bool::True),
mushroom.new(text => "as", pre => "", post => "", doubled => Bool::False),
mushroom.new(text => "well", pre => "", post => "", doubled => Bool::False),
mushroom.new(text => "as", pre => "", post => "", doubled => Bool::False),
mushroom.new(text => "allow", pre => "", post => "", doubled => Bool::False),
mushroom.new(text => "differing", pre => "", post => "", doubled => Bool::False),
mushroom.new(text => "amounts", pre => "", post => "", doubled => Bool::False),
mushroom.new(text => "of", pre => "", post => "", doubled => Bool::False),
mushroom.new(text => "whitespace", pre => "", post => "", doubled => Bool::False),
mushroom.new(text => "spaces", pre => "(", post => ",", doubled => Bool::False)
]
: Line: tabs, newlines, and the like) to lie between the words.
: Content: [
mushroom.new(text => "tabs", pre => "", post => ",", doubled => Bool::False),
mushroom.new(text => "newlines", pre => "", post => ",", doubled => Bool::False),
mushroom.new(text => "and", pre => "", post => "", doubled => Bool::False),
mushroom.new(text => "the", pre => "", post => "", doubled => Bool::False),
mushroom.new(text => "like", pre => "", post => ")", doubled => Bool::False),
mushroom.new(text => "to", pre => "", post => "", doubled => Bool::False),
mushroom.new(text => "lie", pre => "", post => "", doubled => Bool::False),
mushroom.new(text => "between", pre => "", post => "", doubled => Bool::False),
mushroom.new(text => "the", pre => "", post => "", doubled => Bool::False),
mushroom.new(text => "words", pre => "", post => ".", doubled => Bool::False)
]
Find doubled words despite capitalization differences, such as with '[The]
[the]...', as well as allow differing amounts of whitespace (spaces,
$ ./doubled-words -v "to make a word bold: '...it is <B>very</B> very important...'."
: Line: to make a word bold: '...it is <B>very</B> very important...'.
: Content: [
mushroom.new(text => "to", pre => "", post => "", doubled => Bool::False),
mushroom.new(text => "make", pre => "", post => "", doubled => Bool::False),
mushroom.new(text => "a", pre => "", post => "", doubled => Bool::False),
mushroom.new(text => "word", pre => "", post => "", doubled => Bool::False),
mushroom.new(text => "bold", pre => "", post => ":", doubled => Bool::False),
mushroom.new(text => "...it", pre => "'", post => "", doubled => Bool::False),
mushroom.new(text => "is", pre => "", post => "", doubled => Bool::False),
mushroom.new(text => "very", pre => "<B>", post => "</B>", doubled => Bool::True),
mushroom.new(text => "very", pre => "", post => "", doubled => Bool::True),
mushroom.new(text => "important", pre => "", post => "...'.", doubled => Bool::False)
]
to make a word bold: '...it is <B>[very]</B> [very] important...'.
$ ./doubled-words -v "Perl officially stands for Practical Extraction and \
Report Language, except when it doesn't."
: Line: Perl officially stands for Practical Extraction and Report Language, \
except when it doesn't.
: Content: [
mushroom.new(text => "Perl", pre => "", post => "", doubled => Bool::False),
mushroom.new(text => "officially", pre => "", post => "", doubled => Bool::False),
mushroom.new(text => "stands", pre => "", post => "", doubled => Bool::False),
mushroom.new(text => "for", pre => "", post => "", doubled => Bool::False),
mushroom.new(text => "Practical", pre => "", post => "", doubled => Bool::False),
mushroom.new(text => "Extraction", pre => "", post => "", doubled => Bool::False),
mushroom.new(text => "and", pre => "", post => "", doubled => Bool::False),
mushroom.new(text => "Report", pre => "", post => "", doubled => Bool::False),
mushroom.new(text => "Language", pre => "", post => ",", doubled => Bool::False),
mushroom.new(text => "except", pre => "", post => "", doubled => Bool::False),
mushroom.new(text => "when", pre => "", post => "", doubled => Bool::False),
mushroom.new(text => "it", pre => "", post => "", doubled => Bool::False),
mushroom.new(text => "doesn't", pre => "", post => ".", doubled => Bool::False)
]
$ ./doubled-words -v "There's more than one one way to do it.\nEasy things \
should be easy and hard things should be possible."
: Line: There's more than one one way to do it.
: Content: [
mushroom.new(text => "There's", pre => "", post => "", doubled => Bool::False),
mushroom.new(text => "more", pre => "", post => "", doubled => Bool::False),
mushroom.new(text => "than", pre => "", post => "", doubled => Bool::False),
mushroom.new(text => "one", pre => "", post => "", doubled => Bool::True),
mushroom.new(text => "one", pre => "", post => "", doubled => Bool::True),
mushroom.new(text => "way", pre => "", post => "", doubled => Bool::False),
mushroom.new(text => "to", pre => "", post => "", doubled => Bool::False),
mushroom.new(text => "do", pre => "", post => "", doubled => Bool::False),
mushroom.new(text => "it", pre => "", post => ".", doubled => Bool::False)
]
: Line: Easy things should be easy and hard things should be possible.
: Content: [
mushroom.new(text => "Easy", pre => "", post => "", doubled => Bool::False),
mushroom.new(text => "things", pre => "", post => "", doubled => Bool::False),
mushroom.new(text => "should", pre => "", post => "", doubled => Bool::False),
mushroom.new(text => "be", pre => "", post => "", doubled => Bool::False),
mushroom.new(text => "easy", pre => "", post => "", doubled => Bool::False),
mushroom.new(text => "and", pre => "", post => "", doubled => Bool::False),
mushroom.new(text => "hard", pre => "", post => "", doubled => Bool::False),
mushroom.new(text => "things", pre => "", post => "", doubled => Bool::False),
mushroom.new(text => "should", pre => "", post => "", doubled => Bool::False),
mushroom.new(text => "be", pre => "", post => "", doubled => Bool::False),
mushroom.new(text => "possible", pre => "", post => ".", doubled => Bool::False)
]
There's more than [one] [one] way to do it.
I have added the newlines (and indentation) in the mushroom-entries to make them more readable. The program outputs everything on one line.
It is possible to construct examples that are not handled correctly, as the two
first while loops should have been merged, as should the third and fourth.
$ ./doubled-words -v "<b>This.</b> this is not."
: Line: <b>This.</b> this is not.
: Content: [
mushroom.new(word => "This.", pre => "<b>", post => "</b>", doubled => Bool::False),
mushroom.new(word => "this", pre => "", post => "", doubled => Bool::False),
mushroom.new(word => "is", pre => "", post => "", doubled => Bool::False),
mushroom.new(word => "not", pre => "", post => ".", doubled => Bool::False)
]
I have highlighted the problem in red.
This may not be entirely correct:
$ ./doubled-words "this this this this this"
[this] [this] [this] [this] [this]
But I'll let it be, as the five examples are satisfied.
And that's it.