Adjacence hexagonale


28

Exemple de spirale hexagonale

L'image ci-dessus montre une grille hexagonale d'hexagones. Chaque cellule de la grille se voit attribuer un index, en partant du centre et en spirale dans le sens antihoraire comme illustré. Notez que la grille continuera indéfiniment - l'image ci-dessus est simplement la première section. L'hexagone suivant serait adjacent à 60 et 37.

Votre tâche consiste à déterminer si deux cellules données sur cette grille sont adjacentes.

Écrivez un programme ou une fonction qui, étant donné deux indices de cellule, imprime / renvoie une valeur véridique si les deux cellules sont adjacentes, et une valeur de falsey dans le cas contraire.

S'il n'est pas limité par des raisons pratiques, votre code devrait théoriquement fonctionner pour toutes les entrées de taille.

Cas de test véridiques:

0, 1
7, 18
8, 22
24, 45
40, 64
64, 65

Cas de test Falsey:

6, 57
29, 90
21, 38
38, 60
40, 63
41, 39
40, 40

Il s'agit de donc la réponse la plus courte en octets l'emporte. Des explications, même pour les langues non ésotériques, sont encouragées.

Réponses:


7

Élixir , 263 257 264 223 214 218 214 214 octets

a=fn x,y->i=&(&1*(&1-1)*3+1)
[x,y]=Enum.sort [x,y]
if x<1,do: y in 1..6,else: (y-x==1||fn->a=y-trunc i.((r=(:math.sqrt(12*x-3)+3)/6)+1)
t=trunc r
a in [0,1,rem(b=x-i.(t)+1, t)<1&&b-t*6!=0&&2]||b<2&&a==-1 end.())end

Essayez-le en ligne!

version non golfée

def get_ring(x) do
    1/6*(:math.sqrt(12*x-3)+3)
end

def inv_get_ring(x), do: x*(x-1)*3+1

def ring_base(x), do: inv_get_ring(trunc(x))

def is_corner(x) do
    ring = trunc(get_ring(x))
    inv_ring = ring_base(ring)
    stuff = (x-inv_ring+1)
    rem(stuff, ring) == 0
end

def is_last(x),do: ring_base(get_ring(x)+1)-1 == x
def is_first(x),do: ring_base(get_ring(x)) == x

def hex_adj(x, y) do
    {x, y} = {min(x,y), max(x,y)}
    cond do 
        x == 0 ->y in 1..6      
        y-x==1 -> true
        true ->
            adj = trunc(inv_get_ring(get_ring(x)+1))
            number = if is_corner(x)&&!is_last(x), do: 2, else: 1
            if y-adj in 0..number do
                true
            else
                is_first(x) && y == adj-1
            end
    end
end
  • trunc(number) Renvoie la partie entière du nombre
  • rem(a,b) Renvoie le reste de a / b
  • cond do end Cela équivaut à des clauses else if ou switch dans de nombreux langages impératifs

Explication

get_ring (index)

Calcule la "sonnerie" de l'index.

Exemple: 1 pour 1-6, 2 pour 7-18, etc.

Cela ne s'applique que si le résultat est floorédité. Les chiffres de fin représentent la distance de cette tuile autour de l'anneau.

inv_get_ring (anneau)

Calcule l'inverse de get_ring(index).

ring_base (ring)

Calcule l'indice de la première tuile de l'anneau.

is_corner (index)

Les coins sont des carreaux qui ont trois carreaux adjuvants dans l'anneau extérieur. L'anneau le plus intérieur se compose entièrement de coins.

Exemples: 21,24,27,30,33,36

is_last (index)

Est vrai si cet indice est le plus élevé de son anneau.

is_first (index)

Est vrai si c'est la tuile de base de l'anneau.


2
J'ai édité la réponse pour inclure un correctif au cas de bord :)
Garuno

J'ai suivi votre version golfée au cours des premières itérations, mais il semble que vous ayez changé votre approche. Votre version golfée actuelle est-elle toujours équivalente à la version non golfée?
John Michael Law

