You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 4 Next »

NB: WORK IN PROGRESS

 

Dette eksemplet handler om ConnectFour-spillet med en tydelig oppdeling i klasser for brikke, brett og spill-logikk.

Oppgavebeskrivelse

I spillet ConnectFour skal to spillere legge hver sine brikker i et rutenett på 7x7 ruter og prøve å få fire på rad før brettet er fullt. Oppgaven fokuserer på å realisere dette ved bruk av tre klasser, med en tydelig rollefordering: Piece-klassen inneholder verdien til en brikke på brettet, ConnectFour-klassen håndterer selve brettet og hvem sin tur det er, mens ConnectFourProgram-klassen håndterer tekst-basert interaksjon med spillerne gjennom konsollet. Denne tydelige oppdelingen i logikk og interaksjon (også kalt brukergrensesnitt) vil gjøre det lettere å senere lage et grafisk brukergrensesnitt uten å måtte programmere alt på nytt, siden logikk-klassen vil kunne gjenbrukes. For å gjøre klassene noenlunde uavhengig av hverandre brukes prinsippet om Innkapsling.

Oppgaven er åpen i den forstand at den ikke spesifiserer akkurat hvilke metoder hver klasse skal ha, men sier noe om hvordan det skal ta seg ut for brukeren. Dette gjør det vanskeligere å teste enkeltmetoder, så istedenfor testes teksten som kommer ut, basert på hva brukeren (eller testprogrammet) gir inn.

Det er ofte lurt å løse slike oppgaver i mindre trinn, og derfor har vi nedenfor spesifisert hvilke funksjoner vi tror er lurt å lage i hvert trinn.

  • Trinn 1 - kunne vise frem brettet med og uten brikker. I dette trinnet lager du ConnectFour-klassen med en toString()-metode som viser brettet med de brikkene som er satt (f.eks. av et enkelt test-hovedprogram du lager selv). ConnectFour-klassen skal være ordentlig innkapslet. Du må også lage Piece-klassen for å representere brikker.
  • Trinn 2 - spillerne kan legge brikker. I dette trinnet lages en enkel versjon av ConnectFourProgram-klassen, slik at spillerne etter tur kan legge brikker ved å angi kolonnen de ønsker å slippe en brikke ned i. ConnectFourProgram-klassen skal ta seg av all input og utskrift. For interaksjon med brukeren kan det være lurt å bruker Scanner-klassen.
  • Trinn 3 - et helt fungerende spill, hvor ConnectFour-klassen kan si fra til ConnectFourProgram-klassen hvilken spiller som har turen, om spillet er ferdig og hvilken spiller som evt. har vunnet.

Nedenfor har vi vist en mulig spillsekvens som både illustrerer brett-formatet og dialogen mellom spillet og spillerne. Output til brukeren er i svart, mens input fra brukeren er i grønt.

Eksempelløsning

Klassen lagrer radiusen i et attributt (kalt felt i Java) av typen double. Dette feltet initialiseres av konstruktøren, som tar inn et double-argumentet og setter radius-feltet til denne verdien. Her brukes this.radius for å referere til attributtet (og "hoppe over" argumentet) og kun radius for å referere til argumentet.

Metodene getCircumference() og getArea() bruker radius-attributtet i de vanlige formlene for omkrets og areal. Her er bruken av this i this.radius strengt tatt ikke nødvendig, siden radius alene også vil blir tolket som en referanse til attributtet, da det ikke er argumenter eller lokale variable "i veien".

toString()-metoden lager en String med ved å skjøte sammen mange deler med +. En alternativ variant med bruk av String.format er også vist. Den tar inn en String med formatteringsdirektiver som forteller hvor de påfølgende argumentene skal spleises inn i teksten. %.2f brukes som direktiv siden verdiene er desimaltall (f for floating point-verdier) og vi bare vil at tallene skal vises med to desimaler.

package connectfour;

public class Piece {

	private char value;

	public Piece(char value) {
		this.value = value;
	}

	public char getValue() {
		return value;
	}

	public void setValue(char value) {
		this.value = value;
	}

	public String toString() {
		return "" + getValue();
	}
}

something here

