Skip to content

Instantly share code, notes, and snippets.

@Guvalif
Last active July 17, 2023 02:41
Show Gist options
  • Save Guvalif/19dc33579df68c140f6b6d6f013078eb to your computer and use it in GitHub Desktop.
Save Guvalif/19dc33579df68c140f6b6d6f013078eb to your computer and use it in GitHub Desktop.
Functional Blackjack Implementation on JavaScript
/**
* @file Functional Blackjack Implementation on JavaScript
* @author Kazuyuki TAKASE
* @copyright Kazuyuki TAKASE
* @license The MIT License (See also : http://opensource.org/licenses/mit-license.php)
*/
const R = require('ramda');
const Maybe = require('folktale/maybe');
const Result = require('folktale/result');
const { question } = require('readline-sync');
// Constants / Functions Definition
// ============================================================================
const CARDS = {
'A' : [1, 11],
'N2' : [2],
'N3' : [3],
'N4' : [4],
'N5' : [5],
'N6' : [6],
'N7' : [7],
'N8' : [8],
'N9' : [9],
'N10': [10],
'J' : [10],
'Q' : [10],
'K' : [10]
};
const SPADE_SUIT = R.keys(CARDS);
const HEART_SUIT = R.keys(CARDS);
const DIAMOND_SUIT = R.keys(CARDS);
const CLUB_SUIT = R.keys(CARDS);
const SUIT = [ ...SPADE_SUIT, ...HEART_SUIT, ...DIAMOND_SUIT, ...CLUB_SUIT ];
// == toPoint :: string -> [number]
const toPoint = R.prop(R.__, CARDS);
// == suitToPoints :: [string] -> [[number]]
const suitToPoints = R.map(toPoint);
// == addA2 :: [number] -> [number] -> [number]
const addA2 = R.lift(R.add);
// == getScores :: [[number]] -> [number]
const getScores = R.reduce(addA2, [0]);
// == getValidScore :: [number] -> Maybe<number>
const getValidScore = R.pipe(
R.filter(R.gte(21)),
R.ifElse(
R.isEmpty,
R.always(Maybe.Nothing()),
R.o(Maybe.Just, R.reduce(R.max, 0))
)
);
// == getShuffledSuit :: [string] -> [string] !impure
const getShuffledSuit = (suit) =>
{
let shuffled_suit = R.clone(suit);
for (let i = suit.length - 1; i > 0; i--)
{
const j = Math.floor(Math.random() * (i + 1));
[ shuffled_suit[i], shuffled_suit[j] ] = [ shuffled_suit[j], shuffled_suit[i] ];
}
return shuffled_suit;
};
// == getEvenSuit :: [string] -> [string]
const getEvenSuit = R.addIndex(R.filter)((_, i) => i % 2 === 0);
// == getOddSuit :: [string] -> [string]
const getOddSuit = R.addIndex(R.filter)((_, i) => i % 2 !== 0);
// == continuePrompt :: () -> () !impure
const continuePrompt = () =>
{
process.stdin.isTTY = process.stdout.isTTY = true;
const input = question('Do you want to continue the game (Enter/Others) ? ');
if (input === '') return;
process.exit();
};
// Application Entry Point
// ============================================================================
const SHUFFLED_SUIT = getShuffledSuit(SUIT);
const PLAYER_SUIT = getEvenSuit(SHUFFLED_SUIT);
const DEALER_SUIT = getOddSuit(SHUFFLED_SUIT);
// == main :: number -> ()
const main = (hand) =>
{
const scores = R.map(
R.pipe(
R.take(hand),
suitToPoints,
getScores,
getValidScore
),
{ player: PLAYER_SUIT, dealer: DEALER_SUIT }
);
const results = R.map(
m => m.fold(
() => Result.Error('Bust!'),
x => (x === 21) ? Result.Error('Black Jack!') : Result.Ok(x)
),
scores
);
const situation = results.player.chain(R.always(results.dealer));
const report = R.map(m => m.merge(), results);
const report_txt = `Player: ${report.player}, Dealer: ${report.dealer}`;
console.log(report_txt);
situation.fold(
_ => process.exit(),
_ => { continuePrompt(); main(hand + 1); }
);
};
main(2);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment