DearDiary 1.0 Help

A full project using LLM only

I don't know anything useful about HTML/CSS/JS/React/Node/Express/GraphQL/REST/... but I know how to use LLM to generate code. So i'll make a webapp using LLM only and see how far i can go.

I'll use Jetbrain Webstorm, with the Github Copilot & AI Assistant plugin. Wish me luck ?

  • Most of the text here will be the result of the AI Assistant, with some comments and explanations from me.

  • 100% of the code will be generated by the AI Assistant.

Selecting a framework

Webstorm have the following project templates:

  • Angular CLI

  • Bootstrap

  • Express

  • HTML5 Boilerplate

  • Next.js

  • Node.js

  • React

  • React Native

  • Vite

  • Vue.js

I'll ask Bard what i should use.

...

Well, i'll just use HTML5 Boilerplate. It has things, and stuff. Aaaaand, I'm already lost. Should i start with an empty project ? Yes.

What now ?

Ask JetBrains AI Assistant.

To start creating an HTML5 Canvas WebApp, follow these steps:

  1. Create HTML file: First, you need to create an HTML file which will serve as the entry point to your application. Right-click on the project explorer, select New -> HTML file, give it a name, and press Enter.

  2. Add canvas element in HTML file: Add a <canvas> element to your HTML file. The canvas element is a container for graphics, where you can draw graphics via JavaScript.

<canvas id="myCanvas" width="480" height="320"></canvas>
  1. Create JavaScript file: Create a new JavaScript file where we will add our code for the Canvas. Like before, right-click on the project explorer, select New -> JavaScript file, give it a name, and press Enter.

  2. Draw on canvas: In your JavaScript file, you can obtain a reference to the canvas and then draw on it. Here is some example code that draws a rectangle:

let canvas = document.getElementById('myCanvas'); let ctx = canvas.getContext('2d'); ctx.fillStyle = "#FF0000"; ctx.fillRect(0, 0, 150, 75);

Link JavaScript file in HTML file: Don't forget to include the JavaScript file into the HTML file: <script src="your_filename.js"></script>

  1. Run Your Project: Right click on your HTML file in the project explorer and click "Open in Browser" to see your canvas in action. You can expand on the above steps to include more features for your specific WebApp. You might want to add event listeners for user interactions, or create game logic if you're planning to develop a canvas-based game. Remember to frequently save your work and make sure to commit your changes in your version control system to keep track of your project's progression.

index.html

Well i guess i should have written a better prompt. But ok, i created a default index.html.

<!DOCTYPE html> <html> <head> <title>Fullscreen Canvas</title> <style> * { margin: 0; padding: 0; } #myCanvas { position: absolute; width: 100%; height: 100%; } </style> </head> <body> <canvas id="myCanvas"></canvas> <script> const canvas = document.getElementById('myCanvas'); canvas.width = window.innerWidth; canvas.height = window.innerHeight; </script> </body> </html>

Here is how it works:

  • The canvas is positioned absolutely, and it spans the full width and height of the page with CSS width and height set to 100%.

  • The script tag at the end of the body is setting the canvas width and height to match the window's innerWidth and innerHeight. This makes the canvas truly full screen.

Well, it looks like it works

Refactoring

Following the initial instructions, I created an empty main.js file. But first, let's as AI assistant to suggest refactoring. Here is the result:

index.html

<!DOCTYPE html> <html> <head> <title>Fullscreen Canvas</title> <link rel="stylesheet" type="text/css" href="style.css"> </head> <body> <canvas id="myCanvas"></canvas> <script src="app.js"></script> </body> </html>

style.css

* { margin: 0; padding: 0; } #myCanvas { position: absolute; width: 100%; height: 100%; }

app.js

const canvas = document.getElementById('myCanvas'); canvas.width = window.innerWidth; canvas.height = window.innerHeight;

Customizing AI Assistant

  • There is a conflict between AI Assistant and Copilot for auto-completion.

  • I didn't ask for documentation, therefore i didn't get documentation. But we can create custom AI actions to add documentation to the code.

Done. And it works :

// Fetch the HTML canvas by its Id 'myCanvas' const canvas = document.getElementById('myCanvas'); // Set the canvas width to match the window's inner width canvas.width = window.innerWidth; // Set the canvas height to match the window's inner height canvas.height = window.innerHeight;
/* * Reset rule: * Applies to every element. It resets the margin and padding to 0 * to remove the browser's default styling. */ * { margin: 0; padding: 0; } /* * #myCanvas rule: * Applies to the element with id "myCanvas". * This element is set to an absolute position, * and having 100% width and height makes it occupy the full viewport. */ #myCanvas { position: absolute; width: 100%; height: 100%; }