Oui, ça l'est! Je viens d'apprendre que vous pouvez déclarer des variables en ligne dans Elixir. Cela m'a donné la possibilité de me débarrasser des fonctions lambda au début du code. J'ai juste mélangé un peu les variables, pour qu'elles soient plus efficaces.
Garuno

5

MATL , 47 45 44 43 41 octets

s:"JH3/^6:^t5)w5:&)@qY"w@Y"]vYs0hG)d|Yo1=

Essayez-le en ligne! Ou vérifiez tous les cas de test .

En prime, le code génère une spirale hexagonale qui trace les positions des centres cellulaires, ce qui peut être vu graphiquement sur MATL Online en modifiant la dernière partie du code.

Explication

Idée générale    Le code construit d'abord une spirale hexagonale suffisamment grande avec un pas unitaire. La spirale est définie comme un vecteur de nombres complexes représentant les positions des centres cellulaires. L'indexation dans ce vecteur avec les nombres d'entrée et le calcul de la différence absolue donne la distance entre les deux cellules indiquées. Les cellules sont adjacentes si et seulement si le résultat est 1. Cependant, en raison d'inexactitudes en virgule flottante, l'arrondi est nécessaire avant de comparer avec 1.

Construire la spirale    La spirale aura un nombre de "couches" égal à la somme des deux entrées. Ceci est (beaucoup) plus grand que nécessaire et garantit que les cellules d'entrée seront présentes dans la spirale.

Pour construire la spirale, le nombre complexe j 2/3 (où j est l'unité imaginaire) est d'abord calculé. Augmenter cela aux exposants 1 à 6 donne un ensemble de base de déplacements, de sorte que suivre ces déplacements dans l'ordre tracerait un hexagone. Cet hexagone formerait la couche la plus intérieure de la spirale, sauf qu'il serait "fermé". En fait, nous voulons que cet hexagone "grandisse" à la dernière étape, puis nous traçons un hexagone plus grand, avec deux fois plus de points (alignés en groupes de deux), pour former la couche suivante de la spirale; voir l'illustration ici . La couche suivante aura trois fois plus de points que la première (en groupes de trois); voir ici .

Pour ce faire, le cinquième déplacement par rapport à l'ensemble de base (qui pointe dans la direction sud-est) est choisi comme étape "croissante". La couche k commence par cette étape, suivie des cinq premières étapes de base répétées k fois, suivies par la sixième étape (direction est) répétée k -1 fois. J'espère que cela deviendra plus clair en examinant les deux chiffres liés ci-dessus.

Le vecteur résultant, y compris toutes les couches, représente les déplacements complexes qui traceraient la spirale. La somme cumulée donne les coordonnées réelles des centres cellulaires.

Enfin, la cellule initiale, située à 0, est attachée à l'extrémité de ce vecteur. Cela est dû au fait que MATL utilise l'indexation modulaire basée sur 1 et que l'index 0 fait référence à la dernière entrée d'un tableau.

Test d'adjacence    Les deux cellules données par les nombres en entrée sont sélectionnées, leurs coordonnées sont soustraites et la valeur absolue est arrondie et comparée à 1.

Code commenté

s         % Implicitly input array of two numbers. Push their sum, say S
:         % Range [1 2 ... S]
"         % For each k in [1 2 ... S]
  J       %   Push 1j
  H3/     %   Push 2, then 3, then divide: gives 2/3
  ^       %   Power
  6:      %   Push [1 2 ... 6]
  ^       %   Element-wise power. This is the array of 6 basic displacements
  t5)     %   Duplicate. Get 5th entry
  w5:&)   %   Swap. Push subarray with entries 1st to 5th, then push 6th
  @qY"    %   Repeat the 6th displacement k-1 times
  w@Y"    %   Swap. Repeat 1st to 5th displacements k times
]         % End
v         % Concatenate everything into a column vector
Ys        % Cumulative sum. This gives the cell center coordinates
0h        % Append a 0
G)        % Index by the input vector
d|        % Absolute difference
Yo        % Round to nearest integer
1=        % Does it equal 1? Implicitly display

