Planificateur de calendrier ASCII

11

Étant donné une liste d'activités et leur date / heure de début, sortez un calendrier ASCII-art montrant les activités les jours appropriés. Toutes les activités sont garanties pour être dans le même mois, il n'y aura pas deux activités le même jour, et toutes les activités sont garanties pour s'inscrire dans la boîte de calendrier.

Le calendrier a la date dans le coin supérieur gauche de chaque boîte, les boîtes font 9 espaces de large sur 5 espaces de haut, entourées de -et |. L'abréviation à deux lettres du jour de la semaine est centrée au-dessus de la première ligne et les semaines commencent par le dimanche.

Par exemple, compte tenu des activités suivantes:

10/5/2018 - 9:00am - Sandbox calendar challenge
10/9/2018 - 9:00am - Post challenge to main
10/10/2018 - 10:00am - Profit
10/31/2018 - 7:30pm - Halloween party

Sortez ce calendrier correspondant:

    Su        Mo        Tu        We        Th        Fr        Sa     
-----------------------------------------------------------------------
|         |1        |2        |3        |4        |5        |6        |
|         |         |         |         |         |9:00am   |         |
|         |         |         |         |         |Sandbox  |         |
|         |         |         |         |         |calendar |         |
|         |         |         |         |         |challenge|         |
-----------------------------------------------------------------------
|7        |8        |9        |10       |11       |12       |13       |
|         |         |9:00am   |10:00am  |         |         |         |
|         |         |Post     |Profit   |         |         |         |
|         |         |challenge|         |         |         |         |
|         |         |to main  |         |         |         |         |
-----------------------------------------------------------------------
|14       |15       |16       |17       |18       |19       |20       |
|         |         |         |         |         |         |         |
|         |         |         |         |         |         |         |
|         |         |         |         |         |         |         |
|         |         |         |         |         |         |         |
-----------------------------------------------------------------------
|21       |22       |23       |24       |25       |26       |27       |
|         |         |         |         |         |         |         |
|         |         |         |         |         |         |         |
|         |         |         |         |         |         |         |
|         |         |         |         |         |         |         |
-----------------------------------------------------------------------
|28       |29       |30       |31       |         |         |         |
|         |         |         |7:30pm   |         |         |         |
|         |         |         |Halloween|         |         |         |
|         |         |         |party    |         |         |         |
|         |         |         |         |         |         |         |
-----------------------------------------------------------------------

Clarifications

  • Les mots de planification (correspondant à [A-Za-z] +) seront délimités par un seul espace entre eux (comme dans l'exemple).
  • Le fractionnement du texte sur les limites des mots est suffisant. Pas besoin de couper les mots.
  • Si février commence un dimanche d'une année non bissextile, vous n'aurez que quatre lignes de calendrier.
  • Si un mois de 31 jours (par exemple, août) commence tard dans la semaine, vous devrez peut-être afficher six lignes de calendrier.

E / S et règles

  • Votre code doit gérer les dates au moins entre 0001-01-01et 9999-12-31dans le calendrier grégorien, y compris les années bissextiles, le cas échéant. Par exemple, si une entrée est donnée 2016-02-13 9:00am Test, le calendrier de sortie doit avoir le 29 février.
  • Le format de date d'entrée peut être dans n'importe quel format souhaité. ISO 8601, un datetimeobjet, une chaîne particulièrement formatée, etc. L'analyse syntaxique d'entrée n'est pas la partie intéressante de ce défi.
  • L'entrée et la sortie peuvent se faire par n'importe quelle méthode pratique .
  • Les sauts de ligne de début / fin ou d'autres espaces sont facultatifs, à condition que les caractères s'alignent correctement.
  • Un programme complet ou une fonction sont acceptables. S'il s'agit d'une fonction, vous pouvez renvoyer la sortie plutôt que de l'imprimer.
  • La sortie peut être vers la console, retournée sous forme de liste de chaînes, retournée sous forme de chaîne unique, etc.
  • Les failles standard sont interdites.
  • Il s'agit de donc toutes les règles de golf habituelles s'appliquent et le code le plus court (en octets) l'emporte.
AdmBorkBork
la source
1.) Devez-vous diviser les noms d'activités sur les limites des mots? 2.) Lorsque vous avez un mois de février non bissextile commençant un dimanche, avez-vous seulement 4 lignes? 3.) Quand vous auriez besoin de 6 lignes pour afficher le mois (ex. Août à partir du samedi) que se passe-t-il?
nedla2004
Connexes (plus faciles).
Arnauld
@ nedla2004 1) Oui, les limites des mots fonctionneront bien. 2) C'est exact, 4 rangées. 3) Votre calendrier devra afficher 6 lignes. Je vais éditer des clarifications.
AdmBorkBork
@Arnauld Oui, c'est une hypothèse juste
AdmBorkBork
1
1752-09-02 - 09:00am - Wife's Birthday Tomorrow (14th!)
ngm

