If you have a small child in your house, you may have come across foam bath letters. These can be moistened and stuck to flat surfaces such as tiles and the side of the bath to make words and messages.

The range of words and messages is somewhat limited if you only have one set though, since you only get 36 characters: uppercase letters A-Z, and digits 0-9: ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789. However, you can be cunning and abuse some of the letters and digits to form extra copies of other letters:

3: E
1: I
7: L (when rotated)
M: W (when rotated)
0: O
2: S (when flipped)
5: S
W: M (when rotated)
2: Z
5: Z (when flipped)
O: 0
6: 9 (when rotated)
L: 7 (when rotated)
9: 6 (when rotated)

Note that these are not all bi-directional, since it tends to be easier to read a digit as part of a word than a letter as part of a number.

Each set also comes in a range of colours, where each letter is coloured in sequence. For example, if your set has 3 colours, Red, Yellow and Blue, your set would be like:

  • Red: ADGJMPSVY147
  • Yellow: BEHKNQTWZ258
  • Blue: CFILORUX0369

Your task, therefore, is to take three parameters (in any suitable way) indicating a word or phrase, the number of colours to be used, and the number of sets you have, then to output a representation of the word or phrase, in a set of suitable colours, making use of substitutions if required. If it is not possible to make the word or phrase given the number of sets, instead output "Nope", in the appropriate colours.


In all of these, the first parameter is the word or phrase, the second is the number of colours, and the third is the number of sets available.

["bath", 3, 1]


["programming", 3, 2]


["puzzles", 3, 1]


["code golf", 5, 1]


["willow tree", 1, 1]


["impossible phrase", 8, 1]

NOPE - there is only 1 P in a set, and no valid substitutions


  • You can default to 1 set if this helps (e.g. a default parameter value of 1), but you must support multiple sets if requested. The number of sets you have will always be a positive non-zero integer.
  • If you have multiple sets, they are all coloured in the same way: if A is red in the first set, it will be red in the second set too. The colours are applied per set, not to all sets in a given invocation
  • You must support 1 to 36 colours - 1 means all characters are the same colour, 36 means they are all distinct. There will only be integer numbers of colours.
  • You can use any colours, as long as they are visually distinct - if you are outputting to terminal, you can use a combination of foreground and background as a "colour", although you must not use the same colour for foreground and background
  • Your output must use the characters that you actually use, not the characters from the input. For example, if you had ["willow", 1, 1] as input, you could output WIL7OM with 7 and M rotated 180 degrees
  • You should use the correct letters first, then substitute: MI7LOW is incorrect for ["willow", 1, 1]
  • You can use any substitution for a given character: SO5 and SO2 with a flipped 2 are both valid for "SOS"
  • Multiple invocations of your code for the same input do not have to produce identical output
  • Output should be in uppercase, but input can be in any mix of upper and lower case
  • It should be obvious, but a space does not have a colour


  • This is code golf, but with semi-graphical output (I'm not sure if you can do flipped text in a terminal, but am interested to see if Pyth has a built in for it)
  • Standard loopholes apply
  • There are no bonuses for functions, but please vote up interesting answers
  • Include some screenshots of your output in your answers

Related Challenges

  • 1
    \$\begingroup\$ What if it's not possible to output "NOPE" given the sets? \$\endgroup\$
    – Riker
    Commented Dec 18, 2017 at 17:43
  • \$\begingroup\$ @Riker A set contains the 36 given characters. When would it be impossible to output NOPE? \$\endgroup\$
    – mypetlion
    Commented Dec 18, 2017 at 18:10
  • \$\begingroup\$ @mypetlion when there's no sets... \$\endgroup\$
    – Riker
    Commented Dec 18, 2017 at 18:24
  • \$\begingroup\$ @Riker Read "The number of sets you have will always be a positive non-zero integer." \$\endgroup\$
    – mypetlion
    Commented Dec 18, 2017 at 19:49
  • 2
    \$\begingroup\$ Foam bath letters are a good introduction to leet speak... \$\endgroup\$
    – Arnauld
    Commented Dec 18, 2017 at 21:24

1 Answer 1


HTML/JavaScript (with jQuery)/CSS - non-competing/non-golfed

Just to get the ball rolling, and show that it's not impossible, here is a non-golfed, non-competing implementation that takes a naive approach to the task.

It first creates a string of all the possible characters (tempalph), by joining as many copies of the alphabet as there are sets. Then it iterates through the phrase, putting each letter of the phrase into an output variable, and blanking the first instance of that letter from tempalph. If it can't (the letter doesn't exist), it checks if the letter is replaceable with something left in the string. If that also fails, it outputs a pre-defined "NOPE" output. Assuming it doesn't hit the "NOPE" case, it returns the output string and puts it into a DIV on the page.

It then loops through the base alphabet string, and if a letter has been used, assigns it an appropriate CSS class, each of which has a pre-defined colour set.

It uses HTML inputs for input, and updates on keyup in them.

Try it at JSFiddle

Example for ["BATH", 3, 1]:



var alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";

