# Salacious Bacon Tripod

In a certain chatroom, we like making acronym jokes about the build tool we use called sbt. While it usually stands for "Scala Build Tool", we aim to come up with all sorts of meanings, such as:

• Stupid Brick Teeth
• Sussy Baka Training
• Seriously Big Toes
• Silly Bear Topics
• Salacious Bacon Tripod
• Syringed Baby Toys
• Several Belated Tanks

As you can see, the meaning of "sbt" changes each time, and the words used are never repeated. Today's challenge is to generalise this to a block of text.

## Challenge

Your task is to replace all instances of an acronym in a large block of text with a different meaning each time. To do so, you'll be given a string representing the haystack text and the acronym to change, as well as a list of words starting with each letter in the acronym.

For the acronym "sbt", you'll receive: the large text to change, the acronym, a list of words starting with "s", a list of words starting with "b" and a list of words starting with "t".

To each occurrence of the acronym, replace it with a uniformly randomly chosen word from each word list. To make sure the same set of words aren't used twice, one a word is chosen from a list, it can't be used again.

## Worked Example

Given the following text:

I think it might be possible to make an sbt wrapper to allow it to be
used without a shell. That means that instead of having to launch the
sbt console each time, you'll be able to use sbt commands straight
from the command line.


The acronym "sbt" and the following word lists:

["stupid", "sussy", "shady", "seriously", "silly", "salacious", "syringed", "several", "schadenfreude", "slightly"]
["brick", "baka", "blue", "big", "bear", "bacon", "baby", "bringing", "belated", "better"]
["teeth", "training", "tourists", "toes", "topics", "tripod", "toys", "trains", "tanks", "tiet"]


Valid outputs might include:

I think it might be possible to make an salacious bacon tripod wrapper to
allow it to be used without a shell. That means that instead of having to
launch the several big teeth console each time, you'll be able to use silly
blue toys commands straight from the command line.

I think it might be possible to make an stupid bear training wrapper to
allow it to be used without a shell. That means that instead of having to
launch the schadenfreude brick topics console each time, you'll be able to
syringed baka tripod sbt commands straight from the command line.

I think it might be possible to make an sussy baka toes wrapper to
allow it to be used without a shell. That means that instead of having to
launch the stupid brick teeth console each time, you'll be able to use
salacious baby topics commands straight from the command line.


Note that the above paragraphs have been line-wrapped for formatting reasons. They would otherwise be on a single line.

## Rules

• Only acronyms surrounded by spaces or EOF should be replaced. That means that "sbt" in something like "whomsbted" should not be replaced with an expanded meaning. "sbt." shouldn't be expanded either. But "sbt" at the start or end of the long text should be replaced.
• Acronyms will only contain distinct letters.
• The acronym will be case-sensitive. If the acronym is "sbt", only "sbt" should be placed, not "SbT", "sbT", "SBT" and other variations.
• Inputs can be taken in any reasonable and convenient format.
• Inputs can be taken in any order.
• Output can be given in any reasonable and convenient format.
• Each word in expanded meanings should have an equal chance of being included given it hasn't already been used. This can be achieved by removing the chosen word from the word list.
• None of the inputs will be empty.
• You can assume that the word lists will have at least as many words as there are occurrences of the acronym in the large text. They may be different lengths to each other though.

## Test Cases

Format:

text
acronym
word lists
---
possible output

"I think it might be possible to make an sbt wrapper to allow it to be used without a shell. That means that instead of having to launch the sbt console each time, you'll be able to use sbt commands straight from the command line."
"sbt"
["brick", "baka", "blue", "big", "bear", "bacon", "baby", "bringing", "belated"]
["teeth", "training", "tourists", "toes", "topics", "tripod", "toys", "trains", "tanks"]
---
"I think it might be possible to make an salacious bacon tripod wrapper to allow it to be used without a shell. That means that instead of having to launch the several big teeth console each time, you'll be able to use silly blue toys commands straight from the command line."

"y'all ever just see a meme and rofl so hard that you rofl into a sharp object and rofl harder?"
"rofl"
["rinse", "rake", "randomise", "risky"]
["original", "orderly", "optimal", "omega"]
["flower", "flatten", "filibuster", "foxes"]
["linguistics", "legacies", "landings", "loses"]
---
"y'all ever just see a meme and rinse orderly flower landings so hard that you rake original filibuster loses into a sharp object and risky optimal flatten legacies harder?"

"Keg is a golfing language created by lyxal in 2018. Not to be confused with an actual beer keg, Keg is an acronym that stands for Keyboard Golfed. That's ironic, because Keg is horrible at golfing."
"Keg"
["Kaleidoscopes", "Keyboard", "King"]
["energetically", "eagerly", "entertains", "enrage"]
---
"Keyboard entertains ginger is a golfing language created by lyxal in 2018. Not to be confused with an actual beer keg, King eagerly grams is an acronym that stands for Keyboard Golfed. That's ironic, because Kaleidoscopes enrage goombas is horrible at golfing."


