Skip to content

Instantly share code, notes, and snippets.

@Perlkonig
Created July 23, 2018 20:07
Show Gist options
  • Save Perlkonig/72e41996e4b4938556c556ae3111f85d to your computer and use it in GitHub Desktop.
Save Perlkonig/72e41996e4b4938556c556ae3111f85d to your computer and use it in GitHub Desktop.
Practice scoring duplicate bridge contracts as per ACBL rules
<html lang="en">
<head>
<title>Brdige Scoring Drills</title>
<script type="text/javascript" src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<style type="text/css">
.hidden {
display: none;
}
.red {
color: red;
}
#contract {
font-size: x-large;
}
</style>
<script type="text/javascript">
var c;
function toggleAlgo() {
$("#scoringRules").toggleClass("hidden");
}
function getRandomIntInclusive(min, max) {
var min = Math.ceil(min);
var max = Math.floor(max);
return Math.floor(Math.random() * (max - min + 1)) + min; //The maximum is inclusive and the minimum is inclusive
}
var suits = ["&clubs;", "&diams;", "&hearts;", "&spades;", "NT"];
var mults = ["", "", "", "", "", "", "", "X", "X", "XX"];
function genContract() {
var contract = new Object();
contract.num = getRandomIntInclusive(1,7);
contract.suit = suits[getRandomIntInclusive(0,suits.length-1)];
contract.mult = mults[getRandomIntInclusive(0, mults.length-1)];
contract.vulnerable = getRandomIntInclusive(0,1);
var results = [];
for (let i=(-6 - contract.num); i<=7-contract.num; i++) {
results.push(i);
}
//These extra loops try to make more common final contracts more...common
for (let j=0; j<2; j++) {
for (let i=(-2); i<=7-contract.num; i++) {
results.push(i);
}
for (let k=0; k<2; k++) {
for (let i=0; i<=7-contract.num; i++) {
results.push(i);
}
}
}
contract.result = results[getRandomIntInclusive(0, results.length-1)];
return contract;
}
function refresh() {
c = genContract();
$("#num").html(c.num);
$("#suit").html(c.suit);
if ( (c.suit == "&diams;") || (c.suit == "&hearts;") ) {
$("#suit").addClass("red");
} else {
$("#suit").removeClass("red");
}
$("#mult").html(c.mult);
$("#result").html(c.result >= 0 ? '+' + c.result : c.result);
$("#vnv").html(c.vulnerable ? "vulnerable" : "not vulnerable");
}
function score() {
var score = 0;
var isGrandSlam = false;
var isSmallSlam = false;
if (c.num === 7) {
isGrandSlam = true;
} else if (c.num === 6) {
isSmallSlam = true;
}
var isGame = false;
var minMinor = 5;
var minMajor = 4;
var minNT = 3;
if (c.mult === "X") {
minMinor = 3;
minMajor = 2;
minNT = 2;
} else if (c.mult === "XX") {
minMinor = 2;
minMajor = 1;
minNT = 1;
}
if (
( (c.num >= minMinor) && ( (c.suit === "&diams;") || (c.suit === "&clubs;") ) ) ||
( (c.num >= minMajor) && ( (c.suit === "&hearts;") || (c.suit === "&spades;") ) ) ||
( (c.num >= minNT) && (c.suit === "NT") )
) {
isGame = true;
}
var bonusPart = 50;
var bonusGame = 300;
var bonusSSlam = 500;
var bonusGSlam = 1000;
var bonusOver = 100;
var penaltyUnder = 50;
var penaltyDownDoubled = [100, 300, 500];
while (penaltyDownDoubled.length < 13) {
penaltyDownDoubled.push(penaltyDownDoubled[penaltyDownDoubled.length-1]+300);
}
if (c.vulnerable) {
bonusGame = 500;
bonusSSlam = 750;
bonusGSlam = 1500;
bonusOver = 200;
penaltyUnder = 100;
penaltyDownDoubled = [200, 500, 800];
while (penaltyDownDoubled.length < 13) {
penaltyDownDoubled.push(penaltyDownDoubled[penaltyDownDoubled.length-1]+300);
}
}
//contract made
if (c.result >= 0) {
var trickscore = 0;
if (c.suit === "NT") {
trickscore += 10;
}
if ( (c.suit == "&diams;") || (c.suit == "&clubs;") ) {
trickscore += 20 * c.num;
} else {
trickscore += 30 * c.num;
}
score += trickscore;
//standard game/slam/partscore bonus
if (isGrandSlam) {
score += bonusGSlam;
} else if (isSmallSlam) {
score += bonusSSlam;
}
if (isGame) {
score += bonusGame;
} else {
score += bonusPart;
}
//doubled
if (c.mult === "X") {
score += trickscore;
score += bonusOver * c.result;
score += 50;
//redoubled
} else if (c.mult === "XX" ) {
score += 3*trickscore;
score += (bonusOver * 2) * c.result;
score += 100;
} else {
if ( (c.suit == "&diams;") || (c.suit == "&clubs;") ) {
score += 20 * c.result;
} else {
score += 30 * c.result;
}
}
//got set
} else {
//doubled
if (c.mult === "X") {
score -= penaltyDownDoubled[Math.abs(c.result)-1];
//redoubled
} else if (c.mult === "XX" ) {
score -= penaltyDownDoubled[Math.abs(c.result)-1] * 2;
//no multiplier
} else {
score -= penaltyUnder * Math.abs(c.result);
}
}
return score;
}
function checkScore() {
if (score() == $("#score").val()) {
window.alert("CORRECT!");
$("#score").val("");
refresh();
} else {
window.alert("Sorry. Try again.");
}
}
window.onload = refresh;
</script>
</head>
<body>
<h1>Bridge Scoring Drills</h1>
<p id="contract">
<span id="num"></span><span id="suit"></span><span id="mult"></span> <span id="result"></span> (<span id="vnv"></span>)
</p>
<form id="drill" action="#" onsubmit="checkScore(); return false;">
<p>
<label for="score">Score:</label>
<input id="score" name="score" autofocus="autofocus" type="number">
</p>
<input class="submit" type="submit" id="btn_check" value="Check Answer">
</form>
<p>
<button id="btn_answer" onclick="window.alert('Score: '+score());">Reveal Answer</button>&nbsp;
<button id="btn_refresh" onclick="refresh();">New Contract</button>
</p>
<hr>
<p>
<button id="btn_togglealgo" onclick="toggleAlgo();">Show/Hide Scoring Rules</button>
</p>
<div id="scoringRules" class="hidden">
<p><a href="https://www.acbl.org/learn_page/how-to-play-bridge/how-to-keep-score/">As per ACBL.</a></p>
<h2>Trick Score</h2>
<p>You earn 40 points for the first notrump trick scored and 30 points thereafter; 30 points for each spade and heart trick; 20 points for each club and diamond trick.</p>
<p>Trick values double in doubled contracts and are doubled again for redoubled contracts.</p>
<h2>Bonuses</h2>
<p>Whenever one side scores a slam, or a game, or a partscore, the side collects, in addition to the trick score, an immediate bonus. For instance:</p>
<ul>
<li>For making a vulnerable grand slam 1,500</li>
<li>For making a non-vulnerable grand slam 1,000</li>
<li>For making a vulnerable small slam 750</li>
<li>For making a non-vulnerable small slam 500</li>
<li>For making a vulnerable game 500</li>
<li>For making a non-vulnerable game 300</li>
<li>For making a partscore50</li>
<li>For making a doubled contract 50</li>
<li>For making a redoubled contract 100</li>
</ul>
<p>Note that the slam bonuses <em>stack on top of</em> the game bonus. But the game bonus <em>does not</em> stack with the partscore bonus.</p>
<h2>Overtricks</h2>
<p>If the declaring side makes one or more overtricks, the side collects per overtrick:</p>
<ul>
<li>For an undoubled contract, non-vulnerable Trick Value</li>
<li>For a doubled contract, non-vulnerable 100</li>
<li>For a redoubled contract, non-vulnerable 200</li>
<li>For an undoubled contract, vulnerable Trick Value</li>
<li>For a doubled contract, vulnerable 200</li>
<li>For a redoubled contract, vulnerable 400</li>
</ul>
<h2>Undertricks</h2>
<p>When the contract is defeated, the opponents collect:</p>
<ul>
<li>For each non-vulnerable trick 50</li>
<li>For each vulnerable trick 100</li>
<li>Not vulnerable doubled 100,300,500 for down 1, 2 and 3 (300 more for each additional trick)</li>
<li>Vulnerable doubled 200,500,800 for down 1, 2 and 3 (300 more for each additional trick)</li>
<li>Not vulnerable redoubled 200, 600, 1000 for down 1, 2 and 3 (600 more for each additional trick)</li>
<li>Vulnerable redoubled 400, 1000, 1600 for down 1, 2 and 3 (600 more for each additional trick)</li>
</ul>
</div>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment