zeroed-some/bashamole / 1d765cf

Browse files

refactor; new mole; stable

Authored by mfwolffe <wolffemf@dukes.jmu.edu>
SHA
1d765cf63964d7c81a1c97ab8a34e7ceee2c44c3
Parents
2edb0eb
Tree
fc662f5

3 changed files

StatusFile+-
M frontend/public/mole.svg 8 11
M frontend/src/components/Game.tsx 23 9
M frontend/src/components/TreeVisualizer.tsx 137 74
frontend/public/mole.svgmodified
@@ -1,11 +1,8 @@
1
-<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
2
-  <ellipse cx="12" cy="14" rx="6" ry="5" fill="#8B4513"/>
3
-  <circle cx="12" cy="13" r="4" fill="#D2691E"/>
4
-  <circle cx="10" cy="12" r="1" fill="#000000"/>
5
-  <circle cx="14" cy="12" r="1" fill="#000000"/>
6
-  <ellipse cx="12" cy="15" rx="1.5" ry="1" fill="#FFB6C1"/>
7
-  <path d="M6 13C6 13 5 12 4 12" stroke="#000000" stroke-width="0.5" stroke-linecap="round"/>
8
-  <path d="M18 13C18 13 19 12 20 12" stroke="#000000" stroke-width="0.5" stroke-linecap="round"/>
9
-  <circle cx="8" cy="17" r="2" fill="#8B4513"/>
10
-  <circle cx="16" cy="17" r="2" fill="#8B4513"/>
11
-</svg>
1
+<?xml version="1.0" encoding="UTF-8"?>
2
+<svg width="1200pt" height="1200pt" version="1.1" viewBox="0 0 1200 1200" xmlns="http://www.w3.org/2000/svg">
3
+ <path d="m119.28 874.44c0.81641 28.594 11.742 55.977 30.84 77.277-4.082 14.672-3.0234 30.297 3 44.281-1.7578 2.8789-3.207 5.9375-4.3203 9.1211-8.5156 26.387-5.0977 55.18 9.3594 78.84 2.2031 3.4336 6 5.5117 10.078 5.5195 1.1953 0.17969 2.4102 0.17969 3.6016 0 18.086-6.1406 33.758-17.863 44.762-33.48 4.8008 2.6406 9.8398 5.2812 15.238 7.8008-0.67188 18.098 4.1836 35.969 13.922 51.238 2.2031 3.4336 6 5.5156 10.078 5.5195 1.1914 0.18359 2.4062 0.18359 3.6016 0 17.273-5.6328 32.426-16.387 43.438-30.84 5.6406 1.4414 11.281 2.8789 16.559 3.9609h0.003906c-1.1172 18.824 3.7539 37.516 13.918 53.398 2.2031 3.4375 6 5.5156 10.082 5.5234 1.1914 0.17969 2.4062 0.17969 3.5977 0 26.18-9.125 47.137-29.121 57.48-54.84 1.0391-3.168 1.8047-6.4219 2.2812-9.7227 5.5-2.8594 10.586-6.4531 15.121-10.68 10.801 1.9219 21.719 3.7188 32.879 5.2812l5.3984 0.71875c12.48 1.6797 25.078 3.1211 37.801 4.3203l10.199 0.96094c12 0.96094 23.16 1.8008 35.039 2.3984l11.16 0.60156c15.121 0 30.238 1.0781 45.602 1.0781 15.359 0 30.48 0 45.602-1.0781l11.16-0.60156c12 0 24-1.4414 35.039-2.3984l10.199-0.96094c12.719-1.1992 25.32-2.6406 37.801-4.3203l5.3984-0.71875c11.16-1.5586 22.078-3.3594 32.879-5.2812 4.5352 4.2266 9.6211 7.8203 15.121 10.68 0.47656 3.3008 1.2422 6.5547 2.2812 9.7227 10.484 25.492 31.422 45.25 57.48 54.238 1.1914 0.17969 2.4062 0.17969 3.5977 0 4.082-0.007812 7.8789-2.0859 10.082-5.5195 10.172-15.926 15.043-34.66 13.918-53.52 5.2812-0.96094 10.922-2.3984 16.559-3.8398h0.003906c11.012 14.449 26.164 25.207 43.438 30.84 1.1953 0.17969 2.4102 0.17969 3.6016 0 4.0781-0.007813 7.875-2.0859 10.078-5.5195 9.7383-15.27 14.594-33.145 13.922-51.242 5.3984-2.5195 10.441-5.1602 15.238-7.8008 11.168 15.215 26.82 26.547 44.762 32.402 1.1914 0.17969 2.4062 0.17969 3.6016 0 4.0781-0.007812 7.875-2.0859 10.078-5.5195 14.457-23.664 17.875-52.453 9.3594-78.84-1.0898-3.1953-2.5391-6.2539-4.3203-9.1211 5.9141-13.844 6.9727-29.281 3-43.801 19.098-21.301 30.023-48.684 30.84-77.281 0-66.359-64.078-127.44-175.8-168l0.003906-65.52 61.801 36h-0.003906c1.8203 1.0781 3.8867 1.6602 6 1.6797 5.4648 0.039062 10.266-3.6172 11.68-8.8984 1.4102-5.2812-0.92578-10.848-5.6797-13.539l-73.797-42.961v-87.961h115.08c6.6289 0 12-5.3711 12-12 0-6.625-5.3711-12-12-12h-115.08v-79.438l72.84-33.238v-0.003906c6.0273-2.75 8.6875-9.8672 5.9375-15.898s-9.8672-8.6914-15.898-5.9414l-62.879 28.68v-41.039c0-80.871-32.129-158.43-89.312-215.61-57.184-57.184-134.74-89.312-215.61-89.312s-158.43 32.129-215.61 89.312c-57.184 57.184-89.312 134.74-89.312 215.61v41.039l-62.879-28.68c-6.0312-2.75-13.148-0.089843-15.898 5.9414s-0.089843 13.148 5.9375 15.898l72.84 33.238v79.441h-115.08c-6.6289 0-12 5.375-12 12 0 6.6289 5.3711 12 12 12h114.96v87.961l-73.801 42.719c-4.7539 2.6953-7.0898 8.2617-5.6758 13.543 1.4102 5.2773 6.2109 8.9375 11.676 8.8984 2.1133-0.023437 4.1836-0.60156 6-1.6797l61.801-36v66.359c-111.6 41.16-175.68 102.24-175.68 168.6zm24 0c0-53.398 56.398-106.08 151.8-142.68l-0.003906 150.6c-23.945-4.0508-48.473-3.0312-72 3-24.824 5.8164-46.672 20.496-61.438 41.281-11.363-15.086-17.777-33.32-18.359-52.199zm63.48 150.96v-0.003906c-6.6602 15.398-18.332 28.086-33.121 36-6.332-15.66-7.1367-33.016-2.2812-49.199 0.60156-1.8008 6.2383-16.199 18.719-16.199 2.2539 0.023438 4.4844 0.42578 6.6016 1.1992 16.441 6.4805 11.281 24.48 10.082 28.199zm87.48 31.559h-0.003907c-6.5352 15.434-18.191 28.145-33 36-6.3398-15.664-7.1445-33.02-2.2773-49.199 1.0781-3 6.7188-16.199 18.719-16.199v-0.003906c2.2539 0.023437 4.4844 0.42969 6.6016 1.2031 16.559 6.3594 11.277 24.477 9.957 28.199zm87.602 31.441-0.003906-0.003906c-6.5938 15.398-18.234 28.094-33 36-6.3438-15.617-7.1484-32.938-2.2812-49.078 1.0781-3.1211 6.7188-16.32 18.719-16.32h0.003906c2.25 0.023438 4.4844 0.42578 6.5977 1.1992 16.441 6.4805 11.281 24.48 9.9609 28.199zm218.16-20.402c-54.512 0.058594-108.93-4.3555-162.72-13.199 0.96094-2.0391 2.0391-4.0781 2.8789-6.2383v-0.003906c7.2305-21.223 6.2461-44.387-2.7617-64.918 25.562-88.32 89.16-151.2 162.6-151.2s137.04 62.879 162.6 150.72c-9.0078 20.531-9.9922 43.695-2.7617 64.918 0.83984 2.1602 1.9219 4.1992 2.8789 6.2383v0.003906c-53.773 9.0039-108.2 13.582-162.72 13.68zm251.16 57.359c-14.766-7.9062-26.406-20.602-33-36-1.3203-3.7188-6.4805-21.719 9.9609-27.719s24 12 25.32 15c4.8164 15.863 4.0078 32.902-2.2812 48.238zm87.602-31.559c-14.781-7.8945-26.422-20.594-33-36-1.3203-3.8398-6.4805-21.84 9.9609-27.719 16.441-5.8789 24 12 25.32 15l-0.003907-0.003906c4.8164 15.863 4.0117 32.902-2.2773 48.242zm31.922-60h-0.003906c0.070312 0.51562 0.070312 1.043 0 1.5586l-6.8398 3.6016c-0.20703-0.90234-0.48828-1.7852-0.83984-2.6406-2.6797-7.1367-6.7578-13.664-12-19.199-5.3945-5.9961-12.5-10.188-20.355-12.012s-16.082-1.1914-23.566 1.8125c-11.059 4.375-19.863 13.059-24.391 24.059s-4.3828 23.367 0.39062 34.258l1.0781 2.6406-7.5586 1.5586 0.003906 0.003906c-0.16797-0.49609-0.36719-0.97656-0.60156-1.4414-2.7461-7.1055-6.8164-13.621-12-19.199-5.4219-5.9922-12.551-10.18-20.422-12.004-7.8711-1.8203-16.113-1.1914-23.617 1.8047-10.707 3.3945-19.445 11.215-24 21.477-6.3281-5.1602-11.102-11.973-13.801-19.68-7.1133-23.73-1.9883-49.445 13.68-68.637 19.871-26.266 47.168-45.957 78.359-56.523 19.234-7.0234 39.523-10.719 60-10.918 12.156-0.046874 24.262 1.5273 36 4.6797 24.277 4.8125 44.617 21.289 54.359 44.039 2.7891 7.6836 3.4102 15.988 1.8008 24-9.9805-4.9023-21.566-5.3828-31.918-1.3203-10.875 4.457-19.512 13.082-23.977 23.953s-4.3867 23.074 0.21484 33.887zm55.68 28.32h-0.003906c-14.789-7.918-26.461-20.605-33.121-36-1.1992-3.7188-6.3594-21.719 10.078-27.719 16.441-6 24 12 25.32 15l0.003906-0.003906c4.6875 15.961 3.8867 33.031-2.2812 48.48zm-121.44-330.12c96 36.602 151.8 89.281 151.8 142.68h-0.003906c-0.47656 18.84-6.7617 37.07-18 52.199-14.766-20.785-36.613-35.465-61.438-41.277-23.527-6.0312-48.055-7.0547-72-3zm-585.84-106.2 76.922-44.879c5.4258-3.4531 7.168-10.566 3.9531-16.133-3.2188-5.5703-10.254-7.6094-15.953-4.6289l-65.398 37.922v-74.043h68.16c6.625 0 12-5.3711 12-12 0-6.625-5.375-12-12-12h-67.684v-68.039l68.16 31.199c1.5898 0.69922 3.3047 1.0703 5.043 1.082 4.7148-0.011719 8.9844-2.7812 10.918-7.082 2.7031-6.0312 0.023437-13.113-6-15.84l-78.121-36v-52.199c0-74.504 29.598-145.96 82.281-198.64 52.684-52.684 124.14-82.281 198.64-82.281s145.96 29.598 198.64 82.281c52.684 52.684 82.281 124.14 82.281 198.64v51.961l-78.121 36c-6.0234 2.7266-8.7031 9.8086-6 15.84 1.9336 4.3008 6.2031 7.0703 10.918 7.0781 1.7383-0.011718 3.4531-0.37891 5.043-1.0781l68.16-30.961v68.52h-68.16c-6.6289 0-12 5.375-12 12 0 6.6289 5.3711 12 12 12h68.16v74.039l-64.922-38.398c-5.6992-2.9805-12.734-0.94141-15.953 4.6289-3.2148 5.5664-1.4727 12.68 3.9531 16.133l77.398 44.879v262.2c-4.8008 1.4414-9.7188 3-14.52 4.6797-4.8008 1.6797-8.7617 3.3594-13.078 5.2812v-111.36c0-6.6289-5.375-12-12-12-6.6289 0-12 5.3711-12 12v123.24c-19.547 11.555-36.695 26.746-50.52 44.762-32.402-87-99.961-146.16-179.28-146.16s-146.88 59.16-178.8 146.16c-22.531-28.855-52.965-50.535-87.598-62.402-4.8008-1.6797-9.7188-3.2383-14.52-4.6797zm6.3594 288.96h0.003906c31.191 10.566 58.488 30.254 78.359 56.52 15.668 19.191 20.793 44.906 13.68 68.641-2.6992 7.7031-7.4727 14.516-13.801 19.68-5.8477-12.375-17.391-21.098-30.891-23.348-13.496-2.25-27.242 2.2578-36.789 12.066-5.2344 5.7227-9.3086 12.406-12 19.68-0.24609 0.54297-0.44531 1.1055-0.60156 1.6797l-7.5586-1.6797 1.0781-2.6406h0.003906c4.7734-10.891 4.918-23.258 0.39062-34.258s-13.332-19.688-24.391-24.062c-7.4805-3.0391-15.715-3.6875-23.578-1.8633-7.8633 1.8281-14.969 6.0391-20.344 12.062-5.2422 5.5352-9.3203 12.066-12 19.203-0.35156 0.85547-0.63281 1.7383-0.83984 2.6367l-6.8398-3.6016v0.003907c0.12109-0.54688 0.32422-1.0742 0.60156-1.5586 4.7773-10.91 4.9219-23.297 0.39844-34.312-4.5234-11.02-13.332-19.727-24.398-24.129-10.355-4.0625-21.938-3.582-31.922 1.3203-1.6094-8.0156-0.98438-16.316 1.8008-24 9.7422-22.75 30.082-39.23 54.359-44.039 11.738-3.1523 23.844-4.7266 36-4.6836 20.219 0.20703 40.262 3.8164 59.281 10.684z"/>
4
+ <path d="m596.4 403.56c-4.5586 0-111.72 1.3203-111.72 72 0 26.641 13.68 82.68 33.359 134.64-6.5859 0.64844-13.238 0.20312-19.68-1.3203-34.922-8.6406-50.762-49.68-50.879-50.16-2.3203-6.2305-9.25-9.3984-15.48-7.0781s-9.3984 9.25-7.0781 15.48c0.83984 2.1602 20.16 53.16 67.441 65.039h-0.003906c11.449 2.7266 23.34 3.0547 34.922 0.96094 5.2812 12 10.801 24 16.441 34.559h-0.003906c-7.3086 12.992-9.7578 28.164-6.9023 42.797 2.8555 14.633 10.828 27.773 22.484 37.062 11.66 9.293 26.246 14.129 41.148 13.645 14.902-0.48047 29.145-6.2539 40.176-16.281 11.035-10.027 18.137-23.656 20.039-38.441 1.8984-14.789-1.5273-29.77-9.6641-42.262 5.2812-10.078 10.32-21 15.121-32.398 7.1445 1.7812 14.477 2.707 21.84 2.7578 6.6328-0.003906 13.238-0.8125 19.68-2.3984 47.281-12 66.602-62.879 67.441-65.039h-0.003906c2.3203-6.2305-0.84766-13.16-7.0781-15.48s-13.16 0.84766-15.48 7.0781c0 0-15.961 41.281-50.879 49.922-8.6953 1.9258-17.707 1.9258-26.402 0 19.441-51.359 32.762-106.8 32.762-133.32 0-70.441-106.92-71.762-111.6-71.762zm1.9219 333.36v0.003906c-10.312 0-20.203-4.0977-27.492-11.391-7.293-7.2891-11.387-17.18-11.387-27.492s4.0938-20.199 11.387-27.492c7.2891-7.2891 17.18-11.387 27.492-11.387s20.199 4.0977 27.492 11.387c7.293 7.293 11.387 17.18 11.387 27.492-0.03125 10.305-4.1367 20.172-11.422 27.457s-17.156 11.391-27.457 11.426zm34.68-91.199c-10.715-7.2109-23.418-10.891-36.328-10.523-12.91 0.36328-25.383 4.7578-35.672 12.566-25.32-51.719-52.32-130.8-52.32-172.44 0-46.559 87-48 87.719-48s87.84 1.1992 87.84 48c-0.23828 40.68-26.398 118.56-51.238 170.4z"/>
5
+ <path d="m756 180c2.1992 2 5.0664 3.1133 8.0391 3.1211 4.9766 0.035156 9.457-3 11.266-7.6328 1.8125-4.6328 0.57812-9.9023-3.1055-13.25-7.6094-6.9062-15.625-13.359-24-19.316-2.5938-1.8477-5.8125-2.5859-8.9531-2.0586-3.1406 0.52734-5.9414 2.2812-7.7852 4.875-3.8438 5.4023-2.582 12.898 2.8203 16.742 7.5898 5.3906 14.844 11.242 21.719 17.52z"/>
6
+ <path d="m483.48 157.32c29.781-18.684 63.441-30.305 98.41-33.965 34.965-3.6602 70.305 0.73438 103.31 12.844 6.2305 2.2891 13.133-0.91016 15.422-7.1406 2.2852-6.2266-0.91016-13.133-7.1406-15.418-36.426-13.34-75.418-18.176-114-14.141-38.582 4.0352-75.727 16.832-108.61 37.422-60 36.602-99.48 94.199-106.32 153.96v-0.003906c-0.37109 3.168 0.53516 6.3555 2.5156 8.8516 1.9805 2.5 4.875 4.1094 8.0469 4.4688h1.4414-0.003906c6.1445 0.039062 11.324-4.5703 12-10.68 5.8828-52.32 41.402-103.32 94.922-136.2z"/>
7
+ <path d="m841.32 748.68c3.1836 0 6.2344-1.2656 8.4844-3.5156s3.5156-5.3008 3.5156-8.4844v-51c0-6.6289-5.3711-12-12-12-6.6289 0-12 5.3711-12 12v51c0 3.1836 1.2656 6.2344 3.5156 8.4844s5.3008 3.5156 8.4844 3.5156z"/>
8
+</svg>
frontend/src/components/Game.tsxmodified
@@ -29,7 +29,8 @@ const Game: React.FC = () => {
2929
   const [executing, setExecuting] = useState(false);
3030
   const [showHints, setShowHints] = useState(false);
3131
   const [hints, setHints] = useState<string[]>([]);
32
-  const [terminalMinimized, setTerminalMinimized] = useState(false);
32
+  const [terminalMinimized, setTerminalMinimized] = useState(true);
33
+  const [hasPlayedIntro, setHasPlayedIntro] = useState(false);
3334
   
3435
   const terminalRef = useRef<HTMLDivElement>(null);
3536
   const inputRef = useRef<HTMLInputElement>(null);
@@ -60,13 +61,14 @@ const Game: React.FC = () => {
6061
         error: null,
6162
       });
