22
\$\begingroup\$

This question is inspired by this awesome answer.

The challenge is to write a program or function which takes in a positive integer, and outputs "Happy new year to you" or a slight variation thereof, such that the character codes of that string add up to the inputted number. The space counts towards the sum of character codes too.

The case does not matter at all; you may output HappY New yEAR or something so long as it satisfies the specifications below.

The specifications are as follows:

The outputted string must contain happy new year. As stated above, the case does not matter.

The happy new year component of the string may optionally be preceded by have a , again in any case. However, there must be a space before happy if the string starts with have a .

The happy new year string may optionally be followed by to you, again in any case. The space before to is mandatory.

Note that Have a happy new year to you, while not grammatically correct, is an acceptable output for the purposes of this challenge.

Finally, at the very end of the string, any number of exclamation marks can be outputted.

In summary, the outputted string must satisfy the following regular expression:

/^(have a )?happy new year( to you)?!*$/i

You may assume while writing your program or function that you will only be given numbers for which it is possible to construct a "happy new year" string.

I won't allow outputting in array or list format because it defeats the purpose of the challenge.

If there are multiple solutions, feel free to output any one of them, any subset of them, or all of them.

As this challenge is , the shortest code, measured in bytes, wins.

Test cases

2014
-> Happy new year to you!

1995
-> HAPPY NEW YEAR TO You!!!!!!!!!!!!!!

989
-> HAPPY NEW YEAR

1795
-> Have a HAPPY new year!

2997
-> Have a HAppY new year to YOU!!!!!!!!!!!!!!!!!!!
\$\endgroup\$
1
  • 11
    \$\begingroup\$ In case you are wondering, every year after 1980 has a happy new year string. There are 486 years before 1980 that also have a happy new year string. \$\endgroup\$
    – AnttiP
    Feb 10 at 18:19

7 Answers 7

11
\$\begingroup\$

JavaScript (Node.js),  159  148 bytes

This code builds the string without resorting to brute force.

y=>Buffer((s="happy new year",r=n=y%33)>17&n<20?(n-=7,r-=5,"have a "+s):n<18?s+" to you":(n-=20,s)).map(c=>c-32*(c>32&&n-->0))+'!'.repeat(y/33+r-61)

Try it online!

How?

Let \$y\$ be the input year.

Because we can add \$k\times33,k\ge0\$ to the result by adding as many trailing exclamation marks as we want, we need to focus on \$n=y\bmod33\$.

The method that is used here can be split into the 4 following cases:

  • If \$n\le17\$:
    • We use the base string "happy new year to you" and put \$n\$ characters in upper case, for a base score of \$2013-32n\$.
    • We add \$\lfloor y/33\rfloor+n-61\$ exclamation marks.
    • The final score is: $$2013-32n+33\times(\lfloor y/33\rfloor+n-61)\\=33\times\lfloor y/33\rfloor+n=33\times\lfloor y/33\rfloor+(y \bmod 33)=y$$
  • If \$n=18\$:
    • We use the base string "HAVE A HAPPY New year", for a base score of \$1602\$.
    • We add \$\lfloor y/33\rfloor-48\$ exclamation marks.
    • The final score is: $$1602+33\times(\lfloor y/33\rfloor-48)\\=33\times\lfloor y/33\rfloor+18=33\times\lfloor y/33\rfloor+(y \bmod 33)=y$$
  • If \$n=19\$:
    • We use the base string "HAVE A HAPPY NEw year", for a base score of \$1570\$.
    • We add \$\lfloor y/33\rfloor-47\$ exclamation marks.
    • The final score is: $$1570+33\times(\lfloor y/33\rfloor-47)\\=33\times\lfloor y/33\rfloor+19=33\times\lfloor y/33\rfloor+(y \bmod 33)=y$$
  • If \$n\ge20\$, we use the base string "happy new year" and put \$n-20\$ characters in upper case, for a base score of \$2013-32n\$.
    • We add \$\lfloor y/33\rfloor+n-61\$ exclamation marks.
    • The computation of the final score is similar to the 1st case.

