User:Peterwhy/common.js

/* User:Peterwhy/BSiconTooltips */
importScript( 'User:Peterwhy/BSiconTooltips.js' );// Backlink: [[User:Peterwhy/BSiconTooltips]]
/* User:Peterwhy/BSiconTooltips */
//importScript( 'User:Peterwhy/BSiconTooltips/sandbox.js' );// Backlink: [[User:Peterwhy/BSiconTooltips]]
importScript( 'User:Joeytje50/JWB.js/load.js' ); // Backlink: [[User:Joeytje50/JWB.js/load.js]]

(function () {
	var replaceImgWith2x = function () {
		// Test forcing page width
		//document.getElementsByTagName("body")[0].style.width = "980px";
		//document.getElementById("mw-head").style.width="980px";
		//document.getElementById("mw-head").style.left="0px";
				
		// Detect device pixel ratio
		var dpr = 1;
		if(window.devicePixelRatio !== undefined) {
			dpr = window.devicePixelRatio;
		}

		// Replace all images that can upscale
		var bodyContent = document.getElementById("bodyContent");
		var bodyContentImgs = bodyContent.getElementsByTagName("img");
		for (var i in bodyContentImgs) {
			var imgSrc = bodyContentImgs[i].getAttribute("src");
			var imgSrcParts = imgSrc.split("/");
			var imgSrcPartsLen = imgSrcParts.length;
			var imgSrcPx = parseInt(imgSrcParts[imgSrcPartsLen-1], 10);
			
			if (imgSrcParts[imgSrcPartsLen-5] != "thumb" || isNaN(imgSrcPx)) {
				continue;
			}
			
			var imgSrcNewPx = imgSrcPx * 2;
			imgSrcParts[imgSrcPartsLen-1] = imgSrcParts[imgSrcPartsLen-1].replace(imgSrcPx+"px-", imgSrcNewPx+"px-");
			var imgSrcRetinaSize = imgSrcParts.join("/");

			imgSrcParts.splice(imgSrcPartsLen-1,1);
			imgSrcParts.splice(imgSrcPartsLen-5,1);
			var imgSrcFullSize = imgSrcParts.join("/");

			bodyContentImgs[i].setAttribute("width",imgSrcPx);
			bodyContentImgs[i].setAttribute("src",imgSrcRetinaSize);
			bodyContentImgs[i].setAttribute("onerror", "this.setAttribute(\"src\", \""+imgSrcFullSize+"\");this.removeAttribute(\"onerror\");");
		}
	};
	//setTimeout(replaceImgWith2x, 0);
	//window.addEventListener("DOMContentLoaded", replaceImgWith2x);
	window.addEventListener("load", replaceImgWith2x);
	//replaceImgWith2x();
	
	var replaceLogo = function() {
		// Modify main logo
		var logo = document.getElementById("p-logo").getElementsByTagName("a")[0];
		logo.style.backgroundImage = "url(\"//upload.wikimedia.org/wikipedia/commons/thumb/b/b3/Wikipedia-logo-v2-en.svg/270px-Wikipedia-logo-v2-en.svg.png\")";
		logo.style.backgroundSize = "135px";
	};
	//window.addEventListener("load", replaceLogo);
	
	var adjustViewport = function() {
		// Define viewport for iOS
		if ((window.screen.width >= 640 || window.screen.height >= 640) && (window.orientation !== undefined)) {
			var meta = document.getElementById("meta_viewport");
			if (meta === null) {
				meta = document.createElement("meta");
				meta.setAttribute("id","meta_viewport");
				document.getElementsByTagName("head")[0].appendChild(meta);
			}
			meta.name="viewport";
			switch (window.orientation) {
				case 0:
				case 180:
					meta.content="width=device-width, initial-scale=1.0";
					break;
				case 90:
				case -90:
					meta.content="width=device-height";
					break;
			}
			//meta.content="initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no";
		}
	};
	window.addEventListener("DOMContentLoaded", adjustViewport);
	window.addEventListener("orientationchange", adjustViewport);
});

