File size: 11,972 Bytes
d0a5785
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6085efd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <title>Missing Values Tic-Tac-Toe</title>
  <meta name="viewport" content="width=device-width, initial-scale=1" />
  <script src="https://cdn.tailwindcss.com"></script>
</head>
<body class="bg-gradient-to-br from-yellow-50 to-rose-100 min-h-screen flex items-center justify-center p-6">
  <div class="w-full max-w-5xl bg-white rounded-2xl shadow-xl p-6">
    <h1 class="text-3xl md:text-4xl font-extrabold text-center text-rose-700">Missing Values Tic-Tac-Toe</h1>
    <p class="text-center text-sm text-gray-600 mt-2">
      Source:
      <a class="text-blue-600 underline" target="_blank" rel="noopener"
         href="https://www.linkedin.com/pulse/creating-managing-missing-values-data-traditional-ai-driven-lively-eb3se/">
        Creating and Managing Missing Values in Data — Michael Lively
      </a>
    </p>

    <div id="banner" class="hidden bg-rose-100 border border-rose-300 text-rose-900 font-semibold text-center py-2 rounded mt-4"></div>

    <div class="mt-6 flex flex-col md:flex-row md:space-x-6 space-y-4 md:space-y-0">
      <!-- Board -->
      <div id="board" class="grid grid-cols-3 gap-2 p-2 bg-gray-50 rounded-xl border-4 border-gray-200 mx-auto"></div>

      <!-- Question Panel -->
      <div id="questionPanel" class="md:w-1/2 w-full bg-gray-50 rounded-xl shadow-inner p-4">
        <h2 id="panelQuestion" class="text-lg md:text-xl font-semibold text-gray-800">
          Select a square to view the question
        </h2>
        <ul id="panelChoices" class="mt-4 space-y-2"></ul>
        <p id="panelHint" class="mt-3 text-sm text-gray-600 hidden"></p>
        <div class="mt-4 flex items-center space-x-2">
          <button id="hintBtn" class="px-3 py-1.5 bg-gray-200 text-gray-700 rounded hover:bg-gray-300">Show Hint</button>
          <button id="clearBtn" class="px-3 py-1.5 bg-white border rounded hover:bg-gray-100">Clear Panel</button>
        </div>
      </div>
    </div>

    <div class="flex flex-col md:flex-row md:items-center md:justify-between mt-6 space-y-3 md:space-y-0">
      <p id="status" class="text-lg font-medium text-gray-800"></p>
      <div class="flex items-center space-x-2">
        <label class="text-sm text-gray-600">Mode:</label>
        <select id="mode" class="border rounded px-2 py-1 text-sm">
          <option value="two">Two Players (X vs O)</option>
          <option value="solo">Solo (You are X)</option>
        </select>
        <button id="restartBtn" class="px-4 py-2 bg-rose-700 text-white rounded hover:bg-rose-800">Restart</button>
      </div>
    </div>
  </div>

  <script>
    const randShuffle = (arr) => arr
      .map(x => ({ x, r: Math.random() }))
      .sort((a,b) => a.r - b.r)
      .map(o => o.x);

    // --- Question Bank (about Missing Values) ---
    const QUESTION_BANK = [
      {
        question: 'Why do we sometimes create missing values deliberately?',
        choices: [
          'To corrupt the dataset permanently',
          'To test imputation methods, stress-test models, and teach data handling',
          'To hide sensitive features before analysis',
          'To reduce dataset size'
        ],
        answer: 2,
        hint: 'Think about research, robustness, and teaching use cases.'
      },
      {
        question: 'What does MCAR stand for?',
        choices: [
          'Missing Completely at Random',
          'Managed Cases at Runtime',
          'Mean Correction Algorithm Rule',
          'Missing Computations in AI Regression'
        ],
        answer: 1,
        hint: 'It is the simplest type of missingness.'
      },
      {
        question: 'Which traditional method is simplest but can distort variance?',
        choices: [
          'KNN Imputation',
          'Mean/Median/Mode Imputation',
          'Regression Imputation',
          'GAN-based Imputation'
        ],
        answer: 2,
        hint: 'It just fills gaps with averages or modes.'
      },
      {
        question: 'When is regression imputation best applied?',
        choices: [
          'When relationships are nonlinear',
          'When values are missing completely at random',
          'When strong linear correlations exist between variables',
          'When text and tabular data are mixed'
        ],
        answer: 3,
        hint: 'Think straight-line relationships between variables.'
      },
      {
        question: 'Which AI method iteratively predicts missing values for multiple variables?',
        choices: ['Autoencoders', 'MICE', 'Bayesian Networks', 'GANs'],
        answer: 2,
        hint: 'Its name includes the word “Chained Equations”.'
      },
      {
        question: 'Which deep learning model can reconstruct data by compressing and rebuilding inputs?',
        choices: ['Decision Trees', 'Autoencoders', 'Support Vector Machines', 'Random Forests'],
        answer: 2,
        hint: 'It learns a latent representation of data.'
      },
      {
        question: 'What makes GAN-based imputations (like GAIN) powerful?',
        choices: [
          'They always predict exact ground truth',
          'They use adversarial training to generate realistic missing values',
          'They only work for text data',
          'They are deterministic and rule-based'
        ],
        answer: 2,
        hint: 'Think “generator vs. discriminator”.'
      },
      {
        question: 'When is a Bayesian or EM approach most appropriate?',
        choices: [
          'When uncertainty must be quantified',
          'When data is always complete',
          'When no computation budget is available',
          'When working only with images'
        ],
        answer: 1,
        hint: 'They provide distributions, not just point estimates.'
      },
      {
        question: 'What type of data benefits most from Graph Neural Networks in imputation?',
        choices: [
          'Time series only',
          'Graph-structured or relational data',
          'Image pixel grids',
          'Independent random samples'
        ],
        answer: 2,
        hint: 'Think networks: molecules, social media, transport.'
      },
      {
        question: 'Which modern AI technique can handle hybrid text + tabular data?',
        choices: [
          'Self-supervised models and LLMs',
          'Linear regression',
          'Mode imputation',
          'Support Vector Machines'
        ],
        answer: 1,
        hint: 'Think masked language models applied to structured data.'
      }
    ];

    // --- Game State ---
    let currentPlayer, boardState, selectedCell, cellQuestions, mode;
    const boardEl = document.getElementById('board');
    const statusEl = document.getElementById('status');
    const bannerEl = document.getElementById('banner');
    const hintBtn = document.getElementById('hintBtn');
    const clearBtn = document.getElementById('clearBtn');
    const panelQuestion = document.getElementById('panelQuestion');
    const panelChoices = document.getElementById('panelChoices');
    const panelHint = document.getElementById('panelHint');
    const restartBtn = document.getElementById('restartBtn');
    const modeSel = document.getElementById('mode');

    function initGame() {
      mode = modeSel.value;
      currentPlayer = 'X';
      boardState = Array(9).fill('');
      bannerEl.classList.add('hidden');
      statusEl.innerText = '';
      selectedCell = null;
      const picked = randShuffle(QUESTION_BANK).slice(0, 9).map(q => {
        const original = q.choices.map((c, i) => ({ text: c, idx: i + 1 }));
        const shuffled = randShuffle(original);
        const newChoices = shuffled.map(o => o.text);
        const newAnswerIndex = shuffled.findIndex(o => o.idx === q.answer) + 1;
        return {
          question: q.question,
          choices: newChoices,
          answer: newAnswerIndex,
          hint: q.hint || ''
        };
      });
      cellQuestions = picked;
      panelQuestion.innerText = 'Select a square to view the question';
      panelChoices.innerHTML = '';
      panelHint.classList.add('hidden');
      panelHint.innerText = '';
      renderBoard();
    }

    function renderBoard() {
      boardEl.innerHTML = '';
      boardState.forEach((mark, idx) => {
        const btn = document.createElement('button');
        let cls = 'bg-white h-24 w-24 md:h-28 md:w-28 flex items-center justify-center text-3xl font-extrabold rounded-xl shadow hover:bg-gray-100 transition';
        if (mark === 'X') cls += ' text-rose-700';
        if (mark === 'O') cls += ' text-sky-700';
        btn.className = cls;
        btn.innerText = mark;
        btn.disabled = mark !== '';
        btn.addEventListener('click', () => openQuestion(idx));
        boardEl.appendChild(btn);
      });
      statusEl.innerText = `Current: ${currentPlayer}`;
    }

    function openQuestion(idx) {
      selectedCell = idx;
      const q = cellQuestions[idx];
      panelQuestion.innerText = q.question;
      panelChoices.innerHTML = '';
      panelHint.classList.add('hidden');
      panelHint.innerText = q.hint || '';
      q.choices.forEach((c, i) => {
        const li = document.createElement('li');
        const btn = document.createElement('button');
        btn.innerText = c;
        btn.className = 'w-full text-left px-4 py-2 bg-gray-100 rounded hover:bg-gray-200';
        btn.addEventListener('click', () => handleAnswer(i + 1));
        li.appendChild(btn);
        panelChoices.appendChild(li);
      });
    }

    function handleAnswer(choice) {
      const q = cellQuestions[selectedCell];
      if (choice === q.answer) {
        boardState[selectedCell] = currentPlayer;
        renderBoard();
        if (checkWin(currentPlayer)) return endGame(`${currentPlayer} wins!`);
        if (boardState.every(c => c)) return endGame('Stalemate!');
        currentPlayer = currentPlayer === 'X' ? 'O' : 'X';
        statusEl.innerText = `Current: ${currentPlayer}`;
        if (mode === 'solo' && currentPlayer === 'O') {
          setTimeout(aiMove, 450);
        }
      } else {
        alert('Incorrect! Turn missed.');
        currentPlayer = currentPlayer === 'X' ? 'O' : 'X';
        statusEl.innerText = `Current: ${currentPlayer}`;
        if (mode === 'solo' && currentPlayer === 'O') {
          setTimeout(aiMove, 450);
        }
      }
    }

    function checkWin(player) {
      const wins = [
        [0,1,2],[3,4,5],[6,7,8],
        [0,3,6],[1,4,7],[2,5,8],
        [0,4,8],[2,4,6]
      ];
      return wins.some(combo => combo.every(i => boardState[i] === player));
    }

    function endGame(msg) {
      bannerEl.innerText = `🎉 ${msg}`;
      bannerEl.classList.remove('hidden');
      document.querySelectorAll('#board button').forEach(b => b.disabled = true);
    }

    function aiMove() {
      const open = boardState.map((v,i) => v === '' ? i : null).filter(v => v !== null);
      if (open.length === 0) return;
      const pick = open[Math.floor(Math.random() * open.length)];
      const q = cellQuestions[pick];
      const aiCorrect = Math.random() < 0.55;
      if (aiCorrect) {
        boardState[pick] = 'O';
        renderBoard();
        if (checkWin('O')) return endGame('O wins!');
        if (boardState.every(c => c)) return endGame('Stalemate!');
        currentPlayer = 'X';
        statusEl.innerText = `Current: ${currentPlayer}`;
      } else {
        currentPlayer = 'X';
        statusEl.innerText = `Current: ${currentPlayer}`;
      }
    }

    hintBtn.addEventListener('click', () => panelHint.classList.toggle('hidden'));
    clearBtn.addEventListener('click', () => {
      panelQuestion.innerText = 'Select a square to view the question';
      panelChoices.innerHTML = '';
      panelHint.classList.add('hidden');
      panelHint.innerText = '';
    });
    restartBtn.addEventListener('click', initGame);
    modeSel.addEventListener('change', initGame);
    initGame();
  </script>
</body>
</html>