import { Context } from 'shared/parse/Context' ; import { CrawlEntry , CrawlerBase , CrawlEvent } from 'shared/parse/Crawler' ; import { Node } from 'shared/parse/Node' ; export interface CrawlerStats { inputs : Set < string > ; maxPathActions : number ; minPathActions : number ; numInternalStates : number ; outputs : Set < string > ; } export type StatsCrawlEntry = CrawlEntry < Context > ; export class StatsCrawler extends CrawlerBase < Context > { protected statsById : { [ id ] : CrawlerStats ; } ; protected statsByLine : { [ line ] : CrawlerStats ; } ; protected statsByEvent : { [ event ] : Array < { line : number ; id : string ; fromAction : string | number ; } > ; } ; protected root : Node < Context > ; constructor ( ) { super ( ) ; this . statsById = { START : { inputs : new Set ( ) , outputs : new Set ( ) , minPathActions : - 1 , maxPathActions : - 1 , numInternalStates : - 1 } , } ; this . statsByLine = { '-1' : { inputs : new Set ( ) , outputs : new Set ( ) , minPathActions : - 1 , maxPathActions : - 1 , numInternalStates : - 1 } , } ; this . statsByEvent = { END : [ ] , IMPLICIT_END : [ ] , INVALID : [ ] , } ; } public crawl ( root ? , timeLimitMillis = 500 , depthLimit = 150 , visitLimit = 10 ) { if ( ! this . root && root ) { this . root = root ; } return super . crawl ( root , timeLimitMillis , depthLimit , visitLimit ) ; } public getStatsForId ( id ) { return this . statsById [ id ] ; } public getStatsForLine ( line ) { return this . statsByLine [ line ] ; } public getStatsByEvent ( evt ) { return this . statsByEvent [ evt ] ; } public getLines ( ) { return Object . keys ( this . statsByLine ) . filter ( ( k ) => ( k !== '-1' ) ) . map ( ( s ) => parseInt ( s , 10 ) ) ; } public getIds ( ) { return Object . keys ( this . statsById ) . filter ( ( k ) => ( k !== 'START' ) ) ; } private lineWithinCombatRound ( line ) { const n = this . root . elem . closest ( 'quest' ) . find ( `[data-line=${ line }]` ) ; return n . closest ( 'event[on="round"]' ) . length > 0 ; } protected onEvent ( q , e ) { if ( e === 'MAX_DEPTH_EXCEEDED' || e === 'VISIT_LIMIT_EXCEEDED' || e === 'ALREADY_SEEN' ) { return ; } if ( e === 'IMPLICIT_END' && this . lineWithinCombatRound ( q . prevLine ) ) { return ; } this . statsById [ q . prevId ] . outputs . add ( e ) ; this . statsByLine [ q . prevLine ] . outputs . add ( e ) ; this . statsByEvent [ e ] . push ( { line : q . prevLine , id : q . prevId , fromAction : q . fromAction } ) ; } protected onErrors ( q , errors , line ) { } protected onNode ( q , nodeStr , id , line ) { if ( this . statsById [ id ] === undefined ) { this . statsById [ id ] = { inputs : new Set ( ) , maxPathActions : this . statsById [ q . prevId ] . maxPathActions + 1 , minPathActions : this . statsById [ q . prevId ] . minPathActions + 1 , numInternalStates : 1 , outputs : new Set ( ) , } ; } if ( this . statsByLine [ line ] === undefined ) { this . statsByLine [ line ] = { inputs : new Set ( ) , maxPathActions : this . statsByLine [ q . prevLine ] . maxPathActions + 1 , minPathActions : this . statsByLine [ q . prevLine ] . minPathActions + 1 , numInternalStates : 0 , outputs : new Set ( ) , } ; } const lineStats = this . statsByLine [ line ] ; if ( id !== q . prevId ) { this . statsById [ id ] . maxPathActions = Math . max ( this . statsById [ id ] . maxPathActions , ( this . statsById [ q . prevId ] . maxPathActions || 0 ) + 1 ) ; this . statsById [ id ] . minPathActions = Math . min ( this . statsById [ id ] . minPathActions , ( this . statsById [ q . prevId ] . minPathActions || 0 ) + 1 ) ; this . statsById [ id ] . inputs . add ( q . prevId ) ; this . statsById [ q . prevId ] . outputs . add ( id ) ; } else { this . statsById [ id ] . numInternalStates ++ ; } this . statsByLine [ line ] . maxPathActions = Math . max ( this . statsByLine [ line ] . maxPathActions , ( this . statsByLine [ q . prevLine ] . maxPathActions || 0 ) + 1 ) ; this . statsByLine [ line ] . minPathActions = Math . min ( this . statsByLine [ line ] . minPathActions , ( this . statsByLine [ q . prevLine ] . minPathActions || 0 ) + 1 ) ; lineStats . inputs . add ( q . prevNodeStr ) ; this . statsByLine [ q . prevLine ] . outputs . add ( nodeStr ) ; } }