Skip to content

Commit

Permalink
init commit
Browse files Browse the repository at this point in the history
  • Loading branch information
naderidev committed Nov 11, 2024
0 parents commit 0d79c2a
Show file tree
Hide file tree
Showing 4 changed files with 384 additions and 0 deletions.
71 changes: 71 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# Josephus Problem

## Introduction

The Josephus Problem is a theoretical problem that involves a group of people standing in a circle. Every k-th person is eliminated in a sequential manner until only one person remains. This problem has historical significance and applications in computer science, particularly in algorithm design and data structures.

This application provides an interactive visual representation of the Josephus Problem using a circular linked list, implemented in JavaScript and HTML.

## Usage

### Prerequisites

- A modern web browser (Chrome, Firefox, Safari, etc.)
- Ensure all files are in the same directory

### Steps to Run the Application

1. **Open the Application**:
- Download or clone the repository to your local machine.
- Navigate to the directory containing the files.
- Open the `index.html` file in your web browser.

2. **Input Members Count**:
- On the main page, you will see a text box labeled "Members Count".
- Enter the number of people (nodes) you want to include in the circle. For example, to simulate the problem with 10 people, enter `10`.

3. **Start the Simulation**:
- After entering the desired number of members, click the "Play" button to start the simulation.
- The application will create a circular representation of the people standing in the circle.

4. **Visualizing the Process**:
- The nodes will be displayed in a circular formation on the screen.
- The elimination process will begin, and each eliminated node will be marked visually (e.g., turning red or labeled as "Holder").
- The simulation will continue until only one node remains in the circle.

5. **Observe the Result**:
- The last remaining node is the "safe position" in the Josephus Problem.

6. **Restart the Simulation**:
- To run a new simulation, refresh the page and enter a new number of participants.

### Example Use Case

1. **Enter Members Count**:
- Input `10` in the "Members Count" text box.

2. **Play the Simulation**:
- Click the "Play" button.
- The application will display 10 nodes in a circle and begin the elimination process.
- Observe how nodes are sequentially removed until one node is left.

3. **Repeat**:
- Refresh the page to restart the simulation and try different numbers of members to see how the safe position changes.

## Circular Linked List

The application uses a circular linked list to represent the group of people in the Josephus Problem. Here’s a brief explanation of how it works:

- **Circular Linked List**: A linked list where the last node points back to the first node, forming a circle. This structure allows for continuous traversal of the list without reaching an endpoint.
- **Node Elimination**: Starting from a specified point, nodes (people) are removed from the list at regular intervals (every k-th node). The process continues in a circular manner until only one node remains.
- **Visualization**: The application visually marks the eliminated nodes, helping users understand the progression of the problem.

By using this interactive application, you can better understand the mechanics of the Josephus Problem and visualize the elimination process in a clear and intuitive way. The circular linked list provides an efficient way to handle the continuous nature of the problem, making it an ideal choice for this application.

## Alternative Access

