This challenge is to list out all possible words which are built from a pattern of syllables. Words are composed by joining syllables together. Syllables are composed of a number of vowels with some number of consonants optionally to the left and/or right. Your code should take in:

  • a list of consonants
  • a list of vowels (the vowels and consonants may be assumed to be mutually exclusive)
  • a syllable structure in some format you define; the format must contain information describing:
    • the minimum and maximum number of left consonants ( >= 0)
    • the minimum and maximum number of vowels ( >= 1)
    • the minimum and maximum number of right consonants. ( >= 0)
  • A minimum and maximum number of syllables ( >= 1)


Consonants: tk
Vowels: au
Structure: (c)cv(c) (means: 1 to 2 left consonants, 1 vowel, 0 to 1 right consonants)
Syllables: 1
Output: ta tat tak tu tut tuk ka kat kak ku kut kuk tta ttat ttak ttu ttut ttuk tka tkat tkak tku tkut tkuk kta ktat ktak ktu ktut ktuk kka kkat kkak kku kkut kkuk

Consonants: 'b', 'c', 'd' 
Vowels: 'e'
Structure: 1, 1, 1, 2, 0, 0 (means: 1 left consonant, 1 to 2 vowels, 0 right consonants)
Syllables: 1, 2
Output: be bee ce cee de dee bebe bebee bece becee bede bedee beebe beebee beece beecee beede beedee cebe cebee cece cecee cede cedee ceebe ceebee ceece ceecee ceede ceedee debe debee dece decee dede dedee deebe deebee deece deecee deede deedee

Example of using a single string as input("4|P|T|C|K|2|A|I||vvV||ss")
Consonants: "4|P|T|C|K|"
Vowels: "2|A|I|"
Structure: "|vvV||" (means: 0 left consonants, 2 to 3 vowels, 0 right consonants)
Syllables: "ss" (means: 2)

duplication test case:
Consonants: k
Vowels: a
Structure: cVc (means: 0 to 1 left consonants, 1 vowel, 0 to 1 right consonants)
Syllables: 2
(note that there is only one of each aka, kaka, akak, and kakak; you get two if you treat ak a and a ka separately)

