@gotza02/sequential-thinking 10000.1.6 → 10000.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -211,6 +211,153 @@ function optimizeQueries(coverage, baseQueries) {
211
211
  }
212
212
  });
213
213
  }
214
+ /**
215
+ * Build search queries for match analysis
216
+ */
217
+ function buildSearchQueries(homeTeam, awayTeam, league, context) {
218
+ const leagueStr = league ? `${league} ` : '';
219
+ const baseQuery = `${leagueStr}${homeTeam} vs ${awayTeam}`;
220
+ const dateQuery = getDateContext();
221
+ const queries = [
222
+ { type: 'general', query: `${baseQuery} match prediction stats${dateQuery}`, title: 'General & Prediction' },
223
+ { type: 'h2h', query: `${baseQuery} head to head history`, title: 'Head to Head' },
224
+ { type: 'form', query: `${homeTeam} ${awayTeam} recent form last 5 matches${dateQuery}`, title: 'Recent Form' },
225
+ { type: 'news', query: `${baseQuery} team news injuries lineups${dateQuery}`, title: 'Team News & Lineups' },
226
+ { type: 'stats', query: `${baseQuery} xG expected goals stats${dateQuery}`, title: 'Advanced Metrics' },
227
+ { type: 'odds', query: `${baseQuery} Asian Handicap odds prediction today${dateQuery}`, title: 'Latest Handicap Odds' },
228
+ { type: 'fatigue', query: `${homeTeam} ${awayTeam} days rest fixture congestion${dateQuery}`, title: 'Fatigue & Schedule' },
229
+ { type: 'setpieces', query: `${baseQuery} set pieces corners aerial duels${dateQuery}`, title: 'Set Pieces' },
230
+ ];
231
+ if (context) {
232
+ queries.push({ type: 'general', query: `${baseQuery} ${context}${dateQuery}`, title: 'Specific Context' });
233
+ }
234
+ return queries;
235
+ }
236
+ /**
237
+ * Format H2H data for display
238
+ */
239
+ function formatH2HData(h2h, homeTeam, awayTeam) {
240
+ return `### Head-to-Head Record\n` +
241
+ `- Total Matches: ${h2h.totalMatches}\n` +
242
+ `- ${homeTeam} Wins: ${h2h.homeWins}\n` +
243
+ `- Draws: ${h2h.draws}\n` +
244
+ `- ${awayTeam} Wins: ${h2h.awayWins}\n` +
245
+ `- Goals: ${h2h.homeGoals}-${h2h.awayGoals}\n\n`;
246
+ }
247
+ /**
248
+ * Get match result character (W/D/L) for a team
249
+ */
250
+ function getMatchResultChar(match, teamName) {
251
+ const isHome = match.homeTeam.name.includes(teamName);
252
+ const homeScore = match.score?.home ?? 0;
253
+ const awayScore = match.score?.away ?? 0;
254
+ if (isHome) {
255
+ return homeScore > awayScore ? 'W' : homeScore === awayScore ? 'D' : 'L';
256
+ }
257
+ else {
258
+ return awayScore > homeScore ? 'W' : homeScore === awayScore ? 'D' : 'L';
259
+ }
260
+ }
261
+ /**
262
+ * Format team form data for display
263
+ */
264
+ function formatTeamForm(form, teamName, isHomeTeam) {
265
+ const label = isHomeTeam ? 'Home' : 'Away';
266
+ let output = `### ${teamName} Recent Form (Last ${form.length})\n`;
267
+ output += form.map(m => {
268
+ const opponent = isHomeTeam ? m.awayTeam.name : m.homeTeam.name;
269
+ const score = m.score ? `${m.score.home}-${m.score.away}` : '?';
270
+ const result = getMatchResultChar(m, teamName);
271
+ return `- ${result} vs ${opponent} (${score})`;
272
+ }).join('\n');
273
+ return output + '\n\n';
274
+ }
275
+ /**
276
+ * Format API data section for output
277
+ */
278
+ function formatAPIData(apiData, homeTeam, awayTeam) {
279
+ let output = '--- API DATA (Reliable Source) ---\n\n';
280
+ if (apiData.h2h) {
281
+ output += formatH2HData(apiData.h2h, homeTeam, awayTeam);
282
+ }
283
+ if (apiData.homeForm?.length) {
284
+ output += formatTeamForm(apiData.homeForm, homeTeam, true);
285
+ }
286
+ if (apiData.awayForm?.length) {
287
+ output += formatTeamForm(apiData.awayForm, awayTeam, false);
288
+ }
289
+ if (apiData.odds) {
290
+ output += `### Current Odds (from API)\n`;
291
+ output += `- Home Win: ${apiData.odds.homeWin.toFixed(2)}\n`;
292
+ output += `- Draw: ${apiData.odds.draw.toFixed(2)}\n`;
293
+ output += `- Away Win: ${apiData.odds.awayWin.toFixed(2)}\n`;
294
+ if (apiData.odds.asianHandicap) {
295
+ output += `- Asian Handicap: ${apiData.odds.asianHandicap.line}\n`;
296
+ }
297
+ output += '\n';
298
+ }
299
+ return output + '--- END API DATA ---\n\n';
300
+ }
301
+ /**
302
+ * Execute search queries in batches
303
+ */
304
+ async function executeSearches(queries, searchProvider, candidateUrls) {
305
+ const BATCH_SIZE = 4;
306
+ let output = '';
307
+ for (let i = 0; i < queries.length; i += BATCH_SIZE) {
308
+ const batch = queries.slice(i, i + BATCH_SIZE);
309
+ const batchPromises = batch.map(async (q) => {
310
+ try {
311
+ const results = await searchProvider.search(q.query, undefined, 4);
312
+ results.forEach(r => candidateUrls.push(r.url));
313
+ const items = results.map(r => `- [${r.title}](${r.url}): ${r.snippet}`).join('\n');
314
+ return `### ${q.title}\n${items}\n`;
315
+ }
316
+ catch (error) {
317
+ return `### ${q.title} (Failed)\nError: ${error instanceof Error ? error.message : String(error)}\n`;
318
+ }
319
+ });
320
+ const batchResults = await Promise.all(batchPromises);
321
+ output += batchResults.join('\n');
322
+ }
323
+ return output;
324
+ }
325
+ /**
326
+ * Scrape content from URL with error handling
327
+ */
328
+ async function scrapeContentWithErrorHandling(url, type) {
329
+ try {
330
+ const scrapedContent = await scrapeMatchContent(url);
331
+ if (scrapedContent.success) {
332
+ if (type === 'news' && scrapedContent.data) {
333
+ // Truncate news to avoid blowing up context
334
+ const content = scrapedContent.data.substring(0, 3000);
335
+ return content + (scrapedContent.data.length > 3000 ? '\n...(truncated)' : '');
336
+ }
337
+ return scrapedContent.data || '';
338
+ }
339
+ else {
340
+ return `(${type} scrape failed: ${scrapedContent.error})`;
341
+ }
342
+ }
343
+ catch (scrapeError) {
344
+ const errorMsg = scrapeError instanceof Error ? scrapeError.message : String(scrapeError);
345
+ return `(${type} scrape failed: ${errorMsg})`;
346
+ }
347
+ }
348
+ /**
349
+ * Get analysis framework instructions
350
+ */
351
+ function getAnalysisInstructions() {
352
+ return `INSTRUCTIONS: Act as a World-Class Football Analysis Panel. Provide a deep, non-obvious analysis using this framework:\n\n` +
353
+ `1. 📊 THE DATA SCIENTIST (Quantitative):\n - Analyze xG trends & Possession stats.\n - Assess Home/Away variance.\n\n` +
354
+ `2. 🧠 THE TACTICAL SCOUT (Qualitative):\n - Stylistic Matchup (Press vs Block).\n - Key Battles.\n\n` +
355
+ `3. 🚑 THE PHYSIO (Physical Condition):\n - FATIGUE CHECK: Days rest? Travel distance?\n - Squad Depth: Who has the better bench?\n\n` +
356
+ `4. 🎯 SET PIECE ANALYST:\n - Corners/Free Kicks: Strong vs Weak?\n - Who is most likely to score from a header?\n\n` +
357
+ `5. 💎 THE INSIDER (External Factors):\n - Market Movements (Odds dropping?).\n - Referee & Weather impact.\n\n` +
358
+ `6. 🕵️ THE SKEPTIC & SCENARIOS:\n - Why might the favorite LOSE?\n - Game Script: "If Team A scores first..."\n\n` +
359
+ `🏆 FINAL VERDICT:\n - Asian Handicap Leans\n - Goal Line (Over/Under)\n - The "Value Pick"`;
360
+ }
214
361
  /**
215
362
  * Calculate adaptive TTL based on data type and match timing
216
363
  */
