Subtract method in object if greater than zero - javascript

I'm using a game object that has different getters and methods within it. One of the parameters of my assignment is that the "playerDies" method has to subtract 1 from the lives if it is greater than zero and it cannot go below 0 (so it can't be -1 which is what is happening now). This topic is fairly new to me so I'm not sure what I can do to execute the subtraction only when there is a specific value. I've tried using an if-else statement but that resulted in the code not working.
My code:
var game = {
lives: 3,
coins: 0,
get points() {
return this.coins * 10;
},
playerDies: function() {
return this.lives -= 1;
},
newGame: function() {
this.lives = 3;
this.coins = 0;
},
};
console.log("lives = " + game.lives); // should be 3
console.log("coins = " + game.coins); // should be 0
console.log("points = " + game.points); // should be 0
game.coins = 0;
console.log("points = " + game.points); // should be 20
game.playerDies();
console.log("lives = " + game.lives); // should be 2
game.playerDies();
game.playerDies();
game.playerDies();
console.log("lives = " + game.lives); // should be 0
game.newGame();
console.log("lives = " + game.lives); // should be 3
console.log("coins = " + game.coins); // should be 0

You can put if statement in your playerDies method
playerDies: function() {
if (this.lives > 0) {
return this.lives -= 1
}
return 0
}

Related

Is there a "repeat [function] until [property = true]" type of loop in MakeCode JS?

I'm making a game in Microsoft MakeCode Arcade for a school project, and I wanted to know if there was a "repeat [function] until [property = true]" type of loop like there is in Luau. I wanted to use this so that the game waits until my player sprite is at a certain coordinate to run some code. I figured out a way to do this in a different way, but I wanted to know just for future reference.
If anyone is wondering, this is what the alternative way I am using.
game.onUpdateInterval(100, function () {
if (level == 1) {
if (myPlayer.x == 950 && myPlayer.y == 140) {
myPlayer.y = 100
myPlayer.x = 10
if (game.ask("Does " + level_1 + " + " + level1_2 + " = " + level1CorrectAns + "?")) {
console.log("Level 1 Completed successfully")
level += 1
LevelChange()
} else {
game.over(false)
}
}
}
})
You could use either while loop or do...while loop
For while loop, the following code will keep on running as long as the condition is true.
let x = 0
while (x < 3) {
x++
}
console.log(x) // print 3
For do...while loop, the following code will keep on running as long as the condition is true. And this loop will run at least once.
let result = '';
let x = 0;
do {
x = x + 1;
result = result + x;
} while (x < 5);
console.log(result); // print "12345"
Coming back to your example, I believe you're running the loop every 100ms (based on first argument of your game.onUpdateInterval.
You could easily do this by adding a timer function and wrap this loop in as an async function.
const timer = ms => new Promise(res => setTimeout(res, ms))
async function updateInterval() {
while () {
// Your logic here
await timer(100) // You can change the timeout to your desired ms
}
}
updateInterval();
While I'm not 100% sure of the functionality of your current workaround, but this is my interpretation (Hope it works)
const timer = (ms) => new Promise((res) => setTimeout(res, ms));
async function updateInterval() {
let state = true; // This is just a condition if the loop should continue
while (state) {
if (level == 1) {
if (myPlayer.x == 950 && myPlayer.y == 140) {
myPlayer.y = 100;
myPlayer.x = 10;
if (
game.ask(
'Does ' +
level_1 +
' + ' +
level1_2 +
' = ' +
level1CorrectAns +
'?'
)
) {
console.log('Level 1 Completed successfully');
level += 1;
LevelChange();
state = false; // Update the state to false, so it will exit the while loop
} else {
game.over(false);
}
}
}
await timer(100); // You can change the timeout to your desired ms
}
}
updateInterval();

Look up tables and integer ranges - javascript

