I'm having trouble completing my 21blackjack logic - javascript

I am required to code the logic and rules of Blackjack 21 (not a console game of Blackjack). I don't need to get the input from the keyboard, but the solution should be able to run for any scenario (players and hands). The code has to be as if it was production code, I will be reviewed on code quality. As per the test case given, determine the players' results against the dealer in a round of Blackjack, by outputting each players hand and result.
#RULES:
Cards in a hand are summed to determine the hand's value.
An ace can be equal to 1 or 11, based on the other cards in the hand. The goal is to not exceed 21.
Any player whose hand consists of 5 cards without the hand exceeding 21 automatically beats the dealer.
Players play against the dealer and not each other.
Any player whose hand is equal to or exceeds the dealer's hand and is less than or equal to 21 beats the dealer.
I need help with getting all the players' score and outputting them.
github link to my code
https://github.com/Nduh-zn/blackjack-Logic
const reducer = (a,b) => a + b;
const CalculateHand = hand=> {
const getTotal= [];
let AceQty = 0;
hand.forEach((faceCard) => {
let num = parseInt(faceCard);
//if the card value is either 'Q', 'K' or 'J', the card number should equal to 10
switch(true){
case faceCard == 'Q' || faceCard == 'K' || faceCard == 'J' : num = 10
getTotal.push(num)
break;
//case 2: determine the value for Face Card A
case faceCard = 'A': AceQty++;
let aceCheck = getTotal.reduce(reducer, 0);
if(aceCheck < 12 && AceQty>=1)
{
num = 11;
getTotal.push(num)
}
else
{
num = 1;
getTotal.push(num);
}
break;
case faceCard <= 10 : getTotal.push(num)
break;
default:
}
})
return getTotal;
}
//comparing dealer hand to player hand to determine the winner between each player vs the dealer
const calculateWinner = (theDealer, thePlayer) => {
const dealerHand = theDealer.reduce(reducer, 0);
thePlayer.forEach((item, idx) => {
let playerHand = item.reduce(reducer);
if(item.length >= 5 && playerHand <= 21)
{
console.log(`playerName + has won with 5 cards`)
}
else if(playerHand > dealerHand && playerHand <= 21)
{
console.log(`playerName + beats the dealer`)
}
else if(playerHand === dealerHand)
{
console.log(`playerName + tied with dealer`)
}
else
{
console.log(`dealer beats...`)
}
})
}
const getHand = hand => CalculateHand(hand);
(gameStart = () => {
const dealer = getHand(['6', '9']);
const Andrew = getHand(['9','6','J']);
const Billy = getHand(['Q', 'K']);
const Carla = getHand(['2','9','K']);
const Ndu = getHand(['A','k', '10'])
calculateWinner(dealer, [Andrew, Billy, Carla]);
})();

Related

Trying to solve sliding window median problem in leetcode

