สร้าง Web apps เกม + เกียรติบัตร + GG Sheet(MPA)

 



Multiple-page application

Code.gs

     
function doGet(e) {
  // อ่านค่าพารามิเตอร์ ?page=
  const page = e.parameter.page || 'index'; // ถ้าไม่ระบุ page จะโหลด index.html

  // ตรวจสอบว่ามีไฟล์นั้นในโปรเจกต์หรือไม่
  let template;
  try {
    template = HtmlService.createTemplateFromFile(page);
  } catch (error) {
    // ถ้าไม่มีไฟล์ ให้แสดงหน้า error
    template = HtmlService.createTemplateFromFile('404');
  }

  // แปลงเป็นหน้าเว็บ
  const html = template.evaluate()
    .setTitle("Project Kru Chian")
    .setFaviconUrl("https://semicon.github.io/img/web/logoxxx.png")
    .addMetaTag('viewport', 'width=device-width, initial-scale=1')
    .setXFrameOptionsMode(HtmlService.XFrameOptionsMode.ALLOWALL)
    .setSandboxMode(HtmlService.SandboxMode.IFRAME);

  return html;
}

function include(file){
  return HtmlService.createHtmlOutputFromFile(file).getContent()
}

function getScriptURL(){
  return ScriptApp.getService().getUrl()
}


function saveData(dataForm) {
  const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("data");
  const data = sheet.getDataRange().getDisplayValues();
  
  // ตรวจสอบว่ามี header หรือไม่
  const lastRow = data.length;
  
  // อ่านค่า ID ล่าสุดจากแถวสุดท้าย (คอลัมน์ที่ 2 => index 1)
  let newId = 1;
  if (lastRow > 1) { // มีข้อมูลแล้ว
    const lastId = Number(data[lastRow - 1][1]) || 0; // แถวสุดท้ายก่อนหน้า
    newId = lastId + 1;
  }

  const index = data.findIndex(row => row[3] === dataForm.email);
  
  if (index !== -1) {
    const sheetRow = index + 1; // แถวจริงในชีต
    Logger.log("Row found at: " + sheetRow);
    const idx = sheet.getRange("B"+sheetRow).getValue()
    sheet.getRange(sheetRow,1,1,sheet.getLastColumn()).setValues([
      [dataForm.ts,       // วันที่บันทึก
      idx,                // ID ที่สร้างใหม่
      dataForm.name,      // ตัวอย่างช่องชื่อ
      dataForm.email,     // ตัวอย่างช่องอีเมล
      dataForm.score,     // ตัวอย่างช่องคะแนน
      dataForm.level]])
      return { success: true, id: newId };
  }
  
  // บันทึกข้อมูลใหม่ลงแถวถัดไป
  sheet.appendRow([
      dataForm.ts,       // วันที่บันทึก
      newId,             // ID ที่สร้างใหม่
      dataForm.name,     // ตัวอย่างช่องชื่อ
      dataForm.email,    // ตัวอย่างช่องอีเมล
      dataForm.score,    // ตัวอย่างช่องคะแนน
      dataForm.level     // ตัวอย่างช่องด่าน
  ]);
  return { success: true, id: newId };
}

function getData(email) {
  const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("data");
  const data = sheet.getDataRange().getDisplayValues().slice(1); // ตัด header ออก

  // หาแถวที่คอลัมน์ที่ 3 (index 3 = คอลัมน์ D) ตรงกับ email
  const row = data.find(r => (r[3] === email && Number(r[5]) === 6));

  if (!row) {
    return { success: false, message: "ไม่พบข้อมูลของอีเมลนี้" };
  }

  return { success: true, data: row };
}

    

index.html

     
<!DOCTYPE html>
<html lang="th">

