Secret Santa
Dec 15, 2023Arkar
Typescript
Secret Santa with Backtracking and Graph Traversal
import { test, describe, expect } from "bun:test";
const graph = {
alice: ["D", "C", "E"],
bob: ["E", "D", "C"],
C: ["E", "alice", "D", "bob"],
E: ["D", "C", "alice", "bob"],
D: ["bob", "E", "C"],
};
function backtrack(graph, current, visited, currentPath, startNode) {
if (visited.size === Object.keys(graph).length) {
console.log(Array.from(visited).join(" -> "));
}
if (
visited.size === Object.keys(graph).length &&
graph[current].includes(startNode)
) {
currentPath.push(Array.from(visited));
return true;
}
const neighbors = graph[current];
const unvisitedNeighbors = neighbors.filter(
(neighbor) => !visited.has(neighbor),
);
while (unvisitedNeighbors.length > 0) {
const randomIndex = Math.floor(Math.random() * unvisitedNeighbors.length);
const randomNeighbor = unvisitedNeighbors[randomIndex];
visited.add(randomNeighbor);
if (backtrack(graph, randomNeighbor, visited, currentPath, startNode)) {
return true;
}
visited.delete(randomNeighbor);
unvisitedNeighbors.splice(randomIndex, 1);
}
return false;
}
// Example usage:
const startNode = "alice";
const visited = new Set([startNode]);
const currentPath = [];
backtrack(graph, startNode, visited, currentPath, startNode);
console.log(
`
const graph = {
alice: ["D", "C", "E"],
bob: ["E", "D", "C"],
C: ["E", "alice", "D", "bob"],
E: ["D", "C", "alice", "bob"],
D: ["bob", "E", "C", ],
};
`,
);
console.log(currentPath);
describe("backtrack function with various starting points", () => {
const graph = {
alice: ["D", "C", "E"],
bob: ["E", "D", "C"],
C: ["E", "alice", "D", "bob"],
E: ["D", "C", "alice", "bob"],
D: ["bob", "E", "C", "alice"],
};
test.skip('it finds a valid path starting from "alice"', () => {
const startNode = "alice";
const visitedNodes = new Set();
visitedNodes.add(startNode);
const resultPaths = [];
const result = backtrack(
graph,
startNode,
visitedNodes,
resultPaths,
startNode,
);
expect(result).toBe(true);
expect(resultPaths.length).toBeGreaterThan(0);
resultPaths.forEach((path) => {
expect(path[0]).toBe(startNode); // Path starts with the specified start node
const lastNode = path[path.length - 1];
expect(graph[lastNode]).toContain(startNode); // Last node is a neighbor of the start node
// Add more assertions if needed based on your requirements
});
});
test.skip('it finds a valid path starting from "bob"', () => {
const startNode = "bob";
const visitedNodes = new Set();
visitedNodes.add(startNode);
const resultPaths = [];
const result = backtrack(
graph,
startNode,
visitedNodes,
resultPaths,
startNode,
);
expect(result).toBe(true);
expect(resultPaths.length).toBeGreaterThan(0);
resultPaths.forEach((path) => {
expect(path[0]).toBe(startNode); // Path starts with the specified start node
const lastNode = path[path.length - 1];
expect(graph[lastNode]).toContain(startNode); // Last node is a neighbor of the start node
// Add more assertions if needed based on your requirements
});
});
test.skip('it finds a valid path starting from "C"', () => {
const startNode = "C";
const visitedNodes = new Set();
visitedNodes.add(startNode);
const resultPaths = [];
const result = backtrack(
graph,
startNode,
visitedNodes,
resultPaths,
startNode,
);
expect(result).toBe(true);
expect(resultPaths.length).toBeGreaterThan(0);
resultPaths.forEach((path) => {
expect(path[0]).toBe(startNode); // Path starts with the specified start node
const lastNode = path[path.length - 1];
expect(graph[lastNode]).toContain(startNode); // Last node is a neighbor of the start node
// Add more assertions if needed based on your requirements
});
});
// Add more tests as needed
});