So I am looking to create look up tables. However I am running into a problem with integer ranges instead of just 1, 2, 3, etc. Here is what I have:
var ancient = 1;
var legendary = 19;
var epic = 251;
var rare = 1000;
var uncommon = 25000;
var common = 74629;
var poolTotal = ancient + legendary + epic + rare + uncommon + common;
var pool = general.rand(1, poolTotal);
var lootPool = {
1: function () {
return console.log("Ancient");
},
2-19: function () {
}
};
Of course I know 2-19 isn't going to work, but I've tried other things like [2-19] etc etc.
Okay, so more information:
When I call: lootPool[pool](); It will select a integer between 1 and poolTotal Depending on if it is 1 it will log it in the console as ancient. If it hits in the range of 2 through 19 it would be legendary. So on and so forth following my numbers.
EDIT: I am well aware I can easily do this with a switch, but I would like to try it this way.
Rather than making a huge lookup table (which is quite possible, but very inelegant), I'd suggest making a (small) object, choosing a random number, and then finding the first entry in the object whose value is greater than the random number:
// baseLootWeight: weights are proportional to each other
const baseLootWeight = {
ancient: 1,
legendary: 19,
epic: 251,
rare: 1000,
uncommon: 25000,
common: 74629,
};
let totalWeightSoFar = 0;
// lootWeight: weights are proportional to the total weight
const lootWeight = Object.entries(baseLootWeight).map(([rarity, weight]) => {
totalWeightSoFar += weight;
return { rarity, weight: totalWeightSoFar };
});
console.log(lootWeight);
const randomType = () => {
const rand = Math.floor(Math.random() * totalWeightSoFar);
return lootWeight
.find(({ rarity, weight }) => weight >= rand)
.rarity;
};
for (let i = 0; i < 10; i++) console.log(randomType());
Its not a lookup, but this might help you.
let loots = {
"Ancient": 1,
"Epic": 251,
"Legendary": 19
};
//We need loots sorted by value of lootType
function prepareSteps(loots) {
let steps = Object.entries(loots).map((val) => {return {"lootType": val[0], "lootVal": val[1]}});
steps.sort((a, b) => a.lootVal > b.lootVal);
return steps;
}
function getMyLoot(steps, val) {
let myLootRange;
for (var i = 0; i < steps.length; i++) {
if((i === 0 && val < steps[0].lootVal) || val === steps[i].lootVal) {
myLootRange = steps[i];
break;
}
else if( i + 1 < steps.length && val > steps[i].lootVal && val < steps[i + 1].lootVal) {
myLootRange = steps[i + 1];
break;
}
}
myLootRange && myLootRange['lootType'] ? console.log(myLootRange['lootType']) : console.log('Off Upper Limit!');
}
let steps = prepareSteps(loots);
let pool = 0;
getMyLoot(steps, pool);

How to efficiently track rolling min / max values in a frequently updated sliding array

