diff --git a/CMakeLists.txt b/CMakeLists.txt index 49c0947..8ae2528 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,7 +15,11 @@ endif() file(GLOB_RECURSE SRC_FILES "${CMAKE_SOURCE_DIR}/src/*.cpp") # Define el ejecutable y sus archivos fuente -add_executable(${CMAKE_PROJECT_NAME} ${SRC_FILES}) +add_executable(${CMAKE_PROJECT_NAME} ${SRC_FILES} + src/queue.cpp + src/stack.cpp + src/list.cpp + include/event.h) # Establece el directorio de inclusión para los archivos de cabecera usando target_include_directories target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE "${CMAKE_SOURCE_DIR}/include") diff --git a/include/characters/caravana.hpp b/include/characters/caravana.hpp new file mode 100644 index 0000000..c307943 --- /dev/null +++ b/include/characters/caravana.hpp @@ -0,0 +1,95 @@ +#ifndef CARAVAN_HPP +#define CARAVAN_HPP + +#include "artefactoUnico.hpp" +#include "entidadGenerica.hpp" +#include +#include +#include + +/** + * @class Caravana + * @brief Representa a los comerciantes itinerantes de artefactos únicos. + * + * La caravana viaja por el yermo ofreciendo artículos extremadamente raros a precios elevados. + */ +class Caravana : public EntidadGenerica +{ +private: + std::vector> m_stock; ///< Lista de artefactos únicos disponibles + bool m_confia; ///< Confianza en el refugio + +public: + /** + * @brief Constructor + * @param nombre Nombre de la caravana + * @param confia Indica si la caravana está dispuesta a comerciar + */ + Caravana(const std::string& nombre, bool confia) + : EntidadGenerica(nombre) + , m_confia(confia) + { + inicializarStock(); + } + + /** + * @brief Muestra el inventario de la caravana + */ + void showInfo() const override + { + std::cout << "🚚 Caravana: " << m_name << "\n"; + std::cout << " - Confianza: " << (m_confia ? "Sí" : "No") << "\n"; + std::cout << " - Artefactos disponibles:\n"; + for (const auto& item : m_stock) + { + item->showInfo(); + } + } + + /** + * @brief Intenta comprar un artefacto + * @param nombre Nombre del artefacto + * @return Puntero al artefacto si se realizó la transacción, nullptr en caso contrario + */ + std::shared_ptr comprarArtefacto(const std::string& nombre) + { + if (!m_confia) + { + std::cout << "💬 " << m_name << " >>> No confiamos lo suficiente en tu refugio para hacer negocios." + << std::endl; + return nullptr; + } + + for (auto it = m_stock.begin(); it != m_stock.end(); ++it) + { + // if ((*it)->nombre() == nombre) + // { + // std::cout << "💬 " << m_name << " >>> Excelente elección. Espero que lo uses bien." << std::endl; + // auto item = *it; + // m_stock.erase(it); + // return item; + // } + } + + std::cout << "💬 " << m_name << " >>> No tenemos ese artefacto en este momento." << std::endl; + return nullptr; + } + + /** + * @brief Simula una evaluación de confianza futura + */ + void evaluarConfianza() + { + m_confia = true; // Lógica temporal, puede depender del nivel del refugio + } + +private: + void inicializarStock() + { + // m_stock.emplace_back(std::make_shared("Detector de Radiación", "Herramienta", "Épico", 250.0)); + // m_stock.emplace_back(std::make_shared("Pistola Láser Táctica", "Arma", "Legendaria", 400.0)); + // m_stock.emplace_back(std::make_shared("Batería de Fusión", "Energía", "Rara", 320.0)); + } +}; + +#endif // CARAVAN_HPP diff --git a/include/characters/chracterVisitant.hpp b/include/characters/chracterVisitant.hpp new file mode 100644 index 0000000..022754b --- /dev/null +++ b/include/characters/chracterVisitant.hpp @@ -0,0 +1,51 @@ +// +// Created by gvalenzuela on 3/30/25. +// + +#ifndef CHRACTERVISITANT_HPP +#define CHRACTERVISITANT_HPP + +#include "characters/artefactoUnico.hpp" +#include "characters/asaltante.hpp" +#include "characters/caravana.hpp" +#include "characters/enclave.hpp" +#include "characters/ghoul.hpp" +#include "characters/hermanosDeAcero.hpp" +#include "characters/mercader.hpp" +#include "characters/mercaderAgua.hpp" +#include "characters/mutantes.hpp" +#include "characters/refugiado.hpp" +#include "characters/refugio.hpp" +#include "characters/saqueador.hpp" + +using VisitanteVariant = std::variant< +Refugiado, +Mercader, +MercaderAgua, +Saqueador, +HermanoAcero, +Enclave, +Mutante, +Asaltante, +Ghoul, +Caravana +>; + +namespace NPC { + enum class VisitantCategory + { + REFUGEE, + BROTHER, + ENEMY, + MERCHANT, + CARAVAN + }; + + struct VisitantChance + { + VisitantCategory type; + double weight; + }; +}; + +#endif //CHRACTERVISITANT_HPP diff --git a/include/characters/hermanosDeAcero.hpp b/include/characters/hermanosDeAcero.hpp new file mode 100644 index 0000000..920862c --- /dev/null +++ b/include/characters/hermanosDeAcero.hpp @@ -0,0 +1,94 @@ +#ifndef HERMANO_ACERO_HPP +#define HERMANO_ACERO_HPP + +#include "entidadGenerica.hpp" +#include + +/** + * @class HermanoAcero + * @brief Representa a un miembro de la Hermandad del Acero. + * + * Poseen entrenamiento militar avanzado y pueden usar artefactos únicos. + * Requieren muchos recursos y son estrictos con su permanencia en un refugio. + */ +class HermanoAcero : public EntidadGenerica +{ +private: + int m_entrenamiento; ///< Nivel de entrenamiento (impacta en defensa del refugio) + double m_suministros; ///< Recursos necesarios para mantenerlo + bool m_enRefugio; ///< Si actualmente vive en un refugio + int m_consumoMinimo; ///< Cuántos recursos necesita por turno + +public: + /** + * @brief Constructor + * @param nombre Nombre del miembro + * @param entrenamiento Nivel de habilidad (100 por defecto) + */ + HermanoAcero(const std::string& nombre, int entrenamiento = 100) + : EntidadGenerica(nombre) + , m_entrenamiento(entrenamiento) + , m_suministros(0.20f) // 20% de los suministros del refugio + , m_enRefugio(false) + , m_consumoMinimo(20) // el doble de lo estándar + { + } + + /** + * @brief Muestra información del hermano del acero + */ + void showInfo() const override + { + std::cout << "⚙️ Hermano del Acero: " << m_name << "\n" + << " - Nivel de entrenamiento: " << m_entrenamiento << "\n" + << " - Suministros actuales: " << m_suministros << "\n" + << " - En refugio: " << (m_enRefugio ? "Sí" : "No") << "\n"; + } + + /** + * @brief Intenta ingresar al refugio + * @param recursosDisponibles Recursos disponibles del refugio + * @return Recursos consumidos, o 0 si no fue aceptado + */ + int solicitarIngreso(int recursosDisponibles) + { + if (recursosDisponibles >= m_consumoMinimo) + { + m_enRefugio = true; + std::cout << "💬" << m_name << " >>> Por la tecnología, serviré a este refugio." << std::endl; + return m_consumoMinimo; + } + else + { + std::cout << "💬" << m_name << " >>> Sus recursos no están a la altura de la Hermandad." << std::endl; + return 0; + } + } + + /** + * @brief Consume recursos cada ciclo, si no hay suficientes abandona el refugio + */ + void consumir() + { + if (m_suministros >= m_consumoMinimo) + { + m_suministros -= m_consumoMinimo; + std::cout << "💬" << m_name << " >>> Reabastecido. Listo para defender." << std::endl; + } + else + { + m_enRefugio = false; + std::cout << "💬" << m_name << " >>> Sin suministros. Este refugio no merece protección." << std::endl; + } + } + + /** + * @brief Saber si está aún en el refugio + */ + bool estaEnRefugio() const + { + return m_enRefugio; + } +}; + +#endif // HERMANO_ACERO_HPP diff --git a/include/characters/refugio.hpp b/include/characters/refugio.hpp index e534a7d..7d1e9de 100644 --- a/include/characters/refugio.hpp +++ b/include/characters/refugio.hpp @@ -30,6 +30,8 @@ class Refugio : public EntidadGenerica { std::string nombre; EngineData::Faction faccion; + + friend std::ostream& operator<<(std::ostream& os, const Visitante& visitante); }; private: @@ -48,6 +50,8 @@ class Refugio : public EntidadGenerica */ std::string faccionToString(EngineData::Faction faccion) const; + friend std::ostream& operator<<(std::ostream& os, const Visitante& visitante); + public: /** * @brief Constructor del refugio diff --git a/include/dataStructures/list.hpp b/include/dataStructures/list.hpp index 469a344..8e0cb77 100644 --- a/include/dataStructures/list.hpp +++ b/include/dataStructures/list.hpp @@ -33,136 +33,34 @@ class LinkedList ListNode* head; public: - LinkedList() - : head(nullptr) - { - } + LinkedList(); - ~LinkedList() - { - while(head != nullptr) - { - auto temporalNode = head; - head = head->next; - delete temporalNode; - } - } + ~LinkedList(); /** * @brief Inserta un nuevo elemento al inicio de la lista * * @param value Valor a insertar */ - void push_front(const TData& value) - { - auto nuevo = new ListNode(value); - nuevo->next = head; - head = nuevo; - } + void push_front(const TData& value); /** * @brief Remueve un elemento de la lista dada su posición * * @param position Posición del elemento a remover */ - void remove_at(size_t position) - { - if (head == nullptr) - { - return; - } - - if (position == 0) - { - auto temp = head; - head = head->next; - delete temp; - return; - } - - // Para cualquier otra posicion necesitamos mantener un puntero al nodo anterior - auto prev = head; - auto current = head->next; - size_t current_position = 1; - - // Avanzamos hasta encontrar la posicion o llegar al final de la lista - while (current != nullptr && current_position < position) - { - prev = current; - current = current->next; - current_position++; - } - - // Si current es nullptr quiere decir que la posicion especifica es mas grande que la lista, - // no se encuentra la posicion - if (current == nullptr) return; - - // Hacemos que el nodo anterior apunte al nodo siguiente actual - prev->next = current->next; - - delete current; - } + void remove_at(size_t position); /** * @brief Crea una lista nueva de n elementos a partir de una posición dada * */ - ListNode* take(size_t startPosition, size_t nElements) - { - // Si la lista esta vacia retornamos nullptr - if (head == nullptr) - { - return nullptr; - } - - // Buscamos el nodo de inicio - ListNode* current = head; - size_t current_position = 0; - - // Avanzamos hasta la posicion de inicio - while (current != nullptr && current_position < startPosition) - { - current = current->next; - current_position++; - } - - // Si current es nullptr, la posicion esta fuera de rango - if (current == nullptr) return nullptr; - - // Creamos el primer nodo de la nueva lista - ListNode* new_head = new ListNode(current->data); - ListNode* new_current = new_head; - - // Avanzamos al siguiente nodo en la lista original - current = current->next; - - // Copiamos los siguientes mElements-1 nodos (ya copiamos 1) - for (size_t i = 1; i < nElements && current != nullptr; i++) - { - // Creamos un nuevo nodo con el valor actual - new_current->next = new ListNode(current->data); - - // Avanzamos en ambas listas - new_current = new_current->next; - current = current->next; - } - - return new_head; - } + ListNode* take(size_t startPosition, size_t nElements); /** * @brief Imprime todos los elementos de la lista */ - void print() const - { - ListNode* current = head; - while (current != nullptr) - { - std::cout << current->data << " -> "; - current = current->next; - } - std::cout << "nullptr" << std::endl; - } + void print() const; }; /** @@ -197,171 +95,49 @@ class DoublyLinkedList DoublyListNode* head; public: - DoublyLinkedList() - : head(nullptr) - { - } + DoublyLinkedList(); - ~DoublyLinkedList() - { - while(head != nullptr) - { - auto temporalNode = head; - head = head->next; - delete temporalNode; - } - } + ~DoublyLinkedList(); /** * @brief Retorna el primer elemento de la lista * * @return Primer elemento de la lista */ - DoublyListNode* get_head() const - { - return head; - } + DoublyListNode* get_head() const; /** * @brief Inserta un nuevo elemento al inicio de la lista * * @param value Valor a insertar */ - void push_front(const TData& value) - { - auto nuevo = new DoublyListNode(value); - nuevo->next = head; - head = nuevo; - } + void push_front(const TData& value); /** * @brief Inserta un nuevo elemento al final de la lista * * @param value Valor a insertar */ - void push_back(const TData& value) - { - auto nuevo = new DoublyListNode(value); - // nuevo->next = nullptr; No haria falta pq cuando se crea un DoublyListNode next y prev se inicilizan en nullptr - - // Si la lista esta vacia - if (!head) - { - head = nuevo; - } - else - { - // Si la lista tiene elementos, recorrer hasta el ultimo nodo - // DoublyListNode* temp = get_head(); - auto temp = get_head(); - while (temp->next != nullptr) // Buscamos el ultimo nodo - { - temp = temp->next; - } - - // temp es el ultimo nodo, se conecta con el nuevo ultimo nodo - temp->next = nuevo; - nuevo->prev = temp; // El prev de nuevo apunta al antiguo ultimo nodo, osea temp - } - } + void push_back(const TData& value); /** * @brief Remueve un elemento de la lista dada su posición * * @param position Posición del elemento a remover */ - void remove_at(size_t position) - { - // Si la lista esta vacia no hay nada que eliminar - if (head == nullptr) - { - return; - } - - // Caso que el nodo a eliminar sea el primero (nodo 0) - if (position == 0) - { - DoublyListNode* temp = head; - head = head->next; - - // Si hay mas nodos despues del primero actualizamos el prev del nuevo head - if (head != nullptr) - { - head->prev = nullptr; - } - - delete temp; - return; - } - - // Para cualquier otra posicion recorremos la lista, hacemos un contador para llevar track de - // la posicion en la lista - DoublyListNode* current = head; - size_t current_position = 0; - - while (current != nullptr && current_position < position) - { - current = current->next; - current_position++; - } - - // Si current es nullptr quiere decir que la posicion especifica es mas grande que la lista, - // no se encuentra la posicion - if (current == nullptr) return; - - // current es el nodo que queremos eliminar, conectamos sus - // nodos adyacentes - if (current->prev != nullptr) - { - current->prev->next = current->next; - } - - if (current->next != nullptr) - { - current->next->prev = current->prev; - } - - delete current; - - } + void remove_at(size_t position); /** * @brief Copia los elementos de otra lista * * @param other Lista de la cual copiar los elementos */ - void copy_list(const DoublyLinkedList& other) - { - // PREGUNTAR: Borramos los nodos actuales? - while (head != nullptr) - { - auto temporalNode = head; - head = head->next; - delete temporalNode; - } - - // Copiamos los nodos de la otra lista con push_bakc() - auto current = other.get_head(); - while (current != nullptr) - { - push_back(current->data); - current = current->next; - } - } + void copy_list(const DoublyLinkedList& other); /** * @brief Imprime todos los elementos de la lista */ - void print() const - { - DoublyListNode* current = head; - while (current != nullptr) - { - std::cout << current->data << " <-> "; - current = current->next; - } - std::cout << "nullptr" << std::endl; - } + void print() const; }; #endif // LIST_HPP diff --git a/include/dataStructures/queue.hpp b/include/dataStructures/queue.hpp new file mode 100644 index 0000000..d34de8d --- /dev/null +++ b/include/dataStructures/queue.hpp @@ -0,0 +1,87 @@ +#ifndef QUEUE_HPP +#define QUEUE_HPP + +#include + +/** + * @brief Estructura de datos tipo Cola (FIFO - First In, First Out) + * + * @tparam TData Tipo de dato almacenado en la cola + */ + +template +class Queue +{ +private: + /** + * @brief Nodo de la cola + */ + struct Node + { + TData data; ///< Dato almacenado + Node* next; ///< Puntero al siguiente nodo + + explicit Node(const TData& value) + : data(value) + , next(nullptr) + { + } + }; + + Node* m_front; ///< Puntero al primer elemento + Node* m_rear; ///< Puntero al último elemento + size_t m_size; ///< Cantidad de elementos en la cola + +public: + /** + * @brief Constructor por defecto + */ + Queue(); + + /** + * @brief Destructor: libera todos los nodos de la cola + */ + ~Queue(); + + /** + * @brief Inserta un elemento al final de la cola + * + * @param value Valor a insertar + */ + void enqueue(const TData& value); + + /** + * @brief Elimina el primer elemento de la cola + * + * @throws std::underflow_error si la cola está vacía + */ + void dequeue(); + + /** + * @brief Devuelve el primer elemento sin eliminarlo + * + * @return TData Primer valor en la cola + * @throws std::underflow_error si la cola está vacía + */ + TData front() const; + + /** + * @brief Verifica si la cola está vacía + * + * @return true si no hay elementos + * @return false si hay al menos un elemento + */ + bool isEmpty() const; + + /** + * @brief Devuelve la cantidad de elementos en la cola + * + * @return size_t tamaño de la cola + */ + size_t getSize() const; + + void print() const; +}; + + +#endif // QUEUE_HPP diff --git a/include/dataStructures/stack.hpp b/include/dataStructures/stack.hpp new file mode 100644 index 0000000..3dfdd48 --- /dev/null +++ b/include/dataStructures/stack.hpp @@ -0,0 +1,93 @@ +#ifndef STACK_HPP +#define STACK_HPP + +#include + +/** + * @brief Estructura de datos tipo Pila (LIFO - Last In, First Out) + * + * @tparam TData Tipo de dato almacenado en la pila + */ +template +class Stack +{ +private: + /** + * @brief Nodo de la pila + */ + struct Node + { + TData data; ///< Valor almacenado + Node* next; ///< Puntero al siguiente nodo + + Node(const TData& data, Node* next = nullptr) + : data(data) + , next(next) + { + } + }; + + Node* m_top; ///< Puntero al nodo superior (cima) + size_t m_size; ///< Cantidad de elementos en la pila + +public: + /** + * @brief Constructor por defecto + */ + Stack(); + + /** + * @brief Destructor: libera todos los nodos + */ + ~Stack(); + + /** + * @brief Agrega un elemento en la cima de la pila + * + * @param value Valor a insertar + */ + void push(const TData& value); + + /** + * @brief Elimina el elemento superior de la pila + * + * @throws std::underflow_error si la pila está vacía + */ + void pop(); + + /** + * @brief Devuelve una referencia al elemento superior (mutable) + * + * @return TData& referencia al valor + * @throws std::underflow_error si la pila está vacía + */ + TData& top(); + + /** + * @brief Devuelve una referencia constante al elemento superior + * + * @return const TData& referencia constante al valor + * @throws std::underflow_error si la pila está vacía + */ + const TData& top() const; + + /** + * @brief Verifica si la pila está vacía + * + * @return true si no hay elementos + * @return false si hay al menos un elemento + */ + bool isEmpty() const; + + /** + * @brief Devuelve la cantidad de elementos en la pila + * + * @return size_t tamaño de la pila + */ + size_t size() const; + + void print() const; +}; + + +#endif // STACK_HPP diff --git a/include/engine.hpp b/include/engine.hpp index 1101f90..e7762e6 100644 --- a/include/engine.hpp +++ b/include/engine.hpp @@ -5,6 +5,9 @@ #include "randomEventGenerator.hpp" #include "refugio.hpp" #include "engineData.hpp" +#include "event.h" +#include "queue.hpp" +#include "stack.hpp" #include #include #include @@ -122,10 +125,16 @@ class Engine */ void loadConfig(); + void onNewEvent(const Event& evento); + + void registerDecision(const std::string& accion); + EngineData::PlayerInfo m_player; //< Información del jugador RandomEventGenerator m_randomGenerator; //< Generador de eventos aleatorios EngineData::GameConfig m_gameConfig; //< Configuración del juego std::unique_ptr m_shelter; //< Refugio del jugador + Queue m_eventosPendientes; + Stack m_historialDecisiones; /** * @brief: Operaciones que puede realizar el jugador @@ -135,6 +144,7 @@ class Engine SHOW_STATUS, //< Muestra el estado actual del refugio SHOW_EVENTS, //< Muestra los eventos aleatorios CHECK_EVENT, //< Muestra el evento actual + SHOW_HISTORY, // + +struct Event +{ + std::string m_type; //tipo de evento + std::string m_description; //descripcion del evento + std::string m_time; //ticks que durara el evento + + Event(std::string type, std::string description, std::string time) + : m_type(std::move(type)), m_description(std::move(description)), m_time(std::move(time)) {} + + friend std::ostream& operator<<(std::ostream& os, const Event& event) + { + os << "Tipo: " << event.m_type << ", Descripción: " << event.m_description << ", Tiempo: " << event.m_time; + return os; + } +}; + +#endif //EVENT_H diff --git a/src/engine.cpp b/src/engine.cpp index d66bbc3..1177c64 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -150,6 +150,18 @@ void Engine::interactiveConsole() std::cout << "Mostrando detalles..." << std::endl; std::this_thread::sleep_for(std::chrono::milliseconds(1000)); break; + case Operation::SHOW_EVENTS: + std::cout<<"Mostrando eventos pendientes..."<< std::endl; + std::this_thread::sleep_for(std::chrono::milliseconds(1000)); + m_eventosPendientes.print(); + case Operation::CHECK_EVENT: + std::cout<<"Mostrando evento actual..."<< std::endl; + std::this_thread::sleep_for(std::chrono::milliseconds(1000)); + std::cout<; + +template +LinkedList::LinkedList() + : head(nullptr) {} + +template +LinkedList::~LinkedList() +{ + while(head != nullptr) + { + auto temporalNode = head; + head = head->next; + delete temporalNode; + } +} + +template +void LinkedList::push_front(const TData& value) +{ + auto nuevo = new ListNode(value); + nuevo->next = head; + head = nuevo; +} + +template +void LinkedList::remove_at(size_t position) +{ + if (head == nullptr) + { + return; + } + + if (position == 0) + { + auto temp = head; + head = head->next; + delete temp; + return; + } + + // Para cualquier otra posicion necesitamos mantener un puntero al nodo anterior + auto prev = head; + auto current = head->next; + size_t current_position = 1; + + // Avanzamos hasta encontrar la posicion o llegar al final de la lista + while (current != nullptr && current_position < position) + { + prev = current; + current = current->next; + current_position++; + } + + // Si current es nullptr quiere decir que la posicion especifica es mas grande que la lista, + // no se encuentra la posicion + if (current == nullptr) return; + + // Hacemos que el nodo anterior apunte al nodo siguiente actual + prev->next = current->next; + + delete current; +} + +template +ListNode* LinkedList::take(size_t startPosition, size_t nElements) +{ + // Si la lista esta vacia retornamos nullptr + if (head == nullptr) + { + return nullptr; + } + + // Buscamos el nodo de inicio + ListNode* current = head; + size_t current_position = 0; + + // Avanzamos hasta la posicion de inicio + while (current != nullptr && current_position < startPosition) + { + current = current->next; + current_position++; + } + + // Si current es nullptr, la posicion esta fuera de rango + if (current == nullptr) return nullptr; + + // Creamos el primer nodo de la nueva lista + ListNode* new_head = new ListNode(current->data); + ListNode* new_current = new_head; + + // Avanzamos al siguiente nodo en la lista original + current = current->next; + + // Copiamos los siguientes mElements-1 nodos (ya copiamos 1) + for (size_t i = 1; i < nElements && current != nullptr; i++) + { + // Creamos un nuevo nodo con el valor actual + new_current->next = new ListNode(current->data); + + // Avanzamos en ambas listas + new_current = new_current->next; + current = current->next; + } + + return new_head; +} + +template +void LinkedList::print() const +{ + ListNode* current = head; + while (current != nullptr) + { + std::cout << current->data << " -> "; + current = current->next; + } + std::cout << "nullptr" << std::endl; +} + +template +DoublyLinkedList::DoublyLinkedList() + : head(nullptr) {} + +template +DoublyLinkedList::~DoublyLinkedList() +{ + while(head != nullptr) + { + auto temporalNode = head; + head = head->next; + delete temporalNode; + } +} + +template +DoublyListNode* DoublyLinkedList::get_head() const +{ + return head; +} + +template +void DoublyLinkedList::push_front(const TData& value) +{ + auto nuevo = new DoublyListNode(value); + nuevo->next = head; + head = nuevo; +} + +template +void DoublyLinkedList::push_back(const TData& value) +{ + auto nuevo = new DoublyListNode(value); + // nuevo->next = nullptr; No haria falta pq cuando se crea un DoublyListNode next y prev se inicilizan en nullptr + + // Si la lista esta vacia + if (!head) + { + head = nuevo; + } + else + { + // Si la lista tiene elementos, recorrer hasta el ultimo nodo + // DoublyListNode* temp = get_head(); + auto temp = get_head(); + while (temp->next != nullptr) // Buscamos el ultimo nodo + { + temp = temp->next; + } + + // temp es el ultimo nodo, se conecta con el nuevo ultimo nodo + temp->next = nuevo; + nuevo->prev = temp; // El prev de nuevo apunta al antiguo ultimo nodo, osea temp + } +} + +template +void DoublyLinkedList::remove_at(size_t position) +{ + // Si la lista esta vacia no hay nada que eliminar + if (head == nullptr) + { + return; + } + + // Caso que el nodo a eliminar sea el primero (nodo 0) + if (position == 0) + { + DoublyListNode* temp = head; + head = head->next; + + // Si hay mas nodos despues del primero actualizamos el prev del nuevo head + if (head != nullptr) + { + head->prev = nullptr; + } + + delete temp; + return; + } + + // Para cualquier otra posicion recorremos la lista, hacemos un contador para llevar track de + // la posicion en la lista + DoublyListNode* current = head; + size_t current_position = 0; + + while (current != nullptr && current_position < position) + { + current = current->next; + current_position++; + } + + // Si current es nullptr quiere decir que la posicion especifica es mas grande que la lista, + // no se encuentra la posicion + if (current == nullptr) return; + + // current es el nodo que queremos eliminar, conectamos sus + // nodos adyacentes + if (current->prev != nullptr) + { + current->prev->next = current->next; + } + + if (current->next != nullptr) + { + current->next->prev = current->prev; + } + + delete current; +} + +template +void DoublyLinkedList::copy_list(const DoublyLinkedList& other) +{ + // PREGUNTAR: Borramos los nodos actuales? + while (head != nullptr) + { + auto temporalNode = head; + head = head->next; + delete temporalNode; + } + + // Copiamos los nodos de la otra lista con push_bakc() + auto current = other.get_head(); + while (current != nullptr) + { + push_back(current->data); + current = current->next; + } +} + +template +void DoublyLinkedList::print() const +{ + DoublyListNode* current = head; + while (current != nullptr) + { + std::cout << current->data << " <-> "; + current = current->next; + } + std::cout << "nullptr" << std::endl; +} \ No newline at end of file diff --git a/src/queue.cpp b/src/queue.cpp new file mode 100644 index 0000000..9f2b763 --- /dev/null +++ b/src/queue.cpp @@ -0,0 +1,86 @@ +// +// Created by nahue on 5/4/2025. +// + +#include "../include/dataStructures/queue.hpp" +#include +#include + +template class Queue; // Instancia específica + +template +Queue::Queue() + : m_front(nullptr), m_rear(nullptr), m_size(0) {} + +template +Queue::~Queue() +{ + while (!isEmpty()) + { + dequeue(); + } +} + +template +void Queue::enqueue(const TData& value) +{ + Node* newNode = new Node(value); + if (isEmpty()) + { + m_front = m_rear = newNode; + } + else + { + m_rear->next = newNode; + m_rear = newNode; + } + ++m_size; +} + +template +void Queue::dequeue() +{ + if (isEmpty()){ + throw std::underflow_error("Queue is empty"); + } + + Node* temp = m_front; + m_front = m_front->next; + --m_size; + delete temp; +} + +template +TData Queue::front() const +{ + if (isEmpty()){ + throw std::underflow_error("Queue is empty"); + } + + return m_front->data; +} + +template +bool Queue::isEmpty() const +{ + return m_size == 0; +} + +template +size_t Queue::getSize() const +{ + return m_size; +} + +template +void Queue::print() const +{ + Node* temp = m_front; + while (temp != nullptr) + { + std::cout << temp->data << " "; + temp = temp->next; + } +} + + diff --git a/src/refugio.cpp b/src/refugio.cpp index 39258be..ea34bbd 100644 --- a/src/refugio.cpp +++ b/src/refugio.cpp @@ -1,5 +1,20 @@ #include "refugio.hpp" +std::string Refugio::faccionToString(EngineData::Faction faccion) const +{ + switch (faccion) + { + case EngineData::Faction::REFUGEES: return "Refugiados"; + case EngineData::Faction::WATER_MERCHANTS: return "Comerciantes de Agua"; + case EngineData::Faction::MERCHANTS: return "Comerciantes"; + case EngineData::Faction::STEEL_BROTHERS: return "Hermanos del Acero"; + case EngineData::Faction::CARAVAN: return "Caravana"; + case EngineData::Faction::RAIDERS: return "Asaltantes"; + case EngineData::Faction::MUTANTS: return "Mutantes"; + default: return "Desconocida"; + } +} + Refugio::Refugio(const std::string& name, const std::string& leader) : EntidadGenerica(name) , m_leader(leader) @@ -77,3 +92,12 @@ bool Refugio::isSafeFaction(const EngineData::Faction faccion) const faccion == EngineData::Faction::MERCHANTS || faccion == EngineData::Faction::STEEL_BROTHERS || faccion == EngineData::Faction::CARAVAN); } + +std::ostream& operator<<(std::ostream& os, const Refugio::Visitante& visitante) +{ + // Creamos una instancia temporal para llamar a faccionToString (ya que no es static) + Refugio dummy("Dummy", "Líder"); + os << visitante.nombre << " (" << dummy.faccionToString(visitante.faccion) << ")"; + return os; +} + diff --git a/src/stack.cpp b/src/stack.cpp new file mode 100644 index 0000000..912d350 --- /dev/null +++ b/src/stack.cpp @@ -0,0 +1,84 @@ +// +// Created by nahue on 5/4/2025. +// + +#include "../include/dataStructures/stack.hpp" + +#include + +template class Stack; + +template +Stack::Stack() + : m_top(nullptr), m_size(0) {} + +template +Stack::~Stack() +{ + while(isEmpty()){ + pop(); + } +} + +template +void Stack::push(const TData& value) +{ + Node* newNode = new Node(value, m_top); + m_top = newNode; + ++m_size; +} + +template +void Stack::pop() +{ + if (isEmpty()) + { + throw std::underflow_error("Stack is empty"); + } + + auto newNode = m_top; + m_top = m_top->next; + --m_size; + delete newNode; +} + +template +TData& Stack::top() +{ + if (isEmpty()){ + throw std::underflow_error("Stack is empty"); + } + return m_top->data; +} + +template +const TData& Stack::top() const +{ + if (isEmpty()){ + throw std::underflow_error("Stack is empty"); + } + return m_top->data; +} + +template +bool Stack::isEmpty() const +{ + return m_top == nullptr; +} + +template +size_t Stack::size() const +{ + return m_size; +} + +template +void Stack::print() const +{ + auto temp = m_top; + while(temp != nullptr) + { + std::cout << temp->data << " "; + temp = temp->next; + } +} diff --git a/tests/unit/list_test.cpp b/tests/unit/list_test.cpp new file mode 100644 index 0000000..374349a --- /dev/null +++ b/tests/unit/list_test.cpp @@ -0,0 +1,68 @@ +#include +#include "list.hpp" + +TEST(LinkedListTest, PushFrontAndPrint) { + LinkedList list; + list.push_front(3); + list.push_front(2); + list.push_front(1); + EXPECT_EQ(list.take(0, 3)->data, 1); +} + +TEST(LinkedListTest, RemoveAt) { + LinkedList list; + list.push_front(3); + list.push_front(2); + list.push_front(1); + list.remove_at(1); + EXPECT_EQ(list.take(0, 2)->next->data, 3); +} + +TEST(LinkedListTest, TakeWithinBounds) { + LinkedList list; + for (int i = 0; i < 5; ++i) + list.push_front(5 - i); + auto newList = list.take(1, 3); + EXPECT_EQ(newList->data, 2); + EXPECT_EQ(newList->next->data, 3); + EXPECT_EQ(newList->next->next->data, 4); +} + +TEST(DoublyLinkedListTest, PushBackAndPrint) { + DoublyLinkedList list; + list.push_back(1); + list.push_back(2); + list.push_back(3); + EXPECT_EQ(list.get_head()->data, 1); + EXPECT_EQ(list.get_head()->next->data, 2); + EXPECT_EQ(list.get_head()->next->next->data, 3); +} + +TEST(DoublyLinkedListTest, PushFront) { + DoublyLinkedList list; + list.push_front(1); + list.push_front(2); + EXPECT_EQ(list.get_head()->data, 2); + EXPECT_EQ(list.get_head()->next->data, 1); +} + +TEST(DoublyLinkedListTest, RemoveAt) { + DoublyLinkedList list; + list.push_back(1); + list.push_back(2); + list.push_back(3); + list.remove_at(1); + EXPECT_EQ(list.get_head()->next->data, 3); +} + +TEST(DoublyLinkedListTest, CopyList) { + DoublyLinkedList list1; + list1.push_back(1); + list1.push_back(2); + + DoublyLinkedList list2; + list2.copy_list(list1); + + EXPECT_EQ(list2.get_head()->data, 1); + EXPECT_EQ(list2.get_head()->next->data, 2); +} \ No newline at end of file diff --git a/tests/unit/queue_test.cpp b/tests/unit/queue_test.cpp new file mode 100644 index 0000000..611891f --- /dev/null +++ b/tests/unit/queue_test.cpp @@ -0,0 +1,35 @@ + +#include +#include "queue.hpp" + + +TEST(QueueTest, EnqueueDequeueFront) { + Queue q; + EXPECT_TRUE(q.isEmpty()); + q.enqueue(10); + q.enqueue(20); + EXPECT_EQ(q.front(), 10); + q.dequeue(); + EXPECT_EQ(q.front(), 20); + q.dequeue(); + EXPECT_TRUE(q.isEmpty()); +} + +TEST(QueueTest, DequeueOnEmptyThrows) { + Queue q; + EXPECT_THROW(q.dequeue(), std::underflow_error); +} + +TEST(QueueTest, FrontOnEmptyThrows) { + Queue q; + EXPECT_THROW(q.front(), std::underflow_error); +} + +TEST(QueueTest, QueueSize) { + Queue q; + q.enqueue("A"); + q.enqueue("B"); + EXPECT_EQ(q.getSize(), 2); + q.dequeue(); + EXPECT_EQ(q.getSize(), 1); +} \ No newline at end of file diff --git a/tests/unit/stack_test.cpp b/tests/unit/stack_test.cpp new file mode 100644 index 0000000..c6d463d --- /dev/null +++ b/tests/unit/stack_test.cpp @@ -0,0 +1,33 @@ + +#include +#include "stack.hpp" + +TEST(StackTest, PushPopTop) { + Stack s; + EXPECT_TRUE(s.isEmpty()); + s.push(42); + EXPECT_FALSE(s.isEmpty()); + EXPECT_EQ(s.top(), 42); + s.pop(); + EXPECT_TRUE(s.isEmpty()); +} + +TEST(StackTest, PopOnEmptyThrows) { + Stack s; + EXPECT_THROW(s.pop(), std::underflow_error); +} + +TEST(StackTest, TopOnEmptyThrows) { + Stack s; + EXPECT_THROW(s.top(), std::underflow_error); +} + +TEST(StackTest, StackSize) { + Stack s; + s.push("uno"); + s.push("dos"); + s.push("tres"); + EXPECT_EQ(s.size(), 3); + s.pop(); + EXPECT_EQ(s.size(), 2); +} \ No newline at end of file