Last active
September 24, 2016 23:26
-
-
Save gwillen/3e72ae7756b8ac8449e56a7d157de69b to your computer and use it in GitHub Desktop.
Userscript to find OGS games
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==UserScript== | |
// @name Find good Go games | |
// @namespace http://nerdnet.org/ | |
// @version 0.1 | |
// @description find good Go games on online-go.com | |
// @author Glenn Willen (gwillen@nerdnet.org) | |
// @match *://online-go.com/* | |
// @grant none | |
// ==/UserScript== | |
/* Configuration */ | |
// We use our OGS 'overall' rank for comparison. See the definition of 'get_my_rank' below to change this. | |
// A game with handicap is always considered ok; with no handicap, the below restrictions | |
// are enforced relative to myrank: | |
var max_ranks_above = 2; | |
var max_ranks_below = 3; | |
// I wrote a whole general scheme for parsing time controls, but right now everything but | |
// byoyomi is ranked "bad", and byoyomi is "good" if it meets all the following criteria: | |
var min_byoyomi_seconds = 30; | |
var min_byoyomi_periods = 3; | |
// Our "seconds per move" formula divides the main time PLUS all the byoyomi periods (assuming | |
// each is used only once), over the expected average number of moves per player for the | |
// whole game (using a bullshit formula I made up from observation). If we meet the "good" | |
// number, it's green; "ok" is yellow. | |
// The default numbers are very conservative relative to how most people seem to time their | |
// OGS games, because I'm a slow player. | |
var min_seconds_per_move_good = 30; | |
var min_seconds_per_move_ok = 20; | |
/* End of Configuration */ | |
/////////////////////////////////////////////////////////////////////////////////////////// | |
function parse_rank(r) { | |
// 30k => 0 | |
// Nk => 30 - N | |
// 1k => 29 | |
// 1d => 30 | |
// Nd => 29 + N | |
// 9d => 38 | |
var m = r.match(/^(\d+)([dk])$/); | |
if (m[2] == "k") { | |
return 30 - (+m[1]); | |
} else { | |
return 29 + (+m[1]); | |
} | |
} | |
// Defer this until later, when everything has loaded. | |
var myrank; | |
function get_my_rank() { | |
myrank = window.profile.ranking; | |
} | |
function parse_time(t) { | |
if (t === "") { | |
return 0; | |
} | |
if (t.match(/ /)) { | |
var total = 0; | |
var parts = t.split(" "); | |
for (var i = 0; i < parts.length; i++) { | |
total += parse_time(parts[i]); | |
} | |
return total; | |
} | |
var modifiers = { | |
"s": 1, | |
"m": 60, | |
"h": 60 * 60, | |
"d": 60 * 60 * 24, | |
"wk": 60 * 60 * 24 * 7 | |
}; | |
var m = t.match(/^(\d+)([a-z]+)$/); | |
if (m) { | |
return m[1] * modifiers[m[2]]; | |
} else { | |
return undefined; | |
} | |
} | |
function parse_time_control(t) { | |
// remove our own added notes | |
t = t.replace(/ \(.*\)$/, ''); | |
if (t == "None") { | |
//console.log("None"); | |
return { | |
type: "none" | |
}; | |
} else if (t.match(/^(.*)\+ (.*) up to (.*)$/)) { | |
var fischer = t.match(/^(.*)\+ (.*) up to (.*)$/); | |
//console.log("Fischer(" + fischer[1] + ", " + fischer[2] + ", " + fischer[3] + ")"); | |
return { | |
type: "fischer", | |
main: parse_time(fischer[1]), | |
increment: parse_time(fischer[2]), | |
max: parse_time(fischer[3]) | |
}; | |
} else if (t.match(/^(.*)\+ (.*)\/(.*)$/)) { | |
var canadian = t.match(/^(.*)\+ (.*)\/(.*)$/); | |
//console.log("Canadian(" + canadian[1] + ", " + canadian[2] + ", " + canadian[3] + ")"); | |
return { | |
type: "canadian", | |
main: parse_time(canadian[1]), | |
increment: parse_time(canadian[2]), | |
moves_per_increment: +canadian[3] | |
}; | |
} else if (t.match(/^(.*)\+(.*)x(.*)$/)) { | |
var byoyomi = t.match(/^(.*)\+(.*)x(.*)$/); | |
//console.log("Byo-yomi(" + byoyomi[1] + ", " + byoyomi[2] + ", " + byoyomi[3] + ")"); | |
return { | |
type: "byoyomi", | |
main: parse_time(byoyomi[1]), | |
periods: +byoyomi[2], | |
increment: parse_time(byoyomi[3]) | |
}; | |
} else if (t.match(/^(.*)\/move$/)) { | |
var simple = t.match(/^(.*)\/move$/); | |
//console.log("Simple(" + simple[1] + ")"); | |
return { | |
type: "simple", | |
increment: parse_time(simple[1]) | |
}; | |
} else { | |
//console.log("Absolute(" + t + ")"); | |
return { | |
type: "absolute", | |
main: parse_time(t) | |
}; | |
} | |
} | |
function typical_moves(board_size) { | |
return Math.pow(board_size, 1.75) * 0.925; // empirical curve-fitting bullshit | |
} | |
function main_time_per_move(tc, size) { | |
// The name lies a little: | |
// - In byoyomi, we count main time + all periods (since you can at LEAST treat them as main time.) | |
// - In fischer, we SHOULD count main time + avg moves * increment. | |
var avg_moves = typical_moves(size) / 2; // per-player | |
if (tc.type == "byoyomi") { | |
return (tc.main + tc.periods * tc.increment) / avg_moves; | |
} | |
return 0/0; | |
} | |
function good_time_control(size, tc) { | |
var avg_moves = typical_moves(size) / 2; // per-player | |
var mtm = main_time_per_move(tc, size); | |
console.log("main time per average move: ", mtm); | |
var moves_per_inc = tc.moves_per_increment || 1; | |
//console.log("increment per average move: ", tc.increment / moves_per_inc); | |
if ( | |
tc.type == "byoyomi" && | |
tc.increment >= min_byoyomi_seconds && | |
tc.periods >= min_byoyomi_periods) { | |
if (mtm >= min_seconds_per_move_good) { | |
return 2; | |
} else if (mtm >= min_seconds_per_move_ok) { | |
return 1; | |
} | |
} | |
return 0; | |
} | |
// XXX copypasta from above | |
function time_add_info(size, tc) { | |
var avg_moves = typical_moves(size) / 2; // per-player | |
var mtm = main_time_per_move(tc, size); | |
if (tc.type == "byoyomi") { | |
// main time per average move | |
return "m/m: " + mtm.toFixed(2); | |
} else { | |
return "X"; | |
} | |
} | |
function check_challenges() { | |
if (!myrank) { | |
get_my_rank(); | |
} | |
var challenges = $('.challenge-row.ng-scope'); | |
console.log("challenges", challenges); | |
for (var i = 0; i < challenges.length; i++) { | |
var is_live = challenges[i].attributes["ng-repeat"].textContent.match(/live/); | |
if (!is_live) { | |
continue; | |
} | |
var accept_s = (challenges[i].children[0].textContent || challenges[i].children[0].toString()).replace(/(^\s*)|(\s*$)/g,''); | |
var user_s = challenges[i].children[1].children[0].children[0].textContent.replace(/(^\s*)|(\s*$)/g,''); | |
var board_s = challenges[i].children[2].textContent.replace(/(^\s*)|(\s*$)/g,''); | |
var time_s = challenges[i].children[3].textContent.replace(/(^\s*)|(\s*$)/g,''); | |
var ranked_s = challenges[i].children[4].textContent.replace(/(^\s*)|(\s*$)/g,''); | |
var handicap_s = challenges[i].children[5].textContent.replace(/(^\s*)|(\s*$)/g,''); | |
var accept = false; | |
if (accept_s == "Accept") { | |
accept = true; | |
} | |
var rank = parse_rank(user_s.match(/\((\d+[dk])\)$/)[1]); | |
var size = +(board_s.match(/(\d+)x/)[1]); | |
var tc = parse_time_control(time_s); | |
var gtc = good_time_control(size, tc); | |
var ranked = (ranked_s == "Yes"); | |
var handicap_ok = ( | |
handicap_s == "Auto" || | |
(handicap_s == "No" && rank - myrank <= max_ranks_above && myrank - rank <= max_ranks_below)); | |
var handicap_good = ( | |
handicap_s == "Auto" || | |
(handicap_s == "No" && rank == myrank)); | |
// If we didn't already add a note to the time control field, add it now | |
if (!challenges[i].children[3].textContent.match(/\(/)) { | |
challenges[i].children[3].textContent = time_s + " (" + time_add_info(size, tc) + ")"; | |
} | |
if (gtc == 2) { | |
challenges[i].children[3].style.backgroundColor = "green"; | |
} else if (gtc == 1) { | |
challenges[i].children[3].style.backgroundColor = "yellow"; | |
} else { | |
challenges[i].children[3].style.backgroundColor = "red"; | |
} | |
if (accept) { | |
console.log("challenge: ", challenges[i], accept, user_s, rank, size, time_s, tc, ranked, handicap_s); | |
challenges[i].children[0].style.backgroundColor = "green"; | |
} else { | |
challenges[i].children[0].style.backgroundColor = "red"; | |
} | |
if (ranked) { | |
challenges[i].children[4].style.backgroundColor = "green"; | |
} else { | |
challenges[i].children[4].style.backgroundColor = "red"; | |
} | |
if (handicap_good) { | |
challenges[i].children[5].style.backgroundColor = "green"; | |
} else if (handicap_ok) { | |
challenges[i].children[5].style.backgroundColor = "yellow"; | |
} else { | |
challenges[i].children[5].style.backgroundColor = "red"; | |
} | |
} | |
setTimeout(check_challenges, 2500); | |
} | |
(function() { | |
'use strict'; | |
setTimeout(check_challenges, 2500); | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment