card-game.html 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. <!doctype html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>Document</title>
  6. <!-- <script src="https://cdn.jsdelivr.net/gh/alpinejs/alpine@v2.x.x/dist/alpine.min.js" defer></script> -->
  7. <script src="/dist/alpine.js" defer></script>
  8. <link href="https://unpkg.com/tailwindcss@^1.0/dist/tailwind.min.css" rel="stylesheet">
  9. </head>
  10. <body>
  11. <!-- Memory Game -->
  12. <div x-data="game()" class="px-10 flex items-center justify-center min-h-screen">
  13. <h1 class="fixed top-0 right-0 p-10 font-bold text-3xl">
  14. <span x-text="points"></span>
  15. <span class="text-xs">pts</span>
  16. </h1>
  17. <div class="flex-1 grid grid-cols-4 gap-10">
  18. <template x-for="(card, index) in cards" :key="index">
  19. <div>
  20. <button x-show="! card.cleared"
  21. :style="'background: ' + (card.flipped ? card.color : '#999')"
  22. :disabled="flippedCards.length >= 2"
  23. class="w-full h-32"
  24. @click="flipCard(card)"
  25. >
  26. </button>
  27. </div>
  28. </template>
  29. </div>
  30. </div>
  31. <!-- Flash Message -->
  32. <div x-data="{ show: false, message: '' }"
  33. x-show.transition.opacity="show"
  34. x-text="message"
  35. @flash.window="
  36. message = $event.detail.message;
  37. show = true;
  38. setTimeout(() => show = false, 1000)
  39. "
  40. class="fixed bottom-0 right-0 bg-green-500 text-white p-2 mb-4 mr-4 rounded"
  41. >
  42. </div>
  43. <script>
  44. function pause(milliseconds = 1000) {
  45. return new Promise(resolve => setTimeout(resolve, milliseconds));
  46. }
  47. function flash(message) {
  48. window.dispatchEvent(new CustomEvent('flash', {
  49. detail: { message }
  50. }));
  51. }
  52. function game() {
  53. return {
  54. cards: [
  55. { color: 'green', flipped: false, cleared: false },
  56. { color: 'red', flipped: false, cleared: false },
  57. { color: 'blue', flipped: false, cleared: false },
  58. { color: 'yellow', flipped: false, cleared: false },
  59. { color: 'green', flipped: false, cleared: false },
  60. { color: 'red', flipped: false, cleared: false },
  61. { color: 'blue', flipped: false, cleared: false },
  62. { color: 'yellow', flipped: false, cleared: false },
  63. ].sort(() => Math.random() - .5),
  64. get flippedCards() {
  65. return this.cards.filter(card => card.flipped);
  66. },
  67. get clearedCards() {
  68. return this.cards.filter(card => card.cleared);
  69. },
  70. get remainingCards() {
  71. return this.cards.filter(card => ! card.cleared);
  72. },
  73. get points() {
  74. return this.clearedCards.length;
  75. },
  76. async flipCard(card) {
  77. card.flipped = ! card.flipped;
  78. if (this.flippedCards.length !== 2) return;
  79. if (this.hasMatch()) {
  80. flash('You found a match!');
  81. await pause();
  82. this.flippedCards.forEach(card => card.cleared = true);
  83. if (! this.remainingCards.length) {
  84. alert('You Won!');
  85. }
  86. } else {
  87. await pause();
  88. }
  89. this.flippedCards.forEach(card => card.flipped = false);
  90. },
  91. hasMatch() {
  92. return this.flippedCards[0]['color'] === this.flippedCards[1]['color'];
  93. }
  94. };
  95. }
  96. </script>
  97. </body>
  98. </html>