Simon Green

Posted on

# The one about a chess board

## Weekly Challenge 281

Sorry for being MIA over the last few weeks. I've moved house and a new job, so haven't had a chance to partake in the challenges over this time.

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. It's a great way for us all to practice some coding.

You are given coordinates, a string that represents the coordinates of a square of the chessboard as shown below:

Write a script to return `true` if the square is light, and `false` if the square is dark.

### My solution

This is relatively straight forward. The first thing I do is check that the provided position is valid (first character is `a`-`h` and the second character is between 1 and 8).

I then check if the first letter is `a`, `c`, `e` or `g` and the number is even, or the first letter is `b`, `d`, `f` or `h` and the number is odd, return `true`. Otherwise return `false`.

``````def check_color(coords: str) -> bool:
if not re.search('^[a-h][1-8]\$', coords):
raise ValueError('Not a valid chess coordinate!')

if coords[0] in ('a', 'c', 'e', 'g') and int(coords[1]) % 2 == 0:
return True
if coords[0] in ('b', 'd', 'f', 'h') and int(coords[1]) % 2 == 1:
return True
return False
``````

### Examples

``````\$ ./ch-1.py d3
true

\$ ./ch-1.py g5
false

\$ ./ch-1.py e6
true
``````

A Knight in chess can move from its current position to any square two rows or columns plus one column or row away. So in the diagram below, if it starts a S, it can move to any of the squares marked E.

Write a script which takes a starting position and an ending position and calculates the least number of moves required.

### My solution

• `deltas` is a tuples of lists (array of arrays in Perl) with the eight ways the knight can move from its current position.
• `target` is the cell that we want to reach. For this I convert the first letter to a number from one to 8. It's stored as a tuple, the first value is the column and the second value is the row.
• `moves` is the number of moves made and starts at one.
• `seen` is a list of cells we have already visited.
• `coords` is a list of current positions of a knight. It starts with the starting coordinate.
``````def knights_move(start_coord: str, end_coord: str) -> int:
for coord in (start_coord, end_coord):
if not re.search('^[a-h][1-8]\$', coord):
raise ValueError(
f'The position {coord} is not a valid chess coordinate!')

deltas = ([2, 1], [2, -1], [-2, 1], [-2, -1],
[1, 2], [1, -2], [-1, 2], [-1, -2])
coords = [convert_coord_to_list(start_coord)]
target = convert_coord_to_list(end_coord)
moves = 1
seen = []
``````

I then have a double loop of the current `coords` list and the `deltas` list. A set a variable `new_pos` that represents the new coordinates for the knight. If this leads to a position outside the board or a coordinate we've already been to, I skip it. If it lands on the target, I return the `moves` value.

After the loop, I reset the `coords` list to the coordinates collected through the iterations, and increment the `moves` value by one. This continues until we hit the target coordinate.

``````    while True:
new_coords = []

for coord in coords:
for delta in deltas:
new_pos = (coord[0] + delta[0], coord[1] + delta[1])

if not 0 < new_pos[0] < 9 or not 0 < new_pos[1] < 9 or new_pos in seen:
continue

if new_pos == target:
return moves

new_coords.append(new_pos)
seen.append(new_pos)

coords = new_coords
moves += 1
``````

### Examples

``````\$ ./ch-2.py g2 a8
4

\$ ./ch-2.py g2 h2
3
``````