Initial commit

This commit is contained in:
Price Hiller 2023-10-04 00:22:21 -05:00
commit 4681dc4b66
Signed by: Price
SSH Key Fingerprint: SHA256:Y4S9ZzYphRn1W1kbJerJFO6GGsfu9O70VaBSxJO7dF8
5 changed files with 205 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/target

7
Cargo.lock generated Normal file
View File

@ -0,0 +1,7 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "Graph-Theory"
version = "0.1.0"

8
Cargo.toml Normal file
View File

@ -0,0 +1,8 @@
[package]
name = "Graph-Theory"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]

BIN
assets/graph1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 170 KiB

189
src/main.rs Normal file
View File

@ -0,0 +1,189 @@
#![allow(non_snake_case, dead_code)]
use std::collections::{HashMap, HashSet, VecDeque};
fn main() -> Result<(), Box<dyn std::error::Error>> {
// simple_searches()?;
breadth_first_search_grid_shortest_path()?;
Ok(())
}
fn simple_searches() -> Result<(), String> {
let adjacency_list_graph = vec![
(0, vec![1, 9]),
(1, vec![0, 8]),
(2, vec![3]),
(3, vec![2, 4, 5, 7]),
(4, vec![3]),
(5, vec![3, 6]),
(6, vec![5, 7]),
(7, vec![3, 6, 11, 10, 8]),
(8, vec![1, 7]),
(9, vec![0, 8]),
(10, vec![7, 11]),
(11, vec![7, 10]),
(12, vec![]),
];
println!("---Depth first search---");
depth_first_search(&adjacency_list_graph, 0)?;
println!("\n\n---Breadth first search---");
breadth_first_search_shortest_path(&adjacency_list_graph, 0, 11)?;
Ok(())
}
/// See assets/graph1.png
fn depth_first_search(graph: &Vec<(usize, Vec<usize>)>, start_node: usize) -> Result<(), String> {
let mut nodes_to_visit: Vec<usize> = vec![start_node];
let mut visited_nodes = vec![false; graph.len() - 1];
let mut last_node = start_node;
while !nodes_to_visit.is_empty() {
let node = nodes_to_visit.pop().ok_or("Unable to get node!")?;
if visited_nodes[node] {
continue;
}
println!("Current Node: {node}");
visited_nodes[node] = true;
let neighbors: Vec<&usize> = graph[node]
.1
.iter()
.filter(|neighbor| last_node != **neighbor)
.collect();
nodes_to_visit.extend(neighbors);
last_node = node;
}
println!("Visited Nodes: {visited_nodes:#?}");
Ok(())
}
/// See assets/graph1.png
fn breadth_first_search_shortest_path(
graph: &Vec<(usize, Vec<usize>)>,
start_node: usize,
end_node: usize,
) -> Result<(), String> {
let mut visited_nodes = HashSet::new();
let mut queue = VecDeque::new();
let mut dirty_path: Vec<Option<usize>> = vec![None; graph.len() - 1];
queue.push_back(start_node);
while let Some(node) = queue.pop_front() {
if visited_nodes.contains(&node) {
continue;
}
println!("Current node: {node}");
visited_nodes.insert(node);
if node == end_node {
break;
}
for neighbor in &graph[node].1 {
if neighbor != &node && !visited_nodes.contains(neighbor) {
queue.push_back(*neighbor);
dirty_path[*neighbor] = Some(node);
}
}
}
let dirty_path: Vec<_> = dirty_path.iter().flatten().cloned().collect();
let mut path = vec![];
let mut curr_node = end_node;
while curr_node != start_node {
path.push(curr_node);
curr_node = dirty_path[curr_node - 1];
}
path.push(start_node);
path.reverse();
println!("Visited Nodes: {visited_nodes:#?}");
println!("Shortest path from '{start_node}' to '{end_node}': {path:#?}");
Ok(())
}
fn breadth_first_search_grid_shortest_path() -> Result<(), String> {
// S is start
// E is end
// . are open spaces
// # are blocked
let dungeon_grid = vec![
//0 1 2 3 4 5 6
['S', '.', '.', '#', '.', '.', '.'], // 0
['.', '#', '.', '.', '.', '#', '.'], // 1
['.', '#', '.', '.', '.', '.', '.'], // 2
['.', '.', '#', '#', '.', '.', '.'], // 3
['#', '.', '#', 'E', '.', '#', '.'], // 4
];
let mut start_position = (0, 0);
let mut end_position = (0, 0);
// Find the start and end position.
for (row, row_vals) in dungeon_grid.iter().enumerate() {
for (column, char) in row_vals.iter().enumerate() {
if *char == 'S' {
start_position = (row as i32, column as i32);
} else if *char == 'E' {
end_position = (row as i32, column as i32);
}
}
}
let mut visited_nodes = HashSet::new();
let mut queue = VecDeque::new();
queue.push_front(start_position);
let grid_rows = dungeon_grid.len() as i32;
let grid_columns = dungeon_grid[0].len() as i32;
let mut reached_end = false;
let mut dirty_path: HashMap<(i32, i32), (i32, i32)> = HashMap::new();
while let Some((row, column)) = queue.pop_front() {
println!("Current Position: ({row},{column})");
for (vec_row, vec_column) in [(0, -1), (-1, 0), (0, 1), (1, 0)] {
let new_row: i32 = row + vec_row;
let new_column: i32 = column + vec_column;
// Ensure the new position is within the grid
if (new_row < 0 || new_column < 0)
|| (new_row >= grid_rows || new_column >= grid_columns)
{
continue;
}
// Ensure we haven't visited the node yet
if !visited_nodes.contains(&(new_row, new_column)) {
match dungeon_grid[new_row as usize][new_column as usize] {
'.' => {
queue.push_back((new_row, new_column));
dirty_path.insert((new_row, new_column), (row, column));
}
'E' => {
queue.drain(..);
dirty_path.insert((new_row, new_column), (row, column));
reached_end = true;
break;
}
_ => {}
}
}
}
visited_nodes.insert((row, column));
}
if !reached_end {
println!("No solution found, cannot reach the end!");
return Ok(())
}
// Get the shortest path
let mut path: Vec<(i32, i32)> = Vec::new();
let mut curr_node = end_position;
while curr_node != start_position {
path.push(curr_node);
let dirt_path = dirty_path[&(curr_node.0, curr_node.1)];
curr_node = dirt_path;
}
path.push(start_position);
path.reverse();
println!("Shortest path from '{start_position:?}' to '{end_position:?}': {path:?}");
println!("Number of moves taken: {}", path.len());
Ok(())
}