# Project export: ChainEscapeLite

This document was generated by HackStack to give an AI agent context about a hackathon project. Sections are labeled with their provenance; content marked as truncated was cut to keep this document small.

## Project metadata

- Hackathon: TreeHacks 2025
- Tagline: Welcome to ChainEscape Lite, a bold fusion of AI puzzle generation and a decentralized blockchain verification system with both off- and on-chain validation.
- Devpost: https://devpost.com/software/chainescapelite
- GitHub: https://github.com/tunderulesagain/Chainescape-Lite
- Video: https://www.youtube.com/embed/rDvyeMD8sLk?enablejsapi=1&hl=en_US&rel=0&start=&version=3&wmode=transparent
- Team: 1 GitHub contributor(s) — tunderulesagain (5 commits)

## Devpost submission (written by the team)

No Devpost description available.

## README (from the GitHub repository)

TREEHACKS IDEAS
Project Name: ChainEscape: The Decentralized Puzzle Quest
Overview
ChainEscape is a live, digital escape room where teams solve a series of dynamic puzzles generated by an off-chain AI narrative engine. Every twist and turn is intertwined with real-time blockchain events and verifiable computations using Level 1 Agents and Autonomous Verifiable Services (AVS) from EigenLayer. The experience is designed to be immersive, playful, and demonstrative of how complex off-chain AI processes can be securely and transparently linked to on-chain events.

User Experience & UI
Onboarding & Lobby:
Welcome Screen: A visually engaging landing page introduces the game’s narrative—a mysterious digital facility that has been “infected” by chaotic blockchain data.
Team Registration: Users join individually or in teams via a simple web interface that connects to their wallet. Teams receive a unique game token that is used to track progress.
Tutorial & Rules: An interactive tutorial explains how on-chain actions (like sending microtransactions or voting) influence the narrative and how the verifiable AI agent will guide the experience.
Game Dashboard:
Central Console: A dashboard displays a stylized “room” graphic with interactive hotspots. Think of it as a blend between a classic point-and-click adventure and a modern dashboard.
Live Timer & Progress Bar: A countdown clock and progress tracker keep the tension high, recording the team’s real-time performance.
Chat & Clue Feed: An integrated live chat and clue feed allow teams to collaborate and receive periodic hints from the AI narrator.
Puzzle Interface:
Visual Puzzles: For example, a digital mural that morphs based on recent blockchain transactions—players must decipher hidden symbols or color patterns that change in real time.
Trivia & Logic Challenges: AI-generated riddles or blockchain-themed trivia that reference on-chain data (e.g., “Decode the sequence of the last five block hashes”).
Interactive Simulations: Mini-games that simulate a virtual “control panel” where users adjust dials or input codes. These puzzles might require them to align a set of smart-contract addresses or solve a cryptographic challenge.
Real-World Data Puzzles: Puzzles whose conditions are affected by real-world inputs, like current market trends or weather data fed by trusted oracles.
Twists & Branching Paths:
Dynamic Narrative Branches: The off-chain AI (the Level 1 Agent) generates unique puzzle paths based on both user choices and live blockchain events. For instance, if a sudden spike in transaction volume occurs, the narrative might shift to a “system overload” scenario, adding extra puzzles that must be solved to restore order.
Persistent Memory: Progress and choices are stored on-chain, meaning that puzzles evolve not only within a single session but can influence subsequent games. This “echo chamber” effect makes each playthrough distinct and creates a historical record of community decisions.
Surprise Events: Random events (e.g., a “data storm” that scrambles the clues temporarily) are triggered both by time and on-chain signals. Teams might suddenly need to “stabilize” the system by collectively solving a puzzle within a strict time window.

