21
\$\begingroup\$

Let's build an advent Calendar for this Christmas!

Many Advent calendars for children take the form of a large rectangular card with numbered windows for each day of December starting from 1 ( although advent time begins on Advent Sunday, which is the fourth Sunday before Christmas day and often happens to be on November) and leading up to and including Christmas Eve (24) or in some cases Christmas day (25).
Windows are distributed across the calendar at a random position.
Every day children open the corresponding calendar window to reveal a small gift(usually a chocolate)

Task

The task is to display an advent calendar up to date following these rules:

  • Include Christmas day : 1 to 25
  • For days that have passed put an opened empty window [ ] ( no different characters allowed ), so if i run the same code the next day there must be a new window opened.
  • At the current day put an opened window with a gift in it [@] ( instead of @ you can use every printable character excep for space, [ , ] and digits ).
  • For the remaining days put only the day number.
  • At Christmas all the windows will be open, the next day they are all closed ( a new calendar ).
  • Windows must open only on December, windows must stay closed on every month except for December.
  • It should work the same way the next year, and every year, that is for example on December 2099 Windows start opening.
  • Windows are laid down in random order on a 5 rows x 5 columns grid.
  • The grid has not to be accurate, just consider that every windows must have at least one character space separating them on every side: left, top, right and bottom hence numbers must have left and right spaces of at least two characters.

Example

Today (15 December) :



  23   21   [ ]   18   [ ]   

  [ ]   [ ]   [ ]   [ ]   16   

  24   22   [ ]   17   [ ]   

  [ ]   [ ]   [ ]   25   19   

  [ ]   [ ]   [@]   [ ]   20

Tomorrow (16 December) :



  [ ]   17   25   [ ]   [ ]   

  [ ]   19   [ ]   18   21   

  [ ]   23   24   [ ]   [ ]   

  22   [@]   [ ]   [ ]   [ ]   

  [ ]   20   [ ]   [ ]   [ ]

On every month different than December

18 23 21 12 19
25 24 3 15 14
16 9 17 20 4
8 2 11 5 7
6 13 10 22 1

Live example

All rules apply.

