Stratégies gagnantes pour un jeu de construction de cordes

14

Contexte

Alice et Bob jouent à un jeu appelé construire un mot binaire . Pour jouer au jeu, vous fixez une longueur n >= 0, un ensemble Gde nmots binaires de longueur appelés le jeu d'objectifs , et une nchaîne de longueur tcontenant les lettres Aet B, appelée l' ordre du tour . Le jeu dure des ntours, et au tour i, le joueur défini par t[i]sélectionne un peu w[i]. Une fois le jeu terminé, les joueurs regardent le mot binaire wqu'ils ont construit. Si ce mot est trouvé dans l'objectif fixé G, Alice gagne la partie; sinon, Bob gagne.

Par exemple, nous allons fixer n = 4, G = [0001,1011,0010]et t = AABA. Alice obtient le premier tour et elle choisit w[0] = 0. Le deuxième tour est aussi celui d'Alice, et elle choisit w[1] = 0. Bob a le troisième tour, et il choisit w[2] = 0. Au dernier tour, Alice choisit w[3] = 1. Le mot résultant,, 0001se trouve dans G, donc Alice gagne la partie.

Maintenant, si Bob avait choisi w[2] = 1, Alice aurait pu choisir w[3] = 0dans son dernier tour, et toujours gagner. Cela signifie qu'Alice peut gagner le jeu, peu importe la façon dont Bob joue. Dans cette situation, Alice a une stratégie gagnante . Cette stratégie peut être visualisée comme un arbre binaire étiqueté, qui se ramifie aux niveaux correspondant aux tours de Bob, et dont chaque branche contient un mot de G:

A A B A

-0-0-0-1
    \
     1-0

Alice joue en suivant simplement les branches à son tour; quelle que soit la branche choisie par Bob, Alice finit par gagner.

Contribution

On vous donne en entrée la longueur n, et l'ensemble Gcomme une liste (éventuellement vide) de chaînes de longueur n.

Production

Votre sortie est la liste des ordres de tour pour lesquels Alice a une stratégie gagnante, ce qui équivaut à l'existence d'un arbre binaire comme décrit ci-dessus. L'ordre des ordres de tour n'a pas d'importance, mais les doublons sont interdits.

Règles détaillées

Vous pouvez écrire un programme complet ou une fonction. Dans le cas d'un programme, vous pouvez choisir le délimiteur pour l'entrée et la sortie, mais il doit être le même pour les deux. Le nombre d'octets le plus court l'emporte et les failles standard sont interdites.

Cas de test

3 [] -> []
3 [000,001,010,011,100,101,110,111] -> [AAA,AAB,ABA,ABB,BAA,BAB,BBA,BBB]
4 [0001,1011,0010] -> [AAAA,BAAA,AABA]
4 [0001,1011,0010,0110,1111,0000] -> [AAAA,BAAA,ABAA,BBAA,AABA,AAAB]
5 [00011,00110,00111,11110,00001,11101,10101,01010,00010] -> [AAAAA,BAAAA,ABAAA,BBAAA,AABAA,AAABA,BAABA,AAAAB,AABAB]

Fait amusant

Le nombre d'ordres de tour dans la sortie est toujours égal au nombre de mots dans l'objectif fixé.

Zgarb
la source
5
Je suis plutôt intrigué par le fait que l'entrée et la sortie ont une taille égale. Avez-vous une preuve ou une citation de ce fait? Je me demande s'il existe un moyen de calculer cette fonction qui préserve intuitivement la taille.
xnor
2
Votre cas de test n ° 5 contredit votre fait amusant ...
mbomb007
3
@ mbomb007 Le cas de test n ° 5 répertorie 11101deux fois; le fait amusant tient toujours pour les ensembles. Zgarb, l'entrée peut-elle contenir des éléments répétés, ou était-ce une erreur?
xnor
@xnor C'est quelque chose qui est apparu dans mes recherches il y a quelque temps. J'ai une preuve dans cette préimpression , page 16, mais elle est essentiellement la même que la vôtre.
Zgarb
1
@xnor Intuitivement, à n'importe quel tour, si 0 et 1 sont des choix gagnants, alors Alice ou Bob peuvent choisir le coup suivant. S'il n'y a qu'une seule option gagnante, Alice doit choisir la suivante. Ainsi, le nombre de choix pour la chaîne est le même que le nombre de choix de stratégie gagnante. À peine rigoureux, mais convaincant.
Alchymist

Réponses:

1

Dyalog APL, 59 octets

{(a≡,⊂⍬)∨0=⍴a←∪⍵:a⋄(∇h/t)(('A',¨∪),'B',¨∩)∇(~h←⊃¨a)/t←1↓¨a}

Même algorithme que dans la solution @ xnor.

(a≡,⊂⍬)∨0=⍴a←∪⍵:a
           a←∪⍵    ⍝ "a" is the unique items of the argument
        0=⍴a       ⍝ is it empty?
 a≡,⊂⍬             ⍝ is it a vector that contains the empty vector?
       ∨       :a  ⍝ if any of the above, return "a"

