This commit is contained in:
matthias@arch 2022-12-05 14:24:14 +01:00
parent 412ce5a7b0
commit c079e80137
11 changed files with 1578 additions and 0 deletions

View File

@ -3,9 +3,16 @@
Today's language: **C** Today's language: **C**
Extra: Task 1 in **vimscript**, generated by OpenAI with just a few fixes by myself
All the characters (the Elf's items) occuring in (half) a line (backpack) are stored as bits in an unsigned long, duplicate characters can then be determined by bitwise and-ing these numbers. All the characters (the Elf's items) occuring in (half) a line (backpack) are stored as bits in an unsigned long, duplicate characters can then be determined by bitwise and-ing these numbers.
```shell ```shell
# Find the item type that appears in both compartments of each rucksack. What is the sum of the priorities of those item types? # Find the item type that appears in both compartments of each rucksack. What is the sum of the priorities of those item types?
gcc main.c -o day3 gcc main.c -o day3
./day3 ./day3
# OpenAI vim script
vim rucksack_items.txt
# in vim:
# :source day3-1.vim
``` ```

47
3/day3-1.vim Normal file
View File

@ -0,0 +1,47 @@
" Define the function that will be run when the script is executed
function! FindAndSumCharacters()
" Initialize the sum to 0
let sum = 0
" Iterate over every line in the file
" Note: The cursor position is not relevant here, since the script
" will process the entire file
for line in getline(1, '$')
" Get the first and second half of the line
let first_half = line[:len(line)/2]
let second_half = line[len(line)/2:]
" Find the character that occurs in both the first and second half
" of the line.
" Note: There will be only one such character, since the prompt
" specified that each line contains only a-zA-Z
let common_char = ''
for char in first_half
if match(second_half, '\C' . char) >= 0
let common_char = char
" echo first_half . " - " . second_half . " - char" . common_char
break
endif
endfor
" Convert the character to a number
let common_char_num = char2nr(common_char)
let char_num = 0
if common_char_num >= char2nr('a') && common_char_num <= char2nr('z')
let char_num = common_char_num - char2nr('a') + 1
elseif common_char_num >= char2nr('A') && common_char_num <= char2nr('Z')
let char_num = common_char_num + 27 - char2nr('A')
endif
" echo "char_num " . char_num . "for char " . common_char . " char2nr " . char2nr(common_char)
" Add the number to the sum
let sum += char_num
endfor
" Output the final sum
echo sum
endfunction
" Run the script when it is loaded
call FindAndSumCharacters()

3
4/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
*.o
.old
day4

22
4/.vimspector.json Normal file
View File

@ -0,0 +1,22 @@
{
"configurations": {
"vulkan_test": {
"adapter": "vscode-cpptools",
"configuration": {
"name": "day4",
"type": "asmdbg",
"request": "launch",
"externalConsole": true,
"logging": {
"engineLogging": true
},
"stopOnEntry": true,
"stopAtEntry": true,
"debugOptions": [],
"MIMode": "gdb",
"cwd": "~/Sonstiges/AdventOfCode/4",
"program": "~/Sonstiges/AdventOfCode/4/day4"
}
}
}
}

14
4/Makefile Normal file
View File

@ -0,0 +1,14 @@
ASM_SRC = day4.s
C_SRC = day4-from-ai.c
# DEBUG = -g
DEBUG =
asm:
as -o day4.o $(ASM_SRC) $(DEBUG)
ld $(DEBUG) -o day4 day4.o -lc
@# for some reason an invalid interpreter is in the binary, which leads to 'file not found error' when running
patchelf --set-interpreter "/usr/lib/ld-linux-x86-64.so.2" day4
c:
gcc $(C_SRC) -o day4 "section-pairs.txt"

28
4/README.md Normal file
View File

