/*
Casino Sabacc 1.0.3 - JavaScript Sabacc Game
Copyright (C) 2010  Aaron Mavrinac <mavrinac@gmail.com>

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

The GNU General Public License is available at the following URL:
http://www.gnu.org/licenses/gpl.txt
*/

/*
SABACC.JS
This is the main program component, written in JavaScript and
front-ended by INDEX.HTML.
*/

// Initialize the game variables.
var i = 0;
var j = 0;
var k = 0;
var cash = 100;
var bet = 0;
var pot = 0;
var sabpot = 0;
var total = 0;
var dealer = 0;
var flag = 0;
var done = 1;
var tbust = 0;
var dbust = 0;
var handCalled = 0;
var dealerCalled = 0;
var seed = 0.5;
var output = "";
var playername = "";
var checksum = 0;

// Initialize the textboxes.
document.forms[0].txtDesc.value = output;
document.forms[0].txtCash.value = cash;
document.forms[0].txtBet.value = bet;
document.forms[0].txtPot.value = pot;
document.forms[0].txtSabPot.value = sabpot;

// Image parameters, to be used by imgUpdate() to find the card images. Add new card
// sets as elements in the imagPrefix, imagSuffix and imagHeight arrays. The first
// element (0) is the default.
var imagSet = 0;
var imagCardBack = "cardbak";
imagPrefix = new Array;
imagSuffix = new Array;
imagHeight = new Array;
imagPrefix[0] = "images/default/";
imagSuffix[0] = ".png";
imagHeight[0] = 200;
imagPrefix[1] = "images/swag/";
imagSuffix[1] = ".png";
imagHeight[1] = 250;

// Set the heights of the cards. Dealer's cards are at a 1:2 pixel ratio.
// PLAYER
document.card0.height =
document.card1.height =
document.card2.height =
document.card3.height = imagHeight[imagSet];
// DEALER
document.card4.height =
document.card5.height =
document.card6.height =
document.card7.height = (imagHeight[imagSet]/2 - imagHeight[imagSet]%2);

// IMPORTANT: After setting imagCardBack, you must update the static HTML code below so that
// the initial images are correct; they should be imagPrefix + imagCardBack + imagSuffix.

// Customizable alert messages.
var msg_hand_not_done = "You have not finished this hand.";
var msg_wait_4_rounds = "You may not call until the fourth round.";
var msg_no_more_hold = "You can only place up to two cards in the neutral field without calling.";
var msg_not_enough_cash = "You don't have enough credits.";
var msg_cardset_changed = "The card set has been changed.";

var card = new Array(77);
var imag = new Array(77);
var valu = new Array(77);
var hand = new Array(8);
var hold = new Array(8);
var idiot = new Array(8);

// Set the inital hand to hold card backs.
for (i=0; i < 8; i++)
	hand[i] = hold[i] = 0;

// Extra card back slot in case of imgUpdate() before initGame().
card[0] = "Card Back";				imag[0] = "cardbak";	valu[0] = 0;

// Define the name, image and value for each of the 76 cards.

card[1] = "the One of Coins";			imag[1] = "coins1";	valu[1] = 1;
card[2] = "the Two of Coins";			imag[2] = "coins2";	valu[2] = 2;
card[3] = "the Three of Coins";			imag[3] = "coins3";	valu[3] = 3;
card[4] = "the Four of Coins";			imag[4] = "coins4";	valu[4] = 4;
card[5] = "the Five of Coins";			imag[5] = "coins5";	valu[5] = 5;
card[6] = "the Six of Coins";			imag[6] = "coins6";	valu[6] = 6;
card[7] = "the Seven of Coins";			imag[7] = "coins7";	valu[7] = 7;
card[8] = "the Eight of Coins";			imag[8] = "coins8";	valu[8] = 8;
card[9] = "the Nine of Coins";			imag[9] = "coins9";	valu[9] = 9;
card[10] = "the Ten of Coins";			imag[10] = "coins10";	valu[10] = 10;
card[11] = "the Eleven of Coins";		imag[11] = "coins11";	valu[11] = 11;
card[12] = "the Commander of Coins";		imag[12] = "coins12";	valu[12] = 12;
card[13] = "the Mistress of Coins";		imag[13] = "coins13";	valu[13] = 13;
card[14] = "the Master of Coins";		imag[14] = "coins14";	valu[14] = 14;
card[15] = "the Ace of Coins";			imag[15] = "coins15";	valu[15] = 15;

