|
@@ -0,0 +1,116 @@
|
|
|
+<!doctype html>
|
|
|
+<html lang="en">
|
|
|
+<head>
|
|
|
+ <meta charset="UTF-8">
|
|
|
+ <title>Document</title>
|
|
|
+ <!-- <script src="https://cdn.jsdelivr.net/gh/alpinejs/alpine@v2.x.x/dist/alpine.min.js" defer></script> -->
|
|
|
+ <script src="/dist/alpine.js" defer></script>
|
|
|
+ <link href="https://unpkg.com/tailwindcss@^1.0/dist/tailwind.min.css" rel="stylesheet">
|
|
|
+</head>
|
|
|
+
|
|
|
+<body>
|
|
|
+ <!-- Memory Game -->
|
|
|
+ <div x-data="game()" class="px-10 flex items-center justify-center min-h-screen">
|
|
|
+ <h1 class="fixed top-0 right-0 p-10 font-bold text-3xl">
|
|
|
+ <span x-text="points"></span>
|
|
|
+ <span class="text-xs">pts</span>
|
|
|
+ </h1>
|
|
|
+
|
|
|
+ <div class="flex-1 grid grid-cols-4 gap-10">
|
|
|
+ <template x-for="(card, index) in cards" :key="index">
|
|
|
+ <div>
|
|
|
+ <button x-show="! card.cleared"
|
|
|
+ :style="'background: ' + (card.flipped ? card.color : '#999')"
|
|
|
+ :disabled="flippedCards.length >= 2"
|
|
|
+ class="w-full h-32"
|
|
|
+ @click="flipCard(card)"
|
|
|
+ >
|
|
|
+ </button>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- Flash Message -->
|
|
|
+ <div x-data="{ show: false, message: '' }"
|
|
|
+ x-show.transition.opacity="show"
|
|
|
+ x-text="message"
|
|
|
+ @flash.window="
|
|
|
+ message = $event.detail.message;
|
|
|
+ show = true;
|
|
|
+ setTimeout(() => show = false, 1000)
|
|
|
+ "
|
|
|
+ class="fixed bottom-0 right-0 bg-green-500 text-white p-2 mb-4 mr-4 rounded"
|
|
|
+ >
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <script>
|
|
|
+ function pause(milliseconds = 1000) {
|
|
|
+ return new Promise(resolve => setTimeout(resolve, milliseconds));
|
|
|
+ }
|
|
|
+
|
|
|
+ function flash(message) {
|
|
|
+ window.dispatchEvent(new CustomEvent('flash', {
|
|
|
+ detail: { message }
|
|
|
+ }));
|
|
|
+ }
|
|
|
+
|
|
|
+ function game() {
|
|
|
+ return {
|
|
|
+ cards: [
|
|
|
+ { color: 'green', flipped: false, cleared: false },
|
|
|
+ { color: 'red', flipped: false, cleared: false },
|
|
|
+ { color: 'blue', flipped: false, cleared: false },
|
|
|
+ { color: 'yellow', flipped: false, cleared: false },
|
|
|
+ { color: 'green', flipped: false, cleared: false },
|
|
|
+ { color: 'red', flipped: false, cleared: false },
|
|
|
+ { color: 'blue', flipped: false, cleared: false },
|
|
|
+ { color: 'yellow', flipped: false, cleared: false },
|
|
|
+ ].sort(() => Math.random() - .5),
|
|
|
+
|
|
|
+ get flippedCards() {
|
|
|
+ return this.cards.filter(card => card.flipped);
|
|
|
+ },
|
|
|
+
|
|
|
+ get clearedCards() {
|
|
|
+ return this.cards.filter(card => card.cleared);
|
|
|
+ },
|
|
|
+
|
|
|
+ get remainingCards() {
|
|
|
+ return this.cards.filter(card => ! card.cleared);
|
|
|
+ },
|
|
|
+
|
|
|
+ get points() {
|
|
|
+ return this.clearedCards.length;
|
|
|
+ },
|
|
|
+
|
|
|
+ async flipCard(card) {
|
|
|
+ card.flipped = ! card.flipped;
|
|
|
+
|
|
|
+ if (this.flippedCards.length !== 2) return;
|
|
|
+
|
|
|
+ if (this.hasMatch()) {
|
|
|
+ flash('You found a match!');
|
|
|
+
|
|
|
+ await pause();
|
|
|
+
|
|
|
+ this.flippedCards.forEach(card => card.cleared = true);
|
|
|
+
|
|
|
+ if (! this.remainingCards.length) {
|
|
|
+ alert('You Won!');
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ await pause();
|
|
|
+ }
|
|
|
+
|
|
|
+ this.flippedCards.forEach(card => card.flipped = false);
|
|
|
+ },
|
|
|
+
|
|
|
+ hasMatch() {
|
|
|
+ return this.flippedCards[0]['color'] === this.flippedCards[1]['color'];
|
|
|
+ }
|
|
|
+ };
|
|
|
+ }
|
|
|
+ </script>
|
|
|
+</body>
|
|
|
+</html>
|