@@ -261,162 +408,63 @@ function calculateAdaptiveTTL(dataType, matchDate) {
261
408
  * Perform comprehensive match analysis using web search
262
409
  */
263
410
  export async function performMatchAnalysis(homeTeam, awayTeam, league, context) {
264
- const leagueStr = league ? `${league} ` : '';
265
- const baseQuery = `${leagueStr}${homeTeam} vs ${awayTeam}`;
266
- const dateQuery = getDateContext();
267
- const searchProvider = getSearchProvider();
268
- const queries = [
269
- { type: 'general', query: `${baseQuery} match prediction stats${dateQuery}`, title: 'General & Prediction' },
270
- { type: 'h2h', query: `${baseQuery} head to head history`, title: 'Head to Head' },
271
- { type: 'form', query: `${homeTeam} ${awayTeam} recent form last 5 matches${dateQuery}`, title: 'Recent Form' },
272
- { type: 'news', query: `${baseQuery} team news injuries lineups${dateQuery}`, title: 'Team News & Lineups' },
273
- { type: 'stats', query: `${baseQuery} xG expected goals stats${dateQuery}`, title: 'Advanced Metrics' },
274
- { type: 'odds', query: `${baseQuery} Asian Handicap odds prediction today${dateQuery}`, title: 'Latest Handicap Odds' },
275
- { type: 'fatigue', query: `${homeTeam} ${awayTeam} days rest fixture congestion${dateQuery}`, title: 'Fatigue & Schedule' },
276
- { type: 'setpieces', query: `${baseQuery} set pieces corners aerial duels${dateQuery}`, title: 'Set Pieces' },
277
- ];
278
- if (context) {
279
- queries.push({ type: 'general', query: `${baseQuery} ${context}${dateQuery}`, title: 'Specific Context' });
280
- }
281
- // Try to get API data first
411
+ // Step 1: Build search queries
412
+ const queries = buildSearchQueries(homeTeam, awayTeam, league, context);
413
+ // Step 2: Fetch API data
282
414
  const api = createAPIProvider();
283
415
  const matchId = await findMatchId(homeTeam, awayTeam, league);
284
- let apiData = null;
285
- if (matchId) {
286
- apiData = await fetchAPIMatchData(api, matchId, homeTeam, awayTeam);
287
- }
288
- // Assess data coverage and optimize queries
416
+ const apiData = matchId ? await fetchAPIMatchData(api, matchId, homeTeam, awayTeam) : null;
417
+ // Step 3: Assess coverage and optimize queries
289
418
  const coverage = assessDataCoverage(apiData);
290
419
  const queriesToExecute = optimizeQueries(coverage, queries);
420
+ // Step 4: Build header
291
421
  let combinedResults = `--- FOOTBALL MATCH DATA: ${homeTeam} vs ${awayTeam} ---\n`;
292
422
  if (matchId)
293
423
  combinedResults += `Resolved API Match ID: ${matchId}\n`;
294
424
  combinedResults += `Data Quality Score: ${coverage.score}/100\n`;
295
425
  combinedResults += `Match Context Date: ${new Date().toLocaleDateString()}\n\n`;
296
- // Add API Data section if available
426
+ // Step 5: Add API data section
297
427
  if (apiData && coverage.score > 0) {
298
- combinedResults += `--- API DATA (Reliable Source) ---\n\n`;
299
- if (apiData.h2h) {
300
- combinedResults += `### Head-to-Head Record\n`;
301
- combinedResults += `- Total Matches: ${apiData.h2h.totalMatches}\n`;
302
- combinedResults += `- ${homeTeam} Wins: ${apiData.h2h.homeWins}\n`;
303
- combinedResults += `- Draws: ${apiData.h2h.draws}\n`;
304
- combinedResults += `- ${awayTeam} Wins: ${apiData.h2h.awayWins}\n`;
305
- combinedResults += `- Goals: ${apiData.h2h.homeGoals}-${apiData.h2h.awayGoals}\n\n`;
306
- }
307
- if (apiData.homeForm?.length) {
308
- combinedResults += `### ${homeTeam} Recent Form (Last ${apiData.homeForm.length})\n`;
309
- combinedResults += apiData.homeForm.map(m => {
310
- const isHome = m.homeTeam.name.includes(homeTeam);
311
- const score = m.score ? `${m.score.home}-${m.score.away}` : '?';
312
- const opponent = isHome ? m.awayTeam.name : m.homeTeam.name;
313
- const result = isHome
314
- ? (m.score?.home ?? 0) > (m.score?.away ?? 0) ? 'W' : (m.score?.home === m.score?.away ? 'D' : 'L')
315
- : (m.score?.away ?? 0) > (m.score?.home ?? 0) ? 'W' : (m.score?.home === m.score?.away ? 'D' : 'L');
316
- return `- ${result} vs ${opponent} (${score})`;
317
- }).join('\n');
318
- combinedResults += '\n\n';
319
- }
320
- if (apiData.awayForm?.length) {
321
- combinedResults += `### ${awayTeam} Recent Form (Last ${apiData.awayForm.length})\n`;
322
- combinedResults += apiData.awayForm.map(m => {
323
- const isAway = m.awayTeam.name.includes(awayTeam);
324
- const score = m.score ? `${m.score.home}-${m.score.away}` : '?';
325
- const opponent = isAway ? m.homeTeam.name : m.awayTeam.name;
326
- const result = isAway
327
- ? (m.score?.away ?? 0) > (m.score?.home ?? 0) ? 'W' : (m.score?.home === m.score?.away ? 'D' : 'L')
328
- : (m.score?.home ?? 0) > (m.score?.away ?? 0) ? 'W' : (m.score?.home === m.score?.away ? 'D' : 'L');
329
- return `- ${result} vs ${opponent} (${score})`;
330
- }).join('\n');
331
- combinedResults += '\n\n';
332
- }
333
- if (apiData.odds) {
334
- combinedResults += `### Current Odds (from API)\n`;
335
- combinedResults += `- Home Win: ${apiData.odds.homeWin.toFixed(2)}\n`;
336
- combinedResults += `- Draw: ${apiData.odds.draw.toFixed(2)}\n`;
337
- combinedResults += `- Away Win: ${apiData.odds.awayWin.toFixed(2)}\n`;
338
- if (apiData.odds.asianHandicap) {
339
- combinedResults += `- Asian Handicap: ${apiData.odds.asianHandicap.line}\n`;
340
- }
341
- combinedResults += '\n';
342
- }
343
- combinedResults += `--- END API DATA ---\n\n`;
428
+ combinedResults += formatAPIData(apiData, homeTeam, awayTeam);
344
429
  }
430
+ // Step 6: Add coverage notes
345
431
  if (coverage.factors.missingDataPoints.length > 0) {
346
432
  combinedResults += `*Note: Using web search to supplement: ${coverage.factors.missingDataPoints.join(', ')}*\n\n`;
347
433
  }
434
+ // Step 7: Execute searches
348
435
  const candidateUrls = [];
349
- // Execute searches in parallel (in batches)
350
- const BATCH_SIZE = 4;
351
- for (let i = 0; i < queriesToExecute.length; i += BATCH_SIZE) {
352
- const batch = queriesToExecute.slice(i, i + BATCH_SIZE);
353
- const batchPromises = batch.map(async (q) => {
354
- try {
355
- const results = await searchProvider.search(q.query, undefined, 4);
356
- results.forEach(r => candidateUrls.push(r.url));
357
- const items = results.map(r => `- [${r.title}](${r.url}): ${r.snippet}`).join('\n');
358
- return `### ${q.title}\n${items}\n`;
359
- }
360
- catch (error) {
361
- return `### ${q.title} (Failed)\nError: ${error instanceof Error ? error.message : String(error)}\n`;
362
- }
363
- });
364
- const batchResults = await Promise.all(batchPromises);
365
- combinedResults += batchResults.join('\n');
366
- }
367
- // Deep dive: Scrape the best URLs (Dual-Source Strategy)
368
- if (candidateUrls.length > 0) {
369
- const bestStatsUrl = findBestMatchUrl(candidateUrls);
370
- const bestNewsUrl = findBestNewsUrl(candidateUrls);
371
- // 1. Scrape Stats Source (Priority)
372
- if (bestStatsUrl) {
373
- combinedResults += `\n--- DEEP DIVE ANALYSIS: STATS & DATA (${bestStatsUrl}) ---\n`;
374
- try {
375
- const scrapedContent = await scrapeMatchContent(bestStatsUrl);
376
- if (scrapedContent.success) {
377
- combinedResults += scrapedContent.data || '';
378
- }
379
- else {
380
- combinedResults += `(Stats scrape failed: ${scrapedContent.error})`;
381
- }
382
- }
383
- catch (scrapeError) {
384
- combinedResults += `(Stats scrape failed: ${scrapeError instanceof Error ? scrapeError.message : String(scrapeError)})`;
385
- }
386
- combinedResults += `\n--- END STATS ---\n`;
387
- }
388
- // 2. Scrape News Source (if different from stats)
389
- if (bestNewsUrl && bestNewsUrl !== bestStatsUrl) {
390
- combinedResults += `\n--- DEEP DIVE ANALYSIS: NEWS & LINEUPS (${bestNewsUrl}) ---\n`;
391
- try {
392
- const scrapedContent = await scrapeMatchContent(bestNewsUrl);
393
- if (scrapedContent.success) {
394
- // Truncate news to avoid blowing up context, keep first 3000 chars
395
- const newsContent = scrapedContent.data?.substring(0, 3000) || '';
396
- combinedResults += newsContent + (scrapedContent.data && scrapedContent.data.length > 3000 ? '\n...(truncated)' : '');
397
- }
398
- else {
399
- combinedResults += `(News scrape failed: ${scrapedContent.error})`;
400
- }
401
- }
402
- catch (scrapeError) {
403
- combinedResults += `(News scrape failed: ${scrapeError instanceof Error ? scrapeError.message : String(scrapeError)})`;
404
- }
405
- combinedResults += `\n--- END NEWS ---\n`;
406
- }
407
- }
436
+ const searchProvider = getSearchProvider();
437
+ combinedResults += await executeSearches(queriesToExecute, searchProvider, candidateUrls);
438
+ // Step 8: Scrape deep dive sources
439
+ combinedResults += await scrapeDeepDiveSources(candidateUrls);
440
+ // Step 9: Add instructions
408
441
  combinedResults += `\n--- END DATA ---\n\n`;
409
- // Add analysis framework instructions
410
- combinedResults += `INSTRUCTIONS: Act as a World-Class Football Analysis Panel. Provide a deep, non-obvious analysis using this framework:\n\n`;
411
- combinedResults += `1. 📊 THE DATA SCIENTIST (Quantitative):\n - Analyze xG trends & Possession stats.\n - Assess Home/Away variance.\n\n`;
412
- combinedResults += `2. 🧠 THE TACTICAL SCOUT (Qualitative):\n - Stylistic Matchup (Press vs Block).\n - Key Battles.\n\n`;
413
- combinedResults += `3. 🚑 THE PHYSIO (Physical Condition):\n - FATIGUE CHECK: Days rest? Travel distance?\n - Squad Depth: Who has the better bench?\n\n`;
414
- combinedResults += `4. 🎯 SET PIECE ANALYST:\n - Corners/Free Kicks: Strong vs Weak?\n - Who is most likely to score from a header?\n\n`;
415
- combinedResults += `5. 💎 THE INSIDER (External Factors):\n - Market Movements (Odds dropping?).\n - Referee & Weather impact.\n\n`;
416
- combinedResults += `6. 🕵️ THE SKEPTIC & SCENARIOS:\n - Why might the favorite LOSE?\n - Game Script: "If Team A scores first..."\n\n`;
417
- combinedResults += `🏆 FINAL VERDICT:\n - Asian Handicap Leans\n - Goal Line (Over/Under)\n - The "Value Pick"`;
442
+ combinedResults += getAnalysisInstructions();
418
443
  return combinedResults;
419
444
  }
445
+ /**
446
+ * Scrape deep dive sources for additional context
447
+ */
448
+ async function scrapeDeepDiveSources(candidateUrls) {
449
+ if (candidateUrls.length === 0)
450
+ return '';
451
+ let output = '';
452
+ const bestStatsUrl = findBestMatchUrl(candidateUrls);
453
+ const bestNewsUrl = findBestNewsUrl(candidateUrls);
454
+ // Scrape Stats Source
455
+ if (bestStatsUrl) {
456
+ output += `\n--- DEEP DIVE ANALYSIS: STATS & DATA (${bestStatsUrl}) ---\n`;
457
+ output += await scrapeContentWithErrorHandling(bestStatsUrl, 'stats');
458
+ output += `\n--- END STATS ---\n`;
459
+ }
460
+ // Scrape News Source (if different)
461
+ if (bestNewsUrl && bestNewsUrl !== bestStatsUrl) {
462
+ output += `\n--- DEEP DIVE ANALYSIS: NEWS & LINEUPS (${bestNewsUrl}) ---\n`;
463
+ output += await scrapeContentWithErrorHandling(bestNewsUrl, 'news');
464
+ output += `\n--- END NEWS ---\n`;
465
+ }
466
+ return output;
467
+ }
420
468
  /**
421
469
  * Calculate probability range with uncertainty quantification
422
470
  */
@@ -491,6 +539,37 @@ async function detectValueBets(apiData, homeTeam, awayTeam) {
491
539
  }
492
540
  return valueBets.sort((a, b) => b.value - a.value);
493
541
  }
