Oppure

Loading
30/11/09 9:57
kevinpirola
Salve a tutti! Sono lieto di presentarmi su questo forum, che trovo molto interessante e molto utile.
Sono uno studente di ingegneria elettronica a padova, amo la programmazione e la grafica.

Vi scrivo perchè vorrei fare quattro chiacchiere con qualcuno che se ne intende più di me di programming.

Vi spiego cosa devo fare. Per l'esame di D.A. mi è stato assegnato un progetto dal libro Goodrich-Tamassia, Strutture dati e Algoritmi in Java, il P-7.8.

In sostanza devo creare un gioco del tris al quale si possa giocare contro il computer. per ovvi motivi non posso prenderne uno già fatto poichè devo poi spiegarlo all'orale. Se posso allora vi scrivo le mie idee e mi dite se sto sbagliado.

In sostanza devo implementare l'albero di gioco in java e poi fargli calcolare tramite il "minmax" la mossa migliore da fare.
Ho provato a far generare tutto l'albero di combinazioni al momento dell'avvio del programma, ho però grossi problemi con il peso delle operazioni. Il computer infatti va in errore di memoria prima di poter giungere al termine delle operazioni. Ho quindi pensato di fargli calcolare di nodo in nodo i vari figli che mi servono. Però ho le idee molto confuse (e forse si vede anche :) )
vi posto il codice che ho scritto finora, con una piccola classe di prova che mostra a grandi linee come avevo pensato la struttura del codice. Accetto aiuti, critiche di ogni tipo sul codice, però quello che più mi preme è capire cosa devo fare :)

ecco le classi:
Classe Albero
import java.util.ArrayList;

public class Albero
{
	private Campo c;
	private static final int NUMFIGLI = 9;
	private static final int MAXDEPTH = 8;
	private int nFigli;
	private int depth;
	private ArrayList<Albero> arrFigli;
	private Albero padre;
	
	public Albero()			//genera la root
	{
		padre = null;
		c = new Campo();
		arrFigli = new ArrayList<Albero>(NUMFIGLI);
		depth = 0;
		nFigli = NUMFIGLI-depth;
	}
	public Albero(Albero p,int prof,Campo cmp)	//generatore per i figli
	{
		padre =	p;
		depth = prof + 1;
		nFigli = NUMFIGLI - depth;
		c = cmp;
		arrFigli = new ArrayList<Albero>(nFigli);
	}
	
	public void generaFigli() //ArrayList<Albero>
	{
		for(int i = 0; i < nFigli; i++)
		{
			Campo cmp = new Campo(c,i,depth);
			Albero alb = new Albero(this,depth,cmp);
			arrFigli.add(i,alb);
		}
		//return arrFigli;
	}
	public int getNumFigli()
	{
		return nFigli;
	}
	public int getDepth()
	{
		return depth;
	}
	private void setNumFigli(int prof)
	{
		nFigli = NUMFIGLI - prof;
	}
	public Albero getFiglio(int n)
	{
		Albero figlio = arrFigli.get(n);
		return figlio;
	}
	public Campo getCampo(){
		return c;
	}
	public void getMossaMigliore()
	{
		this.generaFigli();
		for(int i=0;i<arrFigli.size();i++)
		System.out.println(arrFigli.get(i));
	}
	public String toString()
	{
		String r = ""+c;
		return r;
	}
}


Classe Campo
public class Campo{

	private int[][] c;
	
	public Campo(){
		c = new int[3][3];
	}
	public Campo(int ri,int co){ //costruttore inizializzo il campo con tutti zeri(default)
		c = new int[ri][co];
	}
	public Campo(Campo cmp,int ri,int co,int prof){ //costruttore inizializzo il campo con tutti zeri(default)
		c = clonaCampo(cmp.c);
		int v = getValPl(prof);
		c[ri][co] = v;
	}
	public Campo(Campo attuale,int step, int val){
		int v = getValPl(val);
		c = clonaCampo(attuale.c);
		for(int i=0; i < c.length; i++){
			for(int j=0; j < c.length; j++){
				if(attuale.c[i][j] == 0){
					if(step == 0){
						c[i][j] = v;
						return;
					}else{
						step--;
					}
				}
			}
		}
	}
	
