TDDC74 Laboration 05 - Ett objektorienterat spel

Relevanta dokument
TDDC74 Laboration 05 - Ett objektorienterat spel

Programmering, abstraktion och modellering PROJEKTFÖRELÄSNING ANDERS MÄRAK LEFFLER IDA/HCS

TDDC74 Lab 02 Listor, sammansatta strukturer

TDDC74: Projekttitel

TDDC74 FÖRELÄSNING 9 ANDERS MÄRAK LEFFLER IDA/HCS

TDDC74 Lab 04 Muterbara strukturer, omgivningar

TDDC74 Programmering: Abstraktion och modellering Dugga 2, , kl 14-16

TDDC74 Programmering: Abstraktion och modellering Dugga 2, , kl 17-19

TDDC74 Programmering: Abstraktion och modellering Datortenta , kl 14-18

TDDC74 Programmering: Abstraktion och modellering Tentamen, lördag 29 augusti 2015, kl 8 12

TDDC74 Programmering: Abstraktion och modellering Datortenta , kl 14-18

TDDC74 Programmering: Abstraktion och modellering Datordugga 2 - exempel

TDDC74 Programmering: Abstraktion och modellering Dugga 3, kl 14 16, 25 mars 2015

TDDC74 Programmering: Abstraktion och modellering Tentamen, lördag 27 augusti 2016, kl 8 12

TDDC74 Programmering: Abstraktion och modellering Datortenta

Laboration 1 Introduktion till Visual Basic 6.0

TDDC74 Programmering: Abstraktion och modellering Datortenta , kl 08-12

Labb LABB 1. Databassagan och en rundtur i databasers märkliga värld. Plushögskolan Frågeutveckling inom MSSQL - SU14

Objektorienterad programmering i Java I. Uppgifter: 2 Beräknad tid: 5-8 timmar (OBS! Endast ett labbtillfälle) Att läsa: kapitel 5 6

TDDC74 Programmering: Abstraktion och modellering Tentamen, onsdag 9 juni 2016, kl 14 18

TUTORIAL: KLASSER & OBJEKT

TDDC74 Programmering: Abstraktion och modellering Tentamen, onsdag 19 oktober 2016, kl 14 18

TDDC74 - Lektionsmaterial C

Laboration 1: Figurer i hierarki

Inlämningsuppgifter, EDAF30, 2015

KARLSTADS UNIVERSITET 12/8/09 informatik & datavetenskap Johan Öfverberg, Kerstin Andersson Laboration 4, ISG A04 och DVG A08 HT-09

Institutionen för datavetenskap, DAT060, Laboration 2 2 För denna enkla simulerings skull kommer handen att representeras som ett par tal μ värdet på

TUTORIAL: SAMLING & KONSOLL

2D1387 Programsystemkonstruktion med C++

Dagens program. Programmeringsteknik och Matlab. Objektorienterad programmering. Vad är vitsen med att ha både metoder och data i objekten?

Obs! Inget ur Javas standardbibliotek får användas i ett svar (om det inte står att man får det).

TDDC74 - Projektspecifikation

Tentamen i TDP004 Objektorienterad Programmering Praktisk del

Börja med att kopiera källkoden till din scheme-katalog (som du skapade i Laboration 1).

TDDC30. Objektorienterad programmering i Java, datastrukturer och algoritmer. Föreläsning 4 Erik Nilsson, Institutionen för Datavetenskap, LiU

Föreläsning 8 - del 2: Objektorienterad programmering - avancerat

725G61 - Laboration 7 Implementation av ett API. Johan Falkenjack

Objektorienterad programmering i Racket

Objektorienterad programmering i Racket

Programmering B med Visual C

Classes och Interfaces, Objects och References, Initialization

TDDC74 Programmering: Abstraktion och modellering Dugga 1, kl 14-16

Att prova på en enkel Applet och att lära sig olika sätt att hämta data från tangentbordet. Du får även prova på att skapa din första riktiga klass.

Labb i Datorsystemteknik och programvaruteknik Programmering av kalkylator i Visual Basic

Laboration: Whitebox- och blackboxtesting

TDDE10 m.fl. Objektorienterad programmering i Java Föreläsning 6 Erik Nilsson, Institutionen för Datavetenskap, LiU

Använda Python Laboration 1 GruDat, DD1344

Tentamen, EDAA10 Programmering i Java

Kort om klasser och objekt En introduktion till GUI-programmering i Java

Objektorienterad Programkonstruktion. Föreläsning 3 7 nov 2016

Objektorienterad Programkonstruktion. Föreläsning 3 9 nov 2015

Chapter 4: Writing Classes/ Att skriva egna klasser.

E-posthantering med Novell Groupwise WebAccess

Programmering för språkteknologer II, HT2014. Rum

TENTAMEN I DATAVETENSKAP

TDDC74 Programmering: Abstraktion och modellering Tenta, kl 14 18, 11 juni 2014

Användarhandledning Version 1.2

TDDC74 Programmering: Abstraktion och modellering Dugga 1, exempeldugga

TENTAMEN I PROGRAMMERING. På tentamen ges graderade betyg:. 3:a 24 poäng, 4:a 36 poäng och 5:a 48 poäng

Grafiska användargränssnitt i Java

Tentamen i TDP004 Objektorienterad Programmering Praktisk del

Vanliga frågor för VoiceXpress

Tentamen i TDP004 Objektorienterad Programmering Praktisk del

Gränssnitt för FakeGranska. Lars Mattsson

Kopiering av objekt i Java

Objektorientering i liten skala

729G04 Programmering och diskret matematik. Föreläsning 7

Typhierarkier del 1 Gränssnitt, ärvning mellan gränssnitt, ärvning mellan klasser