Reset rule ?

WTH is a reset rule ? I'll ask for more information.

A CSS reset rule is used to reduce browser inconsistencies in things like default line heights, margins, and font sizes of headings, etc. It involves setting the styles that you want to have as the defaults in every browser, thus setting a level playing field for all of your styles moving forward. In your provided CSS snippet, this line of code:

* { margin: 0; padding: 0; }

It is indeed a CSS Reset rule. It targets all (*) elements and sets their margin and padding properties to 0, removing the default space that browsers apply to these properties for most elements. It's a way to maintain unified styles across different browsers and ensure your design looks consistent. The * selector in CSS is used to select all elements.

Make it square

I asked AI Assistant to make the canvas square. Here is the result:

// Fetch the HTML canvas by its ID 'myCanvas' const canvas = document.getElementById('myCanvas'); // Get the smaller dimension between the window's inner width and height const minDimension = Math.min(window.innerWidth, window.innerHeight); // Set the canvas width and height to match the smaller dimension canvas.width = minDimension; canvas.height = minDimension;

But there is a problem, the canvas does not stay square. I'll ask for help.

This happens because the size of the canvas is only set when the page initially loads, not when the window is resized. To keep the canvas square even when the window is resized, you can add an event listener for the window's resize event, and update the canvas dimensions in the event handler. Here's how you can modify your code to do this:

// Fetch the HTML canvas by its ID 'myCanvas' const canvas = document.getElementById('myCanvas'); // Function to update canvas dimensions function updateCanvasDimension() { // Get the smaller dimension between the window's inner width and height const minDimension = Math.min(window.innerWidth, window.innerHeight); // Set the canvas width and height to match the smaller dimension canvas.width = minDimension; canvas.height = minDimension; } // Update canvas dimensions initially updateCanvasDimension(); // Add an event listener for the resize event window.addEventListener('resize', updateCanvasDimension);