Réponses:

10

JavaScript (ES8), 380321 320 octets

Prend l'entrée comme (y,m,e)où:

  • y est l'année
  • m est le mois, indexé 0
  • eest un objet dont les clés sont les jours et dont les valeurs sont les événements au [hour, task]format
(y,m,e)=>`SuMoTuWeThFrSa
`.split(/(..)/).join`    `+(p='-'.repeat(d=71)+`
`)+(g=i=>++d<1|(x=G`getMonth`==m)|i%7?`|${[h,t]=e[d]||E,o=[x*d,h],q=E,t&&t.split` `.map(s=>q=(u=q?q+' '+s:s)[9]?o.push(q)&&s:u),[...o,q][r%5]||E}`.padEnd(10)+(++i%7?E:`|
`+(++r%5?(d-=7,E):p))+g(i):E)(E=r='',d=-(G=s=>new Date(y,m,d)[s]())`getDay`)

Essayez-le en ligne!

Comment?

Voici quelques parties importantes du code.

Entête

La ligne d'en-tête est générée avec:

'SuMoTuWeThFrSa\n'.split(/(..)/).join`    `

Lorsqu'il split()est utilisé avec une expression régulière contenant un groupe de capture, ce groupe est inclus dans le tableau de sortie. Dans ce cas, cela donne:

[ '', 'Su', '', 'Mo', '', 'Tu', '', 'We', '', 'Th', '', 'Fr', '', 'Sa', '\n' ]

Nous rejoignons ce tableau avec 4 espaces, conduisant à:

'    Su        Mo        Tu        We        Th        Fr        Sa    \n'

c'est exactement ce que nous voulons.

Structure du mois

La fonction d'assistance construit une date à partir de l'année d'entrée et du mois d'entrée (qui sont constants) et du jour (qui est dynamique). Son paramètre est un nom de méthode, qui permet d'extraire soit le jour de la semaine soit le mois.Gymd

G = s => new Date(y, m, d)[s]()

Il est d'abord utilisé pour identifier le jour de la semaine du premier jour du mois, afin que nous puissions revenir au dernier dimanche à partir de là. Il est ensuite utilisé pour détecter quand nous rentrons le mois correct et quand nous le quittons (ces informations sont stockées dans le booléen ).x

Formatage d'événement

Le code suivant est utilisé pour formater un événement.

[h, t] = e[d] || E,           // split the content of the event into h and t
o = [x * d, h],               // set the first 2 entries of o[]: day and hour
q = E,                        // we start with q = empty string
t &&                          // skip this .map() loop entirely if t is not defined
t.split` `                    // split the string on spaces
.map(s =>                     // for each word s:
  q =                         //   update q:
    (u = q ? q + ' ' + s : s) //     u is the concatenation of the previous string with
                              //     the new word, separated by a space if needed
    [9] ?                     //     if u is more than 9 character long:
      o.push(q)               //       push the previous string in o[]
      && s                    //       and reset q to s
    :                         //     else:
      u                       //       update q to u
),                            // end of map()
[...o, q][r % 5]              // append the last pending part to o[] and extract the
|| E                          // relevant row; or use an empty string by default
Arnauld
la source
3

Python 2 , 326 324 315 312 307 octets

import calendar as c,textwrap as t
c.setfirstweekday(6)
y,m,e=input()
print' Su Mo Tu We Th Fr Sa\n'.replace(' ',' '*8)[4:]+'-'*71
for w in c.monthcalendar(y,m):
 for l in zip(*[[d or'',a]+(t.wrap(b,9)+['']*3)[:3]for d in w for a,b in[e.get(d,'  ')]]):print'|'+'|'.join('%-9s'%v for v in l)+'|'
 print'-'*71

Essayez-le en ligne!

Même entrée que la réponse JS d'Arnauld :

Prend l'entrée comme (y,m,e)où:

  • y est l'année
  • m est le mois, indexé 1
  • eest un objet dont les clés sont les jours et dont les valeurs sont les événements au (hour, task)format