Pourriez-vous ajouter une explication?
Shaggy

@Shaggy J'ai ajouté une explication générale. Faites-moi savoir si c'est clair (c'est difficile à expliquer). J'ajouterai le code commenté plus tard
Luis Mendo

2

05AB1E (hérité) , 30 29 27 octets

α2‹i1q}1ݹ+v12y*3-tîÌy+}Ÿ²å

Essayez-le en ligne!

Explication du code:

α2‹i1q}                     : if the absolute diff of the two number is 1 or 0 return 1
                          ²å: return that the second number is in
                         Ÿ  : range of {
       1Ý                   :  create [0, 1]
         ¹+                 :  add the first number to the elements
           v            }   :  map that list
            12y*3-tîÌy+     :  calculate the corresponding value where it's an adjacency
                                }

Explication des mathématiques:

J'ai "perdu" environ 5 heures à faire ce golf. En bref, j'ai commencé à faire un graphique 2D des entrées et à dessiner Xoù elles étaient adjacentes les unes aux autres. Ensuite, j'ai trouvé un motif. Je l'ai cherché sur OEIS et bingo! J'ai trouvé cette séquence et j'ai utilisé la formule indiquée sur le site Web.


1

C (gcc) , 175 173 bytes

Merci à Peter Taylor d' avoir attrapé un bug.

Merci à plafondcat pour -2 octets. Cet opérateur ~ reste mon principal angle mort.

c,r,C,L;y(a){a=a<L*2?L-a:a<L*3?-L:a<5*L?a-L*4:L;}z(a){L=ceil(sqrt(a/3.+.25)-.5);C=y(a-=3*L*~-L);L?L=y((L+a)%(L*6)):0;}f(a,b){z(a);c=C,r=L;z(b);a=a-b&&(abs(c-C)|abs(r-L))<2;}

Essayez-le en ligne!

Cette approche vise à trouver la ligne et la colonne des deux cellules et à les comparer; les voisins ne peuvent pas voir leurs coordonnées correspondantes différer de plus de 1. En se déplaçant du centre vers l'extérieur, nous observons que chaque couche a 6 cellules de plus que la précédente. Cela signifie que l '"indice" le plus élevé dans chaque couche L est sur la forme 6 * (L * (L - 1) * (L - 2) ...), ou C = 6 * (L 2 + L) / 2 , où C est le numéro de cellule "global". En mélangeant les choses, nous obtenons L 2 + L - C / 3 = 0, ce qui donne des flashbacks mathématiques au lycée. De cela, nous obtenons la formule ceil (sqrt (1/4 / C / 3) + 0,5). En y branchant un index de cellule global, nous recevons la couche dans laquelle se trouve la cellule.

Étant donné que la première cellule de chaque couche est naturellement supérieure à la plus haute de la couche précédente, nous trouvons L start = (6 * (L - 1) 2 + (L - 1)) / 2, ce qui simplifie à 3 * (L 2 - L). De là, nous obtenons l'indice de couche L index = C - L start .

Ensuite, nous voyons que chaque couche est composée de six sections, chacune de longueur L. En partant du nord-est et dans le sens antihoraire, nous voyons que pour les deux premières sections (1 <= indice L <= 2 * L) , nous obtenons la colonne de l - l ' indice . La section suivante L * 2 <L index <= L * 3 a toutes les cellules partageant une seule colonne -L. Les deux sections suivantes sont L * 3 < index L <= L * 5 avec leurs colonnes selon l' indice L - L * 4. Enfin, la sixième section a toutes ses cellules sur la colonne L. Nous pouvons déplacer les limites supérieures d'une étape pour enregistrer quelques octets dans le code.

