ChatGPTにhtmlファイルで二次関数をかいてもらう

## はじめに
ChatGPTにhtmlファイルで二次関数をかいてもらいました。もはや驚くようなものでもないですがやはり便利です。教材をいちいち探してこなくてもいいし、なんなら生徒自身が動くものを作れてしまう時代になりました。すごい。

## 作り方
私が最初に与えたプロンプトは以下。
>htmlとCSSとJavaScriptによる1つのファイルで、二次関数を描画するものをくってほしい。定数部分は画面から指定ができ、定義域も指定できるようにしたい。また、平方完成された式とその前の式のどちらかを選択できるようにしてほしい。

<details><summary>この一回でできたものがこちら(コード全文)</summary>

```html
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>二次関数グラフ描画ツール</title>
  <style>
    body {
      font-family: "Segoe UI", sans-serif;
      background-color: #f9f9f9;
      padding: 20px;
    }
    .container {
      max-width: 800px;
      margin: 0 auto;
      background: #fff;
      padding: 15px;
      box-shadow: 0 0 10px rgba(0,0,0,0.1);
      border-radius: 8px;
    }
    h1 {
      text-align: center;
    }
    form {
      margin-bottom: 20px;
    }
    .form-section {
      margin-bottom: 15px;
    }
    label {
      display: inline-block;
      width: 100px;
      margin-right: 10px;
    }
    input[type="number"] {
      width: 80px;
      padding: 3px;
      margin-right: 15px;
    }
    canvas {
      border: 1px solid #ccc;
      display: block;
      margin: 0 auto;
    }
    .radio-group {
      margin-bottom: 10px;
    }
  </style>
</head>
<body>
  <div class="container">
    <h1>二次関数グラフ描画ツール</h1>
    <form id="plotForm">
      <div class="form-section">
        <span class="radio-group">
          <input type="radio" id="standard" name="formType" value="standard" checked>
          <label for="standard">標準形 (ax² + bx + c)</label>
          <input type="radio" id="vertex" name="formType" value="vertex">
          <label for="vertex">平方完成形 (a(x-h)² + k)</label>
        </span>
      </div>
      <div class="form-section" id="standardParams">
        <label for="a_std">a:</label>
        <input type="number" id="a_std" value="1" step="any">
        <label for="b_std">b:</label>
        <input type="number" id="b_std" value="0" step="any">
        <label for="c_std">c:</label>
        <input type="number" id="c_std" value="0" step="any">
      </div>
      <div class="form-section" id="vertexParams" style="display: none;">
        <label for="a_ver">a:</label>
        <input type="number" id="a_ver" value="1" step="any">
        <label for="h_ver">h:</label>
        <input type="number" id="h_ver" value="0" step="any">
        <label for="k_ver">k:</label>
        <input type="number" id="k_ver" value="0" step="any">
      </div>
      <div class="form-section">
        <label for="xMin">x最小値:</label>
        <input type="number" id="xMin" value="-10" step="any">
        <label for="xMax">x最大値:</label>
        <input type="number" id="xMax" value="10" step="any">
      </div>
      <div class="form-section">
        <button type="submit">グラフ描画</button>
      </div>
    </form>
    <canvas id="graphCanvas" width="600" height="400"></canvas>
  </div>

  <script>
    const plotForm = document.getElementById("plotForm");
    const standardParams = document.getElementById("standardParams");
    const vertexParams = document.getElementById("vertexParams");

    // フォームタイプ変更時に表示を切り替える
    document.querySelectorAll('input[name="formType"]').forEach(radio => {
      radio.addEventListener("change", function() {
        if(this.value === "standard"){
          standardParams.style.display = "block";
          vertexParams.style.display = "none";
        } else {
          standardParams.style.display = "none";
          vertexParams.style.display = "block";
        }
      });
    });

    plotForm.addEventListener("submit", function(e) {
      e.preventDefault();
      drawGraph();
    });

    function drawGraph(){
      const canvas = document.getElementById("graphCanvas");
      const ctx = canvas.getContext("2d");
      // キャンバスの初期化
      ctx.clearRect(0, 0, canvas.width, canvas.height);

      // 入力値の取得
      const formType = document.querySelector('input[name="formType"]:checked').value;
      let a, b, c, h, k;
      if(formType === "standard"){
        a = parseFloat(document.getElementById("a_std").value);
        b = parseFloat(document.getElementById("b_std").value);
        c = parseFloat(document.getElementById("c_std").value);
      } else {
        a = parseFloat(document.getElementById("a_ver").value);
        h = parseFloat(document.getElementById("h_ver").value);
        k = parseFloat(document.getElementById("k_ver").value);
      }
      const xMin = parseFloat(document.getElementById("xMin").value);
      const xMax = parseFloat(document.getElementById("xMax").value);
      if(xMin >= xMax){
        alert("x最小値はx最大値より小さくしてください。");
        return;
      }

      // 二次関数の計算
      function f(x){
        if(formType === "standard"){
          return a * x * x + b * x + c;
        } else {
          return a * (x - h) * (x - h) + k;
        }
      }

      // 描画用の座標変換のため、xとyの範囲を計算
      const samples = 1000;
      let xValues = [];
      let yValues = [];
      const dx = (xMax - xMin) / samples;
      for(let i = 0; i <= samples; i++){
        let x = xMin + i * dx;
        let y = f(x);
        xValues.push(x);
        yValues.push(y);
      }
      // yの最小値と最大値を求める
      let yMin = Math.min(...yValues);
      let yMax = Math.max(...yValues);
      // わずかな余裕を持たせる
      const yMargin = (yMax - yMin) * 0.1;
      yMin -= yMargin;
      yMax += yMargin;
      if(yMin === yMax){ // 定数関数対策
        yMin -= 1;
        yMax += 1;
      }

      // 座標変換用の関数
      function transformX(x){
        return (x - xMin) / (xMax - xMin) * canvas.width;
      }
      function transformY(y){
        // キャンバスのYは下方向が正なので反転させる
        return canvas.height - ( (y - yMin) / (yMax - yMin) * canvas.height );
      }

      // 軸の描画
      ctx.strokeStyle = "#aaa";
      ctx.lineWidth = 1;
      // x軸
      const yAxisZero = transformY(0);
      ctx.beginPath();
      ctx.moveTo(0, yAxisZero);
      ctx.lineTo(canvas.width, yAxisZero);
      ctx.stroke();
      // y軸
      const xAxisZero = transformX(0);
      ctx.beginPath();
      ctx.moveTo(xAxisZero, 0);
      ctx.lineTo(xAxisZero, canvas.height);
      ctx.stroke();

      // グラフの描画
      ctx.strokeStyle = "#007acc";
      ctx.lineWidth = 2;
      ctx.beginPath();
      for(let i = 0; i < xValues.length; i++){
        const xPos = transformX(xValues[i]);
        const yPos = transformY(yValues[i]);
        if(i === 0){
          ctx.moveTo(xPos, yPos);
        } else {
          ctx.lineTo(xPos, yPos);
        }
      }
      ctx.stroke();

      // グラフの説明文（式の表示）
      ctx.fillStyle = "#333";
      ctx.font = "16px sans-serif";
      let equationText = "";
      if(formType === "standard"){
        equationText = `y = ${a}x² + ${b}x + ${c}`;
      } else {
        equationText = `y = ${a}(x - ${h})² + ${k}`;
      }
      ctx.fillText(equationText, 10, 20);
    }
  </script>
</body>
</html>


```
</details>

以下はCodePen。htmlとCSSとJavaScriptに切り分けました。

<p class="codepen" data-height="880" data-default-tab="result" data-slug-hash="RNwqYOZ" data-pen-title="二次関数描画" data-user="8787kazuye" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/8787kazuye/pen/RNwqYOZ">
  二次関数描画</a> by ken1math (<a href="https://codepen.io/8787kazuye">@8787kazuye</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://public.codepenassets.com/embed/index.js"></script>

このままだとまだ少し微妙な感じもしますが、必要最低限のものはでています。一回のプロンプトでここまでできるのはすごい。あとはチャットでやりとりしたり、自分で書きかえてもよさそうです。


## おわりに
この程度のことであれば、すでに優秀なツールがたくさんあるので、それを使った方がいいかもしれないです。でも、自作だと自由度も高いし、思い入れもできそうなので、これもまたよさそう。