(I'll cut the explanation)

#myCanvas { width: 100%; height: 100%; aspect-ratio: 1 / 1; }

Now it works. I asked AI to suggest refactoring and find problem. It's mostly about responsive design, but i don't care.

However

it look like a canvas have an internal size and a display size. I'd like to have the internal size to a fixed known value.

// Fetch the HTML canvas by its ID 'myCanvas' const canvas = document.getElementById('myCanvas'); // Set the canvas width and height to fixed size of 1024 canvas.width = 1024; canvas.height = 1024; // Get the context of the canvas const context = canvas.getContext('2d'); // Set the canvas background to black context.fillStyle = 'black'; context.fillRect(0, 0, canvas.width, canvas.height);

Refactoring again and setting up the structure of the application

I'll spare the back and forth with AI Assistant, here is where i am so far. I didn't write a single line of code, i asked AI Assistant "refactor" stuff, "rewrite" things, and it did.

app.js

// Constants definition const CANVAS_SIZE = 1024; const UPDATE_INTERVAL = 1000; let canvas; let context; // Define setup function function setup() { // Fetch the HTML canvas by its id 'myCanvas' canvas = document.getElementById('myCanvas'); if (!canvas) { console.error("Unable to find canvas"); return; } // Get the context of the canvas context = canvas.getContext('2d'); if (!context) { console.error("Unable to get canvas context"); return; } setupCanvas(canvas, context); // Register your keydown callback window.addEventListener('keydown', handleKeydown); // Register your mouse event callback window.addEventListener('click', handleMouseEvent); console.log('Setup function called'); } function setupCanvas(canvas, context) { // Set the canvas width and height to predefined size canvas.width = CANVAS_SIZE; canvas.height = CANVAS_SIZE; // Set the canvas background to black context.fillStyle = 'black'; context.fillRect(0, 0, canvas.width, canvas.height); } // Define update function function update() { // Put your update logic here console.log('Update function called'); } // Define draw function function draw() { // Put your draw logic here console.log('Draw function called'); // Call requestAnimationFrame again to get smooth animation requestAnimationFrame(draw); } // Replace keypress with keydown callback function handleKeydown(event) { console.log('Keydown event: ', event.key); } // Define your mouse event callback function handleMouseEvent(event) { console.log('Mouse event: ', event.type); } // Call setup function setup(); // Call update function every 'interval' milliseconds setInterval(update, UPDATE_INTERVAL); // Call draw function at each animation frame requestAnimationFrame(draw);

I can't use AI Assistant autocompletion with my french keyboard setup. It's really frustrating, i'll enable Copilot again.

Draw grid

// Define drawGrid function function drawGrid() { context.strokeStyle = '#333'; // Dark color but not black context.lineWidth = 1; for(let i = 0; i <= GRID_SIZE; i++) { context.beginPath(); context.moveTo(i * GRID_CELL_SIZE, 0); context.lineTo(i * GRID_CELL_SIZE, CANVAS_SIZE); context.stroke(); context.beginPath(); context.moveTo(0, i * GRID_CELL_SIZE); context.lineTo(CANVAS_SIZE, i * GRID_CELL_SIZE); context.stroke(); } }

Fixing CSS

/* * Reset rule: * Applies to every element. It resets the margin and padding to 0 * to remove the browser's default styling. */ * { margin: 0; padding: 0; box-sizing: border-box; /* Ensures padding and border are included in an element's total width and height. */ } /* * Body rule: * Set the background color to black, * display it as a flex container which will allow the easy * alignment of child elements (like canvas). Make it fill * the full viewport and center align the child elements vertically * and horizontally. */ body { background-color: black; display: flex; justify-content: center; align-items: center; min-height: 100vh; } /* * #myCanvas element styling rule: * This rule targets an HTML element with an id of myCanvas. * The width and height of myCanvas are each set to 100vh, * which means 100% of the viewport's height. The viewport is * the user's visible area of the web page, so this effectively * makes the size of myCanvas responsive to the height of the * viewport. */ #myCanvas { width: 100vh; height: 100vh; }

I still didn't write a single line of code or comment. I didn't use auto-completion.

Just a dialog with AI Assistant. I have no idea what i'm doing. It works.

toggle grid

I asked GitHub Copilot for a change

"use strict"; // Constants definition const CANVAS_SIZE = 1024; const UPDATE_INTERVAL = 1000 / 60; const GRID_SIZE = 128; const GRID_CELL_SIZE = CANVAS_SIZE / GRID_SIZE; let canvas; let context; let isDrawingEnabled = true; // Add this line // Define setup function function setup() { // ... existing code ... } function setupCanvas(canvas, context) { // ... existing code ... } // Define drawGrid function function drawGrid() { // ... existing code ... } // Update draw function function draw() { // Clear the current canvas context.clearRect(0, 0, canvas.width, canvas.height); // Redraw the setup canvas //setupCanvas(canvas, context); // Draw the grid only if isDrawingEnabled is true if (isDrawingEnabled) { drawGrid(); } requestAnimationFrame(draw); } function update() { // ... existing code ... } function handleKeydown(event) { // If 'r' key is pressed, toggle isDrawingEnabled if (event.key === 'r') { isDrawingEnabled = !isDrawingEnabled; } console.log('Keydown event: ', event.key); } function handleMouseEvent(event) { // ... existing code ... } setup(); setInterval(update, UPDATE_INTERVAL); requestAnimationFrame(draw);

And commented to call to setupCanvas in draw function, manually. It's there for a reason, (if the display size change) but i don't care.

A problem with AI Assistant

I'm facing a frustrating problem. AI Assistant doesn't know the context of the code. Unless a specifically provide the code in the prompt, the answer will always be out of context as if i was not in an IDE. Providing the code gets me the right answer, but it's not practical when there is a lot of code in multiple files.

I don't know what to do. I'll ask AI Assistant, of course, but my guess is that i'll have to do some good old google search. (laaaame!)

Define artificial lifeforms

To implement autonomous artificial life that can live, evolve, compete, reproduce, and mutate in the grid, you can follow these steps:

  • Define a class LifeForm that will represent an individual life form in your world. This class should have properties like health, strength, speed, reproductionRate, mutationRate, etc. It should also have methods for actions like move(), eat(), reproduce(), mutate(), etc.

  • In your update() function, iterate over all the life forms in your world. For each life form, call its move() method to change its position in the grid, call its eat() method to interact with the environment or other life forms, and call its reproduce() method to potentially create new life forms.

  • In the reproduce() method, create a new life form with properties based on the parent life form. You can introduce variation by adding a random factor influenced by the mutationRate.

  • In the eat() method, implement the logic for life forms to compete with each other. For example, a life form could gain health by eating another life form, but only if its strength is higher.

  • Periodically remove life forms that have health below a certain threshold, representing death.

Here is a basic implementation of the LifeForm class and the updated update() function:

class LifeForm { constructor(x, y) { this.x = x; this.y = y; this.health = Math.random(); this.strength = Math.random(); this.speed = Math.random(); this.reproductionRate = Math.random(); this.mutationRate = Math.random(); } move() { // Implement movement logic here } eat() { // Implement eating logic here } reproduce() { if (Math.random() < this.reproductionRate) { let child = new LifeForm(this.x, this.y); child.health = this.health + (Math.random() - 0.5) * this.mutationRate; child.strength = this.strength + (Math.random() - 0.5) * this.mutationRate; child.speed = this.speed + (Math.random() - 0.5) * this.mutationRate; child.reproductionRate = this.reproductionRate + (Math.random() - 0.5) * this.mutationRate; child.mutationRate = this.mutationRate + (Math.random() - 0.5) * this.mutationRate; return child; } return null; } } let lifeForms = [new LifeForm(0, 0)]; function update() { for (let i = 0; i < lifeForms.length; i++) { let lifeForm = lifeForms[i]; lifeForm.move(); lifeForm.eat(); let child = lifeForm.reproduce(); if (child !== null) { lifeForms.push(child); } if (lifeForm.health <= 0) { lifeForms.splice(i, 1); i--; } } }

This is a very basic implementation and there are many ways you could expand on this. For example, you could introduce different types of life forms with different properties and behaviors, implement more complex interactions between life forms, or add a graphical representation of the life forms in your grid.

Bugs

The update() function in your code is freezing the application because it's creating an infinite loop. This happens when a new life form is added to the lifeForms array during the iteration over the same array.

Here's the problematic part:

let child = lifeForm.reproduce(); if (child !== null) { lifeForms.push(child); }

In this code, if lifeForm.reproduce() returns a new life form, it gets added to the lifeForms array. Since this is happening inside a loop that's iterating over lifeForms, it can create an infinite loop if reproduce() keeps returning new life forms.
To fix this, you can create a separate array to hold the new life forms, and then add them to lifeForms after the loop.

Here's how you can do it:

function update() { let newLifeForms = []; for (let i = 0; i < lifeForms.length; i++) { let lifeForm = lifeForms[i]; let child = lifeForm.reproduce(); if (child !== null) { newLifeForms.push(child); } if (lifeForm.health <= 0) { lifeForms.splice(i, 1); i--; } } lifeForms = lifeForms.concat(newLifeForms); console.log('Update function called'); }

In this updated update() function, new life forms are added to the newLifeForms array instead of directly to lifeForms. After the loop, newLifeForms is concatenated to lifeForms, adding all the new life forms at once. This prevents the infinite loop from occurring.

To add a log every time a new child is created, you can add a console.log() statement in the reproduce() method of the LifeForm class, right after a new child is created.

Here's how you can do it:

reproduce() { if (Math.random() < this.reproductionRate) { let child = new LifeForm(this.x, this.y); child.health = this.health + (Math.random() - 0.5) * this.mutationRate; child.strength = this.strength + (Math.random() - 0.5) * this.mutationRate; child.speed = this.speed + (Math.random() - 0.5) * this.mutationRate; child.reproductionRate = this.reproductionRate + (Math.random() - 0.5) * this.mutationRate; child.mutationRate = this.mutationRate + (Math.random() - 0.5) * this.mutationRate; console.log('A new child has been created'); return child; } return null; }

This will print "A new child has been created" to the console every time a new child is created.

Manual fix

I decided to remove the reproduction stuff for now.

reproduce() { return null; }

IA Fix

class LifeForm { constructor(x, y) { this.x = x; this.y = y; this.health = Math.random(); this.strength = Math.random(); this.speed = Math.random(); this.reproductionRate = Math.random(); this.mutationRate = Math.random() / 100000.0; } move() { throw new Error(`Not Implemented: ${this.constructor.name}.move`); } eat() { throw new Error(`Not Implemented: ${this.constructor.name}.eat`); } reproduce() { throw new Error(`Not Implemented: ${this.constructor.name}.reproduce`); } }
Last modified: 13 February 2024