BvS WK Sync

Attempts to solve World Kaiju sync.

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name           BvS WK Sync
// @namespace      BvS
// @version	   1.3
// @history        1.3 New domain - animecubedgaming.com - Channel28
// @history        1.2 Now https compatible (Updated by Channel28)
// @history        1.1 Added grant permissions (Updated by Channel28)
// @history        1.0 Initial Release
// @description    Attempts to solve World Kaiju sync.
// @include        http*://*animecubed.com/billy/bvs/worldkaiju-group.html
// @include        http*://*animecubedgaming.com/billy/bvs/worldkaiju-group.html
// @licence        MIT; http://www.opensource.org/licenses/mit-license.php
// @copyright      2010, Daniel Karlsson
// @grant          GM_log
// ==/UserScript==

const TIMELIMIT = 5000; // ms

var options = document.evaluate("//form[@name='groupcheck']/select[@name='c1']/option",
	document, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);

var colours = [];
for (var i = 0; i < options.snapshotLength; i++) {
	var value = parseInt(options.snapshotItem(i).value);
	var colour = options.snapshotItem(i).textContent;
	colours[value] = colour;
}

function find(val, arr)
{
	for (var i in arr)
		if (arr[i] == val)
			return i;
	return -1;
}
function print(arr)
{
	var xarr = [];
	for (var i in arr)
		xarr.push(colours[arr[i]]);
	return xarr.join(",");
}
	
function Mastermind(pegs, colours)
{
	var my = this;
	
	my.colours = colours;
	my.pegs = pegs;
	my.prevGuesses = [];
	
	// Create array of all possible combinations
	my.combinations = [];
	for (var i = 0; i < Math.pow(my.colours, my.pegs); i++) {
		var n = i;
		var comb = [];
		for (var p = 0; p < my.pegs; p++) {
			comb.push(n % my.colours);
			n = Math.floor(n / my.colours);
		}
		my.combinations.push(comb);
	}
	
	my.appendGuess = function(g, s) {
		my.prevGuesses.push({guess: g, score: s});
	}
	
	// Determine score as [<correct colour, correct position>, <correct colour, wrong position>]
	my.score = function(guess, board) {
		var boardColours = [];
		var guessColours = [];
		
		for (var i = 0; i < my.colours; i++) {
			boardColours.push(0);
			guessColours.push(0);
		}

		var correctPosition = 0;
		for (var i = 0; i < my.pegs; i++) {
			if (board[i] == guess[i])
				correctPosition++;
			else {
				boardColours[board[i]]++;
				guessColours[guess[i]]++;
			}
		}

		var correctColour = 0;
		for (var c = 0; c < my.colours; c++)
			correctColour += Math.min(guessColours[c], boardColours[c]);

		return [correctPosition, correctColour];
	}
	
	my.possibleScores = [];
	for (var c = 0; c <= my.pegs; c++)
		for (var p = 0; p <= my.pegs - c; p++)
			my.possibleScores.push([p, c]);

	// Remove combinations based on guess and score
	my.eliminateCombinations = function(guess, score, splice) {
		var keepers = [];
		var removals = 0;
		for (var c in my.combinations) {
			var s = my.score(guess, my.combinations[c]);
			if (s[0] == score[0] && s[1] == score[1]) {
				if (splice)
					keepers.push(my.combinations[c]);
			} else {
				removals++;
			}
		}
		if (splice)
			my.combinations = keepers;
		return removals;
	}
	
	// Calculate minimum number of eliminations
	my.guessScore = function(guess) {
		var score = my.combinations.length + 1;
		for (var s in my.possibleScores) {
			var removals = my.eliminateCombinations(guess, my.possibleScores[s], false);
			score = Math.min(removals, score);
		}
		return score;
	}
	
	// Estimate time of bsetGuess brute force method
	my.estimateTime = function() {
		var t1 = new Date();
		var score = my.guessScore(my.combinations[0]);
		var t2 = new Date();
		var t = t2.getTime() - t1.getTime();
		return t * my.combinations.length;
	}
	
	// Brute force search of all possibilities
	my.bestGuess = function() {
		var bestGuess;
		var bestScore = -1;
		for (var guess in my.combinations) {
			var score = my.guessScore(my.combinations[guess]);
			if (score > bestScore) {
				bestScore = score;
				bestGuess = my.combinations[guess];
			}
		}
		my.bestScore = bestScore;
		my.bestGuess = bestGuess;
		return bestGuess;
	}

	// Try random guesses until we run out of time
	my.fastGuess = function(limit) {
		var t1 = new Date();
		t1 = t1.getTime();
		var bestGuess;
		var bestScore = -1;
		var time = 0;
		var tried = [];
		do {
			var guess;
			guess = Math.floor(Math.random() * my.combinations.length);
			while (find(guess, tried) >= 0)
				guess = (guess + 1) % my.combinations.length;
			tried.push(guess);
			
			var score = my.guessScore(my.combinations[guess]);
			if (score > bestScore) {
				bestScore = score;
				bestGuess = my.combinations[guess];
			}
			var t2 = new Date();
			time = t2.getTime() - t1;

			if (tried.length >= my.combinations.length)
				break;
		} while (time < limit)
		my.bestScore = bestScore;
		my.bestGuess = bestGuess;
		return bestGuess;
	}
}

var form = document.evaluate("//form[@name='groupcheck']",
	document, null, XPathResult.ANY_UNORDERED_NODE_TYPE, null);
if (form) {
	form = form.singleNodeValue;
	var guesses = document.evaluate("//table/tbody/tr[count(descendant::td)=5]",
		document, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);
		
	var prevGuesses = 0;
	var board = new Mastermind(4, colours.length);

	// Legacy hint
	if (/You have a strong feeling that the first Chakra isn.t (\w+)/.test(form.textContent)) {
		GM_log("Legacy: " + RegExp.lastParen);
		var chakra = find(RegExp.lastParen, colours);
		
		if (chakra >= 0) {
			keepers = [];
			for (var i in board.combinations)
				if (board.combinations[i][0] != chakra)
					keepers.push(board.combinations[i]);
			GM_log("Kept " + keepers.length + " / " + board.combinations.length);
			board.combinations = keepers;
		}
	}

	for (var g = 0; g < guesses.snapshotLength; g++) {
		var tds = guesses.snapshotItem(g).getElementsByTagName("td");
		var guess = [];
		var res = []
		for (var i = 0; i < 4; i++) {
			var col = tds[i].textContent;
			if (find(col, colours))
				guess.push(find(col, colours));
			else
				continue;
		}
		var match = tds[4].textContent.match(/(\d+)[^\d]+(\d+)/);
		if (match && guess.length == 4) {
			res = [parseInt(match[1]), parseInt(match[2])];
			board.appendGuess(guess, res);
			board.eliminateCombinations(guess, res, true);
			prevGuesses++;
		}
	}
	
	var div = document.createElement("div");
	form.parentNode.insertBefore(div, form.nextSibling);
	div.textContent = "Searching...";
	setTimeout(function() {
		var limited = false;
		var bestGuess;
		if (board.estimateTime() > TIMELIMIT) {
			bestGuess = board.fastGuess(TIMELIMIT);
			limited = true;
		} else
			bestGuess = board.bestGuess();

		if (board.combinations.length > 0) {
			if (limited)
				div.textContent = "Guess (" + Math.round(TIMELIMIT / 1000) + " s search): " + print(bestGuess);
			else
				div.textContent = "Best guess: " + print(bestGuess);
		}
	}, 100);
}