card[16] = "the One of Flasks";			imag[16] = "flasks1";	valu[16] = 1;
card[17] = "the Two of Flasks";			imag[17] = "flasks2";	valu[17] = 2;
card[18] = "the Three of Flasks";		imag[18] = "flasks3";	valu[18] = 3;
card[19] = "the Four of Flasks";		imag[19] = "flasks4";	valu[19] = 4;
card[20] = "the Five of Flasks";		imag[20] = "flasks5";	valu[20] = 5;
card[21] = "the Six of Flasks";			imag[21] = "flasks6";	valu[21] = 6;
card[22] = "the Seven of Flasks";		imag[22] = "flasks7";	valu[22] = 7;
card[23] = "the Eight of Flasks";		imag[23] = "flasks8";	valu[23] = 8;
card[24] = "the Nine of Flasks";		imag[24] = "flasks9";	valu[24] = 9;
card[25] = "the Ten of Flasks";			imag[25] = "flasks10";	valu[25] = 10;
card[26] = "the Eleven of Flasks";		imag[26] = "flasks11";	valu[26] = 11;
card[27] = "the Commander of Flasks";		imag[27] = "flasks12";	valu[27] = 12;
card[28] = "the Mistress of Flasks";		imag[28] = "flasks13";	valu[28] = 13;
card[29] = "the Master of Flasks";		imag[29] = "flasks14";	valu[29] = 14;
card[30] = "the Ace of Flasks";			imag[30] = "flasks15";	valu[30] = 15;

card[31] = "the One of Sabres";			imag[31] = "sabres1";	valu[31] = 1;
card[32] = "the Two of Sabres";			imag[32] = "sabres2";	valu[32] = 2;
card[33] = "the Three of Sabres";		imag[33] = "sabres3";	valu[33] = 3;
card[34] = "the Four of Sabres";		imag[34] = "sabres4";	valu[34] = 4;
card[35] = "the Five of Sabres";		imag[35] = "sabres5";	valu[35] = 5;
card[36] = "the Six of Sabres";			imag[36] = "sabres6";	valu[36] = 6;
card[37] = "the Seven of Sabres";		imag[37] = "sabres7";	valu[37] = 7;
card[38] = "the Eight of Sabres";		imag[38] = "sabres8";	valu[38] = 8;
card[39] = "the Nine of Sabres";		imag[39] = "sabres9";	valu[39] = 9;
card[40] = "the Ten of Sabres";			imag[40] = "sabres10";	valu[40] = 10;
card[41] = "the Eleven of Sabres";		imag[41] = "sabres11";	valu[41] = 11;
card[42] = "the Commander of Sabres";		imag[42] = "sabres12";	valu[42] = 12;
card[43] = "the Mistress of Sabres";		imag[43] = "sabres13";	valu[43] = 13;
card[44] = "the Master of Sabres";		imag[44] = "sabres14";	valu[44] = 14;
card[45] = "the Ace of Sabres";			imag[45] = "sabres15";	valu[45] = 15;

card[46] = "the One of Staves";			imag[46] = "staves1";	valu[46] = 1;
card[47] = "the Two of Staves";			imag[47] = "staves2";	valu[47] = 2;
card[48] = "the Three of Staves";		imag[48] = "staves3";	valu[48] = 3;
card[49] = "the Four of Staves";		imag[49] = "staves4";	valu[49] = 4;
card[50] = "the Five of Staves";		imag[50] = "staves5";	valu[50] = 5;
card[51] = "the Six of Staves";			imag[51] = "staves6";	valu[51] = 6;
card[52] = "the Seven of Staves";		imag[52] = "staves7";	valu[52] = 7;
card[53] = "the Eight of Staves";		imag[53] = "staves8";	valu[53] = 8;
card[54] = "the Nine of Staves";		imag[54] = "staves9";	valu[54] = 9;
card[55] = "the Ten of Staves";			imag[55] = "staves10";	valu[55] = 10;
card[56] = "the Eleven of Staves";		imag[56] = "staves11";	valu[56] = 11;
card[57] = "the Commander of Staves";		imag[57] = "staves12";	valu[57] = 12;
card[58] = "the Mistress of Staves";		imag[58] = "staves13";	valu[58] = 13;
card[59] = "the Master of Staves";		imag[59] = "staves14";	valu[59] = 14;
card[60] = "the Ace of Staves";			imag[60] = "staves15";	valu[60] = 15;

