C #, 530 байт
Повна програма C #, приймає вхід як STDIN в якості одного рядка і виводить один рядок в STDOUT, з заднім числом "".
Це досить довго ... і є занадто багато повторень x / y / z, але я досі не зміг звести це до чогось розумного, і скласти іспит за 2 години, можливо, повернуться до цього завтра.
using Q=System.Console;class P{static void Main(){int q=9,w=0,e=9,r=0,t=9,u=0,i=0,x=0,y=0,z=0,p=0;System.Action V=()=>{z=(int)System.Math.Sqrt(i);p=(x=i-z*z)%2;x/=2;y=(++z*z--+~i)/2;},W=()=>{Q.Write(i+","+(x<0|y++<0|z>7?"X":""+(z*z+2*x+1-p))+" ");};foreach(var g in Q.ReadLine().Split(',')){i=int.Parse(g);V();q=q>x?x:q;w=w<x?x:w;e=e>y?y:e;r=r<y?y:r;t=t>z?z:t;u=u<z?z:u;}for(i=64;i-->0;){V();if(!(x<q|x>w|y<e|y>r|z<t|z>u))if(p>0){if(y==r)W();if(x++==w)W();x--;if(z--==t)W();}else{if(y--==e)W();if(x--==q)W();x++;if(z++==u)W();}}}}
Ця діаграма пояснює більшість того, що відбувається.
Визнайте, що через те, що ми не можемо мати секції 0-ширини, "шестикутник" завжди буде найдешевшою формою (і має перевагу надавати гусям максимум місця для пересування).
Програма працює, спочатку переводячи всі індекси вхідних комірок у x / y / z координати та знаходячи min / max кожного з x / y / z.
z = floor(root(i))
x = floor((i - z^2) / 2)
y = floor((z+1)^2 - i - 1) / 2)
p = (i - z^2) % 2
Далі він проходить кожен індекс клітинки та перевіряє, чи відповідає він описаному нами шестикутнику. Якщо це так, він перевіряє, чи є він на будь-якому з крайніх країв меж (тобто x = xmin, або y = ymax) і додає відповідні ребра, якщо вони є. Він повинен опрацювати індекс краю, який знаходиться поруч. Для x і z ми просто збільшуємо / зменшуємо їх, скільки хочемо, а потім використовуємо таку формулу:
i = z^2 + 2*x + (1-p)
Зауважте, що "паритет" завжди змінюється, і що y не бере участь. Так, нам не потрібно нічого змінювати, але код трохи заплутався, оскільки він повинен виконувати "трикутникові" межі, перевіряючи, щоб виявити, чи має сусідня комірка "X" чи ні.
Приклад розчину (клітини з гусей прямо з трьох куточків):
Input
2,50,62
Output
62,63 61,X 59,X 57,X 55,X 53,X 51,X 50,49 48,X 36,X 35,X 25,X 24,X 16,X 15,X 9,X 8,X 4,X 3,X 2,0 1,X
Код Тідьє з коментарями:
using Q=System.Console;
class P
{
static void Main()
{
int q=9,w=0,e=9,r=0,t=9,u=0, // min/max x/y/z/ (init min high, and max low)
i=0, // index of cell we are looking at
x=0,y=0,z=0,p=0; // x,y,z dimension
System.Action V=()=>
{ // translates the index into x/y/z/p
z=(int)System.Math.Sqrt(i);
p=(x=i-z*z)%2; // 'parity'
x/=2; // see p assignment
y=(++z*z--+~i)/2; // ~i == -i - 1
},
W=()=>
{ // writes out the edge of i, and the cell described by x/z/inverse of p (the inversion of p handles y +/-)
Q.Write(i+","+ // write out the edge
(x<0|y++<0|z>7?"X":""+(z*z+2*x+1-p)) // either X (if we go out of 'trianlge' bounds), or we translate x/z/inverse of p into an index
+" "); // leaves a trailing space (as shown in example output)
};
foreach(var g in Q.ReadLine().Split(',')) // for each cell with geese
{
i=int.Parse(g); // grab index of cell
V(); // compute x/y/z/p
q=q>x?x:q; // sort out mins/maxes
w=w<x?x:w;
e=e>y?y:e;
r=r<y?y:r;
t=t>z?z:t;
u=u<z?z:u;
// code like the above suggests a solution with a couple of arrays would be better...
// I've not had success with that yet, but maybe in a couple of days I will try again
}
for(i=64;i-->0;) // for each cell
{
V(); // compute x/y/z/p
if(!(x<q|x>w|y<e|y>r|z<t|z>u)) // if we are inside the 'hex' bounds
if(p>0)
{ // x max, y max, z min
// these checks check that we are on the extremes of the 'hex' bounds,
// and set up the appropriate vars for W calls to put the edges in
// must do y first, because W modifies it for us (saves 2 bytes in the next block)
if(y==r) // don't need the ++ (can't go out of 'trianlge' bounds)
W();
if(x++==w)
W();
x--;
if(z--==t)
W();
//z++; not used again
}
else
{ // x min, y min, z max
if(y--==e) // do need the -- (used for 'trianlge' bounds checking)
W();
// y is reset in W, as such
if(x--==q)
W();
x++;
if(z++==u)
W();
//z--; not used again
}
}
}
}