Duplicate words should be removed. The order and format of the words does not matter, as long as they can be told apart (ie, don't output one long unbroken string of letters). The max and min of the left and right consonants is any non-negative integer; the max and min of the vowel and the number of syllables is any positive integer. The set of consonants will never have tokens from the set of vowels and vice versa. There will always be at least one consonant and one vowel provided in the input. You may define the format and order of the inputs. The words of the output must be built from the same characters as those entered in the input.

, so shortest code wins. To be considered valid, solutions must at least accept English vowels and consonants as valid vowels and consonants (having only upper or lower case is still valid), and must come up with a complete and correct word list for any valid input.

  • \$\begingroup\$ clarification of intent, bc of debate: "You may define the format and order of the inputs" means what it says. format it however you need it to get the information into your language. \$\endgroup\$
    – guest4308
    Commented Oct 4, 2023 at 18:30
  • \$\begingroup\$ Also, not sure how that same example is consistent with "There will always be at least one consonant and one vowel." In the sense that, if the min and max left and right consonant parameters are all set to 0, then we by definition have zero consonants. \$\endgroup\$ Commented Oct 4, 2023 at 21:36
  • \$\begingroup\$ @JosWoolley first question: AIII would be formed from the second (AI) and fourth (II) syllables you list there. second question: I meant, there will always be at least one consonant and one vowel provided in the input not the output. \$\endgroup\$
    – guest4308
    Commented Oct 5, 2023 at 3:00
  • 1
    \$\begingroup\$ Thanks. Re duplicates, why are there 32 such cases in your output for the consonantless example? \$\endgroup\$ Commented Oct 5, 2023 at 7:12
  • 1
    \$\begingroup\$ @JosWoolley because I'm an idiot that didn't realize my unique filter was only looking at space separated tokens. it should be fixed now, thanks! \$\endgroup\$
    – guest4308
    Commented Oct 5, 2023 at 7:24

7 Answers 7


Charcoal, 58 bytes


Try it online! Link is to verbose version of code. See below for input format. Explanation:


Input the minimum and maximum number of syllables and loop from one to the other (inclusive).


Repeat the syllable structure by the number of syllables and push the result to the predefined empty list. (In Charcoal, Divide splits a string, so Multiply joins a list with a string, rather than vectorised repeating the string.)


Loop over the structures and words that will be generated below.


If this is a structure or partial word, then:


Get a digit from the structure.


Choose consonants or vowels depending on whether the digit is less than 3.


If the digit is even then add an empty string to make the letter optional.


Replace the digit with each character in the list and push any new results to the predefined empty list for further processing.


Otherwise, output the word.

The program takes five inputs:

  1. The minimum number of syllables
  2. The maximum number of syllables
  3. A string describing the syllable format
  4. A string of consonants
  5. A string of vowels.

The syllable format is a string of digits from 1 to 4, where they have the following meanings:

  1. A consonant
  2. An optional consonant
  3. A vowel
  4. An optional vowel

JavaScript (V8), 139 bytes

Input format: ([[consonants], [vowels]], min_syllables, max_syllables, [[min, max], [min, max], [min, max]])

Prints all valid words.


Try it online!

  • \$\begingroup\$ that looks like it outputs all possible syllables; but I don't see how it would take in the min/max of syllables per word \$\endgroup\$
    – guest4308
    Commented Oct 4, 2023 at 17:09
  • \$\begingroup\$ nice, I think you're now outputting all possible words; but some of them appear multiple times. for example, with input [["t"], ["a"]], 2, 2, [[0, 1], [1, 1], [0, 1]] it has two of each word with one consonant in the center (ata, tata, atat, tatat) \$\endgroup\$
    – guest4308
    Commented Oct 5, 2023 at 3:09
  • 1
    \$\begingroup\$ @guest4308 I've added the deduplication of the output. \$\endgroup\$
    – Arnauld
    Commented Oct 5, 2023 at 9:21

Vyxal, 24 bytes


Try it Online!

Takes input as

[consonants, vowels]
[min, max]: left-consonants, vowels, right-consonants
[min-syllables, max-syllables]


∞                         # ‎⁡Palindromise [constants, vowels] (1. input) -> [constants, vowels, constants]
 ¨Z      ;                # ‎⁢Zip that with [min, max] (2. input) and map for each item:
   $÷ṡ                    # ‎⁣- Create a range from min to max
      v↔f                 # ‎⁤- For each number n in range, push all combinations of constants/vowels with length n
          Π               # ‎⁢⁡Cartesian product over [left-constant, vowel, right-constant]-combinations
            ÷ṡ            # ‎⁢⁢Create range from syllables-min to syllables-max (3. input)
           £  ƛ¥ÞẊv  ;    # ‎⁢⁣For each number n in range, push cartesian product of combinations-list with itself n times
                  vṅǍ     # ‎⁢⁤Merge each sublist
                      fU  # ‎⁣⁡Flatten and remove duplicates

Created with the help of Luminespire.

  • \$\begingroup\$ After staring at it a bit, I think I sort of understand the output format of words, but it's like on the line of outputting as 'one long unbroken string of letters'. it also looks like you still have duplicates (ie, ⟨ `e` | `be` ⟩ and ⟨ `eb` | `e` ⟩ for the input ["b", "e"] [[0, 1], [1, 1], [0, 1]] [2, 2]) \$\endgroup\$
    – guest4308
    Commented Oct 5, 2023 at 3:20
  • 1
    \$\begingroup\$ @guest4308 ah didn‘t notice the remove duplicates part, fixed it. \$\endgroup\$
    – math scat
    Commented Oct 5, 2023 at 6:26

Zsh, 67 65 bytes

saved 2 bytes by moving everything into eval, so no $=shwordsplit is needed.

eval "c=($1)
C=('' $1)
V=('' $2)
S=('' $3)
echo $4"

Attempt This Online! Attempt This Online!

Abuses flexible structure format to take input of the form $^c$^C$^v$^V$^c/$^s$^S, which when eval'd does cross product concatenations of the list elements.

echo is needed because <<< does not permit expansions which result in lists.

The ATO link enables tracing in the function for your convenience.

  • \$\begingroup\$ That's way too cheaty of an I/O method IMO \$\endgroup\$ Commented Oct 4, 2023 at 15:35
  • \$\begingroup\$ @RydwolfPrograms This could also be zsh --rcexpandparam, which would allow $c$C$v$V as input instead. \$\endgroup\$ Commented Oct 4, 2023 at 17:29
  • \$\begingroup\$ The OP uses | alternation to separate characters in the last example, which I'd argue embeds roughly the same meaning. \$\endgroup\$ Commented Oct 4, 2023 at 17:50

Ruby, 114 ... 82 bytes

->c,v,*s{[*?a..?z*s[7]*s.sum].grep /^(#{[k=?[+c+?],?[+v+?],k,?),?$]*"{%d,%d}"%s}/}

Try it online!

Works only in theory, takes forever to complete.

TIO link contains a hardcoded speedup version.


Excel, 324 312 bytes

z defined within Name Manager as:


(162 bytes)

Within the worksheet:


(150 bytes)

  • Consonant and vowel lists are spilled, vertical ranges A1# and B1# respectively.
  • Left consonant min and max in C1 and C2 respectively.
  • Right consonant min and max in D1 and D2 respectively.
  • Vowel min and max in E1 and E2 respectively.
  • Syllable min and max in F1 and F2 respectively.

PHP, 401 bytes

(surely I can do better than that!?!)

a recursive generator

  • consonants (array)
  • vowels (array)
  • structure (array): V for vowels, C for consonants; lower case for optional and upper case for mandatory
  • minimum number of syllables (int)
  • maximum number of syllables (int)

usage: foreach(f($c,$v,$s,$min,$max)as$w)echo "$w ";

function c($a){if($a){if($u=array_pop($a))foreach(c($a)as$p)foreach($u as$v)yield$p+[count($p)=>$v];}else yield[];}function f($c,$v,$s,$y,$x,$r=NULL){static$z;is_null($r)&&$z=[];$x--;$y&&$y--;foreach(c(array_map(function($h)use($c,$v){return${strtolower($h)}+($h>'Z'?[-1=>'']:[]);},$s))as$b){$w=$r.implode($b);$y||!$w||in_array($w,$z)||yield$w;$z[]=$w;if($x)foreach(f($c,$v,$s,$y,$x,$w)as$w)yield$w;}}

Try it online


(my cartesian generator is explained at https://stackoverflow.com/a/39174062/6442316)

// function to build cartesian product
function c($a){if($a){if($u=array_pop($a))foreach(c($a)as$p)
foreach($u as$v)yield$p+[count($p)=>$v];}else yield[];}

// function f builds one syllable, then recurses
function f($c,$v,$s,$y,$x,$r=NULL)
    $c: array consonants
    $v: array  vowels
    $s: array syllable structure
    $y: int syllables min
    $x: int syllables max
    for recursions:
    $r:string word so far
the first three parameters should be string, but with arrays I can spare two str_split calls
    is_null($r)&&$z=[]; // if new call, empty yielded words
    $x--;$y&&$y--; // decrease (remaining) syllable counts
    // loop through cartesian product of possible syllables:
    foreach(c( // create cartesian product over ...
        array_map(function($h)use($c,$v){ // map structure array -> possible letters
            return${strtolower($h)} # $$c or $$v
            +($h>'Z'?[-1=>'']:[]); # if $h is lower case, add empty char
        $w=$r.implode($b); // add syllable
        $y // if minimum syllables are reached
        || !$w // and the word is not empty
        || in_array($w,$z) // and it has not been yielded
        || yield$w; // yield word
        $z[]=$w; // add word to "already yielded"
        if($x) // if maximum syllables are not reached ...
            foreach(f($c,$v,$s,$y,$x,$w)as$w)yield$w; // ... recurse

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.