Some questions regarding 2d Vectors, C++ - c++

This 2d vector is being used to hold a game-board for minesweeper. I want to create a 2d vector of struct cell, which has several "state" variables all holding information needed to construct the game board (I am creating a basic minesweeper game to run on the command line, very rudimentary, just want to get a better grasp of classes). First of all, what am I doing wrong when trying to pass the vector to the void function? And then how would I be able to access the separate variables to read and write to them? I know this may be unusual (could solve using arrays) but I'd like to do it a little differently. I have looked through various forums but people don't seem to use this approach. Thanks guys.
EDIT:
What I'm trying to accomplish with the vector of cell's is basically 3 vectors in 1 so that I can simultaneously use the information in the different states to check whether various conditions have been met when a player makes a move (i.e. check whether there is a mine there, or whether that spot has already been opened/marked/unmarked etc.) Please let me know if the code below doesn't allow for what I want to accomplish.
code:
#include <iostream>
#include <vector>
using namespace std;
void gameboard(vector<vector<int>> &stateboard)
struct cell
{
int state; //( 0 hidden, 1 revealed, 2 marked)
int value; //(-1 mine, 0 no surrounding, # > 0
bool isMine;
};
void gameboard(vector<vector<int>> &stateboard)
{
}
int main()
{
int columns = 10;
int rows = 10;
vector <vector<cell> > gameboard(rows, vector<cell>(columns));
gameboard(&gameboard);
return 0;
}
sorry guys, this piece of code doesn't even begin to resemble the outline I have in Xcode, I was just trying to present the question in an easier to follow manner and threw this together.
new code:
#include <iostream>
#include <vector>
using namespace std;
struct cell
{
int state; //( 0 hidden, 1 revealed, 2 marked)
int value; //(-1 mine, 0 no surrounding, # > 0
bool isMine;
};
void game_state(vector<vector<cell>> &stateboard)
{
}
int main()
{
int columns = 10;
int rows = 10;
vector <vector<cell> > gameboard(rows, vector<cell>(columns));
game_state(gameboard);
return 0;
}
I guess having the same name for a function and vector was throwing Xcode off, which is why I made game board a reference originally but now I see why that was stupid. Now that this works, how can i specifically read and write to just the bool isMine variable? I'm not asking for you to do it completely but a basic line of code showing me how to access that specific part would be a greatly help me. Am I conceptualizing this incorrectly?

hope it helps you:
#include <iostream>
#include <vector>
// your columns and rows are equal,
//and they should no change, so i think better to do them const
const int BOARD_SIZE = 10;
struct cell {
int state;
int value;
bool isMine;
};
void game_state(std::vector < std::vector <cell > > &stateboard) {
}
int main (){
std::vector < std::vector <cell > > gameboard;
//I give more preference to initialize matrix like this
gameboard.resize(BOARD_SIZE);
for (int x = 0; x < BOARD_SIZE; x++) {
gameboard[x].resize(BOARD_SIZE);
for (int y = 0; y < BOARD_SIZE; y++) {
// and this is an example how to use bool is mine
// here all cells of 10x10 matrix is false
// if you want place mine in a first cell just change it
// to gameboard[0][0].isMine = true;
gameboard[x][y].isMine = false;
}
}
game_state(gameboard);
return 0;
}

Related

How to write BFS function in C++?

