Imamo krožno ploščo s polji, oštevilčenimi od 0 do 5 (ker računalnikarji štejemo od 0 in ne od 1). Začnemo na polju 0 in mečemo kocko. Recimo, da vržemo najprej 2 (smo na polju 2), nato 3 (smo na polju 5), nato 4 (smo na polju 3, ker je plošča - za razliko od Zemlje - okrogla), nato vržemo 2 (spet smo na polju 5) ...

## Obvezna naloga

Napišite program, ki simulira takšno igro in izpiše število metov, po katerem pridemo stotič na polje 5.

Kocko bomo simulirali takole:

    from random import *
    seed(8)

Po tem bo vsak klic funkcije `randint(1, 6)` vrnil naključno število med 1 in 6.

Program

    print(randint(1, 6))
    print(randint(1, 6))
    print(randint(1, 6))
    print(randint(1, 6))

izpiše (slučajno ravno)

    2
    3
    4
    2

Ker bo kocka padala vedno enako (zaradi `seed(8)`, ki nastavi generator naključnih števil na neko začetno stanje; namesto 8 bi lahko uporabili poljubno drugo število in potem dobili drugačno, a spet vedno enako, zaporedje naključnih števil) , lahko izdam, kaj mora izpisati program: 626.

### Rešitev

Pojdimo lepo počasi. Najprej samo izpišimo deset metov.

    from random import *
    seed(8)

    metov = 0
    while metov < 10:
        met = randint(1, 6)
        print(met)
        metov += 1

    2
    3
    4
    2
    2
    6
    1
    1
    2
    2

Zdaj pa izpisujmo še pozicijo figure. Ta se po vsakem metu poveča za toliko, kolikor smo vrgli.

    from random import *
    seed(8)

    metov = 0
    pozicija = 0

    while metov < 10:
        met = randint(1, 6)
        pozicija = pozicija + met
        print(met, pozicija)
        metov += 1

    2 2
    3 5
    4 9
    2 11
    2 13
    6 19
    1 20
    1 21
    2 23
    2 25

To očitno ne bo v redu: največja številka polja je 5, nato pridemo okrog, torej moramo zmanjšati pozicijo za 6.

    from random import *
    seed(8)

    metov = 0
    pozicija = 0

    while metov < 10:
        met = randint(1, 6)
        pozicija = pozicija + met
        if pozicija > 5:
            pozicija -= 6
        print(met, pozicija)
        metov += 1

    2 2
    3 5
    4 3
    2 5
    2 1
    6 1
    1 2
    1 3
    2 5
    2 1

Isto se da doseči tudi na preprostejši način: uporabimo ostanek po deljenju.

    from random import *
    seed(8)

    metov = 0
    pozicija = 0

    while metov < 10:
        met = randint(1, 6)
        pozicija = (pozicija + met) % 6
        print(met, pozicija)
        metov += 1

    2 2
    3 5
    4 3
    2 5
    2 1
    6 1
    1 2
    1 3
    2 5
    2 1

Če je `pozicija + met` več kot `5`, bo imel `% 6` enak učinek kot odštevanje `6`. Če je `pozicija + met` manjša od `6`, pa `% 6` ne bo storil ničesar.

Zdaj pa moramo šteti, kolikokrat smo se znašli na polju 5. Za to potrebujemo še eno spremenljivko.

    from random import *
    seed(8)

    metov = 0
    pozicija = 0
    na_5 = 0

    while metov < 10:
        met = randint(1, 6)
        pozicija = (pozicija + met) % 6
        if pozicija == 5:
            na_5 += 1
        print(met, pozicija, na_5)
        metov += 1

    2 2 0
    3 5 1
    4 3 1
    2 5 2
    2 1 2
    6 1 2
    1 2 2
    1 3 2
    2 5 3
    2 1 3

Izpis je videti pravilen: tretja številka je najprej 0, ko se prvič znajdemo na polju 5, se spremeni v 1, ko drugič, na 2, in potem ostane 2, dokler ne pridemo tretjič na polje 5.

