Codegolf - Нанизывание Жемчужного Ожерелья

  • Автор темы Padeevano72
  • Обновлено
  • 23, Oct 2024
  • #1

Обзор

Жемчуг (или Масю) это логическая игра, в которую играют по сетке. На сетке размещены черные и белые жемчужины. Цель состоит в том, чтобы сформировать одинарный, замкнутый контур который проходит через каждую жемчужину, используя только сегменты прямых линий и прямые углы.

Есть некоторые правила, регулирующие взаимодействие петли с жемчугом:

  • Белый жемчуг нужно путешествовать прямой через, но цикл должен повернуть в предыдущем и/или следующую ячейку на своем пути.
  • Черный жемчуг должен быть повернулся но петля должна пройти прямой через следующий и предыдущие ячейки на своем пути.
  • Петля должна нет пересекать или иным образом пересекать себя. Все ячейки имеют ровно ноль или два входа/выхода цикла.

Пример головоломки из Википедии (и ее решение):

codegolf - Нанизывание жемчужного ожерелья codegolf - Нанизывание жемчужного ожерелья

Ваша цель — решить заданную головоломку. Если существует несколько возможных решений, не имеет значения, какое из них вы предложите.

Вход

Ввод будет нерешённым квадрат сетка. Пример, показанный выше, будет выглядеть так:

 
 
 
 
 
 
 
 
 
 .....w.b.w..
ww..b...b...
.w.....b....
...wbww..b.b
....b.......
w.w.........
..w......b.b
.....bb.....
.....b.....w
w.ww..b.....
...w......w.
b..w.....b..
 

.wb..b ...... ..b... w.ww.. ...... b....b 0 0 2 0 2 2 4 2 4 1 3 1 3 0 5 0 5 5 3 5 3 4 4 4 4 3 1 3 1 4 2 4 2 5 0 5 0 2 1 2 1 1 0 1 is a white pearl, ... w.. ..b 0 0 1 0 2 0 2 1 2 2 1 2 0 2 0 1 это черная жемчужина, и Paste grid input here<br><input id="grid" type="textarea" size="80" oninput='drawGrid()' /><br><br>Paste path output here<br><input id="coords" type="textarea" size="80" oninput='drawCoords()' /><br><br><div id="output"></div> is an empty cell.

Предположим, что введенные данные верны. Это значит, что оно правильно сформировано и возможно хотя бы одно решение. Все допустимые головоломки имеют размер не менее 3х3 и содержат хотя бы одну жемчужину.

Выход

Выходные данные — это строка координат, представляющая путь. Левый верхний угол сетки svg{position: absolute;left: 50px;} , upper right is function draw(){document.getElementById("output").innerHTML=gridSVG+pathSVG}function drawCoords(){coords=document.getElementById("coords").value;var e=prefix+'<path fill="none" d="';if(nums=coords.match(/[^\s]+/g),!(nums.length<2)){for(e+="M "+(nums[0]*scale+offset)+" "+(nums[1]*scale+offset),i=2;i<nums.length;i+=2)e+=" L "+(nums[i]*scale+offset)+" "+(nums[i+1]*scale+offset);e+=" L "+(nums[0]*scale+offset)+" "+(nums[1]*scale+offset),e+='" stroke="black" stroke-width="2"/>'+suffix,pathSVG=e,draw()}}function drawPath(){path=document.getElementById("path").value;var e=prefix+'<path fill="none" d="'+path+'" stroke="black" stroke-width="2"/>'+suffix;pathSVG=e,draw()}function drawGrid(){grid=document.getElementById("grid").value;var e=prefix;for(lines=grid.match(/[^\s]+/g),width=lines[0].length*scale,height=lines.length*scale,i=0;i<=lines.length;i++)e+='<path stroke="gray" d="M 0 '+i*scale+" L "+width+" "+i*scale+'"/>',e+='<path stroke="gray" d="M '+i*scale+" 0 L "+i*scale+" "+height+'"/>';for(j=0;j<lines.length;j++)for(line=lines[j],i=0;i<line.length;i++)"w"==line.charAt(i)&&(e+='<circle cx="'+(i*scale+offset)+'" cy="'+(j*scale+offset)+'" r="'+.8*offset+'" stroke="black" fill="white"/>'),"b"==line.charAt(i)&&(e+='<circle cx="'+(i*scale+offset)+'" cy="'+(j*scale+offset)+'" r="'+.8*offset+'" stroke="black" fill="black"/>');e+=suffix,gridSVG=e,draw()}var prefix='<svg height="400" width="400">',suffix="</svg>",scale=30,offset=scale/2,pathSVG="",gridSVG=""; , где н это ширина сетки.

Путь — это просто серия упорядоченных координат:

1 0 2 0 3 0 4 0 5 0 5 1 ...

Предполагается, что путь закрыт, поэтому вы не нуждаться повторить первую координату в конце, но за это нет штрафа.

Вывод должен состоять из по меньшей мере все углы на пути. Вам не обязательно выводить каждую ячейку пути, если нет поворота. Например, вывод примера может начинаться с:

1 0 5 0 5 1 ...

или

x1 y1 x2 y2 x3 y3 ...

Вывод должен нет содержать любую ячейку, не входящую в путь. Вы можете начать с любой ячейки пути.


Фрагмент

Вот фрагмент, который вы можете использовать для визуализации вашего решения. Просто вставьте сетку, над которой вы работаете, и путь, который вы выводите. Я понимаю, что смотреть на мой код больно, поэтому советую вам этого не делать ;)

n-1 0 0 0 .

Тестовые случаи

Эти тестовые примеры показывают один возможный результат для каждого входа (кроме последнего, который показан нерешенным). Могут быть и другие допустимые пути: вы можете пойти по часовой стрелке или против часовой стрелки, начать с другой точки и т. д. Решения должны позволять решать тестовые случаи за секунды/минуты/часы, а не за дни/недели/эоны.

codegolf - Нанизывание жемчужного ожерелья

b

codegolf - Нанизывание жемчужного ожерелья

w

codegolf - Нанизывание жемчужного ожерелья

..w.w..... ....w...b. ..b.b.w... ...w..w... b....w...w ..w....w.. ..b...w... w...b....w ......ww.. ..b......b

#код-гольф #головоломка-решатель #сетка

Padeevano72


Рег
25 Oct, 2009

Тем
72

Постов
221

Баллов
601
  • 26, Oct 2024
  • #2

Питон - 1669 г.

Все еще довольно долго, но достаточно быстро, чтобы последний пример можно было запустить на моем компьютере менее чем за секунду. Вероятно, можно сделать короче за счет скорости, но на данный момент это во многом эквивалентно коду без гольфа.

Пример вывода для последнего тестового примера:

 
 
 
 
 
 b={}s={0,0,0}R=table.insert Z=unpack for l in io.lines()do w=#l for i=1,w do
c=({b=1,w=2,['.']=3})[l:sub(i,i)]R(b,c)s[c]=s[c]+1 end end h=#b/w for e=0,w*h-1
do function g(p,d,X,t)local function G(I,r)T={Z(t)}a=b[I+1]T[a]=T[a]+1
P={Z(p)}D={Z(d)}R(P,I%w)R(P,I/w-I/w%1)R(D,r)l=#D for U=2,#p,2 do if
I==p[U-1]+w*p[U]then return end end if I==e then if T[1]==s[1]and T[2]==s[2]then
for k=1,l do K=D[k]M=D[(k-2)%l+1]N=D[k%l+1]O=D[(k+1)%l+1]if({K==N or K~=M or
N~=O,K~=N or(K==M and N==O)})[b[1+P[2*k-1]+w*P[2*k]]]then return end end
os.exit(print(table.concat(P,' ')))end else g(P,D,I,T)end end _=X%w<w-1 and
G(X+1,1)_=X/w-X/w%1<h-1 and G(X+w,2)_=X%w>0 and G(X-1,3)_=X/w-X/w%1>0 and
G(X-w,4)end g({},{},e,{0,0,0})end
 