TFeld
la source
3

Charbon de bois , 215 206 octets

Sθ≔I⪪§⪪θ ⁰/η≔⁻⁺×¹²⊟η⊟η²η≔EE²⁻ηι﹪Σ⟦÷ι⁴⁸⁰⁰±÷ι¹²⁰⁰÷ι⁴⁸÷ι¹²÷×¹³⁺⁴﹪ι¹²¦⁵⟧⁷η≔±⊟ηζ≔⁺²⁸﹪⁺⊟ηζ⁷ε⭆⪪SuMoTuWeThFrSa²◨◧ι⁶χ↓←⁷¹W‹ζε«↘F⁷«P↓⁵→≦⊕ζF⁼Iζ§⪪θ/⁰«≔⪪θ - θ≔⟦ω◨§θ¹¦⁹⟧δF⪪⊟θ ⊞δ⎇‹⁺L§δ±¹Lμ⁹⁺⁺⊟δ μμP⪫δ¶Sθ»◨×››ζ⁰›ζεIζ⁹»↓⁵←⁷¹

Essayez-le en ligne! Le lien est vers la version détaillée du code. Prend les dates au format j / m / aaaa. Explication:

Sθ

Saisissez le premier événement.

≔I⪪§⪪θ ⁰/η

Extraire la date et diviser sur /s.

≔⁻⁺×¹²⊟η⊟η²η

Convertir en mois depuis mars, 1 av. Je veux calculer le jour de la semaine du premier du mois suivant et du mois en cours, et travailler en mois est plus facile que de garder les mois et les années séparés et de les transporter à la fin de l'année, et cela me permet également de commencer à compter mois commençant en mars au lieu de janvier, ce qui est requis par la congruence de Zeller.

≔EE²⁻ηι﹪Σ⟦÷ι⁴⁸⁰⁰±÷ι¹²⁰⁰÷ι⁴⁸÷ι¹²÷×¹³⁺⁴﹪ι¹²¦⁵⟧⁷η

Utilisez une congruence de Zeller modifiée pour extraire le jour de la semaine du premier jour du mois suivant et ce mois-ci. La partie de base repose sur le fait que le nombre de jours entre le 30 octobre de l'année précédente et le 1er d'un mois donné où m = 4pour mars et m = 14janvier de l'année suivante est donné par la formule m * 153 / 5, cependant on peut soustraire 140 car on ne fait que attention au jour de la semaine. Il reste alors à procéder aux ajustements dus à l'année; chaque année ajoute un jour, chaque 4e année ajoute un jour supplémentaire, chaque 100e année soustrait un jour et chaque 400e année ajoute à nouveau un jour. (Comme je travaille en mois, ces valeurs sont toutes multipliées par 12.) De manière assez pratique, cela me donne directement la réponse en termes de semaine indexée le dimanche (normalement, vous ajoutez le jour du mois et commencez à compter le samedi).

≔±⊟ηζ

Annulez le jour de la semaine et enregistrez-le comme jour du mois en cours.

≔⁺²⁸﹪⁺⊟ηζ⁷ε

Calculez le nombre de jours du mois à partir du jour de la semaine des deux mois.

⭆⪪SuMoTuWeThFrSa²◨◧ι⁶χ

Sortez les en-têtes de jour.

↓←⁷¹

Imprimez la rangée supérieure du par -.

W‹ζε«

Boucle jusqu'au dernier jour du mois.

Déplacez le curseur au début de la ligne suivante.

F⁷«

Traitez 7 jours à la fois.

P↓⁵→

Imprimez la colonne de |s à gauche.

≦⊕ζ

Incrémentez le jour en cours du mois.

F⁼Iζ§⪪θ/⁰«

Si le jour actuel du mois est le jour de l'événement en cours, ...

≔⪪θ - θ

... extraire les autres parties de l'événement, ...

≔⟦ω◨§θ¹¦⁹⟧δ

... pad le temps à 9 espaces et l'enregistrer et une chaîne vide comme une liste, ...

F⪪⊟θ 

... diviser la description sur les espaces et les parcourir, ...

⊞δ⎇‹⁺L§δ±¹Lμ⁹⁺⁺⊟δ μμ

... en ajoutant chaque mot au mot précédent s'il convient; ...

P⪫δ¶

... afficher l'heure et la description ( Pδne fonctionne pas, peut-être un bug de charbon de bois?), ...

Sθ»

... et saisissez le prochain événement.

◨×››ζ⁰›ζεIζ⁹»