Alors qu'en est-il des rangées? Pour réutiliser le code, nous tournons la grille de sorte que la cellule 44 soit droite. Ensuite, nous exécutons la même logique que pour les colonnes mais appelons les résultats "lignes" cette fois-ci. Bien sûr, au lieu de tourner une grille, nous faisons juste 1/6 de tour autour d'elle.


@PeterTaylor Bonne prise, merci!
gastropner

1

Python 3, 150 octets

def h(a,b):
 L=[];i=1
 while len(L)<a+b:r=sum((i*[1j**(k/3)]for k in range(4,16,2)),[]);r[0]+=1;L+=r;i+=1
 return.9<abs(sum(L[min(a,b):max(a,b)]))<1.1

Ma solution suit fondamentalement la même ligne de pensée que celle de Luis Mendo ci-dessus. S'il est écrit plus lisible, le code est assez explicite:

def h(a,b):
    L=[]
    i=1
    while len(L)<a+b:
        l=sum((i*[1j**(k/3)]for k in range(4,16,2)),[])
        l[0]+=1
        L+=l
        i+=1
return .9<abs(sum(L[min(a,b):max(a,b)]))<1.1
  1. fonctionne hcomme suit:
  2. La liste L contiendra les positions (complexes) de chaque nombre.
  3. i est le numéro de sonnerie.
  4. Dans la boucle while, un nouvel anneau est ajouté à chaque itération. Au lieu de déterminer le nombre d'anneaux dont nous avons besoin, nous continuons simplement à construire la liste jusqu'à ce qu'elle soit suffisamment longue pour contenir a + b, puis elle est certainement assez longue pour contenir l'un ou l'autre.
  5. la 'ring-list' lest une concaténation de 6 listes de len (i) fois le pas-vecteur, où le pas-vecteur est 1j ** (2/3) à une certaine puissance. La plage ne commence pas à 0 mais à 4, ce qui provoque une rotation de toute la grille. Cela me permet de faire:
  6. l[0]+=1 à la ligne 6, qui est le pas d'un anneau au suivant.
  7. L+=l concatène la liste complète et la liste des sonneries.
  8. La liste L ne contient que des vecteurs de pas, qui doivent encore être additionnés (intégrés) pour obtenir une position. Une fonctionnalité intéressante ici est que nous pouvons simplement additionner la tranche du nombre le plus bas au plus élevé pour obtenir leur distance! En raison d'erreurs d'arrondi, le résultat ne sera pas exactement 1, d'où le .9 <... <1.1. Fait intéressant, le cas zéro h(0,0)ou h (0,1) est pris en charge implicitement, car la somme d'une liste vide est nulle. Si je pouvais être sûr que a<b, c'est-à-dire que les arguments viendraient dans l'ordre croissant, je pourrais raser encore 14 octets en remplaçant L[min(a,b):max(a,b)]par L[a:b], mais hélas!

PS: je ne savais pas que c'était un vieux défi, il est apparu il y a quelques jours et n'a cessé de me harceler depuis :)


C'est une excellente réponse! Ne vous inquiétez pas de la réponse tardive, nous n'avons vraiment aucun problème avec cela ici sur PPCG.
Rɪᴋᴇʀ

0

Mathematica, 111 105 104 octets

