I'm looking for some help finding all clusters of chars in the string in C++. The exact task is:
Given the following “2D string” (in C++ expression):
string text =
"#################aa##a###c######\n" +
"####bbbbaaaabbbbbaaaaa###ccc##cc\n" +
"#o##bbbbaaaabbbbbaaaaa###c#c##cc\n" +
"#oo#bbbbaeeabbbbbbbbaa##cc#ccccc\n" +
"#o##bbbbaeeabbbbbaaaaaa#cc#####c\n" +
"#o##bbbbaaaabbbbbaaaaaa#cc#####c\n";
Write a program computing the area of each contiguous region of the same >symbols. Two equal symbols belong to the same area if they are neighbors either >in a row or in a column. Don’t count the newline (\n) symbols, they are just to >form the 2D string.
The main function should be recursive.
Hint: use an extra 2D array to mark each symbol in the 2D string if it is >already counted or not. Scan the array row-wise until a not counted yet symbol >is found. Then, run the recursive area-computing function starting from this >symbol. Continue until all symbols are marked as counted.
The program output should look (more or less) like:
Region of symbols #, area …
Region of symbols a, area …
Region of symbols #, area …
Region of symbols c, area …
My current code looks like this:
#include <iostream>
#include <string>
using namespace std;
int cords (string str, int x, int y) {
int length, i, position, lines = 0, x_max, y_max;
char symbol;
length = str.length();
for (i = 0; i < length; i++) {
symbol = str[i];
if (symbol == '\n')
lines++;
}
length -= lines;
x_max = length / lines;
y_max = length / x_max;
position = x - 1 + (y - 1) * x_max + y - 1;
if (x <= x_max && y <= y_max)
return position;
}
int clusterMiner (char symbol, string str, int x, int y, int counter, int last) {
if (x > 32 || y > 6) {
return counter;
} else {
if (str[cords(str, x++, y)] == symbol) {
counter++;
return clusterMiner(symbol, str, x++, y, counter, x);
} else if (str[cords(str, 1, y++)] == symbol) {
return clusterMiner(symbol, str, 1, y++, counter, x);
}
}
}
int main () {
int length, lines, i, j, k, l, counter;
string text = // 32 elements per line
"#################aa##a###c######\n" // 32
"####bbbbaaaabbbbbaaaaa###ccc##cc\n" // 64
"#o##bbbbaaaabbbbbaaaaa###c#c##cc\n" // 96
"#oo#bbbbaeeabbbbbbbbaa##cc#ccccc\n" // 128
"#o##bbbbaeeabbbbbaaaaaa#cc#####c\n" // 160
"#o##bbbbaaaabbbbbaaaaaa#cc#####c\n"; // 192
counter = clusterMiner('#', text, 1, 1, 0, 0);
cout << counter;
return 0;
}
Cords function is just for easier interaction with the two dimensions of the string.
I'm not sure what to do next. Right now the program counts only some of the symbols as it stops at the first different one ignoring these which are connected to further nodes.
Thanks!
First, do not calculate x_max and y_max all the time newly, just do it once and store it in a variable. Then, you will have to iterate over the whole field:
char get(int x, int y)
{
// + 1: the newline!!!
return field[x + y * (x_max + 1)];
}
void countAll()
{
calculateMaxima();
// created your visited array now
for(unsigned int y = 0; y <= y_max; ++y)
{
for(int x = 0; x <= x_max; ++x)
{
if(!visited[x, y])
{
count = 0;
search(get(x, y), x, y);
// output count here...
}
}
}
}
Each time we hit a character not yet visited, i. e. a new one, we start a new search. For each search, we have to consider four neighbours for each current position {x, y}:{x +/- 1, y} and {x, y +/- (x_max + 1} (apart from the positions at the edges, which have less). So your search might look like this:
void visit(char symbol, int x, int y)
{
if(!visited[x][y] && get(x, y) == symbol)
{
++count;
++visited[x][y] = true;
}
search(symbol, x, y);
}
void search(char symbol, int x, int y)
{
if(x > 0)
visit(x - 1, y);
if(x < max_x)
visit(x + 1, y);
if(y > 0)
visit(x, y - 1);
if(y < max_y)
visit(x, y + 1);
}
For now, I am assuming count, visited and x/y_max being some global variables. Cleaner, as we are C++, would be writing a separate class for this purpose:
class ClusterMiner
{
unsigned int count;
std::string field;
// ...
void visit(char symbol, int x, int y);
void search(char symbol, int x, int y);
// ...
public:
void countAll();
};
Code is untested and incomplete, it shall only give you the necessary hints to find your way...
Side note: If you have unconnected regions of the same character, these will be detected as such. If this is not desired, you might sum up the results e. g. in a std::map<char, unsigned int> and iterate over this one after you finished counting...
Related
I'm writing a program which gets the distances from five ultrasonic sensors and uses a trilateration algorithm on every combination of distance values. It first outputs all the coordinates it recieves from the trilateration algorithm, then it filters out values which are too small or too large. However, it keeps outputting values from previous trilateration cycles as the array has not been reset.
#include "NewPing.h"
#include<stdio.h>
#include<stdlib.h>
#define TP0 11
#define EP0 13
#define TP1 3
#define EP1 4
#define TP2 6
#define EP2 2
#define TP3 5
#define EP3 7
#define TP4 10
#define EP4 8
#define MAX_DISTANCE 150
//sensor coordinates
int xcords [5] = {3, 45, 87, 87, 3};
int ycords [5] = {0, 16, 0, 58, 58};
int r = 3;
int n = 5;
float x = 0;
float y = 0;
int MAX_X = 85;
int MAX_Y = 62;
int sensor;
int count = 0;
int resultCount = 0;
bool reading = false;
void setup() {
Serial.begin (9600);
}
void
loop() {
while(count < 1){
count += 1;
int distances [3];
for (int r = 0; r<5; r++){
distances[r] = getValue(r);
if (distances[r] >= MAX_DISTANCE || distances[r] <= 2) {
getValue(r);
}
//Serial.print("Distance =");
//Serial.print(distances[r]);
//Serial.println(" cm ");
}
printCombination(distances, xcords, ycords, n, r);
//trilateration(distances[0], distances[1], distances[2]);
delay(5000);
}
}
int combinationUtil(int distances[], int data[], int dataCords[], int start, int end, int index, int r, float xresults[], float yresults[]);
// Needed for qsort. See http://w...content-available-to-author-only...s.com/reference/cstdlib/qsort/
int compare (const void * a, const void * b)
{ return ( *(int*)a - *(int*)b ); }
// The main function that prints all combinations of size r
// in arr[] of size n. This function mainly uses combinationUtil()
int printCombination(int distances[], int xcords[], int ycords[], int n, int r)
{
// A temporary array to store all combination one by one
int data[r];
int xdataCords[r];
int ydataCords[r];
float xresults[10];// the arrays causing the issue
float yresults[10];
// Sort array to handle duplicates
qsort (distances, n, sizeof(int), compare);
// Print all combination using temprary array 'data[]'
combinationUtil(distances, data, xdataCords, ydataCords, 0, n-1, 0, r, xresults, yresults);
sorting(xresults,yresults);
resultCount = 0;
Serial.print("\n");
}
/* distances[] ---> Input Array
data[] ---> Temporary array to store current combination
start & end ---> Staring and Ending indexes in distances[]
index ---> Current index in data[]
r ---> Size of a combination to be printed */
float* combinationUtil(int distances[], int data[], int xdataCords[], int ydataCords[], int start, int end, int index, int r, float xresults[], float yresults[])
{
// Current combination is ready to be printed, print it
if (index == r)
{
trilateration(data[0], data[1], data[2], xdataCords[0], xdataCords[1], xdataCords[2], ydataCords[0], ydataCords[1], ydataCords[2], xresults, yresults);
resultCount++;
}
// replace index with all possible elements. The condition
// "end-i+1 >= r-index" makes sure that including one element
// at index will make a combination with remaining elements
// at remaining positions
for (int i=start; i<=end && end-i+1 >= r-index; i++)
{
data[index] = distances[i];
xdataCords[index] = xcords[i];
ydataCords[index] = ycords[i];
combinationUtil(distances, data, xdataCords, ydataCords, i+1, end, index+1, r, xresults, yresults);
// Remove duplicates
while (distances[i] == distances[i+1])
i++;
}
}
float* trilateration(int d1,int d2, int d3, int x1, int x2, int x3, int y1, int y2, int y3, float xresults[], float yresults[]){
float va = ((sq(d2) - sq(d3)) - (sq(x2) - sq(x3)) - (sq(y2) - sq(y3))) / (2) ;
float vb = ((sq(d2) - sq(d1)) - (sq(x2) - sq(x1)) - (sq(y2) - sq(y1))) / (2) ;
y = ((vb*(x3-x2)) - (va*(x1-x2))) / (((y1-y2)*(x3-x2)) - ((y3-y2)*(x1-x2)));
x = (va - (y*(y3-y2))) / (x3-x2);
//Serial.print(d1);
//Serial.print(d2);
//Serial.print(d3);
Serial.print("The coordinates are: ");
Serial.print(abs(x));
xresults[resultCount] = abs(x);
Serial.print(", ");
Serial.println(abs(y));
yresults[resultCount] = abs(y);
//sorting(xresults, yresults);
return xresults, yresults;
}
void sorting(float xresults[], float yresults[]){
float xfinal[10];
float yfinal[10];
qsort (xresults, 10, sizeof(int), compare);
qsort (yresults, 10, sizeof(int), compare);
for (int i = 0; i<10; i++){
if (xresults[i] > 3 && xresults[i] < MAX_X){
xfinal[i] = xresults[i];
Serial.print("x=");
Serial.print(xresults[i]);
Serial.print("\n");
}
if (yresults[i] > 10 && yresults[i] < MAX_Y){
yfinal[i] = yresults[i];
Serial.print("y=");
Serial.print(yresults[i]);
Serial.print("\n");
}
}
}
float getValue(int sensor){
NewPing sonar0(TP0, EP0, MAX_DISTANCE);
NewPing sonar1(TP1, EP1, MAX_DISTANCE);
NewPing sonar2(TP2, EP2, MAX_DISTANCE);
NewPing sonar3(TP3, EP3, MAX_DISTANCE);
NewPing sonar4(TP4, EP4, MAX_DISTANCE);
switch(sensor){
case 0:
return sonar0.ping_cm();
case 1:
return sonar1.ping_cm();
case 2:
return sonar2.ping_cm();
case 3:
return sonar3.ping_cm();
case 4:
return sonar4.ping_cm();
}
}
the arrays "xresults and yresults" is the array causing the problem. You can see that I have redefined it every time printCombination is called so it should be cleared right?
This is my output:
The coordinates are: 40.33, 34.88
The coordinates are: 58.62, 8.00
The coordinates are: 42.09, 8.00
The coordinates are: nan, 7.74
The coordinates are: 59.14, 24.17
The coordinates are: 47.81, 13.41
The coordinates are: 47.81, 7.74
x=40.25
x=58.56
y=24.16
x=47.81
y=13.41
x=47.81
x=32.99
New distance call
The coordinates are: 40.65, 35.72
The coordinates are: 58.62, 8.00
The coordinates are: 42.09, 8.00
The coordinates are: nan, 7.28
The coordinates are: 60.07, 25.03
The coordinates are: 47.81, 13.41
The coordinates are: 47.81, 7.28
x=40.50
x=58.56
y=35.56
x=47.81
y=13.41
x=47.81
x=32.99
New distance call
The coordinates are: 24.98, 15.69
The coordinates are: 50.48, 8.00
The coordinates are: 112.35, 8.00
The coordinates are: nan, 25.91
The coordinates are: 14.66, 84.64
The coordinates are: 91.00, 12.26
The coordinates are: 91.00, 25.91
x=14.63
x=50.31
y=15.70
y=12.26
y=25.91
x=32.99
As you can see there are values which are not in the list of coordinates appearing in the print out after the coordinates.
So I'm currently working in a chunk with a size of 16x256x16 and create a integer grid for block types. But my problem is how do I implement infinite voxel chunks???
I use SFML 1.6 by the way.
Here is my code:
Header (chunk.hpp):
#ifndef CHUNK_HPP
#define CHUNK_HPP
#pragma once
#include <SFML/Graphics.hpp>
#include "player.hpp"
#include "types.hpp"
#include "frustumcull.hpp"
#include "noise_generator.hpp"
const int horiz_chunksize = 16;
const int vert_chunksize = 256;
class Chunk {
public:
Chunk();
~Chunk();
int get(int x, int y, int z); // get block type in position
void set(int x, int y, int z, int type); // set block type in position
void render(Player &p, FrustumCull &cull, int renderDistance = 20); // render chunk
int getTerrainHeight(int x, int y); // returns noise height on the given position
private:
Perlin_Noise m_noise;
void update(int x, int y, int z, int type); // update block faces
};
#endif // CHUNK_HPP
Source (chunk.cpp):
I put the m_blockgrid in .cpp because if I put it in the header it will
only draw one cube and also does in Chunk::~Chunk() delete[] chnk::m_blockgrid necessary???
#include "chunk.hpp"
#include "block.hpp"
#include "maths.hpp"
#include <iostream>
namespace chnk {
int m_blockgrid[horiz_chunksize][vert_chunksize][horiz_chunksize]; // block grid
Block *m_block;
}
Chunk::Chunk() {
chnk::m_block = new Block(); // initialize block class
m_noise.setSeed(sf::Randomizer::Random(2736473, 8476864));
for(int x = 0; x < horiz_chunksize; x++) {
for(int z = 0; z < horiz_chunksize; z++) {
int heightmap = 16;
for(int y =-1; y < heightmap; y++) {
if(y > heightmap-2) set(x, y, z, BlockType::GRASS);
if(y < heightmap-1 && y > heightmap - 4) set(x, y, z, BlockType::DIRT);
if(y < heightmap-3 && y > 0) set(x, y, z, BlockType::STONE);
if(y == 0) set(x, y, z, BlockType::BEDROCK);
}
}
}
}
Chunk::~Chunk() {
delete[] chnk::m_blockgrid;
}
int Chunk::get(int x, int y, int z) {
// check boundary
if((x<0) || (x>=horiz_chunksize) ||
(y<0) || (y>=vert_chunksize) ||
(z<0) || (z>=horiz_chunksize)) return BlockType::AIR;
return chnk::m_blockgrid[x][y][z];
}
void Chunk::set(int x, int y, int z, int type) {
chnk::m_blockgrid[x][y][z] = type;
m_update = true;
}
void Chunk::render(Player &p, FrustumCull &cull, int renderDistance) {
int px = p.m_position.x / chnk::m_block->m_size;
int py = (p.m_position.y + p.m_bottom) / chnk::m_block->m_size;
int pz = p.m_position.z / chnk::m_block->m_size;
float radius = sqrt(Maths::sqr(chnk::m_block->m_size) * 5);
glEnable(GL_CULL_FACE); // hide back face
glEnable(GL_DEPTH_TEST); // depth testing
// render object(s)
for(int x = 0; x < horiz_chunksize; x++) {
for(int z = 0; z < horiz_chunksize; z++) {
for(int y = 0; y < vert_chunksize; y++) {
int type = get(x, y, z);
if(!cull.sphereInFrustum(sf::Vector3f(chnk::m_block->m_size * x + chnk::m_block->m_size / 2, chnk::m_block->m_size * y + chnk::m_block->m_size / 2, chnk::m_block->m_size * z + chnk::m_block->m_size / 2), radius)) continue;
update(x, y, z, type); // update for block texture & etc.
}
}
}
}
void Chunk::update(int x, int y, int z, int type) {
// only show face in outside not inside
// I use get(x, y, z) to get block position at given grid
if(BlockType::getSolidBlocks(type)) {
if(BlockType::getSolidBlocks(get(x, y+1, z)) == 0 && get(x, y+1, z) != type) {
chnk::m_block->setupBlock(x, y, z, Block::Top); // Top Face
}
if(BlockType::getSolidBlocks(get(x, y-1, z)) == 0 && get(x, y-1, z) != type) {
chnk::m_block->setupBlock(x, y, z, Block::Bottom); // Bottom Face
}
if(BlockType::getSolidBlocks(get(x, y, z-1)) == 0 && get(x, y, z-1) != type) {
chnk::m_block->setupBlock(x, y, z, Block::Front); // Front Face
}
if(BlockType::getSolidBlocks(get(x, y, z+1)) == 0 && get(x, y, z+1) != type) {
chnk::m_block->setupBlock(x, y, z, Block::Back); // Back Face
}
if(BlockType::getSolidBlocks(get(x-1, y, z)) == 0 && get(x-1, y, z) != type) {
chnk::m_block->setupBlock(x, y, z, Block::Left); // Left Face
}
if(BlockType::getSolidBlocks(get(x+1, y, z)) == 0 && get(x+1, y, z) != type) {
chnk::m_block->setupBlock(x, y, z, Block::Right); // Right Face
}
}
}
int Chunk::getTerrainHeight(int x, int y) {
return ( m_noise.getHeight(x, y) + 64 ); // total height of the given coordinates
}
Some people uses unordered_map to store loaded/unloaded chunk but I dont know how to use it and if it does work??
Anyone would like to help me?? :)
I will make the following suppositions:
Access to a given chunk is the priority, they are accessed several times a frame so access needs to be O(k)
Insertion and deletion of chunks needs to be done as fast as possible, because they will be generated on the fly, and dropped on the fly. Not as critical as the access.
Most of the time, the amount of chunks in memory is about constant.
Now, let see what are our possibilities:
std::container
Insertion
Access
Erase
Find
PersistentIterators
vector/string
Back: O(1) or O(n)Other: O(n)
O(1)
Back: O(1)Other: O(n)
Sorted: O(log n)Other: O(n)
No
deque
Back/Front: O(1)Other: O(n)
O(1)
Back/Front: O(1)Other: O(n)
Sorted: O(log n)Other: O(n)
Pointers only
list/forward_list
Back/Front: O(1)With iterator: O(1)Index: O(n)
Back/Front: O(1)With iterator: O(1)Index: O(n)
Back/Front: O(1)With iterator: O(1)Index: O(n)
O(n)
Yes
set/map
O(log n)
-
O(log n)
O(log n)
Yes
unordered_set/unordered_map
O(1) or O(n)
O(1) or O(n)
O(1) or O(n)
O(1) or O(n)
Pointers only
priority_queue
O(log n)
O(1)
O(log n)
-
-
std::unordered_map is the most adequate structure for this because it allows constant access, and (most of the time) constant insertion/deletion:
struct ChunkCoordinate
{
int32_t x,
int32_t y
};
class ChunkCoordinateHash{
public:
size_t operator()(const ChunkCoordinate &val) const
{
static_assert(sizeof(size_t)==8);
return (static_cast<size_t>(val.x)<<32ull) + (static_cast<size_t>(val.y)&0xffffffff);
}
};
std::unordered_map<ChunkCoordinate, unique_ptr<Chunk>, ChunkCoordinateHash> m_chunks;
Note: Insertion and Erase on unordered_map is usually O(k)(constant), unless space is missing and all the map needs to be relocated. as you have usually a constant number of chunks loaded, you can unordered_map::reserve a sufficient amount of chunks so that it is never relocated.
Note2: I use pointers to Chunk to make any relocation faster.
Here is some example of usage:
https://onlinegdb.com/FiGzEzHgD
I am attempting an online coding challenge wherein I am to implement a pathfinding algorithm that finds the shortest path between two points on a 2D grid. The code that is submitted is tested against a number of test cases that I, unfortunately, am unable to see, but it will however tell me if my answer for shortest distance is correct or not. My implementation of the A* algorithm returns a correct answer on 2/3 test cases and I cannot seem to figure out what scenario might create an incorrect answer on the third?
I have tried several of my own test cases and have gotten correct answers for all of those and at this point am feeling a little bit lost. There must be something small in my code that I am not seeing that is causing this third case to fail.
More details
The grid is w by h and contains only 1's (passable) and 0's (impassable) with every edge having a cost of 1 and the pathway cannot move diagonally
It all starts with the FindPath function which is to return the length of the shortest path, or -1 if no path is available
pOutBuffer is used to contain the path taken from beginning to end (excluding the starting point). If multiple paths are available then any will be accepted. So it isnt looking for one path in particular
I know the issue is not the result of time or memory inefficiency. I has to be either the distance returned is incorrect, or the values in pOutBuffer are incorrect.
Any help would be greatly appreciated as I am just about out of ideas as to what could possibly be wrong here. Thank you.
#include <set>
#include <vector>
#include <tuple>
#include <queue>
#include <unordered_map>
inline int PositionToIndex(const int x, const int y, const int w, const int h)
{
return x >= 0 && y >= 0 && x < w && y < h? x + y * w : -1;
}
inline std::pair<int, int> IndexToPosition(const int i, const int w)
{
return std::make_pair<int, int>(i % w, i / w);
}
inline int Heuristic(const int xa, const int ya, const int xb, const int yb)
{
return std::abs(xa - xb) + std::abs(ya - yb);
}
class Map
{
public:
const unsigned char* mapData;
int width, height;
const std::vector<std::pair<int, int>> directions = { {1,0}, {0,1}, {-1,0}, {0,-1} };
Map(const unsigned char* pMap, const int nMapWidth, const int nMapHeight)
{
mapData = pMap;
width = nMapWidth;
height = nMapHeight;
}
inline bool IsWithinBounds(const int x, const int y)
{
return x >= 0 && y >= 0 && x < width && y < height;
}
inline bool IsPassable(const int i)
{
return mapData[i] == char(1);
}
std::vector<int> GetNeighbours(const int i)
{
std::vector<int> ret;
int x, y, neighbourIndex;
std::tie(x, y) = IndexToPosition(i, width);
for (auto pair : directions)
{
neighbourIndex = PositionToIndex(x + pair.first, y + pair.second, width, height);
if (neighbourIndex >= 0 && IsWithinBounds(x + pair.first, y + pair.second) && IsPassable(neighbourIndex))
ret.push_back(neighbourIndex);
}
return ret;
}
};
int FindPath(const int nStartX, const int nStartY,
const int nTargetX, const int nTargetY,
const unsigned char* pMap, const int nMapWidth, const int nMapHeight,
int* pOutBuffer, const int nOutBufferSize)
{
int ret = -1;
// create the map
Map map(pMap, nMapWidth, nMapHeight);
// get start and end indecies
int targetIndex = PositionToIndex(nTargetX, nTargetY, nMapWidth, nMapHeight);
int startIndex = PositionToIndex(nStartX, nStartY, nMapWidth, nMapHeight);
// if start and end are same exit
if (targetIndex == startIndex) return 0;
std::unordered_map<int, int> pathway = { {startIndex, startIndex} };
std::unordered_map<int, int> distances = { {startIndex, 0} };
// queue for indecies to process
typedef std::pair<int, int> WeightedLocation;
std::priority_queue<WeightedLocation, std::vector<WeightedLocation>, std::greater<WeightedLocation>> queue;
queue.emplace(0, startIndex);
while (!queue.empty())
{
int currentWeight, currentIndex;
std::tie(currentWeight, currentIndex) = queue.top();
queue.pop();
if (currentIndex == targetIndex)
break;
int newDistance = distances[currentIndex] + 1;
for (int n : map.GetNeighbours(currentIndex))
{
if (distances.find(n) == distances.end() || newDistance < distances[n])
{
distances[n] = newDistance;
int weight = newDistance + Heuristic(n % nMapWidth, n / nMapWidth, nTargetX, nTargetY);
queue.emplace(weight, n);
pathway[n] = currentIndex;
}
}
}
if (pathway.find(targetIndex) != pathway.end())
{
int current = targetIndex;
while (current != startIndex)
{
int outIndex = distances[current] - 1;
pOutBuffer[distances[current] - 1] = current;
current = pathway[current];
}
ret = distances[targetIndex];
}
return ret;
}
I am trying to make optimal algorithm to draw rectangle onto 1D array. I wrote this function:
/** Draws a rectangle in 1D array
* Arguments:
* pixmap - 1D array of Color
* color - rectangle color
* w - rectangle width
* h - rectanhle height
* x - x position, negative coordinates are outside draw area
* y - y position, negative coordinates are outside draw area
* pixmapWidth - width of the image (height can be deducted from width if needed but is practically unnecessary) */
void rectangle(std::vector<int>& pixmap, const int& color, const int w, const int h, int x, const int y, const int pixmapWidth)
{
if(x>=pixmapWidth)
return;
if(x+w<0)
return;
if(y+h<0)
return;
// Width of one consistent line of color of the rectangle
// if the rectangle is partially out of pixmap area,
// thw width is smaller than rectangle width
const int renderWidth = std::min(w, pixmapWidth-x);
// offset in the arrray where the rendering starts
// 0 would be for [0,0] coordinate
int tg_offset = y*pixmapWidth+x;
// maximum offset to ever render, which is the array size
const int tg_end = pixmap.size();
int lines = 0;
for(; tg_offset<tg_end && lines<h; tg_offset+=pixmapWidth) {
for(int cx=0; cx<renderWidth; ++cx) {
// This check keeps failing and my program crashes
if(tg_offset+cx >= pixmap.size())
throw "Oh no, what a bad thing to happen!";
pixmap[tg_offset+cx] = color;
}
lines++;
}
}
Note that I know there's a lot of picture drawing libraries, but I'm trying to learn by doing this. But now I'm stuck and I need help.
The problem is that in the inner loop, condition if(tg_offset+cx >= pixmap.size()) keeps failing meaning I am trying to render outside the array. I have no idea why this keeps happening.
Example problematic code:
const int pixmap_width = 20;
const int pixmap_height = 20;
std::vector<int> pixmap(pixmap_width*pixmap_height);
// tries to render outside the array
rectangle(pixmap, 0, 10, 10, -1, 18, pixmap_width);
Here is a testcase including ASCII output of the pixmap: http://ideone.com/SoJPFF
I don't know how could I improve the question any more...
Making no changes produces a quadrilateral. Is this not the desired functionality?
for(; tg_offset<tg_end && lines<h; tg_offset+=pixmapWidth) {
cout <<"" << endl;
for(int cx=0; cx<renderWidth; ++cx) {
cout << " " << pixmap[tg_offset+cx];
// This check keeps failing and my program crashes
if(tg_offset+cx >= pixmap.size())
throw "Oh no, what a bad thing to happen!";
pixmap[tg_offset+cx] = color;
}
lines++;
}
}
int main()
{
std::vector<int> pixmap(16);
pixmap = { 1,1,1,1,1,0,0,1,1,0,0,1,1,1,1,1 };
int color = 0;
int w = 4;
int h = 4;
int x = 0;
int y = 0;
int pixmapWidth = 4;
cout << "Hello World" << endl;
rectangle(pixmap, color, w, h, x, y, pixmapWidth);
return 0;
}
produces:
Hello World
1 1 1 1
1 0 0 1
1 0 0 1
1 1 1 1
I think a large part of the problem with your function is it being a lot more complex than it needs to be. Here's a much simpler version of your function, done by simply looping over x and y.
void rectangle(std::vector<int>& pixmap, const int& color, const int width, const int height,
int left, const int top, const int pixmapWidth)
{
for (int x = std::max(left, 0); x < left + width && x < pixmapWidth; x++)
for (int y = std::max(top, 0); y < top + height && y*pixmapWidth + x < pixmap.size(); y++)
pixmap[y*pixmapWidth + x] = color;
}
I'm not sure exactly what the output you want when x or y are negative. In your actual algorithm things goes wrong if x is negative due the fact that tg_offset goes back, so the tg_offset + cx can fail.
To solve this you can limit the second for to avoid this, like this:
for(int cx=0; cx<std::min(renderWidth, tg_end - tg_offset); ++cx)
but I think that limiting x and y to be only positive is more correct:
if ( x < 0 ) x = 0;
if ( y < 0 ) y = 0;
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 6 years ago.
Improve this question
Below is my solution to the graphical editor programming challenge, detailed here(which has taken me 3 days to solve). While the output is correct, It keeps producing a time_limit_exceeded error. I think that the problem is in the way I've implemented the flood_fill function which gets called when the user enters "F" as the first letter of a line. I would appreciate it if someone explained what is so inefficient about my code and how i could improve it. My code is below:
// graphical_editor.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <iostream> //provides access to cout and cin
#include <string> //Always import <string> if piping std input to a string in .net
#include <vector>
#include <fstream>
using std::ofstream;
using std::cin;
using std::cout;
using std::string;
using std::vector;
//This is where we store the pixels of the image
static vector<vector <string>> image_array;
ofstream myfile;
//our definition of an X,Y coordinate pair.
typedef struct point {
int x_coordinate, y_coordinate;
};
void initialise_image();
void clear_image();
void save_image(string file_name);
int get_image_width();
int get_image_height();
void color_pixel(int x, int y, string color);
void color_point(point p, string color);
void color_vertical_line(int x, int y1, int y2, string color);
void color_horizontal_line(int x1, int x2, int y, string color);
void color_box(int x1, int x2, int y1, int y2, string color);
void flood_fill(point p, string color);
vector<point> get_matching_neighbours(point p, string color);
int main()
{
string command; //first letter of a given line
myfile.open("example.txt");
while (cin >> command) {
//application terminates when command is X
if (command.compare("X") == 0) {
return 0;
} else if (command.compare("I") == 0) {
initialise_image();
}
else if (command.compare("S") == 0) {
string file_name;
cin >> file_name;
save_image(file_name);
}
else if (command.compare("L") == 0) {
string color;
point p;
cin >> p.x_coordinate >> p.y_coordinate >> color;
color_point(p, color);
}
else if (command.compare("V") == 0) {
string color;
int x, y1, y2;
cin >> x >> y1 >> y2 >> color;
color_vertical_line(x, y1, y2, color);
}
else if (command.compare("H") == 0) {
string color;
int x1, x2, y;
cin >> x1 >> x2 >> y >> color;
color_horizontal_line(x1, x2, y, color);
}
else if (command.compare("K") == 0) {
string color;
int x1, x2, y1, y2;
cin >> x1 >> x2 >> y1 >> y2 >> color;
color_box(x1, x2, y1, y2, color);
}
else if (command.compare("F") == 0) {
string color;
point p;
cin >> p.x_coordinate >> p.y_coordinate >> color;
flood_fill(p, color);
}
else if (command.compare("C") == 0) {
clear_image();
}
}
return 0;
}
void initialise_image()
{
/*read parameters height and width*/
int width, height;
cin >> width >> height;
/*first we create a vector of vectors (numRows+1)x(numColumns matrix+1). */
image_array.clear();
for (int i = 0; i < width+ 1; i++) {
image_array.push_back(vector<string>());
}
/*then we initialize each element of it one by one*/
for (int colNo = 0; colNo < width + 1; colNo++) {
for (int rowNo = 0; rowNo < height + 1; rowNo++) {
image_array[colNo].push_back("O");
}
}
}
void clear_image() {
/*we initialize each element of it one by one*/
for (int y = 1; y < get_image_height()+1 ; y++) {
for (int x = 1; x < get_image_width()+1; x++) {
image_array[x][y] = "O";
}
}
}
void save_image(string file_name) {
myfile << file_name << "\n";
//cout << file_name << "\n";
for (int y = 1; y < get_image_height()+1; y++) {
for (int x = 1; x < get_image_width()+1; x++) {
myfile << image_array[x][y];
//cout << image_array[x][y];
}
myfile << "\n";
//cout << "\n";
}
myfile.close();
}
int get_image_width() {
return image_array.size()-1;
}
int get_image_height() {
return image_array[0].size()-1;
}
void color_point(point p, string color) {
color_pixel(p.x_coordinate,p.y_coordinate, color);
}
void color_pixel(int x, int y, string color) {
image_array[x][y] = color;
}
void color_vertical_line(int x, int y1, int y2, string color) {
for (int y = y1; y <= y2; y++) {
color_pixel(x, y, color);
}
}
void color_horizontal_line(int x1, int x2, int y, string color) {
for (int x = x1; x <= x2; x++) {
color_pixel(x, y, color);
}
}
void color_box(int x1, int x2, int y1, int y2, string color) {
for (int x = x1; x <= x2; x++) {
for (int y = y1; y <= y2; y++) {
color_pixel(x, y, color);
}
}
}
string get_point_color(point p) {
return image_array[p.x_coordinate][p.y_coordinate];
}
void flood_fill(point p, string color) {
vector <point> points_queue;
points_queue.push_back(p);
string original_color = get_point_color(p);
point current_point;
while (points_queue.size() > 0) {
current_point = points_queue[0];
//if the point shares a color with the original point then color it in the new color.
if (get_point_color(current_point).compare(original_color) == 0) {
color_point(current_point, color);
}
// remove current point from the queue
points_queue.erase(points_queue.begin());
// add it's neighbours to the queue
vector<point> matching_neighbours = get_matching_neighbours(current_point, original_color);
for (int i = 0; i < matching_neighbours.size(); i++) {
points_queue.push_back(matching_neighbours[i]);
}
}
}
bool is_valid_point(point p) {
if (p.x_coordinate >= 1 && p.x_coordinate < get_image_width() + 1 && p.y_coordinate >= 1 && p.y_coordinate < get_image_height() + 1) {
return true;
}
else {
return false;
}
}
vector<point> get_matching_neighbours(point p, string color) {
vector<point> neighbours;
point left_neighbour, right_neighbour, upper_neighbour, lower_neighbour;
left_neighbour.x_coordinate = p.x_coordinate - 1;
left_neighbour.y_coordinate = p.y_coordinate;
if (is_valid_point(left_neighbour) && get_point_color(left_neighbour).compare(color) == 0) {
neighbours.push_back(left_neighbour);
}
right_neighbour.x_coordinate = p.x_coordinate + 1;
right_neighbour.y_coordinate = p.y_coordinate;
if (is_valid_point(right_neighbour) && get_point_color(right_neighbour).compare(color) == 0) {
neighbours.push_back(right_neighbour);
}
upper_neighbour.x_coordinate = p.x_coordinate;
upper_neighbour.y_coordinate = p.y_coordinate + 1;
if (is_valid_point(upper_neighbour) && get_point_color(upper_neighbour).compare(color) == 0) {
neighbours.push_back(upper_neighbour);
}
lower_neighbour.x_coordinate = p.x_coordinate;
lower_neighbour.y_coordinate = p.y_coordinate - 1;
if (is_valid_point(lower_neighbour) && get_point_color(lower_neighbour).compare(color) == 0) {
neighbours.push_back(lower_neighbour);
}
return neighbours;
}
There are a couple of really bad performance problems with your flood fill.
These are the 2 biggest ones:
1) You are using a vector as a queue. That is terribly slow, since it takes O(N) time to remove an item from the start of a vector. Use a deque instead, or use the vector like a stack instead of a queue by taking items from the end instead of the start.
2) You enqueue everything that get_matching_neighbors returns, but it can return things that area already in the queue. Because of this you can end up scanning and enqueing the same pixel many times.
To fix problem (2), you should should:
a) return from the flood fill immediately if the target pixel is already the right color. Then
b) color pixels when you put them into the queue, not when you take them out. That way you won't enque anything that's already in the queue, because nothing in the queue has the original color.
It is also pretty expensive to have get_matching_neighbors allocating a new vector. You should pass it a reference to an existing vector and have it replace the contents, or pass it a reference to the queue and have it recolor and add the pixels it finds.
Some overall optimization tips you could apply to your code:
So first, the specification says that color is 1 Latin char... so why are you using string? Strings are slow, especially when you know you're going to receive one char.
Second - when you're using vectors and know that you'll be pushing a lot of elements (and you know the amount prior), then reserve() the actual size of the vector, and the number of elements you're going to add.
When you're appending vector to a vector just reserve enough space and then
insert(first.begin(), second.begin(), second.end());
Not to mention that I would actually advice not to use vectors... they're easy to use, but often are slower than using an array. So if the algorithm itself isn't very complicated - use arrays. Also keep in mind that c-style arrays are faster than pointers (vector operates on pointers).
Third - always use constructors when you can, use pre-incrementation/decrementation operators, and always iterate from n to 0 when you can (generates about 2 less asm instructions)for(int i=0; i<n; i++) -> for(int i(n); i>0; --i)
Fourth - If you have if()...else if()...else if() chain consider using switch(). In most cases the compiler makes a lookup table for both of them. But I believe that it's guaranteed to do that for switch, but not for if chain. In short - switch() is most likely to be faster than if
Fifth - If you must use vectors, and have to fill them with some values, either use <algorithm> and iterators, or use C++11 and vector::data() that comes with it to get the pointer to the allocated array, and iterate over it.
If you didn't understand "how" or "why", just do some research. If you already did and still don't understand then I can't help you more - I won't optimize the code for you. That way you wouldn't learn anything.
Also, when given optimizations seem not to optimize enough, consider thinking about new algorithm.