(function () {
	var Player = function (symbol, color) {
		this.symbol = symbol.charAt(0);
		this.color = color;
	};
	Player.prototype.inviteMove = function (board, opponent, callback, display) {
		if (board.getResult() !== false) {
			throw new Error("Game already ended");
		}
		
		var callbacks = new Array(board.columns.length);
		for (var i = 0; i < board.columns.length; i++) {
			if (board.columns[i].isFull()) {
				callbacks[i] = null;
			} else {
				callbacks[i] = function (response) {
					return function () {
						display.removeControls();
						callback(response);
						return false;
					}
				} (i);
			}
		}
		
		display.addControls(callbacks);
		display.setBusy(false);
	};
	
	var Bot = function (symbol, color) {
		//throw new Error("Bot not ready");
		Player.call(this, symbol, color);
		this.heuristicLib = new Array();
	}
	Bot.prototype = new Player("-");
	Bot.prototype.constructor = Bot;
	Bot.prototype.getRandomResponse = function (board) {
		var emptyCellIds = new Array();
		for (var i = 0; i < board.columns.length; i++) {
			if (!board.columns[i].isFull()) {
				emptyCellIds.push(i);
			}
		}
		return emptyCellIds[Math.floor(Math.random()*emptyCellIds.length)];
	};
	Bot.prototype.getResponse = function (board, opponent) {
		//return this.getRandomResponse(board);
	
		// check if game already ended
		if (board.getResult() !== false) {
			throw new Error("Game already ended");
		}
		
		// finding best move
		var bestScore = Number.NEGATIVE_INFINITY;
		var scores = new Array(board.columns.length);
		var moves = [0, 1, 2, 3, 4, 5, 6]
		for (var i = moves.shift(); i !== undefined; i = moves.shift()) {
			if (!board.columns[i].isFull()) {
				board.columns[i].addCell(this);
				scores[i] = -this.evaluateState(board, opponent, this, 7, Number.NEGATIVE_INFINITY, Number.POSITIVE_INFINITY);
				board.columns[i].undoAddCell();
				bestScore = Math.max(bestScore, scores[i]);
				document.writeln("Move " + (i+1) + ": score = " + scores[i]);
			}
		}
		
		// randomly choose from one of the best moves
		var bestMoves = new Array();
		for (var i = 0; i < scores.length; i++) {
			if (scores[i] === bestScore) {
				bestMoves.push(i);
			}
		}
		return bestMoves[Math.floor(Math.random() * bestMoves.length)];
	}
	Bot.prototype.inviteMove = function (board, opponent, callback) {
		// check if game already ended
		if (board.getResult() !== false) {
			throw new Error("Game already ended");
		}
		
		// finding best move
		var bestScore = Number.NEGATIVE_INFINITY;
		var scores = new Array(board.columns.length);
		var moves = board.toValidMoveString().replace(/\t/g, "").replace(/\s/g, "").split("");
		for (var i = 0; i < moves.length; i++) {
			moves[i] -= 1;
		}
		document.writeln(moves);
		for (var i = moves.shift(); i !== undefined; i = moves.shift()) {
			if (!board.columns[i].isFull()) {
				board.columns[i].addCell(this);
				scores[i] = -this.evaluateState(board, opponent, this, 7, Number.NEGATIVE_INFINITY, Number.POSITIVE_INFINITY);
				board.columns[i].undoAddCell();
				bestScore = Math.max(bestScore, scores[i]);
				document.writeln("Move " + (i+1) + ": score = " + scores[i]);
			}
		}
		
		// randomly choose from one of the best moves
		var bestMoves = new Array();
		for (var i = 0; i < scores.length; i++) {
			if (scores[i] === bestScore) {
				bestMoves.push(i);
			}
		}
		var out = bestMoves[Math.floor(Math.random() * bestMoves.length)];
		
		setTimeout(function (response) {
			return function () {
				callback(response);
			}
		} (out), 0);
	}
	Bot.prototype.evaluateState = function (board, player, opponent, depth, alpha, beta) {
		//document.writeln(board.toString());
		
		// check whether game ended
		var result = board.getResult();
		if (result === true) {
			return 0;
		} else if (result === player) {
			return 1000;
		} else if (result === opponent) {
			return -1000;
		} else if (depth <= 0) {
			return this.heuristic(board, player, opponent);
		}
		
		// if game not ended
		var moves = [3, 4, 2, 5, 1, 6, 0]
		for (var i = moves.shift(); i !== undefined; i = moves.shift()) {
			if (!board.columns[i].isFull()) {
				board.columns[i].addCell(player);
				var score = -this.evaluateState(board, opponent, player, depth-1, -beta, -alpha);
				board.columns[i].undoAddCell();
				alpha = Math.max(alpha, score);
				/*if (depth == 8) {
					document.writeln("\tMove " + (i+1) + ": score = " + score);
				}*/
				if (beta <= alpha) {
					break;
				}
			}
		}
		// return the score from perspective of player
		return alpha;
	}
	Bot.prototype.heuristic = function (board, player, opponent) {
		// load from library
		var boardStr = board.toString();
		if (this.heuristicLib[boardStr] !== undefined) {
			return this.heuristicLib[boardStr];
		}
		
		// getting the true opponent object relative to this
		var trueOpponent = (this != player) ? player : opponent;
		var out = 0;
		
		// calculate winning lines
		for (var i = 0; i < board.columns[0].cells.length; i++) {
			var botCount = 0;
			var opponentCount = 0;
			for (var j = 0; j < board.columns.length; j++) {
				if (board.columns[j].cells[i].owner == this) {
					botCount++;
				} else if (board.columns[j].cells[i].owner) {
					opponentCount++;
				}
			}
			if (botCount > 0 && opponentCount == 0) {
				out+=2;
			} else if (botCount == 0 && opponentCount > 0) {
				out--;
			} else if (botCount == 0 && opponentCount == 0) {
				break;
			}
		}
		
		// calculate threats
		var maxNextEmptyCellId = 0;
		for (var i = 0; i < board.columns.length; i++) {
			maxNextEmptyCellId = Math.max(maxNextEmptyCellId, board.columns[i].nextEmptyCellId);
		}
		for (var i = 0; i < board.columns.length; i++) {
			for (var j = board.columns[i].nextEmptyCellId; j <= maxNextEmptyCellId && j < board.columns[i].cells.length; j++) {
				board.columns[i].cells[j].owner = this;
				if (board.getResult(i, j) == this) {
					out += 10;
				}
				board.columns[i].cells[j].owner = trueOpponent;
				if (board.getResult(i,j) == trueOpponent) {
					out -= 10;
				}
				board.columns[i].cells[j].owner = null;
			}
		}
		
		this.heuristicLib[boardStr] = out;
		return out;
	};
	
	var Cell = function () {
		this.owner = null;
	};
	
	var Column = function () {
		this.cells = new Array(6);
		for (var i = 0; i < this.cells.length; i++) {
			this.cells[i] = new Cell();
		}
		this.nextEmptyCellId = 0;
	};
	Column.prototype.isFull = function () {
		return this.nextEmptyCellId >= this.cells.length;
	};
	Column.prototype.addCell = function (owner) {
		if (!this.isFull()) {
			this.cells[this.nextEmptyCellId++].owner = owner;
		} else {
			throw new Error("Cannot add into a full column");
		}
	};
	Column.prototype.undoAddCell = function () {
		if (this.nextEmptyCellId > 0) {
			this.cells[--this.nextEmptyCellId].owner = null;
		} else {
			throw new Error("Cannot undo an empty Column");
		}
	};
	
	var Board = function () {
		this.columns = new Array(7);
		for (var i = 0; i < this.columns.length; i++) {
			this.columns[i] = new Column();
		}
	};
	Board.prototype.getResult = function (lastColumn, lastRow) {
		// case when last step made is not obvious
		if (lastColumn === undefined) {
			var isDraw = true;
			
			for (var i = 0; i < this.columns.length; i++) {
				var result = this.getResult(i);
				if (result !== true && result !== false) {
					return result;
				}
				isDraw = isDraw && result;
			}
			return isDraw;
		}
		
		// case when last step row is not given
		if (lastRow === undefined) {
			lastRow = this.columns[lastColumn].nextEmptyCellId - 1;
		}
		if (lastRow < 0) {
			return false;
		}
		
		// check if last move makes a win
		var dx = [1, 1, 0, -1, -1, -1, 0, 1];
		var dy = [0, 1, 1, 1, 0, -1, -1, -1];
		var extensions = [0, 0, 0, 0, 0, 0, 0, 0];
		var lastCell = this.columns[lastColumn].cells[lastRow];
		var owner = lastCell.owner;
		
		for (var direction = 0; direction < extensions.length; direction++) {
			for (var x = lastColumn, y = lastRow; (x >= 0 && x < this.columns.length && y >= 0 && y < this.columns[x].cells.length) && this.columns[x].cells[y].owner == owner; x += dx[direction], y += dy[direction]) {
				extensions[direction]++;
			}
		}
		for (var direction = 0; direction < extensions.length/2; direction++) {
			if (extensions[direction] - 1 + extensions[direction+4] == 4) {
				return owner;
			}
		}
		
		// return boolean if board is draw
		for (var i = 0; i < this.columns.length; i++) {
			if (!this.columns[i].isFull()) {
				return false;
			}
		}
		return true;
	};
	Board.prototype.toFormattedString = function() {
		var out = "";
		for (var i = this.columns[0].cells.length-1; i >= 0; i--) {
			for (var j = 0; j < this.columns.length; j++) {
				if (j) {
					out += "\t";
				}
				var owner = this.columns[j].cells[i].owner;
				if (owner) {
					out += owner.symbol;
				} else {
					out += " ";
				}
			}
			if (i) {
				out += "\n";
			}
		}
		return out;
	};
	Board.prototype.toValidMoveString = function() {
		var out = "";
		for (var i = 0; i < this.columns.length; i++) {
			if (!this.columns[i].isFull()) {
				out += i + 1;
			} else {
				out += " ";
			}
			out += "\t";
		}
		return out;
	};
	Board.prototype.toString = function () {
		var out = "";
		for (var i = 0; i < this.columns.length; i++) {
			for (var j = 0; j < this.columns[i].cells.length; j++) {
				if (this.columns[i].cells[j].owner === null) {
					out += " ";
				} else {
					out += this.columns[i].cells[j].owner.symbol;
				}
			}
		}
		return out;
	};
	
	var Game = function (isComputer1, isComputer2) {
		this.players = new Array();
		this.players.push((isComputer1) ? new Bot("O", "Red") : new Player("O", "Red"));
		this.players.push((isComputer2) ? new Bot("X", "Gold") : new Player("X", "Gold"));
		this.currentPlayerId = 0;
		this.nextPlayerId = 1;
		this.board = new Board();
		this.display = new Display(this);
	}
	Game.prototype.newRound = function () {
		// display log message
		document.writeln("-------------" + this.players[this.currentPlayerId].symbol);
		document.writeln(this.board.toFormattedString());
		
		// display board
		this.display.display();
		this.display.setBusy(true);
		
		// obj closure reqired to store "this" object reference
		var callback = function (obj) {
			return function (response) {
				obj.move(response);
			}
		} (this);
		setTimeout(function (obj) {
			return function () {
				obj.players[obj.currentPlayerId].inviteMove(obj.board, obj.players[obj.nextPlayerId], callback, obj.display);
			}
		} (this), 100);
		//this.players[this.currentPlayerId].inviteMove(this.board, this.players[this.nextPlayerId], callback, this.display);
	};
	Game.prototype.move = function (response) {
		document.writeln("Response = " + (response+1));
		
		// modify game
		this.board.columns[response].addCell(this.players[this.currentPlayerId]);
		
		// check if game ended
		var result = this.board.getResult(response);
		if (result !== false) {
			this.display.display();
			this.display.setBusy(false);
			if (result === true) {
				alert("Draw");
			} else {
				alert("Winner = " + result.color);
			}
		} else {
			this.currentPlayerId = this.nextPlayerId;
			this.nextPlayerId = (this.nextPlayerId + 1) % this.players.length;
			this.newRound();
		}
	};
	Game.prototype.load = function (array) {
		for (var i = 0; i < array.length; i++) {
			for (var j = array[i].shift(); j !== undefined; j = array[i].shift()) {
				this.board.columns[i].addCell(this.players[j]);
			}
		}
	}
	
	var Display = function (game, parentDOM) {
		if (!parentDOM) {
			parentDOM = document.getElementsByTagName("body")[0];
		}
		this.game = game;
		
		if (Display.singletonDiv) {
			this.div = Display.singletonDiv;
		} else {
			this.div = Display.singletonDiv = document.createElement("div");
			parentDOM.appendChild(this.div);
			
			var table = document.createElement("table");
			this.div.appendChild(table);
			table.style.borderLeft = table.style.borderRight = table.style.borderBottom = "3px double red";
			table.style.cursor = "default";
			table.style.borderCollapse = "collapse";
			
			var tbody = document.createElement("tbody");
			table.appendChild(tbody);
			
			for (var i = this.game.board.columns[0].cells.length-1; i >= 0 ; i--) {
				var tr = document.createElement("tr");
				tbody.appendChild(tr);
				for (var j = 0; j < this.game.board.columns.length; j++) {
					var td = document.createElement("td");
					td.id = "c" + j + "r" + i;
					td.style.textAlign = "center";
					td.style.width = td.style.height = "2em";
					td.style.fontWeight = "900";
					td.style.fontFamily = "\"Century Gothic\", Verdana, sans-serif";
					td.appendChild(document.createTextNode("\u00a0"));
					if (j) {
						td.style.borderLeft = "1px solid silver";
					}
					tr.appendChild(td);
				}
			}
			//alert(document.getElementsByTagName("table")[0].style.display);
		}
	}
	Display.prototype.display = function () {
		var board = this.game.board;
		var tds = this.div.getElementsByTagName("td");
		for (var i = board.columns[0].cells.length-1; i >= 0; i--) {
			for (var j = 0; j < board.columns.length; j++) {
				var tdId = j + (board.columns[0].cells.length - 1 - i) * board.columns.length;
				var td = tds[tdId];
				while (td.hasChildNodes()) {
					td.removeChild(td.firstChild);
				}
				if (board.columns[j].cells[i].owner) {
					td.style.color = board.columns[j].cells[i].owner.color;
					td.appendChild(document.createTextNode("\u25CE"));
					//td.appendChild(document.createTextNode("\u235F"));
				} else {
				}
			}
		}
	}
	Display.prototype.addControls = function (callbacks) {
		var board = this.game.board;
		var tds = this.div.getElementsByTagName("td");
		for (var i = 0; i < board.columns.length; i++) {
			var isFull = board.columns[i].isFull();
			for (var j = board.columns[0].cells.length-1; j >= 0; j--) {
				var tdId = i + (board.columns[0].cells.length - 1 - j) * board.columns.length;
				var td = tds[tdId];
				if (isFull) {
					td.onclick = null;
					td.style.cursor = "default";
				} else {
					td.onclick = callbacks[i];
					td.style.cursor = "pointer";
				}
			}
		}
	}
	Display.prototype.removeControls = function () {
		var tds = this.div.getElementsByTagName("td");
		for (var i = 0; i < tds.length; i++) {
			tds[i].onclick = null;
			tds[i].style.cursor = "default";
		}
	}
	Display.prototype.setBusy = function (isBusy) {
		document.body.style.cursor = (isBusy) ? "wait" : "auto";
	}
	
	startGame = function () {
		document.getElementById("output").innerHTML = "";
	
		var oldWriteln = document.writeln;
		document.writeln = function (element) {
			return function (str) {
				//element.appendChild(document.createTextNode("\n" + str));
			};
		} (document.getElementById("output"));
		
		var game = new Game(true, false);
		game.newRound();
		return false;
	}
	
	//startGame();
	var button = document.createElement("input");
	button.type = "button";
	button.value = "Start fun";
	if (window.attachEvent) {
		button.attachEvent("onclick", startGame);
	} else {
		button.addEventListener("click", startGame, false);
	}
	document.body.appendChild(button);
	
	var div = document.createElement("pre");
	div.id = "output";
	document.body.appendChild(div);
})

Content Disclaimer

Informasi ini disarikan dari Wikipedia dan disajikan kembali untuk tujuan edukasi. Konten tersedia di bawah lisensi CC BY-SA 3.0. Kami tidak bertanggung jawab atas ketidakakuratan data yang bersumber dari kontribusi publik tersebut.

  1. The information displayed on this website is sourced in part or in whole from Wikipedia and has been adapted for the purpose of restating it. We strive to provide accurate and relevant information, however:
  2. There is no guarantee of absolute accuracy. Wikipedia is an open, collaborative project that can be edited by anyone, so information is subject to change.
  3. It is not intended to constitute professional advice. The content displayed is for informational and educational purposes only. For important decisions (e.g., medical, legal, or financial), please consult a professional.
  4. Content copyright. Wikipedia is licensed under the Creative Commons Attribution-ShareAlike License (CC BY-SA). This means that content may be reused with appropriate attribution and shared under a similar license.
  5. Responsible use. Any risk arising from the use of information from this website is entirely the responsibility of the user.