Spaces:
Running
Running
UI update, wordlists logic
Browse files- battlewords.egg-info/SOURCES.txt +1 -0
- battlewords/__init__.py +1 -1
- battlewords/generator.py +15 -1
- battlewords/ui.py +154 -26
- battlewords/word_loader.py +34 -12
- battlewords/words/__init__.py +0 -0
- battlewords/words/classic.txt +800 -0
- battlewords/words/wordlist.txt +53 -58
battlewords.egg-info/SOURCES.txt
CHANGED
|
@@ -13,6 +13,7 @@ battlewords.egg-info/dependency_links.txt
|
|
| 13 |
battlewords.egg-info/requires.txt
|
| 14 |
battlewords.egg-info/top_level.txt
|
| 15 |
battlewords/words/wordlist.txt
|
|
|
|
| 16 |
tests/test_apptest.py
|
| 17 |
tests/test_generator.py
|
| 18 |
tests/test_logic.py
|
|
|
|
| 13 |
battlewords.egg-info/requires.txt
|
| 14 |
battlewords.egg-info/top_level.txt
|
| 15 |
battlewords/words/wordlist.txt
|
| 16 |
+
battlewords/words/classic.txt
|
| 17 |
tests/test_apptest.py
|
| 18 |
tests/test_generator.py
|
| 19 |
tests/test_logic.py
|
battlewords/__init__.py
CHANGED
|
@@ -1,2 +1,2 @@
|
|
| 1 |
-
__version__ = "0.1.
|
| 2 |
__all__ = ["models", "generator", "logic", "ui"]
|
|
|
|
| 1 |
+
__version__ = "0.1.2"
|
| 2 |
__all__ = ["models", "generator", "logic", "ui"]
|
battlewords/generator.py
CHANGED
|
@@ -126,4 +126,18 @@ def validate_puzzle(puzzle: Puzzle, grid_size: int = 12) -> None:
|
|
| 126 |
raise AssertionError("Radar pulse missing for last cell")
|
| 127 |
|
| 128 |
if counts[4] != 2 or counts[5] != 2 or counts[6] != 2:
|
| 129 |
-
raise AssertionError("Incorrect counts of word lengths")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 126 |
raise AssertionError("Radar pulse missing for last cell")
|
| 127 |
|
| 128 |
if counts[4] != 2 or counts[5] != 2 or counts[6] != 2:
|
| 129 |
+
raise AssertionError("Incorrect counts of word lengths")
|
| 130 |
+
|
| 131 |
+
|
| 132 |
+
def sort_word_file(filepath: str) -> List[str]:
|
| 133 |
+
"""
|
| 134 |
+
Reads a word list file, skips header/comment lines, and returns words sorted
|
| 135 |
+
by length (ascending), then alphabetically within each length group.
|
| 136 |
+
"""
|
| 137 |
+
with open(filepath, "r", encoding="utf-8") as f:
|
| 138 |
+
lines = f.readlines()
|
| 139 |
+
# Skip header/comment lines
|
| 140 |
+
words = [line.strip() for line in lines if line.strip() and not line.strip().startswith("#")]
|
| 141 |
+
# Sort by length, then alphabetically
|
| 142 |
+
sorted_words = sorted(words, key=lambda w: (len(w), w))
|
| 143 |
+
return sorted_words
|
battlewords/ui.py
CHANGED
|
@@ -5,9 +5,10 @@ from typing import Iterable, Tuple, Optional
|
|
| 5 |
import matplotlib.pyplot as plt
|
| 6 |
import streamlit as st
|
| 7 |
|
| 8 |
-
from .generator import generate_puzzle,
|
| 9 |
from .logic import build_letter_map, reveal_cell, guess_word, is_game_over, compute_tier
|
| 10 |
from .models import Coord, GameState, Puzzle
|
|
|
|
| 11 |
|
| 12 |
|
| 13 |
CoordLike = Tuple[int, int]
|
|
@@ -43,7 +44,7 @@ def inject_styles() -> None:
|
|
| 43 |
"""
|
| 44 |
<style>
|
| 45 |
/* Base grid cell visuals */
|
| 46 |
-
.bw-row { display: flex; gap:
|
| 47 |
.bw-cell {
|
| 48 |
width: 100%;
|
| 49 |
aspect-ratio: 1 / 1;
|
|
@@ -51,16 +52,21 @@ def inject_styles() -> None:
|
|
| 51 |
align-items: center;
|
| 52 |
justify-content: center;
|
| 53 |
border: 1px solid #3a3a3a;
|
| 54 |
-
border-radius:
|
| 55 |
font-weight: 700;
|
| 56 |
user-select: none;
|
| 57 |
padding: 0.25rem 0.75rem;
|
| 58 |
min-height: 2.5rem;
|
| 59 |
-
transition: background 0.2s ease;
|
|
|
|
|
|
|
| 60 |
}
|
| 61 |
-
|
| 62 |
-
.bw-cell.
|
| 63 |
-
|
|
|
|
|
|
|
|
|
|
| 64 |
|
| 65 |
/* Final score style */
|
| 66 |
.bw-final-score { color: #1ca41c !important; font-weight: 800; }
|
|
@@ -69,28 +75,52 @@ def inject_styles() -> None:
|
|
| 69 |
div[data-testid="stButton"] button {
|
| 70 |
width: 100%;
|
| 71 |
aspect-ratio: 1 / 1;
|
| 72 |
-
border-radius:
|
| 73 |
-
border: 1px solid #
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 74 |
}
|
| 75 |
|
| 76 |
/* Ensure grid cell columns expand equally for both buttons and revealed cells */
|
| 77 |
div[data-testid="column"], .st-emotion-cache-zh2fnc {
|
| 78 |
width: auto !important;
|
| 79 |
flex: 1 1 auto !important;
|
| 80 |
-
min-width:
|
| 81 |
max-width: 100% !important;
|
| 82 |
}
|
| 83 |
-
.st-emotion-cache-1permvm {
|
| 84 |
-
gap:0.
|
| 85 |
}
|
|
|
|
| 86 |
/* Ensure grid rows generated via st.columns do not wrap and can scroll horizontally. */
|
| 87 |
.bw-grid-row-anchor + div[data-testid="stHorizontalBlock"] {
|
| 88 |
flex-wrap: nowrap !important;
|
| 89 |
-
overflow-x: auto !important;
|
|
|
|
| 90 |
}
|
| 91 |
.bw-grid-row-anchor + div[data-testid="stHorizontalBlock"] > div[data-testid="column"] {
|
| 92 |
flex: 0 0 auto !important;
|
| 93 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 94 |
|
| 95 |
/* Mobile styles */
|
| 96 |
@media (max-width: 640px) {
|
|
@@ -111,12 +141,46 @@ def inject_styles() -> None:
|
|
| 111 |
.bw-grid-row-anchor + div[data-testid="stHorizontalBlock"] {
|
| 112 |
flex-wrap: nowrap !important;
|
| 113 |
overflow-x: auto !important;
|
|
|
|
| 114 |
}
|
| 115 |
.st-emotion-cache-17i4tbh {
|
| 116 |
min-width: calc(8.33333% - 1rem);
|
| 117 |
}
|
| 118 |
-
|
| 119 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 120 |
</style>
|
| 121 |
""",
|
| 122 |
unsafe_allow_html=True,
|
|
@@ -127,7 +191,12 @@ def _init_session() -> None:
|
|
| 127 |
if "initialized" in st.session_state and st.session_state.initialized:
|
| 128 |
return
|
| 129 |
|
| 130 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 131 |
puzzle = generate_puzzle(grid_size=12, words_by_len=words)
|
| 132 |
|
| 133 |
st.session_state.puzzle = puzzle
|
|
@@ -143,7 +212,11 @@ def _init_session() -> None:
|
|
| 143 |
|
| 144 |
|
| 145 |
def _new_game() -> None:
|
|
|
|
|
|
|
| 146 |
st.session_state.clear()
|
|
|
|
|
|
|
| 147 |
_init_session()
|
| 148 |
|
| 149 |
|
|
@@ -189,6 +262,30 @@ def _render_sidebar():
|
|
| 189 |
"- Radar pulses show the last letter position of each hidden word.\n"
|
| 190 |
"- After each reveal, you may submit one word guess below.\n"
|
| 191 |
"- Scoring: length + unrevealed letters of that word at guess time.")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 192 |
|
| 193 |
|
| 194 |
def _render_radar(puzzle: Puzzle, size: int):
|
|
@@ -236,29 +333,32 @@ def _render_grid(state: GameState, letter_map):
|
|
| 236 |
min-height: 32px !important;
|
| 237 |
padding: 0 !important;
|
| 238 |
margin: 0 !important;
|
| 239 |
-
border: 1px solid #
|
| 240 |
-
|
|
|
|
|
|
|
| 241 |
font-weight: bold;
|
| 242 |
font-size: 1rem;
|
| 243 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
| 244 |
</style>
|
| 245 |
""",
|
| 246 |
unsafe_allow_html=True,
|
| 247 |
)
|
| 248 |
|
| 249 |
grid_container = st.container()
|
| 250 |
-
with grid_container:
|
| 251 |
for r in range(size):
|
| 252 |
-
# Anchor to style the following st.columns row container
|
| 253 |
st.markdown('<div class="bw-grid-row-anchor"></div>', unsafe_allow_html=True)
|
| 254 |
cols = st.columns(size, gap="small")
|
| 255 |
for c in range(size):
|
| 256 |
coord = Coord(r, c)
|
| 257 |
revealed = coord in state.revealed
|
| 258 |
-
# Get label if revealed
|
| 259 |
label = letter_map.get(coord, " ") if revealed else " "
|
| 260 |
|
| 261 |
-
# If this coord belongs to a completed (guessed) word
|
| 262 |
is_completed_cell = False
|
| 263 |
if revealed:
|
| 264 |
for w in state.puzzle.words:
|
|
@@ -270,24 +370,27 @@ def _render_grid(state: GameState, letter_map):
|
|
| 270 |
tooltip = f"({r+1},{c+1})"
|
| 271 |
|
| 272 |
if is_completed_cell:
|
| 273 |
-
# Render a styled non-button cell
|
| 274 |
safe_label = (label or " ")
|
| 275 |
cols[c].markdown(
|
| 276 |
f'<div class="bw-cell bw-cell-complete" title="{tooltip}">{safe_label}</div>',
|
| 277 |
unsafe_allow_html=True,
|
| 278 |
)
|
| 279 |
elif revealed:
|
| 280 |
-
#
|
| 281 |
safe_label = (label or " ")
|
|
|
|
|
|
|
|
|
|
| 282 |
cols[c].markdown(
|
| 283 |
-
f'<div class="bw-cell
|
| 284 |
unsafe_allow_html=True,
|
| 285 |
)
|
| 286 |
else:
|
| 287 |
# Unrevealed: render a button to allow click/reveal with tooltip
|
| 288 |
if cols[c].button(" ", key=key, help=tooltip):
|
| 289 |
clicked = coord
|
| 290 |
-
|
| 291 |
if clicked is not None:
|
| 292 |
reveal_cell(state, letter_map, clicked)
|
| 293 |
st.session_state.letter_map = build_letter_map(st.session_state.puzzle)
|
|
@@ -309,6 +412,11 @@ def _render_score_panel(state: GameState):
|
|
| 309 |
st.metric("Score", state.score)
|
| 310 |
with col2:
|
| 311 |
st.markdown(f"Last action: {state.last_action}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 312 |
|
| 313 |
|
| 314 |
def _render_game_over(state: GameState):
|
|
@@ -329,6 +437,26 @@ def _render_game_over(state: GameState):
|
|
| 329 |
st.stop()
|
| 330 |
|
| 331 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 332 |
def run_app():
|
| 333 |
_init_session()
|
| 334 |
_render_header()
|
|
@@ -338,7 +466,7 @@ def run_app():
|
|
| 338 |
|
| 339 |
# Anchor to target the main two-column layout for mobile reversal
|
| 340 |
st.markdown('<div id="bw-main-anchor"></div>', unsafe_allow_html=True)
|
| 341 |
-
left, right = st.columns([
|
| 342 |
with left:
|
| 343 |
_render_grid(state, st.session_state.letter_map)
|
| 344 |
with right:
|
|
|
|
| 5 |
import matplotlib.pyplot as plt
|
| 6 |
import streamlit as st
|
| 7 |
|
| 8 |
+
from .generator import generate_puzzle, sort_word_file
|
| 9 |
from .logic import build_letter_map, reveal_cell, guess_word, is_game_over, compute_tier
|
| 10 |
from .models import Coord, GameState, Puzzle
|
| 11 |
+
from .word_loader import get_wordlist_files, load_word_list # use loader directly
|
| 12 |
|
| 13 |
|
| 14 |
CoordLike = Tuple[int, int]
|
|
|
|
| 44 |
"""
|
| 45 |
<style>
|
| 46 |
/* Base grid cell visuals */
|
| 47 |
+
.bw-row { display: flex; gap: 0.1rem; flex-wrap: nowrap; }
|
| 48 |
.bw-cell {
|
| 49 |
width: 100%;
|
| 50 |
aspect-ratio: 1 / 1;
|
|
|
|
| 52 |
align-items: center;
|
| 53 |
justify-content: center;
|
| 54 |
border: 1px solid #3a3a3a;
|
| 55 |
+
border-radius: 0;
|
| 56 |
font-weight: 700;
|
| 57 |
user-select: none;
|
| 58 |
padding: 0.25rem 0.75rem;
|
| 59 |
min-height: 2.5rem;
|
| 60 |
+
transition: background 0.2s ease, color 0.2s ease, border-color 0.2s ease;
|
| 61 |
+
background: #1d64c8; /* Base cell color */
|
| 62 |
+
color: #ffffff; /* Base text color for contrast */
|
| 63 |
}
|
| 64 |
+
/* Found letter cells */
|
| 65 |
+
.bw-cell.letter { background: #d7faff; color: #050057; }
|
| 66 |
+
/* Optional empty state if ever used */
|
| 67 |
+
.bw-cell.empty { background: #3a3a3a; color: #ffffff; }
|
| 68 |
+
/* Completed word cells */
|
| 69 |
+
.bw-cell.bw-cell-complete { background: #050057 !important; color: #d7faff !important; }
|
| 70 |
|
| 71 |
/* Final score style */
|
| 72 |
.bw-final-score { color: #1ca41c !important; font-weight: 800; }
|
|
|
|
| 75 |
div[data-testid="stButton"] button {
|
| 76 |
width: 100%;
|
| 77 |
aspect-ratio: 1 / 1;
|
| 78 |
+
border-radius: 0;
|
| 79 |
+
border: 1px solid #1d64c8;
|
| 80 |
+
background: #1d64c8;
|
| 81 |
+
color: #ffffff;
|
| 82 |
+
font-weight: 700;
|
| 83 |
+
padding: 0.25rem 0.75rem;
|
| 84 |
+
min-height: 2.5rem;
|
| 85 |
}
|
| 86 |
|
| 87 |
/* Ensure grid cell columns expand equally for both buttons and revealed cells */
|
| 88 |
div[data-testid="column"], .st-emotion-cache-zh2fnc {
|
| 89 |
width: auto !important;
|
| 90 |
flex: 1 1 auto !important;
|
| 91 |
+
min-width: 100% !important;
|
| 92 |
max-width: 100% !important;
|
| 93 |
}
|
| 94 |
+
.st-emotion-cache-1permvm, .st-emotion-cache-1n6tfoc {
|
| 95 |
+
gap:0.1rem !important;
|
| 96 |
}
|
| 97 |
+
|
| 98 |
/* Ensure grid rows generated via st.columns do not wrap and can scroll horizontally. */
|
| 99 |
.bw-grid-row-anchor + div[data-testid="stHorizontalBlock"] {
|
| 100 |
flex-wrap: nowrap !important;
|
| 101 |
+
overflow-x: auto !important;
|
| 102 |
+
margin: 2px 0 !important; /* Reduce gap between rows */
|
| 103 |
}
|
| 104 |
.bw-grid-row-anchor + div[data-testid="stHorizontalBlock"] > div[data-testid="column"] {
|
| 105 |
flex: 0 0 auto !important;
|
| 106 |
}
|
| 107 |
+
.bw-grid-row-anchor { height: 0; margin: 0; padding: 0; }
|
| 108 |
+
.st-emotion-cache-1n6tfoc {
|
| 109 |
+
background: linear-gradient(-45deg, #a1a1a1, #ffffff, #a1a1a1, #666666);;
|
| 110 |
+
gap: 0.1rem !important;
|
| 111 |
+
color: white;
|
| 112 |
+
# border: 10px solid;
|
| 113 |
+
# border-image: linear-gradient(-45deg, #a1a1a1, #ffffff, #a1a1a1, #666666) 1;
|
| 114 |
+
border-radius:15px;
|
| 115 |
+
padding: 10px;
|
| 116 |
+
}
|
| 117 |
+
.st-emotion-cache-1n6tfoc::before {
|
| 118 |
+
content: '';
|
| 119 |
+
position: absolute;
|
| 120 |
+
top: 0; left: 0; right: 0; bottom: 0;
|
| 121 |
+
border-radius: 10px;
|
| 122 |
+
margin: 5px; /* Border thickness */
|
| 123 |
+
}
|
| 124 |
|
| 125 |
/* Mobile styles */
|
| 126 |
@media (max-width: 640px) {
|
|
|
|
| 141 |
.bw-grid-row-anchor + div[data-testid="stHorizontalBlock"] {
|
| 142 |
flex-wrap: nowrap !important;
|
| 143 |
overflow-x: auto !important;
|
| 144 |
+
margin: 2px 0 !important; /* Keep tighter row gap on mobile */
|
| 145 |
}
|
| 146 |
.st-emotion-cache-17i4tbh {
|
| 147 |
min-width: calc(8.33333% - 1rem);
|
| 148 |
}
|
|
|
|
| 149 |
}
|
| 150 |
+
|
| 151 |
+
.metal-border {
|
| 152 |
+
position: relative;
|
| 153 |
+
padding: 20px;
|
| 154 |
+
background: #333;
|
| 155 |
+
color: white;
|
| 156 |
+
border: 4px solid;
|
| 157 |
+
border-image: linear-gradient(45deg, #a1a1a1, #ffffff, #a1a1a1, #666666) 1;
|
| 158 |
+
border-radius: 8px;
|
| 159 |
+
}
|
| 160 |
+
|
| 161 |
+
.shiny-border {
|
| 162 |
+
position: relative;
|
| 163 |
+
padding: 20px;
|
| 164 |
+
background: #333;
|
| 165 |
+
color: white;
|
| 166 |
+
border-radius: 8px;
|
| 167 |
+
overflow: hidden;
|
| 168 |
+
}
|
| 169 |
+
|
| 170 |
+
.shiny-border::before {
|
| 171 |
+
content: '';
|
| 172 |
+
position: absolute;
|
| 173 |
+
top: 0;
|
| 174 |
+
left: -100%;
|
| 175 |
+
width: 100%;
|
| 176 |
+
height: 100%;
|
| 177 |
+
background: linear-gradient(90deg, transparent, rgba(255,255,255,0.4), transparent);
|
| 178 |
+
transition: left 0.5s;
|
| 179 |
+
}
|
| 180 |
+
|
| 181 |
+
.shiny-border:hover::before {
|
| 182 |
+
left: 100%;
|
| 183 |
+
}
|
| 184 |
</style>
|
| 185 |
""",
|
| 186 |
unsafe_allow_html=True,
|
|
|
|
| 191 |
if "initialized" in st.session_state and st.session_state.initialized:
|
| 192 |
return
|
| 193 |
|
| 194 |
+
# Ensure a default selection exists before creating the puzzle
|
| 195 |
+
files = get_wordlist_files()
|
| 196 |
+
if "selected_wordlist" not in st.session_state and files:
|
| 197 |
+
st.session_state.selected_wordlist = "wordlist.txt"
|
| 198 |
+
|
| 199 |
+
words = load_word_list(st.session_state.get("selected_wordlist"))
|
| 200 |
puzzle = generate_puzzle(grid_size=12, words_by_len=words)
|
| 201 |
|
| 202 |
st.session_state.puzzle = puzzle
|
|
|
|
| 212 |
|
| 213 |
|
| 214 |
def _new_game() -> None:
|
| 215 |
+
# Preserve selected wordlist across resets
|
| 216 |
+
selected = st.session_state.get("selected_wordlist")
|
| 217 |
st.session_state.clear()
|
| 218 |
+
if selected:
|
| 219 |
+
st.session_state.selected_wordlist = selected
|
| 220 |
_init_session()
|
| 221 |
|
| 222 |
|
|
|
|
| 262 |
"- Radar pulses show the last letter position of each hidden word.\n"
|
| 263 |
"- After each reveal, you may submit one word guess below.\n"
|
| 264 |
"- Scoring: length + unrevealed letters of that word at guess time.")
|
| 265 |
+
|
| 266 |
+
st.header("Wordlist Controls")
|
| 267 |
+
wordlist_files = get_wordlist_files()
|
| 268 |
+
|
| 269 |
+
if wordlist_files:
|
| 270 |
+
# Ensure current selection is valid
|
| 271 |
+
if st.session_state.get("selected_wordlist") not in wordlist_files:
|
| 272 |
+
st.session_state.selected_wordlist = wordlist_files[0]
|
| 273 |
+
|
| 274 |
+
# Use filenames as options, show without extension
|
| 275 |
+
current_index = wordlist_files.index(st.session_state.selected_wordlist)
|
| 276 |
+
st.selectbox(
|
| 277 |
+
"Select list",
|
| 278 |
+
options=wordlist_files,
|
| 279 |
+
index=current_index,
|
| 280 |
+
format_func=lambda f: f.rsplit(".", 1)[0],
|
| 281 |
+
key="selected_wordlist",
|
| 282 |
+
on_change=_new_game, # immediately start a new game with the selected list
|
| 283 |
+
)
|
| 284 |
+
|
| 285 |
+
if st.button("Sort Wordlist"):
|
| 286 |
+
_sort_wordlist(st.session_state.selected_wordlist)
|
| 287 |
+
else:
|
| 288 |
+
st.info("No word lists found in words/ directory. Using built-in fallback.")
|
| 289 |
|
| 290 |
|
| 291 |
def _render_radar(puzzle: Puzzle, size: int):
|
|
|
|
| 333 |
min-height: 32px !important;
|
| 334 |
padding: 0 !important;
|
| 335 |
margin: 0 !important;
|
| 336 |
+
border: 1px solid #1d64c8 !important;
|
| 337 |
+
border-radius: 0 !important;
|
| 338 |
+
background: #1d64c8 !important;
|
| 339 |
+
color: #ffffff !important;
|
| 340 |
font-weight: bold;
|
| 341 |
font-size: 1rem;
|
| 342 |
}
|
| 343 |
+
/* Further tighten vertical spacing between rows inside the grid container */
|
| 344 |
+
.bw-grid-row-anchor + div[data-testid="stHorizontalBlock"] {
|
| 345 |
+
margin: 2px 0 !important;
|
| 346 |
+
}
|
| 347 |
</style>
|
| 348 |
""",
|
| 349 |
unsafe_allow_html=True,
|
| 350 |
)
|
| 351 |
|
| 352 |
grid_container = st.container()
|
| 353 |
+
with grid_container:
|
| 354 |
for r in range(size):
|
|
|
|
| 355 |
st.markdown('<div class="bw-grid-row-anchor"></div>', unsafe_allow_html=True)
|
| 356 |
cols = st.columns(size, gap="small")
|
| 357 |
for c in range(size):
|
| 358 |
coord = Coord(r, c)
|
| 359 |
revealed = coord in state.revealed
|
|
|
|
| 360 |
label = letter_map.get(coord, " ") if revealed else " "
|
| 361 |
|
|
|
|
| 362 |
is_completed_cell = False
|
| 363 |
if revealed:
|
| 364 |
for w in state.puzzle.words:
|
|
|
|
| 370 |
tooltip = f"({r+1},{c+1})"
|
| 371 |
|
| 372 |
if is_completed_cell:
|
| 373 |
+
# Render a styled non-button cell for a completed word with native browser tooltip
|
| 374 |
safe_label = (label or " ")
|
| 375 |
cols[c].markdown(
|
| 376 |
f'<div class="bw-cell bw-cell-complete" title="{tooltip}">{safe_label}</div>',
|
| 377 |
unsafe_allow_html=True,
|
| 378 |
)
|
| 379 |
elif revealed:
|
| 380 |
+
# Use 'letter' when a letter exists, otherwise 'empty'
|
| 381 |
safe_label = (label or " ")
|
| 382 |
+
has_letter = safe_label.strip() != ""
|
| 383 |
+
cell_class = "letter" if has_letter else "empty"
|
| 384 |
+
display = safe_label if has_letter else " "
|
| 385 |
cols[c].markdown(
|
| 386 |
+
f'<div class="bw-cell {cell_class}" title="{tooltip}">{display}</div>',
|
| 387 |
unsafe_allow_html=True,
|
| 388 |
)
|
| 389 |
else:
|
| 390 |
# Unrevealed: render a button to allow click/reveal with tooltip
|
| 391 |
if cols[c].button(" ", key=key, help=tooltip):
|
| 392 |
clicked = coord
|
| 393 |
+
|
| 394 |
if clicked is not None:
|
| 395 |
reveal_cell(state, letter_map, clicked)
|
| 396 |
st.session_state.letter_map = build_letter_map(st.session_state.puzzle)
|
|
|
|
| 412 |
st.metric("Score", state.score)
|
| 413 |
with col2:
|
| 414 |
st.markdown(f"Last action: {state.last_action}")
|
| 415 |
+
with st.expander("Game summary", expanded=True):
|
| 416 |
+
for w in state.puzzle.words:
|
| 417 |
+
pts = state.points_by_word.get(w.text, 0)
|
| 418 |
+
st.markdown(f"- {w.text} ({len(w.text)}): +{pts} points")
|
| 419 |
+
st.markdown(f"**Total**: {state.score}")
|
| 420 |
|
| 421 |
|
| 422 |
def _render_game_over(state: GameState):
|
|
|
|
| 437 |
st.stop()
|
| 438 |
|
| 439 |
|
| 440 |
+
def _sort_wordlist(filename):
|
| 441 |
+
import os
|
| 442 |
+
import time # Add this import
|
| 443 |
+
|
| 444 |
+
WORDS_DIR = os.path.join(os.path.dirname(__file__), "words")
|
| 445 |
+
filepath = os.path.join(WORDS_DIR, filename)
|
| 446 |
+
sorted_words = sort_word_file(filepath)
|
| 447 |
+
# Optionally, write sorted words back to file
|
| 448 |
+
with open(filepath, "w", encoding="utf-8") as f:
|
| 449 |
+
# Re-add header if needed
|
| 450 |
+
f.write("# Optional: place a large A–Z word list here (one word per line).\n")
|
| 451 |
+
f.write("# The app falls back to built-in pools if fewer than 500 words per length are found.\n")
|
| 452 |
+
for word in sorted_words:
|
| 453 |
+
f.write(f"{word}\n")
|
| 454 |
+
# Show a message in Streamlit
|
| 455 |
+
st.success(f"{filename} sorted by length and alphabetically. Starting new game in 5 seconds...")
|
| 456 |
+
time.sleep(5) # 5 second delay before starting new game
|
| 457 |
+
_new_game()
|
| 458 |
+
|
| 459 |
+
|
| 460 |
def run_app():
|
| 461 |
_init_session()
|
| 462 |
_render_header()
|
|
|
|
| 466 |
|
| 467 |
# Anchor to target the main two-column layout for mobile reversal
|
| 468 |
st.markdown('<div id="bw-main-anchor"></div>', unsafe_allow_html=True)
|
| 469 |
+
left, right = st.columns([2, 2], gap="medium")
|
| 470 |
with left:
|
| 471 |
_render_grid(state, st.session_state.letter_map)
|
| 472 |
with right:
|
battlewords/word_loader.py
CHANGED
|
@@ -1,7 +1,8 @@
|
|
| 1 |
from __future__ import annotations
|
| 2 |
|
| 3 |
import re
|
| 4 |
-
|
|
|
|
| 5 |
|
| 6 |
import streamlit as st
|
| 7 |
from importlib import resources
|
|
@@ -21,15 +22,23 @@ FALLBACK_WORDS: Dict[int, List[str]] = {
|
|
| 21 |
],
|
| 22 |
}
|
| 23 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 24 |
|
| 25 |
@st.cache_data(show_spinner=False)
|
| 26 |
-
def load_word_list() -> Dict[int, List[str]]:
|
| 27 |
"""
|
| 28 |
-
Load
|
| 29 |
-
|
|
|
|
|
|
|
| 30 |
|
| 31 |
If fewer than 500 entries exist for any required length, fall back to built-ins
|
| 32 |
-
for that length (per specs).
|
| 33 |
"""
|
| 34 |
words_by_len: Dict[int, List[str]] = {4: [], 5: [], 6: []}
|
| 35 |
used_source = "fallback"
|
|
@@ -37,14 +46,27 @@ def load_word_list() -> Dict[int, List[str]]:
|
|
| 37 |
def _finalize(wbl: Dict[int, List[str]], source: str) -> Dict[int, List[str]]:
|
| 38 |
try:
|
| 39 |
st.session_state.wordlist_source = source
|
|
|
|
| 40 |
st.session_state.word_counts = {k: len(v) for k, v in wbl.items()}
|
| 41 |
except Exception:
|
| 42 |
pass
|
| 43 |
return wbl
|
| 44 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 45 |
try:
|
| 46 |
-
|
| 47 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 48 |
|
| 49 |
seen = {4: set(), 5: set(), 6: set()}
|
| 50 |
for raw in text.splitlines():
|
|
@@ -62,17 +84,17 @@ def load_word_list() -> Dict[int, List[str]]:
|
|
| 62 |
seen[L].add(word)
|
| 63 |
|
| 64 |
counts = {k: len(v) for k, v in words_by_len.items()}
|
| 65 |
-
if all(counts[k] >=
|
| 66 |
used_source = "file"
|
| 67 |
return _finalize(words_by_len, used_source)
|
| 68 |
|
| 69 |
# Per spec: fallback for any length below threshold
|
| 70 |
mixed: Dict[int, List[str]] = {
|
| 71 |
-
4: words_by_len[4] if counts[4] >=
|
| 72 |
-
5: words_by_len[5] if counts[5] >=
|
| 73 |
-
6: words_by_len[6] if counts[6] >=
|
| 74 |
}
|
| 75 |
-
used_source = "file+fallback" if any(counts[k] >=
|
| 76 |
return _finalize(mixed, used_source)
|
| 77 |
|
| 78 |
except Exception:
|
|
|
|
| 1 |
from __future__ import annotations
|
| 2 |
|
| 3 |
import re
|
| 4 |
+
import os
|
| 5 |
+
from typing import Dict, List, Optional
|
| 6 |
|
| 7 |
import streamlit as st
|
| 8 |
from importlib import resources
|
|
|
|
| 22 |
],
|
| 23 |
}
|
| 24 |
|
| 25 |
+
def get_wordlist_files() -> list[str]:
|
| 26 |
+
words_dir = os.path.join(os.path.dirname(__file__), "words")
|
| 27 |
+
if not os.path.isdir(words_dir):
|
| 28 |
+
return []
|
| 29 |
+
files = [f for f in os.listdir(words_dir) if f.lower().endswith(".txt")]
|
| 30 |
+
return sorted(files)
|
| 31 |
|
| 32 |
@st.cache_data(show_spinner=False)
|
| 33 |
+
def load_word_list(selected_file: Optional[str] = None) -> Dict[int, List[str]]:
|
| 34 |
"""
|
| 35 |
+
Load a word list, filter to uppercase A–Z, lengths in {4,5,6}, and dedupe while preserving order.
|
| 36 |
+
|
| 37 |
+
If `selected_file` is provided, load battlewords/words/<selected_file>.
|
| 38 |
+
Otherwise, try packaged resource battlewords/words/wordlist.txt.
|
| 39 |
|
| 40 |
If fewer than 500 entries exist for any required length, fall back to built-ins
|
| 41 |
+
for that length (per specs).
|
| 42 |
"""
|
| 43 |
words_by_len: Dict[int, List[str]] = {4: [], 5: [], 6: []}
|
| 44 |
used_source = "fallback"
|
|
|
|
| 46 |
def _finalize(wbl: Dict[int, List[str]], source: str) -> Dict[int, List[str]]:
|
| 47 |
try:
|
| 48 |
st.session_state.wordlist_source = source
|
| 49 |
+
st.session_state.wordlist_selected = selected_file or "wordlist.txt"
|
| 50 |
st.session_state.word_counts = {k: len(v) for k, v in wbl.items()}
|
| 51 |
except Exception:
|
| 52 |
pass
|
| 53 |
return wbl
|
| 54 |
|
| 55 |
+
def _read_text_from_disk(fname: str) -> str:
|
| 56 |
+
words_dir = os.path.join(os.path.dirname(__file__), "words")
|
| 57 |
+
path = os.path.join(words_dir, fname)
|
| 58 |
+
with open(path, "r", encoding="utf-8") as f:
|
| 59 |
+
return f.read()
|
| 60 |
+
|
| 61 |
try:
|
| 62 |
+
text: Optional[str] = None
|
| 63 |
+
|
| 64 |
+
if selected_file:
|
| 65 |
+
# Prefer explicit selection from words/ directory.
|
| 66 |
+
text = _read_text_from_disk(selected_file)
|
| 67 |
+
else:
|
| 68 |
+
# Fallback to packaged default wordlist.txt
|
| 69 |
+
text = resources.files("battlewords.words").joinpath("wordlist.txt").read_text(encoding="utf-8")
|
| 70 |
|
| 71 |
seen = {4: set(), 5: set(), 6: set()}
|
| 72 |
for raw in text.splitlines():
|
|
|
|
| 84 |
seen[L].add(word)
|
| 85 |
|
| 86 |
counts = {k: len(v) for k, v in words_by_len.items()}
|
| 87 |
+
if all(counts[k] >= 250 for k in (4, 5, 6)):
|
| 88 |
used_source = "file"
|
| 89 |
return _finalize(words_by_len, used_source)
|
| 90 |
|
| 91 |
# Per spec: fallback for any length below threshold
|
| 92 |
mixed: Dict[int, List[str]] = {
|
| 93 |
+
4: words_by_len[4] if counts[4] >= 250 else FALLBACK_WORDS[4],
|
| 94 |
+
5: words_by_len[5] if counts[5] >= 250 else FALLBACK_WORDS[5],
|
| 95 |
+
6: words_by_len[6] if counts[6] >= 250 else FALLBACK_WORDS[6],
|
| 96 |
}
|
| 97 |
+
used_source = "file+fallback" if any(counts[k] >= 250 for k in (4, 5, 6)) else "fallback"
|
| 98 |
return _finalize(mixed, used_source)
|
| 99 |
|
| 100 |
except Exception:
|
battlewords/words/__init__.py
ADDED
|
File without changes
|
battlewords/words/classic.txt
ADDED
|
@@ -0,0 +1,800 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Optional: place a large A–Z word list here (one word per line).
|
| 2 |
+
# The app falls back to built-in pools if fewer than 500 words per length are found.
|
| 3 |
+
ABLE
|
| 4 |
+
ACID
|
| 5 |
+
ACRE
|
| 6 |
+
ALSO
|
| 7 |
+
AREA
|
| 8 |
+
ARMY
|
| 9 |
+
AUNT
|
| 10 |
+
BAIT
|
| 11 |
+
BANG
|
| 12 |
+
BANK
|
| 13 |
+
BARE
|
| 14 |
+
BASE
|
| 15 |
+
BEAM
|
| 16 |
+
BEAR
|
| 17 |
+
BEAT
|
| 18 |
+
BEET
|
| 19 |
+
BELT
|
| 20 |
+
BENT
|
| 21 |
+
BILL
|
| 22 |
+
BIND
|
| 23 |
+
BOND
|
| 24 |
+
BORE
|
| 25 |
+
BORN
|
| 26 |
+
BOWL
|
| 27 |
+
BULL
|
| 28 |
+
BUMP
|
| 29 |
+
BURN
|
| 30 |
+
BUSY
|
| 31 |
+
CAGE
|
| 32 |
+
CALF
|
| 33 |
+
CALM
|
| 34 |
+
CANE
|
| 35 |
+
CARE
|
| 36 |
+
CASE
|
| 37 |
+
CASH
|
| 38 |
+
CAVE
|
| 39 |
+
CHEW
|
| 40 |
+
CHOP
|
| 41 |
+
CLAY
|
| 42 |
+
CLUB
|
| 43 |
+
COAL
|
| 44 |
+
COIN
|
| 45 |
+
COPY
|
| 46 |
+
CORN
|
| 47 |
+
COST
|
| 48 |
+
COST
|
| 49 |
+
CUTE
|
| 50 |
+
DASH
|
| 51 |
+
DATE
|
| 52 |
+
DAWN
|
| 53 |
+
DEAD
|
| 54 |
+
DEAF
|
| 55 |
+
DEAL
|
| 56 |
+
DEBT
|
| 57 |
+
DECK
|
| 58 |
+
DEED
|
| 59 |
+
DEER
|
| 60 |
+
DESK
|
| 61 |
+
DIME
|
| 62 |
+
DIRT
|
| 63 |
+
DIVE
|
| 64 |
+
DRAG
|
| 65 |
+
DRAW
|
| 66 |
+
DRUG
|
| 67 |
+
DRUM
|
| 68 |
+
DUCK
|
| 69 |
+
DUTY
|
| 70 |
+
EARN
|
| 71 |
+
EAST
|
| 72 |
+
EASY
|
| 73 |
+
ECHO
|
| 74 |
+
EDGE
|
| 75 |
+
ELSE
|
| 76 |
+
FACT
|
| 77 |
+
FAIR
|
| 78 |
+
FAME
|
| 79 |
+
FARE
|
| 80 |
+
FATE
|
| 81 |
+
FEAR
|
| 82 |
+
FEET
|
| 83 |
+
FILE
|
| 84 |
+
FILM
|
| 85 |
+
FOND
|
| 86 |
+
FOOL
|
| 87 |
+
FORD
|
| 88 |
+
FORK
|
| 89 |
+
FORM
|
| 90 |
+
FORT
|
| 91 |
+
FOUL
|
| 92 |
+
FROG
|
| 93 |
+
GAIN
|
| 94 |
+
GANG
|
| 95 |
+
GOAT
|
| 96 |
+
GOLF
|
| 97 |
+
GOWN
|
| 98 |
+
GRAY
|
| 99 |
+
GROW
|
| 100 |
+
GULF
|
| 101 |
+
HAIR
|
| 102 |
+
HARM
|
| 103 |
+
HATE
|
| 104 |
+
HEAP
|
| 105 |
+
HEAT
|
| 106 |
+
HEEL
|
| 107 |
+
HIGH
|
| 108 |
+
HIKE
|
| 109 |
+
HOLE
|
| 110 |
+
HOLY
|
| 111 |
+
HOOK
|
| 112 |
+
HORN
|
| 113 |
+
HOSE
|
| 114 |
+
HOUR
|
| 115 |
+
HOWL
|
| 116 |
+
HUGE
|
| 117 |
+
HUNT
|
| 118 |
+
HURT
|
| 119 |
+
INCH
|
| 120 |
+
IRON
|
| 121 |
+
JAIL
|
| 122 |
+
JOIN
|
| 123 |
+
JOKE
|
| 124 |
+
KICK
|
| 125 |
+
KITE
|
| 126 |
+
KNEE
|
| 127 |
+
KNOT
|
| 128 |
+
KNOW
|
| 129 |
+
LACE
|
| 130 |
+
LACK
|
| 131 |
+
LAKE
|
| 132 |
+
LAMB
|
| 133 |
+
LAMP
|
| 134 |
+
LAWN
|
| 135 |
+
LAZY
|
| 136 |
+
LEAD
|
| 137 |
+
LEAF
|
| 138 |
+
LEAK
|
| 139 |
+
LEAN
|
| 140 |
+
LESS
|
| 141 |
+
LIFE
|
| 142 |
+
LIFT
|
| 143 |
+
LIMB
|
| 144 |
+
LIMP
|
| 145 |
+
LION
|
| 146 |
+
LOOP
|
| 147 |
+
LOUD
|
| 148 |
+
LUMP
|
| 149 |
+
LUNG
|
| 150 |
+
MAID
|
| 151 |
+
MAIL
|
| 152 |
+
MARK
|
| 153 |
+
MASK
|
| 154 |
+
MATE
|
| 155 |
+
MEAL
|
| 156 |
+
MEAN
|
| 157 |
+
MEET
|
| 158 |
+
MELT
|
| 159 |
+
MEND
|
| 160 |
+
MILD
|
| 161 |
+
MILE
|
| 162 |
+
MILL
|
| 163 |
+
MINT
|
| 164 |
+
MISS
|
| 165 |
+
MIST
|
| 166 |
+
MOOD
|
| 167 |
+
MOON
|
| 168 |
+
MOSS
|
| 169 |
+
NAIL
|
| 170 |
+
NAVY
|
| 171 |
+
NEAT
|
| 172 |
+
NONE
|
| 173 |
+
NOTE
|
| 174 |
+
OMIT
|
| 175 |
+
ONCE
|
| 176 |
+
ONLY
|
| 177 |
+
OVEN
|
| 178 |
+
OVER
|
| 179 |
+
PAIL
|
| 180 |
+
PAIN
|
| 181 |
+
PAIR
|
| 182 |
+
PALM
|
| 183 |
+
PART
|
| 184 |
+
PATH
|
| 185 |
+
PEEK
|
| 186 |
+
PILE
|
| 187 |
+
PIPE
|
| 188 |
+
POEM
|
| 189 |
+
POLE
|
| 190 |
+
POND
|
| 191 |
+
PONY
|
| 192 |
+
POOL
|
| 193 |
+
POST
|
| 194 |
+
QUIT
|
| 195 |
+
RACE
|
| 196 |
+
RAKE
|
| 197 |
+
RANK
|
| 198 |
+
RARE
|
| 199 |
+
REAL
|
| 200 |
+
RICE
|
| 201 |
+
RICH
|
| 202 |
+
RIPE
|
| 203 |
+
RISE
|
| 204 |
+
RISK
|
| 205 |
+
ROAR
|
| 206 |
+
ROOT
|
| 207 |
+
RUIN
|
| 208 |
+
SACK
|
| 209 |
+
SAFE
|
| 210 |
+
SAIL
|
| 211 |
+
SAKE
|
| 212 |
+
SALT
|
| 213 |
+
SAVE
|
| 214 |
+
SCAR
|
| 215 |
+
SEAL
|
| 216 |
+
SEAT
|
| 217 |
+
SEEM
|
| 218 |
+
SHED
|
| 219 |
+
SILK
|
| 220 |
+
SINK
|
| 221 |
+
SIZE
|
| 222 |
+
SKIM
|
| 223 |
+
SKIN
|
| 224 |
+
SLIP
|
| 225 |
+
SNAP
|
| 226 |
+
SOAK
|
| 227 |
+
SOAP
|
| 228 |
+
SOIL
|
| 229 |
+
SORE
|
| 230 |
+
SORT
|
| 231 |
+
SOUR
|
| 232 |
+
SPIN
|
| 233 |
+
SUCH
|
| 234 |
+
SUIT
|
| 235 |
+
SWIM
|
| 236 |
+
TAIL
|
| 237 |
+
TEAM
|
| 238 |
+
TEAR
|
| 239 |
+
TEND
|
| 240 |
+
TENT
|
| 241 |
+
TEST
|
| 242 |
+
TINY
|
| 243 |
+
TIRE
|
| 244 |
+
TONE
|
| 245 |
+
TOSS
|
| 246 |
+
TRAP
|
| 247 |
+
TRIM
|
| 248 |
+
TRUE
|
| 249 |
+
TWIN
|
| 250 |
+
UGLY
|
| 251 |
+
UNIT
|
| 252 |
+
VIEW
|
| 253 |
+
WADE
|
| 254 |
+
WAIT
|
| 255 |
+
WALK
|
| 256 |
+
WEAK
|
| 257 |
+
WEST
|
| 258 |
+
WIFE
|
| 259 |
+
WILD
|
| 260 |
+
WINE
|
| 261 |
+
WIRE
|
| 262 |
+
WISE
|
| 263 |
+
WOOD
|
| 264 |
+
WOOL
|
| 265 |
+
WORD
|
| 266 |
+
YARD
|
| 267 |
+
YELL
|
| 268 |
+
ZONE
|
| 269 |
+
ABOVE
|
| 270 |
+
AGAIN
|
| 271 |
+
AGREE
|
| 272 |
+
AHEAD
|
| 273 |
+
ALARM
|
| 274 |
+
ALIKE
|
| 275 |
+
ALLOW
|
| 276 |
+
ALONE
|
| 277 |
+
AMONG
|
| 278 |
+
ANGER
|
| 279 |
+
ANGRY
|
| 280 |
+
APART
|
| 281 |
+
ARROW
|
| 282 |
+
ATTIC
|
| 283 |
+
AWFUL
|
| 284 |
+
BADGE
|
| 285 |
+
BEAST
|
| 286 |
+
BEGIN
|
| 287 |
+
BELOW
|
| 288 |
+
BLADE
|
| 289 |
+
BLAME
|
| 290 |
+
BLAZE
|
| 291 |
+
BLIND
|
| 292 |
+
BLOCK
|
| 293 |
+
BLOOD
|
| 294 |
+
BOARD
|
| 295 |
+
BRAID
|
| 296 |
+
BRAIN
|
| 297 |
+
BRAKE
|
| 298 |
+
BRASS
|
| 299 |
+
BRAVE
|
| 300 |
+
BREAK
|
| 301 |
+
BRICK
|
| 302 |
+
BRIDE
|
| 303 |
+
BROOK
|
| 304 |
+
BRUSH
|
| 305 |
+
BUGGY
|
| 306 |
+
BUILD
|
| 307 |
+
BUNCH
|
| 308 |
+
BURST
|
| 309 |
+
CABIN
|
| 310 |
+
CABLE
|
| 311 |
+
CANAL
|
| 312 |
+
CAUSE
|
| 313 |
+
CHAIN
|
| 314 |
+
CHASE
|
| 315 |
+
CHEAT
|
| 316 |
+
CHECK
|
| 317 |
+
CHEER
|
| 318 |
+
CHIEF
|
| 319 |
+
CLERK
|
| 320 |
+
CLIFF
|
| 321 |
+
CLIMB
|
| 322 |
+
CLOAK
|
| 323 |
+
CLOTH
|
| 324 |
+
CLOWN
|
| 325 |
+
COACH
|
| 326 |
+
COCOA
|
| 327 |
+
COLOR
|
| 328 |
+
COUGH
|
| 329 |
+
COUNT
|
| 330 |
+
COURT
|
| 331 |
+
CRACK
|
| 332 |
+
CRASH
|
| 333 |
+
CRAWL
|
| 334 |
+
CRAZY
|
| 335 |
+
CREEK
|
| 336 |
+
CRIME
|
| 337 |
+
CROSS
|
| 338 |
+
CROWD
|
| 339 |
+
CRUSH
|
| 340 |
+
DAILY
|
| 341 |
+
DEATH
|
| 342 |
+
DELAY
|
| 343 |
+
DIRTY
|
| 344 |
+
DITCH
|
| 345 |
+
DOZEN
|
| 346 |
+
DREAM
|
| 347 |
+
EARLY
|
| 348 |
+
EARTH
|
| 349 |
+
EMPTY
|
| 350 |
+
ENJOY
|
| 351 |
+
ERECT
|
| 352 |
+
EVENT
|
| 353 |
+
FALSE
|
| 354 |
+
FANCY
|
| 355 |
+
FAVOR
|
| 356 |
+
FEAST
|
| 357 |
+
FENCE
|
| 358 |
+
FEVER
|
| 359 |
+
FIELD
|
| 360 |
+
FIFTH
|
| 361 |
+
FLASH
|
| 362 |
+
FLOCK
|
| 363 |
+
FLOOD
|
| 364 |
+
FLOOR
|
| 365 |
+
FLOUR
|
| 366 |
+
FORTY
|
| 367 |
+
FRAME
|
| 368 |
+
FRANK
|
| 369 |
+
FRESH
|
| 370 |
+
FRONT
|
| 371 |
+
GLASS
|
| 372 |
+
GLOBE
|
| 373 |
+
GRACE
|
| 374 |
+
GRAVE
|
| 375 |
+
GRAZE
|
| 376 |
+
GREAT
|
| 377 |
+
GREET
|
| 378 |
+
GRIND
|
| 379 |
+
GROUP
|
| 380 |
+
GROWL
|
| 381 |
+
GUARD
|
| 382 |
+
GUIDE
|
| 383 |
+
HABIT
|
| 384 |
+
HANDY
|
| 385 |
+
HATCH
|
| 386 |
+
HEART
|
| 387 |
+
HEAVY
|
| 388 |
+
HONEY
|
| 389 |
+
HOTEL
|
| 390 |
+
IDEAL
|
| 391 |
+
JELLY
|
| 392 |
+
JUDGE
|
| 393 |
+
JUICY
|
| 394 |
+
KNIFE
|
| 395 |
+
KNOCK
|
| 396 |
+
LAUGH
|
| 397 |
+
LEMON
|
| 398 |
+
LEVEL
|
| 399 |
+
LODGE
|
| 400 |
+
LOOSE
|
| 401 |
+
LUCKY
|
| 402 |
+
MAGIC
|
| 403 |
+
MARCH
|
| 404 |
+
MARRY
|
| 405 |
+
MAYOR
|
| 406 |
+
MODEL
|
| 407 |
+
MONEY
|
| 408 |
+
MONTH
|
| 409 |
+
MOTOR
|
| 410 |
+
MOUSE
|
| 411 |
+
MUDDY
|
| 412 |
+
MUSIC
|
| 413 |
+
NINTH
|
| 414 |
+
NOISE
|
| 415 |
+
NURSE
|
| 416 |
+
OCEAN
|
| 417 |
+
OFFEN
|
| 418 |
+
OFFER
|
| 419 |
+
ORDER
|
| 420 |
+
ORGAN
|
| 421 |
+
OUNCE
|
| 422 |
+
OWNER
|
| 423 |
+
PAPER
|
| 424 |
+
PARTY
|
| 425 |
+
PASTE
|
| 426 |
+
PATCH
|
| 427 |
+
PEACE
|
| 428 |
+
PHONE
|
| 429 |
+
PIANO
|
| 430 |
+
PIECE
|
| 431 |
+
PLAIN
|
| 432 |
+
PLANE
|
| 433 |
+
POINT
|
| 434 |
+
PORCH
|
| 435 |
+
POUND
|
| 436 |
+
POWER
|
| 437 |
+
PRESS
|
| 438 |
+
PRICE
|
| 439 |
+
PRINT
|
| 440 |
+
PRIZE
|
| 441 |
+
PROUD
|
| 442 |
+
PUPIL
|
| 443 |
+
PURSE
|
| 444 |
+
QUART
|
| 445 |
+
QUEEN
|
| 446 |
+
QUEST
|
| 447 |
+
QUIET
|
| 448 |
+
QUITE
|
| 449 |
+
RAINY
|
| 450 |
+
RAISE
|
| 451 |
+
RANCH
|
| 452 |
+
RANGE
|
| 453 |
+
REACH
|
| 454 |
+
READY
|
| 455 |
+
RIFLE
|
| 456 |
+
RIVER
|
| 457 |
+
ROUGH
|
| 458 |
+
ROUTE
|
| 459 |
+
SCALE
|
| 460 |
+
SCARE
|
| 461 |
+
SCARF
|
| 462 |
+
SCOLD
|
| 463 |
+
SCRAP
|
| 464 |
+
SCREW
|
| 465 |
+
SCRUB
|
| 466 |
+
SENSE
|
| 467 |
+
SHACK
|
| 468 |
+
SHAKE
|
| 469 |
+
SHAME
|
| 470 |
+
SHAPE
|
| 471 |
+
SHARE
|
| 472 |
+
SHEEP
|
| 473 |
+
SHEET
|
| 474 |
+
SHELL
|
| 475 |
+
SHINE
|
| 476 |
+
SHIRT
|
| 477 |
+
SHOCK
|
| 478 |
+
SHORE
|
| 479 |
+
SILLY
|
| 480 |
+
SINCE
|
| 481 |
+
SKATE
|
| 482 |
+
SKILL
|
| 483 |
+
SKIRT
|
| 484 |
+
SLIDE
|
| 485 |
+
SMILE
|
| 486 |
+
SMOKE
|
| 487 |
+
SORRY
|
| 488 |
+
SPACE
|
| 489 |
+
SPEAK
|
| 490 |
+
SPEND
|
| 491 |
+
SPOON
|
| 492 |
+
SPORT
|
| 493 |
+
STACK
|
| 494 |
+
STAIR
|
| 495 |
+
STAKE
|
| 496 |
+
STALK
|
| 497 |
+
STEAM
|
| 498 |
+
STEEL
|
| 499 |
+
STEEP
|
| 500 |
+
STONE
|
| 501 |
+
STOOP
|
| 502 |
+
STRAW
|
| 503 |
+
STRIP
|
| 504 |
+
STUDY
|
| 505 |
+
STUMP
|
| 506 |
+
SUNNY
|
| 507 |
+
SWEEP
|
| 508 |
+
SWING
|
| 509 |
+
SWORD
|
| 510 |
+
TEETH
|
| 511 |
+
THICK
|
| 512 |
+
THROW
|
| 513 |
+
TIRED
|
| 514 |
+
TOOTH
|
| 515 |
+
TOUCH
|
| 516 |
+
TOWER
|
| 517 |
+
TRACE
|
| 518 |
+
TRAMP
|
| 519 |
+
TRIBE
|
| 520 |
+
TRICK
|
| 521 |
+
TROOP
|
| 522 |
+
TROUT
|
| 523 |
+
UNCLE
|
| 524 |
+
UNTIL
|
| 525 |
+
UPPER
|
| 526 |
+
WAIST
|
| 527 |
+
WASTE
|
| 528 |
+
WHALE
|
| 529 |
+
WHEEL
|
| 530 |
+
WHOLE
|
| 531 |
+
WHOSE
|
| 532 |
+
WINDY
|
| 533 |
+
WOMAN
|
| 534 |
+
WOMEN
|
| 535 |
+
WORRY
|
| 536 |
+
WORSE
|
| 537 |
+
WORTH
|
| 538 |
+
WOUND
|
| 539 |
+
WRIST
|
| 540 |
+
WRONG
|
| 541 |
+
ABSENT
|
| 542 |
+
ACCENT
|
| 543 |
+
ACROSS
|
| 544 |
+
ACTING
|
| 545 |
+
ADMIRE
|
| 546 |
+
ADVICE
|
| 547 |
+
AFFECT
|
| 548 |
+
AFRAID
|
| 549 |
+
ALMOST
|
| 550 |
+
ALWAYS
|
| 551 |
+
AMOUNT
|
| 552 |
+
ANSWER
|
| 553 |
+
ANYHOW
|
| 554 |
+
ANYONE
|
| 555 |
+
ANYWAY
|
| 556 |
+
APPEAR
|
| 557 |
+
AROUND
|
| 558 |
+
ARREST
|
| 559 |
+
ARRIVE
|
| 560 |
+
ARTIST
|
| 561 |
+
ATTACK
|
| 562 |
+
ATTEND
|
| 563 |
+
AVENUE
|
| 564 |
+
BARREL
|
| 565 |
+
BASKET
|
| 566 |
+
BATTLE
|
| 567 |
+
BEFORE
|
| 568 |
+
BELONG
|
| 569 |
+
BEYOND
|
| 570 |
+
BIGGER
|
| 571 |
+
BLOUSE
|
| 572 |
+
BORDER
|
| 573 |
+
BOTTOM
|
| 574 |
+
BRANCH
|
| 575 |
+
BREAST
|
| 576 |
+
BREATH
|
| 577 |
+
BRIDGE
|
| 578 |
+
BRUISE
|
| 579 |
+
BUCKET
|
| 580 |
+
BUNDLE
|
| 581 |
+
BUTTON
|
| 582 |
+
CANDLE
|
| 583 |
+
CARPET
|
| 584 |
+
CASTLE
|
| 585 |
+
CATTLE
|
| 586 |
+
CELLAR
|
| 587 |
+
CEMENT
|
| 588 |
+
CENTER
|
| 589 |
+
CHANCE
|
| 590 |
+
CHANGE
|
| 591 |
+
CHARGE
|
| 592 |
+
CHERRY
|
| 593 |
+
CHOICE
|
| 594 |
+
CHOOSE
|
| 595 |
+
CHURCH
|
| 596 |
+
CIRCLE
|
| 597 |
+
CLEVER
|
| 598 |
+
CLOSET
|
| 599 |
+
COLLAR
|
| 600 |
+
COLONY
|
| 601 |
+
COPPER
|
| 602 |
+
CORNER
|
| 603 |
+
COTTON
|
| 604 |
+
COUNTY
|
| 605 |
+
COURSE
|
| 606 |
+
COUSIN
|
| 607 |
+
CREDIT
|
| 608 |
+
CROUCH
|
| 609 |
+
DANGER
|
| 610 |
+
DECIDE
|
| 611 |
+
DEFEAT
|
| 612 |
+
DEMAND
|
| 613 |
+
DEPART
|
| 614 |
+
DEPEND
|
| 615 |
+
DESERT
|
| 616 |
+
DESIRE
|
| 617 |
+
DIFFER
|
| 618 |
+
DIRECT
|
| 619 |
+
DIVIDE
|
| 620 |
+
DOCTOR
|
| 621 |
+
DOLLAR
|
| 622 |
+
DOUBLE
|
| 623 |
+
DRIVER
|
| 624 |
+
DURING
|
| 625 |
+
EFFORT
|
| 626 |
+
EIGHTY
|
| 627 |
+
ELEVEN
|
| 628 |
+
ENGINE
|
| 629 |
+
ENOUGH
|
| 630 |
+
ESCAPE
|
| 631 |
+
FAIRLY
|
| 632 |
+
FAMOUS
|
| 633 |
+
FASTEN
|
| 634 |
+
FIGURE
|
| 635 |
+
FINGER
|
| 636 |
+
FINISH
|
| 637 |
+
FLAVOR
|
| 638 |
+
FLIGHT
|
| 639 |
+
FOLLOW
|
| 640 |
+
FORBID
|
| 641 |
+
FOURTH
|
| 642 |
+
FREEZE
|
| 643 |
+
FRIEND
|
| 644 |
+
FRIGHT
|
| 645 |
+
GALLON
|
| 646 |
+
GARAGE
|
| 647 |
+
GARDEN
|
| 648 |
+
GOLDEN
|
| 649 |
+
GROWTH
|
| 650 |
+
HAMMER
|
| 651 |
+
HANDLE
|
| 652 |
+
HARBOR
|
| 653 |
+
HARDER
|
| 654 |
+
HEALTH
|
| 655 |
+
HEIGHT
|
| 656 |
+
HELMET
|
| 657 |
+
HIGHER
|
| 658 |
+
HONEST
|
| 659 |
+
HOPING
|
| 660 |
+
HUNGRY
|
| 661 |
+
IMPORT
|
| 662 |
+
INDEED
|
| 663 |
+
INJURE
|
| 664 |
+
INTEND
|
| 665 |
+
INTENT
|
| 666 |
+
INVENT
|
| 667 |
+
INVITE
|
| 668 |
+
ISLAND
|
| 669 |
+
ITSELF
|
| 670 |
+
JACKET
|
| 671 |
+
JUNGLE
|
| 672 |
+
JUNIOR
|
| 673 |
+
KETTLE
|
| 674 |
+
LADDER
|
| 675 |
+
LATELY
|
| 676 |
+
LENGTH
|
| 677 |
+
LESSON
|
| 678 |
+
LIKELY
|
| 679 |
+
LINING
|
| 680 |
+
LIQUID
|
| 681 |
+
LISTEN
|
| 682 |
+
LIVELY
|
| 683 |
+
LIVING
|
| 684 |
+
LOCATE
|
| 685 |
+
LONELY
|
| 686 |
+
LOVELY
|
| 687 |
+
LUMBER
|
| 688 |
+
MAKING
|
| 689 |
+
MANUAL
|
| 690 |
+
MARBLE
|
| 691 |
+
MASTER
|
| 692 |
+
MATTER
|
| 693 |
+
MEADOW
|
| 694 |
+
MEDIUM
|
| 695 |
+
MEMBER
|
| 696 |
+
MIDDLE
|
| 697 |
+
MINUTE
|
| 698 |
+
MOMENT
|
| 699 |
+
MOSTLY
|
| 700 |
+
MUSCLE
|
| 701 |
+
NATION
|
| 702 |
+
NATURE
|
| 703 |
+
NEARBY
|
| 704 |
+
NEEDLE
|
| 705 |
+
NEPHEW
|
| 706 |
+
NICKEL
|
| 707 |
+
NOBODY
|
| 708 |
+
NOTICE
|
| 709 |
+
NUMBER
|
| 710 |
+
ORANGE
|
| 711 |
+
OUTFIT
|
| 712 |
+
PADDLE
|
| 713 |
+
PALACE
|
| 714 |
+
PEOPLE
|
| 715 |
+
PERIOD
|
| 716 |
+
PERMIT
|
| 717 |
+
PERSON
|
| 718 |
+
PICKLE
|
| 719 |
+
PICNIC
|
| 720 |
+
PILLOW
|
| 721 |
+
PLENTY
|
| 722 |
+
POCKET
|
| 723 |
+
POSTER
|
| 724 |
+
POTATO
|
| 725 |
+
POWDER
|
| 726 |
+
PRAYER
|
| 727 |
+
PREACH
|
| 728 |
+
PRINCE
|
| 729 |
+
PROPER
|
| 730 |
+
PUBLIC
|
| 731 |
+
PURPLE
|
| 732 |
+
PUZZLE
|
| 733 |
+
RACKET
|
| 734 |
+
RAISIN
|
| 735 |
+
READER
|
| 736 |
+
REALLY
|
| 737 |
+
REASON
|
| 738 |
+
RECESS
|
| 739 |
+
RECORD
|
| 740 |
+
REMAIN
|
| 741 |
+
REMARK
|
| 742 |
+
REMIND
|
| 743 |
+
REMOVE
|
| 744 |
+
REPAIR
|
| 745 |
+
REPORT
|
| 746 |
+
RESULT
|
| 747 |
+
RIBBON
|
| 748 |
+
SADDLE
|
| 749 |
+
SAFETY
|
| 750 |
+
SAILOR
|
| 751 |
+
SAVAGE
|
| 752 |
+
SCARCE
|
| 753 |
+
SCRAPE
|
| 754 |
+
SCREAM
|
| 755 |
+
SEASON
|
| 756 |
+
SECOND
|
| 757 |
+
SECRET
|
| 758 |
+
SECURE
|
| 759 |
+
SELECT
|
| 760 |
+
SHADOW
|
| 761 |
+
SHOVEL
|
| 762 |
+
SHOWER
|
| 763 |
+
SIGNAL
|
| 764 |
+
SILENT
|
| 765 |
+
SILVER
|
| 766 |
+
SINGLE
|
| 767 |
+
SLEEPY
|
| 768 |
+
SLEEVE
|
| 769 |
+
SLOWLY
|
| 770 |
+
SPIRIT
|
| 771 |
+
SPREAD
|
| 772 |
+
SQUARE
|
| 773 |
+
STABLE
|
| 774 |
+
STARVE
|
| 775 |
+
STATUE
|
| 776 |
+
STRAIN
|
| 777 |
+
STREAM
|
| 778 |
+
STRONG
|
| 779 |
+
STUPID
|
| 780 |
+
SUPPLY
|
| 781 |
+
SWITCH
|
| 782 |
+
TABLET
|
| 783 |
+
TACKLE
|
| 784 |
+
TEMPER
|
| 785 |
+
TEMPLE
|
| 786 |
+
TENNIS
|
| 787 |
+
THRILL
|
| 788 |
+
TOMATO
|
| 789 |
+
TOWARD
|
| 790 |
+
TURTLE
|
| 791 |
+
TWENTY
|
| 792 |
+
UNEASY
|
| 793 |
+
UNLOAD
|
| 794 |
+
UNLOCK
|
| 795 |
+
VACANT
|
| 796 |
+
VALLEY
|
| 797 |
+
VOYAGE
|
| 798 |
+
WITHIN
|
| 799 |
+
WONDER
|
| 800 |
+
WRENCH
|
battlewords/words/wordlist.txt
CHANGED
|
@@ -1,35 +1,5 @@
|
|
| 1 |
# Optional: place a large A–Z word list here (one word per line).
|
| 2 |
# The app falls back to built-in pools if fewer than 500 words per length are found.
|
| 3 |
-
TREE
|
| 4 |
-
BOAT
|
| 5 |
-
WIND
|
| 6 |
-
FROG
|
| 7 |
-
LION
|
| 8 |
-
MOON
|
| 9 |
-
FORK
|
| 10 |
-
GLOW
|
| 11 |
-
GAME
|
| 12 |
-
CODE
|
| 13 |
-
APPLE
|
| 14 |
-
RIVER
|
| 15 |
-
STONE
|
| 16 |
-
PLANT
|
| 17 |
-
MOUSE
|
| 18 |
-
BOARD
|
| 19 |
-
CHAIR
|
| 20 |
-
SCALE
|
| 21 |
-
SMILE
|
| 22 |
-
CLOUD
|
| 23 |
-
ORANGE
|
| 24 |
-
PYTHON
|
| 25 |
-
STREAM
|
| 26 |
-
MARKET
|
| 27 |
-
FOREST
|
| 28 |
-
THRIVE
|
| 29 |
-
LOGGER
|
| 30 |
-
BREATH
|
| 31 |
-
DOMAIN
|
| 32 |
-
GALAXY
|
| 33 |
ABLE
|
| 34 |
ACID
|
| 35 |
AGED
|
|
@@ -113,7 +83,6 @@ DECK
|
|
| 113 |
DEEP
|
| 114 |
DEER
|
| 115 |
DIAL
|
| 116 |
-
DICK
|
| 117 |
DIET
|
| 118 |
DISC
|
| 119 |
DISK
|
|
@@ -164,14 +133,17 @@ FISH
|
|
| 164 |
FIVE
|
| 165 |
FLAT
|
| 166 |
FLOW
|
|
|
|
| 167 |
FOAM
|
| 168 |
FOOD
|
| 169 |
FOOT
|
| 170 |
FORD
|
|
|
|
| 171 |
FORM
|
| 172 |
FORT
|
| 173 |
FOUR
|
| 174 |
FREE
|
|
|
|
| 175 |
FROM
|
| 176 |
FUEL
|
| 177 |
FULL
|
|
@@ -185,6 +157,7 @@ GIFT
|
|
| 185 |
GIRL
|
| 186 |
GIVE
|
| 187 |
GLAD
|
|
|
|
| 188 |
GOAL
|
| 189 |
GOAT
|
| 190 |
GOLD
|
|
@@ -192,7 +165,6 @@ GOLF
|
|
| 192 |
GONE
|
| 193 |
GOOD
|
| 194 |
GRAY
|
| 195 |
-
GREAT
|
| 196 |
GRID
|
| 197 |
GRIP
|
| 198 |
GROW
|
|
@@ -232,10 +204,6 @@ INCH
|
|
| 232 |
INTO
|
| 233 |
IRON
|
| 234 |
ITEM
|
| 235 |
-
JACK
|
| 236 |
-
JANE
|
| 237 |
-
JEAN
|
| 238 |
-
JOHN
|
| 239 |
JOIN
|
| 240 |
JUMP
|
| 241 |
JURY
|
|
@@ -264,6 +232,7 @@ LIFT
|
|
| 264 |
LIKE
|
| 265 |
LINE
|
| 266 |
LINK
|
|
|
|
| 267 |
LIST
|
| 268 |
LIVE
|
| 269 |
LOAD
|
|
@@ -299,6 +268,7 @@ MISS
|
|
| 299 |
MODE
|
| 300 |
MOOD
|
| 301 |
MOON
|
|
|
|
| 302 |
MORE
|
| 303 |
MOST
|
| 304 |
MOVE
|
|
@@ -309,6 +279,7 @@ NAVY
|
|
| 309 |
NEAR
|
| 310 |
NECK
|
| 311 |
NEED
|
|
|
|
| 312 |
NEWS
|
| 313 |
NEXT
|
| 314 |
NICE
|
|
@@ -466,7 +437,6 @@ TINY
|
|
| 466 |
TOLD
|
| 467 |
TOLL
|
| 468 |
TONE
|
| 469 |
-
TONY
|
| 470 |
TOOL
|
| 471 |
TOUR
|
| 472 |
TOWN
|
|
@@ -511,6 +481,7 @@ WIFE
|
|
| 511 |
WILD
|
| 512 |
WILL
|
| 513 |
WIND
|
|
|
|
| 514 |
WINE
|
| 515 |
WING
|
| 516 |
WIRE
|
|
@@ -525,47 +496,71 @@ YARN
|
|
| 525 |
YEAR
|
| 526 |
YELL
|
| 527 |
YOGA
|
| 528 |
-
YOUNG
|
| 529 |
-
YOUR
|
| 530 |
ZERO
|
| 531 |
ZONE
|
| 532 |
APPLE
|
| 533 |
-
|
| 534 |
-
STONE
|
| 535 |
-
PLANT
|
| 536 |
-
MOUSE
|
| 537 |
BOARD
|
|
|
|
|
|
|
| 538 |
CHAIR
|
| 539 |
-
|
| 540 |
-
|
| 541 |
CLOUD
|
| 542 |
-
ORANGE
|
| 543 |
-
PYTHON
|
| 544 |
-
STREAM
|
| 545 |
-
MARKET
|
| 546 |
-
FOREST
|
| 547 |
-
THRIVE
|
| 548 |
-
LOGGER
|
| 549 |
-
BREATH
|
| 550 |
-
DOMAIN
|
| 551 |
-
GALAXY
|
| 552 |
-
BREAD
|
| 553 |
CRANE
|
|
|
|
|
|
|
|
|
|
| 554 |
FLAME
|
|
|
|
|
|
|
| 555 |
GRAPE
|
|
|
|
|
|
|
|
|
|
|
|
|
| 556 |
LEMON
|
|
|
|
| 557 |
MARCH
|
|
|
|
| 558 |
NURSE
|
|
|
|
|
|
|
|
|
|
| 559 |
PRIZE
|
|
|
|
|
|
|
|
|
|
| 560 |
SHINE
|
|
|
|
|
|
|
| 561 |
TIGER
|
| 562 |
-
|
|
|
|
| 563 |
CANDLE
|
|
|
|
|
|
|
|
|
|
|
|
|
| 564 |
FAMILY
|
|
|
|
|
|
|
|
|
|
| 565 |
GARDEN
|
|
|
|
| 566 |
JACKET
|
| 567 |
LADDER
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 568 |
POCKET
|
| 569 |
SILVER
|
|
|
|
|
|
|
|
|
|
| 570 |
TUNNEL
|
| 571 |
-
WINNER
|
|
|
|
|
|
| 1 |
# Optional: place a large A–Z word list here (one word per line).
|
| 2 |
# The app falls back to built-in pools if fewer than 500 words per length are found.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3 |
ABLE
|
| 4 |
ACID
|
| 5 |
AGED
|
|
|
|
| 83 |
DEEP
|
| 84 |
DEER
|
| 85 |
DIAL
|
|
|
|
| 86 |
DIET
|
| 87 |
DISC
|
| 88 |
DISK
|
|
|
|
| 133 |
FIVE
|
| 134 |
FLAT
|
| 135 |
FLOW
|
| 136 |
+
FLTE
|
| 137 |
FOAM
|
| 138 |
FOOD
|
| 139 |
FOOT
|
| 140 |
FORD
|
| 141 |
+
FORK
|
| 142 |
FORM
|
| 143 |
FORT
|
| 144 |
FOUR
|
| 145 |
FREE
|
| 146 |
+
FROG
|
| 147 |
FROM
|
| 148 |
FUEL
|
| 149 |
FULL
|
|
|
|
| 157 |
GIRL
|
| 158 |
GIVE
|
| 159 |
GLAD
|
| 160 |
+
GLOW
|
| 161 |
GOAL
|
| 162 |
GOAT
|
| 163 |
GOLD
|
|
|
|
| 165 |
GONE
|
| 166 |
GOOD
|
| 167 |
GRAY
|
|
|
|
| 168 |
GRID
|
| 169 |
GRIP
|
| 170 |
GROW
|
|
|
|
| 204 |
INTO
|
| 205 |
IRON
|
| 206 |
ITEM
|
|
|
|
|
|
|
|
|
|
|
|
|
| 207 |
JOIN
|
| 208 |
JUMP
|
| 209 |
JURY
|
|
|
|
| 232 |
LIKE
|
| 233 |
LINE
|
| 234 |
LINK
|
| 235 |
+
LION
|
| 236 |
LIST
|
| 237 |
LIVE
|
| 238 |
LOAD
|
|
|
|
| 268 |
MODE
|
| 269 |
MOOD
|
| 270 |
MOON
|
| 271 |
+
MOON
|
| 272 |
MORE
|
| 273 |
MOST
|
| 274 |
MOVE
|
|
|
|
| 279 |
NEAR
|
| 280 |
NECK
|
| 281 |
NEED
|
| 282 |
+
NERD
|
| 283 |
NEWS
|
| 284 |
NEXT
|
| 285 |
NICE
|
|
|
|
| 437 |
TOLD
|
| 438 |
TOLL
|
| 439 |
TONE
|
|
|
|
| 440 |
TOOL
|
| 441 |
TOUR
|
| 442 |
TOWN
|
|
|
|
| 481 |
WILD
|
| 482 |
WILL
|
| 483 |
WIND
|
| 484 |
+
WIND
|
| 485 |
WINE
|
| 486 |
WING
|
| 487 |
WIRE
|
|
|
|
| 496 |
YEAR
|
| 497 |
YELL
|
| 498 |
YOGA
|
|
|
|
|
|
|
| 499 |
ZERO
|
| 500 |
ZONE
|
| 501 |
APPLE
|
| 502 |
+
BLAST
|
|
|
|
|
|
|
|
|
|
| 503 |
BOARD
|
| 504 |
+
BRAVE
|
| 505 |
+
BREAD
|
| 506 |
CHAIR
|
| 507 |
+
CHALK
|
| 508 |
+
CHESS
|
| 509 |
CLOUD
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 510 |
CRANE
|
| 511 |
+
DANCE
|
| 512 |
+
EARTH
|
| 513 |
+
FAITH
|
| 514 |
FLAME
|
| 515 |
+
FLUTE
|
| 516 |
+
GHOST
|
| 517 |
GRAPE
|
| 518 |
+
GRASS
|
| 519 |
+
GREAT
|
| 520 |
+
HEART
|
| 521 |
+
HEART
|
| 522 |
LEMON
|
| 523 |
+
LIGHT
|
| 524 |
MARCH
|
| 525 |
+
MOUSE
|
| 526 |
NURSE
|
| 527 |
+
PANEL
|
| 528 |
+
PANEL
|
| 529 |
+
PLANT
|
| 530 |
PRIZE
|
| 531 |
+
QUEST
|
| 532 |
+
RIVER
|
| 533 |
+
SCALE
|
| 534 |
SHINE
|
| 535 |
+
SMILE
|
| 536 |
+
STONE
|
| 537 |
TIGER
|
| 538 |
+
YOUNG
|
| 539 |
+
BUNDLE
|
| 540 |
CANDLE
|
| 541 |
+
CHERRY
|
| 542 |
+
CIRCLE
|
| 543 |
+
DOCTOR
|
| 544 |
+
DOMAIN
|
| 545 |
FAMILY
|
| 546 |
+
FOREST
|
| 547 |
+
FRIEND
|
| 548 |
+
GALAXY
|
| 549 |
GARDEN
|
| 550 |
+
HUNTER
|
| 551 |
JACKET
|
| 552 |
LADDER
|
| 553 |
+
LAUNCH
|
| 554 |
+
LOGGER
|
| 555 |
+
MARKET
|
| 556 |
+
MOTHER
|
| 557 |
+
ORANGE
|
| 558 |
+
PALACE
|
| 559 |
POCKET
|
| 560 |
SILVER
|
| 561 |
+
SPIRIT
|
| 562 |
+
STREAM
|
| 563 |
+
THRIVE
|
| 564 |
TUNNEL
|
| 565 |
+
WINNER
|
| 566 |
+
WINTER
|