Datorlaborationer
Transcription
Datorlaborationer
LUNDS TEKNISKA HÖGSKOLA Institutionen för datavetenskap EDA017 Programmeringsteknik för E, I och Pi HT 2013 Datorlaborationer Anvisningar Datorlaborationerna ger exempel på tillämpningar av det material som behandlas under kursen och ger träning på att skriva program. Det finns 9 stycken datorlaborationer. Anmälan till laborationsgrupp sker via kursens hemsida. Laboration 1-5 går HT2 2013 och 6-9 är schemalagda till VT1 2014. Laborationerna är individuella, det vill säga att de ska lösas med självständigt enskilt arbete. Det är tillåtet att diskutera laborationerna och dess lösningar med kurskamraterna, men var och en måste skriva sin egen lösning. Du måste arbeta med varje laboration under ”rätt” vecka (om du inte är sjuk, se nedan). Om du är mycket ambitiös kan du dock arbeta i förväg och få flera uppgifter godkända vid ett redovisningstillfälle, under förutsättning att laborationsledaren har tid med detta. Du får däremot inte komma i efterhand och kräva att bli godkänd på flera uppgifter (utom om du har varit sjuk). Till varje laboration finns det förberedelser som du ska göra före laborationen. Kontakta kursansvarig om det dyker upp några frågor när du förbereder laborationen. • Till varje laborationsuppgift finns det läsanvisningar och förberedelseuppgifter. Före varje laboration måste du ha: – studerat läroboken enligt läsanvisningarna, – läst igenom hela laborationen noggrant, – löst förberedelseuppgifterna. I dessa uppgifter ska du skriva delar av de program som ingår i laborationen. Det krävs inte att allt du skrivit är helt korrekt, men du måste ha gjort ett rimligt försök. Kontakta en lärare om du får problem med uppgifterna. Vi förutsätter att du har förberett dig enligt ovan innan du kommer till laborationen — det är nödvändigt för att labhandledaren ska kunna hjälpa dig på bästa sätt. Om du har förberett dig väl bör du hinna med alla uppgifterna under laborationen. Om du inte gör det så får du göra de resterande uppgifterna på egen hand och redovisa dem vid påföljande laborationstillfälle eller resurstillfälle (och förbereda dig mera till nästa laboration). • Om du är sjuk vid något laborationstillfälle så måste du anmäla detta till kursansvarig (Sandra.Nilsson@cs.lth.se) före laborationen. Om du uteblir utan att ha anmält sjukdom så får du inte göra uppgiften förrän kursen går nästa gång, dvs nästa år, och då får du inte något slutbetyg i kursen i år. Om du varit sjuk bör du göra uppgiften på egen hand och redovisa den vid ett senare tillfälle. Har du varit sjuk och behöver hjälp för att lösa laborationen så gå till något av de resurstillfällen som finns tillgängliga för det program du läser. Det kommer också att anordnas en ”uppsamlingslaboration” i slutet av varje termin. Laboration 1 – introduktion 2 Laboration 1 – introduktion Mål: Under denna laboration ska du lära dig vad ett datorprogram är och se exempel på enkla program. Du ska också lära dig att editera, kompilera och exekvera enkla Java-program med hjälp av programutvecklingsverktyget Eclipse. Du ska också prova att använda Eclipse debugger. Förberedelseuppgifter • Läs igenom kapitel 1 i läroboken. • Läs avsnittet Bakgrund. • Läs igenom anvisningarna om Eclipse (finns i kurskompendiet). Bakgrund Ett datorprogram är ett antal rader text som beskriver lösningen till ett problem. Vi börjar med att titta på ett mycket enkelt problem: att från datorns tangentbord läsa in två tal och beräkna och skriva ut summan av talen. Lösningen kan se ut så här i Java: 1 2 3 4 5 6 7 8 9 10 11 12 import java.util.Scanner; public class Calculator { public static void main(String[] args) { System.out.println("Skriv två tal"); Scanner scan = new Scanner(System.in); double nbr1 = scan.nextDouble(); double nbr2 = scan.nextDouble(); double sum = nbr1 + nbr2; System.out.println("Summan av talen är " + sum); } } Detta är nästan samma program som beskrivs i Eclipse-handledningen, men talen som summeras läses från tangentbordet. Du behöver inte förstå detaljerna i programmet nu, men vi vill redan nu visa ett program som gör någonting intressantare än bara skriver ut ”Hello, world!”. Allt som behövs för att du ska förstå programmet kommer vi att gå igenom under de första veckorna av kursen. Förklaring av de viktigaste delarna av programmet: • Rad 1: en inläsningsklass Scanner importeras från ett ”paket” med namnet java.util. Detta paket och många andra ingår i Javas standardbibliotek. • Rad 3: public class Calculator talar om att programmet, klassen, heter Calculator. • Rad 4: public static void main(String[] args) talar om att det som vi skriver är ett ”huvudprogram”. Varje Javaprogram måste innehålla en klass med en main-metod. Exekveringen börjar och slutar i main-metoden. • Rad 5: texten Skriv två tal skrivs ut på skärmen (println betyder ”print line”). När man använder Eclipse så hamnar utskrifter i konsolfönstret. • Rad 6: inläsningsobjektet scan skapas. Vi kommer senare att gå igenom ordentligt vad detta betyder — nu räcker det att du vet att man alltid måste skapa ett sådant objekt när man vill läsa från tangentbordet. • Rad 7: en variabel nbr1 som kan innehålla ett reellt tal (double-tal) deklareras. Det betyder att man skapar plats för variabeln i datorns minne. Man tilldelar också nbr1 ett talvärde som man läser in från tangentbordet med nextDouble(). • Rad 8: samma sak med variabeln nbr2. Laboration 1 – introduktion 3 • Rad 9: variabeln sum deklareras. Talen nbr1 och nbr2 adderas och summan lagras i sum. • Rad 10: resultatet (innehållet i variabeln sum) skrivs ut. • Rad 11: slut på main-metoden. • Rad 12: slut på klassen. Uppgifter 1. Logga in på datorn. Öppna ett terminalfönster. 2. Under laborationerna kommer du att använda en hel del färdigskrivna eller nästan färdigskrivna program. Nu ska du skapa ett Eclipse-arbetsområde (workspace) som innehåller alla dessa filer. 1. Ett förberett arbetsområde finns i packad form i filen eda017-workspace.zip som finns i katalogen /usr/local/cs/eda017/. Flytta musmarkören till terminalfönstret så att det blir aktivt och skriv följande kommandon: cd unzip /usr/local/cs/eda017/eda017-workspace.zip cd betyder att du flyttar dig till din hemkatalog, unzip packar upp filen. 2. Nu har du fått en katalog eda017-workspace i din hemkatalog. Katalogen innehåller underkatalogerna cs_eda017, cs_eda017_src, lab1, lab2, lab3, . . . Kontrollera innehållet i ptdc-workspace med följande kommando: ls eda017-workspace I ptdc-workspace finns också en katalog .metadata. När du senare startar Eclipse skapas i din hemkatalog en katalog .eclipse. Dessa kataloger syns inte när du gör ls, eftersom deras namn inleds med punkt. Rör inte dessa kataloger — de används av Eclipse för att spara viktig information, och om de inte finns eller har fel innehåll så går det inte att starta Eclipse. Alternativt kan du hämta filen eda017-workspace.zip från kursens hemsida: 1. Starta en webbläsare och gå till kursens hemsida, cs.lth.se/kurs/eda017. 2. Klicka på Laborationer. 3. Ladda ner filen eda017-workspace.zip till din hemkatalog. 4. Packa upp filen enligt punkt 1 ovan. 5. .zip-filen behövs inte mera. Tag bort den med följande kommando: rm eda017-workspace.zip 3. Nu ska du starta Eclipse. Ge följande kommando: eclipse & Efter en stund får du en dialogruta där Eclipse frågar efter vilket arbetsområde som du vill använda. Ändra förslaget workspace till eda017-workspace. &-tecknet betyder att Eclipse ska startas ”i bakgrunden”, alltså som ett fristående program. Det medför att man inte låser terminalfönstret utan kan arbeta med andra saker i Laboration 1 – introduktion 4 det samtidigt som Eclipse kör. Alternativt kan du starta Eclipse genom att välja Eclipse (den senaste versionen) från Gnome-menyn Applications > Programming. I Eclipse-fönstret finns i projektvyn (”Package Explorer”, längst till vänster) projekten cs_eda017, cs_eda017_src, lab1, lab2, lab3, . . . — det är de projektkataloger som finns i ptdc-workspace. Filerna med namn som börjar på lab innehåller färdigskrivna program som utnyttjas under laborationerna. cs_eda017 innehåller en biblioteksfil (.jar-fil) med klasser som utnyttjas. cs_eda017_src innehåller källkoden (.java-filerna) till dessa klasser — de behövs inte för laborationerna, men en del brukar vara intresserade av att titta på dem. 4. Du ska nu ladda in filen Calculator.java i en editor så att du kan ändra den. Man måste klicka en hel del för att öppna en fil: a) Öppna projektet lab1 genom att klicka på pilen bredvid projektet. b) Öppna katalogen src genom att klicka på pilen. c) Öppna paketet (default package) genom att klicka på pilen. d) Öppna filen Calculator.java genom att dubbelklicka på filnamnet. Filen öppnas i en editorflik. 5. Kör programmet: markera Calculator.java i projektvyn, högerklicka och välj Run As > Java Application. I konsolfönstret skrivs texten Skriv två tal. Klicka i konsolfönstret, skriv två tal och tryck på return. Observera: när man skriver reella tal ska man vid inläsning använda decimalkomma. När man skriver reella tal i programkod använder man decimalpunkt. Man kan köra det senaste programmet en gång till genom att klicka på Run-ikonen i verktygsraden. 6. Ändra main-metoden i klassen Calculator så att fyra rader skrivs ut: talens summa, skillnad, produkt och kvot. Exempel på utskrift när talen 24 och 10 har lästs in: Summan av talen är 34.0 Skillnaden mellan talen är 14.0 Produkten av talen är 240.0 Kvoten mellan talen är 2.4 Du ska alltså efter utskriften av summan lägga in rader där talens skillnad, produkt och kvot beräknas och skrivs ut. Subtraktion anger man med tecknet -, multiplikation med *, division med /. Under tiden du skriver så kommer du att märka att Eclipse hela tiden kontrollerar så att allt du skrivit är korrekt. Fel markeras med kryss till vänster om raden. Om du håller musmarkören över ett kryss så visas en förklaring av felet. Om man sparar en fil som innehåller fel så visas felmeddelandena också i Problem-fliken längst ner. Du kommer också att märka att Eclipse kommer med förslag på vad du kan skriva. När du till exempel har skrivit System.out.p så får du upp en ruta med allt som man kan göra med System.out som börjar med p. Det här tycker en del är bra, andra tycker att det är irriterande. Under nästa laboration ska vi visa hur man stänger av denna hjälp, om man inte vill ha den. Laboration 1 – introduktion 5 7. Spara filen. Filen kompileras automatiskt när den sparas. När .java-filen kompileras skapas en ny fil med samma namn som klassen men med tillägget .class. Denna fil innehåller programmet översatt till byte-kod och används när programmet exekveras. Man ser inte .class-filerna inuti Eclipse, men de lagras i arbetsområdet precis som .java-filerna. .java-filerna finns i en katalog src under respektive projektkatalog, .classfilerna finns i en katalog bin. 8. Kör programmet och kontrollera att utskriften är korrekt. Rätta programmet om den inte är det. 9. Du ska nu använda Eclipse debugger för att följa exekveringen av programmet. Normalt använder man debuggern för att hitta fel i program, men här är avsikten bara att du ska se hur programmet exekverar rad för rad och att du ska bekanta dig med debuggerkommandona. • Sätt en brytpunkt på den första raden i main-metoden. Kör programmet under debuggern (Debug As > Java Application). • Svara ja på frågan om du vill byta till Debug-perspektivet. • Klicka på Step Over-ikonen några gånger. Notera att den rad i programmet som ska exekveras markeras i editorfönstret och att variabler som deklareras dyker upp i variabelvyn till höger. Variablernas aktuella värden visas i ett av eclipse-fönsterna, se till att du ser detta och kan identifiera hur variablerna får sina värden under programmets exekvering. • Sätt en brytpunkt på raden där du skriver kvoten mellan talen. • Klicka på Resume-ikonen för att köra programmet fram till brytpunkten. • Klicka på Resume igen för att köra programmet till slut. Byt tillbaka till Java-perspektivet genom att klicka på knappen Java längst till höger i verktygsfältet. 10. Följande program använder den färdiga klassen SimpleWindow: import se.lth.cs.eda017.window.SimpleWindow; public class SimpleWindowExample { public static void main(String[] args) { SimpleWindow w = new SimpleWindow(500, 500, "Drawing Window"); w.moveTo(100, 100); w.lineTo(150, 100); } } Specifikationen av klassen SimpleWindow finns i appendix C i läroboken, eller online via länk från kurshemsidan. Förklaring av de viktigaste delarna av programmet: • På den första raden importeras den färdiga klassen SimpleWindow. • Raderna som börjar med public och de två sista raderna med } är till för att uppfylla Javas krav på hur ett program ska se ut. • På nästa rad deklareras en referensvariabel med namnet w och typen SimpleWindow. Därefter skapas ett SimpleWindow-objekt med storleken 500 × 500 pixlar och med titeln ”Drawing Window”. Referensvariabeln w tilldelas det nya fönsterobjektet. Laboration 1 – introduktion 6 • Sedan flyttas fönstrets ”penna” till punkten 100, 100. • Slutligen ritas en linje. Programmet finns inte i arbetsområdet, utan du ska skriva det från början. Skapa en fil SimpleWindowExample.java: a) Markera projektet lab1 i projektvyn, b) Klicka på New Java Class-ikonen i verktygsraden. c) Skriv namnet på klassen (SimpleWindowExample). d) Klicka på Finish. Dubbelklicka på SimpleWindowExample.java för att ladda in filen i editorn (om detta inte redan gjorts automatiskt). Som du ser har Eclipse redan fyllt i klassnamnet och de parenteser som alltid ska finnas. Komplettera klassen med main-metoden som visas ovan. Spara filen, rätta eventuella fel och spara igen. Provkör programmet. 11. Ändra programmet genom att välja bland metoderna i klassen SimpleWindow. Till exempel kan du rita en kvadrat, ändra färg och linjebredd på pennan, rita fler linjer, skriva text, etc. 12. Öppna filen LineDrawing.java. I filen finns följande kod, komplettera programmet så att det fungerar. Raderna med kommentarer ska ersättas med riktig programkod. public class LineDrawing { public static void main(String[] args) { SimpleWindow w = new SimpleWindow(500, 500, "LineDrawing"); w.moveTo(0, 0); while (true) { // vänta tills användaren klickar på en musknapp // rita en linje till den punkt där användaren klickade } } } Ledtråd: SimpleWindow innehåller också operationer för att ta hand om musklick. Dessa operationer har följande beskrivning: /** Väntar tills användaren har klickat på en musknapp */ void waitForMouseClick(); /** Tar reda på x-koordinaten för musens position vid senaste musklick */ int getMouseX(); /** Tar reda på y-koordinaten för musens position vid senaste musklick */ int getMouseY(); Fundera ut vad som händer i programmet. while (true) betyder att repetitionen ska fortsätta ”i oändlighet”. Man avbryter programmet genom att välja Quit i File-menyn i SimpleWindow-fönstret. Spara filen, rätta eventuella fel och spara då igen. Provkör programmet. Checklista Exempel på vad du ska kunna efter laborationen: Laboration 1 – introduktion 7 • Starta Eclipse, öppna önskat arbetsområde (workspace) och öppna önskat projekt. • Skapa .java-filer och skriva in enkla Java-program. (Med enkla program menas här ett program på några rader som till exempel Calculator.) • Kunna använda operationen System.out.println och förstå vad som händer när den används. • Förstå vad det innebär att läsa in tal från tangentbordet. • Spara .java-filer (kompilering sker då automatiskt). • Exekvera program. • Använda debuggern: – Sätta och ta bort brytpunkt. – Starta debuggern. – Köra fram till en brytpunkt med Resume. – Exekvera stegvis med Step over (och senare i kursen med Step Into). – Byta mellan debug-perspektivet och Java-perspektivet. Laboration 2 – använda färdigskrivna klasser, kvadrat 8 Laboration 2 – använda färdigskrivna klasser, kvadrat Mål: Du ska lära dig att läsa specifikationer av klasser och att utnyttja färdigskrivna klasser för att lösa enkla uppgifter. Du ska också lära dig lite mera om Eclipse. Förberedelser • • • • Studera avsnitt 2 och 3.1-3.2 i läroboken. Läs avsnittet Bakgrund. Lös uppgift 2.2 och 2.5 i läroboken. Tänk igenom följande frågor och se till att du kan svara på dem: 1. 2. 3. 4. 5. Vad är en main-metod? Hur ser en klass med en main-metoden ut i stora drag? Vad är referensvariabler och vad har man dem till? Hur skapar man objekt? Hur utför man en operation på ett objekt? Vad använder man parametrar till? Bakgrund En klass Square som beskriver kvadrater har nedanstående specifikation. /** Skapar en kvadrat med övre, vänstra hörnet i x,y och med sidlängden side. */ Square(int x, int y, int side); /** Ritar kvadraten i fönstret w. */ void draw(SimpleWindow w); /** Raderar bilden av kvadraten i fönstret w. */ void erase(SimpleWindow w); /** Flyttar kvadraten avståndet dx i x-led, dy i y-led. */ void move(int dx, int dy); /** Tar reda på x-koordinaten för kvadratens läge. */ int getX(); /** Tar reda på y-koordinaten för kvadratens läge. */ int getY(); /** Tar reda på kvadratens area. */ int getArea(); Användning av klassen Square I nedanstående program skapas först ett ritfönster. Därefter skapas en kvadrat som placeras mitt i fönstret och ritas upp. import se.lth.cs.eda017.window.SimpleWindow; import se.lth.cs.eda017.square.Square; public class DrawSquare { public static void main(String[] args) { Laboration 2 – använda färdigskrivna klasser, kvadrat 9 SimpleWindow w = new SimpleWindow(600, 600, "DrawSquare"); Square sq = new Square(250, 250, 100); sq.draw(w); } } • På rad 1 och rad 2 importeras de klasser som behövs, i detta fall: klassen SimpleWindow och klassen Square. De klasserna finns inte i Javas standardbibliotek utan har skrivits speciellt för kurserna i programmeringsteknik. Klasserna finns i ”paket” vars namn börjar med se.lth.cs; det betyder att de är utvecklade vid institutionen för datavetenskap vid LTH, som använder domännamnet cs.lth.se. • På rad 5 skapas ett SimpleWindow-objekt. Refererensvariabeln w refererar till detta objekt. • Därefter skapas ett Square-objekt som referensvariabeln sq refererar till. • Slutligen ritas kvadraten sq. Notera parametrarna som man använder när man skapar objekten. I new-uttrycket som skapar kvadratobjektet står det till exempel (250, 250, 100). Det betyder att kvadraten ska ha läget 250, 250 och sidlängden 100. Läget och sidlängden kan man ändra senare i programmet, med sq.move(dx,dy) och sq.setSide(newSide). Lägg också märke till att referensvariablerna w och sq har olika typer. En referensvariabels typ avgör vilka slags objekt variabeln får referera till. w får referera till SimpleWindow-objekt och sq får referera till Square-objekt. Användning av klassen SimpleWindow Bläddra fram källkoden till klassen Square, ni hittar klassen i Eclipse-projektet cs_eda017_src under katalogen src och i paketet se.lth.cs.eda017.square. Titta på källkoden och försök förstå vad som händer. Några av operationerna i SimpleWindow (operationerna för att flytta pennan och för att rita linjer) utnyttjas i operationen draw i klassen Square. SimpleWindow innehåller också operationer, som inte används i Square, t.ex. för att ta hand om musklick. Dessa operationer har följande beskrivning: /** Väntar tills användaren har klickat på en musknapp. */ void waitForMouseClick(); /** Tar reda på x-koordinaten för musens position vid senaste musklick. */ int getMouseX(); /** Tar reda på y-koordinaten för musens position vid senaste musklick. */ int getMouseY(); När exekveringen av ett program kommer fram till waitForMouseClick så ”stannar” programmet och fortsätter inte förrän man klickat med musen någonstans i programmets fönster. Ett program där man skapar ett fönster och skriver ut koordinaterna för varje punkt som användaren klickar på har följande utseende: import se.lth.cs.eda017.window.SimpleWindow; public class PrintClicks { public static void main(String[] args) { SimpleWindow w = new SimpleWindow(600, 600, "PrintClicks"); while (true) { Laboration 2 – använda färdigskrivna klasser, kvadrat 10 w.waitForMouseClick(); w.moveTo(w.getMouseX(), w.getMouseY()); w.writeText("x = " + w.getMouseX() + ", " + "y = " + w.getMouseY()); } } } while (true) betyder att repetitionen ska fortsätta ”i oändlighet”. Man avbryter programmet genom att välja Quit i File-menyn i SimpleWindow-fönstret. Också i nedanstående program ska användaren klicka på olika ställen i fönstret. Nu är det inte koordinaterna för punkten som användaren klickar på som skrivs ut, utan i stället avståndet mellan punkten och den förra punkten som användaren klickade på. Vi sparar hela tiden koordinaterna för den förra punkten (variablerna oldX och oldY). Gå igenom ett exempel ”för hand” och övertyga dig om att du förstår hur programmet fungerar. Laboration 2 – använda färdigskrivna klasser, kvadrat 11 import se.lth.cs.eda017.window.SimpleWindow; public class PrintClickDists { public static void main(String[] args) { SimpleWindow w = new SimpleWindow(600, 600, "PrintClickDists"); int oldX = 0; // x-koordinaten för "förra punkten" int oldY = 0; // y-koordinaten while (true) { w.waitForMouseClick(); int x = w.getMouseX(); int y = w.getMouseY(); w.moveTo(x, y); int xDist = x - oldX; int yDist = y - oldY; w.writeText("Avstånd: " + Math.sqrt(xDist * xDist + yDist * yDist)); oldX = x; oldY = x; } } } Datorarbete 1. Logga in på datorn, starta Eclipse, öppna projektet lab2. (Logga in, starta Eclipse och öppna ett projekt ska du alltid göra, så det skriver vi inte ut i fortsättningen.) 2. Öppna en webbläsare och gå till kursens hemsida, cs.lth.se/kurs/eda017. Under Dokumentation finns en länk till dokumentationen av de färdigskrivna klasser som används under laborationerna, alltså klasserna i paketet se.lth.cs.eda017. Leta upp och titta igenom specifikationen av klassen Square. Det är samma specifikation som finns under Bakgrund, fast i något annorlunda form. 3. Klassen DrawSquare finns i filen DrawSquare.java. Öppna filen, kör programmet. 4. Kopiera filen DrawSquare.java till en ny fil med namnet DrawThreeSquares.java. Enklast är att göra så här: 1. Markera filen i projektvyn, högerklicka, välj Copy. 2. Högerklicka på (default package), välj Paste, skriv det nya namnet på filen. Notera att Eclipse ändrar klassnamnet i den nya filen till DrawThreeSquares. Ändra sedan klassen så att kvadraten ritas tre gånger. Mellan uppritningarna ska kvadraten flyttas. Du ska fortfarande bara skapa ett kvadratobjekt i programmet. Resultatet ska bli en figur med ungefär följande utseende: Testa programmet, rätta eventuella fel. 12 Laboration 2 – använda färdigskrivna klasser, kvadrat 5. Någonstans i ditt program skapar du ett kvadratobjekt med en sats som har ungefär följande utseende: Square sq = new Square(300,300,200). Tag bort denna sats från programmet (eller kommentera bort den). Eclipse kommer att markera flera fel i programmet, eftersom sq inte är deklarerad och du senare i programmet utnyttjar den variabeln. Läs och tolka felmeddelandena. Lägg sedan in satsen Square sq = null i början av programmet. Eclipse kommer inte att hitta några fel, eftersom programmet nu följer de formella reglerna för Javaprogram. Exekvera sedan programmet och se vad som inträffar. Studera felmeddelandet så att du kan tolka det. Meddelanden om exekveringsfel skrivs i konsolfönstret. Det skrivs också ut en ”stack trace” för felet: var felet inträffade, vilken metod som anropade metoden där det blev fel, vilken metod som anropade den metoden, osv. Om man klickar på ett radnummer öppnas rätt fil i editorn med den raden markerad (under förutsättning att programtexten, ”källkoden”, för den filen är tillgänglig). Lägg sedan tillbaka den ursprungliga satsen för att skapa kvadratobjektet. Ändra parametern w i det första anropet av draw till null. Kör programmet, studera det felmeddelande som du får. När man får exekveringsfel kan det vara svårt att hitta orsaken till felet. Här är en debugger till stor hjälp, i och med att man kan sätta brytpunkter och köra programmet stegvis. 6. Kör programmen PrintClicks och PrintClickDists. 7. Skriv ett program där ett kvadratobjekt skapas och ritas upp i ett ritfönster. När användaren klickar med musen i fönstret ska bilden av kvadraten raderas, kvadraten flyttas till markörens position och ritas upp på nytt. Titta gärna på den totala specifikationen av SimpleWindow och Square som finns länkat från kurshemsidan. Vilka metoder som verkar användbara för detta kan du hitta i dokumentationen? Välj ett lämpligt namn på klassen. För att skapa en fil för klassen kan du antingen skapa en tom klass (klicka på New Java Class-ikonen, skriv namnet på klassen, klicka på Finish) eller kopiera någon av de filer som du redan har. Testa programmet. 8. Modifiera programmet i uppgift 7 så att varje flyttning går till så att kvadraten flyttas stegvis till den nya positionen. Efter varje steg ska kvadraten ritas upp (utan att den gamla bilden raderas). Exempel på kvadratbilder som ritas upp när flyttningen görs i 10 steg: klickat här ursprunglig position Kvadratens slutliga position behöver inte bli exakt den position som man klickat på. Om man till exempel ska flytta kvadraten 94 pixlar i 10 steg är det acceptabelt att ta 10 steg med längden 9. Skriv in och testkör programmet. Laboration 2 – använda färdigskrivna klasser, kvadrat 9. 13 Observera att programmet från uppgift 8 ritar många bilder av samma kvadrat och att alla bilderna kommer att synas i fönstret när programmet är slut. Om man raderar den ”gamla” bilden innan man ritar en ny bild så kommer man att få en ”rörlig”, ”animerad”, bild. För att man ska se vad som händer måste man då göra en paus mellan uppritning och radering — det gör man med SimpleWindow-metoden delay, som har en parameter som anger hur många millisekunder man ska vänta. Exempel: while (sq.getSide() > 0) { sq.draw(w); SimpleWindow.delay(10); sq.erase(w); sq.setSide(sq.getSide() - 10); } Kodsnutten ovan animerar en ny mindre kvadrat tills dess att kvadratens sida blivit mindre än eller lika med noll. Kopiera koden från uppgift 8 till en ny fil AnimatedSquare.java. Lägg in fördröjning och radering så att kvadratbilden blir ”animerad”. Anmärkning: i ett ”riktigt” program som visar rörliga bilder åstadkommer man inte animeringen på det här sättet. Denna lösning har bristen att man inte kan göra något annat under tiden som animeringen pågår, till exempel kan programmet inte reagera på att man klickar med musen. 10. I denna uppgift ska du lära dig fler kommandon i Eclipse. Det finns ett otal kommandon, mer eller mindre avancerade, och många av dem använder man sällan. Tre kommandon som man ofta använder: • Source-menyn > Format. Korrigerar programlayouten i hela filen. Ser till exempel till att varje sats skrivs på en rad, att det är blanka runt om operatorer, och så vidare. Man bör formatera sina program regelbundet, så att de blir läsbara. Observera att programmet ska vara korrekt formaterat när du visar det för labhandledaren för att få det godkänt. Notera också kortkommandot som står intill menyalternativet. Prova att använda kortkommandot också, och lär dig gärna det, då det är snabbare och mer bekvämt än att klicka. • Refactor-menyn > Rename. Ändrar namn på den variabel eller metod som markerats (lokalt om det är en lokal variabel, i hela klassen om det är ett attribut, även i andra klasser om det är en publik metod). Man kan ställa in hur mycket hjälp man ska få av editorn när man skriver Javaprogram. Det gör man i Preferences. . . i Window-menyn. Det finns hur många möjligheter som helst att ändra hur Eclipse uppför sig. Om du ändrar i Preferences och har ställt till det för dig: använd knappen Restore Defaults! Om fönstret skulle se konstigt ut: gör Reset Perspective i Windows-menyn. 11. Kontrollera med hjälp av checklistan nedan att du behärskar de olika momenten i laborationen. Diskutera med övningsledaren om någonting är oklart. Checklista Exempel på vad du ska kunna efter laborationen: • Tyda specifikationer av klasser. • Skriva program som använder färdiga klasser: 14 Laboration 2 – använda färdigskrivna klasser, kvadrat – Deklarera referensvariabler och skapa objekt. – Utföra operationer på objekt. Laboration 3 – implementera klasser, kvadrat 15 Laboration 3 – implementera klasser, kvadrat Mål: Du ska träna på att implementera klasser. Förberedelseuppgifter • Studera kap 3 i läroboken noggrant. • Läs igenom texten i avsnittet Bakgrund. Bakgrund Klassen Point beskriver en punkt. Den har följande specifikation: /** Skapar en punkt med koordinaterna x,y. */ Point(int x, int y); /** Tar reda på x-koordinaten. */ int getX(); /** Tar reda på y-koordinaten. */ int getY(); /** Flyttar punkten avståndet dx i x-led, dy i y-led. */ void move(int dx, int dy); /** Returnerar avståndet mellan denna punkt och punkten p. */ double distanceTo(Point p); /** Returnerar en teckensträng som representerar punkten. Strängen innehåller punktens koordinater. Ex: 150 200 */ String toString(); Specifikationen är till för den som ska använda klassen när den är färdig. Här ser man hur man skapar Point-objekt och vilka metoder man kan anropa på ett sådant. String är en typ (egentligen en klass) som används för att beskriva en teckensträng, dvs. en följd av tecken. Se avsnitt 6.7 i läroboken. Implementeringen av klassen Point ser ut så här: public class Point { private int x; // punktens x-koordinat private int y; // punktens y-koordinat /** Skapar en punkt med koordinaterna x,y. */ public Point(int x, int y) { this.x = x; this.y = y; } /** Tar reda på x-koordinaten. */ public int getX() { return x; } /** Tar reda på y-koordinaten. */ public int getY() { return y; } Laboration 3 – implementera klasser, kvadrat 16 /** Flyttar punkten avståndet dx i x-led, dy i y-led. */ public void move(int dx, int dy) { x = dx; y = dy; } /** Returnerar avståndet mellan denna punkt och punkten p. */ public double distanceTo(Point p) { return Math.hypot(x - p.x, y - p.y); } /** Returnerar en teckensträng som representerar punkten. Strängen innehåller punktens koordinater. Ex: 150 200 */ public String toString() { return x + " " + y; } } Observera skillnaden mellan specifikationen (beskrivningen) av klassen och implementeringen (programkoden). Implementeringen av klassen Point ska finnas i en egen fil med namnet Point.java. Den innehåller den fullständiga programkoden för klassen. Här deklareras attribut och här finns de satser som utförs när en metod anropas. Studera klassen ordentligt och försäkra dig att du förstår alla detaljer. Tänk igenom följande frågor och se till att du kan svara på dem: 1. 2. 3. 4. 5. Vilka attribut finns i klassen Point? Vad händer inuti konstruktorn? Vad menas med public och private? Varför står det void framför vissa metodnamn? Vad innebär return? Inuti metoden distanceTo måste man komma åt den andra punktens x- och y-koordinat för att kunna beräkna avståndet till den. Trots att attributen är privata kan man skriva p.x och p.y för att nå dessa koordinater. Det beror på att ett attribut som är deklarerat private är privat för klassen och inte bara för ett speciellt objekt. Men man kan även nå den andra punktens koordinater på ett annat sätt. Hur? Datorarbete 1. Kopiera filen Square.java från projektet cs_eda017_src till detta projektet (Lab3). Kopiera även filen DrawThreeSquares.java från Lab2 (som innehåller en main-metod som använder klassen Square). Din ”nya”, kopierade, DrawThreeSquares.java ska nu använda den version av Square.java som du just kopierat till ditt projekt (Lab3). Detta gör vi genom att i filen DrawThreeSquares.java, ta bort raden som importerar klassen Square. Nu kommer java söka efter klassen Square i samma katalog som vi lagt vår DrawThreeSquares.java, och eftersom Square nu finns där behöver den alltså inte importeras. Kontrollera att ditt projekt kompilerar utan fel innan du går vidare till nästa steg. 2. Ett objekts tillstånd kan representeras på olika sätt och ändras utan att det påverkar specifikationen eller de andra klasser i programmet som utnyttjar klassen. Inuti klassen Square finns det två heltalsattribut x och y som håller reda på kvadratens position. Man skulle lika gärna kunna använda en punkt för detta. Byt ut attributen x och y mot ett attribut av typen Point. Gör de ändringar som krävs i resten av klassen. Kör programmet DrawThreeSquares för att kontrollera att klassen fortfarande fungerar. OBS: Koden i DrawThre- Laboration 3 – implementera klasser, kvadrat 17 eSquares ska inte behöva ändras för att få programmet i att fungera. Klassen Square ska alltså bara ändras internt, anropen till den ska inte förändras alls. 3. Lägg till följande metoder i klassen Square: /** Returnerar true om punkten med koordinaterna x,y ligger inuti kvadraten. */ boolean contains(int x, int y); /** Returnerar true om denna kvadrat och kvadraten sq överlappar. */ boolean overlaps(Square sq); Ledning: Använd contains inuti overlaps. Och glöm inte att det ofta hjälper att först rita och tänka på hur problemet ska lösas utan att tänka på programmeringskod (eller möjligtvis bara skriva pseudokod). För att sen gå tillbaks till att fundera på hur det skrivs i Java. Ni sitter oftast inne med lösningen även om ni inte tror att ni kan programmera det! 4. Skriv en klass med en main-metod där du skapar och ritar några Square-objekt och på något sätt använder metoden overlaps för att testa om metoden är korrekt eller ej. Kör programmet och kontrollera att allt fungerar som det ska. Använd gärna debuggern för att hitta eventuella fel. 5. Kontrollera med hjälp av checklistan nedan att du behärskar de olika momenten i laborationen. Diskutera med övningsledaren om någonting är oklart. Checklista Exempel på vad du ska kunna efter laborationen: • Förstå skillnaden mellan specifikation och implementering. • Implementera en klass: – attribut – konstruktor – void-metoder och funktioner • Tyda felutskrifter vid kompileringsfel och rätta felen. • Tyda felutskrifter vid exekveringsfel och rätta felen. • Förstå hur logiska uttryck kan användas för att jämföra två objekt. • Kunna använda metoder inneifrån andra metoder, och kunna använda metoder som returnerar boolean. Laboration 4 – implementera klasser, gissa tal 18 Laboration 4 – implementera klasser, gissa tal Mål: Du ska fortsätta på att träna att implementera klasser. Du ska också få mer träning i att använda if-, for- och while-satser. Förberedelseuppgifter • Repetera kap 2 och 3 i läroboken. • Läs igenom texten i avsnittet Bakgrund. Bakgrund Under den här laborationen ska du skriva ett program för spelet ”Gissa talet”. Spelet går till så att datorn ”tänker” på ett tal inom ett visst intervall. Användaren gissar på ett tal och får reda på om det är för litet, för stort eller korrekt. Spelet fortsätter tills användaren gissat rätt tal. Klassen NumberToGuess beskriver objekt som kan välja ett slumptal i ett visst intervall och hålla reda på detta. /** Skapar ett objekt med ett slumpmässigt valt heltal i intervallet [min, max]. */ NumberToGuess(int min, int max); /** Tar reda på minsta möjliga värde talet kan ha. */ int getMin(); /** Tar reda på största möjliga värde talet kan ha. */ int getMax(); /** Tar reda på om talet är lika med guess. */ boolean isEqual(int guess); /** Tar reda på om talet är större än guess. */ boolean isBiggerThan(int guess); Förutom denna klass ska du skriva en klass med en main-metod som utför själva spelet. Datorarbete 1. Öppna projektet lab4. Där finns en fil NumberToGuess.java med ett ”skelett” (en klass utan attribut och med tomma metoder) till klassen NumberToGuess. Implementera klassen NumberToGuess: Skriv in attributen i klassen. Skriv gärna en kommentar till varje attribut som förklarar vad attributet betyder. Skriv också konstruktorn och se till att alla attribut får rätt startvärden. Implementera de övriga operationerna. Ett av attributen ska tilldelas ett slumpmässigt valt värde. Användning av slumptal beskrivs i läroboken, avsnitt 6.10. För tillfället behöver du bara veta att man måste skapa ett Random-objekt och att man får ett nytt slumpmässigt heltal i intervallet [0, n) med funktionen nextInt(n). Skrivsättet [0, n) betyder att 0 ingår i intervallet, n ingår inte i intervallet. rand.nextInt(100) ger alltså ett slumpmässigt heltal mellan 0 och 99. 2. I projektet lab4 finns det ett program TestNumberToGuess som är tänkt att användas för att testa att metoderna i klassen NumberToGuess ger rätt resultat. Studera gärna programmet om du vill. Kör programmet. Utskrifterna från programmet ger besked om allt fungerar som det ska eller ej. Rätta eventuella fel och provkör igen. Laboration 4 – implementera klasser, gissa tal 19 3. Skriv programmet som utför en spelomgång där användaren får gissa på ett tal upprepade gånger tills rätt tal gissats. Intervallets min- och max-värde samt användarens gissningar ska läsas in. Efter en gissning ska användaren få besked om ifall det gissade talet var för litet, för stort eller korrekt. Sist i programmet ska antal gissningar som krävdes skrivas ut. Testa programmet. 4. (Frivillig uppgift) Det finns olika mer eller mindre smarta sätt att gissa talet. I den här uppgiften ska du skriva ett program som ska ta reda på hur många gissningar som krävs om man använder en optimal strategi. I programmet ska du använda klassen OptimalGuesser (se nedan). Genom att anropa metoden nbrGuesses ett stort antal gånger och ta reda på det maximala antal gissningar som krävdes kan man uppskatta antal gissningar som krävs för ett visst intervall. /** Beskriver en spelare som gissar tal på ett optimalt sätt. */ public OptimalGuesser(); /** Gissar tal på ett optimalt sätt tills rätt tal gissats. nbr är det objekt som håller reda på det tal som ska gissas. Returnerar antal gissningar som krävdes. */ public int nbrGuesses(NumberToGuess nbr); Implementera klassen OptimalGuesser. Börja med att fundera ut vilken strategi man ska använda för att behöva så få gissningar som möjligt. Diskutera med din labhandledare ifall du är osäker på vilken strategi du ska använda inuti metoden nbrGuesses. 5. (Frivillig uppgift) Skriv ett program som anropar metoden nbrGuesses ett stort antal gånger och tar reda på största antalet gissningar som krävdes. Ledning: För att beräkna maximala antalet gissningar behöver du en lokal variabel som håller reda på hittills största värde. Kontrollera efter varje anrop av nbrGuesses om denna variabel behöver uppdateras. Att beräkna maximum (eller minimum) är ett vanligt problem (behandlas i läroboken, avsnitt 7.13). Här är en generell algoritmen för att beräkna maximum: max = "litet värde"; för alla värden value = "nästa värde"; if (value > max) { max = value; } Laboration 5 – implementera klasser, Turtle 20 Laboration 5 – implementera klasser, Turtle Mål: Du ska fortsätta att träna på att implementera och använda klasser. Du ska också få mer träning i att använda Eclipse debugger. Förberedelseuppgifter • Repetera kap 3 i läroboken. • Tänk igenom följande frågor och se till att du kan svara på dem: 1. Vad är en specifikation respektive en implementering av en klass? 2. Vad är ett attribut? Var deklareras attributen? När reserveras det plats för dem i datorns minne och hur länge finns de kvar? 3. När exekveras satserna i konstruktorn? Vad brukar utföras i konstruktorn? 4. Vilka likheter/skillnader finns det mellan attribut, lokala variabler och formella parametrar. 5. Vad menas med public och private? • Läs avsnittet Bakgrund. Bakgrund I grafiksystemet Turtle graphics kan man rita linjer. Linjerna ritas av en (tänkt) sköldpadda som går omkring i ett ritfönster. Sköldpaddan har en penna som antingen kan vara lyft (då ritas ingen linje då sköldpaddan går) eller sänkt (då ritas en linje). Sköldpaddan kan bara gå rakt framåt, i den riktning som huvudet pekar. När sköldpaddan står stilla kan den vrida sig så att dess huvud pekar åt olika håll. En klass Turtle som beskriver en sköldpadda av detta slag har följande specifikation: /** Skapar en sköldpadda som ritar i ritfönstret w. Från början befinner sig sköldpaddan i punkten x,y med pennan lyft och huvudet pekande rakt uppåt i fönstret (i negativ y-riktning). */ Turtle(SimpleWindow w, int x, int y); /** Sänker pennan. */ void penDown(); /** Lyfter pennan. */ void penUp(); /** Går rakt framåt n pixlar i den riktning som huvudet pekar. */ void forward(int n); /** Vrider beta grader åt vänster runt pennan. */ void left(int beta); /** Går till punkten newX,newY utan att rita. Pennans läge (sänkt eller lyft) och huvudets riktning påverkas inte. */ void jumpTo(int newX, int newY); /** Återställer huvudriktningen till den ursprungliga. */ void turnNorth(); /** Tar reda på x-koordinaten för sköldpaddans aktuella position. */ int getX(); /** Tar reda på y-koordinaten för sköldpaddans aktuella position. */ int getY(); Laboration 5 – implementera klasser, Turtle /** Tar reda på sköldpaddans riktning, i grader från positiv x-led. */ int getDirection(); 21 Observera att vi här bestämmer vilket fönster som sköldpaddan ska rita i när vi skapar ett sköldpaddsobjekt. Det kan väl sägas motsvara verkligheten: en sköldpadda ”befinner” sig ju alltid någonstans. I följande program ritar en sköldpadda en kvadrat med sidorna parallella med axlarna: import se.lth.cs.window.SimpleWindow; public class TurtleDrawSquare { public static void main(String[] args) { SimpleWindow w = new SimpleWindow(600, 600, "TurtleDrawSquare"); Turtle t = new Turtle(w, 300, 300); t.penDown(); for (int i = 0; i < 4; i++) { t.forward(100); t.left(90); } } } I nedanstående variant av programmet har vi gjort två ändringar: 1) längden på sköldpaddans steg väljs slumpmässigt mellan 0 och 99 pixlar, 2) efter varje steg görs en paus på 100 millisekunder. Den första ändringen medför att figuren som ritas inte blir en kvadrat, den andra ändringen medför att man ser hur varje linje ritas. import se.lth.cs.window.SimpleWindow; import java.util.Random; public class TurtleDrawRandomFigure { public static void main(String[] args) { Random rand = new Random(); SimpleWindow w = new SimpleWindow(600, 600, "TurtleDrawRandomFigure"); Turtle t = new Turtle(w, 300, 300); turtle.penDown(); for (int i = 0; i < 4; i++) { t.forward(rand.nextInt(100)); SimpleWindow.delay(100); t.left(90); } } } Användning av slumptal beskrivs i läroboken, avsnitt 6.10. För tillfället behöver du bara veta att man måste skapa ett Random-objekt och att man får ett nytt slumpmässigt heltal i intervallet [0, n) med funktionen nextInt(n). Skrivsättet [0, n) betyder att 0 ingår i intervallet, n ingår inte i intervallet. rand.nextInt(100) ger alltså ett slumpmässigt heltal mellan 0 och 99. När klassen Turtle ska implementeras måste man bestämma vilka attribut som klassen ska ha. En sköldpadda måste hålla reda på: • fönstret som den ska rita i: ett attribut SimpleWindow w, • var i fönstret den befinner sig: en x-koordinat och en y-koordinat. För att minska inverkan av avrundningsfel i beräkningarna ska x och y vara av typ double, Laboration 5 – implementera klasser, Turtle 22 • i vilken riktning huvudet pekar. Riktningen kommer alltid att ändras i hela grader. Man kan själv välja om attributet som anger riktningen ska vara i grader eller i radianer, • om pennan är lyft eller sänkt. Ett sådant attribut bör ha typen boolean. booleanvariabler kan bara anta två värden: true eller false. Man testar om en booleanvariabel isPenDown har värdet true med if (isPenDown) . . . . När sköldpaddan ska gå rakt fram i den aktuella riktningen ska en linje ritas om pennan är sänkt. Den aktuella positionen ska också uppdateras. Antag att sköldpaddan i ett visst ögonblick är vriden vinkeln α i förhållande till positiv x-led. När operationen forward(n) utförs ska pennan flyttas från punkten ( x, y) till en ny position, som vi kallar ( x1, y1): x (x1, y1) n α (x, y) y Av figuren framgår att ( x1, y1) ska beräknas enligt: x1 = x + n cos α y1 = y − n sin α I Java utnyttjas standardfunktionerna Math.cos(alpha) och Math.sin(alpha) för att beräkna cosinus och sinus. Vinkeln alpha ska ges i radianer. x och y är av typ double. När koordinaterna utnyttjas som parametrar till SimpleWindowmetoderna moveTo och lineTo och när de ska returneras som funktionsresultat i getX och getY måste de avrundas till heltalsvärden. Det kan till exempel se ut så här: w.moveTo((int) Math.round(x), (int) Math.round(y)); Datorarbete 1. I filen Turtle.java i projektet lab5 finns ett ”skelett” (en klass utan attribut och med tomma metoder) till klassen Turtle. Implementera klassen Turtle: Skriv in attributen i klassen. Skriv gärna en kommentar till varje attribut som förklarar vad attributet betyder. Skriv också konstruktorn och se till att alla attribut får rätt startvärden. Implementera de övriga operationerna. 2. Klasserna TurtleDrawSquare och TurtleDrawRandomFigure finns i filerna TurtleDrawSquare.java och TurtleDrawRandomFigure.java. Kör programmen och kontrollera att din Turtleimplementation är korrekt. Använd gärna debuggern för att hitta eventuella svårfunna fel. Här kan det vara bra att utnyttja kommandot Step Into. Det fungerar som Step Over med skillnaden att man följer exekveringen in i metoder som anropas. 3. Du ska nu träna mer på att använda debuggern i Eclipse. I fortsättningen förutsätter vi att du utnyttjar debuggern för att hitta fel i dina program. Laboration 5 – implementera klasser, Turtle 23 Välj ett av programmen från uppgift 2. Sätt en brytpunkt på den rad där Turtleobjektet skapas. Kör programmet i debuggern. Använd Step Into och följ hur man från main-metoden ”gör utflykter” in i metoderna i klassen Turtle. Lägg märke till vilka storheter (variabler, attribut, parametrar) som är tillgängliga när man ”är i” main-metoden resp. inuti någon metod i klassen Turtle. Tips! Om du klickar på trekanten framför this i variabelvyn visas Turtle-objektets attribut. 4. Skriv ett program där en sköldpadda tar 1000 steg i ett fönster. Sköldpaddan ska börja sin vandring mitt i fönstret. I varje steg ska steglängden väljas slumpmässigt i intervallet [1, 10]. Efter varje steg ska sköldpaddan vridas ett slumpmässigt antal grader i intervallet [−180, 180]. 5. Skriv ett program där två sköldpaddor vandrar över ritfönstret. Steglängden och vridningsvinkeln ska väljas slumpmässigt i samma intervall som i föregående uppgift. Vandringen ska avslutas när avståndet mellan de båda sköldpaddorna är mindre än 50 pixlar. Sköldpaddorna ska turas om att ta steg på följande sätt: while ("avståndet mellan sköldpaddorna" >= 50) { "låt den ena sköldpaddan ta ett slumpmässigt steg och göra en slumpmässig vridning" "låt den andra sköldpaddan ta ett slumpmässigt steg och göra en slumpmässig vridning" SimpleWindow.delay(10); } Låt den ena sköldpaddan börja sin vandring i punkten (250,250) och den andra i (350,350). 6. Betrakta klasen Turtle. Ge exempel på ett attribut, en formell parameter och en lokal variabel. Ange också var i klassen/programmet respektive storhet är tillgänglig och får användas. Exempel på Attribut Formell parameter Lokal variabel Namn Får användas var Laboration 6 – vektorer, simulering av patiens 24 Laboration 6 – vektorer, simulering av patiens Mål: Du ska skriva ett enkelt simuleringsprogram och lära dig att använda vektorer. Förberedelseuppgifter • Studera avsnitt 8.1–8.3 i läroboken. • Läs avsnittet Bakgrund. • Betrakta följande satser: Card[] theCards = new Card[52]; theCards[0] = new Card(Card.SPADES, 12); theCards[1] = new Card(Card.HEARTS, 13); // 1 // 2 Se till att du kan förklara vad som händer i steg 1 respektive 2 ovan. Rita en figur (på papper) som visar vilka variabler och objekt som finns när man exekverat satserna till och med punkt 1 resp. 2 ovan. Bakgrund Patiensen 1–2–3 läggs på följande sätt: Man tar en kortlek, blandar den och lägger sedan ut korten ett efter ett. Samtidigt som man lägger korten räknar man 1–2–3–1–2–. . . , det vill säga när man lägger det första kortet säger man 1, när man lägger det andra kortet säger man 2, osv. Patiensen går ut om man lyckas lägga ut alla kort i leken utan att någon gång få upp ett ess när man säger 1, någon 2-a när man säger 2 eller någon 3-a när man säger 3. Man kan med hjälp av sannolikhetslära bestämma exakt hur stor sannolikheten är att patiensen ska gå ut.1 Men det är betydligt enklare att uppskatta sannolikheten genom att lägga patiensen många gånger och räkna antalet gånger som den går ut. Allra snabbast går det om datorn lägger patiensen. Då har man användning av följande klasser, som beskriver kort och kortlekar: /** konstanter för färgerna */ static final int SPADES = ...; static final int HEARTS = SPADES + 1; static final int DIAMONDS = SPADES + 2; static final int CLUBS = SPADES + 3; /** Skapar ett spelkort med färgen suit (SPADES, HEARTS, DIAMONDS, CLUBS) och valören rank (1-13). */ Card(int suit, int rank); /** Tar reda på färgen. */ int getSuit(); /** Tar reda på valören. */ int getRank(); /** Returnerar en läsbar representation av kortet, till exempel "spader ess". */ String toString(); 1 Se ”Fråga Lund om matematik”, www.maths.lth.se/query/answers, sök efter ”patiens” på sidan. Laboration 6 – vektorer, simulering av patiens /** Skapar en kortlek. */ CardDeck(); 25 /** Blandar kortleken. */ void shuffle(); /** Undersöker om det finns fler kort i kortleken. */ boolean moreCards(); /** Drar det översta kortet i leken. */ Card getCard(); I följande program skriver man ut färg och valör för alla kort som man drar ur en blandad kortlek: public class CardExample { public static void main(String[] args) { CardDeck deck = new CardDeck(); deck.shuffle(); while (deck.moreCards()) { Card c = deck.getCard(); System.out.println(c); } } } Med hjälp av System.out.println(c) skriver man ut ett kort på skärmen. Inuti println anropas metoden toString och den sträng som metoden returnerar kommer att skrivas ut. Det är alltså i Card-klassens toString-metod man bestämmer hur kortet ska presenteras på skärmen. Datorarbete 1. Öppna projektet lab6. I projektet finns bland annat den färdiga klassen Card. Öppna filen Card.java och se hur klassen är implementerad. 2. Ett program med programraderna från den tredje förberedelseuppgiften finns i filen ArrayExample.java. Sätt en brytpunkt i början av programmet och provkör programmet i debuggern. Klicka på plustecknet framför theCards i variabelvyn så ser du innehållet i vektorn. 3. Implementera klassen CardDeck. I klassen ska man hålla reda på de 52 korten i en kortleken. Det gör man enklast genom att lagra korten i en vektor: import java.util.Random; public class CardDeck { private Card[] cards; private int current; // index för "nästa" kort private static Random rand = new Random(); public CardDeck() { cards = new Card[52]; // skapa vektorn ... // skapa korten, lägg in dem i vektorn current = 0; } Laboration 6 – vektorer, simulering av patiens 26 ... } Att skapa korten i ordning är inte så enkelt – man känner ju inte värdet på konstanterna som anger färgerna. Men man ser att konstanterna ligger i ordning, så man kan skriva satser som den följande: for (int suit = Card.SPADES; suit <= Card.CLUBS; suit++) ... När man ska blanda kortleken bör man använda följande algoritm: för i = 51, 50, ..., 1 { nbr = slumptal i intervallet [0,i+1) byt plats på korten med index i och index nbr } 4. Ett testprogram (i filen TestCardDeck.java) för CardDeck finns i projektet. Testprogrammet skapar en kortlek och skriver ut alla korten. Därefter blandas kortleken och alla kort skrivs ut på nytt. Kör programmet och kontrollera att det som skrivs ut ser ut att stämma. 5. Skriv ett huvudprogram som utnyttjar klasserna Card och CardDeck för att lägga patiensen ett stort antal gånger och därefter skriva ut sannolikheten för att patiensen ska gå ut. Kör programmet. Den korrekta sannolikheten är ungefär 0.008165. Laboration 7 – använda klassen ArrayList 27 Laboration 7 – använda klassen ArrayList Mål: Du ska träna på att att använda klassen ArrayList. Du ska också få träning i att läsa data från textfiler och skriva ut på textfiler. Förberedelseuppgifter • Studera avsnitt 12.1-12.3 och 12.8 i läroboken. • Studera avsnitt 7.8 och 7.9 i läroboken. • Läs igenom texten i avsnittet Bakgrund. Bakgrund Under den här laborationen ska du ska du implementera en klass Drawing som beskriver en teckning bestående av ett antal linjer. Linjerna ritas upp i ett fönster av typen SimpleWindow. I klassen finns det bland annat metoder för att lägga till och ta bort linjer. Till hjälp har du klasserna Point (från lab 3) som beskriver en punkt och Line som beskriver en linje. Klasserna har följande specifikationer: Point /** Skapar en punkt med koordinaterna x,y. */ Point(int x, int y); /** Tar reda på x-koordinaten. */ int getX(); /** Tar reda på y-koordinaten. */ int getY(); /** Flyttar punkten avståndet dx i x-led, dy i y-led. */ void move(int dx, int dy); /** Returnerar avståndet mellan denna punkt och punkten p. */ double distanceTo(Point p); /** Returnerar en teckensträng som representerar punkten. Strängen innehåller punktens koordinater. Ex: 150 200 */ String toString(); Line /** Skapar en linje med ändpunkterna p1 och p2. */ Line(Point p1, Point p2); /** Skapar en linje med ändpunkterna x1, y1 och x2, y2. */ Line(int x1, int y1, int x2, int y2) ; /** Returnerar linjens längd */ double length(); /** Returnerar true om punkten p är nära linjen. */ boolean isNear(Point p); /** Ritar linjen i fönstret w. */ void draw(SimpleWindow w); Laboration 7 – använda klassen ArrayList 28 /** Raderar linjen i fönstret w. */ void erase(SimpleWindow w); /** Returnerar en teckensträng som representerar linjen. Strängen innehåller linjens ändpunkter Ex: 20 25 50 60 */ String toString() Ledning och anvisningar för att implementera klassen Line: • Låt klassen ha två attribut av typen Point för att hålla reda på linjens ändpunkter. • För att avgöra om en viss punkt är ”nära” linjen kan man göra så här. Beräkna och summera avstånden från punkten till linjens två ändpunkter. Om skillnaden mellan denna summa och linjens längd understiger ett visst värde, t. ex. 10, är punkten ”nära” linjen. Drawing /** Skapar ett ritnings-objekt som kan innehålla linjer. Ritningen kommer att ritas i ritfönstret w. */ Drawing(SimpleWindow w); /** Lägger till en linje i ritningen. Koordinater för linjens ändpunkter är x1,y1 och x2,y2. Ritar upp nya linjen i ritfönstret. */ void addLine(int x1, int y1, int x2, int y2); /** Låter användaren lägga till en eller flera linjer i ritningen. Användaren klickar på första linjens startpunkt och därefter på linjernas slutpunkter. Varje ny linje ska ritas ut i ritfönstret. Användaren dubbelklickar i ritfönstret när inga fler linjer ska läggas till. */ void collectLines(); /** Tar reda på antal linjer */ int nbrLines(); /** Tar bort de n senast tillagda linjerna och raderar dem i ritfönstret. Om n är större än antal linjer tas alla linjer bort. */ void erase(int n); /** Tar bort den linje som användaren klickar tillräckligt nära på. Returnerar true om någon linje togs bort. */ boolean eraseLine() /** Läser linjernas koordinater från textfilen med namnet fileName och skapar linjerna. */ void readFromFile(String fileName); /** Skriver linjernas koordinater på textfilen med namnet fileName och skapar linjerna. */ void saveToFile(String fileName); Ledning och anvisningar för att implementera klassen Drawing:: • Klassen ArrayList ska användas för att lagra linjerna inuti Drawing. • I klassen finns en metod, collectLines, som låter användaren lägga till ett valfritt ett antal linjer genom att klicka i fönstret. Så här går det till: – Användaren klickar i fönstret för att bestämma första linjens startpunkt. – Användaren klickar på en ny position i fönstret för att bestämma linjens slutpunkt. Denna punkt blir samtidigt startpunkt för nästa linje. Laboration 7 – använda klassen ArrayList 29 – Det hela upprepas tills användaren dubbelklickar i fönstret (dvs. klickar på samma position i fönstret två gånger i rad). För att rita en teckning behövs det också ett program som använder Drawing. Du ska skriva en klass med en main-metod där du låter användaren åstadkomma en teckning genom att upprepade gånger få välja mellan följande alternativ: 1. Lägga till en eller flera linjer till ritningen. – Användaren klickar på första linjens startpunkt och därefter på linjernas slutpunkter. Användaren dubbelklickar i ritfönstret när inga fler linjer ska läggas till. 2. Ta bort ett antal linjer – Det är de senaste tillagda linjerna som ska tas bort. Antal linjer som ska tas bort läses in från tangentbordet. 3. Ta bort en linje som väljs ut genom att användaren klickar på (nära) den. 4. Avsluta programmet Ledning och anvisningar • Låt användaren välja alternativ genom att skriva in talen 1, 2, 3 ... på tangentbordet. • När det valda alternativet är utfört ska man kunna välja igen. Detta upprepas till användaren väljer att avsluta. • Se till att det finns vettiga utskrifter så att den som använder programmet vet vad som ska göras (klicka i fönstret, skriva in värden på tangentbordet). • I början av programmet ska användaren få möjlighet att läsa in linjer från en textfil. • När programmet ska avslutas ska användaren få möjlighet att spara linjerna på en textfil. Datorarbete 1. Öppna projektet lab7. 2. Ditt program kommer att använda klassen Point från laboration 3. Du måste ange att filerna i projektet lab3 ska vara tillgängliga i detta projekt: 1. Högerklicka på projektet lab7, välj Build Path > Configure Build Path. 2. Klicka på Projects. 3. Klicka på Add. . . , välj projektet lab3. 3. Skapa och implementera klassen Line. I projektet finns det ett program TestLine som anropar alla metoder i klassen Line. Tag bort kommentartecknen i TestLine och testkör programmet. Om testprogrammet inte hittar några fel i Line visas ett fönster med en linje och enbart true skrivs ut i konsolfönstret. 4. Skapa och implementera klassen Drawing. 5. Skriv programmet som låter en användare framställa en teckning. Notera att beskrivningen över hur programmet ska fungera återfinns i slutet av kapitlet Bakgrund ovan. Testa programmet. Laboration 8 – matris, spelet life 30 Laboration 8 – matris, spelet life Mål: Du ska skriva program som använder matriser. Du ska också få mer träning på att använda färdiga klasser och att implementera egna klasser. Förberedelseuppgifter • Studera avsnitt 8.4 i läroboken. • Läs avsnittet Bakgrund. Bakgrund ”Game of Life”, konstruerat av matematikern John Conway, är ett simuleringsspel som uppvisar likheter med de förändringar som äger rum i samhällen med levande individer. Som spelplan används ett rutnät. En ruta kan antingen vara tom eller innehålla en individ. Från en given spelplan, en generation, ska samhället ändra sin struktur enligt vissa regler för födelse och död. Nedanstående regler gäller när en ny generation bildas. Alla förändringar vid en generationsväxling sker samtidigt över hela rutnätet. 1. Fortlevnad. En individ lever vidare till nästa generation om den har 2 eller 3 grannar. 2. Dödsfall. En individ med 4 eller fler grannar dör av trängsel. En individ med 0 eller 1 granne dör av isolering. 3. Födelse. I en tom ruta gränsande till exakt 3 individer föds en individ. Varje ruta i rutnätet har 8 grannrutor. Även de diagonalt intilliggande rutorna betraktas alltså som grannrutor. Exempel på fyra på varandra följande generationer i Life-spelet (individerna markeras med fyllda rutor): Generation 1 Generation 2 Generation 3 Generation 4 Du ska skriva ett program för Life-spelet. Användargränssnittet, det fönster där spelplanen ritas upp, beskrivs av en färdigskriven klass med namnet LifeView. Användaren kan när som helst ändra spelplanens utseende genom att klicka på en ruta i spelplanen. Om man klickar på en tom ruta skapas en individ i rutan, om man klickar på en fylld ruta töms rutan. Detta utnyttjas normalt för att skapa generation 1. Förutom spelplanen ritas en Next-knapp, som användaren klickar på när nästa generation ska skapas och ritas upp, samt en Quit-knapp som användaren klickar på när spelet ska avslutas. Följande klasser ska finnas i lösningen: LifeBoard, som beskriver spelplanen och generationsräknaren. Innehåller operationer för att ta reda på och ändra spelplanens utseende, t ex vilka rutor som ska vara tomma och vilka som ska innehålla individer. LifeView, som beskriver hur spelplanen presenteras på skärmen. Innehåller operationer för att rita upp spelplanen och för att vänta på att användaren klickar i rutorna eller på Next- eller Quit-knappen. Klassen är färdigskriven (specifikationen finns nedan). Laboration 8 – matris, spelet life 31 Life, som beskriver Life-spelet. Innehåller operationer för att skapa en ny generation enligt reglerna och för att skapa en individ i en ruta eller tömma en ruta. LifeController, en klass med en main-metod, som genomför Life-spelet från generation 1 till ”Quit”. Specifikation för LifeBoard och LifeView: /** Skapar en spelplan med rows rader och cols kolonner. Spelplanen är från början tom, dvs. alla rutorna är tomma och generationsnumret är 1. */ LifeBoard(int rows, int cols); /** Undersöker om det finns en individ i rutan med index row,col. Om index row,col hamnar utanför spelplanen returneras false. */ boolean get(int row, int col); /** Lagrar värdet val i rutan med index row, col. */ void put(int row, int col, boolean val); /** Tar reda på antalet rader. */ int getRows(); /** Tar reda på antalet kolonner. */ int getCols(); /** Tar reda på aktuellt generationsnummer. */ int getGeneration(); /** Ökar generationsnumret med ett. */ void increaseGeneration(); /** Skapar vy till spelplanen board. */ LifeView(LifeBoard board); /** Ritar upp de fixa delarna av spelplanen (rutnätet, generationsräknaren och knapparna. */ void drawBoard(); /** Ritar om de delar av fönstret som ändrats sedan föregående uppritning. */ void update(); /** Väntar tills användaren klickar med musen. Ger: 1: klick i ruta på spelplanen. Index för rutan kan hämtas med getRow och getCol. 2: klick i Next-rutan. 3: klick i Quit-rutan. int getCommand(); /** Tar reda på radnummer för den klickade rutan efter kommando nr 1. */ int getRow(); /** Tar reda på kolonnummer för den klickade rutan efter kommando nr 1. */ int getCol(); Rutorna i rutnätet numreras 0..rows–1, 0..cols–1. I klassen LifeBoard finns det dock en tänkt ”ram” kring rutnätet. I ramrutorna finns inga individer. Genom att ramen finns behöver du inte specialbehandla rutorna i kanten av rutnätet. Du kan alltså göra get(row,col) med row/col = –1 eller rows/cols. I de fallen ger metoden get alltid resultatet false. Laboration 8 – matris, spelet life 32 Klassen Life har följande specifikation: /** Skapar ett Life-spel med spelplanen board. */ Life(LifeBoard board); /** Skapar en ny generation. */ void newGeneration(); /** Ändrar innehållet i rutan med index row, col från individ till tom eller tvärtom. */ void flip(int row, int col); Datorarbete Det är lämpligt att lösa ett stort problem stegvis. Först skriver man lite kod, sedan testar man att det fungerar innan man går vidare. Därför är laborationsuppgiften uppdelad i mindre deluppgifter. 1. Öppna projektet lab8. I filen LifeBoard.java finns ett skelett (en klass utan attribut och med tomma metoder) till klassen LifeBoard. Implementera klassen LifeBoard. 2. Ett testprogram (TestLifeBoard.java) för klassen LifeBoard finns i projektet. Testprogrammet testar inte klassen fullständigt, men skapar ett LifeBoard-objekt, låter ett LifeViewobjekt rita upp brädet samt anropar de olika metoderna i LifeBoard. Kör programmet och kontrollera att det fungerar som det ska. Avsluta exekveringen genom att klicka i Quit-rutan på spelplanen. 3. Det är dags att börja arbeta med den riktiga main-metoden. Inspiration kan hämtas från testprogrammet (TestLifeBoard.java). Skapa en fil med namnet LifeController.java som ska innehålla main-metoden. Du ska till att börja med se till att Quit-knappen fungerar (Vänta med rutnätet och Next-knappen). Ledning: Med metoden getCommand i LifeView kan man vänta tills användaren klickar på en ruta eller en knapp. Då returneras ett tal med vars hjälp man kan avgöra vad användaren gjorde. Låt alltså användaren klicka i fönstret tills användaren klickar i Quitrutan. Avsluta då programmet med hjälp av System.exit(0). Kör programmet och kontrollera att du kan avsluta programmet genom att trycka på Quit-knappen. 4. Skapa en fil Life.java som ska innehålla klassen Life. Implementera konstruktorn och metoden flip i klassen Life. Implementera metoden newGeneration, men bara delvis – lägg in ökning av generationsnumret. Spara klassen Life och rätta eventuella kompileringsfel. 5. Fortsätt med klassen med main-metoden. Implementera satser i main-metoden så att rätt saker utförs när man klickar med musen; om man klickar i en ruta ska en individ skapas eller tas bort (med metoden flip). Om man klickar på Next-knappen ska generationsnumret ändras. Om man trycker på Quit-knappen ska programmet avslutas. Kör programmet och kontrollera att det fungerar som det ska. Laboration 8 – matris, spelet life 6. 33 Nu återstår att implementera färdigt metoden newGeneration i klassen Life. Lägg gärna till en intern (privat) metod som beräknar antal grannar till en ruta. Metoden newGeneration blir kortare och mer lättläst då. Tänk på att i metoden newGeneration ska alla förändringar vid en generationsväxling utföras samtidigt över hela spelplanen. Enklast åstadkommer du detta genom att deklarera en hjälpmatris där du lagrar den nya spelplanen. I slutet av metoden kopierar du hjälpmatrisen till den riktiga spelplanen. Alternativt kan du använda ett hjälpbräde (ett annat LifeBoard-objekt) där du lagrar den nya spelplanen. Även i detta fall måste du avsluta med att kopiera hjälpbrädet till den riktiga spelplanen. Testa programmet på åtminstone följande fyra exempel: Exempel 1 (två konfigurationer erhålls omväxlande) Exempel 2 (i generation 7 är alla döda) Exempel 3 (antalet individer växer, i generation 15 erhålls en stabil konfiguration) Prova också följande: Mer att läsa om ”Conway’s Game of Life” och fler exempel att prova finns på nätet. Använd t. ex. sökorden "Conway game of life". Laboration 9 – arv 34 Laboration 9 – arv Mål: Du ska lära dig mera om att skriva och använda klasser som är strukturerade med hjälp av arv. Förberedelseuppgifter • Studera avsnitt 9.1-9.6 i läroboken. • Läs avsnittet Bakgrund. • Tänk igenom följande frågor och se till att du kan svara på dem: 1. Varför är det lämpligt att använda arv i problemet med de olika figurerna (superklassen Shape och subklasserna Square, Triangle samt Circle)? 2. Vad är en abstrakt metod? 3. I en konstruktor i en subklass måste man se till att en av superklassens konstruktorer blir utförd. Varför? Hur åstadkommer man detta? Bakgrund I ritprogram kan man rita figurer som man sedan kan behandla på olika sätt. Man kan till exempel ändra storlek på figurerna, flytta dem och ta bort dem. Här beskrivs ett extremt enkelt ritprogram. Det finns bara tre slags figurer: kvadrater, liksidiga trianglar och cirklar. Det enda användaren kan göra är att flytta omkring figurerna i ritfönstret genom att först klicka på en figur och sedan klicka på den nya positionen. Ritfönstret har följande utseende: I ritprogrammet beskrivs de gemensamma egenskaperna hos figurerna i en klass Shape. Denna klass används som superklass på klasserna som beskriver specifika figurer (kvadrat, triangel och cirkel). public abstract class Shape { protected int x; protected int y; /** Skapar en figur med läget x,y. */ protected Shape(int x, int y) { this.x = x; this.y = y; } Laboration 9 – arv 35 /** Ritar upp figuren i fönstret w. */ public abstract void draw(SimpleWindow w); /** Raderar bilden av figuren, flyttar figuren till newX,newY och ritar upp den på sin nya plats i fönstret w. */ public void moveToAndDraw(SimpleWindow w, int newX, int newY) { java.awt.Color savedColor = w.getLineColor(); w.setLineColor(java.awt.Color.WHITE); draw(w); x = newX; y = newY; w.setLineColor(savedColor); draw(w); } /** Undersöker om punkten xc,yc ligger "nära" figuren. */ public boolean near(int xc, int yc) { return Math.abs(x - xc) < 10 && Math.abs(y - yc) < 10; } } I programmet håller man reda på figurerna som man skapar genom att man lägger in dem i en lista (ett objekt av klassen ShapeList). I följande program skapar man fem figurer, lägger in dem i listan och ritar upp dem: import se.lth.cs.window.SimpleWindow; public class ShapeTest { public static void main(String[] args) { SimpleWindow w = new SimpleWindow(600, 600, "ShapeTest"); ShapeList shapes = new ShapeList(); shapes.insert(new Square(100, 300, 100)); shapes.insert(new Triangle(400, 200, 100)); shapes.insert(new Circle(400, 400, 50)); shapes.insert(new Square(450, 450, 50)); shapes.insert(new Square(200, 200, 35)); shapes.draw(w); } } Parametrarna till konstruktorerna är figurens läge (x och y) och storlek (sidlängd eller radie). Klassen ShapeList har följande specifikation: /** Skapar en tom lista. */ ShapeList(); /** Lägger in figuren s i listan. */ void insert(Shape s); /** Ritar upp figurerna i listan i fönstret w. */ void draw(SimpleWindow w); /** Tar reda på en figur som ligger nära punkten xc,yc; ger null om ingen sådan figur finns i listan. */ Shape findHit(int xc, int yc): Operationen findHit används för att ta reda på vilken figur som användaren pekat och klickat på. Detta hanteras av klassen CommandDispatcher, som ansvarar för programmets kommunika- Laboration 9 – arv 36 tion med användaren. Klassen har följande uppbyggnad: import se.lth.cs.window.SimpleWindow; public class CommandDispatcher { private SimpleWindow w; private ShapeList shapes; public CommandDispatcher(SimpleWindow w, ShapeList shapes) { this.w = w; this.shapes = shapes; } public void mainLoop() { while (true) { // användaren klickar på en figur // användaren klickar på en ny position // figuren flyttas till den nya positionen } } } Datorarbete Råd: i nedanstående uppgifter är det lämpligt att börja med uppgift 1 till 4 men bara hantera kvadrater, så att man ser att allt fungerar. Komplettera sedan programmet med cirklar och trianglar. 1. Öppna projektet lab9. Där finns den färdiga klassen klassen Shape i filen Shape.java. Skriv tre subklasser till Shape: Square, Triangle och Circle. Lägg klasserna i separata filer i projektet lab9 (Square.java, Triangle.java, Circle.java). I subklasserna måste du definiera vad som avses med ”läget” hos en figur. Koordinaterna x och y kan för en kvadrat ange övre vänstra hörnet, för en triangel nedre vänstra hörnet, för en cirkel medelpunkten. Man ska kunna bestämma figurens storlek med en parameter till konstruktorn. När man ska rita en cirkel är det enklast att tänka sig cirkeln som en regelbunden månghörning med många hörn. I lösningen till en av övningsuppgifterna i kapitel 9 i läroboken finns en metod som ritar en cirkel. 2. Skapa och implementera klassen ShapeList med hjälp av en ArrayList<Shape>. 3. Klassen ShapeTest finns i filen ShapeTest.java. Tag bort kommentartecknen i main-metoden och testkör programmet. 4. Skapa och implementera klassen CommandDispatcher (enligt ovan). Modifiera också mainmetoden i ShapeTest så att ett CommandDispatcher-objekt skapas och operationen mainLoop utförs. Testkör det nya programmet. 5. (Frivillig uppgift.) Ändra programmet så att man inte behöver använda insert-satser i main-metoden för att lägga in figurer i listan. I stället ska uppgifter om figurerna som ska skapas läsas från en fil. I projektkatalogen finns en fil shapedata.txt som specificerar samma figurer som i det tidigare exemplet.