Sensorveiledning for eksamen

Repositoriet med start-kode for eksamen finner finner du her: https://git.app.uib.no/ii/inf101/23v/exam/inf101v23exam

Eksamen gir totalt 65 poeng. I tillegg blir det gitt poeng for semesteroppgaver (opptil 30 poeng) og lab’er i kurset (opptil 5 poeng).


Oppgave 1: flervalg

10 poeng. Automatisk rettet.

For hver av påstandene under, avgjør om påstanden er sann eller usann.

Det gis 1.1 poeng for hvert riktig svar, og -0.9 poeng for både gale svar og ubesvarte rader. Hvis du er usikker på noe har du altså ingenting å tape på å gjette. Du kan få maksimalt 10 poeng, og kan ikke få negative poeng på oppgaven. Dette systemet innebærer at du får maksimal score hvis du har alt riktig, og 0 poeng hvis du har 4 eller færre riktige svar.

Avgjør om påstanden er sann eller usann
Standard-implementasjonen av equals-metoden gir alltid false som svar.
Hvis en klasse Milk implementerer grensesnittet Drinkable, da vil en variabel med typen Drinkable kunne peke på et objekt i klassen Milk uten at vi ser advarsler eller får feil.
Et grensesnitt definerer en type og angir hvilke metoder som er tilgjengelig hvis man benytter typen.
Et objekt kan ha flere klasser.
En instansvariabel kan ha forskjellig verdi for ulike objekter i samme klasse.
En klasse kan ha mer enn én konstruktør.
En instansvariabel kan ikke eksistere uten å tilhøre et objekt.
Poenget med å definere en klasse er alltid å opprette objekter i klassen. Dersom man ikke oppretter objekter i en klasse, kunne man like gjerne fjernet den fra kildekoden uten at man merket forskjell.
To objekter i samme klasse kan implementere ulike grensesnitt.
Alle metoder i Java (inkludert dem som er static) har automatisk tilgang til variabelen this.

Avgjør om påstanden er sann eller usann Fasit
Standard-implementasjonen av equals-metoden gir alltid false som svar. ❌ Usant, standard-implementasjonen av equals gir true dersom et objekt sammenlignes med seg selv
Hvis en klasse Milk implementerer grensesnittet Drinkable, da vil en variabel med typen Drinkable kunne peke på et objekt i klassen Milk uten at vi ser advarsler eller får feil. ✅ Sant
Et grensesnitt definerer en type og angir hvilke metoder som er tilgjengelig hvis man benytter typen. ✅ Sant
Et objekt kan ha flere klasser. ❌ Usant, et objekt kan kun være i én klasse. Det kan derimot ha flere typer hvor flere av dem kan være definert av objektets super-klasser.
En instansvariabel kan ha forskjellig verdi for ulike objekter i samme klasse. ✅ Sant
En klasse kan ha mer enn én konstruktør. ✅ Sant
En instansvariabel kan ikke eksistere uten å tilhøre et objekt. ✅ Sant
Poenget med å definere en klasse er alltid å opprette objekter i klassen. Dersom man ikke oppretter objekter i en klasse, kunne man like gjerne fjernet den fra kildekoden uten at man merket forskjell. ❌ Usant, klasser kan også ha metoder og variabler som er static, og slike metoder og variabler kan benyttes uten å opprette objekter i klassen. For eksempel main-metoden.
To objekter i samme klasse kan implementere ulike grensesnitt. ❌ Usant, hvilke grensesnitt et objekt implementerer bestemmes av klassen objektet er i.
Alle metoder i Java (inkludert dem som er static) har automatisk tilgang til variabelen this. ❌ Usant, metoder som er static har ikke tilgang til variabelen this.

Det gis 1.1 poeng for hvert riktig svar, og -0.9 poeng for både gale svar og ubesvarte rader. Det gis maksimalt 10 poeng og minimum 0 poeng. Dette gir følgende poengtabell:

