...and real golf for my code friends.

This is a challenge based on a one year old XKCD comic which consists mostly of toasts (parodies of the first in the comic) following a distinct pattern, but with some slight variation.

Your task is to write a program (or function) that takes the first half of any toast from the comic (everything up to and including the friends) as input (via stdin or a function argument) and outputs (or returns) the precise text of the second half. You do not have to handle invalid input, standard loopholes apply, shortest code in bytes wins.


To help those who don't want to copy the toasts from anywhere else, here are all of them, separated into input and output.

Input: Champagne for my real friends
Output: and real pain for my sham friends!

Input: Pseudopods for my real friends
Output: and real pods for my pseudo-friends!

Input: Petticoats for my real friends
Output: and real coats for my petty friends.

Input: Loosestrife for my real friends
Output: and real strife for my loose friends!

Input: Ladybugs for my real friends
Output: and real bugs for my lady friends!

Input: Single-payer for my real friends
Output: and RealPlayer for my single friends.

Input: Tumbleweeds for my real friends
Output: and real weed for my Tumblr friends!

Input: Fauxhawks for my real friends
Output: and real hawks for my faux friends!

Input: Platonic solids for my real friends
Output: and real solids for my platonic friends!
  • \$\begingroup\$ I think you have a typo at Single-payer (instead of Single-player). \$\endgroup\$ Mar 13, 2017 at 11:08
  • 4
    \$\begingroup\$ Kevin: No. It really is Single-payer. \$\endgroup\$
    – ivzem
    Mar 13, 2017 at 12:23
  • \$\begingroup\$ @KevinCruijssen - It might be a typo, but if so it's Randall@XKCD's, not ivzem. There's a discussion of it on Explain xkcd. Also, ivzem - as soon as I saw the title in the HNQ list, I was sure you were referencing that comic. Well done! \$\endgroup\$
    – Bobson
    Mar 13, 2017 at 21:45

11 Answers 11


Retina, 119 bytes

The previous version didn't manage correctly the space in "platonic solids", this one works and is shorter :)

(\w+) ?([^oatr ]\w{3}.+)real
and real $2$1
real -p

Try it online!

This transforms the input into the output through a series of substitutions.

The most interesting part is this substitution (kind of a regex golf):

(\w+) ?([^oatr ]\w{3}.+)real
and real $2$1

Which does almost all the job, by splitting the first word, placing its pieces in the right places, removing extra spaces, and building the structure of the output. To work correctly on the "Tumbleweeds" test case this depends on the previous substitution "eds"->"ed".

The rest is mostly composed by substitutions that deal with special cases. Other interesting parts are:


This turns everything except "T" (for Tumblr) to lowercase.


This places a "." at the end of each sentence containing a word with a "y" some letters after a "p" ("petty" and "payer" in practice). Then places a "!" at the end of all sentences ending with an "s" (all the others).

  • \$\begingroup\$ This prints an extra space after platonic. Suggested fix: TIO (+5 bytes) \$\endgroup\$ Mar 13, 2017 at 15:18
  • 1
    \$\begingroup\$ @math_junkie Thank you, I've uploaded a new version which fixes the problem with spaces and is even shorter :) \$\endgroup\$
    – Leo
    Mar 14, 2017 at 9:24

Python 2, 291 269 293 255 247 bytes

Thanks to Erik the Outgolfer for saving 22 bytes!

+24 bytes to account for some outputs ending in . instead of !

lambda x:'and '+['real '+'pain%ssham ,pods%spseudo-,coats%spetty ,strife%sloose ,bugs%slady ,weed%sTumblr ,hawks%sfaux ,solids%splatonic '.split(',')['noarsekc'.find(x[7])],'RealPlayer%ssingle ']['-'in x]%' for my '+x[-7:]+'!.'['-'in x or'tt'in x]

Simple solution to start things off. Checks the eighth letter of the input, as suggested in the comments, and looks up the corresponding output in a dictionary an array.

