User:Writ Keeper/Scripts/blackjackExp.js

From Wikipedia, the free encyclopedia
Note: After saving, you have to bypass your browser's cache to see the changes. Google Chrome, Firefox, Microsoft Edge and Safari: Hold down the ⇧ Shift key and click the Reload toolbar button. For details and instructions about other browsers, see Wikipedia:Bypass your cache.
//only do anything if on the blackjack homepage
if (mw.config.get("wgPageName") == "User:Writ_Keeper/blackjack")
{
	//Global state variables
	var currentBalance = 500;
	var currentBetSetting = 10;
	var splitBetValue = 10;
	var playerHand = {count:0,softAce:false,container:"#playerGroup",value:currentBetSetting};
	var dealerHand = {count:0,softAce:false,container:"#cardTableDealer"};
	var splitHand = {count:0,softAce:false,container:"#splitGroup",value:0};
	var previousBalanceExists = true;
	var faceDownCard = null;
	var isSplit = false;
	var splitWinnings = 0;
	var numDecks = 6;
	var shoe = null;
	var nextCard = -1;
	
	function shuffleDeck()
	{
		var rng = window.crypto || window.msCrypto;
		var shoeSize = 52*numDecks;
		var rngArray = new Uint16Array(shoeSize);
		var rejectArray = new Uint16Array(1);
		var maxInt = 65536;
		shoe = new Array(shoeSize);
		for(i = 0; i < shoe.length; i++)
		{
			shoe[i] = i;
		}
		rng.getRandomValues(rngArray);
		for(i=(shoe.length - 1); i >= 0; i--)
		{
			//discard values greater than the maximum multiple of shoeSize below maxInt; these values would skew the distribution
			while(rngArray[i] >= Math.floor(maxInt/shoeSize) * shoeSize)
			{
				console.log("value rejected in " + i + "th shuffle");
				rng.getRandomValues(rejectArray);
				rngArray[i] = rejectArray[0];
			}
			var tempStorage = shoe[i];
			shoe[i] = shoe[(rngArray[i]%shoeSize)];
			shoe[(rngArray[i]%shoeSize)] = tempStorage;
			shoeSize--;
		}
		nextCard = 0;
		return shoe;
	}
	function changeButton(gameButton, clickFunction, label)
	{
		gameButton.off("click");
		gameButton.click(clickFunction);
		gameButton.find(".mw-ui-button").html(label);
		gameButton.show();
	}
	function changeActionButton(gameButton, clickFunction, gameHand,  label)
	{
		gameButton.off("click");
		gameButton.click({hand:gameHand},clickFunction);
		gameButton.find(".mw-ui-button").html(label);
		gameButton.show();
	}
	
	function changeBet()
	{
		var rawResponse = prompt("Enter the new bet (whole numbers, from 1 - " + currentBalance + "):");
		if(rawResponse == null)
		{
			return;
		}
		var userResponse = parseInt(rawResponse);
		if (isNaN(userResponse))
		{
			alert("That's not a number.");
			return;
		}
		if (userResponse > currentBalance)
		{
			alert("You can't bet money you don't have.");
			return;
		}
		if (userResponse < 1)
		{
			alert("This ain't a charity.");
			return;
		}
		currentBetSetting = userResponse;
		playerHand.value = currentBetSetting;
		$("#betAmount").html(currentBetSetting);
	}
	function peaceOut()
	{
		var userResponse = confirm("This makes an edit (which will show up in your contribs) to record your closing balance.\n\nIs that okay?");
		if (userResponse)
		{
			mw.loader.using("mediawiki.api.edit").done(function ()
			{
				var api = new mw.Api();
				if (previousBalanceExists)
				{
					api.edit("User:" + mw.config.get("wgUserName") + "/blackjackBalance.js", function ()
					{
						return {minor:true,text:currentBalance,summary:"updating [[User:Writ_Keeper/blackjack|blackjack]] balance"};
					}
					).done(function ()
					{
						location.reload(true);
					}
					);
				}
				else
				{
					api.create("User:" + mw.config.get("wgUserName") + "/blackjackBalance.js",
						{summary: "updating [[User:Writ_Keeper/blackjack|blackjack]] balance"},
						"" + currentBalance).done(function ()
					{
						location.reload(true);
					}
					).fail(function ()
					{
						api.edit("User:" + mw.config.get("wgUserName") + "/blackjackBalance.js", function ()
						{
							return {minor:true,text:currentBalance,summary:"updating [[User:Writ_Keeper/blackjack|blackjack]] balance"};
						}
						).done(function ()
						{
							location.reload(true);
						}
						);
					}
					);
				}
			}
			);
		}
	}
	function randomCard()
	{
		var cardIndex = shoe[nextCard];
		nextCard++;
		return {"suit": ((Math.floor(cardIndex/13)%4)+1),"rank":(cardIndex%13 + 1)};
	}
	function getCardBackground(card)
	{
		var cardBackground = {"x":0,"y":0};
		switch (card.rank)
		{
			case 1:
				cardBackground.x = -1;
				break;
			case 2:
				cardBackground.x = -100;
				break;
			case 3:
				cardBackground.x = -199;
				break;
			case 4:
				cardBackground.x = -298;
				break;
			case 5:
				cardBackground.x = -396;
				break;
			case 6:
				cardBackground.x = -495;
				break;
			case 7:
				cardBackground.x = -593;
				break;
			case 8:
				cardBackground.x = -691;
				break;
			case 9:
				cardBackground.x = -790;
				break;
			case 10:
				cardBackground.x = -888;
				break;
			case 11:
				cardBackground.x = -987;
				break;
			case 12:
				cardBackground.x = -1085;
				break;
			case 13:
				cardBackground.x = -1184;
				break;
			default:
				cardBackground.x = -199;
		}
		
		switch (card.suit)
		{
			case 1:
			case 2:
			case 3:
			case 4:
				cardBackground.y = (-2+-143*(card.suit-1));
				break;
			default:
				cardBackground.y = -574;
		}
		return cardBackground;
	}
	function addToCount(card, currentTotal)
	{
		switch(card.rank)
		{
			case 2:
			case 3:
			case 4:
			case 5:
			case 6:
			case 7:
			case 8:
			case 9:
				if(currentTotal.count + card.rank > 21 && currentTotal.softAce == true)
				{
					currentTotal.softAce = false;
					currentTotal.count = currentTotal.count - 10 + card.rank ;
				}
				else currentTotal.count += card.rank ;
				break;
			case 10:
			case 11:
			case 12:
			case 13:
				if(currentTotal.count + 10 > 21 && currentTotal.softAce == true)
				{
					currentTotal.softAce = false;
				}
				else currentTotal.count += 10;
				break;
			case 1:
				if(currentTotal.count + 11 > 21)
				{
					currentTotal.count += 1;
				}
				else
				{
					currentTotal.count += 11;
					currentTotal.softAce = true;
				}
		}
		
	}
	function addCard(card, cardContainer, isFaceDown)
	{
		var newCard = '<div class="cardContainer cardFaceDown newCard"><div class="cardFace"> </div></div>';
		var lastCard = $(cardContainer).children(".cardContainer").last();
		$(cardContainer).append(newCard);
		$("#verticalSpacer").hide();
		newCardDom = $(".newCard");
		newCardDom.removeClass("newCard");
		if(lastCard.length != 0)
		{
			lastCard.removeClass("cardFaceUp");
			lastCard.addClass("cardCovered");
		}
		if(!isFaceDown)
		{
			newCardDom.removeClass("cardFaceDown");
			var coords = getCardBackground(card);
			newCardDom.find(".cardFace").css("background-position", ""+ coords.x + "px " + coords.y + "px");
			newCardDom.addClass("cardFaceUp");
		}
	}
	function dealCard(hand, isFaceDown)
	{
		var nextCard = randomCard();
		addToCount(nextCard, hand);
		addCard(nextCard, hand.container, isFaceDown);
		if(isFaceDown)
		{
			faceDownCard = nextCard;
		}
		return nextCard;
	}
	function flipCard()
	{
		var lastCard = $(".cardFaceDown");
		lastCard.removeClass("cardFaceDown");
		var coords = getCardBackground(faceDownCard);
		lastCard.find(".cardFace").css("background-position", ""+ coords.x + "px " + coords.y + "px");
		lastCard.addClass("cardFaceUp");
	}
	function endHand(isBlackjack)
	{
		isSplit = false;
		$("#splitGroup").removeClass("selectedSplit");
		$("#playerGroup").removeClass("selectedSplit");
		var resultValue = (splitHand.value + playerHand.value)
		currentBalance += resultValue;
		var resultString = "";
		if(isBlackjack)
		{
			resultString = "BLACKJACK! ";
		}
		if(resultValue > 0)
		{
			resultString += "You won $" + resultValue + "! ";
		}
		else if(resultValue == 0)
		{
			resultString += "You pushed. ";
		}
		else
		{
			resultString += "Sorry, you lost $" + Math.abs(resultValue) + ". ";
		}
		playerHand.count = 0;
		playerHand.softAce = false;
		dealerHand.count = 0;
		dealerHand.softAce = false;
		splitHand.count = 0
		splitHand.softAce = false;
		playerHand.value = currentBetSetting;
		splitHand.value = 0;
		faceDownCard = null;
		
		if(currentBalance >= 1)
		{
			resultString += "Play again?";
			$("#gameStatus").html(resultString);
			$("#bankAmount").html(currentBalance.toFixed(2));
			if(currentBalance < currentBetSetting)
			{
				currentBetSetting = Math.floor(currentBalance);
				playerHand.value = currentBetSetting;
				$("#betAmount").html(currentBetSetting);
			}
			changeButton($("#gameOption1"), cleanAndDeal, "Deal");
			changeButton($("#gameOption2"), changeBet, "Change bet");
			changeButton($("#gameOption3"), peaceOut, "I'm out");
			$("#gameOption4").hide();
		}
		else
		{
			resultString += "Sorry, you're broke.";
			$("#gameStatus").html(resultString);
			$("#bankAmount").html(currentBalance.toFixed(2));
			currentBetSetting = 0;
			playerHand.value = currentBetSetting;
			splitHand.value = 0;
			$("#betAmount").html("0");
			changeButton($("#gameOption1"), peaceOut, "Start over");
			$("#gameOption2").hide();
			$("#gameOption3").hide();
			$("#gameOption4").hide();
		}
	}
	function playDealer()
	{
		flipCard();
		while(dealerHand.count < 17)
		{
			dealCard(dealerHand, false);
		}
		resolveHand(splitHand);
		resolveHand(playerHand);
		endHand(false);
	}
	function resolveHand(hand)
	{
		if(dealerHand.count <= 21 && hand.count > 0 && dealerHand.count > hand.count)
		{
			hand.value = -(hand.value);
		}
		else if(dealerHand.count == hand.count)
		{
			hand.value = 0;
		}
		return hand.value;
	}
	function cleanAndDeal()
	{
		$("#cardTableDealer").html("");
		$("#playerGroup").html("");
		$("#splitGroup").html("");
		$("#splitGroup").hide();
		dealHand();
	}
	function dealHand()
	{
		if(nextCard < 0 || nextCard > 26*numDecks)
		{
			shuffleDeck();
		}
		var firstCard = dealCard(playerHand, false);
		dealCard(dealerHand, false);
		var secondCard = dealCard(playerHand, false);
		dealCard(dealerHand, true);
		if(playerHand.count == 21)
		{
			flipCard();
			if(dealerHand.count == 21)
			{
				playerHand.value = 0;
				endHand(true);
				return;
			}
			playerHand.value = currentBetSetting*3/2;
			endHand(true);
			return;
		}
		if(dealerHand.count == 21)
		{
			flipCard();
			playerHand.value = -(currentBetSetting);
			endHand(false);
			return;
		}
		$("#gameStatus").html("You have " + playerHand.count + " points. What will you do?");
		changeActionButton($("#gameOption1"), hitMe, playerHand, "Hit");
		changeActionButton($("#gameOption2"), standDown, playerHand, "Stand");
		if(currentBalance >= 2*currentBetSetting)
		{
			changeActionButton($("#gameOption3"), doubleDown, playerHand, "Double");
		}
		else
		{
			$("#gameOption3").hide();
		}
		if(firstCard.rank == secondCard.rank && currentBalance >= 2*currentBetSetting)
		{
			changeButton($("#gameOption4"), splitIt, "Split");
		}
		else
		{
			$("#gameOption4").hide();
		}
	}
	function hitMe(e)
	{
		var currentHand;
		if(e.data != null && e.data.hand != null)
		{
			currentHand = e.data.hand;
		}
		else
		{
			currentHand = playerHand;
		}
		dealCard(currentHand, false);
		if(currentHand.count > 21)
		{
			currentHand.value = -(currentBetSetting);
			currentHand.count = 0;
			standDown();
			return;
		}
		else if(currentHand.count == 21)
		{
			standDown();
			return;
		}
		else
		{
			$("#gameOption3").hide();
			$("#gameOption4").hide();
			$("#gameStatus").html("You have " + currentHand.count + " points. What will you do?");
		}
	}
	function standDown()
	{
		if(playerHand.count == 0 && splitHand.count == 0)
		{
			endHand(false);
			return;
		}
		else if(isSplit)
		{
			isSplit = false;
			$("#splitGroup").removeClass("selectedSplit");
			$("#playerGroup").addClass("selectedSplit");
			changeActionButton($("#gameOption1"), hitMe, playerHand, "Hit");
			changeActionButton($("#gameOption2"), standDown, playerHand, "Stand");
			if(currentBalance >= (2*currentBetSetting + Math.abs(splitBetValue)))
			{
				changeActionButton($("#gameOption3"), doubleDown, playerHand, "Double");
			}
			else
			{
				$("#gameOption3").hide();
			}
			$("#gameStatus").html("Your second hand has " + playerHand.count + " points. What will you do?");
		}
		else
		{
			playDealer();
		}
	}
	function doubleDown(e)
	{
		var currentHand;
		if(e.data != null && e.data.hand != null)
		{
			currentHand = e.data.hand;
		}
		else
		{
			currentHand = playerHand;
		}
		currentHand.value = currentBetSetting * 2;
		dealCard(currentHand, false);
		if(isSplit && playerHand.count < 21)
		{
			if(currentHand.count > 21)
			{
				currentHand.value = -(currentHand.value);
				currentHand.count = 0;
			}
			standDown();
			return;
		}
		else if(currentHand.count > 21)
		{
			currentHand.count = 0;
			currentHand.value = -(currentHand.value);
			endHand();
			return;
		}
		else
		{
			playDealer();
		}
	}
	function splitIt()
	{
		isSplit = true;
		splitHand.value = currentBetSetting;
		$("#gameOption4").hide();
		var firstCard = $("#playerGroup").find(".cardContainer")[0];
		$(firstCard).appendTo($("#splitGroup"));
		$("#splitGroup").show();
		if(playerHand.softAce)
		{
			splitHand.count = 11;
			splitHand.softAce = true;
			playerHand.count = 11;
			dealCard(splitHand, false);
			dealCard(playerHand, false);
			playDealer();
			return;
		}
		else
		{
			splitHand.count = playerHand.count / 2;
			splitHand.softAce = false;
			playerHand.count = splitHand.count;
			dealCard(splitHand, false);
			dealCard(playerHand, false);
			$("#splitGroup").addClass("selectedSplit");
		}
		changeActionButton($("#gameOption1"), hitMe, splitHand, "Hit");
		changeActionButton($("#gameOption2"), standDown, splitHand, "Stand");
		if(currentBalance >= (3*currentBetSetting))
		{
			changeActionButton($("#gameOption3"), doubleDown, splitHand, "Double");
		}
		else
		{
			$("#gameOption3").hide();
		}
		$("#gameStatus").html("You split! Your first hand has " + splitHand.count + " points; what will you do?");
	}
	function runTheNumbers()
	{
		$("#bankAmount").html(currentBalance.toFixed(2));
		$("#betAmount").html(currentBetSetting);
		$("#gameStatus").html("Ready to deal; you in?");
		changeButton($("#gameOption1"), dealHand, "Deal");
		changeButton($("#gameOption2"), changeBet, "Change bet");
		changeButton($("#gameOption3"), peaceOut, "I'm out");
		$("#gameOption4").hide();
	}
	function loadSave()
	{
		var loadRequestData =
		{
			"action": "raw",
			"title": "User:" + mw.config.get("wgUserName") + "/blackjackBalance.js"
		};
		$.post("/w/index.php", loadRequestData, function (response)
		{
			var retrievedValue = parseFloat(response);
			if (!(isNaN(retrievedValue)) && retrievedValue >= 1)
			{
				currentBalance = retrievedValue;
				if (Math.floor(currentBalance / 10) < 10)
				{
					currentBetSetting = Math.floor(currentBalance / 10);
					if (currentBetSetting < 1)
					{
						currentBetSetting = 1;
					}
				}
				else
				{
					currentBetSetting = 10;
				}
			}
			else
			{
				alert("Sorry, you're broke! No worries, I'll get you started with $500.");
				currentBalance = 500;
				currentBetSetting = 10;
			}
			runTheNumbers();
		}
		).fail(function() 
		{
			alert("Ah, you're new here? I'll get you started with $500.");
			currentBalance = 500;
			currentBetSetting = 10;
			previousBalanceExists = false;
			runTheNumbers();
		});
	}
	function prepareBlackjack()
	{
		$("#gameStatus").html("Welcome to Blackjack!");
		changeButton($("#gameOption1"), loadSave, "Get started");
		$("#splitGroup").hide();
		$("#gameOption2").hide();
		$("#gameOption3").hide();
		$("#gameOption4").hide();
	}
	$(document).ready(prepareBlackjack);
}