Antall riktige Poeng
0 0
1 0
2 0
3 0
4 0
5 1
6 3
7 5
8 7
9 9
10 10

Oppgave 2: tetromino-fabrikken

15 poeng. Rettes av sensor.

Denne oppgaven omhandler semesteroppgave 1 (Tetris). I det utleverte repositoriet finner du et utdrag av en mulig løsning på oppgaven i pakken no.uib.inf101.exam23v.tetromino.

Fasongen til en tetromino er her representert av en to-dimensjonal boolsk array.

/**
 * A tetromino is a shape that can be used in the game of Tetris. It 
 * consists of four quadratic blocks glued together along the edges.
 * <p>
 *   
 * This class is immutable.
 */
public final class Tetromino implements Iterable<GridCell<Character>> {

  private final char symbol;
  private final boolean[][] shape; //   <------- to-dimensjonal boolsk array
  private final CellPosition pos;
  /* ... */
}

I test-hierarkiet finner du klassen PatternedTetrominoFactory, som kan lage nye brikker etter et bestemt mønster. En medstudent i kurset ønsker å teste at denne tetrominofabrikken fungerer. I TestPatternedTetrominoFactory har hen derfor en metode som skal teste et veldig enkelt mønster, nemlig tilfellet at vi kun skal få T-brikker fra fabrikken:

@Test
public void testFactoryProducingOnlyT() {
  PatternedTetrominoFactory factory = new PatternedTetrominoFactory("T");
  
  for (int i = 0; i < NUMBER_OF_TRAILS; i++) {
    Tetromino tetro = factory.getNext();
    boolean[][] shapeTetro = null;  // TODO: get the shape of the tetromino here
    
    // Expected shape of T-piece:
    // 0 0 0
    // 1 1 1
    // 0 1 0
    assertFalse(shapeTetro[0][0]);
    assertFalse(shapeTetro[0][1]);
    assertFalse(shapeTetro[0][2]);
    
    assertTrue(shapeTetro[1][0]);
    assertTrue(shapeTetro[1][1]);
    assertTrue(shapeTetro[1][2]);
    
    assertFalse(shapeTetro[2][0]);
    assertTrue(shapeTetro[2][1]);
    assertFalse(shapeTetro[2][2]);  
  }
}

Studenten har nå kommet til deg for å be om hjelp, og påstår (feilaktig?) at testen nesten er ferdig, «det mangler jo bare en linje».

Forklar (med ord, ikke med lange kodeavsnitt) til studenten hvordan du kan gjøre testen ferdig. Hvis det er flere måter å gjøre det på (hint, hint!), må du forklare fordeler og ulemper med hensyn til god stil med objektorientert programmering.

Vi anbefaler at du skriver minst 2-3 avsnitt, men maksimalt 400 ord

Den raskeste løsning er å la kodelinjen være boolean[][] shapeTetro = tetro.shape, og samtidig la shape være pakke-privat i stedet for å være (som nå) privat. Dette er en dårlig løsning, siden vi bryter innkapslingen, og eksponerer en instansvariabel. I tillegg vil dette gjøre at Tetromino slutter å være uforanderlig (immutable), siden vi nå ikke har kontroll innad i Tetromino-klassen på hvovidt instansvariabelen kan endres eller ikke.

Et litt bedre alternativ vil være å ha en metode getShape i Tetromino-klassen som returnerer this.shape. Fordi boolean[][] er en muterbar type løser vi imidlertid ikke problemet med at andre kan mutere tetrominoen vår; tenk hvis noen benytter først getShape og deretter begynner å mutere arrayen! Men ved å ha en getter-metode i stedet for å ekspone en instansvariabel gir vi i det minste oss selv større handlingsrom til å endre implementasjonen senere.

For å løse problemet med mutasjon kan vi for eksempel endre getShape-metoden slik at den returnerer en dyp-kopi av this.shape i stedet for å returnere den direkte.