Inledande programmering med C# (1DV402) Ditt första C#-program med Visual Studio

Objektorienterad programmering i Java I

TDDC30. Objektorienterad programmering i Java, datastrukturer och algoritmer. Föreläsning 11 Jonas Lindgren, Institutionen för Datavetenskap, LiU

Java: Utvecklingsverktyg, datatyper, kontrollstrukturer

Introduktion Schenker-BTL AB, Stab IT Beskrivning över informationsintegreringmed Schenker, metodbeskrivning version 1.

Föreläsning 3. Programmering, C och programmeringsmiljö

Det är principer och idéer som är viktiga. Skriv så att du övertygar rättaren om att du har förstått dessa även om detaljer kan vara felaktiga.

Grafiska användargränssnitt i Java

Arv och polymorfism i Java

Laboration: Grunderna i Matlab

Grafik i DrRacket AV TOMMY KARLSSON

TDDC74 Programmering, abstraktion och modellering. Tentamen

"Är en"-relation. "Har en"-relation. Arv. Seminarium 2 Relevanta uppgifter. I exemplet Boll från förra föreläsningen gällde

Classes och Interfaces, Objects och References Objekt-orienterad programmering och design (DIT952) Niklas Broberg, 2016

Outline. I Vi kan lätt göra samma sak för fyra variabler... I Hur gör vi för 400 inlästa värden? I Ofta behöver man flera likadana variabler

TDDI16: Datastrukturer och algoritmer

Objektorientering: Lagring och livstid

Imperativ programmering. Föreläsning 4

Föreläsning 5-6 Innehåll. Exempel på program med objekt. Exempel: kvadratobjekt. Objekt. Skapa och använda objekt Skriva egna klasser

TDDC74 Programmering: Abstraktion och modellering Dugga 3, kl 8 10, 7 april 2016

Grundläggande programmering, STS 1, VT Sven Sandberg. Föreläsning 18

Objektorientering: Lagring, räckvidd och livstid

Föreläsning 5-6 Innehåll

Att skriva till och läsa från terminalfönstret

TDTS04: Ett chattsystem i java baserat på corba

I STONE. I Variabler, datatyper, typkonvertering. I Logiska och matematiska uttryck. I Metoder-returvärde och parametrar. I Villkorssatser if/else

OOP Objekt-orienterad programmering

Konsolfönster i Windows Momentet ingår i kursen PDA DTR1206 Lab 1 DOS Konsolfönstret

Samlingar, Gränssitt och Programkonstruktion! Förelasning 11!! TDA540 Objektorienterad Programmering!

Introduktion till programmering D0009E. Föreläsning 1: Programmets väg

729G75: Programmering och algoritmiskt tänkande. Tema 1, föreläsning 1 Jody Foo

Läs detta! Uppgifterna är inte avsiktligt ordnade efter svårighetsgrad. Skriv ditt idnummer på varje blad (så att vi inte slarvar bort dem).

Transkript:

TDDC74 Laboration 05 - Ett objektorienterat spel Innehåll 1 Översikt 2 2 Värt att veta 2 3 Projekt: Äventyrsspel 3 3.1 Krav................................ 4 3.2 Story................................ 5 3.3 Kataloger............................. 6 3.4 Filstruktur............................. 6 3.5 provide............................... 7 4 Grundstruktur 7 4.1 Saker/items (inledande)..................... 7 4.1.1 Testning och frågor (behöver ej redovisas)....... 8 4.2 Karaktärer/characters (inledande)................ 8 4.2.1 En parentes om in- och utmatning........... 10 4.2.2 Efter definitionerna.................... 10 4.2.3 Testning och frågor (behöver ej redovisas)....... 11 4.3 Platser (inledande)........................ 11 4.3.1 Testning och frågor (behöver ej redovisas)....... 13 4.4 Platser: Få världen att hänga ihop............... 13 4.4.1 Frågor att besvara (för er själva)............ 14 4.5 Symmetri - items kan också flytta på sig............ 15 5 Att börja bygga spel 15 1

6 Att använda ett enkelt gränssnitt 17 6.1 Motivering............................. 17 6.2 Uppgifter............................. 17 7 Kommandon i spelet 23 7.0.1 Frågor att fundera över (för egen del).......... 24 8 Att koppla det hela till GUI 24 8.1 Fundera på (för egen del)..................... 25 9 Mer speciella kommandon 25 10 Ert egna spel 26 11 Redovisning 26 1 Översikt I den här laborationen kommer ni att bekanta er med: Grundläggande objektorienterad programmering. En praktisk datastruktur. Hur man kan gå tillväga när man utvecklar ett större projekt, och (väldigt primitiv) kontinuerlig testning. In- och utmatning via separat kommunikationsobjekt. 2 Värt att veta Denna laboration innehåller ett lite större projekt. Ni kommer att få använda Rackets inbyggda objektsystem, och ni kan ha god hjälp av dokumentationen på Rackets hemsida. 1 I denna laboration får ni använda alla primitiver som finns att tillgå i Racket. 1 docs.racket-lang.org 2