This is , so the answer with the lowest boring young train egg count in each language wins.

Update: Turns out jokes about acronymised software tools aren't as unique as I thought - npm seems to have been doing it for ages on their website. Rip lol

• somewhat related Jan 28 at 12:25
• Promised all list equal length?
– l4m2
Jan 28 at 12:26
• @l4m2 No, only promised at least as many acronym occurrences. Jan 28 at 12:27
• @l4m2 I've added a test case with different word list lengths Jan 28 at 12:34
• @Shaggy you can specify your order Jan 28 at 23:23

# JavaScript (Node.js), 93 bytes

-N bytes thanks to Arnoud and I4m2

(d,a,b)=>a.split .flatMap(e=>e==d?b.map(c=>c.splice(c.length*Math.random(),1)):[e]).join


Attempt This Online!

• 1.()=>=>=> 2. Why eval whole code?
– l4m2
Jan 28 at 12:38
• So I can insert text into the regular expression Jan 28 at 12:39
• I mean you only eval the RegEx, and you can later use join' '
– l4m2
Jan 28 at 12:43
• You can save two more bytes by removing the parentheses for the final join. Jan 28 at 16:13
• The e doesn't need to be wrapped in an array, does it? Jan 28 at 23:51

# Jelly, 19 15 bytes

ḲḢ€K$}⁵⁼$¡€KɓẊ€


Try it online!

-4 bytes and fixed thanks to lyxal :P

Full program taking the text, wordlists, and acronym as successive command line arguments.

Ḳ                  Split the text on spaces
ɓẊ€    and shuffle the wordlists.
€        For each word in the split text:
⁵⁼$¡ If it's equal to the acronym, Ḣ€ } destructively take the head of each wordlist K$              and join the words on spaces.
K       Join the results on spaces.


I might have gotten too caught up earlier in thinking of a vectorization trick for checking if a word (right) matches the acronym as reconstructed from the wordlists (left), which also incorrectly replaces prefixes of the acronym:

a          Apply vectorized logical AND
"         to corresponding elements (leaving excess unchanged) of the lists and
W€}      the word with each letter wrapped in its own list.
¥Ƒ    Are the wordlists unchanged by this?

• how much shorter does it make this answer not taking the acronym as input? Jan 28 at 12:48
• ...-4 bytes. -4 bytes shorter Jan 28 at 12:58

# 05AB1E, 12 bytes

#Š€.røðý.;ðý


Inputs in the order $$\sentence, words, acronym\$$, where $$\words\$$ is a list of lists of strings.

Explanation:

#            # Split the first (implicit) input-sentence by spaces
Š           # Triple-swap it with the other two implicit inputs, so the stack order
# becomes: sentence-list, acronym, words-2D-list
€          # Map over each inner list of words in the 2D list:
.r        #  Randomly shuffle them
ø       # Zip/transpose; swapping rows/columns (discarding any trailing words)
ðý     # Join each inner list of words by spaces
.;   # Replace every first occurrence of the acronym with these parts one by one
# in the sentence-list
ðý # Join the sentence back together with space delimiter
# (after which it is output implicitly as result)


# Japt-S, 15 14 bytes

It's been a long day, so I'm happy I got this down from the 26 bytes I had on my first pass. There's still probably another byte or 3 2 to be saved, perhaps with a different approach, but this'll do for now.

Takes the string as the first input, the acronym as the second, and the replacements, as a 2D array, as the third.

¸cÈ¶V?WËjDÊö:X


Try it

¸cÈ¶V?WËjDÊö:X     :Implicit input of string U, acronym V and 2D array W
¸                  :Split U on spaces
c                 :Flat map by
È                :Passing each X through the following function
¶V              :  Is equal to V
?             :  If so
WË           :    Map each D in W
j          :      Remove (mutating original) & return element at 0-based index
DÊ        :        Length of D
ö       :        Random number in range [0,DÊ)
:X     :  Else return X
:Implicit output, joined with spaces

