Since it's almost Halloween, I was just thinking of some random Halloween-themed challenges. Eventually I was reading through Frankenstein's wikipedia page, and came across the following text:

The creature

Part of Frankenstein's rejection of his creation is the fact that he does not give it a name, which causes a lack of identity. Instead it is referred to by words such as "wretch", "monster", "creature", "demon", "devil", "fiend", and "it". When Frankenstein converses with the creature in Chapter 10, he addresses it as "vile insect", "abhorred monster", "fiend", "wretched devil", and "abhorred devil".



Given two integers (year and chapter), output a string based on the following rules:

  1. If the year is less than 1818: output an empty string
  2. If the year is exactly 1818 and the chapter is below 10: output a random string from the list ["wretch", "monster", "creature", "demon", "devil", "fiend", "it"]
  3. If the year is exactly 1818 and the chapter is 10 or higher: output a random string from the list ["vile insect", "abhorred monster", "fiend", "wretched devil", "abhorred devil"]
  4. Is the year above 1818 (and thus the book was published), return "Frankenstein's Monster".

Challenge rules:

  • You can choose whether to output in lowercase, uppercase, or a combination.
  • The year will always be in the range 1500-2017
  • The chapter will always be in the range 1-24 (if I googled correctly, the book has 24 chapters)
  • Instead of outputting an empty string for option 1, you are also allowed to output null, undefined, false, 0, or any other value of your choice not part of the other three options (please specify what you've used in your answer).
  • The output may not contain any leading or trailing spaces/tabs, nor any leading new-lines. It may contain one optional trailing new-line.

General rules:

  • This is , so shortest answer in bytes wins.
    Don't let code-golf languages discourage you from posting answers with non-codegolfing languages. Try to come up with an as short as possible answer for 'any' programming language.
  • Standard rules apply for your answer, so you are allowed to use STDIN/STDOUT, functions/method with the proper parameters, full programs. Your call.
  • Default Loopholes are forbidden (especially 'Fake random numbers').
  • If possible, please add a link with a test for your code.
  • Also, please add an explanation if necessary.

Test cases:

year,chapter possible outputs:

1700,1       ""; null; undefined; false; 0; etc.
1700,12      ""; null; undefined; false; 0; etc.
1817,10      ""; null; undefined; false; 0; etc.
1818,1       "wretch"; "monster"; "creature"; "demon"; "devil"; "fiend"; "it"
1818,9       "wretch"; "monster"; "creature"; "demon"; "devil"; "fiend"; "it"
1818,10      "vile insect"; "abhorred monster"; "fiend"; "wretched devil"; "abhorred devil"
1818,11      "vile insect"; "abhorred monster"; "fiend"; "wretched devil"; "abhorred devil"
1818,18      "vile insect"; "abhorred monster"; "fiend"; "wretched devil"; "abhorred devil"
1819,1       "Frankenstein's Monster"; "frankenstein's monster"; "FRANKENSTEIN'S MONSTER"
1819,18      "Frankenstein's Monster"; "frankenstein's monster"; "FRANKENSTEIN'S MONSTER"
2017,24      "Frankenstein's Monster"; "frankenstein's monster"; "FRANKENSTEIN'S MONSTER"

14 Answers 14


Python 2, 227 220 214 bytes

lambda y,c:[[choice([["wretch",m,"creature","demon",d,F,"it"],["vile insect",a+m,F,"wretched "+d,a+d]][c>9]),"Frankenstein's "+m][y>1818],0][y<1818]
from random import*
m,d,F,a='monster','devil','fiend',"abhorred "

Try it online!

  • 1
    \$\begingroup\$ You can save 6 more bytes by putting 'abhorred ' to the list at the bottom as well, since it's been used twice. ('wretch' is also possible, but byte-count stays the same.) \$\endgroup\$ Commented Oct 23, 2017 at 15:07

Perl 5, 172 bytes

171 bytes code + 1 for -p.

@a=<><10?($}=wretch,$M=monster,creature,demon,$D=devil,fiend,it):("vile insect",($A="abhorred ").$M,fiend,"$}ed $D",$A.$D);$_=($a[rand@a],"frankenstein's $M",0)[$_<=>1818]

Try it online!