On-Chain & Off-Chain Integration
Verifiable Puzzle Generation with Level 1 Agents:
AI Narrative Engine: The escape room’s storyline and puzzle generation are driven by an off-chain AI that uses a large language model (LLM) to craft clues and challenges dynamically.
EigenLayer AVS Integration: By leveraging Autonomous Verifiable Services (AVS), the AI agent produces cryptographic proofs (or signatures) of its puzzle generation decisions. These proofs are then logged on-chain, ensuring that each puzzle is fair and tamper-proof.
Randomness & Trustlessness: Instead of relying solely on centralized randomness sources, the AVS-enabled agent combines on-chain randomness (from verifiable randomness functions) with its AI logic to create puzzles that are unpredictable yet auditable.
Real-Time Event Listening:
Blockchain Oracles & Event Triggers: Smart contracts monitor specific on-chain events (e.g., high-value transactions, token transfers) and pass these signals to the off-chain AI. The agent then tailors the narrative or injects bonus challenges in response.
User-Driven Interactions: Users can influence the game directly through on-chain transactions—small microtransactions might “unlock” additional clues or buy hints, all recorded transparently.
Persistent State & Replayability:
On-Chain Progress Recording: Every major decision, puzzle solution, and narrative twist is recorded in smart contracts. This creates a permanent, verifiable record of each session.
Future Interactions: These records can be used to seed new games, ensuring that returning players might encounter echoes of past adventures—blurring the lines between isolated play sessions and a persistent decentralized world.

Rewards & Incentives
NFT Artifacts: Teams that successfully “escape” are rewarded with uniquely minted NFTs that serve as both trophies and keys to unlock future game modes or exclusive content. These NFTs include metadata that tells the story of that session.
Token Prizes & In-Game Bonuses: Additional rewards might include crypto tokens or special in-game powers (e.g., extra hints, time extensions) that can be used in subsequent challenges.
Leaderboard & Community Impact: The game features a live leaderboard stored on-chain, highlighting the fastest escape times and the most creative solutions. This competitive element encourages repeated play and community engagement.

Why ChainEscape is Bold & Groundbreaking
Redefining On-Chain/Off-Chain Synergy: By using AVS and Level 1 Agents, the escape room showcases how advanced AI computations can remain off-chain for efficiency yet be verifiably tied to on-chain actions. This model opens new frontiers for applications that require both rich interactivity and tamper-proof records.
Dynamic, Context-Aware Gameplay: The experience adapts in real time to both user inputs and external blockchain events, creating a living, breathing narrative that is never the same twice. This responsiveness demonstrates a new level of integration between digital storytelling and decentralized data.
A New Use Case for Verifiable Agents: Beyond entertainment, ChainEscape serves as a proof-of-concept for how verifiable AI agents can manage complex, real-time processes in a trustless environment. It paves the way for future applications in training simulations, decentralized governance games, and interactive public events.
Engaging and Accessible: The game is designed to be immediately engaging for both crypto veterans and newcomers. Its playful narrative, combined with real-time challenges and transparent rewards, makes it an ideal showcase for what the future of decentralized interactive applications can look like.

ChainEscape isn’t just an escape room—it’s a demonstration of how decentralized systems and AI can collaborate to create an interactive, verifiable, and ever-evolving digital experience. This project has the potential to captivate audiences, showcase groundbreaking technology, and open a new frontier for on-chain and off-chain integrations.


## Detected evidence (automated analysis)

Indexed codebase: 3 recognized source files, 23 KB.
- JavaScript (language) — detected in the code
- TypeScript (language) — detected in the code

## Codebase structure (from repository index)

### Files (4 of 4)

```
App.Js
Contract
README.md
Server.ts
```

### Dependencies

No dependency index available.

### Recent commits (newest first)

- Create Server.ts
- Create App.Js
- Create README.md
- Update and rename README.md to Contract
- Initial commit

## Key source files (fetched from GitHub, selected and truncated for size)

### Server.ts

