50 Commits
Author SHA1 Message Date
cblanken 612482587d Bump package version 2024-06-25 22:15:02 -04:00
cblanken 4587e7bc70 Optimize boggle tree operations
- Cache WordNode and WordTree properties with frequent lookups
- Reduce `len()` calls
- Remove unnecessary debug logs
2024-06-25 22:07:22 -04:00
cblanken 9b7b05f79d Fix min python version 2024-06-14 11:09:06 -04:00
cblanken 72f3d20401 Bump patch version 2024-06-14 10:36:21 -04:00
cblanken a611f4a7e7 Fixup board_randomizer typehints
Invalid `list[str]` type hints were causing errors in the downstream Boggler Flask app
2024-06-14 10:31:17 -04:00
cblankenandGitHub e183ce07c0 Update README.md 2024-04-13 10:19:20 -04:00
cblankenandGitHub 09e6f4a672 Update README.md
- Fix bad link to wordlists
- Add license link
2024-02-14 22:49:13 -05:00
cblanken 9036848168 Add collapsible sections to README usage section 2023-08-22 19:06:17 -04:00
cblanken a78b277516 Update CLI usage and output in README 2023-08-22 18:57:12 -04:00
cblanken a2630ed82e Initial blacken reformat 2023-08-22 16:56:59 -04:00
cblanken ab2869b858 Ignore board files for blacken pre-commit 2023-08-22 16:56:18 -04:00
cblanken d282cd3676 Install pre-commit and default blacken format hook 2023-08-22 16:53:38 -04:00
cblanken 995a7f42c1 Update build instructions 2023-08-21 18:45:31 -04:00
cblanken b4d7237c1f Add rich table output to default output 2023-06-02 22:56:24 -04:00
cblanken 3b79ca7519 Update solved 4x4 board tests - check paths
- 4x4 board solving tests now checks for the original order of the words as well as the
exact path for each
2023-05-31 18:08:05 -04:00
cblanken 9e2cd55741 Add function docstrings for board tests 2023-05-31 18:07:40 -04:00
cblanken c0fe222832 Add tests for parsing board input files 2023-05-30 19:35:23 -04:00
cblanken 6771ec9205 Add mypy fix some typing errors 2023-05-29 21:25:38 -04:00
cblanken e1981c44de Use pathlib to handle paths instead of os.path
- Replaces all instances of os.path objects with pathlib.Path
2023-05-26 11:50:30 -04:00
cblanken 02bb7c4262 Replace log format strings with lazy formatting
- Pylint recommends to use lazy formatting (%s) for logging functions
2023-05-26 11:27:47 -04:00
cblanken da00afb395 Add JSON output option 2023-05-26 11:27:47 -04:00
cblanken afaa378131 Add clean text output option
- Output valid words for the provided board in a normal text format, one word per line
    - Select between just word output and word+path output for easier post-processing
    - Also includes sort and de-duplication options
2023-05-26 11:26:06 -04:00
cblanken daebee948a Add pylint to dev depencies 2023-05-24 22:18:50 -04:00
cblanken 2bc9d9fb9e Add argument parsing to main module 2023-05-24 19:20:30 -04:00
cblanken 7500720992 Refactor 4x4 board test to use pytest fixtures
- Update missing symoblic link to boggler src files to access wordlists
2023-05-24 17:37:51 -04:00
cblanken d81f5e28cc Replace unittest with pytest
- Pytest requires test modules to start with the "test_" prefix. The new
test_solve_4x4_board.py module replaces the functional_tests.py module
2023-05-24 16:18:40 -04:00
cblanken a0d60d280c Add poetry script to run main boggler script
- Also correct import for boggler_utils to be relative in __main__.py so it can be run
as module with `python -m boggler`
2023-05-24 12:15:18 -04:00
cblanken ac58828521 Move source files for consistency with Poetry
- By default Poetry build packages into "./packagename/packagename" instead of
"./packagename/src/packagename", this commit reflects that structure even though
poetry _could_ build normally from the original structure
2023-05-24 12:10:32 -04:00
cblanken 59c5beb491 Update project config to use poetry 2023-05-24 11:00:52 -04:00
cblanken 49cf552cc1 Merge branch 'main' into dev 2023-04-24 20:47:41 -04:00
cblanken f0cee00bef Update README.md 2023-04-24 19:25:18 -04:00
cblankenandGitHub 843e167bf4 Update README.md 2023-03-30 19:08:47 -04:00
cblanken 1a1f5acb17 Fix memory warnings/errors/leaks per valgrind 2023-03-30 12:06:13 -04:00
cblanken 2f795be626 Add debug information to compilation flags 2023-03-30 12:05:23 -04:00
cblanken 6f4d4a69a0 Add WordTree data structure and helper functions 2023-03-28 19:05:08 -04:00
cblanken 82991e96e7 Add BoardCell struct 2023-03-27 16:32:11 -04:00
cblanken 1386e1cace Add Makefile 2023-03-24 18:27:44 -04:00
cblanken 8c9ed00826 Initial C implementation commit with Board struct 2023-03-24 18:25:56 -04:00
cblanken c09dce4bd6 Add basic solved board tests 2023-03-23 14:19:28 -04:00
cblanken a37548a4ee Merge branch 'main' into dev 2023-03-23 12:46:02 -04:00
cblanken 1dc118e953 Fix boggle tree missing multi-letter nodes
The BoggleTree object when inserting nodes did not properly check for nodes
with values of more than 1 letter. Therefore, any words with "qu" after the
root would not be included in the solved board. As of now, only nodes with
a maximum of 2 letters are accounted for, 3+ letter nodes won't be checked.
2023-03-23 12:42:40 -04:00
cblanken 6d3a58595c Replace print(s) with logging functions 2023-03-22 16:25:37 -04:00
cblanken 8b9dc89420 Update README with build instructions 2023-03-13 21:53:03 -04:00
cblanken a80111072f Bump release version 2022-12-30 20:55:44 -05:00
cblanken fa01d442a5 Fix bug in board randomizer
- The board randomizer did not account for dice files that don't contain an exact integer squared number of dice. For example the 5x5 dice file contains 26 possible dice since 1 extra die is provided.
- The fix takes the square root of the number of dice and takes the floor of that number as the total number of dice to choose.
2022-12-30 19:54:00 -05:00
cblanken 733c1474b0 Add 5x5 dice file 2022-12-30 19:51:41 -05:00
cblanken 54f79c0afe Remove dead code from boggler.py 2022-11-16 20:37:30 -05:00
cblanken 9f364ee90e Remove old dictionary word prefix lists 2022-11-16 20:36:55 -05:00
cblanken e219954ba6 Add function to flatten boggle board results into 1D list 2022-10-10 12:00:42 -04:00
cblanken f4accfe959 Update wordlist splitting script
- use ripgrep to exclude proper nouns (with capitals) output only the
  capture group