Try it Online!

  • \$\begingroup\$ You might have forgotten Single-payer for my real friends... \$\endgroup\$ Mar 14, 2017 at 15:49
  • \$\begingroup\$ @MathieuRodic No, that is quite intentional. RealPlayer is a special case \$\endgroup\$ Mar 14, 2017 at 15:54
  • \$\begingroup\$ Oops, sorry, read too quickly. \$\endgroup\$ Mar 14, 2017 at 16:05

SOGL, 143 bytes

_╔x⅜²‘,8WWAa"⅞+1Tλ²⅞ƨ◄…χŗbdŗu8ņ∑Ι⅓I‼hzΔμō┘═q‼xΘ▼²ηpG⅔─┌¡↕+wd÷[≈┐α┌ļ○(‚δΦEΤα{‚φ▒k׀:╚s&⅛↑»‘ |Θwθ1w"ρ└⁸‘S∆∫⁴‘a1>*+oo"¤Ε○Φr‘o2w _@ŗo"æn‘o"χ}49⁶‘aWT

This uses this string as the main part. "|" are the splitters and "_" are space placeholders so split would work correctly.

RealPlayer single_|weed Tumblr_|strife loose_|bugs lady_|pods pseudo-|pain sham_|solids platonic_|coats petty_|hawks faux_

Input example: "Platonic solids for my real friends" Explanation:

..‘,8WWAa"..‘ |Θwθ1w"..‘S∆∫⁴‘a1>*+oo"..‘o2w _@ŗo"..‘o"..‘aWT  strings shortened
..‘                                                           push "personcake" (the indexing string) ["personcake"]
    8W                                                        get the 8-th char                       
   ,                                                           from the input                         ["personcake", "c"]
      W                                                       get its in "personcake" (1-indexed)     [7]
       Aa                                                     save on variable A                      [7]
         "..‘                                                 push the long string                    [7, "RealPlayer...faux_"]
              |Θ                                              split on "|"                            [7, ["RealPlayer single_", ..., "hawks faux_"]]
                w                                             get the Ath item of the array           [["..."], "solids platonic_"]
                 θ                                            split on spaces                         [["..."], ["solids", "platonic_"]]
                  1w                                          get the 1st item                        [["..."], ["solids", "platonic_"], "solids"]
                    "..‘                                      push "and "                             [["..."], ["solids", "platonic_"], "solids", "and "]
                        S∆∫⁴‘                                 push "real "                            [["..."], ["solids", "platonic_"], "solids", "and ", "real "]
                             a1>*                             multiply by A(index) > 1                [["..."], ["solids", "platonic_"], "solids", "and ", "real "]
                                 +                            join together                           [["..."], ["solids", "platonic_"], "solids", "and real "]
                                  o                           output                                  [["..."], ["solids", "platonic_"], "solids"]
                                   o                          output (the 1st item of the array)      [["..."], ["solids", "platonic_"]]
                                    "..‘o                     output " for my "                       [["..."], ["solids", "platonic_"]]
                                         2w                   get the 2nd item of the array           [["..."], ["solids", "platonic_"], "platonic_"]
                                            _@ŗ               replace "_" with " "                    [["..."], ["solids", "platonic_"], "platonic "]
                                               o              output that                             [["..."], ["solids", "platonic_"]]
                                                "..‘o         output "friends"                        [["..."], ["solids", "platonic_"]]
                                                     "..‘     push ".!!!!!!.!"                        [["..."], ["solids", "platonic_"], ".!!!!!!.!"]
                                                         aW   get the Ath item                        [["..."], ["solids", "platonic_"], "!"]
                                                           T  output, disabling implicit output       [["..."], ["solids", "platonic_"]]

Python 3, 788, 403, 359 396 bytes

Latest Version

This is now my fifth attempt. I've managed to half the size of my original program. It now includes the missing "-" and I believe is a complete solution. Still I suspect on the bulky side; but much closer to the goal. I've had a lot of help. Thanks for all the helpful guidance.

l= "Ch,pain,Sham,Ps,pods,psuedo-,Pe,coats,petty,Lo,strife,loose,La,bugs,lady,Si,RealPlayer,single,Tu,weed,Tumblr,Fa,hawks,faux,Pl,solids,platonic".split(",")
a,b,c,d,e,f,g,h = " and real","for my","friends",".!","print(a,l[p+1],b,l[p+2]",",c+d[0])","+c+d[1])",",c+d[1])"
for p in range(0,27,3):
 if s[:2] == l[p]:
  if x == 2: eval(e+g)
  else: eval(e+f if x in(3,6) else e+h)

