Osem napadalnih kraljic
Testi
testi-osem-napadalnih-kraljic.py
Naloga
Šahovska polja označimo s črkami a-h, ki predstavljajo koordinate stolpcev, in številkami 1-8, ki predstavljajo vrstice. Tako je polje b4 drugo polje v četrti vrsti.
Šahovska kraljica se lahko premika po stolpcih, vrsticah in diagonalah. Kraljica, ki bi jo postavili na b4, bi s svojo požrešnostjo ogrožala vsa polja stolpca b, vrste 4, poleg tega pa še diagonali, torej polja a5, c3, d2, e1 in a3, c5, d6, e7 in f8.
Na šahovnico postavimo določeno število kraljic. Razpored predstavimo s seznamom koordinat, pri čemer so koordinate opisane kot nizi, recimo ["a4", "c7", "d2"].
Spodnje naloge zahtevajo, da napišete različne funkcije. Kadar naloga eksplicitno zahteva, da mora funkcija klicati druge funkcije, se tega navodila obvezno držite. To je eden od namenov te vaje.
Naloge so razdeljene po ocenah. Za vsako oceno je potrebno pravilno rešiti tudi naloge za nižjo oceno. Tako je, na primer, za oceno 8 potrebno rešiti naloge za oceno 6, 7 in 8.
Namig: sam sem na začetku programa definiral niza
Za oceno 6
- Če želiš, napiši funkcijo
stolpec_prost(stolpec, razpored)
, ki kot argument prejme oznako stolpca in razpored kraljic ter vrneTrue
, če v danem stolpcu ni nobene kraljice.Primer:
>>> stolpec_prost("b", ["a4", "c7", "d2"]) True >>> stolpec_prost("c", ["a4", "c7", "d2"]) False - Napiši funkcijo
prosti_stolpci(razpored)
, ki kot argument prejme razpored kraljic in kot rezultat vrne seznam vseh stolpcev, v katerih ni nobene kraljice. Pri reševanju ti bo v pomoč gornja funkcija, če si jo napisal (če je nisi, pa nič).Primer:
>>> prosti_stolpci(["a4", "c7", "d2"]) ["b", "e", "f", "g", "h"] >>> prosti_stolpci(["h8", "a4", "b4", "c7", "g8", "d2", "e1", "f3"])) [] >>> prosti_stolpci([]) ["a", "b", "c", "d", "e", "f", "g", "h"] - Napiši funkcijo
prost_stolpec(razpored)
, ki kot argument prejme razpored kraljic in kot rezultat vrne prvi stolpec, v katerem ni nobene kraljice. Če takšnega stolpca ni, vrneNone
. Funkcija naj uporablja gornjo funkcijo!Primer:
>>> prost_stolpec(["a4", "c7", "d2"]) "b" >>> print(prost_stolpec(["h8", "a4", "b4", "c7", "g8", "d2", "e1", "f3"])) None >>> prost_stolpec([]) "a"
Za oceno 7
- Napiši funkcijo
napada(polje1, polje2)
, ki kot argument prejme koordinati dveh polj ter vrneTrue
, če se polji napadata inFalse
, če se ne. Dogovorimo se, da polje napada tudi samo sebe.Primer:
>>> napada("a4", "a7") True >>> napada("a4", "e4") True >>> napada("a4", "a4") True >>> napada("a4", "c6") True >>> napada("a4", "c2") True >>> napada("a4", "c1") False - Napiši funkcijo
napadajo(polje, razpored)
, ki kot argument prejme koordinate polja in razpored kraljic, kot rezultat pa vrne seznam vseh kraljic, ki napadajo podano polje. Funkcija naj kliče gornjo funkcijo!Primer:
>>> napadajo("c2", ["a4", "c7", "d2"]) ["a4", "c7", "d2"] >>> napadajo("c6", ["a4", "c7"]) ["a4", "c7"] >>> napadajo("e8", ["a4", "c7", "d2"]) ["a4"] >>> napadajo("a4", ["a4", "c7", "d2"]) ["a4"] >>> napadajo("g8", ["a4", "c7", "d2"]) [] - Napiši funkcijo
napadeno(polje, razpored)
, ki kot argument prejme koordinate polja in razpored kraljic, kot rezultat pa vrneTrue
, če katera od kraljic napada izbrano polje, sicer paFalse
. Funkcija naj na primeren način kliče gornjo funkcijo!Primer:
>>> napadeno("a7", ["a4", "c7", "d2"]) True >>> napadeno("a6", ["a4", "c7"]) True >>> napadeno("h5", ["a4", "c7", "d2"]) False >>> napadeno("a4", []) False
Za oceno 8
- Napiši funkcijo
prosto_v_stolpcu(stolpec, razpored)
, ki kot argument prejme oznako stolpca in razpored kraljic, vrne pa seznam koordinat polj v njem, ki še niso napadena. Funkcija naj pokliče ustrezno gornjo funkcijo.>>> prosto_v_stolpcu("a", ["a4", "c7", "d2"]) [] >>> prosto_v_stolpcu("b", ["a4", "c7", "d2"]) ["b1"] >>> prosto_v_stolpcu("f", ["a4", "c7", "d2"]) ["f1", "f3", "f5", "f6", "f8"] >>> prosto_v_stolpcu("f", []) ["f1", "f2", "f3", "f4", "f5", "f6", "f7", "f8"] - Napiši funkcijo
prosto_polje(razpored)
, ki kot argument prejme razpored kraljic, vrne pa koordinato prvega prostega polja - pri čemer s prvim mislimo čim bolj levo polje, znotraj polj v istem stolpcu pa tisto, ki je v vrstici z nižjim indeksom (če so prosta a2, a4 in b1, naj vrne a2). Če prostega polja ni, naj vrneNone
. Funkcija naj uporablja funkcijo za iskanje prostih stolpcev in funkcijo za iskanje prostih vrstic znotraj stolpca.>>> prosto_polje(["a4", "c7", "d2"]) "b1" >>> prosto_polje([]) "a1" >>> print(prosto_polje(["f1", "f2", "f3", "f4", "f5", "f6", "f7", "f8"])) None
Za oceno 9
- Napiši funkcijo
napadajoce_se(razpored)
, ki kot argument dobi nek razpored kraljic in kot rezultat vrne vse pare kraljic, ki se napadajo med sabo. Vsak par naj bo izpisan le enkrat! Funkcija naj uporablja ustrezno funkcijo izmed gornjih. - Napiši funkcij
legalna(postavitev)
, ki kot argument dobi nek razpored in pove, ali je "legalen". Razpored je legalen, če vsebuje osem kraljic, ki se ne napadajo med sabo. Funkcija naj uporablja gornjo funkcijo.
Za oceno 10
- Samostojno napiši program, ki razporedi osem kraljic po šahovnici tako, da se medsebojno ne napadajo. Rezultat naj bo v obliki seznama, kakršni so zgornji.
Sprogramirati moraš tudi vse gornje funkcije, ni pa ti jih potrebno uporabljati.
Rešitev
Kot je namigoval namig, najprej pripravimo niza s črkami, ki predstavljajo stolpce in številkami, ki predstavljajo vrstice. Kdor je nalogo reševal brez njih, bo videl, koliko je z njimi lažje. Kdor je reševal z njimi, naj poskusi brez, pa bo videl, koliko je težje.
Rešitve za začetnike
V ozadju funkcije je ista "fraza", kot jo imamo, recimo, v funkciji, ki ugotavlja, ali je število praštevilo: čim ugotovimo, da ni (da ni praštevilo ali pa da stolpec ni prost), vrnemo False
. Če ne vrnemo False
, pa na koncu, šele po zanki, vrnemo True
.
Naslednja - poišči proste stolpce - je res preprosta (no, enako bomo rekli tudi pri vseh naslednjih ;)). Gremo prek seznama stolpcev (for s in stolpci
), za vsakega pokličemo gornjo funkcijo, da izvemo, ali je prost. Če je, ga dodamo na seznam prostih stolpcev, sicer ne. Na koncu funkcije vrnemo seznam prostih.
Pa naslednja? Ta je šele enostavna! Potrebujemo prvi prost stolpec, napisali pa smo si že funkcijo, ki vrne vse proste stolpce? Pot odtod do konca ni dolga. Pokličemo funkcijo, ki vrne vse proste stolpce in če seznam ni prazen, vrnemo prvega. Če je, ne vrnemo ničesar in funkcija torej vrne None
, kot vse funkcije, ki ne vračajo ničesar.
In tako pridemo do najpreprostejše doslej. Ne zanke ne pogoja ne potrebuje. Dve polji se napadata, če sta v istem stolpcu (polje1[0]==polje2[0]
), isti vrstici (polje1[0]==polje2[0]
) ali pa na isti diagonali. Slednje merimo tako, da pretvorimo vse koordinate v njihove kode ASCII - s funkcijo ord
, ki ste jo prav v ta namen "slučajno" srečali na vajah. Če je absolutna vrednost razlik koordinat enaka - pomaknemo se enako stolpcev levo/desno kot vrstic gor/dol - sta polji na isti diagonali.
Ko je ta funkcija napisana, se lahko le še smejimo naslednji: dano polje napadajo tiste kraljice, ki stojijo na poljih, ki napadajo dano polje. Napadalke bomo zbirali v istoimenskem seznamu; za vsako kraljico iz dane postavitve preverimo, ali napada podano polje in jo po potrebi dodamo na seznam.
Naslednja funkcija ... ah, polje je napadeno, če ga kaka kraljica napada.
Prosta polja so polja, ki jih nihče ne napada. Zapeljemo se prek vrstic, jih sestavimo skupaj s stolpcem (stolpec+vrstica
) in preverimo, ali je polje napadeno. Če ni, ga dodamo na seznam prostih.
Do prvega prostega polja pridemo tako, da gremo prek vseh stolpcev in za vsakega povprašamo, ali je v njem kaj prostih polj. Če so, vrnemo prvega. Sicer nadaljujemo. Če ne najdemo ničesar, ne vrnemo ničesar ... in s tem smo vrnili None
.
In potem pride prva (in zadnja) resna naloga (recimo). Potrebujemo vse pare kraljic, torej naredimo dve zanki. Da pa bi vsak par dobili le enkrat, "enumeriramo" kraljice. Spremenljivka i
vsebuje indeks kraljice kraljica1
. V notranji zanki pogledamo le vse kraljice do i-te. Če se tidve polji napadata, dodamo par kraljic v seznam napadajočih se. Ker naloga nekako nakazuje, naj bi bil par urejen po abecedi (oz. v enakem vrstnem redu, kot so navedene kraljice), damo v par najprej kraljica2
in nato kraljica1
.
In še zadnja: postavitev je legalna, če vsebuje osem kraljic in je seznam napadajočih se prazen.
"Industrijske" rešitve
V jeziku, ki premore osnove funkcijskega programiranja, teh nalog noben zaresen programer ne bi reševal tako, kot smo jih zgoraj. Tule so hitrejše rešitve; zaenkrat za znalce, ostali pa se bomo tega učili enkrat decembra. (Seveda bi se dalo tudi vse ostalo spisati krajše, vendar je tule ideja, da napišemo spodobno, tako, kot bi napisali v praksi.)
Tipične napake
Tule je nekaj tipičnih napak (poleg prepisovanja, ki je vedno največja napaka).
seznam[:1]
ne vrne prvega elementa seznama temveč seznam s prvim elementom.- Ne pišite
if napada(polje, razpored[x]) == True:
temvečif napada(polje, razpored[x]):
. - Podobno je tole
if b==1: return False else: return True Zadoščalo bireturn b==1
. - Tudi letos opažamo
return "True"
. Funkcija naj ne vrača nizaTrue
temveč vrednostTrue
.True
je konstanta, tako kot pi ali 42. Tudi številke 42 ne pišemo pod narekovaje, kadar jo hočemo vrniti (razen, če hočemo vrniti niz 42, ne številke). - Stavka
return None
na koncu funkcije ni potrebno pisati. (Ni pa prepovedano.)
Za konec še enkrat prosimo, da uporabljate Python 3.0 ali višji, saj bodo prihodnje naloge z njim veliko lažje rešljive. Poleg tega nam olajšate popravljanje, če oddajate nestisnjene datoteke .py, ne arhivov .rar ali .zip.