Det er likevel unødvendig å opprette en metode i Tetromino-klassen som eksponerer vår indre representasjon bare for testingen sin skyld – når vi skriver tester bør vi forholde seg til de metodene klassen allerede har og ikke opprette nye (implementasjonsdetaljer bør holdes skjult). Om vi kunne benytte oss av iterator-metoden som allerede eksisterer for å hente ut posisjonene, er det et bedre alternativ (det er tross alt dette som faktisk benyttes av klientkoden til Tetromino, og som derfor bør testes).

Et fjerde alternativ er å teste ved bruk av equals-metoden som allerede er bygget inn i Tetromino. Da blir testen så enkel som å sjekke om resultatet av getNext-kallet på fabrikken returnerer et objekt som er likt resultatet av Tetromino.newTetromino(‘T’) (det er forresten slik en tilsvarende test blir gjort i Tetris-guiden i semesteroppgave 1).

  • 5 poeng: benytter relevante faguttrykk og gir en god drøfting
  • 3 poeng: beskriver minst én måte å få testen til å fungere på
  • 3 poeng: beskriver minst to måter å få testen til å fungere på eller implisitt sammenligner metoden som anbefales med andre mulige designvalg.
  • 4 poeng: velger relevante aspekter ved objekt-orientert programmering å fokusere på.
    • Hva kandidaten velger å ta tak i, kan variere. De mest relevante konseptene er innkaspling/skjuling av implementasjonsdetaler og mutabilitetsproblemer knyttet til å hente ut en array, men det kan også finnes helt andre innfallsvinkler. Hvis kandidatene ikke tar tak i innkapsling/skjuling av detaljer og mutabilitet legges listen høyere her.

Hvis kandidaten ender opp med å anbefale en måte å teste på som legger til en ny metode i Tetromino, er det ikke mulig å få mer enn 13 poeng. Hvis kandidaten ender opp med å anbefale en måte å teste på som gjør Tetrominoen muterbar, er det ikke mulig å få mer enn 12 poeng.

Oppgave 3: whack-a-mole misclick

15 poeng. Rettes av sensor.

I denne oppgaven skal du gjøre en endring i programmet INF101 ColorWhack som du finner i pakken no.uib.inf101.exam23v.colorwhack. Programmet fungerer allerede ganske bra: du kan starte spillet med å klikke på en rute; poenget er deretter å ha så mange svarte ruter som mulig til enhver tid, og prosenten som vises er den gjennomsnittlige andelen av svarte ruter du har klart å bevare så langt. Spillet er ferdig etter 30 sekunder, og den som har den høyeste prosenten da, vinner.

Direktøren i «The Whack-a-Mole Company» har imidlertid hyret deg inn som konsulent for å gjøre spillet enda vanskeligere. Hun ønsker følgende funksjonalitet: dersom brukeren klikker på en rute som allerede er svart, da skal alle de svarte rutene bli fargelagt med en tilfeldig farge. Vedlagt i oppgavebeskrivelsen til jobben har du fått en gif-animasjon som er laget av teamets UX-designer, og som demonstrerer ønsket funksjonalitet:

ønsket funksjonalitet

Det er en hastejobb som visstnok betaler ganske bra, så du takket selvfølgelig ja til oppgaven umiddelbart.

Hint: Du kan bruke metoden ColorWhackModel::getNewColor for å velge hvilken farge du skal tegne med, denne metoden tar allerede hensyn til at fargevalget er mer begrenset i begynnelsen av spillet.

I metoden whack i klassen ColorWhackModel (ved linje 138) endrer vi koden fra

this.grid.set(pos, EMPYT_COLOR);

til

Color oldColorAtPosition = this.grid.get(pos);
if (Objects.equals(oldColorAtPosition, EMPYT_COLOR)) {
  fillAllBlankSquaresWithRandomColors();
} else {
  this.grid.set(pos, EMPYT_COLOR);
}

