Weekly Challenge 376
Each week Mohammad S. Anwar sends out The Weekly Challenge, a chance for all of us to come up with solutions to two weekly tasks. My solutions are written in Python first, and then converted to Perl. Unless otherwise stated, Copilot (and other AI tools) have NOT been used to generate the solution. It's a great way for us all to practice some coding.
Task 1: Chessboard Squares
Task
You are given two coordinates of a square on 8x8 chessboard.
Write a script to find the given two coordinates have the same colour.
My solution
This - like most first tasks - is relatively straight forward. I have a function called is_black and it takes a cell variable as a parameter.
After checking the square is valid (first letter a-g, second number 1-8), it then uses the exclusive xor operator ^ to determine if a square is black.
import re
def is_black(cell: str) -> bool:
if not re.search('^[a-h][1-8]$', cell):
raise ValueError(f"Cell {cell} is not valid!")
return bool((cell[0]in "aceg") ^ (int(cell[1]) % 2))
The same_color function simply checks if the two specified squares are the same.
def same_color(c1: str, c2: str) -> bool:
return is_black(c1) == is_black(c2)
The Perl solution follows the same logic.
sub is_black($cell) {
if ( $cell !~ /^[a-h][1-8]$/ ) {
die "Cell '$cell' is not valid!\n";
}
return ( index( "aceg", substr( $cell, 0, 1 ) ) == -1 ? 0 : 1 )
^ ( substr( $cell, 1, 1 ) % 2 );
}
sub main ( $c1, $c2 ) {
say is_black($c1) == is_black($c2) ? 'true' : 'false';
}
Examples
$ ./ch-1.py a7 f4
true
$ ./ch-1.py c1 e8
false
$ ./ch-1.py b5 h2
false
$ ./ch-1.py f3 h1
true
$ ./ch-1.py a1 g7
true
Task 2: Doubled Words
Task
You are given a string (which may contain embedded newlines) which is taken from a page on a website. The string will not contain brackets qw{ [ ] }.
Write a script that will find doubled words (such as "this this") and highlight (wrap in brackets) each doubled word.
The script should:
- Work across lines, even finding situations where a word at the end of one line is repeated at the beginning of the next.
- Find doubled words despite capitalization differences, such as with 'The the...', as well as allow differing amounts of white-space (spaces, tabs, newlines, and the like) to lie between the words.
- Find doubled words even when separated by HTML tags. For example, to make a word bold: '...it is very very important...'. Only show lines containing doubled words.
My solution
For input from the command line I replace a literal \n (i.e. a backslash followed by the letter 'n') with a new line character. The output replaces new lines with a literal \n. This ensures that I can copy and paste the examples. The Python test suite calls the function directly, so does not need this functionality.
Wow. This one is really tricky. To me an English word is some letters, optionally with a hyphen or apostrophe in the middle (e.g. one-sided or don't). Annoyingly apostrophes can also be used before or after a word to indicate a quote.
My first attempt at solving this was so bad, I went back to the drawing board for my second attempt. I have a solution that words (all five tests pass), but I'm sure that there is a more efficient solution.
I start this task by separating HTML (regular expression <[^>]+>, an open angle bracket, multiple characters that aren't a close angle bracket, and a close angle bracket) and words (regular expression [a-z0-9]+(?:['-][a-z0-9]+)*, letters optionally with hyphen or apostrophe in the middle) from other content (white space, punctuation, etc). As the first parameter to re.split is surround by parentheses, the words and HTML are also returned.
import re
def doubled_words(input_string: str) -> str:
regexp = r"(<[^>]+>|[a-z]+(?:['-][a-z]+)*)"
words_and_html = re.split(regexp, input_string, flags=re.IGNORECASE)
The next step is to find the position of all words only. This uses list comprehension to exclude even (0-based) elements (white space and punctuation) and HTML elements.
parts = [
i
for i, word in enumerate(parts)
if i % 2 == 1 and word[0] != "<" and word[-1] != ">"
]
Next I go through the word_positions list and replace words that are the same as the next word (case insensitive). It should be noted that triple words will only have the first two occurrences replaced.
for pos in range(len(word_positions)-1):
this_pos = word_positions[pos]
next_pos = word_positions[pos+1]
if parts[this_pos].lower() == parts[next_pos].lower():
parts[this_pos] = f"[{parts[this_pos]}]"
parts[next_pos] = f"[{parts[next_pos]}]"
With that done, I then separate the original input and new parts into two lists separated by new lines.
input_lines = re.split(r"[\r\n]+", input_string)
output_lines = re.split(r"[\r\n]+", "".join(parts))
Finally, I find the lines that are different (using list comprehension again) and return only the lines that are different, separated by new lines.
different_lines = [
output
for i, output in enumerate(output_lines)
if output != input_lines[i]
]
return "\n".join(different_lines)
The Perl solution follows the same logic, and uses map and grep to mimic list comprehension.
Examples
$ ./ch-2.py "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"
$ ./ch-2.py "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]\n[the]...', as well as allow differing amounts of whitespace (spaces,"
$ ./ch-2.py "to make a word bold: '...it is <B>very</B> very important...'."
"to make a word bold: '...it is <B>[very]</B> [very] important...'."
$ ./ch-2.py "Perl officially stands for Practical Extraction and Report Language, except when it doesn't."
""
$ ./ch-2.py "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."
$ ./ch-2.py "I can't can't do this"
"I [can't] [can't] do this"
Top comments (0)