	public void setCampo(int ri,int co,int val){
		c[ri][co] = val;
	}
	public int getValPl(int val)
	{
		int v=2;
		int r = val % 2;
		switch(r){
			case 0:
				v = 1;
			break;
			case 1:
				v = -1;
			break;
		}
		return v;
	}
	public boolean isMossaValida(int ri,int co)
	{
		if(c[ri][co]==0) return true;
		return false;
	}
	public int chiVince(){
		int m = 0;
		int s = 0;
		for(int i=0; i<3; i++){
			int n = 0;
			int t = 0;
			for(int j=0; j<3; j++){
				n += c[i][j];	//RIGA
				t += c[j][i];	//COLONNA
			}
			m += c[i][i];	//DIAGONALE 1
			s += c[i][2-i];	//DIAGONALE 2
			if(n==3||t==3) return 1;
			if(n==-3||t==-3) return -1;
		}
		if(m==3||s==3) return 1;
		if(m==-3||s==-3) return -1;
		return 0;
	}
	private int[][] clonaCampo(int[][] cmp){
		int[][] r = new int[3][3];
		for(int i = 0; i < 3; i++){	
			for(int j = 0; j < 3; j++){
				r[i][j] = cmp[i][j];
			}
		}
		return r;
	}
	public String toString(){
		String r = "\n";
		for(int i = 0; i<3; i++){
			r += "|";
			for(int j = 0; j<3; j++){
				switch (this.c[i][j]){
					case 0:
						r += " ";
					break;
					case 1:
						r += "X";
					break;
					case -1:
						r += "O";
					break;
				}
				//r += this.c[i][j];
			r += "|";
			}
			r += "\n";
		}
		return r;
	}
}


Classe Tris
public class Tris
{
	private Albero a;
	private Campo campo;
	private static final int RIGHE = 3;
	private static final int COLON = 3;
	private Giocatore giocatore1,giocatore2,currentpl,winner;
		
	public Tris(boolean g1PC,boolean g2PC)
	{
		nuovaPartita(g1PC,g2PC);
	}
	private void nuovaPartita(boolean g1PC,boolean g2PC)
	{
		a = new Albero();
		campo = new Campo(); //vuoto
		giocatore1 = new Giocatore(g1PC);
		giocatore2 = new Giocatore(g2PC);
		currentpl = giocatore1;
		winner = null;
	}
	public Giocatore getCurrentPl()
	{
		return currentpl;
	}
	public Giocatore getWinner()
	{
		return winner;
	}
	public boolean doMossaGiocatore(int input)
	{	
		int ri=2;
		int co=2;
		switch(input)
		{
			case 1:
				ri=0;
				co=0;
			break;
			case 2:
				ri=0;
				co=1;
			break;
			case 3:
				ri=0;
				co=2;
			break;
			case 4:
				ri=1;
				co=0;
			break;
			case 5:
				ri=1;
				co=1;
			break;
			case 6:
				ri=1;
				co=2;
			break;
			case 7:
				ri=2;
				co=0;
			break;
			case 8:
				ri=2;
				co=1;
			break;
			case 9:
				ri=2;
				co=2;
			break;
		}
		if(campo.isMossaValida(ri,co)){
			campo = new Campo(campo,ri,co,a.getDepth());
			a = new Albero(a,a.getDepth(),campo);
			return true;
		}
		return false;
	}
	public void vince()
	{
		int vinc = campo.chiVince();
		System.out.println(campo);
		if(vinc != 0) winner=currentpl;
	}
	public boolean partitaFinita()
	{	
		if(winner != null) return true;
		return false;
	}
	public void doMossaComputer()
	{
		a.generaFigli();
		for(int i = 0; i < a.getNumFigli(); i++)
		{
			if(a.getFiglio(i).getCampo().chiVince() == -(a.getFiglio(i).getCampo().getValPl(a.getDepth())))
			{
				return;
			}
			else if(a.getFiglio(i).getCampo().chiVince() == a.getFiglio(i).getCampo().getValPl(a.getDepth()))
			{
				a = a.getFiglio(i);
				campo = a.getCampo();
				return;
			}
			else //se pari
			{
				//sceglie la migliore
			}
		}
	}
	public void cambiaGiocatore(){
		if(currentpl == giocatore1){ currentpl = giocatore2;
		}
		else if(currentpl == giocatore2){ currentpl = giocatore1;}
		else{ return; }
	}
}