<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width,initial-scale=1" />
  <title>เกมตะลุยขุมทรัพย์วันปิยมหาราช</title>
  <link href="https://fonts.googleapis.com/css2?family=Pattaya&family=Sriracha&display=swap" rel="stylesheet">

  <style>
    :root {
      --bg: #0b1020;
      --panel: #0f1724;
      --accent: #ffd166;
      --muted: #cacaca;
      --retro: #4ad9a6
    }

    html,
    body {
      height: 100%;
      margin: 0;
      font-family: 'Sriracha', Arial, Helvetica, sans-serif;
      background: linear-gradient(180deg, #00121a 0%, #002233 70%);
      color: #e6eef6;
      background-repeat: no-repeat;
      background-attachment: fixed;
    }

    .container {
      max-width: 1080px;
      margin: 18px auto;
      padding: 18px
    }

    .game-card {
      background: linear-gradient(180deg, rgba(255, 255, 255, 0.03), rgba(0, 0, 0, 0.08));
      border: 4px solid #081424;
      padding: 18px;
      border-radius: 10px;
      box-shadow: 0 8px 28px rgba(0, 0, 0, 0.6)
    }

    header {
      display: flex;
      align-items: center;
      gap: 14px
    }

    .logo {
      width: 86px;
      height: 86px;
      background: repeating-linear-gradient(45deg, #0b2a3a 0 4px, #083445 4px 8px);
      border-radius: 8px;
      display: flex;
      align-items: center;
      justify-content: center;
      font-family: 'Sriracha', monospace;
      color: var(--accent);
      font-size: 13px;
      padding: 8px;
      box-shadow: inset 0 0 0 4px rgba(0, 0, 0, 0.2)
    }

    h1 {
      font-family: 'Pattaya', monospace;
      font-size: 18px;
      margin: 0;
      color: var(--accent);
      letter-spacing: 2px;
    }

    h3 {
      letter-spacing: 1.5px;
    }

    p.lead {
      margin: 6px 0 14px;
      color: var(--muted)
    }

    .board {
      display: grid;
      grid-template-columns: 1fr 300px;
      gap: 10px
    }

    .play-area {
      background: #081a2b;
      border-radius: 8px;
      padding: 12px;
      border: 3px solid #001;
      min-height: 360px;
    }

    .level-info {
      display: flex;
      justify-content: space-between;
      align-items: center;
      margin-bottom: 8px
    }

    .level-badge {
      background: linear-gradient(180deg, #2b5d86, #16394f);
      padding: 8px 12px;
      border-radius: 6px;
      font-weight: bold;
    }

    .question {
      background: #071826;
      border-radius: 6px;
      padding: 14px;
      border: 2px solid rgba(255, 255, 255, 0.03);
      min-height: 86px
    }

    .answers {
      display: grid;
      grid-template-columns: 1fr 1fr;
      gap: 10px;
      margin-top: 12px;
      color: #fff;
    }

    .ans-btn {
      background: linear-gradient(180deg, #122a3a, #071826);
      padding: 10px;
      border-radius: 6px;
      border: 2px solid rgba(255, 255, 255, 0.03);
      cursor: pointer;
      text-align: left;
      font-size: 1.25rem;
      color: #fff;
    }

    .ans-btn:hover {
      transform: translateY(-3px)
    }

    .ans-btn.correct {
      outline: 3px solid rgba(74, 217, 166, 0.6)
    }

    .ans-btn.wrong {
      opacity: 0.6;
      text-decoration: line-through
    }

    .side {
      background: linear-gradient(180deg, #071a2a, #04121a);
      padding: 10px;
      border-radius: 8px;
      border: 2px solid rgba(255, 255, 255, 0.03);
    }

    .score-box {
      background: linear-gradient(180deg, #001219, #05323f);
      padding: 12px;
      border-radius: 6px;
      margin-bottom: 10px;
      text-align: center
    }

    .pbar {
      height: 18px;
      background: #02131b;
      border-radius: 10px;
      overflow: hidden;
      border: 1px solid rgba(255, 255, 255, 0.03)
    }

    .pbar>i {
      display: block;
      height: 100%;
      background: linear-gradient(90deg, var(--retro), var(--accent));
      width: 0%;
      transition: width 600ms ease
    }

    .controls {
      display: flex;
      gap: 8px;
      flex-wrap: wrap;
      margin-top: 10px
    }

    button.btn {
      background: #0b2a3a;
      border: 2px solid rgba(255, 255, 255, 0.03);
      padding: 8px 10px;
      border-radius: 6px;
      color: var(--accent);
      cursor: pointer;
      font-size: 16px;
      font-family: 'Sriracha', Arial, Helvetica, sans-serif;
    }

    button.secondary {
      background: #021216;
      color: var(--muted);
      border: 1px solid #042
    }

    button.success {
      color: rgb(4, 245, 4);
      border: 1px solid #042
    }

    footer {
      margin-top: 18px;
      padding: 12px;
      text-align: center;
      color: var(--muted);
      font-size: 14px
    }

    /* modal */
    .modal {
      position: fixed;
      inset: 0;
      display: flex;
      align-items: center;
      justify-content: center;
      background: rgba(0, 0, 0, 0.6);
      z-index: 100;
      visibility: hidden;
      opacity: 0;
      transition: opacity .18s
    }

    .modal.show {
      visibility: visible;
      opacity: 1
    }

    .modal .window {
      background: #00131a;
      padding: 18px;
      border-radius: 8px;
      border: 3px solid #022;
      max-width: 560px;
      min-width: 300px;
    }

    .form-control {
      border: none;
      border-bottom: 1px solid rgb(0, 255, 255);
      font-size: 16px;
      padding: 5px 2px;
      background: #0000;
      color: #fff;
      width: 100%;
      font-family: 'Sriracha', Arial, Helvetica, sans-serif;
      font-size: 16px;
    }

    .form-label {
      font-family: 'Sriracha', Arial, Helvetica, sans-serif;
      font-size: 16px;
      margin-right: 10px;
    }

    /* summary table */
    table {
      min-width: 790px;
      width: 100%;
      border-collapse: collapse;
      color: #dfeaf3;
      font-family: 'Sriracha', Arial, Helvetica, sans-serif;
      font-size: 16px;
    }

    th {
      letter-spacing: 1.5px;
    }

    table th,
    table td {
      padding: 8px;
      border-bottom: 1px dashed rgba(255, 255, 255, 0.04);
      text-align: left;
      font-size: 16px
    }

    .retro-grid {
      display: grid;
      grid-template-columns: repeat(6, 1fr);
      gap: 6px;
      margin: 10px 0;
    }

    .cell {
      background: #081a2b;
      border: 2px solid #021;
      padding: 6px;
      border-radius: 4px;
      text-align: center;
    }

    @media(max-width:900px) {
      .container {
        padding: 10px;
      }
      .game-card {
        padding: 5px;
      }

      .board {
        grid-template-columns: 1fr;
      }

      .side {
        order: 2
      }
    }
  </style>
</head>

<body>
  <div class="container">
    <div class="game-card">
      <header>
        <div class="logo"><img src="https://semicon.github.io/img/web/logoxxx.png" width="80"></div>
        <div>
          <h1>เกมตะลุยขุมทรัพย์วันปิยมหาราช</h1>
          <p class="lead">ตอบคำถามเกี่ยวกับพระบาทสมเด็จพระจุลจอมเกล้าเจ้าอยู่หัว (ร.5) 6 ด่าน ด่านละ 5 ข้อ
            ข้อละ 10 คะแนน</p>
        </div>
      </header>

      <div class="board">
        <section class="play-area">
          <div class="level-info">
            <div class="level-badge">ด่าน <span id="levelNum">1</span> /6</div>
            <div>คำถาม <span id="qIndex">1</span>/5</div>
          </div>

          <div class="question" id="questionBox">กดปุ่ม เริ่มเกม เพื่อเริ่มการผจญภัย</div>

          <div class="answers" id="answers"></div>

          <div class="controls">
            <button class="btn success" id="startBtn">เริ่มเกม</button>
            <button class="btn success" id="nextBtn" disabled>ข้อต่อไป</button>
            <button class="btn" id="hintBtn">ขอคำใบ้ (-5 คะแนน)</button>
            <button class="btn" id="summaryBtn">สรุปสถิติ</button>
          </div>

          <div style="margin-top:12px;color:var(--muted);font-size:14px">เคล็ดลับ: คำตอบจะสุ่มตำแหน่ง (ก ข ค
            ง) ทุกครั้งที่เริ่มใหม่</div>
        </section>

        <aside class="side">
          <div class="score-box">
            <div style="font-size:16px;color:var(--muted);">คะแนน</div>
            <div style="font-size:30px;font-weight:bold;color:var(--accent)"><span id="score">0</span></div>
            <div style="font-size:16px;color:var(--muted)">เป้าหมายรวม: <span id="target">300</span> คะแนน
            </div>
          </div>

          <div style="margin-bottom:8px;color:var(--muted);font-size:13px">ความคืบหน้า (รวม)</div>
          <div class="pbar" aria-hidden><i id="progressFill"></i></div>

          <div style="margin-top:10px;color:var(--muted);font-size:16px">สถานะด่าน</div>
          <div class="retro-grid" id="levelGrid"></div>

          <div style="margin-top:12px;font-size:16px;color:var(--muted)">เวลาเล่น: <span
                            id="timer" style="color: rgb(4, 245, 4);">00:00</span></div>
          <div style="margin-top:6px; text-align: center;">
            <button class="btn" id="saveStatBtn" style="width:45%; margin-top: 5px;">บันทึกสถิติ</button>
            <button class="btn secondary" id="clearStatBtn" style="width:45%; margin-top: 5px;">ลบสถิติ</button>
            <button class="btn success" style="width:90%; margin-top: 10px;" id="downloadBtn">ดาวน์โหลดเกียรติบัตร</button>
          </div>
        </aside>
      </div>

      <div style="margin-top:12px">
        <h3 style="margin:8px 0 6px">ตารางสรุปสถิติ </h3>

        <div
          style="overflow:auto; max-height:220px;background:#02141a;padding:8px;border-radius:6px;border:1px solid rgba(255,255,255,0.03)">
          <table id="statsTable">
            <thead>
              <tr>
                <th>เวลาเล่น</th>
                <th>คะแนน</th>
                <th>ด่านถึง</th>
                <th>ใช้เวลา</th>
              </tr>
            </thead>
            <tbody></tbody>
          </table>
        </div>
      </div>

      <footer>@2025 เกมตะลุยขุมทรัพย์วันปิยมหาราช | จัดทำโดย Dr.Wichian Ph.</footer>
    </div>
  </div>

  <!-- modal -->
  <div class="modal" id="modal">
    <div class="window">
      <div id="modalContent"></div>
      <div style="text-align:right;margin-top:10px"><button class="btn" id="modalOk">ตกลง</button></div>
    </div>
  </div>

  <!-- modalx -->
  <div class="modal" id="modalx">
    <div class="window">
      <div id="modalContentx"></div>
      <div style="text-align:right;margin-top:10px">
        <button class="btn secondary" onclick="closeModalx()">ยกเลิก</button>
        <button class="btn" id="modalxOk">ตกลง</button>
      </div>
    </div>
  </div>

  <script>
    const url = '<?!= getScriptURL() ?>';
    console.log(url)
    /* ====== ข้อมูลคำถาม (30 ข้อ) — 6 ด่าน ด่านละ 5 ข้อ ======
           แต่ละคำถาม: {q: '...', choices:[{t:'ก...', correct:false},...]} 
        */
        const QUESTIONS = [
            // ด่าน 1
            {
                q: 'พระราชสมภพของพระบาทสมเด็จพระจุลจอมเกล้าเจ้าอยู่หัว (ร.5) ตรงกับวันที่ใด?', choices: [
                    { t: '20 กันยายน 2396 (1853)', correct: true },
                    { t: '23 ตุลาคม 2453 (1910)', correct: false },
                    { t: '6 เมษายน 2464 (1921)', correct: false },
                    { t: '1 มกราคม 2400 (1857)', correct: false }
                ]
            },
            {
                q: 'วันปิยมหาราช ตรงกับวันที่ใดของปีไทย?', choices: [
                    { t: '23 ตุลาคม', correct: true },
                    { t: '13 เมษายน', correct: false },
                    { t: '5 ธันวาคม', correct: false },
                    { t: '12 สิงหาคม', correct: false }
                ]
            },
            {
                q: 'พระนามเต็มของรัชกาลที่ 5 คืออะไร (ที่ใช้กันทั่วไป)?', choices: [
                    { t: 'พระบาทสมเด็จพระจุลจอมเกล้าเจ้าอยู่หัว', correct: true },
                    { t: 'พระบาทสมเด็จพระปกเกล้าเจ้าอยู่หัว', correct: false },
                    { t: 'พระบาทสมเด็จพระมงกุฎเกล้าเจ้าอยู่หัว', correct: false },
                    { t: 'สมเด็จพระนารายณ์', correct: false }
                ]
            },
            {
                q: 'หนึ่งในผลงานสำคัญของรัชกาลที่ 5 คือการปฏิรูปอะไรในประเทศ?', choices: [
                    { t: 'การปฏิรูปการปกครองและระบบราชการ', correct: true },
                    { t: 'การย้ายเมืองหลวง', correct: false },
                    { t: 'การปิดชายแดนทั้งหมด', correct: false },
                    { t: 'การแบนการศึกษา', correct: false }
                ]
            },
            {
                q: 'พระองค์ทรงเป็นที่รู้จักจากการทำอะไรเพื่อชาติ?', choices: [
                    { t: 'ยกเลิกการเป็นทาสและปฏิรูประบบแรงงาน', correct: true },
                    { t: 'สร้างกำแพงเมืองใหม่', correct: false },
                    { t: 'นำเข้าหมากฝรั่ง', correct: false },
                    { t: 'ยกเลิกการศึกษา', correct: false }
                ]
            },
            // ด่าน 2
            {
                q: 'รัชกาลที่ 5 ทรงเสด็จประพาสต่างประเทศเพื่ออะไรสำคัญ?', choices: [
                    { t: 'ศึกษาระบบการปกครองและเจรจาฝ่ายนโยบาย', correct: true },
                    { t: 'เที่ยวพักผ่อนและช็อปปิ้ง', correct: false },
                    { t: 'หนีสงคราม', correct: false },
                    { t: 'ย้ายครอบครัวไปอยู่ยุโรป', correct: false }
                ]
            },
            {
                q: 'สัญลักษณ์หรือมรดกที่ตั้งชื่อตามรัชกาลที่ 5 ในปัจจุบันคืออะไร?', choices: [
                    { t: 'จุฬาลงกรณ์มหาวิทยาลัย (ชื่อมาจากพระนาม)', correct: true },
                    { t: 'มหาวิทยาลัยธรรมศาสตร์', correct: false },
                    { t: 'สวนลุมพินี', correct: false },
                    { t: 'พิพิธภัณฑสถานแห่งชาติ', correct: false }
                ]
            },
            {
                q: 'รัชกาลที่ 5 เสด็จสวรรคตเมื่อใด?', choices: [
                    { t: '23 ตุลาคม 2453 (1910)', correct: true },
                    { t: '20 กันยายน 2396 (1853)', correct: false },
                    { t: '1 มกราคม 2460 (1917)', correct: false },
                    { t: '12 สิงหาคม 2500 (1957)', correct: false }
                ]
            },
            {
                q: 'พระองค์ได้รับพระราชสมัญญาในภาษาไทยว่าอย่างไรเมื่อกล่าวถึงความเมตตา?', choices: [
                    { t: 'พระปิยมหาราช', correct: true },
                    { t: 'พระมหากษัตริย์ผู้ยิ่งใหญ่', correct: false },
                    { t: 'พระราชาผู้สงคราม', correct: false },
                    { t: 'พระราชาผู้ปกครอง', correct: false }
                ]
            },
            {
                q: 'รัชกาลที่ 5 ทรงริเริ่มระบบอะไรเพื่อเพิ่มประสิทธิภาพการบริหาร?', choices: [
                    { t: 'ระบบกระทรวงแบบสมัยใหม่', correct: true },
                    { t: 'ระบบค่ายทหารแบบใหม่', correct: false },
                    { t: 'ระบบไปรษณีย์เท่านั้น', correct: false },
                    { t: 'ระบบภาษีเดียว', correct: false }
                ]
            },
            // ด่าน 3
            {
                q: 'รัชกาลที่ 5 ทรงส่งเสริมด้านใดเพื่อความเจริญของประเทศ?', choices: [
                    { t: 'การศึกษาและการแพทย์', correct: true },
                    { t: 'การปิดเมือง', correct: false },
                    { t: 'การแยกครอบครัว', correct: false },
                    { t: 'การลดการค้า', correct: false }
                ]
            },
            {
                q: 'หนึ่งในพระราชกรณียกิจคือการสร้างถนนและระบบคมนาคมเพื่ออะไร?', choices: [
                    { t: 'เชื่อมโยงส่วนต่างๆ ของประเทศและพัฒนาเศรษฐกิจ', correct: true },
                    { t: 'เพื่อทหารเท่านั้น', correct: false },
                    { t: 'เพื่อจัดงานเฉลิมฉลอง', correct: false },
                    { t: 'เพื่อทำสวนสาธารณะ', correct: false }
                ]
            },
            {
                q: 'พระองค์มีพระราชโอรสธิดาจำนวนมาก — สิ่งนี้สะท้อนถึงอะไรในยุคนั้น?', choices: [
                    { t: 'ความสัมพันธ์ระหว่างราชวงศ์และตระกูลต่างๆ', correct: true },
                    { t: 'การย้ายประชากร', correct: false },
                    { t: 'การเปลี่ยนแปลงธงชาติ', correct: false },
                    { t: 'การรบกับประเทศเพื่อนบ้าน', correct: false }
                ]
            },
            {
                q: 'รัชกาลที่ 5 ทรงสนับสนุนการนำเทคโนโลยีจากประเทศใดเข้ามาในสยามมากขึ้น?', choices: [
                    { t: 'ยุโรป (เช่น อังกฤษ ฝรั่งเศส)', correct: true },
                    { t: 'อเมริกาใต้', correct: false },
                    { t: 'แอฟริกา', correct: false },
                    { t: 'ออสเตรเลีย', correct: false }
                ]
            },
            {
                q: 'พระราชพิธีหรือสิ่งก่อสร้างสำคัญที่พระองค์ทรงมีบทบาทด้านศิลปะคืออะไร?', choices: [
                    { t: 'สถาปัตยกรรมพระบรมมหาราชวัง และอาคารยุคใหม่', correct: true },
                    { t: 'การออกแบบธงชาติไทย', correct: false },
                    { t: 'การประดิษฐ์ดนตรีสมัยใหม่', correct: false },
                    { t: 'การสร้างยุ้งฉาง', correct: false }
                ]
            },
            // ด่าน 4
            {
                q: 'เหตุใดไทยจึงระลึกถึงวันปิยมหาราช?', choices: [
                    { t: 'เพื่อรำลึกถึงพระมหากรุณาธิคุณและการปฏิรูปของพระองค์', correct: true },
                    { t: 'เพื่อต้อนรับฝรั่ง', correct: false },
                    { t: 'เพื่อฉลองเก็บเกี่ยว', correct: false },
                    { t: 'เพื่อการค้าพาณิชย์', correct: false }
                ]
            },
            {
                q: 'รัชกาลที่ 5 ทรงยกเลิกระบบอะไรที่เกี่ยวกับแรงงานซึ่งเป็นส่วนหนึ่งของการเปลี่ยนแปลงสังคม?', choices: [
                    { t: 'การเป็นทาส/ระบบไพร่', correct: true },
                    { t: 'การเกณฑ์แรงงาน', correct: false },
                    { t: 'การค้าเสรี', correct: false },
                    { t: 'การศึกษาไม่บังคับ', correct: false }
                ]
            },
            {
                q: 'พระราชประวัติของรัชกาลที่ 5 มักจะถูกสอนในหัวข้อใดของโรงเรียน?', choices: [
                    { t: 'ประวัติศาสตร์และพลเมืองศึกษา', correct: true },
                    { t: 'คณิตศาสตร์', correct: false },
                    { t: 'ฟิสิกส์', correct: false },
                    { t: 'ศิลปะการต่อสู้', correct: false }
                ]
            },
            {
                q: 'พระองค์ทรงทำให้ระบบกฎหมายเป็นอย่างไร?', choices: [
                    { t: 'เป็นระบบที่มีการจัดการและสอดคล้องตามสมัยใหม่มากขึ้น', correct: true },
                    { t: 'เลิกใช้กฎหมายทั้งหมด', correct: false },
                    { t: 'เพิ่มกฎหมายให้ยากขึ้นสำหรับการค้า', correct: false },
                    { t: 'เปลี่ยนภาษาในการพิจารณาคดี', correct: false }
                ]
            },
            {
                q: 'รัชกาลที่ 5 มีบทบาทสำคัญในการติดต่อกับใครเพื่อปกป้องเอกราชของประเทศ?', choices: [
                    { t: 'ชาติตะวันตก เช่น อังกฤษและฝรั่งเศส', correct: true },
                    { t: 'อาณาจักรโบราณจากทิศตะวันออก', correct: false },
                    { t: 'ชนพื้นเมืองอเมริกา', correct: false },
                    { t: 'อาณาจักรในแอฟริกา', correct: false }
                ]
            },
            // ด่าน 5
            {
                q: 'ชื่อวัน "ปิยมหาราช" สื่อความหมายว่าอย่างไร?', choices: [
                    { t: 'พระมหากรุณาธิคุณอันยิ่งใหญ่ที่ทรงมีต่อปวงชน', correct: true },
                    { t: 'วันเฉลิมฉลองความมั่งคั่ง', correct: false },
                    { t: 'วันการแข่งขันกีฬา', correct: false },
                    { t: 'วันสวนสาธารณะ', correct: false }
                ]
            },
            {
                q: 'พระองค์ทรงสนับสนุนการศึกษาในรูปแบบใด?', choices: [
                    { t: 'การจัดตั้งระบบโรงเรียนและการส่งเสริมการศึกษาสำหรับประชาชน', correct: true },
                    { t: 'การเลิกสอนภาษาไทย', correct: false },
                    { t: 'การห้ามศึกษาในต่างประเทศ', correct: false },
                    { t: 'การเน้นเฉพาะศิลปะ', correct: false }
                ]
            },
            {
                q: 'สัญลักษณ์สมัยรัชกาลที่ 5 ที่ยังปรากฏในปัจจุบันคืออะไร?', choices: [
                    { t: 'อาคารสมัยรัตนโกสินทร์ และชื่อสถานศึกษา', correct: true },
                    { t: 'ธงชาติสากล', correct: false },
                    { t: 'การปกครองแบบสภา', correct: false },
                    { t: 'สกุลเงินใหม่ทั้งหมด', correct: false }
                ]
            },
            {
                q: 'การปฏิรูปของรัชกาลที่ 5 ส่งผลให้เกิดอะไรในระบบภายในประเทศ?', choices: [
                    { t: 'การรวมศูนย์อำนาจและระบบราชการสมัยใหม่', correct: true },
                    { t: 'การแยกอำนาจอย่างรุนแรง', correct: false },
                    { t: 'การยุบสภา', correct: false },
                    { t: 'การเพิ่มภาษีเพียงอย่างเดียว', correct: false }
                ]
            },
            {
                q: 'พระราชกรณียกิจของรัชกาลที่ 5 มักถูกยกย่องในด้านใด?', choices: [
                    { t: 'การทันสมัยและความเมตตาต่อประชาชน', correct: true },
                    { t: 'การเปิดสงครามใหญ่', correct: false },
                    { t: 'การปิดประเทศ', correct: false },
                    { t: 'การทำลายศิลปะ', correct: false }
                ]
            },
            // ด่าน 6
            {
                q: 'ประเพณีการรำลึกวันปิยมหาราช มักกระทำอย่างไร?', choices: [
                    { t: 'จัดพิธีและกิจกรรมเพื่อรำลึกและสำนึกในพระมหากรุณาธิคุณ', correct: true },
                    { t: 'เปิดเทศกาลดนตรี', correct: false },
                    { t: 'วันช็อปปิ้งลดราคา', correct: false },
                    { t: 'ปิดสถานศึกษา', correct: false }
                ]
            },
            {
                q: 'รัชกาลที่ 5 ทรงเป็นต้นแบบของพระมหากษัตริย์ในด้านใด?', choices: [
                    { t: 'การพัฒนาประเทศและปกป้องเอกราช', correct: true },
                    { t: 'การขาดการติดต่อระหว่างประเทศ', correct: false },
                    { t: 'การสถาปนาอาณาจักรใหม่', correct: false },
                    { t: 'การห้ามเดินเรือ', correct: false }
                ]
            },
            {
                q: 'เหตุใดเราจึงควรศึกษาประวัติของรัชกาลที่ 5?', choices: [
                    { t: 'เพราะเป็นแบบอย่างของการปฏิรูปและการพัฒนาประเทศ', correct: true },
                    { t: 'เพื่อให้ได้รางวัล', correct: false },
                    { t: 'เพื่อละเลยอดีต', correct: false },
                    { t: 'เพื่อเลียนแบบทุกอย่าง', correct: false }
                ]
            },
            {
                q: 'วันปิยมหาราช เป็นวันที่คนไทยมาทำอะไรด้วยความเคารพ?', choices: [
                    { t: 'วางพวงมาลาและจัดพิธีรำลึก', correct: true },
                    { t: 'เล่นน้ำกันทั้งประเทศ', correct: false },
                    { t: 'ปาร์ตี้กลางคืน', correct: false },
                    { t: 'ซื้อของลดราคา', correct: false }
                ]
            },
            {
                q: 'ข้อความใดต่อไปนี้เหมาะสมสำหรับการอธิบายรัชกาลที่ 5?', choices: [
                    { t: 'พระมหากษัตริย์ผู้ริเริ่มการปฏิรูปและพัฒนาชาติ', correct: true },
                    { t: 'นักเดินทางเพียงอย่างเดียว', correct: false },
                    { t: 'นักประพันธ์เพลงเพียงคนเดียว', correct: false },
                    { t: 'ผู้นำกองทัพที่ชนะทุกสงคราม', correct: false }
                ]
            }
        ];

        /* ====== ตัวแปรเกม ====== */
        const LEVELS = 6; const Q_PER_LEVEL = 5; const POINTS_PER_Q = 10; const TOTAL_TARGET = LEVELS * Q_PER_LEVEL * POINTS_PER_Q; // 300

        let gameState = { level: 1, qIndex: 0, score: 0, asked: [], startTs: null, elapsed: 0, shuffledLevels: [] };

        /* ====== Helpers ====== */
        function $(id) { 
            return document.getElementById(id) 
        }

        function showModal(html) {
            $('modalContent').innerHTML = html; $('modal').classList.add('show'); 
        }

        function showModalx(html) {
            $('modalContentx').innerHTML = html; $('modalx').classList.add('show'); 
        }

        function closeModal() {
            $('modal').classList.remove('show'); 
        }

        function closeModalx() {
            $('modalx').classList.remove('show'); 
        }

        function formatTime(sec) {
            const m = Math.floor(sec / 60).toString().padStart(2, '0'); 
            const s = Math.floor(sec % 60).toString().padStart(2, '0'); 
            return `${m}:${s}` 
        }

        /* ====== Initialize UI ====== */
        const startBtn = $('startBtn'), nextBtn = $('nextBtn'), hintBtn = $('hintBtn'), summaryBtn = $('summaryBtn');
        const questionBox = $('questionBox'), answersEl = $('answers');
        const scoreEl = $('score'), levelNumEl = $('levelNum'), qIndexEl = $('qIndex');
        const progressFill = $('progressFill'), timerEl = $('timer');
        const levelGrid = $('levelGrid');
        const saveStatBtn = $('saveStatBtn'), clearStatBtn = $('clearStatBtn');

        // populate level grid
        for (let i = 1; i <= LEVELS; i++) { const d = document.createElement('div'); d.className = 'cell'; d.id = 'cell' + i; d.textContent = `${i}`; levelGrid.appendChild(d) }

        /* ====== Game flow ====== */
        function startGame() {
            // prepare shuffled levels (we'll create slices of QUESTIONS per level but shuffle order of levels and questions)
            gameState.level = 1; gameState.qIndex = 0; gameState.score = 0; gameState.asked = []; gameState.startTs = Date.now(); gameState.elapsed = 0;
            // split QUESTIONS into levels (in original order) then shuffle order of levels and questions within each
            const levelsArr = [];
            for (let i = 0; i < LEVELS; i++) { const start = i * Q_PER_LEVEL; const slice = QUESTIONS.slice(start, start + Q_PER_LEVEL).map(q => structuredClone(q)); shuffleArray(slice); levelsArr.push(slice) }
            shuffleArray(levelsArr);
            gameState.shuffledLevels = levelsArr;
            updateUI();
            renderQuestion();
            startTimer();
            showModal(`<strong>ยินดีต้อนรับ!</strong><br>คุณจะต้องผ่าน 6 ด่าน ด่านละ 5 คำถาม ข้อละ 10 คะแนน รวมเป้าหมาย ${TOTAL_TARGET} คะแนน<br>ตำแหน่งตัวเลือก (ก,ข,ค,ง) จะสุ่มทุกครั้ง เริ่มได้เลยหรือไม่?`);
        }

        function renderQuestion() {
            const lvl = gameState.level; const qIdx = gameState.qIndex;
            levelNumEl.textContent = lvl; qIndexEl.textContent = qIdx + 1;
            const qObj = gameState.shuffledLevels[lvl - 1][qIdx];
            questionBox.innerHTML = qObj.q;
            // shuffle choices and map to ก ข ค ง
            const choices = structuredClone(qObj.choices); shuffleArray(choices);
            answersEl.innerHTML = '';
            const labels = ['ก', 'ข', 'ค', 'ง'];
            choices.forEach((c, i) => {
                const btn = document.createElement('button'); btn.className = 'ans-btn'; btn.innerHTML = `<div style="font-family:'Sriracha',monospace">${labels[i]}</div><div style="font-family:'Sriracha'; font-size:16px;margin-top:6px">${c.t}</div>`;
                btn.onclick = () => selectAnswer(btn, c, choices);
                answersEl.appendChild(btn);
            });
            // set buttons
            nextBtn.disabled = true; hintBtn.disabled = false;
        }

        function selectAnswer(btn, choice, allChoices) {
            // disable all
            const btns = answersEl.querySelectorAll('button'); btns.forEach(b => b.disabled = true);
            // show correct / wrong
            btn.classList.add(choice.correct ? 'correct' : 'wrong');
            // reveal correct one
            if (!choice.correct) { for (const b of btns) { if (b !== btn) { const txt = b.innerText || b.textContent; if (allChoices.some(c => c.t === b.querySelector('div:last-child').textContent && c.correct)) { b.classList.add('correct') } } } }

            if (choice.correct) { gameState.score += POINTS_PER_Q; }
            else { gameState.score = Math.max(0, gameState.score - 0); }
            updateUI();
            nextBtn.disabled = false;
        }

        function nextQuestion() {
            // advance
            gameState.qIndex++;
            if (gameState.qIndex >= Q_PER_LEVEL) { // level complete
                markLevelComplete(gameState.level);
                if (gameState.level >= LEVELS) { endGame(); return; }
                gameState.level++; gameState.qIndex = 0;
                showModal(`<strong>ยินดีด้วย!</strong><br>คุณผ่านด่านที่ ${gameState.level - 1} แล้ว! ไปยังด่านต่อไปหรือไม่?`);
            }
            renderQuestion();
        }

        function markLevelComplete(n) { const el = $('cell' + n); if (el) el.style.background = 'linear-gradient(180deg,#193c3a,#06261f)'; }

        function endGame() {
            stopTimer(); updateUI(); showModal(`<strong>จบเกม</strong><br>คะแนนรวมของคุณ: ${gameState.score} / ${TOTAL_TARGET}<br>บันทึกสถิติลง localStorage ได้ที่ปุ่ม "บันทึกสถิติ"`);
        }

        function giveHint() { // cost -5
            if (gameState.score < 5) { showModal('คะแนนไม่พอใช้คำใบ้ (ต้องมีอย่างน้อย 5 คะแนน)'); return; }
            gameState.score = Math.max(0, gameState.score - 5);
            updateUI();
            // hint: mark one wrong option as disabled
            const btns = [...answersEl.querySelectorAll('button')];
            const wrongs = btns.filter(b => !b.classList.contains('correct'));
            if (wrongs.length > 0) { wrongs[Math.floor(Math.random() * wrongs.length)].disabled = true; }
        }

        /* ====== Utilities ====== */
        function shuffleArray(a) { for (let i = a.length - 1; i > 0; i--) { const j = Math.floor(Math.random() * (i + 1));[a[i], a[j]] = [a[j], a[i]] } }

        /* ====== UI updates ====== */
        function updateUI() {
            scoreEl.textContent = gameState.score; progressFill.style.width = `${Math.min(100, Math.round(gameState.score / TOTAL_TARGET * 100))}%`;
            levelNumEl.textContent = gameState.level; qIndexEl.textContent = gameState.qIndex + 1;
        }

        /* ====== Timer ====== */
        let timerInterval = null;
        function startTimer() { if (timerInterval) clearInterval(timerInterval); gameState.startTs = gameState.startTs || Date.now(); timerInterval = setInterval(() => { gameState.elapsed = Math.floor((Date.now() - gameState.startTs) / 1000); timerEl.textContent = formatTime(gameState.elapsed); }, 500); }
        function stopTimer() { if (timerInterval) clearInterval(timerInterval); timerInterval = null; }

        /* ====== Stats (localStorage) ====== */
        function loadStats() { 
            const raw = localStorage.getItem('piyama_stats'); 
            return raw ? JSON.parse(raw) : [];
         }
        function saveStats() { 
            showModalx(`<div style="padding: 5px;">
              <div  style="width: 100%;">
                <label for="fname" class="form-label">ชื่อ นามสกุล</label><br>
                <input type="text" class="form-control" id="fname" placeholder="นายตัวอย่าง สกุลตัวอย่าง">
              </div>
              <div style="padding-top:10px;">
                <label for="email" class="form-label">อีเมล</label><br>
                <input type="text" class="form-control" id="email" placeholder="exam@examp.com" style="max-width: 290px;">
              </div>
            </div>`);
        }
        function clearStats() { 
            localStorage.removeItem('piyama_stats'); 
            renderStatsTable(); showModal('ลบสถิติทั้งหมดแล้ว'); 
        }
        function renderStatsTable() { const stats = loadStats(); const tbody = $('statsTable').querySelector('tbody'); tbody.innerHTML = ''; for (const s of stats) { const tr = document.createElement('tr'); tr.innerHTML = `<td>${s.ts}</td><td>${s.score}</td><td>ด่าน ${s.levelReached}</td><td>${formatTime(s.timeSec)}</td>`; tbody.appendChild(tr); } }

        /* ====== Events ====== */
        $('downloadBtn').addEventListener('click', () => { linkPdf(); });
        startBtn.addEventListener('click', () => { startGame(); });
        nextBtn.addEventListener('click', () => { nextQuestion(); });
        hintBtn.addEventListener('click', () => { giveHint(); });
        summaryBtn.addEventListener('click', () => { renderStatsTable(); showModal(`<strong>สรุปสถิติ</strong><br>จำนวนครั้งที่เล่น: ${loadStats().length}`); });
        $('modalOk').addEventListener('click', () => { closeModal(); renderQuestion(); });
        $('modalxOk').addEventListener('click', () => { 
            const name = $('modalContentx').querySelector('#fname').value;
            const email = $('modalContentx').querySelector('#email').value;
                        
            // --- ตรวจสอบและแจ้งเตือน ---
            if (!isValidFullName(name)) {
              alert("❌ กรุณากรอกชื่อ–นามสกุลให้ถูกต้อง (เช่น สมชาย ใจดี หรือ Somchai Jaidee)");
              return;
            }
            if (!isValidEmail(email)) {
              alert("❌ กรุณากรอกอีเมลให้ถูกต้อง (เช่น example@email.com)");
              return;
            }

            const formdata ={
              ts: new Date().toLocaleString('th-TH'), 
              score: gameState.score,
              level: gameState.level,
              name: name,
              email: email
            }
            const rec = { 
              ts: new Date().toLocaleString('th-TH'), 
              score: gameState.score, 
              levelReached: gameState.level, 
              timeSec: gameState.elapsed 
            }; 
            
            google.script.run.withSuccessHandler(res=>{
              if(res.success){
                closeModalx();
                const stats = loadStats(); 
                stats.unshift(rec); 
                localStorage.setItem('piyama_stats', JSON.stringify(stats)); 
                renderStatsTable();
                showModal('บันทึกเสร็จสมบูรณ์');
              }
            }).saveData(formdata)
            
        });
        saveStatBtn.addEventListener('click', () => { if(gameState.score > 0) saveStats(); });
        clearStatBtn.addEventListener('click', () => { if (confirm('ลบสถิติทั้งหมด?')) clearStats(); });

        // ensure modal dismiss on click outside
        document.getElementById('modal').addEventListener('click', (e) => { if (e.target.id === 'modal') closeModal(); });

        // initial render
        renderStatsTable(); updateUI();

        // --- ตรวจสอบชื่อ-นามสกุล ---
        function isValidFullName(fullName) {
          // ต้องมีอย่างน้อย 2 คำ และแต่ละคำต้องมีตัวอักษรไทยหรืออังกฤษ
          const namePattern = /^[A-Za-zก-ฮะ-๙]+(\s+[A-Za-zก-ฮะ-๙]+)+$/;
          return namePattern.test(fullName);
        }

        // --- ตรวจสอบอีเมล ---
        function isValidEmail(email) {
          const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
          return emailPattern.test(email);
        }

        function linkPdf() {
          window.open(url+'?page=pdf','_top')
        }

  </script>
</body>

</html>

    

pdf.html

     
<!DOCTYPE html>
<html lang="th">
<head>
  <meta charset="UTF-8">
  <title>Certificate PDF with pdfmake</title>
   <link href="https://fonts.googleapis.com/css2?family=Pattaya&family=Sriracha&display=swap" rel="stylesheet">
  <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css" rel="stylesheet">
  <!-- pdfmake -->
  <script src="https://cdnjs.cloudflare.com/ajax/libs/pdfmake/0.2.7/pdfmake.min.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/pdfmake/0.2.7/vfs_fonts.js"></script>
  <style>
    html,
    body {
      height: 100%;
      margin: 0;
      font-family: 'Sriracha', Arial, Helvetica, sans-serif;
      background: linear-gradient(180deg, #00121a 0%, #002233 70%);
      color: #e6eef6;
      background-repeat: no-repeat;
      background-attachment: fixed;
    }
  </style>
</head>
<body>
    <div class="d-flex justify-content-center row" style="text-align:center; margin-top:50px; font-family:sans-serif;">
        <h2>ค้นหาข้อมูล</h2>
        <form style="width: 300px">
          <div class="row mb-3">
            <label for="email" class="col-sm-2 col-form-label">Email</label>
            <div class="col-sm-10">
              <input type="email" class="form-control" id="email" placeholder="examp@examp.com">
            </div>
          </div>
          <button class="btn btn-success mt-3" onclick="getdata()">ดาวน์โหลด เกียรติบัตร</button>
        </form>
    </div>
  
  <script>

    function getdata(){
      event.preventDefault();
      const email = document.getElementById('email').value;
      google.script.run.withSuccessHandler(res =>{
        if(!res.success){
          alert('ไม่มีข้อมูล')
          return;
        }
        generateCertificate(res.data)
      }).getData(email)
    }

    // ✅ โหลดฟอนต์จาก URL (Prompt-Regular / Prompt-Bold)
    async function loadFont(url) {
      const res = await fetch(url);
      const buffer = await res.arrayBuffer();
      let binary = '';
      const bytes = new Uint8Array(buffer);
      const chunk = 0x8000;
      for (let i = 0; i < bytes.length; i += chunk) {
        binary += String.fromCharCode.apply(null, bytes.subarray(i, i + chunk));
      }
      return btoa(binary);
    }

    // ✅ โหลดรูป (พื้นหลัง, โลโก้, QR) เป็น Base64
    async function loadImageAsBase64(url) {
      const res = await fetch(url);
      const blob = await res.blob();
      return new Promise((resolve) => {
        const reader = new FileReader();
        reader.onloadend = () => resolve(reader.result);
        reader.readAsDataURL(blob);
      });
    }



    async function generateCertificate(data) {

      const datax = data;
      // const date = datax[0].split(" ")[0]
      // const dmy = date.split("/")
      // const dd = dmy[0];
      // const mm = dmy[1];
      // const yyyy = dmy[2];
      // const mth = ["", มกราคม, กุมภาพันธ์, มีนาคม, เมษายน, พฤษภาคม, มิถุนายน, กรกฎาคม, สิงหาคม, กันยายน, ตุลาคม, พฤศจิกายน, ธันวาคม];
      // const mmth = mth[mm];
      // โหลดฟอนต์
      const promptRegular = await loadFont("https://semicon.github.io/fonts/Prompt-Regular.ttf");
      const promptBold = await loadFont("https://semicon.github.io/fonts/Prompt-Bold.ttf");

      // โหลดรูป
      const bg = await loadImageAsBase64("https://wichianp.github.io/img/web/rama5_6.png"); // พื้นหลัง
      const logo = await loadImageAsBase64("https://semicon.github.io/img/web/logoxxx.png");
      const sign = await loadImageAsBase64("https://wichianp.github.io/img/web/signCert.png");
      const qr = await loadImageAsBase64("https://api.qrserver.com/v1/create-qr-code/?size=100x100&data=https://guruchian.blogspot.com&margin=8");

      // สร้าง document definition
      const docDefinition = {
        pageSize: "A4",
        pageOrientation: "landscape",
        background: [
          {
            image: bg,
            width: 842, // A4 landscape = 842 x 595 pt
            height: 595
          }
        ],
        content: [
          {
            image: logo,
            width: 80,
            alignment: "center",
            margin: [0, 50, 0, 0]
          },
          {
            text: "เกียรติบัตร",
            style: "header",
            alignment: "center",
            margin: [0, 10, 0, 20]
          },
          {
            text: "ให้ไว้เพื่อแสดงว่า",
            style: "subheader",
            alignment: "center"
          },
          {
            text: datax[2],
            style: "name",
            alignment: "center",
            margin: [0, 10, 0, 10]
          },
          {
            alignment: "center",
            style: "normal",
            text: [
              { text: "ได้เข้าร่วมกิจกรรมแข่งขัน " },
              { text: "เกมตะลุยขุมทรัพย์วันปิยมหาราช", bold: true }
            ]
          },
          {
            text: "ให้ไว้ ณ วันที่ 23 ตุลาคม พ.ศ. 2568",
            style: "normal",
            alignment: "center",
            margin: [0, 10, 0, 40]
          },
          {
            image: sign,
            width: 100,
            alignment: "center",
            margin: [0, 0, 0, 0]
          },
          {
            text: "นายวิเชียร พุ่มพวง",
            style: "normal",
            alignment: "center",
            margin: [0, 0, 0, 0]
          },
          {
            text: "แอดมินเว็บครูเชียร",
            style: "normal",
            alignment: "center",
            margin: [0, 0, 0, 0]
          },
          {
            image: qr,
            width: 100,
            absolutePosition: { x: 720, y: 475 }
          }
        ],
        styles: {
          header: { font: "PromptBold", fontSize: 28, bold: true, color: '#000'},
          subheader: { font: "Prompt", fontSize: 18, color: '#000' },
          name: { font: "PromptBold", fontSize: 24, bold: true, color: '#000' },
          normal: { font: "Prompt", fontSize: 16, color: '#000' }
        },
        defaultStyle: { font: "Prompt", color: '#000' }
      };

      // กำหนดฟอนต์
      pdfMake.vfs["Prompt-Regular.ttf"] = promptRegular;
      pdfMake.vfs["Prompt-Bold.ttf"] = promptBold;

      pdfMake.fonts = {
        Prompt: { normal: "Prompt-Regular.ttf", bold: "Prompt-Bold.ttf" },
        PromptBold: { normal: "Prompt-Bold.ttf", bold: "Prompt-Bold.ttf" }
      };

      // ✅ เซฟ หรือ ดาวน์โหลด PDF
      //pdfMake.createPdf(docDefinition).download("certificate.pdf");

      // ✅ เปิด PDF บน Browser (แทนการบังคับโหลด)
      pdfMake.createPdf(docDefinition).open();
    }
  </script>
</body>
</html>


    


404.html

     
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>404 - Page Not Found</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css">
   <link href="https://fonts.googleapis.com/css2?family=Pattaya&family=Sriracha&display=swap" rel="stylesheet">
  <style>
    html,
    body {
      height: 100%;
      margin: 0;
      font-family: 'Sriracha', Arial, Helvetica, sans-serif;
      background: linear-gradient(180deg, #00121a 0%, #002233 70%);
      color: #e6eef6;
      background-repeat: no-repeat;
      background-attachment: fixed;
    }
  </style>
</head>
<body class="text-center p-5">
  <h1 class="display-3 text-danger">404</h1>
  <p class="lead">ขออภัย ไม่พบหน้าที่คุณค้นหา</p>
</body>
</html>

    
แสดงความคิดเห็น (0)
ใหม่กว่า เก่ากว่า