I tillegg oppretter vi metoden fillAllBlankSquaresWithRandomColors i samme klasse:

private void fillAllBlankSquaresWithRandomColors() {
  for (GridCell<Color> gc : this.grid) {
    if (Objects.equals(gc.value(), EMPYT_COLOR)) {
      this.grid.set(gc.pos(), this.getNewColor());
    }
  }
}

  • 11 poeng: det fungerer
    • -2 poeng: absolutt alle posisjoner får en ny farge, ikke kun dem som var svarte fra før
    • -2 poeng: ruten man klikker på blir ikke fargelagt
    • -2 poeng: benytter ikke eksisterende kode for å velge tilfeldig farge
    • -1 poeng: forsinket visuell oppdatering (sender ikke modelchangedevent)
    • -1 poeng: teller ikke opp score riktig (oppstår for eksempel dersom man ikke modifiserer whack-metoden, men i stedet håndterer logikken i kontrolleren uten å kalle accumulateEmptySquareSeconds)
    • opptil -4 poeng: unødvendig komplisert
  • 2 poeng: benytter hjelpemetode (unngår å bruke for-løkke i selve whack-metoden)
  • 2 poeng: selvdokumenterende metode/variabelnavn og pen formatering

For løsninger som ikke fungerer kan det gis skjønnsmessig uttelling opp til maksimalt 5 poeng for kode som er inne på noe og 4 poeng for kodestil/inndeling i hjelpemetoder (men maksimalt 8 poeng totalt hvis det ikke fungerer)

Oppgave 4: paint

25 poeng. Rettes av sensor.

Illustrasjon av virkemåten til Paint-programmet

Lag et tegneprogram som vist over. Benytt model-view-controller for å skille variabler som beskriver programmets tilstand (modellen) fra kode som viser programmets tilstand på skjermen (visning) og kode som håndterer hendelser (kontroller). La all koden du skriver for denne oppgaven ligge i pakken no.uib.inf101.exam23v.paint og eventuelle underpakker (men du kan selvsagt importere klasser fra andre deler av start-repoet).

Hint: ta gjerne inspirasjon fra Tetris, Whack-a-mole fra forrige oppgave og https://git.app.uib.no/ii/inf101/23v/students/clickablegrid. Bruk gjerne internett/hjelpemidler/AI og søk etter løsninger. Husk (som alltid) å sitere kilder, inkludert alle avsnitt på tre eller flere sammenhengende kodelinjer som er produsert av en AI.

Programmet ditt trenger kun å tegne bilder som 200x200 piksler, men bildestørrelsen bør være lett å endre på senere.

Du vil bli bedømt i følgende kategorier. Underpunktene i hver kategori er rangert etter hvor viktige de er for bedømmelsen.

Funksjonalitet

  • man kan klikke på en piksel for å fargelegge den (ca 30%)
  • man kan velge hvilken farge man skal tegne med (ca 30%)
  • man kan se hvilken farge som er valgt (ca 10%)
  • man kan nullstille tegningen (ca 10%)
  • man kan dra musen når den er nedtrykket for å fargelegge (de fleste) pikslene den passerer (ca 10%)
  • man kan lagre tegningen som en fil (ca 10%)

Modularitet og kodestil (kan redusere totalscore med opptil 40% av score oppnådd på funksjonalitet)

  • programmet benytter model-view-controller og har ellers god modularitet
  • selvdokumenterende kode
  • innkapsling
  • dokumentasjon av public metoder
  • se forøvrig https://inf101.ii.uib.no/notat/stil/

Testing (kan redusere totalscore med opptil 20% av score oppnådd for funksjonalitet)

  • det er skrevet meningsfulle tester for de metoder i modellen som kalles av kontrolleren

Helhetsvurdering (sensor kan redusere eller øke score basert på skjønn dersom rubrikken ikke samsvarer med hvilket nivå kandidatene demonstrerer)