Код:

.b.....b.b.......b.. .....w.....b.w....w. ....w.........w..... ..bb.....w.w.b....b. .w.....b..b......w.. .....b.............. .b..........b.b..bw. ....w....w....b...w. .......bb...b...w... ..b.......w......... ....b.w.....w.b...b. w...b...w..b.w.w.... b.w....w............ ...b.w......b..b...b w......w.b.ww....... .b....b..........b.. ....b....w.bb.w...w. w..b......w...b..... b.....w.........w... ...b....w..w..b...w. ...................b .b..w..bb.b..b..w... ........w......b.... b....w......b..b.b.. ...b..bb.w.w........ ...b.......w......w. w...w.b.w.....b....b ............w..ww... ..b.b..b....b....... ....b.........w...b. .ww.......b.w.w..... b.....w..w.w...b.... ....ww..b.b.w....w.w .............bb..w.. .b....w.b.b........w ....bw..........b...

Негольфед:

#define W 40 int Y,X,T,P,Q[W*W],D[]={-W,-1,1,W};char B[W*W],K[W*W],I[W]; t(x,d,s){for(P=0;B[x];x*=x!=*Q)s-=K[Q[P++]=x]-1, d=(54202126527627840ll>>2*(d*7+B[x+=D[d]]%8))&3;return x?0:s;} m(x){int c=K[x],u=B[x-W],U=u&7,l=B[x-1],L=l&7,r=0, i=U!=3&U!=4&L!=2&L!=4,o=(39>>U)&1?57:70;o&=(52>>L)&1?42:85; if(x/W>Y+1){for(;P--;)printf("%d %d ",Q[P]%W-1,Q[P]/W-1);exit(0);} if(u>7)o&=U>4?~64:~6;if(l>7)o&=L>4?~32:~10; for(o&=c?c>1?c>2?(r=8*i,96):(r=8,i*30):~0:1;o;r++,o/=2) if(o&1)if(B[x]=r,r%8!=1||!t(x,0,T))m(x+1);B[x]=0;} main(){for(;gets(I);Y++)for(X=0;I[X];X++) T+=(K[X+1+Y*W+W]=I[X]/36)-1;m(W);} ||answer||

С, 590 640 760 880 963 1018

Грубая сила для этого достаточно быстра. Тест 12x12 выполняется менее 10 мс. Зная это, можно было бы выбрать какой-нибудь язык, более подходящий для игры в гольф.

Я не предполагаю, что доска квадратная, поскольку большие головоломки, как правило, не квадратные.

W - 2 define sets the limit on the board dimensions. The actual limit is smaller W поскольку я использую дополнительные строки для границ, чтобы избежать проверки границ.

