Inhaltsverzeichnis

Client-Side Vier Gewinnt

Vier gewinnt ist ein Spiel mit 6×7 Feldern. Die Spieler:innen werfen abwechslungsweise ein Stein ihrer Farbe in Spalten mit leeren Feldern. Das Spiel ist beendet, wenn ein Spieler vier Felder in horizontaler, vertikaler oder diagonaler Richtung besetzen kann, oder wenn keine freien Felder übrig sind (unentschieden).

Vier Gewinnt

Auftrag A: Struktur & Formatierung

Erstelle HTML & CSS, um ein Vier-Gewinnt-Spiel darzustellen:

Auftrag B: Spiel-Logik

Das Ziel dieses Auftrags ist, dass zu zweit im Browser Vier gewinnt gespielt werden kann. Dazu nutzen wir JavaScript.

Javascript einbinden

Die Javascript-Datei (z.B. connect4.js) muss im HTML-Code eingebunden werden. Damit die Datei erst ausgeführt wird, wenn das HTML aufgebaut ist, markieren wir sie mit async:

    <script src="connect4.js" async></script>

Zustand

Der Zustand (en. state) des Spiels soll in einem Javascript-Objekt (wie ein Python-Dictionary) gespeichert werden:

game = {
  "state": "playing",  // or "waiting" or "won" or "tie"
  "board": [           // game board, 0 for empty cells, 1 or 2 for filled cells.
    0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0,
  ],
  "next": 1,  // 1 or 2, the player whose turn it is, the winner if state is "won"
}

Alle Änderungen des Spiels sollen zuerst auf diesem Spiel-Modell durchgeführt werden. Anschliessend wird das Spiel vom HTML dargestellt (en. rendering).

Verhalten

Jeder button benötigt einen Click-Handler, der ausgeführt wird, wenn die spielende Person darauf klickt. Mit Code ähnlich dem Folgenden kann jedem Button ein Handler zugewiesen werden:

  let index = 0
  for (let button of document.getElementsByTagName("button")) {
    const column = index % 7;
    button.addEventListener("click", () => dropPiece(game, column)); // TODO: write a dropPiece function
    index++;
  }

Der Click-Handler:

Um den Code etwas zu strukturieren, hilft es, die obigen Aufgaben als Funktionsaufrufe hinzuschreiben, und diese erst anschliessend zu implementieren. Die dropPiece Funktion könnte also so aussehen:

/** Drops a piece in the given column and updates the game state accordingly. */
function dropPiece(grid, status, game, column) {
  // Check if move is valid.
  if (game.state != "playing") {
    return;  // or throw new Error("can't play in this game")
  }
  // Compute the lowest empty row in the selected column.
  let row = computeEmptyRow(game, column);
  let cell = row * 7 + column;
  // Update game state.
  game.board[cell] = game.next;
  // Check for winner / tie, will set game.state if game ended.
  checkWinner(game, cell);
  // Swap active player
  if (game.state == "playing") {
    togglePlayer(game);
  }
  // Update the HTML view.
  updateHtml(grid, game);
  // Update game status area to reflect active player / winner / tie
  updateStatus(status, game);
}

Darstellung in HTML

Wenn der Spielzustand geändert worden ist, wird das HTML (genauer: das HTML Document Object Model (DOM)) entsprechend angepasst:

Um die Darstellung des Spiels zu ändern, setzen wir den Text des Buttons mit der textContent Eigenschaft. Für die Formatierung mit CSS speichern wir den Zustand zudem in einem Attribut data-state. Sobald die Formatierung funktioniert, können wir den Text auch wieder entfernen.

tictactoe.js
    let color = game.board[cell];              // 1, or 2
    button.textContent = color;                // remove once CSS formatting works
    button.setAttribute('data-state', color);

In CSS können wir mit einem geeigneten Selektor die Zellen formatieren, die einen bestimmten Attributwert haben:

tictactoe.css
button[data-state="1"] {  
    background-color: red;
}
button[data-state="2"] {  
    background-color: yellow;
}

Für Benachrichtigungen (z.B. über das Ende des Spiels) kann man die Funktion alert(message) nützen, die ein Browser-Popup anzeigt. Schöner ist es, einen Bereich im HTML zu definieren, der Meldungen anzeigt.