542
+ /**
543
+ * Calculate points from a team's form matches
544
+ */
545
+ function calculateFormPoints(form, teamName) {
546
+ return form.reduce((sum, m) => {
547
+ const isHome = m.homeTeam.name.includes(teamName);
548
+ const score = m.score;
549
+ if (!score)
550
+ return sum;
551
+ const teamScore = isHome ? score.home : score.away;
552
+ const opponentScore = isHome ? score.away : score.home;
553
+ if (teamScore > opponentScore)
554
+ return sum + 3;
555
+ if (teamScore === opponentScore)
556
+ return sum + 1;
557
+ return sum;
558
+ }, 0);
559
+ }
560
+ /**
561
+ * Calculate probabilities from form data
562
+ */
563
+ function calculateProbabilitiesFromForm(homeForm, awayForm, homeTeam, awayTeam) {
564
+ const homePoints = calculateFormPoints(homeForm, homeTeam);
565
+ const awayPoints = calculateFormPoints(awayForm, awayTeam);
566
+ const totalPoints = homePoints + awayPoints + 5; // +5 for draw possibility
567
+ return {
568
+ homeWin: (homePoints + 1.5) / totalPoints,
569
+ draw: 5 / totalPoints,
570
+ awayWin: (awayPoints + 1.5) / totalPoints,
571
+ };
572
+ }
494
573
  /**
495
574
  * Build structured analysis result from API and web data
496
575
  */
