WANDA Avonturen Spel Documentsoort: Behoeftenspecificatie Versie: 2.0 Datum: 12 maart 2002 Auteur: Status: Serge Demeyer Opgeleverd Samenvatting Dit document bevat de specificaties voor een informaticasysteem ter ondersteuning van een avonturenspel. Het is geschreven in het kader van het vak "Inleiding Software Engineering" (1ste kandidatuur informatica - Universiteit Antwerpen). Context Gandalf is een software bedrijfje dat software spelletjes op de markt brengt. Inspelend op de groeiende trend om te kunnen spelen op kleinere systemen verbonden via netwerken met lage bandbreedte (GSM e.d.) wil Gandalf een prototype bouwen van een spel (werknaam WANDA) waarbij verschillende figuren doorheen een virtuele wereld wandelen en daar allerlei avonturen beleven. Het voornaamste doel van dit prototype is na te gaan of het mogelijk is een leuk spel te bouwen op dergelijke beperkte machines, maar als blijkt dat het realiseerbaar is wil Gandalf het prototype uitbouwen tot een heus product. De informatie over de initiële virtuele wereld wordt deels ingelezen van een tekstbestand (zie appendix 1) en deels willekeurig gegenereerd. Daarna laat de gebruiker zijn figuur doorheen de wereld lopen, waarbij het systeem telkens zal antwoorden wat er allemaal te zien is (vb., deuren, kamers, gangen, voorwerpen, monsters, andere figuren). De gebruiker kan op elk van die zaken reageren, waardoor de situatie verandert en het spel aan de gang blijft. De voornaamste criteria voor een succesvol systeem zijn: (a) de mate waarin het mogelijk is spannende scenario s te bouwen door spelelementen te combineren; (b) de mate waarin de uitvoer aangepast kan worden aan de specifieke vereisten van een bepaald systeem [vb. alleen tekst, HTML, grafisch,...]. Overzicht De behoeftespecificatie is opgesteld aan de hand van zogenaamde use-cases. Elke usecase beschrijft een klein gedeelte van de gewenste functionaliteit, en het is de bedoeling dat tijdens elke fase van het project verschillende van die use cases geïmplementeerd worden.
Hieronder volgt een opsomming van alle use-cases inclusief hun prioriteit. Use-case Prioriteit 1. Invoer 1.1: Kamers en gangen inlezen VERPLICHT 1.2: Kamers en gangen met posities inlezen VERPLICHT 1.3: Monsters inlezen VERPLICHT 1.4: Acties inlezen BELANGRIJK 2. Uitvoer 2.1: Simpele uitvoer VERPLICHT 2.2: Standaard uitvoer VERPLICHT 2.3: Status uitvoer BELANGRIJK 2.4: Grafische uitvoer NUTTIG 3. Spelen 3.1: Spelen >>> Deze use-case stuurt alle andere <<< BELANGRIJK 3.2: "Random" animatie NUTTIG Legende Een typische use-case bevat de volgende onderdelen. Refertenummer & titel Wordt gebruikt om naar een bepaalde use-case te verwijzen. De specificatie van een systeem vraagt meer dan wat binnen de voorziene tijd op te leveren is. Vandaar dat we per use-case aangeven in hoeverre die functionaliteit belangrijk is. In volgorde van belangrijkheid kan hier staan: VERPLICHT (deze usecase moet opgeleverd worden), BELANGRIJK (niet essentieel maar bij voorkeur toch opleveren), NUTTIG (interessant maar kan weggelaten worden). Summiere beschrijving van het waarom van de use-case, t.t.z. wat de use-case bijdraagt tot de gehele functionaliteit. Summiere beschrijving van de uitgangspunten bij aanvang van de use-case. Summiere beschrijving van wat opgeleverd zal worden als er niks fout is gegaan.
Een sequentiële beschrijving van hoe de use-case precies zal verlopen als alles goed gaat (het zogenaamde "happy day scenario"). De stappen zijn genummerd en kunnen controle instructies (WHILE, IF,...) bevatten. Uitzonderingen: Een lijst van mogelijke probleemgevallen en hoe die behandeld zullen worden. Een probleem geval (a) verwijst naar het nummer van de stap waar het probleem kan optreden, (b) bevat een conditie die aangeeft wanneer het probleemgeval optreed, (c) omschrijft heel kort (een lijn) hoe het probleem behandeld zal worden. Voorbeeld: Een voorbeeld van wat in- of uitgevoerd kan worden. Soms is een use-case een uitbreiding van een andere use case, en dan zijn de volgende onderdelen relevant. Uitbreiding: Een referte naar de use case waarvan deze een uitbreiding is. Een lijst van extra en/of aangepaste stappen t.o.v de use case waarvan deze een uitbreiding is. Een uitbreiding (a) verwijst naar het nummer van de stap de uitgebreid wordt, (b) zegt of de uitbreiding voor, na of tijdens de normale stap zal gebeuren, (c) omschrijft wat precies in de uitbreiding zal gebeuren. Behoeftes 1. Invoer 1.1: Kamers en gangen inlezen VERPLICHT Inlezen van de basis informatie voor een virtuele wereld, namelijk de verschillende kamers en gangen en hoe die met elkaar verbonden zijn. Een ASCII bestand met daarop een beschrijving van de kamers en gangen. (Zie Appendix 1 voor meer informatie over het bestandsformaat) Het systeem bevat een grondplan voor een virtuele wereld. 1. Open invoerbestand 2. WHILE Bestand niet ingelezen 2.1. Herken het soort element (één van GANG, KAMER) 2.2. Lees verdere informatie voor het element 2.3. IF Verifieer geldige informatie 2.3.1. THEN Voeg element toe aan de virtuele wereld 2.3.1. ELSE Foutboodschap + positioneer op volgende element in het bestand 3. Verifieer consistentie van de wereld
4. Sluit invoerbestand Uitzonderingen: 2.1. [Onherkenbaar element] Foutboodschap + positioneer op volgende element in het bestand => verdergaan vanaf stap 2 2.2. [Ongeldige informatie] Foutboodschap + positioneer op volgende element in het bestand => verdergaan vanaf stap 2 3. [Inconsistente wereld] Foutboodschap => verdergaan vanaf stap 4 1.2: Kamers en gangen met posities inlezen VERPLICHT Uitbreiding van use-case "1.1: Kamers en gangen inlezen" zodat een virtuele wereld op het scherm getekend kan worden. Uitbreidingen: 2.2. [tijdens] Hou rekening met extra "positie" informatie Voorbeeld: Hieronder een voorbeeld van twee kamers verbonden door één gang. #KAMER [ Noorden, naam = "Noordkamer", positie = (2, 0, 8, 3) deuren = ( Zuiden, 3, 3) ] #KAMER [ Zuiden, naam = "Zuidkamer", positie = (2, 5, 8, 8) deuren = ( Noorden, 6, 5) ] #GANG [ NoordZuid, Die er als volgt zal uitzien naam = "NoordZuidGang", positie = (3, 3, 3, 4, 6, 4, 6, 5) begin = (Noorden, Zuiden ), einde = (Zuiden, Noorden ) ]
1.3: Monsters inlezen VERPLICHT Inlezen van de monsters die de virtuele wereld bevolken. Een ASCII bestand met daarop een beschrijving van de monsters. (Zie Appendix 1 voor meer informatie over het bestandsformaat) Het systeem bevat een verzameling monsters. 1. Open invoerbestand 2. WHILE Bestand niet ingelezen 2.1. Herken het soort element (één van MONSTER) 2.2. Lees verdere informatie voor het element 2.3. IF Verifieer geldige informatie 2.3.1. THEN Voeg element toe aan de verzameling monsters 2.3.1. ELSE Foutboodschap + positioneer op volgende element in het bestand 3. Verifieer consistentie van de lijst monsters 4. Sluit invoerbestand
Uitzonderingen: 2.1. [Onherkenbaar element] Foutboodschap + positioneer op volgende element in het bestand => verdergaan vanaf stap 2 2.2. [Ongeldige informatie] Foutboodschap + positioneer op volgende element in het bestand => verdergaan vanaf stap 2 3. [Inconsistente lijst monsters] Foutboodschap => verdergaan vanaf stap 4 1.4: Acties inlezen BELANGRIJK Uitbreiding van use-case "1.3: Monsters inlezen" zodat de monsters zich door de virtuele wereld kunnen bewegen. Uitbreidingen: 2.2. [tijdens] Hou rekening met extra "acties" informatie Voorbeeld: Hieronder een voorbeeld van een monster. Het is een tovenaar, voorgesteld door het karakter @. Als de acties uitgevoerd worden binnen de voorbeeldwereld uit use-case "1.2: Kamers en gangen met posities inlezen" (a) bevindt de tovenaar zich initiëel in de NoordKamer, (b) maakt hij drie stappen zuidwaarts om in de NoordZuidGang terecht te komen; (c) doet vier stappen naar rechts waarmee hij tegen de muur van de gang botst, (d) doet drie stappen naar beneden, eentje naar links en eentje naar boven zodat hij uiteindelijk in de ZuidKamer vlak naast de Noorden deur komt te staan. 2. Uitvoer #MONSTER [ Tovenaar, naam = "Gandalf", positie = (3, 1), voorstelling = "@", acties = "vvv>>>>vvv<^" ] Voorbeeld Invoerdata In de volgende voorbeelden wordt uitgegaan van steeds dezelfde invoerdata, namelijk #KAMER [ Noorden, naam = "Noordkamer", positie = (2, 0, 8, 3) deuren = ( Zuiden, 3, 3) ] #KAMER [ Zuiden,
naam = "Zuidkamer", positie = (2, 5, 8, 8) deuren = ( Noorden, 6, 5) ] #GANG [ NoordZuid, naam = "NoordZuidGang", positie = (3, 3, 3, 4, 6, 4, 6, 5) begin = (Noorden, Zuiden ), einde = (Zuiden, Noorden ) ] 2.1: Simpele uitvoer VERPLICHT Uitvoer van alle informatie in de virtuele wereld. Het systeem bevat een grondplan voor een virtuele wereld. Het systeem heeft een tekstbestand (ASCII) uitgevoerd, waarin de informatie over de virtuele wereld netjes is uitgeschreven. 1. Open uitvoerbestand 2. WHILE Nog kamers beschikbaar 2.1. Schrijf kamergegevens uit 3. WHILE Nog gangen beschikbaar 3.1. Schrijf ganggegevens uit 4. Sluit uitvoerbestand Uitzonderingen: Geen Voorbeeld: KAMERS * Noordkamer - deuren * Zuidkamer Zuiden -> Noord-Zuid gang (begin) - deuren
Noorden -> Noord-Zuid gang (einde) GANGEN * Noord-Zuid gang - begin: Noordkamer (Zuiden) - einde: Zuidkamer (Noorden) 2.2: Standaard uitvoer BELANGRIJK Een "bewaar" van de huidige toestand van het spel, dus een uitvoer van alle informatie in de virtuele wereld in hetzelfde formaat als het inleesformaat. Het systeem bevat een consistente wereld en een consistente verzameling monsters + er zijn twee namen gekend voor uitvoerbestanden; in wat volgt heten ze "wereldbestand" en "monsterbestand" Het systeem heeft twee tekstbestanden (ASCII) uitgevoerd, waarin de informatie over de virtuele wereld en de monsters is uitgeschreven zodat het achteraf opnieuw kan ingelezen worden. 1. Open "wereldbestand" 2. WHILE Nog kamers beschikbaar 2.1. Schrijf kamergegevens uit in het formaat vermeld in Appendix 1 3. WHILE Nog gangen beschikbaar 3.1. Schrijf ganggegevens uit in het formaat vermeld in Appendix 1 4. Sluit "wereldbestand" 5. Open "monsterbestand" 6. WHILE Nog monsters beschikbaar 6.1. Schrijf monstergegevens uit in het formaat vermeld in Appendix 1 7. Sluit "monsterbestand" Uitzonderingen: Geen 2.3: Status uitvoer BELANGRIJK Status informatie geven over de nabije omgeving van een bepaalde "focus" figuur Het systeem bevat een consistente wereld en een consistente verzameling monsters + één bepaald monster is geselecteerd als "focus"
Het systeem heeft een tekstuele beschrijving gegeven van de nabije omgeving van de "focus" figuur. 1. IF focus in kamer 1.2. THEN schrijf informatie uit over alle andere monsters binnen een straal van 3 posities 2. Schrijf mogelijke richtingen uit waarin figuur zich kan bewegen Uitzonderingen: Geen 2.4: Grafische uitvoer NUTTIG Grafische weergave van de huidige toestand van een spel. Het systeem bevat een consistente wereld en een consistente verzameling monsters Het systeem heeft de huidige toestand van de wereld grafisch weergegeven. 1. Initialiseer grafisch scherm 2. WHILE Nog kamers beschikbaar 2.1. Teken kamers 3. WHILE Nog gangen beschikbaar 3.1. Teken Gangen 4. WHILE Nog monsters beschikbaar 4.1. Teken Monster 4. Sluit uitvoerbestand Uitzonderingen: Geen 3. Spelen 3.1: Spelen Deze use-case is de hoofd use case, die het volledige spel stuurt. BELANGRIJK Het spelen van het WANDA spel. Twee ASCII bestanden "wereldbestand" en "monsterbestand" (zie Appendix 1 voor het formaat) met daarop respectievelijk een beschrijving van de virtuele wereld en de monsters die die wereld bevolken. Nihil
1. Lees "wereldbestand" in (via "1.2: Kamers en gangen met posities inlezen") 2. Lees "monsterbestand" in (via "1.3: Monsters inlezen" of "1.4: Acties inlezen") 3. WHILE Nog acties beschikbaar 3.1. Beschrijf de huidige situatie (via "2.3: Status uitvoer" en/of "2.4: Grafische uitvoer") 3.2. WHILE Nog Monsters beschikbaar 3.2.1 Voer eerstvolgende actie uit 4.Sluit spel af Uitzonderingen: 3. [vraag tot bewaren] "2.4: Standaard uitvoer" => verder gaan vanaf stap 4 3.2.1. [botsing] als twee monsters zich naar dezelfde locatie zouden begeven worden de acties van beide monsters genegeerd => verder vanaf stap 3.2 3.2.1. [onmogelijke actie] als een bepaalde actie onmogelijk is (vb. er staat een muur in de weg) wordt de actie genegeerd => verder vanaf stap 3.2 3.2: "Random" animatie NUTTIG Uitbreiding van use-case "3.1: Spelen" zodat het spel uit zichzelf lijkt te spelen. Uitbreidingen: 3.2.1. [voor] De eerstvolgende actie wordt willekeurig gegenereerd (zie Appendix 2: Willekeurige acties)
Appendix 1: Invoer formaat Het invoerformaat voor de virtuele wereld is zodanig gekozen dat nieuwe soorten spel elementen makkelijk kunnen worden toegevoegd. Wereld = { Element }. Element = "#" ElementType "[" ID "," InfoLijst "]". ElementType = "KAMER" "GANG" MONSTER. ID = <ident>. InfoLijst = Info {"," Info }. Info = InfoType "=" InfoWaarde. InfoType = "naam" "deuren" "begin" "einde" "positie" "voorstelling" "acties". InfoWaarde = Primitief Lijst. Primitief = <integer> <string>. Lijst = ( Primitief {, Primitief} ) De definities voor <ident>, <integer> & <string> komen overeen met de Oberon definities, namelijk <ident> = <letter> { <letter> <digit> } <integer> = <digit> { <digit> } <string> = '"' {character} '"' "'" {character} "'" <digit> = "0"... "9" <letter> = "a"... "z" "A"... "Z" Ongeldige informatie Merk op dat de InfoLijst een relatief vrij formaat heeft wat sterk zal afhangen van het soort element dat gedefinieerd wordt. Zo moet voor een KAMER minstens de velden "naam" en "deuren"vermeld worden; en voor een GANG minstens de velden "naam", "begin" en "einde". Bovendien zal afhankelijk van het InfoType slechts een bepaalde InfoWaarde toegelaten zijn: een naam verwacht een <string; begin en einde een Lijst met precies één <ident> en precies één <string>. Vandaar dat tijdens de invoer moet gecontroleerd worden of de invoer al dan niet geldig is. De volgende lijst geeft voor elk wereldelement aan wat er verplicht en optioneel in zijn beschrijving moet staan.
KAMER verplicht: naam (type <string>) positie (type <Lijst(<integer>, <integer>, <integer>, <integer>)> (de positie is een lijst met precies vier natuurlijke getallen. Die lijst specifiëert de coordinaten van een omhullende rechthoek van de kamer in het coordinatensysteem van het scherm. De vier getallen zijn respectievelijk top, links, bodem, rechts) deuren (type <Lijst [<string>, <integer>, <integer>]> (deuren is een lijst met een onbeperkt aantal tripels van een string gevolgd door twee natuurlijke getallen. De string is de naam vn de deur en moet uniek zijn voor alle deuren binnen één kamer. De twee natuurlijke getallen stellen de horizontale, verticale coördinaten voor van de plaats van de deur in het coörindatensysteem van het scherm) optioneel: nihil GANG verplicht: naam (type <string>) positie (type <Lijst[<integer>, <integer>]> (de positie is een lijst met een onbeperkt aantal koppels natuurlijke getallen. Elk koppel specifiëert de coordinaten van een opeenvolgend "breekpunten" in een lijnvoorstelling van de gang) begin (type <Lijst (<ident>, <string>]) einde (type <Lijst (<ident>, <string>]) (begin en einde zijn lijsten met precies twee elementen, een <ident> en een <string>. De <ident> is de ID van een bestaande kamer, de string een naam van een deur binnen die kamer) optioneel: nihil MONSTER verplicht: naam (type <string>)
positie (type <Lijst(<integer>, <integer>)> (de positie is een koppel van natuurlijke getallen en specifiëert de initiële coordinaten van het monster in de virtuele wereld) voorstelling (type < string>) (de voorstelling is een string --normaal van lengte 1-- die gebruikt wordt om het monster op het scherm voor te stellen) optioneel: acties (type < string>) (acties is een string waar elk karakter een bepaalde actie voorsteld. Momenteel zijn er alleen acties om een figuur te laten bewegen, respectievelijk ^ --naar boven, v --naar beneden, < --naar links, > --naar rechts) Inconsistente wereld Het bestand met de in te lezen virtuele wereld en de te gebruiken monsters wordt met de hand geschreven door de spelontwerper. Om er mee te kunnen spelen moeten die beide consistent zijn, en die consistentie moet na het inlezen gecontroleerd worden. Een wereld is consistent als: Voor elke kamer geldt dat elke deur verbonden is met een gang. Dat de positie van deur bovendien gelijk is aan de respectievelijke begin- of eindpositie van de gang. Voor elke gang geldt dat het begin en einde verbonden zijn met een kamer. Een verzameling monsters is consistent als: Voor elke monster geldt dat het zich in op een legale plaats in de wereld bevindt, namelijk in een kamer of in een gang. Geenenkel monster zich op dezelfde plaats bevindt.
Appendix 2: Willekeurige acties Oberon bevat een module "RandomNumbers" waarmee willekeurige getallen kunnen gegenereerd worden. Telkens je de routine "Uniform()" oproept krijg je een getal in het open interval ]0, 1[. Door de gepaste vermenigvuldigingen en afrondingen toe te passen kan zo'n getal omgezet worden in een willekeurige index binnen de verzameling toegelaten acties.