# Konzeptfrage zu Pacman



## raGe666 (31. Jul 2012)

Morgen zusammen,

ich habe eine Frage zum Konzept bezüglich der Bewegung der Monster in Pacman. Da diese selber durch die Gänge laufen sollen, muss die Klasse Monster ja wissen, wo eine Mauer ist und wo nicht.
Das habe ich im Moment so gelöst, dass bei der Aufforderung, ein Feld zu gehen, in der move()-Methode vorher das Feld vor, hinter, rechts und links vom Monster auf den boolean isWall geprüft wird. Wenn das nördliche Feld eine Mauer ist, wird ein 
	
	
	
	





```
int wallFlag
```
 um 3 erhöht, wenn das östliche Feld eine Mauer ist, um 5 erhöht, beim südlichen Feld wird um 7 erhöht und beim westlichen um 11.
Somit ergeben sich verschiedene Summen dieser Zahlen anhand derer entschieden werden kann, wo man überall hinkann.
Zum Beispiel würde die Summe 12 ergeben, dass im östlichen und im südlichen Feld eine Mauer ist und das Monster somit nur nach Norden oder Westen gehen kann.

Nun erscheint mir das recht umständlich und vielleicht auch nicht so effizient, weil dann folgender Code entsteht:
[JAVA=533]    public void move() {
        int wallFlag = scanWalls();
        int i;
        Random r;

        oldX = posX;
        oldY = posY;

        switch (wallFlag) {
            case 3:  moveUp();
                     break;

            case 5:  moveRight();
                     break;

            case 7:  moveDown();
                     break;

            case 11: moveLeft();
                     break;

            case 8:  if (dir.equals("W")) {
                         moveUp();
                     } else {
                         moveRight();
                     }
                     break;

            case 10: if (dir.equals("N")) {
                         moveUp();
                     } else {
                         moveDown();
                     }
                     break;

            case 12: if (dir.equals("W")) {
                         moveDown();
                     } else {
                         moveRight();
                     }
                     break;

            case 14: if (dir.equals("E")) {
                         moveUp();
                     } else {
                         moveLeft();
                     };
                     break;

            case 16: if (dir.equals("E")) {
                         moveRight();
                     } else {
                         moveLeft();
                     }
                     break;

            case 18: if (dir.equals("E")) {
                         posY++;
                     } else {
                         posX--;
                     }
                     break;

            case 13: r = new Random();
                     i = r.nextInt(100);
                     if (i < 33) {
                         moveUp();
                     } else if (i < 66) {
                         moveRight();
                     } else {
                         moveLeft();
                     }
                     break;

            case 19: r = new Random();
                     i = r.nextInt(100);
                     if (i < 33) {
                         moveUp();
                     } else if (i < 66) {
                         moveRight();
                     } else {
                         moveLeft();
                     }
                     break;

            case 21: r = new Random();
                     i = r.nextInt(100);
                     if (i < 33) {
                         moveUp();
                     } else if (i < 66) {
                         moveDown();
                     } else {
                         moveLeft();
                     }
                     break;

            case 23: r = new Random();
                     i = r.nextInt(100);
                     if (i < 33) {
                         moveRight();
                     } else if (i < 66) {
                         moveDown();
                     } else {
                         moveLeft();
                     }
                     break;
        }
    }

    public void moveUp() {
        posY--;
        dir = "N";
    }

    public void moveDown() {
        posY++;
        dir = "S";
    }

    public void moveLeft() {
        posX--;
        dir = "W";
    }

    public void moveRight() {
        posX++;
        dir = "E";
    }

    public int scanWalls() {
        int w = 0;
        if (posY == 0) {
            w += 0;
        } else if (!(g.gameField.field[posX][posY - 1].getWall())) {
            w += 3;
        }
        if (posX == 12) {
            w += 0;
        } else if (!(g.gameField.field[posX + 1][posY].getWall())) {
            w += 5;
        }
        if (posY == 12) {
            w += 0;
        } else if (!(g.gameField.field[posX][posY + 1].getWall())) {
            w += 7;
        }
        if (posX == 0) {
            w += 0;
        } else if (!(g.gameField.field[posX - 1][posY].getWall())) {
            w += 11;
        }
        return w;
    }[/code]

Gibt ein keine bessere, kürzere und/oder schönere Lösung, die Bewegung der Monster zu bestimmen? Trotzdem soll sie ja nicht komplett zufällig sein, also nicht auf einmal mitten im Gang umdrehen oder versuchen in eine Wand zu laufen.


----------



## nazar (31. Jul 2012)

Laufrichtung merken und die Wände z. B. mit Boolean[] übergeben. Spart dir nicht wirklich eine Abfrage, aber das unschöne Addieren fällt weg. 

