Advent of Code 2024 day 9, RPG edition
This is the nineth day of Advent of Code, let’s solve today’s puzzles with RPG.
The puzzle as well as the input data are available here.
The code is available here.
Part 1
We are provided with a (stream) file containing a disk map. It is a single line containing alternatively files and free space sizes (one digit for each).
We need to defragment the disk by moving blocks from files at the end of the disk to the free space at the begining of the disk.
We then need to calculate the filesystem checksum (see the puzzle for the exact definition).
As usual, we read the input file with QSYS2.IFS_READ_UTF8 table function.
We defragment and compute the checksum in one pass by processing the disk map from both ends: we process alternatively the files from the lelft and fill the free space with blocks from the right.
Here is the RPG code for part 1:
**free ctl-opt dftactgrp(*no); // We're using procedure so we can't be in the default activation group dcl-pi *n; input char(50); end-pi; dcl-c LENGTH 19999; dcl-s fileName varchar(50); dcl-s data char(LENGTH); dcl-s position int(10) inz(0); dcl-s leftID int(10) inz(0); dcl-s rightID int(10) inz(9999); dcl-s leftIndex int(10) inz(1); dcl-s rightIndex int(10) inz(19999); dcl-s leftCount int(3); dcl-s rightCount int(3); dcl-s i int(3); dcl-s result int(20) inz(0); fileName = %trim(input); // Read the input data from the IFS, concatenating all lines exec sql select line into :data from table(qsys2.ifs_read_utf8(path_name => :fileName)); leftCount = %int(%subst(data:1:1)); rightCount = %int(%subst(data:19999:1)); dow leftIndex < rightIndex; for i = 1 to leftcount; result += position*leftID; position += 1; endfor; leftIndex += 1; leftCount = %int(%subst(data:leftindex:1)); for i = 1 to leftCount; if rightCount = 0; rightIndex -= 2; rightCount = %int(%subst(data:rightIndex:1)); rightID -= 1; endif; result += position*rightID; rightCount -= 1; position += 1; endfor; leftIndex += 1; leftCount = %int(%subst(data:leftIndex:1)); leftID += 1; enddo; for i = 1 to rightCount; result += position*rightID; position += 1; endfor; snd-msg *info 'Result: ' + %char(result) %target(*pgmbdy:1); // Send message with answer *inlr = *on; return;
Part 2
In part 2, we change the defragmentation rules:
- we can only move a whole file from the end of the disk to a free space
- we start moving files from the end of the disk
- we move a file to the leftmost free space big enough for the file
- if there is no free space for a file, it is not moved at all
We solve this puzzle in 3 passes:
- we compute the initial begining position of each file
- we perform the actual defragmentation be changing the begining position of each file
- we compute the checksum
**free ctl-opt dftactgrp(*no); // We're using procedure so we can't be in the default activation group dcl-pi *n; input char(50); end-pi; dcl-c LENGTH 19999; dcl-s fileName varchar(50); dcl-s data char(LENGTH); dcl-s position int(10) inz(0); dcl-s leftID int(10) inz(0); dcl-s rightID int(10) inz(9999); dcl-s leftIndex int(10) inz(1); dcl-s rightIndex int(10) inz(19999); dcl-s leftCount int(3); dcl-s rightCount int(3); dcl-s i int(5); dcl-s j int(5); dcl-s positions int(10) dim(10000); dcl-s fillings int(10) dim(9999); dcl-s result int(20) inz(0); fileName = %trim(input); // Read the input data from the IFS, concatenating all lines exec sql select line into :data from table(qsys2.ifs_read_utf8(path_name => :fileName)); // Calculate initial position of each File for i = 1 to 10000; positions(i) = position; if i < 10000; position += %int(%subst(data:2*(i-1)+1:1))+%int(%subst(data:2*(i-1)+2:1)); endif; endfor; // Defragment for i = 19999 downto 3 by 2; rightCount = %int(%subst(data:i:1)); j = 2; leftCount = %int(%subst(data:j:1)); dow j<=i and rightCount > leftCount; j += 2; leftCount = %int(%subst(data:j:1)); enddo; if leftCount >= rightCount; positions(%div(i+1:2)) = positions(%div(j:2))+%int(%subst(data:j-1:1))+fillings(%div(j:2)); fillings(%div(j:2)) += rightCount; leftCount -= rightCount; %subst(data:j:1) = %char(leftCount); endif; endfor; // Compute checksum for i = 1 to 10000; position = positions(i); for j = 1 to %int(%subst(data:2*(i-1)+1:1)); result += (i-1)*position; position += 1; endfor; endfor; snd-msg *info 'Result: ' + %char(result) %target(*pgmbdy:1); // Send message with answer *inlr = *on; return;