28
\$\begingroup\$

Your challenge is to write a program which, given a year, outputs the number of "Friday 13ths" in it.

Rules & Details:

  • You can take input via STDIN, or as an argument passed to your program.
  • You should output the result to STDOUT.
  • You may assume that input will be a valid year, and does not pre-date the Gregorian calendar (undefined behaviour is permitted in these cases).
  • Calendar/Date libraries are allowed.

This is a , so the shortest code (in bytes) wins.

(Related challenge link)

\$\endgroup\$
  • 7
    \$\begingroup\$ What is the required range of input? If it goes much before 1800, what assumptions should be made about switchover from Julian to Gregorian calendar? \$\endgroup\$ – Peter Taylor Dec 10 '13 at 22:47
  • \$\begingroup\$ @PeterTaylor I hadn't thought about it. If a date pre-dates gregorian then you can have undefined behaviour. \$\endgroup\$ – Cruncher Dec 11 '13 at 14:05
  • 1
    \$\begingroup\$ The first countries to adopt the Gregorian calendar did so in October 1582, following the bull of Gregory himself. Countries to adopt the new calendar late did not change until the 20th century, for example Greece introduced it on 1 March 1923. \$\endgroup\$ – Jeppe Stig Nielsen Dec 11 '13 at 14:16
  • \$\begingroup\$ @JeppeStigNielsen I don't know much about calendars and such. Whether they adopted them or not doesn't change what the gregorian dates are. Libraries should be able to calculate dates from quite a ways back I presume? \$\endgroup\$ – Cruncher Dec 11 '13 at 14:17
  • 3
    \$\begingroup\$ I am being offtopic here, I guess. Many libraries written by Anglo-American programmers use September 1752 as the "correct" time of change of calendars. This was when the British Empire changed. The new calendar was kept when U.S.A. was founded, of course. (As a curiosity, some SQL software has 1753 as the minimal year since they don't want to cope with the September 1752 issue.) However, using September 1752 is highly anglocentric. You are right Gregorian dates are the same whether they were used historically or not. That is the so-called proleptic Gregorian calendar. \$\endgroup\$ – Jeppe Stig Nielsen Dec 11 '13 at 14:38

32 Answers 32

3
\$\begingroup\$

APL (Dyalog APL) with cal from dfns, 29 bytes

+/{13∊⍎,⍉3↑¯5↑⍉2↓cal⍵}¨⎕,¨⍳12

Try it online!

⍳ 12 the integers one through twelve

⎕ ,¨ take numeric input and prepend to each of the twelve numbers

{ on each of the pairs, apply the function…

cal⍵ get a calendar for that year-month

2 ↓ drop two rows (caption and days)

 transpose (so we can address columns instead of rows)

¯5 ↑ take the last five (two digits for each of Friday and Saturday plus one space)

3 ↑ take the first two (two digits for Friday plus a space)

 transpose (so we get reading order)

, ravel

 execute as APL expression (gives list of Fridays' dates)

13 ∊ is thirteen a member of that list?

+/ sum the 12 Booleans


Using @Wrzlprmft's algorithm, we can do it without libraries for 53 bytes:

'21232211321211'⊃⍨14|2 3 ¯1+.×⊢,0≠.=400 100 4∘.|-∘0 1

-∘0 1 subtract zero and one

400 100 4 ∘.| division remainder table for the two years (across) divided by these numbers (down)

0 ≠.= inner "product" with 0, but using ≠ and = instead of +.×

⊢ , prepend the unmodified argument year

2 3 ¯1 +.× inner product with these numbers

14 | division remainder when divided by fourteen

'21232211321211' ⌷⍨ index into this string

\$\endgroup\$
  • \$\begingroup\$ It's 29 characters, but these are more than 1 byte characters right? \$\endgroup\$ – Cruncher Mar 1 '17 at 12:18
  • \$\begingroup\$ @Cruncher I've added an explanatory link in the heading. If you open the TIO link, you'll see that it says "29 chars, 29 bytes (SBCS)" on the right, i.e. Single Byte Character Set. \$\endgroup\$ – Adám Mar 1 '17 at 12:21
  • \$\begingroup\$ Well, I guess this is the new winner then, is standard practice in this SE to change the accepted answer this long after the question? \$\endgroup\$ – Cruncher Mar 1 '17 at 12:28
  • \$\begingroup\$ @Cruncher Yes. And there are even badges to be had for getting accepted long after OP. \$\endgroup\$ – Adám Mar 1 '17 at 12:31
12
\$\begingroup\$

Mathematica 49 46 45 44 42

As a pure function: 42 chars

DayName@{#,m,6}~Table~{m,12}~Count~Friday&

Example

DayName@{#,m,6}~Table~{m,12}~Count~Friday&[2013]

2


As a named function: 44 chars

f=DayName@{#,m,6}~Table~{m,12}~Count~Friday&

Examples

f[1776]
f[2012]
f[2013]
f[2014]

2
3
2
1

\$\endgroup\$
  • \$\begingroup\$ One character shorter: f=DayName@{#,m,6}~Table~{m,12}~Count~Friday& \$\endgroup\$ – Mr.Wizard Dec 11 '13 at 10:25
  • \$\begingroup\$ @Mr.Wizard Yes. It surprises me that Mathematica can parse the multiple cases of infix notation. \$\endgroup\$ – DavidC Dec 11 '13 at 13:26
  • \$\begingroup\$ David it surprises me that you haven't seen my (over)use of this conjoined notation. :^) (Examples: (1), (2)) \$\endgroup\$ – Mr.Wizard Dec 11 '13 at 18:42
8
\$\begingroup\$

Ruby, 49 48 47 46

f=->m{(1..12).count{|i|Time.gm(m,i,6).friday?}}

Edit: Shaved a character by going back a week, thanks to Jan, and another by switching from Time.new to Time.gm

Edit: At the expense of obfuscating it a bit more, I can get to 46 with

f=->m{(1..12).count{|i|Time.gm(m,i,8).wday<1}}
\$\endgroup\$
  • 5
    \$\begingroup\$ one-char saving if you count the numbers of fridays the 6th \$\endgroup\$ – John Dvorak Dec 10 '13 at 21:53
  • 2
    \$\begingroup\$ @JanDvorak clever! \$\endgroup\$ – histocrat Dec 10 '13 at 22:11
  • \$\begingroup\$ why 6? I didn't get it. \$\endgroup\$ – NARKOZ Dec 11 '13 at 8:28
  • 3
    \$\begingroup\$ If the 6th is a friday, then the 13th is also a friday \$\endgroup\$ – TwiNight Dec 11 '13 at 9:26
  • \$\begingroup\$ If the 8th is a Sunday, then the 1st is as well, and so you can use Time.gm(m,i).wday<1. Also, I don't know why you're naming the function. \$\endgroup\$ – Lee W Dec 14 '16 at 19:08
8
\$\begingroup\$

Powershell, 68 63 58 52 50

Thanks Iszi for the tip.

$n=$args;(1..12|?{!+(date $n-$_).DayOfWeek}).Count

Using the fact that if the 1st day in the month is Sunday, the 13th will be Friday.

I've also tried:

(1..12|?{!+(date $args-$_).DayOfWeek}).Count

but it's not the same $args inside the script block.

\$\endgroup\$
  • 1
    \$\begingroup\$ I like the idea of using the first day of the month. \$\endgroup\$ – Cruncher Dec 10 '13 at 21:54
  • \$\begingroup\$ Nice trick, there. The @ is unnecessary, though. \$\endgroup\$ – Iszi Dec 10 '13 at 23:51
  • \$\begingroup\$ Another thing, though I'm guilty of the same in a lot of my scripts. The challenge does specify that the input can come from an argument. Replace $n with $args in the loop, and you can do without $n=read-host; entirely. Saves 8. Remove @, as mentioned above, and you're down to 54. \$\endgroup\$ – Iszi Dec 11 '13 at 0:08
  • \$\begingroup\$ Correction: Comes down to 52! \$\endgroup\$ – Iszi Dec 11 '13 at 0:18
  • \$\begingroup\$ Trying to figure out why your second script won't work and I'm at a loss. What's interesting is that I can change out $args for $input, thus feeding the year in from the pipeline, and the script will run but it always outputs 3. \$\endgroup\$ – Iszi Dec 12 '13 at 1:27
5
\$\begingroup\$

R 76 72 57

sum(format(as.Date(paste(scan(),1:12,1,sep="-")),"%w")<1)
\$\endgroup\$
  • \$\begingroup\$ Can easily get this down 4 by replacing your "%a %d")=="Fri 13" with "%w%d)=="513") by using dow as a number, and removing the spaces. \$\endgroup\$ – chmullig Dec 11 '13 at 3:01
  • \$\begingroup\$ much appreciated! \$\endgroup\$ – flodel Dec 11 '13 at 3:11
  • \$\begingroup\$ +1 Though doing the seq only on the month is actually shorter here! sum(format(as.Date(paste(scan(),1:12,13,sep="-")),"%w%d")=="513")is only 65 characters! \$\endgroup\$ – plannapus Dec 11 '13 at 8:36
  • \$\begingroup\$ wow I wouldn't have guessed that < would coerce a character to an integer. Nice trick! \$\endgroup\$ – plannapus Dec 11 '13 at 13:35
  • \$\begingroup\$ @plannapus It's rather common. Since character codes are all numbers. Even java can compare int and char \$\endgroup\$ – Cruncher Dec 11 '13 at 14:38
5
\$\begingroup\$

Python2.7 90 86

from datetime import*
s=c=0
exec's+=1;c+=date(%d,s,9).weekday()<1;'%input()*12
print c

Monday the 9th may not have quite the same ring to it but works just as well.

Edit: A year and a half to notice that date is shorter than datetime :)

\$\endgroup\$
  • \$\begingroup\$ Really nice solution! \$\endgroup\$ – leancz Dec 11 '13 at 16:20
  • 2
    \$\begingroup\$ You can save a char by doing from datetime import* \$\endgroup\$ – user80551 Apr 8 '14 at 15:49
  • \$\begingroup\$ Nice! I ended up with something effectively identical, but avoiding exec: f=lambda y:sum([date(y,m,13).weekday()==4 for m in range(1,13)]).... Same size solution with the import (86 bytes), though. \$\endgroup\$ – iwaseatenbyagrue Mar 1 '17 at 15:03
5
\$\begingroup\$

Not using any libraries or built-in date functions:

Golfscript – 51

~..({4/.25/.4/--@}2*2*\3*+-
14%' [3/=RI[)a%:*.'\=5%

' [3/=RI[)a%:*.' could as well be 'feefefgeeffgfe'

Python – 82 79

Essentially the same algorithm.

l=lambda y:y/4-y/100+y/400
i=input()
print"21232211321211"[(2*i+3*l(i)-l(i-1))%14]

Using this trick, this can be golfed down further to:

l=lambda y:y/4-y/100+y/400
i=input()
print 94067430>>(4*i+6*l(i)-2*l(i-1))%28&3

This exploits the fact that, calender-wise, there are only 14 different years, which are distinguishable by their last day and whether they are leaping. l calculates the number of leap years up to its argument (if the Gregorian calendar extended backwards to the year 1). (2*i+3*l(i)-l(i-1))%14 is short for l(i)-l(i-1)+(i+l(i))%7*2, where l(i)-l(i-1) tells us whether the argument is a leap year and i+l(i) sums up the shifts of the last day (one in a normal year, two in a leap year).

\$\endgroup\$
  • \$\begingroup\$ Since this is my first golfscript golf, I would appreciate any hints on further golfing it down. \$\endgroup\$ – Wrzlprmft Apr 10 '14 at 14:27
  • \$\begingroup\$ I was thinking about such a solution utilizing the fact that there's only 14 unique years actually, but wasn't sure the best language to make it competetive. I think this is the shortest answer with no libraries. If leap years were uniformly every 4 years, you might be able to win with this \$\endgroup\$ – Cruncher Apr 10 '14 at 15:36
4
\$\begingroup\$

C 301+ 287

main(int x,char**v){char p[400],*a[]={"abbababbacaacbac","bacabbb","baabbaca","abbb","aabbacaac","abbbbcaac","abbbbaabb"},*b="adcadcadcaebcadcadcafbcadcadcagbcadcadcadc";int c=0,i,y=atoi(v[0]);for(i=0;i<42;i++)strcpy(&p[c],a[b[i]-'a']),c+=strlen(a[b[i]-'a']);printf("%d",p[y%400]-'`');}

Not the shortest answer, but uses no libraries.

\$\endgroup\$
  • \$\begingroup\$ Hey, would you be willing to provide the ungolfed answer with an explanation? I'm interested in what exactly you did \$\endgroup\$ – Cruncher Dec 11 '13 at 14:07
  • 1
    \$\begingroup\$ @Cruncher, it's a lookup table on the basis that the Gregorian calendar follows a 400-year cycle. \$\endgroup\$ – Peter Taylor Dec 11 '13 at 14:20
  • 1
    \$\begingroup\$ More explicitly (and longer), C#: static char GetNumberOfFriday13s(int year) { const string perpetualCalendar = "1221212213113213122221122131122121221311321312222112213112212122131132131222211221311221212213113213112213113213122221122131122121221311321312222112213112212122131132131222211221311221212213113213122213113213122221122131122121221311321312222112213112212122131132131222211221311221212213113213122221122213122221122131122121221311321312222112213112212122131132131222211221311221212213113213122221122131"; return perpetualCalendar[year % 400];. Won't work for negative years. \$\endgroup\$ – Jeppe Stig Nielsen Dec 11 '13 at 15:01
  • \$\begingroup\$ Cute! As a small bug, v[0] should be v[1]. You can also golf this a bit; consider using strcat, storing characters to directly print in a[], and subtracting numeric constants instead of character constants. :) \$\endgroup\$ – user1354557 Dec 11 '13 at 17:23
  • 1
    \$\begingroup\$ I improved the compression as well: main(int x,char**v){char p[400],*a[]={"1221212213113213","2131222","21122131","1222","112213113","122223113","122221122"},*b="adcadcadcaebcadcadcafbcadcadcagbcadcadcadc";*p=0;for(;*b;b++)strcat(p,a[*b-97]);putchar(p[atoi(v[1])%400]);} (215 chars) \$\endgroup\$ – user1354557 Dec 11 '13 at 18:55
4
\$\begingroup\$

C (151 145 137 131 130 chars)

I am surprised to see that there is only one other solution that doesn't use built-in calendar tools. Here is a (highly obfuscated) mathematical approach, also in C:

f(x){return(x+(x+3)/4-(x+99)/100+!!x)%7;}main(int x,char**v){int y=atoi(v[1])%400,a=f(y+1);putchar('1'+((f(y)&3)==1)+(a>2&&a-5));}

(The above compiles in GCC with no errors)

Alternative solution: C (287->215 chars)

I rather enjoyed Williham Totland's solution and his use of compression. I fixed two small bugs and tweaked the code to shorten its length:

main(int x,char**v){char p[400],*a[]={"1221212213113","213122221122131","12213113","22213113","22221122","2131"},*b="abababafcbababafdbababafebababab";*p=0;for(;*b;b++)strcat(p,a[*b-97]);putchar(p[atoi(v[1])%400]);}
\$\endgroup\$
4
\$\begingroup\$

PHP, 82

<?for($i=1,$c=0;$i<13;$i++)$c+=(date("N",mktime(0,0,0,$i,1,$argv[1]))==7);echo $c;

Based on

"Any month that starts on a Sunday contains a Friday the 13th, and there is at least one Friday the 13th in every calendar year."

From http://en.wikipedia.org/wiki/Friday_the_13th

\$\endgroup\$
4
\$\begingroup\$

bash 47 36

seq -f$1-%g-6 12|date -f-|grep -c ^F

Thanks @DigitalTrauma for saving 10 chars by using seq with default start to 1.

date -f<(printf "%s\n" $1-{1..12}-6)|grep -c ^F

(Previous version using echo present a bug because of the empty line when <(echo $1-{1..12}-6$'\n'). So this function worked fine until today is a Friday.

Lets see:

set -- 2013
seq -f$1-%g-6 1 12|date -f-|grep -c ^F
2

date -f<(printf "%s\n" $1-{1..12}-13)|grep -c ^F
2

Is is locale dependant, si if it don't work, you may have to

export LANG=C

or

LANG=C date -f<(printf "%s\n" $1-{1..12}-13)|grep -c ^F

Into a function; +7 -> 43

f(){ seq -f$1-%g-6 12|date -f-|grep -c ^F;}

f 2013
2

for i in {2010..2017};do echo $i $(f $i) ;done
2010 1
2011 1
2012 3
2013 2
2014 1
2015 3
2016 1
2017 2

Bonus: +78 -> 121

From there, if my function become:

f(){ o=();for t in $(seq -f$1-%g-6 12|date -f- +%a,%b);do [ "${t:0:1}" = "F" ]&&o+=(${t#*,});done;echo ${#o[@]} ${o[@]};}

or

f(){ o=();
     for t in $(seq -f$1-%g-6 1 12|date -f- +%a,%b);do
         [ "${t:0:1}" = "F" ]&&o+=(${t#*,})
       done
     echo ${#o[@]} ${o[@]}
}

for i in {2010..2017};do echo $i $(f $i) ;done
2010 1 Aug
2011 1 May
2012 3 Jan Apr Jul
2013 2 Sep Dec
2014 1 Jun
2015 3 Feb Mar Nov
2016 1 May
2017 2 Jan Oct
\$\endgroup\$
  • \$\begingroup\$ This seems to be locale-dependent. \$\endgroup\$ – Peter Taylor Dec 11 '13 at 14:17
  • \$\begingroup\$ Yes, This is based on default C. But there is a bug... \$\endgroup\$ – F. Hauri Dec 11 '13 at 14:36
  • \$\begingroup\$ Save a char by unquoting your printf format string and escaping the \ instead: %s\\n \$\endgroup\$ – Digital Trauma Apr 8 '14 at 21:37
  • 1
    \$\begingroup\$ Or use seq to drop 8 chars: date -f<(seq -f$1-%g-6 1 12)|grep -c ^F \$\endgroup\$ – Digital Trauma Apr 8 '14 at 21:40
  • 1
    \$\begingroup\$ Actually, you can shave off 2 more chars. If you omit the starting sequence number, seq will start at 1 by default, which it what you want: seq -f$1-%g-6 12|date -f-|grep -c ^F \$\endgroup\$ – Digital Trauma Apr 9 '14 at 16:36
4
\$\begingroup\$

JavaScript, 70

f=function(a){b=0;for(c=12;c--;)b+=!new Date(a,c,1).getDay();return b}
\$\endgroup\$
  • \$\begingroup\$ and why it is -1? \$\endgroup\$ – Lukasz 'Severiaan' Grela Dec 11 '13 at 11:03
  • 1
    \$\begingroup\$ Looks good! You can save a few more bytes, by removing ,b,c from the function declaration (it's ok to leak vars for golf!), also as b is cast as a Number you can += the result of test instead of the &&b++: b+=/^F/.test(new Date(a,c,6)). However, you can save another byte, using !new Date(a,c,1).getDay() (this works because getDay returns 0 for Sunday, and if the 13th is a Friday, the 1st will be a Sunday) instead of the test which altogether should save you 7 bytes! \$\endgroup\$ – Dom Hastings Dec 11 '13 at 17:11
  • \$\begingroup\$ @DomHastings : thx for your tips !!! \$\endgroup\$ – guy777 Dec 11 '13 at 21:27
3
\$\begingroup\$

k

64 characters

{+/6={x-7*x div 7}(.:')x,/:(".",'"0"^-2$'$:1+!:12),\:".13"}[0:0]

Reads from stdin

\$\endgroup\$
  • \$\begingroup\$ Ok, just what is going on here? :P \$\endgroup\$ – Williham Totland Dec 11 '13 at 11:31
  • \$\begingroup\$ Read in year, build list of dates of 13th day of every month, test day of week = friday, sum resulting list of booleans \$\endgroup\$ – skeevey Dec 11 '13 at 13:25
3
\$\begingroup\$

Common Lisp (CLISP), 149

(print 
    (loop for i from 1 to 12 count 
        (= 4 (nth-value 6 
            (decode-universal-time
                (encode-universal-time 0 0 0 13 i
                    (parse-integer (car *args*)) 0))))))
\$\endgroup\$
  • \$\begingroup\$ Oh god, I never could read lisp.. \$\endgroup\$ – Cruncher Dec 11 '13 at 14:09
2
\$\begingroup\$

C# 110 101 93 92

int f(int y){int c=0;for(int i=1;i<13;i++)c+=new DateTime(y,i,8).DayOfWeek>0?0:1;return c;}

C# Linq 88

int g(int y){return Enumerable.Range(1,12).Count(m=>new DateTime(y,m,8).DayOfWeek==0);}

Thanks to Jeppe Stig Nielsen for linq and suggestion of checking for sunday on the 8th.

Thanks to Danko Durbić for suggesting > instead of ==.

\$\endgroup\$
  • \$\begingroup\$ Instead of c+=(int)new DateTime(y,i,13).DayOfWeek==5?1:0;, use the equivalent c+=new DateTime(y,i,8).DayOfWeek==0?1:0;. The trick is to subtract 5, because then you can get rid of the cast to int, and also the number 8 has one digit less than the number 13. Sunday the Eighth! \$\endgroup\$ – Jeppe Stig Nielsen Dec 11 '13 at 12:48
  • \$\begingroup\$ With Linq: int g(int y){return Enumerable.Range(1,12).Count(m=>new DateTime(y,m,8).DayOfWeek==0);}. Of course as a lambda this is y=>Enumerable.Range(1,12).Count(m=>new DateTime(y,m,8).DayOfWeek==0). \$\endgroup\$ – Jeppe Stig Nielsen Dec 11 '13 at 12:55
  • \$\begingroup\$ You can save one character by comparing .DayOfWeek<1. \$\endgroup\$ – Danko Durbić Dec 11 '13 at 22:04
  • \$\begingroup\$ @DankoDurbić This can apply to the c# answer but not sure how to apply it to linq . \$\endgroup\$ – Kami Dec 12 '13 at 11:02
  • \$\begingroup\$ My mistake; apparently you can't compare DayOfWeek with any other integer than 0 - error CS0019: Operator '<' cannot be applied to operands of type 'System.DayOfWeek' and 'int'. \$\endgroup\$ – Danko Durbić Dec 12 '13 at 12:38
2
\$\begingroup\$

PHP, 55 bytes

for(;++$i<13;)$c+=!date(w,strtotime($argn.-$i));echo$c;

Run with echo <year> | php -nR '<code>'.

Basically the same that Oleg tried and Damir Kasipovic did, just with better golfing:
Every month that starts with a sunday, has a Friday the 13th.
So I loop through the months and count the first days that are sundays.

breakdown

for(;++$i<13;)          // loop $i from 1 to 12
    $c+=!                   // 4. if result is not truthy (weekday==0), increment $c
        date(w,             // 3. get weekday (0 stands for Sunday)
            strtotime(      // 2. convert to timestamp (midnight 1st day of the month)
                $argn.-$i   // 1. concatenate year, "-" and month
            )
        )
    ;
echo$c;                 // output
\$\endgroup\$
1
\$\begingroup\$

K, 42

{+/1={x-7*x div 7}"D"$"."/:'$+(x;1+!12;1)}

.

k){+/1={x-7*x div 7}"D"$"."/:'$+(x;1+!12;1)}'1776 2012 2013 2014
2 3 2 1
\$\endgroup\$
1
\$\begingroup\$

Bash (52 47 characters)

for m in {1..12};do cal $m $Y;done|grep -c ^15
\$\endgroup\$
1
\$\begingroup\$

Rebol, 63

f: 0 repeat m 12[d: do ajoin["6-"m"-"y]if d/weekday = 5[++ f]]f

Usage example in Rebol console:

>> y: 2012
== 2012

>> f: 0 repeat m 12[d: do ajoin["6-"m"-"y]if d/weekday = 5[++ f]]f
== 3

Alternative solution which collects all the Friday 13th in given year is:

>> collect[repeat m 12[d: do ajoin["13-"m"-"y]if d/weekday = 5[keep d]]]
== [13-Jan-2012 13-Apr-2012 13-Jul-2012]
\$\endgroup\$
1
\$\begingroup\$

Bash and Sed, 39

ncal $1|sed '/F/s/13/\
/g'|grep -c ^\ 2

ncal prints a calendar for the given year with days of the week down the left.

sed with a /g flag subs out all 13s with newlines

grep -c counts the lines that start with " 2" (20 always follows 13)

Thanks to @DigitalTrauma for finding a bug in my old version and proposing a solution!

\$\endgroup\$
  • \$\begingroup\$ Doesn't quite work for me - Friday lines are only printed once even if they contain more than one 13. \$\endgroup\$ – Digital Trauma Apr 8 '14 at 22:16
  • 1
    \$\begingroup\$ I think the best I can do with something like this is 38: ncal $1|sed /F/s/13/\\n/g|grep -c ^\ 2 \$\endgroup\$ – Digital Trauma Apr 8 '14 at 22:17
  • 1
    \$\begingroup\$ @DigitalTrauma You're right. At some point this script was working. let me fix it. \$\endgroup\$ – Not that Charles Apr 9 '14 at 14:07
  • 1
    \$\begingroup\$ @DigitalTrauma looks like some much longer version was working. thanks for the fix! \$\endgroup\$ – Not that Charles Apr 9 '14 at 15:44
  • \$\begingroup\$ Interesting, the unquoted sed expression I proposed works with GNU sed (Linux) but not with BSD sed (OSX). I guess you can gain 1 char at the cost of portability if you choose the GNU version. \$\endgroup\$ – Digital Trauma Apr 9 '14 at 16:26
1
\$\begingroup\$

Scala, 76 68 characters

In 78 chars:

def f(y:Int)=0 to 11 count(new java.util.GregorianCalendar(y,_,6).get(7)==6)

Nothing out of ordinary, except for using magical numbers for DAY_OF_WEEK = 7 and FRIDAY = 6.

68 character version:

def f(y:Int)=0 to 11 count(new java.util.Date(y-1900,_,6).getDay==5)

Yes, Java changed values of day of the week constants between API's.

\$\endgroup\$
  • \$\begingroup\$ It's a shame new java.util.GregorianCalendar has to be so long :( \$\endgroup\$ – Cruncher Apr 10 '14 at 15:32
1
\$\begingroup\$

Python 195 / 204

Works only for previous years, because monthdatescalendar returns a calendar for the given year until now. I think there is a lot of optimizing potential left :).

import calendar, sys
c=calendar.Calendar()
f=0
for m in range(1,12):
 for w in c.monthdatescalendar(int(sys.argv[1]),m):
  for d in w:
   if d.weekday() == 4 and d.day == 13:
    f=f+1
print(f)

Another solution, works for every date but it isn't smaller:

import datetime,sys
y=int(sys.argv[1])
n=datetime.date
f=n(y,1,1)
l=n(y,12,31)
i=0
for o in range(f.toordinal(), l.toordinal()):
 d=f.fromordinal(o)
 if d.day == 13 and d.weekday() == 4:
  i=i+1
print(i)
\$\endgroup\$
  • \$\begingroup\$ In your first example the range should be (1,13) otherwise you'd miss December Friday 13ths, like in 2013. \$\endgroup\$ – leancz Dec 11 '13 at 11:50
  • 1
    \$\begingroup\$ You didn't even bother golfing. Remove some of those spaces. \$\endgroup\$ – mbomb007 Apr 29 '15 at 15:20
1
\$\begingroup\$

Perl 6, 55 53

{sum (1..12).map: {Date.new($_,$^a,1).day-of-week>6}}

Old answer:

{sum (1..12).map: {Date.new($_,$^a,13).day-of-week==5}}
\$\endgroup\$
0
\$\begingroup\$

Python(v2) 120

import datetime as d,sys
s=0
for m in range(1, 13):
    if d.datetime(int(sys.argv[1]),m,6).weekday()==4: s=s+1
print s
\$\endgroup\$
0
\$\begingroup\$

Perl + lib POSIX 55

With the idea of not looking for 13th but first, and as sunday is 0 this let save 3 chars! Thanks @ Iszi and Danko Durbić!

$==$_;$_=grep{!strftime"%w",0,0,0,1,$_,$=-1900}(0..11)

Could compute 2010 to 2017 (for sample) in this way:

perl -MPOSIX -pE '$==$_;$_=grep{!strftime"%w",0,0,0,1,$_,$=-1900}(0..11)' <(
    printf "%s\n" {2010..2017})
11321312

(Ok, there is no newline, but that was not asked;)

Old post: 63

$==grep{5==strftime"%w",0,0,0,13,$_,$ARGV[0]-1900}(0..11);say$=

In action:

for i in {2010..2017};do
    echo $i $(
        perl -MPOSIX -E '
            $==grep{5==strftime"%w",0,0,0,13,$_,$ARGV[0]-1900}(0..11);say$=
            ' $i );
  done
2010 1
2011 1
2012 3
2013 2
2014 1
2015 3
2016 1
2017 2
\$\endgroup\$
0
\$\begingroup\$

In Smalltalk (Squeak/Pharo flavour), implement this method in Integer (86 chars)

countFriday13^(1to:12)count:[:m|(Date year:self month:m day:13)dayOfWeekName='Friday']

Then use it like this: 2014countFriday13.

Of course, we could use a shorter name, but then it would not be Smalltalk

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

C++ - Too many bytes :(

I tried a solution which doesn't make use of any date libraries.

I found a pretty cool (if I may say so myself) solution. Unfortunately I can't get it shorter than this, which really bugs me because it feels like there should be a better way.

The solution hinges on this algorithm which is only 44 bytes in itself. Unfortunately I need another 100 bytes to wrap it nicely...

#include<stdlib.h>
main(int,char**v){int f=0,d,m,y;for(m=1;m<13;++m)d=13,y=atoi(v[1]),(d+=m<3?y--:y-2,23*m/9+d+4+y/4-y/100+y/400)%7-5||++f;return f;}

Output through the return code (in C++, using cout or printf or anything like that requires another #include, which would blow up the solution even more).

Driver / test program:

# Make sure we're not running an old version
rm doomsday.exe

gcc doomsday.cpp -o doomsday.exe

./doomsday.exe 1776
echo 1766: $?

./doomsday.exe 2012
echo 2013: $?

./doomsday.exe 2013
echo 2013: $?

./doomsday.exe 2014
echo 2014: $?

echo `wc doomsday.cpp -c` characters

Output of the driver program:

$ ./test_doomsday 
1766: 2
2013: 3
2013: 2
2014: 1
150 doomsday.cpp characters
\$\endgroup\$
  • \$\begingroup\$ I count 88 for the algorithm, not 44. What is the algorithm and what is the warpping? ($m<3?$y--:$y-2)+3 instead of d=13,, d+=m<3?y--:y-2, and d+4 should work as well and saves a lot. +5 instead of +3 and -5 should work too and saves 2 bytes. for(m=0;++m<13;) saves one byte. Moving m=0 to the function head saves another byte; and moving ()%7||++f to the loop head saves another one. Down from 149 to 136 bytes. \$\endgroup\$ – Titus Mar 1 '17 at 18:46
0
\$\begingroup\$

Clojure, 207 187 bytes

-20 bytes by getting rid of the import, and some whitespace I missed.

(import '[java.time LocalDate DayOfWeek])#(loop[d(LocalDate/parse(str %"-01-01"))c 0](if(=(.getYear d)%)(recur(.plusDays d 1)(if(and(=(.getDayOfMonth d)13)(= (.getDayOfWeek d) DayOfWeek/FRIDAY))(inc c)c))c))

Starting on Janauary 1st of the given year, it loops over each day. If the day is Friday the 13th, it increments the count. It continues to loop until it reaches the next year.

(import '[java.time LocalDate DayOfWeek])

(defn count-friday-13ths [year]
  (loop [d (LocalDate/parse (str year "-01-01")) ; Starting date
         c 0] ; The count
    (if (= (.getYear d) year) ; If we haven't moved onto the next year...
      (recur (.plusDays d 1) ; Add a day...
             (if (and (= (.getDayOfMonth d) 13) ; And increment the count if it's Friday the 13th
                      (= (.getDayOfWeek d) DayOfWeek/FRIDAY))
               (inc c) c))
      c))) ; Return the count once we've started the next year.
\$\endgroup\$
0
\$\begingroup\$

PHP, no builtins, 81 bytes

echo 0x5da5aa76d7699a>>(($y=$argn%400)+($y>102?$y>198?$y>299?48:32:16:0))%28*2&3;

Run with echo <year> | php -nR '<code>'.

breakdown

Weekdays repeat every 400 years.
In the results for 1600 to 1999 (for example), there is a 28-length period with just three gaps:

  0:2212122131132131222211221311
 28:2212122131132131222211221311
 56:2212122131132131222211221311
 84:2212122131132131122
103:       131132131222211221311
124:2212122131132131222211221311
152:2212122131132131222211221311
180:2212122131132131222
199:       131132131222211221311
220:2212122131132131222211221311
248:2212122131132131222211221311
276:221212213113213122221122
300:            2131222211221311
316:2212122131132131222211221311
344:2212122131132131222211221311
372:2212122131132131222211221311

After adjusting the year for these gaps, we can get the result with a simple hash:

$y=$argn%400;foreach([300,199,103]as$k)$y<$k?:$y+=16;
echo"2212122131132131222211221311"[$y%28];

Not short (95 bytes) but pretty. And we can golf

  • 4 bytes by using a ternary chain for the offset,
  • 8 bytes by converting the hash map from a base4 string to integer,
  • one more by using the hex representation,
  • and one by merging the expressions.
\$\endgroup\$
  • \$\begingroup\$ A port of CompuChip´s C++ answer can be golfed down to 84 bytes: for(;++$m<13;23*$m/9+($m<3?$y--:$y-2)+5+$y/4-$y/100+$y/400)%7?:$f++)$y=$argn;echo$f; \$\endgroup\$ – Titus Mar 1 '17 at 18:54
0
\$\begingroup\$

Japt -x, 10 bytes

CÆ5¥ÐUXD e

Try it

CÆ5¥ÐUXD e     :Implicit input of integer U
C              :12
 Æ             :  Map each X in the range [0,C) (months are 0-indexed in JavaScript)
  5¥           :  Check if 5 is equal to
    ÐUXD       :  new Date(U,X,13)
         e     :  0-based index of day of the week
               :Implicitly reduce by addition and output
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.