```typescript
import express from "express";
import cors from "cors";
import dotenv from "dotenv";
import { ethers } from "ethers";

import { createAgent } from "./agent/createAgent.js";
import chainEscapeAbi from "./ChainEscapeAbi.json";

dotenv.config();

const app = express();
const port = process.env.PORT || 3000;

let agent: Awaited<ReturnType<typeof createAgent>>;

// In-memory puzzle storage
let puzzleIdCounter = 0;
const puzzleSolutions: { [puzzleId: number]: string } = {};

// Initialize the agent
createAgent()
  .then((a) => {
    agent = a;
    console.log("Agent initialized successfully");
  })
  .catch((error) => {
    console.error("Failed to initialize agent:", error);
    process.exit(1);
  });

// Helper functions for answer verification
const normalizeAnswer = (input: string): string => {
  return input
    .toLowerCase()
    .replace(/[^a-z0-9]/gi, '') // Remove non-alphanumeric characters
    .replace(/\s+/g, ' ')      // Collapse multiple spaces
    .trim();
};

const isNumericMatch = (guess: string, solution: string): boolean => {
  const extractNumbers = (str: string) => str.replace(/[^0-9]/g, '');
  const guessNums = extractNumbers(guess);
  const solutionNums = extractNumbers(solution);
  return guessNums !== '' && guessNums === solutionNums;
};

const wordsToNumbers = (str: string): string => {
  const numberWords: { [key: string]: string } = {
    zero: '0', one: '1', two: '2', three: '3', four: '4',
    five: '5', six: '6', seven: '7', eight: '8', nine: '9',
    ten: '10', eleven: '11', twelve: '12', thirteen: '13',
    fourteen: '14', fifteen: '15', sixteen: '16', seventeen: '17',
    eighteen: '18', nineteen: '19', twenty: '20', thirty: '30',
    forty: '40', fifty: '50', sixty: '60', seventy: '70',
    eighty: '80', ninety: '90'
  };

  return str.replace(
    /\b(zero|one|two|three|four|five|six|seven|eight|nine|ten|eleven|twelve|thirteen|fourteen|fifteen|sixteen|seventeen|eighteen|nineteen|twenty|thirty|forty|fifty|sixty|seventy|eighty|ninety)\b/gi,
    (match) => numberWords[match.toLowerCase()] || match
  );
};

// Middleware
app.use(cors());
app.use(express.json());

// Health check endpoint
app.get("/health", (req, res) => {
  res.json({ status: "ok", agentReady: !!agent });
});

// Generate verifiable text endpoint
app.post("/api/generate", async (req, res) => {
  try {
    if (!agent) {
      return res.status(503).json({ error: "Agent not ready" });
    }

    const { prompt } = req.body;
    if (!prompt) {
      return res.status(400).json({ error: "Prompt is required" });
    }

    console.log("Generating text for prompt:", prompt);
    const verifiableResp = await agent.generateVerifiableText(prompt);
    const resultText = verifiableResp.content;

    console.log("Generation result:", resultText);
    res.json({ text: resultText });
  } catch (error) {
    console.error("Error generating text:", error);
    res.status(500).json({
      error: "Failed to generate text",
      details: error instanceof Error ? error.message : String(error),
    });
  }
});

// Generate puzzle endpoint
app.post("/api/generatePuzzle", async (req, res) => {
  try {
    if (!agent) {
      return res.status(503).json({ error: "Agent not ready" });
    }

    const { difficulty, theme } = req.body;
    if (!difficulty || !theme) {
      return res.status(400).json({
        error: "Please provide 'difficulty' and 'theme' in the request body.",
      });
    }

    console.log(`Generating puzzle text for difficulty='${difficulty}', theme='${theme}'`);

    // Generate introduction
    const introPrompt = `
