Matthias Scharwies: Wiki-Push: Spiele mit JavaScript

Servus!

ich hatte es schon lange im Test-Wiki und jetzt rübergezogen:

JavaScript/Tutorials/Spiele/Tic-Tac-Toe

Meine Version des Tutorials hat nur noch 23KB Text (60% gekürzt) und kümmert sich

  • zuerst um das HTML (<button aria-label="x" disabled>A1</button>)
  • dann das passende CSS (image-replacement mit SVG)
  • per JS werden dann die buttons disabled und ausgewertet.

Ich hoffe, dass das sowohl fachlich richtig als auch didaktisch verständlich geworden ist.


Jetzt geht es um die anderen Kapitel:

3. Sum-Up

Das Ganze soll jetzt betonen, wie man Elemente mit DOM-Methoden erzeugt und löscht - die Spielidee an sich ist aber nicht so intuitiv. Hat jemand einen besseren Vorschlag? einen, den er womöglich selbst umsetzen möchte?

4. Drag and Drop-Quiz

Das ist wohl etwas, was es in HTML ohne Frameworks nicht gibt.

Wer hat Lust evtl. erst ein Lückentext-Quiz und dann irgendetwas grafisches zu erstellen?

andere Baustellen:

Herzliche Grüße

Matthias Scharwies