Pretty standard, only slightly unsual thing is using the 'spaceship operator' (<=>) with 1818 to return -1, 0, or 1, if the input $_ is less than, equal to, or greater than 1818 to return the last, first, or second index of the source data. Also, I like the fish operator!

  • \$\begingroup\$ I was going to question not using $F=fiend, but after testing it ends up being the same length to do so. \$\endgroup\$ Commented Oct 23, 2017 at 15:47
  • \$\begingroup\$ @KamilDrakari Yeah, needs to be length of six or more because of the $ sigil :( \$\endgroup\$ Commented Oct 23, 2017 at 15:49
  • \$\begingroup\$ $} what a variable name ... \$\endgroup\$
    – Titus
    Commented Oct 24, 2017 at 10:37
  • \$\begingroup\$ @Titus Yup! Almost anything works, although many have extra special meaning. I only used it there as if I'd used $W I'd have needed to use ${W} as it's next to another letter. \$\endgroup\$ Commented Oct 24, 2017 at 11:51
  • 1
    \$\begingroup\$ I reckoned that. gotcha none the less :) \$\endgroup\$
    – Titus
    Commented Oct 24, 2017 at 14:16

C# (Visual C# Compiler), 225 209 bytes

y=>c=>y<1818?"":y>1818?"frankenstein's monster":"wretch,monster,creature,demon,devil,it,fiend,vile insect,abhorred monster,wretched devil,abhorred devil".Split(',')[new System.Random().Next(c>9?6:0,c>9?11:7)];

Try it online!

-16 thanks to Kevin Cruijssen

Not particularly concise, but I would expect C# to take more space to declare and use any repeated strings the way the Python answer does than to just repeat them. The included test runner has a Thread.Sleep(1) inside the enumeration of test cases because default seeding for Random() uses the current time and the actual function runs fast enough to end up with the same seed on subsequent runs without a delay.

Calls to the func use currying syntax, f(year)(chapter) (or whatever name the function is stored in instead of f)

  • 2
    \$\begingroup\$ I see you applied the same trick as I had in mind when I posted the challenge, putting fiend in the middle of a single string-array, since it's present in both lists. You can save some bytes by replacing new[]{"wretch","monster","creature","demon","devil","it","fiend","vile insect","abhorred monster","wretched devil","abhorred devil"} with "wretch,monster,creature,demon,devil,it,fiend,vile insect,abhorred monster,wretched devil,abhorred devil".Split(',') (-16 bytes) \$\endgroup\$ Commented Oct 24, 2017 at 7:04
  • \$\begingroup\$ @KevinCruijssen Nice trick, It doesn't immediately seem beneficial but changing the delimiter from "," to just , makes up for the longer .Split(',') compared to new[]{} with more than a few items. \$\endgroup\$ Commented Oct 24, 2017 at 14:10

Bash, 229, 206, 197, 179 bytes

a=("Frankenstein's $m" wretch $m creature demon devil it fiend vile\ insect {abhorr,wretch}ed\ {devil,$m})
echo "${a[$1>1818?0:$1<1818?13:RANDOM%($2>9?5:7)+($2>9?7:1)]}"

the explanation about brace/variable expansions

{abhorr,wretch}ed\ {devil,$m}

is first expanded to

abhorred\ {$m,devil} wretched\ {devil,$m}
abhorred\ $m abhorred\ devil wretched\ devil wretched\ $m

then variable m is expanded

abhorred\ monster abhorred\ devil wretched\ devil wretched\ monster

however following doesn't work because $w will be first joined with ed and wed is not defined and will be expanded to empty string.

{abhorr,$w}ed\ {devil,$m}

it can be done adding explicit braces arround w but will be longer

{abhorr,${w}}ed\ {devil,$m}


  • \$\begingroup\$ You can save 4 bytes by creating variables for wretch and monster (225 bytes). \$\endgroup\$ Commented Oct 24, 2017 at 7:00
  • \$\begingroup\$ not so simple because of brace expansion which occurs before variable expansion \$\endgroup\$ Commented Oct 24, 2017 at 7:07
  • \$\begingroup\$ I (almost) never programmed in Bash, so could you clarify what you mean by that? It does seem to work in TIO, but maybe I'm missing something due to the way the programming language behaves. \$\endgroup\$ Commented Oct 24, 2017 at 7:09
  • 1
    \$\begingroup\$ @KevinCruijssen thnak you i learnt something when braces are the last element expansion works \$\endgroup\$ Commented Oct 24, 2017 at 9:31
  • 1
    \$\begingroup\$ @DomHastings, yes, fixed \$\endgroup\$ Commented Oct 25, 2017 at 7:25

Perl 5, 156 153 151 bytes

-3 bytes thanks to DomHastings.

Run with -p

$_=($d=devil,$m=monster,$w=wretch,<"{creature,demon,it,fiend,vile insect,{abhorr,$w}ed {$d,$d,$m},Frankenstein's $m}">)[/818/?6*/,../+rand 7:$_/606-5]