Classe Giocatore
public class Giocatore
{	
	private static int numGiocatore = 1;
	private boolean PC = false;
	String nome;
	
	public Giocatore(boolean PCpl)
	{	
		if(numGiocatore%2==1){
			PC = PCpl;
			nome = "Giocatore1[X]";
			numGiocatore++;
		}
		else
		{	
			PC = PCpl;
			nome = "Giocatore2[O]";
			numGiocatore++;
		}
	}
	public boolean isPC()
	{
		return PC;
	}
	public String toString(){
		return nome;
	}
}


Classe di prova TrisTest
import java.util.Scanner;
import java.io.*;
import java.lang.String;

public class TrisTest
{
	public static void main(String[]arg) throws IOException {
		Scanner in = new Scanner(System.in);
		System.out.println("Giocatore 1 - Umano[U] o Computer[C]? (default umano)");
		String input = in.next();
		boolean g1PC = (input.equalsIgnoreCase("c"));
		System.out.println("Giocatore 2 - Umano[U] o Computer[C]?");
		input = in.next();
		boolean g2PC = (input.equalsIgnoreCase("c"));
		Tris game = new Tris(g1PC,g2PC);
		while(!game.partitaFinita())
		{
			System.out.println("\n");
			System.out.println(game.getCurrentPl());
			System.out.println("Inserire il numero della casella da occupare");
			String num = in.next();
			if(!num.equals("c")){
			if(game.doMossaGiocatore(Integer.parseInt(num))){
				game.vince();
				game.cambiaGiocatore();
			}
			}
			else
			{
				game.doMossaComputer();
				game.vince();
				game.cambiaGiocatore();
			}
		}
	}
}


aperto a tutti i commenti.
ditemi quello che vi pare, anche se secondo voi ho fatto un mare di cavolate, farò tesoro di quello che mi direte.
Così com'è la classe funziona se si gioca uno contro uno. La classe prova dà la possibilità di testare la mossa del computer premendo al posto del numero di casella la lettera c. Per il momento funziona solo se il PC deve completare la mossa vincendo ovvero deve mettere il suo simbolo in linea con altri due.

Spero di essere stato esaustivo, se avete qualsiasi domanda chiedete...

EDIT: leggo che il presumibilmente sopra il codice è sbagliato, è tutto Java :D
Ultima modifica effettuata da kevinpirola 30/11/09 9:59
aaa
30/11/09 20:51
Anonymous
Ciao... non ho letto il codice che hai scritto...

però posso dirti come secondo me procederei io...

allora..
nel gioco del tris si sa che, se entrambi i giocatori giocano per bene, si andrebbe sempre in pareggio...
quindi l'algoritmo migliore è quello che riesce a fare in modo che il computer non perda mai.

Ora, supponendo che il computer sia le croci X e il giocatore sia i pallini O, direi di procedere cosi:
Ogni volta che il computer deve giocare, PER PRIMA cosa, controlla SEMPRE se ci sono due croci in fila e la terza è libera... ovvero controlla se potrebbe vincere.
se questo controllo non da esito positivo, allora la mossa subito successiva è quella di andare a controllare se ci sono due O in fila con casella vuota sulla terza, ovvero, controlla se l'avversario vincerebbe alla prossima.
Se questa funzione da riscontro positivo, allora il computer effettuerà la sua mossa andando a mettere la sua X nella casella libera controllata prima, altrimenti si passa al punto 3
dunque...
arrivati qui significa che: 1) non puoi vincere in questo turno 2) non puoi perdere al turno successivo, quindi siamo tranquilli e procediamo con andare a vedere se è libera la casella del centro (sappiamo che nel tris chi domina il centro ha scarsa probabilità di perdere), quindi se il centro è libero allora va a occupare quella casella, altrimenti continuerà con il punto 4 che per il momento potrebbe essere "metti la croce in una casella vuota qualsiasi, scelta a caso"...
aaa
01/12/09 9:53
kevinpirola
si avevo pensato anche io allo stesso metodo risolutivo però ho dovuto rivedere le mie idee per due motivi:

A: devo usare un albero (e quell'idea va a "esperienza" di gioco, non si basa molto su un albero)

B: Computer è O io sono X faccio un esempio:

1:
x|..|..
..|..|..
..|..|..

2:
il computer secodno il nostro ragionamento deve mettere in centro perchè è libero. mossa buona.
x|..|..
..|0|..
..|..|..

3. tocca a me

x|..|..
..|0|..
..|..|x

secondo il nostro ragionamento il pc non vede due suoi simboli allineati, non ne vede due di miei. mette il simbolo in un posto a caso. Tra i vari casi c'è questo

x|..|0
..|0|..
..|..|X

il computer ha perso. ( io metto nell'angolo mancante e mi libero 2 posizioni di vittoria)

io devo far decidere al computer di mettere il cerchio su uno dei centri.

Ultima modifica effettuata da kevinpirola 01/12/09 9:55
aaa
01/12/09 12:50
Anonymous
Postato originariamente da kevinpirola:

si avevo pensato anche io allo stesso metodo risolutivo però ho dovuto rivedere le mie idee per due motivi:

A: devo usare un albero (e quell'idea va a "esperienza" di gioco, non si basa molto su un albero)

B: Computer è O io sono X faccio un esempio:

1:
x|..|..
..|..|..
..|..|..

2:
il computer secodno il nostro ragionamento deve mettere in centro perchè è libero. mossa buona.
x|..|..
..|0|..
..|..|..

3. tocca a me

x|..|..
..|0|..
..|..|x

secondo il nostro ragionamento il pc non vede due suoi simboli allineati, non ne vede due di miei. mette il simbolo in un posto a caso. Tra i vari casi c'è questo

x|..|0
..|0|..
..|..|X

il computer ha perso. ( io metto nell'angolo mancante e mi libero 2 posizioni di vittoria)

io devo far decidere al computer di mettere il cerchio su uno dei centri.



si certo.. ma infatti dal punto 4 in poi io ho detto "che per il momento potrebbe essere" di fare la mossa a caso...
ma una volta che hai implementato i primi 3 punti allora continuerai con il ragionamento...
ma conviene a non andare troppo avanti... inizia ad implementare il codice per il riconoscimento dei punti 1 e 2 (che sostanzialmente useranno la stessa procedura con parametro diverso)


cmq se invece vuoi subito tutto il procedimento... io continuerei cosi:

4) distinguere i casi in cui l'utente abbia messo la sua pedina, agli angoli o no.
se l'utente ha messo agli angolo, e si arriva al punto 4 (ovvero non succede nessun evento dei punti 1,2 e 3) allora la mossa corretta, secondo me, da fare è di metter la pedina in una casella vuota che non sia un angolo, e se possibile, affiancata alla pedina che ha appena messo l'utente.
se invece non ha inserito in una casella d'angolo i casi sono due:
o era il primo turno e l'utente l'ha messa direttamente al centro, a quel punto il computer la deve mettere in un angolo, cosi ha possibilità di fare il giochetto che hai illustrato tu prima
oppure l'ha messe in una posizione interna, e a quel punto il computer la va a mettere sempre in un angolo...
hum.. in effetti è inutile distinguere i due casi :D


cmq... penso che ad occhio e croce, cosi dovrebbe funzionare abbastanza bene....
poi cmq a mano a mano che butti giù il codice te ne accorgerai da solo se qualcosa non va e se c'è qualcosa da migliorare...


cmq preciso che quando dico
Ogni volta che il computer deve giocare, PER PRIMA cosa, controlla SEMPRE se ci sono due croci in fila e la terza è libera... ovvero controlla se potrebbe vincere.

intendo ovviamente anche quando siamo in questa situazione:
x|..|..
..|..|..
x|..|..

e non solo quando siamo in
x|..|..
X|..|..
..|..|..
Ultima modifica effettuata da Anonymous 01/12/09 13:08
aaa
01/12/09 20:59
kevinpirola
oggi mi sono fatto un full immersion nella programmazione del sistema per far scegliere la mossa al computer. E devo dire che qualche risultato l'ho avuto.. ma ho paura che sia casuale :)
Ovviamente mettere un trace dei vari eventi è pressocchè improponibile perchè si generano così tanti cicli nidificati che vai via matto..

d'ogni modo io ho pensato a questo, non lo posto in java, ti scrivo uno pseudocodice... poi devo cercare di implementarlo.


richiamo la funzione per scegliere la mossa.
(preventivamente ho controllato che nessuno ha già vinto)
immediatamente genero le possibilità di mosse successive.

comincio un ciclo for da 0 al num max di opzioni.
effettuo un controllo se tra quelli generati qualcuno è vincente
parto dal primo se è perdente passo al secondo (salto le istruzioni sottostanti)
se è vincente esco dal ciclo e dò il risultato
se porta ad una situazione di parità passo alle mosse successive.


--> incomincio con questo che già mi dà un po' di problemi.
Ho un dubbio ad esempio sulla funzione return.
ad esempio se io sono in un ciclo for e fatto un dato controllo voglio saltare il resto delle istruzioni come posso fare?

se voglio uscire dal ciclo il return funziona... ma per quello che ho detto io?


grazie mille per le risposte ;)
aaa
01/12/09 21:21
Anonymous
Postato originariamente da kevinpirola:

oggi mi sono fatto un full immersion nella programmazione del sistema per far scegliere la mossa al computer. E devo dire che qualche risultato l'ho avuto.. ma ho paura che sia casuale :)
Ovviamente mettere un trace dei vari eventi è pressocchè improponibile perchè si generano così tanti cicli nidificati che vai via matto..

d'ogni modo io ho pensato a questo, non lo posto in java, ti scrivo uno pseudocodice... poi devo cercare di implementarlo.


richiamo la funzione per scegliere la mossa.
(preventivamente ho controllato che nessuno ha già vinto)
immediatamente genero le possibilità di mosse successive.

comincio un ciclo for da 0 al num max di opzioni.
effettuo un controllo se tra quelli generati qualcuno è vincente
parto dal primo se è perdente passo al secondo (salto le istruzioni sottostanti)
se è vincente esco dal ciclo e dò il risultato
se porta ad una situazione di parità passo alle mosse successive.


--> incomincio con questo che già mi dà un po' di problemi.
Ho un dubbio ad esempio sulla funzione return.
ad esempio se io sono in un ciclo for e fatto un dato controllo voglio saltare il resto delle istruzioni come posso fare?

se voglio uscire dal ciclo il return funziona... ma per quello che ho detto io?


grazie mille per le risposte ;)


non ho capito il fatto del ciclo for per scegliere le opzioni...

secondo me devi creare delle funzioni di tipo boolean in modo che le chiami ad una ad una
e partendo dalla prima, chiami la successiva solo se il valore di ritorno della precedente è false...

mi spiego:

la procedura 1 è quella che determina se il computer puù vincere

se la procedura da risultato true, allora il programma esegue la mossa sulla casella risultante libera nella fila vincente
se la procedura da risultato false allora si innesca la seconda procedura che è quella che verifica se puo vincere lì'utente (ovviamente qui si sfrutterà la stessa procedura del punto 1, semplicemente dicendogli che invece che le X dovrà controllare le O )
e se questa da esito negativo allora si attiverà la procedura 3 che è quella che verificherà se la casella del centro è vuota o no e cosi via...

capito?? fai tante procedure ognuna che gestisce un caso particolare e poi le metti una dopo l'altra in ordine di precedenza in modo tale che all'ultima ci si entra solo se non sono soddisfatte le precedenti....

capito quello che intendo??

cmq per l'altra domanda
ad esempio se io sono in un ciclo for e fatto un dato controllo voglio saltare il resto delle istruzioni come posso fare?


se ho ben capito vuoi forzare l'uscita dal ciclo for??
se è cosi ci sono due modi:
usare "break", che praticamente ti esce dal ciclo e ti continua a eseguire il codice dalla prima riga dopo il ciclo for
oppure usare "continue" che praticamente è una specie di break più soft :) nel senso che non ti esce dal ciclo for ma ti esce dall'attuale iterazione e ti continua con la successiva... non so se mi son spiegato....

