Try to convert words to two different unicode fonts.


Your task is to transform your input string into the 𝖬𝖺𝗍𝗁 𝖲𝖺𝗇𝗌 and 𝗠𝗮𝘁𝗵 𝗦𝗮𝗻𝘀 𝗕𝗼𝗹𝗱 unicode characters.

All uppercase words should become lowercase 𝗠𝗮𝘁𝗵 𝗦𝗮𝗻𝘀 𝗕𝗼𝗹𝗱 words.

  • For Example: WORD -> 𝘄𝗼𝗿𝗱

All lowercase words should become 𝖬𝖺𝗍𝗁 𝖲𝖺𝗇𝗌 words

  • For example: other words -> 𝗈𝗍𝗁𝖾𝗋 𝗐𝗈𝗋𝖽𝗌

All mixed case words should remain unchanged

  • For example: Mixed Case Words -> Mixed Case Words

Periods and spaces should remain unchanged.

Words are separated by spaces or periods

  • For example (highlighted things are words):

Hello. This is a word. S.O. are these

  • Inputs: A string containing letters, spaces, and periods ([A-Za-z .]+)
  • Output: The formatted string

As this is a golfing challenge, the lowest number of bytes wins

Example Input and Output


This is an example STRING that c.o.U.L.d. be INPUTTED. It can CONTAIN multiple sentences.


This 𝗂𝗌 𝖺𝗇 𝖾𝗑𝖺𝗆𝗉𝗅𝖾 𝘀𝘁𝗿𝗶𝗻𝗴 𝗍𝗁𝖺𝗍 𝖼.𝗈.𝘂.𝗹.𝖽. 𝖻𝖾 𝗶𝗻𝗽𝘂𝘁𝘁𝗲𝗱. It 𝖼𝖺𝗇 𝗰𝗼𝗻𝘁𝗮𝗶𝗻 𝗆𝗎𝗅𝗍𝗂𝗉𝗅𝖾 𝗌𝖾𝗇𝗍𝖾𝗇𝖼𝖾𝗌.


Math Sans Bold: 𝗮𝗯𝗰𝗱𝗲𝗳𝗴𝗵𝗶𝗷𝗸𝗹𝗺𝗻𝗼𝗽𝗾𝗿𝘀𝘁𝘂𝘃𝘄𝘅𝘆𝘇 (characters 120302 through 120327)

Math Sans: 𝖺𝖻𝖼𝖽𝖾𝖿𝗀𝗁𝗂𝗃𝗄𝗅𝗆𝗇𝗈𝗉𝗊𝗋𝗌𝗍𝗎𝗏𝗐𝗑𝗒𝗓 (characters 120250 through 120275)

  • 3
    \$\begingroup\$ Welcome to PPCG! \$\endgroup\$ – Laikoni Jan 25 '18 at 23:26
  • 6
    \$\begingroup\$ ♫ Philosophy's just 𝗆𝖺𝗍𝗁 𝗌𝖺𝗇𝗌 rigor, sense, and practicality... ♫ \$\endgroup\$ – Esolanging Fruit Jan 26 '18 at 5:12
  • 13
    \$\begingroup\$ Whoa! You got a title in bold in the sidebar? What? I... don't understand.... is the internet breaking? Have you broken the internet? \$\endgroup\$ – Zizouz212 Jan 26 '18 at 6:38
  • 26
    \$\begingroup\$ i.stack.imgur.com/R4V3C.png I came here thinking this challenge was about stacking boxes, bar charts or something... \$\endgroup\$ – Matteo Italia Jan 26 '18 at 7:29
  • 8
    \$\begingroup\$ This shouldn't have been close hammered. This problem is significantly more difficult than a simple character transliteration. The leading answer in the cited challenge can not easily nor competitively be transferred using that same method (afaict, my retina isn't great) \$\endgroup\$ – Conor O'Brien Jan 26 '18 at 15:20

12 Answers 12


QuadR, 45 43 bytes

-2 thanks to ngn.

⎕UCS a+(2>≢b)×120153+84×⊃b←∪96>a←⎕UCS⍵M

Since TIO scrambles Unicode output from QuadR, here is a screenshot of using QuadR as an APL library in an interactive session: interactive session transcript

\w+ replace words with the result of applying the following code to them:

⍵M the found word
⎕UCS the Universal Character Set code points of that
a← store that in a
96> 0 or 1 for whether 96 is greater than each of those
∪ take just the unique; [0] or [1] or [0,1] or [1,0]
b← store that in b
⊃ pick the first from that
84× multiply 84 with that
120153+ add 120153 to that
(…)× multiply the following with that:
 ≢b the tally (length) of b (1 if single-case, 2 if mixed-case)
 2> 0 or 1 for whether two is greater than that (1 if single-case, 0 if mixed-case)