Labben är lite större, så nu (om inte tidigare) vill ni testa era skapade objekt och deras procedurer var för sig. Fråga er labbass! Ni ska alltså låta bli att koda enligt big-bang-metoden. Det är mycket att läsa, men labben görs steg för steg. Bli inte avskräckt av antalet sidor. 3 Projekt: Äventyrsspel Vi ska skapa ett litet textbaserat äventyrsspel. Det kan man göra på många vis. Upplägget här kommer att fokusera på: Utbyggbarhet. Det är lätt att skriva ihop kommandon, spelfigurer och annat. Att lägga till en plats i världen ska inte kräva ändringar i koden på tio ställen i en stor cond. Det ska räcka med att säga så här hänger den nya platsen ihop med de gamla. Att lägga logik hos spelobjekten själva. Vilka steg ska ske när en spelfigur får en sak av en annan? Det kan variera mellan olika sorters spelfigurer. En vakt som får en nyckel kanske flyttar på sig, en butiksinnehavare som får något annat än pengar kanske ger tillbaka sakerna och så vidare. Vi försöker alltså lägga så mycket som möjligt av sådan logik i själva klasserna. Separation av presentation från logik. Det är inte så att en spelfigur ska veta om vad den heter, kunna flytta sig i världen, tolka text och skapa grafikfönster som hanterar inmatning från användaren. Om någon skrev ett alternativt användargränssnitt, skulle er kod i princip inte behöva ändras. Detta får man rätt gratis om man ha gjort rätt. Är det här inte riktigt klart för er än, innan ni har börjat arbeta? Fråga gärna labbass. Fortsätt också med kodandet, och återvänd till punkterna när det börjar komma till situationer där man kan göra olika designval. Ni kommer att utveckla spelet bit för bit, för att sedan sätta ihop det till en helhet. Fokus ligger på att utveckla en enklare spelmotor, skapa mallar för hur sakerna i världen, och sedan använda dessa för att ge spelet innehåll. Utvecklingsgången är ungefär 1. Hitta på historia. 3

2. Skapa möjligheten att ha karaktärer i spelet, och ett par karaktärer. 3. Skapa möjligheten att ha platser i spelvärlden. 4. Hantera inmatning, med hjälp av det givna systemet. 5. Koppla ihop det hela till ett spel. Strikt taget går flera utvecklingsspår - skapa platser, koppla ihop världen - att göra parallellt. Det är viktigt att ni genom hela processen konsekvent skriver på engelska i koden (konsekvent med hur Racket-språket ser ut). Däremot får ni gärna skriva ett spel på svenska (svenska namn, svenska beskrivningar, och så vidare) om ni vill. Spelet kommer att vara objektorienterat och det är därför viktigt att ni har åtminstone en översiktlig koll på hur man skapar och använder objekt i Racket. Se kurshemsidan för material. 3.1 Krav Spelet måste innehålla: En sammanhängande story. Minst 4 platser. Minst 3 karaktärer. Möjlighet att nå alla platser. Minst 3 föremål. Objekt som stämmer överens åtminstone med den minimala beskrivning som ges nedan. Åtminstone de kommandon som tas upp som exempel nedan. Instruktioner om hur man spelar och hur man vinner måste bifogas. Detta innebär alltså en instruktion på kommando-för-kommando-nivå. 2 2 D v s go west, take key, go north, use key lock, snarare än du hittar nyckeln som du behöver i skattkammaren någonstans i skogen. 4

Annat som framgår nedan. Till exempel hur de minimala gränssnitten ser ut. Dessutom måste det ha någon form av specialbeteende som ligger hos ett visst objekt. Det får inte vara hårdkodat i kommandot i sig, i item%, character% eller place%. För att vara mer konkret, kan det t ex vara En karaktär som gör något visst när man ger dem en viss sak. (Om väktaren får en kaka, öppnas dörren. Om väktaren får något annat, ger de tillbaka det.) En sak som gör något visst när man använder den. T ex use Spell on Guard. En plats som bara släpper in spelaren, om hen bär en viss amulett. Detta kan implementeras till exempel genom injicerade funktioner eller subklassning 3. Detta kommer in sent i utvecklingen (och instruktioner kommer senare i labben). Men när ni skriver story - förbered er på något sådant. Ur spelarens synvinkel ska det dessutom gå att Skriva help och få åtminstone en lista med giltiga kommandon. Ta sig runt i spelvärlden och interagera med karaktärer och saker, med hjälp av ett textgränssnitt. Klara spelet, genom att spelaren tar sig till en viss plats eller dylikt. Detta ska inte gå att göra direkt, utan först efter/genom specialhändelsen beskriven ovan. Det får inte finnas en global klarat spelet - variabel. När spelet är klart, ska spelet ge något passande meddelande i en popupruta, och stänga fönstret. Notera särskilt det sista kravet. Diskutera gärna med labbass om du är osäker. 3.2 Story Innan ni börjar skriva kod är det konstnärligt påkallat att ni bestämmer er för vad ert spel ska handla om. Det kan till exempel handla om en riddare 3 Obekant ord? Läs mer om subklassning i OOP-guiden, eller fråga din labbass. Den sortens injektion som vi pratar om finns i den sista - frivilliga - uppgiften på lektion 5. 5

som ska rädda en prins/prinsessa från den onda draken i en episk, interaktiv saga, ett rymdäventyr eller att lösa ett mord i ett deckarspel i noirstil. Skriv ner en liten historia och hur spelaren ska klara spelet, så att ni har något att utgå från under utvecklandet. Tänk på att inte göra det för svårt för er på det här stadiet, eller att skriva för mycket historia. Kom igång med kodandet! Med bra kod bör spelet lätt kunna vidareutvecklas om så önskas (och därmed utökas med mer historia). Stäm av hur kraven uppfylls med din labbass. 3.3 Kataloger Skapa en katalog /kurs/tddc74/lab5 och arbeta där. 3.4 Filstruktur I denna laboration kommer ni att behöva skapa filerna world_init.rkt character.rkt place.rkt item.rkt main.rkt player_commands.rkt character.rkt, place.rkt och item.rkt ska innehålla klass-definitioner. Där beskrivs t ex metoder/procedurer och egenskaper som är gemensamma för alla characters, alla items och så vidare. De kommer inte att behöva vara beroende av funktioner från några andra filer (fråga er labbass annars). world_init.rkt kommer att vara den fil som skapar alla karaktärer, platser och föremål. Konkret, kommer den att innehålla mängder av (define <namn> (new character%...)) och dylikt. Det är alltså den fil som gör spelmotorn till ett spel. Alla kommandon som spelaren kan utföra - take, move, talk,... - definieras i player_commands.rkt. 6

