Min hjärna vann över datorn

Squeeds Julkalender | 2021-12-15 | Lena Stridsberg
Jag hade tänkt skriva om något helt annat än vad som kommer i det här blogginlägget egentligen. Första utkastet handlade om hur jag löste några av mysterierna som vi möts av i Advent of Code och syftet var tänkt att ge mig själv och er en inblick i hur stökig, osmart och "ful" den där första versionen av kod kan vara. Men när jag kom fram till dag 8 så hände någonting. Jag fastnade och hade en jädra resa i mitt inre för att försöka lösa problemet jag stod inför. Och när jag löste problemet flera dagar senare så kände jag att jag måste få berätta om det här för någon - och denna någon blir exakt just DIG.

Vad är Advent of Code?

Det känns rimligt att börja inlägget med att svara på det här, de flesta av er kanske har hört talas om det här och personligen så är det här en höjdpunkt på året för det är enda gången jag faktiskt unnar mig att koda på fritiden bara för nöjes skull.
Advent of Code är en julkalender för nördar. Varje dag får vi en ny lucka - ett nytt problem att lösa - och ofta handlar alla problem om att vi ska nå slutmålet som är att rädda julen. Alla dagar innehåller två problemställningar men vi får bara access till del två om vi första lyckas klura ut den första delen. Så summa summarum så är Advent of Code en julkalender med 50 spännande problemställningar för att utmana hjärnan, psyket, din uthållighet och samtidigt bredda sina kunskaper. OTROLIGT roligt!
Här kan ni läsa mer om vad det är. 

Så.... Dag 8?

De första dagarna flöt på relativt smärtfritt för mig, det vara både roligt och stimulerande att få koda vid sidan av mitt arbete.
När jag kom fram till dag 8 så hade jag löst både delarna av dag 1, 2 och 3 samt den första delen av dag 6 och 7.
Beskrivningen av dag 8 gav mig nästan pirr i magen, det kändes som det ultimata uppgiften för en person som mig att lösa, det kändes som att den var skriven för mig. Hybrisen var ett faktum. Jag kände att det skulle bli så jädra lätt att jag tillochmed bad min pojkvän som satt mitt emot att slå vad med mig om att jag skulle klara av första delen under 30 minuter.
Sagt och gjort, jag slängde på mig lurarna och försvann in i ett transliknande tillstånd där jag och datorn var i symbios, allting var redan färdigt och förberett i hjärnan och det enda som behövdes nu var att översätta tankarna till kod.

Keane strömmade på hög volym i lurarna och fingrarna brann på tangentbordet. 23 minuter gick och jag skickade iväg mitt lösningsförslag och möts av den finaste texten du kan tänka dig.
"Congratulations! That's the right answer."

När du har suttit med ett sånt självförtroende som jag hade så är det fruktansvärt tillfredställande att få bekräftat att mina teorier fortfarande höll i praktiken. Det var del 1.
Del 2 däremot...... Min självsäkra eld brann långsamt ner till en hög av aska och jag funderade på om jag kanske borde byta yrke några gånger. Jag funderade på om min hjärna var trasig. För att ge er lite kontext så kan ni läsa beskrivningen av del 1 här men det är mycket text så jag ska försöka sammanfatta superkort.

Dag 8 - del 1

 acedgfb cdfbe gcdfa fbcad dab cefabd cdfgeb eafb cagedb ab | cdfeb fcadb cdfeb cdbaf

  1. Låtsas att det här är en display där varje bokstav motsvarar en viss sektion av displayen.
  2. För att skapa siffror så lyser vi upp olika sektioner, som exempel så behöver vi tända alla sektioner för att skapa en 8.
    För att skapa en 1:a så behöver vi lysa upp två specifika sektioner (både sektionerna till höger som i exempel ovan är märkt med c och f).
  3. Vi har tillgång till en lista med rader som ser ut som den alldeles ovanför här och ingen är lik den andre.
  4. Vårt uppdrag i del 1 är att räkna hur många av de unika sektionskombinationerna som finns i en lång lista av sådana rader.
  5. Unika kombinationer är siffran 1 (som en ensam om att bara innehålla två upplysta sektioner - c & f),
    siffran 7 (som är ensam om att bara innehålla tre upplysta sektioner - c, f & a),
    siffran 4 (som är ensam om att innehålla fyra upplysta sektioner - b, c, d & f) och
    siffran 8 (som är den enda siffran som behöver alla sektioner upplysta - a, b, c, d, e, f & g).

