Machine à sous de casino

Le problème exposé dans ce sujet a été résolu.

Bonjour à tous,

[Je re-crée ce topic suite au problème qu'a connu le SGBD de ZdS]

Contexte

Je dois mettre en place une machine à sous de N rouleaux qui tournent de façon indépendante. Chacun d'eux est géré par un thread (qui attend 200ms entre chaque incrémentation du compteur). Un thread gère l'affichage des rouleaux toutes les 500ms. Le système est donc de type lecteur-rédacteurs, avec un seul lecteur.

Ma solution

J'ai créé trois classes :

1 - Counter. Chaque instance est associée à un et un seul thread-rouleau, et chaque rouleau a un et un seul objet :Counter.

1
2
3
4
5
6
7
8
9
public class Counter {
    int value;
    public Counter(int value) {
        this.value = value;
    }
    public int getVal() { return value; }
    public void setVal(int v) { value = v; }
    public void inc() { value++; }
}

2 - La classe Launcher. Elle affiche la valeur de l'objet :Counter de chaque thread-rouleau.

Voici le bout de code le plus important de cette classe (l'affichage de ladite valeur). Le code entier est affiché juste après.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
 try {
            while (true) { // Note that writers have also "while(true)" to increment
                for(Writer writer : writers) { // We display each writer's counter
                    synchronized (writer.cmpt) {
                        System.out.print(writer.cmpt.getVal());
                        if(writers.get(writers.size()-1) != writer) {
                            System.out.print(" - ");
                        }
                    }
                }
                System.out.println();
                Thread.sleep(500);
            }
        } catch(InterruptedException e) {
            System.out.println(e);
        }

Code entier de Launcher :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import java.util.ArrayList;

public class Launcher {

    public static void main(String[] args) {
        int n = 3; // Number of threads (ie. : number of rollers)
        ArrayList<Writer> writers = new ArrayList<Writer>();
        while(n-- > 0) {  // We start all the writers
            Writer writer = new Writer();
            writers.add(writer);
            writer.start();
        }

        try {
            while (true) { // Note that writers have also "while(true)" to increment
                for(Writer writer : writers) { // We display each writer's counter
                    synchronized (writer.cmpt) {
                        System.out.print(writer.cmpt.getVal());
                        if(writers.get(writers.size()-1) != writer) {
                            System.out.print(" - ");
                        }
                    }
                }
                System.out.println();
                Thread.sleep(500);
            }
        } catch(InterruptedException e) {
            System.out.println(e);
        }
    }

}

3- Enfin, la classe Writer, c'est-à-dire thread-rouleau.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
public class Writer extends Thread {
    public Counter cmpt;

    public Writer() { cmpt = new Counter(0); }

    public void run() {
        try {
            while (true) { // Note that READER has also "while(true)" to DISPLAY
                synchronized (cmpt) {
                    cmpt.inc();
                    if (cmpt.getVal() >= 10) {
                        cmpt.setVal(0);
                    }
                    Thread.sleep(200);
                }
            }
        } catch(InterruptedException e) {
            System.out.println(e);
        }
    }
}

Comme vous pouvez le voir, aucun thread-rouleau ne partage son :Counter avec un autre thread-rouleau.

Le thread-main, qui s'occupe de l'affichage de la valeur des compteurs, ne doit pas lire celle-ci quand un thread-rouleau est en train d'écrire dedans (système lecteur-rédacteurs). D'où le synchronized présent dans Launcher et Writer.

Le problème

Le souci, c'est que quand j'exécute le programme, le thread-main affiche chaque ensemble de rouleaux (ie. : "0 - 2 - 1" par exemple) petit-à-petit : ici il afficherait dans un premier temps, par exemple, "0 - 2 - " et ensuite le " 1" ce qui donnerait, sur la même ligne : "0 - 2 - 1" mais dans un second temps.

Et je doute que le prof soit d'accord avec ça parce que ce n'est pas comme ça qu'une machine à sous de la vraie vie fonctionne. Si je ne me trompe pas, c'est à cause de l'exclusion mutuelle lecteur-rédacteurs : le lecteur n'affiche pas tant qu'un rédacteur écrit.

Du coup rien de plus normal, puisque c'est ce qu'on veut faire. Mais on veut aussi que chaque ensemble de rouleaux, c'est-à-dire chaque ligne de la console, s'affiche d'un coup. Tout en respectant le délai d'affichage de 500ms.

Du coup je ne sais pas trop quoi faire… Le comportement de mon programme me semble normal, mais pour autant ne doit certainement pas répondre aux exigences du prof…

Quelqu'un aurait-il une idée sur ce qu'il serait possible de faire ?

Merci d'avance !

+0 -0

Plusieurs remarques :

  • D’après ton énoncé (qui est resté dans mon cache), les rouleaux ne sont pas censés s’arrêter en meme temps, si je comprend bien :

    Lorsque l'utilisateur tape sur n'importe quelle touche de son clavier, un rouleau s'arrête (dans l'ordre : de gauche à droite).

  • Le sleep n'a aucune raison de se trouver dans le bloc synchronized, c'est ça qui rend l'affichage aussi saccadé ;
  • Sinon, tu peux toujours préparer la chaîne et l'afficher d'un seul coup une fois prête ;
  • Et surtout, la structure de ton code est l'exact opposé de ce qu'on attend en java. Autant que possible, ce n'est pas les threads qui doivent gérer la synchronisation, mais les objets sous-jacents. Ici, si tu rend les méthodes de ta classe Counter comme étant synchronized, tu débarrasse la logique de synchronisation de tes threads pour le placer sur le bon objet, et ton code devient beaucoup plus clair et plus simple.
+1 -0

Re yoch ! :)

