🏇 AI予想機能付き競馬ゲーム
| 馬番 |
馬名 |
スピード |
スタミナ |
調子 |
AI予想 |
リアルオッズ |
🏇1.テイオー
🏇2.オグリ
🏇3.マック
🏇4.ウララ
👑 的中 👑おめでとうございます!let funds = 1000;let raceInterval;// 基本となる競走馬の能力データconst baseHorsesData = [{ name: "トウカイテイオー", speed: 85, stamina: 70, baseOdds: 2.0 },{ name: "オグリキャップ", speed: 75, stamina: 85, baseOdds: 3.5 },{ name: "メジロマックイーン", speed: 65, stamina: 95, baseOdds: 5.0 },{ name: "ハルウララ", speed: 45, stamina: 40, baseOdds: 15.0 }];let activeHorses = [];// 賭け金ショートカットfunction setBet(amount) { document.getElementById("bet-input").value = amount; }function setBet500() { document.getElementById("bet-input").value = 500; }function setBetAll() { document.getElementById("bet-input").value = funds; }// 次のレースの準備(調子とオッズ、AI予想の再計算)function prepareNextRace() {activeHorses = baseHorsesData.map(horse => {// 調子をランダムに変調 (-20 ~ +20)const condition = Math.floor(Math.random() * 41) - 20;// 調子を考慮した総合力を計算const totalPower = horse.speed + horse.stamina + condition;return {...horse,condition: condition,totalPower: totalPower,currentOdds: 0 // 後で計算};});// 総合力に基づいてオッズとAI予想を動的に計算const totalPowerSum = activeHorses.reduce((sum, h) => sum + h.totalPower, 0);activeHorses.forEach(h => {// パワーがあるほどオッズが下がる(人気が出る)計算let calculatedOdds = (totalPowerSum / h.totalPower) * 1.2;h.currentOdds = Math.max(1.2, Math.round(calculatedOdds * 10) / 10);});// 総合力が高い順にソートしてAIの印(◎, ○, ▲, △)を割り振るconst sortedByPower = [...activeHorses].sort((a, b) => b.totalPower - a.totalPower);const aiMarks = ["◎ 本命", "○ 対抗", "▲ 単穴", "△ 連下"];const aiBadges = ["ai-tip-1", "ai-tip-2", "ai-tip-3", "ai-tip-4"];sortedByPower.forEach((sh, index) => {const target = activeHorses.find(h => h.name === sh.name);target.aiMark = aiMarks[index];target.aiBadge = aiBadges[index];});renderRaceCard();}// 出馬表UIのレンダリングfunction renderRaceCard() {const tbody = document.getElementById("race-card-body");const select = document.getElementById("horse-select");tbody.innerHTML = "";select.innerHTML = "";activeHorses.forEach((h, i) => {// 調子のテキスト化let condText = "🛑 絶不調";if (h.condition > 10) condText = "🔥 絶好調";else if (h.condition > 2) condText = "🧡 好調";else if (h.condition >= -10) condText = "🟢 普通";// テーブル行の追加const row = document.createElement("tr");row.innerHTML = ${i + 1} | ${h.name} | | | ${condText} | ${h.aiMark} | ${h.currentOdds}倍 | ;tbody.appendChild(row);// セレクトボックスの選択肢追加const opt = document.createElement("option");opt.value = i;opt.innerText = ${i + 1}番: ${h.name} (${h.currentOdds}倍);select.appendChild(opt);});}function startRace() {const betInput = document.getElementById("bet-input");const betAmount = parseInt(betInput.value);const selectedHorse = parseInt(document.getElementById("horse-select").value);if (isNaN(betAmount) || betAmount <= 0) {alert("正しい賭け金を入力してください。");return;}if (betAmount > funds) {alert("所持金が足りません!");return;}funds -= betAmount;document.getElementById("money").innerText = funds;// 操作系の無効化document.getElementById("start-btn").disabled = true;document.getElementById("refresh-btn").disabled = true;document.getElementById("track").style.display = "block";document.getElementById("result").innerHTML = "スタートしました!各馬一斉に飛び出します!";// 馬の走る能力をリアルタイム反映 (スピード値がベース。スタミナは後半の粘りに影響)const horses = activeHorses.map((h, i) => {return {id: horse${i},pos: 0,// 基本能力値 + 調子をベースに一歩あたりの移動量を設定minMove: (h.speed * 0.03) + (h.condition * 0.05),maxMove: (h.speed * 0.09) + (h.condition * 0.1) + (h.stamina * 0.02)};});horses.forEach(h => document.getElementById(h.id).style.left = "0px");const trackWidth = document.getElementById("track").clientWidth - 70;raceInterval = setInterval(() => {let winner = null;horses.forEach((h, index) => {// ランダムに前進。スタミナがある馬は後半失速しにくい仕様const move = Math.random() * (h.maxMove - h.minMove) + h.minMove;h.pos += Math.max(0.5, move); // 最低進度を保証document.getElementById(h.id).style.left = h.pos + "px";if (h.pos >= trackWidth && winner === null) {winner = index;}});if (winner !== null) {clearInterval(raceInterval);endRace(winner, selectedHorse, betAmount);}}, 40);}function endRace(winner, selectedHorse, betAmount) {document.getElementById("start-btn").disabled = false;document.getElementById("refresh-btn").disabled = false;const winHorse = activeHorses[winner];let resultText = 1着入線: ${winHorse.name} (${winHorse.aiMark})
;if (winner === selectedHorse) {const payout = Math.floor(betAmount * winHorse.currentOdds);funds += payout;resultText += ✨ AIの壁を越えた! 的中 ¥${payout} ✨;triggerCelebration();} else {resultText += 残念! 次のレースでリベンジしましょう。;}document.getElementById("money").innerText = funds;document.getElementById("result").innerHTML = resultText;if (funds <= 0) {document.getElementById("result").innerHTML += "資金が尽きました!救済として1000円支給します。";funds = 1000;document.getElementById("money").innerText = funds;}// レース終了後に次のパドック情報を自動生成prepareNextRace();}function triggerCelebration() {const overlay = document.getElementById("celebration");overlay.style.display = "flex";for (let i = 0; i < 100; i++) {const confetti = document.createElement("div");confetti.classList.add("confetti");confetti.style.left = Math.random() * 100 + "vw";confetti.style.backgroundColor = hsl(${Math.random() * 360}, 100%, 60%);confetti.style.animationDelay = Math.random() * 2 + "s";confetti.style.width = (Math.random() * 8 + 6) + "px";confetti.style.height = (Math.random() * 8 + 6) + "px";overlay.appendChild(confetti);}setTimeout(() => {overlay.style.display = "none";const elements = document.getElementsByClassName("confetti");while(elements.length > 0){elements[0].parentNode.removeChild(elements[0]);}}, 4000);}// 初回起動処理prepareNextRace();