Hänger ni med ungefär? BRA! Då kör vi vidare.

 acedgfb cdfbe gcdfa fbcad dab cefabd cdfgeb eafb cagedb ab | cdfeb fcadb cdfeb cdbaf

Om vi tittar på den här raden igen så kan jag direkt se att ab  är en 1:a.
Jag kan också se att dab är en 7:a.
Med samma tankesätt så kan jag också förstå att eafb är en 4:a och acedgfb är en 8:a.
Tänk er nu en lång rad av såna här rader som har helt unika kombinationer av bokstäver och mitt uppdrag var att ta reda på hur många av dessa unika kombinationer som bildar antingen en 1:a, 7:a, 4:a och 8:a som finns efter |-tecknet.
Såhär såg min lösning ut:

Nemas problemas. Det är inte snyggt men det fungerade och det räckte fint för mig eftersom jag bara ville lyckas inom 30 minuter. Nu släpper vi del 1 och går vidare till del 2 som långsamt knäckte min själ i flera dagar.

Mardrömmen börjar i del 2

I del två får vi nästan ingen information alls. Det står egentligen bara att vi nu kan lista ut vilka de andra kombinationerna är också och att vi därför ska avkoda alla bokstavskombinationer till siffror och efter det avkoda de bokstavskombinationer som är presenterade efter |-tecknet och sen ska vi slå ihop dem med varandra. 
Vänta VAAAAAA. HUR i HELA?!Jag läste den fattiga beskrivningen hur många gånger som helst och var helt perplex över det faktum att jag inte riktigt visste var jag skulle börja. Så jag tog fram ett papper och en penna och började rita eftersom det alltid är min go-to när jag har fastnat. Nu får ni hålla i hatten för det här blir en liten resa i en djungel av tankevurpor, misstag, frustration och ren och skär ilska ärligt talat.

Steg 1 - visualisera en display

  1. Måla ut en förenklad display (ser att jag glömt 9:an här men låt oss ignorera det).
  2. Fyll i de unika bokstavskombinationerna som jag redan känner till.
  3. Skriv i de möjliga bokstäverna under varje sektion i displayen där är den enda som känner till i dagsläget.

Steg 2 - %&#&!!@

Jag satt i flera timmar första kvällen. Kunde inte somna ordentligt för jag var helt förlorad i att jag inte fick till logiken ens i hjärnan. Hur ska man då kunna skriva koden? Jag var helt blank. Det kändes omöjligt att lösa det här. Dagen efter var precis likadan. Jag gjorde ett försök att skriva koden först och hoppas att jag kunde komma närmare en lösning genom att ta ett steg åt gången. Men alla steg jag skrev ner var fel, ingenting fungerade och jag var inte en millimeter närmare en lösning trots att jag spenderat mer än 8h framför datorn för att lösa det här. Jag funderade på att bara strunta i det här och gå vidare till nästa dag, bara släppa problemet och acceptera min förlust. 

Men jag är inte en person som släpper sånt här, inte ens i mina mest uppgivna ögonblick kände jag helt och hållet att jag kunde släppa det så jag fortsatte tänka. Jag raderade 200 rader kod och började om. Och den här gången tänkte jag ge mig f*n på att lösa det i hjärnan först och sen steg för steg lösa problemet i kod. 

Steg 3 - Andas. Måla. Skriv kod.

Sagt och gjort. Jag hämtade nya papper, skrev och tänkte. Insåg att jag behöver någon typ av ordning i röran så jag bestämde mig för att hantera bokstavskombinationerna i längdordning. Efter de unika kombinationerna började jag därför hantera strängarna som innehöll 5 tecken. I raden i exemplet är dessa cdfbe, gcdfa och fbcad. Dessa kommer vara antingen en 2:a, en 3:a eller en 5:a eftersom det är de enda siffrorna som använder 5 sektioner för att skapa en siffra.

Nu började jag matcha min kod med min hjärna så jag skapade två objekt; SegmentDisplay och Display.
SegmentDisplay innehåller ett Enum för varje siffra (0-9) som jag behöver kartlägga.
Mitt Enum fick heta Numbers och det håller en int (värdet på siffran) samt en sträng (bokstavskombinationen).

Display fick hålla ansvaret för alla potentiella möjligheter som displayen kunde ha.
Om vi jämför min bild - aka inblicken i min hjärna - med koden för Display:

Här kommer possibleTop innehålla ett värde: d.
possibleUpperRight kommer innehålla en lista med två värden: a och b.
osv osv osv osv.

Genom att göra såhär så kunde jag matcha koden med allt min hjärna gjorde på pappret jag ritade på tidigare och det hjälpte mig att ha full kontroll över min kod så länge min hjärna ville samarbeta med mig. Det blev som att lösa ett sudoku för mig vid den här tidpunkten, det finns en logik här som är fullt rimlig men jag hade inte nått hela vägen fram riktigt än. Men min kod gjorde exakt vad min hjärna gjorde och det kändes riktigt, riktigt lovande.

Eftersom jag hade arrangerat min lista av strängar i storleksordning här så var mina objekt populerade exakt likadant som det såg ut på mitt papper när jag kom fram till den första strängen som innehöll 5 tecken. 
Du är smart, du klarar det här, nu är du på rätt väg Lena!Det kändes lovande inför framtiden nu men jag visste inte här att jag skulle kämpa i flera timmar till för att hålla jämna steg mellan hjärna och dator. Det var kritiskt för mig att datorn gjorde exakt samma sak som mitt huvud gjorde. Det var inte längre en fråga om om jag skulle klara den här uppgiften, det var en fråga om att tvinga in det som skedde i min hjärna till datorn. En ren översättning. Vi skulle hitta tillbaka till ett samarbete efter så många timmar av krig. Det var exakt här jag bestämde mig för att blogginlägget skulle handla om det här; att skriva kod som är precis likadan som mina tankar. 

Steg 4 - Är du en 2:a, 3:a eller 5:a??

Påminner om den här strängen som alltså är den jag försöker avkoda nu:

 acedgfb cdfbe gcdfa fbcad dab cefabd cdfgeb eafb cagedb ab | cdfeb fcadb cdfeb cdbaf

1. Leta mönster. Finns det något sätt att kunna särskilja de här siffrorna från varandra och på det sättet kunna utesluta att en viss kombination av bokstäver bara kan vara en möjlig siffra? Svaret är såklart ja och utifrån mitt Display-objekt så kunde jag se att 5:an är den enda av de här strängarna som innehåller båda värden från possibleUpperLeft eller possibleMiddle (dessa kommer alltid se likadana ut i det här steget i koden - ALLTID). I bildexemplet ovanför så kan vi ju se den enda strängen med 5 tecken som innehåller både b och är just en 5:a. Hjärnaktivitet förvandlades till kod och jag påminner återigen om strängen jag försöker avkryptera:

 acedgfb cdfbe gcdfa fbcad dab cefabd cdfgeb eafb cagedb ab | cdfeb fcadb cdfeb cdbaf

Om strängen är 5 tecken lång och innehåller både 'e' och 'f' så vet jag att det är en 5:a.


5 = cdfbe

2. Nu behövde jag särskilja de andra två. Vad är unikt med de andra två strängarna? Nästa mönster jag upptäckte var att 2:an är den enda av strängarna som innehåller båda värden från possibleLowerLeft eller possibleBottom (återigen kommer båda dessa värden vara populerade med exakt samma värden så det spelar ingen roll vilket av dem jag jämför mot här). Fler rader kod uppstod.
Om strängen är 5 tecken lång och innehåller både 'c' och 'g' så vet jag att det är en 2:a.

2 = gcdfa

3. Och vad är då unikt med 3:an? Jo, det är den enda kombinationen av strängar med 5 tecken som kommer innehålla båda värden från possibleUpperRight eller possibleLowerRight (eller hela 1:an if you will).
Om strängen är 5 tecken lång och innehåller både 'a' och 'b' så vet jag att det är en 3:a.


3 = fbcad

Vid den här tidpunkten har jag lyckats avkoda alla bokstavskombinationer som innehåller 2, 3, 4, 5 och 7 tecken i sina strängar och det enda som kvarstår är att avkoda de strängar som innehåller 6 tecken. Dessa strängar kommer att symbolisera siffrorna 0, 6 & 9. LET'S GO!

Steg 5 - är du en 0:a, 6:a eller 9:a??

 acedgfb cdfbe gcdfa fbcad dab cefabd cdfgeb eafb cagedb ab | cdfeb fcadb cdfeb cdbaf

Nu gjorde min kod exakt samma sak som min hjärna gjorde och det kändes mäktigt även om det säkerligen finns bättre sätt att lösa det här på. Men nu handlade det ju inte om att skriva få och snygga rader kod. Det handlade bara om att visa att min hjärna kunde översätta det här.