D’après ton énoncé (qui est reste dans mon cache), les rouleaux ne sont pas censés s’arrêter en meme temps, si je comprend bien :

Lorsque l'utilisateur tape sur n'importe quelle touche de son clavier, un rouleau s'arrête (dans l'ordre : de gauche à droite).

Oui alors par contre ça je ne l'ai pas encore écrit ^^

Le sleep n'a aucune raison de se trouver dans le bloc synchronized, c'est ça qui rend l'affichage aussi saccadé ;

Ah oui ! Je l'ai enlevé et y a plus de saccade, par contre les affichages sont un peu bizarres (on ne voit pas l'incrémentation, mais c'est sûrement normal à cause des délais de 500ms et de 200). Un exemple d'exécution :

1 - 1 - 1

3 - 3 - 3

5 - 5 - 5

8 - 8 - 8

0 - 0 - 0

3 - 3 - 3

5 - 5 - 5

8 - 8 - 8

0 - 0 - 0

Je vais faire ça ! (transférer la gestion de la synchronisation à la classe Counter) Merci !

Alors juste pour être sûr, les seules méthodes de la classe Counter à flagger synchronized, ce sont bien les suivantes non ? :

  1. +setVal(v : int)
  2. +inc()

Donc au final le code Writer deviendrait :

1
2
3
4
5
6
7
  while (true) { // Note that READER has also "while(true)" to DISPLAY
                cmpt.inc();
                if (cmpt.getVal() >= 10) {
                    cmpt.setVal(0);
                }
                Thread.sleep(200);
            }

Tandis que je n'enlèverais pas le synchronized de la classe Launcher. C'est bien ça ?

Non, pourquoi donc ? Tu peux rendre toutes les méthodes synchronized (y compris getVal), et enlever le synchronized du Lancher qui n'a plus d’utilité du coup.

Du reste, je n'aurais pas mis de méthode setVal pour ma part. Vu que ton compteur est modulo 10, ça pourrait être géré directement dans inc. Autrement, ton code (revisité) poserait probleme, puisqu'un thread peut intervenir entre le inc et le set (et afficher 10).

+0 -0

Non, pourquoi donc ? Tu peux rendre toutes les méthodes synchronized (y compris getVal), et enlever le synchronized du Lancher qui n'a plus d’utilité du coup.

Le souci c'est qu'il n'y a pas que getVal mais d'autres instructions qui n'appartiennent pas à la classe Counter.

Dans Launcher :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
  while (true) { // Note that writers have also "while(true)" to increment
                for(Writer writer : writers) { // We display each writer's counter
                    synchronized (writer.cmpt) {
                        System.out.print(writer.cmpt.getVal());
                        if(writers.get(writers.size()-1) != writer) {
                            System.out.print(" | ");
                        }
                        Thread.sleep(500);
                    }
                }
                System.out.println();
            }