Consider the following javascript data structure:
let sensors = {
sensor1: {
min: 1.00,
max: 9.00,
data: [
{
timestamp: 1517760374400,
value: 1.00
},
{
timestamp: 1517760374500,
value: 2.00
},
{
timestamp: 1517760374600,
value: 9.00
},
{
timestamp: 1517760374700,
value: 1.00
},
{
timestamp: 1517760374800,
value: 3.00
},
{
timestamp: 1517760374900,
value: 1.00
},
{
timestamp: 1517760375000,
value: 9.00
},
{
timestamp: 1517760375100,
value: 8.00
},
]
},
// sensor2, sensor3, etc...
}
Imagine there could be thousands of timestamped data for each sensor.
Initially you can easily set a min / max value, every time an object is added by checking if it is bigger or smaller than the current max
But the tricky part and my question is this:
What is the size of the array is limited - in this case we would set it to a length of 8.
Whenever a new item after item 8 is added (the limit is reached), the 1st item will be removed, and the nth item will be pushed into the end of the array.
The problem is that there can be more items with the same value, and even if there isn't we have no way of knowing which min / max is next without iterating the entire array once again
This is supposed to be scalable to thousands of array items, and is to be run roughly every second with ideally - as low cpu utilization as possible - I don't really think looping over thousands of items every second will be effecient enough.
Do you see another other ways of keeping track of min / max values of an array which is changing like this ever second?
Data structure:
Queue size of N to store N item.
Min / Max Heap to track the min / max item.
A hash map to track the frequency of each item.
When you there is a coming data, update the frequency of the new item, if not there in the heap, add it.
When you need to pop an item, decrease the frequency, while frequency of head == 0, remove from the heap.
Head of the heap is the solution.
Pseudo code:
const swap = (data, i, j) => {
let temp = data[i];
data[i] = data[j];
data[j] = temp;
}
class Heap {
constructor() {
this.data = [];
this.inHeap = {};
this.size = 0;
}
head() {
return this.data[0];
}
// add item O(logN);
add(number) {
if (!this.inHeap[number]) {
this.data[this.size++] = number;
let current = this.size - 1;
while (current > 0) {
if (this.data[current >> 1] < this.data[current]) {
swap(this.data, current >> 1, current);
current >>= 1;
} else {
break;
}
}
this.inHeap[number] = true;
}
}
// remove head O(logN);
remove() {
this.size--;
delete this.inHeap[this.data[0]];
this.data[0] = this.data[this.size];
let current = 0;
while (current * 2 + 1 < this.size) {
let next = current * 2 + 1;
if (current * 2 + 2 < this.size && this.data[current * 2 + 2] > this.data[current * 2 + 1]) {
next = current * 2 + 2;
}
if (this.data[current] < this.data[next]) {
swap(this.data, current, next);
current = next;
} else {
break;
}
}
}
}
class Queue {
constructor(maxSize) {
this.maxSize = maxSize;
this.size = 0;
this.data = [];
this.head = -1;
}
// add a number and return the removed item if any
add(number) {
let next = (this.head + 1) % this.maxSize;
let removedItem = this.data[next];
this.data[next] = number;
this.head = next;
if (removedItem === undefined) {
this.size++;
}
return removedItem;
}
get(i) {
return this.data[(this.head - this.size + 1 + i + this.maxSize ) % this.maxSize];
}
}
class Solution {
constructor(n) {
this.n = n;
this.queue = new Queue(this.n);
this.heap = new Heap();
this.frequency = {};
}
add(number) {
let removedItem = this.queue.add(number);
if (!this.frequency[number]) {
this.frequency[number] = 1;
this.heap.add(number);
} else {
this.frequency[number]++;
}
if (removedItem !== undefined) {
this.frequency[removedItem]--;
if (!this.frequency[removedItem]) {
delete this.frequency[removedItem];
}
// remove head if frequency is zero
while (!this.frequency[this.heap.head()]) {
this.heap.remove();
}
}
}
size() {
return this.queue.size;
}
get(i) {
return this.queue.get(i);
}
max() {
return this.heap.head();
}
}
/*** use of solution here!! **/
let solution = new Solution(3);
let numberInput = document.getElementById("number");
let data = document.getElementById("data");
let maxResult = document.getElementById("max");
let heapData = document.getElementById("heap");
let queueData = document.getElementById("queue");
let frequencyData = document.getElementById("frequency");
function addNumber() {
let value = parseInt(numberInput.value);
if (isNaN(value)) {
alert("Please input a number!");
} else {
solution.add(value);
}
maxResult.innerHTML = "Max: " + solution.max();
// gather data
let dataString = "";
for (let i = 0; i < solution.size(); i++) {
dataString += " " + solution.get(i);
}
data.innerHTML = "Data: " + dataString;
heapData.innerHTML = "Heap: " + JSON.stringify(solution.heap.data.slice(0, solution.heap.size));
queueData.innerHTML = "Queue: " + JSON.stringify(solution.queue);
frequencyData.innerHTML = "Frequency: " + JSON.stringify(solution.frequency);
numberInput.value = parseInt(Math.random() * 1000);
}
.input {
display: flex;
}
.input input {
width: 200px;
padding: 5px 10px;
outline: none;
}
.input button {
padding: 5px 10px;
border: 1px solid light gray;
}
div {
padding: 5px 10px;
}
<div class="input">
<input type="text" id="number" />
<button onClick="addNumber()">Add</button>
</div>
<div class="result">
<div class="data" id="data">
Data:
</div>
<div class="max" id="max">
Max: undefined!
</div>
</div>
<div class="debug">
<div>
<code class="data" id="heap">
Heap:
</code>
</div>
<div>
<code class="max" id="queue">
Queue:
</code>
</div>
<div>
<code class="max" id="frequency">
Frequency:
</code>
</div>
</div>
Sounds fun. I think that you're going to run into a problem where you just don't know a priori whether a value is going to be an extreme (max/min) value in the future.
My thought would be to add an expiration counter to your current max & min values. This counter decrements each time you don't replace your rolling min/max. It is reset to 8 when it is updated with a new, fresh value. Min and max obviously have separate counters.
Now you only have to loop through your values if a counter decrements to 0 and your min/max value becomes stale (it is no longer in your list). If, for example, your min counter expires, you now have to identify the remaining minimum that is currently in your list of 8. In this case, you'll reset the expiration counter to match the number of iterations until that new min value gets removed from the list (8 - it's index).
That might save you some CPU cycles!

Negation the functionality of the function

