Skip to content

Commit

Permalink
update MVC stuff and add the chart
Browse files Browse the repository at this point in the history
  • Loading branch information
stjohnfinn committed Oct 8, 2024
1 parent 9af5736 commit 1a1908f
Show file tree
Hide file tree
Showing 3 changed files with 238 additions and 108 deletions.
157 changes: 110 additions & 47 deletions dist/main.js
Original file line number Diff line number Diff line change
@@ -1,48 +1,16 @@
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
console.log("Starting Terrarium!");
function reproduce(parentA, parentB, mutationChance = 0.1) {
let offspring = parentA.split("");
let chars = "abcdefghijklmnopqrstuvxyz ";
for (let i = 0; i < offspring.length; i++) {
if (Math.random() > 0.5) {
offspring.splice(i, 1, parentB[i]);
}
if (Math.random() < mutationChance) {
let mutatedGene = chars.charAt(Math.floor(Math.random() * chars.length));
offspring.splice(i, 1, mutatedGene);
}
}
return offspring.join("");
}
function calculateFitness(str, targetString) {
let fitness = 0;
for (let i = 0; i < str.length; i++) {
if (str[i] == targetString[i]) {
fitness++;
}
}
return fitness;
}
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
// (async () => {
// while (!population.includes(targetString)) {
// await sleep(25);
// generation++;
// // update interface
// updateInterface();
// // find parents
// const parentA = population.sort((a, b) => calculateFitness(a, targetString) > calculateFitness(b, targetString) ? -1 : 1)[0];
// const parentB = population.sort((a, b) => calculateFitness(a, targetString) > calculateFitness(b, targetString) ? -1 : 1)[1]
// // produce offspring based on fitness
// population = [];
// for (let i = 0; i < populationSize; i++) {
// population.push(reproduce(parentA, parentB));
// }
// }
// updateInterface();
// console.log("target matched!");
// })();
class WordGeneticAlgorithmModel {
constructor(populationSize = 50, targetString = "hello", generation = 1) {
// instance variables
Expand All @@ -52,17 +20,64 @@ class WordGeneticAlgorithmModel {
this.targetString = "";
this.generation = 1;
this.averageFitness = 0;
this.generationComplete = false;
this.fitnessRecord = [];
// initialize standard values
this.population = [];
this.populationSize = populationSize;
this.targetStringLength = targetString.length;
this.targetString = targetString;
this.generation = generation;
this.generationComplete = false;
// initialize more complex values
for (let i = 0; i < this.populationSize; i++) {
this.population.push(WordGeneticAlgorithmModel.randomString(this.targetStringLength));
}
}
step() {
this.generationComplete = true;
if (this.generationComplete) {
// create a new generation
this.progressToNextGeneration();
}
}
progressToNextGeneration() {
this.generation++;
// metrics
this.averageFitness = this.population.map(str => WordGeneticAlgorithmModel.calculateFitness(str, this.targetString)).reduce((a, b) => a + b, 0) / this.populationSize;
this.fitnessRecord.push(this.averageFitness);
// find parents
const parentA = this.population.sort((a, b) => WordGeneticAlgorithmModel.calculateFitness(a, this.targetString) > WordGeneticAlgorithmModel.calculateFitness(b, this.targetString) ? -1 : 1)[0];
const parentB = this.population.sort((a, b) => WordGeneticAlgorithmModel.calculateFitness(a, this.targetString) > WordGeneticAlgorithmModel.calculateFitness(b, this.targetString) ? -1 : 1)[1];
// generate offspring
this.population = [];
for (let i = 0; i < this.populationSize; i++) {
this.population.push(WordGeneticAlgorithmModel.produceOffspring(parentA, parentB));
}
}
static produceOffspring(parentA, parentB, mutationChance = 0.1) {
let offspring = parentA.split("");
for (let i = 0; i < offspring.length; i++) {
if (Math.random() > 0.5) {
offspring.splice(i, 1, parentB[i]);
}
if (Math.random() < mutationChance) {
let mutatedGene = WordGeneticAlgorithmModel.possibleStringChars.charAt(Math.floor(Math.random() * WordGeneticAlgorithmModel.possibleStringChars.length));
offspring.splice(i, 1, mutatedGene);
}
}
return offspring.join("");
}
static calculateFitness(sampleString, targetString) {
let correctLetters = 0;
for (let i = 0; i < sampleString.length; i++) {
if (sampleString[i] == targetString[i]) {
correctLetters++;
}
}
let fitness = correctLetters / targetString.length;
return fitness;
}
static randomString(targetLength) {
let result = "";
for (let i = 0; i < targetLength; i++) {
Expand All @@ -75,7 +90,7 @@ class WordGeneticAlgorithmModel {
}
}
// static variables
WordGeneticAlgorithmModel.possibleStringChars = "abcdefghijklmnopqrstuvxyz ";
WordGeneticAlgorithmModel.possibleStringChars = "abcdefghijklmnopqrstuvwxyz ";
class WordGeneticAlgorithmView {
constructor(displayElement) {
this.displayElement = displayElement;
Expand Down Expand Up @@ -117,7 +132,7 @@ class WordGeneticAlgorithmView {
metadataContainer.appendChild(generationParagraph);
// display generation's average fitness
let averageFitnessParagraph = document.createElement("p");
averageFitnessParagraph.innerText = `Average fitness of this generation is ${model.averageFitness}.`;
averageFitnessParagraph.innerText = `Average fitness of this generation is ${model.averageFitness.toFixed(2)}.`;
metadataContainer.appendChild(averageFitnessParagraph);
}
}
Expand All @@ -126,8 +141,56 @@ class WordGeneticAlgorithmController {
console.log("Created a new controller!");
}
}
let model = new WordGeneticAlgorithmModel(20, "wobble", 1);
let model = new WordGeneticAlgorithmModel(20, "geronimo geoff", 1);
let view = new WordGeneticAlgorithmView(document.querySelector("#view"));
console.log(model);
console.log(view);
view.update(model);
// chart for metrics
const ctx = document.getElementById("myChart");
let fitnessGramPacerTest = new Chart(ctx, {
type: "line",
data: {
labels: Array.from({ length: model.population.length }, (_, i) => i + 1),
datasets: [{
label: "Average Fitness",
data: [0, 1, 2, 3],
borderWidth: 1
}]
},
options: {
animation: false,
maintainAspectRatio: true,
scales: {
y: {
beginAtZero: true,
min: 0,
max: 1
},
x: {
type: "linear",
position: "bottom",
ticks: {
autoSkip: true,
maxTicksLimit: 500
}
}
},
responsive: true
}
});
function gameLoop() {
return __awaiter(this, void 0, void 0, function* () {
model.step();
// visuals
view.update(model);
fitnessGramPacerTest.data.labels = Array.from({ length: model.population.length }, (_, i) => i + 1);
fitnessGramPacerTest.data.datasets[0].data = model.fitnessRecord;
fitnessGramPacerTest.update();
yield sleep(10);
if (!model.population.includes(model.targetString)) {
requestAnimationFrame(gameLoop);
}
else {
console.info("we're done!");
}
});
}
requestAnimationFrame(gameLoop);
5 changes: 5 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,14 @@
<head>
<title>Terrarium</title>
<link rel="stylesheet" href="./style/style.css">
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
</head>
<body>
<div id="view"></div>

<div>
<canvas id="myChart" height="200" width="500"></canvas>
</div>

<!-- Call scripts after the DOM has been loaded -->
<script type="text/javascript" src="./dist/terrarium.js"></script>
Expand Down
Loading

0 comments on commit 1a1908f

Please sign in to comment.