Create a 5-sentence introduction for an escape room puzzle game using the given theme: ${theme}. 
The intro should:
Establish why the user is trapped
Include a helpful voice/guide who explains the situation
Provide a clear, logical reason why solving puzzles leads to escape
Create urgency through a time pressure or threat
End by mentioning a puzzle interface (computer, ancient book, magical device, etc.)
Keep the tone intense but optimistic - the user should feel challenged, not hopeless. 
Use clear, accessible language and active voice. 
The guide should be an ally, not an antagonist.`;

    const puzzleIntroRes = await agent.generateVerifiableText(introPrompt);
    const puzzleIntro = puzzleIntroRes.content;

    // Generate puzzle text
    const puzzleTextRes = await agent.generateVerifiableText(
      `Create a unique, engaging puzzle that fits the following criteria:
  - **Theme:** ${theme}
  - **Difficulty:** ${difficulty}
  - **Length:** 2-5 sentences
  - **Type:** Logic Puzzle, Pattern Recognition, Cipher/Codebreaking, Math Challenge, or Word Riddle
  - **Requirements:** Self-contained, objective answer, clear wording`
    );
    const puzzleText = puzzleTextRes.content;

    // Generate solution
    const puzzleSolutionRes = await agent.generateVerifiableText(
      `Given this puzzle: "${puzzleText}", what's the correct solution?`
    );
    const puzzleSolution = puzzleSolutionRes.content;

    console.log("Puzzle solution is:", puzzleSolution);

    // Hash solution
    const puzzleHash = ethers.keccak256(ethers.toUtf8Bytes(puzzleSolution));

    // Connect to contract
    const provider = new ethers.JsonRpcProvider(process.env.RPC_URL);
    const wallet = new ethers.Wallet(process.env.PUZZLE_CREATOR_KEY!, provider);
    const chainEscape = new ethers.Contract(
      process.env.CHAINESCAPE_ADDRESS!,
      chainEscapeAbi.abi,
      wallet
    );

    // Store puzzle
    const puzzleId = puzzleIdCounter++;
    const basePoints = 10;

    console.log(`Storing puzzle on-chain with puzzleId=${puzzleId}, puzzleHash=${puzzleHash}`);
    const tx = await chainEscape.setPuzzleHash(puzzleId, puzzleHash, basePoints);
    await tx.wait();

    console.log(`Puzzle stored successfully! TX hash: ${tx.hash}`);
    puzzleSolutions[puzzleId] = puzzleSolution;

    return res.json({
      puzzleId,
      puzzleIntro,
      puzzleText,
      puzzleHash,
      basePoints,
    });
  } cat
[truncated — 1894 more characters]
```

### App.Js

```javascript
import React, { useState } from "react";
import axios from "axios";

