Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Migrated to Confluence 5.3
Excerpt

Denne oppgaven handler om en FeatureList-klasse, som representerer en såkalt feature vector. Dette er enkelt sagt er et sett med ord/navn med hvert sitt tilhørende (desimal)tall, omtrent som en matematisk vektor i N dimensjoner.

I mange sammenhenger vil objekter av en klasse inneholde eller "eie" objekter av andre klasser, og de underordnede objektene vil kunne flyttes/overføres mellom de overordnede. Når en klasse er assosiert med én instans av en (annen) klasse er dette en 1-1-assosiasjon og når en klasse er assosiert med flere instanser av en annen klasse er dette en 1-n-assosiasjon. Et eksempel er kortspill, hvor kortene starter i kortstokken, fordeles på korthender og til slutt ender i en kortbunke. Et kort kan bare være ett sted om gangen, og må overføres fra ett sted til et annet, f.eks. fra kortstokk til korthender i utdelingsfasen. I denne oppgaven skal du implementere logikk for kortstokk og korthender, både det å fylle opp kortstokken med kort i starten og overføring av kort til korthender. Nedenfor beskrives hver av klassene og metoden disse skal inneholde.

Card-klassen er en såkalt verdiklasse, som kodes slik at objektene ikke kan endres etter at de er opprettet. Et Card-objekt har en kortfarge, som er en av bokstavene 'S' (for spades), 'H' (for hearts), 'D' (for diamonds) og 'C' (for clubs), og tallverdi, som er et heltall mellom 1 (ess) og 13 (konge). Følgende metoder må implementeres:

  • Card(char, int) - konstruktøren initialiserer kortfarge og tallverdi med henholdsvis første og andre argument. Konstruktøren må utløse unntak av typen IllegalArgumentException hvis en (eller begge) av disse verdiene er ugyldige.
  • getSuit() - returnerer kortfargen som en char, en av 'S', 'H', 'D' eller 'C'.
  • getFace() - returnerer tallverdien som en int mellom 1 og 13 (inklusive)
  • toString() - returnerer en streng som består av <suit><face> e.g. for spar ess skal "S1" returneres.

CardDeck-objekter inneholder initielt et visst antall kort av de fire kortfargene S', 'H', 'D' og 'C'. Klassen inneholder standardmetoder for å lese hvor mange og hvilke kort og to metoder for endre tilstand.

Konstruktør:

  • CardDeck(int n) - fyller kortstokken med de n første kortene av hver kortfarge, totalt n * 4 kort, med spar 1 som første kort (indeks nr. 0), spar 2 som andre (nr. 1), spar 3 som tredje (nr. 2), spar 4 som fjerde (nr. 3), ..., hjerter 1 som fjortende (nr. 13), hjerter 2 som femtende (nr. 4) osv. i.e. først alle spar, så hjerter, så ruter og så kløver, alle i stigende rekkefølge. 

Lesemetoder:

  • getCardCount() - returnerer hvor mange Card-objekter som CardDeck-objektet inneholder
  • getCard(int n) - returnerer kort nr. n eller utløser et IllegalArgumentException hvis n ikke er gyldig

