mirror of
https://codeberg.org/cblanken/boggler.git
synced 2026-07-26 11:35:10 -04:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
612482587d | ||
|
|
4587e7bc70 | ||
|
|
9b7b05f79d | ||
|
|
72f3d20401 |
+25
-37
@@ -1,4 +1,5 @@
|
|||||||
"""Boggler Utils"""
|
"""Boggler Utils"""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from multiprocessing import Pool
|
from multiprocessing import Pool
|
||||||
@@ -95,22 +96,22 @@ class BoggleBoard:
|
|||||||
body += header
|
body += header
|
||||||
return f"{header}{body}"
|
return f"{header}{body}"
|
||||||
|
|
||||||
@property
|
@functools.cached_property
|
||||||
def height(self) -> int:
|
def height(self) -> int:
|
||||||
"""Getter for height property"""
|
"""Getter for height property"""
|
||||||
return self.__height
|
return self.__height
|
||||||
|
|
||||||
@property
|
@functools.cached_property
|
||||||
def width(self) -> int:
|
def width(self) -> int:
|
||||||
"""Getter for width property"""
|
"""Getter for width property"""
|
||||||
return self.__width
|
return self.__width
|
||||||
|
|
||||||
@property
|
@functools.cached_property
|
||||||
def max_word_len(self) -> int:
|
def max_word_len(self) -> int:
|
||||||
"""Getter for maximum word length property"""
|
"""Getter for maximum word length property"""
|
||||||
return self.__max_word_len
|
return self.__max_word_len
|
||||||
|
|
||||||
@property
|
@functools.cached_property
|
||||||
def board(self) -> dict[tuple[int, int], BoardCell]:
|
def board(self) -> dict[tuple[int, int], BoardCell]:
|
||||||
"""Getter for board property"""
|
"""Getter for board property"""
|
||||||
return self.__board
|
return self.__board
|
||||||
@@ -158,11 +159,16 @@ class WordNode:
|
|||||||
self.__parent = parent
|
self.__parent = parent
|
||||||
self.__board_pos = board_pos
|
self.__board_pos = board_pos
|
||||||
|
|
||||||
@property
|
@functools.cached_property
|
||||||
def letters(self) -> str:
|
def letters(self) -> str:
|
||||||
"""Getter for letter property"""
|
"""Getter for letter property"""
|
||||||
return self.__letters
|
return self.__letters
|
||||||
|
|
||||||
|
@functools.cached_property
|
||||||
|
def letters_count(self) -> int:
|
||||||
|
"""Getter for letters count"""
|
||||||
|
return len(self.__letters)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_word(self) -> bool:
|
def is_word(self) -> bool:
|
||||||
"""Getter for is_word property"""
|
"""Getter for is_word property"""
|
||||||
@@ -172,7 +178,7 @@ class WordNode:
|
|||||||
def is_word(self, value):
|
def is_word(self, value):
|
||||||
self.__is_word = value
|
self.__is_word = value
|
||||||
|
|
||||||
@property
|
@functools.cached_property
|
||||||
def children(self) -> dict[str, WordNode]:
|
def children(self) -> dict[str, WordNode]:
|
||||||
"""Getter for children property"""
|
"""Getter for children property"""
|
||||||
return self.__children
|
return self.__children
|
||||||
@@ -221,7 +227,7 @@ class WordTree:
|
|||||||
self,
|
self,
|
||||||
alphabet: list[str],
|
alphabet: list[str],
|
||||||
root: WordNode,
|
root: WordNode,
|
||||||
words: list[str] = None,
|
words: list[str] | None = None,
|
||||||
max_word_len=16,
|
max_word_len=16,
|
||||||
) -> None:
|
) -> None:
|
||||||
self.__alphabet = alphabet
|
self.__alphabet = alphabet
|
||||||
@@ -240,27 +246,27 @@ class WordTree:
|
|||||||
for word in words:
|
for word in words:
|
||||||
self.__insert_word(word)
|
self.__insert_word(word)
|
||||||
|
|
||||||
@property
|
@functools.cached_property
|
||||||
def alphabet(self) -> list:
|
def alphabet(self) -> list:
|
||||||
"""Getter for alphabet property"""
|
"""Getter for alphabet property"""
|
||||||
return self.__alphabet
|
return self.__alphabet
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def wordlist(self) -> list[str]:
|
def wordlist(self) -> list[str] | None:
|
||||||
"""Getter for wordlist property"""
|
"""Getter for wordlist property"""
|
||||||
return self.__wordlist
|
return self.__wordlist
|
||||||
|
|
||||||
@property
|
@functools.cached_property
|
||||||
def root(self) -> WordNode:
|
def root(self) -> WordNode:
|
||||||
"""Getter for root property"""
|
"""Getter for root property"""
|
||||||
return self.__root
|
return self.__root
|
||||||
|
|
||||||
@property
|
@functools.cached_property
|
||||||
def max_word_len(self) -> int:
|
def max_word_len(self) -> int:
|
||||||
"""Getter for max_word_len property"""
|
"""Getter for max_word_len property"""
|
||||||
return self.__max_word_len
|
return self.__max_word_len
|
||||||
|
|
||||||
@property
|
@functools.cached_property
|
||||||
def tree(self) -> dict[str, WordNode]:
|
def tree(self) -> dict[str, WordNode]:
|
||||||
"""Getter for tree property"""
|
"""Getter for tree property"""
|
||||||
return self.__tree
|
return self.__tree
|
||||||
@@ -289,7 +295,6 @@ class WordTree:
|
|||||||
board_pos=None,
|
board_pos=None,
|
||||||
):
|
):
|
||||||
"""Create WordNode for `letters` and into WordTree under `parent`"""
|
"""Create WordNode for `letters` and into WordTree under `parent`"""
|
||||||
log.debug("inserting node: %s", letters)
|
|
||||||
node = WordNode(letters, is_word, parent, children, board_pos)
|
node = WordNode(letters, is_word, parent, children, board_pos)
|
||||||
parent.add_child_node(node)
|
parent.add_child_node(node)
|
||||||
|
|
||||||
@@ -298,41 +303,31 @@ class WordTree:
|
|||||||
otherwise returns False"""
|
otherwise returns False"""
|
||||||
|
|
||||||
# Insert root node
|
# Insert root node
|
||||||
prefix = word[0 : len(self.root.letters)] # prefix = first letter block
|
prefix = word[0 : self.root.letters_count] # prefix = first letter block
|
||||||
if word is None or prefix != self.root.letters:
|
if word is None or prefix != self.root.letters:
|
||||||
return False
|
return False
|
||||||
curr_node = self.tree[prefix]
|
curr_node = self.tree[prefix]
|
||||||
word_len = len(word)
|
word_len = len(word)
|
||||||
|
|
||||||
i = len(prefix)
|
i = len(prefix)
|
||||||
i_max = min(self.max_word_len, len(word))
|
i_max = min(self.max_word_len, word_len)
|
||||||
while i < i_max:
|
while i < i_max:
|
||||||
letters = word[i]
|
letters = word[i]
|
||||||
log.debug(
|
|
||||||
"1 letter seq: %s %s %s", word, letters, letters in curr_node.children
|
|
||||||
)
|
|
||||||
if letters not in self.alphabet:
|
if letters not in self.alphabet:
|
||||||
# Check two letter sequences like ("Qu", "Th", etc.) at current index
|
# Check two letter sequences like ("Qu", "Th", etc.) at current index
|
||||||
letters = word[i : i + 2]
|
letters = word[i : i + 2]
|
||||||
log.debug(
|
|
||||||
"2 letter seq: %s %s %s",
|
|
||||||
word,
|
|
||||||
letters,
|
|
||||||
letters in curr_node.children,
|
|
||||||
)
|
|
||||||
if letters not in self.alphabet:
|
if letters not in self.alphabet:
|
||||||
return False
|
return False
|
||||||
# Insert node
|
# Insert node
|
||||||
if letters not in curr_node.children:
|
if letters not in curr_node.children:
|
||||||
self.insert_node(letters, curr_node)
|
self.insert_node(letters, curr_node)
|
||||||
log.debug("3 letter seq: %s %s", word, letters)
|
|
||||||
|
|
||||||
curr_node = curr_node.children[letters]
|
curr_node = curr_node.children[letters]
|
||||||
|
|
||||||
i += len(letters)
|
i += len(letters)
|
||||||
|
|
||||||
# Mark the last node as a word
|
# Mark the last node as a word
|
||||||
curr_node.is_word = len(word) <= self.max_word_len
|
curr_node.is_word = word_len <= self.max_word_len
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def search(self, word: str, curr_node=None) -> WordNode:
|
def search(self, word: str, curr_node=None) -> WordNode:
|
||||||
@@ -373,7 +368,7 @@ class WordTree:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
if word_len > self.max_word_len or word_len >= board.max_word_len:
|
if word_len > self.max_word_len or word_len >= board.max_word_len:
|
||||||
log.debug(f"MAX WORD LENGTH REACHED! len = {word_len}")
|
# MAX WORD LENGTH REACHED!
|
||||||
subtree.active_node = subtree.active_node.parent
|
subtree.active_node = subtree.active_node.parent
|
||||||
self.active_node = self.active_node.parent
|
self.active_node = self.active_node.parent
|
||||||
return subtree
|
return subtree
|
||||||
@@ -382,22 +377,15 @@ class WordTree:
|
|||||||
if self.active_node.is_word and len(self.active_node.children) == 0:
|
if self.active_node.is_word and len(self.active_node.children) == 0:
|
||||||
word_path = subtree.active_node.path[::-1]
|
word_path = subtree.active_node.path[::-1]
|
||||||
subtree.word_paths.append((subtree.active_node.get_word(board), word_path))
|
subtree.word_paths.append((subtree.active_node.get_word(board), word_path))
|
||||||
log.debug(
|
|
||||||
"1: WORD FOUND: %s %s",
|
# Word found
|
||||||
"".join([board.board[x].letters for x in word_path]),
|
|
||||||
word_path,
|
|
||||||
)
|
|
||||||
self.active_node = self.active_node.parent
|
self.active_node = self.active_node.parent
|
||||||
subtree.active_node = subtree.active_node.parent
|
subtree.active_node = subtree.active_node.parent
|
||||||
return subtree
|
return subtree
|
||||||
elif self.active_node.is_word:
|
elif self.active_node.is_word:
|
||||||
|
# Word found
|
||||||
word_path = subtree.active_node.path[::-1]
|
word_path = subtree.active_node.path[::-1]
|
||||||
subtree.word_paths.append((subtree.active_node.get_word(board), word_path))
|
subtree.word_paths.append((subtree.active_node.get_word(board), word_path))
|
||||||
log.debug(
|
|
||||||
"2: WORD FOUND: %s %s",
|
|
||||||
"".join([board.board[x].letters for x in word_path]),
|
|
||||||
word_path,
|
|
||||||
)
|
|
||||||
|
|
||||||
# Branch for each adjacent board cell
|
# Branch for each adjacent board cell
|
||||||
for cell in board_cell.adjacent_cells:
|
for cell in board_cell.adjacent_cells:
|
||||||
|
|||||||
+9
-19
@@ -1,32 +1,22 @@
|
|||||||
[project]
|
|
||||||
name = "boggler"
|
|
||||||
version = "2.0.1"
|
|
||||||
authors = [
|
|
||||||
{ name="Cameron Blankenbuehler", email="cameron.blankenbuehler@gmail.com" },
|
|
||||||
]
|
|
||||||
description = "Utilities for solving the Boggle word game."
|
|
||||||
readme="README.md"
|
|
||||||
license = { file="LICENSE" }
|
|
||||||
requires-python = ">=3.7"
|
|
||||||
classifiers = [
|
|
||||||
"Programming Language :: Python :: 3",
|
|
||||||
"License :: OSI Approved :: MIT License",
|
|
||||||
"Operating System :: POSIX :: Linux",
|
|
||||||
]
|
|
||||||
|
|
||||||
[project.urls]
|
[project.urls]
|
||||||
"Homepage" = "https://github.com/cblanken/boggler"
|
"Homepage" = "https://github.com/cblanken/boggler"
|
||||||
"Bug Tracker" = "https://github.com/cblanken/boggler/issues"
|
"Bug Tracker" = "https://github.com/cblanken/boggler/issues"
|
||||||
|
|
||||||
[tool.poetry]
|
[tool.poetry]
|
||||||
name = "boggler"
|
name = "boggler"
|
||||||
version = "2.0.1"
|
version = "2.0.4"
|
||||||
description = "Utilities for solving the Boggle word game."
|
description = "Utilities for solving the Boggle word game."
|
||||||
authors = ["Cameron Blankenbuehler <cameron.blankenbuehler@protonmail.com>"]
|
authors = ["Cameron Blankenbuehler <cameron.blankenbuehler@gmail.com>"]
|
||||||
|
license = "LICENSE"
|
||||||
|
classifiers = [
|
||||||
|
"Programming Language :: Python :: 3",
|
||||||
|
"License :: OSI Approved :: MIT License",
|
||||||
|
"Operating System :: POSIX :: Linux",
|
||||||
|
]
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
|
|
||||||
[tool.poetry.dependencies]
|
[tool.poetry.dependencies]
|
||||||
python = "^3.11"
|
python = "^3.8"
|
||||||
rich = "^13.4.1"
|
rich = "^13.4.1"
|
||||||
|
|
||||||
[tool.poetry.scripts]
|
[tool.poetry.scripts]
|
||||||
|
|||||||
Reference in New Issue
Block a user