Users can also visit the application online at [naderidev.ir/sdds](http://naderidev.ir/gg) instead of running it locally.

## Contributing

Contributions are welcome! If you have any suggestions or improvements, feel free to create an issue or submit a pull request.
6 changes: 6 additions & 0 deletions bootstrap.min.css

Large diffs are not rendered by default.

68 changes: 68 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- <meta http-equiv="refresh" content="1"> -->
<title>Josephus Problem</title>
<link href="bootstrap.min.css" rel="stylesheet">
<style>
html,
body {
height: 100%;
width: 100%;
}

.node {
width: 3.5rem;
height: 3.5rem;
font-size: 10px;
}
.will-remove{
background-color: red!important;
color: white;
}
</style>
</head>

<body>
<div class="container-lg py-3 h-100">
<div class="row h-100">
<div class="col-12 my-2">
<div class="text-center">
<span class="fw-bolder fs-3 d-block">Josephus Problem</span>
<span class="text-muted fs-6">Circular Linked List Representation</span>
</div>
</div>
<div class="col-12 my-5">
<div id="rounded-table" class="mx-auto border border-secondary rounded-pill position-relative"
style="height: 25rem; width: 25rem;">

</div>
</div>
<div class="col-12">
<div class="row justify-content-center">
<div class="col-2 m-1">
<input type="number" step="1" class="form-control" id="members-count" placeholder="Members Count">
</div>
<button class="btn m-1 btn-success col-2" onclick="play(this)">
Play
</button>
</div>
</div>
<div class="col-12">
<div class="row text-center align-items-end h-100">
<div>
<a class="fs-6 fw-bolder d-block text-decoration-none" href="https://github.com/naderidev" >Naderidev</span>
<small class="text-muted d-block fw-light">Developed by</small>
</div>
</div>
</div>
</div>
</div>

<script src="script.js"></script>
</body>

</html>
239 changes: 239 additions & 0 deletions script.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,239 @@
class CircularLinkedList {
head = null;

/*
* Appends a node to the linked list
* @param {Node} node - an instance of node.
*/
append(node) {
if (this.head === null) {
node.link = node;
} else {
node.link = this.head.link
this.head.link = node;
}
this.head = node;
}

/*
* Appends a list of nodes to the linkd list
* @param {Array[Node]} nodes - an array of nodes.
*/
batchAppend(nodes) {
nodes.forEach(n => this.append(n));
}

/*
* Pops the given node from the linkd list
* @param {Node} nodes - an instance of node.
* @return {boolean} - returns true of the node has poped and false if not.
*/
popNode(node) {
let flag = false;
this.walk(
current => {

// Checks if the next node is the given node
if (current.link === node) {

// links the next node to next->next node.
current.link = node.link;

// if the given node is the head node, so set the node before removed node for head.
if (node === this.head) this.head = current;

// removes the node from memory
delete node.prototype;

// Sets the flag for successfully removed action.
flag = true;

return 0;
}
}
)
return flag;
}

/*
* Walks in nodes and calls the given function with each node.
* @param {callable} func - an callable function.
*/
walk(func) {
var temp = this.head;
do {
if (func(temp) === 0) break;
temp = temp.link;
} while (temp !== this.head);

}

/*
* Holder shoots the next node and gives the gun to node after the victim node.
*/
shoot() {
let flag = false;
this.walk((node) => {

// Checks if the next node does not point to its self and if the current node is the holder.
if (node.link !== node && node.data.isHolder) {

// Takes the gun from the current holder
node.data.isHolder = false;

// Passes the gun to the next next node after the victim node.
node.link.link.data.isHolder = true;

// Marks the victim node for removal
const will_remove = node.link.data.element;
setTimeout(() => will_remove.classList.add("will-remove"), 1000);

// Removes the victim node
this.popNode(node.link);

// sets the removal flag
flag = true;
return 0
}
});
return flag;
}

/*
* Walks on nodes and returns all of them in an array.
* @param {Array[Node]} func - all nodes.
*/
getNodes() {
let nodes = [];
this.walk(node => nodes.push(node));
return nodes;
}

/*
* Prints all of the nodes.
*/
printList() {
this.walk(node => console.log(node))

}
}

class Node {

// Node Data
data = null;

// The link to the next node
link = null;

constructor(data) {
this.data = data;
}
}

class ElementNode extends Node {
constructor(id, isHolder) {
super(
{
id: id,
isHolder: isHolder,
element: null
}
);
}
}

class NodeManager {
constructor(members_count = 10, list = null, table = null) {
this.list = list;
this.table = table;

// َAppends the generated nodes to the linkd list
this.list.batchAppend(this.generateNodes(members_count));

// first render of nodes
this.renderNodes();


}

/*
* Append and renders the given node to the table.
*/
appendNodeElement(node) {
const el = "<div class=\"row align-items-center text-center h-100\">" +
" <div class=\"col-12 fs-5\">" + node.data.id +
" </div>" +
" <div class=\"col-12 border-top border-warning\">" +
" <small>" + (node.data.isHolder ? "Holder" : "---") + "</small>" +
" </div>" +
" </div>" +
"</div>";
const element = document.createElement("div")
element.className = "bg-white node border border-warning overflow-hidden rounded-2 border-1 position-absolute"
element.innerHTML = el;
node.data.element = element;
this.table.appendChild(element)
}

/*
* Removes all of the nodes from the table and re-renders the remaining nodes.
*/
renderNodes() {
this.table.innerHTML = ""
const nodes = this.list.getNodes();
nodes.forEach(n => this.appendNodeElement(n))
this.arrangeNodes(nodes)
}

/*
* plays the game
*/
play() {
var p = setInterval(() => {
this.renderNodes();
if (!this.list.shoot()) {
clearInterval(p);
}
}, 4000)
}

/*
* Arranges the nodes around the table
*/
arrangeNodes(nodes) {
const r = this.table.offsetWidth / 2;
const y0 = r - 20;
const x0 = r - 30;
const d = 2 * Math.PI / nodes.length
for (let i = 0; i < nodes.length; i++) {
const element = nodes[i].data.element;
element.style.bottom = r * Math.sin(d * i) + y0 + "px"
element.style.left = r * Math.cos(d * i) + x0 + "px"
}

}

/*
* Generates the nodes with given length with random ids.
* @param {Integer} length
* @return {Array[Node]} - generated nodes.
*/
generateNodes(length = 8) {
const holderIndex = Math.floor(Math.random() * length);
const startId = Math.floor(Math.random() * 100);
return Array.from({ length: length }, (i, v) => new ElementNode(v + startId, (v === holderIndex)))
}
}

function play(element) {
const members_count = document.getElementById("members-count").value;
if (members_count && members_count > 0) {
new NodeManager(
members_count,
new CircularLinkedList(),
document.getElementById("rounded-table")
).play();
element.parentNode.remove();
}
}

0 comments on commit 0d79c2a

Please sign in to comment.