diff --git a/src/SlippiGame.ts b/src/SlippiGame.ts index e7888073..df99fd2d 100644 --- a/src/SlippiGame.ts +++ b/src/SlippiGame.ts @@ -13,7 +13,8 @@ import { SlpParser } from './utils/slpParser'; import { StockComputer, ComboComputer, ActionsComputer, ConversionComputer, InputComputer, Stats, FrameEntryType, FramesType, StatsType, getSinglesPlayerPermutationsFromSettings, - generateOverallStats + generateOverallStats, + StockType } from './stats'; /** @@ -147,6 +148,7 @@ export class SlippiGame { actionCounts: this.actionsComputer.fetch(), overall: overall, gameComplete: this.parser.getGameEnd() !== null, + winningPlayerIndex: this.determineWinningPlayerIndex(stocks) }; if (this.parser.getGameEnd() !== null) { @@ -160,6 +162,31 @@ export class SlippiGame { return stats; } + public determineWinningPlayerIndex(stocks: StockType[]): number { + if (stocks.length == 0) return -1; + + var playerIndexOccurences: Array<{playerIndex: number, count: number, lastPercent: number}> = []; + + stocks.forEach(stockType => { + if(!playerIndexOccurences.some(occurences => occurences.playerIndex === stockType.playerIndex)){ + playerIndexOccurences.push({playerIndex: stockType.playerIndex, count: 0, lastPercent: 0}); + } + + var occurence = playerIndexOccurences.find(occurence => occurence.playerIndex == stockType.playerIndex); + occurence.count++; + occurence.lastPercent = stockType.endPercent; + }); + + var sortedByOccurence = _.sortBy(playerIndexOccurences, occurence => occurence.count); + var mostOccurences = sortedByOccurence[sortedByOccurence.length - 1]; + + var matchingOccurences = sortedByOccurence.filter(occurence => occurence.count == mostOccurences.count); + if (matchingOccurences.length == 1) return mostOccurences.playerIndex; + + var sortedByPercent = _.sortBy(playerIndexOccurences, occurence => occurence.lastPercent); + return sortedByPercent[sortedByPercent.length - 1].playerIndex; + } + public getMetadata(): MetadataType { if (this.metadata) { return this.metadata; diff --git a/src/stats/common.ts b/src/stats/common.ts index b4d808a3..e3aa0585 100644 --- a/src/stats/common.ts +++ b/src/stats/common.ts @@ -27,6 +27,7 @@ export type StatsType = { combos: ComboType[]; actionCounts: ActionCountsType[]; overall: OverallType[]; + winningPlayerIndex: number; }; export type RatioType = { diff --git a/src/stats/overall.ts b/src/stats/overall.ts index e92467fd..49fec252 100644 --- a/src/stats/overall.ts +++ b/src/stats/overall.ts @@ -32,6 +32,7 @@ export function generateOverallStats( const conversions = _.get(conversionsByPlayer, playerIndex) || []; const successfulConversions = conversions.filter(conversion => conversion.moves.length > 1); const opponentStocks = _.get(stocksByPlayer, opponentIndex) || []; + const playerStocks = _.get(stocksByPlayer, playerIndex) || []; const opponentEndedStocks = _.filter(opponentStocks, 'endFrame'); const conversionCount = conversions.length; diff --git a/src/stats/stocks.ts b/src/stats/stocks.ts index e0eec642..1db03ba6 100644 --- a/src/stats/stocks.ts +++ b/src/stats/stocks.ts @@ -3,6 +3,7 @@ import _ from 'lodash'; import { FrameEntryType, FramesType, isDead, didLoseStock, PlayerIndexedType, StockType } from "./common"; import { StatComputer } from './stats'; +import { PostFrameUpdateType } from '../utils/slpReader'; interface StockState { stock: StockType | null | undefined; @@ -38,10 +39,10 @@ export class StockComputer implements StatComputer { function handleStockCompute(frames: FramesType, state: StockState, indices: PlayerIndexedType, frame: FrameEntryType, stocks: StockType[]): void { const playerFrame = frame.players[indices.playerIndex].post; - // FIXME: use PostFrameUpdateType instead of any - const prevPlayerFrame: any = _.get( + + const prevPlayerFrame: PostFrameUpdateType = _.get( frames, [playerFrame.frame - 1, 'players', indices.playerIndex, 'post'], {} - ); + ) as PostFrameUpdateType; // If there is currently no active stock, wait until the player is no longer spawning. // Once the player is no longer spawning, start the stock