Core JavaScript

Simple Discrete Event Simulation

The JavaScript code example demonstrates how to program a simple discrete event simulation.

SimpleSimulation.html

<!DOCTYPE html>
<html>
	<head>
		<title>XoaX.net's Javascript</title>
		<script type="text/javascript" src="SimpleSimulation.js"></script>
	</head>
	<body onload="Initialization()">
	</body>
</html>

SimpleSimulation.js

var qgEventSimulation = null;

function Initialization() {
	qgEventSimulation = new CEventSimulation();
	Loop();
}

function Loop() {
	if (qgEventSimulation.TimeAllocated() < 7000) {
		if (Math.random() < .01) {
			qgEventSimulation.AddEvent();
		}
	}
	// Check whether the first event in the queue is finished
	let qProcessed = qgEventSimulation.Update();
	if (qProcessed != null) {
		console.log("An event was processed.")
	}
	setTimeout(Loop, 10);
}

class CEvent {
	#mdDuration;
	#mqColor;
	#miIndex;
	constructor(dDuration, iEventIndex) {
		this.#miIndex = iEventIndex;
		this.#mdDuration = dDuration;
		this.#mqColor = "rgb("+Math.floor(256*Math.random())+", "+Math.floor(256*Math.random())+", "+Math.floor(256*Math.random())+")";
	}
	TimeRemaining(dElapsed) {
		return (this.#mdDuration - dElapsed);
	}
	CreateElement() {
		let qNewDiv = document.createElement("div");
		qNewDiv.style.backgroundColor = this.#mqColor;
		qNewDiv.style.border = "2px solid #808080";
		qNewDiv.style.margin = "5px";
		qNewDiv.style.width = "100px";
		qNewDiv.style.height = Math.floor(this.#mdDuration/10)+"px";
		qNewDiv.innerHTML = "Event #"+this.#miIndex;
		return qNewDiv 
	}
	PercentComplete(dElapsed) {
		return Math.floor((100*dElapsed)/this.#mdDuration);
	}
}

class CEventSimulation {
	#mdLastEventFinished;
	#mqEventQueue;
	#mqTimeMarker;
	#miEventsCreated;
	constructor() {
		this.#miEventsCreated = 0;
		this.#mdLastEventFinished = null;
		this.#mqEventQueue = new CQueue();
		this.#mqTimeMarker = document.createElement("div");
		this.#mqTimeMarker.style.backgroundColor = "yellow";
		this.#mqTimeMarker.style.width = "100px";
		this.#mqTimeMarker.style.height = "1px";
		this.#mqTimeMarker.style.position = "relative";
		this.#mqTimeMarker.style.left = "0px";
	}
	AddEvent() {
		let dDuration = Math.floor(1000 + 1000*Math.random());
		this.#miEventsCreated += 1;
		let qNewEvent = new CEvent(dDuration, this.#miEventsCreated);
		if (this.#mdLastEventFinished == null) {
			this.#mdLastEventFinished = Date.now();
		}
		this.#mqEventQueue.Push(qNewEvent);
		let qBody = document.body;
		let qNewDiv = qNewEvent.CreateElement();
		if (qBody.childElementCount > 0) {
			let qLastChild = qBody.lastChild;
			qLastChild.insertAdjacentElement("afterend", qNewDiv);
		} else {
			qBody.appendChild(qNewDiv);
		}
	}
	Update() {
		if (!this.#mqEventQueue.IsEmpty()) {
			let dCurrTime = Date.now();
			let dElapsedTime = dCurrTime - this.#mdLastEventFinished;
			let qFirstEvent = this.#mqEventQueue.GetFirst();
			let dTimeRemaining = qFirstEvent.TimeRemaining(dElapsedTime);
			if (dTimeRemaining < 0.0) {
				let qRemovedEvent = this.#mqEventQueue.Pop();
				if (this.#mqEventQueue.IsEmpty()) {
					this.#mdLastEventFinished = null;
				} else {
					this.#mdLastEventFinished += (dElapsedTime + dTimeRemaining);
				}
				let qBody = document.body;
				qBody.removeChild(qBody.firstElementChild);
				return qRemovedEvent;
			} else {
				// Add the timeline marker
				let dPercentComplete = qFirstEvent.PercentComplete(dElapsedTime);
				this.#mqTimeMarker.style.top = dPercentComplete+"%";
				let qFirstChild = document.body.firstElementChild;
				qFirstChild.appendChild(this.#mqTimeMarker);
			}
		}
	}
	TimeAllocated() {
		let qHead = this.#mqEventQueue.mqList.mqpHead;
		let dTime = 0;
		while (qHead != null) {
			dTime += qHead.mqData.TimeRemaining(0);
			qHead = qHead.mqpNext;
		}
		return dTime;
	}
}

class CLink {
	mqData;
	mqpNext;
	constructor(qData) {
  	this.mqData = qData;
  	this.mqpNext = null;
	}
}

class CLinkedList {
	mqpHead;
	mqpTail;
	constructor() {
  	this.mqpHead = null;
  	this.mqpTail = null;
	}
	InsertAtTail(qData) {
		if (this.mqpTail == null) {
			this.mqpHead = new CLink(qData);
			this.mqpTail = this.mqpHead;
		} else {
			this.mqpTail.mqpNext = new CLink(qData);
			this.mqpTail = this.mqpTail.mqpNext;
		}
	}
	RemoveAtHead() {
		this.mqpHead = this.mqpHead.mqpNext;
		if (this.mqpHead == null) {
			this.mqpTail = this.mqpHead;
		}
	}
	IsEmpty() {
		return (this.mqpHead == null);
	}
}

class CQueue {
	mqList;
	constructor() {
		this.mqList = new CLinkedList();
	}
	Push(qData) {
		this.mqList.InsertAtTail(qData);
	}
	Pop() {
		let qPopped = this.mqList.mqpHead;
		this.mqList.RemoveAtHead();
		return qPopped.mqData;
	}
	IsEmpty() {
		return this.mqList.IsEmpty();
	}
	GetFirst() {
		if (this.IsEmpty()) {
			return null;
		}
		return this.mqList.mqpHead.mqData;
	}
}
 

Output

 
 

© 2007–2026 XoaX.net LLC. All rights reserved.