This output distribution is non-uniform, which is accepted by default. Specifically, if the date is 1818 and the chapter is ≥10, the names “abhorred devil” and “abhorred monster” are weighted twice as much. This lets me use rand 7 for both cases, even though the lists are of different sizes.

The output for years before 1818 is “wretched monster”. Despite some similarities, this string is not part of any of the other three options, making it okay to use.

This fails for years outside of the 1212—2424 range, but luckily the year is guaranteed by the rules to be in the 1500—2017 range.

Try it online!

  • \$\begingroup\$ I didn't know brace expansion was a thing in Perl at all... Using that approach, there's a great solution for the known knowns challenge! Try it online! \$\endgroup\$ Commented Oct 27, 2017 at 7:53
  • \$\begingroup\$ @DomHastings Ooh I didn’t know you could quote spaces like that. That’s -3 bytes here! \$\endgroup\$
    – Grimmy
    Commented Oct 27, 2017 at 8:34
  • \$\begingroup\$ Glad I was able to help! I'll definitely have to try and remember this, I guess it's because <...> assumes globs? \$\endgroup\$ Commented Oct 27, 2017 at 8:38
  • 1
    \$\begingroup\$ <> is either readline or glob, depending on… stuff (classic Perl: the grammar is ambiguous, but the parser tries to DWYM). When it’s a glob, it closely mimics shell globs, including the brace expansion that’s a common feature in shells. \$\endgroup\$
    – Grimmy
    Commented Oct 27, 2017 at 9:45
  • \$\begingroup\$ Could you perhaps add a TIO-link to your answer. :) It's currently the shortest answer, but I just want to make sure everything works before I accept it as the shortest. \$\endgroup\$ Commented Nov 13, 2017 at 13:43

APL (Dyalog Unicode), 189 bytes

a←'abhorred ',⊢
{⊃⍵=1818:{⊃⌽⍵<10:(?7)⊃'wretch'm'creature' 'demon'd f'it'⋄(?5)⊃'vile insect'(a m)f('wretched ',⊢d)(a d)}⍵⋄{⊃⍵>1818:'frankenstein''s ',⊢m⋄''}⍵}

Try it online!

To test, just append the test case to the last curly bracket like so:

{⊃⍵=1818:{⊃⌽⍵<10:(?7)⊃'wretch'm'creature' 'demon'd f'it'⋄(?5)⊃'vile insect'(a m)f('wretched ',⊢d)(a d)}⍵⋄{⊃⍵>1818:'frankenstein''s ',⊢m⋄''}⍵}1818 10

Huge thanks to @ngn for helping out with the leading whitespace issue.

How it works:

It's worth noting that APL interprets strings as arrays (vectors) of characters, so 'wretch' 'creature' would be interpreted in other languages as [[w,r,e,t,c,h],[c,r,e,a,t,u,r,e]]

These are helper functions:

m←'monster'      #These are pretty self explanatory, variables m, f and d are being set to 'monster', 'fiend' and 'devil respectively.
a←'abhorred ',⊢  # a is being set to 'abhorred ' concatenated with the argument to it's right.

This is the main function, which will be broken in 3 parts for better understanding:

{⊃⍵=1818:{⊃⌽⍵<10:(?7)⊃'wretch'm'creature' 'demon'd f'it'⋄(?5)⊃'vile insect'(a m)f('wretched ',⊢d)(a d)}⍵⋄{⊃⍵>1818:'frankenstein''s ',⊢m⋄''}⍵}

Part 1:

{⊃⍵=1818:{Part2}⍵⋄{Part3}⍵} # First conditional
        :                   # If
 ⊃                          # the first element
  ⍵                         # of the argument ⍵
   =1818                    # Equals the literal 1818
         {Part2}⍵           # Executes part 2 with argument ⍵
                 ⋄          # Else
                  {Part3}⍵  # Executes part 3 with argument ⍵

Part 2:

{⊃⌽⍵<10:(?7)⊃'wretch'm'creature' 'demon'd f'it'⋄ # Second conditional
(?5)⊃'vile insect'(a m)f('wretched ',⊢d)(a d)}⍵                
       :                                         # If
 ⊃                                               # the first element
  ⌽                                              # of the inverse
   ⍵                                             # of the argument ⍵
    <10                                          # is less than 10
           ⊃                                     # pick the nth
       (?7)                                      # random element between [1,7]
            'wretch'm'creature' 'demon'd f'it'   # from this string array
                                              ⋄  # Else
    ⊃                                            # pick the nth
