Ampelschaltung boolean

Hallo Leute,

ich hänge schon seit über 5 Std an dieser Aufgabe und schaffe es einfach nicht den Kompletten Ampelzyklus zu programmieren. Im Konstruktor steht die Ampel zunächst auf (gruen=true, gelb=false, rot=false). Durch Anwendung der Methode schalteWeiter() muss nun vom grünen in den gelben Zustannd gewechselt werden, danach in den roten, rot / gelben und wieder zurück auf den grünen. Für einen Zyklus muss die Methode folglich 4x angewendet werden.
Das Problem: man darf nur boolsche Ausdrücke verwenden!

Ich habe das Gefühl, dass ich die boolschen Operatoren “!=”, “==”, “^” nicht verwenden kann.

Hier ist meine bisherige Lösung, die von grün auf gelb auf rot kommt.

/**
 * Eine Zmpel besteht aus drei Lampen in den Farben rot, gelb und gruen.
 * Sie durchlaeuft periodisch vier Phasen: gruen, gelb, rot, rot/gelb.
 * In den ersten drei Phasen leuchtet also nur jeweils eine Lampe,
 * waehrend in der vierten Phase zwei Lampen gleichzeitig leuchten.
 * Der Zustand der einzelnen Lampen kann abgefragt werden,
 * und die Zmpel kann in die naechste Phase geschaltet werden.
 * Die Schaltung geschieht explizit durch einen Methodenaufruf
 * (und nicht etwa durch einen automatischen Timer).
 * 
 */
class Zmpel
{
    private boolean _rot;
    private boolean _gelb;
    private boolean _gruen;

    /**
     * Initialisiert eine neue Zmpel auf die erste Phase (gruen).
     */
    public Zmpel()
    {
        _rot = false;
        _gelb = false;
        _gruen = true;
    }

    /**
     * Gibt an, ob die rote Lampe leuchtet.
     */
    public boolean leuchtetRot()
    {
        return _rot;
    }

    /**
     * Gibt an, ob die gelbe Lampe leuchtet.
     */
    public boolean leuchtetGelb()
    {
        return _gelb;
    }

    /**
     * Gibt an, ob die gruene Lampe leuchtet.
     */
    public boolean leuchtetGruen()
    {
        return _gruen;
    }

    /**
     * Schaltet die Zmpel in die naechste Phase (gruen -> gelb -> rot -> rot/gelb -> gruen).
     */
    public void schalteWeiter()
    {
        _rot = (_gelb && !_gruen) || (_gelb == _gruen);
        _gelb = (_gruen && !_rot) || (_rot == _gruen);
        _gruen = (_rot && _gelb);
    }
}

Wenn jemand die Aufgabe testen will ist die Ampel GUI praktisch Hier ist ihr Code:

import java.awt.Color;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;

/**
 * Stellt eine Ampel graphisch dar und ermoeglicht das Weiterschalten per Knopfdruck.
 *  
 */
class ZmpelGUI
{
    private Zmpel _ampel;
    private JFrame _frame;
    private JButton _roteLampe;
    private JButton _gelbeLampe;
    private JButton _grueneLampe;

    /**
     * Erzeugt eine neue Ampel und stellt diese anschliessend dar.
     */
    public ZmpelGUI()
    {
        this(new Zmpel());
    }

    /**
     * Initialisiert eine neue Ampeldarstellung mit einer gegebenen Ampel.
     * 
     * @param ampel die darzustellende Ampel
     */
    private ZmpelGUI(Zmpel ampel)
    {
        _ampel = ampel;
        initialisiereFrame();
        initialisiereLampen();
        initialisiereWeiter();
        aktualisiereLampen();
        macheGUIsichtbar();
    }

    /**
     * Konfiguriert den Fenstertitel und das Layout.
     */
    private void initialisiereFrame()
    {
        _frame = new JFrame("Z");
        _frame.setLayout(new BoxLayout(_frame.getContentPane(), BoxLayout.PAGE_AXIS));
    }