// There are two of each special card.

card[61] = card[62] = "Balance";			imag[61] = imag[62] = "balance";	valu[61] = valu[62] = -11;
card[63] = card[64] = "Demise";				imag[63] = imag[64] = "demise";		valu[63] = valu[64] = -13;
card[65] = card[66] = "Endurance";			imag[65] = imag[66] = "enduranc";	valu[65] = valu[66] = -8;
card[67] = card[68] = "the Evil One";			imag[67] = imag[68] = "evilone";	valu[67] = valu[68] = -15;
card[69] = card[70] = "the Idiot";			imag[69] = imag[70] = "idiot";		valu[69] = valu[70] = 0;
card[71] = card[72] = "Moderation";			imag[71] = imag[72] = "moderat";	valu[71] = valu[72] = -14;
card[73] = card[74] = "the Queen of Air and Darkness";	imag[73] = imag[74] = "queen";		valu[73] = valu[74] = -2;
card[75] = card[76] = "the Star";			imag[75] = imag[76] = "star";		valu[75] = valu[76] = -17;

// randNum
// Returns a random value from 0 to (j - 1).

function randNum(j)
{
	j *= 1.0;
	return Math.floor(Math.random() * j);
}

// dealHand
// Deals the initial hand and performs shifting afterward. It randomizes whether shifting
// occurs, changes the appropriate cards with imgUpdate() and calls descText(). This is
// the first part of the transition to the next round (including round 1 from New Hand).

function dealHand()
{
if (!done)
{
	do
	{
		flag = 0;
		for (i=0; i < 8; i++)
			if (!hold[i] && (!k || randNum(2)))
				hand[i] = (randNum(76) + 1);
		for (i=0; i < 8; i++)
			for (j=0; j < 8; j++)
				if (i != j)
					if (hand[i] == hand[j])
						flag = 1;
	}
	while (flag == 1);

	sabpot ++;

	imgUpdate(0);
	descText();
}
}

// neutralField
// Holds an unheld card, or unholds a held card. Checks to ensure a maximum of 2 held
// cards at a time.

function neutralField(i)
{
if (!done)
{
	if (!hold[i])
	{
		if ((hold[0] + hold[1] + hold[2] + hold[3]) < 2)
			hold[i] = 1;
		else
			alert(msg_no_more_hold);
	}
	else
	{
		hold[i] = 0;
	}

	imgUpdate(1);
}
}

// cashBet
// Adds 'i' credits to the bet container, where it will be placed into the Sabacc pot
// by the descText() function at the next round. Can be passed negative values to lower
// the bet for the current round - will not let bet field go negative.

function cashBet(i)
{
if (!done)
{
	if ((cash >= i && i > 0) || (bet >= (0-i) && i < 0))
	{
		bet += i;
		cash -= i;
		document.forms[0].txtBet.value = bet;
		document.forms[0].txtCash.value = cash;
	}
	else if (i >= 0)
	{
		alert(msg_not_enough_cash);
	}
}
}

// cashBetIncrement
// Adds the number of credits displayed in the bet increment textbox to the bet
// container.

function cashBetIncrement()
{
    if(document.forms[0].txtBetIncrement.value != "" && !isNaN(document.forms[0].txtBetIncrement.value))
    {
        cashBet(parseInt(document.forms[0].txtBetIncrement.value));
    }
}

// cashBetDecrement
// Adds the number of credits displayed in the bet increment textbox to the bet
// container.

