Next / Previous / Contents / Shipman's homepage

26. Puzzle.__clueCheck(): Eliminate initial choices conflicting with clues

# - - -   P u z z l e . _ _ c l u e C h e c k

    def __clueCheck ( self, choiceList, slot, k ):
        '''Eliminate choices inconsistent with clue at position k.

          [ (choiceList is a set of Word instances) and
            (slot is a Slot instance) and
            (k is a position within slot) ->
                if the cell at slot[k] contains a clue letter ->
                    choiceList  :=  choiceList - (any word not
                        having that letter at position [k])
                    else -> I ]

First we convert the index k into a grid position using Section 56, “Slot.__getitem__(): Slot position to puzzle position”. Then we find the cell at that position using Section 32, “Puzzle.whatCell(): Is there a cell at a given coordinate?”.

        #-- 1 --
        # [ coord  :=  the Coord instance in self corresponding to
        #       index [k] in slot ]
        coord = slot[k]

        #-- 2 --
        # [ cell  :=  the Cell instance in self at coord ]
        cell = self.whatCell ( coord )

If cell has no clue, we are done. Otherwise, examine each word in choiceList, and remove those whose [k]th character doesn't match the clue.

        #-- 3 --
        if cell.text == UNK_CELL:
            clueLetter = cell.text

Removing the conflicts is just a little tricky. You might think that this code would do it:

        for choice in choiceList:
            if choice[k] != clueLetter:

Try it and you will get the message “RuntimeError: Set changed size during iteration”. The correct approach is to form a list from the set, then iterate over the list, whose size will not change in the for loop. By the way, we depend on the Cell class to uppercase the clue letter, and the Word class to uppercase the word list, so that we don't have to a case-insensitive comparison here.

        #-- 4 --
        # [ choiceList  :=  choiceList - (elements that do
        #      not have (clueLetter) in position [k]) ]
        for choice in list(choiceList):
            if choice[k] != clueLetter:
                choiceList.remove ( choice )