    /**
     * Erzeugt fuer jede Lampe einen Button.
     */
    private void initialisiereLampen()
    {
        _roteLampe = neueLampe();
        _gelbeLampe = neueLampe();
        _grueneLampe = neueLampe();
    }

    /**
     * Erzeugt einen LampenButton.
     */
    private JButton neueLampe()
    {
        JButton lampe = new JButton("\u25c9");
        lampe.setFont(font);
        lampe.setBackground(Color.LIGHT_GRAY);
        lampe.setForeground(Color.BLACK);
        _frame.add(lampe);
        return lampe;
    }

    private static final Font font = new Font(Font.MONOSPACED, Font.PLAIN, 50);

    /**
     * Erzeugt den Weiter-Button und fueuegt einen ActionListener hinzu,
     * der die Ampel weiterschaltet, wenn der Button gedrueckt wird.
     */
    private void initialisiereWeiter()
    {
        JButton weiter = new JButton("weiter");
        weiter.addActionListener(new ActionListener()
            {
                public void actionPerformed(ActionEvent e)
                {
                    _ampel.schalteWeiter();
                    aktualisiereLampen();
                }
            });
        _frame.add(weiter);
    }

    /**
     * Setzt die Farben der LampenButtons entsprechend der Phase der verwendetet Ampel.
     */
    private void aktualisiereLampen()
    {
        _roteLampe.setForeground(_ampel.leuchtetRot() ? Color.RED : Color.BLACK);
        _gelbeLampe.setForeground(_ampel.leuchtetGelb() ? Color.YELLOW : Color.BLACK);
        _grueneLampe.setForeground(_ampel.leuchtetGruen() ? Color.GREEN : Color.BLACK);
    }

    /**
     * Setzt die DefaultCloseOperation, packt das Frame auf die richtige Groesse und macht es
     * sichtbar.
     */
    private void macheGUIsichtbar()
    {
        _frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        _frame.pack();
        _frame.setVisible(true);
    }
}

Ich denke, dass das Ergebnis entweder super einfach für jemanden, der den passenden Befehl kennt, oder auch für fortgeschrittene Programmierer nicht ganz offensichtlich sein wird.
Danke im Voraus.

Viele Grüße
skonline90

ein höchstens schwer überwindbares Problem ist hierbei,
dass du die Variablen änderst und sie gleichzeitig noch prüfst,

wenn etwa in Zustand n rot + gelb aktiviert war, und in n+1 rot + gelb korrekt ausgeschaltet werden, grün noch aus war,
dann sind für grün-Prüfung alle Variablen auf false, was ist daraus zu erkennen?
das kann man vielleicht schon als konkretes Kriterium nehmen um grün zu aktivieren, aber doch etwas entgegen aller anschaulichen Logik

besser ist, die drei alten boolean in drei Variablen temporär zu speichen und von diesem Zustand aus die drei neuen Werte bestimmen,
mit dieser Reparatur wären dann nur noch korrekte Regeln zu finden,
dann ist _gruen = rotTemp && gelbTemp; anschaulich und korrekt, selbst wenn inzwischen _rot und _gelb ausgeschaltet wurden

dagegen:
bei _gelb = (_gruen && !_rot) || [..] zielst du vielleicht noch auf das alte durchmischten Vorgehen ab,

mit temporären Variablen wäre diese Bedingung hier dann aber unsinnig: grün und rot können sowieso nie gleichzeitig an sein,
nur von erlaubten Zuständen auf neue erlaubte wechseln

*** Edit ***

sind auch nicht nötig, aber wenn käme man da ja bei boolean durch einfache Vergleich (a && b) || (!a && !b) auch hin

Warum nicht alle Fälle einfach hinschreiben?

gelb = gruen && !gelb && !rot || rot && !gelb && !gruen

rot = gelb && !gruen && !rot

gruen = rot && gelb && !gruen || rot && !gelb && !gruen

@Slater B
Ich glaube nicht, dass wir die Variablen verändern dürfen. Es wird verlangt einen Algorithmus für die Methode zu finden, der bei 4x Anwendung einen Ampelzyklus durchlaufen kann. Es wird ja erst richtig kompliziert, wenn man rot und gelb gleichzeitig zum leuchten bringen will, ohne das grün sich anschaltet. Das ist echt schwer …

