21
\$\begingroup\$

Inspired by the upcoming winter bash event

Objective

Add a hat ^,´ or ` to a single vowel in each word of the input.

Rules

  • The hat and the vowel must be chosen at random. Each hat has to appear with the same probability (33%) and the vowels must have the same probability within the valid vowels in the word (if the word have 2 valid vowels each must be 50% likely to be chosen) - or the closest that your language has.
  • Only AEIOUaeiouare considered vowels (sorry y)
  • Vowels with hats in the input DO NOT interfere with the rules (you can consider it as a consonant)
  • If the input has no vowel it won't be modified
  • Capitalization must be preserved.

Examples

winter > wintér
bash > bâsh
rhythm > rhythm
rng ftw > rng ftw
cat in the hat > cât ìn thê hát
dès > dès
tschüss > tschüss
principî > prìncipî
PROGRAMMING PUZZLES & code golf > PROGRÂMMING PÚZZLES & codé gòlf

Winning

This is so the shortest code wins

\$\endgroup\$
12
  • \$\begingroup\$ "at random (or the closest that your language has)" so if my language has no random commands I can just do something deterministic??? \$\endgroup\$
    – Fatalize
    Dec 16, 2016 at 22:52
  • \$\begingroup\$ @LuisMendo better? \$\endgroup\$
    – Rod
    Dec 16, 2016 at 22:55
  • \$\begingroup\$ @Fatalize not even get the current time random? \$\endgroup\$
    – Rod
    Dec 16, 2016 at 22:57
  • \$\begingroup\$ @Rod I don't have any language in mind. I'm just saying that you have to impose that it is random (and what kind of random) otherwise it makes no sense at all. \$\endgroup\$
    – Fatalize
    Dec 16, 2016 at 23:03
  • 1
    \$\begingroup\$ Are there any constraints on Unicode normalization of the input? A naive algorithm will see an NFC word like tschüss as having an accented u, but an NFD word like tschüss as having a non-accented u, and yet the two look identical. Likewise, are there constraints on Unicode normalization of the ouptut? (I'd imagine NFD output would be shorter in bytes to generate.) \$\endgroup\$
    – user62131
    Dec 17, 2016 at 20:08

8 Answers 8

3
\$\begingroup\$

Vyxal S, 17 bytes

⌈ƛAa[AT›℅3℅767+CṀ

Try it Online!

Some tricks stolen from Seggan's answer.

⌈ƛ                # Over each word
  Aa[             # If it has a vowel...
     A            # Is each a vowel?
      T           # Indices of vowels
       ℅›         # Choose a random one and increment it
         3℅       # Random choice from [1, 2, 3]
           767+   # Add 767
               C  # Make it a char
                Ṁ # Insert that at the specified index.
                  # (S flag) turn the word list into a sentence
\$\endgroup\$
5
  • \$\begingroup\$ Fails: you must put a diacritic on every word \$\endgroup\$
    – Seggan
    Jun 10 at 20:09
  • \$\begingroup\$ @Seggan Oops. One moment \$\endgroup\$
    – emanresu A
    Jun 10 at 20:10
  • \$\begingroup\$ That is, every word that has a vowel \$\endgroup\$
    – Seggan
    Jun 10 at 20:11
  • \$\begingroup\$ @Seggan Hopefully fixed. \$\endgroup\$
    – emanresu A
    Jun 10 at 20:13
  • \$\begingroup\$ Yep. Time to retrofit my answer now lol \$\endgroup\$
    – Seggan
    Jun 10 at 20:15
3
\$\begingroup\$

Vyxal S, 27 bytes

⌈:ƛAT:[℅|u];Zƛ÷›[nt3℅767+CṀ

Try it Online!

The result of my older answer mixed with emanresu A's answer.

Vyxal S, 37 35 33 bytes

⌈:ƛ≬k∨$cḟ:[℅|u];Zƛ÷›:[768:⇧ṡC℅Ṁ|_

Try it Online!

-2 bytes thanks to @Steffan

-2 bytes by inlining the register

I manually generated the diacritics list so this wouldn't have to be scored in UTF-8.

⌈:ƛ≬k∨$cḟ:[℅|u];Zƛ÷›:[768:⇧ṡC℅Ṁ|_ # 'S' flag joins the output on spaces
⌈                                  # Split the input on spaces
 :                                 # Duplicate
  ƛ            ;                   # Map the duplicate to...
   ≬    ḟ                          # Find the indexes in the word that satisfy the following condition:
      $c                           # Is the character contained in...
    k∨                             # "aeiouAEIOU"?
         :[   ]                    # If the list is not empty (i.e. the word has vowels)
           ℅                       # Choose a random index from the list
            |                      # Otherwise...
             u                     # Push -1
                Z                  # Zip the mapped list and the word list together
                 ƛ                 # Map the new list of pairs of words and indexes
                  ÷                # Push the word, then the index
                   ›               # Increment the index (0 becomes 1, -1 becomes 0)
                    :[             # If the index is not 0 (i.e. if there is a vowel)
                      768          # Push the charcode of the first diacritic
                         :         # Duplicate
                          ⇧        # Add 2
                           ṡ       # Inclusive range from 768 - 770
                            C      # Unicode value -> character for each number
                              ℅    # Choose a random diacritic
                               Ṁ   # And insert it before the vowel
                                |  # Otherwise...
                                 _ # Pop the duplicated index, leaving the word on the stack
\$\endgroup\$
1
  • \$\begingroup\$ 768:›:›W => 768:⇧ṡ \$\endgroup\$
    – Steffan
    Jun 9 at 20:27
3
\$\begingroup\$

JavaScript (Node.js), 118 bytes

Spec doesn't state what Unicode normalisation is allowed. Examples use NFC, but NFD is cheaper.

s=>s.replace(/\S+/g,w=>w.replace(x=/[aeiou]/gi,c=>v--?c:c+'̀́̂'[3*r()|0],v=(r=Math.random)()*w.match(x)?.length|0))

Attempt This Online! Note: My test does 1000 trials and checks to see if it got the example output. Occasionally it misses for the long ones.

This is basically the same as the old solution, just swapped out the vowel replacement function.

Old solution (NFC), 159 bytes

s=>s.replace(/\S+/g,w=>w.replace(x=/[aeiou]/gi,c=>v--?c:Buffer(c+c).map((n,j)=>j?n+68-4*!(n&12)-!(n&16)+3*r()|0:195),v=(r=Math.random)()*w.match(x)?.length|0))

Attempt This Online 2!

Ungolfed and somewhat explained

In UTF-8, all the accented character are 2 bytes, the first of which is always 195. The second can be calculated from the base letter.

char:       A   E   I   O   U   a   e   i   o   u
code:      65  69  73  79  85  97 101 105 111 117
 &12:       0   4   8  12   4   0   4   8  12   4
 &16:       0   0   0   0  16   0   0   0   0  16
    
with \:   128 136 140 146 153 160 168 172 178 185
with /:   129 137 141 147 154 161 169 173 179 186
with ^:   130 138 142 148 155 162 170 174 180 187

code to \: 63  67  67  67  68  63  67  67  67  68
code to /: 64  68  68  68  69  64  68  68  68  69
code to ^: 65  69  69  69  70  65  69  69  69  70
              |---same---|        |---same---|

For most letters, we just increase character code by 67 (for Aa, increase by 63, for Uu increase by 68), then offset by 0, 1, or 2 to add the hat.

s => s.replace(  // replace each word
  /\S+/g,
  w => w.replace(  // replace each vowel
    x = /[aeiou]/gi,
    c => v-- ?            // if this is not the vowel to replace
      c :                     // don't replace
      Buffer(c + c).map(      // otherwise create a Buffer of 2 bytes
        (n, j) => j ?         // map (replace) each byte; if it's the 2nd byte
          n                   // start at n
          + 68                // add 68
          - 4 * !(n & 12)     // subtract 4 if it's 'A' or 'a'
          - !(n & 16)         // subtract 1 if it's not 'U' or 'u'
          + 3 * r() | 0 :     // add randomly 0, 1, or 2 (and floor it)
          195                 // set the first byte to 195
      ),
    v = (r = Math.random)() * w.match(x)?.length | 0  // choose vowel
  )
)
\$\endgroup\$
2
  • \$\begingroup\$ Slight typo in your explanation, you've got // add 63, should be 68 \$\endgroup\$
    – jezza_99
    Jun 10 at 4:54
  • 1
    \$\begingroup\$ Ah, thank you! With the amount of transcribing numbers there's bound to be one that slipped through \$\endgroup\$ Jun 10 at 11:55
2
\$\begingroup\$

Perl 6, 101 bytes

~*.words.map: {(my$p=m:ex:i/<[aeiou]>/».to.pick)~~Mu:D??~S/.**{$p}<(/{("\x300".."\x302").pick}/!!$_}

Try it

Expanded:

~      # stringify (join with spaces)
*\     # Whatever lambda (input parameter)
.words # split into words

.map:  # for each word

{ # bare block lambda with implicit parameter 「$_」
  (

    my $p =

    m
    :exhaustive               # all possible matches
    :ignorecase
    /<[aeiou]>/\              # match a vowel

    ».to.pick                 # pick one of the positions

  ) ~~ Mu:D                   # is it defined ( shorter than 「.defined」 )

  ??                          # if 「$p」 is defined

    ~                         # stringify

    S/
      . ** {$p}               # match 「$p」 positions

      <(                      # ignore them
    /{
      ("\x300".."\x302").pick # pick one of the "hats" to add
    }/


  !!                          # if 「$p」 is not defined    
    $_                        # return the word unchanged
}
\$\endgroup\$
2
\$\begingroup\$

C#, 273 267 bytes

using System.Linq;A=s=>{var r=new System.Random();var a=s.Split(' ');return string.Join(" ",a.Select(w=>w.Select((c,i)=>"AEIOUaeiou".Any(d=>c==d)?i:-1).Where(x=>x>=0).ToList()).Select((l,i)=>l.Any()?a[i].Insert(l[r.Next(l.Count)]+1,""+(char)r.Next(768,771)):a[i]));};

repl.it demo

I really feels like I'm cheating, since I still add hats to already accented vowels created by combining characters. If that is not acceptable, let me know so I can add boilerplate codes declare this answer non-competing.

This thing adds a random character among U+0300 or U+0301 or U+0302, after a random vowel of each input word (if any).

Ungolfed (lambda body only)

var r=new System.Random();
// Split sentence to array of words
var a=s.Split(' ');
// Join the (hat-ed) words back to sentence
return string.Join(
    " ",
    // Select an IEnum of list of integers indicating the positions of vowels
    a.Select(w=>
        w.Select((c,i)=>
            // If it's vowel, return position (>=0), else return -1
            "AEIOUaeiou".Any(d=>c==d)?i:-1
        // Filter vowels only
        ).Where(x=>x>=0)
        .ToList()
    // Convert each list of integers to hat-ed words
    ).Select((l,i)=>
        l.Any()
            // Insert "something" into the word...
            ?a[i].Insert(
                // ...at the position after a random vowel in that word...
                l[r.Next(l.Count)]+1,
                // "something" is a random integer in [0x0300, 0x0303), then casted to UTF16 i.e. [U+0300, U+0302]
                ""+(char)r.Next(768,771))
            // List is empty => just return original word
            :a[i]));
\$\endgroup\$
1
\$\begingroup\$

Mathematica, 226 bytes

Join@@((p=Position[w=#,Alternatives@@(v=Characters@"aeiouAEIOU")];If[p!={},i=#&@@RandomChoice@p;w[[i]]=FromCharacterCode[ToCharacterCode["àèìòùÀÈÌÒÙ"][[#&@@Position[v,w[[i]]]]]+RandomInteger@2]];w)&/@Split[#,{##}~FreeQ~" "&])&

Unnamed function taking a list of characters as input and returning a list of characters. Easier-to-read version, slightly ungolfed as well:

 1  v = Characters["aeiouAEIOU"];
 2  a = ToCharacterCode["àèìòùÀÈÌÒÙ"];
 3  Join @@ (
 4    (p = Position[w = #, Alternatives @@ v]; 
 5      If[p != {},
 6        i = First[RandomChoice[p]]; 
 7        w[[i]] =
 8          FromCharacterCode[
 9            a[[ First[ Position[ v, w[[i]] ] ] ]] + RandomInteger[2]
10          ]
11        ]; w
12    ) &
13  ) /@ Split[#1, FreeQ[{##1}, " "] &] &

Line 13 splits the input into words (sublists of characters) at all the spaces; each word is operated on by the function defined by lines 4-12, and the results joined back together again in a single list by line 3.

Line 4 sets p to the list of indices indicating which characters of the word w are vowels. If there are any vowels (line 5), we make a random choice of one such index i (line 6) and then reset that single character of the word to a new character (lines 7-10). Finally we output the (possibly modified) word w.

To select the new character, we find where the vowel to be replaced sits in the string v and choose the corresponding character code from a. But to randomly select the three hats, we take that code and add a random integer between 0 and 2 (line 9) before converting back to a character. (Fortunately the behatted vowels all come in consecutive trios of UTF-8 character codes.)

\$\endgroup\$
1
\$\begingroup\$

Python 3, 170 bytes

from random import *
c=choice
print(' '.join([w,w[:i]+c('̀́̂')+w[i:]][i>0]for w in input().split()for i in[c([j+1 for j,x in enumerate(w)if x in 'aeiouAEIOU']or[0])]))

Ungolfed:

from random import choice
print(' '.join([
                   w,  # Don't modify the word if no vowels were found

                   w[:i] + choice('̀́̂') + w[i:]
               ][i > 0]
               for w in input().split()
                   for i in [choice([j + 1 for j, x in enumerate(w) if x in 'aeiouAEIOU']
                                    or [0])  # choice requires a non-empty sequence
                             ]))
\$\endgroup\$
2
  • 2
    \$\begingroup\$ You do not need the space between import and *. \$\endgroup\$
    – Mr. Xcoder
    Aug 29, 2017 at 21:46
  • \$\begingroup\$ j+1 for can be j+1for. \$\endgroup\$ Feb 8, 2018 at 15:09
0
\$\begingroup\$

Python 3, 162 bytes

from random import*
print(*(w.insert(*choice([(n+1,q)for n,c in enumerate(w)if c in'AEIOUaeiou'for q in'̀́̂']or[(0,'')]))or''.join(w)for*w,in input().split()))

Try it online!

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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