Hello Friends!
I’m wrestling with a chess game challenge involving one queen facing off another, with no other pieces involved. The exercise provides unit tests to work with (included at the very bottom of this post). It’s not a formal homework assignment for a classroom setting, but I am trying to pretend that it is, meaning that I don’t want you people to complete the assignment for me. Instead, please just share hints, tips, pointers, and general guidance. Thanks.
Here are the instructions for the exercise:
That’s the challenge I am working on. I understand the basic concept where the white queen is positioned at c5 (zero-indexed) at column 2, row 3 and how that corresponds to the text based chess board grid. What I need clarification on is the meaning/use of ‘zero-indexed’. Could someone elaborate on zero-indexed in this context? I understand the principle of indexing of strings and lists (arrays) but in the context would the index be pointing to the queen’s location at the coordinartes?
For the dunder __init__
method, I’ve started with this in my script:
class Queen:
def __init__(self, row, column):
self.row = row
self.column = column
I get the sense that the above class attribute declaration isn’t enough. Instead, should I be creating and initializing a 1
-8
and a
-h
matrix or grid board like something more sophisticated than the basic 10x10 checkerboard grid below? The checkers board below is just alternating on/off, zeroes and ones that can be built inside a REPL that I found on Stack Overflow. For the original chess challenge described above, do I need to build a similar grid using hashtables but with 64 unique address locations 1-8 (verticle axis) and a-h (horizontal axis)?
>>> import numpy as NP
>>> def build_checkerboard(w, h) :
re = NP.r_[ w*[0,1] ] # even-numbered rows
ro = NP.r_[ w*[1,0] ] # odd-numbered rows
return NP.row_stack(h*(re, ro))
>>> checkerboard = build_checkerboard(5, 5)
>>> checkerboard
Out[3]: array([[0, 1, 0, 1, 0, 1, 0, 1, 0, 1],
[1, 0, 1, 0, 1, 0, 1, 0, 1, 0],
[0, 1, 0, 1, 0, 1, 0, 1, 0, 1],
[1, 0, 1, 0, 1, 0, 1, 0, 1, 0],
[0, 1, 0, 1, 0, 1, 0, 1, 0, 1],
[1, 0, 1, 0, 1, 0, 1, 0, 1, 0],
[0, 1, 0, 1, 0, 1, 0, 1, 0, 1],
[1, 0, 1, 0, 1, 0, 1, 0, 1, 0],
[0, 1, 0, 1, 0, 1, 0, 1, 0, 1],
[1, 0, 1, 0, 1, 0, 1, 0, 1, 0]])
My (horribly incomplete) script looks like this currently:
class Queen:
def __init__(self, row, column):
self.row = row
self.column = column
def can_attack(self, another_queen):
if self.row < 0: # if the row parameter is negative
raise ValueError("row not positive")
if self.row > 8 or self.row < 0: # if the row parameter is not on the defined board
raise ValueError("row not on board")
if self.column < 0: # if the column parameter is negative
raise ValueError("column not positive")
if self.column < 0 or self.column > 8: # if the column parameter is not on the defined board
raise ValueError("column not on board")
# if both the queens are on the same location
raise ValueError("Invalid queen position: both queens in the same square")
Naturally, it fails 13 of 14 unit tests. I clearly have a lot of work cut out for me.
Let’s just take the second Assertion to see if I understand what it is trying to test:
def test_queen_must_have_positive_row(self):
with self.assertRaises(ValueError) as err:
Queen(-2, 2)
self.assertEqual(type(err.exception), ValueError)
self.assertEqual(err.exception.args[0], "row not positive")
Two arguments are being passed into the Queen class: -2
and 2
. Since 2
is the self.row
attribute and it is less than 0
, it is out of parameters of the chess board. But as far as I can tell, the first line beneath my can_attack()
class method accounts for the scenario. When the row is negative, I’ve directed my script to throw the “row not positive” ValueError. Or at least that’s my intention anyways. The gap in my current script is how to handle the another_queen
variable and integrate that into the conditional logic for ValueErrors. Any hints or suggestions in this regard are welcome. That is my ask for all of you reading this.
Here is the full unit test that comes with the challenge:
import unittest
from queen_attack import (
Queen,
)
# Tests adapted from `problem-specifications//canonical-data.json`
class QueenAttackTest(unittest.TestCase):
# Test creation of Queens with valid and invalid positions
def test_queen_with_a_valid_position(self):
Queen(2, 2)
def test_queen_must_have_positive_row(self):
with self.assertRaises(ValueError) as err:
Queen(-2, 2)
self.assertEqual(type(err.exception), ValueError)
self.assertEqual(err.exception.args[0], "row not positive")
def test_queen_must_have_row_on_board(self):
with self.assertRaises(ValueError) as err:
Queen(8, 4)
self.assertEqual(type(err.exception), ValueError)
self.assertEqual(err.exception.args[0], "row not on board")
def test_queen_must_have_positive_column(self):
with self.assertRaises(ValueError) as err:
Queen(2, -2)
self.assertEqual(type(err.exception), ValueError)
self.assertEqual(err.exception.args[0], "column not positive")
def test_queen_must_have_column_on_board(self):
with self.assertRaises(ValueError) as err:
Queen(4, 8)
self.assertEqual(type(err.exception), ValueError)
self.assertEqual(err.exception.args[0], "column not on board")
# Test the ability of one queen to attack another
def test_cannot_attack(self):
self.assertIs(Queen(2, 4).can_attack(Queen(6, 6)), False)
def test_can_attack_on_same_row(self):
self.assertIs(Queen(2, 4).can_attack(Queen(2, 6)), True)
def test_can_attack_on_same_column(self):
self.assertIs(Queen(4, 5).can_attack(Queen(2, 5)), True)
def test_can_attack_on_first_diagonal(self):
self.assertIs(Queen(2, 2).can_attack(Queen(0, 4)), True)
def test_can_attack_on_second_diagonal(self):
self.assertIs(Queen(2, 2).can_attack(Queen(3, 1)), True)
def test_can_attack_on_third_diagonal(self):
self.assertIs(Queen(2, 2).can_attack(Queen(1, 1)), True)
def test_can_attack_on_fourth_diagonal(self):
self.assertIs(Queen(1, 7).can_attack(Queen(0, 6)), True)
def test_cannot_attack_if_falling_diagonals_are_only_the_same_when_reflected_across_the_longest_falling_diagonal(
self,
):
self.assertIs(Queen(4, 1).can_attack(Queen(2, 5)), False)
# Track-specific tests
def test_queens_same_position_can_attack(self):
with self.assertRaises(ValueError) as err:
Queen(2, 2).can_attack(Queen(2, 2))
self.assertEqual(type(err.exception), ValueError)
self.assertEqual(
err.exception.args[0],
"Invalid queen position: both queens in the same square",
)