mirror of
https://codeberg.org/cblanken/boggler.git
synced 2026-07-26 11:35:10 -04:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a764a41ff6 | ||
|
|
6dba9313d5 | ||
|
|
9036848168 | ||
|
|
a78b277516 | ||
|
|
a2630ed82e | ||
|
|
ab2869b858 | ||
|
|
d282cd3676 | ||
|
|
995a7f42c1 | ||
|
|
b4d7237c1f | ||
|
|
3b79ca7519 | ||
|
|
9e2cd55741 |
@@ -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
|
||||||
@@ -6,7 +6,8 @@ A solver for the popular word game Boggle.
|
|||||||
pip install boggler
|
pip install boggler
|
||||||
```
|
```
|
||||||
|
|
||||||
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:
|
1. Create `.csv` of the board state like so:
|
||||||
|
|
||||||
Here is an example `board.csv`. Note the orientation of the board does not matter.
|
Here is an example `board.csv`. Note the orientation of the board does not matter.
|
||||||
@@ -17,7 +18,7 @@ To use the script to solve a particular Boggle board configuration, you'll need
|
|||||||
d,f,e,y
|
d,f,e,y
|
||||||
n,m,e,qu
|
n,m,e,qu
|
||||||
```
|
```
|
||||||
2. Find a dictionary wordlist file or create your own
|
2. Find or create a dictionary wordlist file or create your own
|
||||||
|
|
||||||
The dictionary wordlist should have each word on a single line like so
|
The dictionary wordlist should have each word on a single line like so
|
||||||
```console
|
```console
|
||||||
@@ -57,354 +58,337 @@ To use the script to solve a particular Boggle board configuration, you'll need
|
|||||||
```
|
```
|
||||||
|
|
||||||
# Usage
|
# Usage
|
||||||
```console
|
<details>
|
||||||
$ python boggler.py
|
<summary><h3 style="display: inline">CLI usage</h3></summary>
|
||||||
Usage: python3 boggler.py <BOARD_FILE> <WORDLISTS_DIR> [MAX_WORD_LENGTH]
|
<pre><code>$ poetry run boggler --help
|
||||||
$ python boggler.py board.csv wordlists/ 15
|
usage: boggler [-h] [-f FORMAT] [-p] [-s] [-d] board wordlists [max_word_length]
|
||||||
Reading in wordlists...
|
|
||||||
>> d: words_d.txt
|
Boggle board game solver
|
||||||
>> e: words_e.txt
|
|
||||||
>> f: words_f.txt
|
positional arguments:
|
||||||
>> m: words_m.txt
|
board Path to board CSV file
|
||||||
>> n: words_n.txt
|
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
|
||||||
>> o: words_o.txt
|
alphabet
|
||||||
>> qu: words_q.txt
|
max_word_length Maximum length of words searched for on provided board
|
||||||
>> r: words_r.txt
|
|
||||||
>> s: words_s.txt
|
options:
|
||||||
>> v: words_v.txt
|
-h, --help show this help message and exit
|
||||||
>> y: words_y.txt
|
-f FORMAT, --format FORMAT
|
||||||
Generating WordTrees...
|
Specify alternative output format including [txt, json]
|
||||||
>> (0, 0): y
|
-p, --include-path Include full paths for each word in output
|
||||||
>> (0, 1): e
|
-s, --sort Sort output alphabetically. By default the results are sorted by the starting block position on the board from top-to-bottom, left-
|
||||||
>> (0, 2): o
|
to-right as given in the board file.
|
||||||
>> (0, 3): s
|
-d, --dedup Remove duplicates from word-only output. Note that de-duplication does not preserve the original order of the output, so it is
|
||||||
>> (1, 0): r
|
recommended to also use the sort option when de-duplicating.
|
||||||
>> (1, 1): e
|
</code></pre>
|
||||||
>> (1, 2): o
|
</details>
|
||||||
>> (1, 3): v
|
|
||||||
>> (2, 0): d
|
<details>
|
||||||
>> (2, 1): f
|
<summary><h3 style="display: inline">Example board solve</h3></summary>
|
||||||
>> (2, 2): e
|
<pre><code>$ poetry run boggler ./boggler/boards/b1.csv boggler/wordlists/scrabble_2019/
|
||||||
>> (2, 3): y
|
|
||||||
>> (3, 0): n
|
|
||||||
>> (3, 1): m
|
|
||||||
>> (3, 2): e
|
|
||||||
>> (3, 3): qu
|
|
||||||
|
|
||||||
BOARD
|
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)
|
||||||
Starting @ (0, 0)...
|
╭──────────┬──────────────────────────────────────────────────────────────────╮
|
||||||
y : [(0, 0)]
|
│ Word │ Path │
|
||||||
yr : [(0, 0), (1, 0)]
|
├──────────┼──────────────────────────────────────────────────────────────────┤
|
||||||
ye : [(0, 0), (1, 1)]
|
│ squall │ [(0, 0), (1, 1), (0, 1), (1, 0), (2, 1)] │
|
||||||
yee : [(0, 0), (1, 1), (0, 1)]
|
│ squall │ [(0, 0), (1, 1), (1, 2), (2, 2), (2, 1)] │
|
||||||
yeo : [(0, 0), (1, 1), (0, 2)]
|
│ squall │ [(0, 0), (1, 1), (1, 2), (2, 1), (1, 0)] │
|
||||||
yed : [(0, 0), (1, 1), (2, 0)]
|
│ squall │ [(0, 0), (1, 1), (1, 2), (2, 1), (2, 2)] │
|
||||||
yee : [(0, 0), (1, 1), (2, 2)]
|
│ squally │ [(0, 0), (1, 1), (1, 2), (2, 1), (2, 2), (1, 3)] │
|
||||||
yer : [(0, 0), (1, 1), (1, 0)]
|
│ squalls │ [(0, 0), (1, 1), (1, 2), (2, 1), (2, 2), (2, 3)] │
|
||||||
yere : [(0, 0), (1, 1), (1, 0), (0, 1)]
|
│ squash │ [(0, 0), (1, 1), (1, 2), (2, 3), (3, 2)] │
|
||||||
yerd : [(0, 0), (1, 1), (1, 0), (2, 0)]
|
│ sal │ [(0, 0), (0, 1), (1, 0)] │
|
||||||
yeo : [(0, 0), (1, 1), (1, 2)]
|
│ sall │ [(0, 0), (0, 1), (1, 0), (2, 1)] │
|
||||||
ye : [(0, 0), (0, 1)]
|
│ sallal │ [(0, 0), (0, 1), (1, 0), (2, 1), (1, 2), (2, 2)] │
|
||||||
yee : [(0, 0), (0, 1), (1, 1)]
|
│ sallals │ [(0, 0), (0, 1), (1, 0), (2, 1), (1, 2), (2, 2), (2, 3)] │
|
||||||
yer : [(0, 0), (0, 1), (1, 0)]
|
│ sallow │ [(0, 0), (0, 1), (1, 0), (2, 1), (3, 0), (3, 1)] │
|
||||||
yerd : [(0, 0), (0, 1), (1, 0), (2, 0)]
|
│ sallowly │ [(0, 0), (0, 1), (1, 0), (2, 1), (3, 0), (3, 1), (2, 2), (1, 3)] │
|
||||||
yere : [(0, 0), (0, 1), (1, 0), (1, 1)]
|
│ sai │ [(0, 0), (0, 1), (0, 2)] │
|
||||||
yeo : [(0, 0), (0, 1), (1, 2)]
|
╰──────────┴──────────────────────────────────────────────────────────────────╯
|
||||||
yeo : [(0, 0), (0, 1), (0, 2)]
|
Starting @ (0, 1)
|
||||||
|
╭────────┬──────────────────────────────────────────────────╮
|
||||||
Starting @ (0, 1)...
|
│ Word │ Path │
|
||||||
e : [(0, 1)]
|
├────────┼──────────────────────────────────────────────────┤
|
||||||
ee : [(0, 1), (1, 1)]
|
│ aqua │ [(0, 1), (1, 1), (1, 2)] │
|
||||||
eer : [(0, 1), (1, 1), (1, 0)]
|
│ aquas │ [(0, 1), (1, 1), (1, 2), (2, 3)] │
|
||||||
eery : [(0, 1), (1, 1), (1, 0), (0, 0)]
|
│ al │ [(0, 1), (1, 0)] │
|
||||||
er : [(0, 1), (1, 0)]
|
│ als │ [(0, 1), (1, 0), (0, 0)] │
|
||||||
erd : [(0, 1), (1, 0), (2, 0)]
|
│ alu │ [(0, 1), (1, 0), (2, 0)] │
|
||||||
erf : [(0, 1), (1, 0), (2, 1)]
|
│ alula │ [(0, 1), (1, 0), (2, 0), (2, 1), (1, 2)] │
|
||||||
ere : [(0, 1), (1, 0), (1, 1)]
|
│ alulas │ [(0, 1), (1, 0), (2, 0), (2, 1), (1, 2), (2, 3)] │
|
||||||
eo : [(0, 1), (1, 2)]
|
│ all │ [(0, 1), (1, 0), (2, 1)] │
|
||||||
eos : [(0, 1), (1, 2), (0, 3)]
|
│ allay │ [(0, 1), (1, 0), (2, 1), (1, 2), (1, 3)] │
|
||||||
eof : [(0, 1), (1, 2), (2, 1)]
|
│ allays │ [(0, 1), (1, 0), (2, 1), (1, 2), (1, 3), (2, 3)] │
|
||||||
ey : [(0, 1), (0, 0)]
|
│ allow │ [(0, 1), (1, 0), (2, 1), (3, 0), (3, 1)] │
|
||||||
eyr : [(0, 1), (0, 0), (1, 0)]
|
│ aa │ [(0, 1), (1, 2)] │
|
||||||
eyre : [(0, 1), (0, 0), (1, 0), (1, 1)]
|
│ aal │ [(0, 1), (1, 2), (2, 2)] │
|
||||||
eye : [(0, 1), (0, 0), (1, 1)]
|
│ aals │ [(0, 1), (1, 2), (2, 2), (2, 3)] │
|
||||||
eyed : [(0, 1), (0, 0), (1, 1), (2, 0)]
|
│ aal │ [(0, 1), (1, 2), (2, 1)] │
|
||||||
eyer : [(0, 1), (0, 0), (1, 1), (1, 0)]
|
│ aas │ [(0, 1), (1, 2), (2, 3)] │
|
||||||
eo : [(0, 1), (0, 2)]
|
│ as │ [(0, 1), (0, 0)] │
|
||||||
eos : [(0, 1), (0, 2), (0, 3)]
|
│ ai │ [(0, 1), (0, 2)] │
|
||||||
|
│ aia │ [(0, 1), (0, 2), (1, 2)] │
|
||||||
Starting @ (0, 2)...
|
│ aias │ [(0, 1), (0, 2), (1, 2), (2, 3)] │
|
||||||
o : [(0, 2)]
|
╰────────┴──────────────────────────────────────────────────╯
|
||||||
oos : [(0, 2), (1, 2), (0, 3)]
|
Starting @ (0,
|
||||||
oof : [(0, 2), (1, 2), (2, 1)]
|
2)
|
||||||
oe : [(0, 2), (1, 1)]
|
╭──────┬──────╮
|
||||||
oer : [(0, 2), (1, 1), (1, 0)]
|
│ Word │ Path │
|
||||||
ovey : [(0, 2), (1, 3), (2, 2), (2, 3)]
|
├──────┼──────┤
|
||||||
oe : [(0, 2), (0, 1)]
|
╰──────┴──────╯
|
||||||
oer : [(0, 2), (0, 1), (1, 0)]
|
Starting @ (0, 3)
|
||||||
os : [(0, 2), (0, 3)]
|
╭─────────┬──────────────────────────────────────────────────────────╮
|
||||||
|
│ Word │ Path │
|
||||||
Starting @ (0, 3)...
|
├─────────┼──────────────────────────────────────────────────────────┤
|
||||||
s : [(0, 3)]
|
│ pya │ [(0, 3), (1, 3), (1, 2)] │
|
||||||
sv : [(0, 3), (1, 3)]
|
│ pyas │ [(0, 3), (1, 3), (1, 2), (2, 3)] │
|
||||||
so : [(0, 3), (1, 2)]
|
│ pa │ [(0, 3), (1, 2)] │
|
||||||
sooey : [(0, 3), (1, 2), (0, 2), (1, 1), (0, 0)]
|
│ paal │ [(0, 3), (1, 2), (0, 1), (1, 0)] │
|
||||||
sooey : [(0, 3), (1, 2), (0, 2), (0, 1), (0, 0)]
|
│ paals │ [(0, 3), (1, 2), (0, 1), (1, 0), (0, 0)] │
|
||||||
soe : [(0, 3), (1, 2), (0, 1)]
|
│ pal │ [(0, 3), (1, 2), (2, 2)] │
|
||||||
soe : [(0, 3), (1, 2), (2, 2)]
|
│ paly │ [(0, 3), (1, 2), (2, 2), (1, 3)] │
|
||||||
sofer : [(0, 3), (1, 2), (2, 1), (1, 1), (1, 0)]
|
│ palas │ [(0, 3), (1, 2), (2, 2), (3, 3), (2, 3)] │
|
||||||
soy : [(0, 3), (1, 2), (2, 3)]
|
│ pall │ [(0, 3), (1, 2), (2, 2), (2, 1)] │
|
||||||
soe : [(0, 3), (1, 2), (1, 1)]
|
│ pals │ [(0, 3), (1, 2), (2, 2), (2, 3)] │
|
||||||
sov : [(0, 3), (1, 2), (1, 3)]
|
│ palsy │ [(0, 3), (1, 2), (2, 2), (2, 3), (1, 3)] │
|
||||||
so : [(0, 3), (0, 2)]
|
│ palsa │ [(0, 3), (1, 2), (2, 2), (2, 3), (3, 3)] │
|
||||||
sooey : [(0, 3), (0, 2), (1, 2), (0, 1), (0, 0)]
|
│ pal │ [(0, 3), (1, 2), (2, 1)] │
|
||||||
sooey : [(0, 3), (0, 2), (1, 2), (2, 2), (2, 3)]
|
│ pall │ [(0, 3), (1, 2), (2, 1), (1, 0)] │
|
||||||
sooey : [(0, 3), (0, 2), (1, 2), (1, 1), (0, 0)]
|
│ palls │ [(0, 3), (1, 2), (2, 1), (1, 0), (0, 0)] │
|
||||||
soe : [(0, 3), (0, 2), (1, 1)]
|
│ palla │ [(0, 3), (1, 2), (2, 1), (1, 0), (0, 1)] │
|
||||||
sov : [(0, 3), (0, 2), (1, 3)]
|
│ pall │ [(0, 3), (1, 2), (2, 1), (2, 2)] │
|
||||||
soe : [(0, 3), (0, 2), (0, 1)]
|
│ pally │ [(0, 3), (1, 2), (2, 1), (2, 2), (1, 3)] │
|
||||||
|
│ palla │ [(0, 3), (1, 2), (2, 1), (2, 2), (3, 3)] │
|
||||||
Starting @ (1, 0)...
|
│ pallah │ [(0, 3), (1, 2), (2, 1), (2, 2), (3, 3), (3, 2)] │
|
||||||
r : [(1, 0)]
|
│ pallahs │ [(0, 3), (1, 2), (2, 1), (2, 2), (3, 3), (3, 2), (2, 3)] │
|
||||||
rye : [(1, 0), (0, 0), (1, 1)]
|
│ palls │ [(0, 3), (1, 2), (2, 1), (2, 2), (2, 3)] │
|
||||||
rye : [(1, 0), (0, 0), (0, 1)]
|
│ pas │ [(0, 3), (1, 2), (2, 3)] │
|
||||||
re : [(1, 0), (0, 1)]
|
│ pash │ [(0, 3), (1, 2), (2, 3), (3, 2)] │
|
||||||
ree : [(1, 0), (0, 1), (1, 1)]
|
│ pasha │ [(0, 3), (1, 2), (2, 3), (3, 2), (3, 3)] │
|
||||||
reef : [(1, 0), (0, 1), (1, 1), (2, 1)]
|
│ pay │ [(0, 3), (1, 2), (1, 3)] │
|
||||||
reed : [(1, 0), (0, 1), (1, 1), (2, 0)]
|
│ pays │ [(0, 3), (1, 2), (1, 3), (2, 3)] │
|
||||||
rd : [(1, 0), (2, 0)]
|
│ pi │ [(0, 3), (0, 2)] │
|
||||||
rf : [(1, 0), (2, 1)]
|
│ pia │ [(0, 3), (0, 2), (1, 2)] │
|
||||||
re : [(1, 0), (1, 1)]
|
│ pial │ [(0, 3), (0, 2), (1, 2), (2, 2)] │
|
||||||
ree : [(1, 0), (1, 1), (0, 1)]
|
│ pial │ [(0, 3), (0, 2), (1, 2), (2, 1)] │
|
||||||
ref : [(1, 0), (1, 1), (2, 1)]
|
│ pias │ [(0, 3), (0, 2), (1, 2), (2, 3)] │
|
||||||
red : [(1, 0), (1, 1), (2, 0)]
|
│ pia │ [(0, 3), (0, 2), (0, 1)] │
|
||||||
ree : [(1, 0), (1, 1), (2, 2)]
|
│ pial │ [(0, 3), (0, 2), (0, 1), (1, 0)] │
|
||||||
reem : [(1, 0), (1, 1), (2, 2), (3, 1)]
|
│ pias │ [(0, 3), (0, 2), (0, 1), (0, 0)] │
|
||||||
reef : [(1, 0), (1, 1), (2, 2), (2, 1)]
|
╰─────────┴──────────────────────────────────────────────────────────╯
|
||||||
|
Starting @ (1, 0)
|
||||||
Starting @ (1, 1)...
|
╭───────┬──────────────────────────────────────────╮
|
||||||
e : [(1, 1)]
|
│ Word │ Path │
|
||||||
ee : [(1, 1), (0, 1)]
|
├───────┼──────────────────────────────────────────┤
|
||||||
eer : [(1, 1), (0, 1), (1, 0)]
|
│ la │ [(1, 0), (0, 1)] │
|
||||||
eery : [(1, 1), (0, 1), (1, 0), (0, 0)]
|
│ las │ [(1, 0), (0, 1), (0, 0)] │
|
||||||
ey : [(1, 1), (0, 0)]
|
│ lull │ [(1, 0), (2, 0), (2, 1), (2, 2)] │
|
||||||
eyr : [(1, 1), (0, 0), (1, 0)]
|
│ lulls │ [(1, 0), (2, 0), (2, 1), (2, 2), (2, 3)] │
|
||||||
eyre : [(1, 1), (0, 0), (1, 0), (0, 1)]
|
╰───────┴──────────────────────────────────────────╯
|
||||||
eye : [(1, 1), (0, 0), (0, 1)]
|
Starting @ (1, 1)
|
||||||
eyer : [(1, 1), (0, 0), (0, 1), (1, 0)]
|
╭───────┬──────────────────────────────────╮
|
||||||
eo : [(1, 1), (0, 2)]
|
│ Word │ Path │
|
||||||
eos : [(1, 1), (0, 2), (0, 3)]
|
├───────┼──────────────────────────────────┤
|
||||||
ef : [(1, 1), (2, 1)]
|
│ qua │ [(1, 1), (0, 1)] │
|
||||||
ed : [(1, 1), (2, 0)]
|
│ quai │ [(1, 1), (0, 1), (0, 2)] │
|
||||||
ee : [(1, 1), (2, 2)]
|
│ quip │ [(1, 1), (0, 2), (0, 3)] │
|
||||||
er : [(1, 1), (1, 0)]
|
│ qua │ [(1, 1), (1, 2)] │
|
||||||
ere : [(1, 1), (1, 0), (0, 1)]
|
│ quai │ [(1, 1), (1, 2), (0, 2)] │
|
||||||
erd : [(1, 1), (1, 0), (2, 0)]
|
│ quash │ [(1, 1), (1, 2), (2, 3), (3, 2)] │
|
||||||
erf : [(1, 1), (1, 0), (2, 1)]
|
│ quay │ [(1, 1), (1, 2), (1, 3)] │
|
||||||
eo : [(1, 1), (1, 2)]
|
│ quays │ [(1, 1), (1, 2), (1, 3), (2, 3)] │
|
||||||
eos : [(1, 1), (1, 2), (0, 3)]
|
╰───────┴──────────────────────────────────╯
|
||||||
eof : [(1, 1), (1, 2), (2, 1)]
|
Starting @ (1, 2)
|
||||||
|
╭────────┬──────────────────────────────────────────────────╮
|
||||||
Starting @ (1, 2)...
|
│ Word │ Path │
|
||||||
o : [(1, 2)]
|
├────────┼──────────────────────────────────────────────────┤
|
||||||
oos : [(1, 2), (0, 2), (0, 3)]
|
│ ai │ [(1, 2), (0, 2)] │
|
||||||
oe : [(1, 2), (0, 1)]
|
│ aia │ [(1, 2), (0, 2), (0, 1)] │
|
||||||
oer : [(1, 2), (0, 1), (1, 0)]
|
│ aias │ [(1, 2), (0, 2), (0, 1), (0, 0)] │
|
||||||
os : [(1, 2), (0, 3)]
|
│ aa │ [(1, 2), (0, 1)] │
|
||||||
oe : [(1, 2), (2, 2)]
|
│ aal │ [(1, 2), (0, 1), (1, 0)] │
|
||||||
of : [(1, 2), (2, 1)]
|
│ aals │ [(1, 2), (0, 1), (1, 0), (0, 0)] │
|
||||||
ofer : [(1, 2), (2, 1), (1, 1), (1, 0)]
|
│ aas │ [(1, 2), (0, 1), (0, 0)] │
|
||||||
oy : [(1, 2), (2, 3)]
|
│ al │ [(1, 2), (2, 2)] │
|
||||||
oe : [(1, 2), (1, 1)]
|
│ ala │ [(1, 2), (2, 2), (3, 3)] │
|
||||||
oer : [(1, 2), (1, 1), (1, 0)]
|
│ alas │ [(1, 2), (2, 2), (3, 3), (2, 3)] │
|
||||||
ovey : [(1, 2), (1, 3), (2, 2), (2, 3)]
|
│ all │ [(1, 2), (2, 2), (2, 1)] │
|
||||||
|
│ allow │ [(1, 2), (2, 2), (2, 1), (3, 0), (3, 1)] │
|
||||||
Starting @ (1, 3)...
|
│ als │ [(1, 2), (2, 2), (2, 3)] │
|
||||||
v : [(1, 3)]
|
│ al │ [(1, 2), (2, 1)] │
|
||||||
vs : [(1, 3), (0, 3)]
|
│ all │ [(1, 2), (2, 1), (1, 0)] │
|
||||||
vo : [(1, 3), (0, 2)]
|
│ alls │ [(1, 2), (2, 1), (1, 0), (0, 0)] │
|
||||||
voe : [(1, 3), (0, 2), (1, 1)]
|
│ alow │ [(1, 2), (2, 1), (3, 0), (3, 1)] │
|
||||||
voe : [(1, 3), (0, 2), (0, 1)]
|
│ alu │ [(1, 2), (2, 1), (2, 0)] │
|
||||||
vee : [(1, 3), (2, 2), (1, 1)]
|
│ alula │ [(1, 2), (2, 1), (2, 0), (1, 0), (0, 1)] │
|
||||||
veer : [(1, 3), (2, 2), (1, 1), (1, 0)]
|
│ alulas │ [(1, 2), (2, 1), (2, 0), (1, 0), (0, 1), (0, 0)] │
|
||||||
veery : [(1, 3), (2, 2), (1, 1), (1, 0), (0, 0)]
|
│ all │ [(1, 2), (2, 1), (2, 2)] │
|
||||||
vee : [(1, 3), (2, 2), (3, 2)]
|
│ ally │ [(1, 2), (2, 1), (2, 2), (1, 3)] │
|
||||||
vefry : [(1, 3), (2, 2), (2, 1), (1, 0), (0, 0)]
|
│ alls │ [(1, 2), (2, 1), (2, 2), (2, 3)] │
|
||||||
vo : [(1, 3), (1, 2)]
|
│ as │ [(1, 2), (2, 3)] │
|
||||||
voe : [(1, 3), (1, 2), (0, 1)]
|
│ asyla │ [(1, 2), (2, 3), (1, 3), (2, 2), (3, 3)] │
|
||||||
voe : [(1, 3), (1, 2), (2, 2)]
|
│ ash │ [(1, 2), (2, 3), (3, 2)] │
|
||||||
voe : [(1, 3), (1, 2), (1, 1)]
|
│ aqua │ [(1, 2), (1, 1), (0, 1)] │
|
||||||
|
│ aquas │ [(1, 2), (1, 1), (0, 1), (0, 0)] │
|
||||||
Starting @ (2, 0)...
|
│ ay │ [(1, 2), (1, 3)] │
|
||||||
d : [(2, 0)]
|
│ ays │ [(1, 2), (1, 3), (2, 3)] │
|
||||||
dr : [(2, 0), (1, 0)]
|
╰────────┴──────────────────────────────────────────────────╯
|
||||||
dry : [(2, 0), (1, 0), (0, 0)]
|
Starting @ (1, 3)
|
||||||
dree : [(2, 0), (1, 0), (0, 1), (1, 1)]
|
╭──────┬──────────────────────────╮
|
||||||
drey : [(2, 0), (1, 0), (0, 1), (0, 0)]
|
│ Word │ Path │
|
||||||
dree : [(2, 0), (1, 0), (1, 1), (0, 1)]
|
├──────┼──────────────────────────┤
|
||||||
drey : [(2, 0), (1, 0), (1, 1), (0, 0)]
|
│ yip │ [(1, 3), (0, 2), (0, 3)] │
|
||||||
dree : [(2, 0), (1, 0), (1, 1), (2, 2)]
|
│ ya │ [(1, 3), (1, 2)] │
|
||||||
de : [(2, 0), (1, 1)]
|
│ yap │ [(1, 3), (1, 2), (0, 3)] │
|
||||||
dee : [(2, 0), (1, 1), (0, 1)]
|
│ yas │ [(1, 3), (1, 2), (2, 3)] │
|
||||||
deer : [(2, 0), (1, 1), (0, 1), (1, 0)]
|
╰──────┴──────────────────────────╯
|
||||||
dey : [(2, 0), (1, 1), (0, 0)]
|
Starting @ (2,
|
||||||
def : [(2, 0), (1, 1), (2, 1)]
|
0)
|
||||||
dee : [(2, 0), (1, 1), (2, 2)]
|
╭──────┬──────╮
|
||||||
deem : [(2, 0), (1, 1), (2, 2), (3, 1)]
|
│ Word │ Path │
|
||||||
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)]
|
Starting @ (2, 1)
|
||||||
dn : [(2, 0), (3, 0)]
|
╭───────┬──────────────────────────────────────────╮
|
||||||
dm : [(2, 0), (3, 1)]
|
│ Word │ Path │
|
||||||
|
├───────┼──────────────────────────────────────────┤
|
||||||
Starting @ (2, 1)...
|
│ la │ [(2, 1), (1, 2)] │
|
||||||
f : [(2, 1)]
|
│ lap │ [(2, 1), (1, 2), (0, 3)] │
|
||||||
fe : [(2, 1), (1, 1)]
|
│ las │ [(2, 1), (1, 2), (2, 3)] │
|
||||||
fee : [(2, 1), (1, 1), (0, 1)]
|
│ lash │ [(2, 1), (1, 2), (2, 3), (3, 2)] │
|
||||||
feer : [(2, 1), (1, 1), (0, 1), (1, 0)]
|
│ lay │ [(2, 1), (1, 2), (1, 3)] │
|
||||||
fey : [(2, 1), (1, 1), (0, 0)]
|
│ lays │ [(2, 1), (1, 2), (1, 3), (2, 3)] │
|
||||||
feyer : [(2, 1), (1, 1), (0, 0), (0, 1), (1, 0)]
|
│ lo │ [(2, 1), (3, 0)] │
|
||||||
fed : [(2, 1), (1, 1), (2, 0)]
|
│ lou │ [(2, 1), (3, 0), (2, 0)] │
|
||||||
fedn : [(2, 1), (1, 1), (2, 0), (3, 0)]
|
│ low │ [(2, 1), (3, 0), (3, 1)] │
|
||||||
fee : [(2, 1), (1, 1), (2, 2)]
|
│ lowly │ [(2, 1), (3, 0), (3, 1), (2, 2), (1, 3)] │
|
||||||
fer : [(2, 1), (1, 1), (1, 0)]
|
╰───────┴──────────────────────────────────────────╯
|
||||||
fere : [(2, 1), (1, 1), (1, 0), (0, 1)]
|
Starting @ (2, 2)
|
||||||
ferd : [(2, 1), (1, 1), (1, 0), (2, 0)]
|
╭───────┬──────────────────────────────────────────╮
|
||||||
fr : [(2, 1), (1, 0)]
|
│ Word │ Path │
|
||||||
fry : [(2, 1), (1, 0), (0, 0)]
|
├───────┼──────────────────────────────────────────┤
|
||||||
free : [(2, 1), (1, 0), (0, 1), (1, 1)]
|
│ la │ [(2, 2), (1, 2)] │
|
||||||
freed : [(2, 1), (1, 0), (0, 1), (1, 1), (2, 0)]
|
│ lap │ [(2, 2), (1, 2), (0, 3)] │
|
||||||
frey : [(2, 1), (1, 0), (0, 1), (0, 0)]
|
│ lall │ [(2, 2), (1, 2), (2, 1), (1, 0)] │
|
||||||
free : [(2, 1), (1, 0), (1, 1), (0, 1)]
|
│ lalls │ [(2, 2), (1, 2), (2, 1), (1, 0), (0, 0)] │
|
||||||
frey : [(2, 1), (1, 0), (1, 1), (0, 0)]
|
│ las │ [(2, 2), (1, 2), (2, 3)] │
|
||||||
fred : [(2, 1), (1, 0), (1, 1), (2, 0)]
|
│ lash │ [(2, 2), (1, 2), (2, 3), (3, 2)] │
|
||||||
free : [(2, 1), (1, 0), (1, 1), (2, 2)]
|
│ lay │ [(2, 2), (1, 2), (1, 3)] │
|
||||||
fo : [(2, 1), (1, 2)]
|
│ lays │ [(2, 2), (1, 2), (1, 3), (2, 3)] │
|
||||||
foo : [(2, 1), (1, 2), (0, 2)]
|
│ la │ [(2, 2), (3, 3)] │
|
||||||
foe : [(2, 1), (1, 2), (0, 1)]
|
│ las │ [(2, 2), (3, 3), (2, 3)] │
|
||||||
foe : [(2, 1), (1, 2), (2, 2)]
|
│ lash │ [(2, 2), (3, 3), (2, 3), (3, 2)] │
|
||||||
foy : [(2, 1), (1, 2), (2, 3)]
|
│ lah │ [(2, 2), (3, 3), (3, 2)] │
|
||||||
foe : [(2, 1), (1, 2), (1, 1)]
|
│ lahs │ [(2, 2), (3, 3), (3, 2), (2, 3)] │
|
||||||
fm : [(2, 1), (3, 1)]
|
╰───────┴──────────────────────────────────────────╯
|
||||||
fn : [(2, 1), (3, 0)]
|
Starting @ (2, 3)
|
||||||
fe : [(2, 1), (3, 2)]
|
╭─────────┬──────────────────────────────────────────────────────────╮
|
||||||
fee : [(2, 1), (3, 2), (2, 2)]
|
│ Word │ Path │
|
||||||
fey : [(2, 1), (3, 2), (2, 3)]
|
├─────────┼──────────────────────────────────────────────────────────┤
|
||||||
fem : [(2, 1), (3, 2), (3, 1)]
|
│ sai │ [(2, 3), (1, 2), (0, 2)] │
|
||||||
feme : [(2, 1), (3, 2), (3, 1), (2, 2)]
|
│ sap │ [(2, 3), (1, 2), (0, 3)] │
|
||||||
fe : [(2, 1), (2, 2)]
|
│ sal │ [(2, 3), (1, 2), (2, 2)] │
|
||||||
fee : [(2, 1), (2, 2), (1, 1)]
|
│ sall │ [(2, 3), (1, 2), (2, 2), (2, 1)] │
|
||||||
feed : [(2, 1), (2, 2), (1, 1), (2, 0)]
|
│ sallow │ [(2, 3), (1, 2), (2, 2), (2, 1), (3, 0), (3, 1)] │
|
||||||
feer : [(2, 1), (2, 2), (1, 1), (1, 0)]
|
│ sal │ [(2, 3), (1, 2), (2, 1)] │
|
||||||
feere : [(2, 1), (2, 2), (1, 1), (1, 0), (0, 1)]
|
│ sall │ [(2, 3), (1, 2), (2, 1), (1, 0)] │
|
||||||
fee : [(2, 1), (2, 2), (3, 2)]
|
│ sall │ [(2, 3), (1, 2), (2, 1), (2, 2)] │
|
||||||
fem : [(2, 1), (2, 2), (3, 1)]
|
│ sally │ [(2, 3), (1, 2), (2, 1), (2, 2), (1, 3)] │
|
||||||
feme : [(2, 1), (2, 2), (3, 1), (3, 2)]
|
│ say │ [(2, 3), (1, 2), (1, 3)] │
|
||||||
fey : [(2, 1), (2, 2), (2, 3)]
|
│ sal │ [(2, 3), (3, 3), (2, 2)] │
|
||||||
|
│ salal │ [(2, 3), (3, 3), (2, 2), (1, 2), (2, 1)] │
|
||||||
Starting @ (2, 2)...
|
│ sall │ [(2, 3), (3, 3), (2, 2), (2, 1)] │
|
||||||
e : [(2, 2)]
|
│ sallow │ [(2, 3), (3, 3), (2, 2), (2, 1), (3, 0), (3, 1)] │
|
||||||
eo : [(2, 2), (1, 2)]
|
│ sh │ [(2, 3), (3, 2)] │
|
||||||
eos : [(2, 2), (1, 2), (0, 3)]
|
│ sha │ [(2, 3), (3, 2), (3, 3)] │
|
||||||
eof : [(2, 2), (1, 2), (2, 1)]
|
│ shaly │ [(2, 3), (3, 2), (3, 3), (2, 2), (1, 3)] │
|
||||||
ee : [(2, 2), (1, 1)]
|
│ shall │ [(2, 3), (3, 2), (3, 3), (2, 2), (2, 1)] │
|
||||||
eer : [(2, 2), (1, 1), (1, 0)]
|
│ shallow │ [(2, 3), (3, 2), (3, 3), (2, 2), (2, 1), (3, 0), (3, 1)] │
|
||||||
eery : [(2, 2), (1, 1), (1, 0), (0, 0)]
|
│ slap │ [(2, 3), (2, 2), (1, 2), (0, 3)] │
|
||||||
evoe : [(2, 2), (1, 3), (0, 2), (1, 1)]
|
│ slay │ [(2, 3), (2, 2), (1, 2), (1, 3)] │
|
||||||
evoe : [(2, 2), (1, 3), (0, 2), (0, 1)]
|
│ sly │ [(2, 3), (2, 2), (1, 3)] │
|
||||||
evoe : [(2, 2), (1, 3), (1, 2), (0, 1)]
|
╰─────────┴──────────────────────────────────────────────────────────╯
|
||||||
evoe : [(2, 2), (1, 3), (1, 2), (1, 1)]
|
Starting @ (3, 0)
|
||||||
ee : [(2, 2), (3, 2)]
|
╭───────┬──────────────────────────────────────────╮
|
||||||
em : [(2, 2), (3, 1)]
|
│ Word │ Path │
|
||||||
emf : [(2, 2), (3, 1), (2, 1)]
|
├───────┼──────────────────────────────────────────┤
|
||||||
eme : [(2, 2), (3, 1), (3, 2)]
|
│ ou │ [(3, 0), (2, 0)] │
|
||||||
ef : [(2, 2), (2, 1)]
|
│ olla │ [(3, 0), (2, 1), (1, 0), (0, 1)] │
|
||||||
ey : [(2, 2), (2, 3)]
|
│ ollas │ [(3, 0), (2, 1), (1, 0), (0, 1), (0, 0)] │
|
||||||
eye : [(2, 2), (2, 3), (3, 2)]
|
│ olla │ [(3, 0), (2, 1), (2, 2), (1, 2)] │
|
||||||
|
│ ollas │ [(3, 0), (2, 1), (2, 2), (1, 2), (2, 3)] │
|
||||||
Starting @ (2, 3)...
|
│ olla │ [(3, 0), (2, 1), (2, 2), (3, 3)] │
|
||||||
y : [(2, 3)]
|
│ ollas │ [(3, 0), (2, 1), (2, 2), (3, 3), (2, 3)] │
|
||||||
yo : [(2, 3), (1, 2)]
|
│ ow │ [(3, 0), (3, 1)] │
|
||||||
yoe : [(2, 3), (1, 2), (0, 1)]
|
│ owl │ [(3, 0), (3, 1), (2, 1)] │
|
||||||
yoe : [(2, 3), (1, 2), (2, 2)]
|
│ owl │ [(3, 0), (3, 1), (2, 2)] │
|
||||||
yoe : [(2, 3), (1, 2), (1, 1)]
|
│ owly │ [(3, 0), (3, 1), (2, 2), (1, 3)] │
|
||||||
yquem : [(2, 3), (3, 3), (2, 2), (3, 1)]
|
│ owls │ [(3, 0), (3, 1), (2, 2), (2, 3)] │
|
||||||
yquem : [(2, 3), (3, 3), (3, 2), (3, 1)]
|
╰───────┴──────────────────────────────────────────╯
|
||||||
ye : [(2, 3), (3, 2)]
|
Starting @ (3, 1)
|
||||||
yee : [(2, 3), (3, 2), (2, 2)]
|
╭───────┬──────────────────────────────────────────╮
|
||||||
ye : [(2, 3), (2, 2)]
|
│ Word │ Path │
|
||||||
yeo : [(2, 3), (2, 2), (1, 2)]
|
├───────┼──────────────────────────────────────────┤
|
||||||
yee : [(2, 3), (2, 2), (1, 1)]
|
│ wull │ [(3, 1), (2, 0), (1, 0), (2, 1)] │
|
||||||
yee : [(2, 3), (2, 2), (3, 2)]
|
│ wull │ [(3, 1), (2, 0), (2, 1), (1, 0)] │
|
||||||
|
│ wulls │ [(3, 1), (2, 0), (2, 1), (1, 0), (0, 0)] │
|
||||||
Starting @ (3, 0)...
|
│ wull │ [(3, 1), (2, 0), (2, 1), (2, 2)] │
|
||||||
n : [(3, 0)]
|
│ wulls │ [(3, 1), (2, 0), (2, 1), (2, 2), (2, 3)] │
|
||||||
nd : [(3, 0), (2, 0)]
|
│ wo │ [(3, 1), (3, 0)] │
|
||||||
nm : [(3, 0), (3, 1)]
|
│ wolly │ [(3, 1), (3, 0), (2, 1), (2, 2), (1, 3)] │
|
||||||
|
│ wha │ [(3, 1), (3, 2), (3, 3)] │
|
||||||
Starting @ (3, 1)...
|
╰───────┴──────────────────────────────────────────╯
|
||||||
m : [(3, 1)]
|
Starting @ (3, 2)
|
||||||
mf : [(3, 1), (2, 1)]
|
╭────────┬──────────────────────────────────────────────────╮
|
||||||
mfr : [(3, 1), (2, 1), (1, 0)]
|
│ Word │ Path │
|
||||||
mfd : [(3, 1), (2, 1), (2, 0)]
|
├────────┼──────────────────────────────────────────────────┤
|
||||||
md : [(3, 1), (2, 0)]
|
│ ha │ [(3, 2), (3, 3)] │
|
||||||
me : [(3, 1), (2, 2)]
|
│ has │ [(3, 2), (3, 3), (2, 3)] │
|
||||||
meo : [(3, 1), (2, 2), (1, 2)]
|
│ halal │ [(3, 2), (3, 3), (2, 2), (1, 2), (2, 1)] │
|
||||||
mee : [(3, 1), (2, 2), (1, 1)]
|
│ hall │ [(3, 2), (3, 3), (2, 2), (2, 1)] │
|
||||||
meed : [(3, 1), (2, 2), (1, 1), (2, 0)]
|
│ hallo │ [(3, 2), (3, 3), (2, 2), (2, 1), (3, 0)] │
|
||||||
meer : [(3, 1), (2, 2), (1, 1), (1, 0)]
|
│ hallow │ [(3, 2), (3, 3), (2, 2), (2, 1), (3, 0), (3, 1)] │
|
||||||
mev : [(3, 1), (2, 2), (1, 3)]
|
╰────────┴──────────────────────────────────────────────────╯
|
||||||
mee : [(3, 1), (2, 2), (3, 2)]
|
Starting @ (3, 3)
|
||||||
mn : [(3, 1), (3, 0)]
|
╭────────┬──────────────────────────────────────────────────╮
|
||||||
me : [(3, 1), (3, 2)]
|
│ Word │ Path │
|
||||||
mee : [(3, 1), (3, 2), (2, 2)]
|
├────────┼──────────────────────────────────────────────────┤
|
||||||
|
│ as │ [(3, 3), (2, 3)] │
|
||||||
Starting @ (3, 2)...
|
│ asyla │ [(3, 3), (2, 3), (1, 3), (2, 2), (1, 2)] │
|
||||||
e : [(3, 2)]
|
│ ash │ [(3, 3), (2, 3), (3, 2)] │
|
||||||
ee : [(3, 2), (2, 2)]
|
│ al │ [(3, 3), (2, 2)] │
|
||||||
ef : [(3, 2), (2, 1)]
|
│ ala │ [(3, 3), (2, 2), (1, 2)] │
|
||||||
ey : [(3, 2), (2, 3)]
|
│ alap │ [(3, 3), (2, 2), (1, 2), (0, 3)] │
|
||||||
eye : [(3, 2), (2, 3), (2, 2)]
|
│ alas │ [(3, 3), (2, 2), (1, 2), (2, 3)] │
|
||||||
em : [(3, 2), (3, 1)]
|
│ alay │ [(3, 3), (2, 2), (1, 2), (1, 3)] │
|
||||||
emf : [(3, 2), (3, 1), (2, 1)]
|
│ alays │ [(3, 3), (2, 2), (1, 2), (1, 3), (2, 3)] │
|
||||||
eme : [(3, 2), (3, 1), (2, 2)]
|
│ all │ [(3, 3), (2, 2), (2, 1)] │
|
||||||
emeer : [(3, 2), (3, 1), (2, 2), (1, 1), (1, 0)]
|
│ allay │ [(3, 3), (2, 2), (2, 1), (1, 2), (1, 3)] │
|
||||||
|
│ allays │ [(3, 3), (2, 2), (2, 1), (1, 2), (1, 3), (2, 3)] │
|
||||||
Starting @ (3, 3)...
|
│ allow │ [(3, 3), (2, 2), (2, 1), (3, 0), (3, 1)] │
|
||||||
qu : [(3, 3)]
|
│ als │ [(3, 3), (2, 2), (2, 3)] │
|
||||||
que : [(3, 3), (2, 2)]
|
│ ah │ [(3, 3), (3, 2)] │
|
||||||
queer : [(3, 3), (2, 2), (1, 1), (1, 0)]
|
│ ahs │ [(3, 3), (3, 2), (2, 3)] │
|
||||||
queery : [(3, 3), (2, 2), (1, 1), (1, 0), (0, 0)]
|
╰────────┴──────────────────────────────────────────────────╯
|
||||||
quem : [(3, 3), (2, 2), (3, 1)]
|
</code></pre>
|
||||||
queme : [(3, 3), (2, 2), (3, 1), (3, 2)]
|
</details>
|
||||||
quey : [(3, 3), (2, 2), (2, 3)]
|
<br>
|
||||||
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
|
# Build
|
||||||
Make sure the hatchling build system is installed
|
Navigate to the project root folder and run the following.
|
||||||
```bash
|
Run `poetry build`
|
||||||
pip install hatchling
|
|
||||||
```
|
|
||||||
|
|
||||||
Navigate to the project folder then run the following.
|
|
||||||
```bash
|
|
||||||
python -m build .
|
|
||||||
```
|
|
||||||
|
|
||||||
# License
|
# License
|
||||||
The included [wordlists](src/boggler/wordlists) are covered by their respective licenses. All other files MIT © Cameron Blankenbuehler
|
The included [wordlists](src/boggler/wordlists) are covered by their respective licenses. All other files MIT © Cameron Blankenbuehler
|
||||||
|
|||||||
+67
-26
@@ -1,40 +1,66 @@
|
|||||||
"""Boggler Demo"""
|
"""Boggler Demo"""
|
||||||
from pprint import pprint
|
|
||||||
import argparse
|
import argparse
|
||||||
import json
|
import json
|
||||||
import csv
|
|
||||||
from itertools import chain
|
from itertools import chain
|
||||||
import sys
|
import sys
|
||||||
from pathlib import Path
|
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
|
from .boggler_utils import BoggleBoard, build_full_boggle_tree, read_boggle_file
|
||||||
|
|
||||||
parser = argparse.ArgumentParser(
|
parser = argparse.ArgumentParser(prog="boggler", description="Boggle board game solver")
|
||||||
prog="boggler",
|
|
||||||
description="Boggle board game solver"
|
|
||||||
)
|
|
||||||
|
|
||||||
parser.add_argument("board", type=Path, help="Path to board CSV file")
|
parser.add_argument("board", type=Path, help="Path to board CSV file")
|
||||||
parser.add_argument("wordlists", type=Path,
|
parser.add_argument(
|
||||||
help="Path to directory of wordlist files. The directory must contain \
|
"wordlists",
|
||||||
text files of the form words_X.txt where \"X\" is a character of \
|
type=Path,
|
||||||
the alphabet")
|
help='Path to directory of wordlist files. The directory must contain \
|
||||||
parser.add_argument("max_word_length", nargs="?", type=int, default=16,
|
text files of the form words_X.txt where "X" is a character of \
|
||||||
help="Maximum length of words searched for on provided board")
|
the alphabet',
|
||||||
parser.add_argument("-f", "--format", type=str,
|
)
|
||||||
help="Specify alternative output format including [txt, json]")
|
parser.add_argument(
|
||||||
parser.add_argument("-p", "--include-path", action="store_true", default=False,
|
"max_word_length",
|
||||||
help="Include full paths for each word in output")
|
nargs="?",
|
||||||
parser.add_argument("-s", "--sort", action="store_true", default=False,
|
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 \
|
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 \
|
block position on the board from top-to-bottom, left-to-right as given in the \
|
||||||
board file.")
|
board file.",
|
||||||
parser.add_argument("-d", "--dedup", action="store_true", default=False,
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"-d",
|
||||||
|
"--dedup",
|
||||||
|
action="store_true",
|
||||||
|
default=False,
|
||||||
help="Remove duplicates from word-only output. Note that de-duplication does not preserve \
|
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 \
|
the original order of the output, so it is recommended to also use the sort option when \
|
||||||
de-duplicating.")
|
de-duplicating.",
|
||||||
|
)
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
"""Command line tool for sovling Boggle boards"""
|
"""Command line tool for sovling Boggle boards"""
|
||||||
board = read_boggle_file(args.board)
|
board = read_boggle_file(args.board)
|
||||||
@@ -49,7 +75,9 @@ def main():
|
|||||||
if args.format:
|
if args.format:
|
||||||
match args.format.lower():
|
match args.format.lower():
|
||||||
case "txt":
|
case "txt":
|
||||||
word_paths = [start_block.word_paths for start_block in boggle_tree.values()]
|
word_paths = [
|
||||||
|
start_block.word_paths for start_block in boggle_tree.values()
|
||||||
|
]
|
||||||
data = list(chain(*word_paths))
|
data = list(chain(*word_paths))
|
||||||
if args.include_path:
|
if args.include_path:
|
||||||
data = [f"{line[0]} {line[1]}" for line in data]
|
data = [f"{line[0]} {line[1]}" for line in data]
|
||||||
@@ -65,20 +93,33 @@ def main():
|
|||||||
print(line)
|
print(line)
|
||||||
|
|
||||||
case "json":
|
case "json":
|
||||||
data = { str(k):v.word_paths for k,v in boggle_tree.items()}
|
data = {str(k): v.word_paths for k, v in boggle_tree.items()}
|
||||||
print(json.dumps(data, indent=2, sort_keys=True))
|
print(json.dumps(data, indent=2, sort_keys=True))
|
||||||
case _:
|
case _:
|
||||||
print(f"Invalid format (-f) option provided: \"{args.format}\"")
|
print(f'Invalid format (-f) option provided: "{args.format}"')
|
||||||
sys.exit()
|
sys.exit()
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
console = Console()
|
||||||
print("\nBOARD")
|
print("\nBOARD")
|
||||||
print(boggle_board)
|
print(boggle_board)
|
||||||
|
|
||||||
for start_pos, tree in boggle_tree.items():
|
for start_pos, tree in boggle_tree.items():
|
||||||
print(f"\nStarting @ {start_pos}...")
|
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:
|
for word in tree.word_paths:
|
||||||
print(f"{word[0]: <{boggle_board.max_word_len}}: {word[1]}")
|
# print(f"{word[0]: <{boggle_board.max_word_len}}: {word[1]}")
|
||||||
|
table.add_row(f"{word[0]}", f"{word[1]}")
|
||||||
|
|
||||||
if __name__ == '__main__':
|
console.print(table)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
|
|||||||
Executable
+135
@@ -0,0 +1,135 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
|
||||||
|
class 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: 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"""
|
||||||
|
return self.__row
|
||||||
|
|
||||||
|
@property
|
||||||
|
def col(self) -> int:
|
||||||
|
"""Getter for col property"""
|
||||||
|
return self.__col
|
||||||
|
|
||||||
|
@property
|
||||||
|
def pos(self) -> tuple[int, int]:
|
||||||
|
"""Getter for pos property"""
|
||||||
|
return self.__pos
|
||||||
|
|
||||||
|
@property
|
||||||
|
def letters(self) -> str:
|
||||||
|
"""Getter for letter property"""
|
||||||
|
return self.__letters
|
||||||
|
|
||||||
|
@property
|
||||||
|
def adjacent_cells(self) -> list[BoardCell]:
|
||||||
|
"""Getter for adjacent_cells property"""
|
||||||
|
return self.__adjacent_cells
|
||||||
|
|
||||||
|
@adjacent_cells.setter
|
||||||
|
def adjacent_cells(self, value):
|
||||||
|
self.__adjacent_cells = value
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f"({self.__row}, {self.__col}): {self.__letters}"
|
||||||
|
|
||||||
|
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) -> 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[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)
|
||||||
|
|
||||||
|
# Generate BoardCell for each position on the board
|
||||||
|
for row in range(0, self.__height):
|
||||||
|
for col in range(0, self.__width):
|
||||||
|
self.__board[(row, col)] = BoardCell(row, col, board[row][col])
|
||||||
|
|
||||||
|
# Update adjacent cell references for each BoardCell
|
||||||
|
for cell in self.__board.values():
|
||||||
|
adjacent_indexes = self.__get_adjacent_indexes(cell.row, cell.col)
|
||||||
|
cell.adjacent_cells = [self.__board[(x[0], x[1])] for x in adjacent_indexes]
|
||||||
|
|
||||||
|
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
|
||||||
|
header_len = self.__width * (max_len + 1) - 1
|
||||||
|
head = "-" * header_len
|
||||||
|
header = f"+{head}+\n"
|
||||||
|
body = ""
|
||||||
|
for row in self.__board_list:
|
||||||
|
body += "|"
|
||||||
|
for col in row:
|
||||||
|
body += f"{col.upper(): ^{max_len}}|"
|
||||||
|
body += "\n"
|
||||||
|
body += header
|
||||||
|
return f"{header}{body}"
|
||||||
|
|
||||||
|
@property
|
||||||
|
def height(self) -> int:
|
||||||
|
"""Getter for height property"""
|
||||||
|
return self.__height
|
||||||
|
|
||||||
|
@property
|
||||||
|
def width(self) -> int:
|
||||||
|
"""Getter for width property"""
|
||||||
|
return self.__width
|
||||||
|
|
||||||
|
@property
|
||||||
|
def max_word_len(self) -> int:
|
||||||
|
"""Getter for maximum word length property"""
|
||||||
|
return self.__max_word_len
|
||||||
|
|
||||||
|
@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`"""
|
||||||
|
indexes = []
|
||||||
|
if row > 0:
|
||||||
|
indexes.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
|
||||||
|
return indexes
|
||||||
|
|
||||||
|
def get_cell(self, row: int, col: int) -> BoardCell:
|
||||||
|
"""Return the value at the specified row x column"""
|
||||||
|
return self.__board[(row, col)]
|
||||||
+16
-10
@@ -1,21 +1,25 @@
|
|||||||
'''Module to generate random Boggle boards for testing'''
|
"""Module to generate random Boggle boards for testing"""
|
||||||
from sys import argv, stderr
|
from sys import argv, stderr
|
||||||
from random import randint, shuffle
|
from random import randint, shuffle
|
||||||
from math import sqrt, floor
|
from math import sqrt, floor
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
def read_dice_file(dice_path: Path):
|
def read_dice_file(dice_path: Path):
|
||||||
'''Return list of die strings from file ignoring all comments (#)'''
|
"""Return list of die strings from file ignoring all comments (#)"""
|
||||||
with open(dice_path, 'r+', encoding="utf-8") as file:
|
with open(dice_path, "r+", encoding="utf-8") as file:
|
||||||
return [line.rstrip().split(',') for line in file.readlines() if line[0] != "#"]
|
return [line.rstrip().split(",") for line in file.readlines() if line[0] != "#"]
|
||||||
|
|
||||||
|
|
||||||
def roll_die(die: str):
|
def roll_die(die: str):
|
||||||
'''Return a face of the given die string to simulate rolling a die'''
|
"""Return a face of the given die string to simulate rolling a die"""
|
||||||
return str(die[randint(0, len(die) - 1)])
|
return str(die[randint(0, len(die) - 1)])
|
||||||
|
|
||||||
|
|
||||||
def roll_dice(dice: list[str]):
|
def roll_dice(dice: list[str]):
|
||||||
'''Return a random roll for each die'''
|
"""Return a random roll for each die"""
|
||||||
return [ roll_die(die) for die in dice ]
|
return [roll_die(die) for die in dice]
|
||||||
|
|
||||||
|
|
||||||
def get_random_board(dice: list[str]):
|
def get_random_board(dice: list[str]):
|
||||||
shuffle(dice)
|
shuffle(dice)
|
||||||
@@ -25,17 +29,19 @@ def get_random_board(dice: list[str]):
|
|||||||
board_size = int(floor(sqrt(len(rolls))))
|
board_size = int(floor(sqrt(len(rolls))))
|
||||||
board = []
|
board = []
|
||||||
for i in range(0, len(dice), board_size):
|
for i in range(0, len(dice), board_size):
|
||||||
board.append(rolls[i:i+board_size])
|
board.append(rolls[i : i + board_size])
|
||||||
return board
|
return board
|
||||||
|
|
||||||
|
|
||||||
def get_random_board_csv(dice: list[str]):
|
def get_random_board_csv(dice: list[str]):
|
||||||
board = get_random_board(dice)
|
board = get_random_board(dice)
|
||||||
board = [",".join(row) for row in board]
|
board = [",".join(row) for row in board]
|
||||||
return board
|
return board
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
|
if __name__ == "__main__":
|
||||||
if len(argv) != 2:
|
if len(argv) != 2:
|
||||||
print('Usage: python3 board_randomizer.py <dice_file>')
|
print("Usage: python3 board_randomizer.py <dice_file>")
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
dice = read_dice_file(Path(argv[1]))
|
dice = read_dice_file(Path(argv[1]))
|
||||||
|
|||||||
+132
-182
@@ -1,142 +1,24 @@
|
|||||||
'''Boggler Utils'''
|
"""Boggler Utils"""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from multiprocessing import Pool
|
from multiprocessing import Pool
|
||||||
import logging as log
|
import logging as log
|
||||||
import functools
|
import functools
|
||||||
import operator
|
import operator
|
||||||
|
from .board import BoardCell, BoggleBoard
|
||||||
|
|
||||||
class 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: 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'''
|
|
||||||
return self.__row
|
|
||||||
|
|
||||||
@property
|
|
||||||
def col(self) -> int:
|
|
||||||
'''Getter for col property'''
|
|
||||||
return self.__col
|
|
||||||
|
|
||||||
@property
|
|
||||||
def pos(self) -> tuple[int, int]:
|
|
||||||
'''Getter for pos property'''
|
|
||||||
return self.__pos
|
|
||||||
|
|
||||||
@property
|
|
||||||
def letters(self) -> str:
|
|
||||||
'''Getter for letter property'''
|
|
||||||
return self.__letters
|
|
||||||
|
|
||||||
@property
|
|
||||||
def adjacent_cells(self) -> list[BoardCell]:
|
|
||||||
'''Getter for adjacent_cells property'''
|
|
||||||
return self.__adjacent_cells
|
|
||||||
|
|
||||||
@adjacent_cells.setter
|
|
||||||
def adjacent_cells(self, value):
|
|
||||||
self.__adjacent_cells = value
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return f"({self.__row}, {self.__col}): {self.__letters}"
|
|
||||||
|
|
||||||
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) -> 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[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)
|
|
||||||
|
|
||||||
# Generate BoardCell for each position on the board
|
|
||||||
for row in range(0, self.__height):
|
|
||||||
for col in range(0, self.__width):
|
|
||||||
self.__board[(row, col)] = BoardCell(row, col, board[row][col])
|
|
||||||
|
|
||||||
# Update adjacent cell references for each BoardCell
|
|
||||||
for cell in self.__board.values():
|
|
||||||
adjacent_indexes = self.__get_adjacent_indexes(cell.row, cell.col)
|
|
||||||
cell.adjacent_cells = [self.__board[(x[0], x[1])] for x in adjacent_indexes]
|
|
||||||
|
|
||||||
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
|
|
||||||
header_len = self.__width * (max_len + 1) - 1
|
|
||||||
head = "-" * header_len
|
|
||||||
header = f"+{head}+\n"
|
|
||||||
body = ""
|
|
||||||
for row in self.__board_list:
|
|
||||||
body += "|"
|
|
||||||
for col in row:
|
|
||||||
body += f"{col.upper(): ^{max_len}}|"
|
|
||||||
body += "\n"
|
|
||||||
body += header
|
|
||||||
return f"{header}{body}"
|
|
||||||
|
|
||||||
@property
|
|
||||||
def height(self) -> int:
|
|
||||||
'''Getter for height property'''
|
|
||||||
return self.__height
|
|
||||||
|
|
||||||
@property
|
|
||||||
def width(self) -> int:
|
|
||||||
'''Getter for width property'''
|
|
||||||
return self.__width
|
|
||||||
|
|
||||||
@property
|
|
||||||
def max_word_len(self) -> int:
|
|
||||||
'''Getter for maximum word length property'''
|
|
||||||
return self.__max_word_len
|
|
||||||
|
|
||||||
@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`'''
|
|
||||||
indexes = []
|
|
||||||
if row > 0:
|
|
||||||
indexes.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
|
|
||||||
return indexes
|
|
||||||
|
|
||||||
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:
|
class WordNode:
|
||||||
'''A node describing a single letter in a WordTree.'''
|
"""A node describing a single letter in a WordTree."""
|
||||||
def __init__(self, letters: str, is_word: bool = False, parent: WordNode = None,
|
|
||||||
children: dict[str, WordNode] = None, board_pos = (None, None)) -> None:
|
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.__letters = letters
|
||||||
self.__is_word = is_word
|
self.__is_word = is_word
|
||||||
self.__children = children if children is not None else {}
|
self.__children = children if children is not None else {}
|
||||||
@@ -145,12 +27,12 @@ class WordNode:
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def letters(self) -> str:
|
def letters(self) -> str:
|
||||||
'''Getter for letter property'''
|
"""Getter for letter property"""
|
||||||
return self.__letters
|
return self.__letters
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_word(self) -> bool:
|
def is_word(self) -> bool:
|
||||||
'''Getter for is_word property'''
|
"""Getter for is_word property"""
|
||||||
return self.__is_word
|
return self.__is_word
|
||||||
|
|
||||||
@is_word.setter
|
@is_word.setter
|
||||||
@@ -159,26 +41,26 @@ class WordNode:
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def children(self) -> dict[str, WordNode]:
|
def children(self) -> dict[str, WordNode]:
|
||||||
'''Getter for children property'''
|
"""Getter for children property"""
|
||||||
return self.__children
|
return self.__children
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def parent(self) -> WordNode:
|
def parent(self) -> WordNode:
|
||||||
'''Getter for parent property'''
|
"""Getter for parent property"""
|
||||||
return self.__parent
|
return self.__parent
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def board_pos(self) -> tuple[int, int]:
|
def board_pos(self) -> tuple[int, int]:
|
||||||
'''Getter for board_pos property'''
|
"""Getter for board_pos property"""
|
||||||
return self.__board_pos
|
return self.__board_pos
|
||||||
|
|
||||||
def add_child_node(self, node):
|
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
|
self.children[node.letters] = node
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def path(self) -> list[WordNode]:
|
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
|
curr_node = self
|
||||||
path = []
|
path = []
|
||||||
path.append(curr_node.board_pos)
|
path.append(curr_node.board_pos)
|
||||||
@@ -189,7 +71,7 @@ class WordNode:
|
|||||||
return path
|
return path
|
||||||
|
|
||||||
def get_word(self, board: BoggleBoard) -> str:
|
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]])
|
return "".join([board.board[x].letters for x in self.path[::-1]])
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
@@ -198,10 +80,17 @@ class WordNode:
|
|||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return f"WordNode: {self.letters}, {self.is_word}, {self.children}"
|
return f"WordNode: {self.letters}, {self.is_word}, {self.children}"
|
||||||
|
|
||||||
|
|
||||||
class WordTree:
|
class WordTree:
|
||||||
'''A tree populated by WordNode(s) to complete words from a given root letter and wordlist'''
|
"""A tree populated by WordNode(s) to complete words from a given root letter and wordlist"""
|
||||||
def __init__(self, alphabet: list[str], root: WordNode, words: list[str] = None,
|
|
||||||
max_word_len = 16) -> None:
|
def __init__(
|
||||||
|
self,
|
||||||
|
alphabet: list[str],
|
||||||
|
root: WordNode,
|
||||||
|
words: list[str] = None,
|
||||||
|
max_word_len=16,
|
||||||
|
) -> None:
|
||||||
self.__alphabet = alphabet
|
self.__alphabet = alphabet
|
||||||
self.__wordlist = words
|
self.__wordlist = words
|
||||||
self.__root = root
|
self.__root = root
|
||||||
@@ -220,32 +109,32 @@ class WordTree:
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def alphabet(self) -> list:
|
def alphabet(self) -> list:
|
||||||
'''Getter for alphabet property'''
|
"""Getter for alphabet property"""
|
||||||
return self.__alphabet
|
return self.__alphabet
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def wordlist(self) -> list[str]:
|
def wordlist(self) -> list[str]:
|
||||||
'''Getter for wordlist property'''
|
"""Getter for wordlist property"""
|
||||||
return self.__wordlist
|
return self.__wordlist
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def root(self) -> WordNode:
|
def root(self) -> WordNode:
|
||||||
'''Getter for root property'''
|
"""Getter for root property"""
|
||||||
return self.__root
|
return self.__root
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def max_word_len(self) -> int:
|
def max_word_len(self) -> int:
|
||||||
'''Getter for max_word_len property'''
|
"""Getter for max_word_len property"""
|
||||||
return self.__max_word_len
|
return self.__max_word_len
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def tree(self) -> dict[str, WordNode]:
|
def tree(self) -> dict[str, WordNode]:
|
||||||
'''Getter for tree property'''
|
"""Getter for tree property"""
|
||||||
return self.__tree
|
return self.__tree
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def word_paths(self) -> dict[(int, int), WordNode]:
|
def word_paths(self) -> dict[(int, int), WordNode]:
|
||||||
'''Getter for leaf_nodes property'''
|
"""Getter for leaf_nodes property"""
|
||||||
return self.__word_paths
|
return self.__word_paths
|
||||||
|
|
||||||
@word_paths.setter
|
@word_paths.setter
|
||||||
@@ -258,19 +147,25 @@ class WordTree:
|
|||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return self.__str__()
|
return self.__str__()
|
||||||
|
|
||||||
def insert_node(self, letters: str, parent: WordNode, is_word: bool = False,
|
def insert_node(
|
||||||
children: dict[str, WordNode] = None, board_pos = None):
|
self,
|
||||||
'''Create WordNode for `letters` and into WordTree under `parent`'''
|
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`"""
|
||||||
log.debug("inserting node: %s", letters)
|
log.debug("inserting node: %s", letters)
|
||||||
node = WordNode(letters, is_word, parent, children, board_pos)
|
node = WordNode(letters, is_word, parent, children, board_pos)
|
||||||
parent.add_child_node(node)
|
parent.add_child_node(node)
|
||||||
|
|
||||||
def __insert_word(self, word: str) -> bool:
|
def __insert_word(self, word: str) -> bool:
|
||||||
'''Returns True if the word could be inserted into the tree with the given alphabet,
|
"""Returns True if the word could be inserted into the tree with the given alphabet,
|
||||||
otherwise returns False'''
|
otherwise returns False"""
|
||||||
|
|
||||||
# Insert root node
|
# Insert root node
|
||||||
prefix = word[0:len(self.root.letters)] # prefix = first letter block
|
prefix = word[0 : len(self.root.letters)] # prefix = first letter block
|
||||||
if word is None or prefix != self.root.letters:
|
if word is None or prefix != self.root.letters:
|
||||||
return False
|
return False
|
||||||
curr_node = self.tree[prefix]
|
curr_node = self.tree[prefix]
|
||||||
@@ -280,11 +175,18 @@ class WordTree:
|
|||||||
i_max = min(self.max_word_len, len(word))
|
i_max = min(self.max_word_len, len(word))
|
||||||
while i < i_max:
|
while i < i_max:
|
||||||
letters = word[i]
|
letters = word[i]
|
||||||
log.debug("1 letter seq: %s %s %s", word, letters, letters in curr_node.children)
|
log.debug(
|
||||||
|
"1 letter seq: %s %s %s", word, letters, letters in curr_node.children
|
||||||
|
)
|
||||||
if letters not in self.alphabet:
|
if letters not in self.alphabet:
|
||||||
# Check two letter sequences like ("Qu", "Th", etc.) at current index
|
# Check two letter sequences like ("Qu", "Th", etc.) at current index
|
||||||
letters = word[i:i+2]
|
letters = word[i : i + 2]
|
||||||
log.debug("2 letter seq: %s %s %s", word, letters, letters in curr_node.children)
|
log.debug(
|
||||||
|
"2 letter seq: %s %s %s",
|
||||||
|
word,
|
||||||
|
letters,
|
||||||
|
letters in curr_node.children,
|
||||||
|
)
|
||||||
if letters not in self.alphabet:
|
if letters not in self.alphabet:
|
||||||
return False
|
return False
|
||||||
# Insert node
|
# Insert node
|
||||||
@@ -300,8 +202,8 @@ class WordTree:
|
|||||||
curr_node.is_word = len(word) <= self.max_word_len
|
curr_node.is_word = len(word) <= self.max_word_len
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def search(self, word: str, curr_node = None) -> WordNode:
|
def search(self, word: str, curr_node=None) -> WordNode:
|
||||||
'''Return leaf (WordNode) if a given word is in the tree otherwise return None'''
|
"""Return leaf (WordNode) if a given word is in the tree otherwise return None"""
|
||||||
if len(word) == 0 or word is None:
|
if len(word) == 0 or word is None:
|
||||||
return curr_node
|
return curr_node
|
||||||
|
|
||||||
@@ -309,22 +211,33 @@ class WordTree:
|
|||||||
if curr_node is None:
|
if curr_node is None:
|
||||||
curr_node = self.root
|
curr_node = self.root
|
||||||
for letters in curr_node.children:
|
for letters in curr_node.children:
|
||||||
if letters == word[len(curr_node.letters):len(curr_node.letters) + len(letters)]:
|
if (
|
||||||
return self.search(word[len(curr_node.letters):], curr_node.children[letters])
|
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
|
# Currently only returns single path for word
|
||||||
# TODO: return all possible paths, maybe create minature WordTree? Or just of list of paths.
|
# TODO: return all possible paths, maybe create minature WordTree? Or just of list of paths.
|
||||||
return curr_node
|
return curr_node
|
||||||
|
|
||||||
def build_boggle_tree(self, board: BoggleBoard, board_cell: BoardCell, subtree: WordTree, word_len: int = 0) -> WordTree:
|
def build_boggle_tree(
|
||||||
'''Return subtree of board given a particular root (first letter).
|
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:
|
Keyword arguments:
|
||||||
board -- the board the new tree is based on
|
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
|
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
|
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
|
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:
|
if word_len > self.max_word_len or word_len >= board.max_word_len:
|
||||||
log.debug(f"MAX WORD LENGTH REACHED! len = {word_len}")
|
log.debug(f"MAX WORD LENGTH REACHED! len = {word_len}")
|
||||||
@@ -336,39 +249,68 @@ class WordTree:
|
|||||||
if self.active_node.is_word and len(self.active_node.children) == 0:
|
if self.active_node.is_word and len(self.active_node.children) == 0:
|
||||||
word_path = subtree.active_node.path[::-1]
|
word_path = subtree.active_node.path[::-1]
|
||||||
subtree.word_paths.append((subtree.active_node.get_word(board), word_path))
|
subtree.word_paths.append((subtree.active_node.get_word(board), word_path))
|
||||||
log.debug("1: WORD FOUND: %s %s", "".join([board.board[x].letters for x in word_path]), word_path)
|
log.debug(
|
||||||
|
"1: WORD FOUND: %s %s",
|
||||||
|
"".join([board.board[x].letters for x in word_path]),
|
||||||
|
word_path,
|
||||||
|
)
|
||||||
self.active_node = self.active_node.parent
|
self.active_node = self.active_node.parent
|
||||||
subtree.active_node = subtree.active_node.parent
|
subtree.active_node = subtree.active_node.parent
|
||||||
return subtree
|
return subtree
|
||||||
elif self.active_node.is_word:
|
elif self.active_node.is_word:
|
||||||
word_path = subtree.active_node.path[::-1]
|
word_path = subtree.active_node.path[::-1]
|
||||||
subtree.word_paths.append((subtree.active_node.get_word(board), word_path))
|
subtree.word_paths.append((subtree.active_node.get_word(board), word_path))
|
||||||
log.debug("2: WORD FOUND: %s %s", "".join([board.board[x].letters for x in word_path]), word_path)
|
log.debug(
|
||||||
|
"2: WORD FOUND: %s %s",
|
||||||
|
"".join([board.board[x].letters for x in word_path]),
|
||||||
|
word_path,
|
||||||
|
)
|
||||||
|
|
||||||
# Branch for each adjacent board cell
|
# Branch for each adjacent board cell
|
||||||
for cell in board_cell.adjacent_cells:
|
for cell in board_cell.adjacent_cells:
|
||||||
# Check dictionary and exclude nodes already in path
|
# 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:
|
if (
|
||||||
new_node = WordNode(cell.letters, self.active_node.is_word, subtree.active_node, board_pos = cell.pos)
|
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.add_child_node(new_node)
|
||||||
subtree.active_node = subtree.active_node.children[cell.letters] # update subtree pointer
|
subtree.active_node = subtree.active_node.children[
|
||||||
self.active_node = self.active_node.children[cell.letters] # update dictionary tree pointer
|
cell.letters
|
||||||
self.build_boggle_tree(board, board.board[cell.pos], subtree, word_len=word_len+len(board_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
|
self.active_node = self.active_node.parent
|
||||||
subtree.active_node = subtree.active_node.parent
|
subtree.active_node = subtree.active_node.parent
|
||||||
return subtree
|
return subtree
|
||||||
|
|
||||||
|
|
||||||
def build_boggle_tree(args):
|
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
|
(alphabet, board, cell, wordlist) = args
|
||||||
root_node = WordNode(cell.letters)
|
root_node = WordNode(cell.letters)
|
||||||
dict_tree = WordTree(alphabet, root_node, wordlist)
|
dict_tree = WordTree(alphabet, root_node, wordlist)
|
||||||
sub_tree = WordTree(alphabet, WordNode(cell.letters, False, board_pos=cell.pos))
|
sub_tree = WordTree(alphabet, WordNode(cell.letters, False, board_pos=cell.pos))
|
||||||
return dict_tree.build_boggle_tree(board, cell, sub_tree)
|
return dict_tree.build_boggle_tree(board, cell, sub_tree)
|
||||||
|
|
||||||
def build_full_boggle_tree(board: BoggleBoard, wordlist_path: Path) -> 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()]))
|
alphabet = sorted(set([cell.letters for cell in board.board.values()]))
|
||||||
board_tree = {}
|
board_tree = {}
|
||||||
index: dict[str] = {}
|
index: dict[str] = {}
|
||||||
@@ -393,7 +335,9 @@ def build_full_boggle_tree(board: BoggleBoard, wordlist_path: Path) -> dict[str,
|
|||||||
index[letters] = {}
|
index[letters] = {}
|
||||||
|
|
||||||
log.info("Generating WordTrees...")
|
log.info("Generating WordTrees...")
|
||||||
params = [ [alphabet, board, cell, index[cell.letters] ] for cell in board.board.values()]
|
params = [
|
||||||
|
[alphabet, board, cell, index[cell.letters]] for cell in board.board.values()
|
||||||
|
]
|
||||||
with Pool(processes=len(board.board)) as pool:
|
with Pool(processes=len(board.board)) as pool:
|
||||||
for i, res in enumerate(pool.map(build_boggle_tree, params)):
|
for i, res in enumerate(pool.map(build_boggle_tree, params)):
|
||||||
log.info(">> %s", params[i][2])
|
log.info(">> %s", params[i][2])
|
||||||
@@ -401,22 +345,25 @@ def build_full_boggle_tree(board: BoggleBoard, wordlist_path: Path) -> dict[str,
|
|||||||
|
|
||||||
return board_tree
|
return board_tree
|
||||||
|
|
||||||
|
|
||||||
def read_wordlist(file):
|
def read_wordlist(file):
|
||||||
'''Return dictionary of words with associated word count (1 by default)'''
|
"""Return dictionary of words with associated word count (1 by default)"""
|
||||||
with open(file, 'r', encoding='utf-8') as file:
|
with open(file, "r", encoding="utf-8") as file:
|
||||||
return file.read().split()
|
return file.read().split()
|
||||||
|
|
||||||
|
|
||||||
class BadBoardFormat(Exception):
|
class BadBoardFormat(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
def read_boggle_file(file):
|
def read_boggle_file(file):
|
||||||
'''Return list of rows from Boggle board csv file
|
"""Return list of rows from Boggle board csv file
|
||||||
|
|
||||||
The size of the board is determined by the width (number of comma-separated values)
|
The size of the board is determined by the width (number of comma-separated values)
|
||||||
of the first (non-empty) line in the file.
|
of the first (non-empty) line in the file.
|
||||||
|
|
||||||
'''
|
"""
|
||||||
with open(file, 'r', encoding='utf-8') as file:
|
with open(file, "r", encoding="utf-8") as file:
|
||||||
board = []
|
board = []
|
||||||
|
|
||||||
board.append([x.strip() for x in file.readline().split(",")])
|
board.append([x.strip() for x in file.readline().split(",")])
|
||||||
@@ -425,7 +372,7 @@ def read_boggle_file(file):
|
|||||||
if line.strip() == "":
|
if line.strip() == "":
|
||||||
raise BadBoardFormat("board files must contain no blank lines")
|
raise BadBoardFormat("board files must contain no blank lines")
|
||||||
|
|
||||||
row = [x.strip() for x in line.strip().split(',')]
|
row = [x.strip() for x in line.strip().split(",")]
|
||||||
if len(row) != board_size:
|
if len(row) != board_size:
|
||||||
raise BadBoardFormat("the length of each row must be the same")
|
raise BadBoardFormat("the length of each row must be the same")
|
||||||
|
|
||||||
@@ -433,9 +380,12 @@ def read_boggle_file(file):
|
|||||||
|
|
||||||
return board
|
return board
|
||||||
|
|
||||||
|
|
||||||
def find_paths_by_word(board_letters, dictionary_path, max_len):
|
def find_paths_by_word(board_letters, dictionary_path, max_len):
|
||||||
'''Return list of paths by word'''
|
"""Return list of paths by word"""
|
||||||
boggle_board = BoggleBoard(board_letters, max_len)
|
boggle_board = BoggleBoard(board_letters, max_len)
|
||||||
boggle_tree = build_full_boggle_tree(boggle_board, dictionary_path)
|
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()], [])
|
paths_by_word = functools.reduce(
|
||||||
|
operator.iconcat, [x.word_paths for x in boggle_tree.values()], []
|
||||||
|
)
|
||||||
return paths_by_word
|
return paths_by_word
|
||||||
|
|||||||
Generated
+248
-19
@@ -1,10 +1,9 @@
|
|||||||
# This file is automatically @generated by Poetry 1.4.2 and should not be changed by hand.
|
# This file is automatically @generated by Poetry 1.5.1 and should not be changed by hand.
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "astroid"
|
name = "astroid"
|
||||||
version = "2.15.5"
|
version = "2.15.5"
|
||||||
description = "An abstract syntax tree for Python with inference support."
|
description = "An abstract syntax tree for Python with inference support."
|
||||||
category = "dev"
|
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.7.2"
|
python-versions = ">=3.7.2"
|
||||||
files = [
|
files = [
|
||||||
@@ -16,11 +15,21 @@ files = [
|
|||||||
lazy-object-proxy = ">=1.4.0"
|
lazy-object-proxy = ">=1.4.0"
|
||||||
wrapt = {version = ">=1.14,<2", markers = "python_version >= \"3.11\""}
|
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]]
|
[[package]]
|
||||||
name = "colorama"
|
name = "colorama"
|
||||||
version = "0.4.6"
|
version = "0.4.6"
|
||||||
description = "Cross-platform colored terminal text."
|
description = "Cross-platform colored terminal text."
|
||||||
category = "dev"
|
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
|
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
|
||||||
files = [
|
files = [
|
||||||
@@ -32,7 +41,6 @@ files = [
|
|||||||
name = "dill"
|
name = "dill"
|
||||||
version = "0.3.6"
|
version = "0.3.6"
|
||||||
description = "serialize all of python"
|
description = "serialize all of python"
|
||||||
category = "dev"
|
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.7"
|
python-versions = ">=3.7"
|
||||||
files = [
|
files = [
|
||||||
@@ -43,11 +51,50 @@ files = [
|
|||||||
[package.extras]
|
[package.extras]
|
||||||
graph = ["objgraph (>=1.7.2)"]
|
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]]
|
[[package]]
|
||||||
name = "iniconfig"
|
name = "iniconfig"
|
||||||
version = "2.0.0"
|
version = "2.0.0"
|
||||||
description = "brain-dead simple config-ini parsing"
|
description = "brain-dead simple config-ini parsing"
|
||||||
category = "dev"
|
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.7"
|
python-versions = ">=3.7"
|
||||||
files = [
|
files = [
|
||||||
@@ -59,7 +106,6 @@ files = [
|
|||||||
name = "isort"
|
name = "isort"
|
||||||
version = "5.12.0"
|
version = "5.12.0"
|
||||||
description = "A Python utility / library to sort Python imports."
|
description = "A Python utility / library to sort Python imports."
|
||||||
category = "dev"
|
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.8.0"
|
python-versions = ">=3.8.0"
|
||||||
files = [
|
files = [
|
||||||
@@ -77,7 +123,6 @@ requirements-deprecated-finder = ["pip-api", "pipreqs"]
|
|||||||
name = "lazy-object-proxy"
|
name = "lazy-object-proxy"
|
||||||
version = "1.9.0"
|
version = "1.9.0"
|
||||||
description = "A fast and thorough lazy object proxy."
|
description = "A fast and thorough lazy object proxy."
|
||||||
category = "dev"
|
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.7"
|
python-versions = ">=3.7"
|
||||||
files = [
|
files = [
|
||||||
@@ -119,11 +164,34 @@ files = [
|
|||||||
{file = "lazy_object_proxy-1.9.0-cp39-cp39-win_amd64.whl", hash = "sha256:db1c1722726f47e10e0b5fdbf15ac3b8adb58c091d12b3ab713965795036985f"},
|
{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]]
|
[[package]]
|
||||||
name = "mccabe"
|
name = "mccabe"
|
||||||
version = "0.7.0"
|
version = "0.7.0"
|
||||||
description = "McCabe checker, plugin for flake8"
|
description = "McCabe checker, plugin for flake8"
|
||||||
category = "dev"
|
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.6"
|
python-versions = ">=3.6"
|
||||||
files = [
|
files = [
|
||||||
@@ -131,11 +199,21 @@ files = [
|
|||||||
{file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"},
|
{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]]
|
[[package]]
|
||||||
name = "mypy"
|
name = "mypy"
|
||||||
version = "1.3.0"
|
version = "1.3.0"
|
||||||
description = "Optional static typing for Python"
|
description = "Optional static typing for Python"
|
||||||
category = "dev"
|
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.7"
|
python-versions = ">=3.7"
|
||||||
files = [
|
files = [
|
||||||
@@ -181,7 +259,6 @@ reports = ["lxml"]
|
|||||||
name = "mypy-extensions"
|
name = "mypy-extensions"
|
||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
description = "Type system extensions for programs checked with the mypy type checker."
|
description = "Type system extensions for programs checked with the mypy type checker."
|
||||||
category = "dev"
|
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.5"
|
python-versions = ">=3.5"
|
||||||
files = [
|
files = [
|
||||||
@@ -189,11 +266,24 @@ files = [
|
|||||||
{file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"},
|
{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]]
|
[[package]]
|
||||||
name = "packaging"
|
name = "packaging"
|
||||||
version = "23.1"
|
version = "23.1"
|
||||||
description = "Core utilities for Python packages"
|
description = "Core utilities for Python packages"
|
||||||
category = "dev"
|
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.7"
|
python-versions = ">=3.7"
|
||||||
files = [
|
files = [
|
||||||
@@ -205,7 +295,6 @@ files = [
|
|||||||
name = "platformdirs"
|
name = "platformdirs"
|
||||||
version = "3.5.1"
|
version = "3.5.1"
|
||||||
description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"."
|
description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"."
|
||||||
category = "dev"
|
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.7"
|
python-versions = ">=3.7"
|
||||||
files = [
|
files = [
|
||||||
@@ -221,7 +310,6 @@ test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.3.1)", "pytest-
|
|||||||
name = "pluggy"
|
name = "pluggy"
|
||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
description = "plugin and hook calling mechanisms for python"
|
description = "plugin and hook calling mechanisms for python"
|
||||||
category = "dev"
|
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.6"
|
python-versions = ">=3.6"
|
||||||
files = [
|
files = [
|
||||||
@@ -233,11 +321,53 @@ files = [
|
|||||||
dev = ["pre-commit", "tox"]
|
dev = ["pre-commit", "tox"]
|
||||||
testing = ["pytest", "pytest-benchmark"]
|
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 = "pygtrie"
|
||||||
|
version = "2.5.0"
|
||||||
|
description = "A pure Python trie data structure implementation."
|
||||||
|
optional = false
|
||||||
|
python-versions = "*"
|
||||||
|
files = [
|
||||||
|
{file = "pygtrie-2.5.0-py3-none-any.whl", hash = "sha256:8795cda8105493d5ae159a5bef313ff13156c5d4d72feddefacaad59f8c8ce16"},
|
||||||
|
{file = "pygtrie-2.5.0.tar.gz", hash = "sha256:203514ad826eb403dab1d2e2ddd034e0d1534bbe4dbe0213bb0593f66beba4e2"},
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pylint"
|
name = "pylint"
|
||||||
version = "2.17.4"
|
version = "2.17.4"
|
||||||
description = "python code static checker"
|
description = "python code static checker"
|
||||||
category = "dev"
|
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.7.2"
|
python-versions = ">=3.7.2"
|
||||||
files = [
|
files = [
|
||||||
@@ -262,7 +392,6 @@ testutils = ["gitpython (>3)"]
|
|||||||
name = "pytest"
|
name = "pytest"
|
||||||
version = "7.3.1"
|
version = "7.3.1"
|
||||||
description = "pytest: simple powerful testing with Python"
|
description = "pytest: simple powerful testing with Python"
|
||||||
category = "dev"
|
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.7"
|
python-versions = ">=3.7"
|
||||||
files = [
|
files = [
|
||||||
@@ -279,11 +408,93 @@ pluggy = ">=0.12,<2.0"
|
|||||||
[package.extras]
|
[package.extras]
|
||||||
testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "xmlschema"]
|
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]]
|
[[package]]
|
||||||
name = "tomlkit"
|
name = "tomlkit"
|
||||||
version = "0.11.8"
|
version = "0.11.8"
|
||||||
description = "Style preserving TOML library"
|
description = "Style preserving TOML library"
|
||||||
category = "dev"
|
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.7"
|
python-versions = ">=3.7"
|
||||||
files = [
|
files = [
|
||||||
@@ -295,7 +506,6 @@ files = [
|
|||||||
name = "typing-extensions"
|
name = "typing-extensions"
|
||||||
version = "4.6.2"
|
version = "4.6.2"
|
||||||
description = "Backported and Experimental Type Hints for Python 3.7+"
|
description = "Backported and Experimental Type Hints for Python 3.7+"
|
||||||
category = "dev"
|
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.7"
|
python-versions = ">=3.7"
|
||||||
files = [
|
files = [
|
||||||
@@ -303,11 +513,30 @@ files = [
|
|||||||
{file = "typing_extensions-4.6.2.tar.gz", hash = "sha256:06006244c70ac8ee83fa8282cb188f697b8db25bc8b4df07be1873c43897060c"},
|
{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]]
|
[[package]]
|
||||||
name = "wrapt"
|
name = "wrapt"
|
||||||
version = "1.15.0"
|
version = "1.15.0"
|
||||||
description = "Module for decorators, wrappers and monkey patching."
|
description = "Module for decorators, wrappers and monkey patching."
|
||||||
category = "dev"
|
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7"
|
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7"
|
||||||
files = [
|
files = [
|
||||||
@@ -391,4 +620,4 @@ files = [
|
|||||||
[metadata]
|
[metadata]
|
||||||
lock-version = "2.0"
|
lock-version = "2.0"
|
||||||
python-versions = "^3.11"
|
python-versions = "^3.11"
|
||||||
content-hash = "a14e3d6fa508612e47f2dab1d5068300be5735fce187ae7543b198ed57eede70"
|
content-hash = "167dc1b59d6da393661109af959693c455aa06c40c18564e16684920f259ae95"
|
||||||
|
|||||||
@@ -27,6 +27,8 @@ readme = "README.md"
|
|||||||
|
|
||||||
[tool.poetry.dependencies]
|
[tool.poetry.dependencies]
|
||||||
python = "^3.11"
|
python = "^3.11"
|
||||||
|
rich = "^13.4.1"
|
||||||
|
pygtrie = "^2.5.0"
|
||||||
|
|
||||||
[tool.poetry.scripts]
|
[tool.poetry.scripts]
|
||||||
boggler = "boggler.__main__:main"
|
boggler = "boggler.__main__:main"
|
||||||
@@ -36,6 +38,7 @@ boggler = "boggler.__main__:main"
|
|||||||
pytest = "^7.3.1"
|
pytest = "^7.3.1"
|
||||||
pylint = "^2.17.4"
|
pylint = "^2.17.4"
|
||||||
mypy = "^1.3.0"
|
mypy = "^1.3.0"
|
||||||
|
pre-commit = "^3.3.3"
|
||||||
|
|
||||||
[build-system]
|
[build-system]
|
||||||
requires = ["poetry-core"]
|
requires = ["poetry-core"]
|
||||||
|
|||||||
@@ -4,21 +4,24 @@ from boggler.boggler_utils import read_boggle_file, BadBoardFormat
|
|||||||
|
|
||||||
boards_path = Path(Path(__file__).parent.resolve())
|
boards_path = Path(Path(__file__).parent.resolve())
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(name="expected_board_4x4")
|
@pytest.fixture(name="expected_board_4x4")
|
||||||
def fixture_expected_board_4x4():
|
def fixture_expected_board_4x4():
|
||||||
return [
|
return [
|
||||||
['u', 'n', 'r', 'e'],
|
["u", "n", "r", "e"],
|
||||||
['qu', 'n', 'i', 'l'],
|
["qu", "n", "i", "l"],
|
||||||
['i', 's', 'h', 'a'],
|
["i", "s", "h", "a"],
|
||||||
['s', 'e', 'l', 'b']
|
["s", "e", "l", "b"],
|
||||||
]
|
]
|
||||||
|
|
||||||
# Boards with clean input
|
|
||||||
@pytest.fixture(name="clean_4x4_path")
|
@pytest.fixture(name="clean_4x4_path")
|
||||||
def fixture_clean_4x4_path():
|
def fixture_clean_4x4_path():
|
||||||
return Path(boards_path, "./boards/4x4_clean.board")
|
return Path(boards_path, "./boards/4x4_clean.board")
|
||||||
|
|
||||||
|
|
||||||
def test_clean_4x4_board(expected_board_4x4, clean_4x4_path):
|
def test_clean_4x4_board(expected_board_4x4, clean_4x4_path):
|
||||||
|
"""Test 4x4 board with clean input"""
|
||||||
assert expected_board_4x4 == read_boggle_file(clean_4x4_path)
|
assert expected_board_4x4 == read_boggle_file(clean_4x4_path)
|
||||||
|
|
||||||
|
|
||||||
@@ -27,7 +30,9 @@ def test_clean_4x4_board(expected_board_4x4, clean_4x4_path):
|
|||||||
def fixture_leading_whitespace_4x4_path():
|
def fixture_leading_whitespace_4x4_path():
|
||||||
return Path(boards_path, "./boards/4x4_leading_whitespace.board")
|
return Path(boards_path, "./boards/4x4_leading_whitespace.board")
|
||||||
|
|
||||||
|
|
||||||
def test_leading_whitespace_4x4_board(expected_board_4x4, leading_whitespace_4x4_path):
|
def test_leading_whitespace_4x4_board(expected_board_4x4, leading_whitespace_4x4_path):
|
||||||
|
"""Test 4x4 board with leading whitepace in rows"""
|
||||||
assert expected_board_4x4 == read_boggle_file(leading_whitespace_4x4_path)
|
assert expected_board_4x4 == read_boggle_file(leading_whitespace_4x4_path)
|
||||||
|
|
||||||
|
|
||||||
@@ -36,7 +41,9 @@ def test_leading_whitespace_4x4_board(expected_board_4x4, leading_whitespace_4x4
|
|||||||
def fixture_between_whitespace_4x4_path():
|
def fixture_between_whitespace_4x4_path():
|
||||||
return Path(boards_path, "./boards/4x4_between_whitespace.board")
|
return Path(boards_path, "./boards/4x4_between_whitespace.board")
|
||||||
|
|
||||||
|
|
||||||
def test_between_whitespace_4x4_board(expected_board_4x4, between_whitespace_4x4_path):
|
def test_between_whitespace_4x4_board(expected_board_4x4, between_whitespace_4x4_path):
|
||||||
|
"""Test 4x4 board with whitespace surrounding letters in block"""
|
||||||
assert expected_board_4x4 == read_boggle_file(between_whitespace_4x4_path)
|
assert expected_board_4x4 == read_boggle_file(between_whitespace_4x4_path)
|
||||||
|
|
||||||
|
|
||||||
@@ -45,7 +52,11 @@ def test_between_whitespace_4x4_board(expected_board_4x4, between_whitespace_4x4
|
|||||||
def fixture_trailing_whitespace_4x4_path():
|
def fixture_trailing_whitespace_4x4_path():
|
||||||
return Path(boards_path, "./boards/4x4_trailing_whitespace.board")
|
return Path(boards_path, "./boards/4x4_trailing_whitespace.board")
|
||||||
|
|
||||||
def test_trailing_whitespace_4x4_board(expected_board_4x4, trailing_whitespace_4x4_path):
|
|
||||||
|
def test_trailing_whitespace_4x4_board(
|
||||||
|
expected_board_4x4, trailing_whitespace_4x4_path
|
||||||
|
):
|
||||||
|
"""Test 4x4 board with trailing whitespace in rows"""
|
||||||
assert expected_board_4x4 == read_boggle_file(trailing_whitespace_4x4_path)
|
assert expected_board_4x4 == read_boggle_file(trailing_whitespace_4x4_path)
|
||||||
|
|
||||||
|
|
||||||
@@ -54,7 +65,9 @@ def test_trailing_whitespace_4x4_board(expected_board_4x4, trailing_whitespace_4
|
|||||||
def fixture_blank_lines_4x4_path():
|
def fixture_blank_lines_4x4_path():
|
||||||
return Path(boards_path, "./boards/4x4_blank_lines.board")
|
return Path(boards_path, "./boards/4x4_blank_lines.board")
|
||||||
|
|
||||||
|
|
||||||
def test_blank_lines_4x4_board(blank_lines_4x4_path):
|
def test_blank_lines_4x4_board(blank_lines_4x4_path):
|
||||||
|
"""Test 4x4 board with blank lines in board file"""
|
||||||
with pytest.raises(BadBoardFormat, match="must contain no blank lines"):
|
with pytest.raises(BadBoardFormat, match="must contain no blank lines"):
|
||||||
read_boggle_file(blank_lines_4x4_path)
|
read_boggle_file(blank_lines_4x4_path)
|
||||||
|
|
||||||
@@ -64,6 +77,8 @@ def test_blank_lines_4x4_board(blank_lines_4x4_path):
|
|||||||
def fixture_inconsistent_width_4x4_path():
|
def fixture_inconsistent_width_4x4_path():
|
||||||
return Path(boards_path, "./boards/4x4_inconsistent_width.board")
|
return Path(boards_path, "./boards/4x4_inconsistent_width.board")
|
||||||
|
|
||||||
|
|
||||||
def test_inconsistent_width_4x4_board(inconsistent_width_4x4_path):
|
def test_inconsistent_width_4x4_board(inconsistent_width_4x4_path):
|
||||||
|
"""Test board with irregular number of blocks per row"""
|
||||||
with pytest.raises(BadBoardFormat, match="length of each row must be the same"):
|
with pytest.raises(BadBoardFormat, match="length of each row must be the same"):
|
||||||
read_boggle_file(inconsistent_width_4x4_path)
|
read_boggle_file(inconsistent_width_4x4_path)
|
||||||
|
|||||||
+994
-101
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user