2022-09-28 13:01:39 -04:00
102 changed files with 2925 additions and 59360 deletions
+12
View File
@@ -0,0 +1,12 @@
exclude: '^.*\.board$'
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v2.3.0
hooks:
- id: check-yaml
- id: end-of-file-fixer
- id: trailing-whitespace
- repo: https://github.com/psf/black
rev: 22.10.0
hooks:
- id: black
+327 -339
View File
@@ -5,14 +5,11 @@ A solver for the popular word game Boggle.
```console
pip install boggler
```
OR
```console
git clone https://github.com/cblanken/boggler.git
```
To use the script to solve a particular Boggle board configuration, you'll need to do a few things
# Setup
To use the script to solve a Boggle board, you'll need to do a few things first.
1. Create `.csv` of the board state like so:
Here is an example `board.csv`. Note the orientation of the board does not matter.
```console
$ cat board.csv
@@ -21,7 +18,7 @@ To use the script to solve a particular Boggle board configuration, you'll need
d,f,e,y
n,m,e,qu
```
2. Find a dictionary wordlist file or create your own
2. Find or create a dictionary wordlist file
The dictionary wordlist should have each word on a single line like so
```console
@@ -47,9 +44,9 @@ To use the script to solve a particular Boggle board configuration, you'll need
3. Split the dictionary wordlist into separate files based on the first letter of each word.
To split an English wordlist the `split_wordlist_alpha.sh` script can be used like so:
To split an English wordlist, use the `split_wordlist_alpha.sh` script like so:
```console
$ split_wordlist_alpha.sh my_wordlist.txt .
$ split_wordlist_alpha.sh my_wordlist.txt .
```
This will generate a series of sub-dictionaries for each letter in the alphabet in the current directory.
```console
@@ -61,343 +58,334 @@ To use the script to solve a particular Boggle board configuration, you'll need
```
# Usage
```console
$ python boggler.py
Usage: python3 boggler.py <BOARD_FILE> <WORDLISTS_DIR> [MAX_WORD_LENGTH]
$ python boggler.py board.csv wordlists/ 15
Reading in wordlists...
>> d: words_d.txt
>> e: words_e.txt
>> f: words_f.txt
>> m: words_m.txt
>> n: words_n.txt
>> o: words_o.txt
>> qu: words_q.txt
>> r: words_r.txt
>> s: words_s.txt
>> v: words_v.txt
>> y: words_y.txt
Generating WordTrees...
>> (0, 0): y
>> (0, 1): e
>> (0, 2): o
>> (0, 3): s
>> (1, 0): r
>> (1, 1): e
>> (1, 2): o
>> (1, 3): v
>> (2, 0): d
>> (2, 1): f
>> (2, 2): e
>> (2, 3): y
>> (3, 0): n
>> (3, 1): m
>> (3, 2): e
>> (3, 3): qu
<details>
<summary><h3 style="display: inline">CLI usage</h3></summary>
<pre><code>$ poetry run boggler --help
usage: boggler [-h] [-f FORMAT] [-p] [-s] [-d] board wordlists [max_word_length]
Boggle board game solver
positional arguments:
board Path to board CSV file
wordlists 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
max_word_length Maximum length of words searched for on provided board
options:
-h, --help show this help message and exit
-f FORMAT, --format FORMAT
Specify alternative output format including [txt, json]
-p, --include-path Include full paths for each word in output
-s, --sort 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.
-d, --dedup 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.
</code></pre>
</details>
<details>
<summary><h3 style="display: inline">Example board solve</h3></summary>
<pre><code>$ poetry run boggler ./boggler/boards/b1.csv boggler/wordlists/scrabble_2019/
BOARD
+---------------+
| Y | E | O | S |
| S | A | I | P |
+---------------+
| R | E | O | V |
| L |QU | A | Y |
+---------------+
| D | F | E | Y |
| U | L | L | S |
+---------------+
| N | M | E |QU |
| O | W | H | A |
+---------------+
Starting @ (0, 0)
╭──────────┬──────────────────────────────────────────────────────────────────╮
│ Word │ Path │
├──────────┼──────────────────────────────────────────────────────────────────┤
│ squall │ [(0, 0), (1, 1), (0, 1), (1, 0), (2, 1)] │
│ squall │ [(0, 0), (1, 1), (1, 2), (2, 2), (2, 1)] │
│ squall │ [(0, 0), (1, 1), (1, 2), (2, 1), (1, 0)] │
│ squall │ [(0, 0), (1, 1), (1, 2), (2, 1), (2, 2)] │
│ squally │ [(0, 0), (1, 1), (1, 2), (2, 1), (2, 2), (1, 3)] │
│ squalls │ [(0, 0), (1, 1), (1, 2), (2, 1), (2, 2), (2, 3)] │
│ squash │ [(0, 0), (1, 1), (1, 2), (2, 3), (3, 2)] │
│ sal │ [(0, 0), (0, 1), (1, 0)] │
│ sall │ [(0, 0), (0, 1), (1, 0), (2, 1)] │
│ sallal │ [(0, 0), (0, 1), (1, 0), (2, 1), (1, 2), (2, 2)] │
│ sallals │ [(0, 0), (0, 1), (1, 0), (2, 1), (1, 2), (2, 2), (2, 3)] │
│ sallow │ [(0, 0), (0, 1), (1, 0), (2, 1), (3, 0), (3, 1)] │
│ sallowly │ [(0, 0), (0, 1), (1, 0), (2, 1), (3, 0), (3, 1), (2, 2), (1, 3)] │
│ sai │ [(0, 0), (0, 1), (0, 2)] │
╰──────────┴──────────────────────────────────────────────────────────────────╯
Starting @ (0, 1)
╭────────┬──────────────────────────────────────────────────╮
│ Word │ Path │
├────────┼──────────────────────────────────────────────────┤
│ aqua │ [(0, 1), (1, 1), (1, 2)] │
│ aquas │ [(0, 1), (1, 1), (1, 2), (2, 3)] │
│ al │ [(0, 1), (1, 0)] │
│ als │ [(0, 1), (1, 0), (0, 0)] │
│ alu │ [(0, 1), (1, 0), (2, 0)] │
│ alula │ [(0, 1), (1, 0), (2, 0), (2, 1), (1, 2)] │
│ alulas │ [(0, 1), (1, 0), (2, 0), (2, 1), (1, 2), (2, 3)] │
│ all │ [(0, 1), (1, 0), (2, 1)] │
│ allay │ [(0, 1), (1, 0), (2, 1), (1, 2), (1, 3)] │
│ allays │ [(0, 1), (1, 0), (2, 1), (1, 2), (1, 3), (2, 3)] │
│ allow │ [(0, 1), (1, 0), (2, 1), (3, 0), (3, 1)] │
│ aa │ [(0, 1), (1, 2)] │
│ aal │ [(0, 1), (1, 2), (2, 2)] │
│ aals │ [(0, 1), (1, 2), (2, 2), (2, 3)] │
│ aal │ [(0, 1), (1, 2), (2, 1)] │
│ aas │ [(0, 1), (1, 2), (2, 3)] │
│ as │ [(0, 1), (0, 0)] │
│ ai │ [(0, 1), (0, 2)] │
│ aia │ [(0, 1), (0, 2), (1, 2)] │
│ aias │ [(0, 1), (0, 2), (1, 2), (2, 3)] │
╰────────┴──────────────────────────────────────────────────╯
Starting @ (0,2)
╭──────┬──────╮
│ Word │ Path │
├──────┼──────┤
╰──────┴──────╯
Starting @ (0, 3)
╭─────────┬──────────────────────────────────────────────────────────╮
│ Word │ Path │
├─────────┼──────────────────────────────────────────────────────────┤
│ pya │ [(0, 3), (1, 3), (1, 2)] │
│ pyas │ [(0, 3), (1, 3), (1, 2), (2, 3)] │
│ pa │ [(0, 3), (1, 2)] │
│ paal │ [(0, 3), (1, 2), (0, 1), (1, 0)] │
│ paals │ [(0, 3), (1, 2), (0, 1), (1, 0), (0, 0)] │
│ pal │ [(0, 3), (1, 2), (2, 2)] │
│ paly │ [(0, 3), (1, 2), (2, 2), (1, 3)] │
│ palas │ [(0, 3), (1, 2), (2, 2), (3, 3), (2, 3)] │
│ pall │ [(0, 3), (1, 2), (2, 2), (2, 1)] │
│ pals │ [(0, 3), (1, 2), (2, 2), (2, 3)] │
│ palsy │ [(0, 3), (1, 2), (2, 2), (2, 3), (1, 3)] │
│ palsa │ [(0, 3), (1, 2), (2, 2), (2, 3), (3, 3)] │
│ pal │ [(0, 3), (1, 2), (2, 1)] │
│ pall │ [(0, 3), (1, 2), (2, 1), (1, 0)] │
│ palls │ [(0, 3), (1, 2), (2, 1), (1, 0), (0, 0)] │
│ palla │ [(0, 3), (1, 2), (2, 1), (1, 0), (0, 1)] │
│ pall │ [(0, 3), (1, 2), (2, 1), (2, 2)] │
│ pally │ [(0, 3), (1, 2), (2, 1), (2, 2), (1, 3)] │
│ palla │ [(0, 3), (1, 2), (2, 1), (2, 2), (3, 3)] │
│ pallah │ [(0, 3), (1, 2), (2, 1), (2, 2), (3, 3), (3, 2)] │
│ pallahs │ [(0, 3), (1, 2), (2, 1), (2, 2), (3, 3), (3, 2), (2, 3)] │
│ palls │ [(0, 3), (1, 2), (2, 1), (2, 2), (2, 3)] │
│ pas │ [(0, 3), (1, 2), (2, 3)] │
│ pash │ [(0, 3), (1, 2), (2, 3), (3, 2)] │
│ pasha │ [(0, 3), (1, 2), (2, 3), (3, 2), (3, 3)] │
│ pay │ [(0, 3), (1, 2), (1, 3)] │
│ pays │ [(0, 3), (1, 2), (1, 3), (2, 3)] │
│ pi │ [(0, 3), (0, 2)] │
│ pia │ [(0, 3), (0, 2), (1, 2)] │
│ pial │ [(0, 3), (0, 2), (1, 2), (2, 2)] │
│ pial │ [(0, 3), (0, 2), (1, 2), (2, 1)] │
│ pias │ [(0, 3), (0, 2), (1, 2), (2, 3)] │
│ pia │ [(0, 3), (0, 2), (0, 1)] │
│ pial │ [(0, 3), (0, 2), (0, 1), (1, 0)] │
│ pias │ [(0, 3), (0, 2), (0, 1), (0, 0)] │
╰─────────┴──────────────────────────────────────────────────────────╯
Starting @ (1, 0)
╭───────┬──────────────────────────────────────────╮
│ Word │ Path │
├───────┼──────────────────────────────────────────┤
│ la │ [(1, 0), (0, 1)] │
│ las │ [(1, 0), (0, 1), (0, 0)] │
│ lull │ [(1, 0), (2, 0), (2, 1), (2, 2)] │
│ lulls │ [(1, 0), (2, 0), (2, 1), (2, 2), (2, 3)] │
╰───────┴──────────────────────────────────────────╯
Starting @ (1, 1)
╭───────┬──────────────────────────────────╮
│ Word │ Path │
├───────┼──────────────────────────────────┤
│ qua │ [(1, 1), (0, 1)] │
│ quai │ [(1, 1), (0, 1), (0, 2)] │
│ quip │ [(1, 1), (0, 2), (0, 3)] │
│ qua │ [(1, 1), (1, 2)] │
│ quai │ [(1, 1), (1, 2), (0, 2)] │
│ quash │ [(1, 1), (1, 2), (2, 3), (3, 2)] │
│ quay │ [(1, 1), (1, 2), (1, 3)] │
│ quays │ [(1, 1), (1, 2), (1, 3), (2, 3)] │
╰───────┴──────────────────────────────────╯
Starting @ (1, 2)
╭────────┬──────────────────────────────────────────────────╮
│ Word │ Path │
├────────┼──────────────────────────────────────────────────┤
│ ai │ [(1, 2), (0, 2)] │
│ aia │ [(1, 2), (0, 2), (0, 1)] │
│ aias │ [(1, 2), (0, 2), (0, 1), (0, 0)] │
│ aa │ [(1, 2), (0, 1)] │
│ aal │ [(1, 2), (0, 1), (1, 0)] │
│ aals │ [(1, 2), (0, 1), (1, 0), (0, 0)] │
│ aas │ [(1, 2), (0, 1), (0, 0)] │
│ al │ [(1, 2), (2, 2)] │
│ ala │ [(1, 2), (2, 2), (3, 3)] │
│ alas │ [(1, 2), (2, 2), (3, 3), (2, 3)] │
│ all │ [(1, 2), (2, 2), (2, 1)] │
│ allow │ [(1, 2), (2, 2), (2, 1), (3, 0), (3, 1)] │
│ als │ [(1, 2), (2, 2), (2, 3)] │
│ al │ [(1, 2), (2, 1)] │
│ all │ [(1, 2), (2, 1), (1, 0)] │
│ alls │ [(1, 2), (2, 1), (1, 0), (0, 0)] │
│ alow │ [(1, 2), (2, 1), (3, 0), (3, 1)] │
│ alu │ [(1, 2), (2, 1), (2, 0)] │
│ alula │ [(1, 2), (2, 1), (2, 0), (1, 0), (0, 1)] │
│ alulas │ [(1, 2), (2, 1), (2, 0), (1, 0), (0, 1), (0, 0)] │
│ all │ [(1, 2), (2, 1), (2, 2)] │
│ ally │ [(1, 2), (2, 1), (2, 2), (1, 3)] │
│ alls │ [(1, 2), (2, 1), (2, 2), (2, 3)] │
│ as │ [(1, 2), (2, 3)] │
│ asyla │ [(1, 2), (2, 3), (1, 3), (2, 2), (3, 3)] │
│ ash │ [(1, 2), (2, 3), (3, 2)] │
│ aqua │ [(1, 2), (1, 1), (0, 1)] │
│ aquas │ [(1, 2), (1, 1), (0, 1), (0, 0)] │
│ ay │ [(1, 2), (1, 3)] │
│ ays │ [(1, 2), (1, 3), (2, 3)] │
╰────────┴──────────────────────────────────────────────────╯
Starting @ (1, 3)
╭──────┬──────────────────────────╮
│ Word │ Path │
├──────┼──────────────────────────┤
│ yip │ [(1, 3), (0, 2), (0, 3)] │
│ ya │ [(1, 3), (1, 2)] │
│ yap │ [(1, 3), (1, 2), (0, 3)] │
│ yas │ [(1, 3), (1, 2), (2, 3)] │
╰──────┴──────────────────────────╯
Starting @ (2, 0)
╭──────┬──────╮
│ Word │ Path │
├──────┼──────┤
╰──────┴──────╯
Starting @ (2, 1)
╭───────┬──────────────────────────────────────────╮
│ Word │ Path │
├───────┼──────────────────────────────────────────┤
│ la │ [(2, 1), (1, 2)] │
│ lap │ [(2, 1), (1, 2), (0, 3)] │
│ las │ [(2, 1), (1, 2), (2, 3)] │
│ lash │ [(2, 1), (1, 2), (2, 3), (3, 2)] │
│ lay │ [(2, 1), (1, 2), (1, 3)] │
│ lays │ [(2, 1), (1, 2), (1, 3), (2, 3)] │
│ lo │ [(2, 1), (3, 0)] │
│ lou │ [(2, 1), (3, 0), (2, 0)] │
│ low │ [(2, 1), (3, 0), (3, 1)] │
│ lowly │ [(2, 1), (3, 0), (3, 1), (2, 2), (1, 3)] │
╰───────┴──────────────────────────────────────────╯
Starting @ (2, 2)
╭───────┬──────────────────────────────────────────╮
│ Word │ Path │
├───────┼──────────────────────────────────────────┤
│ la │ [(2, 2), (1, 2)] │
│ lap │ [(2, 2), (1, 2), (0, 3)] │
│ lall │ [(2, 2), (1, 2), (2, 1), (1, 0)] │
│ lalls │ [(2, 2), (1, 2), (2, 1), (1, 0), (0, 0)] │
│ las │ [(2, 2), (1, 2), (2, 3)] │
│ lash │ [(2, 2), (1, 2), (2, 3), (3, 2)] │
│ lay │ [(2, 2), (1, 2), (1, 3)] │
│ lays │ [(2, 2), (1, 2), (1, 3), (2, 3)] │
│ la │ [(2, 2), (3, 3)] │
│ las │ [(2, 2), (3, 3), (2, 3)] │
│ lash │ [(2, 2), (3, 3), (2, 3), (3, 2)] │
│ lah │ [(2, 2), (3, 3), (3, 2)] │
│ lahs │ [(2, 2), (3, 3), (3, 2), (2, 3)] │
╰───────┴──────────────────────────────────────────╯
Starting @ (2, 3)
╭─────────┬──────────────────────────────────────────────────────────╮
│ Word │ Path │
├─────────┼──────────────────────────────────────────────────────────┤
│ sai │ [(2, 3), (1, 2), (0, 2)] │
│ sap │ [(2, 3), (1, 2), (0, 3)] │
│ sal │ [(2, 3), (1, 2), (2, 2)] │
│ sall │ [(2, 3), (1, 2), (2, 2), (2, 1)] │
│ sallow │ [(2, 3), (1, 2), (2, 2), (2, 1), (3, 0), (3, 1)] │
│ sal │ [(2, 3), (1, 2), (2, 1)] │
│ sall │ [(2, 3), (1, 2), (2, 1), (1, 0)] │
│ sall │ [(2, 3), (1, 2), (2, 1), (2, 2)] │
│ sally │ [(2, 3), (1, 2), (2, 1), (2, 2), (1, 3)] │
│ say │ [(2, 3), (1, 2), (1, 3)] │
│ sal │ [(2, 3), (3, 3), (2, 2)] │
│ salal │ [(2, 3), (3, 3), (2, 2), (1, 2), (2, 1)] │
│ sall │ [(2, 3), (3, 3), (2, 2), (2, 1)] │
│ sallow │ [(2, 3), (3, 3), (2, 2), (2, 1), (3, 0), (3, 1)] │
│ sh │ [(2, 3), (3, 2)] │
│ sha │ [(2, 3), (3, 2), (3, 3)] │
│ shaly │ [(2, 3), (3, 2), (3, 3), (2, 2), (1, 3)] │
│ shall │ [(2, 3), (3, 2), (3, 3), (2, 2), (2, 1)] │
│ shallow │ [(2, 3), (3, 2), (3, 3), (2, 2), (2, 1), (3, 0), (3, 1)] │
│ slap │ [(2, 3), (2, 2), (1, 2), (0, 3)] │
│ slay │ [(2, 3), (2, 2), (1, 2), (1, 3)] │
│ sly │ [(2, 3), (2, 2), (1, 3)] │
╰─────────┴──────────────────────────────────────────────────────────╯
Starting @ (3, 0)
╭───────┬──────────────────────────────────────────╮
│ Word │ Path │
├───────┼──────────────────────────────────────────┤
│ ou │ [(3, 0), (2, 0)] │
│ olla │ [(3, 0), (2, 1), (1, 0), (0, 1)] │
│ ollas │ [(3, 0), (2, 1), (1, 0), (0, 1), (0, 0)] │
│ olla │ [(3, 0), (2, 1), (2, 2), (1, 2)] │
│ ollas │ [(3, 0), (2, 1), (2, 2), (1, 2), (2, 3)] │
│ olla │ [(3, 0), (2, 1), (2, 2), (3, 3)] │
│ ollas │ [(3, 0), (2, 1), (2, 2), (3, 3), (2, 3)] │
│ ow │ [(3, 0), (3, 1)] │
│ owl │ [(3, 0), (3, 1), (2, 1)] │
│ owl │ [(3, 0), (3, 1), (2, 2)] │
│ owly │ [(3, 0), (3, 1), (2, 2), (1, 3)] │
│ owls │ [(3, 0), (3, 1), (2, 2), (2, 3)] │
╰───────┴──────────────────────────────────────────╯
Starting @ (3, 1)
╭───────┬──────────────────────────────────────────╮
│ Word │ Path │
├───────┼──────────────────────────────────────────┤
│ wull │ [(3, 1), (2, 0), (1, 0), (2, 1)] │
│ wull │ [(3, 1), (2, 0), (2, 1), (1, 0)] │
│ wulls │ [(3, 1), (2, 0), (2, 1), (1, 0), (0, 0)] │
│ wull │ [(3, 1), (2, 0), (2, 1), (2, 2)] │
│ wulls │ [(3, 1), (2, 0), (2, 1), (2, 2), (2, 3)] │
│ wo │ [(3, 1), (3, 0)] │
│ wolly │ [(3, 1), (3, 0), (2, 1), (2, 2), (1, 3)] │
│ wha │ [(3, 1), (3, 2), (3, 3)] │
╰───────┴──────────────────────────────────────────╯
Starting @ (3, 2)
╭────────┬──────────────────────────────────────────────────╮
│ Word │ Path │
├────────┼──────────────────────────────────────────────────┤
│ ha │ [(3, 2), (3, 3)] │
│ has │ [(3, 2), (3, 3), (2, 3)] │
│ halal │ [(3, 2), (3, 3), (2, 2), (1, 2), (2, 1)] │
│ hall │ [(3, 2), (3, 3), (2, 2), (2, 1)] │
│ hallo │ [(3, 2), (3, 3), (2, 2), (2, 1), (3, 0)] │
│ hallow │ [(3, 2), (3, 3), (2, 2), (2, 1), (3, 0), (3, 1)] │
╰────────┴──────────────────────────────────────────────────╯
Starting @ (3, 3)
╭────────┬──────────────────────────────────────────────────╮
│ Word │ Path │
├────────┼──────────────────────────────────────────────────┤
│ as │ [(3, 3), (2, 3)] │
│ asyla │ [(3, 3), (2, 3), (1, 3), (2, 2), (1, 2)] │
│ ash │ [(3, 3), (2, 3), (3, 2)] │
│ al │ [(3, 3), (2, 2)] │
│ ala │ [(3, 3), (2, 2), (1, 2)] │
│ alap │ [(3, 3), (2, 2), (1, 2), (0, 3)] │
│ alas │ [(3, 3), (2, 2), (1, 2), (2, 3)] │
│ alay │ [(3, 3), (2, 2), (1, 2), (1, 3)] │
│ alays │ [(3, 3), (2, 2), (1, 2), (1, 3), (2, 3)] │
│ all │ [(3, 3), (2, 2), (2, 1)] │
│ allay │ [(3, 3), (2, 2), (2, 1), (1, 2), (1, 3)] │
│ allays │ [(3, 3), (2, 2), (2, 1), (1, 2), (1, 3), (2, 3)] │
│ allow │ [(3, 3), (2, 2), (2, 1), (3, 0), (3, 1)] │
│ als │ [(3, 3), (2, 2), (2, 3)] │
│ ah │ [(3, 3), (3, 2)] │
│ ahs │ [(3, 3), (3, 2), (2, 3)] │
╰────────┴──────────────────────────────────────────────────╯
</code></pre>
</details>
<br>
Starting @ (0, 0)...
y : [(0, 0)]
yr : [(0, 0), (1, 0)]
ye : [(0, 0), (1, 1)]
yee : [(0, 0), (1, 1), (0, 1)]
yeo : [(0, 0), (1, 1), (0, 2)]
yed : [(0, 0), (1, 1), (2, 0)]
yee : [(0, 0), (1, 1), (2, 2)]
yer : [(0, 0), (1, 1), (1, 0)]
yere : [(0, 0), (1, 1), (1, 0), (0, 1)]
yerd : [(0, 0), (1, 1), (1, 0), (2, 0)]
yeo : [(0, 0), (1, 1), (1, 2)]
ye : [(0, 0), (0, 1)]
yee : [(0, 0), (0, 1), (1, 1)]
yer : [(0, 0), (0, 1), (1, 0)]
yerd : [(0, 0), (0, 1), (1, 0), (2, 0)]
yere : [(0, 0), (0, 1), (1, 0), (1, 1)]
yeo : [(0, 0), (0, 1), (1, 2)]
yeo : [(0, 0), (0, 1), (0, 2)]
Starting @ (0, 1)...
e : [(0, 1)]
ee : [(0, 1), (1, 1)]
eer : [(0, 1), (1, 1), (1, 0)]
eery : [(0, 1), (1, 1), (1, 0), (0, 0)]
er : [(0, 1), (1, 0)]
erd : [(0, 1), (1, 0), (2, 0)]
erf : [(0, 1), (1, 0), (2, 1)]
ere : [(0, 1), (1, 0), (1, 1)]
eo : [(0, 1), (1, 2)]
eos : [(0, 1), (1, 2), (0, 3)]
eof : [(0, 1), (1, 2), (2, 1)]
ey : [(0, 1), (0, 0)]
eyr : [(0, 1), (0, 0), (1, 0)]
eyre : [(0, 1), (0, 0), (1, 0), (1, 1)]
eye : [(0, 1), (0, 0), (1, 1)]
eyed : [(0, 1), (0, 0), (1, 1), (2, 0)]
eyer : [(0, 1), (0, 0), (1, 1), (1, 0)]
eo : [(0, 1), (0, 2)]
eos : [(0, 1), (0, 2), (0, 3)]
Starting @ (0, 2)...
o : [(0, 2)]
oos : [(0, 2), (1, 2), (0, 3)]
oof : [(0, 2), (1, 2), (2, 1)]
oe : [(0, 2), (1, 1)]
oer : [(0, 2), (1, 1), (1, 0)]
ovey : [(0, 2), (1, 3), (2, 2), (2, 3)]
oe : [(0, 2), (0, 1)]
oer : [(0, 2), (0, 1), (1, 0)]
os : [(0, 2), (0, 3)]
Starting @ (0, 3)...
s : [(0, 3)]
sv : [(0, 3), (1, 3)]
so : [(0, 3), (1, 2)]
sooey : [(0, 3), (1, 2), (0, 2), (1, 1), (0, 0)]
sooey : [(0, 3), (1, 2), (0, 2), (0, 1), (0, 0)]
soe : [(0, 3), (1, 2), (0, 1)]
soe : [(0, 3), (1, 2), (2, 2)]
sofer : [(0, 3), (1, 2), (2, 1), (1, 1), (1, 0)]
soy : [(0, 3), (1, 2), (2, 3)]
soe : [(0, 3), (1, 2), (1, 1)]
sov : [(0, 3), (1, 2), (1, 3)]
so : [(0, 3), (0, 2)]
sooey : [(0, 3), (0, 2), (1, 2), (0, 1), (0, 0)]
sooey : [(0, 3), (0, 2), (1, 2), (2, 2), (2, 3)]
sooey : [(0, 3), (0, 2), (1, 2), (1, 1), (0, 0)]
soe : [(0, 3), (0, 2), (1, 1)]
sov : [(0, 3), (0, 2), (1, 3)]
soe : [(0, 3), (0, 2), (0, 1)]
Starting @ (1, 0)...
r : [(1, 0)]
rye : [(1, 0), (0, 0), (1, 1)]
rye : [(1, 0), (0, 0), (0, 1)]
re : [(1, 0), (0, 1)]
ree : [(1, 0), (0, 1), (1, 1)]
reef : [(1, 0), (0, 1), (1, 1), (2, 1)]
reed : [(1, 0), (0, 1), (1, 1), (2, 0)]
rd : [(1, 0), (2, 0)]
rf : [(1, 0), (2, 1)]
re : [(1, 0), (1, 1)]
ree : [(1, 0), (1, 1), (0, 1)]
ref : [(1, 0), (1, 1), (2, 1)]
red : [(1, 0), (1, 1), (2, 0)]
ree : [(1, 0), (1, 1), (2, 2)]
reem : [(1, 0), (1, 1), (2, 2), (3, 1)]
reef : [(1, 0), (1, 1), (2, 2), (2, 1)]
Starting @ (1, 1)...
e : [(1, 1)]
ee : [(1, 1), (0, 1)]
eer : [(1, 1), (0, 1), (1, 0)]
eery : [(1, 1), (0, 1), (1, 0), (0, 0)]
ey : [(1, 1), (0, 0)]
eyr : [(1, 1), (0, 0), (1, 0)]
eyre : [(1, 1), (0, 0), (1, 0), (0, 1)]
eye : [(1, 1), (0, 0), (0, 1)]
eyer : [(1, 1), (0, 0), (0, 1), (1, 0)]
eo : [(1, 1), (0, 2)]
eos : [(1, 1), (0, 2), (0, 3)]
ef : [(1, 1), (2, 1)]
ed : [(1, 1), (2, 0)]
ee : [(1, 1), (2, 2)]
er : [(1, 1), (1, 0)]
ere : [(1, 1), (1, 0), (0, 1)]
erd : [(1, 1), (1, 0), (2, 0)]
erf : [(1, 1), (1, 0), (2, 1)]
eo : [(1, 1), (1, 2)]
eos : [(1, 1), (1, 2), (0, 3)]
eof : [(1, 1), (1, 2), (2, 1)]
Starting @ (1, 2)...
o : [(1, 2)]
oos : [(1, 2), (0, 2), (0, 3)]
oe : [(1, 2), (0, 1)]
oer : [(1, 2), (0, 1), (1, 0)]
os : [(1, 2), (0, 3)]
oe : [(1, 2), (2, 2)]
of : [(1, 2), (2, 1)]
ofer : [(1, 2), (2, 1), (1, 1), (1, 0)]
oy : [(1, 2), (2, 3)]
oe : [(1, 2), (1, 1)]
oer : [(1, 2), (1, 1), (1, 0)]
ovey : [(1, 2), (1, 3), (2, 2), (2, 3)]
Starting @ (1, 3)...
v : [(1, 3)]
vs : [(1, 3), (0, 3)]
vo : [(1, 3), (0, 2)]
voe : [(1, 3), (0, 2), (1, 1)]
voe : [(1, 3), (0, 2), (0, 1)]
vee : [(1, 3), (2, 2), (1, 1)]
veer : [(1, 3), (2, 2), (1, 1), (1, 0)]
veery : [(1, 3), (2, 2), (1, 1), (1, 0), (0, 0)]
vee : [(1, 3), (2, 2), (3, 2)]
vefry : [(1, 3), (2, 2), (2, 1), (1, 0), (0, 0)]
vo : [(1, 3), (1, 2)]
voe : [(1, 3), (1, 2), (0, 1)]
voe : [(1, 3), (1, 2), (2, 2)]
voe : [(1, 3), (1, 2), (1, 1)]
Starting @ (2, 0)...
d : [(2, 0)]
dr : [(2, 0), (1, 0)]
dry : [(2, 0), (1, 0), (0, 0)]
dree : [(2, 0), (1, 0), (0, 1), (1, 1)]
drey : [(2, 0), (1, 0), (0, 1), (0, 0)]
dree : [(2, 0), (1, 0), (1, 1), (0, 1)]
drey : [(2, 0), (1, 0), (1, 1), (0, 0)]
dree : [(2, 0), (1, 0), (1, 1), (2, 2)]
de : [(2, 0), (1, 1)]
dee : [(2, 0), (1, 1), (0, 1)]
deer : [(2, 0), (1, 1), (0, 1), (1, 0)]
dey : [(2, 0), (1, 1), (0, 0)]
def : [(2, 0), (1, 1), (2, 1)]
dee : [(2, 0), (1, 1), (2, 2)]
deem : [(2, 0), (1, 1), (2, 2), (3, 1)]
der : [(2, 0), (1, 1), (1, 0)]
dere : [(2, 0), (1, 1), (1, 0), (0, 1)]
derf : [(2, 0), (1, 1), (1, 0), (2, 1)]
dn : [(2, 0), (3, 0)]
dm : [(2, 0), (3, 1)]
Starting @ (2, 1)...
f : [(2, 1)]
fe : [(2, 1), (1, 1)]
fee : [(2, 1), (1, 1), (0, 1)]
feer : [(2, 1), (1, 1), (0, 1), (1, 0)]
fey : [(2, 1), (1, 1), (0, 0)]
feyer : [(2, 1), (1, 1), (0, 0), (0, 1), (1, 0)]
fed : [(2, 1), (1, 1), (2, 0)]
fedn : [(2, 1), (1, 1), (2, 0), (3, 0)]
fee : [(2, 1), (1, 1), (2, 2)]
fer : [(2, 1), (1, 1), (1, 0)]
fere : [(2, 1), (1, 1), (1, 0), (0, 1)]
ferd : [(2, 1), (1, 1), (1, 0), (2, 0)]
fr : [(2, 1), (1, 0)]
fry : [(2, 1), (1, 0), (0, 0)]
free : [(2, 1), (1, 0), (0, 1), (1, 1)]
freed : [(2, 1), (1, 0), (0, 1), (1, 1), (2, 0)]
frey : [(2, 1), (1, 0), (0, 1), (0, 0)]
free : [(2, 1), (1, 0), (1, 1), (0, 1)]
frey : [(2, 1), (1, 0), (1, 1), (0, 0)]
fred : [(2, 1), (1, 0), (1, 1), (2, 0)]
free : [(2, 1), (1, 0), (1, 1), (2, 2)]
fo : [(2, 1), (1, 2)]
foo : [(2, 1), (1, 2), (0, 2)]
foe : [(2, 1), (1, 2), (0, 1)]
foe : [(2, 1), (1, 2), (2, 2)]
foy : [(2, 1), (1, 2), (2, 3)]
foe : [(2, 1), (1, 2), (1, 1)]
fm : [(2, 1), (3, 1)]
fn : [(2, 1), (3, 0)]
fe : [(2, 1), (3, 2)]
fee : [(2, 1), (3, 2), (2, 2)]
fey : [(2, 1), (3, 2), (2, 3)]
fem : [(2, 1), (3, 2), (3, 1)]
feme : [(2, 1), (3, 2), (3, 1), (2, 2)]
fe : [(2, 1), (2, 2)]
fee : [(2, 1), (2, 2), (1, 1)]
feed : [(2, 1), (2, 2), (1, 1), (2, 0)]
feer : [(2, 1), (2, 2), (1, 1), (1, 0)]
feere : [(2, 1), (2, 2), (1, 1), (1, 0), (0, 1)]
fee : [(2, 1), (2, 2), (3, 2)]
fem : [(2, 1), (2, 2), (3, 1)]
feme : [(2, 1), (2, 2), (3, 1), (3, 2)]
fey : [(2, 1), (2, 2), (2, 3)]
Starting @ (2, 2)...
e : [(2, 2)]
eo : [(2, 2), (1, 2)]
eos : [(2, 2), (1, 2), (0, 3)]
eof : [(2, 2), (1, 2), (2, 1)]
ee : [(2, 2), (1, 1)]
eer : [(2, 2), (1, 1), (1, 0)]
eery : [(2, 2), (1, 1), (1, 0), (0, 0)]
evoe : [(2, 2), (1, 3), (0, 2), (1, 1)]
evoe : [(2, 2), (1, 3), (0, 2), (0, 1)]
evoe : [(2, 2), (1, 3), (1, 2), (0, 1)]
evoe : [(2, 2), (1, 3), (1, 2), (1, 1)]
ee : [(2, 2), (3, 2)]
em : [(2, 2), (3, 1)]
emf : [(2, 2), (3, 1), (2, 1)]
eme : [(2, 2), (3, 1), (3, 2)]
ef : [(2, 2), (2, 1)]
ey : [(2, 2), (2, 3)]
eye : [(2, 2), (2, 3), (3, 2)]
Starting @ (2, 3)...
y : [(2, 3)]
yo : [(2, 3), (1, 2)]
yoe : [(2, 3), (1, 2), (0, 1)]
yoe : [(2, 3), (1, 2), (2, 2)]
yoe : [(2, 3), (1, 2), (1, 1)]
yquem : [(2, 3), (3, 3), (2, 2), (3, 1)]
yquem : [(2, 3), (3, 3), (3, 2), (3, 1)]
ye : [(2, 3), (3, 2)]
yee : [(2, 3), (3, 2), (2, 2)]
ye : [(2, 3), (2, 2)]
yeo : [(2, 3), (2, 2), (1, 2)]
yee : [(2, 3), (2, 2), (1, 1)]
yee : [(2, 3), (2, 2), (3, 2)]
Starting @ (3, 0)...
n : [(3, 0)]
nd : [(3, 0), (2, 0)]
nm : [(3, 0), (3, 1)]
Starting @ (3, 1)...
m : [(3, 1)]
mf : [(3, 1), (2, 1)]
mfr : [(3, 1), (2, 1), (1, 0)]
mfd : [(3, 1), (2, 1), (2, 0)]
md : [(3, 1), (2, 0)]
me : [(3, 1), (2, 2)]
meo : [(3, 1), (2, 2), (1, 2)]
mee : [(3, 1), (2, 2), (1, 1)]
meed : [(3, 1), (2, 2), (1, 1), (2, 0)]
meer : [(3, 1), (2, 2), (1, 1), (1, 0)]
mev : [(3, 1), (2, 2), (1, 3)]
mee : [(3, 1), (2, 2), (3, 2)]
mn : [(3, 1), (3, 0)]
me : [(3, 1), (3, 2)]
mee : [(3, 1), (3, 2), (2, 2)]
Starting @ (3, 2)...
e : [(3, 2)]
ee : [(3, 2), (2, 2)]
ef : [(3, 2), (2, 1)]
ey : [(3, 2), (2, 3)]
eye : [(3, 2), (2, 3), (2, 2)]
em : [(3, 2), (3, 1)]
emf : [(3, 2), (3, 1), (2, 1)]
eme : [(3, 2), (3, 1), (2, 2)]
emeer : [(3, 2), (3, 1), (2, 2), (1, 1), (1, 0)]
Starting @ (3, 3)...
qu : [(3, 3)]
que : [(3, 3), (2, 2)]
queer : [(3, 3), (2, 2), (1, 1), (1, 0)]
queery : [(3, 3), (2, 2), (1, 1), (1, 0), (0, 0)]
quem : [(3, 3), (2, 2), (3, 1)]
queme : [(3, 3), (2, 2), (3, 1), (3, 2)]
quey : [(3, 3), (2, 2), (2, 3)]
que : [(3, 3), (3, 2)]
quey : [(3, 3), (3, 2), (2, 3)]
quem : [(3, 3), (3, 2), (3, 1)]
queme : [(3, 3), (3, 2), (3, 1), (2, 2)]
```
# Build
Navigate to the project root folder and run the following.
Run `poetry build`
# License
The included [wordlists](src/boggler/wordlists) are covered by their respective licenses. All other files MIT © Cameron Blankenbuehler
The included [wordlists](boggler/wordlists) are covered by their respective licenses. All other files [MIT](LICENSE) © Cameron Blankenbuehler
+125
View File
@@ -0,0 +1,125 @@
"""Boggler Demo"""
import argparse
import json
from itertools import chain
import sys
from pathlib import Path
from rich.console import Console
from rich.table import Table
from rich import box
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:
console = Console()
print("\nBOARD")
print(boggle_board)
for start_pos, tree in boggle_tree.items():
table = Table(
title=f"Starting @ {start_pos}",
show_header=True,
header_style="bold purple",
row_styles=["dim", ""],
box=box.ROUNDED,
)
table.add_column("Word")
table.add_column("Path")
for word in tree.word_paths:
# print(f"{word[0]: <{boggle_board.max_word_len}}: {word[1]}")
table.add_row(f"{word[0]}", f"{word[1]}")
console.print(table)
if __name__ == "__main__":
main()
+58
View File
@@ -0,0 +1,58 @@
"""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
from typing import List, TypeAlias
Die: TypeAlias = List[str]
Dice: TypeAlias = List[Die]
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: Die):
"""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: Dice):
"""Return a random roll for each die"""
return [roll_die(die) for die in dice]
def get_random_board(dice: Dice):
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: Dice):
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)
@@ -1,41 +1,48 @@
'''Boggler Utils'''
"""Boggler Utils"""
from __future__ import annotations
from os import path
from pathlib import Path
from multiprocessing import Pool
import logging as log
import functools
import operator
class BoardCell:
'''Boggle Board cell'''
def __init__(self, row: int, col: int, letters: str,
adjacent_cells: list[BoardCell] = None) -> BoardCell:
"""Boggle Board cell"""
def __init__(
self, row: int, col: int, letters: str, adjacent_cells: list[BoardCell] = None
) -> None:
self.__row: int = row
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.__adjacent_cells: list[BoardCell] = adjacent_cells
@property
def row(self) -> int:
'''Getter for row property'''
"""Getter for row property"""
return self.__row
@property
def col(self) -> int:
'''Getter for col property'''
"""Getter for col property"""
return self.__col
@property
def pos(self) -> (int, int):
'''Getter for pos property'''
def pos(self) -> tuple[int, int]:
"""Getter for pos property"""
return self.__pos
@property
def letters(self) -> str:
'''Getter for letter property'''
"""Getter for letter property"""
return self.__letters
@property
def adjacent_cells(self) -> list[BoardCell]:
'''Getter for adjacent_cells property'''
"""Getter for adjacent_cells property"""
return self.__adjacent_cells
@adjacent_cells.setter
@@ -48,13 +55,15 @@ class BoardCell:
def __repr__(self):
return f"(BoardCell({self.__row}, {self.__col}): {self.__letters}"
class BoggleBoard:
'''Boggle board structure'''
def __init__(self, board: list[list[str]], max_word_len: int = 14) -> BoggleBoard:
"""Boggle board structure"""
def __init__(self, board: list[list[str]], max_word_len: int = 14) -> None:
self.__height: int = len(board)
self.__width: int = len(board[0]) if self.__height > 0 else 0
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
self.__max_word_len = min(max_word_len, self.__width * self.__height)
@@ -72,7 +81,9 @@ class BoggleBoard:
def __str__(self):
flattened_board_list = [y for x in self.__board_list for y in x]
max_len = len(max(flattened_board_list, key=len))
max_len = max_len + 1 if max_len % 2 == 0 else max_len + 2 # keep header_len odd
max_len = (
max_len + 1 if max_len % 2 == 0 else max_len + 2
) # keep header_len odd
header_len = self.__width * (max_len + 1) - 1
head = "-" * header_len
header = f"+{head}+\n"
@@ -85,97 +96,110 @@ class BoggleBoard:
body += header
return f"{header}{body}"
@property
@functools.cached_property
def height(self) -> int:
'''Getter for height property'''
"""Getter for height property"""
return self.__height
@property
@functools.cached_property
def width(self) -> int:
'''Getter for width property'''
"""Getter for width property"""
return self.__width
@property
@functools.cached_property
def max_word_len(self) -> int:
'''Getter for maximum word length property'''
"""Getter for maximum word length property"""
return self.__max_word_len
@property
def board(self) -> dict[(int, int), BoardCell]:
'''Getter for board property'''
@functools.cached_property
def board(self) -> dict[tuple[int, int], BoardCell]:
"""Getter for board property"""
return self.__board
def __get_adjacent_indexes(self, row, col):
'''Return adjecency list for board of size `row x col`'''
"""Return adjecency list for board of size `row x col`"""
indexes = []
if row > 0:
indexes.append((row-1, col)) # up
indexes.append((row - 1, col)) # up
if col > 0:
indexes.append((row-1, col-1)) # up-left
indexes.append((row - 1, col - 1)) # up-left
if col < self.width - 1:
indexes.append((row-1, col+1)) # up-right
indexes.append((row - 1, col + 1)) # up-right
if row < self.height - 1:
indexes.append((row+1, col)) # down
indexes.append((row + 1, col)) # down
if col > 0:
indexes.append((row+1, col-1)) # down-left
indexes.append((row + 1, col - 1)) # down-left
if col < self.width - 1:
indexes.append((row+1, col+1)) # down-right
indexes.append((row + 1, col + 1)) # down-right
if col > 0:
indexes.append((row, col-1)) # left
indexes.append((row, col - 1)) # left
if col < self.width - 1:
indexes.append((row, col+1)) # right
indexes.append((row, col + 1)) # right
return indexes
def get_cell(self, row: int, col: int) -> str:
'''Return the value at the specified row x column'''
def get_cell(self, row: int, col: int) -> BoardCell:
"""Return the value at the specified row x column"""
return self.__board[(row, col)]
class WordNode:
'''A node describing a single letter in a WordTree.'''
def __init__(self, letters: str, is_word: bool = False, parent: WordNode = None,
children: dict[str, WordNode] = None, board_pos = None) -> WordNode:
"""A node describing a single letter in a WordTree."""
def __init__(
self,
letters: str,
is_word: bool = False,
parent: WordNode = None,
children: dict[str, WordNode] = None,
board_pos=(None, None),
) -> None:
self.__letters = letters
self.__is_word = is_word
self.__children = children if children is not None else {}
self.__parent = parent
self.__board_pos = board_pos
@property
@functools.cached_property
def letters(self) -> str:
'''Getter for letter property'''
"""Getter for letter property"""
return self.__letters
@functools.cached_property
def letters_count(self) -> int:
"""Getter for letters count"""
return len(self.__letters)
@property
def is_word(self) -> bool:
'''Getter for is_word property'''
"""Getter for is_word property"""
return self.__is_word
@is_word.setter
def is_word(self, value):
self.__is_word = value
@property
@functools.cached_property
def children(self) -> dict[str, WordNode]:
'''Getter for children property'''
"""Getter for children property"""
return self.__children
@property
def parent(self) -> WordNode:
'''Getter for parent property'''
"""Getter for parent property"""
return self.__parent
@property
def board_pos(self) -> (int, int):
'''Getter for board_pos property'''
def board_pos(self) -> tuple[int, int]:
"""Getter for board_pos property"""
return self.__board_pos
def add_child_node(self, node):
'''Add child node to `children` dictionary, indexed by the nodes' `letter`'''
"""Add child node to `children` dictionary, indexed by the nodes' `letter`"""
self.children[node.letters] = node
@property
def path(self) -> list[WordNode]:
'''Return list of nodes from current node to the root'''
"""Return list of nodes from current node to the root"""
curr_node = self
path = []
path.append(curr_node.board_pos)
@@ -186,7 +210,7 @@ class WordNode:
return path
def get_word(self, board: BoggleBoard) -> str:
'''Return word or word fragment based on the WordNode's path property'''
"""Return word or word fragment based on the WordNode's path property"""
return "".join([board.board[x].letters for x in self.path[::-1]])
def __str__(self):
@@ -195,16 +219,23 @@ class WordNode:
def __repr__(self):
return f"WordNode: {self.letters}, {self.is_word}, {self.children}"
class WordTree:
'''A tree populated by WordNode(s) to complete words from a given root letter and wordlist'''
def __init__(self, alphabet: list, root: WordNode, words: list[str] = None,
max_word_len = 16) -> WordTree:
self.__alphabet: list = alphabet
self.__wordlist: list[str] = words
self.__root: WordNode = root
self.__max_word_len: int = max_word_len
"""A tree populated by WordNode(s) to complete words from a given root letter and wordlist"""
def __init__(
self,
alphabet: list[str],
root: WordNode,
words: list[str] | None = None,
max_word_len=16,
) -> None:
self.__alphabet = alphabet
self.__wordlist = words
self.__root = root
self.__max_word_len = max_word_len
self.__tree: dict[str, WordNode] = {}
self.__word_paths: dict[str, list[WordNode]] = []
self.__word_paths: list[str, list[WordNode]] = []
# Generate root node
self.__tree[root.letters] = root
@@ -215,34 +246,34 @@ class WordTree:
for word in words:
self.__insert_word(word)
@property
@functools.cached_property
def alphabet(self) -> list:
'''Getter for alphabet property'''
"""Getter for alphabet property"""
return self.__alphabet
@property
def wordlist(self) -> list[str]:
'''Getter for wordlist property'''
def wordlist(self) -> list[str] | None:
"""Getter for wordlist property"""
return self.__wordlist
@property
@functools.cached_property
def root(self) -> WordNode:
'''Getter for root property'''
"""Getter for root property"""
return self.__root
@property
@functools.cached_property
def max_word_len(self) -> int:
'''Getter for max_word_len property'''
"""Getter for max_word_len property"""
return self.__max_word_len
@property
@functools.cached_property
def tree(self) -> dict[str, WordNode]:
'''Getter for tree property'''
"""Getter for tree property"""
return self.__tree
@property
def word_paths(self) -> dict[(int, int), WordNode]:
'''Getter for leaf_nodes property'''
"""Getter for leaf_nodes property"""
return self.__word_paths
@word_paths.setter
@@ -250,138 +281,162 @@ class WordTree:
self.__word_paths = value
def __str__(self):
return ", ".join(self.wordlist)
return ", ".join([str(x) for x in self.word_paths])
def insert_node(self, letters: str, parent: WordNode, is_word: bool = False,
children: dict[str, WordNode] = None, board_pos = None):
'''Create WordNode for `letters` and into WordTree under `parent`'''
def __repr__(self):
return self.__str__()
def insert_node(
self,
letters: str,
parent: WordNode,
is_word: bool = False,
children: dict[str, WordNode] = None,
board_pos=None,
):
"""Create WordNode for `letters` and into WordTree under `parent`"""
node = WordNode(letters, is_word, parent, children, board_pos)
parent.add_child_node(node)
def __insert_word(self, word: str) -> bool:
'''Returns True if the word could be inserted into the tree with the given alphabet,
otherwise returns False'''
"""Returns True if the word could be inserted into the tree with the given alphabet,
otherwise returns False"""
# Insert root node
prefix = word[0:len(self.root.letters)] # prefix = first letter block
prefix = word[0 : self.root.letters_count] # prefix = first letter block
if word is None or prefix != self.root.letters:
return False
curr_node = self.tree[prefix]
word_len = len(word)
# Insert remaining nodes as letter groups based on alphabet
skip_cnt = 0
for i, letter in enumerate(word[len(prefix):self.max_word_len]):
# Skip word letter iterations for length of a previously inserted letter group
if skip_cnt > 0:
skip_cnt -= 1
continue
try:
alpha_index = self.alphabet.index(letter)
except ValueError: # letters not in given alphabet
return False
# Insert letter (single)
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
#print(f"LONG GROUP: {alpha}; {word}")
else:
i = len(prefix)
i_max = min(self.max_word_len, word_len)
while i < i_max:
letters = word[i]
if letters not in self.alphabet:
# Check two letter sequences like ("Qu", "Th", etc.) at current index
letters = word[i : i + 2]
if letters not in self.alphabet:
return False
# Insert node
if letters not in curr_node.children:
self.insert_node(letters, curr_node)
curr_node = curr_node.children[letters]
i += len(letters)
# Mark the last node as a word
curr_node.is_word = len(word) <= self.max_word_len
curr_node.is_word = word_len <= self.max_word_len
return True
def search(self, word: str, curr_node = None) -> WordNode:
'''Return leaf (WordNode) if a given word is in the tree otherwise return None'''
def search(self, word: str, curr_node=None) -> WordNode:
"""Return leaf (WordNode) if a given word is in the tree otherwise return None"""
if len(word) == 0 or word is None:
return curr_node
log.debug("Searching... %s %s", word, curr_node)
if curr_node is None:
curr_node = self.root
for letters in curr_node.children:
if letters == word[len(curr_node.letters):len(curr_node.letters) + len(letters)]:
return self.search(word[len(curr_node.letters):], curr_node.children[letters])
if (
letters
== word[len(curr_node.letters) : len(curr_node.letters) + len(letters)]
):
return self.search(
word[len(curr_node.letters) :], curr_node.children[letters]
)
# Currently only returns single path for word
# TODO: return all possible paths, maybe create minature WordTree? Or just of list of paths.
return curr_node
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).
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).
Keyword arguments:
board -- the board the new tree is based on
board_node -- a pointer on the board where new branch nodes can be inserted into the tree
dict_node -- a pointer on the dictionary where nodes are read from for validation
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:
#print(f"MAX DEPTH REACHED! Depth = {depth}")
# MAX WORD LENGTH REACHED!
subtree.active_node = subtree.active_node.parent
self.active_node = self.active_node.parent
return
return subtree
# 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:
word_path = subtree.active_node.path[::-1]
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)
# Word found
self.active_node = self.active_node.parent
subtree.active_node = subtree.active_node.parent
return
return subtree
elif self.active_node.is_word:
# Word found
word_path = subtree.active_node.path[::-1]
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)
# Branch for each adjacent board cell
for cell in board_cell.adjacent_cells:
# Check dictionary and exclude nodes already in path
if cell.letters in self.active_node.children and cell.pos not in subtree.active_node.path:
new_node = WordNode(cell.letters, self.active_node.is_word, subtree.active_node, board_pos = cell.pos)
if (
cell.letters in self.active_node.children
and cell.pos not in subtree.active_node.path
):
new_node = WordNode(
cell.letters,
self.active_node.is_word,
subtree.active_node,
board_pos=cell.pos,
)
subtree.active_node.add_child_node(new_node)
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.build_boggle_tree(board, board.board[cell.pos], subtree, word_len=word_len+len(board_cell.letters))
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.build_boggle_tree(
board,
board.board[cell.pos],
subtree,
word_len=word_len + len(board_cell.letters),
)
self.active_node = self.active_node.parent
subtree.active_node = subtree.active_node.parent
return subtree
def build_boggle_tree(args):
'''Build boggle tree from arguments for process Pool'''
"""Build boggle tree from arguments for process Pool"""
(alphabet, board, cell, wordlist) = args
root_node = WordNode(cell.letters)
dict_tree = WordTree(alphabet, root_node, wordlist)
sub_tree = WordTree(alphabet, WordNode(cell.letters, False, board_pos=cell.pos))
return dict_tree.build_boggle_tree(board, cell, sub_tree)
def build_full_boggle_tree(board: BoggleBoard, wordlist_path: str) -> dict[str, WordTree]:
'''Return dictionary of WordTree(s) for every letter on a BoggleBoard'''
def build_full_boggle_tree(
board: BoggleBoard, wordlist_path: Path
) -> dict[str, WordTree]:
"""Return dictionary of WordTree(s) for every letter on a BoggleBoard"""
alphabet = sorted(set([cell.letters for cell in board.board.values()]))
board_tree = {}
index = {}
index: dict[str] = {}
print("Reading in wordlists...")
log.info("Reading in wordlists...")
for letters in alphabet:
if letters == "":
# Skip wordlist read for blocks with empty string
@@ -393,28 +448,65 @@ def build_full_boggle_tree(board: BoggleBoard, wordlist_path: str) -> dict[str,
filename = "words_" + letters[0] + ".txt"
try:
wordlist = read_wordlist(path.join(path.abspath(wordlist_path), filename))
wordlist = read_wordlist(Path(wordlist_path, filename))
index[letters] = wordlist
print(f">> {letters}: {filename}")
log.info(">> %s: %s", letters, filename)
except FileNotFoundError:
print(f">> {letters}: -- Skipping -- no wordlist found for {letters}")
log.info(">> %s: -- Skipping -- no wordlist found for %s", letters, letters)
index[letters] = {}
print("Generating WordTrees...")
params = [ [alphabet, board, cell, index[cell.letters] ] for cell in board.board.values()]
log.info("Generating WordTrees...")
params = [
[alphabet, board, cell, index[cell.letters]] for cell in board.board.values()
]
with Pool(processes=len(board.board)) as pool:
for i, res in enumerate(pool.map(build_boggle_tree, params)):
print(f">> {params[i][2]}")
log.info(">> %s", params[i][2])
board_tree[params[i][2].pos] = res
return board_tree
def read_wordlist(file):
'''Return dictionary of words with associated word count (1 by default)'''
with open(file, 'r', encoding='utf-8') as file:
return {k:1 for k in file.read().split()}
"""Return dictionary of words with associated word count (1 by default)"""
with open(file, "r", encoding="utf-8") as file:
return file.read().split()
class BadBoardFormat(Exception):
pass
def read_boggle_file(file):
'''Return list of rows from Boggle board csv file'''
with open(file, 'r', encoding='utf-8') as file:
return [x.rstrip().split(',') for x in file.readlines()]
"""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:
board = []
board.append([x.strip() for x in file.readline().split(",")])
board_size = len(board[0])
for line in file.readlines():
if line.strip() == "":
raise BadBoardFormat("board files must contain no blank lines")
row = [x.strip() for x in line.strip().split(",")]
if len(row) != board_size:
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
+12
View File
@@ -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
+140
View File
@@ -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
+41
View File
@@ -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);
}
+85
View File
@@ -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 */
+27
View File
@@ -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
1 # Listed alphabetically
2 a,a,a,f,r,s
3 a,a,e,e,e,e
4 a,a,f,i,r,s
5 a,d,e,n,n,n
6 a,e,e,e,e,m
7 a,e,e,g,m,u
8 a,e,g,m,n,n
9 a,f,i,r,s,y
10 an,er,he,in,qu,th
11 b,j,k,qu,x,z
12 c,c,e,n,s,t
13 c,e,i,i,l,t
14 c,e,i,l,p,t
15 c,e,i,p,s,t
16 d,d,h,n,o,t
17 d,h,h,l,n,o
18 d,h,l,n,o,r
19 d,l,o,h,h,r
20 e,i,i,i,t,t
21 e,m,o,t,t,t
22 e,n,s,s,s,u
23 f,i,p,r,s,y
24 g,o,r,r,v,w
25 i,k,l,qu,u,w
26 n,o,o,t,u,w
27 o,o,o,t,t,u
+35
View File
@@ -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
@@ -370100,4 +370100,4 @@ zwinglianism
zwinglianist
zwitter
zwitterion
zwitterionic
zwitterionic
Generated
+612
View File
@@ -0,0 +1,612 @@
# This file is automatically @generated by Poetry 1.5.1 and should not be changed by hand.
[[package]]
name = "astroid"
version = "2.15.5"
description = "An abstract syntax tree for Python with inference support."
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 = "cfgv"
version = "3.4.0"
description = "Validate configuration and produce human readable error messages."
optional = false
python-versions = ">=3.8"
files = [
{file = "cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9"},
{file = "cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560"},
]
[[package]]
name = "colorama"
version = "0.4.6"
description = "Cross-platform colored terminal text."
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"
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 = "distlib"
version = "0.3.7"
description = "Distribution utilities"
optional = false
python-versions = "*"
files = [
{file = "distlib-0.3.7-py2.py3-none-any.whl", hash = "sha256:2e24928bc811348f0feb63014e97aaae3037f2cf48712d51ae61df7fd6075057"},
{file = "distlib-0.3.7.tar.gz", hash = "sha256:9dafe54b34a028eafd95039d5e5d4851a13734540f1331060d31c9916e7147a8"},
]
[[package]]
name = "filelock"
version = "3.12.2"
description = "A platform independent file lock."
optional = false
python-versions = ">=3.7"
files = [
{file = "filelock-3.12.2-py3-none-any.whl", hash = "sha256:cbb791cdea2a72f23da6ac5b5269ab0a0d161e9ef0100e653b69049a7706d1ec"},
{file = "filelock-3.12.2.tar.gz", hash = "sha256:002740518d8aa59a26b0c76e10fb8c6e15eae825d34b6fdf670333fd7b938d81"},
]
[package.extras]
docs = ["furo (>=2023.5.20)", "sphinx (>=7.0.1)", "sphinx-autodoc-typehints (>=1.23,!=1.23.4)"]
testing = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "diff-cover (>=7.5)", "pytest (>=7.3.1)", "pytest-cov (>=4.1)", "pytest-mock (>=3.10)", "pytest-timeout (>=2.1)"]
[[package]]
name = "identify"
version = "2.5.27"
description = "File identification library for Python"
optional = false
python-versions = ">=3.8"
files = [
{file = "identify-2.5.27-py2.py3-none-any.whl", hash = "sha256:fdb527b2dfe24602809b2201e033c2a113d7bdf716db3ca8e3243f735dcecaba"},
{file = "identify-2.5.27.tar.gz", hash = "sha256:287b75b04a0e22d727bc9a41f0d4f3c1bcada97490fa6eabb5b28f0e9097e733"},
]
[package.extras]
license = ["ukkonen"]
[[package]]
name = "iniconfig"
version = "2.0.0"
description = "brain-dead simple config-ini parsing"
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."
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."
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 = "markdown-it-py"
version = "2.2.0"
description = "Python port of markdown-it. Markdown parsing, done right!"
optional = false
python-versions = ">=3.7"
files = [
{file = "markdown-it-py-2.2.0.tar.gz", hash = "sha256:7c9a5e412688bc771c67432cbfebcdd686c93ce6484913dccf06cb5a0bea35a1"},
{file = "markdown_it_py-2.2.0-py3-none-any.whl", hash = "sha256:5a35f8d1870171d9acc47b99612dc146129b631baf04970128b568f190d0cc30"},
]
[package.dependencies]
mdurl = ">=0.1,<1.0"
[package.extras]
benchmarking = ["psutil", "pytest", "pytest-benchmark"]
code-style = ["pre-commit (>=3.0,<4.0)"]
compare = ["commonmark (>=0.9,<1.0)", "markdown (>=3.4,<4.0)", "mistletoe (>=1.0,<2.0)", "mistune (>=2.0,<3.0)", "panflute (>=2.3,<3.0)"]
linkify = ["linkify-it-py (>=1,<3)"]
plugins = ["mdit-py-plugins"]
profiling = ["gprof2dot"]
rtd = ["attrs", "myst-parser", "pyyaml", "sphinx", "sphinx-copybutton", "sphinx-design", "sphinx_book_theme"]
testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"]
[[package]]
name = "mccabe"
version = "0.7.0"
description = "McCabe checker, plugin for flake8"
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 = "mdurl"
version = "0.1.2"
description = "Markdown URL utilities"
optional = false
python-versions = ">=3.7"
files = [
{file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"},
{file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"},
]
[[package]]
name = "mypy"
version = "1.3.0"
description = "Optional static typing for Python"
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."
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 = "nodeenv"
version = "1.8.0"
description = "Node.js virtual environment builder"
optional = false
python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*"
files = [
{file = "nodeenv-1.8.0-py2.py3-none-any.whl", hash = "sha256:df865724bb3c3adc86b3876fa209771517b0cfe596beff01a92700e0e8be4cec"},
{file = "nodeenv-1.8.0.tar.gz", hash = "sha256:d51e0c37e64fbf47d017feac3145cdbb58836d7eee8c6f6d3b6880c5456227d2"},
]
[package.dependencies]
setuptools = "*"
[[package]]
name = "packaging"
version = "23.1"
description = "Core utilities for Python packages"
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\"."
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"
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 = "pre-commit"
version = "3.3.3"
description = "A framework for managing and maintaining multi-language pre-commit hooks."
optional = false
python-versions = ">=3.8"
files = [
{file = "pre_commit-3.3.3-py2.py3-none-any.whl", hash = "sha256:10badb65d6a38caff29703362271d7dca483d01da88f9d7e05d0b97171c136cb"},
{file = "pre_commit-3.3.3.tar.gz", hash = "sha256:a2256f489cd913d575c145132ae196fe335da32d91a8294b7afe6622335dd023"},
]
[package.dependencies]
cfgv = ">=2.0.0"
identify = ">=1.0.0"
nodeenv = ">=0.11.1"
pyyaml = ">=5.1"
virtualenv = ">=20.10.0"
[[package]]
name = "pygments"
version = "2.15.1"
description = "Pygments is a syntax highlighting package written in Python."
optional = false
python-versions = ">=3.7"
files = [
{file = "Pygments-2.15.1-py3-none-any.whl", hash = "sha256:db2db3deb4b4179f399a09054b023b6a586b76499d36965813c71aa8ed7b5fd1"},
{file = "Pygments-2.15.1.tar.gz", hash = "sha256:8ace4d3c1dd481894b2005f560ead0f9f19ee64fe983366be1a21e171d12775c"},
]
[package.extras]
plugins = ["importlib-metadata"]
[[package]]
name = "pylint"
version = "2.17.4"
description = "python code static checker"
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"
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 = "pyyaml"
version = "6.0.1"
description = "YAML parser and emitter for Python"
optional = false
python-versions = ">=3.6"
files = [
{file = "PyYAML-6.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a"},
{file = "PyYAML-6.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f"},
{file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"},
{file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"},
{file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"},
{file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"},
{file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"},
{file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"},
{file = "PyYAML-6.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab"},
{file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"},
{file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"},
{file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"},
{file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"},
{file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"},
{file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"},
{file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"},
{file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"},
{file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd"},
{file = "PyYAML-6.0.1-cp36-cp36m-win32.whl", hash = "sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585"},
{file = "PyYAML-6.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa"},
{file = "PyYAML-6.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3"},
{file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27"},
{file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3"},
{file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c"},
{file = "PyYAML-6.0.1-cp37-cp37m-win32.whl", hash = "sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba"},
{file = "PyYAML-6.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867"},
{file = "PyYAML-6.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595"},
{file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"},
{file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"},
{file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"},
{file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"},
{file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"},
{file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"},
{file = "PyYAML-6.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859"},
{file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"},
{file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"},
{file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"},
{file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"},
{file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"},
{file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"},
]
[[package]]
name = "rich"
version = "13.4.1"
description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal"
optional = false
python-versions = ">=3.7.0"
files = [
{file = "rich-13.4.1-py3-none-any.whl", hash = "sha256:d204aadb50b936bf6b1a695385429d192bc1fdaf3e8b907e8e26f4c4e4b5bf75"},
{file = "rich-13.4.1.tar.gz", hash = "sha256:76f6b65ea7e5c5d924ba80e322231d7cb5b5981aa60bfc1e694f1bc097fe6fe1"},
]
[package.dependencies]
markdown-it-py = ">=2.2.0,<3.0.0"
pygments = ">=2.13.0,<3.0.0"
[package.extras]
jupyter = ["ipywidgets (>=7.5.1,<9)"]
[[package]]
name = "setuptools"
version = "68.1.2"
description = "Easily download, build, install, upgrade, and uninstall Python packages"
optional = false
python-versions = ">=3.8"
files = [
{file = "setuptools-68.1.2-py3-none-any.whl", hash = "sha256:3d8083eed2d13afc9426f227b24fd1659489ec107c0e86cec2ffdde5c92e790b"},
{file = "setuptools-68.1.2.tar.gz", hash = "sha256:3d4dfa6d95f1b101d695a6160a7626e15583af71a5f52176efa5d39a054d475d"},
]
[package.extras]
docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5,<=7.1.2)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"]
testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"]
testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"]
[[package]]
name = "tomlkit"
version = "0.11.8"
description = "Style preserving TOML library"
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+"
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 = "virtualenv"
version = "20.24.1"
description = "Virtual Python Environment builder"
optional = false
python-versions = ">=3.7"
files = [
{file = "virtualenv-20.24.1-py3-none-any.whl", hash = "sha256:01aacf8decd346cf9a865ae85c0cdc7f64c8caa07ff0d8b1dfc1733d10677442"},
{file = "virtualenv-20.24.1.tar.gz", hash = "sha256:2ef6a237c31629da6442b0bcaa3999748108c7166318d1f55cc9f8d7294e97bd"},
]
[package.dependencies]
distlib = ">=0.3.6,<1"
filelock = ">=3.12,<4"
platformdirs = ">=3.5.1,<4"
[package.extras]
docs = ["furo (>=2023.5.20)", "proselint (>=0.13)", "sphinx (>=7.0.1)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"]
test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.3.1)", "pytest-env (>=0.8.1)", "pytest-freezer (>=0.4.6)", "pytest-mock (>=3.10)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=67.8)", "time-machine (>=2.9)"]
[[package]]
name = "wrapt"
version = "1.15.0"
description = "Module for decorators, wrappers and monkey patching."
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 = "ebdcc3cb72dd9c6dc736b31d92471656c82dc96ec6b9e67bf2886d5ca1c4f499"
+25 -14
View File
@@ -1,23 +1,34 @@
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[project.urls]
"Homepage" = "https://github.com/cblanken/boggler"
"Bug Tracker" = "https://github.com/cblanken/boggler/issues"
[project]
[tool.poetry]
name = "boggler"
version = "2.0.0"
authors = [
{ name="Cameron Blankenbuehler", email="cameron.blankenbuehler@gmail.com" },
]
version = "2.0.4"
description = "Utilities for solving the Boggle word game."
readme="README.md"
license = { file="LICENSE" }
requires-python = ">=3.7"
authors = ["Cameron Blankenbuehler <cameron.blankenbuehler@gmail.com>"]
license = "LICENSE"
classifiers = [
"Programming Language :: Python :: 3",
"License :: OSI Approved :: MIT License",
"Operating System :: POSIX :: Linux",
]
readme = "README.md"
[project.urls]
"Homepage" = "https://github.com/cblanken/boggler"
"Bug Tracker" = "https://github.com/cblanken/boggler/issues"
[tool.poetry.dependencies]
python = "^3.8"
rich = "^13.4.1"
[tool.poetry.scripts]
boggler = "boggler.__main__:main"
[tool.poetry.group.dev.dependencies]
pytest = "^7.3.1"
pylint = "^2.17.4"
mypy = "^1.3.0"
pre-commit = "^3.3.3"
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
-48
View File
@@ -1,48 +0,0 @@
'''Module to generate random Boggle boards for testing'''
from sys import argv, stderr
from random import randint, shuffle
from math import sqrt
from os import path
def read_dice_file(dice_path: str):
'''Return list of die strings from file ignoring all comments (#)'''
with open(path.abspath(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(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.abspath(argv[1]))
board = get_random_board_csv(dice)
for row in board:
print(row)
except Exception as e:
print("Argument must be a valid file path!", file=stderr)
-34
View File
@@ -1,34 +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)
elif len(sys.argv) == 4:
try:
boggle_board = BoggleBoard(board, int(sys.argv[3]))
except ValueError:
print("Invalid MAX_WORD_LENGTH. Please try again with a valid integer.")
sys.exit(1)
try:
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)
-35
View File
@@ -1,35 +0,0 @@
#!/bin/sh
if [ -z $1 ] || [ -z $2 ]; then
echo "Usage: WORDLIST OUT_DIR"
exit 0
else
grep -ioP "^a[a-z]+" $1 | tr [A-Z] [a-z] | sort | uniq > "$2/words_a.txt"
grep -ioP "^b[a-z]+" $1 | tr [A-Z] [a-z] | sort | uniq > "$2/words_b.txt"
grep -ioP "^c[a-z]+" $1 | tr [A-Z] [a-z] | sort | uniq > "$2/words_c.txt"
grep -ioP "^d[a-z]+" $1 | tr [A-Z] [a-z] | sort | uniq > "$2/words_d.txt"
grep -ioP "^e[a-z]+" $1 | tr [A-Z] [a-z] | sort | uniq > "$2/words_e.txt"
grep -ioP "^f[a-z]+" $1 | tr [A-Z] [a-z] | sort | uniq > "$2/words_f.txt"
grep -ioP "^g[a-z]+" $1 | tr [A-Z] [a-z] | sort | uniq > "$2/words_g.txt"
grep -ioP "^h[a-z]+" $1 | tr [A-Z] [a-z] | sort | uniq > "$2/words_h.txt"
grep -ioP "^i[a-z]+" $1 | tr [A-Z] [a-z] | sort | uniq > "$2/words_i.txt"
grep -ioP "^j[a-z]+" $1 | tr [A-Z] [a-z] | sort | uniq > "$2/words_j.txt"
grep -ioP "^k[a-z]+" $1 | tr [A-Z] [a-z] | sort | uniq > "$2/words_k.txt"
grep -ioP "^l[a-z]+" $1 | tr [A-Z] [a-z] | sort | uniq > "$2/words_l.txt"
grep -ioP "^m[a-z]+" $1 | tr [A-Z] [a-z] | sort | uniq > "$2/words_m.txt"
grep -ioP "^n[a-z]+" $1 | tr [A-Z] [a-z] | sort | uniq > "$2/words_n.txt"
grep -ioP "^o[a-z]+" $1 | tr [A-Z] [a-z] | sort | uniq > "$2/words_o.txt"
grep -ioP "^p[a-z]+" $1 | tr [A-Z] [a-z] | sort | uniq > "$2/words_p.txt"
grep -ioP "^q[a-z]+" $1 | tr [A-Z] [a-z] | sort | uniq > "$2/words_q.txt"
grep -ioP "^r[a-z]+" $1 | tr [A-Z] [a-z] | sort | uniq > "$2/words_r.txt"
grep -ioP "^s[a-z]+" $1 | tr [A-Z] [a-z] | sort | uniq > "$2/words_s.txt"
grep -ioP "^t[a-z]+" $1 | tr [A-Z] [a-z] | sort | uniq > "$2/words_t.txt"
grep -ioP "^u[a-z]+" $1 | tr [A-Z] [a-z] | sort | uniq > "$2/words_u.txt"
grep -ioP "^v[a-z]+" $1 | tr [A-Z] [a-z] | sort | uniq > "$2/words_v.txt"
grep -ioP "^w[a-z]+" $1 | tr [A-Z] [a-z] | sort | uniq > "$2/words_w.txt"
grep -ioP "^x[a-z]+" $1 | tr [A-Z] [a-z] | sort | uniq > "$2/words_x.txt"
grep -ioP "^y[a-z]+" $1 | tr [A-Z] [a-z] | sort | uniq > "$2/words_y.txt"
grep -ioP "^z[a-z]+" $1 | tr [A-Z] [a-z] | sort | uniq > "$2/words_z.txt"
touch "$2/words__.txt"
exit 0
fi
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,4 @@
u, n,r ,e
qu, n,i, l
i ,s,h, a
s ,e , l,b
+9
View File
@@ -0,0 +1,9 @@
u,n,r,e
qu,n,i,l
i,s,h,a
s,e,l,b
+4
View File
@@ -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
+4
View File
@@ -0,0 +1,4 @@
u,n,r,e,b
qu,n,i,l
i,s,h,a,g,i
s,e,l,b
+1
View File
@@ -0,0 +1 @@
../boggler

Some files were not shown because too many files have changed in this diff Show More