commit 2c58cf52137bc924ae7a9e83d90f37611c866f12 Author: Tyler Beckman Date: Wed Dec 4 16:44:31 2024 -0700 Initial commit diff --git a/.envrc b/.envrc new file mode 100644 index 0000000..3550a30 --- /dev/null +++ b/.envrc @@ -0,0 +1 @@ +use flake diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..651437c --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +A6 +*.o +.direnv \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..21fcf23 --- /dev/null +++ b/Makefile @@ -0,0 +1,67 @@ +# THE NAME OF YOUR EXECUTABLE +TARGET = A6 +# ALL CPP COMPILABLE IMPLEMENTATION FILES THAT MAKE UP THE PROJECT +SRC_FILES = main.cpp + +# NO EDITS NEEDED BELOW THIS LINE + +CXX = g++ +CXXFLAGS = -O2 +CXXFLAGS_DEBUG = -g +CXXFLAGS_WARN = -Wall -Wextra -Wunreachable-code -Wshadow -Wpedantic +CPPVERSION = -std=c++17 + +OBJECTS = $(SRC_FILES:.cpp=.o) + +ifeq ($(OS),Windows_NT) + TARGET := $(TARGET).exe + DEL = del + Q = + + INC_PATH = Z:/CSCI200/include/ + LIB_PATH = Z:/CSCI200/lib/ + + RPATH = +else + DEL = rm -f + Q = " + + INC_PATH = /usr/local/include/ + LIB_PATH = /usr/local/lib/ + + UNAME_S := $(shell uname -s) + ifeq ($(UNAME_S),Linux) + CXXFLAGS += -D LINUX + RPATH = + endif + ifeq ($(UNAME_S),Darwin) + CXXFLAGS += -D OSX + RPATH = -Wl,-rpath,/Library/Frameworks + endif + + UNAME_P := $(shell uname -p) +endif + +LIBS = -lsfml-graphics -lsfml-window -lsfml-system -lsfml-audio -lsfml-network + +all: $(TARGET) + +$(TARGET): $(OBJECTS) + $(CXX) -o $@ $^ $(RPATH) -L$(LIB_PATH) $(LIBS) + +.cpp.o: + $(CXX) $(CXXFLAGS) $(CPPVERSION) $(CXXFLAGS_DEBUG) $(CXXFLAGS_WARN) -o $@ -c $< -I$(INC_PATH) + +clean: + $(DEL) $(TARGET) $(OBJECTS) + +depend: + @sed -i.bak '/^# DEPENDENCIES/,$$d' Makefile + @$(DEL) sed* + @echo $(Q)# DEPENDENCIES$(Q) >> Makefile + @$(CXX) -MM $(SRC_FILES) >> Makefile + +.PHONY: all clean depend + +# DEPENDENCIES +main.o: main.cpp diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..cdc1455 --- /dev/null +++ b/flake.lock @@ -0,0 +1,61 @@ +{ + "nodes": { + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1733212471, + "narHash": "sha256-M1+uCoV5igihRfcUKrr1riygbe73/dzNnzPsmaLCmpo=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "55d15ad12a74eb7d4646254e13638ad0c4128776", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..11671e0 --- /dev/null +++ b/flake.nix @@ -0,0 +1,51 @@ +{ + description = "CSCI200 Lab 6C"; + + inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + inputs.flake-utils.url = "github:numtide/flake-utils"; + + outputs = { nixpkgs, flake-utils, ... }: + flake-utils.lib.eachDefaultSystem (system: let pkgs = nixpkgs.legacyPackages.${system}; in { + devShells.default = pkgs.mkShell { + buildInputs = with pkgs; [ + sfml + ]; + + packages = with pkgs; [ + gcc + clang-tools + ]; + }; + + packages.default = pkgs.stdenv.mkDerivation rec { + pname = "A6"; + version = "1"; + + src = nixpkgs.lib.fileset.toSource { + root = ./.; + fileset = nixpkgs.lib.fileset.unions [ ./main.cpp ./Makefile ./mazePack ]; + }; + + buildInputs = with pkgs; [ + sfml + ]; + + nativeBuildInputs = with pkgs; [ + gcc + ]; + + buildPhase = '' + make + ''; + + installPhase = '' + runHook preInstall + + mkdir -p $out/bin + cp ./${pname} $out/bin/ + + runHook postInstall + ''; + }; + }); +} \ No newline at end of file diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..fc9c57b --- /dev/null +++ b/main.cpp @@ -0,0 +1,290 @@ +/** + Lab 6C - Maze Drawer, a program to parse a .maze text file and display it using SFML. + + @author Tyler Beckman (tyler_beckman@myriation.xyz) + @date 12/4/24 + */ +#include +#include +#include +#include +#include +#include +using namespace sf; + +#include +#include +using namespace std; + +enum class State { + Unexplored, + Added, + Explored, + UsedInPath +}; + +struct Node { + char character; + State state; + Node* prevNode; +}; + +int main(int argc, char** argv) { + string filename; + if (argc >= 2) { + filename = argv[1]; + } else { + cout << "Please enter a maze filename to load: "; + cin >> filename; + } + cout << "Loading maze file " << filename << endl; + + ifstream mazeFile(filename); + if (mazeFile.fail()) { + std::cout << "Failed to open specified file path " << filename + << ", does it exist?" << std::endl; + return 1; + } + + int rows, cols; + mazeFile >> rows >> cols; + mazeFile.get(); // move cursor past newline to actual grid characters + + Node** grid = new Node*[rows]; + pair startNode(-1, -1); + for (int row = 0; row < rows; row++) { + grid[row] = new Node[cols]; + for (int col = 0; col < cols; col++) { + grid[row][col].character = mazeFile.get(); + grid[row][col].state = State::Unexplored; + grid[row][col].prevNode = nullptr; + // Save start as starting node and mark as visited + if (grid[row][col].character == 'S') { + startNode = make_pair(row, col); + grid[row][col].state = State::Explored; + }; + } + mazeFile.get(); // advance past newline + } + + char searchType = '\0'; + cout << "Would you like to use a [b]readth-first search or a [d]epth-first search? "; + cin >> searchType; + if (searchType != 'b' && searchType != 'B' && searchType != 'd' && searchType != 'D') { + cout << "Search type '" << searchType << "' is unrecognized. Please enter a valid search type." << endl; + return 1; + } + + variant>, stack>> nodeList; + // Create stack or queue with start node + if (searchType == 'b' || searchType == 'B') { + nodeList = queue>({startNode}); + } else { + nodeList = stack>({startNode}); + } + + bool searching = true; + + // create a window + RenderWindow window( VideoMode(15*cols, 15*rows), "Maze Runner" ); + window.setVerticalSyncEnabled(true); + + // create an event object once to store future events + Event event; + + // while the window is open + while( window.isOpen() ) { + // clear any existing contents + window.clear(); + + ///////////////////////////////////// + // BEGIN DRAWING HERE + + // Draw unsolved maze from grid by iterating over each column in each row + for (int row = 0; row < rows; row++) { + for (int col = 0; col < cols; col++) { + sf::RectangleShape rectangle; + rectangle.setSize(Vector2f(15, 15)); + rectangle.setPosition(col * 15, row * 15); + switch (grid[row][col].character) { + case 'S': + rectangle.setFillColor(Color::Green); + break; + case 'E': + rectangle.setFillColor(Color::Red); + break; + case '#': + rectangle.setFillColor(Color::Black); + break; + case '.': + rectangle.setFillColor(Color::White); + break; + } + + if (grid[row][col].character != 'S' && grid[row][col].character != 'E') { + switch (grid[row][col].state) { + case State::Added: + rectangle.setFillColor(Color::Blue); + break; + case State::Explored: + rectangle.setFillColor(Color::Magenta); + break; + case State::UsedInPath: + rectangle.setFillColor(Color::Yellow); + break; + default: + break; + } + } + + window.draw(rectangle); + } + } + + // Solve maze + if (searching) { + // Take a node off the stack or queue + pair dequeuedNode; + if (searchType == 'B' || searchType == 'b') { + queue>& nodeQueue = get>>(nodeList); + if (nodeQueue.size() == 0) { + // Handle unsolvable mazes + searching = false; + cout << "Maze is unsolvable :c" << endl; + continue; + } + dequeuedNode = nodeQueue.front(); + nodeQueue.pop(); + } else { + stack>& nodeStack = get>>(nodeList); + if (nodeStack.size() == 0) { + // Handle unsolvable mazes + searching = false; + cout << "Maze is unsolvable :c" << endl; + continue; + } + dequeuedNode = nodeStack.top(); + nodeStack.pop(); + } + grid[dequeuedNode.first][dequeuedNode.second].state = State::Explored; + + // If end, print end and empty structure, then calculate true path + if (grid[dequeuedNode.first][dequeuedNode.second].character == 'E') { + cout << "End reached!" << endl; + searching = false; + + // Empty stack/queue + if (searchType == 'B' || searchType == 'b') { + queue>& nodeQueue = get>>(nodeList); + for (size_t i = 0; i < nodeQueue.size(); i++) { + nodeQueue.pop(); + } + } else { + stack>& nodeStack = get>>(nodeList); + for (size_t i = 0; i < nodeStack.size(); i++) { + nodeStack.pop(); + } + } + + // Mark all path-nodes as State::UsedInPath + for (Node* node = &grid[dequeuedNode.first][dequeuedNode.second]; node != nullptr; node = node->prevNode) { + node->state = State::UsedInPath; + } + } else { + // Take dequeued node and process it + pair nodesToAdd[4]; + int nodesAdded = 0; + + // Add all adjacent nodes in South->East->North->West order, if not added already + + // South + if ( + dequeuedNode.first != (rows - 1) + && grid[dequeuedNode.first + 1][dequeuedNode.second].state == State::Unexplored + && grid[dequeuedNode.first + 1][dequeuedNode.second].character != '#' + ) { + nodesToAdd[nodesAdded] = make_pair(dequeuedNode.first + 1, dequeuedNode.second); + nodesAdded++; + } + // East + if ( + dequeuedNode.second != (cols - 1) + && grid[dequeuedNode.first][dequeuedNode.second + 1].state == State::Unexplored + && grid[dequeuedNode.first][dequeuedNode.second + 1].character != '#' + ) { + nodesToAdd[nodesAdded] = make_pair(dequeuedNode.first, dequeuedNode.second + 1); + nodesAdded++; + } + // North + if ( + dequeuedNode.first != 0 + && grid[dequeuedNode.first - 1][dequeuedNode.second].state == State::Unexplored + && grid[dequeuedNode.first - 1][dequeuedNode.second].character != '#' + ) { + nodesToAdd[nodesAdded] = make_pair(dequeuedNode.first - 1, dequeuedNode.second); + nodesAdded++; + } + // West + if ( + dequeuedNode.second != 0 + &&grid[dequeuedNode.first][dequeuedNode.second - 1].state == State::Unexplored + && grid[dequeuedNode.first][dequeuedNode.second - 1].character != '#' + ) { + nodesToAdd[nodesAdded] = make_pair(dequeuedNode.first, dequeuedNode.second - 1); + nodesAdded++; + } + + // Mark all added notes as State::Added, and link to current node to later display path + for (int i = 0; i < nodesAdded; i++) { + grid[nodesToAdd[i].first][nodesToAdd[i].second].state = State::Added; + grid[nodesToAdd[i].first][nodesToAdd[i].second].prevNode = &grid[dequeuedNode.first][dequeuedNode.second]; + if (searchType == 'B' || searchType == 'b') { + queue>& nodeQueue = get>>(nodeList); + nodeQueue.push(nodesToAdd[i]); + } else { + stack>& nodeStack = get>>(nodeList); + nodeStack.push(nodesToAdd[i]); + } + } + + // Sleep to avoid solving instantly + sleep(milliseconds(50)); + } + } + + // END DRAWING HERE + ///////////////////////////////////// + + + // display the current contents of the window + window.display(); + + ///////////////////////////////////// + // BEGIN EVENT HANDLING HERE + // check if any events happened since the last time checked + while( window.pollEvent(event) ) { + // if event type corresponds to pressing window X + if (event.type == Event::Closed) { + // tell the window to close + window.close(); + } + // If event type corresponds to pressing Q or Esc + if (event.type == Event::KeyPressed) { + switch (event.key.code) { + case Keyboard::Key::Q: + case Keyboard::Key::Escape: + // Tell the window to close + window.close(); + break; + default: + break; + } + } + // check addition event types to handle additional events + } + // END EVENT HANDLING HERE + ///////////////////////////////////// + } + + return 0; +} diff --git a/mazePack/1.maze b/mazePack/1.maze new file mode 100644 index 0000000..631779e --- /dev/null +++ b/mazePack/1.maze @@ -0,0 +1,7 @@ +6 9 +######### +#S#...#E# +#.#.#.#.# +#.#.#.#.# +#...#...# +######### \ No newline at end of file diff --git a/mazePack/2.maze b/mazePack/2.maze new file mode 100644 index 0000000..6b97d2d --- /dev/null +++ b/mazePack/2.maze @@ -0,0 +1,8 @@ +7 8 +######## +#.##..E# +#.##.### +#.#..### +#.##...# +#S...#.# +######## \ No newline at end of file diff --git a/mazePack/3.maze b/mazePack/3.maze new file mode 100644 index 0000000..d254efd --- /dev/null +++ b/mazePack/3.maze @@ -0,0 +1,9 @@ +8 26 +########################## +#S#............#....#....# +#.#...###....###.##.#.##E# +#.#.###.#..#...#.##.#.##.# +#.....#...###....#....##.# +###.##..##.#..####.##..### +###..##....####....####### +########################## \ No newline at end of file diff --git a/mazePack/4.maze b/mazePack/4.maze new file mode 100644 index 0000000..bbfd2f0 --- /dev/null +++ b/mazePack/4.maze @@ -0,0 +1,9 @@ +8 26 +########################## +#S#............#....#.##.# +#.#...###....###.##.#.#.E# +#.#.###.#..#...#.##.#.##.# +#.....#...###....#....#### +###.##..##.#..####.##.#### +###..##....####....####### +########################## \ No newline at end of file diff --git a/mazePack/5.maze b/mazePack/5.maze new file mode 100644 index 0000000..bf70d78 --- /dev/null +++ b/mazePack/5.maze @@ -0,0 +1,9 @@ +8 26 +########################## +#S#............#....#.##.# +#.#...###....###.##.#.#.E# +#.#.###.#..#...#.##.#.##.# +###...#...###....#....#### +###.##..##.#..####.##.#### +###..##....####....####### +########################## \ No newline at end of file diff --git a/mazePack/6.maze b/mazePack/6.maze new file mode 100644 index 0000000..61981a4 --- /dev/null +++ b/mazePack/6.maze @@ -0,0 +1,8 @@ +7 9 +......... +......... +......... +..S...E.. +......... +......... +......... \ No newline at end of file diff --git a/mazePack/7.maze b/mazePack/7.maze new file mode 100644 index 0000000..f108d06 --- /dev/null +++ b/mazePack/7.maze @@ -0,0 +1,9 @@ +8 26 +########################## +#S#............#....#....# +#.#...###....###.##.#.##E# +#.#.#.#.#..#...#.##.#.##.# +#.....#....##.........##.# +###.##..##.#..#.##.##....# +###..........##....####### +########################## \ No newline at end of file diff --git a/mazePack/8.maze b/mazePack/8.maze new file mode 100644 index 0000000..027a66e --- /dev/null +++ b/mazePack/8.maze @@ -0,0 +1,4 @@ +3 60 +############################################################ +#S........................................................E# +############################################################ \ No newline at end of file diff --git a/mazePack/9.maze b/mazePack/9.maze new file mode 100644 index 0000000..ffbfaaa --- /dev/null +++ b/mazePack/9.maze @@ -0,0 +1,17 @@ +16 41 +######################################### +#.....................................#.# +#.....................................#.# +#.....................................#.# +#.....................................#.# +#.....................................#.# +#....................................S#E# +#.....................................#.# +#.....................................#.# +#.....................................#.# +#.....................................#.# +#.....................................#.# +#.....................................#.# +#.....................................#.# +#.....................................#.# +######################################### \ No newline at end of file diff --git a/mazePack/A.maze b/mazePack/A.maze new file mode 100644 index 0000000..a958e5b --- /dev/null +++ b/mazePack/A.maze @@ -0,0 +1,17 @@ +16 41 +######################################### +#.......................................# +#.......................................# +#.......................................# +#.......................................# +#.......................................# +#.......................................# +#.......................................# +#.......................................# +#.......................................# +#.......................................# +#..E....................................# +#.......................................# +#..S....................................# +#.......................................# +######################################### \ No newline at end of file diff --git a/mazePack/B.maze b/mazePack/B.maze new file mode 100644 index 0000000..5727b1f --- /dev/null +++ b/mazePack/B.maze @@ -0,0 +1,9 @@ +8 26 +########################## +#S#............#....#.##E# +#.#...#.#....#.#.##.#....# +#.#.#.#.#..#...#....#.##.# +#.....#....##....#....##.# +###.#...##.#..#.##.##.##.# +###...#......##..........# +########################## diff --git a/mazePack/C.maze b/mazePack/C.maze new file mode 100644 index 0000000..1ef58c2 --- /dev/null +++ b/mazePack/C.maze @@ -0,0 +1,8 @@ +7 9 +......... +......... +......... +..E...S.. +......... +......... +......... diff --git a/mazePack/D.maze b/mazePack/D.maze new file mode 100644 index 0000000..d94bdb3 --- /dev/null +++ b/mazePack/D.maze @@ -0,0 +1,26 @@ +25 3 +### +#S# +#.# +#.# +#.# +#.# +#.# +#.# +#.# +#.# +#.# +#.# +#.# +#.# +#.# +#.# +#.# +#.# +#.# +#.# +#.# +#.# +#.# +#E# +### \ No newline at end of file diff --git a/mazePack/E.maze b/mazePack/E.maze new file mode 100644 index 0000000..980904c --- /dev/null +++ b/mazePack/E.maze @@ -0,0 +1,26 @@ +25 3 +... +.S. +... +... +... +... +... +... +... +... +... +... +... +... +... +... +... +... +... +... +... +... +... +.E. +... \ No newline at end of file