package connectfour;


import java.util.ArrayList;

public class ConnectFour {
	private ArrayList<ArrayList<Piece>> board;
	private char player;

	public ConnectFour() {
		board = new ArrayList<ArrayList<Piece>>();
		for (int r = 0; r < 7; r++) {
			board.add(new ArrayList<Piece>());
			for (int c = 0; c < 7; c++) {
				board.get(r).add(new Piece(' '));
			}
		}
		player = 'o';
	}


	private Piece getPiece(int r, int c) {
		return board.get(r).get(c);
	}


	private void setPiece(int r, int c, Piece piece) {
		board.get(r).set(c, piece);
	}

	public boolean drop(int c) {
		if (getPiece(0, c).getValue() != ' ') {
			return false;
		} else {
			for (int r = 6; r >= 0; r--) {
				if (getPiece(r, c).getValue() == ' ') {
					setPiece(r, c, new Piece(player));
					return true;
				}
			}
			return true;
		}
	}

	public boolean hasWon() {
		for (int r = 0; r < 7; r++) {
			for (int c = 0; c < 7; c++) {
				if (getPiece(r,c).getValue() != ' ' && hasWonFromPosition(r,c)) {
					return true;
				}
			}
		}
		return false;
	}

	private boolean hasWonFromPosition(int r, int c) {
		for (int dr = -1; dr <= 1; dr++) {
			for (int dc = -1; dc <= 1; dc++) {
				if (dr != 0 || dc != 0) {
					if (hasWonFromPositionWithDirection(r,c,getPiece(r,c), dr, dc)) {
						return true;
					}
				}
			}
		}
		return false;
	}

	private boolean hasWonFromPositionWithDirection(int r, int c, Piece piece, int dr, int dc) {
		int counter = 0;
		while (0 <= r && r < 7 && 0 <= c && c < 7 && getPiece(r, c).getValue() == piece.getValue()) {
			r += dr;
			c += dc;
			counter++;
		}
		return counter >= 4;
	}


	public char getPlayer() {
		return player;
	}

	public void changePlayer() {
		if (player == 'o') {
			player = 'x';
		} else {
			player = 'o';
		}
	}

	public String toString() {
		String str = "";
		for (int r = 0; r < 7; r++) {
			str += "| ";
			for (int c = 0; c < 7; c++) {
				str += getPiece(r, c) + " ";
			}
			str += "|\n";
		}
		return str;
	}
}

something here

package connectfour;


import java.util.Scanner;

public class ConnectFourProgram {

	ConnectFour cf;


	public void init() {
		cf = new ConnectFour();
	}

	public void run() {
		Scanner scanner = new Scanner(System.in);
		while (! cf.hasWon()) {
			System.out.println(cf);
			System.out.println("Player " + cf.getPlayer() + ", enter index of column to drop next piece: ");
			int c = scanner.nextInt();
			if (cf.drop(c) && ! cf.hasWon()) {
				cf.changePlayer();
			}
		}
		System.out.println(cf);
		System.out.println("Congratulations player " + cf.getPlayer() + "! You have won the game.");
	}

	public static void main(String[] args) {
		ConnectFourProgram cfp = new ConnectFourProgram();
		cfp.init();
		cfp.run();
	}
}

 

 

For å prøve ut koden lager vi en hovedprogramklasse kalt CircleProgram. Det vanlige er å ha en init()- og en run()-metode, men vi utelater init() her, siden programmet er så lite og enkelt. Det er tross alt vi som velger hva de heter, basert på hva som er ryddig og bekvemt.

I run()-metoden opprettes to Circle-objekter med new. Disse er illustrert i figuren til høyre (id'ene #1 og #2 er kun med for å illustrere at dette er forskjellige objekter). Disse objektene skrives så ut med System.out.println-metoden. Dette vil implisitt kalle toString()-metoden, siden System.out.println bruker denne internt, for å gjøre om argumentet til en String, før det skrives ut.

Merk at main-metoden må være deklarert akkurat slik for å bli kalt av Java, når klassen som helhet skal utføres. Den lager en instans av programmet og kaller run()-metoden som gjør "jobben".

 

 

 

  • No labels