mirror of
https://codeberg.org/cblanken/boggler.git
synced 2026-07-26 11:35:10 -04:00
Compare commits
64
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c0fe222832 | ||
|
|
6771ec9205 | ||
|
|
e1981c44de | ||
|
|
02bb7c4262 | ||
|
|
da00afb395 | ||
|
|
afaa378131 | ||
|
|
daebee948a | ||
|
|
2bc9d9fb9e | ||
|
|
7500720992 | ||
|
|
d81f5e28cc | ||
|
|
a0d60d280c | ||
|
|
ac58828521 | ||
|
|
59c5beb491 | ||
|
|
49cf552cc1 | ||
|
|
f0cee00bef | ||
|
|
843e167bf4 | ||
|
|
1a1f5acb17 | ||
|
|
2f795be626 | ||
|
|
6f4d4a69a0 | ||
|
|
82991e96e7 | ||
|
|
1386e1cace | ||
|
|
8c9ed00826 | ||
|
|
c09dce4bd6 | ||
|
|
a37548a4ee | ||
|
|
1dc118e953 | ||
|
|
6d3a58595c | ||
|
|
8b9dc89420 | ||
|
|
a80111072f | ||
|
|
fa01d442a5 | ||
|
|
733c1474b0 | ||
|
|
54f79c0afe | ||
|
|
9f364ee90e | ||
|
|
e219954ba6 | ||
|
|
f4accfe959 | ||
|
|
bc70b88bf7 | ||
|
|
21aab46b96 | ||
|
|
f18c30cb60 | ||
|
|
da915b723c | ||
|
|
d5a102713d | ||
|
|
c488263eea | ||
|
|
fe1f1c2b97 | ||
|
|
d2584686a3 | ||
|
|
8f75604d15 | ||
|
|
96da732dd9 | ||
|
|
ec53042d18 | ||
|
|
5c18a4aebf | ||
|
|
b197253d86 | ||
|
|
67f8c439b0 | ||
|
|
8c41ad212d | ||
|
|
94800909b4 | ||
|
|
f24b996411 | ||
|
|
e4df95cd08 | ||
|
|
0e9b120e94 | ||
|
|
276c27f1ce | ||
|
|
d30c44af3c | ||
|
|
5da33cf02c | ||
|
|
600efc19c2 | ||
|
|
cdb1542184 | ||
|
|
795f1540b4 | ||
|
|
91390ea425 | ||
|
|
2a6a27b0b8 | ||
|
|
b445071ccc | ||
|
|
9c8e94964c | ||
|
|
2fd5b05717 |
@@ -1 +1,9 @@
|
|||||||
|
# Byte-compiled / optimized
|
||||||
|
__pycache__/
|
||||||
|
*.py[cod]
|
||||||
|
|
||||||
|
# Distribution / packaging
|
||||||
|
dist/
|
||||||
|
|
||||||
|
# Env
|
||||||
.vscode
|
.vscode
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
The MIT License (MIT)
|
||||||
|
Copyright © 2022 Cameron Blankenbuehler
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
@@ -2,8 +2,8 @@
|
|||||||
A solver for the popular word game Boggle.
|
A solver for the popular word game Boggle.
|
||||||
|
|
||||||
# Install
|
# Install
|
||||||
```bash
|
```console
|
||||||
$ git clone https://github.com/cblanken/boggler.git
|
pip install boggler
|
||||||
```
|
```
|
||||||
|
|
||||||
To use the script to solve a particular Boggle board configuration, you'll need to do a few things
|
To use the script to solve a particular Boggle board configuration, you'll need to do a few things
|
||||||
@@ -11,11 +11,11 @@ To use the script to solve a particular Boggle board configuration, you'll need
|
|||||||
|
|
||||||
Here is an example `board.csv`. Note the orientation of the board does not matter.
|
Here is an example `board.csv`. Note the orientation of the board does not matter.
|
||||||
```console
|
```console
|
||||||
$ cat boards/b1.csv
|
$ cat board.csv
|
||||||
s,a,i,p
|
y,e,o,s
|
||||||
l,qu,a,y
|
r,e,o,v
|
||||||
u,l,l,s
|
d,f,e,y
|
||||||
o,w,h,a
|
n,m,e,qu
|
||||||
```
|
```
|
||||||
2. Find a dictionary wordlist file or create your own
|
2. Find a dictionary wordlist file or create your own
|
||||||
|
|
||||||
@@ -394,3 +394,17 @@ quey : [(3, 3), (3, 2), (2, 3)]
|
|||||||
quem : [(3, 3), (3, 2), (3, 1)]
|
quem : [(3, 3), (3, 2), (3, 1)]
|
||||||
queme : [(3, 3), (3, 2), (3, 1), (2, 2)]
|
queme : [(3, 3), (3, 2), (3, 1), (2, 2)]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
# Build
|
||||||
|
Make sure the hatchling build system is installed
|
||||||
|
```bash
|
||||||
|
pip install hatchling
|
||||||
|
```
|
||||||
|
|
||||||
|
Navigate to the project folder then run the following.
|
||||||
|
```bash
|
||||||
|
python -m build .
|
||||||
|
```
|
||||||
|
|
||||||
|
# License
|
||||||
|
The included [wordlists](src/boggler/wordlists) are covered by their respective licenses. All other files MIT © Cameron Blankenbuehler
|
||||||
|
|||||||
@@ -1,31 +0,0 @@
|
|||||||
'''Module to generate random Boggle boards for testing'''
|
|
||||||
from sys import argv
|
|
||||||
from random import randint, shuffle
|
|
||||||
from math import sqrt
|
|
||||||
|
|
||||||
def read_dice_file(path):
|
|
||||||
# TODO: Factor in 'qu' die face
|
|
||||||
'''Return list of die strings from file ignoring all comments (#)'''
|
|
||||||
with open(path, 'r+', encoding="utf-8") as file:
|
|
||||||
return [line.rstrip().split(',') for line in file.readlines() if line[0] != "#"]
|
|
||||||
|
|
||||||
def roll_die(die: str):
|
|
||||||
'''Return a face of the given die string to simulate rolling a die'''
|
|
||||||
return str(die[randint(0, len(die) - 1)])
|
|
||||||
|
|
||||||
def roll_dice(dice: list[str]):
|
|
||||||
'''Return a random roll for each die'''
|
|
||||||
return [ roll_die(die) for die in dice ]
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
if len(argv) != 2:
|
|
||||||
print('Usage: python3 board_randomizer.py <dice_file>')
|
|
||||||
else:
|
|
||||||
dice_strings = read_dice_file(argv[1])
|
|
||||||
shuffle(dice_strings)
|
|
||||||
rolls = roll_dice(dice_strings)
|
|
||||||
|
|
||||||
# Format dice rolls for boards file
|
|
||||||
board_size = int(sqrt(len(rolls)))
|
|
||||||
for i in range(0, len(dice_strings), board_size):
|
|
||||||
print(",".join(rolls[i:i+board_size]))
|
|
||||||
-33
@@ -1,33 +0,0 @@
|
|||||||
"""Boggler Demo"""
|
|
||||||
import sys
|
|
||||||
from pathlib import Path
|
|
||||||
from boggler_utils import BoggleBoard, build_full_boggle_tree, read_boggle_file
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
if len(sys.argv) < 3 or len(sys.argv) > 4:
|
|
||||||
print('Usage: python3 boggler.py <BOARD_FILE> <WORDLISTS_DIR> [MAX_WORD_LENGTH]')
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
board = read_boggle_file(Path(sys.argv[1]))
|
|
||||||
if len(sys.argv) == 3:
|
|
||||||
boggle_board = BoggleBoard(board)
|
|
||||||
print(boggle_board)
|
|
||||||
boggle_tree = build_full_boggle_tree(boggle_board, Path(sys.argv[2]))
|
|
||||||
elif len(sys.argv) == 4:
|
|
||||||
try :
|
|
||||||
boggle_board = BoggleBoard(board, int(sys.argv[3]))
|
|
||||||
print(boggle_board)
|
|
||||||
boggle_tree = build_full_boggle_tree(boggle_board, Path(sys.argv[2]))
|
|
||||||
|
|
||||||
print("\nBOARD")
|
|
||||||
print(boggle_board)
|
|
||||||
|
|
||||||
for start_pos, tree in boggle_tree.items():
|
|
||||||
print(f"\nStarting @ {start_pos}...")
|
|
||||||
for word in tree.word_paths:
|
|
||||||
print(f"{word[0]: <{boggle_board.max_word_len}}: {word[1]}")
|
|
||||||
|
|
||||||
except ValueError as e:
|
|
||||||
print("The [MAX_WORD_LENGTH] argument must be an integer.")
|
|
||||||
print("Please try again.")
|
|
||||||
sys.exit(1)
|
|
||||||
Executable
+84
@@ -0,0 +1,84 @@
|
|||||||
|
"""Boggler Demo"""
|
||||||
|
from pprint import pprint
|
||||||
|
import argparse
|
||||||
|
import json
|
||||||
|
import csv
|
||||||
|
from itertools import chain
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
from .boggler_utils import BoggleBoard, build_full_boggle_tree, read_boggle_file
|
||||||
|
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
prog="boggler",
|
||||||
|
description="Boggle board game solver"
|
||||||
|
)
|
||||||
|
|
||||||
|
parser.add_argument("board", type=Path, help="Path to board CSV file")
|
||||||
|
parser.add_argument("wordlists", type=Path,
|
||||||
|
help="Path to directory of wordlist files. The directory must contain \
|
||||||
|
text files of the form words_X.txt where \"X\" is a character of \
|
||||||
|
the alphabet")
|
||||||
|
parser.add_argument("max_word_length", nargs="?", type=int, default=16,
|
||||||
|
help="Maximum length of words searched for on provided board")
|
||||||
|
parser.add_argument("-f", "--format", type=str,
|
||||||
|
help="Specify alternative output format including [txt, json]")
|
||||||
|
parser.add_argument("-p", "--include-path", action="store_true", default=False,
|
||||||
|
help="Include full paths for each word in output")
|
||||||
|
parser.add_argument("-s", "--sort", action="store_true", default=False,
|
||||||
|
help="Sort output alphabetically. By default the results are sorted by the starting \
|
||||||
|
block position on the board from top-to-bottom, left-to-right as given in the \
|
||||||
|
board file.")
|
||||||
|
parser.add_argument("-d", "--dedup", action="store_true", default=False,
|
||||||
|
help="Remove duplicates from word-only output. Note that de-duplication does not preserve \
|
||||||
|
the original order of the output, so it is recommended to also use the sort option when \
|
||||||
|
de-duplicating.")
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
def main():
|
||||||
|
"""Command line tool for sovling Boggle boards"""
|
||||||
|
board = read_boggle_file(args.board)
|
||||||
|
try:
|
||||||
|
boggle_board = BoggleBoard(board, args.max_word_length)
|
||||||
|
except ValueError:
|
||||||
|
print("Invalid MAX_WORD_LENGTH. Please try again with a valid integer.")
|
||||||
|
sys.exit()
|
||||||
|
|
||||||
|
boggle_tree = build_full_boggle_tree(boggle_board, Path(args.wordlists))
|
||||||
|
|
||||||
|
if args.format:
|
||||||
|
match args.format.lower():
|
||||||
|
case "txt":
|
||||||
|
word_paths = [start_block.word_paths for start_block in boggle_tree.values()]
|
||||||
|
data = list(chain(*word_paths))
|
||||||
|
if args.include_path:
|
||||||
|
data = [f"{line[0]} {line[1]}" for line in data]
|
||||||
|
else:
|
||||||
|
data = [x[0] for x in data]
|
||||||
|
if args.dedup:
|
||||||
|
data = list(set(data))
|
||||||
|
|
||||||
|
if args.sort:
|
||||||
|
data.sort()
|
||||||
|
|
||||||
|
for line in data:
|
||||||
|
print(line)
|
||||||
|
|
||||||
|
case "json":
|
||||||
|
data = { str(k):v.word_paths for k,v in boggle_tree.items()}
|
||||||
|
print(json.dumps(data, indent=2, sort_keys=True))
|
||||||
|
case _:
|
||||||
|
print(f"Invalid format (-f) option provided: \"{args.format}\"")
|
||||||
|
sys.exit()
|
||||||
|
|
||||||
|
else:
|
||||||
|
print("\nBOARD")
|
||||||
|
print(boggle_board)
|
||||||
|
|
||||||
|
for start_pos, tree in boggle_tree.items():
|
||||||
|
print(f"\nStarting @ {start_pos}...")
|
||||||
|
for word in tree.word_paths:
|
||||||
|
print(f"{word[0]: <{boggle_board.max_word_len}}: {word[1]}")
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
Executable
+46
@@ -0,0 +1,46 @@
|
|||||||
|
'''Module to generate random Boggle boards for testing'''
|
||||||
|
from sys import argv, stderr
|
||||||
|
from random import randint, shuffle
|
||||||
|
from math import sqrt, floor
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
def read_dice_file(dice_path: Path):
|
||||||
|
'''Return list of die strings from file ignoring all comments (#)'''
|
||||||
|
with open(dice_path, 'r+', encoding="utf-8") as file:
|
||||||
|
return [line.rstrip().split(',') for line in file.readlines() if line[0] != "#"]
|
||||||
|
|
||||||
|
def roll_die(die: str):
|
||||||
|
'''Return a face of the given die string to simulate rolling a die'''
|
||||||
|
return str(die[randint(0, len(die) - 1)])
|
||||||
|
|
||||||
|
def roll_dice(dice: list[str]):
|
||||||
|
'''Return a random roll for each die'''
|
||||||
|
return [ roll_die(die) for die in dice ]
|
||||||
|
|
||||||
|
def get_random_board(dice: list[str]):
|
||||||
|
shuffle(dice)
|
||||||
|
rolls = roll_dice(dice)
|
||||||
|
|
||||||
|
# Format dice rolls for boards file
|
||||||
|
board_size = int(floor(sqrt(len(rolls))))
|
||||||
|
board = []
|
||||||
|
for i in range(0, len(dice), board_size):
|
||||||
|
board.append(rolls[i:i+board_size])
|
||||||
|
return board
|
||||||
|
|
||||||
|
def get_random_board_csv(dice: list[str]):
|
||||||
|
board = get_random_board(dice)
|
||||||
|
board = [",".join(row) for row in board]
|
||||||
|
return board
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
if len(argv) != 2:
|
||||||
|
print('Usage: python3 board_randomizer.py <dice_file>')
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
dice = read_dice_file(Path(argv[1]))
|
||||||
|
board = get_random_board_csv(dice)
|
||||||
|
for r in range(0, int(floor(sqrt(len(dice))))):
|
||||||
|
print(board[r])
|
||||||
|
except Exception as e:
|
||||||
|
print("Argument must be a valid file path!", file=stderr)
|
||||||
Regular → Executable
+106
-88
@@ -1,17 +1,18 @@
|
|||||||
'''Boggler Utils'''
|
'''Boggler Utils'''
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
from os import path
|
from pathlib import Path
|
||||||
from multiprocessing import Pool
|
from multiprocessing import Pool
|
||||||
import sys
|
import logging as log
|
||||||
|
import functools
|
||||||
|
import operator
|
||||||
|
|
||||||
class BoardCell:
|
class BoardCell:
|
||||||
'''Boggle Board cell'''
|
'''Boggle Board cell'''
|
||||||
def __init__(self, row: int, col: int, letters: str,
|
def __init__(self, row: int, col: int, letters: str,
|
||||||
adjacent_cells: list[BoardCell] = None) -> BoardCell:
|
adjacent_cells: list[BoardCell] = None) -> None:
|
||||||
self.__row: int = row
|
self.__row: int = row
|
||||||
self.__col: int = col
|
self.__col: int = col
|
||||||
self.__pos: (int, int) = (self.__row, self.__col)
|
self.__pos: tuple[int, int] = (self.__row, self.__col)
|
||||||
self.__letters: str = letters
|
self.__letters: str = letters
|
||||||
self.__adjacent_cells: list[BoardCell] = adjacent_cells
|
self.__adjacent_cells: list[BoardCell] = adjacent_cells
|
||||||
|
|
||||||
@@ -26,7 +27,7 @@ class BoardCell:
|
|||||||
return self.__col
|
return self.__col
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def pos(self) -> (int, int):
|
def pos(self) -> tuple[int, int]:
|
||||||
'''Getter for pos property'''
|
'''Getter for pos property'''
|
||||||
return self.__pos
|
return self.__pos
|
||||||
|
|
||||||
@@ -52,11 +53,11 @@ class BoardCell:
|
|||||||
|
|
||||||
class BoggleBoard:
|
class BoggleBoard:
|
||||||
'''Boggle board structure'''
|
'''Boggle board structure'''
|
||||||
def __init__(self, board: list[list[str]], max_word_len: int = 14) -> BoggleBoard:
|
def __init__(self, board: list[list[str]], max_word_len: int = 14) -> None:
|
||||||
self.__height: int = len(board)
|
self.__height: int = len(board)
|
||||||
self.__width: int = len(board[0]) if self.__height > 0 else 0
|
self.__width: int = len(board[0]) if self.__height > 0 else 0
|
||||||
self.__board_list = board
|
self.__board_list = board
|
||||||
self.__board: dict[(int, int), BoardCell] = {}
|
self.__board: dict[tuple[int, int], BoardCell] = {}
|
||||||
|
|
||||||
# Max word length is limited by size of the board
|
# Max word length is limited by size of the board
|
||||||
self.__max_word_len = min(max_word_len, self.__width * self.__height)
|
self.__max_word_len = min(max_word_len, self.__width * self.__height)
|
||||||
@@ -103,7 +104,7 @@ class BoggleBoard:
|
|||||||
return self.__max_word_len
|
return self.__max_word_len
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def board(self) -> dict[(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
|
||||||
|
|
||||||
@@ -128,14 +129,14 @@ class BoggleBoard:
|
|||||||
indexes.append((row, col+1)) # right
|
indexes.append((row, col+1)) # right
|
||||||
return indexes
|
return indexes
|
||||||
|
|
||||||
def get_cell(self, row: int, col: int) -> str:
|
def get_cell(self, row: int, col: int) -> BoardCell:
|
||||||
'''Return the value at the specified row x column'''
|
'''Return the value at the specified row x column'''
|
||||||
return self.__board[(row, col)]
|
return self.__board[(row, col)]
|
||||||
|
|
||||||
class WordNode:
|
class WordNode:
|
||||||
'''A node describing a single letter in a WordTree.'''
|
'''A node describing a single letter in a WordTree.'''
|
||||||
def __init__(self, letters: str, is_word: bool = False, parent: WordNode = None,
|
def __init__(self, letters: str, is_word: bool = False, parent: WordNode = None,
|
||||||
children: dict[str, WordNode] = None, board_pos = None) -> WordNode:
|
children: dict[str, WordNode] = None, board_pos = (None, None)) -> None:
|
||||||
self.__letters = letters
|
self.__letters = letters
|
||||||
self.__is_word = is_word
|
self.__is_word = is_word
|
||||||
self.__children = children if children is not None else {}
|
self.__children = children if children is not None else {}
|
||||||
@@ -167,7 +168,7 @@ class WordNode:
|
|||||||
return self.__parent
|
return self.__parent
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def board_pos(self) -> (int, int):
|
def board_pos(self) -> tuple[int, int]:
|
||||||
'''Getter for board_pos property'''
|
'''Getter for board_pos property'''
|
||||||
return self.__board_pos
|
return self.__board_pos
|
||||||
|
|
||||||
@@ -199,14 +200,14 @@ class WordNode:
|
|||||||
|
|
||||||
class WordTree:
|
class WordTree:
|
||||||
'''A tree populated by WordNode(s) to complete words from a given root letter and wordlist'''
|
'''A tree populated by WordNode(s) to complete words from a given root letter and wordlist'''
|
||||||
def __init__(self, alphabet: str, root: WordNode, words: list[str] = None,
|
def __init__(self, alphabet: list[str], root: WordNode, words: list[str] = None,
|
||||||
max_word_len = 16) -> WordTree:
|
max_word_len = 16) -> None:
|
||||||
self.__alphabet: str = alphabet
|
self.__alphabet = alphabet
|
||||||
self.__wordlist: list[str] = words
|
self.__wordlist = words
|
||||||
self.__root: WordNode = root
|
self.__root = root
|
||||||
self.__max_word_len: int = max_word_len
|
self.__max_word_len = max_word_len
|
||||||
self.__tree: dict[str, WordNode] = {}
|
self.__tree: dict[str, WordNode] = {}
|
||||||
self.__word_paths: dict[str, list[WordNode]] = []
|
self.__word_paths: list[str, list[WordNode]] = []
|
||||||
|
|
||||||
# Generate root node
|
# Generate root node
|
||||||
self.__tree[root.letters] = root
|
self.__tree[root.letters] = root
|
||||||
@@ -218,7 +219,7 @@ class WordTree:
|
|||||||
self.__insert_word(word)
|
self.__insert_word(word)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def alphabet(self) -> str:
|
def alphabet(self) -> list:
|
||||||
'''Getter for alphabet property'''
|
'''Getter for alphabet property'''
|
||||||
return self.__alphabet
|
return self.__alphabet
|
||||||
|
|
||||||
@@ -252,11 +253,15 @@ class WordTree:
|
|||||||
self.__word_paths = value
|
self.__word_paths = value
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return ", ".join(self.wordlist)
|
return ", ".join([str(x) for x in self.word_paths])
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return self.__str__()
|
||||||
|
|
||||||
def insert_node(self, letters: str, parent: WordNode, is_word: bool = False,
|
def insert_node(self, letters: str, parent: WordNode, is_word: bool = False,
|
||||||
children: dict[str, WordNode] = None, board_pos = None):
|
children: dict[str, WordNode] = 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)
|
||||||
|
|
||||||
@@ -271,45 +276,28 @@ class WordTree:
|
|||||||
curr_node = self.tree[prefix]
|
curr_node = self.tree[prefix]
|
||||||
word_len = len(word)
|
word_len = len(word)
|
||||||
|
|
||||||
# Insert remaining nodes as letter groups based on alphabet
|
i = len(prefix)
|
||||||
skip_cnt = 0
|
i_max = min(self.max_word_len, len(word))
|
||||||
for i, letter in enumerate(word[len(prefix):self.max_word_len]):
|
while i < i_max:
|
||||||
# Skip word letter iterations for length of a previously inserted letter group
|
letters = word[i]
|
||||||
if skip_cnt > 0:
|
log.debug("1 letter seq: %s %s %s", word, letters, letters in curr_node.children)
|
||||||
skip_cnt -= 1
|
if letters not in self.alphabet:
|
||||||
continue
|
# Check two letter sequences like ("Qu", "Th", etc.) at current index
|
||||||
|
letters = word[i:i+2]
|
||||||
try:
|
log.debug("2 letter seq: %s %s %s", word, letters, letters in curr_node.children)
|
||||||
alpha_index = [x[0] for x in self.alphabet].index(letter)
|
if letters not in self.alphabet:
|
||||||
except ValueError: # letters not in given alphabet
|
|
||||||
return False
|
return False
|
||||||
|
# Insert node
|
||||||
|
if letters not in curr_node.children:
|
||||||
|
self.insert_node(letters, curr_node)
|
||||||
|
log.debug("3 letter seq: %s %s", word, letters)
|
||||||
|
|
||||||
# Insert letter (single)
|
curr_node = curr_node.children[letters]
|
||||||
if letter == self.alphabet[alpha_index] and letter not in curr_node.children:
|
|
||||||
self.insert_node(letter, curr_node)
|
|
||||||
curr_node = curr_node.children[letter]
|
|
||||||
# Letter already exists
|
|
||||||
elif letter == self.alphabet[alpha_index]:
|
|
||||||
curr_node = curr_node.children[letter]
|
|
||||||
# Check for letter groups (like "Qu") starting with `letter` in alphabet
|
|
||||||
else:
|
|
||||||
alpha = self.alphabet[alpha_index]
|
|
||||||
# Check that letter group is shorter than and matches in the word remainder
|
|
||||||
if len(alpha) < word_len - i and alpha == word[i+1:i+1+len(alpha)]:
|
|
||||||
if alpha not in curr_node.children:
|
|
||||||
self.insert_node(alpha, curr_node)
|
|
||||||
curr_node = curr_node.children[alpha]
|
|
||||||
else: # node already exist
|
|
||||||
curr_node = curr_node.children[alpha]
|
|
||||||
|
|
||||||
skip_cnt = len(alpha) - 1
|
i += len(letters)
|
||||||
#print(f"LONG GROUP: {alpha}; {word}")
|
|
||||||
else:
|
|
||||||
return False
|
|
||||||
|
|
||||||
# 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 = len(word) <= self.max_word_len
|
||||||
#print(f"Added: {word[: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:
|
||||||
@@ -317,6 +305,7 @@ class WordTree:
|
|||||||
if len(word) == 0 or word is None:
|
if len(word) == 0 or word is None:
|
||||||
return curr_node
|
return curr_node
|
||||||
|
|
||||||
|
log.debug("Searching... %s %s", word, curr_node)
|
||||||
if curr_node is None:
|
if curr_node is None:
|
||||||
curr_node = self.root
|
curr_node = self.root
|
||||||
for letters in curr_node.children:
|
for letters in curr_node.children:
|
||||||
@@ -327,7 +316,7 @@ class WordTree:
|
|||||||
# TODO: return all possible paths, maybe create minature WordTree? Or just of list of paths.
|
# TODO: return all possible paths, maybe create minature WordTree? Or just of list of paths.
|
||||||
return curr_node
|
return curr_node
|
||||||
|
|
||||||
def build_boggle_tree(self, board: BoggleBoard, board_cell: BoardCell, subtree: WordTree, depth: int = 1) -> WordTree:
|
def build_boggle_tree(self, board: BoggleBoard, board_cell: BoardCell, subtree: WordTree, word_len: int = 0) -> WordTree:
|
||||||
'''Return subtree of board given a particular root (first letter).
|
'''Return subtree of board given a particular root (first letter).
|
||||||
|
|
||||||
Keyword arguments:
|
Keyword arguments:
|
||||||
@@ -337,24 +326,24 @@ class WordTree:
|
|||||||
subtree -- the partial tree passed to the next recursive step for generating branches
|
subtree -- the partial tree passed to the next recursive step for generating branches
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
if word_len > self.max_word_len or word_len >= board.max_word_len:
|
||||||
|
log.debug(f"MAX WORD LENGTH REACHED! len = {word_len}")
|
||||||
|
subtree.active_node = subtree.active_node.parent
|
||||||
|
self.active_node = self.active_node.parent
|
||||||
|
return subtree
|
||||||
|
|
||||||
# TODO rework active_node refs for recursion so don't have to be reset to parent at every point of return
|
# TODO rework active_node refs for recursion so don't have to be reset to parent at every point of return
|
||||||
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))
|
||||||
#print("1: WORD FOUND:", "".join([board.board[x].letters for x in word_path]), word_path)
|
log.debug("1: WORD FOUND: %s %s", "".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
|
return subtree
|
||||||
elif self.active_node.is_word:
|
elif self.active_node.is_word:
|
||||||
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))
|
||||||
#print("2: WORD FOUND:", "".join([board.board[x].letters for x in word_path]), word_path)
|
log.debug("2: WORD FOUND: %s %s", "".join([board.board[x].letters for x in word_path]), word_path)
|
||||||
|
|
||||||
if depth >= self.max_word_len or depth >= board.max_word_len:
|
|
||||||
#print(f"MAX DEPTH REACHED! Depth = {depth}")
|
|
||||||
subtree.active_node = subtree.active_node.parent
|
|
||||||
self.active_node = self.active_node.parent
|
|
||||||
return
|
|
||||||
|
|
||||||
# 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:
|
||||||
@@ -364,8 +353,7 @@ class WordTree:
|
|||||||
subtree.active_node.add_child_node(new_node)
|
subtree.active_node.add_child_node(new_node)
|
||||||
subtree.active_node = subtree.active_node.children[cell.letters] # update subtree pointer
|
subtree.active_node = subtree.active_node.children[cell.letters] # update subtree pointer
|
||||||
self.active_node = self.active_node.children[cell.letters] # update dictionary tree pointer
|
self.active_node = self.active_node.children[cell.letters] # update dictionary tree pointer
|
||||||
self.build_boggle_tree(board, board.board[cell.pos], subtree, depth=depth+1)
|
self.build_boggle_tree(board, board.board[cell.pos], subtree, word_len=word_len+len(board_cell.letters))
|
||||||
#print(f"depth: {depth}")
|
|
||||||
|
|
||||||
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
|
||||||
@@ -379,23 +367,36 @@ def build_boggle_tree(args):
|
|||||||
sub_tree = WordTree(alphabet, WordNode(cell.letters, False, board_pos=cell.pos))
|
sub_tree = WordTree(alphabet, WordNode(cell.letters, False, board_pos=cell.pos))
|
||||||
return dict_tree.build_boggle_tree(board, cell, sub_tree)
|
return dict_tree.build_boggle_tree(board, cell, sub_tree)
|
||||||
|
|
||||||
def build_full_boggle_tree(board: BoggleBoard, wordlist_path: str) -> dict[str, WordTree]:
|
def build_full_boggle_tree(board: BoggleBoard, wordlist_path: Path) -> dict[str, WordTree]:
|
||||||
'''Return dictionary of WordTree(s) for every letter on a BoggleBoard'''
|
'''Return dictionary of WordTree(s) for every letter on a BoggleBoard'''
|
||||||
alphabet = sorted(set([cell.letters for cell in board.board.values()]))
|
alphabet = sorted(set([cell.letters for cell in board.board.values()]))
|
||||||
board_tree = {}
|
board_tree = {}
|
||||||
index = {}
|
index: dict[str] = {}
|
||||||
|
|
||||||
print("Reading in wordlists...")
|
log.info("Reading in wordlists...")
|
||||||
for letter in alphabet:
|
for letters in alphabet:
|
||||||
filename = "words_" + letter[0] + ".txt"
|
if letters == "":
|
||||||
print(f">> {letter}: {filename}")
|
# Skip wordlist read for blocks with empty string
|
||||||
wordlist = read_wordlist(path.join(path.abspath(wordlist_path), filename))
|
index[""] = {}
|
||||||
index[letter] = wordlist
|
continue
|
||||||
|
elif letters[0] in index:
|
||||||
|
index[letters] = index[letters[0]]
|
||||||
|
continue
|
||||||
|
|
||||||
print("Generating WordTrees...")
|
filename = "words_" + letters[0] + ".txt"
|
||||||
|
try:
|
||||||
|
wordlist = read_wordlist(Path(wordlist_path, filename))
|
||||||
|
index[letters] = wordlist
|
||||||
|
log.info(">> %s: %s", letters, filename)
|
||||||
|
except FileNotFoundError:
|
||||||
|
log.info(">> %s: -- Skipping -- no wordlist found for %s", letters, letters)
|
||||||
|
index[letters] = {}
|
||||||
|
|
||||||
|
log.info("Generating WordTrees...")
|
||||||
params = [ [alphabet, board, cell, index[cell.letters] ] for cell in board.board.values()]
|
params = [ [alphabet, board, cell, index[cell.letters] ] for cell in board.board.values()]
|
||||||
with Pool(processes=len(board.board)) as pool:
|
with Pool(processes=len(board.board)) as pool:
|
||||||
for i, res in enumerate(pool.map(build_boggle_tree, params)):
|
for i, res in enumerate(pool.map(build_boggle_tree, params)):
|
||||||
|
log.info(">> %s", params[i][2])
|
||||||
board_tree[params[i][2].pos] = res
|
board_tree[params[i][2].pos] = res
|
||||||
|
|
||||||
return board_tree
|
return board_tree
|
||||||
@@ -403,21 +404,38 @@ def build_full_boggle_tree(board: BoggleBoard, wordlist_path: str) -> dict[str,
|
|||||||
def read_wordlist(file):
|
def read_wordlist(file):
|
||||||
'''Return dictionary of words with associated word count (1 by default)'''
|
'''Return dictionary of words with associated word count (1 by default)'''
|
||||||
with open(file, 'r', encoding='utf-8') as file:
|
with open(file, 'r', encoding='utf-8') as file:
|
||||||
return {k:1 for k in file.read().split()}
|
return file.read().split()
|
||||||
|
|
||||||
|
class BadBoardFormat(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
def read_boggle_file(file):
|
def read_boggle_file(file):
|
||||||
'''Return list of rows from Boggle board csv file'''
|
'''Return list of rows from Boggle board csv file
|
||||||
|
|
||||||
|
The size of the board is determined by the width (number of comma-separated values)
|
||||||
|
of the first (non-empty) line in the file.
|
||||||
|
|
||||||
|
'''
|
||||||
with open(file, 'r', encoding='utf-8') as file:
|
with open(file, 'r', encoding='utf-8') as file:
|
||||||
return [x.rstrip().split(',') for x in file.readlines()]
|
board = []
|
||||||
|
|
||||||
if __name__ == "__main__":
|
board.append([x.strip() for x in file.readline().split(",")])
|
||||||
#b2 = read_boggle_file(sys.argv[1])
|
board_size = len(board[0])
|
||||||
b2 = read_boggle_file("boards/b1.csv")
|
for line in file.readlines():
|
||||||
b2_board = BoggleBoard(b2)
|
if line.strip() == "":
|
||||||
boggle_tree = build_full_boggle_tree(b2_board, "wordlists/dwyl")
|
raise BadBoardFormat("board files must contain no blank lines")
|
||||||
print("\nBOARD")
|
|
||||||
print(b2_board)
|
|
||||||
|
|
||||||
w1 = boggle_tree[(0,0)].word_paths
|
row = [x.strip() for x in line.strip().split(',')]
|
||||||
for w in w1:
|
if len(row) != board_size:
|
||||||
print(f"{w[0]: <{b2_board.max_word_len}}: {w[1]}")
|
raise BadBoardFormat("the length of each row must be the same")
|
||||||
|
|
||||||
|
board.append(row)
|
||||||
|
|
||||||
|
return board
|
||||||
|
|
||||||
|
def find_paths_by_word(board_letters, dictionary_path, max_len):
|
||||||
|
'''Return list of paths by word'''
|
||||||
|
boggle_board = BoggleBoard(board_letters, max_len)
|
||||||
|
boggle_tree = build_full_boggle_tree(boggle_board, dictionary_path)
|
||||||
|
paths_by_word = functools.reduce(operator.iconcat, [x.word_paths for x in boggle_tree.values()], [])
|
||||||
|
return paths_by_word
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
CC = gcc
|
||||||
|
CFLAGS = -Wall -Werror -std=gnu17 -g3
|
||||||
|
OBJS = boggler.o
|
||||||
|
|
||||||
|
all: $(OBJS)
|
||||||
|
$(CC) $(OBJS) -o boggler
|
||||||
|
|
||||||
|
boggler.o: boggler.c board.h tree.h
|
||||||
|
$(CC) $(CFLAGS) -c boggler.c
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f *~ *.o boggler
|
||||||
@@ -0,0 +1,140 @@
|
|||||||
|
#ifndef BOGGLER_UTILS
|
||||||
|
#define BOGGLER_UTILS
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
/* Board is structured like so when referencing cells
|
||||||
|
* x ->
|
||||||
|
* +---+---+---+---+
|
||||||
|
* y |0,0|1,0|2,0|3,0| -+
|
||||||
|
* | |---------------| |
|
||||||
|
* v |0,1|1,1|2,1|3,1| |
|
||||||
|
* |---------------| | height
|
||||||
|
* |0,2|1,2|2,2|3,2| |
|
||||||
|
* |---------------| |
|
||||||
|
* |0,3|1,3|2,3|3,3| -+
|
||||||
|
* +---+---+---+---+
|
||||||
|
* | |
|
||||||
|
* +-------------+
|
||||||
|
* width
|
||||||
|
*
|
||||||
|
* Vec2 = (x, y) = (col, row)
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct BoardCell {
|
||||||
|
char *letters;
|
||||||
|
uint8_t row;
|
||||||
|
uint8_t col;
|
||||||
|
struct BoardCell **adjacent_cells;
|
||||||
|
uint8_t adjacent_cnt;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct Vec2 {
|
||||||
|
uint8_t row;
|
||||||
|
uint8_t col;
|
||||||
|
} Vec2;
|
||||||
|
|
||||||
|
typedef struct Board {
|
||||||
|
/* BoardCells are indexed from top-to-bottom, left-to-right
|
||||||
|
* For instance a 3x3 board would have cells ordered like this
|
||||||
|
* +---+---+---+
|
||||||
|
* | 0 | 1 | 2 |
|
||||||
|
* |---+---+---|
|
||||||
|
* | 3 | 4 | 5 |
|
||||||
|
* |---+---+---|
|
||||||
|
* | 6 | 7 | 8 |
|
||||||
|
* +---+---+---+
|
||||||
|
*/
|
||||||
|
uint8_t height;
|
||||||
|
unsigned short width;
|
||||||
|
struct BoardCell **cells;
|
||||||
|
} Board;
|
||||||
|
|
||||||
|
struct BoardCell *create_board_cell(char *letters, uint8_t row, uint8_t col) {
|
||||||
|
/* BoardCells are created with 8 adjacent cells since that is the minimum required
|
||||||
|
* for centrally placed (not on sides or corners) cells on a board.
|
||||||
|
*/
|
||||||
|
struct BoardCell *cell = malloc(sizeof(struct BoardCell));
|
||||||
|
cell->letters = letters;
|
||||||
|
cell->row = row;
|
||||||
|
cell->col = col;
|
||||||
|
|
||||||
|
return cell;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct BoardCell *get_cell_at(Board *board, Vec2 pos) {
|
||||||
|
return board->cells[pos.row * pos.col + pos.col];
|
||||||
|
}
|
||||||
|
|
||||||
|
int print_boardcell(struct BoardCell *c) {
|
||||||
|
printf("%3s | (%d,%d)\n", c->letters, c->row, c->col);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int print_board(Board *board) {
|
||||||
|
uint8_t row_len = (board->width * 4 + 1) * sizeof(char) + 1;
|
||||||
|
char *row_delim = malloc(row_len);
|
||||||
|
snprintf(row_delim, 2, "%c", '+');
|
||||||
|
for (uint8_t i = 0; i < board->width; i++) {
|
||||||
|
strcat(row_delim, "----");
|
||||||
|
}
|
||||||
|
row_delim[strlen(row_delim)-1] = '+';
|
||||||
|
|
||||||
|
for (uint8_t row = 0; row < board->height; row++) {
|
||||||
|
printf("%s\n", row_delim);
|
||||||
|
for (uint8_t col = 0; col < board->width; col++) {
|
||||||
|
printf("|%2s ", board->cells[(row * board->width) + col]->letters);
|
||||||
|
}
|
||||||
|
puts("|");
|
||||||
|
}
|
||||||
|
printf("%s\n", row_delim);
|
||||||
|
|
||||||
|
free(row_delim);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void free_board(Board *b) {
|
||||||
|
if (b != NULL) {
|
||||||
|
for (uint8_t i = 0; i < b->width * b->height; i++) {
|
||||||
|
free(b->cells[i]);
|
||||||
|
b->cells[i] = NULL;
|
||||||
|
}
|
||||||
|
free(b->cells);
|
||||||
|
free(b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//Vec2[] get_adjacent_indexes(uint8_t board_size, Vec2 pos) {
|
||||||
|
// // Calculate total possible number of adjacent cells
|
||||||
|
// uint16_t = total_adj_cell_cnt = ((board-size-2) * (board-size-2) * 8) + ((board-size-2) * 4 * 5) + (4 * 3);
|
||||||
|
//
|
||||||
|
// Vec2[] indexes = malloc(8 * sizeof(Vec2));
|
||||||
|
// uint16_t i = 0;
|
||||||
|
// if (pos.row > 0) {
|
||||||
|
// indexes[i].append((row-1, col)) // up
|
||||||
|
// if col > 0:
|
||||||
|
// indexes.append((row-1, col-1)) // up-left
|
||||||
|
// if col < self.width - 1:
|
||||||
|
// indexes.append((row-1, col+1)) // up-right
|
||||||
|
// }
|
||||||
|
// if row < self.height - 1:
|
||||||
|
// indexes.append((row+1, col)) // down
|
||||||
|
// if col > 0:
|
||||||
|
// indexes.append((row+1, col-1)) // down-left
|
||||||
|
// if col < self.width - 1:
|
||||||
|
// indexes.append((row+1, col+1)) // down-right
|
||||||
|
// if col > 0:
|
||||||
|
// indexes.append((row, col-1)) // left
|
||||||
|
// if col < self.width - 1:
|
||||||
|
// indexes.append((row, col+1)) // right
|
||||||
|
//
|
||||||
|
// if (i != 8) {
|
||||||
|
// indexes = realloc(i * sizeof(Vec2));
|
||||||
|
// }
|
||||||
|
// return indexes
|
||||||
|
//}
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include "board.h"
|
||||||
|
#include "tree.h"
|
||||||
|
|
||||||
|
int main(int argc, char **argv) {
|
||||||
|
const uint8_t BOARD_SIZE = 4;
|
||||||
|
char *letters[] = {
|
||||||
|
"u", "n", "r", "e",
|
||||||
|
"qu","n", "i", "l",
|
||||||
|
"i", "s", "h", "a",
|
||||||
|
"s", "e", "l", "b"
|
||||||
|
};
|
||||||
|
char **cell_letters = malloc(BOARD_SIZE * sizeof(struct BoardCell*));
|
||||||
|
|
||||||
|
struct BoardCell **cells = malloc(sizeof(struct BoardCell*) * BOARD_SIZE * BOARD_SIZE);
|
||||||
|
for (uint8_t i = 0; i < BOARD_SIZE * BOARD_SIZE; i++) {
|
||||||
|
cells[i] = create_board_cell(letters[i], (int)(i / BOARD_SIZE), i % BOARD_SIZE);
|
||||||
|
print_boardcell(cells[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
Board board = { .height = 4, .width = 4, .cells = cells };
|
||||||
|
print_board(&board);
|
||||||
|
free(cell_letters);
|
||||||
|
|
||||||
|
struct WordNode *root = malloc(sizeof(struct WordNode) + sizeof(struct WordNode*));
|
||||||
|
init_wordnode(root, false, cells[0], NULL, NULL, 0);
|
||||||
|
add_child_node(&root, cells[5], false);
|
||||||
|
add_child_node(&root, cells[4], false);
|
||||||
|
print_wordnode(root);
|
||||||
|
print_wordnode_path(root->children[0]);
|
||||||
|
|
||||||
|
free_wordnode_children(root);
|
||||||
|
free(root);
|
||||||
|
for (uint8_t i = 0; i < BOARD_SIZE * BOARD_SIZE; i++) {
|
||||||
|
free(cells[i]);
|
||||||
|
}
|
||||||
|
free(cells);
|
||||||
|
}
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
#ifndef WORD_TREE
|
||||||
|
#define WORD_TREE
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include "board.h"
|
||||||
|
|
||||||
|
struct WordNode {
|
||||||
|
bool is_word;
|
||||||
|
struct BoardCell *cell;
|
||||||
|
struct WordNode *parent;
|
||||||
|
uint8_t child_cnt;
|
||||||
|
struct WordNode *children[];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct WordTree {
|
||||||
|
char *alphabet;
|
||||||
|
struct WordNode *root;
|
||||||
|
};
|
||||||
|
|
||||||
|
void *init_wordnode(struct WordNode *n, bool is_word, struct BoardCell *cell,
|
||||||
|
struct WordNode *parent, struct WordNode *children[], uint8_t child_cnt) {
|
||||||
|
n->is_word = is_word;
|
||||||
|
n->cell = cell;
|
||||||
|
n->parent = parent;
|
||||||
|
if (children != NULL) {
|
||||||
|
for (uint8_t i = 0; i < child_cnt; i++) {
|
||||||
|
n->children[i] = children[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
n->child_cnt = child_cnt;
|
||||||
|
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
void *print_wordnode(struct WordNode *n) {
|
||||||
|
if (n == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
printf("%3s | (%d,%d) | %3d\n", n->cell->letters, n->cell->row, n->cell->col, n->child_cnt);
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *print_wordnode_path(struct WordNode *n) {
|
||||||
|
if (n == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
struct WordNode *node = n;
|
||||||
|
|
||||||
|
// Print letters from leaf to root
|
||||||
|
do {
|
||||||
|
printf("%s < ", node->cell->letters);
|
||||||
|
node = node->parent;
|
||||||
|
} while(node != NULL);
|
||||||
|
puts("ROOT");
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct WordNode *add_child_node(struct WordNode **parent, struct BoardCell *cell, bool is_word) {
|
||||||
|
*parent = realloc(*parent, sizeof(struct WordNode) + sizeof(struct WordNode) * ((*parent)->child_cnt + 1));
|
||||||
|
(*parent)->children[(*parent)->child_cnt] = malloc(sizeof(struct WordNode));
|
||||||
|
init_wordnode((*parent)->children[(*parent)->child_cnt], is_word, cell, *parent, NULL, 0);
|
||||||
|
(*parent)->child_cnt++;
|
||||||
|
return (*parent)->children[(*parent)->child_cnt];
|
||||||
|
}
|
||||||
|
|
||||||
|
void free_wordnode_children(struct WordNode *n) {
|
||||||
|
// TODO: recursively free wordnode children
|
||||||
|
for (uint8_t i = 0; i < n->child_cnt; i++) {
|
||||||
|
free(n->children[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void *insert_word(struct WordTree *tree, char *word) {
|
||||||
|
// TODO: Traverse tree from tree->root and add nodes needed to complete `word`
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int search(struct WordTree *tree, char *word) {
|
||||||
|
// TODO: Traverse `tree` node-by-node and return an array of Board paths for `word`
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* WORD_TREE */
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
# Listed alphabetically
|
||||||
|
a,a,c,i,o,t
|
||||||
|
a,b,i,l,t,y
|
||||||
|
a,b,j,m,o,qu
|
||||||
|
a,c,d,e,m,p
|
||||||
|
a,c,e,l,r,s
|
||||||
|
a,d,e,n,v,z
|
||||||
|
a,h,m,o,r,s
|
||||||
|
b,i,f,o,r,x
|
||||||
|
d,e,n,o,s,w
|
||||||
|
d,k,n,o,t,u
|
||||||
|
e,e,f,h,i,y
|
||||||
|
e,g,k,l,u,y
|
||||||
|
e,g,i,n,t,v
|
||||||
|
e,h,i,n,p,s
|
||||||
|
e,l,p,s,t,u
|
||||||
|
g,i,l,r,u,w
|
||||||
|
@@ -0,0 +1,17 @@
|
|||||||
|
# Listed alphabetically
|
||||||
|
a,a,e,e,g,n
|
||||||
|
a,b,b,j,o,o
|
||||||
|
a,c,h,o,p,s
|
||||||
|
a,f,f,k,p,s
|
||||||
|
a,o,o,t,t,w
|
||||||
|
c,i,m,o,t,u
|
||||||
|
d,e,i,l,r,x
|
||||||
|
d,e,l,r,v,y
|
||||||
|
d,i,s,t,t,y
|
||||||
|
e,e,g,h,n,w
|
||||||
|
e,e,i,n,s,u
|
||||||
|
e,h,r,t,v,w
|
||||||
|
e,i,o,s,s,t
|
||||||
|
e,l,r,t,t,y
|
||||||
|
h,i,m,n,qu,u
|
||||||
|
h,l,n,n,r,z
|
||||||
|
@@ -0,0 +1,27 @@
|
|||||||
|
# Listed alphabetically
|
||||||
|
a,a,a,f,r,s
|
||||||
|
a,a,e,e,e,e
|
||||||
|
a,a,f,i,r,s
|
||||||
|
a,d,e,n,n,n
|
||||||
|
a,e,e,e,e,m
|
||||||
|
a,e,e,g,m,u
|
||||||
|
a,e,g,m,n,n
|
||||||
|
a,f,i,r,s,y
|
||||||
|
an,er,he,in,qu,th
|
||||||
|
b,j,k,qu,x,z
|
||||||
|
c,c,e,n,s,t
|
||||||
|
c,e,i,i,l,t
|
||||||
|
c,e,i,l,p,t
|
||||||
|
c,e,i,p,s,t
|
||||||
|
d,d,h,n,o,t
|
||||||
|
d,h,h,l,n,o
|
||||||
|
d,h,l,n,o,r
|
||||||
|
d,l,o,h,h,r
|
||||||
|
e,i,i,i,t,t
|
||||||
|
e,m,o,t,t,t
|
||||||
|
e,n,s,s,s,u
|
||||||
|
f,i,p,r,s,y
|
||||||
|
g,o,r,r,v,w
|
||||||
|
i,k,l,qu,u,w
|
||||||
|
n,o,o,t,u,w
|
||||||
|
o,o,o,t,t,u
|
||||||
|
@@ -0,0 +1,37 @@
|
|||||||
|
# Listed alphabetically
|
||||||
|
_,_,_,e,i,o
|
||||||
|
a,a,a,f,r,s
|
||||||
|
a,a,e,e,e,e
|
||||||
|
a,a,e,e,o,o
|
||||||
|
a,a,f,i,r,s
|
||||||
|
a,b,d,e,i,o
|
||||||
|
a,d,e,n,n,n
|
||||||
|
a,e,e,e,e,m
|
||||||
|
a,e,e,g,m,u
|
||||||
|
a,e,g,m,n,n
|
||||||
|
a,e,i,l,m,n
|
||||||
|
a,e,i,n,o,u
|
||||||
|
a,f,i,r,s,y
|
||||||
|
an,er,he,in,qu,th
|
||||||
|
b,b,b,j,j,z
|
||||||
|
c,c,e,n,s,t
|
||||||
|
c,d,d,l,n,n
|
||||||
|
c,e,e,i,t,t
|
||||||
|
c,e,i,p,s,t
|
||||||
|
c,f,g,n,u,w
|
||||||
|
d,d,h,n,o,t
|
||||||
|
d,h,h,l,o,r
|
||||||
|
d,h,h,n,o,w
|
||||||
|
d,h,l,n,o,r
|
||||||
|
e,h,i,l,r,s
|
||||||
|
e,i,i,l,s,t
|
||||||
|
e,i,l,p,s,t
|
||||||
|
e,m,o,t,t,t
|
||||||
|
e,n,s,s,s,u
|
||||||
|
g,o,r,r,v,w
|
||||||
|
h,i,r,s,t,v
|
||||||
|
h,o,p,r,s,t
|
||||||
|
i,p,r,s,y,y
|
||||||
|
j,k,qu,w,x,z
|
||||||
|
n,o,o,t,u,w
|
||||||
|
o,o,o,t,t,u
|
||||||
|
@@ -0,0 +1,82 @@
|
|||||||
|
# Made from 6x6 dice repeated to reach 9x9 (81) dice rolls
|
||||||
|
_,_,_,e,i,o
|
||||||
|
a,a,a,f,r,s
|
||||||
|
a,a,e,e,e,e
|
||||||
|
a,a,e,e,o,o
|
||||||
|
a,a,f,i,r,s
|
||||||
|
a,b,d,e,i,o
|
||||||
|
a,d,e,n,n,n
|
||||||
|
a,e,e,e,e,m
|
||||||
|
a,e,e,g,m,u
|
||||||
|
a,e,g,m,n,n
|
||||||
|
a,e,i,l,m,n
|
||||||
|
a,e,i,n,o,u
|
||||||
|
a,f,i,r,s,y
|
||||||
|
an,er,he,in,qu,th
|
||||||
|
b,b,b,j,j,z
|
||||||
|
c,c,e,n,s,t
|
||||||
|
c,d,d,l,n,n
|
||||||
|
c,e,e,i,t,t
|
||||||
|
c,e,i,p,s,t
|
||||||
|
c,f,g,n,u,w
|
||||||
|
d,d,h,n,o,t
|
||||||
|
d,h,h,l,o,r
|
||||||
|
d,h,h,n,o,w
|
||||||
|
d,h,l,n,o,r
|
||||||
|
e,h,i,l,r,s
|
||||||
|
e,i,i,l,s,t
|
||||||
|
e,i,l,p,s,t
|
||||||
|
e,m,o,t,t,t
|
||||||
|
e,n,s,s,s,u
|
||||||
|
g,o,r,r,v,w
|
||||||
|
h,i,r,s,t,v
|
||||||
|
h,o,p,r,s,t
|
||||||
|
i,p,r,s,y,y
|
||||||
|
j,k,qu,w,x,z
|
||||||
|
n,o,o,t,u,w
|
||||||
|
o,o,o,t,t,u
|
||||||
|
_,_,_,e,i,o
|
||||||
|
a,a,a,f,r,s
|
||||||
|
a,a,e,e,e,e
|
||||||
|
a,a,e,e,o,o
|
||||||
|
a,a,f,i,r,s
|
||||||
|
a,b,d,e,i,o
|
||||||
|
a,d,e,n,n,n
|
||||||
|
a,e,e,e,e,m
|
||||||
|
a,e,e,g,m,u
|
||||||
|
a,e,g,m,n,n
|
||||||
|
a,e,i,l,m,n
|
||||||
|
a,e,i,n,o,u
|
||||||
|
a,f,i,r,s,y
|
||||||
|
an,er,he,in,qu,th
|
||||||
|
b,b,b,j,j,z
|
||||||
|
c,c,e,n,s,t
|
||||||
|
c,d,d,l,n,n
|
||||||
|
c,e,e,i,t,t
|
||||||
|
c,e,i,p,s,t
|
||||||
|
c,f,g,n,u,w
|
||||||
|
d,d,h,n,o,t
|
||||||
|
d,h,h,l,o,r
|
||||||
|
d,h,h,n,o,w
|
||||||
|
d,h,l,n,o,r
|
||||||
|
e,h,i,l,r,s
|
||||||
|
e,i,i,l,s,t
|
||||||
|
e,i,l,p,s,t
|
||||||
|
e,m,o,t,t,t
|
||||||
|
e,n,s,s,s,u
|
||||||
|
g,o,r,r,v,w
|
||||||
|
h,i,r,s,t,v
|
||||||
|
h,o,p,r,s,t
|
||||||
|
i,p,r,s,y,y
|
||||||
|
j,k,qu,w,x,z
|
||||||
|
n,o,o,t,u,w
|
||||||
|
o,o,o,t,t,u
|
||||||
|
e,m,o,t,t,t
|
||||||
|
e,n,s,s,s,u
|
||||||
|
g,o,r,r,v,w
|
||||||
|
h,i,r,s,t,v
|
||||||
|
h,o,p,r,s,t
|
||||||
|
i,p,r,s,y,y
|
||||||
|
j,k,qu,w,x,z
|
||||||
|
n,o,o,t,u,w
|
||||||
|
o,o,o,t,t,u
|
||||||
|
Executable
+35
@@ -0,0 +1,35 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
if [ -z $1 ] || [ -z $2 ]; then
|
||||||
|
echo "Usage: WORDLIST OUT_DIR"
|
||||||
|
exit 0
|
||||||
|
else
|
||||||
|
rg "(^a[a-z]+).*$" -or '$1' $1 | sort | uniq > "$2/words_a.txt"
|
||||||
|
rg "(^b[a-z]+).*$" -or '$1' $1 | sort | uniq > "$2/words_b.txt"
|
||||||
|
rg "(^c[a-z]+).*$" -or '$1' $1 | sort | uniq > "$2/words_c.txt"
|
||||||
|
rg "(^d[a-z]+).*$" -or '$1' $1 | sort | uniq > "$2/words_d.txt"
|
||||||
|
rg "(^e[a-z]+).*$" -or '$1' $1 | sort | uniq > "$2/words_e.txt"
|
||||||
|
rg "(^f[a-z]+).*$" -or '$1' $1 | sort | uniq > "$2/words_f.txt"
|
||||||
|
rg "(^g[a-z]+).*$" -or '$1' $1 | sort | uniq > "$2/words_g.txt"
|
||||||
|
rg "(^h[a-z]+).*$" -or '$1' $1 | sort | uniq > "$2/words_h.txt"
|
||||||
|
rg "(^i[a-z]+).*$" -or '$1' $1 | sort | uniq > "$2/words_i.txt"
|
||||||
|
rg "(^j[a-z]+).*$" -or '$1' $1 | sort | uniq > "$2/words_j.txt"
|
||||||
|
rg "(^k[a-z]+).*$" -or '$1' $1 | sort | uniq > "$2/words_k.txt"
|
||||||
|
rg "(^l[a-z]+).*$" -or '$1' $1 | sort | uniq > "$2/words_l.txt"
|
||||||
|
rg "(^m[a-z]+).*$" -or '$1' $1 | sort | uniq > "$2/words_m.txt"
|
||||||
|
rg "(^n[a-z]+).*$" -or '$1' $1 | sort | uniq > "$2/words_n.txt"
|
||||||
|
rg "(^o[a-z]+).*$" -or '$1' $1 | sort | uniq > "$2/words_o.txt"
|
||||||
|
rg "(^p[a-z]+).*$" -or '$1' $1 | sort | uniq > "$2/words_p.txt"
|
||||||
|
rg "(^q[a-z]+).*$" -or '$1' $1 | sort | uniq > "$2/words_q.txt"
|
||||||
|
rg "(^r[a-z]+).*$" -or '$1' $1 | sort | uniq > "$2/words_r.txt"
|
||||||
|
rg "(^s[a-z]+).*$" -or '$1' $1 | sort | uniq > "$2/words_s.txt"
|
||||||
|
rg "(^t[a-z]+).*$" -or '$1' $1 | sort | uniq > "$2/words_t.txt"
|
||||||
|
rg "(^u[a-z]+).*$" -or '$1' $1 | sort | uniq > "$2/words_u.txt"
|
||||||
|
rg "(^v[a-z]+).*$" -or '$1' $1 | sort | uniq > "$2/words_v.txt"
|
||||||
|
rg "(^w[a-z]+).*$" -or '$1' $1 | sort | uniq > "$2/words_w.txt"
|
||||||
|
rg "(^x[a-z]+).*$" -or '$1' $1 | sort | uniq > "$2/words_x.txt"
|
||||||
|
rg "(^y[a-z]+).*$" -or '$1' $1 | sort | uniq > "$2/words_y.txt"
|
||||||
|
rg "(^z[a-z]+).*$" -or '$1' $1 | sort | uniq > "$2/words_z.txt"
|
||||||
|
touch "$2/words__.txt"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
# front,right,back,left,top,bottom (start with 'one of' the first alphabetical chars
|
|
||||||
a,h,s,p,o,c
|
|
||||||
e,r,l,i,x,d
|
|
||||||
a,g,a,e,e,a
|
|
||||||
e,y,t,l,r,t
|
|
||||||
e,o,t,s,s,i
|
|
||||||
d,r,y,v,l,e
|
|
||||||
h,n,l,n,z,r
|
|
||||||
e,r,w,t,v,h
|
|
||||||
h,i,u,n,m,qu
|
|
||||||
e,e,h,n,w,g
|
|
||||||
d,t,y,t,i,s
|
|
||||||
a,b,b,o,o,j
|
|
||||||
e,i,u,n,s,e
|
|
||||||
a,s,p,f,k,f
|
|
||||||
c,m,u,o,i,t
|
|
||||||
a,w,t,o,o,t
|
|
||||||
|
Generated
+394
@@ -0,0 +1,394 @@
|
|||||||
|
# This file is automatically @generated by Poetry 1.4.2 and should not be changed by hand.
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "astroid"
|
||||||
|
version = "2.15.5"
|
||||||
|
description = "An abstract syntax tree for Python with inference support."
|
||||||
|
category = "dev"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.7.2"
|
||||||
|
files = [
|
||||||
|
{file = "astroid-2.15.5-py3-none-any.whl", hash = "sha256:078e5212f9885fa85fbb0cf0101978a336190aadea6e13305409d099f71b2324"},
|
||||||
|
{file = "astroid-2.15.5.tar.gz", hash = "sha256:1039262575027b441137ab4a62a793a9b43defb42c32d5670f38686207cd780f"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
lazy-object-proxy = ">=1.4.0"
|
||||||
|
wrapt = {version = ">=1.14,<2", markers = "python_version >= \"3.11\""}
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "colorama"
|
||||||
|
version = "0.4.6"
|
||||||
|
description = "Cross-platform colored terminal text."
|
||||||
|
category = "dev"
|
||||||
|
optional = false
|
||||||
|
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
|
||||||
|
files = [
|
||||||
|
{file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"},
|
||||||
|
{file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "dill"
|
||||||
|
version = "0.3.6"
|
||||||
|
description = "serialize all of python"
|
||||||
|
category = "dev"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.7"
|
||||||
|
files = [
|
||||||
|
{file = "dill-0.3.6-py3-none-any.whl", hash = "sha256:a07ffd2351b8c678dfc4a856a3005f8067aea51d6ba6c700796a4d9e280f39f0"},
|
||||||
|
{file = "dill-0.3.6.tar.gz", hash = "sha256:e5db55f3687856d8fbdab002ed78544e1c4559a130302693d839dfe8f93f2373"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
graph = ["objgraph (>=1.7.2)"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "iniconfig"
|
||||||
|
version = "2.0.0"
|
||||||
|
description = "brain-dead simple config-ini parsing"
|
||||||
|
category = "dev"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.7"
|
||||||
|
files = [
|
||||||
|
{file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"},
|
||||||
|
{file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "isort"
|
||||||
|
version = "5.12.0"
|
||||||
|
description = "A Python utility / library to sort Python imports."
|
||||||
|
category = "dev"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.8.0"
|
||||||
|
files = [
|
||||||
|
{file = "isort-5.12.0-py3-none-any.whl", hash = "sha256:f84c2818376e66cf843d497486ea8fed8700b340f308f076c6fb1229dff318b6"},
|
||||||
|
{file = "isort-5.12.0.tar.gz", hash = "sha256:8bef7dde241278824a6d83f44a544709b065191b95b6e50894bdc722fcba0504"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
colors = ["colorama (>=0.4.3)"]
|
||||||
|
pipfile-deprecated-finder = ["pip-shims (>=0.5.2)", "pipreqs", "requirementslib"]
|
||||||
|
plugins = ["setuptools"]
|
||||||
|
requirements-deprecated-finder = ["pip-api", "pipreqs"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lazy-object-proxy"
|
||||||
|
version = "1.9.0"
|
||||||
|
description = "A fast and thorough lazy object proxy."
|
||||||
|
category = "dev"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.7"
|
||||||
|
files = [
|
||||||
|
{file = "lazy-object-proxy-1.9.0.tar.gz", hash = "sha256:659fb5809fa4629b8a1ac5106f669cfc7bef26fbb389dda53b3e010d1ac4ebae"},
|
||||||
|
{file = "lazy_object_proxy-1.9.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b40387277b0ed2d0602b8293b94d7257e17d1479e257b4de114ea11a8cb7f2d7"},
|
||||||
|
{file = "lazy_object_proxy-1.9.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8c6cfb338b133fbdbc5cfaa10fe3c6aeea827db80c978dbd13bc9dd8526b7d4"},
|
||||||
|
{file = "lazy_object_proxy-1.9.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:721532711daa7db0d8b779b0bb0318fa87af1c10d7fe5e52ef30f8eff254d0cd"},
|
||||||
|
{file = "lazy_object_proxy-1.9.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:66a3de4a3ec06cd8af3f61b8e1ec67614fbb7c995d02fa224813cb7afefee701"},
|
||||||
|
{file = "lazy_object_proxy-1.9.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:1aa3de4088c89a1b69f8ec0dcc169aa725b0ff017899ac568fe44ddc1396df46"},
|
||||||
|
{file = "lazy_object_proxy-1.9.0-cp310-cp310-win32.whl", hash = "sha256:f0705c376533ed2a9e5e97aacdbfe04cecd71e0aa84c7c0595d02ef93b6e4455"},
|
||||||
|
{file = "lazy_object_proxy-1.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:ea806fd4c37bf7e7ad82537b0757999264d5f70c45468447bb2b91afdbe73a6e"},
|
||||||
|
{file = "lazy_object_proxy-1.9.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:946d27deaff6cf8452ed0dba83ba38839a87f4f7a9732e8f9fd4107b21e6ff07"},
|
||||||
|
{file = "lazy_object_proxy-1.9.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:79a31b086e7e68b24b99b23d57723ef7e2c6d81ed21007b6281ebcd1688acb0a"},
|
||||||
|
{file = "lazy_object_proxy-1.9.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f699ac1c768270c9e384e4cbd268d6e67aebcfae6cd623b4d7c3bfde5a35db59"},
|
||||||
|
{file = "lazy_object_proxy-1.9.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:bfb38f9ffb53b942f2b5954e0f610f1e721ccebe9cce9025a38c8ccf4a5183a4"},
|
||||||
|
{file = "lazy_object_proxy-1.9.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:189bbd5d41ae7a498397287c408617fe5c48633e7755287b21d741f7db2706a9"},
|
||||||
|
{file = "lazy_object_proxy-1.9.0-cp311-cp311-win32.whl", hash = "sha256:81fc4d08b062b535d95c9ea70dbe8a335c45c04029878e62d744bdced5141586"},
|
||||||
|
{file = "lazy_object_proxy-1.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:f2457189d8257dd41ae9b434ba33298aec198e30adf2dcdaaa3a28b9994f6adb"},
|
||||||
|
{file = "lazy_object_proxy-1.9.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:d9e25ef10a39e8afe59a5c348a4dbf29b4868ab76269f81ce1674494e2565a6e"},
|
||||||
|
{file = "lazy_object_proxy-1.9.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cbf9b082426036e19c6924a9ce90c740a9861e2bdc27a4834fd0a910742ac1e8"},
|
||||||
|
{file = "lazy_object_proxy-1.9.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9f5fa4a61ce2438267163891961cfd5e32ec97a2c444e5b842d574251ade27d2"},
|
||||||
|
{file = "lazy_object_proxy-1.9.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:8fa02eaab317b1e9e03f69aab1f91e120e7899b392c4fc19807a8278a07a97e8"},
|
||||||
|
{file = "lazy_object_proxy-1.9.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:e7c21c95cae3c05c14aafffe2865bbd5e377cfc1348c4f7751d9dc9a48ca4bda"},
|
||||||
|
{file = "lazy_object_proxy-1.9.0-cp37-cp37m-win32.whl", hash = "sha256:f12ad7126ae0c98d601a7ee504c1122bcef553d1d5e0c3bfa77b16b3968d2734"},
|
||||||
|
{file = "lazy_object_proxy-1.9.0-cp37-cp37m-win_amd64.whl", hash = "sha256:edd20c5a55acb67c7ed471fa2b5fb66cb17f61430b7a6b9c3b4a1e40293b1671"},
|
||||||
|
{file = "lazy_object_proxy-1.9.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2d0daa332786cf3bb49e10dc6a17a52f6a8f9601b4cf5c295a4f85854d61de63"},
|
||||||
|
{file = "lazy_object_proxy-1.9.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cd077f3d04a58e83d04b20e334f678c2b0ff9879b9375ed107d5d07ff160171"},
|
||||||
|
{file = "lazy_object_proxy-1.9.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:660c94ea760b3ce47d1855a30984c78327500493d396eac4dfd8bd82041b22be"},
|
||||||
|
{file = "lazy_object_proxy-1.9.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:212774e4dfa851e74d393a2370871e174d7ff0ebc980907723bb67d25c8a7c30"},
|
||||||
|
{file = "lazy_object_proxy-1.9.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:f0117049dd1d5635bbff65444496c90e0baa48ea405125c088e93d9cf4525b11"},
|
||||||
|
{file = "lazy_object_proxy-1.9.0-cp38-cp38-win32.whl", hash = "sha256:0a891e4e41b54fd5b8313b96399f8b0e173bbbfc03c7631f01efbe29bb0bcf82"},
|
||||||
|
{file = "lazy_object_proxy-1.9.0-cp38-cp38-win_amd64.whl", hash = "sha256:9990d8e71b9f6488e91ad25f322898c136b008d87bf852ff65391b004da5e17b"},
|
||||||
|
{file = "lazy_object_proxy-1.9.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9e7551208b2aded9c1447453ee366f1c4070602b3d932ace044715d89666899b"},
|
||||||
|
{file = "lazy_object_proxy-1.9.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5f83ac4d83ef0ab017683d715ed356e30dd48a93746309c8f3517e1287523ef4"},
|
||||||
|
{file = "lazy_object_proxy-1.9.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7322c3d6f1766d4ef1e51a465f47955f1e8123caee67dd641e67d539a534d006"},
|
||||||
|
{file = "lazy_object_proxy-1.9.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:18b78ec83edbbeb69efdc0e9c1cb41a3b1b1ed11ddd8ded602464c3fc6020494"},
|
||||||
|
{file = "lazy_object_proxy-1.9.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:09763491ce220c0299688940f8dc2c5d05fd1f45af1e42e636b2e8b2303e4382"},
|
||||||
|
{file = "lazy_object_proxy-1.9.0-cp39-cp39-win32.whl", hash = "sha256:9090d8e53235aa280fc9239a86ae3ea8ac58eff66a705fa6aa2ec4968b95c821"},
|
||||||
|
{file = "lazy_object_proxy-1.9.0-cp39-cp39-win_amd64.whl", hash = "sha256:db1c1722726f47e10e0b5fdbf15ac3b8adb58c091d12b3ab713965795036985f"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "mccabe"
|
||||||
|
version = "0.7.0"
|
||||||
|
description = "McCabe checker, plugin for flake8"
|
||||||
|
category = "dev"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.6"
|
||||||
|
files = [
|
||||||
|
{file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"},
|
||||||
|
{file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "mypy"
|
||||||
|
version = "1.3.0"
|
||||||
|
description = "Optional static typing for Python"
|
||||||
|
category = "dev"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.7"
|
||||||
|
files = [
|
||||||
|
{file = "mypy-1.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c1eb485cea53f4f5284e5baf92902cd0088b24984f4209e25981cc359d64448d"},
|
||||||
|
{file = "mypy-1.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4c99c3ecf223cf2952638da9cd82793d8f3c0c5fa8b6ae2b2d9ed1e1ff51ba85"},
|
||||||
|
{file = "mypy-1.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:550a8b3a19bb6589679a7c3c31f64312e7ff482a816c96e0cecec9ad3a7564dd"},
|
||||||
|
{file = "mypy-1.3.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:cbc07246253b9e3d7d74c9ff948cd0fd7a71afcc2b77c7f0a59c26e9395cb152"},
|
||||||
|
{file = "mypy-1.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:a22435632710a4fcf8acf86cbd0d69f68ac389a3892cb23fbad176d1cddaf228"},
|
||||||
|
{file = "mypy-1.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6e33bb8b2613614a33dff70565f4c803f889ebd2f859466e42b46e1df76018dd"},
|
||||||
|
{file = "mypy-1.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7d23370d2a6b7a71dc65d1266f9a34e4cde9e8e21511322415db4b26f46f6b8c"},
|
||||||
|
{file = "mypy-1.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:658fe7b674769a0770d4b26cb4d6f005e88a442fe82446f020be8e5f5efb2fae"},
|
||||||
|
{file = "mypy-1.3.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:6e42d29e324cdda61daaec2336c42512e59c7c375340bd202efa1fe0f7b8f8ca"},
|
||||||
|
{file = "mypy-1.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:d0b6c62206e04061e27009481cb0ec966f7d6172b5b936f3ead3d74f29fe3dcf"},
|
||||||
|
{file = "mypy-1.3.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:76ec771e2342f1b558c36d49900dfe81d140361dd0d2df6cd71b3db1be155409"},
|
||||||
|
{file = "mypy-1.3.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ebc95f8386314272bbc817026f8ce8f4f0d2ef7ae44f947c4664efac9adec929"},
|
||||||
|
{file = "mypy-1.3.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:faff86aa10c1aa4a10e1a301de160f3d8fc8703b88c7e98de46b531ff1276a9a"},
|
||||||
|
{file = "mypy-1.3.0-cp37-cp37m-win_amd64.whl", hash = "sha256:8c5979d0deb27e0f4479bee18ea0f83732a893e81b78e62e2dda3e7e518c92ee"},
|
||||||
|
{file = "mypy-1.3.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:c5d2cc54175bab47011b09688b418db71403aefad07cbcd62d44010543fc143f"},
|
||||||
|
{file = "mypy-1.3.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:87df44954c31d86df96c8bd6e80dfcd773473e877ac6176a8e29898bfb3501cb"},
|
||||||
|
{file = "mypy-1.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:473117e310febe632ddf10e745a355714e771ffe534f06db40702775056614c4"},
|
||||||
|
{file = "mypy-1.3.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:74bc9b6e0e79808bf8678d7678b2ae3736ea72d56eede3820bd3849823e7f305"},
|
||||||
|
{file = "mypy-1.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:44797d031a41516fcf5cbfa652265bb994e53e51994c1bd649ffcd0c3a7eccbf"},
|
||||||
|
{file = "mypy-1.3.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ddae0f39ca146972ff6bb4399f3b2943884a774b8771ea0a8f50e971f5ea5ba8"},
|
||||||
|
{file = "mypy-1.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1c4c42c60a8103ead4c1c060ac3cdd3ff01e18fddce6f1016e08939647a0e703"},
|
||||||
|
{file = "mypy-1.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e86c2c6852f62f8f2b24cb7a613ebe8e0c7dc1402c61d36a609174f63e0ff017"},
|
||||||
|
{file = "mypy-1.3.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:f9dca1e257d4cc129517779226753dbefb4f2266c4eaad610fc15c6a7e14283e"},
|
||||||
|
{file = "mypy-1.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:95d8d31a7713510685b05fbb18d6ac287a56c8f6554d88c19e73f724a445448a"},
|
||||||
|
{file = "mypy-1.3.0-py3-none-any.whl", hash = "sha256:a8763e72d5d9574d45ce5881962bc8e9046bf7b375b0abf031f3e6811732a897"},
|
||||||
|
{file = "mypy-1.3.0.tar.gz", hash = "sha256:e1f4d16e296f5135624b34e8fb741eb0eadedca90862405b1f1fde2040b9bd11"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
mypy-extensions = ">=1.0.0"
|
||||||
|
typing-extensions = ">=3.10"
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
dmypy = ["psutil (>=4.0)"]
|
||||||
|
install-types = ["pip"]
|
||||||
|
python2 = ["typed-ast (>=1.4.0,<2)"]
|
||||||
|
reports = ["lxml"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "mypy-extensions"
|
||||||
|
version = "1.0.0"
|
||||||
|
description = "Type system extensions for programs checked with the mypy type checker."
|
||||||
|
category = "dev"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.5"
|
||||||
|
files = [
|
||||||
|
{file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"},
|
||||||
|
{file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "packaging"
|
||||||
|
version = "23.1"
|
||||||
|
description = "Core utilities for Python packages"
|
||||||
|
category = "dev"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.7"
|
||||||
|
files = [
|
||||||
|
{file = "packaging-23.1-py3-none-any.whl", hash = "sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61"},
|
||||||
|
{file = "packaging-23.1.tar.gz", hash = "sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "platformdirs"
|
||||||
|
version = "3.5.1"
|
||||||
|
description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"."
|
||||||
|
category = "dev"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.7"
|
||||||
|
files = [
|
||||||
|
{file = "platformdirs-3.5.1-py3-none-any.whl", hash = "sha256:e2378146f1964972c03c085bb5662ae80b2b8c06226c54b2ff4aa9483e8a13a5"},
|
||||||
|
{file = "platformdirs-3.5.1.tar.gz", hash = "sha256:412dae91f52a6f84830f39a8078cecd0e866cb72294a5c66808e74d5e88d251f"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
docs = ["furo (>=2023.3.27)", "proselint (>=0.13)", "sphinx (>=6.2.1)", "sphinx-autodoc-typehints (>=1.23,!=1.23.4)"]
|
||||||
|
test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.3.1)", "pytest-cov (>=4)", "pytest-mock (>=3.10)"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pluggy"
|
||||||
|
version = "1.0.0"
|
||||||
|
description = "plugin and hook calling mechanisms for python"
|
||||||
|
category = "dev"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.6"
|
||||||
|
files = [
|
||||||
|
{file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"},
|
||||||
|
{file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
dev = ["pre-commit", "tox"]
|
||||||
|
testing = ["pytest", "pytest-benchmark"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pylint"
|
||||||
|
version = "2.17.4"
|
||||||
|
description = "python code static checker"
|
||||||
|
category = "dev"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.7.2"
|
||||||
|
files = [
|
||||||
|
{file = "pylint-2.17.4-py3-none-any.whl", hash = "sha256:7a1145fb08c251bdb5cca11739722ce64a63db479283d10ce718b2460e54123c"},
|
||||||
|
{file = "pylint-2.17.4.tar.gz", hash = "sha256:5dcf1d9e19f41f38e4e85d10f511e5b9c35e1aa74251bf95cdd8cb23584e2db1"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
astroid = ">=2.15.4,<=2.17.0-dev0"
|
||||||
|
colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""}
|
||||||
|
dill = {version = ">=0.3.6", markers = "python_version >= \"3.11\""}
|
||||||
|
isort = ">=4.2.5,<6"
|
||||||
|
mccabe = ">=0.6,<0.8"
|
||||||
|
platformdirs = ">=2.2.0"
|
||||||
|
tomlkit = ">=0.10.1"
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
spelling = ["pyenchant (>=3.2,<4.0)"]
|
||||||
|
testutils = ["gitpython (>3)"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pytest"
|
||||||
|
version = "7.3.1"
|
||||||
|
description = "pytest: simple powerful testing with Python"
|
||||||
|
category = "dev"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.7"
|
||||||
|
files = [
|
||||||
|
{file = "pytest-7.3.1-py3-none-any.whl", hash = "sha256:3799fa815351fea3a5e96ac7e503a96fa51cc9942c3753cda7651b93c1cfa362"},
|
||||||
|
{file = "pytest-7.3.1.tar.gz", hash = "sha256:434afafd78b1d78ed0addf160ad2b77a30d35d4bdf8af234fe621919d9ed15e3"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
colorama = {version = "*", markers = "sys_platform == \"win32\""}
|
||||||
|
iniconfig = "*"
|
||||||
|
packaging = "*"
|
||||||
|
pluggy = ">=0.12,<2.0"
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "xmlschema"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tomlkit"
|
||||||
|
version = "0.11.8"
|
||||||
|
description = "Style preserving TOML library"
|
||||||
|
category = "dev"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.7"
|
||||||
|
files = [
|
||||||
|
{file = "tomlkit-0.11.8-py3-none-any.whl", hash = "sha256:8c726c4c202bdb148667835f68d68780b9a003a9ec34167b6c673b38eff2a171"},
|
||||||
|
{file = "tomlkit-0.11.8.tar.gz", hash = "sha256:9330fc7faa1db67b541b28e62018c17d20be733177d290a13b24c62d1614e0c3"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "typing-extensions"
|
||||||
|
version = "4.6.2"
|
||||||
|
description = "Backported and Experimental Type Hints for Python 3.7+"
|
||||||
|
category = "dev"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.7"
|
||||||
|
files = [
|
||||||
|
{file = "typing_extensions-4.6.2-py3-none-any.whl", hash = "sha256:3a8b36f13dd5fdc5d1b16fe317f5668545de77fa0b8e02006381fd49d731ab98"},
|
||||||
|
{file = "typing_extensions-4.6.2.tar.gz", hash = "sha256:06006244c70ac8ee83fa8282cb188f697b8db25bc8b4df07be1873c43897060c"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wrapt"
|
||||||
|
version = "1.15.0"
|
||||||
|
description = "Module for decorators, wrappers and monkey patching."
|
||||||
|
category = "dev"
|
||||||
|
optional = false
|
||||||
|
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7"
|
||||||
|
files = [
|
||||||
|
{file = "wrapt-1.15.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:ca1cccf838cd28d5a0883b342474c630ac48cac5df0ee6eacc9c7290f76b11c1"},
|
||||||
|
{file = "wrapt-1.15.0-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:e826aadda3cae59295b95343db8f3d965fb31059da7de01ee8d1c40a60398b29"},
|
||||||
|
{file = "wrapt-1.15.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:5fc8e02f5984a55d2c653f5fea93531e9836abbd84342c1d1e17abc4a15084c2"},
|
||||||
|
{file = "wrapt-1.15.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:96e25c8603a155559231c19c0349245eeb4ac0096fe3c1d0be5c47e075bd4f46"},
|
||||||
|
{file = "wrapt-1.15.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:40737a081d7497efea35ab9304b829b857f21558acfc7b3272f908d33b0d9d4c"},
|
||||||
|
{file = "wrapt-1.15.0-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:f87ec75864c37c4c6cb908d282e1969e79763e0d9becdfe9fe5473b7bb1e5f09"},
|
||||||
|
{file = "wrapt-1.15.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:1286eb30261894e4c70d124d44b7fd07825340869945c79d05bda53a40caa079"},
|
||||||
|
{file = "wrapt-1.15.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:493d389a2b63c88ad56cdc35d0fa5752daac56ca755805b1b0c530f785767d5e"},
|
||||||
|
{file = "wrapt-1.15.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:58d7a75d731e8c63614222bcb21dd992b4ab01a399f1f09dd82af17bbfc2368a"},
|
||||||
|
{file = "wrapt-1.15.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:21f6d9a0d5b3a207cdf7acf8e58d7d13d463e639f0c7e01d82cdb671e6cb7923"},
|
||||||
|
{file = "wrapt-1.15.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ce42618f67741d4697684e501ef02f29e758a123aa2d669e2d964ff734ee00ee"},
|
||||||
|
{file = "wrapt-1.15.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:41d07d029dd4157ae27beab04d22b8e261eddfc6ecd64ff7000b10dc8b3a5727"},
|
||||||
|
{file = "wrapt-1.15.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:54accd4b8bc202966bafafd16e69da9d5640ff92389d33d28555c5fd4f25ccb7"},
|
||||||
|
{file = "wrapt-1.15.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2fbfbca668dd15b744418265a9607baa970c347eefd0db6a518aaf0cfbd153c0"},
|
||||||
|
{file = "wrapt-1.15.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:76e9c727a874b4856d11a32fb0b389afc61ce8aaf281ada613713ddeadd1cfec"},
|
||||||
|
{file = "wrapt-1.15.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e20076a211cd6f9b44a6be58f7eeafa7ab5720eb796975d0c03f05b47d89eb90"},
|
||||||
|
{file = "wrapt-1.15.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a74d56552ddbde46c246b5b89199cb3fd182f9c346c784e1a93e4dc3f5ec9975"},
|
||||||
|
{file = "wrapt-1.15.0-cp310-cp310-win32.whl", hash = "sha256:26458da5653aa5b3d8dc8b24192f574a58984c749401f98fff994d41d3f08da1"},
|
||||||
|
{file = "wrapt-1.15.0-cp310-cp310-win_amd64.whl", hash = "sha256:75760a47c06b5974aa5e01949bf7e66d2af4d08cb8c1d6516af5e39595397f5e"},
|
||||||
|
{file = "wrapt-1.15.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ba1711cda2d30634a7e452fc79eabcadaffedf241ff206db2ee93dd2c89a60e7"},
|
||||||
|
{file = "wrapt-1.15.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:56374914b132c702aa9aa9959c550004b8847148f95e1b824772d453ac204a72"},
|
||||||
|
{file = "wrapt-1.15.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a89ce3fd220ff144bd9d54da333ec0de0399b52c9ac3d2ce34b569cf1a5748fb"},
|
||||||
|
{file = "wrapt-1.15.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3bbe623731d03b186b3d6b0d6f51865bf598587c38d6f7b0be2e27414f7f214e"},
|
||||||
|
{file = "wrapt-1.15.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3abbe948c3cbde2689370a262a8d04e32ec2dd4f27103669a45c6929bcdbfe7c"},
|
||||||
|
{file = "wrapt-1.15.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:b67b819628e3b748fd3c2192c15fb951f549d0f47c0449af0764d7647302fda3"},
|
||||||
|
{file = "wrapt-1.15.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:7eebcdbe3677e58dd4c0e03b4f2cfa346ed4049687d839adad68cc38bb559c92"},
|
||||||
|
{file = "wrapt-1.15.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:74934ebd71950e3db69960a7da29204f89624dde411afbfb3b4858c1409b1e98"},
|
||||||
|
{file = "wrapt-1.15.0-cp311-cp311-win32.whl", hash = "sha256:bd84395aab8e4d36263cd1b9308cd504f6cf713b7d6d3ce25ea55670baec5416"},
|
||||||
|
{file = "wrapt-1.15.0-cp311-cp311-win_amd64.whl", hash = "sha256:a487f72a25904e2b4bbc0817ce7a8de94363bd7e79890510174da9d901c38705"},
|
||||||
|
{file = "wrapt-1.15.0-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:4ff0d20f2e670800d3ed2b220d40984162089a6e2c9646fdb09b85e6f9a8fc29"},
|
||||||
|
{file = "wrapt-1.15.0-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:9ed6aa0726b9b60911f4aed8ec5b8dd7bf3491476015819f56473ffaef8959bd"},
|
||||||
|
{file = "wrapt-1.15.0-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:896689fddba4f23ef7c718279e42f8834041a21342d95e56922e1c10c0cc7afb"},
|
||||||
|
{file = "wrapt-1.15.0-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:75669d77bb2c071333417617a235324a1618dba66f82a750362eccbe5b61d248"},
|
||||||
|
{file = "wrapt-1.15.0-cp35-cp35m-win32.whl", hash = "sha256:fbec11614dba0424ca72f4e8ba3c420dba07b4a7c206c8c8e4e73f2e98f4c559"},
|
||||||
|
{file = "wrapt-1.15.0-cp35-cp35m-win_amd64.whl", hash = "sha256:fd69666217b62fa5d7c6aa88e507493a34dec4fa20c5bd925e4bc12fce586639"},
|
||||||
|
{file = "wrapt-1.15.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:b0724f05c396b0a4c36a3226c31648385deb6a65d8992644c12a4963c70326ba"},
|
||||||
|
{file = "wrapt-1.15.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bbeccb1aa40ab88cd29e6c7d8585582c99548f55f9b2581dfc5ba68c59a85752"},
|
||||||
|
{file = "wrapt-1.15.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:38adf7198f8f154502883242f9fe7333ab05a5b02de7d83aa2d88ea621f13364"},
|
||||||
|
{file = "wrapt-1.15.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:578383d740457fa790fdf85e6d346fda1416a40549fe8db08e5e9bd281c6a475"},
|
||||||
|
{file = "wrapt-1.15.0-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:a4cbb9ff5795cd66f0066bdf5947f170f5d63a9274f99bdbca02fd973adcf2a8"},
|
||||||
|
{file = "wrapt-1.15.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:af5bd9ccb188f6a5fdda9f1f09d9f4c86cc8a539bd48a0bfdc97723970348418"},
|
||||||
|
{file = "wrapt-1.15.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:b56d5519e470d3f2fe4aa7585f0632b060d532d0696c5bdfb5e8319e1d0f69a2"},
|
||||||
|
{file = "wrapt-1.15.0-cp36-cp36m-win32.whl", hash = "sha256:77d4c1b881076c3ba173484dfa53d3582c1c8ff1f914c6461ab70c8428b796c1"},
|
||||||
|
{file = "wrapt-1.15.0-cp36-cp36m-win_amd64.whl", hash = "sha256:077ff0d1f9d9e4ce6476c1a924a3332452c1406e59d90a2cf24aeb29eeac9420"},
|
||||||
|
{file = "wrapt-1.15.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:5c5aa28df055697d7c37d2099a7bc09f559d5053c3349b1ad0c39000e611d317"},
|
||||||
|
{file = "wrapt-1.15.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3a8564f283394634a7a7054b7983e47dbf39c07712d7b177b37e03f2467a024e"},
|
||||||
|
{file = "wrapt-1.15.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:780c82a41dc493b62fc5884fb1d3a3b81106642c5c5c78d6a0d4cbe96d62ba7e"},
|
||||||
|
{file = "wrapt-1.15.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e169e957c33576f47e21864cf3fc9ff47c223a4ebca8960079b8bd36cb014fd0"},
|
||||||
|
{file = "wrapt-1.15.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:b02f21c1e2074943312d03d243ac4388319f2456576b2c6023041c4d57cd7019"},
|
||||||
|
{file = "wrapt-1.15.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:f2e69b3ed24544b0d3dbe2c5c0ba5153ce50dcebb576fdc4696d52aa22db6034"},
|
||||||
|
{file = "wrapt-1.15.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d787272ed958a05b2c86311d3a4135d3c2aeea4fc655705f074130aa57d71653"},
|
||||||
|
{file = "wrapt-1.15.0-cp37-cp37m-win32.whl", hash = "sha256:02fce1852f755f44f95af51f69d22e45080102e9d00258053b79367d07af39c0"},
|
||||||
|
{file = "wrapt-1.15.0-cp37-cp37m-win_amd64.whl", hash = "sha256:abd52a09d03adf9c763d706df707c343293d5d106aea53483e0ec8d9e310ad5e"},
|
||||||
|
{file = "wrapt-1.15.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:cdb4f085756c96a3af04e6eca7f08b1345e94b53af8921b25c72f096e704e145"},
|
||||||
|
{file = "wrapt-1.15.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:230ae493696a371f1dbffaad3dafbb742a4d27a0afd2b1aecebe52b740167e7f"},
|
||||||
|
{file = "wrapt-1.15.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63424c681923b9f3bfbc5e3205aafe790904053d42ddcc08542181a30a7a51bd"},
|
||||||
|
{file = "wrapt-1.15.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d6bcbfc99f55655c3d93feb7ef3800bd5bbe963a755687cbf1f490a71fb7794b"},
|
||||||
|
{file = "wrapt-1.15.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c99f4309f5145b93eca6e35ac1a988f0dc0a7ccf9ccdcd78d3c0adf57224e62f"},
|
||||||
|
{file = "wrapt-1.15.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b130fe77361d6771ecf5a219d8e0817d61b236b7d8b37cc045172e574ed219e6"},
|
||||||
|
{file = "wrapt-1.15.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:96177eb5645b1c6985f5c11d03fc2dbda9ad24ec0f3a46dcce91445747e15094"},
|
||||||
|
{file = "wrapt-1.15.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d5fe3e099cf07d0fb5a1e23d399e5d4d1ca3e6dfcbe5c8570ccff3e9208274f7"},
|
||||||
|
{file = "wrapt-1.15.0-cp38-cp38-win32.whl", hash = "sha256:abd8f36c99512755b8456047b7be10372fca271bf1467a1caa88db991e7c421b"},
|
||||||
|
{file = "wrapt-1.15.0-cp38-cp38-win_amd64.whl", hash = "sha256:b06fa97478a5f478fb05e1980980a7cdf2712015493b44d0c87606c1513ed5b1"},
|
||||||
|
{file = "wrapt-1.15.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2e51de54d4fb8fb50d6ee8327f9828306a959ae394d3e01a1ba8b2f937747d86"},
|
||||||
|
{file = "wrapt-1.15.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0970ddb69bba00670e58955f8019bec4a42d1785db3faa043c33d81de2bf843c"},
|
||||||
|
{file = "wrapt-1.15.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76407ab327158c510f44ded207e2f76b657303e17cb7a572ffe2f5a8a48aa04d"},
|
||||||
|
{file = "wrapt-1.15.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cd525e0e52a5ff16653a3fc9e3dd827981917d34996600bbc34c05d048ca35cc"},
|
||||||
|
{file = "wrapt-1.15.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d37ac69edc5614b90516807de32d08cb8e7b12260a285ee330955604ed9dd29"},
|
||||||
|
{file = "wrapt-1.15.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:078e2a1a86544e644a68422f881c48b84fef6d18f8c7a957ffd3f2e0a74a0d4a"},
|
||||||
|
{file = "wrapt-1.15.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:2cf56d0e237280baed46f0b5316661da892565ff58309d4d2ed7dba763d984b8"},
|
||||||
|
{file = "wrapt-1.15.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:7dc0713bf81287a00516ef43137273b23ee414fe41a3c14be10dd95ed98a2df9"},
|
||||||
|
{file = "wrapt-1.15.0-cp39-cp39-win32.whl", hash = "sha256:46ed616d5fb42f98630ed70c3529541408166c22cdfd4540b88d5f21006b0eff"},
|
||||||
|
{file = "wrapt-1.15.0-cp39-cp39-win_amd64.whl", hash = "sha256:eef4d64c650f33347c1f9266fa5ae001440b232ad9b98f1f43dfe7a79435c0a6"},
|
||||||
|
{file = "wrapt-1.15.0-py3-none-any.whl", hash = "sha256:64b1df0f83706b4ef4cfb4fb0e4c2669100fd7ecacfb59e091fad300d4e04640"},
|
||||||
|
{file = "wrapt-1.15.0.tar.gz", hash = "sha256:d06730c6aed78cee4126234cf2d071e01b44b915e725a6cb439a879ec9754a3a"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[metadata]
|
||||||
|
lock-version = "2.0"
|
||||||
|
python-versions = "^3.11"
|
||||||
|
content-hash = "a14e3d6fa508612e47f2dab1d5068300be5735fce187ae7543b198ed57eede70"
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
[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]
|
||||||
|
"Homepage" = "https://github.com/cblanken/boggler"
|
||||||
|
"Bug Tracker" = "https://github.com/cblanken/boggler/issues"
|
||||||
|
|
||||||
|
[tool.poetry]
|
||||||
|
name = "boggler"
|
||||||
|
version = "2.0.1"
|
||||||
|
description = "Utilities for solving the Boggle word game."
|
||||||
|
authors = ["Cameron Blankenbuehler <cameron.blankenbuehler@protonmail.com>"]
|
||||||
|
readme = "README.md"
|
||||||
|
|
||||||
|
[tool.poetry.dependencies]
|
||||||
|
python = "^3.11"
|
||||||
|
|
||||||
|
[tool.poetry.scripts]
|
||||||
|
boggler = "boggler.__main__:main"
|
||||||
|
|
||||||
|
|
||||||
|
[tool.poetry.group.dev.dependencies]
|
||||||
|
pytest = "^7.3.1"
|
||||||
|
pylint = "^2.17.4"
|
||||||
|
mypy = "^1.3.0"
|
||||||
|
|
||||||
|
[build-system]
|
||||||
|
requires = ["poetry-core"]
|
||||||
|
build-backend = "poetry.core.masonry.api"
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
|
|
||||||
if [ -z $1 ] || [ -z $2 ]; then
|
|
||||||
echo "Usage: ./filter_prefix.sh WORDLIST OUT_DIR"
|
|
||||||
exit 0
|
|
||||||
else
|
|
||||||
grep -P "^a" $1 > "$2/words_a.txt"
|
|
||||||
grep -P "^b" $1 > "$2/words_b.txt"
|
|
||||||
grep -P "^c" $1 > "$2/words_c.txt"
|
|
||||||
grep -P "^d" $1 > "$2/words_d.txt"
|
|
||||||
grep -P "^e" $1 > "$2/words_e.txt"
|
|
||||||
grep -P "^f" $1 > "$2/words_f.txt"
|
|
||||||
grep -P "^g" $1 > "$2/words_g.txt"
|
|
||||||
grep -P "^h" $1 > "$2/words_h.txt"
|
|
||||||
grep -P "^i" $1 > "$2/words_i.txt"
|
|
||||||
grep -P "^j" $1 > "$2/words_j.txt"
|
|
||||||
grep -P "^k" $1 > "$2/words_k.txt"
|
|
||||||
grep -P "^l" $1 > "$2/words_l.txt"
|
|
||||||
grep -P "^m" $1 > "$2/words_m.txt"
|
|
||||||
grep -P "^n" $1 > "$2/words_n.txt"
|
|
||||||
grep -P "^o" $1 > "$2/words_o.txt"
|
|
||||||
grep -P "^p" $1 > "$2/words_p.txt"
|
|
||||||
grep -P "^q" $1 > "$2/words_q.txt"
|
|
||||||
grep -P "^r" $1 > "$2/words_r.txt"
|
|
||||||
grep -P "^s" $1 > "$2/words_s.txt"
|
|
||||||
grep -P "^t" $1 > "$2/words_t.txt"
|
|
||||||
grep -P "^u" $1 > "$2/words_u.txt"
|
|
||||||
grep -P "^v" $1 > "$2/words_v.txt"
|
|
||||||
grep -P "^w" $1 > "$2/words_w.txt"
|
|
||||||
grep -P "^x" $1 > "$2/words_x.txt"
|
|
||||||
grep -P "^y" $1 > "$2/words_y.txt"
|
|
||||||
grep -P "^z" $1 > "$2/words_z.txt"
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
u, n,r ,e
|
||||||
|
qu, n,i, l
|
||||||
|
i ,s,h, a
|
||||||
|
s ,e , l,b
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
|
||||||
|
|
||||||
|
u,n,r,e
|
||||||
|
qu,n,i,l
|
||||||
|
i,s,h,a
|
||||||
|
|
||||||
|
s,e,l,b
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
u,n,r,e
|
||||||
|
qu,n,i,l
|
||||||
|
i,s,h,a
|
||||||
|
s,e,l,b
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
u,n,r,e,a,i
|
||||||
|
qu,n,i,l
|
||||||
|
i,s,h,a,j
|
||||||
|
s,e,l
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
u,n,r,e
|
||||||
|
qu,n,i,l
|
||||||
|
i,s,h,a
|
||||||
|
s,e,l,b
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
u,n,r,e
|
||||||
|
qu,n,i,l
|
||||||
|
i,s,h,a
|
||||||
|
s,e,l,b
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
u,n,r,e,b
|
||||||
|
qu,n,i,l
|
||||||
|
i,s,h,a,g,i
|
||||||
|
s,e,l,b
|
||||||
Symlink
+1
@@ -0,0 +1 @@
|
|||||||
|
../boggler
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
from pathlib import Path
|
||||||
|
import pytest
|
||||||
|
from boggler.boggler_utils import read_boggle_file, BadBoardFormat
|
||||||
|
|
||||||
|
boards_path = Path(Path(__file__).parent.resolve())
|
||||||
|
|
||||||
|
@pytest.fixture(name="expected_board_4x4")
|
||||||
|
def fixture_expected_board_4x4():
|
||||||
|
return [
|
||||||
|
['u', 'n', 'r', 'e'],
|
||||||
|
['qu', 'n', 'i', 'l'],
|
||||||
|
['i', 's', 'h', 'a'],
|
||||||
|
['s', 'e', 'l', 'b']
|
||||||
|
]
|
||||||
|
|
||||||
|
# Boards with clean input
|
||||||
|
@pytest.fixture(name="clean_4x4_path")
|
||||||
|
def fixture_clean_4x4_path():
|
||||||
|
return Path(boards_path, "./boards/4x4_clean.board")
|
||||||
|
|
||||||
|
def test_clean_4x4_board(expected_board_4x4, clean_4x4_path):
|
||||||
|
assert expected_board_4x4 == read_boggle_file(clean_4x4_path)
|
||||||
|
|
||||||
|
|
||||||
|
# Boards with leading whitespace
|
||||||
|
@pytest.fixture(name="leading_whitespace_4x4_path")
|
||||||
|
def fixture_leading_whitespace_4x4_path():
|
||||||
|
return Path(boards_path, "./boards/4x4_leading_whitespace.board")
|
||||||
|
|
||||||
|
def test_leading_whitespace_4x4_board(expected_board_4x4, leading_whitespace_4x4_path):
|
||||||
|
assert expected_board_4x4 == read_boggle_file(leading_whitespace_4x4_path)
|
||||||
|
|
||||||
|
|
||||||
|
# Boards with between whitespace
|
||||||
|
@pytest.fixture(name="between_whitespace_4x4_path")
|
||||||
|
def fixture_between_whitespace_4x4_path():
|
||||||
|
return Path(boards_path, "./boards/4x4_between_whitespace.board")
|
||||||
|
|
||||||
|
def test_between_whitespace_4x4_board(expected_board_4x4, between_whitespace_4x4_path):
|
||||||
|
assert expected_board_4x4 == read_boggle_file(between_whitespace_4x4_path)
|
||||||
|
|
||||||
|
|
||||||
|
# Boards with trailing whitespace
|
||||||
|
@pytest.fixture(name="trailing_whitespace_4x4_path")
|
||||||
|
def fixture_trailing_whitespace_4x4_path():
|
||||||
|
return Path(boards_path, "./boards/4x4_trailing_whitespace.board")
|
||||||
|
|
||||||
|
def test_trailing_whitespace_4x4_board(expected_board_4x4, trailing_whitespace_4x4_path):
|
||||||
|
assert expected_board_4x4 == read_boggle_file(trailing_whitespace_4x4_path)
|
||||||
|
|
||||||
|
|
||||||
|
# Boards with blank lines
|
||||||
|
@pytest.fixture(name="blank_lines_4x4_path")
|
||||||
|
def fixture_blank_lines_4x4_path():
|
||||||
|
return Path(boards_path, "./boards/4x4_blank_lines.board")
|
||||||
|
|
||||||
|
def test_blank_lines_4x4_board(blank_lines_4x4_path):
|
||||||
|
with pytest.raises(BadBoardFormat, match="must contain no blank lines"):
|
||||||
|
read_boggle_file(blank_lines_4x4_path)
|
||||||
|
|
||||||
|
|
||||||
|
# Boards with inconsistent width lines
|
||||||
|
@pytest.fixture(name="inconsistent_width_4x4_path")
|
||||||
|
def fixture_inconsistent_width_4x4_path():
|
||||||
|
return Path(boards_path, "./boards/4x4_inconsistent_width.board")
|
||||||
|
|
||||||
|
def test_inconsistent_width_4x4_board(inconsistent_width_4x4_path):
|
||||||
|
with pytest.raises(BadBoardFormat, match="length of each row must be the same"):
|
||||||
|
read_boggle_file(inconsistent_width_4x4_path)
|
||||||
Executable
+177
@@ -0,0 +1,177 @@
|
|||||||
|
from itertools import chain
|
||||||
|
from pathlib import Path
|
||||||
|
import pytest
|
||||||
|
from boggler.boggler_utils import BoggleBoard, build_full_boggle_tree
|
||||||
|
|
||||||
|
WORDLISTS_DIR = Path("./boggler/wordlists/dwyl/").absolute()
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def board_4x4():
|
||||||
|
return [
|
||||||
|
['u','n','r','e'],
|
||||||
|
['qu','n','i','l'],
|
||||||
|
['i','s','h','a'],
|
||||||
|
['s','e','l','b']
|
||||||
|
]
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def expected_words_max_4():
|
||||||
|
return [
|
||||||
|
"u", "un", "unn", "uns", "uni", "unie", "uni", "unie", "un", "unn", "uni",
|
||||||
|
"unie", "n", "ni", "nis", "nisi", "nil", "nile", "nu", "nun", "nuns", "nr", "r",
|
||||||
|
"rin", "rie", "riel", "risqu", "rise", "riss", "ria", "rial", "rial", "rin",
|
||||||
|
"rins", "rile", "rn", "rle", "rn", "re", "rel", "rei", "rein", "reis", "rein",
|
||||||
|
"e", "el", "ela", "elhi", "eli", "elia", "eir", "eila", "er", "erin", "eris",
|
||||||
|
"eria", "erin", "ern", "erns", "ern", "qu", "qui", "quin", "quis", "quis", "n",
|
||||||
|
"nu", "nun", "nr", "ns", "ni", "nis", "nies", "nies", "nis", "nisi", "ni",
|
||||||
|
"nis", "nisi", "nil", "nile", "i", "ir", "ire", "in", "inn", "inns", "ie",
|
||||||
|
"ihs", "is", "isn", "ise", "isl", "isle", "isis", "ish", "ia", "in", "inn",
|
||||||
|
"ins", "il", "ile", "ila", "l", "le", "lei", "leis", "ler", "lr", "la", "lai",
|
||||||
|
"lair", "lain", "lain", "lab", "lah", "lh", "lhb", "li", "lir", "lire", "lin",
|
||||||
|
"linn", "lie", "lier", "lis", "lise", "liss", "lish", "lin", "linn", "lins",
|
||||||
|
"i", "in", "inn", "ins", "inia", "is", "ise", "ie", "is", "isn", "ise", "isl",
|
||||||
|
"isle", "ish", "s", "sn", "si", "sir", "sire", "sin", "sie", "sier", "sia",
|
||||||
|
"sial", "sial", "sin", "sinh", "sil", "sile", "se", "sei", "seis", "sel", "ss",
|
||||||
|
"ssi", "sl", "sla", "slab", "si", "sin", "sinh", "sis", "sise", "sie", "sh",
|
||||||
|
"shi", "shin", "shia", "shin", "she", "shes", "shel", "sha", "shai", "shab",
|
||||||
|
"h", "hi", "hir", "hire", "hin", "hie", "his", "hisn", "hiss", "hia", "hin",
|
||||||
|
"hins", "hile", "hila", "hl", "hler", "hl", "he", "hes", "hei", "hein", "hes",
|
||||||
|
"hel", "hb", "hs", "hsi", "hsi", "ha", "hal", "hale", "hair", "hain", "hain",
|
||||||
|
"hail", "hab", "hal", "hals", "hale", "a", "al", "ale", "alin", "alin", "ai",
|
||||||
|
"air", "airn", "airn", "aire", "ain", "ainu", "aiel", "ais", "ain", "ainu",
|
||||||
|
"ains", "ail", "aile", "ab", "abl", "able", "al", "als", "ale", "ales", "ales",
|
||||||
|
"alb", "ah", "ahi", "ahir", "ahs", "s", "si", "sin", "sins", "sinh", "sie",
|
||||||
|
"sis", "sisi", "sise", "sish", "ss", "ssi", "ssi", "se", "sei", "seis", "sel",
|
||||||
|
"sels", "e", "es", "ess", "eh", "es", "ess", "el", "elhi", "els", "ela", "elb",
|
||||||
|
"l", "lh", "lhb", "ls", "la", "lai", "lair", "lain", "lain", "lab", "lah", "le",
|
||||||
|
"les", "less", "lei", "leis", "leis", "lehi", "les", "less", "lb", "b", "ba",
|
||||||
|
"bal", "bale", "balr", "bali", "bai", "bain", "bais", "bain", "bail", "bal",
|
||||||
|
"bals", "bale", "bah", "bhil", "bl", "bls", "blah"
|
||||||
|
]
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def expected_words_max_20():
|
||||||
|
return [
|
||||||
|
"u","un","unn","unrelinquishable","unrelishable","unreliable","unrein","uns",
|
||||||
|
"unsin","unslain","unshale","unshale","uni","unie","unhale","unhair","unhale",
|
||||||
|
"uni","unie","un","unn","uni","unie","unrelishable","unreliable",
|
||||||
|
"unrelinquishable","unrein","n","ni","nihal","nihal","nis","nisei","niseis",
|
||||||
|
"nisse","nisi","nil","nile","nu","nun","nuns","nr","r","rin","rie",
|
||||||
|
"riel","risqu","rise","rises","riss","rissel","ria","rial","rial",
|
||||||
|
"rials","rin","rins","rinse","rinses","rile","rn","rle","rn",
|
||||||
|
"re","rel","relais","relinquish","relinquishes","relish","relishes",
|
||||||
|
"relishable","reliable","relinquish","relinquishes","rei","rein","reis",
|
||||||
|
"rein","reins","e","el","ela","elain","elain","elains","elhi","eli","elisha",
|
||||||
|
"elia","eir","eila","er","erin","eris","eria","erin","ern","erns","ernie",
|
||||||
|
"ern","qu","qui","quin","quins","quinse","quinin","quiniela","quinia",
|
||||||
|
"quis","quis","quisle","n","nu","nun","nr","ns","ni",
|
||||||
|
"nis","nisse","nies","nies","niels","nis","nisi","nisse","ni",
|
||||||
|
"nihal","nihal","nis","nisei","niseis","nisse","nisi","nil","nile",
|
||||||
|
"i","ir","ire","in","inn","inns","ie","ihs","is",
|
||||||
|
"isn","ise","issei","isl","isle","isles","isis","ish","ia",
|
||||||
|
"in","inn","ins","inisle","inhale","inhaler","inhale","inhales","inhales",
|
||||||
|
"il","ile","ila","l","le","lei","leis","leiss","ler",
|
||||||
|
"lr","la","lai","lair","lain","laisse","lain","lab","lah",
|
||||||
|
"lh","lhb","li","lir","lire","lin","linn","linns","linquish",
|
||||||
|
"lie","lier","lis","lise","liss","lisle","lisles","lish","liable",
|
||||||
|
"lin","linn","lins","linie","linha","linquish","i","in","inn",
|
||||||
|
"ins","inhale","inhaler","inhale","inhales","inhales","inisle","inia","inial",
|
||||||
|
"inial","is","ise","ie","is","isn","ise","isl","isle",
|
||||||
|
"isles","ish","s","sn","snies","snirl","squin","squinnier","si",
|
||||||
|
"sir","sire","sin","sie","sier","sia","sial","sial","sin",
|
||||||
|
"sinh","sil","sile","se","sei","seis","sel","selah","ss",
|
||||||
|
"ssi","sl","sla","slain","slain","slab","si","sin","sinh",
|
||||||
|
"sis","sise","sisel","sie","sh","shi","shirl","shire","shin",
|
||||||
|
"shinnies","shiel","shier","shia","shin","she","shes","shel","shela",
|
||||||
|
"sha","shale","shalier","shai","shairn","shairn","shab","shale","shales",
|
||||||
|
"h","hi","hir","hire","hin","hinnies","hinnies","hie","his",
|
||||||
|
"hisn","hiss","hissel","hisis","hia","hin","hins","hile","hila",
|
||||||
|
"hl","hler","hl","he","hes","hei","hein","heinie","hes",
|
||||||
|
"hel","hb","hs","hsi","hsi","ha","hal","hale","haler",
|
||||||
|
"hair","haire","hain","hain","hail","hailer","hab","hable","hal",
|
||||||
|
"hals","halse","hale","hales","hales","a","al","ale","alin",
|
||||||
|
"alish","alin","ai","air","airn","airns","airn","aire","ain",
|
||||||
|
"ainu","aiel","ais","aisle","aisles","ain","ainu","ains","ail",
|
||||||
|
"aile","ab","abl","able","ables","ables","al","als","ale",
|
||||||
|
"ales","ales","alb","ah","ahi","ahir","ahs","s","si",
|
||||||
|
"sin","sins","sinh","sinhs","sie","sis","sisi","sise","sisel",
|
||||||
|
"sish","ss","ssi","ssi","se","sesqui","sesia","sei","seis",
|
||||||
|
"seisin","seisin","sel","sels","selah","selahs","e","es","ess",
|
||||||
|
"eshin","eshin","eh","es","ess","essie","el","elhi","els",
|
||||||
|
"elsin","elsin","elsin","elshin","elshin","ela","elain","elain","elains",
|
||||||
|
"elb","l","lh","lhb","ls","la","lai","lair","lain",
|
||||||
|
"laisse","lain","lab","lah","le","les","less","lei","leis",
|
||||||
|
"leiss","leis","leiss","lehi","les","less","lessn","lb","b",
|
||||||
|
"ba","bal","bale","balei","baleise","baler","balr","bali","bai",
|
||||||
|
"bairn","bairns","bairnie","bairnish","bairn","bain","bais","bain","bainie",
|
||||||
|
"bail","baile","bailer","bal","bals","bale","bales","balei","bales",
|
||||||
|
"bah","bhil","bl","bls","blair","blain","blain","blains","blah",
|
||||||
|
"blahs","bless","bless"
|
||||||
|
]
|
||||||
|
|
||||||
|
# -----------------------------------------------------
|
||||||
|
# Solved board max 4-letter words
|
||||||
|
# -----------------------------------------------------
|
||||||
|
|
||||||
|
# Init for word tree (4 board_4x4 or less) and fixtures
|
||||||
|
@pytest.fixture
|
||||||
|
def board_4(board_4x4):
|
||||||
|
return BoggleBoard(board_4x4, 4)
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def tree_4(board_4):
|
||||||
|
return build_full_boggle_tree(board_4, Path(WORDLISTS_DIR))
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def found_words_4(tree_4):
|
||||||
|
return list(chain.from_iterable([x.word_paths for x in tree_4.values()]))
|
||||||
|
|
||||||
|
# Tests for solved board with max of 4-letter words
|
||||||
|
def test_word_count_4_board_4x4_max(expected_words_max_4, tree_4):
|
||||||
|
assert len(expected_words_max_4) == sum(map(lambda x: len(x.word_paths), tree_4.values()))
|
||||||
|
|
||||||
|
def test_words_full_4_board_4x4_max(expected_words_max_4, found_words_4, tree_4):
|
||||||
|
found_words = [x.word_paths for x in tree_4.values()]
|
||||||
|
for letter, subtree in tree_4.items():
|
||||||
|
for i in range(0, min(len(expected_words_max_4), len(found_words_4))):
|
||||||
|
assert expected_words_max_4[i] == found_words_4[i][0]
|
||||||
|
|
||||||
|
def test_format_words_4_to_csv():
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_format_words_4_to_json():
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
# -----------------------------------------------------
|
||||||
|
# Solved board max 20-letter words
|
||||||
|
# -----------------------------------------------------
|
||||||
|
|
||||||
|
# Init for word tree (20 board_4x4 or less) and fixtures
|
||||||
|
@pytest.fixture
|
||||||
|
def board_20(board_4x4):
|
||||||
|
return BoggleBoard(board_4x4, 20)
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def tree_20(board_20):
|
||||||
|
return build_full_boggle_tree(board_20, WORDLISTS_DIR)
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def found_words_20(tree_20):
|
||||||
|
return list(chain.from_iterable([x.word_paths for x in tree_20.values()]))
|
||||||
|
|
||||||
|
# Tests for solved board with max of 20-letter words
|
||||||
|
def test_word_count_20_board_4x4_max(expected_words_max_20, tree_20):
|
||||||
|
assert len(expected_words_max_20) == sum(map(lambda x: len(x.word_paths), tree_20.values()))
|
||||||
|
|
||||||
|
def test_words_full_20_board_4x4_max(expected_words_max_20, found_words_20, tree_20):
|
||||||
|
found_words = [x.word_paths for x in tree_20.values()]
|
||||||
|
for letter, subtree in tree_20.items():
|
||||||
|
for i in range(0, min(len(expected_words_max_20), len(found_words_20))):
|
||||||
|
assert expected_words_max_20[i] == found_words_20[i][0]
|
||||||
|
|
||||||
|
def test_format_words_20_to_csv():
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_format_words_20_to_json():
|
||||||
|
pass
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user