Ultima modifica effettuata da Anonymous 01/12/09 21:23
aaa
01/12/09 22:27
kevinpirola
si ti sei spiegato :)

purtroppo però (forse mi sono dimenticato di dirlo prima lol ) io devo eseguirlo in un particolare schema l'esercizio. Forse faccio prima a postarne il testo.

Tratto dal Goodrich Tamassia - Strutture dati e algoritmi in Java.
Si scriva un programma che possa giocare con successo al gioco tic-tac-toe. Per fare ciò sarà necessario creare un albero di gioco T, che è un albero i cui nodi corrispondono a una configurazione di gioco, che in questo caso è una rappresentazione della tabella tic-tac-toe. Il nodo radice corrisponde alla configurazione iniziale. Per ogni nodo interno v di T, i figli v corrispondono agli stati del gioco raggiungibili dallo stato v in una singola mossa consentita ad un giocatore, A (primo giocatore) o B (secondo giocatore). I nodi aventi profondità pari corrispondono a mosse del giocatore A mentre i nodi aventi profondità dispari corrispondono a mosse del giocatore B. I nodi esterni o sono stati finali del gioco o a profondità oltre la quale non si vuole scendere. Ad ogni nodo esterno si attribuisce un punteggio indicativo della bontà di quello stato per il giocarore A. (...) per i giochi più piccoli come il Tris si può costruire tutto l'albero e attribuire un punteggio +1,0,-1 che indica se il giocatore vince, pareggia, perde in quella configurazione. L'algoritmo minimax è adatto per la scelta delle mosse. In questo algoritmo si assegna un punteggio ad ogni nodo interno v in T tale che se v rappresenta il turno di A, si valuta il punteggio di v come il massimo dei punteggi dei figli di v (che corrisponde al gioco ottimale per A partendo da v). Se un nodo interno v rappresenta il turno di B, si valuta il punteggio di v come il minimo dei punteggi dei figli di v.



