تيك تاك تو الجزء 2: عديمي الجنسية التراجع / الإعادة

تيك تاك تو الجزء 0: مقارنة Svelte والتفاعل
تيك تاك تو الجزء 1: Svelte و Canvas 2D
تيك تاك تو الجزء 2: عديمي الجنسية التراجع / الإعادة
تيك تاك تو ، الجزء 3: تراجع / الإعادة مع تخزين الأوامر
تيك تاك تو الجزء 4: التفاعل مع القارورة الخلفية باستخدام HTTP

استمرار للمادة تيك تاك تو ، الجزء 1 ، والتي بدأنا تطوير هذه اللعبة على Svelte . في هذا الجزء سوف ننهي اللعبة حتى النهاية. أضف فرق التراجع / الإعادة ، وصول عشوائي إلى أي خطوة من اللعبة ، بالتناوب مع الخصم ، وعرض حالة اللعبة ، وتحديد الفائز.


أوامر التراجع / الإعادة

رمز REPL


في هذه المرحلة ، تمت إضافة أوامر تراجع / الإعادة إلى التطبيق. طرق الدفع والإعادة المضافة إلى مخزن التاريخ .


undo: () => update(h => { h.undo(); return h; }), redo: () => update(h => { h.redo(); return h; }), 

طرق الدفع ، الإعادة ، canUndo ، canRedo تضاف إلى فئة History .


 canUndo() { return this.current > 0; } canRedo() { return this.current < this.history.length - 1; } undo() { if (this.canUndo()) this.current--; } redo() { if (this.canRedo()) this.current++; } 

تمت إضافة طريقة المحفوظات إلى طريقة الدفع لحذف جميع الحالات من الحالية إلى الأخيرة. إذا قمنا بتنفيذ أمر التراجع عدة مرات ثم نقرت في الملعب ، فسيتم حذف جميع الحالات الموجودة على يمين التيار إلى الأخير من المتجر وستضاف حالة جديدة.


 push(state) { // remove all redo states if (this.canRedo()) this.history.splice(this.current + 1); // add a new state this.current++; this.history.push(state); } 

تمت إضافة أزرار التراجع والإعادة إلى مكون التطبيق . إذا كان تنفيذ الأوامر غير ممكن ، فسيتم إلغاء تنشيطها.


 <div> {#if $history.canUndo()} <button on:click={history.undo}>Undo</button> {:else} <button disabled>Undo</button> {/if} {#if $history.canRedo()} <button on:click={history.redo}>Redo</button> {:else} <button disabled>Redo</button> {/if} </div> 

تغيير المسار

رمز REPL


يتم إجراء مظهر متبادل لصليب أو إصبع قدم بعد النقر بالماوس.


تمت إزالة طريقة clickCell () من مستودع السجل الخاص بهم ، وقد تم نقل كل رمز الطريقة إلى معالج handleClick () لمكون اللوحة .


 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; const state = $history.currentState(); const squares = state.squares.slice(); squares[i] = state.xIsNext ? 'X' : 'O'; let newState = { squares: squares, xIsNext: !state.xIsNext, }; history.push(newState); } 

وبالتالي ، تم التخلص من الخطأ الذي تم ارتكابه مسبقًا ؛ وكان المستودع يعتمد على منطق هذه اللعبة بالذات. الآن تم إصلاح هذا الخطأ ، ويمكن إعادة استخدام المستودع في الألعاب والتطبيقات الأخرى دون تغييرات.


سابقًا ، تم وصف حالة خطوة اللعبة فقط من خلال مجموعة من 9 قيم. الآن يتم تحديد حالة اللعبة بواسطة الكائن الذي يحتوي على الصفيف والخاصية xIsNext. يبدو تهيئة هذا الكائن في بداية اللعبة كما يلي:


 let state = { squares: Array(9).fill(''), xIsNext: true, }; 

ويمكن الإشارة أيضًا إلى أن تخزين المحفوظات يمكنه الآن إدراك الحالات الموصوفة بأي طريقة.


وصول عشوائي لنقل التاريخ

رمز REPL


في مخزن المحفوظات ، تمت إضافة طريقة setCurrent (الحالية) ، والتي وضعنا بها حالة اللعبة الحالية المحددة.


 setCurrent(current) { if (current >= 0 && current < this.history.length) this.current = current; } 

 setCurrent: (current) => update(h => { h.setCurrent(current); return h; }), 

في مكون التطبيق ، أضف محفوظات الانتقال في شكل أزرار.


 <ol> {#each $history.history as value, i} {#if i==0} <li><button on:click={() => history.setCurrent(i)}>Go to game start</button></li> {:else} <li><button on:click={() => history.setCurrent(i)}>Go to move #{i}</button></li> {/if} {/each} </ol> 

قرار الفائز

رمز REPL


وظيفة مضافة لتحديد الفائز calculateWinner () في ملف helpers.js منفصل:


 export function calculateWinner(squares) { const lines = [ [0, 1, 2], [3, 4, 5], [6, 7, 8], [0, 3, 6], [1, 4, 7], [2, 5, 8], [0, 4, 8], [2, 4, 6], ]; for (let i = 0; i < lines.length; i++) { const [a, b, c] = lines[i]; if (squares[a] && squares[a] === squares[b] && squares[a] === squares[c]) { return squares[a]; } } return null; } 

تمت إضافة مشتق الحالة لتحديد حالة اللعبة ، وهنا يتم تحديد نتيجة اللعبة: الفائز أو التعادل:


 export const status = derived( history, $history => { if ($history.currentState()) { if (calculateWinner($history.currentState().squares)) return 1; else if ($history.current == 9) return 2; } return 0; } ); 

تمت إضافة إخراج حالة اللعبة إلى مكون التطبيق :


 <div class="status"> {#if $status === 1} <b>Winner: {!$history.currentState().xIsNext ? 'X' : 'O'}</b> {:else if $status === 2} <b>Draw</b> {:else} Next player: {$history.currentState().xIsNext ? 'X' : 'O'} {/if} </div> 

في مكون اللوحة ، تمت إضافة قيود إلى معالج المقبض بنقرة handleClick () : من المستحيل النقر في الخلية المملوءة حتى بعد نهاية اللعبة.


 const state = $history.currentState(); if ($status == 1 || state.squares[i]) return; 

انتهت اللعبة! في المقالة التالية ، سننظر في تطبيق نفس اللعبة باستخدام نمط القيادة ، أي مع تخزين أوامر تراجع / إعادة بدلاً من تخزين الحالات الفردية.


مستودع جيثب

https://github.com/nomhoi/tic-tac-toe-part2


تثبيت اللعبة على الكمبيوتر المحلي:


 git clone https://github.com/nomhoi/tic-tac-toe-part2.git cd tic-tac-toe-part2 npm install npm run dev 

نطلق اللعبة في متصفح على العنوان: http: // localhost: 5000 / .

Source: https://habr.com/ru/post/ar459630/


All Articles