Планировщик календаря ASCII

11

Учитывая список действий и их время начала / дату, выведите календарь ASCII-искусства, показывающий действия в соответствующие дни. Все действия гарантированно будут выполняться в одном месяце, никакие два действия не будут выполняться в один и тот же день, и все действия гарантированно помещаются в поле календаря.

Календарь имеет дату в верхнем левом углу каждого блока, прямоугольники имеют ширину 9 и 5 пробелов в окружении -и |. Двухбуквенное сокращение дня недели расположено по центру над первым рядом, а недели начинаются с воскресенья.

Например, с учетом следующих действий:

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

Выведите этот соответствующий календарь:

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

Разъяснения

  • Слова расписания (соответствующие [A-Za-z] +) будут разделены одним пробелом между ними (как в примере).
  • Разделение текста по границам слова достаточно. Нет необходимости в переносе слов.
  • Если февраль начинается в воскресенье не в високосный год, у вас будет только четыре календарных строки.
  • Если 31-дневный месяц (например, август) начинается в конце недели, вам может потребоваться вывести шесть календарных строк.

I / O и правила

  • Ваш код должен обрабатывать даты, по крайней мере, между 0001-01-01и 9999-12-31в григорианском календаре, включая високосные годы в зависимости от ситуации. Например, если заданы входные данные 2016-02-13 9:00am Test, выходной календарь должен иметь 29 февраля.
  • Формат ввода даты может быть в любом желаемом формате. ISO 8601, datetimeобъект, строка специального формата и т. Д. Разбор входных данных не является интересной частью этой задачи.
  • Ввод и вывод может быть любым удобным способом .
  • Лидирующие / завершающие символы новой строки или другие пробелы являются необязательными, при условии, что символы располагаются соответствующим образом.
  • Либо полная программа или функция приемлемы. Если функция, вы можете вернуть вывод, а не распечатать его.
  • Вывод может быть на консоль, возвращен в виде списка строк, возвращен в виде одной строки и т. Д.
  • Стандартные лазейки запрещены.
  • Это поэтому применяются все обычные правила игры в гольф, и выигрывает самый короткий код (в байтах).
AdmBorkBork
источник
1.) Нужно ли разбивать названия занятий на границы слов? 2.) Если у вас не-високосный февраль, начинающийся в воскресенье, у вас есть только 4 ряда? 3.) Когда вам нужно 6 строк, чтобы показать месяц (например, август начинается в субботу), что происходит?
nedla2004
Связанные (проще).
Арно
@ nedla2004 1) Да, границы слов будут работать нормально. 2) Это правильно, 4 ряда. 3) Ваш календарь должен показать 6 строк. Я буду редактировать в пояснениях.
AdmBorkBork
@Arnauld Да, это справедливое предположение
AdmBorkBork
1
1752-09-02 - 09:00am - Wife's Birthday Tomorrow (14th!)
нгм

Ответы:

10

JavaScript (ES8), 380321 320 байт

Принимает вход как (y,m,e)где:

  • y это год
  • m это месяц с 0 индексами
  • eэто объект, ключи которого - дни, а значения - события в [hour, task]формате