#include <iostream>
#include <string>
#include <queue>
using namespace std;
void BFS(const string&, const string[], int[][10]);
int main()
{
const int CAP = 10;
string states[CAP] = { "Arizona", "California", "Idaho", "Nevada", "Oregon", "Utah", "Washington" };
string Point;
int matrix[CAP][CAP] =
{
{0,1,0,1,0,1,0},
{1,0,0,1,1,0,0},
{0,0,0,1,1,1,1},
{0,1,1,1,0,0,1},
{1,1,1,1,0,0,0},
{0,0,1,0,1,0,0},
{0,0,1,0,1,0,0}
};
BFS("California", states, matrix);
}
void BFS(const string& Point, const string states[], int matrix[][10])
{
int SPoint = 0;
queue<string> visited;
queue<string> Queue;
string temp = Point;
visited.push(temp);
do
{
for (int i = 0; i < 10; i++)
{
if (states[i] == temp)
{
SPoint = i;
}
}
for (int i = 0; i < 10; i++)
{
if (matrix[SPoint][i] == 1)
{
Queue.push(states[i]);
}
}
visited.push(Queue.front());
Queue.pop();
temp = visited.back();
} while (!Queue.empty());
for (int i = 0; i < 10; i++)
{
cout << visited.front();
visited.pop();
}
}
I'm doing an exercise where I have to make a function that does Breadth-First Search and prints out the visited path. But my function wouldn't print anything. What am I doing wrong here?
Note: The matrix is alphabetical order and represents the connection between states.
My expected output: California Arizona Oregon Nevada Utah Idaho Washington
Exercise description
While I won't offer a complete solution, I can help identify some of the issues the code exhibits.
Major issues
Since you have a cyclic graph, it's important to mark nodes as visited during the BFS else you'll wind up with an infinite loop (which is why nothing gets printed in your current implementation). Your visited queue could be an unordered_set. When nodes are visited, add them to the set and write a conditional to avoid visiting them again.
The adjacency matrix doesn't appear correct. Since it's an undirected graph, I would anticipate that the matrix would be mirrored from top left to bottom right, but it's not. Also, there are no self-edges in the graph yet Nevada appears to have an edge to itself in the matrix.
There's no need to loop over the adjacency matrix--you can index into it by mapping digit indexes and string names appropriately. If you do need to loop, running to 10 is out of bounds on a 7x7 matrix.
Minor issues
There's no sense in arbitrarily restricting the matrix size. Although the assignment enforces this, it's a poor design choice because the code needs to be rewritten any time you want to use a different input graph.
A matrix seems like a slightly awkward data structure here because it introduces an extra layer of indirection to translate strings into integers and back. Although the project doesn't permit it, I'd prefer using a structure like:
std::unordered_map<std::string, std::vector<std::string>> graph({
{"California", {"Oregon", "Nevada", "Arizona"}},
// ... more states ...
});
Ideally, these would be Node objects with neighbor vector members instead of strings.
C++ offers std::vector and std::array which are preferable to C arrays. I assume they haven't been introduced yet in your class or aren't permitted on the assignment, but if you're stuck, you can try writing the code using them, then re-introducing your instructor's constraints after you get it working. If nothing else, it'd be a learning experience.
Avoid using namespace std;.
Reserve uppercase variable names for class names. Objects and primitives should be lowercase.
Pseudocode for BFS
This assumes the preferred data structure above; it's up to you to convert to and from strings and adjacency matrix indexes as needed.
func BFS(string start, unordered_map<string, vector<string>> graph):
vector traversal
queue worklist = {start}
unordered_set visited = {start}
while !worklist.empty():
curr = worklist.pop_front()
traversal.push_back(curr)
for neighbor in graph[curr]:
if neighbor not in visited:
visited.insert(neighbor)
worklist.push(neighbor)
return traversal
Since this is an assignment, I'll leave it at this and let you take another crack at the code. Good luck.

Inconsistency between int and bool

I just implemented breadth first search in c++ and instead of declaring a vector as bool, I declared it as an int. This lead to a very odd observation. When I used int, the code printed the following:
1
32763
-524268732
Throughout the entire code, I don't provide any such value to variable as the 2nd and 3rd node receive, so I assume that they are just garbage values, but why do garbage values even come up, when I'm initialising the vector to be full of zeroes ??? You may check the code to be that below:
#include <iostream>
#include <queue>
using namespace std;
queue<int> neigh;
vector< vector<int> > graph(3);
vector<int> flag(3, 0);
int main(void)
{
graph[0].push_back(1); graph[0].push_back(2);
graph[1].push_back(0); graph[1].push_back(2);
graph[2].push_back(0); graph[3].push_back(1);
neigh.push(0);
while(!neigh.empty())
{
int cur = neigh.front();
neigh.pop();
flag[cur] = 1;
for(int i = 0, l = graph[cur].size();i < l;i++)
{
if(!flag[graph[cur][i]])
neigh.push(graph[cur][i]);
}
}
for(int i = 0;i < 3;i++)
{
cout << flag[i] << endl;
}
}
Alright, then I changed just a single line of code, line number 7, the one where I declare and initialise the flag vector.
Before:
vector<int> flag(3, 0);
After:
vector<bool> flag(3, false);
And voila! The code started working:
1 //The new output
1
1
So, my question is, what is the problem with the code in the first place ? I believe it may be some kind of error I made, or possibly that its only by chance that my bfs implementation works at all... So, what is the truth, SO? What is my (possible) mistake ?
You are accessing your vector out of bounds here:
graph[3].push_back(1);
At this moment, graph only has three elements. This leads to undefined behaviour.

How can I convert from a class with a 2D vector of chars to a 2D vector of another obj with a char variable?

