I am trying to identify the number of rooms and furniture(S,C,W,P) in an ASCII floorplan. A typical floorplan looks like this with different rooms and layouts. What would be the best way to tackle this?
+---------------+-------------------+ +----------+
| | | | |
| (office) | C | | C |
| | | | |
| W | +-----------+ |
| | | | |
| S | (bathroom) S| S | |
| +---+--------+----------+ | |
| /P S| | |
| / | | |
| / (kitchen) | (bedroom) | P |
+-------+ | | |
| \ | | |
| \ SSWP | W W | |
| +-------------+----------------------+ |
| |
| (hallway) |
| W |
+--------------+-------------+-------------+ |
| | \ |
| | \ C |
| P | \ |
| | \ |
+------+ P | +----------+
|S |
| (balcony) C |
+--------------------+
My approach would be:
That results in the following code (I defined a couple of classes for readability):
from dataclasses import dataclass, field
from re import finditer, search
from typing import Dict, List
@dataclass
class RowSegment():
row_no : int
col_start : int
col_end : int
def __repr__(self):
return f'{self.row_no}:{self.col_start}-{self.col_end}'
@dataclass
class Space():
name : str = 'noname'
furn_count : Dict[str, int] = field(default_factory = dict)
segs : List[RowSegment] = field(default_factory = list)
complete : bool = False
def __repr__(self):
return f'{self.name}: {[i for i in self.furn_count.items()]}\n{[s for s in self.segs]}'
plan = """
+---------------+-------------------+ +----------+
| | | | |
| (office) | C | | C |
| | | | |
| W | +-----------+ |
| | | | |
| S | (bathroom) S| S | |
| +---+--------+----------+ | |
| /P S| | |
| / | | |
| / (kitchen) | (bedroom) | P |
+-------+ | | |
| \ | | |
| \ SSWP | W W | |
| +-------------+----------------------+ |
| |
| (hallway) |
| W |
+--------------+-------------+-------------+ |
| | \ |
| | \ C |
| P | \ |
| | \ |
+------+ P | +----------+
|S |
| (balcony) C |
+--------------------+ """
furn_types = 'CPSW'
workspaces = []
rooms = []
rows = plan.split('\n')
rows.pop(0)
for no, row in enumerate(rows):
found = list(finditer(r'[^/\\|+-]+', row))
if found:
rsegs = [RowSegment(no, rs.start(), rs.end()) for rs in found]
for ws in workspaces:
for rs in rsegs:
if max(ws.segs[-1].col_start, rs.col_start) < min(ws.segs[-1].col_end, rs.col_end): #add rs to ws
ws.segs.append(rs)
rsegs.remove(rs)
break
else: #no rs added => complete
text = ''.join([rows[s.row_no][s.col_start:s.col_end] for s in ws.segs])
name = search(r'\(\w+\)', text)
if name:
ws.name = name[0][1:-1]
ws.furn_count = {f: text.count(f) for f in furn_types}
rooms.append(ws)
ws.complete = True
#reset ws list to only not complete
workspaces = [ws for ws in workspaces if ws.complete == False]
#create new wss with remaining rss
for rs in rsegs:
newws = Space()
newws.segs.append(rs)
workspaces.append(newws)
for r in rooms:
print(r)
The output (I'm also printing the "spaces" that compose each room) is:
bathroom: [('C', 1), ('P', 0), ('S', 1), ('W', 0)]
[1:17-36, 2:17-36, 3:17-36, 4:17-36, 5:17-36, 6:17-36]
office: [('C', 0), ('P', 0), ('S', 1), ('W', 1)]
[1:1-16, 2:1-16, 3:1-16, 4:1-16, 5:1-16, 6:1-16, 7:1-12, 8:1-11, 9:1-10, 10:1-9]
bedroom: [('C', 0), ('P', 0), ('S', 1), ('W', 2)]
[5:37-48, 6:37-48, 7:37-48, 8:26-48, 9:26-48, 10:26-48, 11:26-48, 12:26-48, 13:26-48]
kitchen: [('C', 0), ('P', 2), ('S', 3), ('W', 1)]
[8:12-25, 9:11-25, 10:10-25, 11:9-25, 12:10-25, 13:11-25]
hallway: [('C', 2), ('P', 1), ('S', 0), ('W', 1)]
[1:49-59, 2:49-59, 3:49-59, 4:49-59, 5:49-59, 6:49-59, 7:49-59, 8:49-59, 9:49-59, 10:49-59, 11:49-59, 12:49-59, 13:49-59, 14:49-59, 15:1-59, 16:1-59, 17:1-59, 18:44-59, 19:45-59, 20:46-59, 21:47-59, 22:48-59]
balcony: [('C', 1), ('P', 2), ('S', 1), ('W', 0)]
[19:16-29, 20:16-29, 21:16-29, 22:16-29, 23:16-29, 24:9-29, 25:9-29]