Si le jour actuel du mois est compris entre 1 et le dernier jour du mois, affichez-le, sinon affichez juste assez d'espaces pour passer au jour suivant.

↓⁵←⁷¹

À la fin de la semaine, imprimez la colonne de droite de |s et la ligne inférieure de -s.

Neil
la source
Peut-être que je l'ai sauté dans votre code TIO verbeux, mais êtes-vous sûr que l'implémentation de congruence de votre Zeller est terminée? Il semble être correct pour les mois de mars à décembre, mais pour les mois janvier et février year-1devraient être utilisés à la place de yearet month+12devraient être utilisés à la place de month. Ou avez-vous simplifié en quelque sorte l'algorithme que j'ai mentionné dans ma réponse 05AB1E qui est égale à celle de Wikipedia ?
Kevin Cruijssen du
@KevinCruijssen C'est essentiellement pourquoi je calcule le nombre de mois depuis mars, 1BC, mais c'est trop compliqué à expliquer davantage dans un commentaire.
Neil
1
@KevinCruijssen J'ai mis à jour mon explication; J'espère que cela vous aidera.
Neil
Merci! C'est en effet une belle formule modifiée, et je comprends maintenant le raisonnement derrière cela. Merci beaucoup de l'avoir ajouté à l'explication. +1 de moi.
Kevin Cruijssen
2

Java (JDK) , 538 439 428 425 octets

Probablement la plus longue solution Code Golf que j'ai jamais publiée. J'essaie toujours de jouer au golf à partir d'ici, mais c'est un combat.

Géré pour supprimer 99 octets en modifiant le format d'entrée et en utilisant une analyse syntaxique regex, et 11 autres à partir de bits divers.

3 octets de plus grâce à Kevin!

S'inspirant d'autres réponses, il prend comme année, mois et une carte des jours une chaîne représentant l'heure et l'événement dans le format <time>-<event>.

(y,m,M)->{var C=java.util.Calendar.getInstance();C.set(y,m-1,1);String r=",Su,,Mo,,Tu,,We,,Th,,Fr,,Sa\n".replace(",","    "),e;for(int x=C.getActualMaximum(5),l=0,b=0,j,c,i=0;i<7;r+="\n",l+=b<=x&++i>6?7*(i=1):0)for(j=0;j<71;b=l+j/10+2-C.get(7),e=(e=M.get(b))!=null?e.replaceAll("([^-]{1,9})(-| |$)","$1-")+" - ":null,r+=e=i%6<1?"-":c<1?"|":c*i<2&b>0&b<=x?b+"":c<2&e!=null?e.split("-")[i-2]:" ",j+=e.length())c=j%10;return r;}

Essayez-le en ligne!


Non golfé

(y,m,M)->{                                              // Lambda taking input as a year, month and map
  var C=java.util.Calendar.getInstance();               // Creates a new Calendar instance
  C.set(y,m-1,1);                                       // Sets the calendar to the first of the month in the given year    
  String r=",Su,,Mo,,Tu,,We,,Th,,Fr,,Sa\n"              // Creates the header row by replacing
    .replace(",","    "),e;                             // commas with 4 sets of spaces

  for(                                                  // Creates 7 rows for a calendar row
      int x=C.getActualMaximum(5)                       // Stores last day of the month
      ,l=0,b=0,j,c,i=0;i<7;                             // Initialises other integers
      r+="\n",                                          // Add new line each row
      l+=b<=x&++i>6                                     // If end of a calendar row is reached, and current day is less than max
        ?7*(i=1)                                        // Set i to 1 and add 7 to line count to create another calendar row
        :0)                                             // Otherwise do nothing

    for(j=0;j<71;                                       // Loop 71 times, appending characters to create a row
        b=l+j/10+2-C.get(7),                            // Determine the day of the box we're in
        e=(e=M.get(b))!=null?                           // Get the event for this day from the map and if not null
            e.replaceAll("([^-]{1,9})(-| |$)","$1-")      // Do some regex to separate the line entries by hyphens
            +" - "                                      // Append extra hyphen to prevent array out of bounds
            :null,                                      // Otherwise do nothing
        r+=e=i%6<1?"-":                                 // If it's the first line of a calendar row, append -
           c<1?"|":                                     // If it's the first column of a box, append |
           c*i<2&b>0&b<=x?b+"":                         // If it's the second column of a box, the second row, 
                                                        // and less than the max day, append the day
           c<2&e!=null?e.split("-")[i-2]:               // If it's any other row and there is an event then split and append correct line
           " ",                                         // Else just append a space
        j+=e.length())                                  // Increase the row character count by the length to append
          c=j%10;                                       // Set the column of box (this is the only thing in the loop so happens first)

  return r;                                             // return the calendar string!
}
Luke Stevens
la source
&&(i=1)<2?7:0peut être ?7*(i=1):0d'économiser 3 octets.
Kevin Cruijssen du
@KevinCruijssen Nice one mercis!
Luke Stevens
Suggérer à la b>x|i++<6?0:7*(i=1)place b<=x&++i>6?7*(i=1):0et à la c*i>1|b<1|b>x?c<2&e!=null?e.split("-")[i-2]:" ":b+""place dec*i<2&b>0&b<=x?b+"":c<2&e!=null?e.split("-")[i-2]:" "
plafondcat
1