function getPhrase(phrase, sets){
    var modphrase = "";
  var nope = 'NOPE';
  var re = /^[A-Z0-9 ]+$/;
  if (re.test(phrase)){
    // might be valid - need to check there are enough characters available
    // At this point, need to look at specifically what characters are needed

    var tempalph = "";
    for ( var i=0; i'+char+'';
        if (tempalph.indexOf(char) != -1){
        tempalph = tempalph.replace(char, "#");
      } else {
            case "E":
            if (tempalph.indexOf("3") != -1){
                    tempalph = tempalph.replace("3", "#");
              modchar = '3';
                } else {
                return nope;
          case "I":
            if (tempalph.indexOf("1") != -1){
                    tempalph = tempalph.replace("1", "#");
              modchar = '1';
                } else {
              return nope;
          case "L":
            if (tempalph.indexOf("7") != -1){
                    tempalph = tempalph.replace("7", "#");
              modchar = '7';
                } else {
              return nope;
          case "M":
            if (tempalph.indexOf("W") != -1){
                    tempalph = tempalph.replace("W", "#");
              modchar = 'W';
                } else {
              return nope;
          case "O":
            if (tempalph.indexOf("0") != -1){
                    tempalph = tempalph.replace("0", "#");
              modchar = '0';
                } else {
              return nope;
          case "W":
            if (tempalph.indexOf("M") != -1){
                    tempalph = tempalph.replace("M", "#");
              modchar = 'M';
                } else {
              return nope;
          case "0":
            if (tempalph.indexOf("O") != -1){
                    tempalph = tempalph.replace("O", "#");
              modchar = 'O';
                } else {
              return nope;
          case "6":
            if (tempalph.indexOf("9") != -1){
                    tempalph = tempalph.replace("9", "#");
              modchar = '9';
                } else {
              return nope;
          case "7":
            if (tempalph.indexOf("L") != -1){
                    tempalph = tempalph.replace("L", "#");
              modchar = 'L';
                } else {
              return nope;
          case "9":
            if (tempalph.indexOf("6") != -1){
                    tempalph = tempalph.replace("6", "#");
              modchar = '6';
                } else {
              return nope;

          case "S":
            if (tempalph.indexOf("5") != -1){
                    tempalph = tempalph.replace("5", "#");
              modchar = '5';
                } else if (tempalph.indexOf("2") != -1){
                    tempalph = tempalph.replace("2", "#");
              modchar = '2';
                } else {
              return nope;
                    case "Z":
            if (tempalph.indexOf("2") != -1){
                    tempalph = tempalph.replace("2", "#");
              modchar = '2';
                } else if (tempalph.indexOf("5") != -1){
                    tempalph = tempalph.replace("5", "#");
              modchar = '5';
                } else {
              return nope;
          case " ":
            return nope;

        modphrase += modchar;

    return modphrase;
  } else {
    // contains some other characters, so definitely isn't valid
    return nope;

function addColors(colcount){
  var i = 0;
  for (let char of alphabet){
    exclass = "."+char;
    newclass = "col"+i;
    if ($(exclass).length>0){
    if (i==colcount){

$("#phrase,#sets,#colours").on("keyup", function(){
    var phrase = $("#phrase").val().toUpperCase();
  phrase = getPhrase(phrase, $("#sets").val());


<label>Phrase<input type="text" id="phrase"/></label>
<label>Colours<input type="text" id="colours" value="3"/></label>
<label>Sets<input type="text" id="sets" value="1"/></label>

<div id="output">



.col0{ color: #f00 }
.col1{ color: #0f0 }
.col2{ color: #00f }
.col3{ color: #66CDAA }
.col4{ color: #EE82EE }
.col5{ color: #7FFFD4 }
.col6{ color: #7FFFD4 }
.col7{ color: #FFDEAD }
.col8{ color: #D8BFD8 }
.col9{ color: #FF6347 }
.col10{color: #8B4513 }
.col11{color: #800000 }
.col12{color: #00FFFF }
.col13{color: #32CD32 }
.col14{color: #191970 }
.col15{color: #1E90FF }
.col16{color: #A0522D }
.col17{color: #808000 }
.col18{color: #DC143C }
.col19{color: #90EE90 }
.col20{color: #D2691E }
.col21{color: #48D1CC }
.col22{color: #008000 }
.col23{color: #8B008B }
.col24{color: #6495ED }
.col25{color: #800080 }
.col26{color: #000080 }
.col27{color: #DB7093 }
.col28{color: #7FFF00 }
.col29{color: #00FA9A }
.col30{color: #0000FF }
.col31{color: #BC8F8F }
.col32{color: #A52A2A }
.col33{color: #4169E1 }
.col34{color: #FFFF00 }
.col35{color: #FFA07A }

.rot{ display: inline-block; transform: rotate(0.5turn);}
.flip{ display: inline-block; transform: rotateY(0.5turn);}

  font-family: sans-serif;
  font-size: 3em;
  background-color: #000;
  padding: 10px;
  • \$\begingroup\$ I think non serious contender should be included in question, per this (mini) meta consensus. \$\endgroup\$
    Commented Dec 20, 2017 at 14:38

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.