Advent of Code 2022

Nothing gets me in the Christmas spirit quite like looking up how the Fortran character intrinsics work!

Not sure I will continue, but I was curious and thinking it might be interesting to see how many
could benefit from fpm(1) packages although I did not use any yet. My twist on the first day was I allowed for an arbitrary number of values on a line (which complicates it unnecessarily!)

So far the most interesting thing is looking at others’ solutions. Surprising similarities. Interesting
differences.

Mine generate rather verbose output.

Since I found it interesting to see various approaches, here are my first two. A little too busy to
probably continue right now; but these appear to stay available so I hope to play later, but I
suppose that really is not in the spirit of the game …

GitHub - urbanjost/A2022: Advent of Code 2022 (maybe)

Expect them to not be optimal or elegant (its a game, after all) but other players might enjoy the differences. I know I have so far.

For those, who are looking for real simplicity for the 2nd day: Note, that the problem is one dimensional, so you do not necessarily need any two dimensional arrays or similar. As it maps to a cyclic ordered group with 3 elements, findloc() combined with modulo() allows for a very short solution, as in AoC-2022/02 at main ¡ aradi/AoC-2022 ¡ GitHub (And if you know the difference between the signed and non-signed version of the latter, you can spare the 15 minutes debugging I had fun with :wink:).

Very nice idea to use labels!

I wondered how far we can push this. And the answer is YES!

open(1,file='input.txt')
m=0
1 n=0
2 read(1,'(i9)',end=3)i
n=n+i
m=max(m,n)
if(i)3,1,2
3 print*,m
end

IMHO the number of lines doesn’t matter because you can simply replace most newlines with ;.
The number of characters is 102. And it could be 8 characters less, if the input file name would be just one character.
Any other “improvements”?

Some tricks I discovered during my journey:

  • allocate(a(1)) → a=[0]
  • integer, allocatable :: eat(:) → allocatable::ieat
  • eat(size(eat)) and eat = [eat,meal] → eat(1) and eat = [meal,eat]
2 Likes

Are you allowed to remove the open statement and use read(*,'(i9)')?

BTW, this is starting to look like the kind of fortran I first learned: Implicit declarations only, one-character variable names, arithmetic if (because that compiled to a single instruction).

Used IALL and TRAILZ in Day 3 - fun!

I also thought about this, but I came to the conclusion, that this would just outsource parts of the logic. Therefore, I decided not to change that.

This actually gave me an idea.
At the end of my Fortran course, I will challenge my students to write code with as few characters as possible. I think this is a fun way to introduce some FORTRAN.

1 Like

You are, if the input comes from stdin. OPEN didn’t exist until F77.

we planned on using the language we worked on this year (which is basically an esolang) for the first week, i’m not too sure for the rest, will probably use fortran for at least one or two days though

https://github.com/SRAZKVT/AOC-2022 here are our solutions if anyone is interested

So far, the tasks where perfect advertisements for Fortran, perfectly suited to be solved with simple array operations and elegant vector subscripts (day 3). My Python solutions were hardly longer as the Fortran ones so far.

However, on day 4 I am unsatisfied with the input parsing part of my Fortran solution (as compared to 2 lines in Python with a simple regexp match). Could anybody come up with a more compact (but still readable/teachable :wink: ) solution?

It’s a pitty bset does not accept an array of integers for the pos argument. One could then spare at least one do loop and make the bit-based solution more compact.

For day 4, I read the line into a string, removed dashes and then used list-directed internal file input to read the values. Perhaps not elegant, but simple.

1 Like

You’re referring to day 3, yes? I think you’d need a loop regardless. (Assume you meant ibset.)

Yes, I meant day 3 and ibset, indeed. I’ve managed to solve day 3 with one single loop only, which runs over the lines of the file. Everything else can be handled by using vector subscripts on an array of logicals. I guess, one could do the same compactness with the bit representation, if ibset accepted a list of integers for the pos argument.

Oh, that’s a neat solution, I really like it!

Getting the data from the file into some useable form is always the most tedious part when using Fortran. Fortran is missing so much stuff that makes this trivial in other languages. My solution is to write little helper functions, which I have used for the last couple years. For example: read a line into an allocatable string (without having to guess the max length in the file), a string class so I can have an array of strings (without having to guess the max string length), a split function that uses these things (again, so we don’t have to guess max lengths), etc.

Using the stdlib might also be a good option. Is anyone using that for this?

Me too. This is unexpectedly addicting. Playing with tricks (please do not assume my solutions are reasonable!) I came to question if it is standard to
write back into a character variable that is in the argument list of a WRITE.
Have to look that one up (probably not, just never occurred to me before. Got into a side contest on minimum number of lines of code and used a single write to remove all but the digits, which is definitely not optimal). Will look it up,
do not want to ruin the fun with technical questions. Really surprised there appear to be > 130 000, players. Nicely navigable interface too. Kudos to the author. Addictive.

Nope - not allowed. “During the execution of an output statement that specifies an internal file, no part of that internal file shall be referenced, defined, or become undefined as the result of evaluating any output list item.” 12.6.4.5.1p7

1 Like

Thanks! That saves some time. Did not see it in quick look, but did get ifort(1) to sometimes
warn me of exactly that. gfortran(1) appears to work, could not get any warning from it so far;
but it is undefined behavior afterall.

Was thinking if it ended up being a bug report I was going to get at least one question on why I was doing that in the first place :wink:

minimal example
program bug1 !filter all but digits out of stdin up to 256-character lines
! minimal reproducer
! legal to write back into LINE which appears in argument list?
implicit none
character(len=256)   :: line,line2
integer :: iostat
do
   read(lun,'(a)',iostat=iostat)line
   if(iostat.ne.0)exit
   write(line,'(*(a))')(merge(line(i:i),' ',index('0123456789  ',line(i:i)).ne.0),i=1,len_trim(line)) ! ifort: no warning, returns blank lines
   !write(line,'(*(a))')(merge(line(i:i),' ',index('0123456789  ',line(i:i)).ne.0),i=1,len(line))      ! ifort: warning
   !forrtl: warning (768): Internal file write-to-self; undefined results
   if(line.ne.'')write(*,'(a)')trim(line)
enddo
end program bug1

the little code was what I was going to use if it ended up needing reported. When I was thinking of trying it I was thinking if WRITE was like CALL(LINE,LINE) it was going to be trouble, but if like LINE=FUNC(LINE) it would be OK; but that would probably be up to the compiler and so not a good idea; nice to know the standard actually considers that.

I think that is OK myself, and playing might produce some reusable routines. I think using fpm/stdlib is no different than the external packages (at least, not part of the core language) I see other languages using.

I have my own BRE and SPLIT, SORT, etc. that I plan on using via fpm(1) if required with no qualms myself.

1 Like