r=Floor[(1+Sqrt[(4#-1)/3])/2]&;t=Limit[Pi(#/(3x)+1-x),x->r@#]&;p=r@#*Exp[I*t@#]&;Round@Abs[p@#-p@#2]==1&

Explication:

r=Floor[(1+Sqrt[(4#-1)/3])/2]&définit une fonction rqui prend une entrée #et calcule la distance (en nombre de cellules) à la cellule 0. Elle le fait en exploitant le motif dans les dernières cellules de chaque distance / anneau: 0 = 3 (0 ^ 2 + 0), 6 = 3 (1 ^ 2 + 1), 18 = 3 (2 ^ 2 + 2), 36 = 3 (3 ^ 2 + 3), ... et inverser la formule de ce modèle. Notez que pour la cellule 0, il prend en fait le plancher de (1/2) + i * (sqrt (3) / 6), qu'il calcule par composant pour obtenir 0 + 0 * i = 0.

Avec rdéfini, r@#est l'anneau de cellule #(à l'intérieur de la définition d'une autre fonction). #+3r@#-3(r@#)^2&n'apparaît pas exactement dans le code, mais il prend le numéro d'une cellule et soustrait le nombre le plus élevé d'une cellule dans l'anneau intérieur suivant, de sorte qu'il donne la réponse à la question "quelle cellule de l'anneau actuel est-ce?" Par exemple, la cellule 9 est la 3ème cellule de l'anneau 2, ainsi r[9]sortirait 2 et #+3r@#-3(r@#)^2&[9]sortirait 3.

Ce que nous pouvons faire avec la fonction ci-dessus, c'est l'utiliser pour trouver l' angle polaire , l' angle anti-horaire du rayon "cellule 0, cellule 17, cellule 58" à la cellule en question. La dernière cellule de chaque anneau est toujours à un angle Pi / 6, et nous contournons un anneau par incréments de Pi / (3 * ring_number). Donc, en théorie, nous devons calculer quelque chose comme Pi / 6 + (which_cell_of_the_current_ring) * Pi / (3 * ring_number). Cependant, la rotation de l'image n'affecte rien, nous pouvons donc éliminer la partie Pi / 6 (pour économiser 6 octets). En combinant cela avec la formule précédente et en simplifiant, nous obtenonsPi(#/(3r@#)+1-r@#)&

Malheureusement, cela n'est pas défini pour la cellule 0 car son numéro de sonnerie est 0, nous devons donc contourner cela. Une solution naturelle serait quelque chose comme t=If[#==0,0,Pi(#/(3r@#)+1-r@#)]&. Mais comme nous ne nous soucions pas de l'angle pour la cellule 0 et parce que r@#c'est répété, nous pouvons réellement enregistrer un octet ici avect=Limit[Pi(#/(3x)+1-x),x->r@#]&

Maintenant que nous avons le numéro d'anneau et l'angle, nous pouvons trouver la position d'une cellule (centre) afin que nous puissions tester l'adjacence. Trouver la position réelle est ennuyeux car les anneaux sont hexagonaux, mais nous pouvons simplement prétendre que les anneaux sont des cercles parfaits afin que nous considérions le numéro de l'anneau comme la distance au centre de la cellule 0. Ce ne sera pas un problème car l'approximation est assez jolie proche. En utilisant la forme polaire d'un nombre complexe , nous pouvons représenter cette position approximative dans le plan complexe avec une fonction simple:p = r@#*Exp[I*t@#] &;

La distance entre deux nombres complexes sur le plan complexe est donnée par la valeur absolue de leur différence, puis nous pouvons arrondir le résultat pour prendre soin des erreurs de l'approximation et vérifier si cela est égal à 1. La fonction qui finalement fait ce travail n'a pas de nom, mais l'est Round@Abs[p@#-p@#2]==1&.


Vous pouvez l' essayer en ligne dans le sandbox Wolfram Cloud en collant du code comme celui-ci et en cliquant sur Gear -> "Evaluer la cellule" ou en appuyant sur Maj + Entrée ou sur le pavé numérique Entrée:

r=Floor[(1+Sqrt[(4#-1)/3])/2]&;t=Limit[Pi(#/(3x)+1-x),x->r@#]&;p=r@#*Exp[I*t@#]&;Round@Abs[p@#-p@#2]==1&[24,45]

Ou pour tous les cas de test:

r=Floor[(1+Sqrt[(4#-1)/3])/2]&;t=Limit[Pi(#/(3x)+1-x),x->r@#]&;p=r@#*Exp[I*t@#]&;Round@Abs[p@#-p@#2]==1&//MapThread[#,Transpose[{{0,1},{7,18},{8,22},{24,45},{40,64},{64,65},{6,57},{29,90},{21,38},{38,60},{40,63},{41,39},{40,40}}]]&
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.