Life's Game (html, javascript, css)

I'm struggling with creating a JavaScript code to solve the Game of Life, especially with my run() function that uses a while loop. I can't seem to figure out what's going wrong. Any ideas?

The full exercise description is in Hungarian only. Feel free to ask if you need more information! :)

Here's the HTML structure:

<!DOCTYPE html>
<html lang="hu">
<meta charset="UTF-8">
<title>Game of life</title>
<meta name="viewport" content="width=device-width,initial-scale=1">
<link rel="stylesheet" href="gameoflife.css">
<body>

<h1>Game of life</h1>
<form>
    <label>Size of the matrix [row x column]:</label>
    <select id="row" >
        <option value="5">5</option>
        <option value="6">6</option>
        ...
        <option value="20" selected>20</option>
    </select>
    ...
            playButton.setAttribute("onclick", "run()");
    form.appendChild(playButton);
}

function baseStatement() {
    for (let i = 1; i < row-1; i++)
    {
        for (let j = 1; j < column-1; j++) {
            var ij = i.toString() + "|" + j.toString();
            ...

Providing insights into the JavaScript functionalities:

var form = document.getElementById("matrix");
var row = 0;
var column = 0;

function emptyMatrix()
{
    ...
}

function nextRound() {
    ...
}

function run() {
    //setInterval(nextRound, 5000);
    ...
}

This is how the CSS looks like:

body {
  background-color : saddlebrown;
  font-family      : Arial, Helvetica, sans-serif;
  color            : white;
  }
input:checked {
  accent-color : darkgreen;
  }

Thank you for any help you can provide! :)

Answer №1

It feels like a lifetime ago when I first crafted that code...

// initiate ranges interface...
document
  .querySelectorAll('input[type=range]')
  .forEach(r => r.oninput =_=> r.closest('label').dataset.val=r.value)

const
  action = { run: false, cycle: 0 }
, size   = { cols: 0, rows:0 }
, onAction      = function* () { while(action.run) yield }
, delay         = ms => new Promise(r => setTimeout(r, ms))
, isAlive       = (r,c) => tbl.rows[r].cells[c].classList.contains('life')
, adjCellsCount = (row,col) =>
  {
  let res = 0
  for ( let r = Math.max(0, row-1); r < Math.min(size.rows, row+2); ++r )
  for ( let c = Math.max(0, col-1); c < Math.min(size.cols, col+2); ++c )
    if ((r!==row || c!==col) && isAlive(r,c) ) res++
  return res
  }
, StopGridAction =_=>
  {
  btClear.disabled = btGo.disabled = action.run = false
  btStop.disabled = !btGo.disabled
  tbl.className = 'onBorder'    
  }

cycles.onclick =_=>  // reset cycles count
  {
  cycles.textContent = action.cycle = 0
  } 
addPoints.onclick =_=>  // create new life points
  {
  let 
    freePos = [...tbl.querySelectorAll('td:not([class="life"]')] 
  , ptN     = Math.min( freePos.length, Points.valueAsNumber )
    ;
  if (!ptN) return

  for (let i = freePos.length -1; i > 0; i--)  // shuffle Array
    {
    let j = Math.floor(Math.random() * (i + 1));
    [freePos[i], freePos[j]] = [freePos[j], freePos[i]]
    }
  for (;ptN--;) freePos[ptN].className = 'life'
  }
btDrawGrid.onclick = _ =>      // draw Grid Button
  {
  StopGridAction()
  tbl.innerHTML = ''  // clear Grid
  tbl.style.setProperty('--sz', cellSz.value + 'px')
  size.cols = nCols.valueAsNumber
  size.rows = nRows.valueAsNumber

  for ( let r = 0; r < size.rows; ++r) // construct Grid 
    {
    let nRow = tbl.insertRow()
    for ( let c = 0; c < size.cols; ++c) nRow.insertCell()
    }
  addPoints.click()
  cycles.click()
  }
tbl.onclick = ({target}) =>  // add or remove points on Grid
  {
  if(!target.matches('td')) return
  target.className = target.classList.contains('life') ? '' : 'life';
  }

// initial setup...
btStop.disabled = true
nCols.value     = 20
nRows.value     = 12
cellSz.value    = 16
Points.value    = 24

btDrawGrid.click()
addPoints.click()

// main sections...
btStop.onclick = StopGridAction  // stop Conway's Game of Life action
btClear.onclick =_=>  // clear Grid
  {
  tbl.querySelectorAll('td').forEach(el=>el.className = '')
  } 
btGo.onclick =_=>  // Start button -> see Conway's Game of Life action
  {
  gridOptions.open = false
  btClear.disabled = btGo.disabled = action.run = true
  btStop.disabled  = !btGo.disabled
  tbl.className    = ''
  loopLifes()
  }
async function loopLifes()
  {
  for await (x of onAction())
    {
    cycles.textContent = (++action.cycle).toLocaleString()
    await lifeCycle()
    await delay (300)
    tbl.querySelectorAll('td.die').forEach(el=>el.className = '')
    tbl.querySelectorAll('td.born').forEach(el=>el.className = 'life')
    await delay (800)
    }
  btStop.click()
  }

function lifeCycle()
  {
  return new Promise((successCallback) =>
    {
    let cLives = 0  
    for ( let r = 0; r < size.rows; ++r)
      {
      if ( !action.run ) break
      for ( let c = 0; c < size.cols; ++c) 
        {
        if ( !action.run ) break
        let aroundLife = adjCellsCount(r,c)
        if (isAlive(r,c))
          {
          if ( aroundLife < 2 || aroundLife > 3 )
            tbl.rows[r].cells[c].classList.add('die')
          else
            ++cLives
          }
        else
          {
          if (aroundLife === 3 ) 
            {
            tbl.rows[r].cells[c].className = 'born'
            ++cLives
      } } } }
    action.run &= Boolean(cLives)  
    successCallback()   
    })
  }
table  {
  border-collapse  : collapse;
  margin           : 1em;
  background-color : white;
  }
table tbody {
  --sz : 12px;      
  }
table tbody td {
  width    : var(--sz);
  height   : var(--sz);
  border   : 1px solid transparent;
  position : relative;
  cursor   : pointer;
  }
table tbody td:hover {
  background-color : #28608371;
  }
table tbody:hover td,
table tbody.onBorder td {
  border-color : #d3d3d3cc;
  }
.born::before,
.die::before,
.life::before {
  position : absolute;
  content   : ' ';
  top       : 0;
  left      : 0;
  width      : 100%;
  height     : 100%;
  display      : inline-block;
  border-radius : 50%;
  }
.born::before { background-color : lightseagreen;        }
.die::before  { background-color : #f0808080 !important; }
.life::before { background-color : #286083;              }

body {
  font-family : Arial, Helvetica, sans-serif;
  font-size   : 16px;
  }
label {
  font-size : 12px;
  }
label input {
  vertical-align : middle;
  width          : 300px;
  }
label::after {
  content : ' ' attr(data-val);
  }
label::before {
  display : inline-block;
  width   : 4.5em;
  content : attr(data-lib);
  }
button {
  margin-top     : .4em; 
  min-width      : 4em;
  text-transform : capitalize;
  }
details {
  position      : absolute;
  top           : 1.7em;
  right         : .5em;
  width         : 7em;
  padding       : .4em .6em .3em .6em;
  background    : #286083e5;
  z-index       : 100;
  color         : white;
  border-radius : .4em;
  }
details[open] {
  width      : 24em;
  }
details label:first-of-type {
  display: inline-block;
  margin-top: 1.2em;
  }
summary {
  padding-right : .2em;
  text-align    : right;
  direction     : rtl;
  }
summary,
button:not(:disabled) {
  cursor : pointer;
  }
details button {
  float: right;
  }
mark {
  position      : absolute;
  top           : .4em;
  right         : .5em;
  width         : 7.6em;
  padding       : 0 .3em;
  text-align    : right;
  background    : none;
  cursor        : pointer;
  color         : darkgrey;
  border-radius : .2em;
  font-style    : oblique;
  }
mark:hover {
  background : #286083a2;
  color      : white;
  }
<h3> Conway's Game of Life </h3>
<hr>
<details id="gridOptions">
  <summary>Grid options</summary>
  <label for="nCols" data-val="20" data-lib="Cols :" ><input type="range" id="nCols" min="6" max="60" value="20"></label> <br>
  <label for="nRows" data-val="12" data-lib="Rows :"><input type="range" id="nRows" min="6" max="60" value="12"></label> <br>
  <label for="cellSz" data-val="16" data-lib="size :"><input type="range" id="cellSz" min="6" max="40" value="16" style="width:100px;"></label><small>px</small> <br>
  <label for="Points" data-val="24" data-lib="Point(s) :"><input type="range" id="Points" min="1" max="50" value="24" style="width:240px;"></label><br>
  <button id="btDrawGrid"> draw grid </button>
</details>
<mark id="cycles">0</mark>
<button id="btGo"> go </button>
<button id="btStop"> stop  </button>
<button id="btClear"> clear </button>
<button id="addPoints"> Add Point(s) </button>
<table><tbody id="tbl" class="onBorder"></tbody></table>

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Developing dynamic progress indicators in Django - A guide

I'm in the process of figuring out how to create a real-time progress bar for updating. The idea is that the server will update the user periodically on the current progress. Fortunately, I am familiar with making an Ajax call using Django and jQuery ...

What is the JavaScript design for creating plugins?

[I'm brand new to the world of Javascript, so please bear with me.] I am developing an application in node.js that will feature a collection of plugins. Each "plugin" will consist of a function, or possibly two, that can manipulate a string in some w ...

Display Further/Hide Fewer every third item using Jquery

I am looking to display only the first 3 list items within each div.content. Then, upon clicking "SHOW MORE", I want to reveal the next 3 items. If the user clicks on "SHOW LESS", I need the display to revert back to showing only the initial 3 list items. ...

Utilizing numerous script elements for three.js library

Today, I ventured into the world of three.js for the first time, along with exploring html and js in general. While experimenting with some example code, I encountered an issue. It seems that importing the three.js file from the script tag in my Main.js ...

Is there a way to update my website with a google map feature that allows mousewheel events for panning instead of zooming?

I am currently working on a unique project that involves creating a mashup with Google Maps (v3) to display real-time GPS data of Boston's buses. While Google typically uses scrolling for zooming in the maps, I believe using panning would be more prac ...

How can one extract the input value from a Bootstrap modal field and use jQuery to display it in another input field within the same modal?

Here is the HTML code I am working with: <div class="modal fade" id="modalTambahDataTransaksiZakat"> <div class="vertical-alignment-helper"> <div class="modal-dialog vertical-align-center"> <div class="modal-content"> ...

Switch between Fixed and Relative positions as you scroll

Just a small adjustment is needed to get it working... I need to toggle between fixed and relative positioning. JSFiddle Link $(window).on('scroll', function() { ($(window).scrollTop() > 50) ? ($('.me').addClass('fix ...

JavaScript mouse and touch movement events (mousemove, pointermove, touchmove) are not always accurate

I'm currently working on developing a JavaScript whiteboard and have implemented the following code: let lastTimestamp = 0; const fps = 1000/60; document.addEventListener("pointermove", moveMouse, false); function moveMouse (e) { e.preve ...

Display a modal pop-up containing HTML content

I recently started building a small website using Zurb Foundation. I have created a basic grid layout with large metro style div thumbnails (about 10 on the page). Now, I want to enhance user interaction by implementing modal windows that appear when a th ...

"Seeking guidance on getting my carousel functionality up and running in Angular 8 - any

I tried implementing a carousel from the Bootstrap 4 documentation, but it is only displaying one image. How can I modify the carousel to show all images? I am new to using Angular. Below is the code I have: <div class=" bg-success text-white py-5 tex ...

Working with external data in D3.js involves loading and manipulating datasets efficiently

As a newcomer to D3.js, I have been exploring various tutorials and exercises to familiarize myself with it. My primary goal with D3 is to load external data, typically in JSON format, and create interactive charts based on that data. You can find the bas ...

I am struggling to make callback functions function properly with an ajax GET request

I'm struggling to extract specific elements from a JSON file retrieved from an external REST API and store them in a local dictionary variable for use in my JavaScript code. While my AJAX GET request is successful, I'm facing an issue where I can ...

What is the best way to add my state elements to the state array?

I am currently facing an issue with my input handler function while trying to push state elements (user_number_X) into an array (this.state.results.numbers). Is there a better way to rewrite this code so that it successfully adds input values into the arr ...

The Less compiler (lessc) encounters an issue on a fresh operating system. ([TypeError: undefined is not a function])

After setting up my new development environment on Windows 10, I encountered an issue with less. Following the instructions on lesscss.org, I installed less using: npm install -g less The installation process completed without any errors. However, when ...

Changing a single array into a series of arrays

I'm attempting to reverse the flattening process of an array. The JSON array I have as input contains 4 elements: [ { "nestedObj": { "id":12 } }, { "nestedObj": { "id":555 } ...

It appears that the JavaScript code for validating the form is not being executed

My task involves validating a form using JavaScript to display error messages for empty input fields. However, I am encountering an issue where the code does not trigger on submit. http://jsfiddle.net/LHaav/ Here is the HTML snippet: <head> ... ...

50% greater than the highest of the other values

I'm a beginner when it comes to JavaScript and I need help setting a maximum value of 50% of the selling price. Can someone offer guidance? The fields I have are called "sale_price" and "discount". click here for image description What is the best ...

Troubleshoot my code for clustering markers on a Google map

I'm currently working on a piece of code to generate a Google map that contains 3 hidden points within one marker. The idea is that when the main marker is clicked, these points will either merge into one or expand into 3 separate markers. However, I& ...

What is the best way to dynamically filter checkboxes based on user input using jQuery in the following code snippet?

In order to filter the checkboxes in the code below, I would like to only display the checkbox with a value matching the input entered in the input field with the class "js-filter-input". For example, if I type "uni1" in the input field, I want to show onl ...

Upload a gltf file to a three.js environment using an HTML `<input>` tag

Struggling to incorporate a gltf object into a three.js scene by allowing users to upload it via an HTML input tag. The goal is to choose a specific file from the client's computer, display it on the website, compress it, and then transfer it to a mu ...