Home > Net >  Javascript linked list freezes program
Javascript linked list freezes program

Time:12-24

I am trying to write a simple web-page embedded program in javascript. Right now, I am attempting to write a linked list of all active buttons on the screen at any given time. However, when I attempt to iterate through a linked list that isn't empty, the program freezes.

This is the relevant block of code:

document.addEventListener("click", (event) => {

    if (seekButtons) {

        if (!(activeButtons.isEmpty())) {
            var runner = activeButtons.head;

            /*
             * 
             * This while loop seems to run indefinitely if the activeButtons list has anything in it.
             * 
             */ 
            while (runner) {
                //this list should be populated only by buttons
                console.log("a button! \n");

                if (runner.element.isInside(mouseX, mouseY)) {
                    document.getElementById("fiddleText").innerHTML = ('clicked');
                    console.log("We are in the button! \n");

                    //This line doesn't seem to work correctly. It's meant to move on to the next item in the list (and to exit the while loop if it's the last item), but the loop runs forever.
                    runner = runner.next;
                }
            }
            

        }
        
    }
});

I think I need another set of eyes to look over this and help me figure out why this isn't working.

The linked list code (not written by me) looks like this:

//this code shamelessly stolen from https://www.geeksforgeeks.org/implementation-linkedlist-javascript/

// User defined class node
class Node {
    // constructor
    constructor(element) {
        this.element = element;
        this.next = null
    }
}

// linkedlist class
class LinkedList {
    constructor() {
        this.head = null;
        this.size = 0;
    }

    // adds an element at the end
    // of list
    add(element) {
        // creates a new node
        var node = new Node(element);

        // to store current node
        var current;

        // if list is Empty add the
        // element and make it head
        if (this.head == null)
            this.head = node;
        else {
            current = this.head;

            // iterate to the end of the
            // list
            while (current.next) {
                current = current.next;
            }

            // add node
            current.next = node;
        }
        this.size  ;
    }

    // insert element at the position index
    // of the list
    insertAt(element, index) {
        if (index < 0 || index > this.size)
            return console.log("Please enter a valid index.");
        else {
            // creates a new node
            var node = new Node(element);
            var curr, prev;

            curr = this.head;

            // add the element to the
            // first index
            if (index == 0) {
                node.next = this.head;
                this.head = node;
            } else {
                curr = this.head;
                var it = 0;

                // iterate over the list to find
                // the position to insert
                while (it < index) {
                    it  ;
                    prev = curr;
                    curr = curr.next;
                }

                // adding an element
                node.next = curr;
                prev.next = node;
            }
            this.size  ;
        }
    }

    // removes an element from the
    // specified location
    removeFrom(index) {
        if (index < 0 || index >= this.size)
            return console.log("Please Enter a valid index");
        else {
            var curr, prev, it = 0;
            curr = this.head;
            prev = curr;

            // deleting first element
            if (index === 0) {
                this.head = curr.next;
            } else {
                // iterate over the list to the
                // position to removce an element
                while (it < index) {
                    it  ;
                    prev = curr;
                    curr = curr.next;
                }

                // remove the element
                prev.next = curr.next;
            }
            this.size--;

            // return the remove element
            return curr.element;
        }
    }

    // removes a given element from the
    // list
    removeElement(element) {
        var current = this.head;
        var prev = null;

        // iterate over the list
        while (current != null) {
            // comparing element with current
            // element if found then remove the
            // and return true
            if (current.element === element) {
                if (prev == null) {
                    this.head = current.next;
                } else {
                    prev.next = current.next;
                }
                this.size--;
                return current.element;
            }
            prev = current;
            current = current.next;
        }
        return -1;
    }

    // finds the index of element
    indexOf(element) {
        var count = 0;
        var current = this.head;

        // iterate over the list
        while (current != null) {
            // compare each element of the list
            // with given element
            if (current.element === element)
                return count;
            count  ;
            current = current.next;
        }

        // not found
        return -1;
    }

    // Helper Methods

    // checks the list for empty
    isEmpty() {
        return this.size == 0;
    }

    // gives the size of the list
    size_of_list() {
        console.log(this.size);
    }

    // prints the list items
    printList() {
        var curr = this.head;
        var str = "";
        while (curr) {
            str  = curr.element   " ";
            curr = curr.next;
        }
        console.log(str);
    }
}

My main code looks like this:

