<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8" /> <title>Kevin's Blackjack</title> <style>
body{ background-color:darkgreen; } #blackjack{ position:relative; width:600px; height:600px; background-color:forestgreen; border:1px solid black; text-align:left; font-family:Arial, Helvetica, sans-serif; font-weight:bold; font-size:1.4em; overflow:hidden; user-select:none; } #bl_title{ position:absolute; width:100%; background-color:dimgrey; border:1px solid black; padding:5px 0 5px 10px; text-shadow:0 0 3px black,0 0 3px black; color:dimgrey; font-style:italic; font-size:larger; } #bl_helpButton{ position:absolute; width:40px; height:30px; top:5px; right:5px; background-color:dodgerblue; color:white; padding-top:5px; text-align:center; border-radius:999px; cursor:pointer; } .bl_card{ display:inline-block; width:80px; height:123px; background:url("cards/gray_back.png"); background-size:contain; background-repeat:no-repeat; border-radius:6px; } #bl_shoeBox{ position:absolute; width:150px; height:150px; right:0; top:15%; transform:rotate(-90deg); } #bl_shoe{ position:absolute; width:80px; height:123px; background:url("cards/gray_back.png") silver; background-size:contain; background-repeat:no-repeat; border:1px solid silver; border-radius:6px; } #bl_chipBox{ position:absolute; width:20px; height:20px; right:15px; bottom:330px; } .bl_chip{ position:absolute; width:40px; height:40px; background-image:radial-gradient(white 39%,indianred 40%); border:5px dashed white; border-radius:999px; box-shadow:0 0 7px black; } #bl_chipLabel{ position:absolute; width:190px; height:40px; right:0; bottom:0; color:white; text-shadow:0 0 3px black,0 0 3px black; font-size:larger; text-align:center; } .bl_score{ position:absolute; left:20px; width:160px; text-align:center; font-size:larger; color:rgba(255,255,255,0.5); } #bl_dealerScore{ top:calc(50px + 123px + 10px); } #bl_playerScore{ bottom:calc(180px + 123px + 10px); } .bl_zone{ position:absolute; left:20px; width:999px;/* was 405 */ height:123px; } .bl_zoneSquish > *{ margin-right:-40px; box-shadow:-3px 0 7px black; } #bl_dealerZone{ top:50px; } #bl_playerZone{ bottom:180px; } .bl_button{ position:absolute; width:160px; bottom:120px; padding:5px; background-color:lightskyblue; border:5px double black; border-radius:999px; box-shadow: 0px 0px 10px black; cursor:pointer; text-align:center; } .bl_button:hover{ border-color:white; } #bl_standButton{ left:20px; } #bl_hitButton{ left:225px; } #bl_startButton{ width:200px; left:100px; bottom:40px; padding:15px; } .bl_cardToDealer{ position:absolute; animation: bl_cardToDealer 0.75s ease-in 0s 1 normal;/*match CARD_DELAY*/ animation-fill-mode:forwards; }@keyframes bl_cardToDealer{ from{transform:rotate(-90deg);right:60px;top:90px;} to{transform:rotate(0deg);right:450px;top:50px;} } .bl_cardToPlayer{ position:absolute; animation: bl_cardToPlayer 0.75s ease-in 0s 1 normal;/*match CARD_DELAY*/ animation-fill-mode:forwards; }@keyframes bl_cardToPlayer{ from{transform:rotate(-90deg);right:60px;top:90px;} to{transform:rotate(0deg);right:450px;top:295px;} } .bl_chipToPlayer{ position:absolute; animation: bl_chipToPlayer 1.5s ease-in-out 0s 1 normal;/*match CHIP_DELAY*/ animation-fill-mode:forwards; }@keyframes bl_chipToPlayer{ 0%{right:500px;top:-50px;} 60%{right:400px;} 100%{right:70px;top:400px;} } .bl_chipToDealer{ position:absolute; animation: bl_chipToDealer 1.5s ease-in-out 0s 1 normal;/*match CHIP_DELAY*/ animation-fill-mode:forwards; }@keyframes bl_chipToDealer{ 0%{right:70px;top:400px;} 40%{right:400px;} 100%{right:500px;top:-50px;} } .bl_cardHidden{ background-image:url("cards/gray_back.png") !important; } .bl_cardFlip{ animation:bl_cardFlip 0.75s ease-in-out 0s 1 normal;/*match CARD_DELAY*/ animation-fill-mode:forwards; }@keyframes bl_cardFlip{ 0%{transform:scaleX(100%);background-image:url("cards/gray_back.png");} 50%{transform:scaleX(0%);} 100%{transform:scaleX(100%);} } #bl_message{ position:absolute; width:280px; height:100px; left:160px; top:200px; text-align:center; font-style:italic; font-size:1.2em; color:rgba(0,0,0,0.5); } #bl_message > *{ text-shadow:0 0 3px black,0 0 3px black; animation:bl_textEffect 0.5s ease-in-out 0s infinite alternate; }@keyframes bl_textEffect{ from{font-size:1em;} to{font-size:1.2em;} } .bl_textBad{ color:indianred; } .bl_textGood{ color:lime; } .bl_textNatural{ color:yellow; }
</style> <script>
"use strict"; function $(id){ return document.getElementById(id); } /* =================== Initialization ==================== */ const CARD_BACK="gray_back"; const CARD_DELAY=1000*0.75; const CHIP_DELAY=1000*1.5; let blackjack; let chips=[]; let deck=[]; let shoe=[]; let shoeHeightStep=0; window.onload=function(){ Startup(); EnterFocus(); } function Startup(){ blackjack=$("blackjack"); let html=""; html+="<div id='bl_chipBox'></div>"; html+="<div id='bl_chipLabel'></div>"; html+="<div id='bl_dealerScore' class='bl_score'></div>"; html+="<div id='bl_playerScore' class='bl_score'></div>"; html+="<div id='bl_dealerZone' class='bl_zone'></div>"; html+="<div id='bl_playerZone' class='bl_zone'></div>"; html+="<div id='bl_shoeBox'> <div id='bl_shoe'></div> </div>"; html+="<div id='bl_standButton' class='bl_button'>Stand</div>"; html+="<div id='bl_hitButton' class='bl_button'>Hit</div>"; html+="<div id='bl_startButton' class='bl_button'></div>"; html+="<div id='bl_message'></div>"; html+="<div id='bl_title'>Kevin's Blackjack</div>"; html+="<div id='bl_helpButton'>?</div>"; /* hidden input keyboard detection */ html +="<input id='bl_hiddenInput' type='text' style='width:0;height:0;opacity:0;'>"; blackjack.innerHTML=html; /* hidden input keyboard detection */ blackjack.onclick=EnterFocus; $("bl_hiddenInput").setAttribute("onkeypress","OnKeyPress(event)"); show($("bl_shoe"),false); show($("bl_standButton"),false); $("bl_standButton").onclick=StandButton; show($("bl_hitButton"),false); $("bl_hitButton").onclick=HitButton; show($("bl_startButton"),true); $("bl_startButton").innerText= "New game"; $("bl_startButton").onclick=StartButton; $("bl_helpButton").onclick=function(){ let message="===========================\n"+ "How to play\n\n"+ "Try to hold a more valuable hand than your opponent, but don't exceed a value of 21.\n\n"+ "Choose 'Hit' (or press the H key) to draw a card, or choose 'Stay' (or press the S key) to finish your turn.\n\n"+ "===========================\n"+ "Credits\n\n"+ "Images of cards found at ACBC (American Contract Bridge Club), https://www.acbl.org/\n\n"+ "===========================" ; alert(message); }; StartupChips(); CreateDeck(); } function StartupChips(){ let jitter=0; let rotate=0; let html=""; let style=""; let x,y; for(x=0;x<3;x++){ for(y=0;y<33;y++){ jitter=Math.floor(Math.random()*4)-2; rotate=Math.floor(Math.random()*360); style="right:"+((x*55)+jitter)+"px;top:"+(y*8)+"px;"; style+="transform:rotate("+rotate+"deg);"; style+="visibility:hidden;"; html+="<div class='bl_chip' style='"+style+"'></div>"; } } $("bl_chipBox").innerHTML=html; chips=document.getElementsByClassName("bl_chip"); } function CreateDeck(){ let suits=["C","D","H","S"]; let pips=["A","2","3","4","5","6","7","8","9","10","J","Q","K"]; for(let s=0;s<suits.length;s++){ for(let p=0;p<pips.length;p++){ deck.push(pips[p]+suits[s]); } } } /* =================== Keyboard detection ==================== */ function EnterFocus(){ $("bl_hiddenInput").focus(); } function OnKeyPress(event){ if(event.key=="s"){ if($("bl_standButton").style.visibility=="visible"){ StandButton(); }else if($("bl_startButton").style.visibility=="visible"){ StartButton(); } }else if(event.key=="h"){ if($("bl_hitButton").style.visibility=="visible"){ HitButton(); }else if($("bl_startButton").style.visibility=="visible"){ StartButton(); } } } /* =================== Utilities ==================== */ function show(element,visible){ element.style.visibility=(visible)?"visible":"hidden"; } function RemoveClass(element,c){ let s=element.className; let index=s.indexOf(c); while(index!=-1){ s = s.slice(0,index) + s.slice(index+c.length); index=s.indexOf(c); } element.className=s; } function RemoveFromString(s,phrase){ let index=s.indexOf(phrase); if(index==-1)return s; return s.slice(0,index) + s.slice(index+phrase.length); } function ResetShoe(){ shoe=[]; let tempShoe=[].concat(deck,deck,deck,deck);/* four decks of cards */ shoeHeightStep=100/tempShoe.length; let randomIndex; while(tempShoe.length){ randomIndex=Math.floor(Math.random()*tempShoe.length); shoe.push(tempShoe[randomIndex]); tempShoe.splice(randomIndex,1); } $("bl_shoe").style.borderLeftWidth=(shoe.length*shoeHeightStep)+"px"; } function ResetChips(){ playerChips=15;/* starting chips */ $("bl_chipLabel").innerText=playerChips+" chips"; let x=0; for(x=0;x<playerChips;x++){ show(chips[x],true); } for(x=playerChips;x<chips.length;x++){ show(chips[x],false); } } function CardToPlayer(){ let insignia=DrawCardName(); let animCard=NewCardElement(blackjack,"gray_back"); animCard.className+=" bl_cardToPlayer"; if(playerCards.length==5)$("bl_playerZone").className+=" bl_zoneSquish"; setTimeout(function(){ blackjack.removeChild(animCard); let element=NewCardElement($("bl_playerZone"),insignia); playerCards[playerCards.length]={ element:element, insignia:insignia }; $("bl_playerScore").innerText=GetHandValue(playerCards); },CARD_DELAY); } function CardToDealer(){ let insignia=DrawCardName(); let animCard=NewCardElement(blackjack,"gray_back"); animCard.className+=" bl_cardToDealer"; if(dealerCards.length==5)$("bl_dealerZone").className+=" bl_zoneSquish"; setTimeout(function(){ blackjack.removeChild(animCard); let element=NewCardElement($("bl_dealerZone"),insignia); if(dealerCards.length==1)element.className+=" bl_cardHidden"; dealerCards[dealerCards.length]={ element:element, insignia:insignia }; if(dealerCards.length!=2)$("bl_dealerScore").innerText=GetHandValue(dealerCards); },CARD_DELAY); } function DrawCardName(){ let card=shoe[0]; shoe.shift(); if(shoe.length==0)ResetShoe(); $("bl_shoe").style.borderLeftWidth=(shoe.length*shoeHeightStep)+"px"; return card; } function NewCardElement(parent,name){ let card=document.createElement("div"); parent.appendChild(card); card.className="bl_card"; card.style.backgroundImage= "url('cards/"+name+".png')"; return card; } function GiveChip(){ let chip=document.createElement("div"); blackjack.appendChild(chip); chip.className+=" bl_chip"; chip.className+= " bl_chipToPlayer"; setTimeout(function(){/* give chip @ 98, target 98 */ blackjack.removeChild(chip); if(playerChips<0 || playerChips>98); else show(chips[playerChips],true); playerChips++; $("bl_chipLabel").innerText=playerChips+" chips"; },1500); } function TakeChip(){ let chip=document.createElement("div"); blackjack.appendChild(chip); chip.className+=" bl_chip"; chip.className+= " bl_chipToDealer"; if(playerChips<1 || playerChips>99); else show(chips[playerChips-1],false); playerChips--; $("bl_chipLabel").innerText=playerChips+" chips"; setTimeout(function(){ blackjack.removeChild(chip); },1500); /* if you have 99, just do the animation */ } function FlipDealerCardTwo(){ let card=dealerCards[1].element; RemoveClass(card,"bl_cardHidden"); card.className+=" bl_cardFlip"; setTimeout(function(){ $("bl_dealerScore").innerText=GetHandValue(dealerCards); RemoveClass(card,"bl_cardFlip"); },CARD_DELAY); } function GetCardValue(o){ let pip=o.insignia.slice(0,-1); if(pip=="A")return 11; if(pip=="J" || pip=="Q" || pip=="K")return 10; return parseInt(pip); } function GetHandValue(h){ let i,v; let value=0; let variants=[]; variants[0]=0; for(i=0;i<h.length;i++){ value=GetCardValue(h[i]); if(value==11){ for(v=0;v<variants.length;v++)variants[v]+=1; variants[variants.length]=variants[0]-1 + 11; }else{ for(v=0;v<variants.length;v++)variants[v]+=value; } } let bestValue=-1; for(i=0;i<variants.length;i++){ value=variants[i]; if(value>21)continue; if(value>bestValue)bestValue=value; } if(bestValue==-1)return variants[0]; return bestValue; } function Message(s){ $("bl_message").innerHTML=s; } /* =================== Gameplay ==================== */ let dealerCards=[]; let playerCards=[]; let playerChips=0; function StartButton(){ if(playerChips==0){ /* start a new game */ show($("bl_shoe"),true); $("bl_startButton").innerText="Continue"; ResetChips(); ResetShoe(); } show($("bl_startButton"),false); NewRound(); } function NewRound(){ Message(""); dealerCards=[]; $("bl_dealerScore").innerText=""; $("bl_dealerZone").innerHTML=""; RemoveClass($("bl_dealerZone"),"bl_zoneSquish"); playerCards=[]; $("bl_playerScore").innerText=""; $("bl_playerZone").innerHTML=""; RemoveClass($("bl_playerZone"),"bl_zoneSquish"); CardToPlayer();setTimeout(function(){ CardToDealer();setTimeout(function(){ CardToPlayer();setTimeout(function(){ CardToDealer();setTimeout(function(){ PlayerTurnCheck(); },CARD_DELAY); },CARD_DELAY); },CARD_DELAY); },CARD_DELAY); } function PlayerTurnCheck(){ let playerHandValue=parseInt($("bl_playerScore").innerText); if(playerHandValue>21){/* bust */ Message("<span class='bl_textBad'>Bust</span>"); DelayToDealerTurn(); }else if(playerHandValue==21){ if(playerCards.length==2){/* natural */ Message("<span class='bl_textNatural'>Natural</span>"); }else{/* 21 */ Message("<span class='bl_textGood'>You got 21</span>"); } DelayToDealerTurn(); }else{ Message("What will you do?"); show($("bl_standButton"),true); show($("bl_hitButton"),true); } } function DelayToDealerTurn(){ setTimeout(function(){ Message(""); DealerTurnCheck(); },1000*3); } function StandButton(){ Message(""); show($("bl_standButton"),false); show($("bl_hitButton"),false); DealerTurnCheck(); } function HitButton(){ Message(""); show($("bl_standButton"),false); show($("bl_hitButton"),false); CardToPlayer();setTimeout(PlayerTurnCheck,CARD_DELAY); } function DealerTurnCheck(){ if(dealerCards[1].element.className.indexOf("bl_cardHidden")!=-1){ FlipDealerCardTwo();setTimeout(DealerTurnCheck,CARD_DELAY+1000*0.5); return; } let dealerHandValue=parseInt($("bl_dealerScore").innerText); let playerHandValue=parseInt($("bl_playerScore").innerText); if(playerHandValue>21){ Lose(); }else if(playerHandValue==21 && playerCards.length==2){ if(dealerHandValue==21 && dealerCards.length==2){ Standoff(); }else Win(); }else if(dealerHandValue<17){ CardToDealer();setTimeout(DealerTurnCheck,CARD_DELAY+1000*0.5); }else if(dealerHandValue>21){ Win(); }else{ if(dealerHandValue>playerHandValue){ Lose(); }else if(dealerHandValue<playerHandValue){ Win(); }else Standoff(); } } function Lose(){ Message("<span class='bl_textBad'>You lose</span>"); setTimeout(function(){/* delay */ TakeChip();setTimeout(function(){ if(playerChips==0){ Message("<span class='bl_textBad'>Game over</span>"); $("bl_startButton").innerText= "New game"; } show($("bl_startButton"),true); },CHIP_DELAY); },1000*1); } function Win(){ Message("<span class='bl_textGood'>You WIN</span>"); setTimeout(function(){/* delay */ GiveChip(); let playerHandValue=parseInt($("bl_playerScore").innerText); if(playerHandValue==21 && playerCards.length==2){ setTimeout(GiveChip,1000*0.5);/* natural pays extra chip */ } setTimeout(function(){ show($("bl_startButton"),true); },CHIP_DELAY+1000*0.5); },1000*1); } function Standoff(){ Message("Standoff..."); show($("bl_startButton"),true); }
</script> </head> <body> <center> <div id="blackjack"></div> </center> </body> </html>
0000