I'm still new to this so my apologies in advance if I provide too much or not enough information for my problem.
The Run Down
This question isn't a rogue like game related question, but for a little background of what my program does.. is to be similar to a rogue like game.
Ok, so I had a class that had a member 2D char vector (the chars were manipulated to represent a "dungeon"). Everything worked seamlessly. I wanted to expand what my program could do so I decided to create a new class to replace the 2D vector of chars so that the char is just the visual representation of the new class and other variables can be stored along with that char.
For simplistic sake, I tried to remove what isn't necessary for this question. This new class is called Tile - which represents a space being used for the dungeon level. Tile has:
a char variable called displayChar.. defaults to ' ' changes to represent what it contains
(other variables..)
The Problems / My Poor guesses
I'm not the best at completely understanding some concepts of programming syntax/implementation, so please don't judge.
The way I filled the vector of chars, I resized it to the width of the game board, and set everything to ' ' since all the values were chars
I think I need to fill it with new, Tile objects and push them to the
2D Tile vector?
My other methods that manipulated the char values are giving errors.
I think I should change the methods to take a pointer to the Tile
object and use the setDisplayChar(' ') method to change its
value?
My at(int, int) method used to return the char at that location
I thought I could change it to "return m_vvTiles[y][x].getDisplayChar()" but I get an error
I'm bad at changing how something works and I usually end up making a mess out of my code. I'll post the code that is relevant for this. I'd greatly appreciate any help you can offer. And please let me know if I need to add more. (I'll try to keep the code minimized to only related methods). Thanks!
DungeonLevel.h
#include "Tile.h"
#include <vector>
#include <random>
class DungeonLevel {
public:
DungeonLevel(int iWidth, int iHeight, std::mt19937 & mt);
~DungeonLevel(void);
void dump();
char at(int x, int y);
int getWidth();
int getHeight();
private:
std::vector<std::vector<Tile>> m_vvTiles; //Tile was char
};
DungeonLevel.cpp
#include <iostream>
#include <random>
#include "DungeonLevel.h"
using namespace std;
DungeonLevel::DungeonLevel(int iWidth, int iHeight, std::mt19937 & mt){
// Initialize the blank vector
m_vvTiles.resize(iHeight);
for(auto it = m_vvTiles.begin(); it != m_vvTiles.end(); it++ ){
// Tile tempTile = new;?
(*it).resize(iWidth,' ');
}
// divide the level into 4x2 chunks
int iChunkWidth = iWidth / 4;
int iChunkHeight = iHeight / 2;
// Taking the easy way out, and generating
// a loop of tunnels first to drop rooms on to
for( int x = (iChunkWidth/2); x <= ((3 * iChunkWidth) + (iChunkWidth/2)$
m_vvTiles[iChunkHeight/2][x] = '#';
m_vvTiles[iChunkHeight + (iChunkHeight/2)][x] = '#';
}
for( int y = (iChunkHeight/2); y <= (iChunkHeight + (iChunkHeight/2)); $
m_vvTiles[y][iChunkWidth/2] = '#';
m_vvTiles[y][(3 * iChunkWidth) + (iChunkWidth/2)] = '#';
}
void DungeonLevel::dump(){
for( auto itOuter = m_vvTiles.begin(); itOuter != m_vvTiles.end(); itOu$
for( auto itInner = (*itOuter).begin(); itInner != (*itOuter).e$
cout << *itInner.getDisplayChar(); ///Updated:: CAUSING ERROR//
}
cout << endl;
}
}
//SEVERAL IRRELEVANT LINES FOR THIS PROBLEM..
}
char DungeonLevel::at(int x, int y){
return m_vvTiles[y][x].getDisplayChar();
//return m_vvTiles[y][x]; WORKED BEFORE
}
Tile.h
#include "Entity.h"
#include <vector>
class Tile : public Entity {
public:
Tile(void);
virtual ~Tile(void);
//void setEntity(Entity * entityToSet); BOTH IRRELEVANT
//Entity * getEntity();
void setDisplayChar(char displayCharToSet);
char getDisplayChar();
//virtual void dumpObjectData(); IRRELEVANT AT THIS TIME
private:
char displayChar;
//Entity * theEntity; IRRELEVANT AT THIS TIME
};
Tile.cpp
#include "Tile.h"
#include "Entity.h"
using namespace std;
Tile::Tile(void){
displayChar = '.';
//theEntity = NULL;
}
Tile::~Tile(void){
}
void Tile::setDisplayChar(char displayCharToSet){
displayChar = displayCharToSet;
}
char Tile::getDisplayChar(){
return displayChar;
}
*Heres my existing error: *
For the method dump() in DungeonLevel.cpp,
Some of your questions with my answers
I think I need to fill it with new, Tile objects and push them to the 2D Tile vector?
No you need to resize it and fill it with Tile objects. Pretty much like you did when it was a char array. new doesn't come into it.
I think I should change the methods to take a pointer to the Tile object and use the setDisplayChar(' ') method to change its value?
I would use a reference.
I thought I could change it to return m_vvTiles[y][x].getDisplayChar() but I get an error.
That sounds right to me, what error did you get?
Everything about your redesign sounds well motivated to me. Good idea to make a back up before going on a major reorganization however.

Project Euler 18 - C++ Passing Array By Reference

See: http://projecteuler.net/problem=18 for a better explanation of the problem than I could give here.
I like my algorithm; It's not brute-force. I look at all of the 3x3 triangles at the bottom of the pyramid to calculate which route is best, then assign that value back to the row. That's why my function is called ConsolidateBottomThreeRows -- I'll end up calculating the best path from the bottom up, assigning to the top-most of the bottom 3 rows each time, creating a smaller and smaller pyramid until my top row contains the answer.
My issue isn't a question of algorithm, simply syntax. I can't figure out [yes, even with Googling] how to get my arrays passed correctly. Here is my output when I try to compile:
C:\MyApps\Euler>g++ Prob_18.cpp -o Prob_18
Prob_18.cpp: In function 'void ConsolidateBottomThreeRows(int*, int*, int*)':
Prob_18.cpp:17:20: error: request for member 'size' in 'top', which is of non-class type 'int*'
And my code:
#include <iostream>
using namespace std;
int CrunchSmallTriangle(int top[], int middle[], int bottom[])
{
int biggest=0;
if(middle[0]+bottom[0] > biggest) biggest=middle[0]+bottom[0];
if(middle[0]+bottom[1] > biggest) biggest=middle[0]+bottom[1];
if(middle[1]+bottom[0] > biggest) biggest=middle[1]+bottom[0];
if(middle[1]+bottom[1] > biggest) biggest=middle[1]+bottom[1];
return biggest+top[0];
}
void ConsolidateBottomThreeRows(int top[], int middle[], int bottom[])
{
int SmallTop[0], SmallMiddle[2], SmallBottom[3];
for(int x=0;x<top.size();x++)
{
SmallTop[0]=top[x];
SmallMiddle[0]=middle[x];
SmallMiddle[1]=middle[x+1];
SmallBottom[0]=bottom[x];
SmallBottom[1]=bottom[x+1];
SmallBottom[2]=bottom[x+2];
top[x]=CrunchSmallTriangle(SmallTop, SmallMiddle, SmallBottom);
}
}
int main()
{
int row1[1]={75};
int row2[2]={95,64};
int row3[3]={17,47,82};
int row4[4]={18,35,87,10};
int row5[5]={20,4,82,47,65};
int row6[6]={19,1,23,75,3,34};
int row7[7]={88,2,77,73,7,63,67};
int row8[8]={99,65,4,28,6,16,70,92};
int row9[9]={41,41,26,56,83,40,80,70,33};
int row10[10]={41,48,72,33,47,32,37,16,94,29};
int row11[11]={53,71,44,65,25,43,91,52,97,51,14};
int row12[12]={70,11,33,28,77,73,17,78,39,68,17,57};
int row13[13]={91,71,52,38,17,14,91,43,58,50,27,29,48};
int row14[14]={63,66,4,68,89,53,67,30,73,16,69,87,40,31};
int row15[15]={4,62,98,27,23,9,70,98,73,93,38,53,60,4,23};
ConsolidateBottomThreeRows(row13, row14, row15);
ConsolidateBottomThreeRows(row11, row12, row13);
ConsolidateBottomThreeRows(row9, row10, row11);
ConsolidateBottomThreeRows(row7, row8, row9);
ConsolidateBottomThreeRows(row5, row6, row7);
ConsolidateBottomThreeRows(row3, row4, row5);
ConsolidateBottomThreeRows(row1, row2, row3);
cout<<row1[0];
}
Any help is greatly appreciated. Thanks!
Arrays do not have a size() method! You will either need to pass the size to the function as a separate parameter, or use a container (such as std::vector).
Firstly, I agree with the other answers that switching from raw C-arrays to std::array<> or std::vector<> would be the best move. However, that being said, as chris commented, because your C-arrays are statically sized, making ConsolidateBottomThreeRows into a function template makes the following possible as well:
template<std::size_t TopN, std::size_t MidN, std::size_t BotN>
void ConsolidateBottomThreeRows(int (&top)[TopN],
int (&middle)[MidN],
int (&bottom)[BotN])
{
int SmallTop[1], SmallMiddle[2], SmallBottom[3];
for (std::size_t x = 0; x != TopN; ++x)
{
SmallTop[0] = top[x];
SmallMiddle[0] = middle[x];
SmallMiddle[1] = middle[x + 1];
SmallBottom[0] = bottom[x];
SmallBottom[1] = bottom[x + 1];
SmallBottom[2] = bottom[x + 2];
top[x] = CrunchSmallTriangle(SmallTop, SmallMiddle, SmallBottom);
}
}
Note that your declaration of SmallTop has the wrong dimensions.
You need to replace top.size() with a size passed in to the function. Better yet, don't use arrays and use vectors instead. Then your top.size() will work.

Two point crossover operation

I've been trying to write a code for two point crossover operation in a genetic algorithm. At first two random gene location is selected. After that, two chromosomes swap their genes which are located btw random numbers called genelocation1 and genelocatıon2.
for example First Gene [0.3,0.2,0.4,0,0.1,0.5,0.7]
Second Gene [0.25,0.6,0.45,0.15,0.80,0.9,0.85]
rndm genelocation1=3
rdnm gnelocation2 =5
child Gene1 [0.3,0.2,0.4,0.15,0.80,0.5,0.7]
Gene2 [0.25, 0.6, 0.45, 0, 0.1,0.9,0.85]
my problem is this: since two numbers are generated randomly, i could not define an array like array[genelocation2-genelocation1].. How can i solve the problem. here is my whole code about two point crossover. pointers maybe a solution but i am not good at pointers.
Here is the code:
void Xover (int mother,int father)
{
int tempo;
int Rndmgenelocation1=(rand()%ActivityNumber);
int Rndmgenelocation2=(rand()%ActivityNumber);
if (Rndmgenelocation1>Rndmgenelocation2)//sure that 2>1
{
tempo=Rndmgenelocation1;
Rndmgenelocation1=Rndmgenelocation2;
Rndmgenelocation2=tempo;
}
int size=(Rndmgenelocation2-Rndmgenelocation1);
int Temp1[size];//this makes an error
int ppp=Rndmgenelocation1;
for (int pp=Rndmgenelocation1;pp<Rndmgenelocation2;pp++)
{
Temp1[pp]=Sol_list[father].Chromosome[ppp];
ppp++;
}
int pppx=Rndmgenelocation1;
for (int ppx=Rndmgenelocation1;ppx<Rndmgenelocation2;ppx++)
{
Sol_list[father].Chromosome[ppx]=Sol_list[mother].Chromosome[pppx];
pppx++;
}
int ppplx=Rndmgenelocation1;
for (int pplx=Rndmgenelocation1;pplx<Rndmgenelocation2;pplx++)
{
Sol_list[father].Chromosome[pplx]=Temp1[ppplx];
ppplx++;
}
return;
}
You can't define an array of variable size on the stack.
You could use
int *Temp1=new int[size]
You then must not forget to call
delete[] Temp1;
at the end of your function!
edit:
I didn't test my code below, but the following should do what you want in a more efficient (and more understandable) way:
#include <algorithm>
void Xover (int mother,int father)
{
int Rndmgenelocation1=(rand()%ActivityNumber);
int Rndmgenelocation2=(rand()%ActivityNumber);
if (Rndmgenelocation1>Rndmgenelocation2)//sure that 2>1
{
std::swap(Rndmgenelocation1,Rndmgenelocation2);
}
for (int pp=Rndmgenelocation1;pp<Rndmgenelocation2;pp++)
{
std::swap(Sol_list[father].Chromosome[pp],Sol_list[mother].Chromosome[pp]);
}
return;
}
edit2:
I just found here another even better way - the STL implements a ready-to-use cross over algorithm. Use:
#include <algorithm>
void Xover (int mother,int father)
{
int Rndmgenelocation1=(rand()%ActivityNumber);
int Rndmgenelocation2=(rand()%ActivityNumber);
if (Rndmgenelocation1>Rndmgenelocation2)//sure that 2>1
{
std::swap(Rndmgenelocation1,Rndmgenelocation2);
}
std::swap_ranges(
Sol_list[father].Chromosome[Rndmgenelocation1],
Sol_list[father].Chromosome[Rndmgenelocation2],
Sol_list[mother].Chromosome[Rndmgenelocation1]
);
return;
}
I'm guessing you must not be using g++ as your compiler. If so, you can use a std::vector rather than an array. Simply do
std::vector<int> array(size);
Now you can treat it like a "normal" array though the operator[] syntax. There's also no concern about memory leaks from forgetting to call delete on a pointer.