DEV Community

Simon Green
Simon Green

Posted on

Weekly Challenge: Double chessboard

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.

Challenge, My solutions

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))
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

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';
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

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] != ">"
    ]
Enter fullscreen mode Exit fullscreen mode

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]}]"
Enter fullscreen mode Exit fullscreen mode

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))
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

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"
Enter fullscreen mode Exit fullscreen mode

Top comments (0)