a+ the original code points added to that
⎕UCS convert the resulting code points back to characters


APL (Dyalog Unicode), 63 57 53 bytes

-6 thanks to Erik the Outgolfer. -4 thanks to ngn.

Anonymous tacit prefix function.

'\w+'⎕R{⎕UCS a+(2>≢b)×120153+84×⊃b←∪96>a←⎕UCS⍵.Match}

Since TIO scrambles Unicode output from Dyalog APL, here is a screenshot of the code in action:

code in action

'\w+'⎕R PCRE Replace words with the result of applying the following…

{...} anonymous lambda:

 ⍵.Match the found word

 ⎕UCS the Universal Character Set code points of that

 a← store that in a

 96> 0 or 1 for whether 96 is greater than each of those

 ∪ take just the unique; [0] or [1] or [0,1] or [1,0]

 b← store that in b

 ⊃ pick the first from that

 84× multiply 84 with that

 120153+ add 120153 to that

 (…)× multiply the following with that:

  ≢b the tally (length) of b (1 if single-case, 2 if mixed-case)

  2> 0 or 1 for whether two is greater than that (1 if single-case, 0 if mixed-case)

 a+ the original code points added to that

 ⎕UCS convert the resulting code points back to characters

  • \$\begingroup\$ 57 bytes: '\b([A-Z]+|[a-z]+)\b'⎕R{⎕UCS(⎕UCS+120153+84×∊∘⎕A)⍵.Match} \$\endgroup\$ – Erik the Outgolfer Feb 20 '18 at 12:55
  • \$\begingroup\$ @EriktheOutgolfer Thanks. Why didn't I think of going tacit‽ \$\endgroup\$ – Adám Feb 20 '18 at 14:36
  • \$\begingroup\$ I don't know, but it happens to me when I'm tired. :) \$\endgroup\$ – Erik the Outgolfer Feb 20 '18 at 14:39
  • \$\begingroup\$ @EriktheOutgolfer Actually, I think I wrote this one from home using my wife's computer without APL keyboard layout… \$\endgroup\$ – Adám Feb 20 '18 at 14:47
  • \$\begingroup\$ @Adám that regex is too long; you're better off using \w+ and computing the amount to add to the codepoints in the dfn: '\w+'⎕R{⎕UCS a+(2>≢b)×120153+84×⊃b←∪96>a←⎕UCS⍵.Match} \$\endgroup\$ – ngn Feb 20 '18 at 20:27

Clean, 268 265 232 224 bytes

As a neat bonus, this works with strings containing any character. Including nulls.

import StdLib,StdInt,StdBool,Text.Unicode,Text.Unicode.UChar
$c|l c=fromInt(toInt c+120153+if(u c)84 0)=c
?[h,s:t]=[if(u h<>isLower s)($c)c\\c<-[h,s:t]]
@s=[y\\x<-groupBy(\a b=l a&&l b)s,y<- ?x]

Try it online!

Defines the function @, taking a UString and returning a UString

  • 3
    \$\begingroup\$ Is it also a clean bonus? :D \$\endgroup\$ – Conor O'Brien Jan 26 '18 at 17:22

C, 292 characters, 448 bytes (in UTF-8)

char*t;s,i,k;p(l){for(l=s=*t/96,i=k=strlen(t);i--;)t[i]/96-s&&++l;for(l=l-s&&write(1,t,k);!l&++i<k;)write(1,s?"𝖺𝖻𝖼𝖽𝖾𝖿𝗀𝗁𝗂𝗃𝗄𝗅𝗆𝗇𝗈𝗉𝗊𝗋𝗌𝗍𝗎𝗏𝗐𝗑𝗒𝗓"+t[i]*4-388:"𝗮𝗯𝗰𝗱𝗲𝗳𝗴𝗵𝗶𝗷𝗸𝗹𝗺𝗻𝗼𝗽𝗾𝗿𝘀𝘁𝘂𝘃𝘄𝘅𝘆𝘇"+t[i]*4-260,4);}f(char*s){char b[strlen(s)];for(t=b;*s;++s)*s<47?(*t=0),p(t=b),putchar(*s):(*t++=*s);*t=0;p(t=b);}