(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`)

Попробуйте онлайн!

Как?

Ниже приведены некоторые важные части кода.

заголовок

Строка заголовка генерируется с помощью:

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

Когда split()используется с регулярным выражением, содержащим группу захвата, эта группа включается в выходной массив. В этом случае это дает:

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

Мы объединяем этот массив с 4 пробелами, что приводит к:

'    Su        Mo        Tu        We        Th        Fr        Sa    \n'

что именно то, что мы хотим.

Структура месяца

граммYмd

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

Икс

Форматирование событий

Следующий код используется для форматирования события.

[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
источник
3

Python 2 , 326 324 315 312 307 байт

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

Попробуйте онлайн!

Тот же вклад, что и в ответе Арнаулда :

Принимает вход как (y,m,e)где:

  • y это год
  • m это месяц, 1 проиндексированный
  • eэто объект, ключи которого - дни, а значения - события в (hour, task)формате
TFeld
источник
3

Древесный уголь , 215 206 байт

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

Попробуйте онлайн! Ссылка на подробную версию кода. Принимает даты в формате д / м / гггг. Объяснение:

Sθ

Введите первое событие.

≔I⪪§⪪θ ⁰/η

Извлечь дату и разделить на /с.

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

Перевести в месяцы с 1 марта до н. Я хочу рассчитать день недели первого и следующего месяца, и текущего месяца, и работать в месяцах легче, чем разделять месяцы и годы и переносить их в конце года, плюс это также позволяет мне начать подсчет месяцы начинаются в марте вместо января, что требуется конгруэнтностью Зеллера.

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

Используйте измененную конгруэнтность Целлера, чтобы извлечь день недели первого дня следующего месяца и этого месяца. Основная часть опирается на тот факт, что число дней с 30 октября предыдущего года до 1 числа данного месяца, где m = 4для марта и m = 14января следующего года определяется по формуле m * 153 / 5, однако мы можем вычесть 140, потому что мы только заботиться о дне недели. Затем остается внести коррективы из-за года; каждый год добавляет день, каждый четвертый год добавляет дополнительный день, каждый сотый год вычитает день, а каждый 400-й год добавляет день снова. (Поскольку я работаю в месяцах, все эти значения умножаются на 12.) Скорее всего, это напрямую дает мне ответ с точки зрения индексируемой в воскресенье недели (обычно вы добавляете день месяца и начинаете считать в субботу).

≔±⊟ηζ

Отмените день недели и сохраните его как текущий день месяца.

≔⁺²⁸﹪⁺⊟ηζ⁷ε

Рассчитайте количество дней в месяце от дня недели двух месяцев.

⭆⪪SuMoTuWeThFrSa²◨◧ι⁶χ

Выведите заголовки дня.

↓←⁷¹

Распечатать верхний ряд -с.

W‹ζε«

Цикл, пока не будет выведен последний день месяца.

Переместите курсор в начало следующей строки.

F⁷«

Обрабатывайте 7 дней за один раз.

P↓⁵→

Распечатать столбец |s слева.

≦⊕ζ

Увеличение текущего дня месяца.

F⁼Iζ§⪪θ/⁰«

Если текущий день месяца является днем ​​текущего события, ...

≔⪪θ - θ

... извлечь другие части мероприятия, ...

≔⟦ω◨§θ¹¦⁹⟧δ

... добавьте время к 9 пробелам и сохраните его и пустую строку в виде списка ...

F⪪⊟θ 

... разбить описание на пробелы и зациклить их, ...

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

... добавляя каждое слово к предыдущему слову, если оно будет соответствовать; ...

P⪫δ¶

... вывести время и описание ( Pδне работает, может быть ошибка с углем?), ...

Sθ»

... и введите следующее событие.

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

Если текущий день месяца находится между 1 и последним днем ​​месяца, выведите его, в противном случае просто выведите достаточно пробелов, чтобы перейти к следующему дню.

↓⁵←⁷¹

В конце недели выведите правый столбец |s и нижний ряд -s.

Нил
источник
Может быть, я пропустил это в вашем подробном коде TIO, но вы уверены, что реализация конгруэнтности вашего Zeller завершена? Кажется, это правильно для месяцев с марта по декабрь, но для месяцев year-1следует использовать январь и февраль вместо yearи month+12вместо month. Или вы как-то упростили алгоритм, который я упомянул в этом моем ответе 05AB1E, который совпадает с алгоритмом из Википедии ?
Кевин Круйссен,
@KevinCruijssen По этой причине я рассчитываю количество месяцев с марта 1BC, но это слишком сложно объяснить в комментарии.
Нил
1
@KevinCruijssen Я обновил свое объяснение; Я надеюсь, что вы найдете это полезным.
Нил
Благодаря! Это действительно хорошая модифицированная формула, и теперь я понимаю причину этого. Большое спасибо за добавление этого в объяснение. +1 от меня.
Кевин Круйссен,
2

Java (JDK) , 538 439 428 425 байт

Вполне возможно, самое длинное решение Code Golf, которое я когда-либо размещал. Все еще пытаюсь играть в гольф отсюда, но это борьба.

Удалось сбить 99 байтов, изменив формат ввода и использовав некоторый анализ регулярных выражений, и еще 11 из разных битов.

3 дополнительных байта благодаря Кевину!

Вдохновленный другими ответами, он принимает данные в виде года, месяца и карты дней в строку, представляющую время и событие в формате <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;}

Попробуйте онлайн!


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!
}
Люк Стивенс
источник
&&(i=1)<2?7:0можно ?7*(i=1):0сохранить 3 байта.
Кевин Круйссен,
@KevinCruijssen Хорошее спасибо!
Люк Стивенс
Предлагаю b>x|i++<6?0:7*(i=1)вместо b<=x&++i>6?7*(i=1):0и c*i>1|b<1|b>x?c<2&e!=null?e.split("-")[i-2]:" ":b+""вместоc*i<2&b>0&b<=x?b+"":c<2&e!=null?e.split("-")[i-2]:" "
потолок кошка
1

Красный , 674 651 байт

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

Попробуйте онлайн!

Более читабельно:

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
    ]
]
Гален Иванов
источник
if e = 0[e: 7]можно удалить, верно? Вы используете e: 1 + c/weekday % 7, поэтому eвсегда будете в диапазоне [1, 7].
Кевин Круйссен
@KevinCruijssen: Может быть, я что-то упускаю, но думаю, что мне это нужно. Индексация красного цвета основана на 1. Пожалуйста, взгляните на это: >> c: сейчас / время / дата == 12 октября 2018 года >> c: c + 1 == 13 октября 2018 года >> 1 + c / день недели% 7 == 0; >> 1 + 2 * 3 - 9 в красном, а не 7
Гален Иванов
1
РЕДАКТИРОВАТЬ: Ах НВМ .. Сначала 1 + происходит .. Хорошо, я вижу свою ошибку. Я привык %и имею /приоритет оператора над +.
Кевин Круйссен
1
@KevinCruijssen Да, именно так. В Red нет приоритета операторов, вместо этого нужно использовать ()
Гален Иванов