Wenn ich die Pacmanbewegungen noch richtig in Erinnerung habe, kann das Monster bei Abzweigungen abbiegen und muss dem Gang nicht folgen.
Ein anderer Ansatz wäre damit die Bewegung auch gleich in scanWalls() zu werfen, solang sie nicht direkt gegen die Laufrichtung geht. Diese kannst du scanWalls() ja mitgeben.

Für das Wegrennen bei Superpacman weiß ich jetzt nicht wirklich was. Pacman fokussieren könnte aber mit *Algorithmus klappen, falls du soetwas implementieren willst.


----------



## raGe666 (31. Jul 2012)

klar, mit "mitten im Gang umdrehen" meinte ich natürlich, wenns nur vor/zurück gibt, dass das Monster die momentane Richtung beibehält.
Die Sache mit SuperPacman wollte ich erst noch nicht behandeln (auch wenn ich die Möglichkeit dafür offen zuhalten versuche^^), weil das im Moment noch meine Fähigkeiten übersteigt, denke ich 

kannst du die Idee mit dem Boolean[] noch weiter ausführen? Kann mir im Moment noch nicht denken wie du das gemeint hast


----------



## nazar (31. Jul 2012)

Damit meinte ich nur den Return-Wert deiner scanWalls()-Methode. 

```
boolean[] baWalls = new boolean[4];
return baWalls;
```
 oder so.
Umgeht den Int-Return-Wert und erspart eben das Addieren.
Nun kann man nichtmehr mit Switch-Case arbeiten, aber "bEastWall=true" erscheint mir einfach schöner als 5.


----------



## raGe666 (31. Jul 2012)

Ah, das ist tatsächlich ein Fortschritt!:rtfm:


----------



## SlaterB (31. Jul 2012)

die vierfache Wiederholung der Random-Geschichte verhinderst du allein dadurch, dass du nur aus Monstersicht agierst:
vorwärts, links rum, rechts rum, rückwärts, 
was das dann jeweils bedeutet, ob auf dem Spielfeld nach Norden, Süden, Osten, Westen, kann danach in einem klaren Schritt umgerechnet werden,

und wozu ints in einer objektorientierten Sprache? 
verwende Enums für die Richtungen bzw. subjektiven Werte (vorwärts, links rum, rechts rum, rückwärts),
dann eine Liste/ Set davon, falls nützlich, was mir aber gerade noch nicht so scheint, lieber:
speichere 4 boolean, das ist zwar auch etwas primitiv, aber zumindest leicher zu durchschauen als die Flags in der Zahl,
du musst hier nicht extrem sparen


```
linksFrei = istFrei(bestimmeFeld(monsterPosition,monsterAusrichtung, (inks)true, false, false, false));
...
```
statt der 4 boolean-Parameter für die Richtung dann doch ein Enum, in der Methode bestimmeFeld() 
muss aber irgendwann dann doch die ganzen if/else kommen, posY + 1 usw., durch monsterRichtung sogar noch zusätzlich kompliziert

monsterRichtung könnte auch Enum sein, (Norden, Süden, Osten, Westen), 
zu  (vorwärts, links rum, rechts rum, rückwärts) bisschen redudant, aber lieber nicht zusammenfassen

ok, diese Methode wird jetzt ziemlich kompliziert, falls man da nicht wieder bisschen abstrahiert, x und y austauschbar macht,
nicht das Glanzstück im Sparen, wenn auch interessanter Code herauskommen kann

----

jedenfalls wird es danach interessant wenn die 4 boolean vorhanden sind, bzw. nur 3, 
nach hinten interessiert im Moment anscheindend nicht oder wäre wenn dann sowieso immer frei, von dort kommt das Monster ja,
dann die Bewegung bestimmen:
ich sehe in deinem Code nicht den Fall einer T-Kreuzung, nach vorne gehts nicht weiter, aber nach links und rechts?
dann muss doch sicher 50/50 gewählt werden,

ich würde aber bei dem 33/66-Code bleiben, bzw. wähle eine der drei Richtungen, wenn frei dann dorthin, sonst wiederholen,
falls paar mal umsonst gerechnet wird, ist das kein Beinbruch,
damit ist auch T mit 50/50 abgedeckt, bei nur einer Richtung, ob geradeaus oder Ecke, wird die irgendwann auch gefunden,

schließlich noch die gewählte Richtung wiederum mit der bisherigen Monster-Ausrichtung in ein Feld umrechnen, neue Monsterausrichtung speichern usw.,

-----

jetzt fällt mir gerade auf, dass man sich bei meinem Vorschlag zu 33/66 die Variablen linksFrei usw. gleich ganz sparen kann,
einfach eine Zielrichtung ausrechnen, schauen ob dort frei, dann dorthin, sonst neu, und gut,

