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:

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:

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.