Obviously, it's not possible to add a negative number of exclamation marks, which is why only some years before 1981 have a happy new year string (as pointed out by @AnttiP).

\$\endgroup\$
6
\$\begingroup\$

JavaScript (Node.js), 130 bytes

f=(n,s='',...a)=>n|!/^(have a )?happy new year( to you)?!*$/i.test(s)?f(...a,...[...f+f].map(_=>(a=!a)?s+Buffer([i++]):n-i,i=2)):s

Try it online!

Run out of memory on any computer as it use >128^14 args, but theoretically it works

\$\endgroup\$
5
\$\begingroup\$

Excel, 219 bytes

=LET(r,MOD(A1,33),m,(r<20)+(r<18)+1,s,REPT("HAVE A ",m=2)&"HAPPY NEW YEAR"&REPT(" TO YOU",m=3),a,INDEX({989;1410;1469},m),b,MOD(a-r,33),d,b+SUM((b>{5,8,12,14})*1),LOWER(LEFT(s,d))&MID(s,d+1,26)&REPT("!",(A1-a-b*32)/33))

Inspired by @Arnauld 's answer. One of the ingenious things about his solution is that the numbers of cases that start with "HAVE A" is so small, that a different test to account for the spaces is not needed.

Link to Spreadsheet

=LET(r,MOD(A1,33), Set r = \$n \mod 33\$.

m,(r<20)+(r<18)+1, Set mode. Mode 1 \$> 19\$; Mode 2 \$[18, 19]\$; mode 3 \$< 18\$.

s,REPT("HAVE A ",m=2)&"HAPPY NEW YEAR"&REPT(" TO YOU",m=3), if mode 2, prepend "HAVE A "; if mode 3, append " TO YOU".

a,INDEX({989;1410;1469},m), Sum of character values for base string

b,MOD(a-r,33), distance of base string from \$n \mod 33\$

d,b+SUM((b>{5,8,12,14})*1), portion of base string to be converted to lower case. The numbers in the brackets account for the spaces.

LOWER(LEFT(s,d))&MID(s,d+1,26)&REPT("!",(A1-a-b*32)/33)) return the string with the appropriate number of characters converted to lower case plus the number of "!" to reach the target number.

\$\endgroup\$
3
\$\begingroup\$

Charcoal, 83 bytes

NθF²F²«≔⁺⁺×ιHAVE A ”&±″/⍘=↧5"SA”×κ TO YOUκF⊕Lκ⊞υ⭆κ⎇‹νλμ↧μ»W⬤υ⁻θΣEκ℅μ≔⁺υ!υ⊟Φυ⁼θΣEι℅λ

Try it online! Link is to verbose version of code. Explanation:

Nθ

Input the desired year.

F²F²«

Loop over the possibilities of prefixes and suffixes.

≔⁺⁺×ιHAVE A ”&±″/⍘=↧5"SA”×κ TO YOUκ

Take the compressed string HAPPY NEW YEAR and optionally prefix or suffix it as appropriate.

F⊕Lκ⊞υ⭆κ⎇‹νλμ↧μ

Generate more than enough permutations of the case of the characters in the strings. (In particular, unnecessary extra copies are created for each space in the string, because it's golfer not to filter them out.)

»W⬤υ⁻θΣEκ℅μ

While none of the strings has the desired character sum...

≔⁺υ!υ

... append an ! to each string.

⊟Φυ⁼θΣEι℅λ

Output one of the strings with the desired character sum.

74 bytes for a version that's too slow for TIO:

NθFθF²F²«≔⁺⁺×κHAVE A ”&±″/⍘=↧5"SA”×λ TO YOUλF⊕Lλ⊞υ⁺⭆λ⎇‹ξμν↧ν×ι!»⊟Φυ⁼θΣEι℅λ

Try it online! with the second θ changed to χ so it does at least work for some years.

\$\endgroup\$
2
\$\begingroup\$

Jelly, 52 bytes

“ṭ½IṾFhĿ_)Wẓȷ⁻⁸ẉ»ḲŒP“7dx‘ịK€ŒuƤŻoƊ€Ẏ;þḶ”!xⱮƊẎOS⁼ɗƇ⁸Ḣ

