Voisin julkaista koodaukseen liittyen sarjana tunnettujen pähkinöiden ratkaisuja. Suuntaan nämä julkaisut sellaisille henkilöille, joilla on ainakin koodauksen alkeet jo hallussa.

Ohjelmointikielistä tykkään koodailla perus Pythonilla. Olen joissakin palveluissani, kuten Rautatie.guru käyttänyt TypeScriptia / NextJS -alustaa. Mielestäni useimmat webbisivut, kuten tämä blogi, voi kuitenkin hyvin rakentaa ilmankin erillistä fronttikirjastoa.

Yksi suosikkisivuni koodauspähkinöille on Advent of Code.

AoC on julkaissut 2015 lähtien joka joulukuussa sarjan uusia pähkinöitä. Onneksi joulun lisäksi niitä voi ratkaista myös juhannuksena.

Jos haluaa opetella koodausta, voin lämpimästi suosittaa AoC:n tehtäviä. Ne ovat monipuolisia ja opettavaisia varsinkin data scientistin rooliin kasvamassa oleville, mutta suurelta osin tarjoavat haastetta myös senior-tason ohjelmoijille, joihin ehkä uskallan jo nykyään laskea myös itseni.

Tehtävät ovat välillä siis, varsinkin vuoden loppua kohti, hyvinkin kinkkisiä, mutta aivan alkupää on ainakin kokeneelle ohjelmoijalle vielä melko helppoa ja kivaa.

Tehtävistä saa maksimissaan 2 tähteä per tehtävä ja yhteensä tähtiä on useimpina vuosina maksimissaan 50. Aloitin itse joulukuussa 2025 tehtävien teon ja tällä hetkellä olen suorittanut noin 350 tähteä yhteensä vähän päälle 500:sta mahdollisesta, eli noin 2 tähteä tai yhden kokonaisen tehtävän päivässä.

Vuosilta 2015 ja 2016 itseltäni puuttuu vielä pari suoritusta, mutta 2017 olen kerännyt jo kaikki 50 tähteä. Aloitetaan siis ensimmäisestä tehtävästä.

Ratkaisu on tarkoituksella napakka. Luetaan syöte numerojonona deque-rakenteeseen, jolloin samaa rengasmaista dataa voi pyörittää ilman erillisiä indeksi-temppuja. Ensimmäisessä osassa verrataan aina seuraavaan numeroon, toisessa puolestaan puolikkaan kierroksen päässä olevaan numeroon.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# Day 1: Inverse Captcha

from collections import deque
from time import perf_counter
from u.colors import cyan, purple, red

def d1a(dq):
  # Compare each digit to the next digit in the circular sequence.
  c = 0
  for _ in range(len(dq)):
    cur = dq[0]
    dq.rotate(-1)
    if dq[0] == cur:
      c += cur
  return c

def d1b(dq):
  # Compare against the digit halfway around the ring.
  c, avg = 0, len(dq) // 2
  for _ in range(len(dq)):
    cur = dq[0]
    dq.rotate(-avg)
    if dq[0] == cur:
      c += cur
    dq.rotate(avg - 1)
  return c

t = perf_counter()
f = list(open('y2017/d1/i.txt').read())

# Convert the input once so both parts can reuse the same circular buffer shape.
dq = deque(list(map(int, f)), len(f))

cyan(f'\na) {d1a(dq)}')
purple(f'b) {d1b(dq)}')
red(f'T: {(perf_counter() - t) * 1000} ms\n')

Tässä tyylissä viehättää se, että ratkaisu pysyy aika lähellä itse tehtävän määritelmää. deque.rotate() tekee ring buffer -logiikasta selkeää, vaikka suoraviivainen indeksiratkaisukin toimisi oikein hyvin.

Värit ovat kivoja, joten käytän seuraavia aliaksia usein printtaamiseen:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
# u.colors aliases. Colors are nice, so I often use these when printing out.

def red(s): print("\033[91m{}\033[00m".format(s))

def green(s): print("\033[92m{}\033[00m".format(s))

def yellow(s): print("\033[93m{}\033[00m".format(s))

def magenta(s): print("\033[94m{}\033[00m".format(s))

def purple(s): print("\033[95m{}\033[00m".format(s))

def cyan(s): print("\033[96m{}\033[00m".format(s))

def lightgray(s): print("\033[97m{}\033[00m".format(s))

def black(s): print("\033[90m{}\033[00m".format(s))

def bold(s): print("\033[1m{}\033[00m".format(s))

def underline(s): print("\033[4m{}\033[00m".format(s))

Seuraavassa AoC-postauksessa voidaan ottaa vähän vaikeampi tehtävä ja samalla vertailla, missä kohtaa suosin lyhyttä kilpailuhenkistä koodia ja missä kohtaa taas vähän tuotantomaisempaa selkeyttä.