Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Ferdigstilt
Excerpt

Denne oppgaven er en videreføring av de tidligere spilloppgavene TicTacToe, Sokoban, Sudoku og Battleship, hvor en skal gjøre rutenettet observerbart og implementere et par grensesnitt, så spillogikken kan kobles til et ferdig skrevet grafisk brukergrensesnitt. I tillegg skal spillet utvides med ekstra funksjonalitet (Jeg glemte helt, skulle videreutvikling av spill inkluderes eller ikke? Har fjernet de midlertidig, men skal legge de til og reviewe hvis det skulle med allikevel. Nås fremdeles fra her Observatør-observert-teknikken - Videreføring av spilloppgaver | 2016)


I denne oppgaven skal spillogikken og rutenettet fra tidligere spill kobles til et forbedret grensesnitt med bilder. Det vil være en del ferdige og nesten ferdige filer fra før av, alle i pakken patterns.observable.gridgame. Spillklassen din skal implementere grensesnittet GenericGridGame. Koden i brukergrensesnittet er dokumentert godt, les denne for å finne ut nøyaktig hva de påkrevde metodene skal gjøre.

Beskrivelse

GenericGameController må du instansiere spillet ditt på indikert sted (se kommentaren i koden). 

PlantUML Macro
interface GenericGridGame {
    +void addGridListener(GridListener gridListener)
    +void removeGridListener(GridListener gridListener)
	+flere metoder..
}

interface GridListener {
    +void gridChanged(ObservableGrid grid, int x, int y, int w, int h)
}

class DinSpillklasse{
}

 
GenericGridGame <|.. DinSpillklasse
DinSpillklasse --> "*" GridListener: gridListeners
GridListener <|.. GenericGridGameController

Den ferdigskrevne GenericGameController implementerer GridListener-grensesnittet, som gjør GenericGameController til en observatør av spillklassen din og krever metoden:

  • gridChanged(GenericGridGame<T> gridGame, int col, int row, int width, int height) - metoden kalles for å gi beskjed om at en eller flere ruter i rutenettet (grid) innenfor det angitte rektanglet er endret. Rektanglet er angitt med posisjon x, y og dimensjonene w, h (bredde, høyde).

Hvis f.eks. metoden kalles med x=1, y=2, w=2 og h=1, så angir det at rutene 1,2 og 2,2 er endret. Det er opp til rutenett-klassen å avgjøre om endringer av flere ruter rapporteres med ett eller flere kall til gridChanged-metoden. Så endringer i rutene 1,2 og 2,2 kan også rapporteres med to kall, ett hvor x=1, y=2 og ett hvor x=2, y=2, med w=1 og h=1 for begge. Så selv om en kaller denne metoden, så er det ikke dermed sagt at alle rutene i det angitte rektangelet er endret (strengt tatt trenger ingen å være det).

 

Grensesnittet GenericGridGame inkluderer følgende metoder for lytterhåndtering:

  • void addGridListener(GridListener) - registrerer en lytter skal skal ha beskjed hver gang rutenettet endres
  • removeGridListener(GridListener)- avregistrere en lytter som tidligere er registrert med addGridListener

Tanken er at lyttere skal kunne registrere seg selv som lytter til spillet ditt. Spillklassen din må da holde styr på sine lyttere, og bør bruke de to sistnevnte metodene til dette. Dette er observert-delen av observatør-observert-teknikken.

 

GenericGridGame har også metodene

  • T getCell(int col, int row)
  • String[] getImages(T t)

Disse kan være vanskelige å skjønne - tanken er at t er innholdet i en celle, av klasse T. Ved å inspisere controller-koden kan du se at getImages direkte bruker resultatet fra getCell. T kan være så enkelt som en char, eller det kan være et objekt av en klasse du har definert selv for innhold i en rute. Selve metoden getImages skal returnere et array av strenger som inneholder enten:

  • navnet på en/flere bildefiler (eks. for TicTacToe: "x.png"), eller
  • en/flere strenger (eks for TicTacToe: "x") som representerer innhold i ruten