//canvas elements
var canvas = document.getElementById("SnekGamCanvas");
var ctx = canvas.getContext("2d");
canvas.addEventListener('click', function () { }, false);


//some important variables
var px = canvas.width / 2;
var py = canvas.height / 2;

var snekColor = "#EC942D";

var clock = 0;

var mouseX = 0.5;
var mouseY = 0.5;

var activeButtons = new LinkedList();

var seekButtons = true;

//classes

class clickButton {
    constructor(text, color, altColor, width, height, radius, xpos, ypos) {
        this.text = text;
        this.color = color;
        this.altColor = altColor;
        this.width = width;
        this.height = height;
        this.radius = radius;
        this.xpos = xpos;
        this.ypos = ypos;
    }

    isInside(datX, datY) {
        //usually, datX will be mouseX, and datY will be mouseY.
        if (datX > (this.xpos) && datX < (this.xpos   this.width)) {
            if ((datY > this.ypos) && datY < (this.ypos   this.height)) {
                return true;
            }
        }
        return false;
    }

    drawButton() {
        ctx.strokeStyle = "#000000"
        if (this.isInside(mouseX, mouseY)) {
            ctx.fillStyle = this.altColor;

            roundRect(this.xpos, this.ypos, this.width, this.height, this.radius, true, true, this.altColor);

            ctx.fillStyle = "#000000";
            ctx.strokeStyle = "#000000";
            ctx.font = '40px san-serif';

            ctx.strokeText(this.text, this.xpos   10, this.ypos   40);
            ctx.fillText(this.text, this.xpos   10, this.ypos   40);
        }
        else {
            ctx.fillStyle = this.color;

            roundRect(this.xpos, this.ypos, this.width, this.height, this.radius, true, true, this.color);

            ctx.fillStyle = "#000000";
            ctx.strokeStyle = "#000000";
            ctx.font = '40px san-serif';

            ctx.strokeText(this.text, this.xpos   10, this.ypos   40);
            ctx.fillText(this.text, this.xpos   10, this.ypos   40);
        }
        

        //draw_Ball(303, 500, 50, snekColor);
    }

    clickOnButton() {
        document.getElementById("fiddleText").innerHTML = ('clicked');
    }

}

//buttons

var startButton = new clickButton("Start Game", "#74B5ED", "#1824C7", 200, 50, 20, ((canvas.width / 2) - 100), (canvas.height * (4 / 5)));

//images
var seel = new Image();
seel.onload = function () {
    ctx.drawImage(seel, 0, 0, canvas.width, canvas.height);
}
seel.src = "https://assets.pokemon.com/assets/cms2/img/pokedex/full/086.png"

var snek_title = new Image();
snek_title.onload = function () {
    ctx.drawImage(snek_title, 0, 0, canvas.width, canvas.height);
}
snek_title.src = "https://globin347.com/images/Snake Title.png"

//stuff about mouse moving
//the relative mouse position code came from this stackoverflow page: https://stackoverflow.com/questions/17130395/real-mouse-position-in-canvas

function getMousePosX(canvas, evt) {
    var rect = canvas.getBoundingClientRect(), // abs. size of element
        scaleX = canvas.width / rect.width;    // relationship bitmap vs. element for X

    return (evt.clientX - rect.left) * scaleX;   // scale mouse coordinates after they have
}

function getMousePosY(canvas, evt) {
    var rect = canvas.getBoundingClientRect(), // abs. size of element
        scaleY = canvas.height / rect.height;  // relationship bitmap vs. element for Y

    return (evt.clientY - rect.top) * scaleY;
}

document.addEventListener('mousemove', (event) => {
    //document.getElementById("fiddleText").innerHTML = (`Mouse X: ${event.clientX}, Mouse Y: ${event.clientY}`);


    mouseX = getMousePosX(canvas, event);
    mouseY = getMousePosY(canvas, event);
    //document.getElementById("fiddleText").innerHTML = ('mouseX: '   mouseX   ', mouseY: '   mouseY);

    //now convert total position to canvas position
    //mouseX, mouseY = getMousePos(canvas, event);

    //document.getElementById("fiddleText").innerHTML = ('mouseX: '   mouseX   ', mouseY: '   mouseY);
});

//mouse clicking

