Tic Tac Toe Bagian 0: Membandingkan Langsing dan Bereaksi
Tic Tac Toe Bagian 1: Svelte dan Canvas 2D
Tic Tac Toe Bagian 2: Stateless Undo / Redo
Tic Tac Toe, bagian 3: Undo / Redo dengan penyimpanan perintah
Tic Tac Toe Bagian 4: Berinteraksi dengan Flask Backend Menggunakan HTTP
Dalam artikel "Perbandingan: Svelte dan Bereaksi" saya mencoba mengulangi pengembangan game Tic Tac Toe. Di sana saya menyelesaikan hanya bagian pertama dari tutorial asli untuk Bereaksi tanpa mendukung sejarah gerakan. Pada artikel ini, kita akan memulai pengembangan game ini menggunakan kerangka Svelte dengan dukungan untuk sejarah pergerakan. Sejarah gerakan sebenarnya adalah sistem Undo / Redo. Dalam tutorial asli tentang Bereaksi, sistem Undo / Redo dengan penyimpanan keadaan dengan akses acak ke keadaan apa pun diimplementasikan. Saat menerapkan sistem Undo / Redo, pola Command biasanya digunakan, dan perintah Undo / Redo disimpan dalam daftar perintah. Kami akan mencoba menerapkan pendekatan ini nanti, sekarang kami akan menjalankan sistem Undo / Redo dengan penyimpanan negara.
Pengembangan menggunakan solusi arsitektur Flux menggunakan penyimpanan. Ini adalah bagian terpisah dalam artikel.
Mulai kode
Kode REPL
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>
Kode awal menampilkan kisi kosong. Ini ditampilkan menggunakan elemen kanvas HTML5. Untuk informasi lebih lanjut tentang menggunakan elemen ini, lihat artikel sebelumnya Mengembangkan Breakout on Svelte . Cara menggambar kotak memata-matai di sini . Komponen board dapat digunakan kembali di game lain. Dengan mengubah variabel lebar dan tinggi , Anda bisa mengubah ukuran grid, dengan mengubah nilai variabel cellWidth dan cellHeight, Anda bisa mengubah ukuran sel.
Isi sel dengan nol
Kode REPL
Dalam fungsi onMount () menambahkan output nol dalam sel setelah output grid. Dan beberapa angka ajaib terkait dengan nilai posisi dalam sel.
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();
Tambahkan penyimpanan negara
Kode REPL
Di bagian ini, kami memverifikasi perubahan status menggunakan repositori pengguna . Menambahkan penyimpanan keadaan dalam file stores.js yang terpisah, penyimpanan ini diimpor ke dalam dua komponen: Aplikasi dan Papan. Metode state1 dan state2 yang mengubah status permainan didefinisikan dalam repositori ini.
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();
Menambahkan dua tombol, State 1 dan State 2 ke komponen App. Dengan mengklik tombol, kami memanggil metode yang sesuai di repositori.
<button on:click={state.state1}>State 1</button> <button on:click={state.state2}>State 2</button>
Dalam komponen Dewan, saya mengubah garis output nol ke output data dari penyimpanan negara mereka. Di sini kami menggunakan langganan otomatis ke penyimpanan .
ctx.fillText($state[k], j * cellWidth + d + 1, (i + 1) * cellHeight - d);
Pada tahap ini, bidang permainan diisi dengan nol secara default, klik tombol State 1 - bidang diisi dengan unit, klik tombol State 2 - bidang diisi dengan deuces.
Mengisi sel dengan klik mouse
Kode REPL
Di state store, saya menambahkan metode setCell () , yang mengisi sel yang dipilih dengan tanda silang.
setCell: (i) => update(a => {a[i] = 'X'; return a;}),
Penangan peristiwa klik-mouse telah ditambahkan ke kanvas, di sini kita menentukan indeks sel dan memanggil metode setCell () dari penyimpanan keadaan .
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); }
Secara default, bidang bermain diisi dengan nol, kita klik pada sel apa pun, nol diganti dengan tanda silang.
Sejarah bergerak
Kode REPL
Biarkan saya mengingatkan Anda bahwa kami sedang menjalankan sistem Undo / Redo dengan penyimpanan negara dengan akses acak.
import { writable } from 'svelte/store'; class History { constructor() { this.history = new Array; this.current = -1; } currentState() { return this.history[this.current]; } push(state) {
Toko negara telah dihapus, toko riwayat telah ditambahkan untuk menyimpan riwayat perpindahan. Kami menggambarkannya menggunakan kelas History . Untuk menyimpan status, gunakan larik riwayat . Kadang-kadang, ketika menerapkan sistem Undo / Redo, dua tumpukan LIFO digunakan: undo-stack dan redo-stack. Kami menggunakan larik riwayat tunggal untuk menyimpan status di Riwayat . Properti saat ini digunakan untuk menentukan kondisi permainan saat ini. Semua status dalam sejarah dari awal array ke status dengan indeks saat ini , bisa dikatakan, ada di daftar Undo, dan semua yang lain ada di daftar Redo. Mengurangi atau menambah properti saat ini, dengan kata lain, menjalankan perintah Undo atau Redo, kami memilih negara yang lebih dekat ke awal atau ke akhir permainan. Metode undo dan redo belum diimplementasikan, mereka akan ditambahkan nanti. Metode CurrentState () dan push () telah ditambahkan ke kelas History . Metode currentState () mengembalikan status permainan saat ini, menggunakan metode push () yang kami tambahkan status baru ke riwayat pemindahan.
Dalam komponen Aplikasi , kami menghapus tombol Status 1 dan Status 2 . Dan menambahkan tombol tekan :
<button on:click={() => history.push(Array(9).fill($history.current + 1))}>Push</button>
Dengan mengklik tombol ini, status baru ditambahkan ke histori gerakan, array riwayat hanya diisi dengan nilai indeks saat ini .
Dalam komponen Dewan , tampilkan status saat ini dari riwayat gerakan. Berikut cara menggunakan langganan otomatis ke penyimpanan :
ctx.fillText($history.currentState()[k], j * cellWidth + d + 1, (i + 1) * cellHeight - d);
Dalam metode push dari toko sejarah , Anda dapat menambahkan output ke konsol browser dan melihat bagaimana itu berubah setelah mengklik tombol Push .
h.push(state); console.log(h); return h;
Mengklik pada sel
Kode REPL
Di komponen App , tombol Push telah dihapus.
Metode clickCell telah ditentukan di penyimpanan riwayat . Di sini kami membuat salinan lengkap dari kondisi permainan, mengubah status sel yang dipilih dan menambahkan status baru ke riwayat pemindahan:
clickCell: (i) => update(h => {
Dalam komponen Dewan , panggilan metode penyimpanan callClick () ditambahkan ke fungsi handleClick () :
history.clickCell(i);
Di konsol peramban di sini kita juga dapat melihat bagaimana status riwayat perpindahan berubah setelah setiap klik mouse.
Dalam artikel berikut, kami akan menyelesaikan game sampai akhir, dengan antarmuka untuk membatalkan / mengembalikan langkah-langkah dan akses acak ke setiap langkah permainan. Pertimbangkan implementasi sistem Undo / Redo menggunakan pola desain Perintah. Pertimbangkan interaksi dengan backend, pemain akan bersaing dengan agen intelektual di backend.
Arsitektur fluks
Dalam pengembangan game ini, penggunaan solusi arsitektur Flux diamati. Tindakan diimplementasikan sebagai metode dalam definisi riwayat toko di file stores.js . Ada repositori sejarah, yang dijelaskan dalam bentuk kelas Sejarah . Tampilan diimplementasikan sebagai komponen Aplikasi dan Dewan . Bagi saya, semua ini sama dengan arsitektur MVC , tampilan samping. Tindakan - pengontrol, model penyimpanan, tampilan - tampilan. Deskripsi kedua arsitektur praktis bertepatan.
Repositori GitHub
https://github.com/nomhoi/tic-tac-toe-part1
Memasang game di komputer lokal:
git clone https://github.com/nomhoi/tic-tac-toe-part1.git cd tic-tac-toe-part1 npm install npm run dev
Kami meluncurkan game di browser di alamat: http: // localhost: 5000 / .