main.rkt är den fil där användaren börjar. Där skapar man GUI-objekt (mer om det senare). 3.5 provide För att få tillgång till klasser och procedurer som ligger i en annan fil måste man ladda in den filen precis som i laboration 3. För att förenkla lite här, lägg alla filer i samma katalog (förslagsvis lab5-katalogen). Använd require och provide som tidigare. Gör bara relevanta delar tillgängliga! 4 Grundstruktur 4.1 Saker/items (inledande) Vi kommer att behöva saker, spelkaraktärer och platser (och sätt att knyta ihop dem) för att skapa en spelvärld. Vi börjar med en förenklad version av sakerna (för att senare utöka dem). Skapa en ny klass, i en ny fil som uppfyller åtminstone nedanstående och heter item%. Varje item% ska till att börja med ha fält för (ett) namn, (en) beskrivning och en plats. Inledande gränssnitt för klassen item% Procedur Argument Beskrivning get-name - Returnerar namn. get-description - Returnera beskrivningen. get-place - Returnera platsen där saken är (character%- objekt eller place%-objekt).... mer senare... Notera att det här ovan finns ett underförstått krav på att objektet har (ett) namn, (en) beskrivning och plats. Ni kan låta place ha värdet #f som standard, och description värdet som standard (en tom sträng, notera att det är citattecken och inte apostrofer). Det vill säga, skapa en klass som ser ut något i stil med (define item% 7

(class object%... (super-new))) Spara klassdefinitionen i item.rkt. Glöm inte att lägga till en lämplig providerad. 4.1.1 Testning och frågor (behöver ej redovisas) Vi har här (liksom i livet) inga autotester, utan får försöka hitta strategier för att övertyga oss om att det vi har skrivit är korrekt (eller lista ut vad vi behöver veta för att kunna testa delarna av programmet i framtiden, om de är beroende av något). Än så länge behöver vi bara kunna testa getters. Skapa en separat fil test.rkt där ni får tillgång till item% (tänk require/provide). Det enda som egentligen ska synas utåt därifrån är själva item% (och senare eventuella subklasser/barn, men det har vi inte skapat än). Skapa ett par item%-objekt med olika egenskaper, och kontrollera att ni kan göra saker i stil med: > (define screwdriver (new item% [name "Screwdriver"] [description "A fantastic screwdriver."])) > (define kitten (new item% [name "Kitten"] [description "It s really a shark."])) > (send kitten get-name) "Kitten" > (send screwdriver get-name) ;; ser det ut att vara olika objekt? "Screwdriver" 4.2 Karaktärer/characters (inledande) Varje karaktär i spelet ska vara ett objekt, som spelmotorn kan kommunicera med på ett förutsägbart sätt. Vi bygger upp vad karaktärerna gör, och hur man kommunicerar med dem, genom att skapa en klass för karaktärsobjekt. 8

När man sedan skapar en specifik karaktär, ska man ange vad den har för name respektive description. Namnet ska vara en sträng 4. Karaktärer befinner sig på platser, och de ska därför kunna hålla reda på var de själva är (platsobjekt skapar vi senare). Se till att de har denna möjligheten. Om en plats inte anges, ska place vara #f. Ett minimalt gränssnitt för klassen character% (OBS! Detta kommer att behöva utökas senare.) Metod Parametrar Beskrivning get-name - Returnera denna karaktärs namn. get-description - Returnera denna karaktärs beskrivning. get-place - Returnerar platsen karaktären är på (place%). get-talk-line - Returnerar karaktärens talk-line. move-to new-place Flyttar karaktären till new-place. Returnera #t om det lyckades, #f annars. get-inventory - Returnerar lista av item%-objekt karaktären bär med sig. get-item item-name Returnerar item%-objekt med det namnet. Om karaktären ej har det, returnera #f. receive give item, giver item-name recipient Tar mot item från giver (en character%). Efter ska karaktären ha saken. giver ansvarar för att den inte längre har objektet. Om karaktären har en sak vid namn item-name, ge den till recipient (vars receive-metod ska köras). Notera att det kan finnas vissa saker underförstått ovan. Till exempel, kan man ha ett item-objekt om man inte har någon form av intern behållare/inventory för saker? Ni får gärna lägga till saker, och skapa privata hjälpfunktioner. När det gäller att knyta ihop nycklar och värden, och spara saker på det viset, rekommenderas hashtabeller 5 Datastrukturen kan visa sig vara praktisk i projektet, och livet i allmänhet. 4 Till exempel sven. Observera citattecknen! 5 Mycket kan sägas om dessa. Ni kan, rätt förenklat och allmänt, tänka på det som en tabell med en nyckelkolumn och en värdeskolumn, där det går snabbt att hitta värden utifrån nycklarna. Hur det fungerar kan man läsa mer om i Racket Guide, http://docs. racket-lang.org/guide/hash-tables.html. Fråga din labbass. 9

