A word square consists of a list of words written out in a square grid, such that the same words can be read both horizontally and vertically from the corresponding row and column in the grid. The number of words, which is equal to the number of letters in each word, is known as the order of the square. For example, this is an order 3 square:
Large word squares are dramatically harder to make than small ones. To date, the largest anyone has managed to find are composed of 9-letter words:
Finding a perfect $$10 \times 10$$ word square has been a central goal for wordplay fans for more than 100 years. The task was looking impossible when in 1972 Dmitri Borgmann found an unexpected resource in the African journal of David Livingstone, whose entry for Sept. 26, 1872, reads:
Through forest, along the side of a sedgy valley. Cross its head-water, which has rust of iron in it, then west and by south. The forest has very many tsetse. Zebras calling loudly, and Senegal long claw in our camp at dawn, with its cry, 'O-o-o-o-o-o-o-o-o-o.'
This is exactly what was needed. Given a pen, the yellow-bellied longclaw, Macronyx flavigaster, could have drawn for Livingstone a perfect $$10 \times 10$$ square:
We ought to consult other species more often. Any longclaw could have given us this contribution — indeed, this is the only word square the bird is capable of making!
Because word squares are symmetric with respect to their main diagonal, they can be fully described by reading the letters on or below the main diaginal from left to right and from top to bottom. The order 3 word square that is given as an example in the introduction, can be described in this way as the 6-letter string BICTEN. In general, any order $$n$$ word square can be described as an $$\frac{n(n + 1)}{2}$$-letter string containing the letters on or below the main diagonal. Your task is to define a class WordSquare that can be used to represent word squares in Python. This class must support at least the following methods:
An initialisation method that takes two arguments: the order of the word square and a string of letters on or below the main diagonal, read from left to right and from top to bottom. In case the number of letters on or below the main diagonal does not correspond to the given order of the word square, the method must raise an AssertionError with the message invalid square.
A method letter that takes two arguments: the row and column index of a cell in the word square. The method must return the upper case version of the letter at the given cel in the word square. Cells in a word square are located by indexing the rows from top to bottom, and the columns from left to right, each time starting at index zero. In case the given arguments do not point to the location of a cell inside the grid, the method must raise an AssertionError with the message invalid index.
A method word that takes the index of a row or column in the word square as its argument (using the same indexing as in the previous method). The method must return the upper case version of the word that can be read in the word square at the indicated row or column. In case the given index does not corresspond to the index of a row/column in the word square, the method must raise an AssertionError with the message invalid index.
A method valid that takes the location of a text file as its argument. This text file must contain a list of words, each on a separate line. The method must return a Boolean value, that indicates whether or not all words in the word square (either read from the rows or the columns) occur in the given list of words. In comparing the words in the word square with the words in the given text file, no distinction should be made between uppercase and lowercase letters.
In addition, it should be possible to use the built-in function print to output a string representation of each object that is an instance of the class WordSquare. In this string representation, the letters of the word square are written out row by row, as illustrated in the examples below.
In the following interactive session, we assume that the text file words.txt1 is located in the current directory.
>>> square = WordSquare(3, 'bicten')
>>> square.letter(1, 0)
'I'
>>> square.letter(1, 1)
'C'
>>> square.letter(1, 2)
'E'
>>> square.word(1)
'ICE'
>>> print(square)
BIT
ICE
TEN
>>> square.valid('words.txt')
True
>>> square.letter(1, 3)
Traceback (most recent call last):
AssertionError: invalid index
>>> square.word(3)
Traceback (most recent call last):
AssertionError: invalid index
>>> square = WordSquare(3, 'ABCDE')
Traceback (most recent call last):
AssertionError: invalid square
>>> square = WordSquare(9, 'ACRHEXANABLINOLADDLEHSERINETINITIATOASCENDERS')
>>> square.word(1)
'CRENIDENS'
>>> square.word(7)
'INITIATOR'
>>> print(square)
ACHALASIA
CRENIDENS
HEXANDRIC
ANABOLITE
LINOLENIN
ADDLEHEAD
SERINETTE
INITIATOR
ASCENDERS
>>> square.valid('words.txt')
True
>>> square = WordSquare(4, 'ABCDEFGHIJ')
>>> print(square)
ABDG
BCEH
DEFI
GHIJ
>>> square.valid('words.txt')
False