1. Jag började återigen studera alla siffror och inser först att jag kan se vilken som är 6:an för det är den enda strängen med 6 tecken som ska innehålla värdena från både antingen possibleBottom eller possibleLowerLeft samt båda värden från antingen possibleMiddle eller possibleUpperLeft. Som tidigare nämnt så spelar det ingen roll vilken av dem jag jämför mot för de kommer vara populerade med samma värden. Koden skrevs.
Om strängen är 6 tecken lång och innehåller både 'c', 'g', 'e' och 'f' så vet jag att det är en 6:a.


6 = cdfgeb

2. Nästa siffra som jag hittade ett mönster i som helt och hållet kunde utesluta de andra två strängarna med 6 tecken var 0:an, det är den enda av dessa strängar som innehåller båda värden från både antingen possibleUpperRight eller possibleLowerRight samt både värden från antingen possibleBottom eller possibleLowerLeft. Ni förstår logiken vid det här laget!
Om strängen är 6 tecken lång och innehåller både 'a', 'b', 'c' och 'g' så vet jag att det är en 0:a.


0 = cagedb

3. Nu finns det bara en sträng kvar som innehåller 6 tecken och genom den otroligt avancerade och komplexa metoden som kallas för uteslutningsmetoden så kan jag med full säkerhet säga att om koden går hit så är strängen med 100% säkerhet en 9:a. Koden blir därför den mest komplexa och svårlästa hittills.
Om strängen inte är uppfångad i någon av de andra scenariona så vet jag att det är en 9:a. Kalla mig Lena Säkerhet Stridsberg hädanefter.


9 = cefabd

Steg 6 - Räkna ihop allting

Hur ser allting ut nu då? Här har jag alltså avkodat alla bokstäver med hjälp av att utesluta alla andra möjligheter till vad de betyder och resultatet stämmer. GÖTT ÄR DET.

 

Nu när jag har skapat den här fina avkrypteringsmetoden så tänkte jag att det bara var att kasta in alla värden i en ny metod som skulle få ta hand om mappningen av strängar till siffror. Men var det så lätt? Va? Skulle det vara SÅ lätt??? NEJ. Vad jag möts av när jag ska försöka avkoda allting är att bokstäverna i strängarna är blandade lite hur som helst medan jag försökte matcha på exakta strängar.
Så det här satt jag med några timmar till innan jag insåg att jag återigen försökte koda fram en lösning utan att tänka först. Så jag gick tillbaka in i min hjärna, tillbaka till mitt papper, tillbaka till att skriva och måla. Vad som uppenbarade sig på pappret var att jag behövde en ordning på strängarna och den här gången ordnade jag dem alfabetiskt - både mina strängar och strängarna jag skulle avkoda.
Det är därför jag populerar segmentDisplay med en metod som heter sortString. Det är i den alla strängar sorteras i alfabetisk ordning.
Då kunde jag garantera att de såg exakt likadana ut.
Den här gången gick det mycket bättre och jag avkodade rader på löpande band genom att matcha dem mot mina enum som var populerade med rätt strängar. Jag svettades när jag körde den riktiga inputen. Men den som väntar på något gott väntar.... tydligen i över 14h som jag la ner på det här? För tillslut kom den där underbara raden.

Congratulations! That's the right answer.

Avslutning och summering

Så fort jag fick bekräftat att jag klarade dag 8 så stängde jag ner locket på datorn, svor några gånger och la mig i soffan som om ingenting har hänt, men inombords bubblade det. Det bubblade av känslan som är min största drivkraft i det här yrket;
Success.
Grönt bygge.
Att övervinna mina tvivel på mig själv.
Att tänka tillsammans med tekniken.
Att faktiskt prata med min dator.
Att lära den någonting.

Jag hade gärna visat hela min kod här men det är egentligen ett annat inlägg så inom en snar framtid kommer jag istället dela med mig av mitt githubrepo där koden kommer finnas i sin befintliga form, jag har bestämt mig för att inte röra den mer nu. Inte snygga till någonting för sakens skull. Inte korta ner onödiga kodrader. Inte använda moderna syntaxer eller ramverk för att förenkla saker. Ingen magi någonstans.

Det här är mitt hantverk.Det här blev en perfekt avbild av mina tankar.
Jag är så stolt över att jag inte gav upp.
Till dig som inte heller gav upp och faktiskt läste hela vägen hit; god jul och gott nytt år!