I am working on LeetCode code challenge 480. Sliding Window Median:
You are given an integer array nums and an integer k. There is a sliding window of size k which is moving from the very left of the array to the very right. You can only see the k numbers in the window. Each time the sliding window moves right by one position.
Return the median array for each window in the original array. Answers within 10-5 of the actual value will be accepted.
I submitted my code, but it fails on test cases. I suspect that there is a problem in this part of my code:
const medianSlidingWindow = (array, window) => {
let start = 0;
let end = window - 1;
const min = new MinHeap(array);
const max = new MaxHeap(array);
const insert = (index) => {
if(max.size === 0){
max.push(index);
return;
}
(array[index] >= max.peak) ? min.push(index) : max.push(index);
balance();
}
const balance = () => {
if(Math.abs(max.size - min.size) >= 2){
const returned = (max.size > min.size) ? max.pop() : min.pop();
(max.size > min.size) ? min.push(returned) : max.push(returned);
}
}
const remove = (index) => {
(max.has(index)) ? max.pop(index, true) : min.pop(index, true);
balance();
}
const next = () => {
remove(start++);
insert(++end);
}
const getMedian = () => {
if(window % 2 === 0) return (max.peak + min.peak)/2;
return (max.size > min.size) ? max.peak : min.peak;
}
for(let i = 0; i <= end; i++){
insert(i);
}
const ret = [];
while(end < array.length){
ret.push(getMedian());
next();
}
return ret;
}
Here is the full code:
class MaxHeap{
#array = [];
#size = 0;
#reference = [];
#map = new Map();
constructor(reference = []){
this.#reference = reference;
}
get size(){
return this.#size;
}
/* Debug */
get array(){
return this.#array;
}
get peak(){
return this.get(0);
}
get(index){
if(index === null || index < 0 || index >= this.#array.length) return null;
return this.#reference[this.#array[index]];
}
has(indexReference){
return this.#map.has(indexReference);
}
swap(indexA, indexB){
let temp = this.#map.get(this.#array[indexA]);
this.#map.set(this.#array[indexA], indexB);
this.#map.set(this.#array[indexB], temp);
[this.#array[indexA], this.#array[indexB]] = [this.#array[indexB], this.#array[indexA]];
}
sink(index){
let currentIndex = index;
let greterChild;
while((this.get(greterChild = this.get(2*currentIndex+1) >= this.get(2*currentIndex + 2) ? 2*currentIndex + 1 : 2*currentIndex + 2) ?? Number.MIN_SAFE_INTEGER) > this.get(currentIndex)){
this.swap(currentIndex, greterChild);
currentIndex = greterChild;
}
}
bubble(index){
let currentIndex = index;
let parent;
while((this.get(parent = Math.ceil((currentIndex - 2)/2)) ?? Number.MAX_SAFE_INTEGER) < this.get(currentIndex)){
this.swap(currentIndex, parent);
currentIndex = parent;
}
}
push(...char){
if(char[0].constructor === Array) char = char.flat();
for(let i = 0; i < char.length; i++){
this.#array.push(char[i]);
this.#map.set(char[i], this.#array.length - 1)
this.bubble(this.#array.length - 1);
this.#size++;
}
}
pop(index = 0, fromReference = false){
const ret = (fromReference) ? index :this.#array[index];
if(fromReference) index = this.#map.get(index);
this.swap(index, this.#array.length - 1);
this.#map.delete(ret);
this.#array.pop();
this.sink(index);
this.#size--;
return ret;
}
}
class MinHeap extends MaxHeap{
constructor(reference = []){
super(reference);
}
get size(){
return super.size;
}
get peak(){
return super.peak;
}
/* Debug */
get array(){
return super.array;
}
bubble(index){
let currentIndex = index;
let parent;
while((this.get(parent = Math.ceil((currentIndex - 2)/2)) ?? Number.MIN_SAFE_INTEGER) > this.get(currentIndex)){
this.swap(currentIndex, parent);
currentIndex = parent;
}
}
sink(index){
let currentIndex = index;
let lesserChild;
while((this.get(lesserChild = this.get(2*currentIndex+1) >= this.get(2*currentIndex + 2) ? 2*currentIndex + 2 : 2*currentIndex + 1) ?? Number.MAX_SAFE_INTEGER) < this.get(currentIndex)){
this.swap(currentIndex, lesserChild);
currentIndex = lesserChild;
}
}
}
const medianSlidingWindow = (array, window) => {
let start = 0;
let end = window - 1;
const min = new MinHeap(array);
const max = new MaxHeap(array);
const insert = (index) => {
if(max.size === 0){
max.push(index);
return;
}
(array[index] >= max.peak) ? min.push(index) : max.push(index);
balance();
}
const balance = () => {
if(Math.abs(max.size - min.size) >= 2){
const returned = (max.size > min.size) ? max.pop() : min.pop();
(max.size > min.size) ? min.push(returned) : max.push(returned);
}
}
const remove = (index) => {
(max.has(index)) ? max.pop(index, true) : min.pop(index, true);
balance();
}
const next = () => {
remove(start++);
insert(++end);
}
const getMedian = () => {
if(window % 2 === 0) return (max.peak + min.peak)/2;
return (max.size > min.size) ? max.peak : min.peak;
}
for(let i = 0; i <= end; i++){
insert(i);
}
const ret = [];
while(end < array.length){
ret.push(getMedian());
next();
}
return ret;
}
What went wrong:
On the 30th testcase of the problem (link: https://leetcode.com/problems/sliding-window-median/submissions/859041571/), it resolves to a wrong answer but when I pick one of the windows that resolves to a wrong answer it gives me a correct answer. I'm currently confused because two of the heaps are fairly balanced (as one heap doesn't exceed above one element) and I've tested my heap that both seem to work perfectly. It will be very helpful if somebody helps me.
Link to SO questions I've followed:
How to implement a Median-heap
There are these problems in your heap implementation:
The get function will return null when an out of range index is given, which means the while condition in your sink method could sometimes choose an non-existing child (when there is only one child). Note that a numerical comparison with null will treat that null as 0, and depending of the sign of the value you compare it with can give false or true.
For example, your code fails this test case for that reason:
nums=[1,2,3,4]
k=4
You can fix this by returning undefined instead of null. Then also make sure that the false side of the comparison operator is the one with +1 (choosing the left child), while the true side takes the other child.
The pop method, when called with true for the second argument, does not guarantee to restore the heap property. It takes care of sinking the value at the given index, but does not consider the case where this value should actually bubble up!
For example, your code fails this test case for that reason:
nums=[10,6,5,2,3,0,8,1,4,12,7,13,11,9]
k=11
Here is a simplified example where I depict a min-heap with the referenced values:
5
/ \
8 6
/ \ /
10 12 7
If the node with value 10 is to be removed, the swap action will give this min-heap (which is correct):
5
/ \
8 6
/ \ /
7 12 10
And then your code calls sink on that node with value 7. It is clear that there is nothing to sink here, but instead that 7 should bubble up and swap with 8. Your code must foresee both scenarios: sift or bubble.
If you fix those two issues in your heap implementation, it will work.
I provide here the literal changes you have to make:
In the get method, replace return null with return undefined (or omit the explicit value)
In the MaxHeap sink method, swap the comparator expression, replacing:
while((this.get(greterChild = this.get(2*currentIndex+1) >= this.get(2*currentIndex + 2) ? 2*currentIndex + 1 : 2*currentIndex + 2) ?? Number.MIN_SAFE_INTEGER) > this.get(currentIndex)){
with:
while((this.get(greterChild = this.get(2*currentIndex+1) <= this.get(2*currentIndex + 2) ? 2*currentIndex + 2 : 2*currentIndex + 1) ?? Number.MIN_SAFE_INTEGER) > this.get(currentIndex)){
In the pop method, replace:
this.sink(index);
with:
this.sink(index);
this.bubble(index);
(You can also first check which of both is needed, but it doesn't hurt to just call both methods)

Random number upper limit and lower limit

I'm working on a random number guessing game in JavaScript. I want the user to input a lowLimit and a highLimit and have the random number generated between those two numbers. I tried hardcoding the lowLimit and highLimit as below:
let lowLimit = 5;
let highLimit = 20;
let random = Math.floor(Math.random() * highLimit);
if (random < lowLimit) {
random += lowLimit;
}
console.log(random);
and everything works well.
However, when I allow the user to input values, the random number always becomes the sum of lowLimit and upperLimit. I cannot figure this out!
My final code is this:
let lowLimit = prompt('Input the lower limit:');
let highLimit = prompt('Input the upper limit:');
let random = Math.floor(Math.random() * highLimit);
let tries = 0;
if (random < lowLimit) {
random += lowLimit;
}
console.log(random);
let guess = prompt('Secret number generated. Enter guess:');
while (guess !== random) {
if (guess === 'q') break;
tries += 1;
if (guess > random) {
prompt('You guessed too high. Guess again...');
} else if (guess < random) {
prompt('You guessed too low. Guess again...');
} else {
alert('You guessed correctly! You made ' + tries + " guesses.");
}
}
This solution works. Any refactoring suggestions are welcome.
let lowLimit = Number(prompt('Input the lower limit:'));
let highLimit = Number(prompt('Input the upper limit:'));
while (!lowLimit || !highLimit) {
lowLimit = Number(prompt('Input a valid lower limit:'));
highLimit = Number(prompt('Input a valid upper limit:'));
}
lowLimit = Number(lowLimit);
highLimit = Number(highLimit);
let random = Math.floor(Math.random() * (highLimit - lowLimit) + lowLimit);
let guesses = 0;
console.log(random);
guess = prompt('Enter guess:');
while (guess !== random) {
if (guess === 'q') {
alert('Ok. Quitting... You made ' + guesses + ' guesses')
break;
}
guesses += 1;
guess = Number(guess);
if (guess > random) {
guess = prompt('You guessed too high. Guess again...');
} else if (guess < random) {
guess = prompt('You guessed too low. Guess again...');
} else alert('You guessed correctly! You made ' + guesses + " guesses.");
}
A few tweaks to improve the code, and one bug fix (the case where user guesses correctly on the first try, they will receive no feedback)...
// + is concise way to coerce an int
const lowLimit = +prompt('Input the lower limit:');
const highLimit = +prompt('Input the upper limit:');
// note - we could add checks here for invalid or disordered values
// this presumes we want random to be exclusive of highLimit. if not, we'll need to tweak
const random = Math.floor(Math.random() * (highLimit - lowLimit) + lowLimit);
// we'll vary the prompt during the game
let promptMsg = 'Enter guess:', guesses = 0, guess;
// bug fix and cleanup: do while, so we always play at least once
// prompt in just one place, alter the prompt message to represent game state
do {
guess = prompt(promptMsg);
guesses++;
if (guess !== 'q') {
guess = +guess;
if (guess > random) {
promptMsg = 'You guessed too high. Guess again...';
} else if (guess < random) {
promptMsg = 'You guessed too low. Guess again...';
}
}
} while (guess !== 'q' && guess !== random);
const endMsg = guess === 'q' ? 'Ok. Quitting' : 'You guessed correctly.'
const guessesMsg = `You made ${guesses} ${guesses === 1 ? 'guess' : 'guesses'}`;
alert(`${endMsg} ${guessesMsg}`)
We can generate a random number by using the Date.now()
let res = Date.now() % (highLimit - lowLimit + 1) + lowLimit.
This is because nobody can estimate the time in milisecond the code runs.

How do I pick the closest integer to a random number?

I tried to make a small game were you have to guess which number the computer will pick. The pick that is closer to the number should win. Now I don't know how to write an if/switch that compares the values and chooses the one that is closer to the secretNumber.
This is my current code for evaluating who won. As you can see, I can only work with winners having the exact same number as the secret one.
if (user1Guess == user2Guess && user1Guess == secretGuess) {
console.log(`TIE!`)
} else if (user1Guess == secretNumber && user2Guess !== secretNumber){
console.log(`Player 1 wins!`)
} else if (user1Guess !== secretNumber && user2Guess == secretNumber)
{
console.log(`Player 2 wins!`)
};
Take the absolute value of the difference between each guess and the secretNumber. The closest guess will be the one whose difference is smaller:
const user1Diff = Math.abs(user1Guess - secretGuess);
const user2Diff = Math.abs(user2Guess - secretGuess);
if (user1Diff === user2Diff) {
console.log('Tie');
} else if (user1Diff > user2Diff) {
console.log('Player 2 wins');
} else {
console.log('Player 1 wins');
}
You canuser Math.abs() to get difference between user guesses and secretNumber to compare and decide who wins.
Hope this snippet helps:
const user1Guess = Math.floor(Math.random() * 100) + 1, // Random number between 1-100 to mock user input
user2Guess = Math.floor(Math.random() * 100) + 1, // Random number between 1-100 to mock user input
secretNumber = Math.floor(Math.random() * 100) + 1, // Random number between 1-100 to mock computer pick
user1Diff = Math.abs(user1Guess - secretNumber),
user2Diff = Math.abs(user2Guess - secretNumber);
if (user1Diff === user2Diff) {
console.log(`TIE!`)
} else if (user1Diff < user2Diff) {
console.log(`Player 1 wins!`)
} else if (user1Diff > user2Diff) {
console.log(`Player 2 wins!`)
} else {
console.log(`You broke the game, congrats!`)
}
Btw, you have a typo at first if statement: secretGuess needs to be secretNumber
Let's think about what it means for a guess to be closer.
If x is closer to n than y is. Then the distance from x to n must be less than the distance from y to n.
With numbers, the distance from x to n is abs(n - x), which is the absolute value of the difference. The absolute value is always non-negative number. For example, the absolute value of -3 is 3.
So if x is closer to n than y is, that must mean that the following is also true:
Math.abs(n - x) < Math.abs(n - y)
You can then use these in your if statement conditions.
const user1Distance = Math.abs(secretNumber - user1Guess);
const user2Distance = Math.abs(secretNumber - user2Guess);
if (user1Distance === user2Distance) {
console.log("TIE!");
} else if (user1Distance < user2Distance) {
console.log("Player 1 wins!");
} else {
console.log("Player 2 wins!");
}

Convert JSON data into new JSON output

I'm trying to write a script to output JSON according to these constraints.
So far I think my logic is correct.
Any help is appreciated.
CURRENT ISSUES:
[working now]I can't figure out why duration continues to return 0
[working now]how to tackle setting the max/min
tackling how to handle when two excursions of different types occur back to back (“hot” ⇒ “cold” or “cold” ⇒ “hot”)
This is how each new object should appear
let current_excursion = {
'device_sensor' : '',
'start_at' : [],
'stop_at' : 0,
'duration' : 0,
'type': '',
'max/min':0
}
device_sensor
The sId this excursion was detected on.
start_at
The date and time the temperature first is out of range in ISO_8601 format.
stop_at
The date and time the temperature is back in range in ISO_8601 format.
duration
The total time in seconds the temperature was out of range.
type
Either the string “hot” or “cold” depending on the type of excursion.
max/min
The temperature extreme for the excursion. For a “hot” excursion this will be the max and for a “cold” excursion the min.
A temperature excursion event starts
when the temperature goes out of range and ends when the temperature returns
to the range.
For a “hot” excursion this is when the temperature is greater than 8 °C,
and for a “cold” excursion this is when the temperature is less than 2 °C.
If two excursions of different types occur back to back
(“hot” ⇒ “cold” or “cold” ⇒ “hot”) please take the midpoint of the two
timestamps as the end of the first excursion and the start of the second.
If an excursion is occurring at the end of the temperature readings
please end the excursion at the last reading (duration = 0)
Here is the link to the test data
Test Case Data
Here is what I've written so far:
const tempTypeTernary = (num) =>{
if(num < 2){
return 'cold'
} else if(num > 8){
return 'hot'
}
}
const excursion_duration = (x,y) =>{
let start = new Date(x) / 1000
let end = new Date(y) / 1000
return end - start
}
const reset_excursion = (obj) => {
Object.keys(obj).map(key => {
if (obj[key] instanceof Array) obj[key] = []
else obj[key] = ''
})
}
const list_excursion = (array) =>{
let result = [];
let max_min_excursion = 0;
let current_excursion = {
'device_sensor' : '',
'start_at' : [],
'stop_at' : 0,
'duration' : 0,
'type': '',
'max/min':0
}
for(let k = 0; k < array.length;k++){
if( array[k]['tmp'] < 2 || array[k]['tmp'] > 8){
current_excursion['device_sensor'] = array[k]['sId'];
current_excursion['start_at'] = [new Date(array[k]['time']).toISOString(),array[k]['time']];
current_excursion['type'] = tempTypeTernary(array[k]['tmp']);
if( array[k]['tmp'] > 2 || array[k]['tmp'] < 8){
current_excursion['stop_at'] = new Date(array[k]['time']).toISOString();
current_excursion['duration'] = excursion_duration(current_excursion['start_at'][1],array[k]['time'])
}
result.push(current_excursion)
reset_excursion(current_excursion)
}
}
return result
}
list_excursion(json)
Let me be bold and try to answer on just eyeballing the code; please tryout this:
const tempTypeTernary = (num) =>{
if(num < 2){
return 'cold'
} else if(num > 8){
return 'hot'
}
}
const excursion_duration = (x,y) =>{
let start = new Date(x) / 1000
let end = new Date(y) / 1000
return end - start
}
const reset_excursion = (obj) => {
Object.keys(obj).map(key => {
if (obj[key] instanceof Array) obj[key] = []
else obj[key] = ''
})
}
const list_excursion = (array) =>{
let result = [];
let max_min_excursion = 0;
let current_excursion = {
'device_sensor' : '',
'start_at' : [],
'stop_at' : 0,
'duration' : 0,
'type': '',
'max/min':0
}
for(let k = 0; k < array.length;k++){
if( array[k]['tmp'] < 2 || array[k]['tmp'] > 8)
{
if (current_excursion['type']==null)
{
current_excursion['device_sensor'] = array[k]['sId'];
current_excursion['start_at'] = [new Date(array[k]['time']).toISOString(),array[k]['time']];
current_excursion['type'] = tempTypeTernary(array[k]['tmp'])
}
}
else // this is where the second 'if' was
{
if (current_excursion['type']!=null)
{
current_excursion['stop_at'] = new Date(array[k]['time']).toISOString();
current_excursion['duration'] = excursion_duration(current_excursion['start_at'][1],array[k]['time'])
result.push(current_excursion)
reset_excursion(current_excursion)
}
}
}

how do I loop back to the previous functions when stop is only prompted at the

I am facing a problem to create a loop which will loop back to the previous functions when the user does not want the program to stop (if the user wants it to stop, the program will continue with other functions).
I need to create a list of functions to do base conversion while showing the logic:
Step1: prompt for a number
Step2: prompt for an alphabet (b for Binary/o for Octal/h for Hexadecimal) as the base
Step3: convert it to a string (e.g. "108sup10 = 1101100sup2" & "63300268sup10 = 3C5E2A7sup16")
Step4: alert the string answer in a statement (e.g: Base 10 number 63300268 is 3C5E2A7 in Hexadecimal)
Step5: prompt to stop. If user's input is not "s", it will repeat step 1~4, else it continue to step 6.
Step 6: alert the max and min number entered from (repeated) step1's input.
for step 1,2,3,4,6, it is mandatory to use functions.
May I know how do I code for STEP5 in order to loop back from step 1-4 when stopping is prompted? Do I need a function for this?
//prompt to get number
function getNumber() {
var myNumber;
do {
myNumber = Number(prompt("Enter an unsigned base 10 number:")); //prompt user's input to be excecuted first
} while (myNumber < 0) //loop will run again and again as long as the number is less than zero
return myNumber;
}
//prompt to get base
function getBase() {
var myBase;
do {
myBase = (prompt("Enter b for binary, o for octal and h for hexadecimal"));
} while (!(myBase == "b" || myBase == "B" || myBase == "s" || myBase == "S"|| myBase == "h" || myBase == "H")) //loop if the input is not b, s or h
return myBase;
}
//converting the base to the number
function baseConversion(number, newBase) {
var arr = [];
if (newBase == "b" || newBase == "B") {
newBase = 2;
} else if (newBase == "o" || newBase == "O") {
newBase = 8;
}else if (newBase == "h" || newBase == "H") {
newBase = 16;
}
do { //putting the each remainder at the front of the array
arr.unshift(number%newBase);
number = Math.floor(number/newBase); //round down the divided answer
} while (number>newBase-1) //loop as long as this condition holds
arr.unshift(number);
return arr;
}
//function to string the arrays
function convertToString(number, base) {
var resultString = ""
for (var i = 0; i < results.length; i++) {
var digit = results[i];
if (digit > 9) {
switch (digit) {
case 10:
digit = 'A'
break;
case 11:
digit = 'B'
break;
case 12:
digit = 'C'
break;
case 13:
digit = 'D'
break;
case 14:
digit = 'E'
break;
case 15:
digit = 'F'
break;
}
}
resultString += digit;
}
return resultString
}
//function to alert the answer statement
function alertAnswer() {
var statement = alert("Base 10 number:" + myNumber + "is" + base + "in" + myBase);
return statement;
}
//function to find the maximum number in the array
function myMax(myArray) {
var max = myArray[0];
for (var z = 0; z < myArray.length; z++) {
if (myArray[z] > max) {
max = myArray[z];
}
}
return max;
}
//function to find the minimum number in the array
function myMin(myArray) {
var min = myArray[0];
for (var z = 0; z < myArray.length; z++) {
if (myArray[z] > min) {
min = myArray[z];
}
}
return min;
}
Sorry if I'm mistaken, but is this what you're looking for?
var promptVar = false;
do
{
//function calls for steps 1~4
prompt();
}
while (promptVar == false)
function prompt()
{
if (confirm('Do you want to continue to step 6?'))
{
promptVar = true;
} else {
promptVar = false;
}
}

Categories