I im quite confused in the negation of the functionality of my function. The original function takes 9 elements starting at nth index and decrease their transform position.
function pushIt(max, target, index, count) {
if (count == max || count == img.children ) {
running = false;
return;
}
var tmp = target[index];
var matrix = window.getComputedStyle(tmp).getPropertyValue("transform");
var translate_left = matrix.split(",")[4];
var translate_top = matrix.split(",")[5].split(")")[0]-215;
tmp.style.transform = "translate3d(" + translate_left + "px," + translate_top + "px,0)";
setTimeout(function(){
pushIt( max, target, index + 1, count + 1 );
},50)
}
What i wanted to do is to negate its functionality , e.g it wont decrease but increase transform position of (nth * 3) - 1 element ( counting down 9 elements )
function pushItDOWN(max, target, index , count) {
if ( count == max || index < 0 ) {
running = false;
return;
}
console.log("down");
var tmp = target[index];
var matrix = window.getComputedStyle(tmp).getPropertyValue("transform");
var translate_left = matrix.split(",")[4];
var translate_top = matrix.split(",")[5].split(")")[0]+215;
tmp.style.transform = "translate3d(" + translate_left + "px," + translate_top + "px,0)";
setTimeout(function(){
pushItDOWN(max, target, index - 1, count + 1 );
},50)
}
}
What second function does is takes elements and set their transform to oblivion (e.g out of viewport) and somehow break the functionality of first function.
Did i overlook some key fact that is causing the problem , i seem to fail to find the root of the problem.
Live demo for better understanding
I'm not 100% sure, but most likely this is your error:
This will result in a string:
matrix.split(",")[5].split(")")[0]
Lets say it is "500", then
matrix.split(",")[5].split(")")[0] + 215
// equals
"500" + 215
// results in (because + is both used as string concatenation as addition)
"500215"
// - will work, because it only has one meaning
"500" - 215 // results in 285
Parse the value as an int (or float if necessary) before adding the 215:
parseInt(matrix.split(",")[5].split(")")[0]) + 215

Greedy Algorithm in JavaScript