Encore le sout(writer.cmpt.getVal())), je comprends qu'on puisse le mettre hors-synchronized si getVal était marquée synchronized.

Mais le reste, la condition + le sout(" | ") + le sleep, vu que je ne vais pas les mettre dans Counter, il faut qu'ils restent dans le synchronized de Launcher.

Autrement, ton code (revisité) poserait probleme, puisqu'un thread peut intervenir entre le inc et le set (et afficher 10).

Exact, je vais faire ça. Il faut penser à plein de choses en multi-threading quand même :o

EDIT : classes Writer et Counter ré-écrites (disparition de setVal)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
public class Writer extends Thread {
    public Counter cmpt;

    public Writer() { cmpt = new Counter(0); }

    public void run() {
        try {
            while (true) { // Note that READER has also "while(true)" to DISPLAY
                cmpt.inc();
                Thread.sleep(200);
            }
        } catch(InterruptedException e) {
            System.out.println(e);
        }
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
public class Counter {
    int value;
    public Counter(int value) {
        this.value = value;
    }
    public int getVal() { return value; }
    public synchronized void inc() {
        value++;
        if (this.value >= 10) {
            value = 0;
        }
    }
}
+0 -0

Le souci c'est qu'il n'y a pas que getVal mais d'autres instructions qui n'appartiennent pas à la classe Counter.

[…]

Encore le sout(writer.cmpt.getVal())), je comprends qu'on puisse le mettre hors-synchronized si getVal était marquée synchronized.

Mais le reste, la condition + le sout(" | ") + le sleep, vu que je ne vais pas les mettre dans Counter, il faut qu'ils restent dans le synchronized de Launcher.

Lern-X

Justement, cette portion de code ne risque rien, mis a part le getVal, aucune raison de tout mettre dans un bloc synchronized.

(et de toute facon, synchroniser des trucs sans rapport sur Counter est assez suspect en soi, ça devrait te mettre la puce a l'oreille.)

+0 -0

Justement, cette portion de code ne risque rien, mis a part le getVal, aucune raison de tout mettre dans un bloc synchronized.

(et de toute facon, synchroniser des trucs sans rapport sur Counter est assez suspect en soi, ça devrait te mettre la puce a l'oreille.)

Tu veux dire que l'on ne doit synchroniser que les accès aux données ?

Je pensais qu'il pouvait être utile de synchroniser non seulement ça, mais aussi des instructions en rapport avec ces accès, à savoir ici un System.out.print, une condition, etc.

Donc si je comprends bien ce que tu dis, dans 90% des cas, un synchronized(truc) { ... } ne contiendra qu'une seule ligne, un accès à la donnée, en tout cas aucune ligne qui ne soit pas un accès ?

Je n'ai pas dit ça. Tout dépend de ce que tu cherche a protéger.

Par exemple, il faut bien voir que System.out.print(writer.cmpt.getVal()); est équivalent aux deux lignes suivantes :

1
2
int tmp = writer.cmpt.getVal();
System.out.print(tmp);

or dans un tel cas, il est clair que seule la première ligne doit être protégée. En revanche, si tu veux protéger contre l’accès concurrent à stdout, c'est un autre probleme, mais c'est inutile ici.

Pour la condition if (writers.get(writers.size()-1) != writer), elle porte également sur des objets qui ne sont accédés nulle part en concurrence dans le reste de ton code, donc rien a craindre non plus de ce coté.

Bref, il ne s'agit pas ici de données en particulier, mais il faut avoir clairement a l'esprit quelle est la ressource que tu veux protéger contre un accès concurrent (ça peut très bien être des lignes de code dont tu veux t'assurer l’exécution atomique, par ex.).

NB: Dans le même registre, attention, car dans certains cas le compilateur est autorisé a effectuer certaines simplifications sur le bytecode, qui font qu'un code d'apparence correcte ne l'est pas en réalité.

+0 -0

Alors j'ai implémenté la détection de l'appui d'une touche (je pense que le prof fait allusion à une saisie-clavier), qui permet d'arrêter un rouleau en partant de celui qui a été lancé le premier à celui qui a été lancé en dernier.

Ca compile bien, il n'y a pas de bug, et ça fait le travail, mais je voulais savoir s'il n'y avait pas une façon plus élégante de mettre en place cette fonctionnalité. ^^'

Voici ce que j'ai fait :

1- Dans Writer, j'ai mis en commentaire le System.out.println(e); (exception InterruptedException), ce qui donne ce très court code :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
public class Writer extends Thread {
    public Counter cmpt;

    public Writer() {
        cmpt = new Counter(0);
    }

    public void run() {

        try {
            while (true) { // Note that READER has also "while(true)" to DISPLAY
                cmpt.inc();
                Thread.sleep(200);
            }
        } catch (InterruptedException e) {
          //  System.out.println(e);
        }

    }
}

J'ai fait ça car quand l'utilisateur-final appuie sur une touche pour stopper un rouleau, celui-ci, qui faisait un sleep, engendre de fait cette exception.

===================================================

2- Dans Launcher, j'ai juste rajouté une ligne en début de code : new HandlerInput(n, writers).start(); // This detects the user's event

Ce qui donne donc :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import java.util.ArrayList;

public class Launcher {

    public static void main(String[] args) {

        int n = 5; // Number of threads (ie. : number of rollers)

        ArrayList<Writer> writers = new ArrayList<Writer>();
        new HandlerInput(n, writers).start(); // This detects the user's event

        while(n-- > 0) {  // We start all the writers
            Writer writer = new Writer();
            writers.add(writer);
            writer.start();
        }

        try {
            while (true) {
                for(Writer writer : writers) { // We display each writer's counter
                    System.out.print(" | " + writer.cmpt.getVal() + " | ");
                    Thread.sleep(500);
                }
                System.out.println();
            }
        } catch(InterruptedException e) {
            System.out.println(e);
        }
    }

}

===================================================

3- Enfin, la modification majeure : j'ai créé la classe HandlerInput, chargée de détecter la saisie-clavier et de la traiter (ie. : d'arrêter (interrupt) le premier rouleau lancé, c'est-à-dire le premier rouleau encore actif (alive)). Elle doit aussi déterminer si le joueur a gagné (nombre de rouleaux identiques au moins égal à N-1).

Voici le code :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
import java.util.ArrayList;
import java.util.Scanner;

public class HandlerInput extends Thread {
    private int n;
    private ArrayList<Writer> writers;

    public HandlerInput(int n, ArrayList<Writer> writers) {
        this.n = n;
        this.writers = writers;
    }

    public void run() {
        boolean ended = false;

        Scanner scanner = new Scanner(System.in);
        // ON KEY PRESSED
        if(!scanner.next().isEmpty()) {
            for(Writer writer : writers) {
                if(writer.isAlive()) {

                    writer.interrupt(); // We stop ONE roll (the first alive)
                    if(writers.indexOf(writer) == writers.size()-1) { // If it's the last, the game is finished
                        ended = true;
                    }
                    if(!ended) { // The current handled will be killed, so we need another one
                        new HandlerInput(n, writers).start();
                    }

                    break;
                }
            }
        }

        // WHEN THE GAME IS FINISHED
        if(ended) {
            boolean won = false;
            int counter_occurences = 0;

            System.out.println("Partie terminée sur les rouleaux : ");
            for(Writer writer : writers) {
                System.out.print(" | " + writer.cmpt.getVal() + " | "); // "Partie terminée sur les rouleaux"

                counter_occurences = 0; // Counts the number of occurences for THIS roll (`writer`)
                for(Writer writer2 : writers) {
                    if(writer.cmpt.getVal() == writer2.cmpt.getVal()) {
                        counter_occurences++;
                    }
                }

                if(counter_occurences >= n-1) {
                    won = true;
                }
            }

            // WHEN THE GAME IS WON OR NOT
            if(won) {
                System.out.println("\nVous avez gagné !");
            } else {
                System.out.println("\nVous avez perdu !");
            }

            System.exit(0);
        }
    }

}
+0 -0

Ton code pour HandlerInput est plutôt laid…

Tout d'abord, ce n'est pas forcément ce qu'il y a de plus propre de gérer ça dans un thread et de quitter tout avec un System.exit(). Mais ce qui me gêne vraiment, c'est le fait de lancer un nouvel HandlerInput pour chaque rouleau, alors que tu te trouves dans une boucle et qu'il suffit d'y rester. D'ailleurs, ton test avec isAlive est assez artificiel, il suffit d’arrêter les rouleaux un par un jusqu'au dernier.

Quant au paramètre n de ton HandlerInput, il est tout bonnement useless.

Ah, et puis pour pinailler un peu, tu démarre HandlerInput trop tôt (avant les rouleaux).

+0 -0

Je suis en train de ré-écrire le code pour tenir compte de tes remarques, par contre :

D'ailleurs, ton test avec isAlive est assez artificiel, il suffit d’arrêter les rouleaux un par un jusqu'au dernier.

Quand l'utilisateur-final appuie sur son clavier, le premier rouleau doit s'arrêter. Quand il appuie de nouveau, c'est au deuxième de s'arrêter et ainsi de suite. Le test avec isAlive me permet d'arrêter un et un seul processus, le premier à ne pas être encore interrompu.

Là je suis en train de remplacer ce test tout simplement par l'interruption du premier rouleau de l'ArrayList de rouleaux, suivie d'un remove de cet élément. Le souci c'est que la partie du code affichant "Partie terminée sur les rouleaux :", qui affiche le contenu de cette liste, n'affiche qu'un seul rouleau à cause de tous les remove.

Enfin du coup aurais-tu une idée de comment arrêter un à un les rouleaux, après chaque saisie-clavier, sans utiliser de structure de données et de isAlive ?

Sinon en effet n ne servait à rien, je l'ai remplacé par un size()-1 sur l'ArrayList et j'ai aussi remplacé la création du HandlerInput par la boucle comme tu me l'as conseillé.

Au final voici, pour l'instant, le code :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
import java.util.ArrayList;
import java.util.Scanner;

public class HandlerInput extends Thread {
    private ArrayList<Writer> writers;

    public HandlerInput(ArrayList<Writer> writers) {
        this.writers = new ArrayList<Writer>(writers);
    }

    public void run() {
        int n = writers.size();
        boolean ended = false;

        // ON KEY PRESSED
        int i = 0;
        Writer writer_to_interrupt;
        while(!ended) {
            Scanner scanner = new Scanner(System.in);
            if(!scanner.next().isEmpty()) {
                writer_to_interrupt = writers.get(i);
                writer_to_interrupt.interrupt(); // We stop ONE roll (the first alive)
                if(i == writers.size()-1) { // If it's the last, the game is finished
                    ended = true;
                }
                writers.remove(writer_to_interrupt);
                i++;
            }
        }

        // WHEN THE GAME IS FINISHED
        if(ended) {
            boolean won = false;
            int counter_occurences;

            System.out.println("Partie terminée sur les rouleaux : ");
            for(Writer writer : writers) {
                System.out.print(" | " + writer.cmpt.getVal() + " | "); // "Partie terminée sur les rouleaux"

                counter_occurences = 0; // Counts the number of occurences for THIS roll (`writer`)
                for(Writer writer2 : writers) {
                    if(writer.cmpt.getVal() == writer2.cmpt.getVal()) {
                        counter_occurences++;
                    }
                }

                if(counter_occurences >= n-1) {
                    won = true;
                }
            }

            // WHEN THE GAME IS WON OR NOT
            if(won) {
                System.out.println("\nVous avez gagné !");
            } else {
                System.out.println("\nVous avez perdu !");
            }

            System.exit(0);
        }
    }

}

Enfin du coup aurais-tu une idée de comment arrêter un à un les rouleaux, après chaque saisie-clavier, sans utiliser de structure de données et de isAlive ?

Par exemple (avec une référence sur le tableau de writers) :

1
2
3
4
5
6
7
8
9
    public void run() {
        Scanner scanner = new Scanner(System.in);
        for (int i = 0; i < writers.size(); ) {
            if(!scanner.next().isEmpty()) { // ON KEY PRESSED
                writers.get(i++).interrupt(); // We stop the NEXT roll
            }
        }
        scanner.close();
    }
+0 -0

Merci encore yoch, je n'avais pas pensé à utiliser le fait que Scanner.next() soit bloquante et donc qu'on pouvait très bien placer cette instruction dans une boucle for dont la condition de continuité serait "i < writers.size();" (ce for se finissant donc bien seulement quand l'utilisateur tape N fois une saisie).

J'ai mis à jour le code, ayant également supprimé le booléen won :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
import java.util.ArrayList;
import java.util.Scanner;

public class HandlerInput extends Thread {
    private ArrayList<Writer> writers;

    public HandlerInput(ArrayList<Writer> writers) {
        this.writers = new ArrayList<Writer>(writers);
    }

    public void run() {
        // ON KEY PRESSED
        Scanner scanner = new Scanner(System.in);
        for(int i = 0; i < writers.size(); i++) {
            if(!scanner.next().isEmpty()) {
                writers.get(i).interrupt(); // We stop ONE roll (the first alive)
            }
        }
        scanner.close();

        // WHEN THE GAME IS FINISHED
        int counter_occurences = 0;
        System.out.println("Partie terminée sur les rouleaux : ");
        for(Writer writer : writers) {
            System.out.print(" | " + writer.cmpt.getVal() + " | "); // "Partie terminée sur les rouleaux"

            counter_occurences = 0; // Counts the number of occurences for THIS roll (`writer`)
            for(Writer writer2 : writers) {
                if(writer.cmpt.getVal() == writer2.cmpt.getVal()) {
                    counter_occurences++;
                }
            }
        }

        // WHEN THE GAME IS WON OR NOT
        if(counter_occurences >= writers.size()-1) {
            System.out.println("\nVous avez gagné !");
        } else {
            System.out.println("\nVous avez perdu !");
        }

        System.exit(0);
    }

}

Est-ce que tu trouves ça propre ? Il me semble que tu trouvais ça pataud d'utiliser System.exit ; penses-tu que lancer une interruption au thread principal serait une solution plus élégante ?

Il y a de petites erreurs subtiles dans ton nouveau code.

  • Le fait que tu incrémente i dans tous les cas, même si tu n'entre pas dans le if, est problématique.
  • En enlevant le booléen won, tu as rendu le code faux. D'ailleurs, un code de ce genre me fait envisager une fonction auxiliaire.

Sinon, pour le System.exit, ce n'est pas du tout dramatique, mais le plus simple aurait été de mettre cette partie du code dans le main, et la partie affichage toutes les 500 ms dans un thread séparé, plutôt que l'inverse.

penses-tu que lancer une interruption au thread principal serait une solution plus élégante ?

Pas spécialement.

Le fait que tu incrémente i dans tous les cas, même si tu n'entre pas dans le if, est problématique.

Bein le i n'est incrémenté que si l'utilisateur tape quelque chose non ? Vu que next est bloquante.

En enlevant le booléen won, tu as rendu le code faux.

Du fait du caractère bloquant de next, le premier for ne se finit que quand l'utilisateur a arrêté tous les rouleaux. Par la suite, bein je gère mal le comptage en effet… Je vais corriger ça ^^'

+0 -0

Alors j'ai remis le booléen won et finalement j'ai enlevé le test isEmpty qui ne servait à rien en fait.

Après si l'utilisateur-final tape sur sa touche Entrée, ça ne débloque pas le Scanner.next mais c'est du détail…

Voici le nouveau code, qui cette fois est le bon normalement, et en très très grande partie grâce à toi, mille mercis !

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
import java.util.ArrayList;
import java.util.Scanner;

public class HandlerInput extends Thread {
    private ArrayList<Writer> writers;

    public HandlerInput(ArrayList<Writer> writers) {
        this.writers = new ArrayList<Writer>(writers);
    }

    public void run() {
        // ON KEY PRESSED
        Scanner scanner = new Scanner(System.in);
        for(int i = 0; i < writers.size(); i++) {
            scanner.next();
            writers.get(i).interrupt(); // We stop ONE roll (the first alive)
        }
        scanner.close();

        // WHEN THE GAME IS FINISHED
        boolean won = false;
        int counter_occurences = 0;
        System.out.println("Partie terminée sur les rouleaux : ");
        for(Writer writer : writers) {
            System.out.print(" | " + writer.cmpt.getVal() + " | "); // "Partie terminée sur les rouleaux"

            counter_occurences = 0; // Counts the number of occurences for THIS roll (`writer`)
            for(Writer writer2 : writers) {
                if(writer.cmpt.getVal() == writer2.cmpt.getVal()) {
                    counter_occurences++;
                }
            }

            if(counter_occurences >= writers.size()-1) {
                won = true;
            }
        }

        // WHEN THE GAME IS WON OR NOT
        if(won) {
            System.out.println("\nVous avez gagné !");
        } else {
            System.out.println("\nVous avez perdu !");
        }

        System.exit(0);
    }

}
Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte