I thought that this kata will be very cool and easy to get back to js after a break.
I was so wrong lol.
So the URL to kata is here with all of the logic and math informations, i'll put the necessary ones below.
URL:
https://www.codewars.com/kata/51fda2d95d6efda45e00004e/train/javascript
Code:
class User {
constructor() {
this.rank = -8;
this.progress = 0;
this.rankTable=[-8,-7,-6,-5,-4,-3,-2,-1,1,2,3,4,5,6,7,8];
this.rankIndicator=0;
}
incProgress(rankOfActivity) {
if (rankOfActivity == 0 || rankOfActivity > 8 || rankOfActivity < -8) throw new error("Rank input out of range");
if (rankOfActivity <= this.rank - 2) return;
let diff = this.rankTable.indexOf(rankOfActivity)-this.rankTable.indexOf(this.rank);
if (diff==0) this.progress+=3;
else if(diff==-1) this.progress+=1;
else if(this.rank!=8){
this.progress+= 10*diff*diff;
while(this.progress>=100 && this.rank<8){
this.progress-=100;
this.rankIndicator++;
this.rank=this.rankTable[this.rankIndicator];
}
}
if (this.rank==8) this.progress=0;
}
}
Completing an activity that is ranked one ranking lower than the user's will be worth 1 point
After i saw that, my point of view was:
If the difference between activity rank and user's rank was -1 ,for example:
User's rank is -2 (index 6 in rankTable array)
Activity ranked at -3 (index 5 in rankTable array)
The difference would be 5-6 = -1
So it should add up 1 point of progress, and it looks like it doesn't do that and i cant figure it out why it doesn't add up.
Here are bunch of errors to show that it happens on any rank.
After applying rank of -1 the progress was expected to be 21, but was actually 20
After applying rank of 3 the progress was expected to be 61, but was actually 60
After applying rank of 8 the progress was expected to be 51, but was actually 50
//...
//if (rankOfActivity <= (this.rank - 2)) return;
//bug
let diff = (this.rankTable.indexOf(rankOfActivity)) - (this.rankTable.indexOf(this.rank));
if (diff <=-2)return;//
//fixed
//...
Personally, i do not like array and indexOf use here. Using a rank counter of [0 to 15] would be a little better.
Related
I'm a current software development student looking to get some help with my latest self-learning project.
My coworkers and I play Settlers of Catan, so a couple of weeks ago I thought it would be a neat idea if I could make a site that would tell a person what they can buy with the resource cards they have in their hand.
For those who don't know, the basic Catan purchases are as follows:
Building
Cost
City
2 wheat, 3 ore
Settlement
1 wood, 1 brick, 1 wool, 1 ore
Road
1 wood, 1 brick
Dev. Card
1 wool, 1 wheat, 1 ore
What I need to do is take a players hand, which has any number of resource cards, and run it through a function that will determine a list of possible options along with the cards left over.
As an example it should return something like:
Option 1: You can build 1 City, 1 Road. 1 Sheep left over.
Option 2: You can build 1 Settlement. 3 Ore and 1 Wheat left over.
The function that I am currently using in JavaScript is shown below:
/* Below dictionary is updated via HTML inputs
Also sheep == wool, ignore the lack of using the accurate card name
*/
var resources = { 'wood': 1, 'brick': 1, 'sheep': 1, 'wheat': 2, 'ore': 3 };
/* Takes resources (your hand), then tries to determine what
the player can build with that hand */
function basicBuilder(resources) {
var buildDict = { 'roads': 0, 'settlements': 0, 'cities': 0, 'dcards': 0 }
while (resources['wheat'] >= 2 && resources['ore'] >= 3) {
resources['wheat'] -= 2;
resources['ore'] -= 3;
buildDict['cities'] += 1;
}
while (resources['wood'] >= 1 && resources['brick'] >= 1 && resources['sheep'] >= 1 && resources['wheat'] >= 1) {
resources['wood'] -= 1;
resources['brick'] -= 1;
resources['sheep'] -= 1;
resources['wheat'] -= 1;
buildDict['settlements'] += 1;
}
while (resources['sheep'] >= 1 && resources['wheat'] >= 1 && resources['ore'] >= 1) {
resources['sheep'] -= 1;
resources['wheat'] -= 1;
resources['ore'] -= 1;
buildDict['dcards'] += 1;
}
while (resources['wood'] >= 1 && resources['brick'] >= 1) {
resources['wood'] -= 1;
resources['brick'] -= 1;
buildDict['roads'] += 1;
}
return buildDict;
}
This way would work fine if none of the buildings shared resources, but since some buildings use the same cards it doesn't suit my purpose. For example if you use the resources that I supplied, buildDict['settlements'] will equal 0 since all the wheat was already used up by the city.
There has to be a better and cleaner way to go about this, so my question to all of you is this:
How can I best determine the players possible build choices on their turn?
As a reminder, the input will be a hand of resources in dictionary form.
The output should be similar to:
Option {n}: You can build {x} Cities, {x} Settlements, {x} Roads, etc... with {x} cards leftover
Bonus points for any of the following:
Solutions that easily allow for additional buildings later on (expansion packs)
Solutions that can somehow accept resource trading (in Catan, 4 of a resource can be traded in for 1 of any other resource. If you own a port, this can be reduced to either 3 or 2 for one trades)
Solutions that will still allow for the display of what the leftover cards are
EDIT - Reworded the question to be more specific per bots request, very new to stackoverflow. See past question above
With a hand (dictionary) full of resource cards, what is a good JavaScript way to find all the possible combinations of buildings that could be purchased?
Each building could be purchased multiple times
There is no maximum or fixed hand size
When a resource is used for a building, it is removed from the hand
In the example below, I don't want to make a counter as a param. Rather, I just want to return '+ 1' each time so that what gets returned is the number of steps it takes. My issue lies with the base case. If I do return + 1, I get the correct number of steps plus one additional step so I tried just return but that delivers NaN. Is it even possible?
var numberOfSteps = function(num) {
if (num == 0) {
return;
} else {
if (num % 2 == 0) {
return 1 + numberOfSteps(num/2);
} else {
return 1 + numberOfSteps(num - 1);
}
}
};
edit : The goal is to track how many steps it takes to reduce a number to 0. If it's even, divide by 2 or else subtract by 1. Ultimately, I want to return the number of steps it takes for any given number to get reduced to 0 following those rules
I hope the point has gotten through in the long comment thread and other answers that return + 1 is equivalent to return (+1), that is, return the integer positive one. And since there are no steps to take once you've reached zero, +1 is the wrong answer. Similarly, a plain return is functionally equivalent to return undefined. But undefined is not a number, and you're going to run into problems if you later try to add 1 to it. So the solution from the comments or other answers to return the correct number of steps, which in this case 0, will fix your code.
I would like to point out another way to solve this, though:
const numberOfSteps = (n) =>
n <= 0
? 0
: 1 + numberOfSteps (n % 2 == 0 ? n / 2 : n - 1)
console .log (numberOfSteps (12))
There are superficial differences here from the other solutions, such as using an arrow function, using a conditional statement (ternary) rather than if-statements, and using <= 0 instead of < 0 to avoid possible infinite loops on negative numbers.
But the fundamental difference is that this code only has one recursive branch. I think this is a better match to the problem.
We can think of this as a function which answers "How many steps does it take to reach 0 from our input number if each step cuts even numbers in half and subtracts one from odd ones?" Well that logically leads to a base case (we're already at 0) so have to return 0, and a recursive one (we're at some positive integer) so have to add 1 to the total steps required from our next entry.
By doing this single recursive call and adding one to the result, we make it clearer what the recursion is doing.
If this is unclear, then this alternative might show what I mean:
const takeStep = (n) =>
n % 2 == 0 ? n / 2 : n - 1
const numberOfSteps = (n) =>
n <= 0
? 0
: 1 + numberOfSteps (takeStep (n))
Think you just need to return 0 when it's...zero.
var numberOfSteps = function(num) {
if (num == 0) {
return 0;
} else {
if (num % 2 == 0) {
return 1 + numberOfSteps(num/2);
} else {
return 1 + numberOfSteps(num - 1);
}
}
}
return + 1 maybe doesn't do what you think it does: it returns the number 1. + here means positive not negative, there is no addition or subtraction going on. It will also give you one too many steps.
return; by itself returns undefined, which when converted to a Number, translates to NaN, because, well, it's not a number.
Can anyone find out what is wrong with this code? I run the code on CodeWars and pass every test except one... sadly it does not display what the input was for that specific test so it is very difficult to figure it out.
Here are the challenge instructions:
The new "Avengers" movie has just been released! There are a lot of people at the cinema box office standing in a huge line. Each of them has a single 100, 50 or 25 dollars bill. A "Avengers" ticket costs 25 dollars.
Vasya is currently working as a clerk. He wants to sell a ticket to every single person in this line.
Can Vasya sell a ticket to each person and give the change if he initially has no money and sells the tickets strictly in the order people follow in the line?
Return YES, if Vasya can sell a ticket to each person and give the change. Otherwise return NO.
I found that the code works for ALL tests if I swap the check for amount50 >= 1 and amount25 >= 1 with the amount25 >= 3 but I am not sure WHY this works.
function tickets(peopleInLine){
let amount25 = 0;
let amount50 = 0;
let amount100 = 0;
for(let i = 0; i < peopleInLine.length; i++){
if(peopleInLine[i] === 100){
if(amount25 >= 3){
amount25 -= 3;
amount100++;
}else if(amount25 >= 1 && amount50 >= 1){
amount25 -= 1;
amount50 -= 1;
amount100++;
}else{
return "NO";
}
}
if(peopleInLine[i] === 50){
if(amount25 >= 1){
amount25--;
amount50++;
} else {
return "NO";
}
}
if(peopleInLine[i] === 25){
amount25++;
}
}
return "YES";
}
On Codewars you can put console.log statements (or equivalent statements in languages other than JavaScript) in your code to print the input parameters or other variables when you're having trouble figuring out what is going wrong in your code or with your assumptions about the input. I just did this with your code and saw that the test your code is failing on is [ 25, 25, 25, 25, 50, 100, 50 ], so that should show you exactly why your code would be failing when in response to getting a $100 you first try to return three $25s as change instead of checking for a $50 and $25 first -- you receive four $25s, one of which you give as change to the first person with a $50, but then because you give the remaining three $25s (rather than the $50 and one $25) as change to the person with the $100, you no longer have a $25 to make change for the last person's $50.
I have a list of numbers e.g 1-to 60, but not necessarily in increments of 1. E.g 1-40 in increments of 1, and 40-60 in increments of two.
I have another set of defined numbers (buckets) –e.g 2, 3, 5 , 10, 30, 50
I need to produce a two dimensional array with percentages of where each number from above (1 to 60) fits into which bucket.
to make this simpler: let’s say we have numbers 1 to 10, and buckets, 2, 3, 5, 10
I want my two dimensional array to look like this:
I can do this with a bunch of conditionals, but I think there’s a solution out there which I have not thought of and would be great if someone could shed some light!
I need to do this in JavaScript, but any language would help me to try and understand any solution more optimal than lots of if’s deciding where each number fits and then doing (6-5/10-5)=0.2 for each cell.
I’m trying to avoid hardcoding buckets, 2, 3, 5, 10 so that any set of buckets or numbers can do the job.
EDIT:
First of all, I'm sorry for the incomplete description - I was on my phone at the time and couldn't post on stackoverflow via a computer.
Both 1-10 and 2,3,5,10 represent years. Effectively, I'm trying to bucket each year from 1 to 10.
Year 1 goes 100% into Bucket 2 - I guess there isn't a specific
formula for this cell
Year 2 goes 100% into Bucket 2 - No specific formula either
Year 3 goes 100% into Bucket 3 - 3==3
Year 4 is split half between Bucket 3, and half between Bucket 5. The formula for this is: (Year 4 - Bucket 3)/(Bucket 5 - Bucket 3) = 0.5
Year 5 goes 100% into Bucket 5.
Year 6, goes 80% into Bucket 5, and 20% into Bucket 10. The formula for this is: 1-(6-5)/(10-5)=0.8 and for its neighbouring cell (6-5)/(10-5)
...and so on...
I hope this makes it clearer.
You could do something like this. It puts it in exactly the format you asked for in the post:
function bucketize(numberList, buckets) {
// buckets must contain values sorted from smallest to largest
var bucketized = [];
var i, j, lowBucket, difference, bucketSpan, ratio;
for (i=0; i<numberList.length; i++) {
bucketized[i]=new Array(buckets.length + 1);
bucketized[i][0]=numberList[i];
lowBucketIndex=null;
for (j=0; j<buckets.length; j++) {
if (lowBucketIndex === null && numberList[i] < buckets[j]) {
lowBucketIndex=j-1;
if (lowBucketIndex < 0) {
// this bucket gets it all
bucketized[i][j+1]=1;
} else {
//divide this value between buckets
difference = numberList[i] - buckets[lowBucketIndex];
bucketSpan = buckets[j] - buckets[lowBucketIndex];
ratio=difference/bucketSpan;
bucketized[i][lowBucketIndex+1] = 1-ratio;
bucketized[i][j+1] = ratio;
}
} else {
bucketized[i][j+1]=0;
}
}
if (lowBucketIndex === null) {
bucketized[i][buckets.length] = 1;
}
}
return bucketized;
}
var buckets = [2,3,5,10];
var numberList=[1,2,3,4,5,6,7,8,9,10];
var bucketized = bucketize(numberList, buckets);
var i;
for (i=0; i<bucketized.length; i++) {
console.log(bucketized[i].join(','));
}
Here's a fiddle.
I have a canvas game which calls a function incScore every time an action is performed in the game to increase the score.
Inside incScore I have a few if statements to draw a particular image to represent a level number on the canvas.
I also want to have a sound play once per level up. The way I've gone about things the lvlup sound will play every time the score matches the if statement.
Can anyone please help me get this so that the sound will only play once when the level changes and not again until the next level change? I'm also mention I'm using jQuery incase it has anything that could help me.
incScore(); //everytime an action in the game causes the score to increase
function incScore(){
if (scoreTotal < 500){
lvlimg = "L01";
drawLevel(lvlimg);
lvlupSound();
}
else if (scoreTotal > 500 && scoreTotal < 1000){
lvlimg = "L02";
drawLevel(lvlimg);
lvlupSound();
}
else{
lvlimg = "L03";
drawLevel(lvlimg);
lvlupSound();
}
}
You could shorten your function and use a semi static property to save the state. Using that, you can compare the current level to the previous and play a sound if they differ.
function incScore(){
incScore.level = incScore.level || 'L0'; //< initialize property
lvlimg = "L0" + scoreTotal < 500 ? 1 : scoreTotal < 1000 ? 2 : 3;
drawLevel(lvlimg);
if (incScore.level!=='L0' &&
incScore.level !== lvlimg) { lvlupSound(); };
// ^compare local level to current
incScore.level = lvlimg;
// ^ update local level
}
[edit, based on comment] The third line is a so called ternary, or conditional operator. See MDN. You can use more conditions.
To avoid playing a sound before the score has reached a first level, you could use
if (incScore.level!=='L0' && incScore.level !== lvlimg).
I've created a mockup jsFiddle
A simple solution could be comparing the current level to the old one, to detect when the level changed:
function scoreToLevel(score)
if(score < 500){
return 1
}else if (score < 1000){
return 2
}else{
return 3
}
}
function incScore()
var next_level = scoreToLevel(scoreTotal)
if(next_level !== current_level){
lvlimg = "L0" + next_level;
drawLevel(lvlimg)
lvlupSound()
}
}
The easiest solution is to factor the sound out of those if statements. If the levels are nice and regular like that(every 500 points) and the points always increase in a way that you will always land exactly on an even multiple of 500 when you level up, something like this should do:
if(scoreTotal % 500 === 0 && scoreTotal < 1001)
{
lvlupSound();
}
If you won't always land directly on the gate to the next level(maybe the player can earn anywhere between 1 and 15 points at a time) then you should be able to get by using something along the lines of this before you increment the player's score:
if( (scoreTotal % 500) > ((scoreTotal + increment) % 500)
{
lvlupSound();
}
if your level boundries are not regular like that obviously it gets a little bit more complex, but that should get you started.
That is because you have the in every statement for every score (which means from 0 to infinite).
You will need to write inner if statements such as;
if (scoreTotal < 500){
lvlimg = "L01";
drawLevel(lvlimg);
if(scoreTotal x times of each the level) // That means for each level completed
{
lvlupSound();
}
}
If your score increment is only 1, then only play the tone when the score equals the threshold for a new level.
If they can increase their score by more than 1, then you could pass the number of points in and check the score before and after to see if the numbers fall on each side of the threshold.
If that still doesn't work, some more info on the "level" and points would be appreciated.
Try something like this (demo):
var scoreTotal,
lastLevel = 0,
levels = [500, 1000, 2500, 5000, 10000, 25000, 50000, 75000],
currentLevel = 0,
lvlImg;
function incScore() {
while (scoreTotal > levels[currentLevel]) {
currentLevel++;
}
if (lastLevel !== currentLevel) {
lastLevel = currentLevel;
// gives a two digit number with a leading zero
lvlImg = ('0' + currentLevel).slice(-2);
drawLevel("L" + lvlimg);
lvlupSound();
}
}
Then you can easily add additional levels by adding the score cutoff to the levels variable.