@@ -78,12 +78,17 @@ function render(method) {
7878
7979function renderAll ( ) {
8080 $ ( '.filterchart' ) . each ( function ( ) { this . render ( ) } ) ;
81+
82+ // TODO make original scout page work again
8183 list . each ( render ) ;
82- d3 . select ( "#active" ) . text ( formatNumber ( gr_all . value ( ) ) ) ;
83- numWins = _ . find ( winGrp . all ( ) , function ( grp ) { return grp . key == "true" } ) . value . value
84- pctWins = Math . round ( 1000.0 * numWins / gr_all . value ( ) ) / 10.0 ;
84+
85+ d3 . select ( "#active" ) . text ( formatNumber ( cf_all . value ( ) ) ) ;
86+
87+ // TODO make original scout page work again
88+ // numWins = _.find(winGrp.all(), function(grp) {return grp.key == "true"}).value.value
89+ // pctWins = Math.round(1000.0 * numWins / cf_all.value()) / 10.0;
8590 // console.log("wins:", numWins, pctWins);
86- d3 . select ( "#winrate" ) . text ( pctWins ) ;
91+ // d3.select("#winrate").text(pctWins);
8792}
8893
8994function matchList ( elem ) {
@@ -127,7 +132,21 @@ function entities_url() {
127132 return data_host ( ) + "/ents" + debug_suffix ( ) + ".csv" ;
128133}
129134
130- function filter_chart ( element , dimension , group , colorgroup , domain ) {
135+ function cumulativize ( group ) {
136+ // TODO can be more efficient, but does it matter.
137+ return _ . map ( group , function ( element , index , list ) {
138+ var allFollowingGroups = _ . rest ( list , index ) ;
139+ var allFollowingSummary = _ . reduce ( allFollowingGroups ,
140+ function ( memo , group ) {
141+ return { wins : memo . wins + group . value . wins ,
142+ games : memo . games + group . value . games } ;
143+ } , { wins :0 , games :0 } ) ;
144+ allFollowingSummary . value = Math . round ( 1000.0 * allFollowingSummary . wins / allFollowingSummary . games ) / 10.0 ;
145+ return { key : element . key , value : allFollowingSummary } ;
146+ } ) ;
147+ }
148+
149+ function filter_chart ( element , dimension , group , colorgroup , domain , tooltip ) {
131150 element . group = group ;
132151 element . colorgroup = colorgroup ;
133152 element . dimension = dimension ;
@@ -136,15 +155,23 @@ function filter_chart(element, dimension, group, colorgroup, domain) {
136155
137156 element . render = function ( ) {
138157 var grp = this . group . all ( ) ;
139- var colorGrp = this . colorgroup . all ( ) ;
140-
141- var xycolor = _ . map ( _ . zip ( grp , colorGrp ) , function ( gs ) {
158+ // var cumColorGrp = cumulativize(colorGrp);
159+ var data ;
160+
161+ if ( this . colorgroup ) {
162+ var colorGrp = this . colorgroup . all ( ) ;
163+ data = _ . map ( _ . zip ( grp , colorGrp ) , function ( gs ) {
142164 var winpct = gs [ 1 ] . value . value ;
143165 var color = winInterp ( logistic ( ( winpct - 50 ) / 10.0 ) ) ;
144166 return { x :gs [ 0 ] . key , y :gs [ 0 ] . value . value , color :color , winpct :winpct } ;
145- } ) ;
167+ } ) ;
168+ } else {
169+ data = _ . map ( grp , function ( g ) {
170+ return { x :g . key , y :g . value . value , color : 'steelblue' } ;
171+ } ) ;
172+ }
146173
147- this . chart . series [ 0 ] . setData ( xycolor ) ;
174+ this . chart . series [ 0 ] . setData ( data ) ;
148175 }
149176
150177 element . reset = function ( ) {
@@ -157,7 +184,7 @@ function filter_chart(element, dimension, group, colorgroup, domain) {
157184 }
158185
159186 element . range_select = function ( event ) {
160- console . log ( "Selected" , event . xAxis [ 0 ] . min , event . xAxis [ 0 ] . max , event ) ;
187+ // console.log("Selected", event.xAxis[0].min, event.xAxis[0].max, event);
161188
162189 event . xAxis [ 0 ] . axis . removePlotBand ( 'plot-band-1' ) ;
163190
@@ -182,9 +209,7 @@ function filter_chart(element, dimension, group, colorgroup, domain) {
182209 event . preventDefault ( ) ;
183210 } ;
184211
185- element . tooltip = function ( ) {
186- return this . x + ': ' + this . y + ' games, ' + this . point . winpct + '% win.' ;
187- } ;
212+ element . tooltip = tooltip ;
188213
189214 var options = {
190215 chart : {
@@ -254,7 +279,9 @@ function reset_click(e) {
254279 filterchart . reset ( ) ;
255280}
256281
257- function add_filterchart ( element , dimension , group , title , domain ) {
282+ function add_filterchart ( element , dimension , group , title , domain , addWinPct , tooltip ) {
283+
284+ addWinPct = typeof addWinPct !== 'undefined' ? addWinPct : true ;
258285
259286 var chartContainer = $ ( document . createElement ( 'div' ) ) . addClass ( 'djchart' ) ;
260287
@@ -271,9 +298,12 @@ function add_filterchart(element, dimension, group, title, domain) {
271298 var thechart = $ ( document . createElement ( 'div' ) ) . addClass ( 'filterchart' ) ;
272299 chartContainer . append ( thechart ) ;
273300
274- var colorgroup = groupWinPct ( group . clone ( ) ) ;
301+ var colorgroup = null ;
302+ if ( addWinPct ) {
303+ colorgroup = groupWinPct ( group . clone ( ) ) ;
304+ }
275305
276- filter_chart ( thechart [ 0 ] , dimension , group , colorgroup , domain ) ;
306+ filter_chart ( thechart [ 0 ] , dimension , group , colorgroup , domain , tooltip ) ;
277307
278308 element . append ( chartContainer ) ;
279309}
@@ -325,7 +355,7 @@ function scout_init() {
325355 } ) ;
326356 var rec_built = Date . now ( ) ;
327357 gr_cf = crossfilter ( gamerecords ) ;
328- gr_all = gr_cf . groupAll ( ) ;
358+ cf_all = gr_cf . groupAll ( ) ;
329359
330360 raceDim = gr_cf . dimension ( function ( gr ) { return gr . player . race } ) ;
331361 raceGrp = groupDJ ( raceDim . group ( ) ) ;
@@ -388,15 +418,232 @@ function scout_init() {
388418 } )
389419 } ) ;
390420
391- add_filterchart ( $ ( '#filtercharts' ) , lgDim , lgGrp , 'Game league' , [ 0 , 6 ] ) ;
392- add_filterchart ( $ ( '#filtercharts' ) , asDim , asGrp , 'Player\'s Army Strength @ X minutes' , [ 0 , 2500 ] ) ;
393- add_filterchart ( $ ( '#filtercharts' ) , oasDim , oasGrp , 'Opponent\'s Army Strength @ X minutes' , [ 0 , 2500 ] ) ;
394- add_filterchart ( $ ( '#filtercharts' ) , wsDim , wsGrp , 'Player\'s Workers @ X minutes' , [ 0 , 60 ] ) ;
395- add_filterchart ( $ ( '#filtercharts' ) , owsDim , owsGrp , 'Opponent\'s Workers @ X minutes' , [ 0 , 60 ] ) ;
396- add_filterchart ( $ ( '#filtercharts' ) , mb2Dim , mb2Grp , 'Player\'s 2nd Mining Base Timing' , [ 0 , 15 ] ) ;
397- add_filterchart ( $ ( '#filtercharts' ) , omb2Dim , omb2Grp , 'Opponent\'s 2nd Mining Base Timing' , [ 0 , 15 ] ) ;
398- add_filterchart ( $ ( '#filtercharts' ) , durDim , durGrp , 'Game Length, minutes' , [ 0 , 40 ] ) ;
399- add_filterchart ( $ ( '#filtercharts' ) , dateDim , dateGrp , 'Game Date' , [ Date . UTC ( 2014 , 6 , 25 ) , Date . UTC ( 2014 , 8 , 15 ) ] ) ;
421+
422+ tooltip = function ( ) {
423+ return this . x + ': ' + this . y + ' games, ' + this . point . winpct + '% win.' ;
424+ } ;
425+
426+ add_filterchart ( $ ( '#filtercharts' ) , lgDim , lgGrp , 'Game league' , [ 0 , 6 ] , tooltip ) ;
427+ add_filterchart ( $ ( '#filtercharts' ) , asDim , asGrp , 'Player\'s Army Strength @ X minutes' , [ 0 , 2500 ] , tooltip ) ;
428+ add_filterchart ( $ ( '#filtercharts' ) , oasDim , oasGrp , 'Opponent\'s Army Strength @ X minutes' , [ 0 , 2500 ] , tooltip ) ;
429+ add_filterchart ( $ ( '#filtercharts' ) , wsDim , wsGrp , 'Player\'s Workers @ X minutes' , [ 0 , 60 ] , tooltip ) ;
430+ add_filterchart ( $ ( '#filtercharts' ) , owsDim , owsGrp , 'Opponent\'s Workers @ X minutes' , [ 0 , 60 ] , tooltip ) ;
431+ add_filterchart ( $ ( '#filtercharts' ) , mb2Dim , mb2Grp , 'Player\'s 2nd Mining Base Timing' , [ 0 , 15 ] , tooltip ) ;
432+ add_filterchart ( $ ( '#filtercharts' ) , omb2Dim , omb2Grp , 'Opponent\'s 2nd Mining Base Timing' , [ 0 , 15 ] , tooltip ) ;
433+ add_filterchart ( $ ( '#filtercharts' ) , durDim , durGrp , 'Game Length, minutes' , [ 0 , 40 ] , tooltip ) ;
434+ add_filterchart ( $ ( '#filtercharts' ) , dateDim , dateGrp , 'Game Date' , [ Date . UTC ( 2014 , 6 , 25 ) , Date . UTC ( 2014 , 8 , 15 ) ] , tooltip ) ;
435+
436+ renderAll ( ) ;
437+
438+ var end = Date . now ( ) ;
439+ var total_time = end - start ;
440+ console . log ( "init took " + ( total_time / 1000 ) + " seconds" ) ;
441+
442+ var build_rec = ( rec_built - start ) / 1000 ;
443+ var build_dims = ( dims_built - rec_built ) / 1000 ;
444+ var build_chart = ( end - dims_built ) / 1000 ;
445+ console . log ( build_rec , build_dims , build_chart ) ;
446+ } ) ;
447+ } ) ;
448+
449+
450+ }
451+
452+ // TODO do this with angular rather than d3, so we can ditch d3 someday
453+ function playerList ( elem ) {
454+ var playersByNumGames = numGamesDim . top ( 80 ) ;
455+
456+ elem . each ( function ( ) {
457+ var player = d3 . select ( this ) . selectAll ( ".player" )
458+ . data ( playersByNumGames ) ;
459+
460+ var playerEnter = player . enter ( ) . append ( "tr" ) . attr ( "class" , "player" ) ;
461+ playerEnter . append ( "td" ) . attr ( "class" , "num" ) . append ( "a" ) ;
462+ playerEnter . append ( "td" ) . attr ( "class" , "race" ) ;
463+ playerEnter . append ( "td" ) . attr ( "class" , "league" ) ;
464+ playerEnter . append ( "td" ) . attr ( "class" , "apm" ) ;
465+ playerEnter . append ( "td" ) . attr ( "class" , "num_games" ) ;
466+ playerEnter . append ( "td" ) . attr ( "class" , "winpct" ) ;
467+
468+ player . exit ( ) . remove ( ) ;
469+
470+ player . select ( '.num a' ) . text ( function ( p ) { return p . identity_id } ) ;
471+ player . select ( '.num a' ) . attr ( "href" , function ( p ) { return "http://ggtracker.com/players/" + p . identity_id } ) ;
472+ player . select ( '.race' ) . text ( function ( p ) { return p . race ( ) ; } ) ;
473+ player . select ( '.league' ) . text ( function ( p ) { return p . league ( ) ; } ) ;
474+ player . select ( '.apm' ) . text ( function ( p ) { return p . apm ( ) ; } ) ;
475+ player . select ( '.num_games' ) . text ( function ( p ) { return p . num_games . total ; } ) ;
476+ player . select ( '.winpct' ) . text ( function ( p ) { return p . win_pct ( 'total' ) ; } ) ;
477+ } ) ;
478+ }
479+
480+ function scout_players_init ( ) {
481+ var start = Date . now ( ) ;
482+
483+ /**
484+ * Each player has the following defined:
485+ *
486+ * identity_id
487+ * num_games[matchup] <-- where matchup is each of: PvT, PvZ, PvP...TvT and total
488+ * num_wins[matchup]
489+ * sum_apm <-- sum of apm over all games
490+ * sum_league <-- sum of league over all games
491+ * num_games_by_race[chosen_race] <-- where chosen_race is P, T, Z, R
492+ *
493+ * apm()
494+ * league()
495+ * win_pct(matchup)
496+ * race()
497+ */
498+
499+ matches = { } ;
500+ entities = [ ] ;
501+ players = { } ;
502+ match_winner = { } ;
503+ match_loser = { } ;
504+ gamerecords = [ ] ;
505+
506+
507+ var matchup_zero = { 'PvT' : 0 ,
508+ 'PvZ' : 0 ,
509+ 'PvP' : 0 ,
510+ 'TvT' : 0 ,
511+ 'TvP' : 0 ,
512+ 'TvZ' : 0 ,
513+ 'ZvZ' : 0 ,
514+ 'ZvT' : 0 ,
515+ 'ZvP' : 0 ,
516+ 'total' : 0 } ;
517+ var race_zero = { 'P' :0 , 'T' :0 , 'Z' :0 , 'R' :0 } ;
518+
519+ var apm = function ( ) {
520+ if ( this . num_games . total == 0 ) return 0 ;
521+ return Math . floor ( this . sum_apm / this . num_games . total ) ;
522+ }
523+ var league = function ( ) {
524+ if ( this . num_games . total == 0 ) return 0 ;
525+ return Math . floor ( 10 * this . sum_league / this . num_games . total ) / 10.0 ;
526+ }
527+ var win_pct = function ( matchup ) {
528+ if ( this . num_games [ matchup ] == 0 ) return 0 ;
529+ return Math . floor ( 1000.0 * this . num_wins [ matchup ] / this . num_games [ matchup ] ) / 10.0 ;
530+ }
531+ var race = function ( ) {
532+ var num_most_played = _ . max ( _ . values ( this . num_games_by_race ) ) ;
533+ var race_most_played = _ . find ( _ . pairs ( this . num_games_by_race ) , function ( kv ) { return kv [ 1 ] == num_most_played ; } ) [ 0 ] ;
534+ return race_most_played ;
535+ }
536+
537+ // record
538+ record_player_in_match = function ( match , player_entity , opponent_entity ) {
539+ var matchup = player_entity . race + 'v' + opponent_entity . race ;
540+
541+ var player ;
542+ if ( ! ( player_entity . identity_id in players ) ) {
543+ player = { identity_id : player_entity . identity_id ,
544+ num_games : _ . clone ( matchup_zero ) ,
545+ num_wins : _ . clone ( matchup_zero ) ,
546+ num_games_by_race : _ . clone ( race_zero ) ,
547+ sum_apm : 0 ,
548+ sum_league : 0 ,
549+ apm : apm ,
550+ league : league ,
551+ win_pct : win_pct ,
552+ race : race
553+ } ;
554+ players [ player_entity . identity_id ] = player ;
555+ } else {
556+ player = players [ player_entity . identity_id ] ;
557+ }
558+ player . num_games [ matchup ] ++ ;
559+ player . num_games [ 'total' ] ++ ;
560+ player . num_games_by_race [ player_entity . chosen_race ] ++ ;
561+ if ( player_entity . win == 'true' ) {
562+ player . num_wins [ matchup ] ++ ;
563+ player . num_wins [ 'total' ] ++ ;
564+ }
565+ player . sum_apm += player_entity . apm ;
566+ player . sum_league += match . average_league ;
567+ }
568+
569+ entity_non_numerics = [ "race" , "chosen_race" , "win" ]
570+
571+ d3 . csv ( matches_url ( ) , function ( error , csv_matches ) {
572+ csv_matches . forEach ( function ( match , index ) {
573+ match . play_date = new Date ( match . play_date ) ;
574+ match . id = + match . id
575+ match . average_league = + match . average_league
576+ match . duration_minutes = + match . duration_minutes
577+ matches [ match . id ] = match
578+ } ) ;
579+ d3 . csv ( entities_url ( ) , function ( error , csv_ents ) {
580+ csv_ents . forEach ( function ( entity , index ) {
581+
582+ // convert numeric fields to be actual numbers
583+ for ( var key in entity ) {
584+ if ( ! ( _ . contains ( entity_non_numerics , key ) ) ) {
585+ entity [ key ] = + entity [ key ] ;
586+ }
587+ }
588+
589+ if ( entity . match_id in matches ) {
590+ match = matches [ entity . match_id ] ;
591+ if ( entity . win == "true" ) {
592+ match_winner [ entity . match_id ] = entity ;
593+ } else if ( entity . win == "false" ) {
594+ match_loser [ entity . match_id ] = entity ;
595+ }
596+
597+ if ( entity . match_id in match_winner && entity . match_id in match_loser ) {
598+ record_player_in_match ( match , match_winner [ entity . match_id ] , match_loser [ entity . match_id ] ) ;
599+ record_player_in_match ( match , match_loser [ entity . match_id ] , match_winner [ entity . match_id ] ) ;
600+ }
601+ }
602+ } ) ;
603+
604+ // get rid of players with less than 10 games, not useful here
605+ players = _ . values ( players ) ;
606+ players = _ . filter ( players , function ( player ) { return player . num_games . total >= 10 } ) ;
607+
608+ var rec_built = Date . now ( ) ;
609+ p_cf = crossfilter ( players ) ;
610+ cf_all = p_cf . groupAll ( ) ;
611+
612+ raceDim = p_cf . dimension ( function ( p ) { return p . race ( ) ; } ) ;
613+ raceGrp = groupDJ ( raceDim . group ( ) ) ;
614+
615+ numGamesDim = p_cf . dimension ( function ( p ) { return p . num_games . total ; } ) ;
616+ numGamesGrp = groupDJ ( numGamesDim . group ( function ( d ) { return 5 * Math . floor ( d / 5 ) ; } ) ) ;
617+
618+ leagueDim = p_cf . dimension ( function ( p ) { return p . league ( ) ; } ) ;
619+ leagueGrp = groupDJ ( leagueDim . group ( function ( d ) { return Math . floor ( d * 2 ) / 2 ; } ) ) ;
620+
621+ winPctDim = p_cf . dimension ( function ( p ) { return p . win_pct ( 'total' ) ; } ) ;
622+ winPctGrp = groupDJ ( winPctDim . group ( function ( d ) { return 5 * Math . floor ( d / 5 ) ; } ) ) ;
623+
624+ apmDim = p_cf . dimension ( function ( p ) { return p . apm ( ) ; } ) ;
625+ apmGrp = groupDJ ( apmDim . group ( function ( d ) { return 5 * Math . floor ( d / 5 ) ; } ) ) ;
626+
627+ var dims_built = Date . now ( ) ;
628+
629+ // Render the initial lists.
630+ list = d3 . selectAll ( ".list tbody" )
631+ . data ( [ playerList ] ) ;
632+
633+ if ( false ) {
634+
635+ d3 . selectAll ( "#total" )
636+ . text ( formatNumber ( p_cf . size ( ) ) ) ;
637+ }
638+
639+ tooltip = function ( ) {
640+ return this . x + ': ' + this . y + ' players.' ;
641+ } ;
642+
643+ add_filterchart ( $ ( '#filtercharts' ) , numGamesDim , numGamesGrp , 'Number of Games Played' , [ 0 , 150 ] , false , tooltip ) ;
644+ add_filterchart ( $ ( '#filtercharts' ) , leagueDim , leagueGrp , 'League' , [ 0 , 6 ] , false , tooltip ) ;
645+ add_filterchart ( $ ( '#filtercharts' ) , winPctDim , winPctGrp , 'Win Percentage' , [ 0 , 100 ] , false , tooltip ) ;
646+ add_filterchart ( $ ( '#filtercharts' ) , apmDim , apmGrp , 'APM' , [ 0 , 300 ] , false , tooltip ) ;
400647
401648 renderAll ( ) ;
402649
0 commit comments