I'm working on a Word Search puzzle game, and I'm struggling with generating the grid with letters. At the moment, I'm able to generate the grid, but the performance is very slow (way too slow for a decent usage, it can take up to 30-40 seconds before generating the grid).
To fit words into the grid, I'm using a recursive function that tries to fit the last word from a given list, then the one before it, etc, and if it doesn't fit, backtracks to the previous word and changes its place, then tries again. I guess I'm not doing this right, because it keeps going back and forth.
I tried to use weighed probabilities for directions, so that the generating is made in a "smarter" way, but I'm not getting any results yet.
My question :
How can I optimize this code to make it more performant and reliable? I accept any suggestions, even if it makes me do a lot of changes (if there is an iterative solution for example instead of a recursive one, or if I'm not reasoning correctly in the function...).
Here is the function:
function tryWord(grid, wordList, index, gridLength){
var valid = false;
var clear = false;
if(index==(wordList.length-1)){
/* Clear grid for current index */
for(var j=0; j<gridLength.x; j++){
grid[index][j] = [];
for (var k=0; k<gridLength.y; k++){
grid[index][j][k] = '';
}
}
var nbIterations = 0;
// Try current word
while(valid == false){
// Impossible to resolve this grid
if(nbIterations>500){
return false;
}
nbIterations++;
var initX = Math.floor(Math.random() * gridLength.x); // x coord of first letter of the word
var initY = Math.floor(Math.random() * gridLength.y); // y coord of first letter of the word
var direction = Math.floor(Math.random() * 8); // direction of the word (0=top-left; 1=top; 2=top-right; 3=right; 4=bottom-right; 5=bottom; 6=bottom-left; 7=left)
valid = checkValidWord(wordList[index].length, initX, initY, direction, gridLength);
}
clear = checkClearWord(wordList[index], initX, initY, direction, grid[index]);
if(!clear){
return false;
}
var x = initX;
var y = initY;
for(var j=0; j<wordList[index].length; j++){
grid[index][x][y] = wordList[index].charAt(j);
switch(direction){
case 0:
x--;
y--;
break;
case 1:
y--;
break;
case 2:
x++;
y--;
break;
case 3:
x++;
break;
case 4:
x++;
y++;
break;
case 5:
y++;
break;
case 6:
x--;
y++;
break;
case 7:
x--;
break;
default:
break;
}
}
return grid;
}
else if(index!=(wordList.length-1)){
var emptyGrid = true;
for(var p=0; p<grid[index].length; p++){
for(var q=0; q<grid[index][p].length; q++){
if(grid[index][p][q]!=''){
emptyGrid = false;
}
}
}
if(emptyGrid || $scope.nbIterations>50){
$scope.nbIterations=0;
grid = tryWord(grid, wordList, index+1, gridLength);
}
/* Prepare grid for current index */
grid[index] = grid[index+1];
if(grid!=false){
// Try current word
while(valid == false){
var initX = Math.floor(Math.random() * gridLength.x); // x coord of first letter of the word
var initY = Math.floor(Math.random() * gridLength.y); // y coord of first letter of the word
var direction = Math.floor(Math.random() * 8); // direction of the word (0=top-left; 1=top; 2=top-right; 3=right; 4=bottom-right; 5=bottom; 6=bottom-left; 7=left)
valid = checkValidWord(wordList[index].length, initX, initY, direction, gridLength); // Check that word fits in the grid
}
clear = checkClearWord(wordList[index], initX, initY, direction, grid[index]);
// If word is obstructed by other words
if(!clear){
$scope.nbIterations++;
return tryWord(grid, wordList, index, gridLength); // Try again
}
else{
var x = initX;
var y = initY;
for(var j=0; j<wordList[index].length; j++){
grid[index][x][y] = wordList[index].charAt(j);
switch(direction){
case 0:
x--;
y--;
break;
case 1:
y--;
break;
case 2:
x++;
y--;
break;
case 3:
x++;
break;
case 4:
x++;
y++;
break;
case 5:
y++;
break;
case 6:
x--;
y++;
break;
case 7:
x--;
break;
default:
break;
}
}
return grid;
}
}
else{
return false;
}
}
}
Parameters of the function :
grid : at the beginning, just a 10x11x11 array filled with '' string.
wordList : an array of words to put in the grid
index : 0 when calling the function for the first time, then it's used to check how deep we are in the wordList
gridLength : an array : {x:11, y:11}, giving the grid length
Some more precisions about the function :
The function checkValidWord checks if a given word going to a given direction fits in a grid with a given size. Returns true or false.
The function checkClearWord checks if a given word going to a given direction fits in the grid with other words already in it (no obstruction etc). Returns true or false.
The function tryWord is supposed to output a 3-dimensional array of size [wordList.length;11;11]. I then use the grid[0] as my 2 dimensional grid for the game.
Related
I recently started to study javascript
I'm currently watching Javascript course in Udemy.
While code challenging, There's something I cant get it about parameter of 'switch'
let john = {
fullName: 'John Smith',
bills: [124, 48, 268, 180, 42],
calcTips: function() {
this.tips = [];
this.finalValues = [];
for (let i = 0; i < this.bills.length; i++) {
let percentage;
let bill = this.bills[i]
switch (bill) { // If I put parameter as 'bill' variation, The result is only defalut.
case bill < 50:
percentage = 0.2;
break;
case bill >= 50 && bill < 200:
percentage = 0.15;
break;
default:
percentage = 0.1;
}
this.tips[i] = bill * percentage;
this.finalValues[i] = bill + bill * percentage;
}
}
}
john.calcTips();
console.log(john);
However
let john = {
fullName: 'John Smith',
bills: [124, 48, 268, 180, 42],
calcTips: function() {
this.tips = [];
this.finalValues = [];
for (let i = 0; i < this.bills.length; i++) {
let percentage;
let bill = this.bills[i]
switch (true) { // If I put 'ture' as a parameter, It work's. Why?
case bill < 50:
percentage = 0.2;
break;
case bill >= 50 && bill < 200:
percentage = 0.15;
break;
default:
percentage = 0.1;
}
this.tips[i] = bill * percentage;
this.finalValues[i] = bill + bill * percentage;
}
}
}
john.calcTips();
console.log(john);
I've searched in google about this problem.
But I can't find specific way to solve this issue.
I'll appreciate your help.
Switch statements compare values strictly. Which means that you can compare for the exact value of the switch variable.
switch (x) {
case 1: console.log(1); break;
case 2: console.log(2); break;
}
You can do a trick however if you want to make the switch statement work on numerical ranges like this:
var x = this.dealer;
switch (true) {
case (x < 5):
alert("less than five");
break;
case (x < 9):
alert("between 5 and 8");
break;
case (x < 12):
alert("between 9 and 11");
break;
default:
alert("none");
break;
}
The implementation works on the strict comparison of booleans. The switch statement is for true and will match wherever the case is true.
Related question: Switch on ranges of integers in JavaScript
The switch statement tests the value of a variable and compares it with multiple cases. Once the case match is found, a block of statements associated with that particular case is executed. So in this case you switching on a constant value.
More detail :
javascript: using a condition in switch case
I'm not sure if this has been asked already, but my code is a bit different than the usual snake code. I basically have some parts working already, which are:
Generate and render the board
Snake movement (with no eating and dying)
Generate a fruit randomly inside the board
Generate a fruit randomly again after being eaten
My issue now is to make the snake update and re-render itself inside the tick() every time it eats a fruit.
/**
* #returns {TickReturn}
*/
tick() {
// very simple movement code which assumes a single length snake
// no eating or dieing implemented yet.
let oldPosition = { ...this._snake[0]
};
switch (this._direction) {
case Direction.Up:
this._snake[0].y -= 1;
break;
case Direction.Down:
this._snake[0].y += 1;
break;
case Direction.Left:
this._snake[0].x -= 1;
break;
case Direction.Right:
this._snake[0].x += 1;
break;
}
if (this.eating(this._fruit)) {
this._fruit = this.nextFruitFn(); // update the position of the fruit
this.update(); // nothing's being done yet
}
return {
gameOver: this._isGameOver,
eating: false, // currently static, not doing anything yet
changes: [{
position: oldPosition,
tileValue: Tiles.Empty
},
{
position: this._snake[0],
tileValue: Tiles.Snake
},
{
position: this._fruit,
tileValue: Tiles.Fruit
}
]
};
}
eating(pos) {
let hasEaten = false;
this._snake.map((s, idx) => {
// if snake and position merged indices,
// then fruit has been eaten
hasEaten = (s.y === pos.y && s.x === pos.x) ? true : false;
if (hasEaten) {
this._board[pos.x][pos.y] = Tiles.Empty;
// make sure to clear the tile after eating
}
});
return hasEaten;
}
I am lost with two things here:
How do you update the snake array? Do you simply get the current x,y index and reduce both of them by 1? { y: y-1, x: x-1 }?
Once I successfully added the new array inside the snake, how do I properly render it in the TickReturn object? Currently, it is simply updating this._snake[0] at a position.
How do I update the switch case that will include the succeeding parts of the snake? At the moment, it can only move itself fixed at index 0, like so:
switch (this._direction) {
case Direction.Up:
this._snake[0].y -= 1;
break;
case Direction.Down:
this._snake[0].y += 1;
break;
case Direction.Left:
this._snake[0].x -= 1;
break;
case Direction.Right:
this._snake[0].x += 1;
break;
}
Is this also a similar approach if I start considering the walls? Here's a screenshot of what I have so far:
function updateGrid() {
//this for loop moves the matrix and sets a position
for (var row = row_start; row < row_end; row+=5) {
for (var i = 0; i < 5; i++) {
//these for loops call each individual rect on the grid by a unique id.
d3.select("#x-"+row+"y-"+(i*20))
.transition()
.style("fill", function(d) {
var alt = 0;
switch (i) {
//ignore this switch statement. It's just reversing the i because the tag is backwards.
case 0: alt = 4; break;
case 1: alt = 3; break;
case 3: alt = 1; break;
case 4: alt = 0; break;
default: alt = 2; break;
}
//color_matrix is an object. row is the key, and alt is the position of the array that is the key value.
if (color_matrix[row][alt] == 0) return colors[0];
if (color_matrix[row][alt] == 1) return colors[1];
if (color_matrix[row][alt] == 2) return colors[2];
if (color_matrix[row][alt] == 3) return colors[3];
if (color_matrix[row][alt] >= 4) return colors[4];
})
.duration(750);
}
}
//this moves the matrix by one column. console.log() shows that it works.
row_start += 5;
row_end += 5;
}
var run = window.setInterval(function() { updateGrid() }, 5000);
So I cannot seem to figure this one out. A few things to note first:
1. The colors work perfectly in the rect initialization.
2. The transition works correctly the first time as well.
3. The function is being called correctly, because I console.log() tested it.
4. The matrix/object is also cycling correctly.
5. the position of duration doesn't seem to matter.
The only thing that doesn't work correctly is the color change. This update function will be called every five seconds to change the color of the rects. However, it doesn't seem to work. Is there something wrong with the way the transition is being called?
Turns out the matrix wasn't cycling correctly after all. The for loop was trying to cycle rect where there wasn't any, and to fix it I added a separate increment.
I'm currently working on an idle game and have encountered a problem.
I'm trying to create a function that will lessen the value of 1 of 6 variables (each represents a type of worker) a number of times. However, if one of these 6 variables is at 0, I need it to pick one of the other 5, and I can't get that to work.
With fiddling around, I have gotten this:
function killWorker(amount){
var job;
for (var i=0;i<amount;i++){
job = Math.floor((Math.random()*6)+1);
switch (job){
case 1:
if(unass_workers>0){
unass_workers -= 1;
}else{
i-=1;
}
case 2:
if(farm_workers>0){
farm_workers -= 1;
}else{
i-=1;
}
break;
case 3:
if(tree_workers>0){
tree_workers -= 1;
}else{
i-=1;
}
break;
case 4:
if(metMine_workers>0){
metMine_workers -= 1;
}else{
i-=1;
}
break;
case 5:
if(golMine_workers>0){
golMine_workers -= 1;
}else{
i-=1;
}
break;
case 6:
if(paper_workers>0){
paper_workers -= 1;
}else{
i-=1;
}
break;
default:
console.log("error: killWorker() didn't work properly");
break;
}
}
}
This worked when I did low amounts, but when I increased the amount higher the whole thing crashed. I'd be quite happy to drastically change the function if it would work better, and I am using jquery too if that could help get an easier or more effective solution.
As i said, what probably happens is that amount is larger than the sum of X_workers, resulting in an infinite loop.
A fix that keeps your logic correct would be to check that enough workers can be killed:
function killWorker(amount){
if (amount < unass_workers + farm_workers + ...) return "Genocide";
var job;
for (var i=0;i<amount;i++){
...
}
A better way to organize your data structures could be:
var workerNames = [ 'unass', 'farm', 'tree', 'metMine', 'golMine', 'paper' ];
var workers = {
unass : 23,
farm : 45,
tree : 6,
metMine : 99,
golMine : 3,
paper: 43
}
function getRandomLiveJobName() {
var jobs = workerNames.filter(function(name) {
return workers[name] > 0;
});
return jobs[Math.floor(Math.random()*jobs.length)];
}
function killWorkers(amount) {
for (;amount--;) {
var job = getRandomLiveJobName();
if (!job) return "Genocide";
workers[job]--;
}
}
killWorkers(150);
console.log(workers); // { farm: 0, golMine: 0, metMine: 64, paper: 5, tree: 0, unass: 0 }
Of course it could be optimized by not creating a new array each time you need a random live job by updating a single array inside killWorkers, but i think it's easier to read now.
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Javascript dynamic variable name
I have variables being passed from an onClick event to a JavaScript function. There are four variables in total: two that tell the direction and two that tell the speed change. I want the function to evaluate which direction was chosen (either h_ or v_, for horizontal and vertical) and then apply the necessary speed (either faster or slower).
Right now, I do this successfully by first evaluating the direction and calling a different changeSpeed function depending on the direction which was chosen.
What I would like to do is combine these functions. In the example, $(direction + "speed") is meant to become either h_speed or v_speed.
Is JavaScript equipped to do this? (sincerely, miguel)
var h_speed = 10;
var v_speed = 10;
function changeSpeed(speed, direction){
var direction = direction;
switch (speed)
{
case 'slower':
$($direction + "speed") = $($direction + "speed")*2;
break;
case 'faster':
$($direction + "speed") = $($direction + "speed")/2;
break;
}
}
Here are two versions of my working code:
VERSION 1
var h_speed = 10;
var v_speed = 10;
function identifyDirection(speed, direction){
switch (direction)
{
case 'vertical':
v_changeSpeed(speed);
break;
case 'horizontal':
h_changeSpeed(speed);
break;
}
}
function h_changeSpeed(speed){
switch (speed)
{
case 'slower':
h_speed = h_speed*2;
break;
case 'faster':
h_speed = h_speed/2;
break;
}
}
function v_changeSpeed(speed){
switch (speed)
{
case 'slower':
v_speed = v_speed*2;
break;
case 'faster':
v_speed = v_speed/2;
break;
}
}
VERSION 2
/**
* the changeSpeed functions' arguments
* are placed directly in the function that
* determines whether horizontal or vertical
* speed is changing.
*
*/
function changeSpeed(speed, direction){
switch (direction)
{
case 'vertical':
switch (speed)
{
case 'slower':
v_speed = v_speed*2;
break;
case 'faster':
v_speed = v_speed/2;
break;
}
break;
case 'horizontal':
switch (speed)
{
case 'slower':
h_speed = h_speed*2;
break;
case 'faster':
h_speed = h_speed/2;
break;
}
break;
}
}
Variables are made properties of a variable object. The only variable object you can access by name is the global variable object (this in a global context or window in a browser). So for global variables you could do:
function hSpeed() {...}
function vSpeed(){...}
// Set direction
var direction = 'h';
// Call related function
window[direction + 'Speed']();
However, you can't do that in a function execution context (because ECMA-262 explicitly denies access to function execution and variable objects), you need to make the "variable" a property of an object that you access the same way (i.e. using square bracket notation):
var lib = {};
var lib.hSpeed = function(){...};
var lib.vSpeed = function(){...};
// Set direction
var direction = 'h';
// Call related function
lib[direction + 'Speed']();
Put the 2 variables in a single object like:
var directions = {
horizontal: 1,
vertical: 1
}
Then you'd be able to take the direction out of the arguments and match the child of the object:
function changeSpeed(speed, direction) {
//operate on diections[direction]
}
As far as changing the speed you could do a similar thing with functions in an object, but in your case I'd just suggest using another data structure since the logic doesn't change, only the parameter:
var speedFactor = {
faster: 2,
slower: .5
}
then you'd be able to do everything with:
function changeSpeed(speed, direction) {
directions[direction] = directions[direction] * speedFactor[speed]
}
There are certainly better ways to do what you wanna achieve, but if you wanna have the same thing (note that you shouldn't use global variables, you can use function scoping to make them private, but that's another topic).
var speed = {
h: 10,
v: 10
};
function changeSpeed(speedChange, direction) {
switch (speedChange) {
case 'slower':
speed[direction] *= 2;
break;
case 'faster':
speed[direction] /= 2;
break;
}
}
Now you can change the speed by calling, for example:
changeSpeed("slower", "h");
and access that speed by speed.h or speed.v
Okay...
Tricky but:
//Global namespace
var speeds = {};
speeds['h_speed'] = 10;
speeds['v_speed'] = 10;
function changeSpeed(speed, direction){
var dir = direction.substring(0,1);
var sp = (speed === 'slower') ? 0.5 : 2;
//Still accessible from inside your function
speeds[dir + '_speed'] = speeds[dir + '_speed'] * sp;
}
Will do the work.