\$\endgroup\$
16
  • 1
    \$\begingroup\$ Is it supposed to work as well for the next coming years or only for this year? (I suppose it's possible to golf a few bytes on some implementations by assuming a 2019 timestamp.) \$\endgroup\$
    – Arnauld
    Commented Dec 15, 2019 at 0:41
  • 1
    \$\begingroup\$ So this was in the Sandbox for… 8 minutes? \$\endgroup\$
    – Adám
    Commented Dec 15, 2019 at 0:43
  • 2
    \$\begingroup\$ @Adám sorry but Christmas is just around the corner, since there was no Christmas challenge I felt a bit sad, I used the sandbox to check if my script worked but as I expected it didn't. This is an exception, I'll continue to use the sandbox in the future. \$\endgroup\$
    – AZTECCO
    Commented Dec 15, 2019 at 0:57
  • 3
    \$\begingroup\$ @Jo King I don't want to see only open windows, at this time we have a good open/closed ratio \$\endgroup\$
    – AZTECCO
    Commented Dec 15, 2019 at 1:06
  • 2
    \$\begingroup\$ Earlier this year, I saw an Advent calendar where the gift was a different bottle of wine, so it's not just for kids to enjoy the gifts! \$\endgroup\$
    – Giuseppe
    Commented Dec 15, 2019 at 1:59

11 Answers 11

9
\$\begingroup\$

APL (Dyalog Extended), 57 bytesSBCS

Full program.

5 5⍴('[ ]'¨@{⍵∊⍳d}'[@]'¨@{⍵=d})⍣(</11 25≥m d←2↑1↓⎕TS)?⍨25

Try it online!

?⍨25 random selection without replacement of 25 out of 1…25

⍣() if:

⎕TS date in [Y,M,D,h,m,s,t] format

1↓ drop one element; [M,D,h,m,s,t]

2↑ take two elements; [M,D]

m d← distribute to variables m=M and d=D (for month-of-year and day-of-month numbers)

11 25≥ are [11,25] greater than or equal to these (gives [0,1] during advent, [1,0] or [1,1] before, and [0,0] after)

</ if the first number is less than the second…

() then apply the following function:

  @{} at elements where the following condition is true:

  ⍵=d the elements are equal to d (today's day-of-month number)

  '[@]'¨ replace each with the constant "[@]"

  @{} at elements where the following condition is true:

  ⍵∊⍳d the elements are members of 1…d (today's day-of-month number)

  '[ ]'¨ replace each with the constant "[ ]"

5 5⍴ reshape into a five-by-five matrix

\$\endgroup\$
4
  • \$\begingroup\$ I started learning APL but I'm still at early stages (chapter 10) , I suppose it checks for months as </11 25≥m , can I ask you why you used p in the input? Is it something like an anonymous function? \$\endgroup\$
    – AZTECCO
    Commented Dec 15, 2019 at 1:19
  • 1
    \$\begingroup\$ @AZTECCO p is just the name of the program. TIO's Input corresponds to APL's session (REPL), so p is there to run the program. \$\endgroup\$
    – Adám
    Commented Dec 15, 2019 at 1:20
  • 1
    \$\begingroup\$ @AZTECCO I'm always happy to assist with your APL learning over in the APL Orchard. \$\endgroup\$
    – Adám
    Commented Dec 15, 2019 at 1:27
  • 1
    \$\begingroup\$ @AZTECCO And regarding the checking for months, yes indeed. I've added a full explanation now. Let me know if you want any further explanation of the algorithm. \$\endgroup\$
    – Adám
    Commented Dec 15, 2019 at 1:28
8
\$\begingroup\$

Python 2, 181 189 181 179 Bytes

Original Code

Since I can't comment on other answers yet, this is a slight improvement on ElPedro's previous answer. Try it online! I changed ElPedro's 5th line to save an additional 9 bytes.

i=__import__
t=i("datetime").date.today();n=t.day
c=[('['+(' ','@')[d==n]+']',`d`)[t.month<12or d>n]for d in i("random").sample(range(1,26),25)]
while c:print' '.join(c[:5]);c=c[5:]

Correction #1

Gained 6 bytes in order to fix an error where windows remain open after December 25th until the new year. Try it online!
See corrected version of line 3 below:

c=[('['+(' ','@')[d==n]+']',`d`)[t.month<12or n>25or d>n]for d in i("random").sample(range(1,26),25)]

Correction #2

As per AZTECCO's suggestions, I'm back at 181 bytes :) Try it online!

c=[('['+' @'[d==n]+']',`d`)[t.month-12|25-n|n-d<0]for d in i("random").sample(range(1,26),25)]

Final

With streamlined import statements (Thanks to Reinstate Monica)

import random,datetime as D
t=D.date.today();n=t.day
c=[('['+' @'[d==n]+']',`d`)[t.month-12|25-n|n-d<0]for d in random.sample(range(1,26),25)]
while c:print' '.join(c[:5]);c=c[5:]
\$\endgroup\$
6
  • \$\begingroup\$ Hi and welcome to the site! Thanks for crediting me with the original answer. There seems to be a small problem with dates after 25 Dec Try it online!. If you fix that you will certainly get an upvote from me :) \$\endgroup\$
    – ElPedro
    Commented Dec 16, 2019 at 19:48
  • \$\begingroup\$ Nice catch! Thanks for pointing that out. The new version should work past Dec 25th now :) \$\endgroup\$
    – Grumpy_Boy
    Commented Dec 16, 2019 at 22:15
  • 1
    \$\begingroup\$ Suggestion c=[('['+' @'[d==n]+']',d) \$\endgroup\$
    – AZTECCO
    Commented Dec 16, 2019 at 23:52
  • 2
    \$\begingroup\$ [t.month-12|25-n|n-d<0] saves 2 more (if one of the 3 sets some negative bits the expression becomes true) \$\endgroup\$
    – AZTECCO
    Commented Dec 17, 2019 at 0:15
  • 2
    \$\begingroup\$ You can get this even shorter in Python 3 \$\endgroup\$
    – squid
    Commented Dec 17, 2019 at 16:55
6
\$\begingroup\$

Perl 6, 91 bytes

-1 bytes thanks to nwellnhof

.put for (("[@]"if Date.today~~/12.(0?@(^26))$/),"[ ]"xx+$0,+$0^..25)[*;*].pick(*).rotor(5)

Try it online!

Explanation:

.put for (     ...    )         # Print for each of
          ("[@]"if Date.today~~/12.(0?@(^26))$/), # [@] if today is within the Christmas period
          "[ ]"xx+$0,     # As many [ ]s as the current date
          +$0^..25        # All the number from today to the 25th
                      [*;*]                  # Flatten
                           .pick(*)          # Randomise
                                   .rotor(5) # And split into chunks of 5
\$\endgroup\$
1
  • \$\begingroup\$ I think you need xx+$0 if the regex doesn't match. But @(^26) saves a byte. \$\endgroup\$
    – nwellnhof
    Commented Dec 15, 2019 at 9:37
6
\$\begingroup\$

JavaScript (ES6),  158 ... 150  148 bytes

Saved 2 bytes thanks to @Deckerz

Note: Strictly speaking, this should be marked as ES9 (ECMAScript 2018), in which the output format of Date.prototype.toString has been standardized.

f=(k=25,m,d=Math.random()*25+1|0,t=(new Date+0).match(/c ([01].|2[0-5])|:/)[1])=>k?(m^(m|=1<<d)?(d^t?d<t?'[ ]':d:'[@]')+`
 `[--k%5&&1]:'')+f(k,m):''

Try it online!

How?

(new Date).toString() returns the current date in the following format:

DDD MMM dd yyyy hh:mm:ss TimeZoneString

For instance:

Sun Dec 15 2019 12:29:35 GMT+0000 (Coordinated Universal Time)

We apply the following regular expression to this string:

/c ([01].|2[0-5])|:/

It attempts to match a day of December less than \$26\$ (it's the only month whose 3-letter abbreviation ends in "c"). If not found, we force the first : separator to be matched instead, in order to prevent match() from returning null.

Therefore, the variable \$t\$ (for 'today') is set to either a valid day of December, or undefined if we're outside the relevant period:

t = (new Date + 0).match(/c ([01].|2[0-5])|:/)[1]

\$f\$ is a recursive function that generates a random day \$d\$ in \$[1-25]\$ at each iteration and draws the corresponding slot in the calendar. It keeps track of drawn days in the bitmask \$m\$.

f = (                     // f is a recursive function taking:
  k = 25,                 //   k = remaining number of days to draw
  m,                      //   m = bitmask of drawn days
  d = Math.random() * 25  //   d = random day in [1-25]
      + 1 | 0,            //
  t = ...                 //   t = current day or undefined (see above)
) =>                      //
  k ?                     // if k is not equal to 0:
    ( m ^ (m |= 1 << d) ? //   if m is modified by setting the d-th bit:
        ( d ^ t ?         //     if d is not equal to t:
            d < t ?       //       if t is defined and d is less than t:
              '[ ]'       //         draw an opened window
            :             //       else:
              d           //         draw a closed window
          :               //     else:
            '[@]'         //       draw today's gift
        ) +               //
        `\n `             //     decrement k; append a linefeed at the end of a row
        [--k % 5 && 1]    //     or a space otherwise
      :                   //   else:
        ''                //     we need to pick another day: leave k unchanged and
                          //     append nothing
    ) + f(k, m)           //   append the result of a recursive call
  :                       // else:
    ''                    //   done: stop recursion
\$\endgroup\$
5
  • \$\begingroup\$ does it check for months? \$\endgroup\$
    – AZTECCO
    Commented Dec 15, 2019 at 1:13
  • \$\begingroup\$ although I found your motivation convincing I'm glad you updated your answer. And it's a very valuable one! \$\endgroup\$
    – AZTECCO
    Commented Dec 15, 2019 at 15:22
  • 1
    \$\begingroup\$ @AZTECCO No worries. On second thought, I think it actually makes much more sense to test the month. \$\endgroup\$
    – Arnauld
    Commented Dec 15, 2019 at 15:51
  • \$\begingroup\$ Saved you 2 bytes f=(a=25,b,c=0|25*Math.random()+1,d=(new Date+0).match(/c ([01].|2[0-5])|:/)[1])=>a?(b^(b|=1<<c)?(c^d?c<d?"[ ]":c:"[@]")+` `[--a%5&&1]:"")+f(a,b):"" \$\endgroup\$
    – Deckerz
    Commented Dec 16, 2019 at 17:18
  • \$\begingroup\$ @Deckerz Good catch. Thanks! \$\endgroup\$
    – Arnauld
    Commented Dec 16, 2019 at 19:57
5
\$\begingroup\$

Python 2, 231 222 216 202 197 193 189 bytes

i=__import__
t=i("datetime").date.today()
y=t.day
d=[(((`x`,"[ ]")[x<y],"[@]")[x==y],`x`)[t.month<12or y>25]for x in i("random").sample(range(1,26),25)]
while d:print' '.join(d[:5]);d=d[5:]

Try it online!

-3 with many thanks to @AZTECCO

Examples below use an earlier version of the answer but the formula is the same.

Before December: Try it online!

December after 25th: Try it online!

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

05AB1E, 38 37 32 31 30 bytes

žeD₂‹žf‹P'@ú€…[ÿ]Dg25Ÿ«¦.r5ô»

-6 bytes thanks to @Grimmy.

Try it online or try it online with own specified month / day.

Explanation:

že          # Push the current day
  D         # Duplicate it
   ₂‹       # Check if it's smaller than 26
     žf     # Push the current month
       Â    # Bifurcate it (short for Duplicate & Reverse copy)
        ‹   # And check if this is smaller than the month itself
            # (this will check 1-9 < 1-9 (falsey); 10 < 01 (falsey); 11 < 11 (falsey);
            #  and 12 < 21 (truthy) - so this will only be truthy for 12 / December)
         P  # Take the product of the day and both checks,
            # which result in either the day, or 0 if either check was falsey
'@         '# Push character "@"
  ú         # Pad it with the earlier number amount of spaces
   €        # Map over each of these characters:
    …[ÿ]    #  Push string "[ÿ]", where the `ÿ` is automatically filled with the character
Dg          # Duplicate the list, and pop and push its length
  25Ÿ       # Create a list in the range [length, 25]
     «      # And merge it to the earlier created list
¦           # Then remove the first item (either a "[ ]", or the "[@]" if the checks
            #                             were falsey)
 .r         # Randomly shuffle the list
   5ô       # Split the list into sublists of size 5
     »      # Join each inner list by spaces, and these strings by newlines
            # (after which the result is output implicitly)
\$\endgroup\$
5
  • 1
    \$\begingroup\$ 32 \$\endgroup\$
    – Grimmy
    Commented Dec 17, 2019 at 16:13
  • 1
    \$\begingroup\$ @Grimmy I was about to save the edits of the 33 byte version.. And that one 32-byte version can be golfed further to 31 by using €…[ÿ]. :) \$\endgroup\$ Commented Dec 17, 2019 at 16:19
  • \$\begingroup\$ Alternative 31 \$\endgroup\$
    – Grimmy
    Commented Dec 17, 2019 at 16:34
  • 1
    \$\begingroup\$ 12Q to ‹ for -1. \$\endgroup\$
    – Grimmy
    Commented Dec 18, 2019 at 16:46
  • \$\begingroup\$ @Grimmy Oh, very smart, thanks! :) \$\endgroup\$ Commented Dec 18, 2019 at 17:50
