This question is on code for C++ builder 6. The bounty is interested in a standard C++ algorithm to solve the problem given a standardized input (see this for more information.)
The txt file which also represents the data I have in an array:
1101 0110 1101 0110 1100 0101 0110
1110 1001 0110 1011 1010 1111 1010
1000 0101 0011 1110 1011 1110 1010
1011 1101 0101 0001 0101 0011 1011
Explanation of the txt:
The numbers from the txt file are a 4-bit representation of the walls to a room, with a set bit representing a wall. The wall bits are in clockwise order starting with the most significant bit being the West wall. For example, 1101 represents a room where:
Given:
numeric_limits<int>::max()
roomsI was asked to post my code so here it is:
I've tried to solve this but I'm getting an EAAccessviolation can someone tell me what I'm doing wrong?
int rn=0,z=0, global=0,coord[15],c[411],b1[411];
void peruse ( int i, int j,int* bb)
{
bool top=false,bottom=false,right=false,left=false;
//truth checks
if (bb[i*m+j]<1000) left=true;
if (bb[i*m+j]<100) top=true; else if (bb[i*m+j]-1000<100) top=true;
if (bb[i*m+j]<10) right=true; else
if ( (bb[i*m+j]-100<10) || (bb[i*m+j]-1000<10) || (bb[i*m+j]-100<10) ) right=true;
if (bb[i*m+j]<1) bottom=true; else
if ( (bb[i*m+j]-10<1) || (bb[i*m+j]-100<1) || (bb[i*m+j]-1000<1) ||(bb[i*m+j]-100<1))
bottom=true;
//marc
if (left)
{
c[i*m+j]=c[i*m+j]+1000; // EAaccessViolation i dont know why.....
peruse(i,j-1,c);
}
if (top)
{
c[i*m+j]=c[i*m+j]+100;
peruse(i-1,j,c);
}
if (right)
{
c[i*m+j]=c[i*m+j]+10;
peruse(i,j+1,c);
}
if (bottom)
{
c[i*m+j]=c[i*m+j]+1;
peruse(i+1,i,c);
}
if ( !(left) && !(top) && !(right) && !(bottom) )
{
bb[411]++;
}
}
void __fastcall TForm1::Button7Click(TObject *Sender)
{
b1[411]=0;
for(int i=0;i<n;i++)
for (int j=0;j<m;j++)
{
b1[i*m+j]=b[i][j];
c[i*m+j]=b[i][j];
}
peruse (1,1,b1);
ShowMessage("Nr. "+IntToStr(b1[411]) );
}
This is a typical problem of finding the total number of connected components in a graph.
Let me help you visualize the analogy by focusing on following points, keeping in mind that we are dealing with undirected graphs here.
1.In a graph, we have various vertices and two vertices are said to be adjacent to each other, if there is an edge between them. Just like your castle where two cells are adjacent to each other if one cell could lead to another cell.
2. In a graph we have two vertices belong to the same connected component, if there is a path between two vertices using the edges. Just like your castle where two cells belong to the same room number if one cell can by following a path of cells could lead to another.
3. In a graph we have connected components, such that a connected component is made up of vertices such that every two vertices of the connected component have a path between them.Just like your castle where we have rooms, such that every two cells of the same room have a path of cells between them.
Now if you are still wondering how to build the graph, well its easy.
The number of vertices will be NxM
(for a castle of size N rows and M columns) which is equal to the number of cells.
Just number the cells sequentially and there is an edge between cell a(meaning vertex a)
and cell b(vertex b)
if both cells are adjacent.
Now the total number of rooms can be easily counted by applying bfs or dfs algorithm on your graph that you build.
The algorithm is described in the first link I provided.
So honestly I just really wanted to try to solve this. So I'm going to say you've made a valiant effort at this and just go ahead and show you how to do it. I'm going to assume that you can supply the algorithm the following:
const vector<char> rooms
size_t width
(which must be consistent across all rows, we have to be working with a rectangle of rooms)numeric_limits<int>::max()
"rooms"We'll use vector<int> temp
to label each room, we'll construct it of the size of rooms
and initialize each label to 0. int result
will be used to label rooms, and will be initialized to 0. But because all the room labels will not be decremented when a smaller label is replaced, size(set<int>(cbegin(temp), cend(temp)))
will be used to find the final label count.
Our solution will be built around a function taking in 2 "rooms" between which there is no wall; such that either:
An important note about this function, I'm using the unary plus operator to construct an R-Value int
from an L-Values int&
more information here. A clearer solution would probably be to use static_cast<int>
but for some reason Visual Studio 2015 doesn't work as expected more information here.
void generate(vector<int>& temp, int& target, const size_t width, const size_t i) {
const auto replacement = temp[i];
if (target > replacement) {
replace(begin(temp), next(begin(temp), min(size(temp), i + width - 1)), target, replacement);
} else {
target = replacement;
}
}
Using this code we are able to:
for (size_t i = 0U; i < size(rooms); ++i) {
const auto toWest = (rooms[i] & 0b1000) == 0;
const auto toNorth = (rooms[i] & 0b100) == 0;
const auto toEast = (rooms[i] & 0b10) == 0;
const auto toSouth = (rooms[i] & 0b1) == 0;
const auto west = toWest && temp[i - 1] != 0 ? temp[i - 1] : numeric_limits<int>::max();
const auto north = toNorth && temp[i - width] != 0 ? temp[i - width] : numeric_limits<int>::max();
const auto east = toEast && temp[i + 1] != 0 ? temp[i + 1] : numeric_limits<int>::max();
temp[i] = min({ temp[i] != 0 ? temp[i] : numeric_limits<int>::max(), result + 1, west, north, east });
if (temp[i] == result + 1) ++result;
if (toWest) generate(temp, temp[i - 1], width, i);
if (toNorth) generate(temp, temp[i - width], width, i);
if (toEast) generate(temp, temp[i + 1], width, i);
if (toSouth) temp[i + width] = temp[i];
}
Live Example
There are few problems with your code prohibiting proper debug form third side like insufficient info about how it works, undefined variables (m,n,b
) array overruns (numerous access to [411]
while the size is only 411
instead of 412
) discouraging anyone from even start to try (making one wonder if the code is really useful and worthy of time spending). I was also curious so here my simple unoptimized attempt in BDS2006 Turbo C++ (successor to BCB6 so this code should work there as well) for this task.
[rooms.h]
//---------------------------------------------------------------------------
#ifndef _rooms_h
#define _rooms_h
//---------------------------------------------------------------------------
class rooms
{
public:
// variables
int xs,ys; // map resolution
DWORD **map; // map[xs][ys]
enum
{
_W=8,
_N=4,
_E=2,
_S=1
};
// internals
rooms();
~rooms();
void _free(); // release map memory
// inteface
void resize(int _xs,int _ys); // realloc map to new resolution
void set(AnsiString txt); // copy txt to map
void draw(TCanvas *scr,int x,int y,int sz); // draw map on Canvas at (x,y) with grid size sz
int count(); // count rooms
};
//---------------------------------------------------------------------------
rooms::rooms() { map=NULL; xs=0; ys=0; }
rooms::~rooms() { _free(); }
//---------------------------------------------------------------------------
void rooms::_free()
{
if (map)
{
for (int x=0;x<xs;x++)
if (map[x])
delete[] map[x];
delete[] map;
}
map=NULL; xs=0; ys=0;
}
//---------------------------------------------------------------------------
void rooms::resize(int _xs,int _ys)
{
if ((xs==_xs)&&(ys==_ys)) return;
_free();
xs=_xs; ys=_ys;
map=new DWORD*[xs];
for (int x=0;x<xs;x++)
map[x]=new DWORD[ys];
}
//---------------------------------------------------------------------------
void rooms::set(AnsiString txt)
{
int i,l,x,y,n0,n1;
l=txt.Length(); if (!l) return;
// count eof lines (ys)
for (y=0,i=1;i<=l;i++)
if ((txt[i]==13)||(txt[i]==10))
{
y++;
if (i<l)
if ((txt[i+1]==13)||(txt[i+1]==10)) i++;
}
if ((txt[l]!=13)&&(txt[l]!=10)) y++; // handle missing last eof
// count numbers per line (xs)
for (n1=0,x=0,i=1;i<=l;i++)
{
n0=n1; n1=0;
if ((txt[i]=='0')||(txt[i]=='1')) n1=1;
if ((txt[i+1]==13)||(txt[i+1]==10)) break;
if ((!n0)&&(n1)) x++;
}
// copy data
resize(x,y);
for (x=0,y=0,i=1;i<=l;)
{
// skip spaces
while ((i<=l)&&(txt[i]!='0')&&(txt[i]!='1')) i++;
// read 4 bit bin number
n0= 0; if (i>l) break; if (txt[i]=='1') n0|=1; i++;
n0<<=1; if (i>l) break; if (txt[i]=='1') n0|=1; i++;
n0<<=1; if (i>l) break; if (txt[i]=='1') n0|=1; i++;
n0<<=1; if (i>l) break; if (txt[i]=='1') n0|=1; i++;
map[x][y]=n0;
x++; if (x>=xs) { x=0; y++; if (y>=ys) break; }
}
// clear the rest in case of error in data
if ((y<ys)&&(x<xs)) for (;;)
{
map[x][y]=0;
x++; if (x>=xs) { x=0; y++; if (y>=ys) break; }
}
}
//---------------------------------------------------------------------------
void rooms::draw(TCanvas *scr,int x0,int y0,int sz)
{
int x,y,xx,yy;
DWORD a;
scr->Brush->Color=clDkGray;
scr->Brush->Style=bsSolid;
scr->FillRect(Rect(x0,y0,x0+xs*sz,y0+ys*sz));
scr->Pen->Color=clBlue;
scr->Pen->Width=5;
scr->Font->Color=clBlack;
scr->Brush->Style=bsClear;
for (xx=x0,x=0;x<xs;x++,xx+=sz)
for (yy=y0,y=0;y<ys;y++,yy+=sz)
{
a=map[x][y]&15;
if (DWORD(a&_W)) { scr->MoveTo(xx ,yy ); scr->LineTo(xx ,yy+sz); }
if (DWORD(a&_N)) { scr->MoveTo(xx ,yy ); scr->LineTo(xx+sz,yy ); }
if (DWORD(a&_E)) { scr->MoveTo(xx+sz,yy ); scr->LineTo(xx+sz,yy+sz); }
if (DWORD(a&_S)) { scr->MoveTo(xx ,yy+sz); scr->LineTo(xx+sz,yy+sz); }
scr->TextOutA(xx+(sz>>1),yy+(sz>>1),map[x][y]>>4);
}
scr->Brush->Style=bsSolid;
scr->Pen->Width=1;
}
//---------------------------------------------------------------------------
int rooms::count()
{
int x,y,i,i0,i1,w0,w1,n,e;
// each block is a separate room
for (n=0,x=0;x<xs;x++)
for (y=0;y<ys;y++,n+=16)
{
map[x][y]&= 0x0000000F; // low 4 bits are walls
map[x][y]|=n&0xFFFFFFF0; // rest is room index
} n>>=4;
// reindex all indexes i0 to i1
#define map_reindex(i0,i1) \
for (x=0;x<xs;x++) \
for (y=0;y<ys;y++) \
if (DWORD(map[x][y]&0xFFFFFFF0)==i0) \
{ \
map[x][y]&= 0x0000000F; \
map[x][y]|=i1&0xFFFFFFF0; \
}
// loop until no change has occured
for (e=1;e;)
{
e=0;
// merge columns
for (x=0;x<xs;x++)
for (y=1;y<ys;y++)
{
w0=map[x][y-1]&0x0000000F;
i0=map[x][y-1]&0xFFFFFFF0;
w1=map[x][y ]&0x0000000F;
i1=map[x][y ]&0xFFFFFFF0;
if ((i0!=i1)&&(DWORD(w0&_S)==0)&&(DWORD(w1&_N)==0)) { map_reindex(i0,i1); n--; e=1; }
}
// merge rows
for (y=0;y<ys;y++)
for (x=1;x<xs;x++)
{
w0=map[x-1][y]&0x0000000F;
i0=map[x-1][y]&0xFFFFFFF0;
w1=map[x ][y]&0x0000000F;
i1=map[x ][y]&0xFFFFFFF0;
if ((i0!=i1)&&(DWORD(w0&_E)==0)&&(DWORD(w1&_W)==0)) { map_reindex(i0,i1); n--; e=1; }
}
}
return n;
#undef map_reindex
}
//---------------------------------------------------------------------------
#endif
//---------------------------------------------------------------------------
[And Single window VCL Form app C++ source without any component]
//$$---- Form CPP ----
//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"
#include "rooms.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
Graphics::TBitmap *bmp; // back buffer
int xs,ys; // actual window resolution
rooms map; // map of rooms
//---------------------------------------------------------------------------
void draw()
{
int x,y,sz;
// clear bachground
bmp->Canvas->Brush->Color=clBlack;
bmp->Canvas->Brush->Style=bsSolid;
bmp->Canvas->FillRect(Rect(0,0,xs,ys));
// compute grid size
x=(xs-20)/map.xs; sz=x;
y=(ys-20)/map.ys; if (x>y) sz=y;
// and map position so it is centered
x=(xs-(sz*map.xs))>>1;
y=(ys-(sz*map.ys))>>1;
// render to backbuffer (avoid flickering)
map.draw(bmp->Canvas,x,y,sz);
// render backbuffer to window
Form1->Canvas->Draw(0,0,bmp);
}
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner):TForm(Owner)
{
// init backbuffer
bmp=new Graphics::TBitmap;
bmp->HandleType=bmDIB;
bmp->PixelFormat=pf32bit;
// init map
map.set("1101 0110 1101 0110 1100 0101 0110\r\n1110 1001 0110 1011 1010 1111 1010\r\n1000 0101 0011 1110 1011 1110 1010\r\n1011 1101 0101 0001 0101 0011 1011\r\n");
Caption=map.count(); // result count is in the Window Caption
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormDestroy(TObject *Sender) { delete bmp; }
//---------------------------------------------------------------------------
void __fastcall TForm1::FormPaint(TObject *Sender) { draw(); }
//---------------------------------------------------------------------------
void __fastcall TForm1::FormResize(TObject *Sender)
{
// get actual window size
xs=ClientWidth;
ys=ClientHeight;
// resize backbufer and force redraw
bmp->SetSize(xs,ys);
draw();
}
//---------------------------------------------------------------------------
The idea behind my solver for this is simple:
ID
each grid cell by distinct room number
remember the cell count as n
merge all neighboring rooms without any wall between them
so loop through all rooms and if any neighbor cell has no wall in the way and has different room ID
re-index its room number so booth rooms has the same one. Also decrement the room counter n
.
loop #2 until no re-indexing occurs
[Notes]
Do not forget to create appropriate events in your IDE instead of just copying the code otherwise it will not work.
one easy way would be creating an array with the size of the room and initialize it with "0". After that you should iterate over the array. If you find a "0", you start an BFS from that point and color the array results to the current number.
Information about the BFS
The BFS has to look to the direct neighbor and check if there is a "0" inside this array. If this is true, the BFS has to check if there are no wall between the two boxes. If there is no wall in between, color this box with the current color (at the beginning 1) and call the BFS on the new box.
The BFS stops automaticly when the room is completely colored. then the global color will be increased by 1 and the loop continues and looks for the next "0".
After the loop the count of the rooms is stored inside the global color value.
this algorithm works in O(n)
Small example:
//CountingRooms.h
#include <vector>
class CountingRooms
{
public:
CountingRooms();
~CountingRooms();
int Count();
void TestFill();
void Print();
private:
struct Point
{
int x = 0;
int y = 0;
};
unsigned int* m_arrFieldColors = nullptr;
unsigned int* m_arrFieldWalls = nullptr;
int m_nSizeX = 0;
int m_nSizeY = 0;
int m_nCurrentColor = 0;
unsigned int GetValue(unsigned int* field, int x, int y);
void SetValue(unsigned int* field, int x, int y, unsigned int value);
bool CanPass(int x1, int y1, int x2, int y2);
void DFS(int posX, int posY);
bool IsInsideArray(int x1, int y1);
};
//CountingRooms.cpp
#include "stdafx.h"
#include "CountingRooms.h"
#include <iostream>
CountingRooms::CountingRooms()
{
}
CountingRooms::~CountingRooms()
{
if (m_arrFieldColors)
{
delete[]m_arrFieldColors;
}
if (m_arrFieldWalls)
{
delete[]m_arrFieldWalls;
}
}
bool CountingRooms::IsInsideArray(int x, int y)
{
return x >= 0 && y >= 0 && x < m_nSizeX && y < m_nSizeY;
}
bool CountingRooms::CanPass(int x1, int y1, int x2, int y2)
{
if (IsInsideArray(x1, y1) && IsInsideArray(x2, y2)) //inside the array range
{
if (x2 - x1 == 1 && y2 - y1 == 0) // right
{
if (!(GetValue(m_arrFieldWalls, x1, y1) & 2) && !(GetValue(m_arrFieldWalls, x2, y2) & 8)) { return true; }
}
if (x2 - x1 == 0 && y2 - y1 == -1) // up
{
if (!(GetValue(m_arrFieldWalls, x1, y1) & 4) && !(GetValue(m_arrFieldWalls, x2, y2) & 1)) { return true; }
}
if (x2 - x1 == -1 && y2 - y1 == 0) // left
{
if (!(GetValue(m_arrFieldWalls, x1, y1) & 8) && !(GetValue(m_arrFieldWalls, x2, y2) & 2)) { return true; }
}
if (x2 - x1 == 0 && y2 - y1 == 1) // down
{
if (!(GetValue(m_arrFieldWalls, x1, y1) & 1) && !(GetValue(m_arrFieldWalls, x2, y2) & 4)) { return true; }
}
}
return false;
}
void CountingRooms::DFS(int posX, int posY)
{
if (GetValue(m_arrFieldColors, posX, posY)) // check if the field is already colored
{
return;
}
Point sStart;
sStart.x = posX;
sStart.y = posY;
std::vector<Point> vecList;
vecList.push_back(sStart);
m_nCurrentColor++;
while (vecList.size()) // as long as something is inside the list
{
Point sTemp = vecList[vecList.size()-1]; //get out the last element
vecList.pop_back();
if (IsInsideArray(sTemp.x, sTemp.y))
{
if (!GetValue(m_arrFieldColors, sTemp.x, sTemp.y)) // is field not colored
{
SetValue(m_arrFieldColors, sTemp.x, sTemp.y, m_nCurrentColor);
if (CanPass(sTemp.x, sTemp.y, sTemp.x + 1, sTemp.y)) /* right*/
{
Point newPoint;
newPoint.x = sTemp.x + 1;
newPoint.y = sTemp.y;
vecList.push_back(newPoint);
}
if (CanPass(sTemp.x, sTemp.y, sTemp.x - 1, sTemp.y)) /* left*/
{
Point newPoint;
newPoint.x = sTemp.x - 1;
newPoint.y = sTemp.y;
vecList.push_back(newPoint);
}
if (CanPass(sTemp.x, sTemp.y, sTemp.x, sTemp.y - 1)) /* up*/
{
Point newPoint;
newPoint.x = sTemp.x;
newPoint.y = sTemp.y - 1;
vecList.push_back(newPoint);
}
if (CanPass(sTemp.x, sTemp.y, sTemp.x, sTemp.y + 1)) /* down*/
{
Point newPoint;
newPoint.x = sTemp.x;
newPoint.y = sTemp.y + 1;
vecList.push_back(newPoint);
}
}
}
}
}
int CountingRooms::Count()
{
m_nCurrentColor = 0;
for (int i = 0; i < m_nSizeY; ++i)
{
for (int j = 0; j < m_nSizeX; ++j)
{
DFS(j, i);
}
}
return m_nCurrentColor;
}
void CountingRooms::TestFill()
{
m_arrFieldWalls = new unsigned int[42]{13, 6,13, 6,12, 5, 6,
14, 9, 6,11,10,15,10,
8, 5, 3,14,11,14,10,
11,13, 5, 1, 5, 3,11};
m_arrFieldColors = new unsigned int[42];
for (int i = 0; i < 42;i++)
{
m_arrFieldColors[i] = 0;
}
m_nSizeX = 7;
m_nSizeY = 4;
}
unsigned int CountingRooms::GetValue(unsigned int* field, int x, int y)
{
if (IsInsideArray(x, y))
{
return field[x + m_nSizeX*y];
}
return -1;
}
void CountingRooms::SetValue(unsigned int* field, int x, int y, unsigned int value)
{
if (IsInsideArray(x, y))
{
field[x + m_nSizeX*y] = value;
}
}
void CountingRooms::Print()
{
std::cout << "Walls:" << std::endl;
for (int j = 0; j < m_nSizeY;++j)
{
for (int i = 0; i < m_nSizeX;++i)
{
std::cout << GetValue(m_arrFieldWalls, i, j) << "\t";
}
std::cout << std::endl;
}
std::cout << std::endl<<"Colors:" << std::endl;
for (int j = 0; j < m_nSizeY;++j)
{
for (int i = 0; i < m_nSizeX;++i)
{
std::cout << GetValue(m_arrFieldColors, i, j) << "\t";
}
std::cout << std::endl;
}
}
//main.cpp
#include "stdafx.h"
#include <iostream>
#include "CountingRooms.h"
int main()
{
CountingRooms cr;
cr.TestFill();
std::cout<<"There are "<<cr.Count()<<" rooms"<<std::endl;
cr.Print();
char key = 0;
std::cin >> key;
return 0;
}
btw: the BFS is replaced by a DFS, but both is working.
Output
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With