Try it online!

Original Posting

This is my first post on code golf so apologies in advance for my clumsy program. Can't see how a program could be made to convert "Champagne" to "pain", "sham" by parsing the words. However I would like to see someone else solve that. So, as my level of ability dictates that my program needs to know in advance that "Champagne" is "pain", "sham", there seemed little point in actually coding an input request. As a result I've left it out and been a bit literal with my print output. Hope that's ok : )

phraseList= [   ("Champagne","pain","Sham"), 
                ("Platonic solids","real solids","platonic")

for phrase in phraseList:
    print("Input: ",phrase[0], "for my real friends.")
    if "-" in phrase[2]:
        print("Output: and real", phrase[1], "for my", phrase[2]+ "friends!")
        print("Output: and real", phrase[1], "for my", phrase[2], "friends!")
  • 3
    \$\begingroup\$ From the Help Center: All solutions to challenges should: [...] Be a serious contender for the winning criteria in use. For example, an entry to a code golf contest needs to be golfed, and an entry to a speed contest should make some attempt to be fast. \$\endgroup\$ Mar 12, 2017 at 16:27
  • 1
    \$\begingroup\$ Hello and welcome to the site. This is a code-golf question which requires you to make an effort to minimize the length of your code. You should remove unnecessary whitespace and rename variables to be 1 character long. \$\endgroup\$
    – Wheat Wizard
    Mar 12, 2017 at 16:27
  • \$\begingroup\$ You can put the whole phraseList in one line, reduce the indention to one space, put the prints together in one line with if and else, and the whitespace in the prints isn't necessary. Anyway, I do not know if you're allowed to just print all outputs ;) \$\endgroup\$
    – univalence
    Mar 12, 2017 at 16:28
  • 1
    \$\begingroup\$ Hello! It seems like this is your first code golf answer. Unfortunately, you seem to have misunderstood the question, especially the "Examples" table at the bottom, which just shows how each input should map to a respective output. Currently, your program just prints that table._Also, it is standar practice to place your code in a code block (select it and click the {}) for better readability and to mention your language of choice and byte count, as this site is more like a competition than a Q&A site._ Wheat Wizard already did that, so that's better \$\endgroup\$
    – ivzem
    Mar 12, 2017 at 16:30
  • \$\begingroup\$ Hi and welcome to PPCG. You could lose a lot of bytes by removing spaces and using single letter variables. I remember when I first started here it took some time to get used to. I guess you are like me. You spend a lot of time writing readable code. As I said, welcome to PPCG and a new world ;) \$\endgroup\$
    – ElPedro
    Mar 12, 2017 at 19:27

Röda, 299 292 288 259 bytes

4 bytes saved thanks to @fergusq for using , instead of .. in the push statements

Bytes saved thanks to @fergusq showing me the way of indexOf

h a{r=indexOf(a[7:8],"noarspekc")A="pain>pods>coats>strife>Player>bugs>weed>hawks>solids>sham >pseudo->petty >loose >lady >single >Tumblr >faux >platonic >!>!>.>!>!>.>!>!>!"/">";["and "];["Real"]if[r=4]else["real "];[A[r]," for my ",A[r+9],"friends",A[r+18]]}

Try it online!

So close to Python... so close.... Your move, Python.


