Python az iskolában Wiki
Advertisement

Áttekintés[]

A feladatra két megoldást adunk. Az első egy kicsit klasszikusabb, jobban felismerhető benne a programozási tételek tankönyvi alakja, de ez a megoldás is tartalmaz néhány igazán pythonos rövidítést. A másodikban merészebben élünk a Python lehetőségeivel, és „szorgalmi feladatként” kibővítve a feladatot, BMP k/épen vizuálisan is ellenőrizzük a megoldást (ld. a lap alját), ami nemcsak arra ad lehetőséget, hogy vidám színekkel fessük ki Patakfalva térképét, hanem az objektumok bemutatására is. Mindkét megoldást bőségesen igyekeztem kommentálni a nyelvvel most ismerkedők kedvéért.

Vizsgázónk nem erőlteti a procedurális felépítést, mivel ez egy „csináld végig sorban” típusú feladat, ezért függvények csak ott lesznek, ahol ez indokolt. Más a helyzet a „szorgalmi feladatnál”, ahol a nagyobb lélegzetű feladat áttekinthetősége és az ismétlődő eljárások egyaránt indokolják a függvényekre bontást.

A telkek maximális számát és szélességét nem használjuk fel, a Python dinamikus adatkezelése miatt nincs rá szükség. (Más nyelvekben kéne a tömbmérethez.)

Néhány helyen objektumszerű hivatkozásokat fedezhetünk fel, például az első feladatnál ilyen a sor.append() és a G.sort(). Ennek oka, hogy a Python objektumorientált nyelv, és ebben az értelemben mindent objektumként kezel, még egy egyszerű stringet is. Ez egy sor kényelmes beépített eljárást tesz lehetővé az egyes adattípusokhoz kapcsolódóan. Mindaddig azonban, amíg a class szót le nem írjuk, ez az átható objektumorientáltság nincs a „látható rétegben” a kezdő számára, nyugodtan tekinthetjük puszta szintaktikai megoldásnak is.

Érdemes még előrebocsátani a for ciklus talán kevésbé megszokott használatát. A for a Pythonban inkább a Pearl foreach ciklusához hasonlít. A klasszikus számlálós ciklust a for i in range(…) szerkezettel valósíthatjuk meg, ahogy a 6. feladat második megoldásában látható.

Első megoldás[]

A vizsgázó a feladatsort áttekintve úgy dönt, hogy a beolvasott adatokat telkenként a beolvasás sorrendje szerinti háromelemű listában tárolja (házszám, szélesség, hosszúság), ezeket pedig újfent egy listában. Később továbbhaladva rájön, hogy célszerűbb a területet is tárolni, mint folyton újra kiszámolni, ezért visszatér ehhez a részhez, és kibővíti a telkek adatait a területtel. Látja, hogy a két oldal szétválogatására mindenképp szüksége lesz, és a 7. feladathoz a sorbarendezésre is. A 6. feladat megoldását is leegyszerűsíti a sorbarendezés, s mivel a Pythonban ez szemérmetlenül egyszerű dolog, gyorsan túlesik rajta már a beolvasás után. Így a 6. feladat mintegy melléktermékként fog kiesni a 7. feladat megoldásából, tehát érdemes összevonni őket. A 2. feladatot azonban jobban szeretné még a szétválogatás előtt megoldani, ezért azt is összevonja az elsővel. A szöveget olvasva ugyanis nem teljesen egyértelmű a számára, hogy a két oldal egyforma hosszan van felosztva. Közben megnyugtatásul néhány menet közbeni kiíratást is beiktat, amelyeket utóbb kikommentel.

Az 1. feladatban tehát a beolvasás közben azonnal megtörténik a területszámítás, az összegzés és a szétválogatás. Sok más nyelvhez hasonlóan a Python is igaz értéknek tekinti az if után írt nem nulla számokat és hamisnak a nullát, ezt kihasználva akár a házszám modulo 2-es maradékát, akár a telek hosszúságát használhatjuk feltételnek. A lista sort metódusa egyéb paraméter híján a háromelemű listák 0. eleme, azaz a házszám szerint rendez. A formázott kiírás a C-hez hasonlóan történik.