document.addEventListener("click", (event) => {

    if (seekButtons) {

        if (!(activeButtons.isEmpty())) {
            var runner = activeButtons.head;

            /*
             * 
             * This while loop seems to run indefinately if the activeButtons list has anything in it.
             * 
             */ 
            while (runner) {
                //this list should be populated only by buttons
                console.log("a button! \n");

                if (runner.element.isInside(mouseX, mouseY)) {
                    document.getElementById("fiddleText").innerHTML = ('clicked');
                    console.log("We are in the button! \n");

                    //This line doesn't seem to work correctly. It's meant to move on to the next item in the list (and to exit the while loop if it's the last item), but the loop runs forever.
                    runner = runner.next;
                }
            }
            
        }
        
    }
});


//begin
var gameState = -1;

function draw() {

    clock  = 1;
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    //document.getElementById("fiddleText").innerHTML = ("Clock: "   clock);
    if (gameState == -1) {
        //startup
        setup();
    }
    else if (gameState == 0) {
        //this hasn't been implemented yet
        startMenu();
    }
    else if (gameState == 1) {
        //this hasn't been implemented yet either
        playGame();
    }
    else if (gameState == 2) {
        //ditto
        gameOver();
    }
    else {
        //something's wrong

        ctx.drawImage(seel, 0, 0, canvas.width, canvas.height);

        ctx.fillStyle = "#b30000";
        ctx.strokeStyle = "#000000";
        ctx.font = '140px san-serif';

        ctx.fillText('OH NO', 120, 120);
        ctx.strokeText('OH NO', 120, 120);

        ctx.fillText('IT BLOKE', 200, 630);
        ctx.strokeText('IT BLOKE', 200, 630);
    }

}
setInterval(draw, 10);

function setup() {

    //this should be added to the buttons list before the program starts in earnest

    activeButtons.add(startButton);
    activeButtons.printList();

    //document.getElementById("fiddleText").innerHTML = ('Oh Boy Baby');
    //document.getElementById("fiddleText").innerHTML = ('large');

    gameState = 0;

}

function startMenu() {
    ctx.drawImage(snek_title, 0, 0, canvas.width, canvas.height);

    startButton.drawButton();

    //draw_Ball(mouseX, mouseY, 50, snekColor);
}

function playGame() {
    draw_Ball(200, 700, 50, snekColor);
    draw_Ball(400, 700, 50, snekColor);
    draw_Ball(300, 500, 50, snekColor);
}

function gameOver() {

}

//this function was stolen from stack overflow
function showImage(width, height, image_source, alt_text) {
    var img = document.createElement("img");
    img.src = image_source;
    img.width = width;
    img.height = height;
    img.alt = alt_text;

}

function draw_Ball(bx, by, size, ballColor) {
    ctx.beginPath();
    ctx.arc(bx, by, size, 0, (Math.PI * 2));
    ctx.fillStyle = ballColor;
    ctx.fill();
    ctx.strokeStyle = "#000000";
    ctx.stroke();
    ctx.closePath();
}

//This next function was taken from stack overflow

function roundRect(x, y, width, height, radius, stroke, fill, color) {
    ctx.beginPath();
    ctx.moveTo(x   radius, y);
    ctx.lineTo(x   width - radius, y);
    ctx.quadraticCurveTo(x   width, y, x   width, y   radius);
    ctx.lineTo(x   width, y   height - radius);
    ctx.quadraticCurveTo(x   width, y   height, x   width - radius, y   height);
    ctx.lineTo(x   radius, y   height);
    ctx.quadraticCurveTo(x, y   height, x, y   height - radius);
    ctx.lineTo(x, y   radius);
    ctx.quadraticCurveTo(x, y, x   radius, y);
    if (stroke) {
        ctx.stroke();
    }
    if (fill) {
        ctx.fill();
    }
    ctx.closePath();
    return;
}

And, just in case, here are my css and html files:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>@ViewData["Title"] - Portfolio</title>
    <link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css" />
    <link rel="stylesheet" href="~/css/site.css" />
