MachineLearningAlgorithms / templates /decision_tree_game.html
deedrop1140's picture
Upload 137 files
f7c7e26 verified
{% extends "layout.html" %}
{% block content %}<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Clever Critter Classifier</title>
<script src="https://cdn.tailwindcss.com"></script>
<style>
/* Custom styles to enhance attractiveness and functionality */
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background-color: #e2e8f0; /* Light blue-gray background */
display: block;
flex-direction: column;
align-items: center;
justify-content: center;
max-height: 100vh;
padding: 20px;
overflow-x: hidden; /* Prevent horizontal scroll */
}
.main-wrapper {
max-width: 1400px; /* Wider to accommodate 8 nodes in level 4 */
width: 100%;
display: flex;
flex-direction: column;
gap: 2rem;
}
.game-container {
background-color: #ffffff;
padding: 2.5rem;
border-radius: 1rem;
box-shadow: 0 10px 15px rgba(0, 0, 0, 0.1);
text-align: center;
position: relative;
}
h1 {
font-size: 3rem;
font-weight: 800;
margin-bottom: 1rem;
color: #1a202c;
letter-spacing: -0.05em;
}
.tagline {
font-size: 1.25rem;
color: #4a5568;
margin-bottom: 2rem;
font-style: italic;
}
.tree-container {
position: relative;
margin-top: 3rem;
min-height: 850px; /* Adjusted height for 4 levels + spacing */
display: flex;
flex-direction: column;
align-items: center;
padding-top: 20px;
}
#tree-svg {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none; /* Allows clicks to pass through to nodes */
overflow: visible;
}
.tree-row {
display: flex;
justify-content: center;
width: 100%;
margin-bottom: 7rem; /* Spacing between levels */
position: relative; /* For z-index stacking */
z-index: 1; /* Ensure nodes are above lines */
}
.tree-node {
background-color: #ebf8ff; /* Tailwind blue-100 */
border: 2px solid #63b3ed; /* Tailwind blue-400 */
border-radius: 0.75rem;
padding: 0.8rem 0.8rem; /* Reduced padding */
margin: 0 1rem; /* Reduced space between sibling nodes */
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
transition: all 0.4s ease-in-out, transform 0.2s ease-out, opacity 0.4s ease-in-out; /* Smooth transitions */
width: 220px; /* Reduced fixed width */
min-height: 110px; /* Reduced min height */
display: flex;
flex-direction: column;
justify-content: center; /* Center content vertically */
align-items: center; /* Center content horizontally */
position: relative;
opacity: 0.3; /* Faded by default */
pointer-events: none; /* Not clickable by default */
text-align: center; /* Ensure text is centered */
}
.tree-node.active {
border-color: #3182ce; /* Tailwind blue-600 */
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.25);
background-color: #bee3f8; /* Tailwind blue-200 */
opacity: 1; /* Fully visible when active */
pointer-events: all; /* Clickable when active */
transform: translateY(-5px); /* Subtle lift */
z-index: 2; /* Bring active node slightly to front */
}
.tree-node.path-taken {
border-color: #90cdf4; /* Lighter blue for path */
background-color: #e0f2fe; /* Even lighter blue */
opacity: 0.7; /* Still visible but not as prominent as active */
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.08);
pointer-events: none; /* Not clickable once passed */
transform: translateY(0); /* Reset lift */
z-index: 1;
}
.root-node {
background-color: #38a169; /* Darker green for root */
border-color: #2f855a; /* Even darker green */
color: white;
padding: 1.2rem; /* Adjusted padding for root */
font-size: 1.1rem; /* Adjusted font size for root */
opacity: 1; /* Root is always fully visible */
pointer-events: all; /* Root is always clickable (its buttons) */
width: 260px; /* Slightly wider root node for distinction */
min-height: 120px; /* Slightly taller root node */
}
.root-node.active {
background-color: #2f855a;
}
.leaf-node {
background-color: #d6eaff; /* Light blue for leaves, softer than green */
border-color: #63b3ed;
opacity: 0.3; /* Faded by default */
pointer-events: none; /* Not clickable by default */
}
.leaf-node.active {
background-color: #a7d3ff;
box-shadow: 0 0 20px rgba(99, 179, 237, 0.7); /* Glow effect for leaf */
transform: scale(1.05); /* Slightly pop out */
opacity: 1; /* Fully visible when reached */
pointer-events: none; /* Not clickable once reached */
}
.node-question {
font-size: 1.1rem; /* Adjusted font size for internal nodes */
font-weight: 700;
margin-bottom: 0.8rem; /* Adjusted margin */
color: inherit;
}
.classification-text {
font-size: 1.5rem; /* Adjusted classification text size */
font-weight: bold;
color: #2f855a;
padding: 0.4rem; /* Adjusted padding for leaf node text */
}
.node-buttons {
display: flex;
justify-content: center;
gap: 0.6rem; /* Adjusted gap */
flex-wrap: wrap;
margin-top: auto; /* Push buttons to bottom if node grows */
}
.node-button {
background-color: #4299e1;
color: white;
padding: 0.6rem 1.2rem; /* Adjusted padding */
border-radius: 0.5rem;
font-weight: 700;
cursor: pointer;
border: none;
transition: background-color 0.2s ease-in-out, transform 0.1s ease-in-out, box-shadow 0.2s ease-in-out;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
font-size: 0.9rem; /* Adjusted font size */
}
.node-button:hover:not(:disabled) {
background-color: #3182ce;
transform: translateY(-2px);
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.15);
}
.node-button:active:not(:disabled) {
transform: translateY(0);
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.node-button:disabled {
background-color: #a0aec0;
cursor: not-allowed;
opacity: 0.7;
transform: none;
box-shadow: none;
}
/* --- Line Styling --- */
.connector-line {
stroke: #a0aec0; /* Gray for inactive lines */
stroke-width: 3;
transition: stroke 0.4s ease-in-out, stroke-width 0.4s ease-in-out, opacity 0.4s ease-in-out;
}
.connector-line.active-path {
stroke: #3182ce !important; /* Deeper blue for active path */
stroke-width: 5 !important; /* Thicker line */
opacity: 1 !important; /* Fully visible */
}
.connector-line.inactive-path {
opacity: 0.3; /* Faded lines for unchosen paths */
stroke-width: 2; /* Thinner lines */
}
/* --- Control Buttons --- */
.reset-button {
background-color: #e53e3e; /* Tailwind red-600 */
color: white;
padding: 0.9rem 2rem;
border-radius: 0.5rem;
font-weight: 700;
cursor: pointer;
border: none;
transition: background-color 0.2s ease-in-out, transform 0.1s ease-in-out, box-shadow 0.2s ease-in-out;
margin-top: 2rem;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}
.reset-button:hover {
background-color: #c53030;
transform: translateY(-2px);
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.15);
}
.reset-button:active {
transform: translateY(0);
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.llm-explanation-container {
margin-top: 2rem;
background-color: #f0f8ff;
border: 1px solid #a7d3ff;
padding: 2rem;
border-radius: 0.75rem;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.08);
text-align: left;
}
.llm-explanation-container h4 {
font-size: 1.25rem;
font-weight: bold;
margin-bottom: 1rem;
color: #2c5282;
}
.llm-explanation-container p {
font-size: 1.05rem;
line-height: 1.7;
color: #4a5568;
}
.loading-indicator {
text-align: center;
font-style: italic;
color: #718096;
padding: 1rem;
}
/* Confetti animation (simple CSS based) */
.confetti-container {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
overflow: hidden;
pointer-events: none;
z-index: 10;
}
.confetti {
position: absolute;
width: 10px;
height: 10px;
background-color: #f06; /* Pink */
opacity: 0;
animation: fall 3s ease-out forwards;
}
.confetti:nth-child(2n) { background-color: #6cf; /* Blue */ }
.confetti:nth-child(3n) { background-color: #ffc; /* Yellow */ }
.confetti:nth-child(4n) { background-color: #9c9; /* Green */ }
.confetti:nth-child(5n) { background-color: #f60; /* Orange */ }
@keyframes fall {
0% { transform: translateY(0) rotateZ(0deg); opacity: 1; }
100% { transform: translateY(100vh) rotateZ(720deg); opacity: 0; }
}
/* Media Queries for Responsiveness */
@media (max-width: 768px) {
h1 {
font-size: 2rem;
}
.tagline {
font-size: 1rem;
}
.tree-node {
width: 90%; /* Keep width responsive on small screens */
min-height: unset; /* Remove min-height for mobile to avoid overflow */
margin: 1rem auto;
padding: 1rem;
}
.tree-row {
flex-direction: column;
align-items: center;
margin-bottom: 3rem;
}
.node-buttons {
flex-direction: column;
gap: 0.75rem;
}
.node-button {
width: 100%;
padding: 0.75rem 1rem;
}
.root-node {
width: 90%; /* Responsive root */
min-height: unset; /* Remove min-height for mobile */
padding: 1.5rem;
}
}
</style>
</head>
<body>
<div class="main-wrapper">
<div class="game-container">
<h1 class="text-gray-900">🐾 Clever Critter Classifier! 🌿</h1>
<p class="tagline text-gray-700">
Think of your favorite animal, answer wisely, and watch the tree reveal its identity!
</p>
<div class="tree-container" id="game-tree-container">
<svg id="tree-svg"></svg>
<div class="tree-row level-1">
<div id="node-root" class="tree-node root-node">
<p class="node-question"></p>
<div id="buttons-root" class="node-buttons"></div>
</div>
</div>
<div class="tree-row level-2">
<div id="node-land-branch" class="tree-node internal-node">
<p class="node-question"></p>
<div id="buttons-land-branch" class="node-buttons"></div>
</div>
<div id="node-water-branch" class="tree-node internal-node">
<p class="node-question"></p>
<div id="buttons-water-branch" class="node-buttons"></div>
</div>
</div>
<div class="tree-row level-3">
<div id="node-fur-branch" class="tree-node internal-node">
<p class="node-question"></p>
<div id="buttons-fur-branch" class="node-buttons"></div>
</div>
<div id="node-no-fur-branch" class="tree-node internal-node">
<p class="node-question"></p>
<div id="buttons-no-fur-branch" class="node-buttons"></div>
</div>
<div id="node-water-mammal-branch" class="tree-node internal-node">
<p class="node-question"></p>
<div id="buttons-water-mammal-branch" class="node-buttons"></div>
</div>
<div id="node-water-non-mammal-branch" class="tree-node internal-node">
<p class="node-question"></p>
<div id="buttons-water-non-mammal-branch" class="node-buttons"></div>
</div>
</div>
<div class="tree-row level-4">
<div id="node-leaf-lion" class="tree-node leaf-node">
<p class="classification-text"></p>
</div>
<div id="node-leaf-cat" class="tree-node leaf-node">
<p class="classification-text"></p>
</div>
<div id="node-leaf-eagle" class="tree-node leaf-node">
<p class="classification-text"></p>
</div>
<div id="node-leaf-snake" class="tree-node leaf-node">
<p class="classification-text"></p>
</div>
<div id="node-leaf-dolphin" class="tree-node leaf-node">
<p class="classification-text"></p>
</div>
<div id="node-leaf-whale" class="tree-node leaf-node">
<p class="classification-text"></p>
</div>
<div id="node-leaf-fish" class="tree-node leaf-node">
<p class="classification-text"></p>
</div>
<div id="node-leaf-octopus" class="tree-node leaf-node">
<p class="classification-text"></p>
</div>
</div>
</div>
<button id="explain-classification-button" class="node-button mt-6 hidden">✨ Deep Dive into Classification ✨</button>
<div id="llm-explanation-container" class="llm-explanation-container hidden">
<div id="llm-explanation-box" class="llm-explanation-box"></div>
</div>
<button id="reset-button" class="reset-button hidden">Start Over</button>
</div>
<div class="mt-8 p-6 bg-blue-50 rounded-lg border border-blue-200 shadow-md">
<h3 class="text-xl font-extrabold mb-4 text-center text-blue-800">Understanding Decision Trees</h3>
<p class="text-gray-700 leading-relaxed mb-4">
This game is a simple, fun way to explore how a **Decision Tree** algorithm works in Machine Learning. Imagine a flowchart that helps make decisions!
</p>
<ul class="list-disc list-inside text-gray-800 space-y-2">
<li>
<strong>🌳 Root Node (Top):</strong> This is your starting point, like the first big question everyone asks. In real Decision Trees, this node considers all possible data and picks the best question to split it.
</li>
<li>
<strong>🌿 Internal Nodes (Middle Questions):</strong> These are where you make choices based on features (like "Does it have fur?"). Each answer leads you down a specific branch, getting you closer to a final decision.
</li>
<li>
<strong>🌸 Leaf Nodes (Bottom - Classifications):</strong> You've reached the end of a path! These are the final answers or predictions. In machine learning, this could be classifying an email as "Spam" or predicting if a customer will "Churn."
</li>
<li>
<strong>🔗 Paths (Decision Rules):</strong> The sequence of choices you make from the root to a leaf node creates a unique "rule." This rule can then be applied to new, unseen data to make a prediction!
</li>
</ul>
<p class="mt-4 text-gray-700 leading-relaxed">
Just like your choices guide you through this game, Decision Trees learn these question-and-answer paths from large datasets to classify new information.
</p>
</div>
</div>
<script>
const treeData = {
'root': {
question: 'Does your favorite animal live on land or in water?',
options: [
{ text: 'Land 🏞️', next: 'land-branch', value: 'land' },
{ text: 'Water 🌊', next: 'water-branch', value: 'water' }
],
level: 1
},
'land-branch': {
question: 'Does your favorite animal have fur?',
options: [
{ text: 'Yes 👍', next: 'fur-branch', value: 'fur' },
{ text: 'No 👎', next: 'no-fur-branch', value: 'no_fur' }
],
level: 2
},
'water-branch': {
question: 'Is your favorite animal a mammal?',
options: [
{ text: 'Yes 👍', next: 'water-mammal-branch', value: 'mammal' },
{ text: 'No 👎', next: 'water-non-mammal-branch', value: 'not_mammal' }
],
level: 2
},
'fur-branch': {
question: 'Is your favorite animal large?',
options: [
{ text: 'Yes 👍', next: 'leaf-lion', value: 'large' },
{ text: 'No 👎', next: 'leaf-cat', value: 'small' }
],
level: 3
},
'no-fur-branch': {
question: 'Can it fly?',
options: [
{ text: 'Yes 👍', next: 'leaf-eagle', value: 'flies' },
{ text: 'No 👎', next: 'leaf-snake', value: 'no_fly' }
],
level: 3
},
'water-mammal-branch': {
question: 'Is it found in saltwater or freshwater?',
options: [
{ text: 'Saltwater 🧂', next: 'leaf-dolphin', value: 'saltwater' },
{ text: 'Freshwater 💧', next: 'leaf-whale', value: 'freshwater_mammal' } // Example: Whale if freshwater
],
level: 3
},
'water-non-mammal-branch': {
question: 'Does it have scales?',
options: [
{ text: 'Yes ✅', next: 'leaf-fish', value: 'scales' },
{ text: 'No ❌', next: 'leaf-octopus', value: 'no_scales' } // Example: Octopus if no scales
],
level: 3
},
// Leaf Nodes (classifications) - all are now Level 4
'leaf-lion': { classification: 'Lion 🦁', explanation: 'Lions are large, furry land animals. They fit the criteria for this classification because they reside on land, possess fur, and are generally considered large mammals.', level: 4 },
'leaf-cat': { classification: 'Cat 🐈', explanation: 'Cats are small, furry land animals. This classification is reached as cats live on land, have fur, and are small in size.', level: 4 },
'leaf-eagle': { classification: 'Eagle 🦅', explanation: 'Eagles are non-furry land animals that can fly. They are classified here because they inhabit land, lack fur, and are characterized by their ability to fly.', level: 4 },
'leaf-snake': { classification: 'Snake 🐍', explanation: 'Snakes are non-furry land animals that cannot fly. This classification applies to snakes as they live on land, do not have fur, and are unable to fly.', level: 4 },
'leaf-dolphin': { classification: 'Dolphin 🐬', explanation: 'Dolphins are water mammals found in saltwater. They are classified here because they live in water, are mammals, and inhabit saltwater environments.', level: 4 },
'leaf-whale': { classification: 'Whale 🐳', explanation: 'Whales are typically large water mammals. This classification applies to water animals that are mammals and are very large.', level: 4 },
'leaf-fish': { classification: 'Fish 🐠', explanation: 'Fish are aquatic animals, often with scales, found in various water bodies. This classification is given to non-mammalian water creatures with scales.', level: 4 },
'leaf-octopus': { classification: 'Octopus 🐙', explanation: 'Octopuses are aquatic animals without scales. This classification is for water non-mammals that do not have scales.', level: 4 }
};
let currentNodeLogicalId = 'root';
let currentPath = []; // Stores logical IDs of nodes in the current path
let lastClassificationNode = null; // Stores the logical ID of the last classified leaf node
// --- Helper Functions for DOM Manipulation and Tree Drawing ---
function getElementById(id) {
return document.getElementById(id);
}
function clearNodeButtons(nodeId) {
const buttonsContainer = getElementById(`buttons-${nodeId}`);
if (buttonsContainer) {
buttonsContainer.innerHTML = '';
}
}
function drawLine(startX, startY, endX, endY, id, is_active_path = false) {
const svg = getElementById('tree-svg');
let line = getElementById(id);
if (!line) {
line = document.createElementNS("http://www.w3.org/2000/svg", "line");
line.setAttribute('id', id);
line.setAttribute('class', 'connector-line');
svg.appendChild(line);
}
line.setAttribute('x1', startX);
line.setAttribute('y1', startY);
line.setAttribute('x2', endX);
line.setAttribute('y2', endY);
// Apply classes based on active path
if (is_active_path) {
line.classList.add('active-path');
line.classList.remove('inactive-path');
} else {
line.classList.remove('active-path');
line.classList.add('inactive-path');
}
}
// Gets absolute position relative to the SVG container (center bottom for parent, center top for child)
function getAbsolutePosition(element, type = 'bottom') {
if (!element) return { x: 0, y: 0, width: 0, height: 0 };
const rect = element.getBoundingClientRect();
const svg = getElementById('tree-svg');
const svgRect = svg.getBoundingClientRect();
let x = rect.left - svgRect.left + rect.width / 2;
let y;
if (type === 'bottom') {
y = rect.top - svgRect.top + rect.height; // Bottom center
} else { // 'top'
y = rect.top - svgRect.top; // Top center
}
return { x, y, width: rect.width, height: rect.height };
}
// Function to draw and update all lines based on the current path
function drawAllLines() {
const svg = getElementById('tree-svg');
// Clear existing lines to redraw all
while (svg.firstChild) {
svg.removeChild(svg.firstChild);
}
// Iterate through all possible connections in treeData
for (const parentLogicalId in treeData) {
const parentData = treeData[parentLogicalId];
if (parentData.options) { // If it's a node with options (not a leaf)
parentData.options.forEach(option => {
const childLogicalId = option.next;
const parentNode = getElementById(`node-${parentLogicalId}`);
const childNode = getElementById(`node-${childLogicalId}`);
if (parentNode && childNode) {
const parentPos = getAbsolutePosition(parentNode, 'bottom');
const childPos = getAbsolutePosition(childNode, 'top');
const lineId = `line-${parentLogicalId}-to-${childLogicalId}`;
// Check if this connection is part of the current active path
const isPathSegment = currentPath.includes(parentLogicalId) && currentPath.includes(childLogicalId);
drawLine(parentPos.x, parentPos.y, childPos.x, childPos.y, lineId, isPathSegment);
}
});
}
}
}
// Confetti effect
function launchConfetti() {
const confettiContainer = document.createElement('div');
confettiContainer.classList.add('confetti-container');
getElementById('game-tree-container').appendChild(confettiContainer);
for (let i = 0; i < 50; i++) {
const confetti = document.createElement('div');
confetti.classList.add('confetti');
confetti.style.left = `${Math.random() * 100}%`;
confetti.style.backgroundColor = `hsl(${Math.random() * 360}, 70%, 60%)`;
confetti.style.animationDelay = `${Math.random() * 0.5}s`;
confetti.style.animationDuration = `${3 + Math.random() * 2}s`;
confettiContainer.appendChild(confetti);
}
// Remove confetti after animation
setTimeout(() => {
confettiContainer.remove();
}, 5000);
}
// --- Game Logic Functions ---
function initializeNode(nodeLogicalId) {
const nodeData = treeData[nodeLogicalId];
const nodeElement = getElementById(`node-${nodeLogicalId}`);
const buttonsContainer = getElementById(`buttons-${nodeLogicalId}`);
if (!nodeElement || !nodeData) {
console.error(`Node element or data not found for ID: ${nodeLogicalId}`);
return;
}
// Reset all nodes to inactive/faded state first, and clear all buttons
document.querySelectorAll('.tree-node').forEach(node => {
node.classList.remove('active', 'path-taken');
// Set initial opacity and pointer-events for all nodes
node.style.opacity = '0.3';
node.style.pointerEvents = 'none';
clearNodeButtons(node.id.replace('node-', ''));
});
// Set 'path-taken' for all nodes in the current path (except the current active one)
currentPath.forEach(pathId => {
const pathNodeElement = getElementById(`node-${pathId}`);
if (pathNodeElement && pathId !== nodeLogicalId) {
pathNodeElement.classList.add('path-taken');
pathNodeElement.style.opacity = '0.7'; // Less faded
pathNodeElement.style.pointerEvents = 'none'; // Still not clickable
}
});
// Set content and activate the current node
nodeElement.classList.add('active');
nodeElement.style.opacity = '1';
nodeElement.style.pointerEvents = 'all';
if (nodeData.classification) { // It's a leaf node
nodeElement.querySelector('.classification-text').textContent = nodeData.classification;
clearNodeButtons(nodeLogicalId);
lastClassificationNode = nodeLogicalId;
getElementById('explain-classification-button').classList.remove('hidden');
getElementById('reset-button').classList.remove('hidden');
launchConfetti(); // Yay, classification!
} else { // It's an internal/root node
const questionElement = nodeElement.querySelector('.node-question');
questionElement.textContent = nodeData.question;
buttonsContainer.innerHTML = ''; // Clear previous buttons
nodeData.options.forEach(option => {
const button = document.createElement('button');
button.classList.add('node-button');
button.textContent = option.text;
button.onclick = () => chooseOption(nodeLogicalId, option.next);
buttonsContainer.appendChild(button);
});
getElementById('explain-classification-button').classList.add('hidden');
getElementById('llm-explanation-container').classList.add('hidden');
getElementById('reset-button').classList.add('hidden');
}
// Always redraw all lines to reflect current path highlight
drawAllLines();
}
function chooseOption(parentNodeLogicalId, nextNodeLogicalId) {
// Add current node to path if not already there (prevents duplicates on restart)
if (!currentPath.includes(parentNodeLogicalId)) {
currentPath.push(parentNodeLogicalId);
}
currentPath.push(nextNodeLogicalId);
currentNodeLogicalId = nextNodeLogicalId;
// Initialize the next node (which will apply active/path-taken styles and redraw lines)
initializeNode(nextNodeLogicalId);
}
function startGame() {
// Reset game state
currentNodeLogicalId = 'root';
currentPath = [];
lastClassificationNode = null;
// Initialize all nodes with their content but set to faded/inactive state initially
for (const id in treeData) {
const nodeData = treeData[id];
const nodeElement = getElementById(`node-${id}`);
if (nodeElement) {
// Populate questions/classifications for all nodes first
if (nodeData.classification) {
const classificationText = nodeElement.querySelector('.classification-text');
if (classificationText) classificationText.textContent = nodeData.classification;
} else if (nodeData.question) {
const questionText = nodeElement.querySelector('.node-question');
if (questionText) questionText.textContent = nodeData.question;
}
// Reset styling
nodeElement.classList.remove('active', 'path-taken');
nodeElement.style.opacity = '0.3';
nodeElement.style.pointerEvents = 'none';
clearNodeButtons(nodeElement.id.replace('node-', '')); // Corrected this line to pass proper ID
}
}
// Activate the root node to start the game
initializeNode(currentNodeLogicalId);
}
// Function to explain classification using LLM (placeholder for now)
async function explainClassification() {
const explanationContainer = getElementById('llm-explanation-container');
const explanationBox = getElementById('llm-explanation-box');
explanationContainer.classList.remove('hidden');
explanationBox.innerHTML = '<p class="loading-indicator">Loading explanation...</p>';
if (lastClassificationNode && treeData[lastClassificationNode]) {
const animalName = treeData[lastClassificationNode].classification.split(' ')[0]; // Get just the name
const explanationText = treeData[lastClassificationNode].explanation;
// Simulate LLM response delay
setTimeout(() => {
explanationBox.innerHTML = `
<h4>Why ${animalName}?</h4>
<p>${explanationText}</p>
`;
}, 1000); // 1 second delay
} else {
explanationBox.innerHTML = '<p class="text-red-500">No classification to explain yet. Please play the game!</p>';
}
}
// --- Event Listeners ---
window.onload = () => {
startGame();
// Re-draw lines on window resize to adjust for responsive layout changes
window.addEventListener('resize', drawAllLines);
};
getElementById('reset-button').addEventListener('click', startGame);
getElementById('explain-classification-button').addEventListener('click', explainClassification);
</script>
<div class="back-button">
<a href="/decision_tree" class=" bg-gray-200 center hover:bg-gray-300 text-black-800 px-4 py-2 rounded shadow">
← Back to decision tree
</a>
</div>
</body>
</html>
{% endblock %}