4.2.1 En parentes om in- och utmatning Tanken är inte att folk ska spela detta genom DrRacket-tolken, utan det kommer lite grafiska textrutor som används som användargränssnitt senare. Än så länge bygger vi bara databehållare och spellogik, och lägger till gränssnitt senare. Då kommer vi att ha gränssnitt som programmet kan tala med på vissa välspecificerade vis (på samma sätt som en character% har ett visst beteende när man skickar den meddelandet get-name ). Det här gör att vi kommer att kunna skriva spel som egentligen fungerar oavsett hur data kommer in och skrivs ut. Är det en spelare som skriver i DrRackets nedre interaction-window? Som skriver i en grafikruta? Som sitter och spelar detta på en dator på andra sidan internet, och kommunicerar via någon form av nätverkskomponent som vi har lagt till 6? Det bryr sig inte vårt spel om än så länge! 4.2.2 Efter definitionerna Spara koden där ni skrivit klassdefinitionen i character.rkt. Uppdatera er test.rkt med require-rad. I denna fil, se till att ni har tillgång till character% från character.rkt (tänk require/provide), och skriv något i stil med (define james-bond (new character% [name "James-Bond-agent"] [description "Shaken, not stirred"] [talk-line "There s always something formal about the point of a pistol."])) Det ska nu gå att göra till exempel > (send james-bond get-name) "James-Bond-agent" När ni skapar character%-objekt, ge dem alltid namn som är ett ord. 7 6 Detta ingår inte i labben! 7 Detta för att underlätta hantering av argument senare. Om spelaren skriver give James Bond The golden gun kommer det att vara ett namn på ett kommando ( give ) och fem argument ( James, Bond, the, golden, gun ). När man skriver give - 10

4.2.3 Testning och frågor (behöver ej redovisas) Detta är en förhållandevis stor labb, men mindre än projektet. Det är väldigt litet i relation till större projekt som ni kommer stöta på med fler än två inblandade programmerare. Försök ställa er frågor om testning, eller testbarhet kontinuerligt. Ser alla metoder ut att fungera som de ska? Finns det något som ni redan nu kan tänka er inte går att testa fullständigt, eller som kanske behöver ses över när ni skapat senare delar av programmet? Notera det. Är alla character%-instanser isolerade från varandra, eller delar de - felaktigt - någon form av data (namn, inventory...)? Hur testar ni det? Skapa ett par item%-objekt med olika namn, och ett par character%- objekt. Se till att den ena karaktären har båda sakerna. Kan den sedan ge någon av sakerna till den andra karaktären? Har den kvar saken den gett bort? Vad händer om ni skickar in konstiga item-names? Har ni hantering av det? Se till att alla metoder är testade. 4.3 Platser (inledande) Hittills har vi skapat möjligheten att ha karaktärer, men inte haft möjlighet att bygga en spelvärld. Implementera klassen place% som ger den möjligheten. (Inledande) gränssnitt för place%. proceduren är det bekvämt att veta att det första argumentet, om det finns, alltid är namnet på karaktären, och det andra är namnet på saken. Det är inte jättesvårt att lösa på annat sätt, men ligger utanför denna laborations fokus. 11

Metod Parametrar Beskrivning get-name - Returnerar platsens namn get-description - Returnera platsens beskrivning. add-character! character Lägger till en karaktär get-character character-name Returnera karaktären med det namnet, om den finns på platsen. Annars returneras #f. delete-character! character-name Ta bort karaktären från platsen. characters - Returnera en lista med alla karaktärer på platsen (character%-objekt) Så här bör det fungera: > (define java-cafe (new place% [name "Java"] [description "Of all the cafes in this world..."])) > java-cafe (object:place%...) > (send java-cafe get-name) "Java" > (send java-cafe characters) () > (send java-cafe get-character "James-Bond-agent") ;; inte där #f > (send james-bond get-place) #f > (send java-cafe add-character! james-bond) #t > (send java-cafe add-character! james-bond) ;; redan där #f > (send java-cafe get-character "James-Bond-agent") (object:character%...) > (send james-bond get-place) #f > (send java-cafe delete-character! "James-Bond-agent") #t > (send java-cafe delete-character! "James-Bond-agent") ;; karaktären ej där #f Märk särskilt detta: 12

> (send java-cafe get-character "James-Bond-agent") ;; inte där #f > (send james-bond move-to java-cafe) ;; säg åt karaktären att flytta sig #t > (send james-bond get-place) ;; karaktären vet var den är (object:place%...) > (send java-cafe get-character "James-Bond-agent") ;; och det gör platsen! (object:character%...) 4.3.1 Testning och frågor (behöver ej redovisas) Hur får ni fram namnet på platsen som en karaktär (enligt egen utsago) befinner sig på? Fungerar det att lägga till och ta bort characters? Testa ordentligt. Vad händer om man man ber en karaktär att flytta sig från en plats till en annan? Skapa ett par platser och visa att det går att flytta en karaktär mellan dem. Finns någon övrig testning som behöver göras? Spara koden i place.rkt. Glöm inte en lämplig provide-rad. 4.4 Platser: Få världen att hänga ihop Vi kan nu skapa platser och karaktärer och kan flytta en karaktär mellan platser. Däremot har vi än så länge inget i spelet som säger att två platser hänger ihop. Det skulle man kunna implementera genom att - när man skriver movekommandot - skriva en lång lista modell om spelaren är på plats X och vill gå åt riktning Y, byt till plats Z, om spelaren är på plats W.... Det blir snabbt helt ohanterligt. Dessutom leder det snabbt till kodduplicering. Vi vill lyckas beskriva hur världen hänger ihop, fristående från de kommandon vi använder för att ta oss runt i den. Det val vi gör här är att säga att vi lägger till utgångar, exits, som kopplar samman platserna. Varje plats känner till närliggande platser, och vad utgångarna heter, men egentligen inget mer av världen. Namnen på utgångarna kan vara helt godtyckliga (man kan gå up för en trappa, west om man vandrar på en väg och så vidare). 13