class Grid: def __init__(self,input): self.input = input.split('\n') self.x = len(self.input[0]) self.y = len(self.input) self.options = {'.':{0,1,2,3,4,5,6},'w':{1,2},'b':{3,4,5,6}} def convert(self,grid): directions = [None,{0,2},{1,3},{2,3},{0,3},{0,1},{1,2}] for y in range(self.y): for x in range(self.x): if grid[y][x] != 0: break chain = [] start_pos = (x,y) dir = -1 pos = start_pos while dir == -1 or pos != start_pos: chain.extend(pos) x,y = pos next_dir = (directions[grid[y][x]]-{(dir+2)%4}).pop() if next_dir == 0: nx,ny = x+1,y elif next_dir == 1: nx,ny = x,y-1 elif next_dir == 2: nx,ny = x-1,y elif next_dir == 3: nx,ny = x,y+1 dir = next_dir pos = (nx,ny) return chain def solve(self,grid=None,chain_ids=None,pos=(0,0)): x,y = pos if y >= self.y: return None if grid is None: grid = [[-1]*self.x for i in range(self.y)] if chain_ids is None: chain_ids = [[-1]*self.x for i in range(self.y)] options = self.options[self.input[y][x]].copy() if y == 0 or grid[y-1][x] in [0,1,5,6]: options &= {0,1,3,4} else: options &= {2,5,6} if y == self.y-1: options &= {0,1,5,6} if x == 0 or grid[y][x-1] in [0,2,3,6]: options &= {0,2,4,5} else: options &= {1,3,6} if x == self.x-1: options &= {0,2,3,6} if y != 0 and grid[y-1][x] in [2,3,4]: if self.input[y][x] == 'b' and grid[y-1][x] != 2: return None if self.input[y-1][x] == 'b': options &= {2} elif self.input[y-1][x] == 'w': if grid[y-2][x] == 2: options &= {5,6} if x != 0 and grid[y][x-1] in [1,4,5]: if self.input[y][x] == 'b' and grid[y][x-1] != 1: return None if self.input[y][x-1] == 'b': options &= {1} elif self.input[y][x-1] == 'w': if grid[y][x-2] == 1: options &= {3,6} new_chain_ids = [[i for i in row] for row in chain_ids] if y != 0 and grid[y-1][x] in [2,3,4]: if x != 0 and grid[y][x-1] in [1,4,5]: if chain_ids[y-1][x] == chain_ids[y][x-1]: if 6 not in options: return None if any(any(i != chain_ids[y-1][x] and i != -1 for i in row) for row in chain_ids) or \ any(self.input[v][u] != '.' and (v!=y or u>x) for v in range(y,self.y) for u in range(self.x)): return None grid[y][x] = 6 for v in range(y,self.y): for u in range(self.x): if v != y or u > x: grid[v][u] = 0 return self.convert(grid) else: for v in range(y+1): for u in range(self.x): if new_chain_ids[v][u] == chain_ids[y][x-1]: new_chain_ids[v][u] = chain_ids[y-1][x] new_chain_ids[y][x] = chain_ids[y-1][x] else: new_chain_ids[y][x] = chain_ids[y-1][x] elif x != 0 and grid[y][x-1] in [1,4,5]: new_chain_ids[y][x] = chain_ids[y][x-1] else: new_chain_ids[y][x] = max(max(row) for row in chain_ids)+1 for n in sorted(list(options),key=lambda n: -n): grid[y][x] = n if n == 0: new_chain_ids[y][x] = -1 if x == self.x-1: nx,ny = 0,y+1 else: nx,ny = x+1,y result = self.solve(grid,new_chain_ids,(nx,ny)) if result is not None: return result input = """ .....w.b.w.. ww..b...b... .w.....b.... ...wbww..b.b ....b....... w.w......... ..w......b.b .....bb..... .....b.....w w.ww..b..... ...w......w. b..w.....b.. """.strip() def print_grid(grid): for y,row in enumerate(grid): s = "" for i in row: s += {-1:'xxx',0:' ',1:' ',2:' | ',3:' ',4:' ',5:' | ',6:' | '}[i] s += '\n' for x,i in enumerate(row): s += {-1:'x%sx',0:' %s ',1:'-%s-',2:' %s ',3:'-%s ',4:' %s-',5:' %s-',6:'-%s '}[i] % input.split('\n')[y][x] s += '\n' for i in row: s += {-1:'xxx',0:' ',1:' ',2:' | ',3:' | ',4:' | ',5:' ',6:' '}[i] s += '\n' print s result = Grid(input).solve() print result

Тест мне.

Вот более сложный тестовый пример:

I=raw_input().split('\n');X=len(I[0]);Y=len(I);R=range def S(g=0,c=0,x=0,y=0): if y>=Y:return 0 if g==0:g=[[-1]*X for i in R(Y)];c=[[-1]*X for i in R(Y)] o={'.':set(R(7)),'w':{1,2},'b':{3,4,5,6}}[I[y][x]].copy() o&={0,1,3,4}if y<1 or g[y-1][x]in[0,1,5,6]else{2,5,6} o&={0,2,4,5}if x<1 or g[y][x-1]in[0,2,3,6]else{1,3,6} if y>Y-2:o&={0,1,5,6} if x>X-2:o&={0,2,3,6} if y>0 and g[y-1][x]in[2,3,4]: if'b'==I[y][x]and g[y-1][x]!=2:return 0 if'b'==I[y-1][x]:o&={2} elif'w'==I[y-1][x]and g[y-2][x]==2:o&={5,6} if x>0 and g[y][x-1]in[1,4,5]: if'b'==I[y][x]and g[y][x-1]!=1:return 0 if'b'==I[y][x-1]:o&={1} elif'w'==I[y][x-1]and g[y][x-2]==1:o&={3,6} h=[r[:]for r in c] if y>0 and g[y-1][x]in[2,3,4]: if x>0 and g[y][x-1]in[1,4,5]: if c[y-1][x]==c[y][x-1]: if(6 not in o)+any(any(i!=c[y-1][x]and i!=-1 for i in r)for r in c)+any(I[v][u]!='.'and(v>y)+(u>x)for v in R(y,Y)for u in R(X)):return 0 g[y][x]=6 for v in R(y,Y): for u in R(X): if v!=y or u>x:g[v][u]=0 for y in R(Y): for x in R(X): if g[y][x]>0:break f=[];d=-1;u,v=p,q=x,y while(u,v)!=(p,q)or-1==d:f+=[u,v];d=([0,{0,2},{1,3},{2,3},{0,3},{0,1},{1,2}][g[v][u]]-{(d+2)%4}).pop();i,j={0:(u+1,v),1:(u,v-1),2:(u-1,v),3:(u,v+1)}[d];u,v=i,j return f else: for v in R(y+1): for u in R(X): if h[v][u]==c[y][x-1]:h[v][u]=c[y-1][x] h[y][x]=c[y-1][x] else:h[y][x]=c[y-1][x] elif x>0 and g[y][x-1]in[1,4,5]:h[y][x]=c[y][x-1] else:h[y][x]=max(max(r)for r in c)+1 for n in sorted(list(o))[::-1]: if n==0:h[y][x]=-1 if x>X-2:i,j=0,y+1 else:i,j=x+1,y g[y][x]=n;r=S(g,h,i,j) if r!=0:return r return 0 for i in S():print i,

Мне повезло, что мой код находит решение довольно рано (<5 минут), но полный поиск занимает гораздо больше времени (67 минут).

 

Vodamk


Рег
24 Sep, 2011

Тем
70

Постов
179

Баллов
579
  • 26, Oct 2024
  • #3

Луа, 830 810 765 752 739 729 710

Плата1 и плата2 работает нормально, но на плате3 требуется некоторое время.

Он мог бы сэкономить 9 байт, выведя каждый путь, а не только первый...

0 11 1 11 2 11 3 11 4 11 4 10 3 10 2 10 1 10 1 9 2 9 3 9 4 9 4 8 3 8 3 7 4 7 5 7 5 6 5 5 6 5 6 6 6 7 7 7 8 7 8 8 7 8 6 8 5 8 5 9 5 10 5 11 6 11 6 10 6 9 7 9 8 9 8 10 7 10 7 11 8 11 9 11 9 10 9 9 10 9 10 10 10 11 11 11 11 10 11 9 11 8 11 7 10 7 10 8 9 8 9 7 9 6 10 6 11 6 11 5 11 4 11 3 10 3 9 3 9 4 9 5 8 5 8 4 8 3 8 2 8 1 9 1 10 1 10 0 9 0 8 0 7 0 7 1 7 2 6 2 5 2 5 1 6 1 6 0 5 0 4 0 3 0 2 0 2 1 3 1 4 1 4 2 4 3 5 3 6 3 7 3 7 4 6 4 5 4 4 4 4 5 4 6 3 6 3 5 3 4 3 3 3 2 2 2 2 3 1 3 1 2 1 1 1 0 0 0 0 1 0 2 0 3 0 4 0 5 0 6 1 6 1 5 1 4 2 4 2 5 2 6 2 7 1 7 1 8 0 8 0 9 0 10
 

Kakadyshka


Рег
06 Nov, 2011

Тем
67

Постов
189

Баллов
614
Тем
403,760
Комментарии
400,028
Опыт
2,418,908

Интересно