function App() {
  // Basic puzzle generation inputs
  const [difficulty, setDifficulty] = useState("easy");
  const [theme, setTheme] = useState("animals");

  // Data returned by /api/generatePuzzle
  const [puzzleData, setPuzzleData] = useState(null);

  // For error messages
  const [errorMessage, setErrorMessage] = useState("");

  // Track username, guess, and feedback
  const [username, setUsername] = useState("");
  const [userGuess, setUserGuess] = useState("");
  const [feedback, setFeedback] = useState("");

  // Local points state
  const [points, setPoints] = useState(0);

  // Generate a new puzzle
  const handleGeneratePuzzle = async () => {
    try {
      setErrorMessage("");
      setFeedback("");
      setUserGuess("");
      // Reset points to 100 for each new puzzle
      setPoints(100);

      // Call your Node server at http://localhost:3000/api/generatePuzzle
      const res = await axios.post("http://localhost:3000/api/generatePuzzle", {
        difficulty,
        theme,
      });
      setPuzzleData(res.data);
    } catch (err) {
      console.error("Error generating puzzle:", err);
      setErrorMessage("Failed to generate puzzle. Check console for details.");
    }
  };

  // Submit an answer guess
  const handleSubmitGuess = async () => {
    if (!puzzleData) {
      return alert("No puzzle generated yet!");
    }
    if (!username) {
      return alert("Please enter a username first.");
    }

    try {
      const res = await axios.post("http://localhost:3000/api/checkAnswer", {
        username,
        puzzleId: puzzleData.puzzleId,
        userGuess,
      });

      if (res.data.correct) {
        // Correct => +25 points
        setPoints((prev) => prev + 25);
        setFeedback("Correct! " + (res.data.message || ""));
      } else {
        // Wrong => -5 points
        setPoints((prev) => prev - 5);
        setFeedback("Wrong answer. " + (res.data.message || ""));
      }
    } catch (err) {
      console.error("Error submitting guess:", err);
      setFeedback("Error verifying guess. See console.");
    }
  };

  // Skip the puzzle => -15 points, remove puzzle
  const handleSkipPuzzle = () => {
    if (!puzzleData) {
      return alert("No puzzle to skip!");
    }
    // Deduct 15 points
    setPoints((prev) => prev - 15);
    // Clear puzzle and feedback
    setPuzzleData(null);
    setFeedback("You skipped the puzzle. -15 points.");
  };

  return (
    // Main container with a simple background
    <div
      style={{
        minHeight: "100vh",
        margin: 0,
        padding: 20,
        background: "linear-gradient(to right, #a1c4fd, #c2e9fb)",
        fontFamily: "Verdana, sans-serif",
      }}
    >
      <div
        style={{
          maxWidth: 600,
          margin: "0 auto",
          backgroundColor: "rgba(255,255,255,0.85)",
          borderRadius: 8,
          boxShadow: "0 4px 8px rgba(0,0,0,0.1)",
          padding: 20,
        }}
      >
        <h1 style={{ textAlign: "center", marginBottom: 20 }}>
          ChainEscape Lite: Puzzle Generator
        </h1>

        {/* Username Field */}
        <div style={{ marginBottom: 10 }}>
          <label style={{ marginRight: 5 }}>Username:</label>
          <input
            style={{
              padding: "5px",
              borderRadius: "4px",
              border: "1px solid #ccc",
            }}
            value={username}
            onChange={(e) => setUsername(e.target.value)}
          />
        </div>

        {/* Difficulty & Theme Fields */}
        <div style={{ marginBottom: 10 }}>
          <label style={{ marginRight: 5 }}>Difficulty:</label>
          <input
            style={{
              padding: "5px",
              borderRadius: "4px",
              border: "1px solid #ccc",
            }}
            value={difficulty}
            onChange={(e) => setDifficulty(e.target.value)}
          />
        </div>

        <div style={{ marginBottom: 10 }}>
          <label style={{ marginRight: 5 }}>Theme:</label>
          <input
            style={{
              padding: "5px",
              borderRadius: "4px",
              border: "1px solid #ccc",
            }}
            value={theme}
            onChange={(e) => setTheme(e.target.value)}
          />
        </div>

        {/* Generate Puzzle Button */}
        <button
          onClick={handleGeneratePuzzle}
          style={{
            padding: "8px 16px",
            borderRadius: 4,
            border: "none",
            backgroundColor: "#4CAF50",
            color: "#fff",
            cursor: "pointer",
          }}
        >
          Generate Puzzle
        </button>

        {/* Error Message */}
        {errorMessage && (
          <div
            style={{
              marginTop: 20,
              color: "red",
              fontWeight: "bold",
            }}
          >
            {errorMessage}
          </div>
        )}

        {/* Puzzle Display */}
        {puzzleData && (
          <div
            style={{
              marginTop: 20,
              backgroundColor: "#f9f9f9",
              borderRadius: 6,
              padding: 15,
              border: "1px solid #eee",
            }}
          >
            {/* Show the introduction above the puzzle, if present */}
            {puzzleData.puzzleIntro && (
              <div
                style={{
                  marginBottom: 20,
                  whiteSpace: "pre-wrap",
                  fontStyle: "italic",
                  color: "#555",
                }}
              >
                <strong>Introduction:</strong> {puzzleData.puzzleIntro}
              </div>
            )}

            {/* Show current points */}
            <p style={{ fontWeight: "bold", marginBottom: 5 }}>
              Points: {points}
            </p>

            <h2 style={{ margin: "0.5em 0" }}>
              Puzzle ID: {puzzleData.puzzleId}
            </h2>
        
[truncated — 1923 more characters]
```