Umrechnungen zu den Richtungen würden dabei aber bestehen bleiben (bestimmeFeld), 
deutlich kürzer zu dem vorher von mir skizzierten ist das nicht unbedingt

----

oder doch wieder auf einfacherem Niveau, weitgehend Verzicht auf Richtungen usw.:
rechne einfach nur mit Zufall 25/50/75 eines von 4 Nachbarfeldern aus, 
wenn es das vorherige des Monster war (merken), dann ablehnen, wiederholen
wenn es eine Wand ist, dann ablehnen, wiederholen,
sonst dorthin 
dürfte den einfachen Bewegungsregeln im Moment genügen


----------



## raGe666 (31. Jul 2012)

Wow danke, das dürfte wohl genügend Stoff zum Nachdenken und Ausprobieren sein  :rtfm:


----------



## Marco13 (31. Jul 2012)

Allgemein (auch in bezug auf SlaterBs Einwand der Durchschaubarkeit und Nachvollziehbarkeit) : Eine Option wären "richtige" Flags - im Gegensatz zu den pseudo-Flags, die addiert werden.


```
private static final int NORTH = (1<<0);
private static final int SOUTH = (1<<1);
private static final int EAST = (1<<2);
private static final int WEST = (1<<3);

private int computeWalls()
{
    ...
    return NORTH | WEST; // Beispiel
}


int walls = computeWalls(...);
if ((walls & NORTH) != 0) { // Im Norden ist eine Wand }
if ((walls & WEST ) != 0) { // Im Westen ist eine Wand }
if ((walls & SOUTH ) == 0) { // Im Süden ist KEINE Wand }
...
```
Aber ... das sieht vom Stil her schon so aus, als würde es aus dem Original-PacMan stammen  Man kann sich da heute schon etwas Objektorienterung gönnen...


----------



## raGe666 (31. Jul 2012)

danke nochmal!
Als Sozusagen-Einsteiger kann ich aber leider nichts mit 
	
	
	
	





```
private static final int NORTH = [B](1<<0)[/B];
```
 oder 
	
	
	
	





```
return [B]NORTH | WEST[/B];
```
 anfangen 
Kannst du mir 1,2 Stichworte geben, unter was ich das nachlesen kann?


----------



## Marco13 (31. Jul 2012)

"Bitweise Operatoren", "Shift-Operatoren", "Logische Operatoren".  Forensuche,  oder 
Bitwise and Bit Shift Operators (The Java™ Tutorials > Learning the Java Language > Language Basics)
oder 
Shift and Logical Operators


----------



## tuttle64 (31. Jul 2012)

Die Monstersicht ist schon mal ein guter Ansatz. Leider genügt diese nicht, um aus konzeptioneller Sicht die Frage zu beantworten, wie ein Monster auf dem kürzesten Weg zum Pacman gelangt oder auch wie sich die verfügbaren Monster bewegen müssen, um Pacman einzukesseln.


----------



## raGe666 (31. Jul 2012)

tuttle64 hat gesagt.:


> Die Monstersicht ist schon mal ein guter Ansatz. Leider genügt diese nicht, um aus konzeptioneller Sicht die Frage zu beantworten, wie ein Monster auf dem kürzesten Weg zum Pacman gelangt oder auch wie sich die verfügbaren Monster bewegen müssen, um Pacman einzukesseln.



die Monster sollen Pacman nicht aktiv verfolgen (was sie im Original auch nicht machen, sondern nur zufällig durchs Spielfeld ziehen und dabei zufällig Pacman einkreisen, glaub ich), sondern nur, in einem fernen Stadium der Spieleentwickung, vor einem Superpacman wegrennen.


----------



## SlaterB (31. Jul 2012)

beim Superpacman dürfte eine einfache Entfernungsbewertung der verfügbaren Felder schon gut funktionieren,
wenn links der Superpacman, dann ist das rechte Feld günstiger, da weiter entfernt, also dorthin


----------



## AngryDeveloper (31. Jul 2012)

raGe666 hat gesagt.:


> die Monster sollen Pacman nicht aktiv verfolgen (was sie im Original auch nicht machen, sondern nur zufällig durchs Spielfeld ziehen und dabei zufällig Pacman einkreisen, glaub ich)



Ist nicht einfach nur zufällig. Die Geister haben auch jeweils ein unterschiedliches Verhalten:
GameInternals - Understanding Pac-Man Ghost Behavior
Evtl. recht interessant zu lesen.

Edit:
Hier alles sogar noch genauer. Ist aber auf der anderen Seite eh auch verlinkt:
The Pac-Man Dossier


----------