</head>
<body >
    <header>
        <nav >
            <div >
                <a  asp-area="" asp-controller="Home" asp-action="Index">Portfolio</a>
                <button  type="button" data-toggle="collapse" data-target=".navbar-collapse" aria-controls="navbarSupportedContent"
                        aria-expanded="false" aria-label="Toggle navigation">
                    <span ></span>
                </button>
                <div >
                    <ul >
                        <li >
                            <a  asp-area="" asp-controller="Home" asp-action="Index">Home</a>
                        </li>
                        <!--
                        <li >
                            <a  asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a>
                        </li>
                        -->
                        <li >
                            <a  asp-area="" asp-controller="Home" asp-action="Resume">Resume</a>
                        </li>
                        <!----
                        <li >
                            <a  asp-area="" asp-controller="Home" asp-action="Art3D">3D Art</a>
                        </li>
                        <li >
                            <a  asp-area="" asp-controller="Home" asp-action="Art2D">2D Art</a>
                        </li>
                        <!---->
                        <li >
                            <a  asp-area="" asp-controller="Home" asp-action="Snake">Snake</a>
                        </li>
                        
                        <li >
                            <a  asp-area="" asp-controller="Home" asp-action="CodeExamples">Code Examples</a>
                        </li>

                        <li >
                            <a  asp-area="" asp-controller="Home" asp-action="Ballad">Ballad of the Masked Bandits</a>
                        </li>
                        <!--
    <li >
        <a  asp-area="" asp-controller="Home" asp-action="DataBaseHub">Database Hub</a>
    </li>
    --->
                        <!--
    <li >
        <a  asp-area="" asp-controller="Home" asp-action="Unavailable">???</a>
    </li>
        -->
                        <!--Temporary Links-->
                    </ul>
                </div>
            </div>
        </nav>
    </header>
    <div  id="MainDiv">
        <main role="main"  style="width:100%">
            <!--Where the other code goes-->
            
                @{
                    ViewData["Title"] = "Snake Game";
                }
                
                <div >
                    <h1>Snake Game</h1>
                </div>
                <div ></div>
                <div >
                    <div >
                        <div >
                            <div >
                                <h1>By the power of Javascript, here is a playable snake game.</h1>
                                <div ></div>
                                <h1 id="fiddleText">Give it a moment to load.</h1>
                            </div>
                
                            <div ></div>
                
                            <div >
                                <canvas onl oad="draw()"  id="SnekGamCanvas" width="1000" height="1000"></canvas>
                            </div>
                
                        </div>
                
                    </div>
                
                    <div >
                        <div ></div>
                
                        <a  asp-area="" asp-controller="Home" asp-action="Index">Back to Home</a>
                
                        <div ></div>
                    </div>
                
                    <!--The code be here but if you are reading this you probably already knew that-->
                    <script src="~/js/Snake.js"></script>
                
                </div>
                
        </main>
    </div>

    <footer >
        <div >
            &copy; 2021 - Portfolio - <a asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a>
        </div>
    </footer>
    <script src="~/lib/jquery/dist/jquery.min.js"></script>
    <script src="~/lib/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
    <script src="~/js/site.js" asp-append-version="true"></script>
    <script src="../jsc3d-master/jsc3d/jsc3d.js"></script>
    @RenderSection("Scripts", required: false)
</body>
</html>

.

/* Please see documentation at https://docs.microsoft.com/aspnet/core/client-side/bundling-and-minification
for details on configuring this project to bundle and minify static web assets. */

a.navbar-brand {
  white-space: normal;
  text-align: center;
  word-break: break-all;
}

/* Provide sufficient contrast against white background */
a {
  color: #0366d6;
}