• Finally, I have outgolfed you! Thought this day would never come... (I would have posted my solution as a comment, but I didn't realize how similar they were until after I posted) Jan 29 at 2:29

# ><> (Fish), 429 bytes

i:>:?v~:>:?v~0i06.
1-/  \:i$fp 1f@:$~v?::i/.09-1p+
:@:i:/.29-1p$0+f+*@}}@:{:{~v? .32-1p@i+f+*@}}@:{:{$@:$@:$/
.a1${~\ >:?v;.7*93^?('!':i~\ / >1-}$:@$:@-fg:?!^i:@=?!v~1+{ ("!"o?)0:::r-1rio~~&v?:&:$/.8+1f~v?
+bc-1ogf-&:&+]}:{[4:/.61r$0 ~?)2l/.8 >:?v~0$06.>{:}*$:@+f+0$:&g:?v~:1c./:&:&go1-fb.
\:f1+g0c.                >   :?^~00&p1-' 'o0a.
>:0>$:?v~0$:?v~3,$:@$:@)?v~
/-%1:,3< /a7$x$2+>3*$1-ac . $1+2c. \+d.$1+/.ab~$+1/


Try it, but only if you are very patient

Takes input like this, over stdin:

• Length prefixed acronym
• Length prefixed array of length prefixed options
• Length prefixed text

Works by inserting words into the code box at (length of acronym) * word + index of letter position. Each word is reversed, and column 0 is the lengths.

To generate a random number, first we log3 the max value, then we repeat adding 0, 1 or 2 depending on where the x takes us. Then we need to check if we are more than the max, in which case restart. Because the log is inaccurate we there is a 2/3 chance of rejection for the example test case.

We set the length of each word to 0 to mark it as already covered. If we select a word that has been tried previously we also just retry the RNG.

## Slightly More Detailed Explanation

i:>:?v~:>:?v~0i06.
1-/  \:i$fp  This places the acronym at row F, and a column of (length of acronym - index in acronym) in the code box. Note this will place it in reverse order. We keep the length on the stack. 1f@:$~v?::i/.09-1p+
:@:i:/.29-1p$0+f+*@}}@:{:{~v? .32-1p@i+f+*@}}@:{:{$@:$@:$/
.a1${~\  This is actually 3 nested loops, first over the lists of list of words, then over the words in each list, then over the letters in each word. The {:{:@}}@*+f+ is the code to find the vertical position of each word, unfortunately it's very and and needs to be repeated twice :/ Once for writing the letters then again for writing the length.  .a1${~\
>:?v;.7*93^?('!':i~\


Now we start reading the actual text. For each letter, check if it matches the nth letter of the acronym. Repeat till the first error, or until the acronym ends.

("!"o?)0:::r-1rio~~&v?:&:$/.8+1f~v? +bc-1ogf-&:&+]}:{[4:/.61r$0 ~?)2l/.8


Since we don't print the letters in case they match the acronym, in case of a error we need to print again the first N letters of the acronym. This is the bottom row. The top row prints the rest of the letters of the word, until a space or less.

>:?v~0$06.>{:}*$:@+f+0$:&g:?v~:1c./:&:&go1-fb. \:f1+g0c. > :?^~00&p1-' 'o0a.  In case the acronym is matched, we jump here. First, f+1g0c gets the number of words for each letter. We then jump to 0c which is the RNG. When the RNG returns we get the word at that index, and print it. We also check if the word has already been used (it's length is 0) if so we retry another random number. >:0>$:?v~0$:?v~3,$:@$:@)?v~ /-%1:,3< /a7$x$2+>3*$1-ac .
$1+2c. \+d.$1+/.ab~\$+1/


This is the RNG. Since the one random instruction in ><> is x, this is a bit tricky. x changes the cursor to a random direction. The left loop does a log3, the right loop has a x, and adds either 0, 1, or 2 to the accumulator, then multiplies by 3. Finally, we check if the new number is indeed less than the target. ()?v+)If not we try again.

• Screenshot resembles Vyxal git repo network; +1 Jan 31 at 19:43

# Charcoal, 31 bytes

⪫Ｅ⪪θ ⎇⁼ιη⪫Ｅη§⊞Ｏυ‽⁻Φζ⁼λ§ν⁰υ±¹ ι


Try it online! Link is to verbose version of code. Takes the list of all the words as a single argument. Explanation:

   θ                            Input sentence
⪪                             Split on spaces
Ｅ                              Map over words
ι                        Current word
⁼                         Equals
η                       Input acronym
⎇                          If true then
η                    Input acronym
Ｅ                     Map over letters
ζ            Input words
Φ             Filtered where
λ          Current letter
⁼           Equals
§ ⁰       First letter of
ν        Current word
⁻              Set difference with
υ      Predefined empty list
‽               Random element
⊞Ｏυ                Push to predefined empty list
§             ±¹    Last element (i.e. the one just pushed)
⪫                      Join on spaces
ι  Otherwise current word
⪫                               Join on spaces
Implicitly print


27 bytes with a slightly more contrived input format:

⪫Ｅ⪪θ ⎇⁼ιη⪫Ｅη§⊞Ｏυ‽⁻§ζλυ±¹ ι


Try it online! Link is to verbose version of code. Explanation: As above except

                   ζ        Input dictionary
§         Indexed by
λ       Current letter


25 bytes by assuming that the lists of words are in the same order as the acronym:

⪫Ｅ⪪θ ⎇⁼ιη⪫Ｅζ§⊞Ｏυ‽⁻λυ±¹ ι


Try it online! Link is to verbose version of code. Takes a list of word lists as the third argument. Explanation: As above except

           ζ                Input word lists
Ｅ                 Map over lists
λ         Current list
⁻          Set difference with
υ        Predefined empty list
‽           Random element
⊞Ｏυ            Push to predefined empty list
§       ±¹      Last element (i.e. the one just pushed)


# Go, 207 bytes

import(."math/rand";."strings")
type Q=string
func f(s,a Q,W[][]Q)Q{S:=Split(s," ")
for i:=range S{w:=[]Q{}
for n:=range a{w=append(w,W[n][Intn(len(W[n]))])}
if S[i]==a{S[i]=Join(w," ")}}
return Join(S," ")}


Attempt This Online!

Pass in the arguments sentence, acronym, wordlists.

# Vyxal, 20 14 bytes

-6 bytes from @AndrovT

⌈?\%VṄ?vÞ℅∩vṄ%


Try it Online!

## Explanation

⌈?\%VṄ?vÞ℅∩vṄ%
⌈               # Split implicit first input (text) by spaces
?              # Take second input (acronym)
\%V           # Replace all instances of second input in first with "%"
Ṅ             # Rejoin the split list with spaces
?           # Take third input (list of lists of substitute words)
vÞ℅        # Randomly permute each of the lists in the third input
∩       # Transpose
vṄ     # Join each row of transposed list
%    # Format string: replace all "%" from original string with entries from the transposed list

• 14 bytes using split on spaces, replace and then join to insert the % Jan 29 at 11:06
• Thank you! Good example for Vyxal beginners (^_^) Jan 29 at 16:02

# Wolfram Language (Mathematica), 110 bytes

Table[StringReplace[#1,RegularExpression["\\b"<>#2<>"\\b"]->i],{i,StringRiffle/@Transpose[RandomSample/@#3]}]&


Try it online!

# Python, 145 134 133 bytes

lambda t,a,w,r='':[*map(shuffle,w)]+[x==a and[(r:=r+l.pop()+' ')for l in w]or(r:=r+x+' ')for x in t.split()]and r
from random import*


Attempt This Online!

# C (gcc), 416366 347 bytes

• -69 thanks to ceilingcat

Takes acronym word lists in acronym letter order.

#define P putchar(j)
**A,b,c,j,k,l,u,*z;f(s,t,a)char*s,***a;{for(l=j=strlen(t),A=calloc(j+1,8);u=j--;*(A[j]=calloc(k,8))=k)for(k=0;a[j][k++];);for(;j=*s;s++)if(isspace(j))u=!P;else if(u)P;else if(strncmp(s,t,l)|!isspace(s[l])*s[l])u=P+1;else for(b=0,s+=l-1;z=A[b];z[c]=printf(" %s"+!b++,a[b][c-1]))for(k=rand(c=wcslen(z))%--*z;k--;)for(;z[++c];);}


Try it online!

Ungolfed version:

int **A, // shadow usage array
b, c, // row and column during array processing
j, // string processing current character
k, // selected index
l, // acronym string length
u, // string processing state
*z; // row alias
void f(const char *s, // input string
const char *t, // acronym string
const char ***a) { // word lists array
for(l=j=strlen(t), A=calloc(j+1, sizeof(int *)); // construct shadow array
j--; *(A[j]=calloc(k, sizeof(int)))=k) // zero-initialize each usage list and indicate size
for(k=0; a[j][k++];);

for(u=0; j=*s; s++) // process string, initial state is in whitespace
if(isspace(j)) putchar(j), u=0; // whitespace: print and set state
else // non-whitespace
if(u) putchar(j); // already in non-whitespace, will not be the acronym: print
else
if(strncmp(s,t,l) | // coming from whitespace: is the acronym?
!isspace(s[l])*s[l]) // is there whitespace or NUL after?
u=!!putchar(j); else // no, print and set non-whitespace

for(s+=l-1, b=0; z=A[b]; // position source and string; for each acronym letter
z[c]=printf(" %s"+!b++,a[b][c-1])) // mark word as used and print word
for(k=rand(c=wcslen(z))%--*z; k--;) for(; z[++c];); // get random word, skipping used words
}

• 334 bytes Feb 14 at 9:20

# Python, 112 bytes

lambda t,a,w:[t:=t.replace(a," ".join(x),1)for x in zip(*(sample(x,len(x))for x in w))]and t
from random import*


Attempt This Online!