Advent of Code 2023

I do not see the purpose of implementing brute force for part 2. A nearly identical algorithm for part 1 will solve day 5 part 2, just extremely inefficiently.

That said, I am thinking about the problem and I don’t see what the “faster way” might look like… I was thinking work backwards through the maps, but even if you do that, there’s the leftover bin of being “unmapped” and the previous number falling through.

Brute force on a single core 5800x did it in 3 minutes, which I don’t love…

spoiler

I see you can spawn ranges, but I guess the self imposed limitation of never generating a temporary array makes that more difficult as well. I believe in this case tiem spend allocating memory for temporaries would not matter compared to how much faster you can check billions of numbers, but alas.

Despite many attempts, I can’t seem to figure out where I’m going wrong in part two of day 1. I’ve managed to obtain the calibration for each day and then add up the results, but I can’t see why the solution isn’t correct.

LINE											  CAL  SUM

two65eightbkgqcsn91qxkfvg                         21    21
neightwompstbkqv1fourfthdcfgtrkqzgrbfrczxbdn      84   105
43qsrrlxxq                                        43   148
898dbpjmdqjgtrvdvlxxdnvlfhncdzrt                  88   236
jninedsrvftdlcg4hhztwofourskrjhcjvthree           93   329
five562                                           52   381
bpnjmtmeightninesix2391                           81   462
rftqshh47n                                        47   509
ctpkqsdqz97zqptzjlfbtwo                           92   601
sjtwonesix6cqbv4                                  24   625
9zclhrrssvzpcfpqlshfsxs                           99   724
ninemcctrb5glhmctwol7                             97   821
eight5fourtwotwo                                  82   903
18frdsvjxdpxf8dxsevenm                            17   920
five55foureight                                   58   978
dbqeightwo4sxzsix                                 86  1064
ftjjqbgphtmhthreesix1six                          36  1100
8sczkklgr5ncxkhkq                                 85  1185
126dzbvg6two4oneightntd                           11  1196
fiveqcplndmmcsixksmmpdqgttwosixnine7eight         58  1254
eightseven5threesevennine                         89  1343
nmxmcvrzbcppktgbznz2                              22  1365
five83                                            53  1418
57rqxmvf12                                        52  1470
4nklcvfsix1jvsvxh8nine                            49  1519
tpjppv6seven4sixsevenvnhcxonefjztthdcv            61  1580
dfkcrcfxkmxccpf7sixkzlgf                          76  1656
ninetwo6hfg                                       96  1752
.
.
.
43eightnvdrthree1eightoneggrdmnp                  41 57198
pffldcmnlpsevensixqxhdncrclbc51five               75 57273
5bqnlphone6                                       56 57329
195one                                            11 57340

-------------------
TOTAL       57340

What am I doing bad?

How do you deal with overlapping numbers? E.g. nineight?

First I create an array with the first position of each string (‘one’, ‘two’, …) then I replace in order:

program aoc_01b
    use mod_utils
    implicit none

    integer:: f, ios, i, j, k
    character:: c, calibration(2)
    integer:: total
    logical:: b_exit

    integer:: pos(10)   ! Posición de cada palabra letra en la línea de entrada
    character(len=5), dimension(9):: num = ["one","two", "three", "four", &
                                             "five", "six", "seven", "eight", "nine"]
    character(len=80):: line, line_original

    total = 0

    calibration = "__"
    open(newunit=f, file="in/01.txt", iostat=ios, access="sequential")
    read(f, "(a)", iostat=ios) line_original
    line = trim(line_original)

    linea: do
        replace: do
            b_exit = .true.
            ! Find the first position of each number in the input line
            busca: do i=1, size(num) 
                pos(i) = str_find(trim(num(i)), line)
                if (pos(i) .ne. -1) b_exit = .false.
            end do busca

            if (b_exit) exit replace

            ! Discover what number is in the first position.
            ! j: the first position
            ! k: the entry in the array num(k)
            ! Example: The first position corresponds to num(k) that is in position j."
            j = len(line)
            first_pos: do i=1, size(num)
                if (pos(i) .le. 0) cycle
                if (pos(i) .lt. j) then
                    j = pos(i)
                    k = i
                end if
            end do first_pos
            line = str_replace(trim(num(k)), num2str(k), line)
        end do replace

        ! Process each line character by character.
        caracter: do i=1, len(trim(line))
            call regular_character(line(i:i), calibration)
        end do caracter

        ! Sum total
        total = total + ichar(calibration(1))*10-480
        total = total + ichar(calibration(2))-48
        write (*,"(a50, 2a, i6)") line_original, calibration, total
        calibration = "__"

        ! Read other line or exit
        read(f, "(a)", iostat=ios) line_original
        line = trim(line_original)
        if (ios .lt. 0) exit linea
    end do linea
    close(f)
    print *, "TOTAL", total
    ! No es 57340