@@ -501,34 +580,10 @@ async function buildStructuredAnalysis(homeTeam, awayTeam, league, apiData, cove
501
580
  let awayWinProb = 0.35;
502
581
  // Adjust based on form if available
503
582
  if (apiData?.homeForm && apiData?.awayForm) {
504
- const homePoints = apiData.homeForm.reduce((sum, m) => {
505
- const isHome = m.homeTeam.name.includes(homeTeam);
506
- const score = m.score;
507
- if (!score)
508
- return sum;
509
- if (isHome) {
510
- return sum + (score.home > score.away ? 3 : score.home === score.away ? 1 : 0);
511
- }
512
- else {
513
- return sum + (score.away > score.home ? 3 : score.away === score.home ? 1 : 0);
514
- }
515
- }, 0);
516
- const awayPoints = apiData.awayForm.reduce((sum, m) => {
517
- const isAway = m.awayTeam.name.includes(awayTeam);
518
- const score = m.score;
519
- if (!score)
520
- return sum;
521
- if (isAway) {
522
- return sum + (score.away > score.home ? 3 : score.away === score.home ? 1 : 0);
523
- }
524
- else {
525
- return sum + (score.home > score.away ? 3 : score.home === score.away ? 1 : 0);
526
- }
527
- }, 0);
528
- const totalPoints = homePoints + awayPoints + 5; // +5 for draw possibility
529
- homeWinProb = (homePoints + 1.5) / totalPoints;
530
- awayWinProb = (awayPoints + 1.5) / totalPoints;
531
- drawProb = 1 - homeWinProb - awayWinProb;
583
+ const probs = calculateProbabilitiesFromForm(apiData.homeForm, apiData.awayForm, homeTeam, awayTeam);
584
+ homeWinProb = probs.homeWin;
585
+ drawProb = probs.draw;
586
+ awayWinProb = probs.awayWin;
532
587
  }
533
588
  // Calculate probability ranges
534
589
  const predictions = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gotza02/sequential-thinking",
3
- "version": "10000.1.6",
3
+ "version": "10000.1.7",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },