Advent of Code Macerası – Gün 5

Herkese merhabalar! Bugün 5. gün ile birlikteyiz. Hayli zorlayıcı olan bu bölümde konumuz ‘İkmal Yığınları‘ hakkında. Benim için uzun vakit olan bu bulmacayı elimden geldiğince detaylı açıklamaya çalışacağım. Hikayemiz şu şekilde başlıyor:

Birinci Bölüm:

Sefer, son malzemeler gemilerden indirilir indirilmez yola çıkabilir. Malzemeler işaretli sandıklarda depolanır, ancak ihtiyaç duyulan malzemeler diğer birçok sandığın altına gömülü olduğu için sandıkların yeniden düzenlenmesi gerekir.

Gemide kasaları istifler arasında taşıyabilen dev bir kargo vinci vardır. Hiçbir kasanın ezilmemesini veya devrilmemesini sağlamak için vinç operatörü dikkatle planlanmış bir dizi adımla kasaları yeniden düzenleyecektir. Kasalar yeniden düzenlendikten sonra, istenen kasalar her istifin en üstünde yer alacaktır.

Elfler bu hassas işlem sırasında vinç operatörünü rahatsız etmek istemezler, ancak ona hangi sandığın nereye gideceğini sormayı unutmuşlardır ve gemiye binebilmek için sandıkları bir an önce boşaltmaya hazır olmak isterler.

https://adventofcode.com/2022/day/5

Bununla birlikte, başlangıç sandık yığınlarının ve yeniden düzenleme prosedürünün (bulmaca girdiniz) bir çizimi vardır. Örneğin:

    [D]    
[N] [C]    
[Z] [M] [P]
 1   2   3 

move 1 from 2 to 1
move 3 from 1 to 3
move 2 from 2 to 1
move 1 from 1 to 2

Bu örnekte, üç sandık yığını vardır. Yığın 1 iki kasa içerir: Z kasası en altta ve N kasası en üsttedir. Yığın 2 üç kasa içerir; bunlar aşağıdan yukarıya doğru M, C ve D kasalarıdır. Son olarak, yığın 3 tek bir kasa içerir, P.

Ardından, yeniden düzenleme prosedürü verilmektedir. Prosedürün her adımında, bir miktar sandık bir istiften farklı bir istifin üzerine taşınır. Yukarıdaki yeniden düzenleme prosedürünün ilk adımında, bir sandık 2. yığından 1. yığına taşınır ve bu konfigürasyon elde edilir:

[D]        
[N] [C]    
[Z] [M] [P]
 1   2   3 

İkinci adımda, üç sandık yığın 1’den yığın 3’e taşınır. Kasalar teker teker taşınır, böylece taşınacak ilk kasa (D) ikinci ve üçüncü kasaların altında kalır:

        [Z]
        [N]
    [C] [D]
    [M] [P]
 1   2   3

Ardından, her iki sandık da yığın 2’den yığın 1’e taşınır. Yine, kasalar teker teker taşındığı için C kasası M kasasının altında kalır:

        [Z]
        [N]
[M]     [D]
[C]     [P]
 1   2   3

Son olarak, bir sandık yığın 1’den yığın 2’ye taşınır:

        [Z]
        [N]
        [D]
[C] [M] [P]
 1   2   3

Elflerin her bir yığının en üstünde hangi sandığın olacağını bilmeleri yeterlidir; bu örnekte, en üstteki sandıklar yığın 1’deki C, yığın 2’deki M ve yığın 3’teki Z’dir, bu nedenle bunları birleştirmeli ve Elflere CMZ mesajını vermelisiniz.

Yeniden düzenleme prosedürü tamamlandıktan sonra, hangi sandık her bir istifin en üstünde yer alır?

Ben bu problemi çözmek için öncelikle bana verilen inputu python scriptimin bulunduğu klasörde bir txt dosya olarak kaydettim ve aşağıdaki kodu yazdım.

N=9

# Opening the file, reading the lines of the file, removing the newline character from each line, and
# creating a list of lists.
with open('stack.txt', 'r', encoding="utf-8") as f: # Open the file
    stack = f.readlines() # Read the lines of the file
    stack = [line.strip() for line in stack] # Remove the newline character from each line
    S = [[] for _ in range(N)]
    
    # To save each colums of a stack to a list
    for i in range(N-1): # N-1 because the last line is empty
        line = stack[i] # Get the line
        crates = line[1::4] # Get the crates
        for s in range(len(crates)): # For each crate
            if crates[s] != " ": # If the crate is not empty
                S[s].append(crates[s]) # Add the crate to the list

    # Reverse all stacks
    stacks = [stack[::-1] for stack in S] 

    # Move crates one by one
    for line in stack[N+1:]: # N+1 because the first N lines are the stacks
        tokens = line.split(" ") # Split the line
        n, src, dst = map(int, [tokens[1], tokens[3], tokens[5]]) # Get the number of crates, the source, and the destination
        src -= 1 # Subtract 1 because the stacks are 0-indexed
        dst -= 1 # Subtract 1 because the stacks are 0-indexed

        for _ in range(n): # For each crate
            pop = stacks[src].pop() # Pop the crate from the source
            stacks[dst].append(pop) # Add the crate to the destination
    
    tops = [stack[-1] for stack in stacks] # Get the top of each stack
    print("".join(tops)) # Print the top of each stack

Bu kodu tek tek incelemek gerekirse:

İlk olarak, dosya açılır ve satırları okunur:

with open('stack.txt', 'r', encoding="utf-8") as f: # Open the file
    stack = f.readlines() # Read the lines of the file

Daha sonra, okunan satırların sonundaki yeni satır karakterleri kaldırılır ve bu satırlar bir diziye kaydedilir:

stack = [line.strip() for line in stack] # Remove the newline character from each line

Daha sonra, ‘N’ değişkenine göre bir dizi oluşturulur ve bu dizinin boyutu, dosyada verilen yığıt yapısının sütun sayısına eşit olacaktır.

S = [[] for _ in range(N)]

Daha sonra, yığıt yapısının her sütunu bir diziye kaydedilir:

for i in range(N-1):
    line = stack[i]
    crates = line[1::4]
    for s in range(len(crates)):
        if crates[s] != " ":
            S[s].append(crates[s])

Bu kod parçacığı, her bir satırdaki ilk karakteri atlar ve daha sonra her 4. karakteri alarak bir dizi oluşturur. Bu dizinin her elemanı, yığıt yapısının bir sütununu temsil eder. Eğer bir eleman boşluk ise, bu eleman diziye eklenmez.

Daha sonra, oluşturulan diziler ters çevrilir:

stacks = [stack[::-1] for stack i S]

Bu kod parçacığı, her bir dizinin elemanlarını ters çevirerek yeni bir dizi oluşturur.

Sonra, her bir satır için belirtilen kaynak ve hedef sütunlar arasında kutular taşınır:

for line in stack[N+1:]:
    tokens = line.split(" ")
    n, src, dst = map(int, [tokens[1], tokens[3], tokens[5]])
    src -= 1
    dst -= 1

    for _ in range(n):
        pop = stacks[src].pop()
        stacks[dst].append(pop)

Öncelikle, dosyada verilen satırlar arasından yığıt yapısından sonraki satırlar seçilir.

Bu satırlar, yığıt yapısından sonraki komut satırlarını temsil eder. Bu satırlar “n src dst” formatında olacaktır, burada ‘n’ taşınacak kutu sayısı, ‘src’ kaynak sütun numarası ve ‘dst’ hedef sütun numarasıdır. Bu komut satırları parçalanarak belirtilen değişkenlere atanır.

her bir satırı boşluklara göre parçalayarak bir dizi oluşturur. Daha sonra, bu dizinin 2., 4. ve 6. elemanlarını alarak bu değişkenlere atar. Bu değişkenler sayısal değerler olacağı için, ‘map()’ fonksiyonu kullanılarak bu değişkenler sayısal değerlere dönüştürülür.

Daha sonra, kaynak ve hedef sütun numaraları 1 azaltılır. Python dizileri 0 ile başladığı için, bu değişkenler 1 azaltılır. ‘n’ değişkenine göre belirtilen sayıda döngü oluşturur. Her bir döngüde, kaynak sütunun en üst kısmındaki kutu çıkarılır ve hedef sütunun en altına eklenir. Bu işlem, belirtilen sayıda yapılır ve bu sayede belirtilen kaynak ve hedef sütunlar arasında değişiklikler meydana gelir.

tops = [stack[-1] for stack in stacks]
print("".join(tops))

Yukarıdaki kod parçacığı sonraki adımda, yapılan işlemler sonucunda elde edilen yığıtların en üst kısımları birleştirilerek yazdırılır.

Bu şekilde ilk problemi çözmüş olduk. Hadi gelin ikinci bölüme bakalım:

İkinci Bölüm:

Bu bölümde hikaye şu şekilde devam ediyor.

Vinç operatörünün sandıkları ustalıkla yeniden düzenlemesini izlerken, sürecin sizin öngörünüze uymadığını fark ediyorsunuz.