4
\$\begingroup\$

R, 113 103 bytes

x=as.POSIXlt(Sys.time())
a=sample(25)
if(x$mo>10&(y=x$md)<26){a[a<y]="[ ]";a[a==y]="[@]"}
write(a,"",5)

Try it online!

A full program that prints an advert calendar as specified.

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

Red, 135 bytes

d: now c: collect[repeat n 25[keep n]]if all[d/3 = 12
d/4 < 26][repeat n d/4[c/:n:"[ ]"]c/:n:"[@]"]random c
loop 5[print take/part c 5]

Try it online!

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

Jelly, 76 75 bytes

⁶o”@;Ø[jⱮ;@Jị@
“j⁶ṖgqS^OßƭẸ#pÆẸ¥aRŀçĠrṬỤ{½9?ȷ²Yḅ»ḲḢ;Ɱ$ŒV12;Ɱ25¤i+Ɱ25ẊÇs5K€Y

Try it online!

A full program that prints an advent calendar. Longer than most Jelly programs because it has to call __import__('datetime').datetime.today().month and __import__('datetime').datetime.today().day in Python.

If the date and time could be provided as an argument, it could be much shorter:

Jelly, 35 34 bytes

⁶o”@;Ø[jⱮ;@Jị@
12;Ɱ25¤i+Ɱ25ẊÇs5K€Y

Try it online!

\$\endgroup\$
2
  • 1
    \$\begingroup\$ How would the code look if given month and day as arguments? \$\endgroup\$
    – Adám
    Commented Dec 15, 2019 at 16:08
  • 1
    \$\begingroup\$ @Adám I’ve added a version that does this. \$\endgroup\$ Commented Dec 15, 2019 at 16:26
3
\$\begingroup\$

Ruby, 112 bytes

x=[*1..25].shuffle;5.times{|c|puts x[c*5,5].map{|w|['[@]','[ ]',w][((d=Time.now).day-(d.month-12)*30)<=>w]}*" "}

Try it online!

\$\endgroup\$
0
\$\begingroup\$

C#, 267 bytes

using System;using System.Linq;class _{static void Main(){var n=DateTime.Now;var t=n.Month==12?n.Day:0;Console.WriteLine(string.Join(" ",Enumerable.Range(1,25).OrderBy(_=>new Random().Next(-9,9)).Select((d,i)=>(i%5==0?"\r\n":"")+(d<t?$"[ ]":d>t?$"{d}":"[@]"))));}}

.Net Fiddle

\$\endgroup\$

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.