Under finner du et UML-diagram for en eksempel-løsning. Kun enkelte public metoder og relasjoner er vist. Merk at eksempel-løsningen benyttet en EventBus for kommunikasjon, men denne delen av programmet er ikke tegnet inn for å gjøre tegningen mer oversiktlig. Du velger selv om du ønsker å følge diagrammet nøyaktig, om du vil gjøre dine egne tilpasninger, eller om du vil løse det på en annen måte.

UML -diagram for eksempelløsning

Dersom du ikke blir ferdig, kan det være lurt å kommentere ut kode som fører til kompileringsfeil, slik at det du leverer inn faktisk kan kjøres. Da blir sensor glad i deg!

Løsningsstrategi 1: Denne løsningen er også implementert i eget repo: https://git.app.uib.no/ii/inf101/23v/exam/inf101v23exam-solution

  1. Begynn med å opprette klassene og metodene fra UML-diagrammet.
  2. Opprett passende instansvariabler i PaintModel (et grid IGrid<Color> pixels for bildet som tegnes, en Color penColor for penselen)
  3. Implementer metodene i PaintModel med fornuftige implementasjoner (set/getPenColor, draw/getPixels, clear, getImageSize), og skriv enkle tester for disse.
  4. Visning: få til å vise bildet
    • Opprett modellen i main-metoden, og gi som argument ved opprettelse av ViewMain. La ViewMain vises i rammen.
    • La ViewMain opprette et ViewCanvas, og la også konstruktøren til ViewCanvas ta imot modellen som argument. Legg til ViewCanvas-objektet som underkomponent av ViewMain med add-metoden.
    • La ViewCanvas -klassen tegne rutenettet på samme måte som gjort i Tetris og Whack-a-mole (la margin mellom pikslene være 0, og la foretrukket størrelse være det samme som størrelsen på bildet, 200x200)
  5. Kontroller: få til å tegne på bildet
    • La ControllerCanvas få PaintModel-objektet og ViewCanvas-objektet som argumenter ved opprettelse.
    • Legg til en mouseListener på ViewCanvas-objektet, og håndter klikkene i ControllerCanvas. La ViewCanvas ha en metode som konverterer fra museklikkpunkt til koordinat i bildet som tegnes, kall på denne metoden som nødvendig når museklikk håndteres, på samme måte som i Whack-a-Mole eller ClickableGrid. Til syvende og sist gjøres et kall til draw-metoden på modellen. Kall repaint på visningen hvis du ikke benytter eventbus eller observable for å tegne visningen på nytt når modellen endres.
  6. Gjør videre tilpasninger for å tegne fargevelger (kan tegnes som et grid av farger med én rad). Museklikk kan enten håndteres på lignende måte som tegning på bildet, eller man kan benytte eventbus (som i repositoriet med løsningsforslag).
  7. Legg til knapper for reset og save.

Løsningsstrategi 2: Observer at Whack-a-mole fra forrige oppgave egentlig er ganske likt programmet som skal lages. Begynn med å kopiere alt fra Whack-a-mole, men gi ting bedre navn og fjern unødvendig stuff:

  • whack-metoden blir til en draw-metode (men fjern alt som har med tid å gjøre),

  • reset-knappen blir til en clear-knapp, og man kan legge til en save-knapp ved siden av i tillegg,

  • panelet som tegner score endres til å bli et panel som viser en rekke med farger,

  • og så videre.

Poengene man oppnår i denne oppgaven er et produkt av tre verdier, pluss en skjønnsmessig justering. Den endelige poengsummen kan uttrykkes som regnestykket f * mFactor * tFactor + s, hvor

  • f er poengsum for funksjonalitet (et tall mellom 0 og 25),
  • mFactor er et tall mellom 0.6 og 1.0 som bestemmes av score for modularitet og kodestil,
  • tFactor er et tall mellom 0.8 og 1.0 som bestemmes av score for testing, og
  • s er sensor sin skjønnsmessige justering (benyttes som hovedregel ikke)