Lägg till så att åtminstone detta finns med i gränssnittet för place%-objekt: Metod Argument Beskrivning......... get-neighbour exit-name Returnera grannen med det angivna namnet (om ej bundet, returnera #f). add-neighbour! exit-name Lägg till granne under det angivna namnet. place remove-neighbour! exit-name Ta bort grannen med angivet namn. Returnera #f om det ej finns. exits - Returnera en lista av utgångar (namnen). neighbours - Returnera en lista av alla grannar (place%- objekt). 4.4.1 Frågor att besvara (för er själva) Fungerar alla funktioner? Hur testar ni det? Om ni har place%-objektet java-cafe ovan, hur tar ni reda på vad grannen i riktning Up har för namn? Om plats A är kopplad till plats B, måste plats B vara kopplad till plats A? Tips: Eftersom ni ofta kommer att skriva kod på formen (send java-cafe add-neighbour! "Left" su00) (send su00 add-neighbour! "Right" java-cafe)... kan en hjälpprocedur, modell (define (connect-places! place1 exit1 place2 exit2) (send place1 add-neighbour! exit1 place2) (send place2 add-neighbour! exit2 place1)) vara praktisk. 14

4.5 Symmetri - items kan också flytta på sig Precis som figurer flyttar på sig, kommer saker (item%-objekt) också att behöva kunna flyttas. Se åtminstone till att item% stödjer move-to! och get-place (som här får ge antingen den plats/place%-objekt där saken befinner sig, eller den karaktär - character% - som har den). place% stödjer add-item!, get-item, remove-item!. character% stödjer add-item!, get-item, remove-item!. 5 Att börja bygga spel Ni ska nu börja koppla ihop era karaktärer med platserna. Gör detta genom att skapa en ny fil, world_init.rkt, i vilken ni laddar in era tidigare tidigare filer, samt cmd_store.rkt med hjälp av require. Här är ett exempel: #lang racket (require "character.rkt") (require "place.rkt") (require "item.rkt") (require "cmd_store.rkt") (provide (all-defined-out)) ;; ugly hack! Only OK here. ;; ------------ Places (define java-cafe (new place% [name "Java"] [description "Of all the cafes in this world..."])) (define stairs (new place% [name Stairs ] [description "It would be easy to fall here, I reminded myself. "]))... 15

;; Connect the world. (connect-places! java-cafe stairs Up Coffeewards ) ;; connecting places ;; ------------- Characters ;; Creates and adds a character to a given place. Returns the character. (define (make&add-character name_ desc_ talk-line_ place) (let ([new-char (new character% [name name_] [description desc_] [talk-line talk-line_])]) (send new-char move-to place) new-char)) (define player (make&add-character Me "The coat looked worse for wear. Its wearer even more so." "You again! I have nothing to say to myself." java-cafe))... ;; ------------- Items... Testa så att allt fungerar innan ni går vidare. 16

6 Att använda ett enkelt gränssnitt I denna mer eller mindre guidade tur genom labbskelettet, kommer du att få hantera kommunikation med användaren, se lite på callbackstruktur och kommandon. Dessutom kommer det ett par frågor att diskutera med din labbpartner, eller labbass. Det finns lösningsförslag till dessa övningar. 6.1 Motivering Vi kommer att behöva något sätt att interagera med användaren, och något sätt att lagra kommandon. I ett äventyrsspel kanske vi vill kunna skriva enkla kommandon, som exempelvis move north talk farmer och driva spelet framåt på det sättet. I denna guide kommer vi få fram Ett sätt att hantera in- och utmatning. Det liknar bitvis grafiska projekt. Ett sätt att fånga upp och använda inmatningen. 6.2 Uppgifter Kopiera filerna från TDDC74/lab/lab5 till till din labb5-katalog. Öppna presentation.rkt i DrRacket. Öppna ett separat fönster, eller en separat flik, och skriv #lang racket (require "presentation.rkt") Spara filen som tut5.rkt i samma katalog, och kör den. I alla uppgifter nedan, gör du bara ändringar i tut5.rkt. I presentationfilen har vi en klassdefinition, som beskriver hur man kan använda vårt UI. Sedan är det vår sak att använda det, utan att ändra i gränssnittet (även om det kan vara lockande). 17

Uppgift 1 ----------------------------------------------------------------------- Från tut5.rkt, skapa ett fönster som är 500 x 300 pixlar. Hitta på en lämplig rubrik. Skapa ett till fönster som är 400 x 200 (båda ska vara öppna samtidigt). Hur gör man detta? Öppna presentation.rkt och se på alla init-field-namn och värden. Här har vi inte skrivit någon dokumentation utöver kodkommentarerna. Uppgift 2 ----------------------------------------------------------------------- Använd send (i interaktionsfönstret) för att lägga till lite text i ett av fönstrens utmatningsrutor. Hur gör man detta? Se på de publika metoderna i klassen. Det är de som definieras med define/public. Pröva båda present-... -funktionerna, och övertyga dig om att du förstått vad skillnaden är. Även om du inte läst på om grafiksystemet, kan det vara möjligt att du förstår hur de hänger ihop om du läser koden. Uppgift 3 ----------------------------------------------------------------------- Pröva att byta platsangivelse (place-name) för fönstret. Uppgift 4 ----------------------------------------------------------------------- När vi skriver objekt som flyter fritt ute i världen behöver vi beskriva hur de ska hantera olika impulser utifrån. Det kan handla om att någon säger till en figur att flytta sig från en plats, vilket får följdverkningar. Här kommer impulserna från användaren, som matar in saker. I adventure-ui% är det skrivet så att den funktion som heter handle-input kommer att köras om användaren matat in något i inmatningsfältet (bredvid»>) och tryckt på enter. Skapa en lämplig callback (handle-input), som gör att ni ser åtminstone vad som händer, vad för argument den får in. Om ni ser på standard-varianten av handle-input (den som gäller om ni inte skickar in något när fönstret skapas), kan ni se att den är på formen (lambda (this-ui user-command user-arguments)...). Än så länge kan ni strunta i vad this-ui gör (ha bara med det som parameter). (define my-ui (new adventure-ui% 18

[window-name "my game"]... [handle-input (lambda (this-ui command arguments)... )])) Fråga Det bör åtminstone gå att skapa något som gör att ni ser att den körs. Hur? Uppgift 5 ----------------------------------------------------------------------- Skapa en handle-input som gör att användarens kommandon och argumenten till det skrivs ut i fönstret ni har skapat Notera att det vi får in är strängar respektive listor med strängar. Ni kan slå ihop strängar med hjälp av string-append, och listor av strängar med string-join. > (string-join ("hej" "hopp")) "hej hopp" > (string-join (list "Hunden" "katten")) "Hunden katten" > (string-join (list "Hunden" "katten") ", ") ;; vi kan ange separator också. "Hunden, katten" En praktisk sak när man vill bygga längre strängar med string-join är backquote. > (define cat "Tesco") > (string-join ("Katten heter ",cat ".") "") "Katten heter Tesco." Notera kommatecknet, och att det inte är en vanlig apostrof. Det som avses är en backquote 8. Håll in shift och tryck på knappen bredvid backspace. Fråga: Kan ni göra detta utan att hänvisa till namnet my-ui (eller vad ni nu har kallat ert adventure-ui%-objekt? 8 Är du väldigt intresserad av funktionaliteten, se http://docs.racket-lang.org/ guide/qq.html. 19

Uppgift 6 ----------------------------------------------------------------------- Skapa två fönster med olika namn. Se till att meddelanden som skrivs in i det ena fönstret dyker upp i det andra. Notera: Vi har nu två saker som utgår från exakt samma förlaga/klassdefinition, men som har helt separata identiteter. Det enda sätt vi har kopplat ihop dem på, är via våra callbacks. Uppgift 7 ----------------------------------------------------------------------- Skapa en procedur echo-args som tar två argument: först själva ui-objektet, och sedan en tänkt lista av argument som användaren skrivit in. Den ska sedan skriva ut dem i rutan. (define (echo-args this-ui arguments)...)) Skapa en handle-input som använder denna procedur, oavsett vad för kommando användaren skriver in. Till exempel: Användaren skriver in take fish and chips och trycker på enter. Det som skrivs ut i this-ui är något i stil med User wrote: fish and chips. Skapa nu ett till fönster enligt samma mönster. Testa att funktionen fungerar även här, utan att vi behöver ändra i echo-args. (define first-ui (new adventure-ui% [window-name "1"] [height 500] [handle-input (lambda (this-ui command arguments) (echo-args this-ui arguments))])) (define second-ui (new adventure-ui% [window-name "2"] [height 300] [handle-input (lambda (this-ui command arguments) (echo-args this-ui arguments))])) 20

Här har vi en av poängerna med argumentet this-ui. Proceduren echo-args behöver inte veta om vilket UI-objekt som anropat det, och vi behöver inte hårdkoda in first-ui eller second-ui i det. Det är överlag väldigt lite proceduren behöver veta om objektet. Om du inte riktigt greppat det här vid första genomläsningen, oroa dig inte. Få det att fungera som önskat, och fortsätt. Uppgift 8 ----------------------------------------------------------------------- Här ovan har vi en enkel procedur som inuti spelet skulle kunna knytas till kommandot "echo"eller dylikt. Tanken är att vi ska kunna skriva något modell echo fish and chips i den nedre inmatningsrutan och få ut motsvarande information i den övre. Om ni följt uppgiften ovan sker det oavsett vilket kommando som skrivs in (t ex take fish and chips). Vi skulle kunna göra en enda stor cond där man frågar skrev användaren echo? Skrev användaren take? och så vidare. Det blir snabbt ohanterligt (och det blir svårt att lätt få svar på frågor som vilka kommandon är tillåtna, utan att skapa en separat tabell över det 9 ). Här väljer vi att göra det lite mer generellt, med en tabell som håller reda på kommandon. Fråga: Varför inte bara göra detta via (define (echo...)...), och sköta uppslagningen där? Fundera lite, och fråga din labbass. Om vi vill ha en sådan tabell, behöver vi någon slags struktur som knyter ihop namnet/nyckeln echo med värdet, själva echo-args-procedurobjektet. Skriv (require "cmd_store.rkt"). I den finns ett sätt att knyta ihop namn och värden i hashtabeller. Skriv (add-command! "echo" echo-args) för att lägga till kommandot i tabellen. Hur tabellerna fungerar kan ni gärna kolla på. Det kommer att vara användbart i labben. Gör sedan en anpassad handle-input som hämtar proceduren från tabellen. En förstaskiss, där vi definierar en procedur och skickar med den (kan behöva utökas): (define (handle-input_ this-ui command user-arguments)...) (new adventure-ui%... [handle-input handle-input_]) 9 Denna ska sedan underhållas, en ständig källa till irritation. 21

Uppgift 9 ----------------------------------------------------------------------- Anpassa er handle-input så att den klarar att ta fram procedurer beroende på vad användaren skriver in för kommando. (add-command! "greet" (lambda (this-ui arguments) (send this-ui present "You say: HI!"))) (add-command! "dance" (lambda (this-ui arguments) (send this-ui present (string-join (list "You dance " (string-join arguments) ".") "")))) När du gjort detta, ska det gå att skriva t ex greet tyrion eller dance furiously (utan citattecken) och få ut vettiga svar i utmatningsrutan. Se också till att det inte kraschar om användaren skriver något kommando som inte har lagts in i databasen. 22

7 Kommandon i spelet Skapa en fil som heter player_commands.rkt. Den ska börja så här: #lang racket (require "cmd_store.rkt") (require "world_init.rkt") Därefter följer alla kommandon, på formen (define (look_ this-ui arguments)...) (add-command! "look" look_) eller liknande. Den första definitionen skapar en procedur. Argumentordningen känns (förhoppningsvis) igen från avsnittet ovan. Den ska se likadan ut för alla kommandon, så att vi kan använda dem på exakt samma sätt. Börja med att skapa följande enkla kommandon: inventory presenterar vad spelaren bär med sig. Utdata - som presenteras i UI:t - ska vara något i stil med You look in your backpack, and see that you re carrying: Coffee, Key, Fish. Det bör vara hyfsat snyggt och läsbart. look. Presentera beskrivning av platsen i this-ui. Beskrivningen (om vi står i java-cafe i exemplet ovan, med lite tillägg) bör vara något i stil med You re at Java. Of all the cafes in this world... You see the following exits: Up You see the following items: Coffee Den informationen ska ivartfall inkluderas. Om man skriver look Up bör man få en beskrivning av platsen där, modell 23

You look Up. You see: It would be easy to fall here, I reminded myself. move med argumentet riktning. Om man skriver move Up och det finns en sådan utgång, ska spelaren flytta sig dit. Annars ska någon lämplig text skickas till användargränssnittet (UI:t). take med argument vad som ska tas. Om spelaren skriver take Coffee och den saken finns där, ska kaffet flyttas till spelaren. drop släpper en sak som spelaren har. Den hamnar på platsen hen står på. När ni sedan skriver mer specialiserade kommandon - modell give - fundera på vad för funktionalitet som bör beskrivas i själva kommandot, och vad som bör ligga hos objekten själva. I character% finns en give-funktion, och det kan hända att själva give-kommandot därför blir väldigt kort och enkelt att skriva. Omvänt kan det finnas specialhantering som ska kunna beröra flera (men inte alla) karaktärer, där mer av logiken bör ligga i kommandot. Notera här att vi inte antar så mycket om hur spelaren faktiskt anropat den här funktionen, eller hur svaren kommer presenteras. Era funktioner vet inte om this-ui är ett objekt som sköter in- och utmatning via ett textfält skapat med read, en grafisk applikation (som det råkar vara), eller om det är ett objekt som bara skickar och tar mot kommandon från spelarens telefon. 7.0.1 Frågor att fundera över (för egen del) Hur hittar man spelarens nuvarande plats? Vilket objekt vet om sådant? Om ni inte redan kopplat ihop det med GUI:t: hur kan man testa om man lyckats konstruera rätt sak att visa på skärmen? Kan ni (innan ni skriver ihop det) skriva enkla testprocedurer som skriver ut med printf eller display för att se att ni får rätt budskap? Hur testar ni om en karaktär faktiskt flyttat på sig, som ni bad den? 8 Att koppla det hela till GUI Hittills har vi skapat en spelvärld, och en hel del logik. Det kan vara värt att koppla ihop det med delen som presenterar allt för användaren. (Det 24

rekommenderas inte att bygga allt fullt så parallellt i senare projekt.). Skapa filen main.rkt. Den ska börja med #lang racket (require "presentation.rkt") (require "cmd_store.rkt") (require "world_init.rkt") (require "player_commands.rkt") Definiera därefter ett adventure-ui%-objekt, med en lämplig handle-inputfunktion. Jämför det ni tog fram i övningarna ovan. När man startar spelet, ska en beskrivning av platsen dyka upp i rutan. Modell Welcome to the Racket adventure! Type help and press enter to review a list of available commands. You are at Java.... 8.1 Fundera på (för egen del) Hur testar man de olika kommandon ni skapat? 9 Mer speciella kommandon Än så länge är spelet extremt generellt. Man kan vandra runt i världen, ta saker, ge bort saker, prata med karaktärer, och så vidare. Men inget kan egentligen hända. Vi kan inte låsa upp någon dörr, eller få något. Implementera något sådant specialbeteende. Se bara till att inte skriva om de generella item%-klasserna till något hyperspecialiserat. Om t ex en nyckel ska öppna en dörr om spelaren skriver use Key, ska inte item% innehålla något som handlar om nycklar eller vad denna nyckel gör. Det ska inte heller stå något i use-kommandot i player_commands.rkt om om det spelaren säger att det hen vill använda är nyckeln, så.... Förmågan eller specialbeteendet ska ligga hos själva nyckeln, genom t ex en injicerad funktion eller en key-item%-subklass 10. 10 Det kan du läsa mer om i kompendiet. 25

10 Ert egna spel Efter att ha utvecklat detta skelett, skriv nu ert spel med hjälp av det. Kraven har ni i början av avsnittet. Kontrollera särskilt att ni har uppfyllt kravet om att olika karaktärer ska reagera olika på att få vissa saker. När ni lämnar in labben, bifoga även förslag på saker för labbassen att göra i spelet (så att man demonstrerar de kommandon som finns, och saker som kan hända i världen), och dessutom en komplett walkthrough på nivån lista av kommandon att skriva, för att klara spelet. 11 Redovisning Innan redovisning, stäm av Att listan på krav från början av labben uppfylls. Att ni har en bra filindelning (och inga onödiga requires från item.rkt,...). Att ni inte har lagt in för mycket av specialbeteenden i kommandona eller de generella klasserna (enligt ovan). Att ni har skrivit en riktig walkthrough. Visa er labb för labbassen på plats, så att ni kan kolla igenom det lite översiktligt. 26