Hint til oppgave 11
Løsningsforslaget til denne oppgaven er relativt omfattende. Noen hint til en litt enklere løsning blir gitt her.
Første råd: Lag en versjon for to "menneskespillere" før du utvider med en "maskinspiller".
Tegne "kryss" og "rundinger"
Ta gjerne utgangspunkt i listing 8.5 på side 349. Tilpass koden slik at programmet vekselvis tegner "kryss" og "rundinger" ved annethvert museklikk.
Representere og tegne spillbrettet
Oppgaveteksten foreslår at spillbrettet blir representert ved en 2-dimensjonal heltallstabell. Man kan holde seg til "tre-på-rad", eller lage en litt mer generell løsning:
final int MARG = 30; final int BOKS = 40; final int LENGDE = 20; int[][] brett = new int[LENGDE][LENGDE];
Her er tanken at hver (kvadratiske) rute har sidelengde 40 piksler, at spillbrettet har 20 rader og 20 kolonner, og at det øverste venstre hjørnet i spillbrettet skal tegnes i punktet (x=MARG, y=MARG).
Lag en metode som tegner rutenettet:
void tegnBrett(Graphics g) { ... }
Legg deretter inn kall på denne metoden fra paintComponent.
Fra skjermkoordinater til tabellindekser
Neste utfordring er oversette skjermkoordinater (x,y) som fanges opp i mouseClicked til posisjoner i tabellen brett.
Divisjonen x/BOKS gir antall ruter fra venstre kant av tegneflaten. Dessuten må du korrigere for margen.
Når du etter et museklikk har funnet riktige indekser (rad, kolonne) i spillbrettet, er det bare å oppdatere verdien med 1 for spiller1 eller 2 for spiller2. Da oppdager du samtidig behovet for å ta vare på hvem som er i trekket.
I tillegg til selve rutenettet kan du nå tegne ut "kryss" og "rundinger". Da må du regne om fra indekser i tabellen (rad, kolonne) til skjermkoordinater. Toppen av rad 4 vil f.eks. ligge 4*BOKS piksler nedover i vinduet - igjen med litt korrigering for marg.
Er spillet avgjort?
Når du har fått til museklikking og tegning av spillbrettet med "kryss" og "rundinger", kan du forsøke å oppdage når et spill er avgjort, og i så fall vise en passende melding på skjermen.
Måter et spill kan avgjøres:
- En spiller har fått "n på rad" enten horisontalt, vertikalt eller diagonalt.
- Ingen har vunnet og ingen ruter er ledige.
For å sjekke om det finnes en sammenhengende sekvens med enten bare "kryss" eller bare "rundinger": Undersøk alle ruter og sjekk om denne ruten er starten på en slik sekvens. For å dekke alle muligheter må du bevege deg bortover mot "høyre", "nedover", "oppover på skrå" og "nedover på skrå".
Utvide med maskinspiller
I løsningsforslaget er dette løst ved å innføre en klasse MaskinSpiller, men som en første versjon kan man nøye seg med en metode maskinTrekk. Metoden kan bare velge første og beste ledige rute, og deretter merke av i spillbrett-tabellen.
Endre i mouseClicked slik at brukeren og maskinen bytter på å gjøre trekk. Tips: Maskinen får kun lov til å gjøre sitt trekk hvis spillet ennå ikke er avgjort.
En mer sofistikert maskinspiller bør avveie egen gevinst mot mulige mottrekk fra motspilleren:
- Har maskinen mulighet til å danne en lang sammenhengende sekvens?
- Bør vi hindre brukeren i å danne en lang sammenhengende sekvens?
Undersøk alle rutene og gi rutene en "vekt" basert på hvor viktige de er. Man kan f.eks. gi ruten vekt 4 hvis et "kryss" her fører til at maskinen får 4 på rad. Løsningsforslaget er basert på en slik teknikk. For å få til en smartere maskinspiller kan man utvide vektingen ved å analysere hva som kan skje flere trekk framover, men det ligger nok godt utenfor et innføringskurs i programmering.