I know the title sucks... but I have no clue what actual issue I have got at my hands...
I have defined a base class for linked lists and a specialized class that inherits the base class.
Base:
list.h
#ifndef LIST_H
#define LIST_H
#include <string>
#include "../../declarations.h"
#include "element.h"
class List: public Element{
/*
* #brief is a generic list class, designed to be inherited by more
* specialised list emulating classes.
*/
public: // methods
/// generates empty list.
List(){
init();
cout << "defining List" << endl;
}
/// Removes first list element
bool remove_first(){
if (!is_empty()){
Element* newFirst = first;
delete first;
first = newFirst->next;
return true;
}
else{
std::cerr << "list already empty" << endl;
return false;
}
}
/// removes last list element
bool remove_last(){
// store length fo list in min_len for switch
unsigned int min_len = 0;
if (!is_empty()){
if ((first->next) != 0){
min_len = 2;
}
else {
min_len = 1;
}
}
// as switch doesn't allow for comparisons, map any value greater
// 1 to 2 if (min_len>1) min_len = 2;
switch (min_len){
case (2):{
// Iterate over list, always look two elements ahead.
// If the next element points to 0 you have found your new last
// element (the current next to last one). Delete current-> next
// to remove the last element and let the new last one point to
// zero.
Element* current = first;
while((current->next)->next != 0){
current = (current->next);
}
delete (current->next);
current->next = 0;
return true;
}
// if the list contaisn only one element use remove_first()
// as the logic for that case is already present there
case (1): return remove_first();
case (0):{
std::cerr << "list already empty" << endl;
return false;
}
}
}
// Declare several virtual functions for appending elements
// that get defined in the iheriting classes.
virtual void append_front(int i){};
virtual void append_front(char c){};
virtual void append_front(std::string s){};
virtual void append_back(int i){};
virtual void append_back(char c){};
virtual void append_back(std::string s){};
/// todo
std::string head(){}
/// returns length of list
unsigned length(){
unsigned counter = 0;
Element* current = first;
while(current != 0) {
++counter;
current = current->next;
}
return counter;
}
/// determines whether list is empty
bool is_empty(){
return first == 0;
}
// printing depends on type of list, so this method gets defined in
// inheriting class
virtual void print(){};
private: // methods
void init(){
// initialise empty list with a NULL pointer
first = 0;
}
protected: // data members
/// points to first list element or zero, if list is empty
Element* first;
};
#endif
Derived:
transitionsList.h
#ifndef TRANSITION_LIST_H
#define TRANSITION_LIST_H
#include "../../../generic/list.h"
#include "../../elements/transition/transition.h"
class TransitionList: public List {
public:
void append_front(char c){
// generate list element and store in pointer e
Element* e;
e = new Transition();
// store parameter c in the object *e points to
e->set_content(c);
// Either list is empty. In thet case object at &e becomes first
// list element and also the last.
// Or list wasn't empty and e->next points to the current first, then
// object *e points to becomes first.
if (is_empty()) {
first = e;
e->next = 0;
}
else {
e->next = first;
first = e;
}
}
void append_back(char c){
if (is_empty()) {
append_front(c);
}
else {
Element* e;
e = new Transition();
e->set_content(c);
e->next = 0;
// Go through the list until you find the last element, let it
// then ppint to the new last element.
Element* current = first;
while(current->next != 0) {
current = current->next;
}
current->next = e;
}
}
void print(){
cout << "[";
// go through the list, starting with first element
Element* current = first;
// as long as the last list element is not the current one, keep
// printing elements.
while (current != 0) {
cout << " " << current->get_content();
// let current be the next of current current, to move
// through the list
current = current->next;
} // while
cout << " ]";
}
};
#endif
These two depend on a few other classes:
element.h:
#ifndef ELEMENT_H
#define ELEMENT_H
#include "../../declarations.h"
class State;
class Element{
/*
* #brief is a generic list element class, designed to be inherited by
* more specialised list element classes.
*/
public:
Element(){}
virtual ~Element(){}
virtual void set_content(int i){};
virtual void set_content(char c){};
virtual void set_content(std::string s){};
virtual char get_content() = 0;
Element* next; ///< is the pointer to the next transition in the list
};
#endif
state.h:
#ifndef STATE_H
#define STATE_H
#include "../../declarations.h"
class List;
class State{
/**
* #brief is a class emulating a trie state
*/
public: // methods
State(){
cout << "defining State" << endl;
init();
}
private: // methods
void init(){
}
private: // variables
// Pointer required due to forward declarations
List* transitions; ///< is the list of states dominated by THIS.
// count stores how often the word the current path codes for has been
// found in the training data.
int count; ///< is the number of occurances of some word in this path.
};
#endif
and transition.h
#ifndef TRANSITION_H
#define TRANSITION_H
#include "../../../generic/element.h"
#include "../../state.h"
typedef char cont;
class Transition: public Element {
public:
void set_content(cont c){
content = c;
};
cont get_content(){
return content;
};
State* successor;
Transition* next; ///< is the pointer to the next transition in the list
protected:
cont content; ///< is the character this transition codes for
};
#endif
When I now run this cpp-file:
list_test.cpp:
#include "transitionList.h"
int main(){
TransitionList* derivedList;
List *baseList = &derivedList;
}
I get the error:
list_test2.cpp:5:8: error: cannot initialize a variable of type 'List *' with an rvalue of type 'TransitionList **'
Which I don't understand, because I am under the (apparently phony) impression, that this would be analogous to what I saw in this tutorial:
https://youtu.be/DudHooleNVg?t=4m59s
Where is
int main(){
TransitionList* derivedList;
List *baseList = &derivedList;
}
not analogous to
int main(){
Ninja* n;
Enemy *enemy1 = &n;
}
:/ Very confused.
Related
I was wondering how I could call the toString() method in my Link List of the class BoxClass. BoxClass has a double length, width and height.
my BoxClass:
class BoxClass{
private:
double length;
double width;
double height;
public:
// Default constructor w/ no parameters
BoxClass(){
length = 0;
width = 0;
height = 0;
}
// Constructor with arguments
BoxClass(double boxLength, double boxWidth, double boxHeight){
length = boxLength;
width = boxWidth;
height = boxHeight;
}
// Setters and Getters
void setLength(double boxLength){
length = boxLength;
}
double getLength(){
return length;
}
void setWidth(double boxWidth){
width = boxWidth;
}
double getWidth(){
return width;
}
void setHeight(double boxHeight){
height = boxHeight;
}
double getHeight(){
return height;
}
// Returns the volume of the boxes
double Volume(){
return (length * width * height);
}
// toString method for boxes, returns "(length) x (width) x (height) string
string toString(){
return ("(" + to_string(length)+ "x" + to_string(width) + "x" + to_string(height) + ")");
}
}; // End of BoxClass() class
LinkNode.h
//Template ListNode class definition.
#ifndef LINKNODE_H
#define LINKNODE_H
template <typename T> class LinkList;
template <typename T> class LinkNode{
friend class LinkNode <T>;
public:
LinkNode(const T &);
T getData()const;
T data;
LinkNode <T> *nextPtr;
};
template <typename T> LinkNode <T>::LinkNode(const T &info):data(info), nextPtr(NULL){
// Empty body
}
template <typename T>T LinkNode<T>::getData()const{
return data;
}
#endif
Main (Creating the class, adding it to Link List
// Create the Box class
BoxClass userBox(length, width, height);
// Add box class to Link List
Box.insertNode(userBox);
Box.print();
LinkList.h print() method
template<typename T>void LinkList<T>::print()const {
// To list off nodes
int counter = 1;
if (isEmpty()) {
cout << "No boxes in list!\n";
} else {
LinkNode<T>*currentPtr = firstPtr;
cout << "Your boxes in increasing order of volume is:\n";
// while (currentPtr) {
while (currentPtr != NULL) {
// Output as "#. (length x width x height)
cout << counter << ". " << currentPtr->data << endl;
printf(" %i. %.2f\n", counter, currentPtr->data);
currentPtr = currentPtr->nextPtr;
counter++;
}
}
}
LinkList.h
//Template LinkList class definition.
#ifndef LINKLIST_H
#define LINKLIST_H
#include <iostream>
#include "LinkNode.h"
using namespace std;
template<typename T> class LinkList {
public:
LinkList();
void addNode(const T &);
void insertNode(const T &);
bool isEmpty() const;
void print() const;
private:
LinkNode<T>*firstPtr;
LinkNode<T>*getNewNode(const T &);
};
template<typename T>LinkList<T>::LinkList() :firstPtr(NULL) {
// Empty body
}
template <typename T>void LinkList<T>::insertNode(const T &value) {
LinkNode<T>*newPtr = getNewNode(value);
bool inserted = false;
if (isEmpty() || (newPtr->data < firstPtr->data)) {
newPtr->nextPtr = firstPtr;
firstPtr = newPtr;
// cout << " " << newPtr->data << " inserted at front of list.\n";
printf(" %.2f inserted at front of list.\n", newPtr->data);
} else {
LinkNode<T>*currentPtr = firstPtr;
while (currentPtr->nextPtr && !inserted) {
if (newPtr->data < currentPtr->nextPtr->data) {
// cout << " " << newPtr->data << " inserted before " << currentPtr->nextPtr->data << ". " << endl;
printf(" %.2f inserted before %.2f.\n", newPtr->data, currentPtr->nextPtr->data);
newPtr->nextPtr = currentPtr->nextPtr;
currentPtr->nextPtr = newPtr;
inserted = true;
} else {
currentPtr = currentPtr->nextPtr;
}
}
if (!inserted) {
currentPtr->nextPtr = newPtr;
printf(" %.2f inserted at the end of list.\n", newPtr->data);
}
}
}
template<typename T>bool LinkList<T>::isEmpty()const {
return firstPtr == NULL;
}
template<typename T>LinkNode<T>*LinkList<T>::getNewNode(const T &value) {
return new LinkNode<T>(value);
}
template<typename T>void LinkList<T>::print()const {
// To list off nodes
int counter = 1;
if (isEmpty()) {
cout << "No boxes in list!\n";
} else {
LinkNode<T>*currentPtr = firstPtr;
cout << "Your boxes in increasing order of volume is:\n";
// while (currentPtr) {
while (currentPtr != NULL) {
// Output as "#. (length x width x height)
cout << counter << ". " << currentPtr->data << endl;
printf(" %i. %.2f\n", counter, currentPtr->data);
currentPtr = currentPtr->nextPtr;
counter++;
}
}
}
#endif
So again, my question is- How do I go about traversing the list and calling the toString() BoxClass method? I tried everything from cout << data.toString() << endl; but that doesn't work. I've been stuck on this for days, can someone help me out?
edit: added LinkList.h
When you write template <typename T> class LinkNode{ you are specifically stating that your node class will have no built-in knowledge of the type of the node that it contains.
You have not shown us your LinkList<T> class, but obviously, the same thing applies to it: since it consists of LinkNode<T> it has to also accept a generic parameter of type <T>, so it cannot have built-in knowledge of the actual type of <T> either.
Therefore, you cannot suddenly introduce a method which has such knowledge. It does not make sense. "It does not compute".
What you need to do instead is add this print() method of yours elsewhere, and make it accept a LinkList<BoxClass>. Then, it will be able to view the LinkNodes as LinkNode<BoxClass>, and it will be able to invoke linkNode.data.toString().
The problem is that your implementation of LinkList<T> class has no way for the client code to go through each node of the list in a loop. What if we don't want to print, but do something else with each box?
In addition, it would look weird if I have a LinkList<Widget>, and I see the text when I call print():
"Your boxes in increasing order of volume is:";
I would say, "what boxes? what volume? I have Widgets, not boxes".
A more complete implementation would look something like this (caveat: This has not been compiled. It is to give you the gist of what you should be doing):
template<typename T> class LinkList {
public:
LinkList();
void addNode(const T &);
void insertNode(const T &);
bool isEmpty() const;
// this is what you're missing from the current implementation
typedef LinkNode<T>* Iterator;
Iterator begin() { return firstPtr; }
Iterator next(Iterator ptr) { return ptr->nextPtr; }
Iterator end() { return NULL; }
private:
LinkNode<T>* firstPtr;
LinkNode<T>* getNewNode(const T &);
};
Then with this, the print function need not be part of the linked list. It can live on the outside:
LinkList<BoxClass> boxList;
//...
void print()
{
if (boxList.isEmpty())
cout << "No boxes in list!\n";
else
{
int counter = 1;
cout << "Your boxes in increasing order of volume is:\n";
// get first box
LinkList<BoxClass>::Iterator curBox = boxList.begin();
// loop until no more boxes
while (curBox != boxList.end())
{
// now use curBox to do whatever you want with this box
BoxClass& b = curBox->getData();
cout << counter << ". " << b.toString();
// go to the next box
curBox = boxList.next(curBox);
counter++;
}
}
}
Note how print is no longer a member of LinkList. Also, note the typedef to give us a "nice" name for the LinkNode pointer that the client uses. The comments should be self-explanatory.
I didn't want to overcomplicate the code by introducing a "real" iterator (i.e. overloaded ++), but that operator would replace the LinkList<T>:::next() function call. I leave that to you as an additional exercise.
I'm trying to solve a queue problem that is required in my assignment.
The details are as follows :
- I am asked to create an automate robust queuing sub-system (note that the each dental clinic has 2 doctors and 4 dentist chairs, 1 x-ray machine on its premise)
- Time-optimised algorithm (different type of treatment). For example, tooth decay would require 30 minutes, checkup would be only 15 minutes, etc.
- Every clinic expected about ~20 and ~15 patients in the morning and afternoon per day respectively.
- The clinic timing is 8am – 12pm, 2pm – 6pm
Upon asking my lecturer, he said that I would need 2 queues(for each doctor), x-ray machines would be an instant process and requires no time at all and in the end of the day, when comparing these 2 queues, the difference of the total duration would be almost equal.
I have been trying to solve this problem but to no avail. Can someone explain how these would be solved?
EDIT :
I have created the queue implementation and a Visit class that is supposed to be enqueued into the queue. What I need to figure out is the algorithm on how to schedule all these visits such that the queues will be time-efficient.
Visit.h
#ifndef Visit_H
#define Visit_H
#include "MC.h"
#include "Doctor.h"
#include "Assistant.h"
#include "Condition.h"
#include "Medicine.h"
#include "Treatment.h"
class Visit
{
private:
std::string date;
std::string time;
double duration;
Staff staff;
MC mc;
bool xRayStatus;
List<Treatment> treatmentList;
List<Condition> conditionList;
List<Medicine> medicineList;
public:
Visit();
Visit(std::string, std::string, double);
std::string getDate();
void addStaff(Staff);
Staff getStaff();
void setMC(MC);
MC getMC();
void addXRay();
bool getXRayStatus();
void addCondition(Condition c);
void addMedicine(Medicine m);
void addTreatment(Treatment t);
List<Treatment> getTreatmentList();
List<Medicine> getMedicineList();
List<Condition> getConditionList();
};
#endif
Visit.cpp
#ifndef Visit_CPP
#define Visit_CPP
#include "Visit.h"
Visit::Visit()
{
}
Visit::Visit(std::string d, std::string t, double dur)
{
date = d;
time = t;
duration = dur;
}
std::string Visit::getDate()
{
return date;
}
void Visit::addStaff(Staff s)
{
staff = s;
}
Staff Visit::getStaff()
{
return staff;
}
void Visit::setMC(MC m)
{
mc = m;
}
MC Visit::getMC()
{
return mc;
}
void Visit::addXRay()
{
xRayStatus = true;
}
bool Visit::getXRayStatus()
{
return xRayStatus;
};
void Visit::addCondition(Condition c)
{
conditionList.add(c);
}
void Visit::addMedicine(Medicine m)
{
medicineList.add(m);
}
void Visit::addTreatment(Treatment t)
{
treatmentList.add(t);
}
List<Treatment> Visit::getTreatmentList()
{
return treatmentList;
}
List<Medicine> Visit::getMedicineList()
{
return medicineList;
}
List<Condition> Visit::getConditionList()
{
return conditionList;
};
#endif
Queue.h
//Queue.h - - Specification of Queue ADT (implemented using Pointers)
#include<string>
#include<iostream>
#include "Visit.h"
using namespace std;
typedef Visit ItemType;
class Queue
{
private:
struct Node
{
ItemType item; // item
Node *next; // pointer pointing to next item
};
Node *frontNode; // point to the first item
Node *backNode; // point to the first item
public:
// constructor
Queue();
//destructor
~Queue();
// check if the queue is empty
bool isEmpty();
// enqueue item at the back of queue
bool enqueue(ItemType& newItem);
// dequeue item from front of queue
bool dequeue();
// dequeue and retrieve item from front of queue
bool dequeue(ItemType& item);
// retrieve item from front of queue
void getFront(ItemType& item);
};
Queue.cpp
/** #file Queue.cpp */
#include <cstddef> // for NULL
#include <iostream>
#include <new> // for bad_alloc
#include "Queue.h" // header file
using namespace std;
Queue::Queue()
{
backNode = NULL;
frontNode = NULL;
} // end default constructor
Queue::~Queue()
{
while (!isEmpty())
dequeue();
} // end destructor
bool Queue::isEmpty()
{
return backNode == NULL;
} // end isEmpty
bool Queue::enqueue(ItemType& item)
{
// create a new node
Node *newNode = new Node;
newNode->item = item;
newNode->next = NULL;
// insert the new node
if (isEmpty())
// insertion into empty queue
frontNode = newNode;
else
// insertion into nonempty queue
backNode->next = newNode;
backNode = newNode; // new node is at back
return true;
} // end enqueue
bool Queue::dequeue()
{
if(!isEmpty())
{ // queue is not empty; remove front
Node *temp = frontNode;
if (frontNode == backNode) // special case?
{ // yes, one node in queue
frontNode = NULL;
backNode = NULL;
}
else
frontNode = frontNode->next;
temp->next = NULL;
delete temp;
temp = NULL;
return true;
} // end if
else
{
cout << "empty queue, cannot dequeue" << endl;
return false;
}
} // end dequeue
bool Queue::dequeue(ItemType& item)
{
if (!isEmpty())
{ // queue is not empty; retrieve front
item = frontNode->item;
dequeue(); // delete front
return true;
} // end if
else
{
cout << "empty queue, cannot dequeue" << endl;
return false;
}
} // end dequeue
void Queue::getFront(ItemType& item)
{
if (!isEmpty())
// queue is not empty; retrieve front
item = frontNode->item;
else
cout << "empty queue, cannot getFront" << endl;
} // end getFront
// End of implementation file.
You need to think of the assignment as an event driven system.
One queue would contain events that need to be handled.
If we have an event class like so:
struct Event
{
virtual bool execute_if_time(const Time& t) = 0;
};
We could have a vector of pointers to events:
typedef std::vector< boost::smart_ptr<Event> > Event_Container;
Event_Container events;
There would be a scheduler loop:
while (1)
{
Time t = now();
for (Event_Container::iterator iter = events.begin();
iter != events.end();
++it)
{
(*iter)->exeute_if_time(t);
}
sleep(/* some duration */)
}
The scheduler would execute events in the container at periodic intervals.
Note: this is only one scheme, as there are many others that would suffice.
The rest of the code is left as an exercise for the reader.
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 9 years ago.
Improve this question
I'm totally at wit's end: I can't figure out how my dependency issues. I've read countless posts and blogs and reworked my code so many times that I can't even remember what almost worked and what didnt. I continually get not only redefinition errors, but class not defined errors. I rework the header guards and remove some errors simply to find others. I somehow got everything down to one error but then even that got broke while trying to fix it.
Would you please help me figure out the problem?
card.cpp
#include <iostream>
#include <cctype>
#include "card.h"
using namespace std;
// ====DECL======
Card::Card()
{
abilities = 0;
flavorText = 0;
keywords = 0;
artifact = 0;
classType = new char[strlen("Card") + 1];
classType = "Card";
}
Card::~Card (){
delete name;
delete abilities;
delete flavorText;
artifact = NULL;
}
// ------------
Card::Card(const Card & to_copy)
{
name = new char[strlen(to_copy.name) +1]; // creating dynamic array
strcpy(to_copy.name, name);
type = to_copy.type;
color = to_copy.color;
manaCost = to_copy.manaCost;
abilities = new char[strlen(to_copy.abilities) +1];
strcpy(abilities, to_copy.abilities);
flavorText = new char[strlen(to_copy.flavorText) +1];
strcpy(flavorText, to_copy.flavorText);
keywords = new char[strlen(to_copy.keywords) +1];
strcpy(keywords, to_copy.keywords);
inPlay = to_copy.inPlay;
tapped = to_copy.tapped;
enchanted = to_copy.enchanted;
cursed = to_copy.cursed;
if (to_copy.type != ARTIFACT)
artifact = to_copy.artifact;
}
// ====DECL=====
int Card::equipArtifact(Artifact* to_equip){
artifact = to_equip;
}
Artifact * Card::unequipArtifact(Card * unequip_from){
Artifact * to_remove = artifact;
artifact = NULL;
return to_remove;
// put card in hand or in graveyard
}
int Card::enchant( Card * to_enchant){
to_enchant->enchanted = true;
cout << "enchanted" << endl;
}
int Card::disenchant( Card * to_disenchant){
to_disenchant->enchanted = false;
cout << "Enchantment Removed" << endl;
}
// ========DECL=====
Spell::Spell()
{
currPower = basePower;
currToughness = baseToughness;
classType = new char[strlen("Spell") + 1];
classType = "Spell";
}
Spell::~Spell(){}
// ---------------
Spell::Spell(const Spell & to_copy){
currPower = to_copy.currPower;
basePower = to_copy.basePower;
currToughness = to_copy.currToughness;
baseToughness = to_copy.baseToughness;
}
// =========
int Spell::attack( Spell *& blocker ){
blocker->currToughness -= currPower;
currToughness -= blocker->currToughness;
}
//==========
int Spell::counter (Spell *& to_counter){
cout << to_counter->name << " was countered by " << name << endl;
}
// ============
int Spell::heal (Spell *& to_heal, int amountOfHealth){
to_heal->currToughness += amountOfHealth;
}
// -------
Creature::Creature(){
summoningSick = true;
}
// =====DECL======
Land::Land(){
color = NON;
classType = new char[strlen("Land") + 1];
classType = "Land";
}
// ------
int Land::generateMana(int mana){
// ... //
}
card.h
#ifndef CARD_H
#define CARD_H
#include <cctype>
#include <iostream>
#include "conception.h"
class Artifact;
class Spell;
class Card : public Conception
{
public:
Card();
Card(const Card &);
~Card();
protected:
char* name;
enum CardType { INSTANT, CREATURE, LAND, ENCHANTMENT, ARTIFACT, PLANESWALKER};
enum CardColor { WHITE, BLUE, BLACK, RED, GREEN, NON };
CardType type;
CardColor color;
int manaCost;
char* abilities;
char* flavorText;
char* keywords;
bool inPlay;
bool tapped;
bool cursed;
bool enchanted;
Artifact* artifact;
virtual int enchant( Card * );
virtual int disenchant (Card * );
virtual int equipArtifact( Artifact* );
virtual Artifact* unequipArtifact(Card * );
};
// ------------
class Spell: public Card
{
public:
Spell();
~Spell();
Spell(const Spell &);
protected:
virtual int heal( Spell *&, int );
virtual int attack( Spell *& );
virtual int counter( Spell*& );
int currToughness;
int baseToughness;
int currPower;
int basePower;
};
class Land: public Card
{
public:
Land();
~Land();
protected:
virtual int generateMana(int);
};
class Forest: public Land
{
public:
Forest();
~Forest();
protected:
int generateMana();
};
class Creature: public Spell
{
public:
Creature();
~Creature();
protected:
bool summoningSick;
};
class Sorcery: public Spell
{
public:
Sorcery();
~Sorcery();
protected:
};
#endif
conception.h -- this is an "uber class" from which everything derives
class Conception{
public:
Conception();
~Conception();
protected:
char* classType;
};
conception.cpp
Conception::Conception{
Conception(){
classType = new char[11];
char = "Conception";
}
game.cpp -- this is an incomplete class as of this code
#include <iostream>
#include <cctype>
#include "game.h"
#include "player.h"
Battlefield::Battlefield(){
card = 0;
}
Battlefield::~Battlefield(){
delete card;
}
Battlefield::Battlefield(const Battlefield & to_copy){
}
// ===========
/*
class Game(){
public:
Game();
~Game();
protected:
Player** player; // for multiple players
Battlefield* root; // for battlefield
getPlayerMove(); // ask player what to do
addToBattlefield();
removeFromBattlefield();
sendAttack();
}
*/
#endif
game.h
#ifndef GAME_H
#define GAME_H
#include "list.h"
class CardList();
class Battlefield : CardList{
public:
Battlefield();
~Battlefield();
protected:
Card* card; // make an array
};
class Game : Conception{
public:
Game();
~Game();
protected:
Player** player; // for multiple players
Battlefield* root; // for battlefield
getPlayerMove(); // ask player what to do
addToBattlefield();
removeFromBattlefield();
sendAttack();
Battlefield* field;
};
list.cpp
#include <iostream>
#include <cctype>
#include "list.h"
// ==========
LinkedList::LinkedList(){
root = new Node;
classType = new char[strlen("LinkedList") + 1];
classType = "LinkedList";
};
LinkedList::~LinkedList(){
delete root;
}
LinkedList::LinkedList(const LinkedList & obj)
{
// code to copy
}
// ---------
// =========
int LinkedList::delete_all(Node* root){
if (root = 0)
return 0;
delete_all(root->next);
root = 0;
}
int LinkedList::add( Conception*& is){
if (root == 0){
root = new Node;
root->next = 0;
}
else
{
Node * curr = root;
root = new Node;
root->next=curr;
root->it = is;
}
}
int LinkedList::remove(Node * root, Node * prev, Conception* is){
if (root = 0)
return -1;
if (root->it == is){
root->next = root->next;
return 0;
}
remove(root->next, root, is);
return 0;
}
Conception* LinkedList::find(Node*& root, const Conception* is, Conception* holder = NULL)
{
if (root==0)
return NULL;
if (root->it == is){
return root-> it;
}
holder = find(root->next, is);
return holder;
}
Node* LinkedList::goForward(Node * root){
if (root==0)
return root;
if (root->next == 0)
return root;
else
return root->next;
}
// ============
Node* LinkedList::goBackward(Node * root){
root = root->prev;
}
list.h
#ifndef LIST_H
#define LIST_H
#include <iostream>
#include "conception.h"
class Node : public Conception {
public:
Node() : next(0), prev(0), it(0)
{ it = 0;
classType = new char[strlen("Node") + 1];
classType = "Node";
};
~Node(){
delete it;
delete next;
delete prev;
}
Node* next;
Node* prev;
Conception* it; // generic object
};
// ----------------------
class LinkedList : public Conception {
public:
LinkedList();
~LinkedList();
LinkedList(const LinkedList&);
friend bool operator== (Conception& thing_1, Conception& thing_2 );
protected:
virtual int delete_all(Node*);
virtual int add( Conception*& ); //
virtual Conception* find(Node *&, const Conception*, Conception* ); //
virtual int remove( Node *, Node *, Conception* ); // removes question with keyword int display_all(node*& );
virtual Node* goForward(Node *);
virtual Node* goBackward(Node *);
Node* root;
// write copy constrcutor
};
// =============
class CircularLinkedList : public LinkedList {
public:
// CircularLinkedList();
// ~CircularLinkedList();
// CircularLinkedList(const CircularLinkedList &);
};
class DoubleLinkedList : public LinkedList {
public:
// DoubleLinkedList();
// ~DoubleLinkedList();
// DoubleLinkedList(const DoubleLinkedList &);
protected:
};
// END OF LIST Hierarchy
#endif
player.cpp
#include <iostream>
#include "player.h"
#include "list.h"
using namespace std;
Library::Library(){
root = 0;
}
Library::~Library(){
delete card;
}
// ====DECL=========
Player::~Player(){
delete fname;
delete lname;
delete deck;
}
Wizard::~Wizard(){
delete mana;
delete rootL;
delete rootH;
}
// =====Player======
void Player::changeName(const char[] first, const char[] last){
char* backup1 = new char[strlen(fname) + 1];
strcpy(backup1, fname);
char* backup2 = new char[strlen(lname) + 1];
strcpy(backup1, lname);
if (first != NULL){
fname = new char[strlen(first) +1];
strcpy(fname, first);
}
if (last != NULL){
lname = new char[strlen(last) +1];
strcpy(lname, last);
}
return 0;
}
// ==========
void Player::seeStats(Stats*& to_put){
to_put->wins = stats->wins;
to_put->losses = stats->losses;
to_put->winRatio = stats->winRatio;
}
// ----------
void Player::displayDeck(const LinkedList* deck){
}
// ================
void CardList::findCard(Node* root, int id, NodeCard*& is){
if (root == NULL)
return;
if (root->it.id == id){
copyCard(root->it, is);
return;
}
else
findCard(root->next, id, is);
}
// --------
void CardList::deleteAll(Node* root){
if (root == NULL)
return;
deleteAll(root->next);
root->next = NULL;
}
// ---------
void CardList::removeCard(Node* root, int id){
if (root == NULL)
return;
if (root->id = id){
root->prev->next = root->next; // the prev link of root, looks back to next of prev node, and sets to where root next is pointing
}
return;
}
// ---------
void CardList::addCard(Card* to_add){
if (!root){
root = new Node;
root->next = NULL;
root->prev = NULL;
root->it = &to_add;
return;
}
else
{
Node* original = root;
root = new Node;
root->next = original;
root->prev = NULL;
original->prev = root;
}
}
// -----------
void CardList::displayAll(Node*& root){
if (root == NULL)
return;
cout << "Card Name: " << root->it.cardName;
cout << " || Type: " << root->it.type << endl;
cout << " --------------- " << endl;
if (root->classType == "Spell"){
cout << "Base Power: " << root->it.basePower;
cout << " || Current Power: " << root->it.currPower << endl;
cout << "Base Toughness: " << root->it.baseToughness;
cout << " || Current Toughness: " << root->it.currToughness << endl;
}
cout << "Card Type: " << root->it.currPower;
cout << " || Card Color: " << root->it.color << endl;
cout << "Mana Cost" << root->it.manaCost << endl;
cout << "Keywords: " << root->it.keywords << endl;
cout << "Flavor Text: " << root->it.flavorText << endl;
cout << " ----- Class Type: " << root->it.classType << " || ID: " << root->it.id << " ----- " << endl;
cout << " ******************************************" << endl;
cout << endl;
// -------
void CardList::copyCard(const Card& to_get, Card& put_to){
put_to.type = to_get.type;
put_to.color = to_get.color;
put_to.manaCost = to_get.manaCost;
put_to.inPlay = to_get.inPlay;
put_to.tapped = to_get.tapped;
put_to.class = to_get.class;
put_to.id = to_get.id;
put_to.enchanted = to_get.enchanted;
put_to.artifact = to_get.artifact;
put_to.class = to_get.class;
put.to.abilities = new char[strlen(to_get.abilities) +1];
strcpy(put_to.abilities, to_get.abilities);
put.to.keywords = new char[strlen(to_get.keywords) +1];
strcpy(put_to.keywords, to_get.keywords);
put.to.flavorText = new char[strlen(to_get.flavorText) +1];
strcpy(put_to.flavorText, to_get.flavorText);
if (to_get.class = "Spell"){
put_to.baseToughness = to_get.baseToughness;
put_to.basePower = to_get.basePower;
put_to.currToughness = to_get.currToughness;
put_to.currPower = to_get.currPower;
}
}
// ----------
player.h
#ifndef player.h
#define player.h
#include "list.h"
// ============
class CardList() : public LinkedList(){
public:
CardList();
~CardList();
protected:
virtual void findCard(Card&);
virtual void addCard(Card* );
virtual void removeCard(Node* root, int id);
virtual void deleteAll();
virtual void displayAll();
virtual void copyCard(const Conception*, Node*&);
Node* root;
}
// ---------
class Library() : public CardList(){
public:
Library();
~Library();
protected:
Card* card;
int numCards;
findCard(Card&); // get Card and fill empty template
}
// -----------
class Deck() : public CardList(){
public:
Deck();
~Deck();
protected:
enum deckColor { WHITE, BLUE, BLACK, RED, GREEN, MIXED };
char* deckName;
}
// ===============
class Mana(int amount) : public Conception {
public:
Mana() : displayTotal(0), classType(0)
{ displayTotal = 0;
classType = new char[strlen("Mana") + 1];
classType = "Mana";
};
protected:
int accrued;
void add();
void remove();
int displayTotal();
}
inline Mana::add(){ accrued += 1; }
inline Mana::remove(){ accrued -= 1; }
inline Mana::displayTotal(){ return accrued; }
// ================
class Stats() : public Conception {
public:
friend class Player;
friend class Game;
Stats() : wins(0), losses(0), winRatio(0) {
wins = 0; losses = 0;
if ( (wins + losses != 0)
winRatio = wins / (wins + losses);
else
winRatio = 0;
classType = new char[strlen("Stats") + 1];
classType = "Stats";
}
protected:
int wins;
int losses;
float winRatio;
void int getStats(Stats*& );
}
// ==================
class Player() : public Conception{
public:
Player() : wins(0), losses(0), winRatio(0) {
fname = NULL;
lname = NULL;
stats = NULL;
CardList = NULL;
classType = new char[strlen("Player") + 1];
classType = "Player";
};
~Player();
Player(const Player & obj);
protected:
// member variables
char* fname;
char* lname;
Stats stats; // holds previous game statistics
CardList* deck[]; // hold multiple decks that player might use - put ll in this
private:
// member functions
void changeName(const char[], const char[]);
void shuffleDeck(int);
void seeStats(Stats*& );
void displayDeck(int);
chooseDeck();
}
// --------------------
class Wizard(Card) : public Player(){
public:
Wizard() : { mana = NULL; rootL = NULL; rootH = NULL};
~Wizard();
protected:
playCard(const Card &);
removeCard(Card &);
attackWithCard(Card &);
enchantWithCard(Card &);
disenchantWithCard(Card &);
healWithCard(Card &);
equipWithCard(Card &);
Mana* mana[];
Library* rootL; // Library
Library* rootH; // Hand
}
#endif
At least one of your problems is that in "player.h" you have
#ifndef player.h
#define player.h
"player.h" is not a legal pre-processor symbol. Did you mean
#ifndef player_h
#define player_h
?
Secondly, conception.cpp doesn't #include anything.
Third, your class definitions are largely invalid.
class Foo()
is not legal, nor is
class Foo() : public class Bar()
What does '()' have to do with a class name? Are you thinking of the constructor?
Then there is this
char = "Conception";
You can't assign a value to a type.
----- Feedback to help you clean up the code -----
. Choose a style
Or - if you are working with someone else's code, take theirs.
But stick with it.
A huge percentage of software defects that make it past initial development are there because they were hard to spot - missing semi-colons, missing {s around compound statements, etc. C.f. "CARD_H" vs "player.h".
. Inconsistency is the mother of most bugs
classType = new char[11];
char = "Conception";
You probably mean
classType = new char[11];
classType = "Conception";
but this is a memory leak and a bug waiting to happen. In Card:: you do it more correctly
name = new char[strlen(to_copy.name) +1]; // creating dynamic array
strcpy(to_copy.name, name);
The version you use elsewhere
classType = new ...
classType = "String";
allocates some memory, stores the address in classType. Then it looks up the variable of the compiled char* array "String\0" and stores it's address in classType instead.
When the class goes away, it will try to delete the static string and crash.
If this is a learning exercise and you're trying to learn about memory management, this general approach may be fair enough. But placing ownership of pointers in your classes like this is a sure-fire way to wind up with memory leaks and undefined behavior bugs.
It's best to encapsulate pointers in an RAII-style class (a class which owns the pointer and does the delete when it goes out of scope). Take a look into "std::unique_ptr", "std::shared_ptr" and "std::weak_ptr". For your purposes, "std::string" may help you reduce the number of defects by eliminating a lot of the managerial overhead.
. Try to avoid mixing initializer lists with assignment lists.
It's generally better to use one or the other. You can probably get away with using initializer lists when all of your members can be initialized that way, but if they can't, it may be better to use assignment.
Foo() : m_a(), m_b(), m_c() { m_b = 1; m_c = 2; } // is m_a not having a value a bug or intentional?
. Distinguish member variables from ordinary variables.
You're going to run into bugs where values dissapear on you as a result of shadowing: Shadowing variables
#include <iostream>
int i = 0;
int main() {
int i = 1;
for (int i = 0; i < 10; ++i) {
int i = 2 * i;
std::cout << i << std::endl;
}
return 0;
}
when you don't distinguish your member variables (a lot of people use an "m_" prefix, others use a "_" suffix) this is GOING to happen to you frequently.
. Don't assign numeric values to pointers.
name = 0;
while this compiles, you're setting yourself up for less obvious cases appearing to be numbers and Bad Things Ensuing.
abilities = 0;
No, I'm superman, I have ALL the abilities.
abilities = 42;
Two more correct ways to do this would be
name = NULL; // Traditional C++
or
name = nullptr; // C++11
You've done this in someplaces, again consistency is failing you.
. (minor but it'll bite you in the ass in a few weeks) "it" is generally used to reference an "iterator", you might want to use "data" or "value" or "element".
. avoid making members of classes/objects public.
Your "Node" class looks incredibly buggy (the destructor deletes both prev and next???) and you can't tell, from looking at the class, how the "it" pointer gets set, presumably because that happens elsewhere. Where else do you tamper with the "it", prev and next pointers? Encapsulate.
. 'const' can be your friend (by being a pain in your ass)
if (to_get.class = "Spell"){
This will assign "Spell" to to_get.class, causing a memory leak and other issues, and then succeed -- "Spell" evaluates to a fixed const char* address, which is non-zero, which is therefore true.
(It also doesn't compile because 'class' is a keyword and the actual variable is 'className').
You can prevent this by protecting your actual members and only exposing them thru carefully chosen accessors.
const char* Class() const { return m_className; }
Let me break this one down:
const char* :- you cannot modify the contents,
Class() :- instead of to_get.class you'll use to_get.Class()
const :- this function does not have side-effects on the object
The last part means that it can be used on a const object.
class Beer {
bool m_isFull;
public:
Beer() : m_isFull(true) {}
// non-const function, has side-effects (changes isFull);
void drink() { m_isFull = false; }
// const function, returns a copy of "m_isFull". you can
// change the value that's returned, but it doesn't affect us.
void isFull() const { return m_isFull; }
// example of a non-const accessor, if you REALLY want
// the caller to be able to modify m_isFull for some reason.
const bool& getIsFull() { return m_isFull; }
};
. Lastly: Learn to isolate concepts and algorithms.
A lot of the mistakes/bugs/errors in the code appear to be because you're not 100% with some of the nuances or even details. That's not unreasonable, but you need to find a way to be able to try out the little bits of the language.
Take a little time to learn to roll out micro-programs on something like ideone.com. If you are using Linux, make yourself a "srctest" directory with a "test.cpp" and "test.h" and a Makefile
Makefile
all: srctest
srctest: srctest.cpp srctestextern.cpp srctest.h
g++ -o srctest -Wall -ggdb srctest.cpp srctestextern.cpp
srctest.cpp
#include "srctest.h"
#include <iostream>
// add your other includes here and just leave them.
int main() {
return 0;
}
srctest.h
#ifndef SRCTEST_SRCTEST_H
#define SRCTEST_SRCTEST_H
// anything I want to test in a .h file here
#endif
srctestextern.cpp
#include "srctest.h"
// Anything you want to test having in a separate source file goes here.
If you're using visual studio, set yourself up something similar.
The idea is to have somewhere you can go and drop in a few lines of code and be comfortable stepping thru what you're trying in a debugger.
Being able to quickly localize problems is a key part of being a successful programmer as opposed to being an employed code monkey.
I'm am fairly new to C++.
I have this code from an assignment, i don't quite understand all of it, but i have to make the program give an option at the end for the user to recall any partnumber and model year/engine no. that was entered.
I have no idea on how to go about doing this task... maybe have some kind of id for each node so i can recall it?
Or is it my only option to rewrite the program using an array or vector data structure?
#include <iostream>
using namespace std;
typedef unsigned long ULONG;
typedef unsigned short USHORT;
// **************** Part ************
// Abstract base class of parts
class Part
{
friend void showPart();
public:
Part():itsPartNumber(1) {}
Part(ULONG PartNumber):itsPartNumber(PartNumber){}
virtual ~Part(){};
ULONG GetPartNumber() const { return itsPartNumber; }
virtual void Display() const =0; // must be overridden
private:
ULONG itsPartNumber;
};
// implementation of pure virtual function so that
// derived classes can chain up
void Part::Display() const
{
cout << "\nPart Number: " << itsPartNumber << endl;
}
// **************** Car Part ************
class CarPart : public Part
{
friend void showPart();
public:
CarPart():itsModelYear(94){}
CarPart(USHORT year, ULONG partNumber);
virtual void Display() const
{
Part::Display(); cout << "Model Year: ";
cout << itsModelYear << endl;
}
private:
USHORT itsModelYear;
};
CarPart::CarPart(USHORT year, ULONG partNumber):
itsModelYear(year),
Part(partNumber)
{}
// **************** AirPlane Part ************
class AirPlanePart : public Part
{
friend void showPart();
public:
AirPlanePart():itsEngineNumber(1){};
AirPlanePart(USHORT EngineNumber, ULONG PartNumber);
virtual void Display() const
{
Part::Display(); cout << "Engine No.: ";
cout << itsEngineNumber << endl;
}
private:
USHORT itsEngineNumber;
};
AirPlanePart::AirPlanePart(USHORT EngineNumber, ULONG PartNumber):
itsEngineNumber(EngineNumber),
Part(PartNumber)
{}
// **************** Part Node ************
class PartNode
{
public:
PartNode (Part*);
~PartNode();
void SetNext(PartNode * node) { itsNext = node; }
PartNode * GetNext() const;
Part * GetPart() const;
private:
Part *itsPart;
PartNode * itsNext;
};
// PartNode Implementations...
PartNode::PartNode(Part* pPart):
itsPart(pPart),
itsNext(0)
{}
PartNode::~PartNode()
{
delete itsPart;
itsPart = 0;
delete itsNext;
itsNext = 0;
}
// Returns NULL if no next PartNode
PartNode * PartNode::GetNext() const
{
return itsNext;
}
Part * PartNode::GetPart() const
{
if (itsPart)
return itsPart;
else
return NULL; //error
}
// **************** Part List ************
class PartsList
{
public:
PartsList();
~PartsList();
// needs copy constructor and operator equals!
Part* Find(ULONG & position, ULONG PartNumber) const;
ULONG GetCount() const { return itsCount; }
Part* GetFirst() const;
static PartsList& GetGlobalPartsList()
{
return GlobalPartsList;
}
void Insert(Part *);
void Iterate(void (Part::*f)()const) const;
Part* operator[](ULONG) const;
private:
PartNode * pHead;
ULONG itsCount;
static PartsList GlobalPartsList;
};
PartsList PartsList::GlobalPartsList;
// Implementations for Lists...
PartsList::PartsList():
pHead(0),
itsCount(0)
{}
PartsList::~PartsList()
{
delete pHead;
}
Part* PartsList::GetFirst() const
{
if (pHead)
return pHead->GetPart();
else
return NULL; // error catch here
}
Part * PartsList::operator[](ULONG offSet) const
{
PartNode* pNode = pHead;
if (!pHead)
return NULL; // error catch here
if (offSet > itsCount)
return NULL; // error
for (ULONG i=0;i<offSet; i++)
pNode = pNode->GetNext();
return pNode->GetPart();
}
Part* PartsList::Find(ULONG & position, ULONG PartNumber) const
{
PartNode * pNode = 0;
for (pNode = pHead, position = 0;
pNode!=NULL;
pNode = pNode->GetNext(), position++)
{
if (pNode->GetPart()->GetPartNumber() == PartNumber)
break;
}
if (pNode == NULL)
return NULL;
else
return pNode->GetPart();
}
void PartsList::Iterate(void (Part::*func)()const) const
{
if (!pHead)
return;
PartNode* pNode = pHead;
do
(pNode->GetPart()->*func)();
while (pNode = pNode->GetNext());
}
void PartsList::Insert(Part* pPart)
{
PartNode * pNode = new PartNode(pPart);
PartNode * pCurrent = pHead;
PartNode * pNext = 0;
ULONG New = pPart->GetPartNumber();
ULONG Next = 0;
itsCount++;
if (!pHead)
{
pHead = pNode;
return;
}
// if this one is smaller than head
// this one is the new head
if (pHead->GetPart()->GetPartNumber() > New)
{
pNode->SetNext(pHead);
pHead = pNode;
return;
}
for (;;)
{
// if there is no next, append this new one
if (!pCurrent->GetNext())
{
pCurrent->SetNext(pNode);
return;
}
// if this goes after this one and before the next
// then insert it here, otherwise get the next
pNext = pCurrent->GetNext();
Next = pNext->GetPart()->GetPartNumber();
if (Next > New)
{
pCurrent->SetNext(pNode);
pNode->SetNext(pNext);
return;
}
pCurrent = pNext;
}
}
int main()
{
PartsList pl = PartsList::GetGlobalPartsList();
Part * pPart = 0;
ULONG PartNumber;
USHORT value;
ULONG choice;
while (1)
{
cout << "(0)Quit (1)Car (2)Plane: ";
cin >> choice;
if (!choice)
break;
cout << "New PartNumber?: ";
cin >> PartNumber;
if (choice == 1)
{
cout << "Model Year?: ";
cin >> value;
pPart = new CarPart(value,PartNumber);
}
else
{
cout << "Engine Number?: ";
cin >> value;
pPart = new AirPlanePart(value,PartNumber);
}
pl.Insert(pPart);
}
void (Part::*pFunc)()const = &Part::Display;
pl.Iterate(pFunc);
cout << "\n\n\nThere are " << pl.GetCount() << " items in the list" << endl;
return 0;
}
I tried using The Find() in the PartsList class. Does Find() take the partnumber and return the address of the part?
I wrote this to dereference the retrieved address, but it gives me the error no match for 'operator<<' in 'std::cout << * show':
int findnumber;
ULONG position;
cout << "Enter Partnumber" << endl;
cin >> findnumber;
Part* show = pl.Find(position, findnumber);
cout << *show;
Am i doing this all wrong? D: Show me please...
The function Find does take a part number, but returns a pointer to a part, which is not the same as the address of the part (that would be a reference, denoted by an &). In addition, Find takes a reference to a variable called 'position', so after calling the Find function, the variable that was passed in for 'position' will contain the value of where the part is in the linked list.
The reason you can't use the << operator, is that it hasn't been provided for the Part class. However, from the source code provided, it looks like the objective is for you to understand Polymorphism and rather than trying to use <<, call the Display function on the part that you have found. e.g: -
Part* part = pl.Find(position, findnumber);
part->Display();
This will display the text for the relevant type of part, so if the part returned was a CarPart, the CarPart's Display function will be called, whereas if the part is an AirPlane part, its Display function is called.
If you wanted to use the stream operator (<<) you'd need to overload the io operators, which you can read more about here.
The class PartsList already has a Find() method that could be used to retrieve any part based on its partnumber. You can then call the Display() method of that part.
So I have a project of which I want to switch to Astar due to speed reasons.
But C++ is not my strongest point. Could anyone help me (or tell me how to do the..) converting the algorythm from Dijkstra to Astar?
I found this Astar implementation:
http://code.google.com/p/a-star-algorithm-implementation/
But I don't know how to use it with my existing code.
Here is the graph file which got the algorithm:
#include "Graph.h"
#include <iostream>
#include <algorithm>
#include <stack>
Graph::Graph(void)
{
}
Graph::~Graph(void)
{
while(!mNodes.empty())
{
delete mNodes.back();
mNodes.pop_back();
}
}
void Graph::addNode(int name, bool exists, Node** NodeID )
{
Node* pStart = NULL;
mNodes.push_back(new Node(name,exists));
std::vector<Node*>::iterator itr;
itr = mNodes.begin()+mNodes.size()-1;
pStart = (*itr);
if(exists == true)pStart->DoesExist_yes();
*NodeID = pStart;
}
void Graph::connect_oneway(Node* pFirst, Node* pSecond, int moveCost)
{
if(pFirst != NULL && pSecond != NULL)
{
pFirst->createEdge(pSecond, moveCost);
}
}
#define MAX_NODES (32768)
#define MAX_CONNECTIONS (5)
#include <time.h>
int * Graph::findPath_r(Node* pStart, Node* pEnd)
{
int *arr = new int[MAX_NODES+2];
for (int i=0; i<MAX_NODES; i++)
arr[i] = -1;
arr[0] = 0;
if(pStart == pEnd)
{
return arr;
}
std::vector<Node*> openList;
openList.push_back(pStart);
Node* pCurrNode = NULL;
while(!openList.empty())
{
//Get best node from open list (lowest F value).
//Since we sort the list at the end of the previous loop we know
//the front node is the best
pCurrNode = openList.front();
//Exit if we're are the goal
if(pCurrNode == pEnd)
break;
//Remove the node from the open list and place it in the closed
openList.erase(openList.begin());
pCurrNode->setClosed(true); //We use a flag instead of a list for speed
//Test all of the edge nodes from the current node
std::vector<Edge*>* pEdges = pCurrNode->getEdges();
Node* pEdgeNode = NULL;
for(std::vector<Edge*>::iterator i = pEdges->begin(); i != pEdges->end(); ++i)
{
pEdgeNode = (*i)->pNode;
//If it's closed we've already analysed it
if(!pEdgeNode->getClosed() && pCurrNode->DoesExist() == true)
{
if(!inList(pEdgeNode,&openList))
{
openList.push_back(pEdgeNode);
pEdgeNode->setGCost(pCurrNode->getGCost()+(*i)->moveCost);
pEdgeNode->calcFCost();
pEdgeNode->setParent(pCurrNode);
}
else
{
//If this is a better node (lower G cost)
if(pEdgeNode->getGCost() > pCurrNode->getGCost()+(*i)->moveCost)
{
pEdgeNode->setGCost(pCurrNode->getGCost()+(*i)->moveCost);
pEdgeNode->calcFCost();
pEdgeNode->setParent(pCurrNode);
}
}
}
}
//Place the lowest F cost item in the open list at the top, so we can
//access it easily next iteration
std::sort(openList.begin(), openList.end(), Graph::compareNodes);
}
//Make sure we actually found a path
if(pEnd->getParent() != NULL)
{
//Output the path
//Use a stack because it is LIFO
std::stack<Node*> path;
while(pCurrNode != NULL)
{
path.push(pCurrNode);
pCurrNode = pCurrNode->getParent();
}
int counter = 0;
arr[1] = 0;
while(!path.empty())
{
arr[counter+2] = path.top()->getName();
counter++;
arr[1] += path.top()->getGCost();
path.pop();
}
arr[0] = counter;
return arr;
}
return arr;
}
bool Graph::inList(Node* pNode, std::vector<Node*>* pList)
{
for(std::vector<Node*>::iterator i = pList->begin(); i != pList->end(); ++i)
{
if((*i) == pNode)
{
return true;
}
}
return false;
}
bool Graph::compareNodes(Node* pFirst, Node* pSecond)
{
return pFirst->getFCost() < pSecond->getFCost();
}
void Graph::reset(void)
{
for(std::vector<Node*>::iterator i = mNodes.begin(); i != mNodes.end(); ++i)
{
(*i)->reset();
}
}
The function for finding the path is this one:
Graph::findPath_r
What I really want to do is preserve the edges (because they decide if the road is both or one-way).
Here are the other files:
Graph.h
#ifndef _GRAPH_H_
#define _GRAPH_H
#include "Node.h"
class Graph
{
public:
Graph(void);
~Graph(void);
//void addNode(int name, bool exists);
void addNode(int name, bool exists, Node** NodeID );
void connect_oneway(int ppFirst, int ppSecond, int moveCost);
void connect_oneway(Node* pFirst, Node* pSecond, int moveCost);
//int * findPath_r(int start, int end);
int * findPath_r(Node* pStart, Node* pEnd);
void reset(void);
private:
void findNodesx(int firstName, Node** ppFirstNode);
bool inList(Node* pNode, std::vector<Node*>* pList);
static bool compareNodes(Node* pFirst, Node* pSecond);
std::vector<Node*> mNodes;
};
#endif
Node.h
#ifndef _NODE_H_
#define _NODE_H_
#include <string>
#include <vector>
//Forward declare Node so Edge can see it
class Node;
struct Edge
{
Edge(Node* node, int cost) : pNode(node), moveCost(cost){}
Node* pNode;
int moveCost;
};
class Node
{
public:
Node(void);
Node(int name, bool exists);
~Node(void);
void createEdge(Node* pTarget, int moveCost);
void setGCost(int cost);
void setClosed(bool closed);
void setParent(Node* pParent);
int getGCost(void);
int getFCost(void);
bool getClosed(void);
Node* getParent(void);
int getName(void);
bool DoesExist(void);
bool DoesExist_yes(void);
std::vector<Edge*>* getEdges(void);
void calcFCost(void);
void reset(void);
private:
int mGCost;
int mTotal;
bool mClosed;
Node* mpParent;
int mName;
bool mHeur;
std::vector<Edge*> mEdges;
};
#endif
Node.cpp
#include "Node.h"
Node::Node(void)
{
}
Node::Node(/*const std::string&*/int name, bool exists) : mGCost(0), mTotal(0), mClosed(false), mpParent(NULL), mName(name), mHeur(exists)
{
}
Node::~Node(void)
{
while(!mEdges.empty())
{
delete mEdges.back();
mEdges.pop_back();
}
}
int Node::getName(void)
{
return mName;
}
void Node::createEdge(Node* pTarget, int moveCost)
{
mEdges.push_back(new Edge(pTarget, moveCost));
}
void Node::setClosed(bool closed)
{
mClosed = closed;
}
bool Node::getClosed(void)
{
return mClosed;
}
std::vector<Edge*>* Node::getEdges(void)
{
return &mEdges;
}
int Node::getGCost(void)
{
return mGCost;
}
void Node::setGCost(int cost)
{
mGCost = cost;
}
void Node::calcFCost(void)
{
mTotal = mGCost;
}
void Node::setParent(Node* pParent)
{
mpParent = pParent;
}
int Node::getFCost(void)
{
return mTotal;
}
bool Node::DoesExist(void)
{
return mHeur;
}
bool Node::DoesExist_yes(void)
{
mHeur = true;
return true;
}
Node* Node::getParent(void)
{
return mpParent;
}
void Node::reset(void)
{
mGCost = 0;
mTotal = 0;
mClosed = false;
mpParent = NULL;
}
You mentioned a library on GoogleCode. It is node clear what you want to do with, and I think the best is to write your implementation yourself.
First, you should know that Dijsktra is a special case of A*. In A*, you have an heuristic, named h; A* = possible implementation of Dijsktra when h is the null function.
Then, about your implementation, let's start with Node. It will need the following functions:
constructor, destructor
create/get edge
set/get parent
set/is closed (for speed)
set/get GCost
set/get FCost
set/is obstacle (name way more descriptive than 'DoesExist')
set/get position
reset
// optional method:
get name
Hopefully, this part of your code won't change a lot. The heuristic code will be placed in the pathfinder. The Edge class is left untouched.
Now the big one: Graph. You won't need to delete any of your public methods.
You will need a heuristic method. For the implementation which will be described, you will need an admissible consistent heuristic:
it must not over-estimate the distance to the goal (admissible)
it must be monotone (consistent)
The general case signature is int getHCost(Node* node);. If you always return 0, you will have a Dijsktra algorithm, which is not what you want. Here we will take the euclidiean distance between the node and the goal. Slower to compute than manhattan distance, but better results. You can change this afterwards.
int getHCost(Node* node, Note* goal);
This implies you must place your nodes in the 3d space. Note that the heuristic is a heuristic, ie, an estimation of the distance.
I won't write the code. I will write some pseudo-code adapted to your situation. The original pseudocode is located on the Wikipedia A* page. This pseudo-code is your findPath_r function:
function A*(start,goal)
set all nodes to not closed // The set of nodes already evaluated.
openset = {start} // The set of tentative nodes to be evaluated, initially containing the start node
start.gcost = 0 // Cost from start along best known path.
// Estimated total cost from start to goal through y.
start.fcost = start.gcost + getHCost(start, goal)
while openset is not empty
current = the node in openset having the lowest f_cost (usually the first if you use a sorted list)
if current == goal
return construct_path(goal)
remove current from openset
current.closed = true
for each neighbor in (node connected by edge in current.edges) // Here is the condition for one-way edges
if neighbor.closed or neighbor.obstacle
continue
gcost = current.gcost + dist_between(current,neighbor) // via edge distance
if neighbor not in openset
add neighbor to openset
neighbor.parent = current
neighbor.gcost = gcost
neighbor.fcost = neighbor.gcost + getHCost(neighbor, goal)
else if gcost < neighbor.gcost
neighbor.parent = current
neighbor.gcost = gcost
neighbor.fcost = neighbor.gcost + getHCost(neighbor, goal)
update neighbor position in openset
return failure
function construct_path(current_node)
std::vector<Node*> path
while current_node != 0
path.push_front(current_node)
current_node = current_node.parent
return path
The implementation above use one-way edges.
You were able to write Dijsktra algorithm in C++, so writing this pseudocode in C++ shouldn't be a problem.
Second part, performances. First, measure ;).
I have some hints that can improve performances:
use a memory pool for allocation deallocation
use an intrusive list for the open list (you can also make it auto-sorted with this technique)
I advise you to read A* for beginners, which is a useful reading, even if you don't use tilemap.