I am creating a minesweeper game for a javascript project and have run into a problem that I can't get my head round. When you click on an empty cell (one which does not have any mines around it and so does not display a number) in the minesweeper game for those who don't know, this will reveal the whole block of empty cells that are neighbouring eachother, stopping when the "wall of numbers" containing these empty blocks is found. Example below:
[1]http://datagenetics.com/blog/june12012/hole.png
This requires a recursive function to figure out which blocks are to be revealed. My code at the moment only reveals the block that is clicked on:
function revealGridContents()
{
switch (positionClickContents)
{
case 0:
ctx.drawImage(clickedImage, (xClick*20), (yClick*20));
break;
case 1:
ctx.drawImage(num1Image, (xClick*20), (yClick*20));
break;
case 2:
ctx.drawImage(num2Image, (xClick*20), (yClick*20));
break;
case 3:
ctx.drawImage(num3Image, (xClick*20), (yClick*20));
break;
case 4:
ctx.drawImage(num4Image, (xClick*20), (yClick*20));
break;
case 5:
ctx.drawImage(num5Image, (xClick*20), (yClick*20));
break;
case 6:
ctx.drawImage(num6Image, (xClick*20), (yClick*20));
break;
case 7:
ctx.drawImage(num7Image, (xClick*20), (yClick*20));
break;
case 8:
ctx.drawImage(num8Image, (xClick*20), (yClick*20));
break;
};
};
The number passed into the switch statement is the value of the data in the array grid[xClick][yClick]; eg a 4 symbolises a block with 4 mines around it, so the image for 4 will be displayed.
Case 0 is the case that a blank block is clicked and so the code for this needs modifying but I really can't think what to do.
From what I can understand, I will need to call the revealGridContents(); function from case 0, but passing in new values for xClick and yClick (the x and y values of the array position) for each square that I want to check.
Any help shedding the light on what I need to do next would be greatly appreciated!
Without knowing slightly more of your program it's hard to give you an exact solution. You'll probably need a separate function to do this, as just using the same function will reveal everything (which is obviously not how the game works). You'll also need some way of keeping track of revealed cells, otherwise you'll get into a loop (I'm assuming this is stored in another 2d array revealed[x][y]).
You probably want to do something like this (I haven't tested this so there may be errors in it - apologies):
function revealGridContents(){
switch (positionClickContents){
case 0:
ctx.drawImage(clickedImage, (xClick*20), (yClick*20));
checkAdjacentCells(xClick, yClick);
break;
...
}
}
function checkAdjacentCells(x,y){
var cellsToCheck = [
[x,y+1],
[x,y-1],
[x+1,y],
[x-1,y]];
var x,y;
for(var i=0; i<=cellsToCheck.length; i++){
x = cellsToCheck[i][0];
y = cellsToCheck[i][1];
if(!revealed[x][y] && grid[x][y] == 0){
ctx.drawImage(clickedImage, x*20, y*20);
checkAdjacentCells(x,y);
}
}
}
Just as a general advice, you need a better separation between the model of your game and the UI.
Here is the begining of my interpretation of the minesweeper game:
function init() {
var i,j; // indexes
map = []; // global map, because i'm lazy
for (i=0; i<10; i++) {
var row = [];
for (j=0; j<10; j++)
row.push({
bomb : Math.round(Math.random()-0.4), // set bombs randomly, change to your correct ratio
revealed : false, // nothing is visible at start
count : 0 // counts will be computed after all the map is created
});
map.push(row);
}
// set adjacent bomb counts
for (i=0; i<10; i++)
for (j=0; j<10; j++) {
if (map[i-1] && map[i-1][j-1] && map[i-1][j-1].bomb) map[i][j].count++;
if (map[i-1] && map[i-1][j] && map[i-1][j].bomb) map[i][j].count++;
if (map[i-1] && map[i-1][j+1] && map[i-1][j+1].bomb) map[i][j].count++;
if (map[i] && map[i][j-1] && map[i][j-1].bomb) map[i][j].count++;
if (map[i] && map[i][j+1] && map[i][j+1].bomb) map[i][j].count++;
if (map[i+1] && map[i+1][j-1] && map[i+1][j-1].bomb) map[i][j].count++;
if (map[i+1] && map[i+1][j] && map[i+1][j].bomb) map[i][j].count++;
if (map[i+1] && map[i+1][j+1] && map[i+1][j+1].bomb) map[i][j].count++;
}
}
function print() { // uses console to display instead of canvas
var output = '\n';
for (var i=0; i<10; i++) {
for (var j=0; j<10; j++) {
var item = map[i][j];
output += (item.revealed ? item.count : 'x') + ' ';
}
output += '\n';
}
console.log(output);
}
function click(x,y) {
reveal(x,y);
print(map);
}
function reveal(x,y) {
// break early if click is invalid (invalid clicks are generated)
if (x < 0 || x > 9 || y < 0 || y > 9 || map[x][y].revealed) return;
// mark the square as clicked
map[x][y].revealed = true;
if (map[x][y].bomb) { // losing click
console.log('You lost');
} else if (map[x][y].count === 0) { // click on 0 adjacent bombs
reveal(x-1, y);
reveal(x, y-1);
reveal(x, y+1);
reveal(x+1, y);
}
}
init();
console.log('First print');
print();
console.log('Click 1,3');
click(1,3);
The difficult part is in the click() function.
Try this demo (click 'run with JS' several times until you don't lose and hit a 0):
http://jsbin.com/iqeganU/1/edit
Related
I'm trying to write a game of Black Jack but I need some help trying to figure out how to switch the value of the Ace card between 1 & 11 as needed. This is the function that I have so far.
function cValue(card){
if (typeof(card) === "string"){
switch(card){
case 'J':
case 'Q':
case 'K':
return 10;
break;
case 'A':
return 11;
break;
}
}
else return card;
}
Quite simply: it's at another point in the logic that whether the Ace needs to be 1 or 11 needs to be determined...
But adding the ability to choose in that particular function whether it returns 1 or 11 is simple enough:
function cValue(card, aceIs1)
{
if (typeof card === "string") {
switch(card) {
case 'J':
case 'Q':
case 'K':
return 10;
case 'A':
return aceIs1 ? 1 : 11;
}
}
else return card;
}
That way you have an optional argument you can send to make the ace return 1.
cValue('A'); // -> 11
cValue('A', false); // -> 11
cValue('A', true); // -> 1
However, whether or not that is useful to your program depends on how you're writing your game.
You most likely need to do a lot more planning/structuring based around the fact that after counting the points you need to determine if there's aces in the hand and subtract 10 for each ace until you're under 21, and only if there's not enough aces to get under 21, THEN call it a bust.
Something like, if you have an array of card values like var hand = [9, 'A', 'A'] you could have a function that calculates the total hand like follows:
function calculateHand(hand)
{
var total = 0, aces = 0, card;
for (var i = 0, ii = hand.length; i < ii; i++)
{
card = hand[i];
if (typeof card === 'number') { total += card; }
else if (card === 'A') { total += 11; aces++; }
else { total += 10; }
}
while (total > 21 && aces > 0)
{ total -= 10; aces--; }
return total;
}
calculateHand(['A', 8]); // -> 19
calculateHand(['A', 8, 'A']); // -> 20
calculateHand(['A', 8, 'A', 'A']); // -> 21
I got a function that should return a result depending on a dealed Black Jack hand. I used a switch statement for this, eventhough I'm not sure you could use multiple switch in a function. Anyhow I got an error saying 'missing ; before statement' after the first 'result' text in the first 'case'. This code is what I have been taught so I'm not sure where I did go wrong. Could you please give me a hint or anything, please? Refards, Thomas.
function printResult(playResult, dealResult) {
var text = "";
switch(playResult) {
case (playResult == 21) : result "black jack";
break;
case (playResult > 21) : result "busted";
break;
case (playResult < 21) : result "safe";
break;
}
switch(dealResult) {
case (dealResult < 17) : result "safe";
break;
case (dealResult == 17 && < 21) : result "stop";
break;
case (dealResult == 21) : result "black jack";
break;
}
return result;
}
var result = "Player: " + playResult + ", Dealer: " + dealResult;
ANSWER = (printResult(5+9+10, 6+3+7));
Maybe you want result to be a variable assigment?
result = "black jack"
or maybe a return?
return "black jack"
It looks like you're trying to do "if logic" in the case statement; that's not how a switch works. A switch is going to take the value of dealResult and do a straight compare.
switch(dealResult)
{
case 21: //dealResult MUST be 21
result = 'blackjack!'
break;
case 20: //dealResult MUST be 20
//etc..
break;
case >20: //not valid, will break
}
As far as I know, if you need to do > and < compares, you should be using an if -> else block.
You have to remove all of the playResult variables inside of the switch block.
In addition, switch does not support higher than or lower than so a if is better suited for this situation.
function printResult(playResult, dealResult) {
var result;
if(playResult == 21) {
result = "black jack";
} else if(playResult > 21) {
result = "busted";
} else {
result = "safe";
}
if(dealResult < 17) {
result = "stop";
} else if(dealResult == 17 && dealResult < 21) {
result = "stop";
} else {
result = "black jack";
}
return result;
}
var result = "Player: " + playResult + ", Dealer: " + dealResult;
ANSWER = (printResult(5+9+10, 6+3+7));
This is a lot closer, but there is still problems with your logic.
Looking at your function, even if you get the switch working i don't think the logic is what you're aiming it to be.
If the purpose of the function is tell you who won the game, ergo the result as your name implies then you need to first put the rules down.. lets just for readability write it in pseudo..
Where either player has blackjack check the following
Player Blackjack to Dealer Blackjack = Push (Draw)
Player Blackjack to Dealer Anything = Player Win
Player Anything to Dealer Blackjack = Dealer Win
Then check if one is bust, to win a player must of held
Player > 21 = Dealer Win
Player <=21 and Dealer > 21 = Player Win
After that, we can do the simple checks.
Player == Dealer = Push
Player > Dealer = Player Win
Player < Dealer = Dealer Win
You can't really use a switch statement effectively to do this. You 'can' use it but for something like this, if else will suffice.
function blackJackWinnerIs(player, dealer)
{
if((player == 21) || (dealer==21))
{
if(player > dealer)
return "Player";
else if (player < dealer)
return "Dealer";
else
return "Push";
}
else if(player > 21)
return "Dealer";
else if(dealer > 21) // already checked if player<=21
return "Player";
else if(player > dealer)
return "Player";
else if(dealer > player)
return "Dealer";
else return "Push";
}
var hands = [
{player:21,dealer:21},
{player:20,dealer:21},
{player:21,dealer:17},
{player:22,dealer:17},
{player:17,dealer:22},
{player:19,dealer:18},
{player:18,dealer:20},
{player:20,dealer:20}
];
for(var i=0; i<hands.length;i++)
{
var winner = blackJackWinnerIs(hands[i].player,hands[i].dealer);
console.log("Player:",hands[i].player," Dealer:",hands[i].dealer," => Winner:",winner);
}
This is functional.. but it won't protect you from bad inputs e.g. if you accidentally passed in two nulls you would get a "push" result when really you should either send out an error or void the game... but thats all on you.
This question already has answers here:
Expression inside switch case statement
(8 answers)
Closed 8 years ago.
The evaluations in the console print in the second line seem correct, but the switch statement won't work. And I am not getting any errors.
for (var i = 0; i < 100; i++) {
console.log(i % 3 === 0, i % 5 === 0);
switch (i) {
case i % 3 === 0:
console.log(i, " by three");
break;
case i % 5 === 0:
console.log(i, " by five ");
break;
}
}
http://jsfiddle.net/vL4omdxs/
As the comment said, that's not how you use switch/case.
You evaluate the condition in switch, then create different behaviours using cases.
Here is your code slightly modified (actually not so slightly, there's a small math twist):
var res = document.getElementById('r');
for (var i = 0; i < 100; i++) {
//console.log(i % 3 === 0, i % 5 === 0);
switch (i % 15) {
case 0:
r.innerHTML += i + " by three and five<br>";
break;
case 3:
case 6:
case 9:
case 12:
r.innerHTML += i + " by three<br>";
break;
case 5:
case 10:
r.innerHTML += i + " by five<br>";
break;
}
}
<div id="r"></div>
Just a hint (offtopic, but might help): switch/case is not the best approach for the 3/5 problem. See how much simpler it looks using ifs:
var res = document.getElementById('r');
for (var i = 0; i < 100; i++) {
res.innerHTML += "<br>" + i + ": ";
if (i % 3 == 0) {
res.innerHTML += "by three ";
}
if (i % 5 == 0) {
res.innerHTML += "by five ";
}
}
<div id="r"></div>
Case expressions are tested for strict equality so you need to change the switch from switch (1) to switch (true). However note that only one of the case blocks will be executed.
That's not the way to do the switch statement. It must be:
switch (i % 3) {
case 0:
...
break;
case 1:
...
break;
}
The expression within switch brackets is compared with the expression after case keyword. Take your code as example:
for (var i = 0; i < 100; i++) {
console.log(i % 3 === 0, i % 5 === 0);
switch (i) {
case i % 3 === 0: // if (i) equals (i % 3 === 0), run this branch
console.log(i, " by three");
break;
case i % 5 === 0: // if (i) equals (i % 5 === 0), run this branch
console.log(i, " by five ");
break;
}
}
And please remember, "equal" here means ===. Since your case expressions all return boolean, they'll never be equal to your i, which is a number.
I'm trying to make a function that changes the value of the attribute x or y of the dot object four times, each time only if two conditions are met. The variable permissionSomething has to be 1, and x or y has to be more or less then a certain number, because in this case it is used for a moving dot on a canvas, which has to stay within the canvas. PermissionSomething is set to 1 if a certain key is pressed down, and changes back to the default value of -1 if that key goes up again.
var permissionLeft = -1;
var permissionRight = -1;
var permissionUp = -1;
var permissionDown = -1;
function update()
{
switch (true)
{
case permissionLeft = 1 && dot.x > 29:
dot.x--;
break;
case permissionRight = 1 && dot.x < 841:
dot.x++;
break;
case permissionUp = 1 && dot.y > 29:
dot.y--;
break;
case permissionDown = 1 && dot.y < 541:
dot.y++;
break;
}
}
If I run this, even without pressing any key, it starts to move in some strange way, stops and starts moving back and forth. If I don't press any keys permissionSomething should never be 1 so it should never move. The same thing happens if I write four if-statements like this:
if (permissionLeft = 1 && dot.x > 29) {
dot.x--};
if (.....
I would like some advice on how to slim down this switch:
switch (lotUser | winningLot) {
case lotUser === winningLot[0]:
case lotUser === winningLot[1]:
case lotUser === winningLot[2]:
case lotUser === winningLot[3]:
case lotUser === winningLot[4]:
case lotUser === winningLot[5]:
case lotUser === winningLot[6]:
case lotUser === winningLot[7]:
console.log("You win!");
break;
default:
console.log("You do not win!");
break;
}
Instead of
case lotUser === winningLot[0]:
I wrote the script to be:
switch (lotUser | winnendLot) {
case lotUser === winnendLot[0|1|2|3|4|5|6|7]:
console.log("You win!");
break;
default:
console.log("You do not win!");
break;
}
I just don't know if this works the way I want it to work. It needs to check if the generated lotUser is equal to one of the values in an array (winningLot). If the lotUser equals one or more of the values in the winningLot array, it should output "You win!".
Could someone please confirm that my code does the description I gave?
What about Array.prototype.indexOf()?
if (winnedLot.indexOf(lotUser) !== -1) {
console.log("Won!");
}
else {
console.log("Lost!");
}
It searches the array for the first occurrence of the value stored in lotUser and returns its respective index.
Since you do not need to count the occurrences, this should be the best way.
If you want to count them, use a loop:
var count = 0;
for (var i=0, len=winnedLot.length; i<len; i++) {
if (winnedLot[i] === lotUser) {
count++;
}
}
You can simply use indexOf:
if(winningLot.indexOf(lotUser) >= 0) {
...
} else {
...
}
Well for starters you're using switch incorrectly. The value to compare goes in the switch(...) part, and the possible values are listed by each case ...:
Anyway, that aside, all you want is to check if lotUser is in the winnendLot array. Easy:
// assuming supporting browser:
if( winnendLot.indexOf(lotUser) > -1) console.log("You win!");
// full browser support:
var winner = false, l = winnendLot.length, i;
for( i=0; i<l; i++) {
if( winnendLot[i] === lotUser) {
winner = true;
break;
}
}
if( winner) console.log("You won!");