(?5)                                             # random element between [1,5]
     'vile insect'(a m)f('wretched ',⊢d)(a d)    # from this string array
                                             }⍵  # using argument ⍵

Part 3:

{⊃⍵>1818:'frankenstein''s ',⊢m⋄''}⍵ # Third conditional
        :                           # If
 ⊃                                  # the first element
  ⍵                                 # of the argument ⍵
   >1818                            # is greater than 1818
         'frankenstein''s ',⊢m      # print the string "frankenstein's" concatenated with m (which prints monster)
                              ⋄     # else
                               ''   # print an empty string
                                 }⍵ # with argument ⍵

Java (OpenJDK 8), 275 269 255 207 203 bytes

y->c->y<1818?0:y>1818?"Frankenstein's monster":"wretch;monster;creature;demon;devil;it;fiend;vile insect;abhorred monster;wretched devil;abhorred devil".split(";")[c+=Math.random()*(c>9?5:7)+(c>9?6:0)-c]

Try it online!


PHP, 184 176 bytes

<?=strtr([0,1,2,Creature,Demon,It,Fiend,30,31,"2ed 1","Vile Insect","Frankenstein´s 0"][[rand(6*$x=$argv[2]>9,6+4*$x),11][$argv[1]<=>1818]],[Monster,Devil,Wretch,"Abhorred "]);

Run with -n and give year and chapter as command line arguments or try it online.

index and thus result evaluate to NULL for year<1818, leading to empty output.


Javascript (ES6), 205 bytes

a="abhorred ",d="devil",f="fiend",m="monster"
y=>c=>y<1818?0:y>1818?"frankenstein's "+m:(l=c<10?[f,"wretch",m,"creature","demon",d,f,"it"]:["vile insect",a+m,f,"wretched "+d,a+d])[0|Math.random()*l.length]

Returns 0 if the year is less than 1818

a="abhorred ",d="devil",f="fiend",m="monster"
n=y=>c=>y<1818?0:y>1818?"frankenstein's "+m:(l=c<10?[f,"wretch",m,"creature","demon",d,f,"it"]:["vile insect",a+m,f,"wretched "+d,a+d])[0|Math.random()*l.length]
<div oninput="o.innerText=n(y.value)(c.value)"><input id=y type=number value=1817 /><input id=c type=number value=10 /></div><p id=o>0


Javascript 199 bytes

A=(y,c)=>y<1818?"":y^1818?"Frankenstein's "+M:[W="wretch",M,"creature","demon",D="devil",F="fiend","it","vile insect",(H="abhorred ")+M,F,W+"ed "+F,H+D][Math.random()*(c>9?5:7)+7*(c>9)|0]



C, 380 BYTES

void ok(int *year, int* chapter){char*below_10[] = {"wretch", "monster", "creature", "demon", "devil", "fiend", "it"}; char*at_10[] = {"vile insect", "abhorred monster", "fiend", "wretched devil", "abhorred devil"}; year < 1818?printf("0"):(year == 1818?(chapter<10 printf("%s",below_10[rand()%8]):printf("%s",at_10[rand()%7])):printf("Frankenstein's Monster"));}
  • 4
    \$\begingroup\$ Hi, welcome to PPCG! In a code-golf challenge the goal is to have as few bytes as possible. So method/variable names are best as single characters. In addition, you can remove all unnecessary spaces and new-lines, and use the "Frankenstein's Monster" directly instead of making a variable for it. Also, where is the year and chapter coming from now? I don't see it as parameters of the method. Tips for golfing in <all languages> and Tips for golfing in C might be useful. Enjoy your stay! :) \$\endgroup\$ Commented Oct 24, 2017 at 11:37
  • \$\begingroup\$ No problem. And you can golf a bit more: void o(int y,int c){char*b[]={"wretch","monster","creature","demon","devil","fiend","it"},*a[]={"vile insect","abhorred monster","fiend","wretched devil","abhorred devil"};printf("%s",y<1818?0:y==1818?c>9?a[rand()%6]:b[rand()%7]:"Frankenstein's Monster");} 256 bytes Try it here! (I removed all unnecessary spaces, renamed method/variables/parameters to single letter; removed some unnecessary parenthesis at the ternary-if; single printf; changed <10 to >9; etc.) Not sure why rand() isn't working in TIO.. :S \$\endgroup\$ Commented Oct 25, 2017 at 7:25
  • \$\begingroup\$ 234 bytes \$\endgroup\$
    – ceilingcat
    Commented Jan 16, 2019 at 9:35