Here is the question that I need to consult for help:
Write a greedy algorithm to make change with the fewest coins possible
using the Greedy Algorithm. You are given an array of coin values and
an amount: computeChange(coins, amount). Return an array with the
counts of each coin.
For example: computeChange([50, 25, 10, 5, 1], 137) should return
the array [2, 1, 1, 0, 2] which indicates how many of each coin: 2
50-cent pieces, 1 quarter (25 cents), 1 dime (10 cents), no nickels (5
cents), and 2 pennies (1 cent), which add up to 137 cents.
The array you return from computeChange should be the same length as
the first argument (coins). Assume that coins contains the values of
different coin types in decreasing order.
The greedy algorithm says that you repeatedly look for the largest
coin less than or equal to the remaining amount of money, then
subtract that coin from the remaining amount. When the remaining
amount reaches zero (or less), return the counts of coins used. (This
algorithm is not always optimal.)
You can change the variables COINS, which gives the values of the
different coins you can use to make change, and AMOUNT, which is the
total value of the change to make. Changing these values might be
useful for debugging your program.
Here is my code which I did but it did not display the standard change for 36 cents. Can anyone help me? Thank you.
<html>
<head>
<title>The Greedy Algorithm</title>
<script>
// ======== Here is the problem to be solved: ========
COINS = [50, 25, 10, 5, 1];
AMOUNT = 137
coincount = [0,0,0,0,0];
// ======== Here is where your solution begins: ========
// define the function named computeChange here:
function computeChange(coins, amount) {
var i = 0; var creminder = AMOUNT; var ccoin;
while( i < COINS.length )
{
while ( COINS[i] <= creminder )
{
creminder = creminder - COINS[i];
ccoin = coincount [i] ;
ccoin += 1;
coincount [i] = ccoin ;
}
i++;
}
return coincount;
}
// ===================================================================
// ======== Everything below here simply displays your output ========
// ======== Do NOT change anything below this line ===================
// ===================================================================
function rightJustify(s, w) {
// return a string of width w with s in the rightmost characters and
// at least one space on the left. For simplicity, assume w < 20.
var slen = s.length;
var blanks = " "
return blanks.substr(0, Math.min(20, Math.max(1, w - slen))) + s;
}
function makeChange() {
// compute change as an array: each element of change tells
// how many of the corresponding value in COINS to give. The
// total value should equal AMOUNT.
var change = computeChange(COINS, AMOUNT);
// now format the results. Output should look like:
// NUMBER VALUE
// 1 50
// 0 25
// 1 10
// 1 5
// 3 1
// TOTAL AMOUNT: 68 (total is correct)
//
// First, we'll do some type checking in case change is not of the
// expected type.
change = [].concat(change); // force whatever it is to be an array
// it should be an array of numbers, so let's check
for (i = 0; i < change.length; i++) {
if (typeof(change[i]) != 'number') {
return "Error: the function computeChange did not return " +
"an array of numbers.";
}
}
if (change.length > COINS.length) {
return "Error: the function computeChange returned an array " +
"longer than the length (" + COINS.length + ") of COINS.";
}
if (change.length < COINS.length) {
return "Error: the function computeChange returned an array " +
"shorter than the length (" + COINS.length + ") of COINS.";
}
var output = "<pre>NUMBER VALUE\n"
var sum = 0;
for (i = 0; i < change.length; i++) {
sum += change[i] * COINS[i];
var n = change[i].toString();
var a = COINS[i].toString();
output += rightJustify(n, 4) + rightJustify(a, 9) + "\n";
}
output += "TOTAL AMOUNT: " + sum + " (total is ";
output += (sum == AMOUNT ? "correct" :
"incorrect, should be " + AMOUNT) + ")\n";
return output;
}
function runSolution()
{
parent.console.log('loaded, calling runSolution()\n');
parent.console.log('answer: ' + document.getElementById('answer').toString());
document.getElementById('answer').innerHTML = makeChange();
}
</script>
</head>
<body>
<!-- the output is displayed using HTML -->
<!-- the ? will be replaced with the answer -->
<div id = "answer">?</div></p>
<br>
<script>runSolution();</script>
</body>
</html>
Thoughts:
After reading the replys, first at thought is that this may be used to other codes that we didn't see here, so we need to make the function sufficient to solve the question by input, not using the GLOBAL VALUES like AMOUNT, COINS and coincount, instead, use params given like coins and amount, and return a self created coincount.
I'll explain this directly use comments in the codes
function computeChange(coins, amount) {
// Create a array that is used to return the final result, instead of the global one.
var coincount = [];
// use the given `amount` to set `creminder ` rather than `AMOUNT` which may not be accessible if your code is called otherplace rather than here.
var i = 0; var creminder = amount; var ccoin;
while( i < coins.length )
{
// Lazily init the used coin for coin type i to 0.
coincount[i] = 0;
while ( coins[i] <= creminder )
{
creminder = creminder - coins[i];
ccoin = coincount[i];
ccoin += 1;
coincount[i] = ccoin;
}
i++;
}
return coincount;
}
Your origin version's creminder is determined by AMOUNT, so no matter I call computeChanges(COINS, AMOUNT) or computeChanges(COINS, 37), the result will be the same, because the 37 in the second example is not used, ignored and creminder is still set to AMOUNT. Both Nina Scholz and I do is to make that given amount account, so it matters when your function generates a result set.
While the answers above are very correct, I think one could also think of the solution to this particular problem in a different way.
With the example of computeChange([50, 25, 10, 5, 1], 137), a single loop could be used to get the required solution.
function computeChange(changeArray, amount) {
const result = [];
for (let i = 0; i < changeArray.length; i++) {
let changeAmount = Math.floor(amount / changeArray[i]);
amount -= (changeArray[i] * changeAmount);
result.push(changeAmount);
}
return result;
}
computeChange([50, 25, 10, 5, 1], 137); //  [2, 1, 1, 0, 2]
Some remarks:
You get values for coins and amount. The original function access
COINSand AMOUNT even if there is a local copy of this values.
creminder is not necessary, because you have amount.
ccoin is not necessary, because you can directly subtract the value of the selected coin from the amount.
var COINS = [50, 25, 10, 5, 1],
AMOUNT = 36; //137
function computeChange(coins, amount) {
var i = 0,
coincount = coins.map(function () { return 0; }); // returns an array and for each element of coins zero
while (i < coins.length) {
while (coins[i] <= amount) {
amount -= coins[i];
coincount[i]++;
}
i++;
}
return coincount;
}
out(JSON.stringify(computeChange(COINS, AMOUNT), null, 4), true);
function out(s, pre) {
var descriptionNode = document.createElement('div');
if (pre) {
var preNode = document.createElement('pre');
preNode.innerHTML = s + '<br>';
descriptionNode.appendChild(preNode);
} else {
descriptionNode.innerHTML = s + '<br>';
}
document.getElementById('out').appendChild(descriptionNode);
}
<div id="out"></div>
function cc(c, a) {
for (var ra=[],i=0,r=a; i<c.length; ra[i] = (r/c[i])|0, r -= ra[i]*c[i], i++);
return ra;
}
function cc2(c, a) {
return c.map((c, i) => { var t = (a/c)|0; a -= c*t; return t; })
}
cc([50, 25, 10, 5, 1], 137); // [2, 1, 1, 0, 2]
cc2([50, 25, 10, 5, 1], 137); // [2, 1, 1, 0, 2]

Categories

Resources