© Ragnar Nohre, Den 9:e Maj 2023 2 © Ragnar Nohre, 2021-2023 Innehåll 0 Välkomna till kursen OOP! 9 0.1 Anvisningar . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 1 Kortspelet 21 11 1.1 minafunktioner . . . . . . . . . . . . . . . . . . . . . . . . . . 13 1.2 Kort . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14 1.3 Kortlek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15 1.4 Korthand . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17 1.5 Spelare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18 1.6 Bankir . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19 1.7 main . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21 1.8 Modifiera spelet . . . . . . . . . . . . . . . . . . . . . . . . . . 22 2 Klassen Trace 23 2.1 Skapa ett nytt projekt . . . . . . . . . . . . . . . . . . . . . . 24 2.2 Deklarera klassen . . . . . . . . . . . . . . . . . . . . . . . . . 24 2.2.1 Implementera klassen . . . . . . . . . . . . . . . . . . . 25 2.3 Provkör . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27 2.4 Förenkla användandet med en define . . . . . . . . . . . . . 28 2.4.1 Ändra till klassvariabler . . . . . . . . . . . . . . . . . 29 2.5 Flytta kod till h-filen . . . . . . . . . . . . . . . . . . . . . . . 29 2.6 Skapa ny h-fil . . . . . . . . . . . . . . . . . . . . . . . . . . . 30 3 Strang och Komplex 33 3.1 Klassen Strang . . . . . . . . . . . . . . . . . . . . . . . . . . 33 3.1.1 Skapa klassdefinitionen . . . . . . . . . . . . . . . . . . 34 3.1.2 Implementera cpp-filen . . . . . . . . . . . . . . . . . . 34 3.1.3 Testa din strängklass . . . . . . . . . . . . . . . . . . . 38 3.1.4 Spåra implicita anrop! . . . . . . . . . . . . . . . . . . 39 3.2 Komplex . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40 3 3.2.1 Skapa nytt projekt . . . . . . . . . . . . . . . . . . . . 40 3.2.2 Implementera och testa . . . . . . . . . . . . . . . . . . 44 3.3 Polynom [F] . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45 4 Arv 47 4.1 Fordon . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47 4.1.1 Skapa basklassen Fordon . . . . . . . . . . . . . . . . . 49 4.1.2 Skapa klassen Bil . . . . . . . . . . . . . . . . . . . . 49 4.1.3 Skapa klassen Cykel . . . . . . . . . . . . . . . . . . . 51 4.1.4 Skapa testkoden och testa . . . . . . . . . . . . . . . . 51 4.2 Under skalet på arvsmekanismen [F] . . . . . . . . . . . . . . 51 4.2.1 Arv utan virtuella funktioner . . . . . . . . . . . . . . 51 4.2.2 Arv med virtuella funktioner . . . . . . . . . . . . . . . 54 4.3 Mer arvsrelaterad syntax . . . . . . . . . . . . . . . . . . . . . 56 4.3.1 Inparameter till basklassens konstruktör . . . . . . . . 56 4.3.2 Up Casting . . . . . . . . . . . . . . . . . . . . . . . . 57 4.3.3 Om referensanropet . . . . . . . . . . . . . . . . . . . . 57 4.3.4 Gör så att basklassen blir abstrakt . . . . . . . . . . . . 58 4.3.5 Anropa basklassens metod . . . . . . . . . . . . . . . . 58 4.3.6 Nyckelordet protected . . . . . . . . . . . . . . . . . . 59 4.3.7 Super [F] . . . . . . . . . . . . . . . . . . . . . . . . . 59 4.4 Fler experiment med arv . . . . . . . . . . . . . . . . . . . . . 60 4.4.1 Skapa projekt . . . . . . . . . . . . . . . . . . . . . . . 60 4.4.2 Skapa klasserna Bas och Sub . . . . . . . . . . . . . . 61 4.4.3 Experiment med virtuella metoder . . . . . . . . . . . 61 4.4.4 Experiment med virtuell destruktor . . . . . . . . . . . 63 4.4.5 Experiment med dynamisk typomvandling . . . . . . . 63 4.5 Spelarbot . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64 5 Felhantering 67 5.1 Hantera förväntade fel . . . . . . . . . . . . . . . . . . . . . . 67 5.1.1 Födelseår från namn . . . . . . . . . . . . . . . . . . . 68 5.1.2 Heltal från string . . . . . . . . . . . . . . . . . . . . . 70 5.2 Hantera exceptionellt ovanliga fel . . . . . . . . . . . . . . . . 71 6 Fönsterprogram 1: Masken 75 6.1 Skapa ett grafiskt projekt . . . . . . . . . . . . . . . . . . . . 75 6.1.1 Skapa en s.k. Widget Application . . . . . . . . . . . . 75 6.1.2 Provkör . . . . . . . . . . . . . . . . . . . . . . . . . . 76 6.1.3 Inspektera pro- och build-filerna . . . . . . . . . . . . . 77 6.1.4 Inspektera main.cpp . . . . . . . . . . . . . . . . . . . 78 4 © Ragnar Nohre, 2021-2023 6.1.5 Inspektera fönsterfilerna . . . . . . . . . . . . . . . . . 80 6.2 Skapa spelplansklassen . . . . . . . . . . . . . . . . . . . . . . 84 6.2.1 Skapa klassen Spelplan . . . . . . . . . . . . . . . . . 84 6.2.2 Skapa ett spelplansobjekt i designern . . . . . . . . . . 84 6.3 Implementera klassen Spelplan . . . . . . . . . . . . . . . . . 85 6.3.1 Påbörja implementeringen av spelplan.cpp . . . . . . . 88 6.3.2 Experiment med meddelandepumpen . . . . . . . . . . 91 6.4 Skapa maskens kropp . . . . . . . . . . . . . . . . . . . . . . . 92 6.5 ? Resten av spelet . . . . . . . . . . . . . . . . . . . . . . . . . 92 7 Fönsterprogram 2: Grafikeditor 95 7.1 Skapa grafiskt projekt . . . . . . . . . . . . . . . . . . . . . . 95 7.2 Skapa grafikyteklass . . . . . . . . . . . . . . . . . . . . . . . 95 7.3 Rita grafiska figurer . . . . . . . . . . . . . . . . . . . . . . . . 96 7.3.1 Uppgift: Rita ansikte . . . . . . . . . . . . . . . . . . . 98 7.4 Skapa en ren arkitektur . . . . . . . . . . . . . . . . . . . . . 98 7.4.1 Skapa programskelettet . . . . . . . . . . . . . . . . . . 100 7.4.2 Slutför kopplingarna . . . . . . . . . . . . . . . . . . . 105 7.5 Färdigställ Grafikyta . . . . . . . . . . . . . . . . . . . . . . . 105 7.6 Färdigställ dokument.cpp . . . . . . . . . . . . . . . . . . . . 107 7.6.1 Klasserna Figur Ellips och Rektangel . . . . . . . . 107 7.6.2 Klassen Dokument . . . . . . . . . . . . . . . . . . . . . 108 7.7 Färdigställ grafikapp.cpp . . . . . . . . . . . . . . . . . . . . . 110 7.7.1 musNed , musFlytt och musUpp . . . . . . . . . . . . . . 110 8 Egenskapsanimation [F] 113 8.1 Skapa bildresursen . . . . . . . . . . . . . . . . . . . . . . . . 113 8.2 Skapa bildyta . . . . . . . . . . . . . . . . . . . . . . . . . . . 114 8.2.1 Inkluderingsfilerna . . . . . . . . . . . . . . . . . . . . 115 8.2.2 Konstruktorn . . . . . . . . . . . . . . . . . . . . . . . 115 8.2.3 paintEvent . . . . . . . . . . . . . . . . . . . . . . . . . 116 8.2.4 mousePressEvent . . . . . . . . . . . . . . . . . . . . . 116 8.3 Visa baksidan . . . . . . . . . . . . . . . . . . . . . . . . . . . 118 8.3.1 Skapa ett menyalternativ . . . . . . . . . . . . . . . . . 118 8.3.2 Implementera visaFramsidan . . . . . . . . . . . . . . . 119 8.4 Uppgift: Refaktorisera koden! . . . . . . . . . . . . . . . . . . 120 9 Miniprojekt 123 9.1 Ritprogram . . . . . . . . . . . . . . . . . . . . . . . . . . . . 124 9.2 Memory . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125 9.3 Breakout . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125 © Ragnar Nohre, 2021-2023 5 10 Standardbiblioteket 127 10.1 Iteratorer och moderna for-loopar . . . . . . . . . . . . . . . . 127 10.2 Algoritmerna . . . . . . . . . . . . . . . . . . . . . . . . . . . 129 10.2.1 Kom igång . . . . . . . . . . . . . . . . . . . . . . . . . 130 10.2.2 transform och back_inserter . . . . . . . . . . . . . . . 131 10.2.3 iota . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133 10.2.4 generate_n . . . . . . . . . . . . . . . . . . . . . . . . 133 10.2.5 sort . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 134 10.2.6 stable_sort . . . . . . . . . . . . . . . . . . . . . . . . 135 10.2.7 remove_if . . . . . . . . . . . . . . . . . . . . . . . . . 137 10.2.8 count, count_if, och all_of . . . . . . . . . . . . . . . . 138 10.2.9 find, find_first_of och search . . . . . . . . . . . . . . 138 10.3 Prova containrar . . . . . . . . . . . . . . . . . . . . . . . . . 138 10.3.1 Prova vector . . . . . . . . . . . . . . . . . . . . . . . 138 10.3.2 Prova set<int> . . . . . . . . . . . . . . . . . . . . . . 139 10.3.3 Prova set<Person> . . . . . . . . . . . . . . . . . . . . 139 10.3.4 Prova 2D-vector . . . . . . . . . . . . . . . . . . . . . . 140 10.3.5 Prova skapa template . . . . . . . . . . . . . . . . . . . 140 10.4 Hitta anagram . . . . . . . . . . . . . . . . . . . . . . . . . . . 141 10.4.1 Hämta ordlista . . . . . . . . . . . . . . . . . . . . . . 141 10.4.2 Programskelett . . . . . . . . . . . . . . . . . . . . . . 142 10.4.3 Implementera skrivUtAnagram . . . . . . . . . . . . . 143 10.5 Smarta pekare . . . . . . . . . . . . . . . . . . . . . . . . . . . 143 11 Elva 145 A Qt Creator 147 A.1 Installera programutvecklingsmiljön . . . . . . . . . . . . . . . 147 A.1.1 Installera Visual Studio eller XCode . . . . . . . . . . . 147 A.1.2 Installera Qt Creator . . . . . . . . . . . . . . . . . . . 148 B När Qt Creator strular 151 B.1 Inga mellanslag eller åäö . . . . . . . . . . . . . . . . . . . . . 151 B.2 Använd inte åäö i variabelnamn etc. . . . . . . . . . . . . . . . 151 B.3 Tag bort pro.user och build . . . . . . . . . . . . . . . . . . . 151 B.4 Var inte för snabb . . . . . . . . . . . . . . . . . . . . . . . . . 152 B.5 Mac: Sökvägar till iostream mm hittas inte . . . . . . . . . . . 152 C Kodstandard 153 C.1 Namngivning . . . . . . . . . . . . . . . . . . . . . . . . . . . 153 C.1.1 Versaler och gemener . . . . . . . . . . . . . . . . . . . 153 6 © Ragnar Nohre, 2021-2023 C.1.2 Namnen . . . . . . . . . . . . . . . . . . . . . . . . . . 153 C.1.3 Medlemsvariabler i strukturer och klasser . . . . . . . . 154 C.1.4 Pekarvariabler . . . . . . . . . . . . . . . . . . . . . . . 154 C.1.5 Globala variabler . . . . . . . . . . . . . . . . . . . . . 154 © Ragnar Nohre, 2021-2023 7 8 © Ragnar Nohre, 2021-2023 Kapitel 0 Välkomna till kursen OOP! Detta kompendium innehåller preliminära programmeringslabbar för OOP, objektorienterad programmering . Eftersom kursen fortfarande är under utveckling kan kompendiet komma att ändras något under kursens gång. Vi kommer att använda samma programmeringsspråk och utvecklingsverktyg som i kurserna introProg och DALGO , dvs. C++ och Qt Creator Den största nyheten för i år (2023) är att jag har tagit bort vissa deluppgifter som har med fönsterprogrammering i Qt att göra. 0.1 Anvisningar De flesta laborationer i detta häfte inleds med en s.k. tutorial med steg-för-steg anvisningar och avslutas med några uppgifter som skall göras mer självständigt. För dem som vill arbeta ännu mer självständigt kan man i kapitel 9 välja mellan några frivilliga miniprojekt. Det är obligatoriskt att utföra alla laborationer (med undantag av den F-märkta labben 8 och miniprojekten i kapitel 9) men de redovisas oftast bara med Quizz. Man måste klara dem i rätt ordningsföljd, och om man kuggar på en quizz måste man vänta tills nästa vecka innan man får försöka igen. Några kanske är sugna på att bara göra quizzarna och skippa labbarna, 9 men detta är omoget och absolut inte rekommenderat! För att stävja denna typ av fusk brukar tentorna innehålla labrelaterade frågor som inte som inte är hämtade från från kursens quizzpool. Fördelen med quizz-redovisningar är att handledarna därmed kan ägna mer tid åt dem som vill ha hjälp eller feedback. Ta gärna för vana att visa upp några av veckans labbuppgifter för din handledare och be om muntlig feedback. Den som ej är färdig med alla quizzar när kursen tar slut måste invänta kursens resttentaperiod för att få ett nytt quizförsök. Den som ej är färdig med alla quizzar när nästa års kurs börjar kommer att få sina gamla quizzresultat nollställda. 10 © Ragnar Nohre, 2021-2023 Kapitel 1 Kortspelet 21 Uppgiften går ut på att programmera kortspelet 21. Motsvarande uppgift fanns i kursen introProg , och den finns också i kapitel 7 i Skansholms bok, men vi kommer här att använda mer objektorientering än vi gjorde tidigare och också lite mer objektorientering än Skansholm. Nedan ger jag en kort beskrivning av spelets regler: En person spelar mot en s.k. bank. Bankiren plockar kort från en kortlek och ger dem till spelaren som adderar dem till sin korthand. En kortands värde är summan av de enskilda kortens värden. Ett ess är värt 1 eller 14, spelaren får själv välja, och övriga kort har samma värde som deras valörer. Målet är att komma så nära 21 som möjligt utan att överskrida detta tal. Spelaren får stanna när den vill. Om korthandens värde överskrider 21 har spelaren förlorat (spruckit) och banken vunnit. Om spelaren får exakt 21 vinner den. Om spelaren stannat före 21 är det bankens (datorns) tur att ta kort till sin korthand. Banken måste ta kort så länge som dess korthands värde understiger 16, och måste stanna när värdet är 16 eller högre. Om banken spricker vinner spelaren, annars vinner den som fick flest poäng. Blev de lika vinner banken. Om man letar efter substantiv i ovanstående text hittar man flera verkliga s.k. domänobjekt , exempelvis person/spelare, bank, kort, kortlek, korthand. Dessa kommer att bli objekt i vårt program, och vi kommer därför att skapa motsvarande klasser. 11 Objektorienterade program har ofta en main -funktion som skapar ett antal objekt som kan sköta sig själva. Detta kortspelsprogram skall få en liknande struktur. I main -funktionen (bläddra fram till avsnitt 1.7 och kika lite) kommer vi att skapa ett bankirobjekt och ett spelarobjekt som vi kopplar ihop med ett anrop till registreraSpelare . Därefter kan bankiren i princip sköta sig själv. Inte sällan kommer bankiren att behöva kommunicera med spelarobjektet, t.ex. ge spelaren ett kort. Detta implementeras med metodanrop, bankiren kan exempelvis anropa en metod tagEmotKort på spelarobjektet och skicka med ett kort via en inparameter. (I oo-parlör brukar man säga att det objekt som utför anropet skickar ett meddelande till mottagaren.) Innan vi börjar kanske jag bör poängtera att målet inte att skriva ett så kort program som möjligt. Resultatet kommer istället att bli ett program som möjligtvis kan kännas lite onödigt stort, men som förhoppningsvis är enkelt att ändra i. Om vi senare förändrar spelet på olika sätt kommer vi förmodligen att kunna behålla klasserna och spelets övergripande struktur, detta eftersom klasserna är hämtade från den aktuella domänen, dvs från objekt som finns i verkligheten. Gör nu så här: 1. Börja med att skapa ett nytt projekt i Qt Creator (Non Qt Project, Plain C++ Application). Kalla projektet Kortspel21. 2. Använd Qt Creator fil-meny 1 för att skapa nya klasser med namnen: • minafunktioner . Vi vill egentligen inte ha någon klass med detta namn, men vi vill att få lite hjälp med att skapa en h- och en cpp-fil med samma namn. • Kort (det är ok att hitta på andra namn, t.ex. Card, men det måste vara ett substantiv i singular och inledas med en versal, se bilaga C.) • Kortlek • Korthand • Spelare • Bankir I de avsnitt som följer beskriver jag dessa klasser mer i detalj. 1 new file or project... C/C++ C++-class 12 © Ragnar Nohre, 2021-2023 1.1 minafunktioner Programmet behöver några (i alla fall en) globala/fria funktioner som vi skall placera i en fil med namnet minafunktioner.cpp . Motsvarande förhandsdeklarationer skall vi placera i minafunktioner.h . För att skapa en cpp- och h-fil med samma namn bad vi Qt Creator att skapa en klass med detta namn. Förutom en klassdefinition innehåller h-filen som bekant (se introProg ) några ifndef - , define - och endif -rader vars syften är att förhindra preproessorn från att inkludera h-filen flera gånger till samma cpp-fil. Tag bort klassdefinitionen från minafunktioner.h och förhandsdeklarera istället funktionen boolFranAnvandare . Eftersom funktionens inparameter är av typ std::string bör man också inkludera <string> . (Jag har för vana att till h-filer inkludera klassdefinitionerna för alla klasser h-filen använder.) När du är färdig bör minafunktioner.h ha följande innehåll: För att spara plats i detta labhäfte kommer jag framledes inte att visa h-filernas automatgenererade ifndef - , define - och endif -raderna, och ofta inte heller de filer jag inkluderat. I minafunktioner.cpp , radera konstruktören och implementera boolFranAnvandare enligt nedan (ersätt TODO med din kod): Vid anrop kommer inparametern txt normalt att innehålla en fråga, t.ex. Vill du ha ett nytt kort? . Funktionens uppgift är att skriva ut frågetexten i © Ragnar Nohre, 2021-2023 13 terminalfönstret, läsa användaren svar (normalt ja eller nej ) och returnera true eller false till anroparen. 1.2 Kort Klassen Kort skall kunna representera spelkort. Klassen känner till att ett spelkort kan ha 4 möjliga färger (klover, ruter, hjarter, spader) och 13 möjliga valörer (1. . . 13). Klassen känner också till hur spelkorten skall representeras som text ( klöver sju , spader dam , etc.). Knappa in nedanstående klassdefinition i kort.h (glöm inte att inkludera <string> ): Som synes innehåller ett kort två medlemsvariabler m_farg och m_valor Dessa skall få sina värden i konstruktören och därefter aldrig ändras. För att förhindra att klientprogrammeraren ändrar dem har vi gjort dem privata och infört metoderna farg() och valor() som skall returnera deras värden. Efter det att man knappat in en klassdefinition i en h-fil måste man också implementera den i motsvarande cpp-fil. Detta medför lite dubbelarbete, men med Qt Creator kan man lätt skapa motsvarande tomma medlemsfunktioner (s.k. stubbar ) i cpp-filen: I h-filen, kontextklicka på klassnamnet Kort och välj Refactor och sedan Create Implementations for Member Functions från popup-menyn. 14 © Ragnar Nohre, 2021-2023 När du väl skapat stubbarna i cpp-filen återstår det bara att implementera dem. Gör det nu! Nedan visar jag min egen implementering av text() : Medlemsfunktionen fargstrang (färgsträng) har jag själv implementerat m.h.a. fyra if -satser. 1.3 Kortlek Klassen Kortlek skall kunna representera en kortlek. En sprillans ny kortlek innehåller 52 olika kort sorterade i någon ordning. Man skall kunna blanda en sådan kortlek och sedan ta kort från den. Knappa in nedanstående klassdefinition i filen kortlek.h (glöm inte att inkludera kort.h och <vector> ): Den privata medlemsvariabeln m_korten lagrar alla kort i en vektor. Konstruktören skall initiera en tom lek (i praktiken kan konstruktören därför lämnas tom). Metoden antalKort() skall självklart returnera antalet kort i vektorn (dvs m_korten.size() ). Metoden utplockatKort() skall avlägsna ett kort från leken och returnera det. Metoden aterstallKortlek() (återställ kortlek) skall fylla leken med alla 52 kort, och blanda() skall blanda leken. Metoden aterstallBlandadKortlek() © Ragnar Nohre, 2021-2023 15 skall både återställa och därefter blanda leken. Den sistnämnda metoden är lite onödig, men den gör att anroparens s.k. klientkod blir snyggare att titta på. När man skapar en klass bör man fundera över hur klientkoden kommer att se ut, och eftersträva en kompakt och snygg klientkod. Den lönar sig i längden, för i praktiken är det ofta en själv som skriver klientkoden. Skapa nu stubbarna i klassens cpp-fil och implementera dem. Nedan visar jag min egen implementering av aterstallKortlek och blanda Funktionen blanda använder sig av grejer som finns deklarerade i inkluderingsfilerna <random> , <chrono> och <algorithm> : På raderna 39 och 40 initierar vi ett par static -variabler. En lokal static variabel är som en global variabel, fast den är bara är synlig i den funktion där den finns deklarerad. Variabeln seed kommer därför att initieras med sitt värde när programmet startar och inte varje gång blanda anropas. Det värde som seed får är antalet sekunder som gått sedan unix födelse den 1:a januari 1970. Detta värde används för att fröa det s.k. funktionsobjektet slumptalsgenerator . Det är mycket viktigt att även denna variabel är static då en slumptalsgenerator bara skall fröas en gång! (Du förväntas inte förstå alla detaljer kring funktionsobjekt just nu, de behandlas senare i 16 © Ragnar Nohre, 2021-2023 kursen.) Att blanda (eng. shuffle ) en array är ett välkänt problem som har en klassisk lösning 2 som påminner mycket om selectionSort . I en tidigare kurs ( introProg ) skulle man själv implementera denna blandningsalgoritm, men på rad 41 i ovanstående kod använder jag istället shuffle från <algorithm> 1.4 Korthand Klassen Korthand används för att representera en korthand, dvs de kort spelaren (och bankiren) har i sin hand. Den skall veta hur man räknar ut handens värde och exempelvis förstå att ett ess kan vara 14 eller 1. Knappa in nedanstående klassdefinition i korthand.h (glöm inte att inkludera kort.h och <vector> ): Som synes använder vi en vektor för att lagra handens kort. Konstruktören kan lämnas tom. Metoden rensa skall nollställa handen. Metoden antalKort skall returnera antalet kort i handen, kort(nr) skall returnera kort nummer nr , stoppaInKort(kort) skall addera kort till handen. (När man implementerar dessa metoder kan det vara bra att känna till att std::vector har metoderna clear , size , back , push_back , operatorn [i] , etc.). Metoden poang() skall returnera summan av kortens värden. Ett ess kan som sagt vara värt antingen 1 eller 14 (handens ägare väljer). Eftersom 2 läs mer på wiikipedian, https://en.wikipedia.org/wiki/Fisher–Yates_shuffle © Ragnar Nohre, 2021-2023 17 målet med spelet är att komma nära 21, utan att överskrida 21, skall alla ess räknas som 14 så länge det inte medför att handen spricker. Skapa stubbarna i korthand.cpp och implementera dem sedan. Verifiera att din implementering av poang() ger följande resultat: • hand: 4, 3, 3 => 10 • hand: 4, 1, 3 => 21 • hand: 4, 1, 4 => 9 • hand: 1, 1, 1, 1, 5 => 9 • hand: 1, 10,10,10 => 31 1.5 Spelare Klassen Spelare skall används för att representera en spelare. I praktiken kommer ett spelarobjekt att vara proxy (sv. ställföreträdare) för den människa som spelar mot datorn. (I en senare laboration, kapitel 4.5, skall vi byta ut spelaren mot en robot som följer någon spelstrategi.) Knappa in nedanstående klass i spelare.h Den privata variabeln m_hand lagrar spelarens korthand. Konstruktören kan lämnas tom. Metoden villSpela() skall returnera true om spelaren vill spela spelet. Din implementering bör (m.h.a. boolFranAnvandare ) fråga anvädaren om denne vill spela eller inte och returnera resultatet. Metoden villHaKort() skall på motsvarande sätt returnera true om spelaren vill ha ytterligare ett kort. 18 © Ragnar Nohre, 2021-2023 Metoden tagEmotKort(kort) skall addera kort till m_hand . Därtill skall metoden skriva ut kortet i terminalfönstret så att användaren av detta spel ser vilket kort den fick. Om spelaren stannar innan den spruckit kommer bankiren att ge sig själv kort. Nar man spelar 21 på riktigt kan man se dessa kort. I analogi med detta kommer bankiren att anropa bankenTogKort(kort) varje gång den gett sig själv ett kort. I din implementation av av denna metod behöver du bara skriva ut vilket kort bankiren tog i terminalfönstret. Innan bankiren påbörjar ett nytt spel skall den verifiera att kortleken har minst 15 kort. Om kortleken skulle ha färre än 15 kort skall bankiren ta fram en ny kortlek och blanda den väl. Det är viktigt att spelarna ser när detta sker. En del spelare kanske försöker minnas vilka kort som har gått och de måste nu få reda på att leken återställts. I analogi med detta kommer bankiren att anropa metoden kortlekenHarBlandats varje gång den återställt en ny kortlek. Din implementation av av denna metod behöver bara skriva ut texten kortleken har blandats i terminalfönstret. När spelrundan har avslutats och någon har vunnit kommer speletAvslutat att anropas, inparametern anger vem som vunnit. Din implementation bör skriva ut vinnaren i terminalfönstret. När man spelar 21 på riktigt är spelarens korthand öppen (d.v.s inte hemlig). I analogi med detta har spelarklassen en metod korthand som skall returnera korthanden. Med hjälp av denna kan bankiren exempelvis undersöka om spelaren har spruckit. 1.6 Bankir När man spelar 21 på riktigt är det den s.k. bankiren som styr spelet. Bankiren känner till alla spelarna och det är bankiren som äger kortleken. Den frågar spelarna om de vill ha fler kort, och ger dem nya om de svarar ja. Korten ges med framsidan upp. Om en spelare t.ex. skulle spricka ser bankiren detta. Så här ser Bankir -klassen ut: © Ragnar Nohre, 2021-2023 19 Som synes innehåller en bankir en kortlek och en pekare till spelaren (vårt spel har bara en spelare). Knappa in klassen i bankir.h och skapa motsvarande stubbar i cpp-filen. Kortleken initierar sig själv, men bankirens konstruktör måste initiera m_pSpelare till nullptr , eftersom den från början inte känner till någon spelare. Metoden registreraSpelare skall lagra den bifogade pekaren i m_pSpelare och harRegistreradeSpelere skall returnera true omm m_pSpelare != nullptr Metoden spelaEnOmgang skall spela en omgång mot spelaren. Nedan visar jag min egen implementering. Läs igenom koden noggrant så att du förstår hur spelet skall fungera och implementera sedan gärna din egen version av koden. 20 © Ragnar Nohre, 2021-2023