[[{“value”:”In this case, I wanted to talk about the interesting possibility of updating data in our tables while using a SQL cursor.
1:10 Creating a RPG program that will do the job”}]] Read More
Robot Console | How to Answer a QSYSOPR Message via Email Fortra
If you’re interested in learning more about Robot Console, click here: https://www.helpsystems.com/products/message-event-monitoring-software-ibm-iThis video shows you how Robot Console combined with Robot Alert provides a secure means for sending and receiving QSYSOPR messages and replies via email. Read More
Once upon a time : Fort Knox Common Europe Luxembourg
[[{“value”:”In 1982, a project named Fort Knox commenced, which was intended to consolidate the System/36, the System/38, the IBM 8100, the Series/1 and the IBM 4300 series into a single product line based around an IBM 801-based processor codenamed Iliad, while retaining backwards compatibility with all the systems it was intended to replace.
A new operating system would be created for Fort Knox, but the operating systems of each platform which Fort Knox was intended to replace would also be ported to the Iliad processor to allow customers to migrate their software to the new platform.”}]] Read More
Advent of Code 2024 day 13, RPG edition
[[{“value”:”This is the thirteenth 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 configurations for a claw machine.
The claw machine has 2 buttons: A and B. Each button moves a given quantity (defined in the input) on X and Y axis. To win, we need to press A and B buttons a given number of times each to reach a given X and Y position (defined in the input).
It’s not possible to win all configurations.
Pressing A button costs 3 tokens, pressing B button costs 1 token.
The goal is to calculate how many token are need to win all winnable configurations.
The puzzle description seem to encourage a brute force method but let’s just math it out!
A solution can be represented as 2 equations with two unknown:
Ax + By = C
Dx + Ey = F
Where:
x is the number of A button press
y is the number of B button press
A is the X offset of A button
B is the X offset of B button
C is the target X coordinate
D is the Y offset of A button
E is the Y offset of B button
F is the target Y coordinate
If you don’t feel like solving those equations, you can ask Wolfram.
The solution is:
x=(CE-BF)/(AE-BD) and y=(CD-AF)/(BD-AE)
We simply calculate the value of x and y. To be a solution, x and y must be positive (or null) whole number. To avoid a false solution because of a rounding error, we verify the x and y values actually work.
As usual, we read the input file with QSYS2.IFS_READ_UTF8 table function.
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-s fileName varchar(50);
dcl-s data varchar(255);
dcl-s val varchar(10);
dcl-s result int(20) inz(0);
dcl-s ax int(5);
dcl-s ay int(5);
dcl-s bx int(5);
dcl-s by int(5);
dcl-s x int(20);
dcl-s y int(20);
dcl-s a packed(20:5);
dcl-s b packed(20:5);
fileName = %trim(input);
// Read the input data from the IFS
exec sql declare c1 cursor for select line from table(qsys2.ifs_read_utf8(path_name => :fileName));
exec sql open c1;
// Read first line
exec sql fetch c1 into :data;
dow sqlcode = 0;
// extract ax
exec sql set :val = regexp_substr(:data, ‘Button A: X+(d+), Y+(d+)’, 1, 1, ‘c’, 1);
ax = %int(val);
// extract ay
exec sql set :val = regexp_substr(:data, ‘Button A: X+(d+), Y+(d+)’, 1, 1, ‘c’, 2);
ay = %int(val);
// Read second line
exec sql fetch c1 into :data;
// extract bx
exec sql set :val = regexp_substr(:data, ‘Button B: X+(d+), Y+(d+)’, 1, 1, ‘c’, 1);
bx = %int(val);
// extract by
exec sql set :val = regexp_substr(:data, ‘Button B: X+(d+), Y+(d+)’, 1, 1, ‘c’, 2);
by = %int(val);
// Read third line
exec sql fetch c1 into :data;
// extract x
exec sql set :val = regexp_substr(:data, ‘Prize: X=(d+), Y=(d+)’, 1, 1, ‘c’, 1);
x = %int(val);
// extract y
exec sql set :val = regexp_substr(:data, ‘Prize: X=(d+), Y=(d+)’, 1, 1, ‘c’, 2);
y = %int(val);
a = (x*by-bx*y)/(ax*by-bx*ay);
b = (x*ay-ax*y)/(-ax*by+bx*ay);
if a>=0 and b>=0 and %int(a) = a and %int(b) = b;
if a*ax+b*bx = x and a*ay+b*by = y;
result += %int(a)*3 + %int(B);
endif;
endif;
// Read next 2 lines
exec sql fetch c1 into :data;
exec sql fetch c1 into :data;
enddo;
exec sql close c1;
snd-msg *info ‘Result: ‘ + %char(result) %target(*pgmbdy:1); // Send message with answer
*inlr = *on;
return;
Part 2
In part 2, we add a big number to the target coordinates.
If had used a brute force method in part 1, we would be in trouble. Since we went the math way, we just have to change the target coordinates.
**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-s fileName varchar(50);
dcl-s data varchar(255);
dcl-s val varchar(10);
dcl-s result int(20) inz(0);
dcl-s ax int(5);
dcl-s ay int(5);
dcl-s bx int(5);
dcl-s by int(5);
dcl-s x int(20);
dcl-s y int(20);
dcl-s a packed(20:5);
dcl-s b packed(20:5);
fileName = %trim(input);
// Read the input data from the IFS
exec sql declare c1 cursor for select line from table(qsys2.ifs_read_utf8(path_name => :fileName));
exec sql open c1;
// Read first line
exec sql fetch c1 into :data;
dow sqlcode = 0;
// extract ax
exec sql set :val = regexp_substr(:data, ‘Button A: X+(d+), Y+(d+)’, 1, 1, ‘c’, 1);
ax = %int(val);
// extract ay
exec sql set :val = regexp_substr(:data, ‘Button A: X+(d+), Y+(d+)’, 1, 1, ‘c’, 2);
ay = %int(val);
// Read second line
exec sql fetch c1 into :data;
// extract bx
exec sql set :val = regexp_substr(:data, ‘Button B: X+(d+), Y+(d+)’, 1, 1, ‘c’, 1);
bx = %int(val);
// extract by
exec sql set :val = regexp_substr(:data, ‘Button B: X+(d+), Y+(d+)’, 1, 1, ‘c’, 2);
by = %int(val);
// Read third line
exec sql fetch c1 into :data;
// extract x
exec sql set :val = regexp_substr(:data, ‘Prize: X=(d+), Y=(d+)’, 1, 1, ‘c’, 1);
x = %int(val)+10000000000000;
// extract y
exec sql set :val = regexp_substr(:data, ‘Prize: X=(d+), Y=(d+)’, 1, 1, ‘c’, 2);
y = %int(val)+10000000000000;
a = (x*by-bx*y)/(ax*by-bx*ay);
b = (x*ay-ax*y)/(-ax*by+bx*ay);
if a>=0 and b>=0 and %int(a) = a and %int(b) = b;
if a*ax+b*bx = x and a*ay+b*by = y;
result += %int(a)*3 + %int(B);
endif;
endif;
// Read next 2 lines
exec sql fetch c1 into :data;
exec sql fetch c1 into :data;
enddo;
exec sql close c1;
snd-msg *info ‘Result: ‘ + %char(result) %target(*pgmbdy:1); // Send message with answer
*inlr = *on;
return;”}]] Read More
Advent of Code 2024 day 14, RPG edition
[[{“value”:”This is the fourteenth 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 robots initial positions and speeds.
The robots teleport at the other en of the map when they reach the border.
We need to calculate how many robots are in the four quadrants of the map after 100 seconds and multiply the quantity of robots in each quadrant.
As usual, we read the input file with QSYS2.IFS_READ_UTF8 table function.
Rather than calculating the position of the robots each second, we can directly calculate the position of a robot after 100 seconds by multiplying the speed by 100 and adding the initial position, then calculating the reminder of the division by the width or they heighth of the map (depending on the axis) with the %REM built-in function.
We also don’t need to calculate the number of robots in each position, only in each quadrant.
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 WIDTH 101;
dcl-c HEIGHT 103;
dcl-c MID_X 50;
dcl-c MID_Y 51;
dcl-c LENGTH 10403;
dcl-c TIME 100;
dcl-s fileName varchar(50);
dcl-s data varchar(255);
dcl-s x int(3);
dcl-s y int(3);
dcl-s vx int(3);
dcl-s vy int(3);
dcl-s result int(20) inz(0);
dcl-s map uns(5) dim(4); // We store the number of robots per quarter
dcl-s quadrant uns(3);
fileName = %trim(input);
// Read the input data from the IFS
exec sql declare c1 cursor for select line from table(qsys2.ifs_read_utf8(path_name => :fileName));
exec sql open c1;
// Read first line
exec sql fetch c1 into :data;
dow sqlcode = 0;
// Read initial position and speed
exec sql set 😡 = int(regexp_substr(:data, ‘p=(d+),(d+) v=(-?d+),(-?d+)’, 1, 1, ”, 1));
exec sql set :y = int(regexp_substr(:data, ‘p=(d+),(d+) v=(-?d+),(-?d+)’, 1, 1, ”, 2));
exec sql set :vx = int(regexp_substr(:data, ‘p=(d+),(d+) v=(-?d+),(-?d+)’, 1, 1, ”, 3));
exec sql set :vy = int(regexp_substr(:data, ‘p=(d+),(d+) v=(-?d+),(-?d+)’, 1, 1, ”, 4));
// Compute final position
x = %rem(x+vx*TIME:WIDTH);
y = %rem(y+vy*TIME:HEIGHT);
// Adjust x and y if they are negative
if x < 0;
x += WIDTH;
endif;
if y < 0;
y += HEIGHT;
endif;
// We ignore robots in the middle
if x<>MID_X and y<>MID_Y;
// determine the quadrant
if x<MID_X;
quadrant = 1;
else;
quadrant = 2;
endif;
if y>MID_Y;
quadrant += 2;
endif;
map(quadrant) += 1;
endif;
// Read next line
exec sql fetch c1 into :data;
enddo;
exec sql close c1;
result = map(1)*map(2)*map(3)*map(4);
snd-msg *info ‘Result: ‘ + %char(result) %target(*pgmbdy:1); // Send message with answer
*inlr = *on;
return;
Part 2
Contrary to the usual precision of the puzzle, part 2 was extremely vague and the puzzle quite difficult to code because we don’t know exactly what we’re looking for.
The solution I ended up using is drawing each line of the map at every second from second 1 to 10000 and look for an horizontal line of 17 robots, it happened to work.
**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 WIDTH 101;
dcl-c HEIGHT 103;
dcl-c MID_X 50;
dcl-c MID_Y 51;
dcl-c LENGTH 10403;
dcl-c MIN_TIME 0;
dcl-c MAX_TIME 10000;
dcl-s fileName varchar(100);
dcl-s data varchar(255);
dcl-s x int(3);
dcl-s y int(3);
dcl-s vx int(3);
dcl-s vy int(3);
dcl-s time int(5);
dcl-s result int(20) inz(0);
dcl-s map uns(5) dim(LENGTH); // We store the number of robots per position
fileName = %trim(input);
// Read the input data from the IFS
exec sql declare c1 cursor for select line from table(qsys2.ifs_read_utf8(path_name => :fileName));
for time = MIN_TIME to MAX_TIME;
reset map;
exec sql open c1;
// Read first line
exec sql fetch c1 into :data;
dow sqlcode = 0;
// Read initial position and speed
exec sql set 😡 = int(regexp_substr(:data, ‘p=(d+),(d+) v=(-?d+),(-?d+)’, 1, 1, ”, 1));
exec sql set :y = int(regexp_substr(:data, ‘p=(d+),(d+) v=(-?d+),(-?d+)’, 1, 1, ”, 2));
exec sql set :vx = int(regexp_substr(:data, ‘p=(d+),(d+) v=(-?d+),(-?d+)’, 1, 1, ”, 3));
exec sql set :vy = int(regexp_substr(:data, ‘p=(d+),(d+) v=(-?d+),(-?d+)’, 1, 1, ”, 4));
// Compute final position
x = %rem(x+vx*TIME:WIDTH);
y = %rem(y+vy*TIME:HEIGHT);
// Adjust x and y if they are negative
if x < 0;
x += WIDTH;
endif;
if y < 0;
y += HEIGHT;
endif;
map(x+y*WIDTH+1) += 1;
// Read next line
exec sql fetch c1 into :data;
enddo;
exec sql close c1;
for y = 0 to HEIGHT-1;
data = ”;
for x = 0 to WIDTH -1;
if map(x+y*WIDTH+1) > 0;
data += ‘#’;
else;
data += ‘.’;
endif;
endfor;
exec sql set :result = regexp_count(:data, ‘#################’);
if result > 0;
snd-msg *info ‘Time: ‘ + %char(time) + ‘s’ %target(*pgmbdy:1); // Send message with answer
endif;
endfor;
endfor;
*inlr = *on;
return;”}]] Read More