IB111 Simulace a návrh aplikace Založeno na 9. kapitole knihy J. M. Zelle:  Python Programming: An Introduction to Computer Science Simulace hry volejbal • Chceme vytvořit simulátor hry volejbal – Ne grafický :‐) • Jde nám o počty vítězství 2 teamů, známe‐li  pravděpodobnosti výhry jejich podání. • Například: – První team vyhrává svá podání s 60% úspěšností – Druhý team vyhrává svá podání s 50% úspěšností • Úkolem je vytvoření programu, který bude  simulovat několik her volejbalu – Používáme stará pravidla a existenci setů ignorujeme Volejbal • Začíná jeden team – Pokud získá své podání, dostane bod a opět podává – Pokud ztratí své podání, podává druhý team a bod  nezískává nikdo • Hra končí pokud nějaký team získá 15 bodů – Pokud je však rozdíl ve skóre pouze jednobodový, je třeba  hrát dál dokud rozdíl není alespoň 2 body Detailnější specifikace problému • Vstup: Program se zeptá na potřebné údaje – Pravděpodobnost výhry podání teamu A – Pravděpodobnost výhry podání teamu B – Počet her, které budeme simulovat • Výstup: Program vypíše výsledné statistiky – Počet simulovaných her – Počet her, které vyhrál team A – Počet her, které vyhrál team B Náhoda • Pro deterministický počítač není jednoduché vytvářet zcela náhodná čísla • Pro simulace typicky používáme generátor  pseudonáhodných čísel – Založené na „semínku“ (seed) – Vytváří posloupnost čísel s dobrými statistickými  vlastnostmi • Vhodné pro simulace • Python – Modul random – Pseudonáhodný generátor „Mersenne Twister “ inicializovaný aktuálním časem • Zcela nevhodné pro kryptografické účely Generování náhodných čísel • Python – random.randint(a, b)  • Return a random integer N such that a <= N <= b. – random.random()  • Return the next random floating point number in the range [0.0, 1.0). • Výhra podání if random() < prob: score = score + 1 kde prob je pravděpodobnost výhry hráče Návrh shora dolů (top‐down) • Návrh začínáme na abstraktní (vysoké) úrovní • Specifikujeme co principielně bude program dělat • Uvedeme, jak se bude postupovat, aniž bychom  museli rovnou zmínit všechny detaily jak to udělat – To upřesníme později • V dalších krocích pak postupně upřesňujeme detaily,  které jsme v předchozích krocích přeskočili • Nakonec tak získáme kompletní program Náš příklad se simulací volejbalu • Základní algoritmus: Vypiš úvodní informace Získej vstupní údaje: probA, probB, n Simuluj n her teamů A a B za pomoci probA, probB Vypiš kolik her se hrálo, kolikrát vyhrál team A, B • Pro každý krok dále musíme upřesnit vstupy a  výstupy  – parametry a návratové hodnoty funkcí – Interface / rozhraní První návrh programu • Program v pythonu: def main(): printInstructions() probA, probB, n = getInputs() winsA, winsB = simNGames(n, probA, probB) printSummary(winsA, winsB) Grafický návrh • Náš návrh lze zobrazit i graficky Druhá úroveň návrhu • Po navržení první (kořenové) úrovně návrhu  pokračujeme druhou úrovní • Specifikujeme chování funkcí použitých v návrhu  první úrovně. • Tj. specifikujeme funkce – printIntro – getInputs – simNGames – printSummary Výpis úvodních informací • Funkce v pythonu: def printIntro(): print "Tento program simuluje hru volejbal mezi 2" print ’teamy "A" a "B". Schopnosti teamu jsou dany’ print "pravdepodobnostmi (cislo mezi 0 a 1)  s jakymi" print "team vyhraje sve podani. Zacina vždy team A." Získání vstupních dat • Funkce v pythonu def getInputs(): a = input("S jakou pravdepodobnosti vyhraje  team A sve podani") b = input("S jakou pravdepodobnosti vyhraje  team B sve podani") n = input("Kolik her bude simulovano?") return a, b, n Simulace n her • Intuitivně bude simulace vypadat nějak takto: iniciálně počet výher winsA a winsB bude nula opakuj n‐krát simuluj jednu hru jestliže vyhrál team A team A získává bod jinak team B získává bod Simulace n her • Přepíšeme do pythonu: def simNGames(n, probA, probB): winsA = winsB = 0 for i in range(n): scoreA, scoreB = simOneGame(probA, probB) if scoreA > scoreB: winsA = winsA + 1 else: winsB = winsB + 1 return winsA, winsB Výpis výsedku • Zapíšeme v pythonu def printSummary(winsA, winsB): n = winsA + winsB print "\nPocet simulovanych her:", n print "Pocet vyher A: %d (%0.1f%%)" % (winsA,  float(winsA)/n*100) print "Pocet vyher B: %d (%0.1f%%)" % (winsB,  float(winsB)/n*100) Grafické znázornění • Opět může aktuální specifikaci zobrazit graficky: Třetí úroveň návrhu • Zbývá dospecifikovat funkci pro jednu hru • Intuitivně bude funkce fungovat takto: Inicializuje skóre obou teamů na nulu Team A má podávat Opakuj dokud není konec hry: simuluj jedno podání teamu, který podává aktualizuj skóre Vrať skóre obou teamů Implementace v pythonu def simOneGame(probA, probB): scoreA = 0 scoreB = 0 serving = "A" while gameNotOver(scoreA, scoreB): if serving == "A": if random() < probA: scoreA = scoreA + 1 else: serving = "B" else: if random() < probB: scoreB = scoreB + 1 else: serving = "A" return scoreA, scoreB Grafické zobrazení Zbývá funkce pro zjištění konce hry • Hra končí, pokud některý z teamů získá 15 bodů – A rozdíl skóre obou teamů je nejméně 2 def gameOver(a,b): return (a>=15 or b>=15) and (a>=b+2 or b>=a+2) Jak jsme postupovali 1. Vyjádřili jsme algoritmus jako sérii menších  problémů (bloků/funkcí) 2. Pro každý menší problém (blok/funkci) jsme  vyvinuli rozhraní 3. Algoritmus jsem pak upřesnili  tím, že jsme  specifikovali, jak využijeme rozhraní s menšími  problémy (bloky/funkcemi). 4. A obdobně jsme postupovali při návrhu  jednotlivých menších problémů (bloků/funkcí) na  nižších úrovních. Testování • Program implementujeme v pythonu • Když ho poprvé spustíme je možné, že díky chybám  nebude fungovat, tak jak si představujeme – Chybujeme všichni – Chyby jsou běžné – Bug • Program budeme testovat po částech – Použijeme přístup „zdola nahoru“ (botton‐up) – Nejprve ověříme funkčnost komponent na nižší úrovni a  později komponent na vyšší úrovni Testování funkce gameOver • Vyzkoušíme na několika voláních s různými  parametry: • Pokud se zdá, že funkce funguje dobře, pokračujeme  o úroveň výše. • Pozn: Skutečná verifikace toho, že funkce dělá přesně to co má, je netriviální. Jiné přístup k návrhu a vývoji SW • Prototypování a spirálový vývoj – Prototyp – iniciální verze programu s omezenou  funkčností • Vhodné pro ověření, zda principy, na kterých chceme  SW založit fungují – Prototyp dále vylepšujeme, až získáme kompletní program s plnou funkčností • Inkrementální vývoj Prototypování: úvodní verze def simOneGame(): scoreA = 0 scoreB = 0 serving = "A" for i in range(30): if serving == "A": if random() < .5: scoreA = scoreA + 1 else: serving = "B" else: if random() < .5: scoreB = scoreB + 1 else: serving = "A" print scoreA, scoreB Prototypování: inkrementální vývoj • Fáze: 1. Iniciální prototyp: 30 podání s pravděpodobností vyhrání podání 50 % a 50 %. Pomocný výpis po každém podání. 2. Přidáme parametry pro pravděpodobnosti výher podání. 3. Přidáme funkci sledující konec hry při získání 21 bodů jedním hráčem. Tím máme funkční simulaci jedné hry. 4. Rozšíříme na opakování několika her. Výstupem bude  počet výher jednotlivých hráčů. 5. Vývoj plně funkčního programu. Dořešení získání vstupu  od uživatele a vhodného formátování výstupu. Vývoj softwaru • Vývoj softwaru je umění • Již dlouhou dobu se jí zabývá věda nazývaná „Softwarové inženýrství“ („Software engineering“) • S dalšími aspekty vývoje SW se setkáte v dalších  předmětech na FI – například PB007  Softwarové inženýrství I Ladění programů v pythonu • Modul pdb • Spustíme ladění vykonávání určitého kódu • Nebo program zastavíme na  určitém místě (breakpoint)  pomocí pdb.set_trace() • Potom můžeme – Pokračovat (c) – Krokovat (s) – Prohlížet proměnné (p) – … Ladění programů v Pythonu v IDLE Ladění programů v C: Visual Studio C++ Dokumentování programů • Poznámky/komentáře – Usnadňují pochopení jednotlivých konstrukcí – Píšeme pro sebe i ostatní • Užitečné pro pozdější modifikace programu  – Prováděné námi i jinými • Dokumentace rozhraní – API – Ty funkce/moduly/knihovny, které budou používány  ostatními • Názvy modulů, funkcí, proměnných… Dokumentace rozhraní • Funkce – Jméno funkce – Vstupní parametry • Povinné • Nepovinné • Defaultní hodnoty – Návratová hodnota – Funkčnost Příklad z dokumentace Pythonu Příklad z dokumentace Windows API Dokumentujeme v Pythonu • Dokumentační řetězce (docstring) • První řetězec třídy, modulu, funkce (metody) • Všechny „veřejné“ části by měly být dokumentovány Jak správně dokumentovat? Příklad využití Styl • Styl psaní programů – Styl programování, coding style • Stejný program lze zapsat různým způsobem – Pěkně vs. škaredě (nevhodně) Doporučení pro Python • Odsazení: 4 mezery • Délka řádku: pod 80 znaků • Prázdné řádky mezi funkcemi a třídami • Komentáře na separátních řádcích • Mezery mezi operátory – Ale žádné mezery u závorek při volání funkcí • Rozumné a konzistentní názvy funkcí – dlouhy_nazev_funkce – DlouhyNazevFunkce Příklady Další příklady