29
\$\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\$
5
  • 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\$ Dec 10, 2013 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, 2013 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\$ Dec 11, 2013 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, 2013 at 14:17
  • 5
    \$\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\$ Dec 11, 2013 at 14:38

37 Answers 37

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\$
3
  • \$\begingroup\$ One character shorter: f=DayName@{#,m,6}~Table~{m,12}~Count~Friday& \$\endgroup\$
    – Mr.Wizard
    Dec 11, 2013 at 10:25
  • \$\begingroup\$ @Mr.Wizard Yes. It surprises me that Mathematica can parse the multiple cases of infix notation. \$\endgroup\$
    – DavidC
    Dec 11, 2013 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, 2013 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\$
6
  • 5
    \$\begingroup\$ one-char saving if you count the numbers of fridays the 6th \$\endgroup\$ Dec 10, 2013 at 21:53
  • 2
    \$\begingroup\$ @JanDvorak clever! \$\endgroup\$
    – histocrat
    Dec 10, 2013 at 22:11
  • \$\begingroup\$ why 6? I didn't get it. \$\endgroup\$
    – NARKOZ
    Dec 11, 2013 at 8:28
  • 3
    \$\begingroup\$ If the 6th is a friday, then the 13th is also a friday \$\endgroup\$
    – TwiNight
    Dec 11, 2013 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, 2016 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\$
7
  • 1
    \$\begingroup\$ I like the idea of using the first day of the month. \$\endgroup\$
    – Cruncher
    Dec 10, 2013 at 21:54
  • \$\begingroup\$ Nice trick, there. The @ is unnecessary, though. \$\endgroup\$
    – Iszi
    Dec 10, 2013 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, 2013 at 0:08
  • \$\begingroup\$ Correction: Comes down to 52! \$\endgroup\$
    – Iszi
    Dec 11, 2013 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, 2013 at 1:27
7
\$\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

This is locale dependant, so if this 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\$
8
  • \$\begingroup\$ This seems to be locale-dependent. \$\endgroup\$ Dec 11, 2013 at 14:17
  • \$\begingroup\$ Yes, This is based on default C. But there is a bug... \$\endgroup\$ Dec 11, 2013 at 14:36
  • \$\begingroup\$ Save a char by unquoting your printf format string and escaping the \ instead: %s\\n \$\endgroup\$ Apr 8, 2014 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\$ Apr 8, 2014 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\$ Apr 9, 2014 at 16:36
6
\$\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\$
2
  • \$\begingroup\$ Since this is my first golfscript golf, I would appreciate any hints on further golfing it down. \$\endgroup\$
    – Wrzlprmft
    Apr 10, 2014 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, 2014 at 15:36
5
\$\begingroup\$

R 76 72 57

sum(format(as.Date(paste(scan(),1:12,1,sep="-")),"%w")<1)
\$\endgroup\$
7
  • \$\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, 2013 at 3:01
  • \$\begingroup\$ much appreciated! \$\endgroup\$
    – flodel
    Dec 11, 2013 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, 2013 at 8:36
  • \$\begingroup\$ wow I wouldn't have guessed that < would coerce a character to an integer. Nice trick! \$\endgroup\$
    – plannapus
    Dec 11, 2013 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, 2013 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\$
3
  • \$\begingroup\$ Really nice solution! \$\endgroup\$
    – leancz
    Dec 11, 2013 at 16:20
  • 2
    \$\begingroup\$ You can save a char by doing from datetime import* \$\endgroup\$
    – user80551
    Apr 8, 2014 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\$ Mar 1, 2017 at 15:03
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\$
6
  • \$\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, 2013 at 14:07
  • 1
    \$\begingroup\$ @Cruncher, it's a lookup table on the basis that the Gregorian calendar follows a 400-year cycle. \$\endgroup\$ Dec 11, 2013 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\$ Dec 11, 2013 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\$ Dec 11, 2013 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\$ Dec 11, 2013 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\$
1
  • \$\begingroup\$ I got my C entry down to 76 bytes - not quite so mathematical as this, but the necessary tables are well hidden until you read the explanation. \$\endgroup\$ Nov 19, 2023 at 19:58
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\$

JavaScript, 70

f=function(a){b=0;for(c=12;c--;)b+=!new Date(a,c,1).getDay();return b}
\$\endgroup\$
3
  • \$\begingroup\$ and why it is -1? \$\endgroup\$ Dec 11, 2013 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\$ Dec 11, 2013 at 17:11
  • \$\begingroup\$ @DomHastings : thx for your tips !!! \$\endgroup\$
    – guy777
    Dec 11, 2013 at 21:27
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\$
4
  • \$\begingroup\$ It's 29 characters, but these are more than 1 byte characters right? \$\endgroup\$
    – Cruncher
    Mar 1, 2017 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, 2017 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, 2017 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, 2017 at 12:31
3
\$\begingroup\$

k

64 characters

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

Reads from stdin

\$\endgroup\$
2
  • \$\begingroup\$ Ok, just what is going on here? :P \$\endgroup\$ Dec 11, 2013 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, 2013 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\$
1
  • \$\begingroup\$ Oh god, I never could read lisp.. \$\endgroup\$
    – Cruncher
    Dec 11, 2013 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\$
5
  • \$\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\$ Dec 11, 2013 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\$ Dec 11, 2013 at 12:55
  • \$\begingroup\$ You can save one character by comparing .DayOfWeek<1. \$\endgroup\$ Dec 11, 2013 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, 2013 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\$ Dec 12, 2013 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\$
7
  • \$\begingroup\$ Doesn't quite work for me - Friday lines are only printed once even if they contain more than one 13. \$\endgroup\$ Apr 8, 2014 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\$ Apr 8, 2014 at 22:17
  • 1
    \$\begingroup\$ @DigitalTrauma You're right. At some point this script was working. let me fix it. \$\endgroup\$ Apr 9, 2014 at 14:07
  • 1
    \$\begingroup\$ @DigitalTrauma looks like some much longer version was working. thanks for the fix! \$\endgroup\$ Apr 9, 2014 at 15:44
  • 1
    \$\begingroup\$ Make that 32: ncal $1|grep F|grep -o 13|wc -l \$\endgroup\$ Nov 19, 2023 at 17:07
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\$
1
  • \$\begingroup\$ It's a shame new java.util.GregorianCalendar has to be so long :( \$\endgroup\$
    – Cruncher
    Apr 10, 2014 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\$
2
  • \$\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, 2013 at 11:50
  • 1
    \$\begingroup\$ You didn't even bother golfing. Remove some of those spaces. \$\endgroup\$
    – mbomb007
    Apr 29, 2015 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\$
1
  • \$\begingroup\$ 48 bytes \$\endgroup\$
    – Jo King
    Feb 13, 2019 at 0:12
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\$
1
  • \$\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, 2017 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\$
1
  • \$\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, 2017 at 18:54
0
\$\begingroup\$

C, 76 bytes

A function that accepts a year and returns the count of Fridays the 13th for that year:

d(y){return(y+y/4-y/100+y/400)%7;}f(y){return(22>>d(y)&1|2)-(55>>d(y-1)&1);}

If a full program is really required, it comes in at 111 bytes, accepting a single argument and printing to standard out:

d(y){return(y+y/4-y/100+y/400)%7;}main(y,v)char**v;{y=atoi(*++v);putchar('0'+(22>>d(y)&1|2)-(55>>d(y-1)&1));}

Explanation

int day_index(int year)
{
    return (year + year/4 - year/100 + year/400) % 7;
}
int f(int year)
{
    const char a[] = "2332322";
    const char b[] = "1110110";
    return a[day_index(year)]
        -  b[day_index(year-1)];
}

We split the year into two parts (Jan-Feb and Mar-Dec) so that leap years don't disrupt the calculation.

The helper function computes the weekday of New Year's Eve (0 = Sunday). We first use this to determine (in a) how many Fridays the 13th are in March-December. Then we use the weekday index of the preceding year to get the count from b for January and February.

The golfed code uses a bit mask rather than a character array: a becomes 22 and b becomes 55.

Demo

The demo is on Try It Online.

#include <stdio.h>
int main(void)
{
    for (int year = 1990;  year < 2050;  ++year) {
        printf("%d: %d\n", year, f(year));
    }
}
1990: 2
1991: 2
1992: 2
1993: 1
1994: 1
1995: 2
1996: 2
1997: 1
1998: 3
1999: 1
2000: 1
2001: 2
2002: 2
2003: 1
2004: 2
2005: 1
2006: 2
2007: 2
2008: 1
2009: 3
2010: 1
2011: 1
2012: 3
2013: 2
2014: 1
2015: 3
2016: 1
2017: 2
2018: 2
2019: 2
2020: 2
2021: 1
2022: 1
2023: 2
2024: 2
2025: 1
2026: 3
2027: 1
2028: 1
2029: 2
2030: 2
2031: 1
2032: 2
2033: 1
2034: 2
2035: 2
2036: 1
2037: 3
2038: 1
2039: 1
2040: 3
2041: 2
2042: 1
2043: 3
2044: 1
2045: 2
2046: 2
2047: 2
2048: 2
2049: 1
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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