BvS WK Sync

Attempts to solve World Kaiju sync.

スクリプトをインストールするには、Tampermonkey, GreasemonkeyViolentmonkey のような拡張機能のインストールが必要です。

You will need to install an extension such as Tampermonkey to install this script.

スクリプトをインストールするには、TampermonkeyViolentmonkey のような拡張機能のインストールが必要です。

スクリプトをインストールするには、TampermonkeyUserscripts のような拡張機能のインストールが必要です。

このスクリプトをインストールするには、Tampermonkeyなどの拡張機能をインストールする必要があります。

このスクリプトをインストールするには、ユーザースクリプト管理ツールの拡張機能をインストールする必要があります。

(ユーザースクリプト管理ツールは設定済みなのでインストール!)

このスタイルをインストールするには、Stylusなどの拡張機能をインストールする必要があります。

このスタイルをインストールするには、Stylus などの拡張機能をインストールする必要があります。

このスタイルをインストールするには、Stylus tなどの拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

(ユーザースタイル管理ツールは設定済みなのでインストール!)

このスクリプトの質問や評価の投稿はこちら通報はこちらへお寄せください
// ==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);
}