0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

中高生向け:キュウリ出荷シミュレーションゲームをHTMLだけで作ってみよう🥒🚚

Posted at

はじめに

中高生向けに、キュウリ出荷シミュレーションゲームをHTMLで作ってみました。
image.png

「今日の収穫、どこに出荷したら一番もうかる?」
そんな農家の意思決定を、ブラウザで遊べるミニゲームにしてみます。

このゲームは、天気・収穫量・品質・曲がり具合・需要*(SNSの話題)が毎日ランダムに変わり、あなたは出荷先を選びます。さらに、**“AIっぽい計算”**でおすすめを出すモデルも登場します。

できること(このゲームで学べること)

  • ランダム(乱数)で「今日の状況」を作る
  • 条件分岐(switch / if)で「出荷先ごとの特徴」を表現する
  • 数式で「売れ残り」「単価」「利益」を計算する
  • 複数の選択肢を比べて、最大のもの(AIのおすすめ)を選ぶ
  • 「機械学習の考え方(特徴量→予測→評価)」の入口を体験する

遊び方

  1. 下のコードを index.html として保存
  2. ダブルクリックしてブラウザで開く(Chrome / Edge 推奨)
  3. 「新しい一日をはじめる」 を押して状況を生成
  4. 出荷先カードをクリックして利益を比較!
    image.png

ルールのイメージ(ざっくり)

  • スーパー:A品が多いと有利、曲がりは少しならOK
  • 大都市の市場:高く売れるが、曲がりに厳しい&輸送コスト高
  • 直売所:需要が高いと爆売れ、低いと売れ残り
  • 工場:全部買い取ってくれるが単価は安め(ロスほぼなし)

“AIきゅうりモデル”って何?

本物の機械学習ライブラリは使っていません。
でもやっていることは「それっぽい」です:

  • 入力(特徴量):天気・収穫量・品質・曲がり率・需要
  • 出力(予測):各出荷先の利益を計算
  • 評価:利益が最大の出荷先=AIおすすめ

本物の機械学習だと、この「利益の出やすさ(スコア)」を過去データから学習します。
今回は授業で説明しやすいように、ルール(計算式)で再現しています。

image.png

完成コード(コピペで動きます)