dopo tutto sto casino i problemi sono:

1: devo assolutamente usare un albero. poco ma sicuro da là non ci scappo :)
ne ho fatta anche io una versione "empirica" che guarda tutti i casi possibili. Purtroppo su questo esercizio non va bene, il computer deve capire da solo quale scegliere, e non solo perchè io sono bravo a giocare e so i trucchi :)

2: l'algoritmo minmax. Di cui io ho fatto una interpretazione diversa semplicemente mettendo qualche segno meno di qui e di là :D



è abbastanza un casino vero?
aaa
02/12/09 9:15
Anonymous
Postato originariamente da kevinpirola:
è abbastanza un casino vero?



beh si asd

cmq allora io non saprei aiutarti...

so cos'è un albero e come è fatto...

ma sinceramente non saprei come poterlo sfruttare per poter risolvere un programma del genere....

o meglio.... forse ho capito ma spero di no...

quello che posso immaginare è che praticamente si parte dalla radice che è praticamente la tabella vuota...

poi questa radice ha 9 figli?? (uno a seconda di dove l'utente può mettere la pedina)
e ciascuno di questi 9 figli ha a sua volta altri 7 figli (7 perché sono rimaste 7 le caselle libere dopo che ha giocato una volta l'utente e una volta il computer)
e poi a scendere ancora, ciascuno di questi 7 ne ha altri 5 ecc??

è cosi che si deve fare???

spero vivamente di no.... :)
aaa