I'm making a Tetris game with javascript, but there's a bug I haven't been able to fix yet. It only happens when pieces Z, S, and T collide with an object while going down. I thought the problem was with the 3rd row of 0s, but the other pieces draw fine when I add it to other pieces. I also can't remove the rows because I need them to rotate the pieces.
If anyone can find the error I'd really appreciate it! Thank you.
Here's the code on repl.it:
https://repl.it/#julkothegu1/Oke-Oke
Raw:
const canvas = document.getElementById('tetris');
const context = canvas.getContext('2d');
context.scale(20, 20);
function arena_sweep(){
let row_count = 1
outer: for (let y = arena.length -1; y > 0; y--){
for (let x = 0; x < arena[y].length; x++){
if (arena[y][x] == 0){
continue outer
}
}
const row = arena.splice(y, 1)[0].fill(0);
arena.unshift(row);
++y;
player.score += row_count * 10
row_count *= 2
}
}
function draw(){
context.fillStyle = '#000';
context.fillRect(0, 0, canvas.width, canvas.height);
draw_matrix(arena, {x:0, y:0})
draw_matrix(player.matrix, player.pos)
}
function create_matrix(w, h){
const matrix = []
while (h--){
matrix.push(new Array(w).fill(0))
}
return matrix
}
function player_reset(){
const pieces = 'ILJOZST'
player.matrix = create_piece(pieces[pieces.length * Math.random() | 0])
player.pos.y = 0
player.pos,x = (arena[0].length / 2 | 0) - (player.matrix[0].length / 2 | 0)
if (collide(arena, player)){
arena.forEach(row => row.fill(0))
player.score = 0
update_score()
}
}
function create_piece(type)
{
if (type === 'I') {
return [
[0, 1, 0, 0],
[0, 1, 0, 0],
[0, 1, 0, 0],
[0, 1, 0, 0],
];
} else if (type === 'L') {
return [
[0, 2, 0],
[0, 2, 0],
[0, 2, 2],
];
} else if (type === 'J') {
return [
[0, 3, 0],
[0, 3, 0],
[3, 3, 0],
];
} else if (type === 'O') {
return [
[4, 4],
[4, 4],
];
} else if (type === 'Z') {
return [
[5, 5, 0],
[0, 5, 5],
[0, 0, 0],
];
} else if (type === 'S') {
return [
[0, 6, 6],
[6, 6, 0],
[0, 0, 0],
];
} else if (type === 'T') {
return [
[0, 7, 0],
[7, 7, 7],
[0, 0, 0],
];
}
}
function draw_matrix(matrix, offset){
matrix.forEach((row, y) => {
row.forEach((value, x) => {
if (value !== 0){
context.fillStyle = colors[value]
context.fillRect(x + offset.x, y + offset.y, 1, 1)
}
});
});
}
function merge(arena, player){
player.matrix.forEach((row, y) => {
row.forEach((value, x) => {
if (value !== 0){
arena[y + player.pos.y][x + player.pos.x] = value;
}
});
});
}
function collide(arena, player){
const m = player.matrix;
const o = player.pos;
for (let y = 0; y < m.length; y++){
for (let x = 0; x < m[y].length; x++){
if (m[y][x] != 0 && (arena[y + o.y] && arena[y + o.y][x + o.x]) != 0){
return true
}
}
}
return false
}
let drop_counter = 0
let last_time = 0
let drop_interval = 400
function update(time = 0){
const delta_time = time - last_time
drop_counter += delta_time
if (drop_counter > drop_interval){
player_drop()
}
last_time = time
draw()
requestAnimationFrame(update)
}
function player_drop(){
player.pos.y++
if (collide(arena, player)){
player.pos.y--
merge(arena, player)
player_reset()
arena_sweep()
update_score()
}
drop_counter = 0
}
function player_move(dir){
player.pos.x += dir
if (collide(arena, player)){
player.pos.x -= dir
}
}
function player_rotate(dir){
const pos = player.pos.x
let offset = 1
rotate(player.matrix, dir)
while (collide(arena, player)){
player.pos.x += offset
offset = -(offset + (offset > 0 ? 1 : -1))
if (offset > player.matrix[0].length){
rotate(player.matrix, -dir)
player.pos.x = pos
return
}
}
}
function update_score(){
document.getElementById('score').innerText = player.score
}
function rotate(matrix, dir){
for (let y = 0; y < matrix.length; y++){
for (let x = 0; x < y; x++){
[
matrix[x][y],
matrix[y][x],
] = [
matrix[y][x],
matrix[x][y],
];
}
}
if (dir > 0){
matrix.forEach(row => row.reverse());
} else {
matrix.reverse();
}
}
const colors = [
null,
'green',
'blue',
'violet',
'red',
'purple',
];
const player = {
pos: {x: 0, y: 0},
matrix: null,
score: 0,
}
const arena = create_matrix(12, 20)
document.addEventListener('keydown', event => {
if (event.keyCode === 37){
player_move(-1);
}else if (event.keyCode == 39){
player_move(1);
}else if (event.keyCode == 40){
player_drop();
}else if (event.keyCode == 81){
player_rotate(-1)
}else if (event.keyCode == 87){
player_rotate(1)
}
});
update_score()
player_reset()
update()
Never mind, I found the error. I forgot to give 7 colors to the pieces, so it was looping on colors it shouldn't have been.
Related
After debugging for more than an hour, I found the problem in my code. I do not understand why it updates board[y][x] but not board itself.
Here is the code:
puzzle = [
[0, 0, 7, 0, 0, 6, 2, 0, 0],
[1, 0, 0, 8, 9, 0, 7, 6, 0],
[8, 0, 2, 0, 7, 3, 0, 5, 0],
[0, 0, 0, 1, 0, 9, 5, 0, 6],
[0, 1, 0, 0, 0, 0, 0, 8, 0],
[7, 0, 3, 6, 0, 8, 0, 0, 0],
[0, 7, 0, 2, 4, 0, 6, 0, 3],
[0, 4, 1, 0, 6, 7, 0, 0, 5],
[0, 0, 9, 3, 0, 0, 1, 0, 0]
];
function find_empty(board) {
for (let y = 0; y < 9; ++y) {
for (let x = 0; x < 9; ++x) {
if (board[y][x] === 0) {
return [y, x];
}
}
}
return null;
}
function is_valid(board, num, pos) {
let y = pos[0];
let x = pos[1];
// Horizontal checking...
for (let i = 0; i < 9; ++i) {
if (board[y][i] === num && x !== i) {
return false;
}
}
// Vertical checking
for (let i = 0; i < 9; ++i) {
if (board[i][x] === num && y !== i) {
return false;
}
}
// 3*3 box checking
box_start_y = (Math.floor(y / 3)) * 3;
box_start_x = (Math.floor(x / 3)) * 3;
for (let i = box_start_y; i < box_start_y + 3; ++i) {
for (let j = box_start_x; j < box_start_x + 3; ++j) {
if (board[i][j] === num && y !== i && x !== j) {
return false;
}
}
}
return true;
}
function solve_board(board) {
console.log("start", board);
empty_pos = find_empty(board);
console.log("empty_pos", empty_pos)
if (empty_pos === null) {
return board;
}
let y = empty_pos[0];
let x = empty_pos[1];
for (let num = 1; num < 10; ++num) {
console.log(board)
if (is_valid(board, num, empty_pos)) {
console.log(true, num)
console.log("Before assignment", board[y][x]);
board[y][x] = num;
console.log("y", y, ", x", x);
console.log("After true", board[y][x])
console.log("After true", board);
let solved_board = solve_board(board)
console.log("solved", solved_board);
if (solved_board !== false) {
return solved_board;
}
console.log(num, "did not work!!")
board[y][x] = 0;
}
console.log(false, num)
}
return false;
}
console.log("result", solve_board(puzzle))
The problem is on line 72 in solve_board function.
It updates board[y][x] as seen in the console.logs, but does not affect board. Is it some reference issue or pass by value problem?
If anyone can help me solve this issue, I would be really grateful.
you are assigning the new value correctly. Arrays are passed to the function by reference, so there should be no problem. You can verify it with this code snippet:
const arr = [[0, 0, 0], [0, 0, 0], [0, 0, 0]]
function foo(x) {
x[0][0] = 2
}
foo(arr)
console.log(arr[0][0]) // 2
There probably is some issue with the game logic itself, the conditions in the is_valid don't look right. Consider taking a look on this article explaining how to solve sudoku programmatically with logic and without brute force.
Alternatively, I suggest you look at the backtracking algorithm, which tries all possible options, which should be straightforward to implement.
I'm trying to create a random board for my project but it finishes without filling the whole board(leaves 0)
I converted the original code from this site:
www.101computing.net/sudoku-generator-algorithm/
from python to javascript manually and checked all the functions that I created but some things were wrong.
would love to get some help here thnks!
the code:
let grid = Array(9).fill().map(()=>Array(9).fill(0))
let numberList = [1, 2, 3, 4, 5, 6, 7, 8, 9]
console.log(grid);
fillGrid(grid)
console.log(grid)
function fillGrid(grid){
//var counter;
// Find next empty cell
for (i in range(0,81)){
row = ~~(i / 9);
col = i % 9;
if (grid[row][col]== 0){
shuffle(numberList);
for (g in numberList){
value = numberList[g]
// Check that this value has not already be used on this row and col
if (!checkForNumebrInRaw(grid,row,value) && !checkForNumebrInColomn(grid,col,value)){
// Identify which of the 9 squares we are working on
square = []
if (row<3){
if (col<3){
square = getSquare(grid,0,3,0,3);
}else if (col<6){
square = getSquare(grid,3,6,0,3);
}else{
square = getSquare(grid,6,9,0,3);
}
}
else if (row<6){
if (col<3){
square = getSquare(grid,0,3,3,6);
}else if (col<6){
square = getSquare(grid,3,6,3,6);
}else{
square = getSquare(grid,6,9,3,6);
}
}
else{
if (col<3){
square = getSquare(grid,0,3,6,9);
}else if (col<6){
square = getSquare(grid,3,6,6,9);
}else{
square = getSquare(grid,6,9,6,9);
}
}
// Check that this value has not already be used on this 3x3 square
if (!checkForNumInSquare(square,value)){
grid[row][col] = value;
if (checkGrid(grid)){
return true;
}else
{
if (fillGrid(grid)){
return true;
}
}
}
}
break
}
grid[row][col] = 0;
}
}
}
function range(start, end) {
let res=[];
for (i=start;i<end;i++){
res.push(i)
}
return res
};
function getSquare(mat,a,b,c,d) {
var _pj_a = []
_pj_b = range(c, d);
for (var _pj_c = 0, _pj_d = _pj_b.length; _pj_c < _pj_d; _pj_c += 1) {
var i = _pj_b[_pj_c];
_pj_a.push(mat[i].slice(a, b));
}
return _pj_a;
};
function checkForNumebrInRaw(grid,raw,num){
for (let i = 0; i<grid.length;i++){
if (grid[raw][i] == num){return true;}
}
return false;
}
function checkForNumebrInColomn(grid,colomn,num){
for (let i = 0; i<grid.length;i++){
if (grid[i][colomn] == num){return true;}
}
return false;
}
function shuffle(array) {
let currentIndex = array.length, randomIndex;
// While there remain elements to shuffle...
while (currentIndex != 0) {
// Pick a remaining element...
randomIndex = Math.floor(Math.random() * currentIndex);
currentIndex--;
// And swap it with the current element.
[array[currentIndex], array[randomIndex]] = [
array[randomIndex], array[currentIndex]];
}
return array;
}
function checkForNumInSquare(grid,num){
for (let i=0;i<grid.length;i++){
for(let j=0;j<grid[i].length;j++){
if (grid[i][j]== num){return true;}
}
}
return false
}
// A function to check if the grid is full
function checkGrid(grid){
for (let i=0;i<grid.length;i++){
for(let j=0;j<grid[i].length;j++){
if (grid[i][j]== 0){return false;}
}
}
return true
}
output:
[
[
0, 0, 0, 0, 0,
0, 0, 0, 0
],
[
0, 0, 0, 0, 0,
0, 0, 0, 0
],
[
0, 0, 0, 0, 0,
0, 0, 0, 0
],
[
0, 0, 0, 0, 0,
0, 0, 0, 0
],
[
0, 0, 0, 0, 0,
0, 0, 0, 0
],
[
0, 0, 0, 0, 0,
0, 0, 0, 0
],
[
0, 0, 0, 0, 0,
0, 0, 0, 0
],
[
0, 0, 0, 0, 0,
0, 0, 0, 0
],
[
0, 0, 0, 0, 0,
0, 0, 0, 0
]
]
[
[
3, 8, 7, 4, 1,
2, 5, 6, 9
],
[
5, 9, 6, 3, 7,
8, 1, 4, 0
],
[
1, 2, 4, 5, 6,
9, 7, 3, 8
],
[
9, 4, 3, 1, 5,
7, 0, 8, 2
],
[
2, 1, 8, 0, 9,
4, 3, 7, 6
],
[
7, 0, 5, 6, 8,
3, 9, 1, 4
],
[
4, 7, 2, 8, 0,
1, 6, 9, 5
],
[
0, 6, 9, 2, 3,
5, 8, 0, 7
],
[
8, 3, 1, 7, 0,
6, 4, 2, 0
]
]
Ok so after a million tries i made it!
// true - row and colomn without the number
function rowAndColomnCheck(grid,row,col,num){
function checkForNumebrInRaw(grid,row,num){
for (let i = 0; i<grid.length;i++){
if (grid[row][i] == num){return true;}
}
return false;
}
function checkForNumebrInColomn(grid,colomn,num){
for (let i = 0; i<grid.length;i++){
if (grid[i][colomn] == num){return true;}
}
return false;
}
if (!checkForNumebrInColomn(grid,col,num) && !checkForNumebrInRaw(grid,row,num)){
return true;
}else{
return false;
}
};
function range(start, end) {
let res=[];
for (i=start;i<end;i++){
res.push(i)
}
return res
};
function getSquare(mat,a,b,c,d) {
var _pj_a = []
_pj_b = range(c, d);
for (var _pj_c = 0, _pj_d = _pj_b.length; _pj_c < _pj_d; _pj_c += 1) {
var i = _pj_b[_pj_c];
_pj_a.push(mat[i].slice(a, b));
}
return _pj_a;
};
function shuffle(array) {
let currentIndex = array.length, randomIndex;
// While there remain elements to shuffle...
while (currentIndex != 0) {
// Pick a remaining element...
randomIndex = Math.floor(Math.random() * currentIndex);
currentIndex--;
// And swap it with the current element.
[array[currentIndex], array[randomIndex]] = [
array[randomIndex], array[currentIndex]];
}
return array;
};
// check if number is in sqare given
function checkForNumInSquare(grid,num){
for (let i=0;i<grid.length;i++){
for(let j=0;j<grid[i].length;j++){
if (grid[i][j]== num){return true;}
}
}
return false
};
// A function to check if the grid is full
function checkGrid(grid){
for (let i=0;i<grid.length;i++){
for(let j=0;j<grid[i].length;j++){
if (grid[i][j]== 0){return false;}
}
}
// complete
return true
};
function fillSudoku(mat){
for(let i in range(0,81)){
var mainRow = ~~(i / 9);
var mainCol = i % 9;
if (mat[mainRow][mainCol] == 0){
shuffle(numberList);
for (let value of numberList){
if(rowAndColomnCheck(mat,mainRow,mainCol,value)){
var mySquare = [[]];
if(mainRow<3){
if (mainCol<3){
mySquare = getSquare(mat,0,3,0,3);
}
else if (mainCol<6){
mySquare = getSquare(mat,3,6,0,3);
}
else{
mySquare = getSquare(mat,6,9,0,3);
}
}
else if(mainRow<6){
if (mainCol<3){
mySquare = getSquare(mat,0,3,3,6);
}
else if (mainCol<6){
mySquare = getSquare(mat,3,6,3,6);
}
else{
mySquare = getSquare(mat,6,9,3,6);
}
}
else{
if (mainCol<3){
mySquare = getSquare(mat,0,3,6,9);
}
else if (mainCol<6){
mySquare = getSquare(mat,3,6,6,9);
}
else{
mySquare = getSquare(mat,6,9,6,9);
}
}
bol_t = checkForNumInSquare(mySquare,value);
if (bol_t == false){
mat[mainRow][mainCol] = value;
if (checkGrid(mat)){
return true;
}
else{
if (fillSudoku(mat)){
return true;
}
}
}
}
}
break
}
}
mat[mainRow][mainCol] = 0;
};
var numberList = [1, 2, 3, 4, 5, 6, 7, 8, 9];
var grid = Array(9).fill().map(()=>Array(9).fill(0));
fillSudoku(grid);
console.log(grid);
I'm trying to solve a programing puzzle/game.
It's a Spiral Matrix and the result is correct but my array is outputting .0 at the end of my arrays.
For example, the input is
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
The output is
[1.0,2.0,3.0,6.0,9.0,8.0,7.0,4.0,5.0]
But I'm looking for
[ 1, 2, 3, 6, 9, 8, 7, 4, 5]
The code I am using is from the YouTube video "LeetCode 54 Spiral Matrix in javascript".
How do I Logger.log(result) without the .0?
function myFunction() {
matrix = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
];
// result array
const result = [];
// boundry varibals
let left = 0;
let top = 0;
let right = matrix[0].length - 1;
let bottom = matrix.length - 1;
// starting direction clock wise
let direction = 'right';
// start loop
while (left <= right && top <= bottom) {
if (direction === 'right') {
for (let i = left; i <= right; i += 1) {
result.push(matrix[top][i]);
}
top += 1;
direction = 'down';
} else if (direction === 'down') {
for (let i = top; i <= bottom; i += 1) {
result.push(matrix[i][right]);
}
right -= 1;
direction = 'left';
} else if (direction === 'left') {
for (let i = right; i >= left; i -= 1) {
result.push(matrix[bottom][i]);
}
bottom -= 1;
direction = 'up';
} else if (direction === 'up') {
for (let i = bottom; i >= top; i -= 1) {
result.push(matrix[i][left]);
}
left += 1;
direction = 'right';
}
}
Logger.log(result);
return result;
}
I'm using google-apps-script.
Add JSON.stringify() to your output.
Logger.log(JSON.stringify(result));
Output:
Reference:
JSON.stringify()
// global variable
// accessible to all functions
var sol =
[[0, 7, 0, 2, 3, 8, 0, 0, 0],
[0, 0, 0, 7, 4, 0, 8, 0, 9],
[0, 6, 8, 1, 0, 9, 0, 0, 2],
[0, 3, 5, 4, 0, 0, 0, 0, 8],
[6, 0, 7, 8, 0, 2, 5, 0, 1],
[8, 0, 0, 0, 0, 5, 7, 6, 0],
[2, 0, 0, 6, 0, 3, 1, 9, 0],
[7, 0, 9, 0, 2, 1, 0, 0, 0],
[0, 0, 0, 9, 7, 4, 0, 8, 0]];
//this function prints the board
var printBoard = function () {
for (let row = 0; row < sol.length; row += 1) {
for (let col = 0; col < sol[row].length; col += 1) {
let id = 'r' + (row + 1) + (col + 1);
let elem = document.getElementById(id);
elem.innerHTML = sol[row][col];
}
}
};
function isValid(sol, row, col, k) {
for (let i = 0; i < 9; i++) {
const m = 3 * Math.floor(row / 3) + Math.floor(i / 3);
const n = 3 * Math.floor(col / 3) + i % 3;
if (sol[row][i] == k || sol[i][col] == k || sol[m][n] == k) {
return false;
}
}
return true;
}
var solve = function() {
for (let row = 0; row < 9; row++) {
for (let col = 0; col < 9; col++) {
if (sol[row][col] == 0) {
for (let k = 1; k <= 9; k++) {
if (isValid(sol, row, col, k) == true) {
sol[row][col] == '${k}';
if(solve() == true) {
return true;
} else {
sol[row][col] == 0;
}
}
}
return false;
}
}
}
return true;
}
printBoard();
I am having trouble debugging this. I keep getting an error message when I click on my solve button, which will take the grid and complete a sudoku puzzle: "Uncaught RangeError: Maximum call stack size exceeded". Looking at the file, it seems that my isValid() function, is causing the issues here. I think I am stuck in a continuous loop somewhere? Any help would be appreciated. It also says the error lies on lines 40 and 24, which are:
function isValid(sol, row, col, k) {
and
if (isValid(sol, row, col, k) == true) {
Please help, as I am not even a CS major and this is for a class requirement, and I am not exactly experience with this type of stuff. I tried to search for help thorugh other resources but it is just confusing me even more.
EDIT:
//global variable
//accesible to all functions
var sol =
[[0, 7, 0, 2, 3, 8, 0, 0, 0],
[0, 0, 0, 7, 4, 0, 8, 0, 9],
[0, 6, 8, 1, 0, 9, 0, 0, 2],
[0, 3, 5, 4, 0, 0, 0, 0, 8],
[6, 0, 7, 8, 0, 2, 5, 0, 1],
[8, 0, 0, 0, 0, 5, 7, 6, 0],
[2, 0, 0, 6, 0, 3, 1, 9, 0],
[7, 0, 9, 0, 2, 1, 0, 0, 0],
[0, 0, 0, 9, 7, 4, 0, 8, 0]];
//this function prints the board
var printBoard = function () {
for (let row = 0; row < sol.length; row += 1) {
for (let col = 0; col < sol[row].length; col += 1) {
let id = 'r' + (row + 1) + (col + 1);
let elem = document.getElementById(id);
elem.innerHTML = sol[row][col];
}
}
};
function isValid(sol, row, col, k) {
for (let i = 0; i < 9; i++) {
const m = 3 * Math.floor(row / 3) + Math.floor(i / 3);
const n = 3 * Math.floor(col / 3) + i % 3;
if (sol[row][i] == k || sol[i][col] == k || sol[m][n] == k) {
return false;
}
}
return true;
}
var solve = function(sol) {
for (let row = 0; row < 9; row++) {
for (let col = 0; col < 9; col++) {
if (sol[row][col] == 0) {
//set the check for the row and column
for (let k = 1; k <= 9; k++) {
if (isValid(sol, row, col, k) == true) {
sol[row][col] == k;
//check if its not true, then check the row and column again
if(solve() == true) {
return true;
} else {
sol[row][col] = 0;
}
}
}
return false;
}
}
}
return true;
};
printBoard();
You never change sol before recursively calling solve() again.
if (isValid(sol, row, col, k) == true) {
sol[row][col] == '${k}'; // <- does not update sol
if(solve() == true) {
return true;
} else {
sol[row][col] == 0; // <- does not update sol
}
}
When you find an option for k that is a valid you should update sol before calling solve() again. Otherwise you will create an endless recursive loop which will eventually exceed the maximum stack size and result in the error you receive.
Use the assignment operator (=) instead of the equality operator (==) to assign a new value. Furthermore '${k}' will result in the literal ${k} use backticks (`) if you want to use string interpolation. I don't see a reason to use a string here, simply assigning k would be sufficient.
if (isValid(sol, row, col, k) == true) {
sol[row][col] = k; // <- assign k to a cell in the matrix
if(solve() == true) {
return true;
} else {
sol[row][col] = 0; // <- reset the cell back to 0 if it cannot be solved
}
}
I would also suggest using guard clauses. Instead of nesting the whole body of the for-loop inside an if-statement, use a guard and skip to the next iteration with continue:
for (...) {
if (check) {
// code
}
}
Can be changed to:
for (...) {
if (!check) continue;
// code
}
This generally improves the code indention and readability.
Given two numbers X and Y, write a function that:
returns even numbers between X and Y, if X is greater than Y else it returns odd numbers between x and y
expected result:
12, 0 => [2,4,6,8,10]
actual result:
12, 0 => [ 2, 4, 6, 8, 10 ]
0, 12 => [ 1, 3, 5, 7, 9, 11 ]
const number_game = (x, y) => {
let numArray = [];
if (x === 0 && y === 0) {
return [];
} else if (x > y) {
for (let i = y + 1; i < x; i++) {
if (i % 2 == 0) {
numArray.push(i);
}
}
} else {
for (let i = x + 1; i < y; i++) {
if (i % 2 == 1) {
numArray.push(i);
}
}
}
return numArray;
}
console.log(number_game(12, 0));
console.log(number_game(0, 12));