Visualizzazione dei risultati da 1 a 8 su 8

Discussione: Problema giochino

  1. #1

    Problema giochino

    Mi stavo esercitando con i threads. Sto cercando di creare un semplice programmino
    in cui due giocatori devono mangiare tutte le mele presenti nelle loro ceste. Il primo che
    finisce vince.

    Il codice è questo:

    Cesto

    codice:
    package GiocoDellaMela;
    /*
     * Classe che rappresenta un cesto pieno di mele, che
     * può essere svuotato dai giocatori.
     */
    public class Cesto {
    
    	private int mele;
    	
    	public Cesto(int mele) {
    		this.mele = mele;
    	}
    	
    	public int getMele() {
    		return mele;
    	}
    	
    	public void setMele(int mele) {
    		this.mele = mele;
    	}
    	
    	public void mangiaMela() {
    		if(!meleFinite()) {
    			mele--;
    		}
    	}
    	
    	public boolean meleFinite() {
    		return mele == 0;
    	}
    }
    Giocatore

    codice:
    package GiocoDellaMela;
    
    import java.util.Random;
    
    /*
     * Classe che rappresenta un giocatore. 
     * Il suo scopo è mangiare tutte le mele presenti 
     * nel suo cesto prima degli altri giocatori.
     */
    public class Giocatore implements Runnable {
    
    	private String nome;
    	private int meleMangiate;
    	private Cesto cesto;
    	private Random random = new Random();
    	
    	public Giocatore(String nome, Cesto cesto) {
    		this.nome = nome;
    		meleMangiate = 0;
    		this.cesto = cesto;
    	}
    	
    	public void run() {
    		
    		while(!cesto.meleFinite()) {
    			cesto.mangiaMela();	
    			stampa();
    			attendi();
    		}
    	}
    	
    	/*
    	 * Simula una pausa random tra una mela mangiata ed un'altra
    	 */
    	private void attendi() {
    		//Genero un'attesa tra 400 e 2000 millisecondi
    		int j = 400;
    		int n = 2000 - j;
    		int attesa = random.nextInt(n) + j;
    		try {
    			Thread.sleep(attesa);
    		} catch (InterruptedException e) {
    			System.out.println("Qualcosa è andato storto...");
    		}
    	}
    	
    	public String stampa() {
    		String s = "Il giocatore " + this.nome + " ha mangiato " + this.meleMangiate + " mele" + "\n";
    		return s;
    	}
    }
    Gestore

    codice:
    package GiocoDellaMela;
    
    public class Gestore {
    
    	public static void main(String[]args) {
    	
    		Cesto primoCesto = new Cesto(300);
    		Cesto secondoCesto = new Cesto(300);
    		Giocatore andrea = new Giocatore("Andrea", primoCesto);
    		Giocatore loris = new Giocatore("Loris", secondoCesto);
    		
    		Thread andreaThread = new Thread(andrea);
    		Thread lorisThread = new Thread(loris);
    		
    		andreaThread.start();
    		lorisThread.start();
    		
    		try {
    			andreaThread.join();
    			lorisThread.join();
    		} catch (InterruptedException e) {
    			System.out.println("Qualcosa è andato storto...");
    		}	
    	}
    }
    Beh.. semplicemente se avvio il programma, non funziona. Comunque, una volta che avrò
    trovato il problema, come posso fare per capire quale dei due giocatori ha finito per primo le mele?

  2. #2
    Utente di HTML.it L'avatar di andbin
    Registrato dal
    Jan 2006
    residenza
    Italy
    Messaggi
    18,284

    Re: Problema giochino

    Originariamente inviato da Javino89
    semplicemente se avvio il programma, non funziona.
    Tecnicamente funziona, ma non "vedi" alcun risultato utile principalmente perché stampa() compone una stringa ma alla fine non la manda in output! E anche se la mandasse in output, scopriresti che ti direbbe sempre "... ha mangiato 0 mele ..." perché hai una variabile meleMangiate che non incrementi.
    (suggerimento: il conto delle mele "mangiate" fallo fare a Cesto, non a Giocatore).

    Originariamente inviato da Javino89
    come posso fare per capire quale dei due giocatori ha finito per primo le mele?
    I due oggetti Cesto dovrebbero avere entrambi un riferimento ad un oggetto condiviso, che ha un metodo "sincronizzato" (perché invocabile da più thread) la cui invocazione segnala appunto il vincitore (Cesto invoca questo metodo quando le mele sono finite). Essendo sincronizzato, c'è la mutua esclusione: il primo thread che riesce ad acquisire il lock, entra nel metodo ed è ... il vincitore.
    L'unica questione è che si "sa" solo quale è il Thread che è arrivato prima, non direttamente il Cesto o il Giocatore. Quindi si tratta solo di vedere come "legare" insieme queste informazioni.

    Potresti anche "girare" il design in un altro modo, per poter essere anche più coerente con la logica reale di una gara, in cui un giocatore si registra per una gara, riceve un cesto e c'è "qualcuno" che dà il via.
    Andrea, andbin.devSenior Java developerSCJP 5 (91%) • SCWCD 5 (94%)
    java.util.function Interfaces Cheat SheetJava Versions Cheat Sheet

  3. #3
    Ok che errori sciocchi, in effetti funziona, ed ho anche apportato la modifica che hai detto
    Non dovrebbero essere i giocatori ad avere un riferimento ad un oggetto condiviso? Faccio una chiamata a questo oggetto subito dopo il while dentro il metodo run.

  4. #4
    Ho scritto questo codice, che non funziona esattamente bene, nel senso che ogni tanto da come vincitore anche chi non è effettivamente il vincitore

    Giocatore

    codice:
    package GiocoDellaMela;
    
    import java.util.Random;
    
    /*
     * Classe che rappresenta un giocatore. 
     * Il suo scopo è mangiare tutte le mele presenti 
     * nel suo cesto prima degli altri giocatori.
     */
    public class Giocatore implements Runnable {
    
    	private String nome;
    	private Cesto cesto;
    	private Controllore controllore;
    	private Random random = new Random();
    	
    	public Giocatore(String nome, Cesto cesto, Controllore c) {
    		this.nome = nome;
    		this.cesto = cesto;
    		this.controllore = c;
    	}
    	
    	public void run() {
    		
    		while(!cesto.meleFinite()) {
    			cesto.mangiaMela();	
    			stampa();
    			attendi();
    		}
    		
    		controllore.setStato();
    		controllore.setVincitore(this.nome);
    	}
    	
    	/*
    	 * Simula una pausa random tra una mela mangiata ed un'altra
    	 */
    	private void attendi() {
    		//Genero un'attesa tra 100 e 1000 millisecondi
    		int j = 100;
    		int n = 1000 - j;
    		int attesa = random.nextInt(n) + j;
    		try {
    			Thread.sleep(attesa);
    		} catch (InterruptedException e) {
    			System.out.println("Qualcosa è andato storto...");
    		}
    	}
    	
    	public void stampa() {
    		String s = "Il giocatore " + this.nome + " ha mangiato " + cesto.getMeleMangiate() + " mele" + "\n";
    		System.out.println(s);
    	}
    }
    Gestore

    codice:
    package GiocoDellaMela;
    
    public class Gestore extends Thread {
    	
    	private static Controllore controllore;
    	
    	public Gestore() {
    		controllore = new Controllore();
    	}
    	
    	/* Controllo continuo della partita
    	 * per verificare chi è il vincitore.
    	 */
    	public void run() {
    		
    		while(controllore.getStato() == 0) {}
    		
    		System.out.println("Il vincitore è: " + controllore.getVincitore());
    	}
    
    	public static void main(String[]args) {
    		
    		Gestore gestore = new Gestore();
    	
    		Cesto primoCesto = new Cesto(20);
    		Cesto secondoCesto = new Cesto(20);
    		Giocatore andrea = new Giocatore("Andrea", primoCesto, controllore);
    		Giocatore loris = new Giocatore("Loris", secondoCesto, controllore);
    		
    		Thread andreaThread = new Thread(andrea);
    		Thread lorisThread = new Thread(loris);
    		
    		gestore.setPriority(MAX_PRIORITY);
    		gestore.start();
    		andreaThread.start();
    		lorisThread.start();
    	}
    }
    Cesto

    codice:
    package GiocoDellaMela;
    /*
     * Classe che rappresenta un cesto pieno di mele, che
     * può essere svuotato dai giocatori.
     */
    public class Cesto {
    
    	private int mele;
    	private int meleMangiate;
    	
    	public Cesto(int mele) {
    		this.mele = mele;
    		this.meleMangiate = 0;
    	}
    	
    	public int getMele() {
    		return mele;
    	}
    	
    	public int getMeleMangiate() {
    		return meleMangiate;
    	}
    	
    	public void setMele(int mele) {
    		this.mele = mele;
    	}
    	
    	public void mangiaMela() {
    		if(!meleFinite()) {
    			mele--;
    		}
    		meleMangiate++;
    	}
    	
    	public boolean meleFinite() {
    		return mele == 0;
    	}
    }
    Controllore

    codice:
    package GiocoDellaMela;
    
    public class Controllore {
    	
    	private int stato;
    	private String vincitore = null;
    	
    	public Controllore() {}
    	
    	public synchronized void setStato() {
    		stato = 1;
    	}
    	
    	public synchronized void setVincitore(String vincitore) {
    		this.vincitore = vincitore;
    	}
    	
    	public synchronized int getStato() {
    		return stato;
    	}
    	
    	public synchronized String getVincitore() {
    		return vincitore;
    	}
    }
    Mi vien da pensare che devo implementare una wait-notify nel gestore per controllare la partita.. vero?

  5. #5
    Utente di HTML.it L'avatar di andbin
    Registrato dal
    Jan 2006
    residenza
    Italy
    Messaggi
    18,284
    Originariamente inviato da Javino89
    ogni tanto da come vincitore anche chi non è effettivamente il vincitore
    Mi vien da pensare che devo implementare una wait-notify nel gestore per controllare la partita.. vero?
    Un meccanismo di wait-notify ti è sicuramente utile innanzitutto per evitare il "brutto" (e pesante) ciclo while di polling dello stato.

    Ma il problema è anche un altro: è vero che setVincitore è synchronized, quindi c'è mutua esclusione ma in sostanza offre solo una "serializzazione" degli accessi a setVincitore. Non hai messo alcun controllo, quindi se dopo un primo giocatore, ne entra un altro, sovrascrive il vincitore. Devi fare un controllo: solo il primo può impostare il "vincitore", gli altri eventuali dopo no.

    Tornando al concetto di wait-notify: se vuoi puoi anche usare le classi di sincronizzazione di "alto livello" disponibili da Java 5, come ad esempio un java.util.concurrent.CountDownLatch.
    Se non sai come funziona, o non vuoi (visto che ti stai esercitando con i thread per imparare questi concetti), puoi anche usare benissimo i wait()/notify()/notifyAll() degli oggetti. Ma devi farlo in modo corretto. Devi invocare questi metodi su un oggetto di cui si possiede il lock e il wait deve essere in un ciclo che testa una "condizione" che è quella che deve reggere il concetto della attesa.

    Ah, suggerimento per questa logica: in Controllore metti un metodo es. attendiVincitore(). È questo che deve tenere bloccato un qualunque altro thread finché non c'è un vincitore.
    Andrea, andbin.devSenior Java developerSCJP 5 (91%) • SCWCD 5 (94%)
    java.util.function Interfaces Cheat SheetJava Versions Cheat Sheet

  6. #6
    Originariamente inviato da andbin
    Un meccanismo di wait-notify ti è sicuramente utile innanzitutto per evitare il "brutto" (e pesante) ciclo while di polling dello stato.

    Ma il problema è anche un altro: è vero che setVincitore è synchronized, quindi c'è mutua esclusione ma in sostanza offre solo una "serializzazione" degli accessi a setVincitore. Non hai messo alcun controllo, quindi se dopo un primo giocatore, ne entra un altro, sovrascrive il vincitore. Devi fare un controllo: solo il primo può impostare il "vincitore", gli altri eventuali dopo no.

    Se non sai come funziona, o non vuoi (visto che ti stai esercitando con i thread per imparare questi concetti), puoi anche usare benissimo i wait()/notify()/notifyAll() degli oggetti. Ma devi farlo in modo corretto. Devi invocare questi metodi su un oggetto di cui si possiede il lock e il wait deve essere in un ciclo che testa una "condizione" che è quella che deve reggere il concetto della attesa.

    Ah, suggerimento per questa logica: in Controllore metti un metodo es. attendiVincitore(). È questo che deve tenere bloccato un qualunque altro thread finché non c'è un vincitore.
    Per la prima cosa che hai detto, la storia del controllo, non dovrei lanciare una interrupt() al giocatore che non ha vinto? In questo modo è impossibile che sovrascriva i dati del vincitore. Solo che per com'è fatto ora il codice, non riesco ad implementarlo. Dovrei creare i due Threads Giocatore come variabili globali della classe Gestore, in modo da lanciare la interrupt() nel run() del gestore stesso. Ora come ora ho un oggetto stringa che mi determina il vincitore, ma ho appurato che non posso fare la wait() su di esso perché all'inizio è null. Aaah aspetta ho un'idea... provo u_u

    Ps:no, idea non valida. Avevo pensato di modificare setVincitore() in modo che ritornasse un valore booleano. Ma non posso fare wait() e notify() su un metodo che io sappia.. xD

    Aggiornamento:

    Ho fatto queste modifiche, anche se non riesco comunque a fermare il giocatore che ha perso -.-

    run() di Giocatore

    codice:
    public void run() {
    		
    		while(!cesto.meleFinite()) {
    			if(!controllore.thereIsVincitore()) {
    				cesto.mangiaMela();	
    				stampa();
    				attendi();
    			}
    			else {
    				return;
    			}
    		}
    		
    		if(!controllore.thereIsVincitore()) {
                            controllore.setVincitore();
    			controllore.setNomeVincitore(this.nome);
    		}
    		else {
    			return;
    		}
    	}
    Controllore

    codice:
    package giocodellamela;
    
    public class Controllore {
    	
    	private String vincitore;
    	private boolean flag;
    	
    	public Controllore() {
    		 this.vincitore = null;
    		 flag = false;
    	}
    	
    	public synchronized void setNomeVincitore(String vincitore) {
    		this.vincitore = vincitore;
    	}
    	
    	public synchronized String getVincitore() {
    		return vincitore;
    	}
    	
    	public synchronized void setVincitore() {
    		flag = true;
    	}
    	
    	public synchronized boolean thereIsVincitore() {
    		return flag == true;
    	}
    }
    Ps: mi sono scordato di settare a true il vincitore nel run() -.-

    Pps: l'ho aggiunto, ed infatti ora pare funzionare. Non mi resta che cercare di implementare la wait() - notify()

  7. #7
    Utente di HTML.it L'avatar di andbin
    Registrato dal
    Jan 2006
    residenza
    Italy
    Messaggi
    18,284
    Originariamente inviato da Javino89
    non dovrei lanciare una interrupt() al giocatore che non ha vinto?
    Innanzitutto una questione generale sulla interruzione di un thread. Questa è una cosa che va fatta in modo "cooperativo", ovvero è anche la implementazione del lavoro del thread che deve cooperare affinché possa essere interrotto in modo (possibilmente) rapido e senza conseguenze negative per il lavoro che sta facendo.
    Inoltre la interruzione non è che fa terminare direttamente e brutalmente il thread!

    Ci sono dei momenti in cui un thread è intrinsecamente interrompibile (vedi documentazione di interrupt() che lo spiega). Non è un caso che ad esempio la sleep() di Thread dichiara di lanciare InterruptedException.
    Se il thread è in una sleep() o in una wait(), interromperlo vuol solo dire che da questi metodi si esce con InterruptedException ma tu potresti catturarlo e continuare a fare altro.
    Insomma se il lavoro del thread non "coopera", interromperlo con un interrupt() potrebbe non servire a nulla. E se il thread sta facendo solo computazioni in continuazione, un interrupt() non gli fa un baffo ....

    Per tornare al tuo Giocatore, il suo "lavoro" in teoria è facilmente interrompibile, poiché la maggior parte del tempo lo passa nella sleep() (il resto sono cose praticamente quasi immediate, impercettibili come tempo).
    Ma se fai:
    codice:
    		try {
    			Thread.sleep(attesa);
    		} catch (InterruptedException e) {
    			System.out.println("Qualcosa è andato storto...");
    		}
    L'unica cosa che puoi interrompere è quella sleep ... non tutto il "lavoro" del tuo giocatore. Il tuo while(!cesto.meleFinite()) continuerebbe con il ciclo successivo.

    Ti è chiara ora la questione della interruzione di un thread?

    Nota: anche se gestisci correttamente la interruzione in modo cooperativo, questo non impedisce che un secondo thread possa arrivare nel setVincitore. Perché dal momento in cui ricevi notifica che c'è un vincitore al momento in cui potresti interrompere gli altri giocatori, è possibile che questi arrivino nel setVincitore!

    P.S. consigli finali: supponiamo di non voler interrompere gli altri thread. Se ti basta questo, allora in Controllore hai bisogno di solo 3 metodi:
    - setVincitore( ...quello che vuoi... )
    - getVincitore() (nota: per correttezza, se invocato prima di avere un vincitore potresti o restituire null o in modo più pignolo lanciare IllegalStateException).
    - attendiVincitore() (metodo "bloccante", che si sblocca solo dopo un setVincitore).
    Andrea, andbin.devSenior Java developerSCJP 5 (91%) • SCWCD 5 (94%)
    java.util.function Interfaces Cheat SheetJava Versions Cheat Sheet

  8. #8
    Ho risolto stravolgendo il codice. Ora sto provando a far funzionare il mio senza stravolgerlo troppo. Ho fatto queste modifiche cercando di seguire i tuoi consigli, ma continuo ad avere lo stesso problema.

    Giocatore

    codice:
    package giocodellamela;
    
    import java.util.Random;
    
    /*
     * Classe che rappresenta un giocatore. 
     * Il suo scopo è mangiare tutte le mele presenti 
     * nel suo cesto prima degli altri giocatori.
     */
    public class Giocatore implements Runnable {
    
    	private String nome;
    	private Cesto cesto;
    	private Controllore controllore;
    	private Random random = new Random();
    	
    	public Giocatore(String nome, Cesto cesto, Controllore c) {
    		this.nome = nome;
    		this.cesto = cesto;
    		this.controllore = c;
    	}
    	
    	public void run() {
    		
    		while(!controllore.thereIsVincitore()) {
    			if(!cesto.isEmpty()) {
    				cesto.pull();
    				System.out.println("Il giocatore " + nome + " ha mangiato " + cesto.getMeleMangiate() + " mele.");
    				attendi();
    			}
    			else {
    				controllore.setVincitore(nome);
    			}
    		}
    	}
    	
    	/*
    	 * Simula una pausa random tra una mela mangiata ed un'altra
    	 */
    	private void attendi() {
    		//Genero un'attesa tra 100 e 1000 millisecondi
    		int j = 100;
    		int n = 1000 - j;
    		int attesa = random.nextInt(n) + j;
    		try {
    			Thread.sleep(attesa);
    		} catch (InterruptedException e) {
    			System.out.println("Qualcosa è andato storto...");
    		}
    	}
    	
    	public void stampa() {
    		String s = "Il giocatore " + this.nome + " ha mangiato " + cesto.getMeleMangiate() + " mele" + "\n";
    		System.out.println(s);
    	}
    }
    Cesto

    codice:
    package giocodellamela;
    /*
     * Classe che rappresenta un cesto pieno di mele, che
     * può essere svuotato dai giocatori.
     */
    public class Cesto {
    
    	private int mele;
    	private int meleMangiate;
    	
    	public Cesto(int mele) {
    		this.mele = mele;
    		this.meleMangiate = 0;
    	}
    	
    	public int getMele() {
    		return mele;
    	}
    	
    	public int getMeleMangiate() {
    		return meleMangiate;
    	}
    	
    	public void setMele(int mele) {
    		this.mele = mele;
    	}
    	
    	public void pull() {
    		mele--;
    		meleMangiate++;
    	}
    	
    	public boolean isEmpty() {
    		return mele == 0;
    	}
    }
    Controllore

    codice:
    package giocodellamela;
    
    public class Controllore implements Runnable {
    	
    	private String vincitore;
    	private boolean flag;
    	
    	public Controllore() {
    		 this.vincitore = null;
    		 flag = false;
    	}
    	
    	public void run() {
    		attendiVincitore();
    	}
    	
    	public synchronized String getVincitore() {
    		if(vincitore != null) {
    			return vincitore;
    		}
    		else return null;
    	}
    	
    	public synchronized void setVincitore(String nome) {
    		vincitore = nome;
    		flag = true;
    		wakeUp();
    	}
    	
    	public synchronized boolean thereIsVincitore() {
    		return flag == true;
    	}
    	
    	public void attendiVincitore() {
    		synchronized(this) {
    			while(flag == false) {
    				try {
    					wait();
    				} catch (InterruptedException e) {
    					e.printStackTrace();
    				}
    			}
    		}
    		System.out.println("Il vincitore è " + this.vincitore);
    	}
    	
    	public void wakeUp() {
    		notifyAll();
    	}
    }
    Gestore

    codice:
    package giocodellamela;
    
    public class Gestore {
    	
    	private static Controllore controllore;
    	
    	public Gestore() {
    		controllore = new Controllore();
    	}
    	
    	public static void main(String[]args) {
    		
    		Gestore gestore = new Gestore();
    	
    		Cesto primoCesto = new Cesto(20);
    		Cesto secondoCesto = new Cesto(20);
    		Giocatore andrea = new Giocatore("Andrea", primoCesto, controllore);
    		Giocatore loris = new Giocatore("Loris", secondoCesto, controllore);
    		
    		Thread andreaThread = new Thread(andrea);
    		Thread lorisThread = new Thread(loris);
    		Thread control = new Thread(controllore);
    		
    		control.start();
    		andreaThread.start();
    		lorisThread.start();
    	}
    }

Permessi di invio

  • Non puoi inserire discussioni
  • Non puoi inserire repliche
  • Non puoi inserire allegati
  • Non puoi modificare i tuoi messaggi
  •  
Powered by vBulletin® Version 4.2.1
Copyright © 2025 vBulletin Solutions, Inc. All rights reserved.