MBASIC, 288 bytes

1 DIM R$(12):FOR I=1 TO 12:READ R$(I):NEXT:INPUT Y,C:IF Y<1818 THEN 5
2 IF Y>1818 THEN O$="Frankenstein's Monster":GOTO 5
3 IF C>9 THEN O=7
4 O$=R$(O+INT(RND(1)*N+1))
5 PRINT O$:DATA wretch,monster,creature,demon,devil,fiend,it,vile insect,abhorred monster,fiend,wretched devil,abhorred devil

05AB1E, 114 110 bytes

0…뢼–±¹.•ʒ¦Ë"ìËM₂G¶тá≠•ì#.•1ÏiT¥Ƶʒ“T¡Äg… 5ÀΘO¼™Ãí?ιË%4₄‡‡λYιi¾н••Jtm•17в£‚…'s 'ì³R7£R’¡¬ÿÿ±¹’¸¸)sŽ7X.S>èsT@èΩ

Outputs 0 when the year is below 1818.

Try it online or verify all test cases.


0                    # Push a 0
…뢼–±¹              # Push dictionary string "creature devil monster"
 .•ʒ¦Ë"ìËM₂G¶тá≠•   "# Push compressed string "demon fiend it wretch "
  ì                  # Prepend it: "demon fiend it wretch creature devil monster"
   #                 # Split by spaces: ["demon","fiend","it","wretch","creature","devil","monster"]
.•1ÏiT¥Ƶʒ“T¡Äg… 5ÀΘO¼™Ãí?ιË%4₄‡‡λYιi¾н•
                     # Push compressed string "abhorred devilabhorred monsterfiendvile insectwretched devil"
 •Jtm•               # Push compressed integer 1249548
      17в            # Converted to Base-17 as list: [14,16,5,11,14]
         £           # Split the string into parts of that size: ["abhorred devil","abhorred monster","fiend","vile insect","wretched devil"]
‚                    # Pair both string-lists together
…'s                 '# Push string "'s "
   'ì³              '# Push dictionary string "liechtenstein"
      R7£R           # Leave the last 7 characters: "enstein"
          ’¡¬ÿÿ±¹’   # Push dictionary string "frankÿÿmonster", where the "ÿ" are automatically
                     # replaced with the strings at the top of the stack: "frankenstein's monster"
                  ¸¸ # Wrap it twice into a list: [["frankenstein's monster"]]
)                    # Wrap everything on the stack into a list
                     # (We now have: [0,[["demon","fiend","it","wretch","creature","devil","monster"],["abhorred devil","abhorred monster","fiend","vile insect","wretched devil"]],[["frankenstein's monster"]]])
 s                   # Swap to get the first (implicit) input
  Ž7X                # Push compressed integer 1818
     .S              # Compare it with the input (-1 if below; 0 if equal; 1 if above)
       >             # Increase it by 1 (0 if below; 1 if equal; 2 if above)
        è            # Use it to index into the list
         s           # Swap to get the second (implicit) input
          T@         # Check if it's larger than or equal to 10 (results in 0 for falsey; 1 for truthy)
            è        # Use it to index into the inner list
             Ω       # And finally get a random value from that list (which is output implicitly)

See this 05AB1E tip of mine (sections How to use the dictionary?; How to compress strings not part of the dictionary?; How to compress large integers?; and How to compress integer lists?) to understand why:

  • …뢼–±¹ is "creature devil monster";
  • .•ʒ¦Ë"ìËM₂G¶тá≠• is "demon fiend it wretch ";
  • .•1ÏiT¥Ƶʒ“T¡Äg… 5ÀΘO¼™Ãí?ιË%4₄‡‡λYιi¾н• is "abhorred devilabhorred monsterfiendvile insectwretched devil";
  • •Jtm• is 1249548 and •Jtm•17в is [14,16,5,11,14];
  • 'ì³ is "liechtenstein ";
  • ’¡¬ÿÿ±¹’ is "frankÿÿmonster";
  • Ž7X is 1818.

JavaScript, 194 B

e=Math.random,f=Math.floor;x=(a,b,c=`insect,monster,wretch,creature,demon,devil,fiend,it`.split`,`,d=[`vile `,`abhorred `])=>a<1818||a-1818?`Frankenstein's `+c[1]:(b<10||c[f(e()*2)])+c[f(e()*8)]
  • \$\begingroup\$ doesnt really match the test cases \$\endgroup\$ Commented Oct 24, 2017 at 18:12

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.