Code golf for my real friends

35

5

...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.

Examples

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!

ivzem

Posted 2017-03-12T15:33:38.130

Reputation: 1 129

I think you have a typo at Single-payer (instead of Single-player). – Kevin Cruijssen – 2017-03-13T11:08:21.980

4Kevin: No. It really is Single-payer. – ivzem – 2017-03-13T12:23:41.327

@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!

– Bobson – 2017-03-13T21:45:34.353

Answers

18

Retina, 119 bytes

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

ew
rw
eds
ed
(\w+) ?([^oatr ]\w{3}.+)real
and real $2$1
C
S
gne
in
o 
o-
ti
ty
T`TL`Tl
p\w+y.+
$&.
s$
s!
real -p
RealPl

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:

T`TL`Tl

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

p\w+y.+
$&.
s$
s!

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).

Leo

Posted 2017-03-12T15:33:38.130

Reputation: 8 482

This prints an extra space after platonic. Suggested fix: TIO (+5 bytes)

– math junkie – 2017-03-13T15:18:27.167

1@math_junkie Thank you, I've uploaded a new version which fixes the problem with spaces and is even shorter :) – Leo – 2017-03-14T09:24:50.610

10

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!

math junkie

Posted 2017-03-12T15:33:38.130

Reputation: 2 490

You might have forgotten Single-payer for my real friends... – Mathieu Rodic – 2017-03-14T15:49:16.540

@MathieuRodic No, that is quite intentional. RealPlayer is a special case – math junkie – 2017-03-14T15:54:33.710

Oops, sorry, read too quickly. – Mathieu Rodic – 2017-03-14T16:05:50.153

6

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.

s=input()
x=0
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):
 x+=1
 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"), 
                ("Psudeopods","pods","psuedo-"), 
                ("Petticoats","coats","petty"),
                ("Loosestrife","strife","loose"),
                ("Ladybugs","bugs","lady"),
                ("Single-payer","coats","petty"),
                ("Petticoats","RealPlayer","single"),
                ("Tumbleweeds","weed","Tumblr"),
                ("Fauxhawks","hawks","faux"),
                ("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!")
    else:
        print("Output: and real", phrase[1], "for my", phrase[2], "friends!")

Brian

Posted 2017-03-12T15:33:38.130

Reputation: 69

3From 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.* – Erik the Outgolfer – 2017-03-12T16:27:32.157

1Hello and welcome to the site. This is a [tag: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. – Post Rock Garf Hunter – 2017-03-12T16:27:44.077

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 ;) – Mega Man – 2017-03-12T16:28:56.983

1Hello! 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 – ivzem – 2017-03-12T16:30:20.100

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 ;) – ElPedro – 2017-03-12T19:27:24.380

Here is a golf of your answer. I just has a few trivial reductions to your existing code. Feel free to include it or not. – Post Rock Garf Hunter – 2017-03-12T22:51:17.567

Thnx, I added in all the improvements suggested. It did end up bigger as I'd forgotten to deal with the "-" in "pseudo-friends". However I think it now does everything. Which is quite pleasing. – Brian – 2017-03-12T23:47:24.237

You can save a byte by replacing all commas in L with spaces and .split() – ivzem – 2017-03-13T04:57:18.473

6

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.

Explanation

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*/
}

user41805

Posted 2017-03-12T15:33:38.130

Reputation: 16 320

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 ,. – fergusq – 2017-03-12T20:08:54.217

@fergusq Thanks for the tip! – user41805 – 2017-03-12T20:14:23.657

Also you can just use r=indexOf(a[7:8],"noarspekc"). – fergusq – 2017-03-12T20:28:34.970

@fergusq Ah, I didn't know such a builtin existed. Thanks! – user41805 – 2017-03-13T05:35:09.070

6

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_"]]

dzaima

Posted 2017-03-12T15:33:38.130

Reputation: 19 048

5

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)]

Test

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"));

Arnauld

Posted 2017-03-12T15:33:38.130

Reputation: 111 334

3

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];

user63956

Posted 2017-03-12T15:33:38.130

Reputation: 1 571

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{
  my($_)=@_;
  s,(\S+[oieyxm ])(\S{4}.+)real,and real $2\l$1,;
  s,gne,in,;
  s,ch,sh,;
  s,ti,ty,;
  s,eds,ed,;
  s,tumble,Tumblr,;
  s,real -p,RealPl,;
  s,o ,o-,;
  s,c ,c,;
  /ng|tt/?"$_.":"$_!"
}

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!

Kjetil S.

Posted 2017-03-12T15:33:38.130

Reputation: 1 049

2

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..

Explanation:

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
    put("Ch","pain");put("Ps","pods");put("Pe","coats");put("Lo","strife");put("La","bugs");put("Si","Player");put("Tu","weed");put("Fa","hawks");
                               //  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"));
  }
}

Kevin Cruijssen

Posted 2017-03-12T15:33:38.130

Reputation: 67 575

1I think you can save bytes by using double-brace initialization and put instead of m.put. – Zircon – 2017-03-13T20:21:17.240

2

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);}

Steadybox

Posted 2017-03-12T15:33:38.130

Reputation: 15 798

2

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!

gastropner

Posted 2017-03-12T15:33:38.130

Reputation: 3 264