Funksjonalitet (f). Det gis 2.5 poeng for hvert av punktene i listen, hvert punkt rettes som alt (2.5 poeng), for lite (0 poeng), eller godt på vei (1.25 poeng)». Vi ser etter tilsynelatende fornuftig kode som representerer følgende:

  • en modell for et bilde: det finnes en variabel som lagrer et rutenett med farger eller lignende
  • en modell for en pensel: def finnes en variabel som lagrer hvilken farge som tegnes med, og denne fargen brukes for å tegne på bildet
  • rutenettet med farger kan tegnes til skjermen: det finnes kode som tegner et rutenett
  • det vises en palett med farger man kan velge mellom (vi ser her etter kode i visningen som kan vise en fargepalett/liste med farger, og at denne er integrert i helheten av visningen)
  • en eller annen visuell indikasjon på hvilken farge som er valgt (metode for å hente ut pensel-farge, visuell indikasjon i paletten fungerer for full pott)
  • museklikk gjør at man tegner en farge på bildet (mouselistener er installert på view, konverterer fra punkt på skjermen til punkt i bildet, metode i modellen oppdaterer bildet, kall til repaint)
  • man kan endre fargevariabelen (metode i modellen for å endre fargen, museklikk/tastetrykk kan føre til at metoden kalles)
  • man kan nullstille bildet ved å klikke på en reset-knapp (metode i modellen for å resette bilde, visning av reset-knapp og kall til metode i modell når den trykkes for full pott)
  • man kan klikke-og-dra musen for å tegne «streker» med frihånd (bruker mouseMoved eller mouseDragged)
  • man kan lagre bildet som en fil ved å klikke på en save-knapp (kode som lagrer bilde som fil, meny som tillater å velge filnavn)

Modularitet og kodestil (m og mFactor). For enkelhets skyld vurderer vi modularitet og kodestil som en poengverdi m fra 0-4 der 0 er dårligst og 4 er best. Hvis man får 0 modularitetspoeng mister man 40% av scoren på funksjonalitet, men hvis man får 4 modularitetspoeng, mister man ingenting.

Vurderingen tar kun hensyn til koden så langt den er kommet. For eksempel er det fullt mulig å få full pott i modularitet og kodestil selv om man bare er kommet halvveis med funksjonaliteten.

Modulariteten og kodestil (m) vurderes på en skala fra 0-4:

  • 2 poeng: benytter model-view-controller og har god inndeling i klasser.
  • 2 poeng: selvdokumenterende kode, javadocs for viktige/public metoder, formatering, kodestil, ingen død kode eller misvisende kommentarer/javadocs/metodenavn. Merk at vi med «død» kode ikke vil straffe uferdig kode, men kun kode som ikke hører hjemme i et ferdig produkt (f. eks. hvis man av en eller annen obskur årsak har en ‘whack’ -metode liggende i paint-modellen sin).

Ved utregning av den endelige poengsummen finner vi faktoren for modularitet og kodestil ved uttrykket mFactor = 0.6 + m / 10.0

Testing (t og tFactor). For enkelhets skyld vurderer vi testing som en poengverdi t fra 0-2 der 0 er dårligst og 2 er best. Hvis man får 0 testpoeng mister man opptil 20% av scoren sin, men hvis man får 2 testpoeng, mister man ingenting.

Testing (t) vurderes på en skala fra 0-2:

  • 1 poeng: det er skrevet minst én test for noe som helst
  • 1 poeng: det er skrevet en test for både å tegne noe, og å endre fargen

Ved utregning av den endelige poengsummen finner vi faktoren for testing ved uttrykket tFactor = 0.8 + t / 10.0

Skjønnsmessig justering (s). Dersom den utregnede poengsummen gir et misvisende bilde av evnene kandidaten har demonstrert slik sensor ser det, har sensor anledning til å gjøre en skjønnsmessig justering. I utgangspunktet skal bruken av dette virkemiddelet være sparsom.