Advent of code, day 4. Znajdź wszystkie XMAS.

Advent of code, day 4. Znajdź wszystkie XMAS.
PW
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 17
0

Witam, Święta za pasem to zabrałem się za advent of code. https://adventofcode.com/2024/day/4. W skrócie: mamy blok tekstu o kształcie kwadratu. W tym bloku mamy znaleźć wszystkie wystąpienia słowa XMAS (poziomo,pionowo,skośnie i wspak oraz mogą nachodzić na siebie: XMASAMX). Skrypt działa na bloku testowym (inputblok liter 10x10) i zwraca poprawnie 18 wystąpień słowa. W bloku z zadania (https://adventofcode.com/2024/day/4/input) nie zwraca poprawnie ilości wystąpień - brakuje mi ze 200 rekordów. Czy ktoś może podpowiedzieć co robię źle? Jakieś sugestie jak pozbyć się 4 bloków while ? Wydaje mi się, że problem leży w obliczaniu przekątnych ale policzyłem że skrypt wycina odpowiedną ich ilość (38 dla kwadratu z liter o boku 10), sprawdziłem też że wycina je odpowiednio z lewej góry na dół i z prawej góry na dół. HELP!

Popełniłem takiego tasiemca jak poniżej:

Kopiuj
import regex as re
path = r'C:\TEST\advent4_test.txt'
import itertools as it

def count_overlapping(line, search_for):
    return len(re.findall(search_for, line, overlapped=True))

# HORIZONTAL MATCHES

count_horizontal = 0
with open(path,'r') as f:
    for line in f.readlines():
        if count_overlapping(line,'XMAS'):
            count_horizontal +=1
        if count_overlapping(line,'SAMX'):
            count_horizontal +=1

# stworzenie listy list poziomych znaków, oraz usunięcie znaku końca linii

list_of_horizontals = [] # to będzie nasze matrix,lista list

with open(path,'r') as f:
    for line in f.readlines():
        line = line.rstrip('\n')
        lista = []
        for i in line:
            lista.append(i)
        list_of_horizontals.append(lista)


# VERTICAL MATCHES

rows = len(list_of_horizontals)
columns = len(list_of_horizontals[0])

vert_string=''
list_of_vert_strings = []
for i in range(rows):
    for j in range(columns):
        vert_string += list_of_horizontals[j][i]
    list_of_vert_strings.append(vert_string)
    vert_string =''

count_vertical = 0
for i in list_of_vert_strings:
    count_vertical += count_overlapping(i,'XMAS') + count_overlapping(i,'SAMX')

# DIAGONAL MATCHES

count_diagonal = 0
matrix = list_of_horizontals.copy()

flatt_matrix = list(it.chain.from_iterable(matrix)) # spłaszczenie całej matrycy(lista w liście) do ciągu znaków w jednej liście

rlist = [] # będzie zawierała odwrócone kolejnością znaki w poszczeg. listach
for r in matrix:
    new = r.copy()
    new.reverse()
    rlist.append(new)

flatt_reversed_matrix = list(it.chain.from_iterable(rlist))


# prawy górny trójkąt

list_of_diagonals = []
iter_count = len(matrix)
i=0
while iter_count !=0:
    for n in range(i,iter_count):
        list_slice_right = list(it.islice(flatt_matrix,i,(len(flatt_matrix)) - (n*len(matrix)),len(matrix)+1))
        list_of_diagonals.append(list_slice_right)
        print(list_slice_right,'prawa góra matrycy')
        i +=1
        iter_count -=1
print('#'*80)

# lewy górny trójkąt

iter_count = len(matrix)
i=0
while iter_count !=0:
    for n in range(i,iter_count):
        list_slice_right = list(it.islice(flatt_reversed_matrix,i,(len(flatt_reversed_matrix)) - (n*len(matrix)),len(matrix)+1))
        list_of_diagonals.append(list_slice_right)
        print(list_slice_right,'lewa góra matrycy')
        i +=1
        iter_count -=1
print('#'*80)

# lewy dolny trójkąt

iter_count = len(matrix)
i=1
while iter_count !=1:
    for n in range(i,iter_count):
        list_slice_right = list(it.islice(flatt_matrix,(len(matrix)*i),(len(flatt_matrix)),len(matrix)+1))
        list_of_diagonals.append(list_slice_right)
        print(list_slice_right,'lewy dół')
        i +=1
        iter_count -=1
print('#'*80)

# lewy dolny trójkąt
iter_count = len(matrix)
i=1
while iter_count !=1:
    for n in range(i,iter_count):
        list_slice_right = list(it.islice(flatt_reversed_matrix,(len(matrix)*i),(len(flatt_reversed_matrix)),len(matrix)+1))
        list_of_diagonals.append(list_slice_right)
        print(list_slice_right,'prawy dół')
        i +=1
        iter_count -=1

diagon_string=''
list_of_diagon_strings = []

# zamiana listy z literami na stringi
for i in list_of_diagonals:
    for j in i:
        diagon_string += j
    list_of_diagon_strings.append(diagon_string)
    diagon_string = ''


# TESTING AREA AND ENDING

print(list_of_diagon_strings) # czy prawidłowa liczba przekątnych
print(len(list_of_diagon_strings))

# iteracja po liście ze stringami
for i in list_of_diagon_strings:
    count_diagonal += count_overlapping(i,'XMAS')
    count_diagonal += count_overlapping(i,'SAMX')

# PODSUMOWANIE

whole_amount = count_horizontal + count_vertical + count_diagonal
print(whole_amount,'whole amount of all XMAS and SAMX')
print(count_horizontal,'horizontal',count_vertical,'vertical',count_diagonal,'diagonal')
lion137
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 5023
0

