|
from re import match |
|
import matplotlib.pyplot as plt |
|
from matplotlib.patches import Arc |
|
|
|
EMPTY = -1 |
|
WIRE = 0 |
|
SOURCE = 1 |
|
SINK = 2 |
|
|
|
NORTH = 0 |
|
EAST = 1 |
|
SOUTH = 2 |
|
WEST = 3 |
|
|
|
|
|
SCALE = 1 |
|
WIDTH = .25 |
|
CIRCRAD = .25 |
|
|
|
|
|
HLF = WIDTH/2 |
|
LW = 50 * WIDTH * SCALE |
|
|
|
def parseDirs(dirs): |
|
if dirs == '': return [] |
|
return list(map(["North","East","South","West"].index,dirs.split(','))) |
|
|
|
def parse(s:str): |
|
s = ''.join(s.split()) |
|
s = match(r'\[(.*)\]',s).group(1) |
|
lines = s.split(']],') |
|
|
|
tiles = [] |
|
|
|
for l in lines: |
|
currline= [] |
|
ts = l.split('],') |
|
for t in ts: |
|
t = t.strip('[]') |
|
if r:=match(r'Wire\[?(.*)',t): |
|
currline.append((WIRE,parseDirs(r.group(1)))) |
|
elif r:=match(r'Source\[?(.*)',t): |
|
currline.append((SOURCE,parseDirs(r.group(1)))) |
|
elif r:=match(r'Sink\[?(.*)',t): |
|
currline.append((SINK,parseDirs(r.group(1)))) |
|
else: |
|
raise Exception("didnt work :/") |
|
tiles.append(currline) |
|
tiles.reverse() |
|
print(tiles) |
|
return tiles |
|
|
|
def join(xs,ys): |
|
return [x + [(EMPTY,[])] + y for x,y in zip(xs,ys)] |
|
|
|
|
|
def rectpos(x, y, d): |
|
return [((x - HLF, y), WIDTH, .5), ((x, y - HLF), .5, WIDTH), ((x - HLF, y - .5), WIDTH, .5), ((x - .5, y - HLF), .5, WIDTH)][d] |
|
|
|
def mktile(tile, pos, wirevalid=True, circlevalid=True, |
|
wirevalidcolor='green', wireinvalidcolor='red', |
|
circlevalidcolor='green', circleinvalidcolor='red' |
|
): |
|
|
|
wirecolor = wirevalidcolor if wirevalid else wireinvalidcolor |
|
circlecolor = circlevalidcolor if circlevalid else circleinvalidcolor |
|
tiletype,ds = tile |
|
x,y = pos |
|
|
|
if tiletype == EMPTY: return |
|
|
|
box = plt.Rectangle((x-.5,y-.5),1,1,color='grey',fill=False, zorder=2) |
|
plt.gca().add_patch(box) |
|
|
|
if not (wirevalid and circlevalid): |
|
plt.gca().add_patch(plt.Rectangle((x-.5,y-.5),1,1,color='red',fill=False, zorder=3, lw=1)) |
|
|
|
|
|
if tiletype == WIRE and len(ds) == 2 and (ds[0] - ds[1]) % 2 == 1: |
|
ds = sorted(ds) |
|
if ds == [0,3]: |
|
plt.gca().add_patch(Arc((x-.5,y+.5),1,1,theta1=270,theta2=360,color=wirecolor, lw=LW)) |
|
elif ds[0] == 0: |
|
plt.gca().add_patch(Arc((x+.5,y+.5),1,1,theta1=180,theta2=270,color=wirecolor, lw=LW)) |
|
elif ds[0] == 1: |
|
plt.gca().add_patch(Arc((x+.5,y-.5),1,1,theta1=90,theta2=180,color=wirecolor, lw=LW)) |
|
elif ds[0] == 2: |
|
plt.gca().add_patch(Arc((x-.5,y-.5),1,1,theta1=0,theta2=90,color=wirecolor, lw=LW)) |
|
|
|
else: |
|
if len(ds) == 1: |
|
circ = plt.Circle((x,y),HLF,color=wirecolor) |
|
plt.gca().add_patch(circ) |
|
|
|
for d in ds: |
|
rect = plt.Rectangle(*rectpos(x,y,d),color=wirecolor) |
|
plt.gca().add_patch(rect) |
|
|
|
if tiletype > 0: |
|
circ = plt.Circle((x,y),CIRCRAD,color=circlecolor) |
|
plt.gca().add_patch(circ) |
|
if tiletype == 2: |
|
circ = plt.Circle((x,y),CIRCRAD/2,color='white') |
|
plt.gca().add_patch(circ) |
|
|
|
def mkgrid(grid): |
|
for y,l in enumerate(grid): |
|
for x,t in enumerate(l): |
|
if t[0] > WIRE: |
|
mktile(t,(x,y), isWireConnected(grid,x,y), sinksource(grid,x,y)) |
|
else: |
|
mktile(t,(x,y), isWireConnected(grid,x,y)) |
|
|
|
directions = [(0,1),(1,0),(0,-1),(-1,0)] |
|
|
|
def isWireConnected(puzzle,x,y): |
|
_,ds = puzzle[y][x] |
|
drs= [directions[d] for d in ds] |
|
return all(0 <= y+dy < len(puzzle) and 0 <= x+dx < len(puzzle[y+dy]) and (d+2)%4 in puzzle[y+dy][x+dx][1] for d,(dx,dy) in zip(ds, drs)) |
|
|
|
def neighbours(puzzle,x,y): |
|
_,ds = puzzle[y][x] |
|
for d in ds: |
|
dx,dy = directions[d] |
|
if 0 <= y+dy < len(puzzle) and 0 <= x+dx < len(puzzle[y+dy]): |
|
yield (x+dx,y+dy) |
|
|
|
def sinksource(puzzle,x,y): |
|
ttype,_ = puzzle[y][x] |
|
found = [(x,y)] |
|
ttype2 = SINK if ttype == SOURCE else SOURCE |
|
assert ttype in [SOURCE,SINK] |
|
|
|
flag = True |
|
while flag: |
|
flag = False |
|
for (x1,y1) in found.copy(): |
|
for (x2,y2) in neighbours(puzzle,x1,y1): |
|
if (x2,y2) in found: |
|
continue |
|
found.append((x2,y2)) |
|
flag = True |
|
if puzzle[y2][x2][0] == ttype2: |
|
return True |
|
return False |
|
|
|
def joined(puzzle,x1,y1,x2,y2): |
|
|
|
_,ds = puzzle[y1][x1] |
|
_,ds2 = puzzle[y2][x2] |
|
diff = (x2-x1,y2-y1) |
|
if diff not in [(0,1),(1,0),(0,-1),(-1,0)]: return False |
|
d = directions.index(diff) |
|
return (d+2)%4 in ds2 and d in ds |
|
|
|
def setup(p, show_axes=False): |
|
plt.figure(figsize=(len(p[0])*SCALE,len(p)*SCALE)) |
|
plt.axes().set_aspect('equal') |
|
plt.gca().xaxis.set_major_locator(plt.MultipleLocator(1)) |
|
plt.gca().yaxis.set_major_locator(plt.MultipleLocator(1)) |
|
plt.gca().get_xaxis().set_visible(show_axes) |
|
plt.gca().get_yaxis().set_visible(show_axes) |
|
mkgrid(p) |
|
plt.axis('scaled') |
|
|
|
def fig(p): |
|
setup(p) |
|
return plt |
|
|
|
def show(p, show_axes=False): |
|
setup(p, show_axes) |
|
plt.show() |
|
|
|
def main(): |
|
inp = input(">>>") |
|
|
|
while inp != "": |
|
show(parse(inp)) |
|
inp = input(">>>") |
|
|
|
if __name__ == '__main__': |
|
main() |
|
|