contains

    subroutine regular_character(c, calibration)
        implicit none
        character, intent(in) :: c
        character, intent(out):: calibration(2)
        select case(ichar(c))
            case (48:57)    ! Is a digit
                if (calibration(1) .eq. "_") then
                    calibration(1) = c
                    calibration(2) = c
                else
                    calibration(2) = c
                end if
        end select
    end subroutine





end program aoc_01b

I’m afraid, I know how to get the calibration numbers, but my problem is that my total is no the correct answer, so I am looking for the problem in the upper text file whose second column are calibrations and last one the total

What calibration number do you get with the line sevenineight?

77!!!

Thank you, this is the way to solve the problem

EDIT

No, I made a mistake, I got 78, no 77, I must go on, but thank you (copy & paste error)

Finally I solved it. Thank to a member of this forum. Thank you, this is a great community :smiling_face::+1:t2:

4 Likes

Day 7 is a more familiar problem. Very similar to poker except “easier to play while riding a camel” - meaning no patterns (therefore no flush or straight flush) and the sequence of each hand matters.
Now, I don’t know about you, but the original poker (which is a “harder” problem to solve than Day7) was some kind of “unofficial homework” in my first programming class. No tutor ever asked to implement a program to evaluate a poker hand (only boring problems as homework - not necessarily easy, but boring). Nevertheless many students took the “poker challenge”, ignoring their actual homework. :laughing:
I do remember at least a Pascal, Fortran 77, and even a… Basic implementation being written in the lab. And vaguely remember the algorithm I thought it was convenient to solve it - which is, of course, a spoiler.

1 Like

Day 7 I like a lot more than 6. I thought I had a pretty decent method for part 1, but it appears this is not complete enough to easily extend for part 2…

For day 10 part 2, I just took the easy way out and used the locpt routine that originally came from the NSWC Mathematics Subroutine Library, written in the early 1990s. Worked like a charm in about 1.5 sec.

Day 11

Day 11 hugely benefits from array operations. I used count and sum.
But I struggled a lot of reading the input. I wanted to simply do this:

integer, parameter :: n = 10
character(1) :: space(n, n)
! ...
read(file_unit, *) space

But I couldn’t get this to work. I ended up with a loop, transferring whole lines into space:

do y=1,n
    read(file_unit, "(a)") line
    space(:, y) = transfer(line, space(:, y))
end do

Is there an easier way to do this?

For those who didn’t read the task, here is the example input and I wanted to store each character into a 2D field of character(len=1):

Example input
...#......
.......#..
#.........
..........
......#...
.#........
.........#
..........
.......#..
#...#.....

How about this:

program t
   integer, parameter :: n = 10
   character(1) :: space(n, n)
   open(newunit=i,file='aoc.txt')
   read(i,"(10a)",end=1) space
   1 print "(10a)", SPACE
end program

(works with gfortran but am not sure it’s 100% general)

1 Like

I was sure that I had tested this :sweat_smile:

1 Like

An unrelated question but I am curious: how do you blur parts of your posts in the Discourse?

This reminds me that it would be nice to have a one-stop-shop library for basic computational geometry in Modern Fortran, e.g. for calculating intersections, point-in-polygon, clipping, convex hulls, Voronoi diagrams etc. Extensive libraries are available in other languages and so a partial port may be the best way to go. Some of these routines would benefit from the parallelization features available in Modern Fortran. I could certainly make use of these but am not competent enough to write them.

3 Likes

2 Likes

Day 14 was tricky

It gave me flash backs to 2022 day 17, when you had to simulate a trillion tetris pieces for part 2. I didn’t know it back then, but now I know that the trick is to assume that eventually, a periodic cycle will begin where the pattern returns to the same state as an earlier step.

1 Like

For Day 16, I fully overengineered it by solving it with a Lattice Boltzmann type Method!

I didn’t encounter the same issues with infinite loops that others have, but it’s not super fast. Part 2 especially takes a long time (~14 seconds). Perhaps I’m doing something silly that is keeping the performance down. Is there someway to get FPM to set -march=native? Or does it do that already? Does FPM work with OpenMP?

I had a really hard time reading the input. Kept segfaulting or just being wrong. Finally had to use an exact number of characters to read to get what I wanted. I/O in fortran is just…difficult if you don’t do it a lot.

I’d appreciate any input on how to make this solution faster/better/more fortranic! (github link)