*** Edit ***
@Bleiglanz
Das geht nicht. Schaltet zwar von grün auf gelb aber nicht von gelb auf rot. Denn bei zweiten Methodenaufruf ist die Ampel ja auf (gruen = false, gelb = true, rot = false) geschaltet. Dann wird die gelbe Leuchte einfach ausgehen, weil ja gruen && !gelb && !rot nicht mehr gilt. Danach würde rot nicht angehen, weil ja gelb nicht mehr leuchtet :frowning:

@Bleiglanz
bei direkter Veränderung der Variablen sieht es eben mau aus,

Zustand n gelb → Zustand n+1 gelb wird deaktiviert, danach rot nicht aktiviert (weil ja gelb aus ist), grün auch nicht,
mit temporären Variablen ginge es

lokale zusätzliche Variablen widerspricht nicht unbedingt

vermute ich wiederum

vielleicht geht es aber auch innerhalb der Variablen, ich schau jetzt mal nach Regeln dafür,
verstehen und entscheiden zwischen diesen beiden fundamentalen Varianten musst du dich aber auf jeden Fall

*** Edit ***

ja, auch ohne lokale Variablen einen Weg gefunden,
die Reihenfolge

        _gelb = ..;
        _gruen = ..;

kann es schaffen, während erst gelb scheitern muss (edit: doch nicht, geht auch gelb zuerst)

falls es ohne lokale Variablen sein soll, einfach immer weiter dran arbeiten,
wie weit funktioniert es, welchen Fehler gibt es → diese Stelle dann verfeinern

ich poste mal vorerst noch nicht meine Lösung, ruhig dran arbeiten, irgendwas nennen was Schwierigkeiten bereitet
‚alles konfus‘ hilft nicht, sondern konkrete Schaltung von nach, welche versuchte Bedingung geht nicht, warum ungefähr Variante X und Y nicht versucht

einzeln dran arbeiten, wann ist rot zu aktiveren? dabei noch gar nicht an gelb und grün denken, das kommt dann auch noch,
rot ist am einfachsten weil da alle drei boolean noch auf alten Stand

[Edit]

Habe das mit den “nur mit booleschen Operationen” überlesen… :ka:

[Originalbeitrag]

Das ist ein (wenn auch super-einfacher) endlicher Automat, warum soll man den dann nicht so modellieren?

public enum State {
    GREEN(true, false, false), 
    YELLOW(false, true, false), 
    RED(false, false, true), 
    RED_YELLOW(false, true, true);

    public final static boolean greenLight; 
    public final static boolean yellowLight; 
    public final static boolean redLight; 

    public State(boolean greenLight, boolean yellowLight, boolean redLight) {
        this.greenLight = greenLight;
        this.yellowLight = yellowLight;
        this.redLight = redLight;
    }

    public State next() {
       return values()[(ordinal()+1) % 4];
    }
}

Jetzt kann man mit state = state.next() weiterschalten und mit state.greenLight u.s.w. die Lämpchen abfragen.

weil man dann nicht das in diesem Fall geplante lernt, logisches Vorstellungs- und Ausdrucksvermögen etwa abseits vom Malen 4 verschiedener Bilder :wink:
einen aufs 1x1 bis 10 begrenzten Taschenrechner könnte man auch mit 100 fertigen Ergebnissen hinterlegen

[QUOTE=Bleiglanz]Warum nicht alle Fälle einfach hinschreiben?

gelb = gruen && !gelb && !rot || rot && !gelb && !gruen

rot = gelb && !gruen && !rot

gruen = rot && gelb && !gruen || rot && !gelb && !gruen[/QUOTE]

Ja, seltener Käse :slight_smile:

Geht natürlich, wenn man

gelb_neu

rot_neu

gruen_neu

d.h. Zwischenspeicher verwendet

@Slater:

beim Übergang von gelb zu rot wird doch bei der Reihenfolge