Zdaj, ko vidimo, da to deluje, popravimo pogoj pri zanki `while`: namesto da izvede deset metov, mora teči toliko časa, dokler `na_5` ne doseže 100. V zanki ne bomo več ničesar izpisovali, pač pa po zanki izpišemo število metov.

    from random import *
    seed(8)

    metov = 0
    pozicija = 0
    na_5 = 0

    while na_5 < 100:
        met = randint(1, 6)
        pozicija = (pozicija + met) % 6
        if pozicija == 5:
            na_5 += 1
        metov += 1

    print(metov)

    690

In to je rešitev naše naloge.

#### Tipične napake

Tipični trdovratnejši napaki sta bili dve.

Nekateri niso znali pravilno postaviti pogoja pri `while`. Videl sem pogoje, ki so bili resnični vedno, na primer `while 0 <= pozicija <= 6` in pogoje, ki niso bili resnični v začetku, na primer `while na_5 == 100`. V prvem primeru se zanka nikoli ne ustavi, v drugem primeru se nikoli ne izvede. V prvem primeru so bili pogoji pogosto zelo čudni, kar morda namiguje na to, da nekateri niso vedeli, kaj bi napisali in so napisali ... kar nekaj, ker pogoj pač mora biti. Ta strategija programiranja navadno ni uspešna. :) Drugi primer kaže na manj nevarno napako, na nerazumevanje pomena pogoja. Zanka se izvaja, dokler je pogoj resničen in ne do takrat, ko se pogoj uresniči.

Druga pogosta napaka je bila tale. Študent namesto `pozicija = (pozicija + met) % 6` napiše pogoj in odštevanje, kot smo ga tudi v tej rešitve napisali na začetku, torej `pozicija = pozicija + met` in na to `if pozicija > 5: pozicija -= 6`. S tem ni še nič narobe. Pač pa so naredili napako v nadaljevanju.

    from random import *
    seed(8)

    metov = 0
    pozicija = 0
    na_5 = 0

    while na_5 < 100:
        met = randint(1, 6)
        pozicija += met
        if pozicija == 5:
            na_5 += 1
        if pozicija > 5:
            pozicija -= 6
        metov += 1

    print(metov)

    791

Ta program izpiše preveliko število potrebnih metov, 791. Študentom, ki so naredili to napako ter niso znali naprej in so mi pisali, sem svetoval, da pred pogoj `if pozicija == 5` dodajo `print(pozicija)`. V izpisu so se pojavljale številke večje od 5, kar jim je, upam, dalo razmisliti tole: do napake pride v primeru, da smo na polju 5 in vržemo 6. V tem primeru je `pozicija` enaka `11`. To je v resnici `5`, vendar ta program odšteje `6` šele po tem, ko preveri, ali je pozicija enaka 5.

Vrstni red pogojev je potrebno zamenjati: najprej popravimo pozicijo in šele nato primerjamo, ali je enaka 5.

    from random import *
    seed(8)

    metov = 0
    pozicija = 0
    na_5 = 0

    while na_5 < 100:
        met = randint(1, 6)
        pozicija += met
        if pozicija > 5:
            pozicija -= 6
        if pozicija == 5:
            na_5 += 1
        metov += 1

    print(metov)

    690

## Dodatna naloga

Funkcijo `seed` lahko v programu pokličemo večkrat in tako nastavljamo generator naključnih števil na različna začetna stanja.

Napiši program, ki preveri vse možne začetne nastavitve od `0` do `999` in odigra igro. V vsaki igri bomo potrebovali različno število metov do stotega prihoda na polje 5. Program naj izpiše, kakšno je bilo minimalno število metov, potrebnih v eni od teh 1000 iger.

### Rešitev

Celoten program, razen `import`-a moramo zapreti v novo zanko, ki teče prek različnih semen. Za vsako seme, izvedemo notranjo zanko in preverimo, ali je število metov manjše od najmanjšega, ki smo ga videli doslej.

    from random import *

    najmanj = 1000000000
    seme = 0
    while seme < 1000:
        seed(seme)

        metov = 0
        pozicija = 0
        na_5 = 0

        while na_5 < 100:
            met = randint(1, 6)
            pozicija += met
            if pozicija > 5:
                pozicija -= 6
            if pozicija == 5:
                na_5 += 1
            metov += 1

        if metov < najmanj:
            najmanj = metov

        seme += 1

    print(najmanj)

    446

Zunanjo zanko `while` bomo nadomestili s `for`, vendar se o njej učimo šele v tednu po tej nalogi.