I'm implementing a function to insert a set of nodes in a set of routes. This function is described as follows:
void Repair_ChooseARouteAndAPositionRandomly (SOLUTION &sol, vector<int> &NodesPscine, vector<int>&RoutesPscine, DATA data, int max_slot){
while (NodesPscine.size() != 0) {
int aux;
int pairpos;
int pairRoute;
int pairNode;
list<VEHICLE>:: iterator irouteit;
vector<bool> RotaJaFoiSorteada = vector<bool>(sol.SetP.size(),false);
pairpos = rand() % NodesPscine.size();
pairNode = NodesPscine[pairpos];
pairRoute = RoutesPscine[pairpos];
bool sorteaDenovo = true;
while (sorteaDenovo == true) {
bool rotaJafoiSorteada = false;
while (rotaJafoiSorteada == false) {
aux = rand() % sol.SetP.size();
if (RotaJaFoiSorteada[aux] == false) {
irouteit = next(sol.SetP.begin(), aux);
rotaJafoiSorteada = true;
RotaJaFoiSorteada[aux] = true;
}
}
bool NoJaExisteNaRota = false;
list<int>:: iterator ii;
for (ii = irouteit->rotaVehicle.begin(); ii != irouteit->rotaVehicle.end(); ii++) {
if (pairNode == *ii) {
NoJaExisteNaRota = true;
break;
}
}
if (NoJaExisteNaRota == false) {
double melhorDur = 0;
list <int>::iterator melhorPos = irouteit->rotaVehicle.begin();
double NovaDur;
InsereNaPosicaoMaisBarata (irouteit->rotaVehicle, irouteit->type, pairNode, data, melhorDur, melhorPos);
NovaDur = irouteit->duracaoTotal + melhorDur;
if (NovaDur <= max_slot) {
irouteit->rotaVehicle.insert(melhorPos,pairNode);
irouteit->duracaoTotal = NovaDur;
int cont = 0;
list<int>::iterator itprim = irouteit->rotaVehicle.begin();
list<int>::iterator itseg;
irouteit->distanceTotal = 0;
while (cont < irouteit->rotaVehicle.size()-1) {
itseg = next(itprim, 1);
irouteit->distanceTotal += data.dist[*itprim][*itseg];
++itprim;
++cont;
}
sorteaDenovo = false;
NodesPscine.erase(NodesPscine.begin()+pairpos);
RoutesPscine.erase(RoutesPscine.begin()+pairpos);
} // if (NovaDur <= max_slot) {
} // if (NoJaExisteNaRota == false) {
} // while (sorteaDenovo == true) {
} // while (NodesPscine.size() != 0) {
}
I also describe another function and some structs that may be important to identify my mistake.
void InsereNaPosicaoMaisBarata (list<int> Rota, int vehicle, int no, DATA data, double &melhorDur, list <int>::iterator &melhorPos ) {
list <int>:: iterator itprim = Rota.begin();
list <int>:: iterator itseg;
int cont = 0;
melhorDur = 10000000000000000;
while (cont < (Rota.size()-1)) {
itseg = next(itprim, 1);
double aux = data.l[vehicle][*itprim][no] + data.l[vehicle][no][*itseg] - data.l[vehicle][*itprim][*itseg];
if (melhorDur > aux) {
melhorDur = aux;
melhorPos = itseg;
}
++itprim;
++cont;
}
}
struct VEHICLE {
int type;
int idx;
double custo;
double distanceTotal;
double duracaoTotal;
list<int> rotaVehicle;
};
struct SOLUTION {
list<VEHICLE> SetP;
};
Everything goes fine to insert the first pairNode randomly chosen. However, when a second pairNode is chosen, the program runs until the line just after calling the function InsereNaPosicaoMaisBarata. If I put any cout inside this function, it does not appear. I think the problem is in the line "irouteit->rotaVehicle.insert(melhorPos,pairNode);", because when I comment it, the code runs until the end.
If I have, for example, these routes:
route 1 { 4 2 1 3 4 }
route 2 { 3 2 4 3 }
route 3 { 4 2 1 3 4 }
route 4 {4 1 3 4 }
route 5 { 2 4 2 }
route 6 { 3 2 4 3 }
If we have the nodes to be inserted in any of these routes as NodesPscine = {3,2}. If node 3 is randomly chosen to be inserted in route 5, it works well.
After, node 4 is chosen to be inserted in route 4. Then, the program runs until the function InsereNaPosicaoMaisBarata and the error message appears:
*** Error in `./heuristica': malloc(): memory corruption (fast): 0x00000000035d0680 ***
I suppose the way I'm identifying the melhorPos is not okay. But I can't figure out what is wrong.
You did not post all of your code, but one obvious mistake is this:
InsereNaPosicaoMaisBarata (irouteit->rotaVehicle, // <-- This parameter
irouteit->type,
pairNode,
data,
melhorDur,
melhorPos);
Your function is declared as this:
void InsereNaPosicaoMaisBarata (list<int> Rota, // <-- passed by value
int vehicle,
int no,
DATA data,
double &melhorDur,
list <int>::iterator &melhorPos )
Then inside the function, you do this:
list <int>:: iterator itprim = Rota.begin(); // <-- This is a local std::list
list <int>:: iterator itseg;
//...
itseg = next(itprim, 1);
//...
melhorPos = itseg; // <-- This is now referencing a local std::list entry
On return, you then use the last parameter:
irouteit->rotaVehicle.insert(melhorPos,pairNode); // <-- Trouble
The problem with all of this is that the first parameter (Rota) is passed by value. That means the function InsereNaPosicaMaisBarata is working with a temporary std::list.
You then set melhorPos to point to an entry into the temporary list. The problem is that on return, Rota has been destroyed, and you now have an invalid iterator being used on the return.
The solution is to change the function to have the first parameter as a reference:
void InsereNaPosicaoMaisBarata (list<int>& Rota, // <-- passed by reference
int vehicle,
int no,
DATA data,
double &melhorDur,
list <int>::iterator &melhorPos )
Related
I'm working on a C++ quadtree implementation for collision detection. I tried to adapt this Java implementation to C++ by using pointers; namely, storing the child nodes of each node as Node pointers (code at the end). However, since my understanding of pointers is still rather lacking, I am struggling to understand why my Quadtree class produces the following two issues:
When splitting a Node in 4, the debugger tells me that all my childNodes entries are identical to the first one, i.e., same address and bounds.
Even if 1. is ignored, I get an Access violation reading location 0xFFFFFFFFFFFFFFFF, which I found out is a consequence of the childNode pointees being deleted after the first split, resulting in undefined behaviour.
My question is: what improvements should I make to my Quadtree.hpp so that each Node can contain 4 distinct child node pointers and have those references last until the quadtree is cleared?
What I have tried so far:
Modifying getChildNode according to this guide and using temporary variables in split() to avoid all 4 entries of childNodes to point to the same Node:
void split() {
for (int i = 0; i < 4; i++) {
Node temp = getChildNode(level, bounds, i + 1);
childNodes[i] = &(temp);
}
}
but this does not solve the problem.
This one is particularly confusing. My initial idea was to just store childNodes as Nodes themselves, but turns out that cannot be done while we're defining the Node class itself. Hence, it looks like the only way to store Nodes is by first creating them and then storing pointers to them as I tried to do in split(), yet it seems that those will not "last" until we've inserted all the objects since the pointees get deleted (run out of scope) and we get the aforementioned undefined behaviour. I also thought of using smart pointers, but that seems to only overcomplicate things.
The code:
Quadtree.hpp
#pragma once
#include <vector>
#include <algorithm>
#include "Box.hpp"
namespace quadtree {
class Node {
public:
Node(int p_level, quadtree::Box<float> p_bounds)
:level(p_level), bounds(p_bounds)
{
parentWorld = NULL;
}
// NOTE: mandatory upon Quadtree initialization
void setParentWorld(World* p_world_ptr) {
parentWorld = p_world_ptr;
}
/*
Clears the quadtree
*/
void clear() {
objects.clear();
for (int i = 0; i < 4; i++) {
if (childNodes[i] != nullptr) {
(*(childNodes[i])).clear();
childNodes[i] = nullptr;
}
}
}
/*
Splits the node into 4 subnodes
*/
void split() {
for (int i = 0; i < 4; i++) {
childNodes[i] = &getChildNode(level, bounds, i + 1);;
}
}
/*
Determine which node the object belongs to. -1 means
object cannot completely fit within a child node and is part
of the parent node
*/
int getIndex(Entity* p_ptr_entity) {
quadtree::Box<float> nodeBounds;
quadtree::Box<float> entityHitbox;
for (int i = 0; i < 4; i++) {
nodeBounds = childNodes[i]->bounds;
ComponentHandle<Hitbox> hitbox;
parentWorld->unpack(*p_ptr_entity, hitbox);
entityHitbox = hitbox->box;
if (nodeBounds.contains(entityHitbox)) {
return i;
}
}
return -1; // if no childNode completely contains Entity Hitbox
}
/*
Insert the object into the quadtree. If the node
exceeds the capacity, it will split and add all
objects to their corresponding nodes.
*/
void insertObject(Entity* p_ptr_entity) {
if (childNodes[0] != nullptr) {
int index = getIndex(p_ptr_entity);
if (index != -1) {
(*childNodes[index]).insertObject(p_ptr_entity); // insert in child node
return;
}
}
objects.push_back(p_ptr_entity); // add to parent node
if (objects.size() > MAX_OBJECTS && level < MAX_DEPTH) {
if (childNodes[0] == nullptr) {
split();
}
int i = 0;
while (i < objects.size()) {
int index = getIndex(objects[i]);
if (index != -1)
{
Entity* temp_entity = objects[i];
{
// remove i-th element of the vector
using std::swap;
swap(objects[i], objects.back());
objects.pop_back();
}
(*childNodes[index]).insertObject(temp_entity);
}
else
{
i++;
}
}
}
}
/*
Return all objects that could collide with the given object
*/
std::vector<Entity*> retrieve(Entity* p_ptr_entity, std::vector<Entity*> returnObjects) {
int index = getIndex(p_ptr_entity);
if (index != -1 && childNodes[0] == nullptr) {
(*childNodes[index]).retrieve(p_ptr_entity, returnObjects);
}
returnObjects.insert(returnObjects.end(), objects.begin(), objects.end());
return returnObjects;
}
World* getParentWorld() {
return parentWorld;
}
private:
int MAX_OBJECTS = 10;
int MAX_DEPTH = 5;
World* parentWorld; // used to unpack entities
int level; // depth of the node
quadtree::Box<float> bounds; // boundary of nodes in the game's map
std::vector<Entity*> objects; // list of objects contained in the node: pointers to Entitites in the game
Node* childNodes[4];
quadtree::Box<float> getQuadrantBounds(quadtree::Box<float> p_parentBounds, int p_quadrant_id) {
quadtree::Box<float> quadrantBounds;
quadrantBounds.width = p_parentBounds.width / 2;
quadrantBounds.height = p_parentBounds.height / 2;
switch (p_quadrant_id) {
case 1: // NE
quadrantBounds.top = p_parentBounds.top;
quadrantBounds.left = p_parentBounds.width / 2;
break;
case 2: // NW
quadrantBounds.top = p_parentBounds.top;
quadrantBounds.left = p_parentBounds.left;
break;
case 3: // SW
quadrantBounds.top = p_parentBounds.height / 2;
quadrantBounds.left = p_parentBounds.left;
break;
case 4: // SE
quadrantBounds.top = p_parentBounds.height / 2;
quadrantBounds.left = p_parentBounds.width / 2;
break;
}
return quadrantBounds;
}
Node& getChildNode(int parentLevel, Box<float> parentBounds, int quadrant) {
static Node temp = Node(parentLevel + 1, getQuadrantBounds(parentBounds, quadrant));
return temp;
}
};
}
Where Box is just a helper class that contains some helper methods for rectangular shapes and collision detection. Any help would be greatly appreciated!
Inside the ArrayList I'm trying to delete all possible 0's that are appended as input, but for now it only deletes just one 0, no matter where it is located. But seems like I can't delete more than one zero at the time. How can I fix this?
void AList::elimZeros(){
int i;
int curr = 0;
for(i=0; i < listSize; i++) {
if ( (listArray[i] != 0 ) && (curr<listSize) ){
listArray[curr] = listArray[i];
curr++;
}
else if (listArray[i] == 0 )
{
listArray[curr] = listArray[i+1];
listSize--;
curr++;
}
}
}
This is the class for the ADT
class AList : public List {
private:
ListItemType* listArray; // Array holding list elements
static const int DEFAULT_SIZE = 10; // Default size
int maxSize; // Maximum size of list
int listSize; // Current # of list items
int curr; // Position of current element
// Duplicates the size of the array pointed to by listArray
// and update the value of maxSize.
void resize();
public:
// Constructors
// Create a new list object with maximum size "size"
AList(int size = DEFAULT_SIZE) : listSize(0), curr(0) {
maxSize = size;
listArray = new ListItemType[size]; // Create listArray
}
~AList(); // destructor to remove array
This is the input I'm testing with:
int main() {
AList L(10);
AList L2(20);
L.append(10);
expect(L.to_string()=="<|10>");
L.append(20);
expect(L.to_string()=="<|10,20>");
L.append(30);
L.append(0);
L.append(40);
L.append(0);
L.append(0);
expect(L.to_string()=="<|10,20,30,0,40>");
L.elimZeros();
expect(L.to_string()=="<|10,20,30,40>");
assertionReport();
}
It'd be helpful if you posted the class code for AList. Think you confused Java's ArrayList type, but assuming you're using vectors you can always just do:
for (int i = 0; i < listSize; i++) {
if(listArray[i] == 0) listArray.erase(i);
}
EDIT: Assuming this is the template of for the AList class, then there is simply a remove() function. In terms of your code, there are two issues.
You reference listSize in the for loop, then decrement it inside of the loop. Each iteration evaluates the value separately so you're reducing the number of total loop iterations and stopping early.
The other thing is if the entry is zero you shouldn't increment curr and set listArray[curr] = listArray[i+1]. This is basically assuming the next entry will not be a zero. So if it is, then you're copying the element and moving to the next. Your if statement can be cleaned up with:
if (listArray[i] == 0) {
listSize--;
} else {
listArray[curr] = listArray[i];
curr++;
}
Given a sequence of integers and a number, the program must say if there's any cobination in that sequence that sums the number. For example:
Input: 1 2 3 4 5 # 6
Output: true (because 1+5 = 6, or 2 + 4 = 6, or 1 + 2 + 3 = 6).
It doesn't matter what solution it finds, only if there's a solution.
For input: 1 2 3 4 5 # 100
Output: false. None of the combination of that numbers sums 100.
Now, for input:
243 5 35 24 412 325 346 24 243 432 # 1000
I'm getting
main: malloc.c:2401: sysmalloc: Assertion `(old_top == initial_top (av) && old_size == 0) || ((unsigned long) (old_size) >= MINSIZE && prev_inuse (old_top) && ((unsigned long) old_end & (pagesize - 1)) == 0)' failed.
When it's suppose to say false.
I must use 3 classes. Solver, solution and candidat.
Solver just calls the backtracking method.
Solution has a possible solution.
Candidat has the indeix of the number of the sequence which is being looked.
I don't understand how to use the integer _lvl of Solution class to move around the different candidates.
Class Solver is correct. The error must be in solution class and candidats.
My question is, how must I use candidats and _lvl to check the possible solutions?
How should I implement the following methods in solution class?:
Acceptable, complet, anotate, desanotate.
Im getting wrong answers and out_of_ranges errors.
class solver
{
public:
solver();
bool solve(const solution &initial);
solucio getSolution() const;
private:
void findASolution();
bool _found;
solution _sol;
};
solver.cpp
bool solver::solve(const solution &initial)
{
_found = false;
_sol = initial;
findASolution();
return (_found);
}
void solver::findASolution()
{
candidat iCan = _sol.inicializateCandidats();
while ((not iCan.isEnd()) and (!_found))
{
if (_sol.acceptable(iCan)) {
_sol.anotate(iCan);
if(not _sol.complet()) {
findASolution();
if (!_found) {
_sol.desanotate(iCan);
}
}
else {
_found = true;
}
}
iCan.next();
}
}
This class is supposed to be correct. Im having trouble with classes solution and candidat. Class solution have 5 important methods: Acceptable, Complet, inicializateCandidates(), anotate and desanotate.
Acceptable is true if a candidate can be part of the solution.
Complet if a solution is found.
Anotate to save the possible candidates.
Desanotate to remove candidates that no long can be part of the solution.
inicializateCandidates invoces the candidats constructor.
solution();
solution(const int sequence[], const int &n, const int &sum) {
_searchedSum = sum;
_n = n;
_sum = 0;
_lvl = 0;
reserve(); // bad_alloc. Makes space for vectors
for (int i = 0; i < n; i++) {
_sequence[i] = sequence[i];
_candidates[i] = - 1;
}
solution(const solution &o);
~solution();
solution & operator=(const solution &o);
candidat inicializateCandidats() const {
return candidat(_n);
}
bool acceptable(const candidat &iCan) const {
return (_sum + _sequence[iCan.actual()] <= _searchedSum);
}
bool complet() const {
return (_sum == _searchedSum);
}
void show() const;
void anotate(const candidat &iCan) {
_niv++;
_candidates[_niv] = iCan.actual();
_sum += _sequence[iCan.actual()];
}
void desanotate(const candidat &iCan) {
_candidates[_niv] = - 1;
_sum -= _sequence[iCan.actual()];
_niv--;
}
private:
// memory gestion methods
void solution::reserve() {
_sequence = new int[_n];
_candidates = new int[_n];
}
int *_sequence; // original sequence
int *_candidates; // possible subsequence part of solution
int _n; // size of the array
int _lvl; // lvl of the tree generated by backtracking
int _searchedSum;
int _sum; // total sum of actual solution
And class candidat, which is just a counter. Nothing else.
candidat::candidat(const int &n) {
_size = n;
_iCan = 0;
}
bool candidat::isEnd() const {
return (_iCan >= _size);
}
int candidat::actual() const {
if (esEnd()) {
throw ("No more candidates");
}
return _iCan;
}
void candidat::next() {
if (esFi()) {
throw ("No more candidates");
}
_iCan++;
}
I've found a possible solution but it does not fit the requirements at all.
In class solver, I create an attribute to save anterior candidate, inicializate at -1.
The constructor of candidat class changes at this way:
candidat::candidat(const int &n, const int &ant) {
_size = n;
_iCan = ant + 1;
}
In solution.h now there is a boolean array to save the candidates that can be part of the solution. _lvl is eliminated.
In solver.cpp, the backtracking changes a little, but it shouldn't be changed.
bool solver::solve(const solution &initial) {
_found = false;
_ant = -1;
_sol = initial;
findASolution();
return (_found);
}
void solver::findASolution() {
**candidat iCan = _sol.inicializateCandidats(_ant);**
while ((not iCan.isEnd()) and (!_found))
{
if (_sol.acceptable(iCan)) {
_sol.anotate(iCan);
if(not _sol.complet()) {
**_ant = iCan.actual();**
findASolution();
if (!_found) {
_sol.desanotate(iCan);
}
}
else {
_found = true;
}
}
iCan.next();
}
}
Differences remarked.
But this is not the best solution. The correct solution should be using _lvl attribute. The solver class shouldn't know aything about the attributes of solution. Just if it's found or not.
I am passing in a sorted vector that contains a data as such:
Job Details {Start Time, Finish Time, Profit}
Job 1: {1 , 2 , 50 }
Job 2: {3 , 5 , 20 }
Job 3: {6 , 19 , 100 }
Job 4: {2 , 100 , 200 }
The code finds which jobs are the best for profit by checking all paths that don't overlap for example job 1,2,3 or job 1,4 are possible and it determines job 1,4 is the best value. I am trying to build a function that displays the path on how the code got to the best possible solution.
Ex. Job 1 --> Job 4 --> $250.
But am lost on the implementation.
Main.cpp
// Find the latest job (in sorted array) that doesn't
// conflict with the job[i]. If there is no compatible job,
// then it returns -1.
int latestNonConflict(vector<Task>someVector, int i)
{
for (int j = i - 1; j >= 0; j--)
{
if (someVector[j].getEndTime() <= someVector[i - 1].getStartTime())
{
return j;
}
}
return -1;
}
// A recursive function that returns the maximum possible
// profit from given array of jobs. The array of jobs must
// be sorted according to finish time.
int bruteForceMethod(vector<Task>someVector, int n)
{
// Base case
if (n == 1)
{
return someVector[n - 1].getValue();
}
// Find profit when current job is inclueded
int inclProf = someVector[n - 1].getValue();
int i = latestNonConflict(someVector, n);
if (i != -1)
cout << someVector[i].getLabel() << "-->";
inclProf += bruteForceMethod(someVector, i + 1);
// Find profit when current job is excluded
int exclProf = bruteForceMethod(someVector, n - 1);
return max(inclProf, exclProf);
}
// The main function that returns the maximum possible
// profit from given array of jobs
int findMaxProfit(vector<Task>someVector, int n)
{
return bruteForceMethod(someVector, n);
}
int main()
{
cout << "The optimal profit is " << bruteForceMethod(tasksVector,
tasksVector.size()) << endl;
return 0;
}
Task.h
#include <string>
using namespace std;
#ifndef Task_h
#define Task_h
class Task
{
public:
Task();
Task(string, int, int, int);
void setLabel(string);
string getLabel();
void setStartTime(int);
int getStartTime();
void setEndTime(int);
int getEndTime();
void setValue(int);
int getValue();
private:
string label;
int startTime;
int endTime;
int value;
};
#endif
Task.cpp
#include "Task.h"
Task::Task()
{
}
Task::Task(string inLabel, int inStartTime, int inEndTime, int inValue)
{
label = inLabel;
startTime = inStartTime;
endTime = inEndTime;
value = inValue;
}
void Task::setLabel(string inLabel)
{
label = inLabel;
}
string Task::getLabel()
{
return label;
}
void Task::setStartTime(int inStartTime)
{
startTime = inStartTime;
}
int Task::getStartTime()
{
return startTime;
}
void Task::setEndTime(int inEndTime)
{
endTime = inEndTime;
}
int Task::getEndTime()
{
return endTime;
}
void Task::setValue(int inValue)
{
value = inValue;
}
int Task::getValue()
{
return value;
}
You can simply consider a weighted graph G where
a node is a job
a node A is linked to a node B if A.endTime < B.startTime
weight of edge(A,B) is B.profit (taking the path to B means doing job B)
You want to get the path of maximal weight of G.
Usually algorithm want a function to minimize so instead lets take for weight -B.profit.
We can always cite the Floyd–Warshall algorithm , there is even the path reconstruction algorithm provided in link aforementionned.
Home made
But let's do it home-made since it seems to be some homework.
You can do it the bruteforce way (which is less efficient but easier to grasp than Floyd Warshall) and check all the longest paths...
create a root node to which you add for children all the jobs with their respective weight associated then consider the recursive function:
def get_longest_path(node):
if !node.children
return 0
best_all = {
w: weight(node, node.children[0]),
path: [node, get_longest_path(node.children[0])]
}
for node.children as child //starting from 1
best_path_i = get_longest_path(child)
//if we found a path with lower total weight (that is, with maximal profit)
if best_path_i != 0 && best_path_i.weight < best_all.weight
best_all = {
w: weight(node, child),
path:[node, best_path_i]
}
return best_all
get_longest_path(root)
note that you can trivially memoize get_longest_path (to avoid reevalution for an already visited node) without much burden
cache = {}
def get_longest_path(node):
if !node.children
return 0
//node.id is jobId
if node.id in cache
return cache[node.id]
best_all = {
w: weight(node,node.children[0]),
path: [node, get_longest_path(node.children[0])]
}
for node.children as child //starting from 1
best_path_i = get_longest_path(child)
//if we found a path with lower total weight (that is, with maximal profit)
if best_path_i != 0 && best_path_i.weight < best_all.weight
best_all = {
w: weight(node, child),
path:[node, best_path_i]
}
cache[node.id] = best_all
return best_all
get_longest_path(root)
No cycles handled but you don't have a job which reverses time I guess
This algorithm can be approached very similarly to a recursive permutation implementation of say a string ABC which produces ABC, ACB, BAC, BCA, CAB, CBA.
Here is a simple demonstration
You could modify this to "prune" the tree when a condition is not met (eg. the letter after is lower in the alphabet than the previous), so you would get ABC as it is the only one where every succesive letter is lower (A<B<C).
Once you have that, you now understand how to recurse over Task's and prune when comparing the startTime and endTime of jobs...
So here is an implementation of the above in C++:
#include <iostream>
#include <vector>
using namespace std;
struct Task {
// global counter tracking how many instances
static int counter;
int startTime;
int endTime;
int value;
int label;
Task(int inStartTime, int inEndTime, int inValue) {
startTime = inStartTime;
endTime = inEndTime;
value = inValue;
label = Task::counter++;
}
};
// store an index to each Task to keep track
int Task::counter = 1;
// build a search tree of all possible Task sequences
// pruning if next Task and current Task overlap
void jobSearchTree(vector<Task> jobSequence,
vector<Task> possibleJobs,
vector<vector<Task>> &possibleJobSequences) {
for (int i = 0; i < possibleJobs.size(); i++) {
vector<Task> l;
for (int j = 0; j < jobSequence.size(); j++)
{
l.push_back(jobSequence.at(j));
}
l.push_back(possibleJobs[i]);
// initial recursive call
if (!jobSequence.size()) {
vector<Task> searchJobs(possibleJobs);
searchJobs.erase(searchJobs.begin() + i);
jobSearchTree(l, searchJobs, possibleJobSequences);
}
// test if jobs occur sequentially
else if (l.at(l.size()-2).endTime <= l.at(l.size()-1).startTime) {
// add the Task sequence
possibleJobSequences.push_back(l);
vector<Task> searchJobs(possibleJobs);
// remove this Task from the search
searchJobs.erase(searchJobs.begin() + i);
// recursive call with Task sequence as the head
// and the remaining possible jobs as the tail
jobSearchTree(l, searchJobs, possibleJobSequences);
}
}
}
vector<int> getBestJobSequence(vector<vector<Task>> possibleJobSequences) {
int maxProfit = 0;
int totalProfit = 0;
vector<Task> bestJobSequence;
for (auto jobSequence : possibleJobSequences) {
totalProfit = 0;
for (auto Task : jobSequence) {
totalProfit += Task.value;
}
if (totalProfit > maxProfit) {
maxProfit = totalProfit;
bestJobSequence = jobSequence;
}
}
vector<int> jobIds;
for (auto Task : bestJobSequence) {
jobIds.push_back(Task.label);
}
return jobIds;
}
int main()
{
Task s1(1, 2, 50);
Task s2(3, 5, 20);
Task s3(6, 19, 100);
Task s4(2, 100, 200);
vector<Task> allJobs = {s1, s3, s4};
vector<vector<Task>> possibleJobSequences;
vector<Task> currentJobSequence;
jobSearchTree(currentJobSequence, allJobs, possibleJobSequences);
vector<int> bestJobSequence = getBestJobSequence(possibleJobSequences);
for (auto job : bestJobSequence) {
cout << job << endl;
}
return 0;
}
There is a initial game difficulty which is
game_difficulty=5 //Initial
Every 3 times if you get it right, your difficulty goes up to infinity but every 3 times you get it wrong, your difficulty goes down but not below 5. So, in this code for ex:
if(user_words==words) win_count+=1;
else() incorrect_count+=1;
if(win_count%3==0) /*increase diff*/;
if(incorrect_count%3==0) /*decrease difficulty*/;
How should I go about doing this?
Simple answer:
if(incorrect_count%3==0) difficulty = max(difficulty-1, 5);
But personally I would wrap it up in a small class then you can contain all the logic and expand it as you go along, something such as:
class Difficulty
{
public:
Difficulty() {};
void AddWin()
{
m_IncorrectCount = 0; // reset because we got one right?
if (++m_WinCount % 3)
{
m_WinCount = 0;
++m_CurrentDifficulty;
}
}
void AddIncorrect()
{
m_WinCount = 0; // reset because we got one wrong?
if (++m_IncorrectCount >= 3 && m_CurrentDifficulty > 5)
{
m_IncorrectCount = 0;
--m_CurrentDifficulty;
}
}
int GetDifficulty()
{
return m_CurrentDifficulty;
}
private:
int m_CurrentDifficulty = 5;
int m_WinCount = 0;
int m_IncorrectCount = 0;
};
You could just add this as a condition:
if (user words==words) {
win_count += 1;
if (win_count %3 == 0) {
++diff;
}
} else {
incorrect_count += 1;
if (incorrect_count % 3 == 0 && diff > 5) {
--diff
}
}
For example:
if(win_count%3==0) difficulty++;
if(incorrect_count%3==0 && difficulty > 5) difficulty--;
This can be turned into a motivating example for custom data types.
Create a class which wraps the difficulty int as a private member variable, and in the public member functions make sure that the so-called contract is met. You will end up with a value which is always guaranteed to meet your specifications. Here is an example:
class Difficulty
{
public:
// initial values for a new Difficulty object:
Difficulty() :
right_answer_count(0),
wrong_answer_count(0),
value(5)
{}
// called when a right answer should be taken into account:
void GotItRight()
{
++right_answer_count;
if (right_answer_count == 3)
{
right_answer_count = 0;
++value;
}
}
// called when a wrong answer should be taken into account:
void GotItWrong()
{
++wrong_answer_count;
if (wrong_answer_count == 3)
{
wrong_answer_count = 0;
--value;
if (value < 5)
{
value = 5;
}
}
}
// returns the value itself
int Value() const
{
return value;
}
private:
int right_answer_count;
int wrong_answer_count;
int value;
};
And here is how you would use the class:
Difficulty game_difficulty;
// six right answers:
for (int count = 0; count < 6; ++count)
{
game_difficulty.GotItRight();
}
// check wrapped value:
std::cout << game_difficulty.Value() << "\n";
// three wrong answers:
for (int count = 0; count < 3; ++count)
{
game_difficulty.GotItWrong();
}
// check wrapped value:
std::cout << game_difficulty.Value() << "\n";
// one hundred wrong answers:
for (int count = 0; count < 100; ++count)
{
game_difficulty.GotItWrong();
}
// check wrapped value:
std::cout << game_difficulty.Value() << "\n";
Output:
7
6
5
Once you have a firm grasp on how such types are created and used, you can start to look into operator overloading so that the type can be used more like a real int, i.e. with +, - and so on.
How should I go about doing this?
You have marked this question as C++. IMHO the c++ way is to create a class encapsulating all your issues.
Perhaps something like:
class GameDifficulty
{
public:
GameDifficulty () :
game_difficulty (5), win_count(0), incorrect_count(0)
{}
~GameDifficulty () {}
void update(const T& words)
{
if(user words==words) win_count+=1;
else incorrect_count+=1;
// modify game_difficulty as you desire
if(win_count%3 == 0)
game_difficulty += 1 ; // increase diff no upper limit
if((incorrect_count%3 == 0) && (game_difficulty > 5))
game_difficulty -= 1; //decrease diff;
}
inline int gameDifficulty() { return (game_difficulty); }
// and any other access per needs of your game
private:
int game_difficulty;
int win_count;
int incorrect_count;
}
// note - not compiled or tested
usage would be:
// instantiate
GameDiffculty gameDifficulty;
// ...
// use update()
gameDifficulty.update(word);
// ...
// use access
gameDifficulty.gameDifficulty();
Advantage: encapsulation
This code is in one place, not polluting elsewhere in your code.
You can change these policies in this one place, with no impact to the rest of your code.