Endringsmetoder:

  • deal(CardHand, int n) - flytter n kort fra kortstokken (CardDeck-objektet) til korthånda (CardHand-objektet, som er første argument), ved å ta ett og ett kort med høyeste gyldige indeks, fjerne det fra CardDeck-objektet og legge det til CardHand-objektet
  • shufflePerfectly() - stokker kortstokken ved å dele den i to like store deler og flette de to delene perfekt, slik at kortet på toppen forblir på toppen og kortet på bunnen forblir på bunnen (se http://en.wikipedia.org/wiki/Out_shuffle)

CardHand-objekter inneholder initielt ingen kort, og klassen inneholder de samme standardmetodene som CardDeck, altså getCardCount() og getCard(int), for å lese hvor mange og hvilke kort den inneholder. I tillegg har den to metoder for å endre tilstand:

  • addCard(Card) - legger argumentet til dette CardHand-objektet
  • play(int n) - returnerer og fjerner kort nr. n (første kort har nr. 0) fra dette CardHand-objektet (som om det ble spilt ut)

 

Bakgrunn

En feature vector (norsk: egenskapsvektor?) knytter et sett med navngitte "dimensjoner" til hvert sitt (desimal)tall, slik at en kombinasjon av egenskaper kan representeres som et punkt i et N-dimensjonalt rom. En slik vektor brukes i mange anvendelser, f.eks. til å representere evner. En figur i et spill kan f.eks. ha tallverdier for kløkt, smidighet, utholdenhet osv., tilsvarende evnevektoren (kløkt: 10, smidighet: 15, utholdenhet: 5). Hvis figuren utfører visse (trenings)oppgaver, så vil evnene endre/utvikle seg. Trikset da er å knytter en tilsvarende evnevektor til oppgaven, så en kan beregne figurens nye evneprofil ved å legge samme de to vektorene: ny evnevektor = gammel evnevektor + oppgavevektor.

Slike vektorer brukes i mange fagområder for klassifisering og beregninger, og i denne oppgaven skal du lage en FeatureList-klasse som representerer og opererer på slike vektorer.

FeatureList-klassen

FeatureList-klassen har en del grunnleggende metoder for å hente ut informasjon om hvilke dimensjoner eller egenskapsnavn og verdier vektoren har. I tillegg har den en del metoder for å endre på innholdet, både legge til dimensjoner og verdier og endre eksisterende verdier.

Metoder som leser tilstanden

FeatureList() - konstruktøren initialiserer objektet slik at det fra starten er helt tomt, dvs. uten egenskaper (navn og verdier).

FeatureList fl1 = new FeatureList() => fl1 er ()

Merk at du kan godt lage andre konstruktører i tillegg, hvis du synes det er praktisk. F.eks. kan du lage en med et varargs-parameter av typen Object[], altså FeatureList(Object... namesValues), slik at en kan skrive new FeatureList("a", 2.0, "b", 3.0)

boolean hasFeature(String featureName) - returnerer true dersom egenskapen (dimensjonen) med det gitt navnet finnes i denne FeatureList-instansen, og false ellers.

// Anta fl1 er (a: 2.0, b: 3.0)
fl1.hasFeature("a") => true
fl1.hasFeature("c") => false 

Collection<String> getFeatureNames() - returnere alle egenskapsnavnene (dimensjonene) i denne FeatureList-instansen på én gang. Rekkefølgen er uten betydning.

// Anta fl1 er (a: 2.0, b: 3.0)
fl1.getFeatureNames() => Collection<String> med elementene "a" og "b" 

double getFeatureVaue(String featureName) - returnerer verdien for den angitte egenskapen (dimensjonen) i denne FeatureList-instansen. Hvis den angitte egenskapen ikke finnes, så skal metoden returnere 0.0, som er den (implisitte) verdien for manglende egenskaper.

// Anta fl1 er (a: 2.0, b: 3.0)
fl1.getFeatureValue("a") => 2.0
fl1.getFeatureValue("c") => 0.0

FeatureList toFeatureList() - lager en kopi (altså en ny FeatureList-instans) med samme innhold som denne FeatureList-instansen. Hvis denne instansen eller kopien siden endres (med en av metodene nedenfor) så skal ikke den andre endres.

// Anta fl1 er (a: 2.0, b: 3.0)
FeatureList fl2 = fl1.toFeatureList() => fl2 er nå (a: 2.0, b: 3.0)
// hvis en nå endrer fl1 eller fl2 så vil ikke den andre blir endret

I tillegg kan det være lurt å lage en toString()-metode, som returnerer en String på formen som er brukt i eksemplene.

 

Metoder som endrer tilstanden

void addFeature(String featureName, double value) - legger til egenskapen med det angitte navnet, dersom denne ikke finnes fra før. Hvis den finnes fra før, skal egenskapen ikke endres.

FeatureList fl1 = new FeatureList();
fl1.addFeature("a", 2.0) => fl1 er (a: 2.0)
fl1.addFeature("b", 3.0) => fl1 er (a: 2.0, b: 3.0)
fl1.addFeature("a", 4.0) => fl1 er (a: 2.0, b: 3.0)

void setFeatureValue(String featureName, double value) - endrer verdien til den angitte, eksisterende egenskapen. Hvis egenskapen ikke finnes, så skal FeatureList-instansen ikke endres.

// fortsettelse, fl1 er altså (a: 2.0, b: 3.0)
fl1.setFeatureValue("a", 4.0) => fl1 er
(a: 4.0, b: 3.0)
fl1.setFeatureValue("c", 1.0) => fl1 er (a: 4.0, b: 3.0)

void increment(double value) - øker verdien til alle egenskapene i denne FeatureList-instansen med den angitte verdien.

// fortsettelse, fl1 er altså (a: 4.0, b: 3.0)
fl1.increment(3.0) => fl1 er (a: 7.0, b: 6.0)

void add(FeatureList featureList) - endrer denne FeatureList-instansen, slik at den etterpå har alle egenskapene som denne og den angitte instansen hadde. Verdien til hver egenskap er summen av verdiene i de to instansene. Hvis en instans mangler en egenskap, så er det som om verdien til den er 0.0.

// fl1 er fortsatt (a: 7.0, b: 6.0), mens fl2 er (b: 1.0, c: 6.0)
fl1.add(f2) => fl1 er (a: 7.0, b: 7.0, c: 6.0), fl2 er uendret

void mult(FeatureList featureList) - endrer denne FeatureList-instansen, slik at den etterpå har alle egenskapene som denne og den angitte instansen hadde. Verdien til hver egenskap er produktet av verdiene i de to instansene. Hvis en instans mangler en egenskap, så er det som om verdien til den er 0.0.

// fl1 er fortsatt (a: 7.0, b: 7.0, c: 6.0) og fl2 fortsatt (b: 1.0, c: 6.0)
fl2.addFeature("d", 3.0) => fl2 er (b: 1.0, c: 6.0, d: 3.0)
fl1.mult(f2) => (a: 0.0, b: 7.0, c: 36.0, d: 0.0)

Løsningshint:

  • Egenskapsnavn og -verdier kan representeres på tre måter: 1) Med to lister med navn og tilhørende verdi i samme posisjon, 2) én liste med instanser av en hjelpeklasse, f.eks. kalt NameValuePair, som holder på et navn og den tilhørende verdien og 3) en Map. Velg den varianten du synes virker enklest.
  • Velg representasjon først, altså hvilke felt du trenger, og skriv så metodene som leser tilstanden. Deretter skriver du én og én metode i den rekkefølgen de står over.
  • Hvis én metode har flere krav, f.eks. hvordan den håndterer spesialtilfeller, så skriv kode for den typiske oppførselen først.
  • Test hver nye metode du lager i din egen main-metode eller hovedprogram, f.eks. basert på eksemplene over, før du kjører våre tester.

JExercise-testkode for denne oppgaven finner du her: objectstructures/CardTestFeatureListTest.java, objectstructures/CardDeckTest. java og objectstructures/CardHandTest.java. jextest-koden finner du her: objectstructures/CardFeatureList.jextest, objectstructures/CardDeck.jextest og objectstructures/CardHand.jextest.

.ex for bruk med Exercise-panelet finner du her: CardFeatureList.ex.

Include Page
Bunntekst for JExercise-oppgaver
Bunntekst for JExercise-oppgaver