(∇h/t)(('A',¨∪),'B',¨∩)∇(~h←⊃¨a)/t←1↓¨a
                                 t←1↓¨a  ⍝ drop an item from each of "a" and call that "t"
                         ~h←⊃¨a          ⍝ first of each of "a", call that "h", then negate it
                                /        ⍝ use "~h" as a boolean mask to select from "t"
                       ∇                 ⍝ apply a recursive call
(∇h/t)                                   ⍝ use "h" as a boolean mask on "t", then a recursive call
      (('A',¨∪),'B',¨∩)                  ⍝ apply a fork on the results from the two recursive calls:
       ('A',¨∪)                          ⍝   prepend 'A' to each of the intersection
               ,                         ⍝   concatenated with
                'B',¨∪                   ⍝   prepend 'B' to each of the union
ngn
la source
13

Python, 132

def f(S,n):
 if n<1:return S
 a,b=[f({x[1:]for x in S if x[0]==c},n-1)for c in'01']
 return{'A'+y for y in a|b}|{'B'+y for y in a&b}

Exemple d'exécution:

f({'000','001','010','011','100','101','110','111'},3) == 
{'ABA', 'ABB', 'AAA', 'AAB', 'BBB', 'BBA', 'BAB', 'BAA'}

Ce n'est qu'une sorte de golf, principalement pour montrer l'algorithme. Les entrées et sorties sont des ensembles de chaînes. Python ne semble pas avoir les bonnes fonctionnalités pour exprimer des parties de cela de manière compacte, donc ce serait cool si quelqu'un écrivait cela dans un langage mieux adapté.

Voici comment la récursivité peut être exprimée mathématiquement. Malheureusement, PPCG manque toujours de rendu mathématique, donc je vais devoir utiliser des blocs de code.

Les objets d'intérêt sont des ensembles de chaînes. Soit |representer l'union set et &representer l'intersection set.

Si cest un caractère, laissez c#Srepresent précéder le caractère cde toutes les chaînes de caractères S. Inversement, que la contraction c\Ssoit les chaînes d'un caractère plus courtes Squi suivent le caractère initial c, par exemple 0\{001,010,110,111} = {01,10}.

Nous pouvons diviser de façon unique un ensemble de chaînes Savec des caractères 01par leur premier caractère.

S = 0#(0\S) | 1#(1\S)

Ensuite, nous pouvons exprimer la fonction souhaitée fcomme suit, avec les cas de base dans les deux premières lignes et la boîte récursive dans la dernière ligne:

f({})   = {}
f({''}) = {''}
f(S)    = A#(f(0\S)|f(1\S)) | B#(f(0\S)&f(1\S))

Notez que nous n'avons pas besoin d'utiliser la longueur n.

Pourquoi ça marche? Penchons-nous sur les chaînes de déplacement qui permettent à Alice de gagner pour un ensemble de chaînes S.

Si le premier personnage est A, Alice peut choisir le premier coup ('0' ou '1'), lui laissant choisir de réduire le problème à S0ou S1. Alors maintenant , le mouvement chaîne restante doit être dans au moins l' un des f(S0)ou f(S1), d' où nous prenons leur union |.

De même, si le premier caractère est 'B', Bob arrive à choisir, et il choisira le pire pour Alice, donc la chaîne de déplacement restante doit être dans l'intersection ( &).

Les cas de base vérifient simplement si Sest vide ou non à la fin. Si nous suivons la longueur des chaînes n, en soustrayant 1 à chaque récurrence, les bases peuvent être écrites à la place:

f(S) = S if n==0

La solution récursive explique également le fait amusant qui f(S)a la même taille que S. C'est vrai pour les cas de base et pour le cas inductif

f(S) = A#(f(0\S)|f(1\S)) | B#(f(0\S)&f(1\S))

on a

size(f(S)) = size(A#(f(0\S)|f(1\S)) | B#(f(0\S)&f(1\S)))
           = size(A#(f(0\S)|f(1\S))) + size(B#(f(0\S)&f(1\S))))
           = size((f(0\S)|f(1\S))) + size((f(0\S)&f(1\S))))
           = size(f(0\S)) + size(f(1\S))  [since size(X|Y) + size(X&Y) = size(X) + size(Y)]
           = size(0\S) + size(1\S)
           = size(S)
xnor
la source
L'exécution de votre code donne TypeError: 'int' object is not subscriptable. Avez-vous un lien vers un programme exécutable? Je viens de le coller et de l' print f([0001,1011,0010],4)
exécuter
@ mbomb007 La fonction doit être invoquée comme f({'000','001','010','011','100','101','110','111'},3). Obtenez-vous une erreur de cette façon?
xnor
Ah, je n'ai pas vu qu'il me manquait des guillemets, merci. Il fonctionne également avecprint f(['0001','1011','0010'],4)
mbomb007
Si vous voulez exécuter le programme en sachant nindépendamment des paramètres, ce seraitn=len(S[0])if S!=[]else 0
mbomb007
Exécutez-le ici: repl.it/7yI
mbomb007