Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Counting Rooms While Knowing Where Walls Are

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 castle

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:

  • The set bit in the most significant position indicates a wall to the West
  • The set bit in the next most significant position indicates a wall to the North
  • The unset bit indicates no wall to the East
  • The set bit in the least significant position indicates a wall to the South

Given:

  1. The exterior walls of rooms will always have a wall
  2. Interior walls will always be expressed in both rooms (in the example since the room at (1, 1) is 1101 it contains a wall to the South, the room at (1, 2) 1110 must contain a wall to the North
  3. There will never be more than numeric_limits<int>::max() rooms

I 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]) );
}
like image 529
Nikita Cebotari Avatar asked Jun 16 '16 19:06

Nikita Cebotari


4 Answers

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.

like image 157
Sumeet Avatar answered Nov 14 '22 06:11

Sumeet


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:

  1. Input numbers read in in binary (so "1101" will be read in as the decimal number "13")
  2. All these numbers in a const vector<char> rooms
  3. The width of each row of "rooms" can be put in size_t width (which must be consistent across all rows, we have to be working with a rectangle of rooms)
  4. All exterior "walls" of "rooms" will have a "wall"
  5. There are less than 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:

  1. One room is not yet labeled, in which case it will take on the other room's label
  2. Both rooms share a label in which case no action will take place
  3. One rooms label is smaller in which case all rooms of the larger label will be assigned the smaller label

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

like image 6
Jonathan Mee Avatar answered Nov 14 '22 04:11

Jonathan Mee


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:

  1. ID each grid cell by distinct room number

    remember the cell count as n

  2. 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.

  3. loop #2 until no re-indexing occurs

example

[Notes]

Do not forget to create appropriate events in your IDE instead of just copying the code otherwise it will not work.

like image 2
Spektre Avatar answered Nov 14 '22 05:11

Spektre


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

enter image description here

like image 1
Thomas Avatar answered Nov 14 '22 04:11

Thomas