Try it online!



    for (l=s=*t/96, i=k=strlen(t); i--;)
        t[i]/96-s && ++l;

    for (l=l-s&&write(1, t, k); !l&++i<k;)
        write(1, s ? "𝖺𝖻𝖼𝖽𝖾𝖿𝗀𝗁𝗂𝗃𝗄𝗅𝗆𝗇𝗈𝗉𝗊𝗋𝗌𝗍𝗎𝗏𝗐𝗑𝗒𝗓"+t[i]*4-388
                   : "𝗮𝗯𝗰𝗱𝗲𝗳𝗴𝗵𝗶𝗷𝗸𝗹𝗺𝗻𝗼𝗽𝗾𝗿𝘀𝘁𝘂𝘃𝘄𝘅𝘆𝘇"+t[i]*4-260, 4);

    char b[strlen(s)];

    for (t=b; *s; ++s)
        *s<47 ? (*t=0), p(t=b), putchar(*s) : (*t++=*s);

    *t = 0;

Java 8, 221 219 203 201 bytes

s->{StringBuffer r=new StringBuffer();for(String x:s.split("(?<=[\\. ])|(?=[\\. ])"))x.codePoints().forEach(c->r.appendCodePoint(c+(x.matches("[A-Z]+")?120237:x.matches("[a-z]+")?120153:0)));return r;}

I have to use a StringBuffer instead of a regular String to use .appendCodePoint, unfortunately..


Try it online.

s->{                           // Method with String parameter and StringBuffer return-type
  StringBuffer r=new StringBuffer();
                               //  Resulting StringBuffer
  for(String x:s.split("(?<=[\\. ])|(?=[\\. ])"))
                               //  Split by space or dot, and keep them as separate items,
                               //  and loop over all those substrings
   x.codePoints().forEach(c->  //   Inner loop over the codepoints of that substring
      r.appendCodePoint(       //    Convert int to char, and append it to the result:
        c                      //     The next codepoint of the substring
        +(x.matches("[A-Z]+")? //     If the word is fully uppercase:
           120237              //      Add 120237 to convert it to Math Sans Bold
          :x.matches("[a-z]+")?//     Else-if the word is fully lowercase:
           120153              //      Add 120153 to convert it to Math Sans
          :                    //     Else (mixed case, or a dot/space)
           0)));               //      Leave the codepoint (and thus the character) as is
  return r;}                   //  Return the resulting StringBuffer

Haskell, 172 170 bytes