h a{
  r=indexOf(a[7:8],"noarspekc")  /*Gets the index of the character in this string*/
  /*Variable A contains all the unique words for each test case*/
  A="pain>pods>coats>strife>Player>bugs>weed>hawks>solids>sham >pseudo->petty >loose >lady >single >Tumblr >faux >platonic >!>!>.>!>!>.>!>!>!"/">"
  ["and "]
  ["Real"]if[r=4]else["real "]   /*RealPlayer*/
  [A[r]," for my ",A[r+9],"friends",A[r+18]]   /*Print everything using var A*/
  • \$\begingroup\$ Can you save one byte by using string interpolation in the last statement? Edit: actually the shortest way is probably to push many strings to the stream instead of one, ie. replace .. with ,. \$\endgroup\$
    – fergusq
    Mar 12, 2017 at 20:08
  • \$\begingroup\$ @fergusq Thanks for the tip! \$\endgroup\$
    – user41805
    Mar 12, 2017 at 20:14
  • \$\begingroup\$ Also you can just use r=indexOf(a[7:8],"noarspekc"). \$\endgroup\$
    – fergusq
    Mar 12, 2017 at 20:28
  • \$\begingroup\$ @fergusq Ah, I didn't know such a builtin existed. Thanks! \$\endgroup\$
    – user41805
    Mar 13, 2017 at 5:35

JavaScript (ES6), 230 228 221 216 bytes

s=>'and '+((n='paonrsekc'.search(s[7]))?'real ':'')+'RealPl740s5 /450p3y /540p5-/pain0s3 /460l4 /340l3 /540T4r /350f3 /860p7 '.split`/`[n].replace(/\d\d?/g,n=>s.substr(n/10+1,n%10)||' for my ')+'friends'+'.!'[+(n>1)]


let f =

s=>'and '+((n='paonrsekc'.search(s[7]))?'real ':'')+'RealPl740s5 /450p3y /540p5-/pain0s3 /460l4 /340l3 /540T4r /350f3 /860p7 '.split`/`[n].replace(/\d\d?/g,n=>s.substr(n/10+1,n%10)||' for my ')+'friends'+'.!'[+(n>1)]

console.log(f("Champagne for my real friends"));
console.log(f("Pseudopods for my real friends"));
console.log(f("Petticoats for my real friends"));
console.log(f("Loosestrife for my real friends"));
console.log(f("Ladybugs for my real friends"));
console.log(f("Single-payer for my real friends"));
console.log(f("Tumbleweeds for my real friends"));
console.log(f("Fauxhawks for my real friends"));
console.log(f("Platonic solids for my real friends"));


PHP, 202 220 204 203 bytes

and real <?=[pods,solids,hawks,strife,RealPlayer,pain,bugs,weed,coats][$q=md5($argv[1].LnI)%9].' for my '.[pseudo,platonic,faux,loose,single,sham,lady,Tumblr,petty][$q].' -'[!$q].friends.'.!'[!$q||$q&3];

Perl, 173 168 bytes

Removing newlines and indentations, this becomes 173 bytes of Perl5 code. Shamelessly stole first regexp from Leo's Retina answer. (Mine was a few chars longer)

sub f{
  s,(\S+[oieyxm ])(\S{4}.+)real,and real $2\l$1,;
  s,real -p,RealPl,;
  s,o ,o-,;
  s,c ,c,;

For perl5 version >= 5.14 another 5 bytes can be shaved off with eval and the new /r regexp substitution modifier. Ending up with 168 bytes:

sub f{my($_)=@_;eval's,(\S+[oieyxm ])(\S{4}.+)real,and real $2\l$1Xgne,inXch,shXti,tyXeds,edXtumble,TumblrXreal -p,RealPlXo ,o-Xc ,c,;/ng|tt/?"$_.":"$_!"'=~s/X/,;s,/gr}

Try it online!


C, 367 bytes

Didn't end up as short as it seemed it would.

i,j,c;f(char*s){c=s[7]-97;char t[9],u[200];char*r=s;s+=j=c&&c-17?c-10&&c-18?c-2?5:8:3:4;for(i=0;*++s-32;t[i++]=*s);t[c-4?i:i-1]=0;for(i=0;*r++-t[0];)u[i++]=*(r-1);u[i]=32;u[i+1]=0;u[0]+=32;u[c?i:i-1]=c?c-2?c-14?32:45:0:121;printf("and %s%s for my %sfriends%c",c-15?"real ":"RealPlayer",c-15?c-13?t:"pain":"",c-13?c-4?c-17?u:"loose ":"Tumblr ":"sham ",c&&c-15?33:46);}

Java 7, 585 553 bytes

import java.util.*;String c(String s){Map m=new HashMap(){{put("Ch","pain");put("Ps","pods");put("Pe","coats");put("Lo","strife");put("La","bugs");put("Si","Player");put("Tu","weed");put("Fa","hawks");put("Pl","solids");put("Ch1","sham ");put("Ps1","pseudo-");put("Pe1","petty ");put("Lo1","loose ");put("La1","lady ");put("Si1","single ");put("Tu1","Tumblr ");put("Fa1","faux ");put("Pl1","platonic ");}};String r=s.substring(0,2);int c=r.charAt(1);return"and "+(c=='i'?"Real":"real ")+m.get(r)+" for my "+m.get(r+1)+"friends"+(c=='i'|c=='e'?'.':'!');}

-32 bytes thanks to @Zircon.

Can definitely be golfed by using something different than a Map..


import java.util.*;            // Import required for the Map & HashMap
String c(String s){            // Method with String parameter and String return-type
  Map m=new HashMap(){{        //  The Map
                               //  Add mapping from first two characters with first word
    put("Ch1","sham ");put("Ps1","pseudo-");put("Pe1","petty ");put("Lo1","loose ");put("La1","lady ");put("Si1","single ");put("Tu1","Tumblr ");put("Fa1","faux ");put("Pl1","platonic ");
                               //  Add mapping from first two characters + "1" with second word (+ space or '-' for edge-case `pseudo-friends`)
  }};                          // End of Map initialization
  String r=s.substring(0,2);   //  Get the first two characters of the input String
  int c=r.charAt(1);           //  Get the second character
  return "and "                //  return "and "
    + (c=='i'?"Real":"real ")  //   + "Real" or "real " for edge-case `RealPlayers`
    + m.get(r)                 //   + first word from Map
    + " for my "               //   + " for my "
    + m.get(r+1)               //   + second word from Map
    + "friends"                //   + "friends"
    + (c=='i'|c=='e' ? '.'     //   + '.' for edge-cases 'Petticoats' and 'Single-player
      : '!');                  //     or '!' for all other cases
}                              // End of method

Test code:

Try it here. (ideone.com is bugged lately, so I'm using TIO now..)

import java.util.*;
class M{
  static String c(String s){Map m=new HashMap();m.put("Ch","pain");m.put("Ps","pods");m.put("Pe","coats");m.put("Lo","strife");m.put("La","bugs");m.put("Si","Player");m.put("Tu","weed");m.put("Fa","hawks");m.put("Pl","solids");m.put("Ch1","sham ");m.put("Ps1","pseudo-");m.put("Pe1","petty ");m.put("Lo1","loose ");m.put("La1","lady ");m.put("Si1","single ");m.put("Tu1","Tumblr ");m.put("Fa1","faux ");m.put("Pl1","platonic ");String r=s.substring(0,2);int c=r.charAt(1);return"and "+(c=='i'?"Real":"real ")+m.get(r)+" for my "+m.get(r+1)+"friends"+(c=='i'|c=='e'?'.':'!');}
  public static void main(String[] a){
    System.out.println(c("Champagne for my real friends"));
    System.out.println(c("Pseudopods for my real friends"));
    System.out.println(c("Petticoats for my real friends"));
    System.out.println(c("Loosestrife for my real friends"));
    System.out.println(c("Ladybugs for my real friends"));
    System.out.println(c("Single-player for my real friends"));
    System.out.println(c("Tumbleweeds for my real friends"));
    System.out.println(c("Fauxhawks for my real friends"));
    System.out.println(c("Platonic solids for my real friends"));
  • 1
    \$\begingroup\$ I think you can save bytes by using double-brace initialization and put instead of m.put. \$\endgroup\$
    – Zircon
    Mar 13, 2017 at 20:21

C (gcc), 311 bytes

Clear patterns, but so many exceptions. Still, there must be a better way than this!

f(char*s){char*t="\"5$#(=931",*r[]={"pain", "sham","pods","pseudo","coats","petty","strife","loose","bugs","lady","Player","single","weed","Tumblr","hawks","faux","solids","platonic"};int i=strchr(t,*s^s[2])-t;printf("and %s%s for my %s%cfriends%c",i^5?"real ":"Real",r[i*2],r[i*2+1],i^1?32:45,i^5&&i^2?33:46);}

Try it online!


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.