メモ帳を開いて以下のコードをコピペして、拡張子を.htmlにしてください。特別なライブラリーは要りません。

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>キュウリ出荷戦略ゲーム</title>
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <style>
    * { box-sizing: border-box; }
    body {
      margin: 0;
      font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
      background: linear-gradient(135deg, #e5ffe8, #f5fff8);
      color: #234;
    }
    header {
      background: #2e8b57;
      color: #fff;
      padding: 16px 20px;
      text-align: center;
      box-shadow: 0 2px 6px rgba(0,0,0,0.2);
    }
    header h1 { margin: 0; font-size: 1.8rem; }
    header p { margin: 6px 0 0; font-size: 0.95rem; opacity: 0.9; }
    main { max-width: 1100px; margin: 20px auto 40px; padding: 0 12px; }
    .top-bar {
      display: flex; flex-wrap: wrap; gap: 10px;
      align-items: center; justify-content: space-between;
      margin-bottom: 14px;
    }
    .top-bar button {
      border: none; border-radius: 999px; padding: 8px 18px;
      font-size: 0.95rem; cursor: pointer;
      background: #2e8b57; color: #fff;
      box-shadow: 0 2px 4px rgba(0,0,0,0.15);
      transition: transform 0.05s ease, box-shadow 0.05s ease, background 0.2s ease;
    }
    .top-bar button:hover { transform: translateY(-1px); box-shadow: 0 3px 6px rgba(0,0,0,0.2); background: #36a067; }
    .top-bar button:active { transform: translateY(0); box-shadow: 0 1px 3px rgba(0,0,0,0.25); }

    .day-label { font-weight: 600; font-size: 0.95rem; display: inline-flex; align-items: center; gap: 6px; }
    .day-label span { display: inline-block; padding: 4px 10px; border-radius: 999px; background: #fff; border: 1px solid #b8e0c5; }

    .columns { display: grid; grid-template-columns: minmax(0, 1.1fr) minmax(0, 1.1fr); gap: 18px; }
    @media (max-width: 780px) { .columns { grid-template-columns: minmax(0, 1fr); } }

    .card {
      background: #ffffffd9;
      border-radius: 16px;
      padding: 14px 16px 16px;
      box-shadow: 0 2px 6px rgba(0,0,0,0.08);
      border: 1px solid #cce9d4;
      backdrop-filter: blur(4px);
    }
    .card h2 { margin: 0 0 8px; font-size: 1.15rem; display: flex; align-items: center; gap: 6px; }
    .badge {
      display: inline-flex; align-items: center; justify-content: center;
      padding: 3px 8px; border-radius: 999px;
      font-size: 0.7rem; font-weight: 600;
      background: #e5f7eb; color: #2e8b57;
      border: 1px solid #b8e0c5;
    }

    .scenario-grid { display: grid; grid-template-columns: repeat(auto-fill,minmax(140px,1fr)); gap: 8px; margin-top: 8px; font-size: 0.9rem; }
    .scenario-item { background: #f5fff7; border-radius: 12px; padding: 8px 9px; border: 1px dashed #b8e0c5; }
    .scenario-label { font-size: 0.8rem; color: #567; margin-bottom: 3px; }
    .scenario-value { font-weight: 600; }
    .scenario-hint { margin-top: 8px; font-size: 0.8rem; color: #566; line-height: 1.5; }
    .hint-highlight { font-weight: 600; color: #2e8b57; }

    .plan-list { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; margin-top: 4px; }
    @media (max-width: 650px) { .plan-list { grid-template-columns: minmax(0,1fr); } }

    .plan-card {
      border-radius: 14px; padding: 10px 10px 11px;
      background: #f7fff9; border: 1px solid #cfe8d6;
      cursor: pointer; display: flex; flex-direction: column; gap: 4px;
      transition: transform 0.05s ease, box-shadow 0.05s ease, border-color 0.1s ease, background 0.2s ease;
      position: relative; overflow: hidden;
    }
    .plan-card::before { content: ""; position: absolute; inset: 0; background: radial-gradient(circle at top, rgba(255,255,255,0.7), transparent 55%); opacity: 0; transition: opacity 0.15s ease; pointer-events: none; }
    .plan-card:hover::before { opacity: 1; }
    .plan-card:hover { transform: translateY(-1px); box-shadow: 0 3px 6px rgba(0,0,0,0.12); border-color: #60bf7a; background: #f0fff4; }
    .plan-card:active { transform: translateY(0); box-shadow: 0 1px 3px rgba(0,0,0,0.16); }

    .plan-title { font-weight: 700; font-size: 0.95rem; display: flex; align-items: center; gap: 6px; }
    .plan-title span.icon { font-size: 1.1rem; }
    .plan-desc { font-size: 0.8rem; line-height: 1.4; color: #556; }
    .plan-meta { margin-top: 2px; font-size: 0.75rem; color: #678; display: flex; flex-wrap: wrap; gap: 6px; }
    .pill { padding: 2px 7px; border-radius: 999px; background: #e8f6ec; border: 1px solid #c2e0cc; }

    .result-card { margin-top: 8px; padding: 10px 12px; border-radius: 12px; background: #f4fff6; border: 1px solid #cce9d4; font-size: 0.9rem; line-height: 1.6; }
    .result-row { display: flex; flex-wrap: wrap; gap: 6px 14px; align-items: center; margin-bottom: 3px; }
    .result-label { font-weight: 600; }
    .result-highlight { font-weight: 700; color: #1f8a55; }
    .compare { margin-top: 6px; padding-top: 6px; border-top: 1px dashed #b8e0c5; font-size: 0.85rem; }
    .compare span.win { color: #1f8a55; font-weight: 700; }
    .compare span.lose { color: #c0392b; font-weight: 700; }
    .compare span.draw { color: #8e44ad; font-weight: 700; }

    .ai-hint { margin-top: 6px; font-size: 0.8rem; color: #567; }
    .ai-hint code { font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; font-size: 0.78rem; background: #eaf6ee; padding: 1px 4px; border-radius: 4px; }

    .ml-notes { margin-top: 18px; font-size: 0.85rem; line-height: 1.6; }
    .ml-notes details { margin-top: 6px; padding: 8px 10px; border-radius: 10px; background: #f3fff7; border: 1px dashed #c0e0cc; }
    .ml-notes summary { cursor: pointer; font-weight: 600; list-style: none; }
    .ml-notes summary::marker, .ml-notes summary::-webkit-details-marker { display: none; }
    .ml-notes summary span { margin-right: 4px; }
    .ml-notes ul { margin: 6px 0 0 1.1em; padding: 0; }
    .ml-notes li { margin-bottom: 2px; }
    .tiny-caption { margin-top: 6px; font-size: 0.75rem; color: #6b7; }
    .empty-message { font-size: 0.85rem; color: #566; margin-top: 4px; }
  </style>
</head>

<body onload="init()">

<header>
  <h1>🥒 キュウリ出荷戦略ゲーム</h1>
  <p>あなたはキュウリ農家! 今日の状況を見て、どこに出荷すると一番儲かるかな?</p>
</header>

<main>
  <div class="top-bar">
    <div class="day-label">
      今日のシミュレーション
      <span id="dayText">Day 0</span>
    </div>
    <div>
      <button id="newDayBtn" onclick="newDay()">🔄 新しい一日をはじめる</button>
    </div>
  </div>

  <div class="columns">
    <section class="card">
      <h2>🌱 今日の畑のようす <span class="badge">入力なしでランダム生成</span></h2>
      <p style="margin: 4px 0 6px; font-size: 0.85rem;">
        ボタンを押すと、天気・収穫量・キュウリの曲がり具合・需要をランダムに決めます。
      </p>

      <div id="scenarioArea" class="scenario-grid"></div>

      <div id="scenarioHint" class="scenario-hint">
        🔍 「新しい一日をはじめる」を押して、今日の条件を決めてみよう。
      </div>
    </section>

    <section class="card">
      <h2>🚚 出荷先をえらぶ <span class="badge">AI きゅうりモデル も参戦</span></h2>
      <p style="margin: 4px 0 8px; font-size: 0.85rem;">
        下のどれかをクリックして、<span class="hint-highlight">全部のキュウリをそこに出荷</span>してみよう。  
        そのあとで、AIがこっそり計算したベスト戦略も表示されます。
      </p>

      <div class="plan-list" id="planList"></div>

      <div id="resultArea" class="result-card" style="display:none;"></div>

      <div id="emptyResult" class="empty-message">
        ※ まずは「新しい一日をはじめる」を押して状況を決めてから、出荷先を選んでみてね。
      </div>
    </section>
  </div>

  <section class="card ml-notes">
    <h2>🤖 AIきゅうりモデルって何してるの?</h2>
    <p>
      このゲームでは、本物の機械学習ライブラリは使っていませんが、  
      「<span class="hint-highlight">機械学習モデルが考えそうなこと</span>」をマネしたルールでAIが出荷先を決めています。
    </p>

    <details>
      <summary><span></span>AIきゅうりモデルの「考えかた」(機械学習っぽいイメージ)</summary>
      <ul>
        <li>入力(特徴量)として、<strong>天気・収穫量・品質・曲がり率・需要レベル</strong>を使う。</li>
        <li>それぞれの出荷先(スーパー/市場/直売所/工場)ごとに、利益が出そうかをスコア化する。</li>
        <li>そのスコアが一番大きくなる出荷先を、<strong>「AIのおすすめ」</strong>として選ぶ。</li>
        <li>本物の機械学習では、この<strong>「スコアの付け方」</strong><span class="hint-highlight">過去のデータから自動で学習</span>する。
        </li>
        <li>このゲームでは、説明しやすいように、あらかじめ決めたルール(if文 &amp; 計算)で真似しています。</li>
      </ul>
      <p class="tiny-caption">
        ※ 授業などで使う場合は、「入力(特徴量)→ 出力(予測)→ 利益を比べて評価」という流れを説明すると、
        教師あり学習の導入にも使えます。
      </p>
    </details>
  </section>
</main>

<script>
  // ====== データ定義 ======
  var state = {
    day: 0,          // ★Day表示がズレないように0から開始
    scenario: null,
    aiChoice: null
  };

  var plans = {
    supermarket: {
      key: "supermarket",
      icon: "🏪",
      name: "近所のスーパーに出荷",
      desc: "A品が多いと高く買ってくれる。曲がりキュウリは少しは混ざってもOK。",
      meta: ["価格:中〜高", "距離:近い", "残ったら少しロス"]
    },
    market: {
      key: "market",
      icon: "🏙️",
      name: "大都市の市場に出荷",
      desc: "品質さえ良ければ高く売れるが、形が悪いとハネられやすい。輸送コストも高め。",
      meta: ["価格:高め", "距離:遠い", "曲がりに厳しい"]
    },
    direct: {
      key: "direct",
      icon: "🧺",
      name: "直売所で販売",
      desc: "お客さんと直接やりとり。天気や休日かどうかで売れ行きが大きく変わる。",
      meta: ["価格:高いことも", "距離:とても近い", "売れ残りリスクあり"]
    },
    factory: {
      key: "factory",
      icon: "🏭",
      name: "漬物工場に出荷",
      desc: "曲がりキュウリも全部買ってくれるが、単価は安め。",
      meta: ["価格:低め", "距離:中くらい", "ロスほぼなし"]
    }
  };

  var weathers = [
    { label: "快晴", effectDemand: 0.15 },
    { label: "晴れ", effectDemand: 0.1 },
    { label: "くもり", effectDemand: 0.0 },
    { label: "小雨", effectDemand: -0.05 },
    { label: "大雨", effectDemand: -0.1 },
    { label: "猛暑", effectDemand: 0.05 }
  ];

  var snsLevels = [
    { label: "バズっている(テレビ・SNSで話題)", bonus: 0.25 },
    { label: "そこそこ話題", bonus: 0.1 },
    { label: "普通", bonus: 0.0 },
    { label: "あまり話題になっていない", bonus: -0.05 }
  ];

  // ====== ユーティリティ関数 ======
  function randRange(min, max) { return Math.random() * (max - min) + min; }
  function randInt(min, max) { return Math.floor(randRange(min, max + 1)); }
  function pickRandom(arr) { return arr[Math.floor(Math.random() * arr.length)]; }
  function formatYen(num) { return Math.round(num).toLocaleString("ja-JP") + ""; }

  // ====== シナリオ生成 ======
  function createRandomScenario() {
    var weather = pickRandom(weathers);
    var sns = pickRandom(snsLevels);

    var harvestKg = randInt(300, 900);
    var qualityScore = Math.random() * 0.4 + 0.5; // 0.5〜0.9
    var curveRate = Math.random() * 0.35;         // 0〜0.35

    var baseDemand = 0.5 + weather.effectDemand + sns.bonus;
    baseDemand += (qualityScore - 0.7) * 0.5;
    baseDemand = Math.min(1.0, Math.max(0.1, baseDemand));

    var demandLabel = (baseDemand >= 0.75) ? "かなり高い"
      : (baseDemand >= 0.55) ? "高め"
      : (baseDemand >= 0.4)  ? "ふつう"
      : "やや低い";

    var qualityLabel = (qualityScore >= 0.82) ? "ピカピカ A 品多め"
      : (qualityScore >= 0.7) ? "A〜B 品まじり"
      : "ちょっと傷あり B 品多め";

    var curveLabel = (curveRate <= 0.08) ? "ほぼまっすぐ"
      : (curveRate <= 0.2) ? "少し曲がりが混ざる"
      : "かなり曲がり多め";

    return {
      weather: weather.label,
      harvestKg: harvestKg,
      qualityScore: qualityScore,
      qualityLabel: qualityLabel,
      curveRate: curveRate,
      curveLabel: curveLabel,
      demand: baseDemand,
      demandLabel: demandLabel,
      snsLabel: sns.label
    };
  }

  // ====== 利益計算(AIっぽいルール) ======
  function calcProfit(planKey, s) {
    var harvestKg = s.harvestKg;
    var qualityScore = s.qualityScore;
    var curveRate = s.curveRate;
    var demand = s.demand;

    var pricePerKg = 0;
    var sellRate = 0;
    var costPerKg = 0;

    switch (planKey) {
      case "supermarket":
        pricePerKg = 140 + 40 * qualityScore + 20 * demand;
        sellRate = 0.8 + 0.1 * demand - 0.1 * curveRate;
        costPerKg = 5;
        break;
      case "market":
        pricePerKg = 150 + 60 * qualityScore + 30 * demand;
        sellRate = 0.9 + 0.05 * demand - 0.2 * curveRate;
        costPerKg = 15;
        break;
      case "direct":
        pricePerKg = 160 + 50 * qualityScore + 40 * demand;
        sellRate = 0.6 + 0.3 * demand - 0.05 * curveRate;
        costPerKg = 3;
        break;
      case "factory":
        pricePerKg = 110 + 10 * qualityScore + 5 * demand;
        sellRate = 1.0;
        costPerKg = 8;
        break;
      default:
        return { profit: 0, pricePerKg: 0, soldKg: 0, wastedKg: harvestKg };
    }

    sellRate = Math.min(1.0, Math.max(0.0, sellRate));

    var soldKg = harvestKg * sellRate;
    var wastedKg = harvestKg - soldKg;

    var revenue = soldKg * pricePerKg;
    var cost = harvestKg * costPerKg;
    var profit = revenue - cost;

    return { profit: profit, pricePerKg: pricePerKg, soldKg: soldKg, wastedKg: wastedKg };
  }

  function computeAiChoice() {
    if (!state.scenario) return null;
    var best = null;
    var keys = Object.keys(plans);
    for (var i = 0; i < keys.length; i++) {
      var key = keys[i];
      var result = calcProfit(key, state.scenario);
      if (!best || result.profit > best.profit) {
        best = { planKey: key, profit: result.profit, pricePerKg: result.pricePerKg, soldKg: result.soldKg, wastedKg: result.wastedKg };
      }
    }
    state.aiChoice = best;
    return best;
  }

  // ====== UI 更新 ======
  function renderScenario() {
    var scenarioArea = document.getElementById("scenarioArea");
    var scenarioHint = document.getElementById("scenarioHint");

    if (!state.scenario) {
      scenarioArea.innerHTML = "";
      scenarioHint.innerHTML = "🔍 「新しい一日をはじめる」を押して、今日の条件を決めてみよう。";
      return;
    }

    var s = state.scenario;
    scenarioArea.innerHTML = `
      <div class="scenario-item"><div class="scenario-label">天気</div><div class="scenario-value">☀️ ${s.weather}</div></div>
      <div class="scenario-item"><div class="scenario-label">収穫量(きょう)</div><div class="scenario-value">${s.harvestKg.toLocaleString("ja-JP")} kg</div></div>
      <div class="scenario-item"><div class="scenario-label">品質</div><div class="scenario-value">${s.qualityLabel}</div></div>
      <div class="scenario-item"><div class="scenario-label">曲がりキュウリの割合</div><div class="scenario-value">${Math.round(s.curveRate * 100)} %(${s.curveLabel})</div></div>
      <div class="scenario-item"><div class="scenario-label">需要レベル</div><div class="scenario-value">📈 ${s.demandLabel}</div></div>
      <div class="scenario-item"><div class="scenario-label">世の中の話題</div><div class="scenario-value">📰 ${s.snsLabel}</div></div>
    `;

    scenarioHint.innerHTML = `
      ✅ 条件が決まったら、<span class="hint-highlight">右側の出荷先カード</span>をクリックしてみよう。<br>
      AIきゅうりモデルは、これらの情報をもとに「利益が最大になる出荷先」をこっそり計算しています。
    `;
  }

  function renderPlans() {
    var planList = document.getElementById("planList");
    planList.innerHTML = "";

    Object.keys(plans).map(function(k){ return plans[k]; }).forEach(function(plan) {
      var div = document.createElement("div");
      div.className = "plan-card";

      var metaHtml = plan.meta.map(function(m) { return '<span class="pill">' + m + "</span>"; }).join("");

      div.innerHTML = `
        <div class="plan-title"><span class="icon">${plan.icon}</span><span>${plan.name}</span></div>
        <div class="plan-desc">${plan.desc}</div>
        <div class="plan-meta">${metaHtml}</div>
      `;

      div.onclick = function() { handlePlanClick(plan.key); };
      planList.appendChild(div);
    });
  }

  function handlePlanClick(planKey) {
    if (!state.scenario) {
      var emptyResult = document.getElementById("emptyResult");
      emptyResult.textContent = "まずは「新しい一日をはじめる」で畑の状況を決めてから出荷先を選んでね。";
      emptyResult.style.color = "#c0392b";
      return;
    }

    var resultArea = document.getElementById("resultArea");
    var emptyResult = document.getElementById("emptyResult");
    emptyResult.style.display = "none";
    resultArea.style.display = "block";

    var userResult = calcProfit(planKey, state.scenario);
    var aiChoice = state.aiChoice || computeAiChoice();
    var userPlan = plans[planKey];
    var aiPlan = plans[aiChoice.planKey];

    var verdictText = "";
    var verdictClass = "";

    if (Math.round(userResult.profit) > Math.round(aiChoice.profit)) {
      verdictText = "あなたの勝ち! AIよりうまい出荷戦略です 🎉";
      verdictClass = "win";
    } else if (Math.round(userResult.profit) < Math.round(aiChoice.profit)) {
      verdictText = "AIの勝ち! 機械学習モデルの読みもなかなかやります 🤖";
      verdictClass = "lose";
    } else {
      verdictText = "引き分け! AIと同じくらいの読みです 🤝";
      verdictClass = "draw";
    }

    var userWasteRate = (userResult.wastedKg / state.scenario.harvestKg) * 100;
    var aiWasteRate = (aiChoice.wastedKg / state.scenario.harvestKg) * 100;

    resultArea.innerHTML = `
      <div class="result-row"><span class="result-label">あなたの選択:</span><span class="result-highlight">${userPlan.icon} ${userPlan.name}</span></div>
      <div class="result-row"><span>売れた量:${Math.round(userResult.soldKg)} kg / ロス:${Math.round(userResult.wastedKg)} kg(約 ${Math.round(userWasteRate)}%)</span></div>
      <div class="result-row"><span>平均単価:${Math.round(userResult.pricePerKg)} 円/kg / 利益:<strong>${formatYen(userResult.profit)}</strong></span></div>

      <div class="result-row" style="margin-top:6px;"><span class="result-label">AIきゅうりモデルのおすすめ:</span><span class="result-highlight">${aiPlan.icon} ${aiPlan.name}</span></div>
      <div class="result-row"><span>売れた量:${Math.round(aiChoice.soldKg)} kg / ロス:${Math.round(aiChoice.wastedKg)} kg(約 ${Math.round(aiWasteRate)}%)</span></div>
      <div class="result-row"><span>平均単価:${Math.round(aiChoice.pricePerKg)} 円/kg / 利益:<strong>${formatYen(aiChoice.profit)}</strong></span></div>

      <div class="compare">
        <span class="${verdictClass}">${verdictText}</span><br>
        利益の差は <strong>${formatYen(userResult.profit - aiChoice.profit)}</strong> でした。
      </div>

      <div class="ai-hint">
        💡 <strong>AIきゅうりモデルのイメージ</strong><br>
        <code>天気, 収穫量, 品質, 曲がり率, 需要レベル</code> を受け取り、<br>
        「どこに出荷したら利益が最大になるか」を予測する設定です。<br>
        今回は <strong>シンプルな計算ルール</strong> で真似しています。
      </div>
    `;
  }

  function newDay() {
    state.day += 1; // ★ここでDayを進める
    state.scenario = createRandomScenario();
    state.aiChoice = null;

    document.getElementById("dayText").textContent = "Day " + state.day;
    renderScenario();

    var resultArea = document.getElementById("resultArea");
    var emptyResult = document.getElementById("emptyResult");
    resultArea.style.display = "none";
    resultArea.innerHTML = "";
    emptyResult.style.display = "block";
    emptyResult.style.color = "#566";
    emptyResult.textContent = "条件を確認して、出荷先カードをクリックしてみよう。";

    computeAiChoice();
  }

  function init() {
    renderPlans();
    renderScenario();
  }
</script>

</body>
</html>

仕組みをもう少しだけ解説

1) 今日の状況をランダムに作る
createRandomScenario() が、天気やSNS話題などをランダムに選び、需要を計算しています。
ここが「毎回ちがう問題になる」ポイントです。
2) 出荷先ごとのルールで利益を計算する
calcProfit(planKey, s)switchの部分が肝です。
「市場は曲がりに厳しい」「工場は全部売れる」などの特徴を、で表現しています。
3) AIは“全パターン試して一番良いものを選ぶ”
computeAiChoice() は、4つの出荷先を全部計算して、利益が最大のものを選ぶだけ。
でもこれだけでも「AIっぽい」動きになります。

おわりに

このミニゲームは、農業のリアルな悩みどこに出すか)を、プログラミングの題材に落とし込んだ例です。
「式でルールを作る → シミュレーションする → 比較して意思決定する」は、農業だけでなく色々な分野で使えます。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?