Zamiast spłaszczania, spróbuj numpy, tam jest taka funkcyjka diagonal.

PW
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 17
0
lion137 napisał(a):

Zamiast spłaszczania, spróbuj numpy, tam jest taka funkcyjka diagonal.

Dziękuję, spróbuję też z numpy. Ciekawe rozwiązanie przedstawił gość w tym filmie: Spoiler alert. Sprawdził czy w danym polu jest X i idąc we wszystkich kierunkach musiały występować kolejno litery M A S.

Spine
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 6965
1

Ja sobie najpierw wyciągnąłem wszystkie rodzaje linii i dla uproszczenia zrobiłem ich odwrócone kopie.
Potem miałem listę linii, w których liczyłem wystąpienia słowa XMAS.

Kopiuj
import re
#horizontal, vertical, diagonal, written backwards, or even overlapping other words

#idea: extract diagonal lines

def extract_all_lines(txt):
	# horizontal lines
	result = txt.splitlines()
	
	# vertical lines
	chars_per_line = len(result[0])
	lines_count = len(result)
	
	for char_idx in range(chars_per_line):
		vertical_line = ""
		for line_idx in range(lines_count):
			vertical_line += result[line_idx][char_idx]
		
		result.append(vertical_line)
	
	# diagonal lines
	for start_x in range(chars_per_line):
		diagonal_line = ""
		
		x = start_x
		y = 0
		
		while x < chars_per_line and y < lines_count:
			diagonal_line += result[y][x]
			x += 1
			y += 1
		
		result.append(diagonal_line)
		
	for start_y in range(1, lines_count):
		diagonal_line = ""
		
		x = 0
		y = start_y
		
		while x < chars_per_line and y < lines_count:
			diagonal_line += result[y][x]
			x += 1
			y += 1
		
		result.append(diagonal_line)
	
	for start_x in range(chars_per_line):
		diagonal_line = ""
		
		x = start_x
		y = 0
		
		while x >= 0 and y < lines_count:
			diagonal_line += result[y][x]
			x -= 1
			y += 1
			
		result.append(diagonal_line)
	
	for start_y in range(1, lines_count):
		diagonal_line = ""
		
		x = chars_per_line - 1
		y = start_y
		
		while x >= 0 and y < lines_count:
			diagonal_line += result[y][x]
			x -= 1
			y += 1
		
		result.append(diagonal_line)
	
	# reverse all lines
	lines_count = len(result)
	for line_idx in range(lines_count):
		result.append(result[line_idx][::-1])
	
	return result

def count_XMAS(all_lines):
	result = 0
	
	for line in all_lines:
		result += len(re.findall(r"XMAS", line))
		
	return result
		

we = """MMMSAMXAMMXSXXMAXAXXXSASAMXSAMXSAMXMAXSAMXSXSMSASAMXXXSASXSMSSSSXMAMSAXMSSXXASASMASXAXXMSMSSMMMSAMXMMMMXMXSXXMSXMAXXMAMXMSSXXMAMMMSMMMMMSXAX
XAAXAMXMASXMASMSSSXSASASAMXMXMAMXMAMXASXAXMAXAMXSAMSMMSAMASAAAAAMXMASMMMMMMSMMASAXAMMMSMSMAAAASMMXSMSMSASMAASXMASMMSXMASASASXMASXMASAAASMMAM
...
SSMSAXSMMMSSSMSMSMSXSSMSASMMMSMMMSXMAXXAMMSAMXXMMSXXSAMXMMSMMASMMSAMXXMMMXMMAMMMMSMMMXSAMSSSMSSSXAASXXXSXSAXAMXSSXMASXSMMSASMXSASXMASMXXMAMA"""
		
print (count_XMAS(extract_all_lines(we)))

Part 2 zrobiłem znacznie łatwiej.
Dla każdej literki A sprawdzałem, czy sąsiaduje po skosie z literkami M i S.

Kopiuj
def extract_all_lines(txt):
	lines = txt.splitlines()
	
	return lines

def count_XMAS(all_lines):
	result = 0
	
	chars_per_line = len(all_lines[0])
	lines_count = len(all_lines)
	
	for y in range(1, lines_count - 1):
		for x in range(1, chars_per_line - 1):
			if all_lines[y][x] == 'A':
				diagonal1 = all_lines[y-1][x-1] + all_lines[y+1][x+1]
				diagonal2 = all_lines[y+1][x-1] + all_lines[y-1][x+1]
				if "M" in diagonal1 and "S" in diagonal1 and "M" in diagonal2 and "S" in diagonal2:
					result += 1
		
	return result
		

we = """MMMSAMXAMMXSXXMAXAXXXSASAMXSAMXSAMXMAXSAMXSXSMSASAMXXXSASXSMSSSSXMAMSAXMSSXXASASMASXAXXMSMSSMMMSAMXMMMMXMXSXXMSXMAXXMAMXMSSXXMAMMMSMMMMMSXAX
XAAXAMXMASXMASMSSSXSASASAMXMXMAMXMAMXASXAXMAXAMXSAMSMMSAMASAAAAAMXMASMMMMMMSMMASAXAMMMSMSMAAAASMMXSMSMSASMAASXMASMMSXMASASASXMASXMASAAASMMAM
...
SSMSAXSMMMSSSMSMSMSXSSMSASMMMSMMMSXMAXXAMMSAMXXMMSXXSAMXMMSMMASMMSAMXXMMMXMMAMMMMSMMMXSAMSSSMSSSXAASXXXSXSAXAMXSSXMASXSMMSASMXSASXMASMXXMAMA"""
		
print (count_XMAS(extract_all_lines(we)))

Dla moich danych wejściowych to daje poprawne wyniki.
Dane wejściowe wkleja się do zmiennej we.

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.