.btn-primary {
  color: #fff;
  background-image: linear-gradient(30deg, #b6e2dd, #2a5efe);
  border-color: #1861ac;
}

/*Link colors*/
.nav-pills .nav-link.active, .nav-pills .show > .nav-link {
  color: #fff;
  background-color: #1b6ec2;
  border-color: #1861ac;
}

/* Sticky footer styles
-------------------------------------------------- */
html {
  font-size: 14px;
}
@media (min-width: 768px) {
  html {
    font-size: 16px;
  }
}

.border-top {
  border-top: 1px solid #e5e5e5;
}
.border-bottom {
  border-bottom: 1px solid #e5e5e5;
}

.box-shadow {
  box-shadow: 0 .25rem .75rem rgba(0, 0, 0, .05);
}

button.accept-policy {
  font-size: 1rem;
  line-height: inherit;
}

/* Sticky footer styles
-------------------------------------------------- */
html {
  position: relative;
  min-height: 100%;
}

body {
  /* Margin bottom by footer height */
  margin-bottom: 60px;
}
.footer {
  position: absolute;
  bottom: 0;
  width: 100%;
  white-space: nowrap;
  line-height: 60px; /* Vertically center the text there */
}

/* My Stuff
--------------------------------------------------------------------------
--------------------------------------------------------------------------
--------------------------------------------------------------------------
*/

/*This gives me more control over the exact dark background color*/
.dark-bg
{
    background-color: #161631;
}

.purple_gradient 
{
    /*The image used*/
    background-image: linear-gradient(#4b1ac4, #fff);

    height:100%;
    width:100%;

    background-position: center;
    background-repeat: no-repeat;
    background-size: cover;
}

.test_box_blue
{
    /* A container with a solid color and an outline */
    background-color: #2d1eb2;

    width: 100%;
    height: 100%;
    margin: 0px;

}

.test_box
{
    border:solid #000000;
}

#MainDiv
{
    padding:0;
    margin:0;

    left:0;
    top:0;

    width:100%;
    height:100%;
}

.tundra_backround
{
    background-image: url('../images/Tundra_Fixed.png');
    width:100%;
    height:100%;
}

.white_space_box
{
    height:50 px;
}

.background_gradient
{
    background-image:linear-gradient(320deg, #fff, #96cbde);
}

.glo_button
{
    min-width: 30%;
    height: 20%;
    border-radius: 25px;
    padding: 20px;
    margin: 10px;
    box-shadow: 0 8px 16px 0 rgba(0,0,0,0.2), 0 6px 20px 0 rgba(0,0,0,0.19);
    transition-duration: 0.4s;
    border: 4px solid #000;
}

.big_r_button {
    background-color: #a10000;
    color: #fff;
}

.big_r_button:hover {
    color: #fff;
    background-color: #4e0505;
}

.big_b_button {
    background-color: #080e9f;
    color: #fff;
}

.big_b_button:hover {
    color: #fff;
    background-color: #161631;
}

.big_g_button {
    background-color: #0a7727;
    color: #fff;
}

.big_g_button:hover {
    color: #fff;
    background-color: #07340e;
}

.big_p_button {
    background-color: #6f1cbf;
    color: #fff;
}

.big_p_button:hover {
   color: #fff;
   background-color: #2a073e;

}

.buffer
{
    padding: 20px;
}

.big_text
{
    font-size: 60px;
    font-family:'Times New Roman', Times, serif;
    text-shadow: 2px 2px rgb(12 14 39 / 0.67);
}

.fancy_text_box{
    background-image: linear-gradient(300deg, #ece1c4, #c99e69);
    border-radius: 25px;
    border: 4px solid #5d3c08;
}

.simple_text_box{
    background-color: #fff;

    border: 2px solid #000;
}

.ghostly_text_box{
    background-color: rgb(255 255 255 / 0.60);
    border-radius: 25px;
    padding: 10px;
    border: 3px solid #000;
}

.thick_border{
    border: 4px solid #000;
}

.black_and_white_gradient{
    background-image: linear-gradient(310deg, #fff, #000);
}

.red_border{
    padding: 0px;
    margin: 0px;
    border: 4px solid #8f0000;
}

.model_box{
    border: 4px solid #000;
    background-color: #fff;
    border-radius: 25px;
}

.image_box{
    border: 4px solid #000;
    background-color: #fff;
}

.chain_image_box {
    border-top: 4px solid #000;
    border-left: 4px solid #000;
    border-right: 4px solid #000;
    border-bottom: 0px;
    background-color: #fff;
}

.margin_setter {
    margin: 20px;
    padding: 20px;
}

#model_display_1{

}

CodePudding user response:

You're only moving to the next runner when the current runner is inside the button. So when your while loop gets to a runner that isn't in the button, it gets stuck on that element and loops infinitely.

Take the runner = runner.next; line out of the if.

            while (runner) {
                //this list should be populated only by buttons
                console.log("a button! \n");

                if (runner.element.isInside(mouseX, mouseY)) {
                    document.getElementById("fiddleText").innerHTML = ('clicked');
                    console.log("We are in the button! \n");
                }
                runner = runner.next;
            }

A for loop might make things simpler:

for (let runner = activeButtons.head; runner; runner = runner.next) {
    if (runner.element.isInside(mouseX, mouseY)) {
        document.getElementById("fiddleText").innerHTML = ('clicked');
        console.log("We are in the button! \n");
    }
}
  • Related