(s#w)r=[x|all(`elem`s)w,c<-w,(x,k)<-zip r s,c==k]
t w=filter(>[])[['A'..'Z']#w$['𝗮'..],['a'..'z']#w$['𝖺'..],w]!!0
f s|(a,b:c)<-span(>'.')s=t a++b:f c|1>0=t s

Try it online!

Fairly straightforward. The # operator takes the set s of charcters (upper or lower case) the word w, and the math sans set r. It returns the word in the math sans font if all the characters in the word are in s or the empty list otherwise. The t function takes a word and tries all three possiblities (all upper, all lower, or mixed), returning the first one that isn't empty. The f function finds the first word by using span, transforming it with t and concatenating it with the separator (either . or space) and recurring on the rest of the string. The alternate case is for if span can't find a separator; we just transform the string.

Edit: Thanks to @Laikoni for taking off 2 bytes! I'm not used to the whole "operator that takes three arguments" thing

  • 1
    \$\begingroup\$ (['A'..'Z']#w)['𝗮'..] can be ['A'..'Z']#w$['𝗮'..]. \$\endgroup\$ – Laikoni Jan 27 '18 at 0:37

Jelly, 34 bytes


Try it online!

Full program.

  • 2
    \$\begingroup\$ It's probably obvious to professional Jellyists, but could you add a brief explanation to show ẇƬƒ is going on here? \$\endgroup\$ – Mick Mnemonic Jan 26 '18 at 23:21
  • \$\begingroup\$ @MickMnemonic sorry, right now I don't have time to \$\endgroup\$ – Erik the Outgolfer Jan 26 '18 at 23:29

Retina, 84 bytes


Try it online! Explanation: Retina is a .NET application and therefore works in UTF-16 internally. Unfortunately as the Math Sans characters aren't in the BMP I can't directly transliterate them because the number of code points differs. Worse, I can't use unpaired surrogates at all. Instead, I shift the appropriate words into characters in the range 0xFF-0x7FF which conveniently only take two bytes to encode, plus I also prefix them with the 0x135 character. Finally I map that range onto the a range that overlaps the unpaired surrogates, creating valid BMP pairs.


Python 3, 173 122 120 bytes

lambda s:''.join(chr(ord(c)+120153*t.islower()+120237*t.isupper())for t in re.split(r'\b(\w+)\b',s)for c in t)
import re

-51 bytes from ShreevatsaR

-2 bytes from abccd

Try it online!

Splits on word boundaries (re.split(r'\b(\w+)\b',s)), then maps lowercase words to 𝗅𝗈𝗐𝖾𝗋𝖼𝖺𝗌𝖾 𝗆𝖺𝗍𝗁 𝗌𝖺𝗇𝗌 (+120153*t.islower()), and uppercase words to 𝗯𝗼𝗹𝗱 𝗺𝗮𝘁𝗵 𝘀𝗮𝗻𝘀 (+120237*t.isupper()), and leaves mixed-case words alone, then joins the words back up.

Ungolfed and un-lambda-ed:

def f(s):
    words = re.split(r'\b(\w+)\b', s)
    ret = ''
    for word in words:
        for char in word:
            if word.isupper():
                ret += chr(ord(c) + 120237)
            elif word.islower():
                ret += chr(ord(c) + 120153)
                ret += c
    return ret
  • \$\begingroup\$ could it be less bytes if you set a variable to either 120237 or 120153 depending on if it was upper or lower? It looks like it might \$\endgroup\$ – pfg Jan 26 '18 at 19:08
  • 1
    \$\begingroup\$ @pfg Indeed, can easily shave off 13 bytes (down to 160). \$\endgroup\$ – ShreevatsaR Jan 26 '18 at 20:10
  • \$\begingroup\$ @pfg Actually, replacing the map-lambda with (easier to read) comprehensions brings it down to 149 bytes. \$\endgroup\$ – ShreevatsaR Jan 26 '18 at 20:18
  • 5
    \$\begingroup\$ 122 :-) I'll stop here; quite proud of how golfing it further has made it easier to read. Only in Python! \$\endgroup\$ – ShreevatsaR Jan 26 '18 at 21:20
  • 2
    \$\begingroup\$ -2 by getting rid of spaces before for \$\endgroup\$ – abccd Jan 27 '18 at 6:00

Japt, 34 33 32 31 bytes

Includes an unprintable (charcode 153) after the last #.

rVv Èc+#x#

Try it


                        :Implicit input of string U
r                       :Replace
   "%b%A+%b"            :/\b[A-Z]+\b/g
 V=                     :Assign ^that to variable V
            È           :Run each match through a function
             c          :Map over the codepoints of the current match
              +#x#í     :  Add 120237
\n                      :Assign the result of that replacement to variable U
rVv                     :Another replacement, this time with V lowercased to give us the RegEx /\b[a-z]+\b/g
    Èc+#x#              :And, again, map over the codepoints of each match, this time adding 120153 to each

Original 32 Byte Japt v2 Solution


Try it

r                                     :Replace
 /\b(\A+|\a+)\b/                      :...all matches of this RegEx (\A=[A-Z], \a=[a-z])
                È                     :Pass each match through a function, with X being the current match
                 c_                   :Pass the codepoints of X through a function
                   +                  :Add to the current codepoint
                    #x#               :120153 (there's an unprintable after the second #)
                        +#T           :Plus 84
                           *          :  Multiplied by
                            (X¶u      :  Is X equal to its uppercase self
  • 1
    \$\begingroup\$ Mind adding an XXD dump? \$\endgroup\$ – Stan Strum Jan 27 '18 at 5:15
  • \$\begingroup\$ A reversible hexdump? For the unprintables. \$\endgroup\$ – Stan Strum Jan 27 '18 at 22:26

05AB1E, 33 bytes


Try it online!


JavaScript (ES6), 99 114 113 bytes


(Thanks to @pfg for pointing out an important flaw in my first solution.)

-1 bytes thanks to @Neil.


let f = 


d.innerHTML=f('This is an example STRING that c.o.U.L.d. be INPUTTED. It can CONTAIN multiple sentences.');
<p id="d">

  • \$\begingroup\$ This only works with the HTML because of the &#, to do it with pure JS you would need to use String.fromCodePoint(120237) which would increase the size \$\endgroup\$ – pfg Jan 26 '18 at 21:30
  • \$\begingroup\$ Don't yet grok that, but I'll come back to it later, thanks. \$\endgroup\$ – Rick Hitchcock Jan 26 '18 at 21:33
  • \$\begingroup\$ let a = s=>s.replace(/\b([A-Z]+|[a-z]+)\b/g,e=>e.replace(/./g,f=>String.fromCodePoint(f.charCodeAt(0)+120153+(f<'a')*84))) works pure JS but adds many extra bytes \$\endgroup\$ – pfg Jan 26 '18 at 21:33
  • \$\begingroup\$ Ah, I understand! Daggum. \$\endgroup\$ – Rick Hitchcock Jan 26 '18 at 21:38
  • 3
    \$\begingroup\$ Save 1 byte by using charCodeAt() without the 0. \$\endgroup\$ – Neil Jan 27 '18 at 0:22

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.