Last active
July 17, 2023 02:41
-
-
Save Guvalif/19dc33579df68c140f6b6d6f013078eb to your computer and use it in GitHub Desktop.
Functional Blackjack Implementation on JavaScript
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* @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