132 lines
4.1 KiB
Python
132 lines
4.1 KiB
Python
from __future__ import annotations
|
|
|
|
import argparse
|
|
from pathlib import Path
|
|
from typing import List
|
|
|
|
from build_vocabulary import (
|
|
FILTERED_OUTPUT_PATH,
|
|
METADATA_OUTPUT_PATH,
|
|
OUTPUT_PATH,
|
|
build_vocabulary,
|
|
)
|
|
from crossword_filler import CrosswordFiller, load_vocabulary, load_vocabulary_metadata
|
|
from crossword_generator import CrosswordGenerator, WORDS, render_grid
|
|
|
|
|
|
def parse_args() -> argparse.Namespace:
|
|
parser = argparse.ArgumentParser(description="Generatore e filler di cruciverba.")
|
|
parser.add_argument(
|
|
"--build-vocabulary",
|
|
action="store_true",
|
|
help="Rigenera il vocabolario esteso, filtrato e i metadati prima dell'esecuzione.",
|
|
)
|
|
parser.add_argument(
|
|
"--skip-fill",
|
|
action="store_true",
|
|
help="Genera solo la griglia iniziale senza eseguire il filler.",
|
|
)
|
|
parser.add_argument(
|
|
"--vocabulary",
|
|
type=Path,
|
|
default=None,
|
|
help="Percorso opzionale a un vocabolario personalizzato.",
|
|
)
|
|
parser.add_argument(
|
|
"--target-empty-ratio",
|
|
type=float,
|
|
default=1 / 6,
|
|
help="Rapporto target di celle vuote residue dopo il filler.",
|
|
)
|
|
parser.add_argument(
|
|
"--time-limit",
|
|
type=float,
|
|
default=8.0,
|
|
help="Tempo massimo in secondi per la fase di generazione iniziale.",
|
|
)
|
|
parser.add_argument(
|
|
"--max-candidates",
|
|
type=int,
|
|
default=12,
|
|
help="Numero massimo di candidati esplorati per parola nella generazione iniziale.",
|
|
)
|
|
parser.add_argument(
|
|
"--diffxy",
|
|
type=int,
|
|
default=7,
|
|
help="Differenza massima preferita tra larghezza e altezza della griglia.",
|
|
)
|
|
return parser.parse_args()
|
|
|
|
|
|
def ensure_vocabulary(args: argparse.Namespace) -> None:
|
|
needs_build = args.build_vocabulary or not FILTERED_OUTPUT_PATH.exists() or not METADATA_OUTPUT_PATH.exists()
|
|
if not needs_build:
|
|
return
|
|
|
|
totals = build_vocabulary()
|
|
print("Vocabolario rigenerato")
|
|
print(f"- esteso: {OUTPUT_PATH}")
|
|
print(f"- filtrato: {FILTERED_OUTPUT_PATH}")
|
|
print(f"- metadati: {METADATA_OUTPUT_PATH}")
|
|
print(f"- parole estese: {totals['extended_words']}")
|
|
print(f"- parole filtrate: {totals['filtered_words']}")
|
|
|
|
|
|
def load_selected_vocabulary(path: Path | None) -> List[str]:
|
|
if path is None:
|
|
return load_vocabulary()
|
|
return path.read_text(encoding="utf-8").splitlines()
|
|
|
|
|
|
def main() -> None:
|
|
args = parse_args()
|
|
ensure_vocabulary(args)
|
|
|
|
generator = CrosswordGenerator(
|
|
WORDS,
|
|
diffxy=args.diffxy,
|
|
time_limit_seconds=args.time_limit,
|
|
max_candidates_per_word=args.max_candidates,
|
|
)
|
|
initial_state = generator.solve()
|
|
|
|
print("Griglia iniziale")
|
|
print(f"Parole inserite: {initial_state.placed_words}/{len(generator.words)}")
|
|
print(f"Intersezioni: {initial_state.intersections}")
|
|
print(f"Dimensioni: {initial_state.width()} x {initial_state.height()} (diff={initial_state.shape_difference()})")
|
|
print()
|
|
print(render_grid(initial_state.grid, initial_state.placements))
|
|
|
|
if args.skip_fill:
|
|
return
|
|
|
|
vocabulary = load_selected_vocabulary(args.vocabulary)
|
|
metadata = load_vocabulary_metadata()
|
|
filler = CrosswordFiller(
|
|
initial_state,
|
|
vocabulary,
|
|
target_empty_ratio=args.target_empty_ratio,
|
|
vocabulary_metadata=metadata,
|
|
)
|
|
final_state = filler.fill()
|
|
|
|
print()
|
|
print("Griglia riempita")
|
|
print(f"Parole totali: {final_state.placed_words}")
|
|
print(f"Intersezioni totali: {final_state.intersections}")
|
|
print(f"Dimensioni: {final_state.width()} x {final_state.height()} (diff={final_state.shape_difference()})")
|
|
print()
|
|
print(render_grid(final_state.grid, final_state.placements))
|
|
|
|
if filler.added_words:
|
|
print()
|
|
print("Parole aggiunte dal filler:")
|
|
for index, placement in enumerate(filler.added_words, start=1):
|
|
direction = "orizzontale" if placement.direction == "H" else "verticale"
|
|
print(f"{index:>2}. {placement.word} ({placement.x}, {placement.y}) {direction}")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|