生成AIを簡単に組み込めるMarkdown AIで「相関図クイズゲーム」を作ってみた！

## この記事でわかること
- Markdown AIを使ったオリジナルコンテンツ制作の事例
- 「相関図クイズゲーム」の制作プロセス
- Markdown AIの良かった点＆改善希望ポイント

---

## はじめに

突然ですがクイズです。

これはなんの相関図でしょう？

![Screenshot 2025-01-29 at 10.33.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/527661/e9841515-0f9a-ad37-9a53-c8642af22077.png)

　　　　　　　　　　　　　　　　👇


　　　　　　　　　　　　　　　　👇


　　　　　　　　　　　　　　　　👇


　　　　　　　　　　　　　　　　👇


　　　　　　　　　　　　　　　　👇


　　　　　　　　　　　　　　　　👇


　　　　　　　　　　　　　　　　👇


　　　　　　　　　　　　　　　　👇


　　　　　　　　　　　　　　　　👇


　　　　　　　　　　　　　　　　👇

                
　　　　　　　　　　　　　　　　👇


　　　　　　　　　　　　　　　　👇
    

　　　　　　　　　　　　　　　　👇


　　　　　　　　　　　　　　　　👇

答えは「桃太郎」でした!
![Screenshot 2025-01-29 at 10.33.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/527661/0ce9872f-1283-6d66-4fe5-1ae57478a9e6.png)
👦 - 桃太郎
👴 - おじいさん
👵 - おばあさん
🐕 - 犬
🐒 - 猿
🐦 - キジ
👹 - 鬼

今回私は、**Markdown AI** を使って「相関図クイズゲーム」という遊べるWebアプリを制作しました！