A monadic Link accepting a positive integer that yields a list of characters (or 0). Or a full program that prints a valid result (or 0).

Don't Try it online! (it won't finish)

Rather, see this slightly altered test-suite which considers fewer possible strings (by using \$\frac{1}{33}\$ as many optional trailing !s).

How?

“...»ḲŒP“7dx‘ịK€ŒuƤŻoƊ€Ẏ;þḶ”!xⱮƊẎOS⁼ɗƇ⁸Ḣ - Link: integer, N
“...»                                    - "have a happy new year to you"
     Ḳ                                   - split at spaces
      ŒP                                 - powerset
        “7dx‘                            - [55,100,120]
             ị                           - index into -> [["happy", "new", "year"], ["have", "a", "happy", "new", "year"], {"happy", "new", "year", "to", "you"]]
              K€                         - join each with spaces -> ["happy new year", "have a happy new year", "happy new year to you"]
                     Ɗ€                  - last three links as a monad for each:
                ŒuƤ                      -   upper-case prefixes
                   Ż                     -   prefix with a zero
                    o                    -   logical OR (the full lower-case)
                       Ẏ                 - tighten
                               Ɗ         - last three links as a monad - f(N):
                          Ḷ              -   [0..N-1]
                           ”!            -   '!'
                             xⱮ          -   map with times -> ["", "!", "!!", ..., '!'*(N-1)]
                        ;þ               - table with concatenate
                                Ẏ        - tighten
                                      ⁸  - use N as the right argument of...
                                     Ƈ   - filter keep those for which:
                                    ɗ    -   last three links as a dyad - f(possibles, N):
                                 O       -     ordinals
                                  S      -     sum
                                   ⁼     -     equals N?
                                       Ḣ - head -> valid result or 0 if none exists
\$\endgroup\$
1
\$\begingroup\$

Ruby, 128 bytes

->y{n=y%33
s="HAVE A HAPPY NEW YEAR TO YOU"[n/2==9?0:7,w=21-n/20*7]
w.times{|i|(n=y-s.sum)%33>0&&s[i]=s[i].downcase}
s+?!*n/=33}

Try it online!

Similar to Arnauld's answer. Some notes on how it works:

  • "HAPPY NEW YEAR".sum % 33 is 32. Converting a letter to lowercase adds 32, reducing the remainder by 1 for each letter down to a minimum of 20 when all 12 letters are in lowercase.
  • "HAVE A HAPPY NEW YEAR".sum % 33 is 24. We convert 5 or 6 letters to lowercase to cover years of remainder 19 and 18.
  • "HAPPY NEW YEAR TO YOU".sum % 33 is 17. We convert 0 to 17 letters to lowercase to cover years of remainder 17 down to 0.

It's a fairly simple approach, avoiding the sophisticated formulas in Arnauld's answer (although it wouldn't work without them being true).

  • Select the appropriate substring from HAVE A HAPPY NEW YEAR TO YOU as above
  • Convert characters from uppercase to lowercase until (y-s.sum)%33 is zero
  • Append (y-s.sum)/33 exclamation marks

n is used in two different ways. First it represents y%33. Later it represents y-s.sum. The reason for the re-use of n is that the second assignment occurs inside a loop and if a new variable were used (implicitly declared) inside the loop, it would not be recognised outside the loop, causing an error.

\$\endgroup\$
1
\$\begingroup\$

Python3, 211 bytes:

lambda y:next(f(y,'',s:='happy new year to you'),0)or next(f(y,'','have a '+s))
def f(y,s,j):
 if(v:=sum(map(ord,s)))==y:yield s
 if v<y:
  for i in{(k:=['!',j][j!=''])[0],k[0].upper()}:yield from f(y,s+i,k[1:])

Try it online!

\$\endgroup\$

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.