Advent calendar

18

2

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.

AZTECCO

Posted 2019-12-15T00:26:38.030

Reputation: 2 441

1Is 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.) – Arnauld – 2019-12-15T00:41:05.283

1

So this was in the Sandbox for… 8 minutes?

– Adám – 2019-12-15T00:43:48.413

1@JonathanAllan Indeed, Danish advent calenders don't include the 25th. – Adám – 2019-12-15T00:46:25.523

@Arnauld yes it's reusable like most of the commercial calendars, so year doesn't matter – AZTECCO – 2019-12-15T00:46:38.757

@Jonathan Allan yes and sorry .. I meant Christmas day.. Fixing – AZTECCO – 2019-12-15T00:49:09.163

1@Jonathan Allan yes you have to use system time, no input – AZTECCO – 2019-12-15T00:51:05.507

2@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. – AZTECCO – 2019-12-15T00:57:01.517

There's still ten days until Christmas... – Jo King – 2019-12-15T01:03:28.097

3@Jo King I don't want to see only open windows, at this time we have a good open/closed ratio – AZTECCO – 2019-12-15T01:06:01.617

2Earlier 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! – Giuseppe – 2019-12-15T01:59:58.373

@Adám Wow, with this challenge I learnt that there are advent calendars with 25 windows. Also in Austria and Germany (and pretty sure in all countries where Christmas is celebrated at the evening of the 24th of December), advent calenders have 24 windows only. – rexkogitans – 2019-12-16T08:13:04.477

Could you provide more testcase about Jan. 1st, Dec. 1st, Dec. 25th, Dec. 26th? since I don't know what advent calendar is... – tsh – 2019-12-16T08:20:48.450

@rexkogitans actually the regular advent calendar has 24 days but there are many exceptions on the market with 25 days or 24 + a merry Christmas slot – AZTECCO – 2019-12-16T08:31:42.093

@rexkogitans Though, to be fair, in later years, I've seen those that begin with the 25th — of November! One more week of shopping… – Adám – 2019-12-16T08:36:06.863

@tsh for this challenge, on every month except for December, it must print only days (no opened windows) although as pointed out before nothing stop you to open windows on every month. Gonna add test cases for clarity. – AZTECCO – 2019-12-16T08:40:33.337

@Adám in fact the real advent time begins on Advent Sunday, which is the fourth Sunday before Christmas day. It often happens to be on November. – AZTECCO – 2019-12-16T08:48:06.663

Answers

9

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

Adám

Posted 2019-12-15T00:26:38.030

Reputation: 37 779

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? – AZTECCO – 2019-12-15T01:19:20.193

1@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. – Adám – 2019-12-15T01:20:47.127

1

@AZTECCO I'm always happy to assist with your APL learning over in the APL Orchard.

– Adám – 2019-12-15T01:27:22.243

1@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. – Adám – 2019-12-15T01:28:24.020

7

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

Grumpy_Boy

Posted 2019-12-15T00:26:38.030

Reputation: 71

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

– ElPedro – 2019-12-16T19:48:20.213

Nice catch! Thanks for pointing that out. The new version should work past Dec 25th now :) – Grumpy_Boy – 2019-12-16T22:15:27.420

1Suggestion c=[('['+' @'[d==n]+']',d) – AZTECCO – 2019-12-16T23:52:32.677

2[t.month-12|25-n|n-d<0] saves 2 more (if one of the 3 sets some negative bits the expression becomes true) – AZTECCO – 2019-12-17T00:15:32.710

2

You can get this even shorter in Python 3

– Reinstate Monica – 2019-12-17T16:55:57.177

import random,datetime as D saves 2 bytes over i=__import__ – Reinstate Monica – 2019-12-17T18:18:35.827

6

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

Arnauld

Posted 2019-12-15T00:26:38.030

Reputation: 111 334

does it check for months? – AZTECCO – 2019-12-15T01:13:55.003

although I found your motivation convincing I'm glad you updated your answer. And it's a very valuable one! – AZTECCO – 2019-12-15T15:22:44.640

1@AZTECCO No worries. On second thought, I think it actually makes much more sense to test the month. – Arnauld – 2019-12-15T15:51:22.563

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):"" – Deckerz – 2019-12-16T17:18:17.883

@Deckerz Good catch. Thanks! – Arnauld – 2019-12-16T19:57:49.450

6

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

Jo King

Posted 2019-12-15T00:26:38.030

Reputation: 38 234

I think you need xx+$0 if the regex doesn't match. But @(^26) saves a byte. – nwellnhof – 2019-12-15T09:37:19.690

5

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)

Kevin Cruijssen

Posted 2019-12-15T00:26:38.030

Reputation: 67 575

132 – Grimmy – 2019-12-17T16:13:09.690

1@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 €…[ÿ]. :) – Kevin Cruijssen – 2019-12-17T16:19:20.037

Alternative 31 – Grimmy – 2019-12-17T16:34:31.210

112Q to ‹ for -1. – Grimmy – 2019-12-18T16:46:26.457

@Grimmy Oh, very smart, thanks! :) – Kevin Cruijssen – 2019-12-18T17:50:00.617

4

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.

Nick Kennedy

Posted 2019-12-15T00:26:38.030

Reputation: 11 829

4

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!

ElPedro

Posted 2019-12-15T00:26:38.030

Reputation: 5 301

2

This python trick : https://codegolf.stackexchange.com/a/187046/84844 helps you save a bit.. 190

– AZTECCO – 2019-12-15T15:38:57.130

3

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!

Galen Ivanov

Posted 2019-12-15T00:26:38.030

Reputation: 13 815

3

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!

Nick Kennedy

Posted 2019-12-15T00:26:38.030

Reputation: 11 829

1How would the code look if given month and day as arguments? – Adám – 2019-12-15T16:08:39.253

1@Adám I’ve added a version that does this. – Nick Kennedy – 2019-12-15T16:26:36.000

3

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!

G B

Posted 2019-12-15T00:26:38.030

Reputation: 11 099

0

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

aschoenebeck

Posted 2019-12-15T00:26:38.030

Reputation: 101