function cashBetDecrement()
{
    if(document.forms[0].txtBetIncrement.value != "" && !isNaN(document.forms[0].txtBetIncrement.value))
    {
        cashBet(-parseInt(document.forms[0].txtBetIncrement.value));
    }
}

// callHand
// Calls the hand (initiating the next round) by holding all cards and setting a flag
// to the descText() function that will cause the hands to be evaluated.

function callHand()
{
if (!done)
{
	if (k > 3)
	{
		for (i=0; i < 8; i++)
			hold[i] = 1;
		handCalled = 1;
		dealerCalled = 0;
		imgUpdate(0);
		descText();
	}
	else
	{
		alert(msg_wait_4_rounds);
	}
}
}

// descText
// Controls the output to the text area and evaluates the hands when the hand is called.
// Also home to the dealer AI.

function descText()
{
if (!done)
{
	// Say the round number.
	k++;
	output = "It is round " + k + ". ";

	// Describe the cards in the hand.
	output += "You are holding " + card[hand[0]];
	if (hold[0] && !handCalled) output += " (held)";
	output += ", " + card[hand[1]];
	if (hold[1] && !handCalled) output += " (held)";
	output += ", " + card[hand[2]];
	if (hold[2] && !handCalled) output += " (held)";
	output += " and " + card[hand[3]];
	if (hold[3] && !handCalled) output += " (held)";
	output += ". ";

	// Describe the value of the hand.
	total = valu[hand[0]] + valu[hand[1]] + valu[hand[2]] + valu[hand[3]];
	dealer = valu[hand[4]] + valu[hand[5]] + valu[hand[6]] + valu[hand[7]];

	// First, check for an Idiot's Array.
	for (i=0; i < 8; i++)
		idiot[i] = 0;
	for (i=0; i < 4; i++)
	{
		// Player
		if (valu[hand[i]] == 0)
			idiot[0] = 1;
		else if (valu[hand[i]] == 2)
			idiot[1] = 1;
		else if (valu[hand[i]] == 3)
			idiot[2] = 1;

		// Dealer
		if (valu[hand[i+4]] == 0)
			idiot[4] = 1;
		else if (valu[hand[i+4]] == 2)
			idiot[5] = 1;
		else if (valu[hand[i+4]] == 3)
			idiot[6] = 1;
	}

	if (idiot[0] && idiot[1] && idiot[2])
		idiot[3] = 1;
	if (idiot[4] && idiot[5] && idiot[6])
		idiot[7] = 1;

	if (!idiot[3])
	{
		if (total == 23 || total == -23)
			output += "This hand has a value of 23, a Pure Sabacc! ";
		else if (total == 46)
			output += "This hand has a value of " + total + ", a Sabacc! ";
		else if (total < -23 || total > 23 || total == 0)
			output += "This hand has a value of " + total + ", a bomb-out. ";
		else
			output += "This hand has a value of " + total + ". ";
	}
	else
	{
		output += "This hand is an Idiot's Array! ";
	}

	// Either describe what the dealer is showing, or if finished, describe the dealer's
	// whole hand and win/lose/draw and cash payout.
	if (handCalled)
	{
		if (!idiot[7])
		{
			if (dealer == 23 || dealer == -23)
				output += "The dealer shows a Pure Sabacc! ";
			else if (dealer == 46)
				output += "The dealer shows " + dealer + ", a Sabacc! ";
			else
				output += "The dealer shows " + dealer + ". ";
		}
		else
		{
			output += "The dealer shows an Idiot's Array! ";
		}

		if (dealerCalled)
			output += "The dealer calls the hand, ";
		else
			output += "You call the hand, ";

		// Check for a bust.
		if ((dealer > 23 || dealer < -23 || dealer == 0) && dealer != 46)
		{
			dbust = 1;
			output += "the dealer bombs out, ";
		}
		if ((total > 23 || total < -23 || total == 0) && total != 46)
		{
			tbust = 1;
			output += "you bomb out, ";
		}

		if ((idiot[3] && !idiot[7])
		   || ((total == 23 || total == -23) && dealer != 23 && dealer != -23 && !idiot[7])
		   || (total == 46 && dealer != 23 && dealer != -23 && dealer != 46 && !idiot[7]))
		{
			cash += pot + sabpot;
			output += "and you win " + pot + " credits, plus the Sabacc pot of " + sabpot + " credits! ";
			sabpot = 0;
			if (handCalled && dealerCalled)
			{
				sabpot += (pot/2);
				output += "The dealer pays " + (pot/2) + " credits to the Sabacc pot. ";
			}
		}
		else if ((idiot[7] && !idiot[3])
			|| ((dealer == 23 || dealer == -23) && total != 23 && total != -23 && !idiot[3])
			|| (dealer == 46 && total != 23 && total != -23 && total != 46 && !idiot[3]))
		{
			sabpot = 0;
			output += "the dealer wins the Sabacc pot, ";
			output += "and you lose your bet of " + (pot/2) + " credits. ";
			if (handCalled && !dealerCalled)
			{
				cash -= (pot/2);
				sabpot += (pot/2);
				output += "You pay " + (pot/2) + " credits to the Sabacc pot. ";
			}
		}
		else if (dbust && !tbust)
		{
			cash += pot;
			sabpot += (pot/2);
			output += "and you win " + pot + " credits! "
			output += "The dealer pays " + (pot/2) + " credits to the Sabacc pot. ";
		}
		else if (!dbust && tbust)
		{
			cash -= (pot/2);
			sabpot += (pot/2);
			output += "and you lose your bet of " + (pot/2) + " credits. "
			output += "You pay " + (pot/2) + " credits to the Sabacc pot. ";
		}
		else if (dbust && tbust)
		{
			sabpot += (pot);
			output += "and the hand is a draw. ";
			output += "The hand pot goes to the Sabacc pot. "
		}
		else if ((idiot[3] && idiot[7]) || total == dealer)
		{
			cash += (pot/2);
			output += "and the hand is a draw. ";
		}
		else if (Math.abs(total) > Math.abs(dealer))
		{
			cash += pot;
			output += "and you win " + pot + " credits! ";
			if (handCalled && dealerCalled)
			{
				sabpot += (pot/2);
				output += "The dealer pays " + (pot/2) + " credits to the Sabacc pot. ";
			}
		}
		else
		{
			output += "and you lose your bet of " + (pot/2) + " credits. ";
			if (handCalled && !dealerCalled)
			{
				cash -= (pot/2);
				sabpot += (pot/2);
				output += "You pay " + (pot/2) + " credits to the Sabacc pot. ";
			}
		}
		cash += bet;
		pot = 0;
		bet = 0;
 		document.forms[0].txtCash.value = cash;
		done = 1;
	}
	else
	{
		output += "The dealer is showing " + card[hand[4]] + ". ";
		// Calculate Sabacc pot.
		pot += bet * 2;
		bet = 0;
		output += "You have bet " + (pot/2) + " credits into the hand pot so far. ";
	}

	document.forms[0].txtBet.value = bet;
	document.forms[0].txtPot.value = pot;
	document.forms[0].txtSabPot.value = sabpot;

	// Output to the textarea.
	document.forms[0].txtDesc.value = output;

	// The dealer decides whether to call. He will NF his cards for the next shift, so the player
	// gets one shift after the call. It doesn't need to work the other way around (on player call)
	// because the dealer's cards are mostly random anyway. More AI to come?
	if (k > 3 && ((dealer > 17 && dealer < 24) || dealer == -23 || dealer == 46 || idiot[7]))
	{
		handCalled = dealerCalled = 1;
		for (i=4; i < 8; i++)
			hold[i] = 1;
	}
}
}

