A One-Page Ledger Method for Generating Voynich-Like Text
(26-05-2026, 11:28 PM)Dunsel wrote: You are not allowed to view links. Register or Login to view.PRECISELY... which is what my ledger does.  Weighted letters.
I was thinking more along the lines of an extension for “random.” Just for a quick suggestion, I decided to ask the AI for the first time. The basic idea seems viable, but of course the accuracy of the weighting would need to be verified.

Code:
# oshfdk_literal_generator_modified.py # No external files required. # # Literal version of: # 1) create 20-30 common words # 2) create 10-20 common combinations # 3) mix common words + common combinations + random longer words # 4) random longer words occasionally repeat # # No mutation. # No ledger. # No syllables. # No hidden morphology. # # FIX for Problem: # Long words are no longer generated by flat random character sampling. # Instead, positional character pools (INITIAL / MEDIAL / TERMINAL) with # weights give each word a plausible frame: #  INITIAL  – q, d, s, c dominate word-starts #  MEDIAL  – o, e, a, i are the backbone of word bodies #  TERMINAL – n, y, r, l, m are the most common word-endings import random from collections import Counter SEED          = 42 PAGES          = 100 WORDS_PER_PAGE = 95 WORDS_PER_LINE = 8 # --------------------------------------------------------------------------- # Standard EVA-ish alphabet, sorted alphabetically. # --------------------------------------------------------------------------- VOYNICH_ALPHABET = "acdefhiklnoqrstxy" LONG_WORD_MIN_LEN      = 5 LONG_WORD_MAX_LEN      = 12 LONG_WORD_REPEAT_CHANCE = 0.25 RECENT_LONG_WORD_WINDOW = 10 # --------------------------------------------------------------------------- # Common words and combinations (unchanged) # --------------------------------------------------------------------------- COMMON_WORDS = [     "daiin", "dain", "ol", "or", "chol",     "chedy", "qokain", "qokeedy", "qotedy", "otedy",     "shey", "cthey", "cthol", "shol", "chor",     "dair", "qokair", "saiin", "aiin", "okain",     "sary", "okol", "qol", "qokal", "chdy", ] COMMON_COMBINATIONS = [     ["qokain", "daiin"],     ["chol",  "chedy"],     ["qokeedy","qokedy"],     ["ol",    "chedy"],     ["dain",  "chol"],     ["shey",  "qokain"],     ["cthey",  "daiin"],     ["shol",  "chor"],     ["qotedy", "qokain"],     ["otedy",  "ol"],     ["saiin",  "daiin"],     ["qokair", "dair"],     ["chol",  "daiin"],     ["cthol",  "chedy"],     ["or",    "chol"], ] # --------------------------------------------------------------------------- # Weights are intentionally skewed to reflect the strong positional biases # observed in Voynich script: #  INITIAL  – q, d, s, c dominate word-starts #  MEDIAL  – o, e, a, i are the backbone of word bodies #  TERMINAL – n, y, r, l, m are the most common word-endings # --------------------------------------------------------------------------- INITIAL_CHARS = {     "q": 8, "d": 7, "s": 6, "c": 6,     "o": 4, "a": 3, "f": 2, "r": 1,     "k": 1, "t": 1, } MEDIAL_CHARS = {     "o": 9, "e": 7, "a": 6, "i": 6,     "l": 4, "n": 3, "k": 3, "h": 3,     "r": 2, "t": 2, "d": 1, "s": 1, } TERMINAL_CHARS = {     "n": 8, "y": 8, "r": 6, "l": 5,     "m": 4, "s": 3, "d": 3, } def weighted_choice(weight_dict: dict) -> str:     """Return a single character sampled according to integer weights."""     chars  = list(weight_dict.keys())     weights = list(weight_dict.values())     return random.choices(chars, weights=weights, k=1)[0] def make_random_long_word() -> str:     """     Build a long word using positional character pools.     Structure:       [INITIAL char]  +  [MEDIAL body]  +  [TERMINAL char]     """     length = random.randint(LONG_WORD_MIN_LEN, LONG_WORD_MAX_LEN)     # --- Word-initial character (positional pool) ---     word = weighted_choice(INITIAL_CHARS)     # --- Medial body (positional pool) ---     for _ in range(length - 2):         word += weighted_choice(MEDIAL_CHARS)     # --- Word-terminal character (positional pool) ---     word += weighted_choice(TERMINAL_CHARS)     return word # --------------------------------------------------------------------------- # Page generation (unchanged logic, fixed long-word generator plugged in) # --------------------------------------------------------------------------- def generate_page() -> list[str]:     page            = []     recent_long_words = []     while len(page) < WORDS_PER_PAGE:         mode = random.choice(["common_word", "combination", "long_word"])         if mode == "common_word":             page.append(random.choice(COMMON_WORDS))         elif mode == "combination":             combo = random.choice(COMMON_COMBINATIONS)             page.extend(combo)         else:  # long_word             if recent_long_words and random.random() < LONG_WORD_REPEAT_CHANCE:                 word = random.choice(recent_long_words)             else:                 word = make_random_long_word()                 recent_long_words.append(word)                 if len(recent_long_words) > RECENT_LONG_WORD_WINDOW:                     recent_long_words.pop(0)             page.append(word)     return page[:WORDS_PER_PAGE] # --------------------------------------------------------------------------- # Analysis helpers (unchanged) # --------------------------------------------------------------------------- def char_ngrams(words: list[str], n: int) -> Counter:     counts = Counter()     for word in words:         if len(word) < n:             continue         for i in range(len(word) - n + 1):             counts[word[i:i + n]] += 1     return counts def analyze(all_words: list[str]) -> None:     token_count      = len(all_words)     type_count      = len(set(all_words))     word_freq        = Counter(all_words)     char_bigram_counts  = char_ngrams(all_words, 2)     char_trigram_counts = char_ngrams(all_words, 3)     print()     print("=" * 60)     print("GLOBAL WORD STATISTICS")     print("=" * 60)     print()     print(f"Tokens : {token_count}")     print(f"Types  : {type_count}")     print(f"TTR    : {type_count / token_count:.4f}")     print(f"Hapax  : {sum(1 for c in word_freq.values() if c == 1)}")     print()     print("Top 25 word tokens")     print()     for word, count in word_freq.most_common(25):         print(f"{word:15} {count}")     print()     print("=" * 60)     print("CHARACTER BIGRAMS")     print("=" * 60)     print()     for bg, count in char_bigram_counts.most_common(30):         print(f"{bg:5} {count}")     print()     print("=" * 60)     print("CHARACTER TRIGRAMS")     print("=" * 60)     print()     for tg, count in char_trigram_counts.most_common(30):         print(f"{tg:5} {count}") def print_page(page_num: int, page: list[str]) -> None:     print()     print("=" * 60)     print(f"PAGE {page_num}")     print("=" * 60)     print()     for i in range(0, len(page), WORDS_PER_LINE):         print(" ".join(page[i:i + WORDS_PER_LINE])) # --------------------------------------------------------------------------- # Entry point # --------------------------------------------------------------------------- def main() -> None:     random.seed(SEED)     all_words = []     for page_num in range(1, PAGES + 1):         page = generate_page()         all_words.extend(page)         print_page(page_num, page)     analyze(all_words) if __name__ == "__main__":     main()

Sorry, I was a little slow in posting this. - Edit: Where exactly can I find “Ledger_Scribe1.json”?
Reply


Messages In This Thread
RE: A One-Page Ledger Method for Generating Voynich-Like Text - by bi3mw - 27-05-2026, 12:04 AM

Possibly Related Threads…
Thread Author Replies Views Last Post
  Discussion of "A possible generating algorithm of the Voynich manuscript" Emma May Smith 246 218,594 11-07-2025, 08:02 AM
Last Post: Pepper
  Bing AI Generating Continuations of Voynich Text Psillycyber 4 9,518 11-04-2023, 03:51 PM
Last Post: Psillycyber

Forum Jump:


Users browsing this thread: 7 Guest(s)