--
Einfach mal was von der ToDo-Liste auf die Was-Solls-Liste setzen.“
  1. Lieber Matthias,

    JavaScript/Tutorials/Spiele/Tic-Tac-Toe

    Meine Version des Tutorials hat nur noch 23KB Text (60% gekürzt)

    sieht gut aus! Wenn ich aber die großen if-Statements lese, frage ich mich, ob das auch leserlicher geht.

    // 3 senkrecht
    if (fields[0 + i].getAttribute('aria-label') != ''
      && fields[0 + i].getAttribute('aria-label') == fields[3 + i].getAttribute('aria-label')
      && fields[3 + i].getAttribute('aria-label') == fields[6 + i].getAttribute('aria-label')
    )
    

    Diese Bedingungen prüfen doch auf den Wert des aria-label-Attributs. Da könnte man doch eine kürzere Schreibweise verwenden:

    function mark (node) {
      if (node.hasAttribute('aria-label')) {
        return node.getAttribute('aria-label');
      }
    }
    
    if (mark(fields[0 + i])
      && mark(fields[0 + i]) == mark(fields[3 + i])
      && mark(fields[3 + i]) == mark(fields[6 + i])
    )
    

    Ob das jetzt unbedingt besser ist, mag eine Frage der Betrachtungsweise und der persönlichen Vorlieben sein.

    Liebe Grüße

    Felix Riesterer

    1. Hallo Felix,

      den Vorschlag mit der Accessor-Funktion mark sollte man auf jeden Fall aufgreifen. Sie muss aber vervollständigt werden - für den Fall, dass das aria-label nicht existiert, sollte ein definierter Wert zurückgegeben werden. JS würde jetzt undefined liefern, aber ich halte es für schlechten Stil, einen Pfad durch eine Funktion, von der ein Wert erwartet wird, nicht auf return enden zu lassen. Das mögen andere anders sehen.

      Man kann dabei ausnutzen, dass getAttribute("foo") laut aktueller Spec null liefert und laut alter Spec den leeren String "", die beide falsy sind.

      fields ist eine globale Variable, bzw. in einem Spielobjekt wäre das eine Instanzvariable - da kann man auch einfach die Zellindizes als Parameter durchreichen und den Zugriff auf fields intern behandeln.

      function getMark(cell) {
         return fields[cell].getAttribute("aria-label") || "";
      }
      

      Dazu noch zwei weitere Helper

      function checkForVictory(cell1, cell2, cell3) {
         let mark1 = getMark(cell1),
             win = mark1 && mark1 == getMark(cell2) && mark1 == getMark(cell3);
      
         if (!win) return "";
      
         highlightCell(cell1);
         highlightCell(cell2);
         highlightCell(cell3);
      
         return mark1;
      }
      
      function highlightCell(cell) {
         fields[cell].classList.add('highlighted');
      }
      

      Und die eigentliche Abfrage würde ich dann stumpf runterprogrammieren statt aufwändig Schleifen zu bauen. Das ist natürlich anders, wenn man größere Felder implementieren will - aber ein TicTacToe mit 3 Gewinnfeldern im 4x4 Raster ist meines Wissens unspielbar (wer anfängt, gewinnt) und läuft eher auf Vier Gewinnt hinaus.

      let winner = checkForVictory(0,3,6) || checkForVictory(1,4,7) || checkForVictory(2,5,8) ||
                   checkForVictory(0,1,2) || checkForVictory(3,4,5) || checkForVictory(6,7,8) ||
                   checkForVictory(0,4,8) || checkForVictory(2,4,6);
      

      Rolf

      --
      sumpsi - posui - obstruxi
    2. @@Felix Riesterer

      function mark (node) {
      

      Meine erste Intuition war, die Funktion label zu benennen, weil sie ebendas zurückliefert. Aber bei dem Spiel ist das ja Kreuz oder Kreis … Da finde ist Rolfs Vorschlag getMark gar nicht schlecht.

        if (node.hasAttribute('aria-label')) {
          return node.getAttribute('aria-label');
        }
      }
      

      Die Abfrage ist wohl überflüssig. Wenn das Ding kein solches Attribut hat, liefert node.getAttribute('aria-label') auch null, was gleich als Rückgabewert verwendet werden kann.

      Ich finde null auch passender als ''; ich denke, dabei kann man es belassen:

      function getMark(node) {
        return node.getAttribute('aria-label');
      }
      

      😷 LLAP

      --
      „Sag mir, wie Du Deine Maske trägst, und ich sage Dir, ob Du ein Idiot bist.“ —@Ann_Waeltin
      1. @@Gunnar Bittersmann

        Die Abfrage ist wohl überflüssig. Wenn das Ding kein solches Attribut hat, liefert node.getAttribute('aria-label') auch null, was gleich als Rückgabewert verwendet werden kann.

        Wenn man da was abfragen sollte, dann, ob der übergebene node tatsächlich existiert:

        function getMark(node) {
          return node?.getAttribute('aria-label');
        }
        

        Bei der Spiellogik ist wohl aber sichergestellt, dass es das Argument wirklich gibt; die Abfrage also nicht nötig.

        Die würde im Rahmen des Tutorials eher verwirren. Zumal man auch noch erklären müsste, wie das für Uralt-Browser zu übersetzen wäre.

        😷 LLAP

        --
        „Sag mir, wie Du Deine Maske trägst, und ich sage Dir, ob Du ein Idiot bist.“ —@Ann_Waeltin
  2. Hallo Matthias,

    zum Thema Drag und Drop: Das Wiki zitiert einen ziemlich unflätigen Peter-Paul Koch, dass drag und dragover das Gleiche seien, und empfiehlt dann, dass man dragover nicht nutzen solle.

    Es brummt der A-Bär: Entweder verstehe ich da was grundsätzlich falsch, oder PPK hat sich in seinem Zorn schwer vergaloppiert. ODER aber er moppert nur dann zu Recht, wenn mal ein Browser das dropzone Attribut implementiert.

    Denn es gibt zwei entscheidendende Unterschiede: Das Event Target. Beim drag Event ist das Target das bewegte Element. Und bei dragover ist des das Element, über dem man gerade mit dem bewegten Element schwebt. Das ist ein entscheidender Unterschied, denn in dragover kann ich prüfen, ob auf diese Weise kann ich prüfen, ob ich gerade über einem drop-fähigen Element schwebe (mangels droppable-Attribut) und entsprechend den dropEffect setzen. In dragstart und dragenter gelingt mir das nicht.

    Hinzu kommt: Ohne einen preventDefault-Aufruf im dragover Event ist es unmöglich, irgendwas zu droppen. Der Mauszeiger bleibt auf "Stop". Die Frickl-Beispiele machen das ebenfalls.

    Also - PPK redete Unfug und der wurde unreflektiert nachgeplappert, oder ich plappere hier unverstandenen Käu daher. Was ist richtig?

    Rolf

    --
    sumpsi - posui - obstruxi
    1. Servus!

      Hallo Matthias,

      zum Thema Drag und Drop: Das Wiki zitiert einen ziemlich unflätigen Peter-Paul Koch, dass drag und dragover das Gleiche seien, und empfiehlt dann, dass man dragover nicht nutzen solle.

      Es brummt der A-Bär: Entweder verstehe ich da was grundsätzlich falsch, oder PPK hat sich in seinem Zorn schwer vergaloppiert. ODER aber er moppert nur dann zu Recht, wenn mal ein Browser das dropzone Attribut implementiert.

      Ich kann nur sagen: 2015! Da hab ich versucht, das Wiki zu füllen. Sorry! Gerade bei Drag und drop gab es viele Teaser-Artikel und eigentlich keinen Anwendungszweck (außer D&D bei File-Upload).

      Wsl. hat PPK da eben auch auf die Schnelle etwas versucht und ist am Fehlen des dropzone-Attributs gescheitert.

      Denn es gibt zwei entscheidendende Unterschiede: Das Event Target. Beim drag Event ist das Target das bewegte Element. Und bei dragover ist des das Element, über dem man gerade mit dem bewegten Element schwebt. Das ist ein entscheidender Unterschied, denn in dragover kann ich prüfen, ob auf diese Weise kann ich prüfen, ob ich gerade über einem drop-fähigen Element schwebe (mangels droppable-Attribut) und entsprechend den dropEffect setzen. In dragstart und dragenter gelingt mir das nicht.

      Hinzu kommt: Ohne einen preventDefault-Aufruf im dragover Event ist es unmöglich, irgendwas zu droppen. Der Mauszeiger bleibt auf "Stop". Die Frickl-Beispiele machen das ebenfalls.

      Also - PPK redete Unfug und der wurde unreflektiert nachgeplappert, oder ich plappere hier unverstandenen Käu daher. Was ist richtig?

      Ja, ich habe deine Erkenntnisse in den Artikel integriert:

      Die ersten 2 Beispiele sind aktualisiert; bei den Drag-Events muss wsl. noch etwas erklärt werden.

      Das (sinnfreie) Beispiel mit den Kugeln steht als "muss überarbeitet werden" unten drunter.

      Vor allem kann man die Kugeln( p-Absätze) nicht nur in die div-Elemente ablegen, sondern auch in die p-Absätze?!?

      [EDIT] Ein paar Zeilen weiter steht's: "Ist Ihnen aufgefallen, dass man im oberen Beispiel eine Kugel auch innerhalb einer anderen Kugel ablegen konnte? Das lag nur daran, dass in der Funktion ablegen() das target nicht explizit auf das div eingestellt worden war - was sich mit zwei zusätzlichen Zeilen problemlos beheben lässt:"

      Das ändert nichts am fehlenden Sinn. Eigentlich müsste man hier schon ein kleines Rätsel stellen.

      [/EDIT]

      Es bleibt viel zu tun!

      (Ich kümmer mich jetzt mal um das Zahlenspiel "Sum-Up")

      Herzliche Grüße

      Matthias Scharwies

      --
      Einfach mal was von der ToDo-Liste auf die Was-Solls-Liste setzen.“
  3. Moin,

    wollte an der Stelle mal eins los werden

    MEGA COOOOL !!

    Auch wenn es nicht zum Thema passt. Ich finde eure Bemühungen super! Ich weiß noch zu meiner Anfangszeit waren solche Seiten Gold wert, aber leider noch zu selten. Deshalb - weiter machen ! Ich mag was ihr da so alles fabriziert !

    Gruß Fan T-Rex

    1. Hallo T-Rex,

      wollte an der Stelle mal eins los werden

      MEGA COOOOL !!

      Vielen Dank!

      Deshalb - weiter machen ! Ich mag was ihr da so alles fabriziert !

      Du kannst dich gern beteiligen.

      Bis demnächst
      Matthias

      --
      Du kannst das Projekt SELFHTML unterstützen,
      indem du bei Amazon-Einkäufen Amazon smile (Was ist das?) nutzt.