Rouge , 674 651 octets

func[a][z: func[a b c][to-tuple reduce[a b c]]c: a/1 c/4: 1 d: c + 31
d/4: 1 d: d - 1 e: 1 + c/weekday % 7 if e = 0[e: 7]foreach
t[:Su:Mo:Tu:We:Th:Fr:Sa][prin pad pad/left t 6 10]h:
pad/with"-"71 #"-"print["^/"h]m: copy[]foreach[i b]a[put m z r:(t: e - 1)+
i/4 / 7 + 1 n: i/4 % 7 + t 2 b/1 t: split b/2" "l: 0
until[if t/2[if 10 >((length? t/1)+ length? t/2)[t/1:
rejoin reduce[t/1" "take next t]]]put m z r n 2 + l: l + 1 take t
tail? t]i: 0]n: k: 0 repeat v(g: e - 1 + d/4)/ 7 + sign? g % 7[repeat
r 5[repeat i 7[t: copy"|"if i = e[k: 1]if all[k = 1 r = 1 n < d/4][append t
n: n + 1]if s: select m z v i r[append t s]prin pad t 10]print"|"]print h]]

Essayez-le en ligne!

Plus lisible:

func [ a ] [
    c: d: a/1
    c/4: d/4: 1
    d: d + 31
    d/4: 1
    d: d - 1
    e: 1 + c/weekday % 7
    if e = 0[e: 7]
    g: e - 1 + d/4
    w: g / 7 + sign? g % 7
    foreach t [ :Su :Mo :Tu :We :Th :Fr :Sa ] [
        prin pad pad/left t 6 10
    ]
    h: pad/with "-" 71 #"-"
    print[ "^/" h ]
    m: copy #()
    foreach[ i b ] a [
        n: i/4 % 7 + t: e - 1
        r: t + i/4 / 7 + 1
        put m to-tuple reduce[ r n 2 ] b/1
        t: split b/2" "
        i: 0
        until [
            if t/2 [ if 9 >= ((length? t/1) + (length? t/2)) [
                t/1: rejoin reduce[t/1" "take next t]
                ]
            ]
            put m to-tuple reduce [ r n 2 + i: i + 1 ] take t
            tail? t
        ]

    ]
    n: 0
    g: off
    repeat v w [
        repeat r 5 [
           repeat i 7 [
                t: copy "|"
                if i = e[ g: on ]
                if all [ g r = 1 n < d/4 ] [ append t n: n + 1 ]
                if s: select m to-tuple reduce [ v i r ]
                    [ append t s ]
                prin pad t 10
            ]
            print "|"
        ]
        print h
    ]
]
Galen Ivanov
la source
if e = 0[e: 7]peut être retiré, non? Vous utilisez e: 1 + c/weekday % 7, donc esera toujours dans la plage [1, 7].
Kevin Cruijssen du
@KevinCruijssen: Peut-être que je manque quelque chose, mais je pense que j'en ai besoin. L'indexation en rouge est basée sur 1. Veuillez regarder ceci: >> c: maintenant / heure / date == 12-oct-2018 >> c: c + 1 == 13-oct-2018 >> 1 + c / jour de semaine% 7 == 0; >> 1 + 2 * 3 est 9 en rouge, pas 7
Galen Ivanov
1
EDIT: Ah, nvm .. Le 1 + premier arrive .. Ok, je vois mon erreur. J'ai l'habitude %et la /priorité des opérateurs est terminée +.
Kevin Cruijssen du
1
@KevinCruijssen Oui, exactement. Il n'y a pas de priorité d'opérateur en rouge, il faut utiliser () à la place
Galen Ivanov