@ -0,0 +1,28 @@
# [Day 4](https://adventofcode.com/2022/day/4)
:gift::gift::gift::gift:
Today's language: **gnu x86-64** (and alternatively **c**)
Today I wanted to use OpenAI to solve the tasks using gnu x86-64, which I have never used before
(I did some small things in [6502-asssembly](https://github.com/MatthiasQuintern/6502), so it wasn't all new).
The prompt can be seen in prompt.txt.
It did a lot of things right, however:
- It did generate x86 (32-bit) code because I did not specify x86-64. So I changed the `int $0x80` system calls to use `syscall` (which also meant I had to change all the registers and [syscall numbers](https://filippo.io/linux-syscall-table/))
- It did get the argument order for `sscanf` wrong, even when I specifically asked it told me that the line to read comes last
- It did not correctly implement the logic to test the condition
- It sometimes me wrong syscall numbers (they were just wrong, not even x86)
- It told me the `read` syscall can read until a `newline` by passing `newline` as last arg
So I had to fix all that. I tried to exclusively use the AI to get answers to my questions, however I did sometimes revert to the internet.
It was still a good starting point and provided lots of help.
At the end of the ~~evening~~ next morning I had coded the task in x86-64 assembly.
To test scanf, I gave it the exact same prompt but said I wanted it in *c*.
It instantly produced compilable c code that gave the right answer...
```shell
# for the assembly program
make asm
./day4
# for the c promgram
make c
./day4
```

71
4/day4-from-ai.c Normal file
View File

@ -0,0 +1,71 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/*
* This file was generated by OpenAI and was not modified apart from this comment. The prompt was:
* consider a text file like this:
* 22-65,22-66
* 91-94,63-91
* 6-88,5-31
* 85-86,64-86
* 40-65,40-65
* 25-82,24-94
*
* write c program that:
* - reads a text file like this line by line and count how often one of the two ranges contains the other.
* - accepts the filename via command line arg
* - outputs the result using printf with a suitable string
* A range r1 contains the other r2 when the r1.lower <= r2.lower and r1.upper >= r2.upper
* the program needs to output how often that is the case in the entire file
*/
// Structure to represent a range in the file
struct range {
int lower;
int upper;
};
// Function to parse a line from the file and return the two ranges in the line
void parse_line(const char *line, struct range *range1, struct range *range2) {
// Parse the two ranges in the line
sscanf(line, "%d-%d,%d-%d", &range1->lower, &range1->upper, &range2->lower, &range2->upper);
}
int main(int argc, char *argv[]) {
// Check if a filename was provided
if (argc < 2) {
fprintf(stderr, "Error: No filename provided\n");
return 1;
}
// Open the file for reading
FILE *fp = fopen(argv[1], "r");
if (fp == NULL) {
fprintf(stderr, "Error: Unable to open file for reading\n");
return 1;
}
// Read the file line by line
char line[256];
int count = 0;
while (fgets(line, sizeof(line), fp) != NULL) {
// Parse the two ranges in the line
struct range range1, range2;
parse_line(line, &range1, &range2);
// Check if one range contains the other
if ((range1.lower <= range2.lower && range1.upper >= range2.upper) ||
(range2.lower <= range1.lower && range2.upper >= range1.upper)) {
count++;
}
}
// Close the file
fclose(fp);
// Print the result
printf("The file contains %d lines where one of the two ranges contains the other\n", count);
return 0;
}

66
4/day4-from-ai.s Normal file
View File

@ -0,0 +1,66 @@
.data
filename: .asciz "input.txt"
format: .asciz "%d-%d,%d-%d"
result: .asciz "Result: %d\n"
.text
.globl main
main:
pushl %ebp
movl %esp, %ebp
xorl %eax, %eax # initialize count to 0
movl $1, %ebx # initialize loop variable to 1
# open the file and store the file descriptor in %ecx
movl $5, %eax # set the number of the "open" syscall to 5
movl $filename, %ebx # move the address of the filename to %ebx
xorl %edx, %edx # set the flags to 0
int $0x80
# read the file line by line
.loop:
movl $3, %eax # set the number of the "read" syscall to 3
movl %ecx, %ebx # move the file descriptor to %ebx
movl $buffer, %ecx # move the address of the buffer to %ecx
movl $BUFSIZ, %edx # move the size of the buffer to %edx
int $0x80
# check if end of file has been reached
cmpl $0, %eax
jz .done # if so, jump to .done
# parse the line and check if it satisfies the condition
call sscanf
movl %eax, %edi
movl %ebx, %esi
movl %ecx, %ebp
movl %edx, %ebx
cmpl %edi, %ebp
jl .not_contained
cmpl %esi, %ebx
jg .not_contained
incl %eax # increment count
.not_contained:
incl %ebx # increment loop variable
jmp .loop # repeat the loop
.done:
# print the result
movl $1, %eax # set the number of the "write" syscall to 1
movl $1, %ebx # set the file descriptor to stdout (1)
movl $result, %ecx # move the address of the result string to %ecx
movl %eax, %edx # move the count to %edx
int $0x80
# close the file
movl $6, %eax # set the number of the "close" syscall to 6
movl %ecx, %ebx # move the file descriptor to %ebx
int $0x80
leave
ret

294
4/day4.s Normal file
View File

@ -0,0 +1,294 @@
# Parts of this code were generated by OpenAI
# I did however have to modifiy nearly all of it
.data
count_contained:.long 0
count_overlap: .long 0
file: .quad 0 # file descriptor
line_length: .quad 0 # store the length of the current line in line_buffer
line_nr: .long 0 # store the line number
r1_upper: .long 0 # range 1 upper value
r2_upper: .long 0 # range 2 upper value
r1_lower: .long 0 # range 1 lower value
r2_lower: .long 0 # range 2 lower value
current_char: .long 0
filename: .asciz "section-pairs.txt"
format: .asciz "%d-%d,%d-%d"
f_contained: .asciz "Current line has containment. count_contained: [%d]\n"
f_res_cont: .asciz "The file contains %d lines where one of the two ranges contains the other\n"
f_res_over: .asciz "The file contains %d lines where one of the two ranges overlaps the other\n"
f_line_nr: .asciz "Line Nr: [%d] -\t"
f_line_length: .asciz "Line Length: [%d] -\tContent: '"
f_q_newline: .asciz "'\n"
f_error_open: .asciz "Error %d while opening file\n"
f_error_read: .asciz "Error %d while reading file.\n"
f_error_match: .asciz "Error %d while matching line.\n"
LINE_BUFSIZ = 32
PRINT_BUFSIZ = 100
SYS_READ = 0
SYS_WRITE = 1
SYS_OPEN = 2
SYS_CLOSE = 3
SYS_EXIT = 60
STDOUT = 1
.bss
line_buffer: .skip LINE_BUFSIZ
print_buffer: .skip PRINT_BUFSIZ
.text
.globl _start
.globl sscanfl
# syscall arg order
# (call #: rax) - rdi rsi rdx r10 r8 r9
_start:
push %rbp
mov %rsp, %rbp
# xorl %eax, %eax # initialize count_contained to 0
# movl $1, %ebx # initialize loop variable to 1
# open the file and store the file descriptor in %ecx
movq $SYS_OPEN, %rax # set the number of the "open" syscall
movq $filename, %rdi # move the address of the filename to %ebx
xorq %rsi, %rsi # set the flags to 0
xorq %rdx, %rdx # set the mode to 0 = read
# movq $777, %rsi
syscall
# if read fails, jump to error
jl .error_open
jg .error_open
# negative return value is error
cmpq $0, %rax
jl .error_open
movq %rax, file # store file descriptor in file
# read the file line by line
.loop:
call read_next_line
cmpq $0, %rax # rax is number of chars read, if 0 we are done
je .done
# get and print line lenght
movq %rax, line_length
# increment line nr
incl line_nr # increment line_nr
# print line nr
movl line_nr, %edx # load to print
leaq f_line_nr, %rsi # set print format
push %rdx
call print_rsi_rdx
pop %rdx
# print line lenght
movq line_length, %rdx
leaq f_line_length, %rsi
push %rdx
call print_rsi_rdx
pop %rdx
# print the line
movq $SYS_WRITE, %rax # set the number of the "write" syscall to 4
movq $STDOUT, %rdi # move the file descriptor (1 for stdout) to %ebx
movq $LINE_BUFSIZ, %rdx # number of bytes
leaq line_buffer, %rsi # buffer
syscall
# print newline
movq $SYS_WRITE, %rax # set the number of the "write" syscall to 4
movq $STDOUT, %rdi # move the file descriptor (1 for stdout) to %ebx
movq $2, %rdx # number of bytes
leaq f_q_newline, %rsi # buffer
syscall
# from first arg to last
leaq line_buffer, %rdi # Load the address of the line_buffer into %rdi
leaq format, %rsi # Move the address of the format string into %rs
leaq r1_lower, %rdx # Move the address of the first output register into %esi
leaq r1_upper, %rcx # Move the address of the second output register into %edx
leaq r2_lower, %r8 # Move the address of the third output register into %ecx
leaq r2_upper, %r9 # Move the address of the fourth output register into %r8d
push %rbx # to align the stack
call sscanf # Call the scanf function
pop %rbx # restore stack ptr
cmp $0, %rax
je .error_match # 0 is not succesful
# load the results into registers
movl r1_lower, %eax
movl r2_lower, %ebx
movl r1_upper, %ecx
movl r2_upper, %edx
cmp %ebx, %eax # compare r1l to r2l
jg .cont_r1l_greater_r2l
je .is_contained # if lower bounds are equal, one must be contained in the other
jl .cont_r1l_less_r2l
# 1) check containment
.cont_r1l_greater_r2l: # r1l > r2l
# to be contained: r1u <= r2u
cmp %edx, %ecx
jl .is_contained
je .is_contained
jmp .not_contained
.cont_r1l_less_r2l:
# to be contained: r1u >= r1u
cmp %edx, %ecx
jg .is_contained
je .is_contained
jmp .not_contained
movq $42, %rax
jmp .error_open # it should not come to this
.is_contained:
incl count_contained
jmp .overlaps # containment overlap
.not_contained:
# 2) overlap
# two ranges overlap if
# r1l > r2l:
# r1l <= r2u
# r1l < r2l:
# r1u >= r2l
# r1l == r2l
cmpl %ebx, %eax # compare r1l to r2l
jg .over_r1l_greater_r2l
je .overlaps
jl .over_r1l_less_r2l
movq $43, %rax
jmp .error_open # should not happen
.over_r1l_greater_r2l: # r1l > r2l
cmpl %edx, %eax # overlap if r1l <= r2u
je .overlaps
jl .overlaps
jmp .no_overlap
.over_r1l_less_r2l: # r1l < r2l
cmpl %ebx, %ecx # overlap if r1u >= r2l
jg .overlaps
je .overlaps
jmp .no_overlap
.overlaps:
incl count_overlap
.no_overlap:
jmp .loop # repeat the loop
.done:
movl count_contained, %edx
leaq f_res_cont, %rsi # set print format
push %rsi
call print_rsi_rdx
pop %rsi
movl count_overlap, %edx
leaq f_res_over, %rsi # set print format
push %rsi
call print_rsi_rdx
pop %rsi
movq $0, %r10 # set exit status
jmp .close_file
.error_open:
leaq f_error_open, %rsi # set print format
jmp .error
.error_read:
leaq f_error_read, %rsi # set print format
jmp .error
.error_match:
leaq f_error_match, %rsi # set print format
.error:
movq %rax, %rdx # print rax
# leaq f_error, %rsi # set print format
push %rax
call print_rsi_rdx
pop %rax
movq %rax, %r10 # set exit status
.close_file:
# close the file, expect exit status in %r10
movq $SYS_CLOSE, %rax # set the number of the "close" syscall to 3
movq file, %rdi # move the file descriptor to %rdi
syscall
leave
movq $SYS_EXIT, %rax
movq %r10, %rdi
syscall
read_next_line:
# read char by char, until newline or eof, return number of chars in rax
xorq %r10, %r10 # charcount_contained
xorq %rsi, %rsi # make sure rsi is 0
xorq %r11, %r11 # make sure r11 is 0
.load_char:
movq $SYS_READ, %rax # set the number of the "read" syscall
movq file, %rdi # move the file descriptor to %rdi
leaq current_char, %rsi # move the address of the charbuffer to %rsi
movq $1, %rdx # read only 1 char
syscall
jl .error_read
cmpq $0, %rax # if no char is loaded - eof
je .line_complete
movb current_char, %r11b
cmpb $'\n', %r11b # if newline
je .line_complete
movb %r11b, line_buffer(,%r10,1) # copy current char into the place at charcount_contained
incq %r10 # increment charcount_contained
jmp .load_char
.line_complete:
# null terminate the line
leaq line_buffer, %rax
movq $0, (%rax,%r10,1)
movq %r10, %rax # return number of chars loaded in rax
ret
print_rsi_rdx:
# print an int with $format, first get the formatted string from sprintf and the do a write syscall to stdout
# print the format string in %rsi which contains one int in %rdx
push %rdx # save the value
# Set up the format string and the integer value
leaq print_buffer, %rdi # Load the address of the buffer into %rdi
# Use sprintf to format the string and the integer value
call sprintf # Call the sprintf function
# Determine the number of bytes written to the buffer
movq %rax, %rdx # Move the number of bytes written to %rdx
# Set up the write syscall
movq $SYS_WRITE, %rax # set the number of the "write" syscall to 4
movq $STDOUT, %rdi # move the file descriptor (1 for stdout) to %ebx
leaq print_buffer, %rsi
syscall
pop %rdx
ret

26
4/prompt.md Normal file
View File

@ -0,0 +1,26 @@
# original prompt
consider a text file like this:
22-65,22-66
91-94,63-91
6-88,5-31
85-86,64-86
40-65,40-65
25-82,24-94
write an gnu-x86 assembly program that:
- reads a text file like this line by line and count how often one of the two ranges contains the other.
- accepts the filename via command line arg
- outputs the result using printf with a suitable string
A range r1 contains the other r2 when the r1.lower <= r2.lower and r1.upper >= r2.upper
the program needs to output how often that is the case in the entire file
# add task 2 to c code:
can you add another check to the c code:
it should now also check if one of the ranges overlaps the other.
A range r1 overlaps the other r2 if:
- r1.lower == r2.lower
- r1.upper == r2.upper
- r1.lower > r2.lower and r1.lower <= r2.upper
- r1.upper < r1.upper r1.upper >= r2.lower
...
sorry i failed to mention that the overlaps and containment should be counted separately

1000
4/section-pairs.txt Normal file

File diff suppressed because it is too large Load Diff