Tic Tac Toe Teil 0: Vergleich von Svelte und React
Tic Tac Toe Teil 1: Svelte und Canvas 2D
Tic Tac Toe Teil 2: Staatenloses Rückgängigmachen / Wiederherstellen
Tic Tac Toe, Teil 3: Rückgängig / Wiederherstellen mit Befehlsspeicher
Tic Tac Toe Teil 4: Interaktion mit dem Flask Backend über HTTP
In dem Artikel "Vergleich: Svelte and React" habe ich versucht, die Entwicklung des Spiels Tic Tac Toe zu wiederholen. Dort habe ich nur den ersten Teil des ursprünglichen Tutorials für React abgeschlossen, ohne die Geschichte der Züge zu unterstützen. In diesem Artikel beginnen wir mit der Entwicklung dieses Spiels unter Verwendung des Svelte-Frameworks mit Unterstützung für die Geschichte der Züge. Die Geschichte der Bewegungen ist eigentlich ein Rückgängig / Wiederherstellen-System. Im ursprünglichen Tutorial zu React ist das Rückgängig / Wiederherstellen-System mit Statusspeicher mit wahlfreiem Zugriff auf einen beliebigen Status implementiert. Bei der Implementierung des Undo / Redo-Systems wird normalerweise das Befehlsmuster verwendet, und die Undo / Redo-Befehle werden in der Befehlsliste gespeichert. Wir werden versuchen, diesen Ansatz später zu implementieren, und jetzt werden wir das Rückgängig / Wiederherstellen-System mit Statusspeicher ausführen.
Die Entwicklung verwendet die Flux-Architekturlösung unter Verwendung von Speicher. Dies ist ein separater Abschnitt im Artikel.
Code starten
REPL-Code
App.svelte<script> import Board from './Board.svelte'; </script> <div class="game"> <div class="game-board"> <Board /> </div> <div class="game-info"> <div class="status">Next player: X</div> <div></div> <ol></ol> </div> </div> <style> .game { font: 14px "Century Gothic", Futura, sans-serif; margin: 20px; display: flex; flex-direction: row; } .game-info { margin-left: 20px; } .status { margin-bottom: 10px; } ol { padding-left: 30px; } </style>
Board.svelte <script> import { onMount } from 'svelte'; export let width = 3; export let height = 3; export let cellWidth = 34; export let cellHeight = 34; export let colorStroke = "#999"; let boardWidth = 1 + (width * cellWidth); let boardHeight = 1 + (height * cellHeight); let canvas; onMount(() => { const ctx = canvas.getContext('2d'); ctx.clearRect(0, 0, boardWidth, boardHeight); ctx.beginPath(); </script> <canvas bind:this={canvas} width={boardWidth} height={boardHeight} ></canvas>
Der Startcode zeigt ein leeres Raster an. Es wird mit dem HTML5-Canvas-Element angezeigt. Weitere Informationen zur Verwendung dieses Elements finden Sie im vorherigen Artikel Entwickeln von Breakout auf Svelte . Wie zeichnet man ein hier ausspioniertes Gitter. Die Board- Komponente kann in anderen Spielen wiederverwendet werden. Durch Ändern der Variablen width und height können Sie die Größe des Rasters ändern. Durch Ändern der Werte der Variablen cellWidth und cellHeight können Sie die Größe der Zelle ändern.
Füllen Sie die Zellen mit Nullen
REPL-Code
Die Funktion onMount () fügte die Ausgabe von Nullen in den Zellen nach der Ausgabe des Rasters hinzu. Und einige magische Zahlen beziehen sich auf Positionierungswerte in Zellen.
ctx.beginPath(); ctx.font = "bold 22px Century Gothic"; let d = 8; for (let i = 0; i < height; i+=1) { for (let j = 0; j < width; j+=1) { ctx.fillText("O", j * cellWidth + d + 1, (i + 1) * cellHeight - d); } } ctx.closePath();
Statusspeicher hinzufügen
REPL-Code
In diesem Abschnitt überprüfen wir die Statusänderungen mithilfe des Benutzerrepositorys . Der Statusspeicher wurde in einer separaten Datei storage.js hinzugefügt und in beide Komponenten importiert: App und Board. In diesem Repository werden die Methoden state1 und state2 definiert, die den Status des Spiels ändern.
import { writable } from 'svelte/store'; function createState() { const { subscribe, set, update } = writable(Array(9).fill('O')); return { subscribe, state1: () => set(Array(9).fill('1')), state2: () => set(Array(9).fill('2')), }; } export const state = createState();
Der App-Komponente wurden zwei Schaltflächen " Status 1" und " Status 2" hinzugefügt. Durch Klicken auf die Schaltflächen rufen wir die entsprechenden Methoden im Repository auf.
<button on:click={state.state1}>State 1</button> <button on:click={state.state2}>State 2</button>
In der Board-Komponente habe ich die Ausgabezeile von Nullen in die Ausgabe von Daten aus ihrem Statusspeicher geändert. Hier verwenden wir das automatische Abonnement für die Speicherung .
ctx.fillText($state[k], j * cellWidth + d + 1, (i + 1) * cellHeight - d);
In diesem Stadium ist das Spielfeld standardmäßig mit Nullen gefüllt. Klicken Sie auf die Schaltfläche Status 1 - das Feld ist mit Einheiten gefüllt. Klicken Sie auf die Schaltfläche Status 2 - das Feld ist mit Zweien gefüllt.
Füllen einer Zelle mit einem Mausklick
REPL-Code
Im Statusspeicher habe ich die setCell () -Methode hinzugefügt, die die ausgewählte Zelle mit einem Kreuz füllt.
setCell: (i) => update(a => {a[i] = 'X'; return a;}),
Der Zeichenfläche wurde ein Mausklick-Ereignishandler hinzugefügt. Hier ermitteln wir den Index der Zelle und rufen die setCell () -Methode des Statusspeichers auf .
function handleClick(event) { let x = Math.trunc((event.offsetX + 0.5) / cellWidth); let y = Math.trunc((event.offsetY + 0.5) / cellHeight); let i = y * width + x; state.setCell(i); }
Standardmäßig ist das Spielfeld mit Nullen gefüllt, wir klicken auf eine beliebige Zelle, die Null wird durch ein Kreuz ersetzt.
Geschichte der Bewegungen
REPL-Code
Ich möchte Sie daran erinnern, dass wir derzeit das Rückgängig / Wiederherstellen-System mit Statusspeicher mit wahlfreiem Zugriff ausführen.
import { writable } from 'svelte/store'; class History { constructor() { this.history = new Array; this.current = -1; } currentState() { return this.history[this.current]; } push(state) {
Der Statusspeicher wurde gelöscht, der Verlaufsspeicher wurde hinzugefügt, um den Verlauf von Verschiebungen zu speichern. Wir beschreiben es mit der History- Klasse. Verwenden Sie zum Speichern der Status das Verlaufsarray . Bei der Implementierung des Undo / Redo-Systems werden manchmal zwei LIFO-Stapel verwendet: Undo-Stack und Redo-Stack. Wir verwenden ein einzelnes Verlaufsarray , um Zustände im Verlauf zu speichern. Die aktuelle Eigenschaft wird verwendet, um den aktuellen Status des Spiels zu bestimmen. Alle Zustände im Verlauf vom Beginn des Arrays bis zum Zustand mit dem aktuellen Index befinden sich sozusagen in der Liste "Rückgängig" und alle anderen in der Liste "Wiederherstellen". Wenn Sie die aktuelle Eigenschaft verringern oder erhöhen, dh die Befehle Rückgängig oder Wiederherstellen ausführen, wählen Sie den Status näher am Anfang oder am Ende des Spiels aus. Die Rückgängig- und Wiederherstellungsmethoden sind noch nicht implementiert, sie werden später hinzugefügt. Die Methoden CurrentState () und push () wurden der History- Klasse hinzugefügt. Die currentState () -Methode gibt den aktuellen Status des Spiels zurück. Mit der push () -Methode fügen wir dem Verschiebungsverlauf einen neuen Status hinzu.
In der App- Komponente haben wir die Schaltflächen Status 1 und Status 2 entfernt . Und fügte einen Druckknopf hinzu:
<button on:click={() => history.push(Array(9).fill($history.current + 1))}>Push</button>
Durch Klicken auf diese Schaltfläche wird ein neuer Status zum Verlauf der Bewegungen hinzugefügt. Das Verlaufsarray wird einfach mit dem aktuellen Statusindexwert aktuell gefüllt.
Zeigen Sie in der Board- Komponente den aktuellen Status aus der Bewegungshistorie an. So verwenden Sie das automatische Abonnement für den Speicher :
ctx.fillText($history.currentState()[k], j * cellWidth + d + 1, (i + 1) * cellHeight - d);
In der Push- Methode des Verlaufsspeichers können Sie der Browserkonsole eine Ausgabe hinzufügen und beobachten, wie sich diese ändert, nachdem Sie auf die Schaltfläche Push geklickt haben.
h.push(state); console.log(h); return h;
Klicken Sie auf eine Zelle
REPL-Code
In der App- Komponente wurde der Druckknopf entfernt.
Die clickCell- Methode wurde im Verlaufsspeicher definiert. Hier erstellen wir eine vollständige Kopie des Spielstatus, ändern den Status der ausgewählten Zelle und fügen dem Bewegungsverlauf einen neuen Status hinzu:
clickCell: (i) => update(h => {
In der Board- Komponente wurde der Funktion handleClick () der Aufruf der Speichermethode callClick () hinzugefügt:
history.clickCell(i);
In der Browserkonsole hier können wir auch sehen, wie sich der Status des Bewegungsverlaufs nach jedem Mausklick ändert.
In den folgenden Artikeln werden wir das Spiel bis zum Ende beenden, mit einer Oberfläche zum Abbrechen / Zurückgeben von Schritten und wahlfreiem Zugriff auf jeden Schritt des Spiels. Betrachten Sie eine Implementierung des Rückgängig / Wiederherstellen-Systems unter Verwendung des Befehlsentwurfsmusters. Betrachten Sie die Interaktion mit dem Backend, der Spieler wird mit dem intellektuellen Agenten im Backend konkurrieren.
Flussarchitektur
Bei der Entwicklung dieses Spiels wird die Verwendung der Flux-Architekturlösung beobachtet. Aktionen werden als Methoden in der Verlaufsspeicherdefinition in der Datei store.js implementiert . Es gibt ein Verlaufs- Repository , das in Form der Verlaufsklasse beschrieben wird. Ansichten werden als App- und Board- Komponenten implementiert. Für mich ist dies alles dasselbe wie die MVC-Architektur , Seitenansicht. Aktionen - Controller, Speichermodell, Ansicht - Ansicht. Die Beschreibungen beider Architekturen stimmen praktisch überein.
GitHub-Repository
https://github.com/nomhoi/tic-tac-toe-part1
Installieren des Spiels auf dem lokalen Computer:
git clone https://github.com/nomhoi/tic-tac-toe-part1.git cd tic-tac-toe-part1 npm install npm run dev
Wir starten das Spiel in einem Browser unter der Adresse: http: // localhost: 5000 / .