LotteryTest / update.js
DennisChan0909's picture
initial deploy: 今彩539 AI 預測系硱
2c7a564
Raw
History Blame Contribute Delete
6 kB
/**
* 今彩539 ε’žι‡ζ›΄ζ–°θ…³ζœ¬ (Incremental Update)
* εͺζŠ“ε–ζ―”ηΎζœ‰θ³‡ζ–™ζ›΄ζ–°ηš„ζœŸθ™ŸοΌŒι€ŸεΊ¦εΏ«γ€ηœζ΅ι‡
* 用法: node update.js
*/
const https = require('https');
const fs = require('fs');
const DATA_FILE = 'lottery_539_data.json';
const API_URL = 'https://www.pilio.idv.tw/Json_ltonew.asp';
// ── Load existing data ────────────────────────────────────────────────────────
const existing = JSON.parse(fs.readFileSync(DATA_FILE, 'utf8'));
const lastDex = Math.max(...existing.draws.map(d => parseInt(d.dex)));
console.log(`πŸ“‚ ηΎζœ‰θ³‡ζ–™οΌš${existing.draws.length} 期 (ζœ€ζ–° dex=${lastDex})`);
// ── Fetch helper ──────────────────────────────────────────────────────────────
function fetchJson(lindex, ldesc) {
return new Promise((resolve, reject) => {
const postData = `Lkind=lto539&Lindex=${lindex}&Ldesc=${ldesc}`;
const opts = {
hostname: 'www.pilio.idv.tw',
port: 443,
path: '/Json_ltonew.asp',
method: 'POST',
headers: {
'User-Agent': 'Mozilla/5.0',
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': Buffer.byteLength(postData)
}
};
const req = https.request(opts, res => {
let raw = '';
res.on('data', c => raw += c);
res.on('end', () => {
try { resolve(JSON.parse(raw)); }
catch { reject(new Error('JSON parse failed: ' + raw.slice(0, 100))); }
});
});
req.on('error', reject);
req.write(postData);
req.end();
});
}
function pad(n) { return String(parseInt(n)).padStart(2, '0'); }
function parseDate(raw) {
const clean = raw.replace(/<[^>]+>/g, ' ').trim();
const m = clean.match(/^(\d{2})[\/-](\d{2})\s+(\d{2})\((.)\)/);
if (!m) return clean;
const yr = parseInt(m[3]) < 50 ? `20${m[3]}` : `19${m[3]}`;
return `${yr}/${m[1]}/${m[2]} (${m[4]})`;
}
// ── Fetch only new draws ──────────────────────────────────────────────────────
async function fetchNewDraws() {
const newDraws = [];
console.log('πŸ” ζͺ’ζŸ₯ζ˜―ε¦ζœ‰ζ–°ζœŸζ•Έ...');
let currentIndex = lastDex;
while (true) {
let batch;
try { batch = await fetchJson(currentIndex, 0); } // 0 is ascending (old to new)
catch (e) {
console.log('⚠️ API Error:', e.message);
break;
}
const arr = batch.lotto || [];
if (!arr.length) break;
for (const item of arr) {
const idx = parseInt(item.dex);
if (idx <= lastDex) {
continue;
}
const numText = item.num.replace(/<[^>]+>/gi, '').replace(/&nbsp;/gi, ' ');
const nums = numText.match(/\d{1,2}/g).map(n => pad(n));
if (nums.length < 5) continue;
newDraws.push({
date: parseDate(item.date),
dateSort: (() => {
const clean = item.date.replace(/<[^>]+>/g, ' ').trim();
const m = clean.match(/^(\d{2})[\/-](\d{2})\s+(\d{2})\(/);
if (m) {
const y = parseInt(m[3]);
const fullYr = y < 50 ? `20${String(y).padStart(2,'0')}` : `19${String(y).padStart(2,'0')}`;
return `${fullYr}${m[1]}${m[2]}`;
}
return '';
})(),
numbers: nums.slice(0,5),
dex: item.dex,
});
}
currentIndex = arr[arr.length - 1].dex;
}
// API fetches descending, but our newDraws needs to be appended in ascending order later
// wait, we just return it, the main function sorts it: .sort((a,b)=>parseInt(a.dex)-parseInt(b.dex))
return newDraws;
}
// ── Prediction (M3 SpreadOverdue) ─────────────────────────────────────────────
const BUCKETS = [
[1,2,3,4,5,6,7,8],
[9,10,11,12,13,14,15],
[16,17,18,19,20,21,22],
[23,24,25,26,27,28,29,30],
[31,32,33,34,35,36,37,38,39],
];
function predict(draws) {
const gaps = {};
for (let i = 1; i <= 39; i++) gaps[pad(i)] = draws.length;
for (let i = draws.length - 1; i >= Math.max(0, draws.length - 600); i--)
for (const n of draws[i].numbers)
if (gaps[pad(n)] === draws.length) gaps[pad(n)] = draws.length - 1 - i;
return BUCKETS.map(b =>
b.map(n => ({ n: pad(n), s: gaps[pad(n)] || 0 })).sort((a,b)=>b.s-a.s)[0].n
).sort((a,b) => parseInt(a)-parseInt(b));
}
// ── Main ──────────────────────────────────────────────────────────────────────
(async () => {
const newDraws = await fetchNewDraws();
if (newDraws.length === 0) {
console.log('βœ… ε·²ζ˜―ζœ€ζ–°θ³‡ζ–™οΌŒη„‘ιœ€ζ›΄ζ–°');
return;
}
console.log(`✨ ζ–°ε’ž ${newDraws.length} 期:`);
for (const d of newDraws) console.log(` 期 ${d.dex} ${d.date} ${d.numbers.join(', ')}`);
// Merge & sort
const merged = [...existing.draws, ...newDraws]
.sort((a, b) => parseInt(a.dex) - parseInt(b.dex));
// Deduplicate by dex
const seen = new Set();
const deduped = merged.filter(d => {
if (seen.has(d.dex)) return false;
seen.add(d.dex);
return true;
});
// Update prediction
const prediction = predict(deduped);
console.log(`🎯 ζ–°ι ζΈ¬θ™Ÿη’ΌοΌš${prediction.join(', ')}`);
const out = {
...existing,
draws: deduped,
prediction,
generatedAt: new Date().toISOString(),
modelVersion: 'M3_SpreadOverdue',
modelNote: 'ε›žζΈ¬β‰₯1ε‘½δΈ­ηŽ‡ 51.3%',
};
fs.writeFileSync(DATA_FILE, JSON.stringify(out, null, 2), 'utf8');
console.log(`βœ… lottery_539_data.json ε·²ζ›΄ζ–° (ε…± ${deduped.length} 期)`);
console.log('\nβ–Ά ζŽ₯θ‘—θ«‹εŸ·θ‘Œ: node backtest.js');
})();