Planejador de calendário ASCII

11

Dada uma lista de atividades e sua hora / data de início, produza um calendário de arte ASCII mostrando as atividades nos dias apropriados. Todas as atividades são garantidas no mesmo mês, não há duas atividades no mesmo dia e todas as atividades cabem na caixa do calendário.

O calendário tem a data no canto superior esquerdo de cada caixa, as caixas têm 9 espaços de largura por 5 espaços de altura, cercados por -e |. A abreviação de duas letras para o dia da semana está centralizada acima da primeira linha e as semanas começam no domingo.

Por exemplo, dadas as seguintes atividades:

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

Saída este calendário correspondente:

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

Esclarecimentos

  • As palavras da programação (combinando [A-Za-z] +) serão delimitadas por um único espaço entre elas (como no exemplo).
  • Dividir o texto nos limites das palavras é suficiente. Não há necessidade de hifenizar palavras.
  • Se fevereiro começar em um domingo em um ano que não seja bissexto, você terá apenas quatro linhas do calendário.
  • Se um mês de 31 dias (por exemplo, agosto) começar no final da semana, talvez você precise gerar seis linhas do calendário.

E / S e regras

  • Seu código deve lidar com datas pelo menos entre 0001-01-01e 9999-12-31no calendário gregoriano, incluindo anos bissextos, conforme apropriado. Por exemplo, se uma entrada é fornecida 2016-02-13 9:00am Test, o calendário de saída deve ter 29 de fevereiro.
  • O formato da data de entrada pode estar em qualquer formato desejado. ISO 8601, um datetimeobjeto, uma sequência particularmente formatada, etc. A análise de entrada não é a parte interessante desse desafio.
  • A entrada e a saída podem ser feitas por qualquer método conveniente .
  • Novas linhas à esquerda / à direita ou outro espaço em branco são opcionais, desde que os caracteres sejam alinhados adequadamente.
  • Um programa completo ou uma função são aceitáveis. Se uma função, você pode retornar a saída em vez de imprimi-la.
  • A saída pode ser no console, retornada como uma lista de cadeias, retornada como uma única cadeia, etc.
  • As brechas padrão são proibidas.
  • Isso é portanto todas as regras usuais de golfe se aplicam e o código mais curto (em bytes) vence.
AdmBorkBork
fonte
1.) Você precisa dividir os nomes das atividades nos limites das palavras? 2.) Quando você tem um mês de fevereiro não bissexto a partir de domingo, você tem apenas 4 linhas? 3.) Quando você precisaria de 6 linhas para mostrar o mês (por exemplo, agosto a partir de sábado) o que acontece?
nedla2004
Relacionado (mais fácil).
Arnauld 10/10
@ nedla2004 1) Sim, os limites das palavras funcionarão bem. 2) Isso está correto, 4 linhas. 3) Seu calendário precisará mostrar 6 linhas. Vou editar em esclarecimentos.
AdmBorkBork 10/10
@Arnauld Sim, isso é uma justa suposição
AdmBorkBork
1
1752-09-02 - 09:00am - Wife's Birthday Tomorrow (14th!)
ngm

Respostas:

10

JavaScript (ES8), 380321 320 bytes

Toma entrada como (y,m,e)onde:

  • y é o ano
  • m é o mês, indexado a 0
  • eé um objeto cujas chaves são os dias e cujos valores são os eventos no [hour, task]formato