rot=
gelb=
gruen=

in der ersten zeile rot gesetzt (wenn korrekt)

Nach der ersten Zeile ist dann rot=true und gelb=true, dann geht das nie mehr weiter

wie soll gruen in der letzten zeile dann wissen, dass es false sein muss: rot ist ja in jedem fall aktiv, korrekterweise ist gelb dann falsch…???

doch, gelb schaltet sich dann ab :wink:
denn das kann ja nix gutes werden, es ist offensichtlich nicht die grün->gelb-Situation,
und es ist auch nicht die rot->rot+gelb-Situation denn dann wäre ja gelb bisher aus gewesen

mit temporären Variablen kann man aber auf solche verrückten Gedanken verzichten

edit: es geht doch auch in der Reihenfolge mit gelb als erstes gesetzt, man muss nur genug aufpassen,
ob auch mit grün überlasse ich anderen, bestimmt


    private static boolean g = true;
    private static boolean y,r = r = false;
    
    private static void next() {
        g = (r && y);
        y = !y;
        r = (!g && !y) || (r && y);
        
        
      print();
    }
    
    public static void print() {
        if(g && !y && !r) {
            System.out.println("Grün");
        } else if(!g && y && !r) {
            System.out.println("Gelb");
        } else if(!g && !y && r) {
            System.out.println("Rot");
        }else if(!g && y && r) {
            System.out.println("Gelb Rot");
        } else {
            System.out.println("Fehler");
        }
        
    }
    
    public static void main(String[] args) {
       print();
       for(int i = 0; i< 10; i++){
           next();
       }
    }    
}```

Das funktioniert zumindest mal auch ohne Temp-Variablen.

Erkenntnis 1

Gelb ist nur von sich selbst abhängig.

Gelb an => Gelb aus
Gelb aus => Gelb an

Erkenntnis 2
Grün wird es nur wenn zuvor rot und gelb an sind.

D.h. zuerst Grün schalten.
Danach gelb schalten, da nur abhängig von gelb.

Zuletzt um rot kümmern, wenn das vorige erledigt ist.

Erkenntnis 3
Rot geht an wenn sonst alles aus ist.

Rot bleibt an, wenn Rot und Gelb an sind.

    private static boolean g = true;
    private static boolean y,r = r = false;
    
    private static void next() {
        g = (r && y);
        y = !y;
        r = (!g && !y) || (r && y);
        
        
      print();
    }
    
    public static void print() {
        if(g && !y && !r) {
            System.out.println("Grün");
        } else if(!g && y && !r) {
            System.out.println("Gelb");
        } else if(!g && !y && r) {
            System.out.println("Rot");
        }else if(!g && y && r) {
            System.out.println("Gelb Rot");
        } else {
            System.out.println("Fehler");
        }
        
    }
    
    public static void main(String[] args) {
       print();
       for(int i = 0; i< 10; i++){
           next();
       }
    }    
}```

Das funktioniert zumindest mal auch ohne Temp-Variablen.

Erkenntnis 1

Gelb ist nur von sich selbst abhängig.

Gelb an => Gelb aus
Gelb aus => Gelb an

Erkenntnis 2
Grün wird es nur wenn zuvor rot und gelb an sind.

D.h. zuerst Grün schalten.
Danach gelb schalten, da nur abhängig von gelb.

Zuletzt um rot kümmern, wenn das vorige erledigt ist.

Erkenntnis 3
Rot geht an wenn sonst alles aus ist.

Rot bleibt an, wenn Rot und Gelb an sind.

*** Edit ***

private static void next() {
g = (r && y);
y = !y;
r = r == y;
}


Erkenntnis 4

Hat man Gelb und Grün bereits geschalten, dann ist rot nur rot wenn rot gleich gelb.

Bzw. 
Rot nur wenn rot ungleich gelb und gelb zuletzt geschalten wird.

private static void next() {
g = (r && y);
r = r != y;
y = !y;
}

das einfache gelb hilft natürlich noch ne Menge, fast schon cheaten,
dann hätten wir es ja