// imgUpdate
// Updates the card images with the current values. Only shows the dealer's hand if the
// hand has been called. The 'i' parameter ensures that the dealer's hand is not shown
// prematurely when the card set is changed.

function imgUpdate(i)
{

	// The naming scheme for card images is as follows:
	//
	// Example: src = "images/evilone0.gif"
	//                    |      |   | |
	//		      1      2   3 4
	//
	// 1. imagPrefix[x]:	A path to the images, typically, for cardset 'x'.
	// 2. imag[x]:		Individual image for card 'x'.
	// 3. hold[x]:		Indicates whether card 'x' is NF'd (different images for held cards).
	// 4. imagSuffix[x]:	Image file extension, typically, for cardset 'x'.

	document.card0.src = imagPrefix[imagSet] + imag[hand[0]] + hold[0] + imagSuffix[imagSet];
	document.card1.src = imagPrefix[imagSet] + imag[hand[1]] + hold[1] + imagSuffix[imagSet];
	document.card2.src = imagPrefix[imagSet] + imag[hand[2]] + hold[2] + imagSuffix[imagSet];
	document.card3.src = imagPrefix[imagSet] + imag[hand[3]] + hold[3] + imagSuffix[imagSet];
	document.card4.src = imagPrefix[imagSet] + imag[hand[4]] + "0" + imagSuffix[imagSet];
	document.card0.alt = card[hand[0]] + " (" + valu[hand[0]] + ")";
	document.card1.alt = card[hand[1]] + " (" + valu[hand[1]] + ")";
	document.card2.alt = card[hand[2]] + " (" + valu[hand[2]] + ")";
	document.card3.alt = card[hand[3]] + " (" + valu[hand[3]] + ")";
	document.card4.alt = card[hand[4]] + " (" + valu[hand[4]] + ")";

	if (handCalled && !i)
	{
		document.card5.src = imagPrefix[imagSet] + imag[hand[5]] + "0" + imagSuffix[imagSet];
		document.card6.src = imagPrefix[imagSet] + imag[hand[6]] + "0" + imagSuffix[imagSet];
		document.card7.src = imagPrefix[imagSet] + imag[hand[7]] + "0" + imagSuffix[imagSet];
		document.card5.alt = card[hand[5]] + " (" + valu[hand[5]] + ")";
		document.card6.alt = card[hand[6]] + " (" + valu[hand[6]] + ")";
		document.card7.alt = card[hand[7]] + " (" + valu[hand[7]] + ")";
	}
	else
	{
		document.card5.src = imagPrefix[imagSet] + imagCardBack + "0" + imagSuffix[imagSet];
		document.card5.alt = "";
		document.card6.src = imagPrefix[imagSet] + imagCardBack + "0" + imagSuffix[imagSet];
		document.card6.alt = "";
		document.card7.src = imagPrefix[imagSet] + imagCardBack + "0" + imagSuffix[imagSet];
		document.card7.alt = "";
	}
}