Vincin yan tarafındaki yazıyı biraz çamur kaplamıştı ve siz onu çabucak sildiniz. Vinç bir CrateMover 9000 değil – bir CrateMover 9001.

CrateMover 9001 birçok yeni ve heyecan verici özelliğiyle dikkat çekiyor: klima, deri koltuklar, ekstra bardak tutucu ve aynı anda birden fazla kasayı alıp taşıyabilme.

Yine yukarıdaki örnek göz önüne alındığında, kasalar aynı konfigürasyonda başlar:

    [D]    
[N] [C]    
[Z] [M] [P]
 1   2   3 

Tek bir sandığı yığın 2’den yığın 1’e taşımak daha önce olduğu gibi davranır:

[D]        
[N] [C]    
[Z] [M] [P]
 1   2   3 

Ancak, üç sandığı yığın 1’den yığın 3’e taşıma eylemi, taşınan üç sandığın aynı sırada kalması ve bu yeni konfigürasyonun ortaya çıkması anlamına gelir:

        [D]
        [N]
    [C] [Z]
    [M] [P]
 1   2   3

Ardından, her iki sandık da yığın 2’den yığın 1’e taşındığında, sıralarını da korurlar:

        [D]
        [N]
[C]     [Z]
[M]     [P]
 1   2   3

Son olarak, tek bir sandık hala yığın 1’den yığın 2’ye taşınır, ancak şimdi taşınan sandık C’dir:

        [D]
        [N]
        [Z]
[M] [C] [P]
 1   2   3

Bu örnekte, CrateMover 9001 kasaları tamamen farklı bir sıraya koymuştur: MCD.

Yeniden düzenleme işlemi bitmeden önce simülasyonunuzu güncelleyin, böylece Elfler son malzemeleri boşaltmaya hazır olmak için nerede durmaları gerektiğini bilsinler. Yeniden düzenleme işlemi tamamlandıktan sonra, hangi sandık her bir yığının en üstünde yer alır?

Bu problemi çözmek için uyguladığım python kodu aşağıdaki gibidir.

N=9

# Opening the file, reading the lines of the file, removing the newline character from each line, and
# creating a list of lists.
with open('stack.txt', 'r', encoding="utf-8") as f: # Open the file
    stack = f.readlines() # Read the lines of the file
    stack = [line.strip() for line in stack] # Remove the newline character from each line
    S = [[] for _ in range(N)]
    
    # To save each colums of a stack to a list
    for i in range(N-1): # N-1 because the last line is empty
        line = stack[i] # Get the line 
        crates = line[1::4] # Get the crates 
        for s in range(len(crates)): # For each crate
            if crates[s] != " ": # If the crate is not empty
                S[s].append(crates[s]) # Add the crate to the list

    # Reverse all stacks 
    stacks = [stack[::-1] for stack in S]

    # Move crates all at the same time
    for line in stack[N+1:]: # N+1 because the first N lines are the stacks 
        tokens = line.split(" ") # Split the line 
        n, src, dst = map(int, [tokens[1], tokens[3], tokens[5]]) # Get the number of crates, the source, and the destination
        src -= 1 # Subtract 1 because the stacks are 0-indexed
        dst -= 1 # Subtract 1 because the stacks are 0-indexed

        stacks[dst].extend(stacks[src][-n:]) # Add the crates to the destination
        stacks[src] = stacks[src][:-n] # Remove the crates from the source


    tops = [stack[-1] for stack in stacks] # Get the top of each stack
    print("".join(tops)) # Print the top of each stack

Gördüğünüz üzere kod neredeyse birinci bölümle tamamen aynı ufak farklılık sadece kodun aşağıdaki kımındadır:

stacks[dst].extend(stacks[src][-n:]) # Add the crates to the destination
stacks[src] = stacks[src][:-n] # Remove the crates from the source

Böylelikle yığınları tek tek taşımak yerine toplu alıp taşıyabiliyoruz.


AoC’un beşinci günü de bu kadardı. Vakit ayırıp okuduğunuz için teşekkür ederim. Eğer yazıyı beğendiyseniz, kodlamaya meraklı arkadaşlarınızla paylaşmayı unutmayın 


Herhangi bir sorunuz olursa veya benimle iletişim kurmak isterseniz tüm sosyal medya hesaplarıma bu linke tıklayarak ulaşabilirsiniz. Ayrıca tüm çözümlerimi görebilmeniz için bir github reposu açtım. Bu repoya da bu linkten ulaşabilirsiniz.

Yorum bırakın