C++ SFML: How to successfully Iteratively render shapes? - c++

I'm looking to iteratively create a set of rectangle shapes of different colours and display them on the screen in different positions. This should produce a maze made up of rectangles.
The positions of the shapes should reflect the array that stores a 9x9 maze.
I'm able to successfully create a single shape, change it's colour and position. However, when I try to do the same to a shape inside an array it does not work. Only a single blue rectangle is generated in the top right corner of the screen.
int main() {
sf::RenderWindow window(sf::VideoMode(1920, 1080), "Baphomet");
sf::RectangleShape tiles[81] = {sf::RectangleShape(sf::Vector2f(20, 20))};
char maze[81] = {0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 1, 0, 1, 0, 1, 1, 1, 0,
0, 1, 0, 1, 0, 1, 0, 1, 0,
0, 1, 1, 1, 0, 1, 0, 1, 0,
0, 0, 0, 1, 0, 1, 0, 1, 0,
0, 1, 1, 1, 0, 1, 1, 1, 0,
0, 0, 1, 0, 0, 0, 1, 0, 0,
0, 0, 1, 1, 1, 1, 1, 1, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0};
int x;
int y;
for (int i = 0; i < 81; i++) {
if (maze[i] == 0) {
tiles[i].setFillColor(sf::Color::Blue);
} else if (maze[i] == 1) {
tiles[i].setFillColor(sf::Color::Red);
}
x = (i % 9) * 20;
y = (i / 9) * 20;
std::cout << x << " " << y << std::endl;
tiles[i].setPosition((float)x, (float)y);
}
while (window.isOpen())
{
sf::Event event;
while (window.pollEvent(event))
{
if (event.type == sf::Event::Closed)
window.close();
}
window.clear();
for (int i = 0; i < 81; i++) {
window.draw(tiles[i]);
}
window.display();
}
return 0;
}
How can I make the shapes change their positions to the x and y variables successfully?

I think the main problem is the definition
sf::RectangleShape tiles[81] = {sf::RectangleShape(sf::Vector2f(20, 20))};
Here you explicitly initialize only the first element (tiles[0]). All the rest of the elements will only have default-constructed shape objects.
To properly initialize all elements to the same values, either use a loop
for (auto& shape : tiles)
shape = sf::RectangleShape(sf::Vector2f(20, 20));
Or use a vector
std::vector<sf::RectangleShape> tiles(81, sf::RectangleShape(sf::Vector2f(20, 20)));

Related

I want to know how to add names to the edges? I'm new with Dijkstra algorithm

I'm suppose to to give the shortest path from one place to another. I'm totally new with this, so I'm starting small. Just like 10 intersections around my place. However, I got how to print out the path from one place to another. I'm trying to figure out how to assign the names to this edges. And I don't know if using adjacency matrix is the best option. What if I have more than 100 nodes? How can I do it?
// A C++ program for Dijkstra's single source shortest path algorithm.
// The program is for adjacency matrix representation of the graph
#include <iostream>
#include <limits.h>
#include <iomanip>
using namespace std;
// Number of vertices in the graph
#define V 14
// A utility function to find the vertex with minimum distance value, from
// the set of vertices not yet included in shortest path tree
int minDistance(int dist[], bool sptSet[])
{
// Initialize min value
int min = INT_MAX, min_index;
for (int v = 0; v < V; v++)
if (sptSet[v] == false && dist[v] <= min)
min = dist[v], min_index = v;
return min_index;
}
void printPath(int parent[], int j)
{
// Base Case : If j is source
if (parent[j] == -1)
return;
printPath(parent, parent[j]);
cout << " --> " << j;
}
// A utility function to print the constructed distance array
void printSolution(int dist[], int n, int parent[])
{
printPath(parent, n);
cout << "\nThe distance from Source to Destination is: "<< dist[n] << endl;
}
// Function that implements Dijkstra's single source shortest path algorithm
// for a graph represented using adjacency matrix representation
void dijkstra(int graph[V][V], int src, int destination)
{
int dist[V]; // The output array. dist[i] will hold the shortest distance from src to i
bool sptSet[V]; // sptSet[i] will be true if vertex i is included in shortest path tree or shortest distance from src to i is finalized
int parent[V]; // Parent array to store shortest path tree
// Initialize all distances as INFINITE and stpSet[] as false
for (int i = 0; i < V; i++) {
dist[i] = INT_MAX;
sptSet[i] = false;
parent[i] = -1;
}
// Distance of source vertex from itself is always 0
dist[src] = 0;
// Find shortest path for all vertices
for (int count = 0; count < V - 1; count++)
{
// Pick the minimum distance vertex from the set of vertices not
// yet processed. u is always equal to src in the first iteration.
int u = minDistance(dist, sptSet);
if (u == destination)
break;
// Mark the picked vertex as processed
sptSet[u] = true;
// Update dist value of the adjacent vertices of the picked vertex.
for (int v = 0; v < V; v++)
// Update dist[v] only if is not in sptSet, there is an edge from
// u to v, and total weight of path from src to v through u is
// smaller than current value of dist[v]
if (!sptSet[v] && graph[u][v] && dist[u] != INT_MAX && dist[u] + graph[u][v] < dist[v]) {
parent[v] = u;
dist[v] = dist[u] + graph[u][v];
}
}
// print the constructed distance array
cout << "From " << src;
printSolution(dist, destination, parent);
}
// driver program to test above function
int main()
{
/* Let us create the example graph discussed above */
int graph[V][V] = { {0, 5, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0},
{5, 0, 6, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0},
{0, 6, 0, 3, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 3, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 7, 0, 4, 0, 0, 0, 0, 0, 0, 0, 7},
{0, 0, 7, 0, 4, 0, 4, 0, 0, 0, 0, 0, 6, 0},
{0, 8, 0, 0, 0, 4, 0, 4, 0, 0, 0, 5, 0, 0},
{0, 0, 0, 0, 0, 0, 4, 0, 3, 0, 4, 0, 0, 0},
{8, 0, 0, 0, 0, 0, 0, 3, 0, 3, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 3, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 4, 0, 3, 0, 4, 0, 0},
{0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 4, 0, 4, 0},
{0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 4, 0, 4},
{0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 4, 0}
};
dijkstra(graph, 0, 13);
return 0;
}

The safest way to add integer into array value without having to call it?

What is the safest way to add integer into array value without having to call the array value ? In this case I would rather not to call the array value because the array is inside a nested loop and the loop itself can repeat for thousands times.
For example dots[1] = I want to add value of this array with 3. Here's my sample code :
void box(const Mat &dots, Mat &newdots, int rows, int cols)
{
for (int i = 0; i < dots.rows; i++) {
for (int j = 0; j < dots.cols; j++) {
newdots.at<Vec3b>(i, j)[0] = dots.at<Vec3b>(i, j)[0];
newdots.at<Vec3b>(i, j)[1] = dots.at<Vec3b>(i, j)[1]; //add this with 3
newdots.at<Vec3b>(i, j)[2] = dots.at<Vec3b>(i, j)[2]; //add this with 5
}
}
Is it possible ? Any suggestion how to do it ? Thanks.
This simplest way is to use cv::add, which overloads the + operator for the Mat class:
// Create a Mat of all 0's
cv::Mat dots = cv::Mat(5, 4, CV_8UC3, cv::Scalar(0,0,0));
std::cout << "dots:\n" << dots << std::endl;
// Add 0 to the B channel, 3 to the G channel, and 5 to R
cv::Mat newdots = dots + cv::Scalar(0, 3, 5);
std::cout << "newdots:\n" << newdots << std::endl;
Result:
dots:
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0;
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0;
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0;
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0;
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
newdots:
[ 0, 3, 5, 0, 3, 5, 0, 3, 5, 0, 3, 5;
0, 3, 5, 0, 3, 5, 0, 3, 5, 0, 3, 5;
0, 3, 5, 0, 3, 5, 0, 3, 5, 0, 3, 5;
0, 3, 5, 0, 3, 5, 0, 3, 5, 0, 3, 5;
0, 3, 5, 0, 3, 5, 0, 3, 5, 0, 3, 5]
Note that dots += Scalar(0,3,5) also works if you just want to modify the original Mat.

How convert text to pixel array in QT

I'm new user of QT. Is it possible to convert for example one character or string to array where 0 means white field and 1 means black field? For example when I have character "a" saved as QString or any representation of font and I would like to get following array:
int array[9][6] = { //"a" char array
{0, 0, 0, 0, 0, 0}, //- - - - - -
{0, 1, 1, 1, 1, 0}, //- * * * * -
{1, 0, 0, 0, 0, 1}, //* - - - - *
{0, 0, 0, 0, 0, 1}, //- - - - - *
{0, 1, 1, 1, 1, 1}, //- * * * * *
{1, 0, 0, 0, 0, 1}, //* - - - - *
{1, 0, 0, 0, 1, 1}, //* - - - * *
{0, 1, 1, 1, 0, 1}, //- * * * - *
{0, 0, 0, 0, 0, 0}, //- - - - - -
};
Is there any way in QT for reach that goal ? I hope I describe my problem as clear as possible and someone can help me with this?
And very important thing size array must depend on selected font size, so if I choose for example 30 pt font, array should be appropriate bigger than 10 pt font.
Thank You very much.
Best regards.
You can draw text onto a bitmap image, which will give you a bitmap containing text. Which is a pixel array, for all intents and purposes.
Note that you will also have to use a "bitmap font", regular fonts won't really do the trick as most have been created to utilize antialiasing and won't result in a crisp and readable bitmap image.
Qt also offers font metrics, which can give you a clear idea how big text or individual characters are.
I misinterpreted the question on my first read through... well, here is what was asked for...
#include <QApplication>
#include <QPixmap>
#include <QLabel>
#include <QPainter>
#include <QDebug>
#include <QVector>
#include <QFontMetrics>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QSize size;
// QPixmap pixmap(100,100);
QImage image(20,20, QImage::Format_Mono);// Change the width and height here as needed if your letters get too big
image.fill(0);
{
QPainter painter(&image);
//// alternative to fill method
// painter.setBrush(Qt::black);
// painter.drawRect(0,0,image.width(), image.height());
painter.setPen(Qt::white);
QFontMetrics fm(painter.font());
size.setHeight(fm.height());
size.setWidth(fm.width('a'));
painter.drawText(0,fm.height(),"a");
}
QLabel label;
label.setPixmap(QPixmap::fromImage(image).scaled(400,400, Qt::KeepAspectRatio));
label.show();
QVector <QVector <int> > array(size.height(), QVector < int> (size.width(), 0));
for(int r = 0; r < size.height(); r++)
for(int c = 0; c < size.width(); c++)
array[r][c] = (image.pixelColor(c,r) == Qt::white ? 1 : 0);
// qDebug() << size << array;
foreach(QVector <int> row, array)
qDebug() << row;
return a.exec();
}
Output:
QVector(0, 0, 0, 0, 0, 0)
QVector(0, 0, 0, 0, 0, 0)
QVector(0, 0, 0, 0, 0, 0)
QVector(0, 0, 0, 0, 0, 0)
QVector(0, 0, 0, 0, 0, 0)
QVector(0, 0, 0, 0, 0, 0)
QVector(0, 0, 0, 0, 0, 0)
QVector(0, 1, 1, 1, 0, 0)
QVector(0, 0, 0, 0, 1, 0)
QVector(0, 1, 1, 1, 1, 0)
QVector(1, 0, 0, 0, 1, 0)
QVector(1, 0, 0, 0, 1, 0)
QVector(0, 1, 1, 1, 1, 0)

How to get Player to interact with Monster using ncurses library?

I've been trying to create my roguelike type of game for C++ using the ncurses library. After following many different tutorials, I am able to create the Player character, the map, the code to prevent the Player from going through walls and the random movements for the Monster character.
The next problem I'm encountering is implementing a boolean so whenever the Player character interacts with the Monster character, the game will quit (much like the roguelike games). However, I can't seem to get it to function the way I wanted. I think it has to do with the coordinates that I have set for the Player and the Monster but I'm still not sure. Can anyone help me out please?
Here is the code:
#include <iostream>
#include <ncurses.h>
#define MAP_WIDTH 22
#define MAP_HEIGHT 15
#define TILE_FLOOR 0
#define TILE_WALL 1
int PlayerX, PlayerY;
void erase (int y, int x) {
mvaddch(y, x, '.');
}
int nMapArray[MAP_HEIGHT][MAP_WIDTH] = {
{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
{ 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 },
{ 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 },
{ 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 },
{ 1, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 },
{ 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1 },
{ 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1 },
{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1 },
{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1 },
{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }
};
bool IsPassable (int nMapX, int nMapY) { //prevents from walking into walls
if (nMapX < 0 || nMapX >= MAP_WIDTH || nMapY < 0 || nMapY >= MAP_HEIGHT)
return false;
int nTileValue = nMapArray[nMapY][nMapX];
if( nTileValue == TILE_FLOOR) {
return true;
}
return false;
}
class Monster {
public:
void Appearance(char monster) {
this->Monster = monster;
}
void SetPos(int x, int y) {
this->PosX = x;
this->PosY = y;
}
void Movement(int &MonsX, int &MonsY) {
int x = (rand() % 3 - 1);
int y = (rand() % 3 - 1);
if (IsPassable(this->PosX+x, this->PosY+y)) {
erase(PosY, PosX);
MonsX = this->PosX += x;
mvaddch(this->PosY, MonsX, this->Monster);
refresh();
erase(PosY, PosX);
MonsY = this->PosY += y;
mvaddch(MonsY, this->PosX, this->Monster);
refresh();
}
}
protected:
int PosX;
int PosY;
char Monster;
};
bool MonsterContact (int nMapY, int nMapX, int x, int y) {
if (nMapArray[nMapY][nMapX] == nMapArray[y][x]) {
return true;
}
return false;
}
void map() {
for (int y = 0; y < MAP_HEIGHT; y++) { //loops to print the map
move(y,0);
for (int x = 0; x < MAP_WIDTH; x++) {
switch (nMapArray[y][x]) {
case TILE_FLOOR:
printw(".");
break;
case TILE_WALL:
printw("#");
break;
}
}
}
};
void init() { //starts the ncurses screen.
initscr();
clear();
noecho();
raw();
keypad(stdscr, TRUE);
curs_set(0);
}
void game_loop (char Player, int row, int col, int ch) {
Monster npc;
npc.SetPos(7, 8);
npc.Appearance('g');
int MonsX,MonsY;
mvaddch(row,col, Player); //player movement
refresh();
while(true) {
npc.Movement(MonsX, MonsY);
ch = getch();
switch (ch) {
case 'w':
if (IsPassable(col, row-1)) {
erase(row,col);
row = row - 1;
mvaddch(row, col, Player);
refresh();
}
if (MonsterContact(col, row, MonsX, MonsY)) {
return();
}
break;
case 's':
if (IsPassable(col, row+1)) {
erase(row, col);
row = row + 1;
mvaddch(row, col, Player);
refresh();
}
if (MonsterContact(col, row, MonsX, MonsY)) {
return();
}
break;
case 'a':
if (IsPassable(col-1, row)) {
erase(row,col);
col = col - 1;
mvaddch(row, col, Player);
refresh();
}
if (MonsterContact(col, row, MonsX, MonsY)) {
return();
}
break;
case 'd':
if (IsPassable(col+1, row)) {
erase(row,col);
col = col + 1;
mvaddch(row,col, Player);
refresh();
}
if (MonsterContact(col, row, MonsX, MonsY)) {
return();
}
break;
case 'q':
return;
default:
break;
}
}
}
int main(int argc, const char * argv[]) {
PlayerX = 2, PlayerY = 1; //Player initial position.
char Player = '#';
init(); //starts the ncurses screen.
printw("Press any key to start the game");
int ch = getch();
clear();
map();
game_loop(Player, PlayerY, PlayerX, ch);
endwin();
return 0;
}
I'll sum up what jonhopkins commented on.
This is essentially a result of inconsistency. Your code passes arguments in different orders in different functions (first x, then y in isPassable, but vice versa in MonsterContact), and uses different names for the same things (row and x are the same.)
Your problem is caused by the fact that you passed col, row into MonsterContact, when you should have passed row, col. Perhaps you subconsciously copied the order of arguments from when you wrote isPassable a little earlier, forgetting that the order of arguments is reversed. Or you momentarily mistakenly thought that col meant y and row meant x.
Always remember to keep your code as consistent as possible, and you can avoid these sorts of mistakes in the future.

Why is the size and values of this OpenCV Matrix incorrect?

I use the following function to return a weight matrix of values between 0 and 1 depending on how close to the center a particular matrix position is. Also after a threshold the values are all 1 (it is like plateau with the points closer to center having value 1 and points away from center after a threshold of distance linearly fall from 1 to 0 at the edges)?
cv::Mat2f getWeightsMatrix(int N, int M, float r){
cv::Mat2f weights = cv::Mat2f(N,M);
int i,j;
for(i=0;i<N;i++){
for(j=0;j<M;j++){
if(i<=floor(N*(1-r)/2)){
if(j<=floor(M*(1-r)/2)){
weights[i][j]=((float)(i/N-j/M)/(1-r));
}
else{
weights[i][j]=(2*(float)(i/N)/(1-r));
}
}
else if (i>=floor(N*(1+r)/2)){
if(j>=floor(M*(1+r)/2)){
weights[i][j]=(((float)((N-i)/N))-((float)((M-j)/M)))/(1-r);
}
else{
weights[i][j]=(2*(float)((N-i)/N)/(1-r));
}
}
else{
if(j<=floor(M*(1-r)/2)){
weights[i][j]=(2*(float)(j/M)/(1-r));
}
else if(j>=floor(M*(1+r)/2)){
weights[i][j]=(2*(float)((M-j)/M)/(1-r));
}
else{
weights[i][j]=1;
}
}
}
}
cout << weights << endl;
return weights;
}
Now my problem is that I am having some casting issues and only values 0 and 1 are being returned (no floats). Also my matrix size displayed by the cout is 20x10 when I call the function with N=10, M=10 and r=0.5.
Please helP!
EDIT: This is the output
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0;
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0;
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0;
0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0;
0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0;
0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0;
0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0;
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0;
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0;
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
Your matrix consists only if integers because when you do basic operations on integers, the results gets rounded automatically.
For example: when you write
weights[i][j]=((float)(i/N-j/M)/(1-r));
The result of i/N is rounded to a integer, j/M is also rounded to an integer, and finally, the division by (1-r) is also rounded. Your (float) cast is a good idea but it's applied too late.
You can do several things:
Cast inside elementary operations, for instance, float(i)/N instead of i/N
Use float numbers instead of integers: write 1.0 instead of 1
Use floats inside your loops and statements
For example:
for(float i = 0; i < N-0.5; i++) {
for(float j = 0; j < M-0.5; j++) {
if(i <= floor(N*(1.0-r)/2.0)) {
// ...
It's important you understand that because of floating point precision, a test such as i < N might or might not pass when float i = N. This is why I did a little trick by substracting 0.5 from your loop bounds N and M.