制作したアプリは[こちら](https://mdown.ai/content/eb995fb2-c2a4-4772-bd17-08c52573f5d1) です！


この記事では、制作したクイズゲームの内容や、Markdown AIを使った制作プロセスについて詳しく紹介します。Markdown記法を駆使し、**ロボットAI** や**Mermaid**を活用するアイデアも盛り込みました。  
「Markdown AIって何？」という方にもわかりやすく、かつ楽しく読める記事を目指しています。

---

## 相関図クイズゲームとは？

「相関図クイズゲーム」は、ある作品のキャラクターや関係性をヒントに、その作品名を当てるシンプルなクイズです。  
クイズを解く中で、キャラクター同士の関係性やストーリーの奥深さも楽しむことができます！  

例えば、ジャンルを「国民的アニメ」に設定すると、「ドラえもん」や「クレヨンしんちゃん」などが出題される仕組みです。

### 主な機能
1. ジャンルを選んでスタートボタンをクリック！ジャンルは自分で入力することも可能！
2. クイズに挑戦（ヒントや相関図を確認しながら楽しめます）
3. 答えがわからない場合は答えを見る機能も完備！

---

## 制作に使用した技術

### Markdown AIを選んだ理由
Markdown AIは、QiitaなどでおなじみのMarkdown記法を使いながら、簡単にWebサイトやツールを作れるツールです。特に以下の点に魅力を感じました：
- **HTML/CSS、JavaScript対応**で高度なカスタマイズが可能。
- **ロボットAI**を使ったAIの組み込みがスムーズ。
- サーバーを設定せずにサイトを公開できる。

### 使用した主な技術や機能
- **Mermaid**  
  キャラクター相関図を作成するのに使用。関係性を視覚的に表現しました。
- **JavaScript**  
  ユーザーインタラクション（スタートボタンや次のクイズ機能）を実装。
- **Markdown AIのロボットAI**  
  クイズ生成プロセスで利用。プロンプトを入力するだけで、関係図やキャラクター情報を自動生成しました。

---

## 制作プロセス

1. **アイデア出し**
   - 「みんなが知っている作品をクイズにしたら面白そう」と思い、相関図を使ったゲームを企画。
   - ジャンルごとに出題作品を選定。

2. **Markdown AI環境で開発**
   - HTMLとJavaScriptを組み合わせてUIを作成。
   - Mermaidを使って相関図の描画に挑戦。
   - ロボットAIでクイズ生成を自動化。

3. **テストと改善**
   - クイズ生成時のエラーや、相関図が正しく表示されない問題をデバッグ。
   - ユーザーが操作しやすいインターフェースに微調整。

---

## Markdown AIを使った感想

### 良かった点
1. **手軽さとスピード**  
   サイト制作のハードルが大幅に下がりました。特に、サーバー不要で即公開できる点は画期的！

2. **AIの活用**  
   簡単に生成AI（gpt-4o-mini、Claude、Geminiなど）を組み込んだアプリが作れるのがとてもいいと思いました。

3. **無料**  
   AIを使用するためのAPIキーを発行する必要がなく、全て無料で使えるところが非常にありがたいです。

### もう少しだった点
1. **Mermaidのサポート**
    Markdown AIでは Mermaid の描画がサポートされているのですが、スクリプトで動的に Mermaid コードを作成した場合は描画されず、個別に JavaScript で Mermaid 描画用のライブラリを CDN からダウンロードして使用する必要がありました。

2. **ドキュメントの不足**
    今はβ版とのことで、まだドキュメントが十分に揃っていないように感じました。最初 Markdown AI で何ができるかがすぐには分からず、Qiitaの記事などを参考に把握することができました。

3. **ファイル数の制限**
    1つのサイトを作成するのに1ファイルだけしか作れないので、全てのコードを1ファイルにまとめる必要があります。

<details><summary>こんな感じで1ファイルにまとめています</summary>

```html
# 相関図クイズ!

<p>相関図の作品名を当てるクイズゲーム🤖</p>
<form id="genreForm">
    <select id="genre" name="genre">
        <option value="" disabled selected style="display:none;">ジャンルを選んでね！</option>
    </select>
</form>
<form id="customGenre">
    <input type="text" id="customGenreInput" placeholder="自分でジャンルを入力！">
</form>
<button class="button" type="button" id="button-start">スタート！</button>
<div style="display: none;" id="loading-section">
    <div class="loader"></div>
    <p id="text-loading">
        クイズ作成中...
        ちょっと待っててね!（もしかしたら1分くらいかかっちゃうかも🥺）
    </p>
</div>
<div id="diagram"></div>
<div class="buttons">
    <button class="button " type="button" id="button-hint" style="display: none;">💡 グループ分けを見る</button>
    <button class="button " type="button" id="button-answer" style="display: none;">👀 答えを見る </button>
</div>
<h3 id="answer"></h3>
<p id="emojiMap"></p>
<button class="button" style="display: none;" type="button" id="button-next">👉 次の問題</button>

<style>
    body {
        font-family: Arial, sans-serif;
        background-color: #F2EFE7;
        margin: 0;
        padding: 20px;
    }

    h1 {
        color: #777;
        font-size: 24px;
        margin-bottom: 10px;
    }

    p {
        color: #777;
        font-size: 16px;
    }

    form {
        margin: 10px 0;
    }

    select,
    input {
        font-size: 16px;
        padding: 8px;
        margin: 5px;
        border: 1px solid #ccc;
        border-radius: 5px;
        width: 250px;
    }

    .button {
        font-size: 18px;
        padding: 10px 20px;
        margin: 10px;
        border: none;
        border-radius: 5px;
        background-color: #48A6A7;
        color: #F2EFE7;
        cursor: pointer;
        transition: 0.3s;
    }

    .button:hover {
        background-color: #9ACBD0;
        color: #F2EFE7;
    }

    #loading-section {
        display: flex;
        flex-direction: column;
        align-items: center;
        justify-content: center;
        margin-top: 20px;
    }

    .loader {
        border: 5px solid #F2EFE7;
        border-top: 5px solid #2973B2;
        border-radius: 50%;
        width: 40px;
        height: 40px;
        animation: spin 1s linear infinite;
    }

    @keyframes spin {
        0% {
            transform: rotate(0deg);
        }

        100% {
            transform: rotate(360deg);
        }
    }

    #text-loading {
        font-size: 14px;
        color: #777;
        margin-top: 10px;
    }

    #diagram {
        margin-top: 20px;
        padding: 10px;
        border: 1px solid #ddd;
        background: white;
        min-height: 100px;
        border-radius: 5px;
    }

    .buttons {
        margin-top: 10px;
    }

    h3 {
        color: #777;
        font-size: 20px;
        margin-top: 20px;
    }
</style>

<script>
    document.title = "相関図クイズ!";
    document.querySelector('meta[property="og:site_name"]').setAttribute('content', '相関図クイズ!');
    async function loadMermaid() {
        const response = await fetch("https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.min.js");
        const scriptContent = await response.text();
        const scriptElement = document.createElement("script");
        scriptElement.textContent = scriptContent;
        document.head.appendChild(scriptElement);

        mermaid.initialize({ startOnLoad: true });

        const genres = [
            "国民的アニメ", "少年漫画", "少女漫画", "スポーツ漫画", "NHKアニメ", "日常系アニメ", "ジブリ", "ディズニー", "ディズニープリンセス", "ピクサー", "日本の昔話", "世界の昔話", "グリム童話", "ことわざ", "四字熟語", "古事成語", "絵本", "児童書", "国語の教科書に載ってる話", "日本の偉人伝", "世界の偉人伝", "ミステリー小説", "国民的ドラマ", "朝ドラ", "月9ドラマ", "90年代人気ドラマ", "00年代人気ドラマ", "2010年代人気ドラマ", "2020年代人気ドラマ", "バラエティ番組", "邦画", "洋画", "ハリウッド名作", "アメリカンコメディドラマ", "数学の定理"
        ];

        const form = document.getElementById('genreForm');
        const select = document.getElementById('genre');


        genres.forEach((genre) => {
            const option = document.createElement('option');
            option.value = genre;
            option.textContent = genre;
            select.appendChild(option);
        });

        form.appendChild(select);

        let quizzes = [];
        let currentQuizIndex = 0;
        let isCreatingQuiz = false;

        function cleanseJson(jsonString) {
            jsonString = jsonString.replace(/'/g, '"');
            jsonString = jsonString.replace(/,\s*([}\]])/g, '$1');
            jsonString = jsonString.replace(/([,:{\[]\s*)([a-zA-Z0-9_]+)\s*(?=\s*:)/g, '$1"$2"');
            jsonString = jsonString.replace(/\n/g, '\\n').replace(/\r/g, '\\r').replace(/\t/g, '\\t');
            return jsonString;
        }

        function parseCharacterData(input) {
            try {
                const jsonStart = input.indexOf("{");
                const jsonEnd = input.lastIndexOf("}") + 1;
                if (jsonStart === -1 || jsonEnd === 0) {
                    throw new Error("JSONが見つかりません");
                }

                const jsonString = input.substring(jsonStart, jsonEnd);
                const processedJsonString = cleanseJson(jsonString);
                const parsedData = JSON.parse(processedJsonString);
                const diagram = parsedData["相関図"] || "";
                let diagramWithoutSubgraph = "";

                // 相関図のセクションを解析
                if (diagram) {
                    diagramWithoutSubgraph = diagram.replace(/subgraph (.+) \[(.*?)\]/g, (match, p1, p2) => `subgraph ${p1} [　]`);
                }

                return {
                    relationships: parsedData["キャラクター同士の関係性"] || "",
                    diagram: diagram,
                    diagramWithoutSubgraph: diagramWithoutSubgraph,
                    emojiMap: parsedData["キャラクターと絵文字の対応表"] || ""
                };
            } catch (error) {
                console.error("データのパースに失敗しました:", error.message);
                return null;
            }
        }

        const startButton = document.getElementById('button-start');
        startButton.addEventListener('click', async event => {
            isCreatingQuiz = true;
            const selectedGenre = select.value;
            const customGenreInput = document.getElementById('customGenreInput').value;
            if (selectedGenre && customGenreInput) {
                alert('ジャンルを一つだけ選んでね！');
            } else if (selectedGenre || customGenreInput) {
                const genre = selectedGenre || customGenreInput;
                const loadingSection = document.getElementById('loading-section');
                loadingSection.style.display = 'flex';
                startButton.style.display = 'none';
                document.getElementById('diagram').innerHTML = "";
                const serverAi = new ServerAI();

                const prompt = `${genre}の人気の作品を10個教えてください。出力はカンマで区切り、それ以外の文章は加えないでください。出力例：「ドラえもん, クレヨンしんちゃん, ドラゴンボール, サザエさん, ちびまる子ちゃん, キン肉マン, ワンピース, ナルト, 名探偵コナン, あたしんち」`;
                const answer = await serverAi.getAnswerText('7M1Cgeu4c9xhSyGwfYMCux', '', prompt);

                const stories = answer.split(',').map(story => story.trim());


                try {
                    const quizPromises = stories.map(async (story, index) => {
                        const storyPrompt = `${genre}の「${story}」ついて、以下の出力例（ドラえもん）と同じ形式で、キャラクター同士の関係性とmaermaidの相関図とキャラクターと絵文字の対応表を作ってください。相関図の中の各キャラクターは絵文字1つで表現してください。\n注意点：\n1.嘘を書かないでください。あなたが持っている正確な情報の範囲で文章を生成してください。情報がない場合は「わかりません」のみ出力してください。2. maemaid記法は参考例を忠実に真似しシンタックスエラーが発生しないようにしてください。3. subgraphは必ず[]を使ってラベルをつけてください\n\n以下、ドラえもんを例にした出力形式\n{"キャラクター同士の関係性": "ドラえもんは22世紀からやってきたロボット猫で、のび太の良き相談相手であり支援者です。のび太は主人公で、成績が悪く運動も苦手ですが、優しい心を持っています。しずかちゃんはのび太の同級生で、のび太が好意を持っている女の子です。ジャイアンは力が強く、時にのび太をいじめる存在ですが、友情も持っています。スネ夫はジャイアンの取り巻きで、お金持ちの家庭の息子です。ドラミはドラえもんの妹で、時々未来からやってきて皆を手助けします。","相関図": "graph TD\\nsubgraph School [学校]\\nNobita[🧒]\\nShizuka[👧]\\nGian[👦]\\nSuneo[👦]\\nend\\n\\nsubgraph Future [未来]\\nDoraemon[🐱]\\nDorami[🐱]\\nend\\n\\nNobita <--親友--> Doraemon\\nNobita --好き--> Shizuka\\nNobita --いじめられる--> Gian\\nNobita --いじめられる--> Suneo\\nGian --手下--> Suneo\\nDoraemon --妹--> Dorami\\nDoraemon --助ける--> Nobita","キャラクターと絵文字の対応表": "🧒 - のび太\\n👧 - しずかちゃん\\n👦 - ジャイアン\\n👦 - スネ夫\\n🐱 - ドラえもん\\n🐱 - ドラミ"}`;
                        const storyAnswer = await serverAi.getAnswerText('7M1Cgeu4c9xhSyGwfYMCux', '', storyPrompt);
                        const parsedAnswer = parseCharacterData(storyAnswer);
                        if (parsedAnswer) {
                            quizzes.push({
                                story: story,
                                relationships: parsedAnswer.relationships,
                                diagram: parsedAnswer.diagram,
                                diagramWithoutSubgraph: parsedAnswer.diagramWithoutSubgraph,
                                emojiMap: parsedAnswer.emojiMap
                            });
                            loadingSection.style.display = 'none';
                            const buttonHint = document.getElementById('button-hint');
                            const buttonAnswer = document.getElementById('button-answer');

                            const isShowingNextButton = nextButton.style.display === 'block';

                            if (!isShowingNextButton) {
                                showQuiz(currentQuizIndex);
                            }
                        }
                    });

                    // バックグラウンドでリクエストが続行
                    await Promise.allSettled(quizPromises);
                    isCreatingQuiz = false;
                } catch (error) {
                    console.error("エラーが発生しました:", error.message);
                    alert("データの取得中にエラーが発生しました。もう一度試してください。");
                }
            } else {
                alert('ジャンルを選んでね！');
            }
        });

        // クイズの内容を表示
        function showQuiz(index) {
            if (quizzes.length > 0 && quizzes[index]) {

                buttonHint.style.display = 'block';
                buttonAnswer.style.display = 'block';
                nextButton.style.display = 'block';
                const quiz = quizzes[index];

                const diagramDiv = document.getElementById('diagram');
                diagramDiv.removeAttribute('data-processed');
                diagramDiv.innerHTML = quiz.diagramWithoutSubgraph;
                diagramDiv.classList.add("mermaid");
                mermaid.init();
            }
        }

        // 次の問題に進む
        const nextButton = document.getElementById('button-next');
        nextButton.addEventListener('click', () => {
            document.getElementById('answer').innerText = "";
            document.getElementById('emojiMap').innerText = "";
            currentQuizIndex++;
            if (currentQuizIndex < quizzes.length) {
                showQuiz(currentQuizIndex);
            } else {
                nextButton.style.display = 'none';
                buttonAnswer.style.display = 'none';
                buttonHint.style.display = 'none';
                const diagramDiv = document.getElementById('diagram');
                diagramDiv.removeAttribute('data-processed');
                diagramDiv.classList.remove("mermaid");
                if (isCreatingQuiz) {
                    document.getElementById('loading-section').style.display = 'flex';
                    diagramDiv.innerHTML = "";
                } else {
                    startButton.style.display = 'block';
                    select.value = '';
                    select.focus();
                    document.getElementById('customGenreInput').value = '';
                    diagramDiv.innerHTML = "全部のクイズが終わったよ👏\nジャンルを変えてまた遊んでみてね✨";
                }
            }
        });

        // ヒントを見るボタン
        const buttonHint = document.getElementById('button-hint');
        buttonHint.addEventListener('click', () => {
            const quiz = quizzes[currentQuizIndex];
            const diagramDiv = document.getElementById('diagram');
            diagramDiv.removeAttribute('data-processed');
            diagramDiv.innerHTML = quiz.diagram;
            diagramDiv.classList.add("mermaid");
            mermaid.init();
            buttonHint.style.display = 'none';
        });

        // 答えを見るボタン
        const buttonAnswer = document.getElementById('button-answer');
        buttonAnswer.addEventListener('click', () => {
            const quiz = quizzes[currentQuizIndex];
            document.getElementById('answer').innerText = `答えは「${quiz.story}」でした!`;
            document.getElementById('emojiMap').innerText = quiz.emojiMap;
            buttonAnswer.style.display = 'none';
        });

    }
    loadMermaid();
</script>
```

</details>

---

## まとめ

Markdown AIを使った「相関図クイズゲーム」の制作プロセスを紹介しました！  
このツールは、Markdown記法の手軽さと高度な機能を両立しており、クリエイターや開発者にとって強力な味方です。

今回のプロジェクトでは、「ロボットAI」と「Mermaid」を駆使して、クイズ生成の効率化と視覚的な楽しさを両立させました。ぜひ皆さんもMarkdown AIを使って、オリジナルコンテンツを制作してみてください！  

👇 Markdown AIはこちらから試せます👇  
[Markdown AI公式サイト](https://mdown.ai/)  

👇相関図クイズゲームはこちらから👇
[相関図クイズゲーム](https://mdown.ai/content/eb995fb2-c2a4-4772-bd17-08c52573f5d1)