Note: I also posted this question on p5.js' forum: https://discourse.processing.org/t/p5-js-prims-algorithm-maze-generation-stuck-in-infinite-loop/8277
I'm trying to implement a randomized Prim's algorithm to generate a maze. However, the program keeps getting stuck in an infinite loop as the length of the list of walls (wallList) is always in the thousands. Currently I am using an if statement that stops the maze generation after 11500 iterations to prevent an infinite loop.
My pseudocode is based on Wikipedia's description of the algorithm:
Start with a grid full of walls.
Pick a cell, mark it as part of the
maze. Add the walls of the cell to the wall list.
While there are
walls in the list:
Pick a random wall from the list. If only one of
the two cells that the wall divides is visited, then:
Make the wall a
passage and mark the unvisited cell as part of the maze.
Add the
neighboring walls of the cell to the wall list.
Remove the wall from
the list.
HTML:
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="https://ajax.aspnetcdn.com/ajax/jQuery/jquery-3.3.1.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.6.0/p5.js"></script>
</script>
</head>
<body>
<div id="game-wrapper">
<div id="canvas-wrapper">
</div>
</div>
</div>
<script type="text/javascript" src="maze.js"></script>
</body>
</html>
JavaScript (maze.js):
// Note to self: JavaScript variables have function scope, not block scope
var numIterations = 0; // Just for debugging purposes (stop at 1000 iterations otherwise the program goes into
// an infinite loop
// The directions and vectors arrays correspond to each other
// For example, the first element of directions is "N" and the first element of vectors also represents a
// north vector
var directions = ["N", "E", "S", "W"]
var vectors = [
[-1, 0],
[0, 1],
[1, 0],
[0, -1]
];
var wallList = {}; // Structure of a wall [rol (num), col (num), direction (string)]
function Maze(numRows, numColumns) {
/*
Defines a maze given the number of rows and the number of columns in the maze
*/
this.numColumns = numColumns;
this.numRows = numRows;
this.numCells = numRows * numColumns;
this.cellGraph = [];
for (var i = 0; i < numRows; i++) { // For every single row
this.cellGraph.push([]); // Start out with an empty row
}
}
Maze.prototype.computeFrontierWalls = function (cellRow, cellColumn) {
/*
The frontier walls of a cell is defined as all the walls of the adjacent cells
*/
/*
Coordinates of adjacent cells:
Up [cellRow - 1, cellColumn]
Down [cellRow + 1, cellColumn]
Right [cellRow, cellColumn + 1]
Left [cellRow, cellColumn - 1]
*/
var coordinates = [
[cellRow - 1, cellColumn],
[cellRow + 1, cellColumn],
[cellRow, cellColumn + 1],
[cellRow, cellColumn - 1]
];
var computedFrontier = []; // List of frontier cells
var originalCell = this.cellGraph[cellRow][cellColumn]; // We want to calculate the frontier of the original cell
for (var i = 0; i < coordinates.length; i++) {
// Get the coordinates of the adjacent cell
var coordinate = coordinates[i];
var row = coordinate[0];
var col = coordinate[1];
// See if a cell exists at that area
// If there is a cell that exists, add all of the walls of the cell to the computedFrontier array
if (row >= 0 && row < this.cellGraph.length && col >= 0 && col < this.cellGraph[0].length) {
var cell = this.cellGraph[parseInt(row)][parseInt(col)];
for (var j = 0; j < directions.length; j++) {
computedFrontier.push([cell.row, cell.column, directions[j]]);
}
}
}
return computedFrontier;
}
function Cell(cellSize, row, column) {
this.cellSize = cellSize; // The width and height of the cell
this.column = column;
this.row = row;
this.xPos = column * cellSize;
this.yPos = row * cellSize;
this.walls = [true, true, true, true]; // 0 = top, 1 = right, 2 = bottom, 3 = left
this.visited = false; // Whether the cell has been traversed or not
}
function getRandomPos(widthCells, heightCells) {
var row = Math.floor(Math.random() * heightCells); // Generate a random row
var column = Math.floor(Math.random() * widthCells); // Generate a random column
return [row, column];
}
var mazeIntro = function (p) {
var maze = new Maze(20, 35); // Generate a new maze with 20 rows and 35 columns
Maze.prototype.createMaze = function () { // Build an empty maze
for (var i = 0; i < this.numRows; i++) { // Iterate through every row
for (var j = 0; j < this.numColumns; j++) { // Iterate through every column
var cell = new Cell(20, i, j); // Create a new size at row i and column j with size 20
maze.cellGraph[i].push(cell); // Add the cell to the row
}
}
}
maze.createMaze(); // Build the maze
p.setup = function () {
var canvas = p.createCanvas(700, 400);
p.background(255, 255, 255);
p.smooth();
p.noLoop();
// Pick a cell, mark it as part of the maze. Add the walls of the cell to the wall list
var pos = getRandomPos(maze.cellGraph[0].length, maze.cellGraph.length);
;
var row = pos[0];
var column = pos[1];
maze.cellGraph[row][column].visited = true;
for (var k = 0; k < directions.length; k++) {
var key = row.toString() + column.toString() + directions[k].toString();
if (!wallList[key]) {
wallList[key] = [row, column, directions[k]];
}
}
}
Cell.prototype.display = function () {
/*
For each wall:
1. Check if it is on the border of the maze:
2. If it is on the border: don't draw the wall
3. If it isn't on the border: draw the wall
*/
p.stroke(0, 0, 0);
if (this.walls[0] && this.row != 0) { // Top
p.line(this.xPos, this.yPos, this.xPos + this.cellSize, this.yPos);
}
if (this.walls[1] && this.column != maze.widthCells - 1) { // Right
p.line(this.xPos + this.cellSize, this.yPos, this.xPos + this.cellSize, this.yPos + this.cellSize);
}
if (this.walls[2] && this.row != maze.heightCells - 1) { // Bottom
p.line(this.xPos + this.cellSize, this.yPos + this.cellSize, this.xPos, this.yPos + this.cellSize);
}
if (this.walls[3] && this.column != 0) { // Left
p.line(this.xPos, this.yPos + this.cellSize, this.xPos, this.yPos);
}
p.noStroke();
}
Cell.prototype.toString = function () {
/*
Mainly used for debugging purposes, converts the object into a string containing the row and the column of the cell
*/
return this.row + "|" + this.column;
}
function deleteWall(current, neighbor) {
/*
Deletes two walls separating two cells: current and neighbor
Calculates if neighbor is to the left, right, top, or bottom of cell
Removes the current's wall and the corresponding neighbor's wall
*/
var deltaX = current.column - neighbor.column;
var deltaY = current.row - neighbor.row;
if (deltaX == 1) { // Current is to the right of the neighbor
current.walls[3] = false;
neighbor.walls[1] = false;
}
if (deltaX == -1) { // Current is to the left of the neighbor
current.walls[1] = false;
neighbor.walls[3] = false;
}
if (deltaY == 1) { // Current is to the bottom of the neighbor
current.walls[0] = false;
neighbor.walls[2] = false;
}
if (deltaY == -1) { // Current is to the top of the neighbor
current.walls[2] = false;
neighbor.walls[0] = false;
}
}
function isWall(cellA, cellB) {
// Whether there's a wall or not depends on the orientation of the blocks
// If it's vertical, it has to be false between even numbers
// If it's horizontal, it has to be false between odd numbers
for (var j = 0; j < cellA.walls.length; j++) {
for (var k = 0; k < cellB.walls.length; k++) {
if (Math.abs(j - k) == 2 && !cellA.walls[j] && !cellB.walls[k]) {
var rA = cellA.row;
var cA = cellA.column;
var rB = cellB.row;
var cB = cellB.column
if ((rA - rB) == 1 && j == 0 || (rA - rB) == -1 && j == 2 || (cA - cB) == 1 && j == 3 || (cA - cB) == -1 && j == 1) {
return false;
}
}
}
}
return true;
}
function calculateCellDivision(wall) {
// Calculate the two cells that the wall divides
// For example:
// If the wall is [10, 11, "N"]
// The two cells that the wall divides are (10, 11) and (9, 11)
var row = wall[0];
var col = wall[1];
var cell1 = maze.cellGraph[row][col]; // Get the cell of the wall
// Get the corresponding vector based upon the direction of the wall
var vectorIndex = directions.indexOf(wall[2]);
// Add the vector to the position of cell1
var cell2Row = parseInt(cell1.row) + vectors[vectorIndex][0];
var cell2Column = parseInt(cell1.column) + vectors[vectorIndex][1];
if (cell2Row < 0 || cell2Row >= maze.cellGraph.length || cell2Column < 0 || cell2 >= maze.cellGraph[0].length) {
return -1;
}
var cell2 = maze.cellGraph[cell2Row][cell2Column]; // Get the corresponding cell
var cellsVisited = 0;
var unvisitedCell;
if (cell1.visited) {
cellsVisited += 1;
unvisitedCell = cell2;
}
if (!cell2) { // This means that the wall is a border wall
return -1;
}
if (cell2.visited) {
cellsVisited += 1;
unvisitedCell = cell1;
}
if (cellsVisited == 1) {
return [cell1, cell2, cellsVisited, unvisitedCell];
}
return -1;
}
function getCellWalls(row, col) {
// Gets a cell's walls
var cellWalls = [];
for (var i = 0; i < directions.length; i++) {
cellWalls.push(row + col + directions[i]);
}
return cellWalls;
}
p.draw = function () {
while (Object.keys(wallList).length > 0) { // While there are still walls in the list
console.log("Object.keys(wallList).length = " +
// Pick a random wall of the list
var wallListKeys = $.map(wallList, function (value, key) {
return key;
});
var randomKey = wallListKeys[Math.floor(Math.random() * wallListKeys.length)];
var randomWall = wallList[randomKey];
var components = calculateCellDivision(randomWall);
if (components != -1) {
var numVisited = components[2];
var cell1 = components[0];
var cell2 = components[1];
// If only one of the two cells that the wall divides is visited, then:
// 1. Make the wall a passage and mark the unvisited cell as part of the maze.
// 2. Add the neighboring walls of the cell to the wall list.
// Remove the wall from the list.
if (numVisited == 1) {
deleteWall(cell1, cell2);
var unvisitedCell = maze.cellGraph[components[3].row][components[3].column];
unvisitedCell.visited = true;
var unvisitedString = unvisitedCell.row + "|" + unvisitedCell.column;
// Add the neighboring walls of the cell to the wall list
// Format of the walls (by index):
// 0 = top, 1 = right, 2 = bottom, 3 = left
var computedFrontierWalls = maze.computeFrontierWalls(unvisitedCell.row, unvisitedCell.column);
for (var k = 0; k < computedFrontierWalls.length; k++) {
var computedWall = computedFrontierWalls[k];
var keyString = computedWall[0].toString() + computedWall[1].toString() + computedWall[2];
if (!wallList[keyString]) {
wallList[keyString] = computedWall;
}
}
// Delete randomKey from the list of walls, and then delete the same wall from the corresponding cell
delete wallList[randomKey];
// Calculate the corresponding cell
var direction = randomWall[2];
var directionIndex = directions.indexOf(direction);
var oppositeDirectionIndex = -1;
if (directionIndex == 0) {
oppositeDirectionIndex = 2;
}
if (directionIndex == 2) {
oppositeDirectionIndex = 0;
}
if (directionIndex == 1) {
oppositeDirectionIndex = 3;
}
if (directionIndex == 3) {
oppositeDirectionIndex = 1;
}
var vector = vectors[directionIndex];
var correspondingString = (randomWall[0] + vector[0]).toString() + (randomWall[1] + vector[1]).toString() + directions[oppositeDirectionIndex];
delete wallList[correspondingString];
}
}
numIterations += 1;
if (numIterations == 11500) { // Prevents infinite loop
break;
}
}
p.clear();
// Draw the maze
for (var i = 0; i < maze.cellGraph.length; i++) { // Iterate through row
for (var j = 0; j < maze.cellGraph[i].length; j++) { // Iterate through every column
maze.cellGraph[i][j].display(); // Display the cell
}
}
p.line(0, 400, 400, 400);
}
};
var myp5 = new p5(mazeIntro, "canvas-wrapper"); // Initialize the graphics engine for the canvas
The generation does work, as shown in the screenshot below. But I'm pretty sure my implementation is not correct as the wall list shouldn't contain thousands of walls.
According to the Wikipedia article, a wall must be removed once is picked randomly and analysed, regardless of the results of said analysis. In your code, the lines delete wallList[randomKey]; and delete wallList[correspondingString]; must be outside of the conditional for the wall to be eliminated from the list.
After deleting said lines, replace this:
numIterations += 1;
if (numIterations == 11500) { // Prevents infinite loop
break;
}
with this:
delete wallList[randomKey];
delete wallList[correspondingString];
and you're good to go. (Disclaimer: I tested it and it worked; but that's a hefty amount of code you have there, so I'm not sure if anything else breaks).
I wanted to try and make it more readable so I could understand better and came up with this. Hope it helps.
// 1. Start with a grid full of walls.
const _WALL = '█';
const _PATH = ' ';
const _COLS = 60;
const _ROWS = 60;
let maze = [];
for(let i = 0; i < _COLS; i++){
maze.push([]);
for(let j = 0; j < _ROWS; j++)
maze[i][j] = _WALL;
}
// 2. Pick a cell, mark it as part of the maze.
let cell = {x:Math.floor(Math.random()*_COLS), y:Math.floor(Math.random()*_ROWS)};
maze[cell.x][cell.y] = _PATH;
// 2.1 Add the walls of the cell to the wall list.
let walls = [];
if(cell.x+1 < _COLS) walls.push({x:cell.x+1, y:cell.y});
if(cell.x-1 >= 0) walls.push({x:cell.x-1, y:cell.y});
if(cell.y+1 < _ROWS) walls.push({x:cell.x, y:cell.y+1});
if(cell.y-1 >= 0) walls.push({x:cell.x, y:cell.y-1});
// 3. While there are walls in the list:
while(walls.length > 0){
// 3.1 Pick a random wall from the list.
let wallIndex = Math.floor(Math.random() * walls.length);
let wall = walls[wallIndex];
// 3.2 If only one of the two cells that the wall divides is visited, then:
let uc = []; // uc will be short for 'unvisited cell'
if(wall.x+1 < _COLS && maze[wall.x+1][wall.y] === _PATH) uc.push({x:wall.x-1, y:wall.y});
if(wall.x-1 >= 0 && maze[wall.x-1][wall.y] === _PATH) uc.push({x:wall.x+1, y:wall.y});
if(wall.y+1 < _ROWS && maze[wall.x][wall.y+1] === _PATH) uc.push({x:wall.x, y:wall.y-1});
if(wall.y-1 >= 0 && maze[wall.x][wall.y-1] === _PATH) uc.push({x:wall.x, y:wall.y+1});
if(uc.length === 1){
// 3.2.1 Make the wall a passage and mark the unvisited cell as part of the maze.
maze[wall.x][wall.y] = _PATH;
if(uc[0].x >=0 && uc[0].x <_COLS && uc[0].y >=0 && uc[0].y<_ROWS){
maze[uc[0].x][uc[0].y] = _PATH;
// 3.2.2 Add the neighboring walls of the cell to the wall list.
if(uc[0].x+1 < _COLS && maze[uc[0].x+1][uc[0].y] === _WALL) walls.push({x:uc[0].x+1, y:uc[0].y});
if(uc[0].x-1 >= 0 && maze[uc[0].x-1][uc[0].y] === _WALL) walls.push({x:uc[0].x-1, y:uc[0].y});
if(uc[0].y+1 < _ROWS && maze[uc[0].x][uc[0].y+1] === _WALL) walls.push({x:uc[0].x, y:uc[0].y+1});
if(uc[0].y-1 >= 0 && maze[uc[0].x][uc[0].y-1] === _WALL) walls.push({x:uc[0].x, y:uc[0].y-1});
}
}
// 3.3 Remove the wall from the list.
walls.splice(wallIndex, 1);
}
console.table(maze);
function setup(){
createCanvas(400, 400);
fill(0);
let widthUnit = width / _COLS;
let heightUnit = height / _ROWS;
for(let i = 0; i < _COLS; i++)
for(let j = 0; j < _ROWS; j++)
if(maze[i][j] === _WALL){
//rect(i*widthUnit, j*heightUnit, widthUnit, heightUnit);
if(i-1 >= 0 && i+1 < _COLS){
if(maze[i-1][j] === _WALL) line((i+0.5)*widthUnit, (j+0.5)*heightUnit, i*widthUnit, (j+0.5)*heightUnit);
if(maze[i+1][j] === _WALL) line((i+0.5)*widthUnit, (j+0.5)*heightUnit, (i+1)*widthUnit, (j+0.5)*heightUnit);
}
if(j-1 >= 0 && j+1 < _ROWS){
if(maze[i][j-1] === _WALL) line((i+0.5)*widthUnit, (j+0.5)*heightUnit, (i+0.5)*widthUnit, j*heightUnit);
if(maze[i][j+1] === _WALL) line((i+0.5)*widthUnit, (j+0.5)*heightUnit, (i+0.5)*widthUnit, (j+1)*heightUnit);
}
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.7.3/p5.min.js"></script>
i'm trying to make a right aligned triangle. I am able to make a left aligned triangle easily, but can't get the number of spaces to decrease with each additional row.
output should be:
#
##
###
####
#####
let levels = 8;
let hash = '';
for (let i = 1; i <= levels; i++) {
hash += '#';
console.log(hash)
}
Instead of reusing the same string, consider generating each row with repeat() and padStart():
function rightAlignedTriangle (levels) {
for (let i = 1; i <= levels; i++) {
const row = '#'.repeat(i).padStart(levels)
console.log(row)
}
}
rightAlignedTriangle(5)
To implement this using a nested loop instead of string methods, you can manually implement the above two methods as an inner loop on a variable string declared in the outer loop:
function rightAlignedTriangle (levels) {
for (let i = 1; i <= levels; i++) {
let row = ''
for (let j = 0; j < levels; j++) {
if (j < i) { row += '#' } // repeat(i)
else { row = ' ' + row } // padStart(levels)
}
console.log(row)
}
}
rightAlignedTriangle(5)
I am considerably new to JavaScript. I have an 8 x 8 HTML table as a board for a game that is dynamically created in the JS file like this:
function drawBoard(rows, cols){
var grid = document.getElementById("grid");
grid.className = 'grid';
for (var r = 0; r < rows; r++){
var row = grid.appendChild(document.createElement('tr'));
for (var c = 0; c < cols; c++){
var cell = row.appendChild(document.createElement('td'));
}
}
return grid;
}
document.body.appendChild(drawBoard(ROW, COL));
The board displays successfully when I load the page. I need to insert 5 keywords randomly on 5 cells. These cells cannot be along the outside border of squares (i.e., cannot be in the first or last row or column). I have the 5 keywords as an array in my javascript file. How can I randomly pick 5 cells on the table excluding the first or last row or column and insert these keywords on them everytime I draw the board? I tried cell.innerHTML = "keyword" but ended up inserting only one of the keywords on every single cell on the table. Did a thorough search online without any luck. Thank you!
// I'm assuming ROW, COL and keywords are declared somewhere in your code
const ROW = 8;
const COL = 8;
const keywords = ['a', 'b', 'c', 'd', 'e'];
// --- your code, no changes
function drawBoard(rows, cols, keywords) {
var grid = document.getElementById("grid");
grid.className = 'grid';
for (var r = 0; r < rows; r++) {
var row = grid.appendChild(document.createElement('tr'));
for (var c = 0; c < cols; c++) {
row.appendChild(document.createElement('td'));
}
}
return grid;
}
// save the table in a variable
const grd = document.body.appendChild(drawBoard(ROW, COL));
//--- NEW CODE STARTS HERE
const shuffle = array => {
const shuffled = [];
while (array.length) {
const index = Math.floor(Math.random() * array.length);
shuffled.push(array.splice(index, 1).pop());
}
return shuffled;
};
const filtered = Array.from(grd.querySelectorAll('td'))
.filter(({cellIndex, parentElement: {rowIndex}}) => rowIndex && rowIndex < ROW - 1 && cellIndex && cellIndex < COL - 1);
const shuffled = shuffle(filtered);
const sliced = filtered.slice(0, keywords.length);
sliced.forEach((cell, index) => cell.innerHTML = keywords[index]);
<table id="grid"></table>
filtered is all the cells not on the border
shuffled array is simply all the filtered cells shuffled using the shuffle function
sliced array is the first n cells from the shuffled array, where n is the number of keywords
then it's a simple matter of adding the keywords
To explain the filter function:
({cellIndex, parentElement: {rowIndex}}) =>
rowIndex && rowIndex < ROW - 1 && cellIndex && cellIndex < COL - 1;
this is ES6 way of performing
function(cell) {
var cellIndex = cell.cellIndex;
var parentElement = cell.parentElement;
var rowIndex = parentElement.rowIndex;
return rowIndex && rowIndex < ROW - 1 && cellIndex && cellIndex < COL - 1;
}
First of all extract 5 random cells in the 64 - 28 available. We have a 6x6 square available then.
//We keep an array to store cells for keywords
var keyCells = [];
//A function to check if we already choosed a cell
function checkChoosed( x, y ) {
if( keyCells.length > 0 ) {
for( var j = 0; j < keyCells.length; j++ ) {
if( ( keyCells[ j ].x == x ) && ( keyCells[ j ].y == y ) ) return 1;
}
}
return 0;
}
//Pick random coordinates (in 6x6 square) for cells
function randomCells() {
var choosed = 0;
while( choosed < 5 ) {
var x = Math.round( Math.random() * 5 ) + 1; //Math.random returns a number between 0 and 0.99, so * 5 to get from 0 to 4.95, + 1 because we skip first row and column of the grid
var y = Math.round( Math.random() * 5 ) + 1;
if( !checkChoosed( x, y ) ) {
keyCells.push( { x: x, y: y } );
choosed++;
}
}
}
//Now we draw the table and access it for keywords
function drawBoard(rows, cols) {
var grid = document.getElementById("grid");
grid.className = 'grid';
for ( var r = 0; r < rows; r++ ) {
var row = grid.appendChild(document.createElement('tr'));
for ( var c = 0; c < cols; c++ ) {
var cell = row.appendChild(document.createElement('td'));
}
}
randomCells();
var trs = document.getElementById('grid').children;
for( var i = 0; i < keyCells.length; i++ ) {
var currX = keyCells[ i ].x;
var currY = keyCells[ i ].y;
//Here I used the ":nth-child" CSS selector to access "tr" (our Y) and then "td" (our X)
var currRow = trs[ currY ].children;
currRow[ currX ].innerHTML = /* your keyword here */
}
return grid;
}
I am trying to create a script that allows the user to enter a certain amount of rows which will then print a large letter X, so far I am able to print it as a v shape but am struggling to get the rest of the x together.
rows = 0;
space = " ";
var user_input = parseFloat(prompt("Enter how many rows:", 0));
while (rows < user_input) {
space_counter = 0;
while (space_counter < rows) { //process1
document.write(space);
space_counter++;
}
document.write("x"); //process2
rows++;
midspace_counter = 0;
while (midspace_counter < user_input - rows) { //process3
document.write(space + space);
midspace_counter++;
}
document.write("x<br>"); //process4
rows++;
}
How i would do that:
let result = "";
const maxRow = +prompt("how many rows?");
// Knowing the half size of our x might be useful
const half = (maxRow - 1) / 2;
for(let row = 0; row < maxRow; row++){
// For e.g. maxRow = 7 this will be 3 2 1 0 1 2 3
const midspace = Math.abs(half - row);
const pad = maxRow - midspace - 1;// 1 = "x"
// \n means newline
result += " ".repeat(pad) + "x" + " ".repeat(midspace * 2) + "x\n";
}
Then you just need to append that to the document.
Okay so I need the output to print hollow squares and I'm at a loss right now. I don't want the answer but I would like some hints to help get me on the right track. Thanks!
"use strict"
if (process.argv.length < 3) {
console.log("Not enough command-line arguments given.");
console.log("Usage: node lab13_4.js num");
process.exit();
}
var width = parseInt(process.argv[2]);
function makeLine(width) {
var L = "";
for(var w = 0; w < width; w += 1) { // repeated width many times
L = L + ".";
}
return L;
}
// print the line some number of times.
function printLines(line, howMany) {
// print the right number of lines
for (var i = 0; i < howMany; i += 1) { // repeated height many times
console.log(line);
}
}
for (var x = 0; x <= width; x += 1) {
var line = makeLine(x);
printLines(line, x);
}
If LENGTH be the length of the sides of the square.
You have to make nested loop and check boundary conditions.
for (var i = 0; i < LENGTH; i++) {
for (var j = 0; j < LENGTH; j++) {
if (i == 0 || i == LENGTH - 1 || j == 0 || j == LENGTH - 1) {
process.stdout.write("*")
} else {
process.stdout.write(" ")
}
}
process.stdout.write("\n")
}
We are looking for a solution like this:
*****
* *
* *
* *
*****
The top and bottom sides of the square have the full amount of stars.
All lines in between will only have one star in the beginning of the line and one star at the end of the line.
I use the following function:
function square(input) {
// top line
console.log('*'.repeat(input));
// middle lines
for(let i = 1; i < input - 1; i++) {
console.log('*' + ' '.repeat(input-2) + '*');
}
// bottom line
console.log('*'.repeat(input));
};
square(5);
hope this makes sense :)