Logikken for å gå fra T til String[] skrives i spillklassen i metoden getImages. Et array skal returneres fordi du får da muligheten til å legge flere bilder over hverandre hvis bildene du bruker har gjennomsiktighet. Dersom du kun vil bruke ett enkelt bilde per rute, eller velger å bruke en streng (alternativ 2 over) skal du kun returnere et array av lengde 1, hvor det ene elementet er bildefilnavnet eller strengen som beskrevet over.

Dersom du velger å bruke bilder skal de ligge i pakken patterns.observable.gridgame, sammen med resten av .java-filene.

For å kjøre spillet ditt i JavaFX kan du høyreklikke på GenericGame.fxml > Run As > FXML Application.

Poengfordeling

  • 80% gis dersom du korrekt implementerer observatør-observert-delen av oppgaven.
  • De resterende 20% gis dersom du får grafikken til å virke som beskrevet i oppgaven.

Ekstraoppgave

De spesielt interesserte kan utvide spillet sitt med enda mer funksjonalitet. Den utvidede funksjonaliteten er ikke støttet i JavaFX-applikasjonen, men du har mulighet til å selv legge til knapper eller lignende for å støtte dette. Relevante filer er GenericGameController.java og GenericGame.fxml

TicTacToe

TicTacToe skal utvides med muligheten for å la datamaskinen spille.

  • void doAIMove() - utfører et trekk for spilleren som er i trekket. Det er opp til deg hvordan datamaskinen velger trekk, enten et tilfeldig lovlig trekk (enklest) eller et optimalt trekk (utfordring).

Battleship

Spillet ditt skal utvides med muligheten for å la datamaskinen spille som den ene spilleren. Den enkleste versjonen vil være å la AI-en skyte tilfeldig helt til spillet er over. En mer utfordrene løsning vil være å først la den skyte tilfeldig, men når den treffer så leter den etter resten av målet i nærliggende ruter. For mer informasjon og tips se http://www.datagenetics.com/blog/december32011/.

Sudoku

Spillet ditt skal utvides med en funksjon for å vise lovlige tall som kan plasseres i en rute:

  • List<Integer> placeableDigits(int x, int y) - returnerer en liste med hvilke tall som er lovlige å plassere på lokasjonen angitt ved x, y. Altså, alle tall som verken er til stede i tilstøtende blokk, kolonne eller rad.

Sokoban

Sokoban skal utvides med to funksjoner, som begge gjør det raskere å flytte spilleren. 

  • String movePlayerTo(int x, int y) - flytter spilleren til cellen (x,y) ved hjelp av en sekvens med flytt (altså ikke dytt), og returnerer en String som representerer flyttene. Flyttene representeres med tegnene l, r, u, og d for i indikere retning, altså l for left/venstre, r for right/høyre, u for up/opp og d for down/ned. Dersom et kall til metoden gjør at spilleren flytter til venstre og så opp, så skal altså metoden returnere "lu". Hvis flyttet ikke er lovlig/mulig, så skal metoden returnere null.
  • String moveBox(int x, int y, ind dy, int dy) - flytter boksen i cellen (x,y) i retning (dx, dy) ved hjelp av en sekvens med flytt, for om nødvendig å få spilleren i rett posisjon, og så et dytt for å flytte boksen. Metoden skal returne en streng som representerer flyttene og dyttet. Flyttene representeres som beskrevet over, mens det siste dyttet er tilsvarende store bokstav, altså L, R, U, eller D. Som over så skal metoden returnere null, dersom sekvensen ikke er lovlig/mulig.

movePlayerTo-metoden er nok vanskeligst, fordi en må prøve seg frem med ulike sekvenser av flytt for å se om en kan nå frem til ønsker rute. Det kan være lurt å kunne lagre litt hjelpeinformasjon i en rute, for å gjøre det enklere. moveBox-metoden er enklere, og nøkkelen er å innse at for å kunne dytte boksen i den angitt retningen, så må spilleren først flytte seg til riktig (motsatt) side av boksen, før boksen dyttes. Og flytting av spilleren er jo nettopp det movePlayerTo gjør!