A 3. feladat nem igényel megjegyzést, a 4. pedig csak annyit, hogy a listák indexelése 0-tól indul, a range(1,N) pedig az [1,N[ félig zárt intervallumot szolgáltatja. Az ötödikben a kerekítéshez a padlós osztás műveleti jelét használjuk, amely pozitív számok esetén a hányados egészrészével azonos.

A vizsgázó szerencsére előbb olvas, aztán kódol, így rájön, hogy a 7. feladat megoldásához úgyis nyilván kell tartania, milyen messze van az utca elejétől, ezért a 6. feladattal fölösleges külön foglalkoznia, csak ki kell írni az adatokat. Az utolsó három telket a szeletelés segítségével válogatja ki, így megspórolja egy külön számláló használatát (amely a for ciklus fent részletezett sajátosságai miatt egyébként nem lenne szükséges). Mivel azonban a feladat kitűzője ravaszul fordított sorrendben kérte a házszámokat, a vizsgázó – ugyancsak ravaszul – elővesz egy listát a tartalék készletből, amelyet a beépített append és pop metódus segítségével egyszerű veremként használ.

Check Tesztelve a 3.1.2 verzióban. (A 2.5 verzió elérhető a laptörténetből.)

# -*- coding: utf-8 -*-

#1-2. feladat
path='telkek.txt'
f=open(path,'r')
N=int(f.readline()) #Stringet olvasott, számmá alakítjuk.
G=[]
J=[]
utcahossz=80 #"Nullázás" összegzéshez
for i in range(N): #N tétel beolvasása
    sor=f.readline().split(' ')
    #Beolvas egy sort, és listaként adja vissza a szóközzel tagolt részeket
    #A listánk most stringeket tartalmaz, helyben számokká alakítjuk.
    #Az utolsóban van egy \n is, de ez az átalakítást nem zavarja meg.
    for j in range(3): #Ez 0-tól 2-ig megy, ahogy az indexelés is!
        sor[j]=int(sor[j])
    #Tároljuk a területet is! Futásidőben és tárhelyben sem jelentős
    #különbség, de a megoldást egyszerűsíti.
    #A Jólétsoron mindenhol 0 lesz, de legyen egyforma a kettő.
    sor.append(sor[1]*sor[2])
    #Nem a megoldás része, csak menet közbeni ellenőrzéshez
    #print (sor)
    utcahossz+=sor[1]
    if sor[0] % 2: #páratlan
        #Másik megoldás: if sor[2]: (mivel csak ott pozitív a hosszúság)
        G.append(sor)
    else:
        J.append(sor)
G.sort()
J.sort()
#Nem a megoldás része, csak menet közbeni ellenőrzéshez
#print("\nG:",G,"\n\nJ:",J)
print("1. feladat: %d tétel beolvasása kész." % N)
print("\n2. feladat: a szöveg kétféleképpen értelmezhető!")
print("Ha csak a két utcát akarjuk körbejárni, %d méter lesz." % utcahossz)
print("Teljes kör a kiindulási pontig: %d méter." % (utcahossz+80))
#Ha az utcahossz+80 nincs zárójelben, hibaüzenetet kapunk!
#(Műv. sorrend, konkatenációnak veszi.)

#3. feladat
darab=0
for telek in J:
    if telek[1]<=20:
      darab+=1
print ("\n3. feladat: a Jólétsoron %d telek nem szélesebb 20 méternél." % darab)

#4. feladat
"""
Min./max.kiválasztás tétele, index szerint
Jobb híján tételezzük fel, hogy egyértelműen létezik a min. és a max.
A szöveg nem utal másra, és ez egy érettségi feladat, korlátos időben.
Egy általános megoldás több maximumot megengedve sokkal bonyolultabb lenne.
Jobb lenne, ha ezen nem kéne gondolkozni egy érettségin.
Igaz viszont, hogy a vizsgázót sem akadályozza meg senki abban, hogy Excelben 
rendezgesse a telkek.txt-t, és úgy ellenőrizze a feltevéseit. :-))
"""

min=0; max=0 #Több utasítás egy sorban, nem túl Python-lelkű, de működik
for i in range(1,len(G)):
    #Ezzel azért megoldás közben ellenőrizzük, hogy tényleg egy van-e.
    #print G[i]
    if G[i][3]>G[max][3]:
        max=i
    if G[i][3]<G[min][3]:
        min=i
print("\n4. feladat: ")
print("A legnagyobb területű telek a %d. számú, a területe %d." % (G[max][0],G[max][3]))
print("A legkisebb területű telek a %d. számú, a területe %d." % (G[min][0],G[min][3]))
print("A kettő között %d telek van." % (abs(G[max][0]-G[min][0])/2-1))
#Ezen az oldalon csak páratlan házszámok vannak!
#A zárójel szerepét lásd fent a 2. feladat végén.

#5. feladat 
bevetel=0
for telek in G:
    if telek[3]<=700:
        ado=51*telek[3]
    elif telek[3]<=1000:
        ado=51*700+39*(telek[3]-700)
    else:
        ado=51*700+39*300+200
    if telek[1]<=15 or telek[2]<=25:
        ado*=0.8
    ado=(ado+50)//100*100 #kerekítés padlós osztással
    bevetel+=ado
print("\n5. feladat: az adóbevétel : %d." % bevetel)

#6-7. feladat
#Mivel az utolsó három telket fordított sorrendben kell kiírni, ez jó ürügy
#annak bemutatására, miként használhatjuk a lista adattípust egyszerű veremként.
feladat6=[]     #Üres lista a fordított kiíráshoz (veremként használjuk)
Jtelekvege=0    #Ezek méterben értendőek
Gindexvege=0    #A J oldali aktuális telekkel szemközti G-telkek indexe
Gszumma=G[0][1] #A G oldali összhossz méterben az aktuális pontig
f=open('joletsor.csv','w')
for telek in J:
    Jtelekeleje=Jtelekvege
    Jtelekvege+=telek[1]
    Gindexeleje=Gindexvege
    i=Gindexeleje
    maxGhossz=G[Gindexeleje][2]
    while Gszumma<Jtelekvege:
        i+=1
        Gszumma+=G[i][1]
        if G[i][2]>maxGhossz:
            maxGhossz = G[i][2]
    Gindexvege=i
    #Akár az előző érték is maradhat, ha a G-telek szélesebb két J-teleknél
    telek[2]=70-maxGhossz
    #print(telek[0],[telek[1],telek[2],sep=";") #Ez 3.x-es szintaxis!
    f.write("%d;%d;%d\n" % (telek[0],telek[1],telek[2]))
    #6. feladat:
    if telek in J[-3:]:    #Az utolsó hármat verembe teszi külön számláló nélkül
        feladat6.append("Házszám: %d; a telek eleje: %d méter." %(telek[0],Jtelekeleje))
f.close()
print("\n6. feladat:")
while len(feladat6):
    print(feladat6.pop())

Második megoldás[]

Ambox important Ez a szakasz az érettségi követelményeket meghaladó ismeretet tartalmaz.

Az első megoldásban már látott kommenteket itt mellőztem. A program azért ilyen hosszú, mert a kötelező feladatok megoldása után szabadjára engedtem a fantáziámat, és megrajzoltam Patakfalva térképét a kanyargó Dirib patakkal. Ha futtatni akarod ezt a programot, a bitmap.py-t kell letölteni (pl. az aktuális könyvtárba) vagy a vonalnál levágni a grafikus részt.

Ebben a megoldásban a programozási tételek meglehetősen bújtatott alakot öltenek. Rögtön az első feladatban egy szétválogatást láthatunk a szeletelés segítségével, ezért előzőleg az összes telket egy listába olvastuk be, és ezt rendeztük. Így a két oldal a páros/páratlan indexek szerint különíthető el. A második feladat most különválasztható, és a telkek listából helyben, segédváltozók nélkül adja össze a telekszélességeket.

A harmadik feladat minden látható ciklus nélkül végzi el a megszámlálást. A for itt nem vezérlő szerkezetként, hanem iterátorként mutatkozik. A megoldást két változatban mutatom be, hogy kisebb legyen a sokkhatás. A map valamilyen függvény szerint nullákat és egyeseket rendel egy sorozat értékeihez (jelen esetben ez a szélességeket tároló telek[1]), a sum pedig ezek összegzésével valójában megszámlálást végez. A map paraméterként szokatlan módon egy függvény nevét várja, ezért definiálunk egy aprócska segédfüggvényt, amely az eldöntés magját valósítja meg a C nyelv ?: szerkezetének megfelelő feltételes kifejezéssel. Mindenki érezheti, hogy ez luxus, hiszen a függvényt másra nem használjuk, és logikailag sincs ok kitenni a map függvényből. Erre kínál megoldást a Python a lambda kulcsszó használatával, de ez már meghaladja az érettségi követelményeket. A második megoldásban egy ilyen „egyszer használatos” lambdafüggvény végzi az eldöntést, és így egyetlen utasítással, segédváltozók nélkül írhatjuk ki a 20 méternél nem szélesebb telkek számát. A 4. feladatban egy ugyanilyen lamdafüggvény a sorted függvény paramétere lesz, így a telkek terület (azaz a 3. kulcs szerinti) rendezését is egyszerűen oldhatjuk meg.

Az 5. feladatnak ez a természetes alakja, nem érdemes erőltetni egy másik megoldást, csak a kerekítésen módosítottam. A módosítás furcsa eredményt hozott: míg a 2.5-ös verzióban még tökéletesen működött a round függvény, most százzal kisebb eredmény jön ki. A magyarázat a round()nagymértékben szokatlan viselkedése, amely lentebb olvasható a kódnál a súgóból kimásolva. Úgy tűnik, a kerekítőfüggvényre nem számíthatunk (ez még úgy is probléma, hogy a pontozási útmutató elfogadja ezt az eredményt is), az első megoldásban látott módszer szolgáltatja a pontos eredményt. Jobban befolyásolható kerekítési eljárások a decimal modulban vannak. Lásd még:

A 6. feladat a klasszikus for ciklust utánozza, a ciklusfejet a range iterátorba helyezve. Az opcionális harmadik paraméter a lépésköz. A telkek távolságát az utca elejétől ismét helyben végzett összegzéssel számoljuk ki, J[:t] fejezi ki a t. telek előtti telkek sorozatát a Jólétsoron. A 7. feladatban ismét meghaladjuk az érettségi követelményt azzal, hogy a lemezre írás során a kivételkezelés segítségével hibaellenőrzést végzünk, és ha nem sikerül menteni a megoldást, legalább a képernyőre kiírjuk. Figyeljük meg a kiírás egyszerűbb alakját az első megoldással szemben: formázott string helyett a print függvény sep paraméterét használjuk a pontosvessző elhelyezésére, és a file paramétert a file-ba írásra (azaz nem kell az f.write sem, a kiírás képernyőre és lemezre majdnem egyformán működik).

A szorgalmi feladatban látható a véletlenszámok használata (ehhez már a Pythonban is szükség van egy import utasításra, ami az include/uses megfelelője). A rajzlap nevű kétdimenziós listában rajzoljuk ki a telkeket kétszeres nagyításban, ügyelve rá, hogy szomszédos telkek ne legyenek egyforma színűek. A kétpixeles vonalvastagság valamennyi kerekítési nehézséget okoz, hiszen az eredeti feladatban maga a telekhatár idealizálva van, és nem rendelkezik szélességgel. A pataknak természetesen kanyarognia is kell, vigyázva, hogy kerítést ne szakítson. A megoldás során először csomópontokat határozunk meg, amelyek között a patak halad. Ahol a csomópontok távolsága túl nagy, ott véletlenszerűen újabbakat iktatunk be közéjük. Ezek után két-két csomópont között határozzuk meg a patak útját. Ezen még lehetne finomítani, hogy holtágak ne legyenek, de a feladat szemléltetéséhez így is tökéletes. Végül a bitmap.py modulban definiált bitmap objektumosztály egy példányát hozzuk létre a kötelező paraméter és a nem kötelező paraméterek egy részének megadásával, és ennek a rajzol metódusával alakítjuk BMP grafikává a kétdimenziós listánkat.

Check Tesztelve a 3.1.2 verzióban. (A 2.5 verzió elérhető a laptörténetből.)

# -*- coding: utf-8 -*-

#1. feladat
f=open('telkek.txt','r')
N=int(f.readline()) #Stringet olvasott, számmá alakítjuk.
telkek=[]
for i in range(N): #N tétel beolvasása
    sor=f.readline().split(' ')
    for j in range(3):
        sor[j]=int(sor[j])
    sor.append(sor[1]*sor[2])
    telkek.append(sor)
telkek.sort()
G=telkek[::2]
J=telkek[1::2]
print("1. feladat: %d tétel beolvasása kész." % N)

#2. feladat
utcahossz=sum(telek[1] for telek in telkek) + 80
print("\n2. feladat: a szöveg kétféleképpen értelmezhető!")
print("Ha csak a két utcát akarjuk körbejárni, %d méter lesz." % utcahossz)
print("Teljes kör a kiindulási pontig: %d méter." % (utcahossz+80))

#3. feladat, első megoldás
#A map függvényt használjuk egy segédfüggvénnyel, amely 1-ekből és 0-kból
#álló sorozatot állít elő, és ezt a sum függvénnyel összegezzük.
#A "telek[1] for telek in J" a telkek szélességeit tartalmazó lista.
#A map első paramétere egy függvény!
def kell(t):
    return 1 if t<=20 else 0
print("\n3. feladat (segédfüggvénnyel): a Jólétsoron %d telek nem szélesebb 20 méternél." % 
        sum(map(kell, (telek[1] for telek in J))))

#3. feladat, második megoldás
#Az előző megoldást tömöríthetjük az ún. lambdafüggvény használatával.
#A lambda helyben állítja elő a függvényt, éppen a map az egyik tipikus
#felhasználása. Lásd még a sortnál az 5. feladatban.
#1 és 0 helyett most logikai értékeket ad vissza, de a sum ezeket is
#össze tudja adni (True=1, False=0).
#Ez a megszámlálásnak egy meglehetősen egyszerű módja. :-)
print("3. feladat (lambdával): a Jólétsoron %d telek nem szélesebb 20 méternél." % 
        sum(map(lambda t:t<=20, (telek[1] for telek in J))))


#4. feladat
#G-t egy mozdulattal sorbarendezzük terület szerint.
#Nem kell maximumot keresni: a 0. elem a legkisebb, a -1., azaz utolsó a legnagyobb.
#A G.sort helyett a g=sorted(G) alakot használjuk, hogy G rendezése megmaradjon.
#a 7. feladathoz (g és G különböző nevek!)
g=sorted(G,key=lambda t:t[3]) #Segédfüggvény megspórolása lambdával
print("\n4. feladat: ")
print("A legnagyobb területű telek a %d. számú, a területe %d." % (g[-1][0],g[-1][3]))
print("A legkisebb területű telek a %d. számú, a területe %d." % (g[0][0],g[0][3]))
print("A kettő között %d telek van." % (abs(g[-1][0]-g[0][0])/2-1))

#5. feladat
#Ezen is lehetne tömöríteni, de nem nyernénk annyit a rövidséggel,
#amennyit az olvashatóságon veszítenénk. Így itt marad a klasszikus megoldás,
#csak a kerekítésen változtattam.
bevetel=0
for telek in G:
    if telek[3]<=700:
        ado=51*telek[3]
    elif telek[3]<=1000:
        ado=51*700+39*(telek[3]-700)
    else:
        ado=51*700+39*300+200
    if telek[1]<=15 or telek[2]<=25:
        ado*=0.8
    ado=round(ado/100)*100
    #Az eredmény 100-zal kevesebb lesz a round hibás viselkedése miatt.
    #A súgóból:
    #  if two multiples are equally close, rounding is done toward the even
    #  choice (so, for example, both round(0.5) and round(-0.5) are 0,
    #  and round(1.5) is 2). 
    #Megoldás lehetne a decimal modul használata, de egyszerűbb az 1. megoldás.
    #A kerekítés fontos feladat az érettségin, jobb a padlós osztást tanítani.
    bevetel+=ado
print("\n5. feladat: az adóbevétel : %d." % bevetel)

#6. feladat
"""
A második megoldásban elkülönítjük a két feladatot, és egyszerű összegzést végzünk.
A fordított kiírást a szeletelés és a range iterátor segítségével végezzük.
Ez felel meg a klasszikus for ciklusnak (de -4-ig kell írnunk, hogy -3-ig menjen).
Ezáltal a 6. feladatot akkor is megtudjuk oldani, ha a hetedikre nem marad idő,
és nem kell semmit tárolnunk hozzá.
"""
print("\n6. feladat:")
for t in range(-1,-4,-1):
    print("Házszám: %d; a telek eleje: %d méter." %
           (J[t][0],sum(telek[1] for telek in J[:t])))

#7. feladat
"""
Segédfüggvény következik: kibővítjük a T teleklista tagjait két új elemmel:
a 4. a telek elejének, az 5. a telek végének a távolsága az utca elejétől.
Ezt alkalmazzuk majd a J és a G listára is.
Ez az a pont, ahol már elegánsabb volna objektumokat használni, hogy akármit
ne írhassunk az argumentumba, de most maradjunk az érettségi követelményeknél.
"""
def summa(T):
    for i in range(len(T)):
        T[i].append(sum(telek[1] for telek in T[:i]))
        T[i].append(T[i][4]+T[i][1])
        #Nem a megoldás része, csak menet közbeni ellenőrzéshez:
        #print T[i]

print("\n7. feladat:")
try:
    f=open('joletsor.csv','w')
    hiba=False
except IOError:
    hiba=True
    print('Nem sikerült a lemezre írás, az eredményeket a képernyőre írom.')
summa(J)
summa(G)
for telek in J:
    szemben=[]
    for Gtelek in G:
        if Gtelek[5]>=telek[4] and Gtelek[4]<=telek[5]:
            szemben.append(Gtelek)
    szemben.sort(key=lambda t:t[2])
    telek[2]=70-szemben[-1][2]
    if not hiba:
        print(telek[0],telek[1],telek[2], sep=';',file=f)
    else:
        print(telek[0],telek[1],telek[2], sep=';')
if not hiba:
    try:
        f.close()
        print("Sikeresen elmentve.")
    except IOError:
        print("Mentési hiba történt.")




#---------- Itt kell levágni, ha csak az érettségi feladat érdekel. -----------




"""
Innentől nem része a feladat megoldásának, hanem kirajzoljuk a
megoldást. Az import utasítások ezúttal nem az elején vannak, hogy a
kitűzött feladat megoldása és a rajzolás elkülönüljön. Egy saját, egy
gyári (a formai szokások szerint azt illik előbbre írni).
"""

import random
import bitmap #Ez saját gyártmány, lásd a bitmap.py szócikket!
random.seed() #Inicializálás a rendszerórából

def szinek():
    #0=világosszürke háttér
    #1=fekete kerítés
    #2=zöld patakmeder fűvel
    #3=kék patak
    #4-8: telekszínek (4 színnel sokszor végtelen ciklusba került.)
    #A csúcsokon érintkező telkek nem elégítik ki a négyszíntétel feltételét!
    #http://hu.wikipedia.org/wiki/N%C3%A9gysz%C3%ADnt%C3%A9tel
    colors=[240,240,240,0, 0,0,0,0, 0,155,0,0, 255,0,0,0]
    colors+=[0,128,255,0, 255,50,255,0, 0,255,255,0, 250,250,0,0, 0,0,255,0]
    colors+=[0,0,0,0]*247
    return colors

#Segédfüggvény, mert két teleksor van:
def telekrajz(oldal,lenti):
    telekszin=elozo=-1
    for telek in oldal:
        #hosszú falak: (a kétszeres nagyítás kis többletmunkát igényel...)
        b=hossz-perem-2*telek[4]
        c=hossz-perem-2*telek[5]
        eltol=0 if lenti else 1
        for y in range(eltol,telek[2]+eltol):
            a=perem+2*y if lenti else perem+164-2*y
            rajzlap[a][b]=1
            rajzlap[a+1][b]=1
            rajzlap[a][b-1]=1
            rajzlap[a+1][b-1]=1
            rajzlap[a][c]=1
            rajzlap[a+1][c]=1
            rajzlap[a][c-1]=1
            rajzlap[a+1][c-1]=1
        rajzlap[perem-1][hossz-utcahossz+perem+39]=1
        rajzlap[perem+164][hossz-utcahossz+perem+39]=1
        rajzlap[perem-1][hossz-perem]=1
        rajzlap[perem+164][hossz-perem]=1
        #Legalább a külső sarka legyen meg, a többi kerekítési hiba

        #Rövid falak:
        rb=perem if lenti else perem+164
        rc=perem+2*telek[2] if lenti else perem+164-2*telek[2]
        for x in range(1,telek[1]+1):
            a=hossz-perem-2*telek[4]-2*x
            rajzlap[rb][a]=1
            rajzlap[rb-1][a]=1
            rajzlap[rb][a+1]=1
            rajzlap[rb-1][a+1]=1
            rajzlap[rc][a]=1
            rajzlap[rc-1][a]=1
            rajzlap[rc][a+1]=1
            rajzlap[rc-1][a+1]=1
            
        #Kitöltés vidám színekkel (a randomizálás lassítja a futást, de megéri):
        szemben=szembeszin(c,b)
        while telekszin==elozo or \
                not lenti and telekszin in szemben:
            telekszin=random.randint(4,8)
        elozo=telekszin
        alja, teteje = (rb+1, rc-1) if lenti else (rc+1, rb-1)
        for x in range(c+1,b-1):
            for y in range(alja,teteje):
                rajzlap[y][x]=telekszin

def szembeszin(c,b):
    #Csak a G oldali telkeknél érdekes, azt rajzoljuk másodjára.
    #Milyen szín van szemközt? Erre találták ki a halmaztípust.
    szembe=set()
    for x in range(c-3,b+3):
        #A 3-mal a sarkokon érintkezőket is kilőjük (12. és 9. telek).
        szembe.add(rajzlap[perem+2][x])
    return szembe

def ferde(x1,y1,x2,y2):
    x,y = x1,y1
    dx=1 if x2>x else -1
    dy=1 if y2>y else -1
    while x-x2!=0 and y-y2!=0:
        for i in range(-2,2):
            rajzlap[y][x+i]=3
        rajzlap[y-1][x]=3
        rajzlap[y+1][x]=3
        if abs(x2-x-dx)>abs(y2-y-dy) or (rajzlap[y+5*dy][x+2]==1):
            #A második egy tapasztalati képlet, amitől kikerüli a sarkokat.
            x+=dx
        else:
            y+=dy
    rajzlap[y2][x2]=3

def egyenes(x1,x2,y):
    for i in range(x2,x1+1):
        for j in range (-1,2):
            rajzlap[y+j][i]=3
    
def mehet(x,y,j,g):
    #Ez a függvény azért kell, mert enélkül a patakmeder rajza
    #beletörölt a "kilógó" kerítésekbe.
    OK=True
    if G[g][5]==x:
        if g<len(G)-1 and y>=perem+164-2*G[g+1][2]:
            OK = False
    if J[j][5]==x:
        if j<len(J)-1 and y<=perem+2*J[j+1][2]:
            OK = False
    return OK
    
def mederrajz():
    j=0
    g=0
    #//=padlós osztás (egy / jellel ptlan számból float lenne, és hibajelzés)
    for x in range((utcahossz-80)//2+1):
        if J[j][5]<x:
            j+=1
        if G[g][5]<x:
            g+=1
        kezdet,veg=perem+2*J[j][2]+1,perem+164-2*G[g][2]-1
        #Kijelöljük a patakmedret és a kanyarok helyét:
        kozepe=(veg-kezdet)//2+kezdet
        patak[x][0]=kozepe
        if patak[x][0]!=patak[x-1][0] and x>1:
            patak[x-3][1]=True
            patak[x+2][1]=True
        #Zöld fű nő a patak partján:
        for y in range(kezdet,veg):
            if mehet(x,y,j,g):
                rajzlap[y][hossz-perem-2*x]=2
                rajzlap[y][hossz-perem-2*x-1]=2
    #Az utolsó és az első csomópont
    patak[utcahossz//2-40][1]=True
    patak[0][1]=True
    #Jobbról az első ponton kézi kozmetikázás, hogy szebb legyen
    patak[13][1]=False
    patak[10][1]=True

def patakrajzfogfolyni():
    #Lesz egy ciklus, amelyik 2-2 szomszédos csomópontot keres.
    #Ha egyforma magasan vannak, egyenesen folyunk, ha nem, akkor ferdén.
    #Jobb széle:
    p2=0;
    while p2<utcahossz/2-40:
        i=p2+1
        while not patak[i][1]:
            i+=1
        if i-p2>16:
            #Túl messze vannak, teszünk be mesterséges csomópontot unalom ellen
            #Utána visszalép, és elölről kezdi.
            #A 16, a 12 és a randint(4,6) módosításával lehet szabályozni a
            #patakot, ha változtatni akarsz a kanyargósságán. Jó szórakozást!
            for j in range(p2,i,12):
                #Bónusz csomópont
                patak[j][1]=True
                #És most megpiszkáljuk a nyugalmát:
                patak[j][0]+=random.randint(4,6)*(1 if random.randint(0,1) else -1)
        else:
            p1=p2
            p2=i
        #print hossz-perem-2*p1
        if patak[p1][0]==patak[p2][0]:
            egyenes(hossz-perem-2*p1,hossz-perem-2*p2,patak[p1][0])
        else:
            ferde(hossz-perem-2*p1,patak[p1][0],hossz-perem-2*p2,patak[p2][0])

def prerajz():
    #Előszínezzük, mert egy-egy pixel kimaradt a sarkokon.
    #//=padlós osztás (egy / jellel ptlan számból float lenne, és hibajelzés)
    for x in range((utcahossz-80)//2+1):
        for y in range(perem,perem+164):
            rajzlap[y][hossz-perem-x]=1
    for x in range((utcahossz-80)//2+1):
        for y in range(perem,perem+164):
            rajzlap[y][hossz-perem-2*x]=2
            rajzlap[y][hossz-perem-2*x-1]=2

#Minden sor kb. 800 pixel, és a bal alsó sarokból indulunk jobbra.
#200 sor van, és alulról felfelé haladunk.
hossz=804 #A képméret, dupla nagyításban az eredetihez
magas=200
rajzlap=[]
for i in range(magas):
    sor=[0]*hossz
    rajzlap.append(sor)
    #A sor=... sort látszólag kiemelhetnénk a ciklus elé. De nem!
    #Akkor pl. rajzlap[10][180]=3 egyszerre váloztatná az összes sort!
    #rajzlap[11][180] is 3 lesz tőle...
    #Lásd a második megjegyzést ebben a szakaszban:
    #http://docs.python.org/py3k/library/stdtypes.html#sequence-types-str-bytes-bytearray-list-tuple-range
perem=20
patak=[] #Ez a kanyargáshoz kell, a 2. érték a fix pontokat mutatja
for i in range(utcahossz):
    patak.append([0,False])
prerajz()
telekrajz(J,True)
telekrajz(G,False)
mederrajz()
patakrajzfogfolyni()
#Demo only -- fejlesztés közben ezzel ellenőriztem a csomópontokat
#Ha kiveszed a kommentjeleket, látványosan kirajzolja a pontokat.
#for x in range(hossz-perem,hossz-perem-utcahossz-4,-1):
#    if patak[x][1]:
#        rajzlap[patak[x][0]][hossz-perem-2*x]=4
print("""\n
Bónuszfeladat: ha nem látsz hibaüzenetet, a telkek.bmp ábrázolja a megoldást.
""")

#Ennyi előkészítés után végre használatba vesszük a fent importált bitmap modult,
#és létrehozzuk a benne definiált bitmap osztály egy példányát rajz néven.
#A rajz objektum rajzol metódusa fogja elmenteni a képet.
rajz=bitmap.bitmap('telkek.bmp',width=hossz, height=magas,
                   bits=rajzlap, colors=szinek())
rajz.rajzol()

#Búcsúzik a Füles mára. :-)

És a keletkezett kép GIF-be konvertálva:

Telkek


Érettségi feladatok programozásból
2004 Személyazonosító jel

2005 LottóVigenère-tábla 2006 TelefonszámlaFehérjeZenei adók 2007 SMS-szavakFoci 2008 SMSRobot 2009 LiftAutomataÚtépítés 2010 HelyjegyTelekAnagramma 2011 SzójátékRejtvényPitypang 2012 FutárTörtekSzín-kép 2013 VálasztásokSzámokKözúti ellenőrzés 2014 IPv6CéllövészetNézőtér 2015 ExpedícióLatin táncokFej vagy írás 2016 ÖtszázZárTelefonos ügyfélszolgálat 2017 TesztversenyFürdőHiányzások 2018 TársalgóFogadóóraKerítés 2019 Céges autókTantárgyfelosztáseUtazás 2020 Meteorológiai jelentésMenetrendSorozatok 2021 GödrökBányató

Advertisement