(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`)

Experimente online!

Quão?

Abaixo estão algumas partes importantes no código.

Cabeçalho

A linha do cabeçalho é gerada com:

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

Quando split()é usado com uma expressão regular que contém um grupo de captura, esse grupo é incluído na matriz de saída. Nesse caso, fornece:

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

Juntamos essa matriz com 4 espaços, levando a:

'    Su        Mo        Tu        We        Th        Fr        Sa    \n'

que é exatamente o que queremos.

Estrutura do mês

Gymd

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

x

Formatação de evento

O código a seguir é usado para formatar um evento.

[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
fonte
3

Python 2 , 326 324 315 312 307 bytes

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

Experimente online!

Mesma entrada que a resposta JS de Arnauld :

Toma entrada como (y,m,e)onde:

  • y é o ano
  • m é o mês, indexado em 1
  • eé um objeto cujas chaves são os dias e cujos valores são os eventos no (hour, task)formato
TFeld
fonte
3

Carvão , 215 206 bytes

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

Experimente online! Link é a versão detalhada do código. Tira datas no formato d / m / aaaa. Explicação:

Sθ

Insira o primeiro evento.

≔I⪪§⪪θ ⁰/η

Extraia a data e divida em /s.

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

Converta em meses desde 1 de março de BC. Quero calcular o dia da semana do primeiro mês do próximo mês e do mês atual, e trabalhar em meses é mais fácil do que manter os meses e os anos separados e continuar no final do ano, além de permitir que eu comece a contar meses a partir de março, em vez de janeiro, o que é exigido pela congruência de Zeller.

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

Use a congruência de um Zeller modificado para extrair o dia da semana do primeiro dia do próximo mês e deste mês. A parte básica baseia-se no fato de que o número de dias de 30 de outubro do ano anterior ao 1º de um determinado mês em que m = 4para março e m = 14janeiro do ano seguinte é dado pela fórmula m * 153 / 5, no entanto, podemos subtrair 140 porque apenas se preocupam com o dia da semana. Resta então fazer os ajustes devidos ao ano; cada ano adiciona um dia, cada quarto ano adiciona um dia extra, cada centésimo ano subtrai um dia e cada 400º ano adiciona um dia novamente. (Como trabalho em meses, todos esses valores são multiplicados por 12.) De maneira conveniente, isso me dá a resposta diretamente em termos de uma semana indexada no domingo (normalmente você adiciona o dia do mês e começa a contar no sábado).

≔±⊟ηζ

Negue o dia da semana e salve-o como o dia atual do mês.

≔⁺²⁸﹪⁺⊟ηζ⁷ε

Calcule o número de dias no mês a partir do dia da semana dos dois meses.

⭆⪪SuMoTuWeThFrSa²◨◧ι⁶χ

Saída dos cabeçalhos do dia.

↓←⁷¹

Imprima a linha superior de -s.

W‹ζε«

Loop até o último dia do mês ter sido produzido.

Mova o cursor para o início da próxima linha.

F⁷«

Processo 7 dias de cada vez.

P↓⁵→

Imprima a coluna de |s à esquerda.

≦⊕ζ

Incremente o dia atual do mês.

F⁼Iζ§⪪θ/⁰«

Se o dia atual do mês for o dia do evento atual, ...

≔⪪θ - θ

... extrair as outras partes do evento, ...

≔⟦ω◨§θ¹¦⁹⟧δ

... coloque o tempo em 9 espaços e salve-o e uma string vazia como uma lista, ...

F⪪⊟θ 

... divida a descrição em espaços e faça um loop sobre eles, ...

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

... adicionando cada palavra à palavra anterior, se for adequada; ...

P⪫δ¶

... imprimir a hora e a descrição ( Pδnão funciona, pode ser um bug do carvão vegetal?), ...

Sθ»

... e insira o próximo evento.

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

Se o dia atual do mês estiver entre 1 e o último dia do mês, produza-o, caso contrário, apenas produza espaços suficientes para passar para o dia seguinte.

↓⁵←⁷¹

No final da semana, imprima a coluna da direita de se |a linha inferior de -s.

Neil
fonte
Talvez eu tenha pulado isso no seu código TIO detalhado, mas você tem certeza de que a implementação de congruência do Zeller está concluída? Parece estar correto nos meses de março a dezembro, mas nos meses de janeiro e fevereiro year-1deve ser usado em vez de yeare month+12deve ser usado em vez de month. Ou você de alguma forma simplificou o algoritmo que mencionei nesta minha resposta 05AB1E, que é igual à da Wikipedia ?
Kevin Cruijssen 12/10
@KevinCruijssen É basicamente por isso que eu calculo o número de meses desde março, 1 aC, mas é muito complicado explicar mais em um comentário.
Neil
1
@KevinCruijssen Atualizei minha explicação; Espero que você ache isso útil.
1140 Neil
Obrigado! Essa é realmente uma boa fórmula modificada, e agora entendo o raciocínio por trás disso. Muito obrigado por adicioná-lo à explicação. +1 de mim.
Kevin Cruijssen 13/10
2

Java (JDK) , 538 439 428 425 bytes

Possivelmente, a solução mais longa do Code Golf que eu já publiquei. Ainda estou tentando jogar golfe daqui, mas é uma luta.

Conseguiu eliminar 99 bytes alterando o formato de entrada e usando algumas análises de expressões regulares e outras 11 de bits diversos.

3 bytes extras de desconto graças a Kevin!

Inspirando-se em outras respostas, ele leva informações como ano, mês e um Mapa de dias para uma String que representa a hora e o evento no formato <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;}

Experimente online!


Ungolfed

(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
fonte
&&(i=1)<2?7:0pode ser ?7*(i=1):0para salvar 3 bytes.
Kevin Cruijssen 12/10
@KevinCruijssen Nice one thanks!
21418 Luke Stevens
Sugerir em b>x|i++<6?0:7*(i=1)vez de b<=x&++i>6?7*(i=1):0e em c*i>1|b<1|b>x?c<2&e!=null?e.split("-")[i-2]:" ":b+""vez dec*i<2&b>0&b<=x?b+"":c<2&e!=null?e.split("-")[i-2]:" "
tetocat 27/09/19
1

Vermelho , 674 651 bytes

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

Experimente online!

Mais legível:

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
fonte
if e = 0[e: 7]pode ser removido, certo? Você usa e: 1 + c/weekday % 7, então esempre estará no intervalo [1, 7].
Kevin Cruijssen 12/10
@KevinCruijssen: Talvez esteja faltando alguma coisa, mas acho que preciso. A indexação vermelha é baseada em 1. Dê uma olhada no seguinte: >> c: now / time / date == 12 / out / 2018 >> c: c + 1 == 13 / out / 2018 >> 1 + c / weekday% 7 == 0; >> 1 + 2 * 3 é 9 em vermelho, não 7
Galen Ivanov
1
EDIT: Ah, nvm .. O que 1 + acontece primeiro .. Ok, eu vejo o meu erro. Estou acostumado %e /com precedência de operador +.
Kevin Cruijssen 12/10
1
@KevinCruijssen Sim, exatamente. Não há nenhuma precedência do operador em vermelho, é preciso usar () em vez
Galen Ivanov