// cardSet
// Changes the card set being used.

function cardSet()
{
	imagSet = document.forms[0].cmbCardSet.value;
	// Set the heights of the cards. Dealer's cards are at a 1:2 pixel ratio.
	// PLAYER
	document.card0.height =
	document.card1.height =
	document.card2.height =
	document.card3.height = imagHeight[imagSet];
	// DEALER
	document.card4.height =
	document.card5.height =
	document.card6.height =
	document.card7.height = (imagHeight[imagSet]/2 - imagHeight[imagSet]%2);
	imgUpdate(1);
}

// initGame
// Initializes a new hand by unsetting all hand-specific variables, placing a 5 credit
// bet, and calling dealHand().

function initGame()
{
if (done)
{
	done = 0;
	for (i=0; i < 8; i++)
		hand[i] = hold[i] = 0;

	// Unset the Idiot's Array array.
	for (i=0; i < 8; i++)
		idiot[i] = 0;

	// Initial required bet.
	bet = pot = 0;
	cashBet(5);

	k = handCalled = dealerCalled = tbust = dbust = done = 0;
	dealHand();
}
else
{
	alert(msg_hand_not_done);
}
}

// cashOut
// Sends a player name and final score to the high score CGI script.

function cashOut()
{
if (done)
{
	playername = prompt("Enter your name:","Anonymous");
	checksum = playername.length * cash;
	location.href = ("sabacc_hs.cgi?player=" + playername + "&credit=" + cash + "&checksum=" + checksum);
}
else
{
	alert(msg_hand_not_done);
}
}
