예제 #1
0
 def __init__(self, squares=None, filename=None, size=15, dictionary_path="dictionaries/"):
     if squares:
         self.grid = Grid(squares, size)
     elif filename and path.exists(filename):
         squares = storage.load(filename)
         self.grid = Grid(squares)
     else:
         self.grid = Grid(size=size)
     self.size = self.grid.size
     self.focus = (0, 0)
     self.highlight = []
     self.mode = Mode.ACROSS
     self.dictionary = Dictionary(dictionary_path)
예제 #2
0
class DictionaryTests(unittest.TestCase):

    def setUp(self):
        # use test dictionary since the real one isn't checked in
        self.dictionary = Dictionary("test/dictionaries/")

    def test_single_response(self):
        results = self.dictionary.search("bird")
        self.assertEqual(results, [["bird", '50']])

    def test_case_insensitive(self):
        results = self.dictionary.search("bIrD")
        self.assertEqual(results, [["bird", '50']])

    def test_no_response(self):
        results = self.dictionary.search("kdlka")
        self.assertEqual(results, [])

    def test_multiple_responses(self):
        results = self.dictionary.search("bi d")
        expected = [
            ['bind', '50'],
            ['bird', '50']
        ]
        self.assertEqual(results, expected)

    @parameterized.expand([
        ["bi d", 2, {"n", "r"}],
        ["xx   ", 3, set()],
        ["a ple", 1, {"p"}]
    ])
    def test_allowed_letters(self, word, index, expected):
        letters = self.dictionary.get_allowed_letters(word, index)
        self.assertEqual(letters, expected)

    @parameterized.expand([
        ["bird", 50],
        ["id a", 0],
        ["fioakld", 0]
        ])
    def test_word_score(self, word, expected):
        score = self.dictionary.word_score(word)
        self.assertEqual(score, expected)

    @parameterized.expand([
        [None, ["bind", "bird", "date", "idea", "note", "word"]],
        [1, ["date", "idea", "word", "bind", "note", "bird"]],
        [2, ["date", "idea", "bird", "note", "word", "bind"]],
    ])
    def test_seeded_shuffling(self, seed, expected):
        dictionary = Dictionary("test/dictionaries/", seed)
        words = [w[0] for w in dictionary.search("    ")]
        self.assertEqual(words, expected)
예제 #3
0
 def setUp(self):
     # use default dictionary file, not available on CI
     dictionary = Dictionary()
     self.generator = Generator(dictionary)
예제 #4
0
class Model:
    """
    A class that holds all of the UI state for a crossword puzzle.
    """

    def __init__(self, squares=None, filename=None, size=15, dictionary_path="dictionaries/"):
        if squares:
            self.grid = Grid(squares, size)
        elif filename and path.exists(filename):
            squares = storage.load(filename)
            self.grid = Grid(squares)
        else:
            self.grid = Grid(size=size)
        self.size = self.grid.size
        self.focus = (0, 0)
        self.highlight = []
        self.mode = Mode.ACROSS
        self.dictionary = Dictionary(dictionary_path)

    def toggle_orientation(self):
        self.mode = self.mode.opposite()
        self.update_highlighted_squares()

    def update_focus(self, row, col):
        self.focus = (row, col)
        self.update_highlighted_squares()

    def save(self, filename):
        storage.save(self.grid.squares, filename)

    def update_square(self, row, col, text):
        # maintain block symmetry
        if text == BLOCK:
            # add corresponding block
            self.grid.set_square((self.size - 1 - row, self.size - 1 - col), BLOCK)
        elif self.grid.get_square((row, col)) == BLOCK and text != BLOCK:
            # remove corresponding block if this square used to be a block
            self.grid.set_square((self.size - 1 - row, self.size - 1 - col), '')
        self.grid.set_square((row, col), text)
        self.get_next_focus(text)
        self.update_highlighted_squares()

    def get_square(self, row, col):
        text = self.grid.get_square((row, col))
        background = Background.WHITE
        if text == BLOCK:
            background = Background.BLACK
        elif (row, col) in self.highlight:
            background = Background.YELLOW
        focused = (row, col) == self.focus
        return Square(text, background, focused)

    def update_highlighted_squares(self):
        self.highlight = self.grid.get_word_squares(self.focus, self.mode)

    def get_next_focus(self, text):
        """
        Get the coordinates of the square that should be focused after the given square
        """
        if text == '':
            # text was deleted, go backwards
            if self.mode is Mode.ACROSS:
                self.move_left()
            else:
                self.move_up()
        else:
            # text was added
            if self.mode is Mode.ACROSS:
                self.move_right()
            else:
                self.move_down()

    def move_up(self):
        self.focus = (max(0, self.focus[0] - 1), self.focus[1])

    def move_down(self):
        self.focus = (min(self.size - 1, self.focus[0] + 1), self.focus[1])

    def move_left(self):
        self.focus = (self.focus[0], max(0, self.focus[1] - 1))

    def move_right(self):
        self.focus = (self.focus[0], min(self.size - 1, self.focus[1] + 1))

    def get_suggestions(self):
        # returns suggestions for the focused square
        # prioritizes words that use a letter compatible with crossing words
        # (across, down), each is a list of tuples (word, score)

        if self.grid.get_square(self.focus) == BLOCK:
            return [], []

        across = self._get_suggestions(self.focus, Mode.ACROSS)
        down = self._get_suggestions(self.focus, Mode.DOWN)

        return across, down

    def _get_suggestions(self, square, mode):
        word = self.grid.get_word(square, mode)
        squares = self.grid.get_word_squares(square, mode)

        # copy to avoid modifying cached lists
        words = self.dictionary.search(word).copy()
        compatible_words = words.copy()

        # loop through empty letter index and remove words that don't fit crossing words
        for i, s in enumerate(squares):
            if not self.grid.is_empty(s):
                # skip squares that are already filled in
                continue

            cross_index = self.grid.get_word_squares(s, mode.opposite()).index(s)
            cross_word = self.grid.get_word(s, mode.opposite())
            available_letters = self.dictionary.get_allowed_letters(cross_word, cross_index)
            compatible_words = [w for w in compatible_words if w[0][i] in available_letters]

        # add bonus to compatible words
        for i, w in enumerate(words):
            if w in compatible_words:
                words[i] = w[0], str(int(w[1]) + 100)

        words.sort(key=lambda w: int(w[1]), reverse=True)
        return words

    def fill(self):
        """ Fill the blank squares using a Generator """
        filled_grid, _ = optimize(self.grid, self.dictionary)
        self.grid = filled_grid

    def print(self):
        self.grid.print()
예제 #5
0
 def setUp(self):
     # use test dictionary since the real one isn't checked in
     self.dictionary = Dictionary("test/dictionaries/")
예제 #6
0
 def test_seeded_shuffling(self, seed, expected):
     dictionary = Dictionary("test/dictionaries/", seed)
     words = [w[0] for w in dictionary.search("    ")]
     self.assertEqual(words, expected)
예제 #7
0
 def setUp(self):
     dictionary = Dictionary("test/dictionaries")
     self.generator = Generator(dictionary)
import time

from crossword.dictionary import Dictionary

dictionary = Dictionary()
start = time.time()
query = "A   "
count = 500
for i in range(0, count):
    dictionary.search(query)
    dictionary.search.cache_clear()

seconds = time.time() - start
print(f"{count / seconds:.2f} searches/sec")