6263
       setCommandHistory([{
63
-        command: '🎮 Game started!',
64
+        command: 'Hunt started!',
6465
         output: response.mole_hint + '\nType "help" for available commands.',
6566
         success: true,
6667
       }]);
6768
       setHints([]);
6869
       setShowHints(false);
69
-      setTerminalMinimized(false);
70
+      setTerminalMinimized(true); // Keep terminal minimized on new game
71
+      setHasPlayedIntro(false); // Reset intro for new game
7072
     } catch (error) {
7173
       setGameState({
7274
         ...gameState,
@@ -176,6 +178,17 @@ const Game: React.FC = () => {
176178
     startNewGame();
177179
   }, []);
178180
 
181
+  // Mark intro as played after first render
182
+  useEffect(() => {
183
+    if (gameState.tree && !hasPlayedIntro) {
184
+      // Set timeout to mark intro as played after animation completes
185
+      const timer = setTimeout(() => {
186
+        setHasPlayedIntro(true);
187
+      }, 6500); // Total intro duration
188
+      return () => clearTimeout(timer);
189
+    }
190
+  }, [gameState.tree, hasPlayedIntro]);
191
+
179192
   if (gameState.loading) {
180193
     return (
181194
       <div className="flex items-center justify-center min-h-screen bg-gray-900 text-white">
@@ -207,7 +220,7 @@ const Game: React.FC = () => {
207220
     return (
208221
       <div className="flex items-center justify-center min-h-screen bg-gray-900 text-white">
209222
         <div className="text-center">
210
-          <h1 className="text-4xl font-bold mb-4">🐭 Bashamole</h1>
223
+          <h1 className="text-4xl font-bold mb-4">Bashamole</h1>
211224
           <p className="text-gray-400 mb-8">Hunt the mole in the Unix filesystem!</p>
212225
           <button
213226
             onClick={startNewGame}
@@ -228,6 +241,7 @@ const Game: React.FC = () => {
228241
           treeData={gameState.tree.tree_data}
229242
           playerLocation={gameState.tree.player_location}
230243
           onNodeClick={handleNodeClick}
244
+          playIntro={!hasPlayedIntro}
231245
         />
232246
       </div>
233247
 
@@ -256,7 +270,7 @@ const Game: React.FC = () => {
256270
               {commandHistory.map((entry, index) => (
257271
                 <div key={index} className="mb-2">
258272
                   <div className="text-gray-400">
259
-                    {entry.command.startsWith('🎮') ? (
273
+                    {entry.command.startsWith('Game started!') ? (
260274
                       <span className="text-yellow-400">{entry.command}</span>
261275
                     ) : (
262276
                       <>$ {entry.command}</>
@@ -306,7 +320,7 @@ const Game: React.FC = () => {
306320
           >
307321
308322
           </button>
309
-          <h3 className="text-yellow-400 font-bold mb-2">💡 Hints:</h3>
323
+          <h3 className="text-yellow-400 font-bold mb-2">Hints:</h3>
310324
           {hints.map((hint, index) => (
311325
             <p key={index} className="text-yellow-200 text-sm">{hint}</p>
312326
           ))}
@@ -317,7 +331,7 @@ const Game: React.FC = () => {
317331
       <div className="absolute bottom-0 left-0 right-0 bg-gray-900/90 backdrop-blur-sm border-t border-gray-700 p-4 z-20">
318332
         <div className="max-w-7xl mx-auto flex justify-between items-center">
319333
           <div className="flex items-center gap-4">
320
-            <h1 className="text-2xl font-bold">🐭 Bashamole</h1>
334
+            <h1 className="text-2xl font-bold">Bashamole</h1>
321335
             <div className="text-sm text-gray-400">
322336
               Location: <span className="font-mono text-blue-400">{gameState.tree.player_location}</span>
323337
             </div>
@@ -326,7 +340,7 @@ const Game: React.FC = () => {
326340
           <div className="flex items-center gap-3">
327341
             {gameState.tree.is_completed ? (
328342
               <div className="text-green-400 font-bold animate-pulse">
329
-                🎉 You found the mole!
343
+                You found the mole!
330344
               </div>
331345
             ) : (
332346
               <>
@@ -334,7 +348,7 @@ const Game: React.FC = () => {
334348
                   onClick={getHints}
335349
                   className="px-3 py-1.5 bg-yellow-600 text-white text-sm rounded hover:bg-yellow-700 transition"
336350
                 >
337
-                  Get Hint 💡
351
+                  Get Hint
338352
                 </button>
339353
                 <div className="text-xs text-gray-500">
340354
                   Click nodes or use terminal
frontend/src/components/TreeVisualizer.tsxmodified
@@ -9,19 +9,28 @@ interface TreeVisualizerProps {
99
   treeData: TreeNode;
1010
   playerLocation: string;
1111
   onNodeClick?: (path: string) => void;
12
+  playIntro?: boolean;
1213
 }
1314
 
1415
 const TreeVisualizer: React.FC<TreeVisualizerProps> = ({
1516
   treeData,
1617
   playerLocation,
1718
   onNodeClick,
19
+  playIntro = true,
1820
 }) => {
1921
   const svgRef = useRef<SVGSVGElement>(null);
2022
   const containerRef = useRef<HTMLDivElement>(null);
23
+  const previousLocationRef = useRef<string | null>(null);
2124
 
2225
   useEffect(() => {
2326
     if (!treeData || !svgRef.current || !containerRef.current) return;
2427
 
28
+    const isNavigation = previousLocationRef.current !== null && 
29
+                        previousLocationRef.current !== playerLocation && 
30
+                        !playIntro;
31
+    
32
+    previousLocationRef.current = playerLocation;
33
+
2534
     // Clear previous render
2635
     d3.select(svgRef.current).selectAll('*').remove();
2736
 
@@ -29,9 +38,21 @@ const TreeVisualizer: React.FC<TreeVisualizerProps> = ({
2938
     const containerWidth = containerRef.current.clientWidth;
3039
     const containerHeight = containerRef.current.clientHeight;
3140
 
32
-    // Set up dimensions with dynamic sizing
33
-    const margin = { top: 100, right: 50, bottom: 100, left: 50 };
34
-    const width = Math.max(containerWidth, 1600);
41
+    // Create hierarchy and calculate dimensions
42
+    const root = d3.hierarchy(treeData);
43
+    
44
+    // Calculate the maximum number of nodes at any depth
45
+    const levelCounts: { [key: number]: number } = {};
46
+    root.each(d => {
47
+      levelCounts[d.depth] = (levelCounts[d.depth] || 0) + 1;
48
+    });
49
+    const maxNodesAtLevel = Math.max(...Object.values(levelCounts));
50
+    
51
+    // Dynamic width based on tree structure
52
+    const nodeSpacing = 120; // Increased from 80 for better spacing
53
+    const dynamicWidth = Math.max(maxNodesAtLevel * nodeSpacing, containerWidth * 2); // Increased multiplier
54
+    const margin = { top: 100, right: 150, bottom: 100, left: 150 }; // Increased margins
55
+    const width = dynamicWidth;
3556
     const height = Math.max(containerHeight, 1200);
3657
 
3758
     const svg = d3
@@ -70,29 +91,50 @@ const TreeVisualizer: React.FC<TreeVisualizerProps> = ({
7091
 
7192
     const g = svg
7293
       .append('g')
73
-      .attr('transform', `translate(${width / 2},${margin.top})`);
94
+      .attr('transform', `translate(${margin.left},${margin.top})`);
7495
 
75
-    // Create tree layout - vertical orientation with more spacing
96
+    // Create tree layout - vertical orientation with better spacing
7697
     const treeLayout = d3
7798
       .tree<TreeNode>()
7899
       .size([width - margin.left - margin.right, height - margin.top - margin.bottom])
79100
       .separation((a, b) => {
101
+        // Special handling for directories with many children
102
+        const aParentChildCount = a.parent ? (a.parent.children?.length || 0) : 0;
103
+        const bParentChildCount = b.parent ? (b.parent.children?.length || 0) : 0;
104
+        
105
+        // If nodes share a parent with many children (like home dirs), give more space
106
+        if (a.parent === b.parent && aParentChildCount > 3) {
107
+          const aIsLeaf = !a.children || a.children.length === 0;
108
+          const bIsLeaf = !b.children || b.children.length === 0;
109
+          
110
+          if (aIsLeaf && bIsLeaf) {
111
+            // Extra space for leaf nodes in crowded directories
112
+            return 2.5;
113
+          }
114
+          return 2;
115
+        }
116
+        
117
+        // Base separation on node depth
118
+        if (a.depth === 0 || b.depth === 0) return 4;
119
+        if (a.depth === 1 || b.depth === 1) return 3;
120
+        
80121
         const aIsLeaf = !a.children || a.children.length === 0;
81122
         const bIsLeaf = !b.children || b.children.length === 0;
82123
         
83
-        // Much more spacing for leaf nodes to prevent crowding
84124
         if (aIsLeaf && bIsLeaf) {
85
-          return 3; // Increased from 1.5
86
-        }
87
-        if (aIsLeaf || bIsLeaf) {
88
-          return 2.5; // Mixed leaf/non-leaf
125
+          return 1.5;
89126
         }
90
-        return a.parent === b.parent ? 2 : 2.5; // Increased general spacing
127
+        return a.parent === b.parent ? 1.5 : 2;
91128
       });
92129
 
93
-    // Create hierarchy
94
-    const root = d3.hierarchy(treeData);
130
+    // Apply tree layout
95131
     const treeNodes = treeLayout(root);
132
+    
133
+    // Adjust the x-coordinate to center the root
134
+    const rootX = width / 2;
135
+    treeNodes.each(d => {
136
+      d.x = d.x + (rootX - root.x);
137
+    });
96138
 
97139
     // Create gradient for links
98140
     const linkGradient = defs
@@ -154,9 +196,9 @@ const TreeVisualizer: React.FC<TreeVisualizerProps> = ({
154196
     node
155197
       .append('circle')
156198
       .attr('r', d => {
157
-        if (d.data.path === '/') return 14;
158
-        if (d.data.path === playerLocation) return 11;
159
-        return 9;
199
+        if (d.data.path === '/') return 16;
200
+        if (d.data.path === playerLocation) return 13;
201
+        return 11;
160202
       })
161203
       .style('fill', d => {
162204
         if (d.data.path === playerLocation) return '#3B82F6';
@@ -177,14 +219,14 @@ const TreeVisualizer: React.FC<TreeVisualizerProps> = ({
177219
         d3.select(this)
178220
           .transition()
179221
           .duration(200)
180
-          .attr('r', d.data.path === '/' ? 16 : 12)
222
+          .attr('r', d.data.path === '/' ? 18 : 14)
181223
           .style('stroke-width', 3);
182224
       })
183225
       .on('mouseout', function(event, d) {
184226
         d3.select(this)
185227
           .transition()
186228
           .duration(200)
187
-          .attr('r', d.data.path === '/' ? 14 : d.data.path === playerLocation ? 11 : 9)
229
+          .attr('r', d.data.path === '/' ? 16 : d.data.path === playerLocation ? 13 : 11)
188230
           .style('stroke-width', 2);
189231
       })
190232
       .on('click', (event, d) => {
@@ -203,64 +245,39 @@ const TreeVisualizer: React.FC<TreeVisualizerProps> = ({
203245
       .append('text')
204246
       .attr('dy', d => d.children ? -20 : 25)
205247
       .attr('text-anchor', 'middle')
206
-      .style('font-size', '12px')
207
-      .style('font-weight', d => d.data.path === playerLocation ? '600' : '400')
248
+      .style('font-size', '14px')
249
+      .style('font-weight', d => d.data.path === playerLocation ? '700' : '500')
208250
       .style('fill', d => d.data.path === playerLocation ? '#93C5FD' : '#E5E7EB')
209251
       .style('text-shadow', '0 0 4px rgba(0,0,0,0.8)')
210252
       .text(d => d.data.name || '/')
211253
       .style('pointer-events', 'none');
212254
 
213
-    // Add player indicator with SVG
255
+    // Add player indicator with SVG overlaid on node
214256
     const playerNode = treeNodes.descendants().find(d => d.data.path === playerLocation);
215257
     if (playerNode) {
216258
       const playerGroup = node
217259
         .filter(d => d.data.path === playerLocation)
218
-        .append('g')
219
-        .attr('transform', 'translate(0, -30)');
260
+        .append('g');
220261
 
221
-      // Add pulsing animation
222
-      playerGroup
223
-        .append('circle')
224
-        .attr('r', 15)
225
-        .style('fill', 'none')
226
-        .style('stroke', '#3B82F6')
227
-        .style('stroke-width', 2)
228
-        .style('opacity', 0)
229
-        .append('animate')
230
-        .attr('attributeName', 'r')
231
-        .attr('from', '15')
232
-        .attr('to', '25')
233
-        .attr('dur', '2s')
234
-        .attr('repeatCount', 'indefinite');
235
-
236
-      playerGroup
237
-        .select('circle')
238
-        .append('animate')
239
-        .attr('attributeName', 'opacity')
240
-        .attr('from', '0.8')
241
-        .attr('to', '0')
242
-        .attr('dur', '2s')
243
-        .attr('repeatCount', 'indefinite');
244
-
245
-      // Add player icon
262
+      // Add player SVG directly on the node
246263
       playerGroup
247264
         .append('image')
248265
         .attr('xlink:href', '/player.svg')
249
-        .attr('width', 24)
250
-        .attr('height', 24)
251
-        .attr('x', -12)
252
-        .attr('y', -12);
266
+        .attr('width', 20)
267
+        .attr('height', 20)
268
+        .attr('x', -10)
269
+        .attr('y', -10)
270
+        .style('pointer-events', 'none');
253271
     }
254272
 
255
-    // Add mole indicator with SVG if game is won
273
+    // Add mole indicator with SVG overlaid if game is won
256274
     const moleNode = treeNodes.descendants().find(d => d.data.has_mole);
257275
     if (moleNode) {
258276
       const moleGroup = node
259277
         .filter(d => d.data.has_mole)
260
-        .append('g')
261
-        .attr('transform', 'translate(0, -30)');
278
+        .append('g');
262279
 
263
-      // Add celebration animation
280
+      // Add celebration animation ring
264281
       moleGroup
265282
         .append('circle')
266283
         .attr('r', 15)
@@ -284,41 +301,87 @@ const TreeVisualizer: React.FC<TreeVisualizerProps> = ({
284301
         .attr('dur', '1s')
285302
         .attr('repeatCount', 'indefinite');
286303
 
287
-      // Add mole icon
304
+      // Add mole SVG directly on the node
288305
       moleGroup
289306
         .append('image')
290307
         .attr('xlink:href', '/mole.svg')
291
-        .attr('width', 24)
292
-        .attr('height', 24)
293
-        .attr('x', -12)
294
-        .attr('y', -12);
308
+        .attr('width', 20)
309
+        .attr('height', 20)
310
+        .attr('x', -10)
311
+        .attr('y', -10)
312
+        .style('pointer-events', 'none');
295313
     }
296314
 
297315
     // Add zoom and pan behavior
298316
     const zoom = d3.zoom<SVGSVGElement, unknown>()
299
-      .scaleExtent([0.3, 2])
317
+      .scaleExtent([0.1, 3])
300318
       .on('zoom', (event) => {
301319
         g.attr('transform', event.transform);
302320
       });
303321
 
322
+    // Apply zoom behavior immediately
304323
     svg.call(zoom);
305324
 
306
-    // Center on player location with animation
307
-    if (playerNode) {
308
-      const scale = 0.8;
309
-      const x = width / 2 - playerNode.x * scale;
310
-      const y = containerHeight / 2 - playerNode.y * scale - margin.top;
325
+    // Helper function to create zoom transform for centering on a node
326
+    const getZoomTransform = (node: d3.HierarchyPointNode<TreeNode>, scale: number, offsetX: number = 0, offsetY: number = 0) => {
327
+      const viewBoxCenterX = width / 2;
328
+      const viewBoxCenterY = height / 2;
311329
       
312
-      svg
330
+      // Calculate translation to center the node with optional offset
331
+      const translateX = viewBoxCenterX - (node.x + margin.left) * scale + (width * offsetX);
332
+      const translateY = viewBoxCenterY - (node.y + margin.top) * scale + (height * offsetY);
333
+      
334
+      return d3.zoomIdentity.translate(translateX, translateY).scale(scale);
335
+    };
336
+
337
+    // Animated intro sequence using zoom transitions
338
+    if (playIntro && playerNode) {
339
+      // Start zoomed in on root
340
+      const rootTransform = getZoomTransform(treeNodes, 3);
341
+      svg.call(zoom.transform, rootTransform);
342
+      
343
+      // Calculate full tree view
344
+      const allNodes = treeNodes.descendants();
345
+      const xExtent = d3.extent(allNodes, d => d.x) as [number, number];
346
+      const yExtent = d3.extent(allNodes, d => d.y) as [number, number];
347
+      
348
+      const treeWidth = xExtent[1] - xExtent[0] + 200;
349
+      const treeHeight = yExtent[1] - yExtent[0] + 200;
350
+      
351
+      const scaleX = (width - margin.left - margin.right) / treeWidth;
352
+      const scaleY = (height - margin.top - margin.bottom) / treeHeight;
353
+      const fullTreeScale = Math.min(scaleX, scaleY, 0.8);
354
+      
355
+      const treeCenterX = (xExtent[0] + xExtent[1]) / 2;
356
+      const treeCenterY = (yExtent[0] + yExtent[1]) / 2;
357
+      const treeCenter = { x: treeCenterX, y: treeCenterY } as d3.HierarchyPointNode<TreeNode>;
358
+      const fullTreeTransform = getZoomTransform(treeCenter, fullTreeScale);
359
+      
360
+      // Final player position - nudged 10% left and 15% down
361
+      const playerTransform = getZoomTransform(playerNode, 3, 0.1, 0.2);
362
+      
363
+      // Animate using zoom transitions
364
+      svg.transition()
365
+        .duration(1000)
366
+        .call(zoom.transform, rootTransform)
367
+        .transition()
368
+        .duration(2000)
369
+        .ease(d3.easeCubicInOut)
370
+        .call(zoom.transform, fullTreeTransform)
371
+        .transition()
372
+        .duration(1000)
373
+        .call(zoom.transform, fullTreeTransform)
313374
         .transition()
314
-        .duration(750)
315
-        .call(
316
-          zoom.transform as any,
317
-          d3.zoomIdentity.translate(x, y).scale(scale)
318
-        );
375
+        .duration(1500)
376
+        .ease(d3.easeCubicInOut)
377
+        .call(zoom.transform, playerTransform);
378
+    } else if (playerNode) {
379
+      // No intro: directly position on player - nudged 10% left and 15% down
380
+      const playerTransform = getZoomTransform(playerNode, 3, 0.1, 0.2);
381
+      svg.call(zoom.transform, playerTransform);
319382
     }
320383
 
321
-  }, [treeData, playerLocation, onNodeClick]);
384
+  }, [treeData, playerLocation, onNodeClick, playIntro]);
322385
 
323386
   return (
324387
     <div ref={containerRef} className="w-full h-full">