Rock, Papier, Ciseaux, Lézard, Tournoi Spock

13

Donner un défi impliquant une référence Star Trek juste après le 4 mai peut être mal vu, mais c'est parti.

Vous, Luke, Anakin, Palpatine, Yoda et Han Solo êtes impliqués dans un tournoi fou de Rock, Paper, Scissor, Lizard, Spock.

Le problème ici est que vous n'êtes autorisé à utiliser qu'un ordre fixe de mouvements. Si votre commande est "R", alors vous devez utiliser Rock, jusqu'à ce que vous perdiez ou gagniez contre tout le monde. Si votre commande est RRV, vous devez utiliser 2 Roches suivies d'un Spock et continuer à répéter jusqu'à ce que vous ayez gagné ou perdu.

Luke, Anakin, Palpatine, Yoda et Han Solo ont soumis leurs commandes respectives et vous, en tant que hacker expert, avez mis la main sur chacune de leurs commandes!

Avec cette connaissance, vous devez concevoir votre commande pour le tournoi. Puisque tout le monde veut gagner, vous voulez créer un ordre tel que vous gagnez le tournoi en battant tout le monde. Mais cela peut ne pas être possible en toutes circonstances.

Au cas où il y aurait une commande gagnante possible, imprimez-la. S'il n'y a aucun moyen possible de gagner, imprimez -1 (ou 0 ou Faux ou "impossible")

Entrée : une liste de 5 commandes

Sortie : une seule commande ou -1

Exemple d'entrée 1

R
P
S
L
V

Exemple de sortie 1

-1

Explication 1

Peu importe ce que vous jouez lors de votre premier coup, il y aura au moins une personne qui vous battra, il n'est donc pas possible pour vous de gagner.

Exemple d'entrée 2

RPS
RPP
R
SRR
L

Exemple de sortie 2

RPSP

Explication 2

Une fois que vous jouez Rock dans votre premier coup, vous finissez par battre "L" et "SRR" et vous égalisez contre les autres. C'est parce que le lézard et les ciseaux perdent contre Rock. Lorsque vous jouez à Paper ensuite, vous battrez "R" et vous égalerez contre les 2 restants. C'est parce que Rock perd contre Paper. La prochaine fois que vous jouerez à Ciseaux, vous gagnerez contre "RPP" alors que Scissor bat Paper.

Enfin, vous battrez "RPS" avec votre Paper comme Paper bat Rock.

Voici une liste de notations (vous pouvez utiliser n'importe quel 5 littéraux, mais veuillez préciser dans votre réponse):

R : Rock
P : Paper
S : Scissor
L : Lizard
V : Spock

Voici une liste de tous les résultats possibles:

winner('S', 'P') -> 'S'
winner('S', 'R') -> 'R'
winner('S', 'V') -> 'V'
winner('S', 'L') -> 'S'
winner('S', 'S') -> Tie
winner('P', 'R') -> 'P'
winner('P', 'V') -> 'P'
winner('P', 'L') -> 'L'
winner('P', 'S') -> 'S'
winner('P', 'P') -> Tie
winner('R', 'V') -> 'V'
winner('R', 'L') -> 'R'
winner('R', 'S') -> 'R'
winner('R', 'P') -> 'P'
winner('R', 'R') -> Tie
winner('L', 'R') -> 'R'
winner('L', 'V') -> 'L'
winner('L', 'S') -> 'S'
winner('L', 'P') -> 'L'
winner('L', 'L') -> Tie
winner('V', 'R') -> 'V'
winner('V', 'L') -> 'L'
winner('V', 'S') -> 'V'
winner('V', 'P') -> 'P'
winner('V', 'V') -> Tie

C'est du , donc moins d'octets gagnent.

PS: dites-moi si vous avez besoin de plus de cas de test.

Koishore Roy
la source
4
Veuillez changer "Star Trek " en "Star Wars " dans votre intro;)
movatica
1
C'est un problème assez difficile. Eh bien, ou je suis mauvais dans ce genre de programmation.
CrabMan
@CrabMan C'est un peu un problème difficile pour le golf. surtout dans les langues pratiques.
Koishore Roy
1
plusieurs travaux, mais il existe en théorie des stratégies de gain infinies, alors gardez cela à l'esprit
Koishore Roy
1
Lié , et aussi un KOTH (cc: @Arnauld)
DLosc

Réponses:

2

Gelée , 29 octets

_%5ḟ0ḢḂ¬
ṁ€ZLḤƊçþ`Ạ€Tị;‘%5Ɗ$€

Un lien monadique qui accepte une liste de listes d'entiers (dont chacun est la stratégie d'un adversaire) qui produit une liste de listes d'entiers - dont chacune est une stratégie gagnante (donc une liste vide si aucune n'est possible).
(Ajoutez simplement pour ne générer qu'une seule liste de stratégies ou 0si cela est impossible.)

Essayez-le en ligne! (les formats de pied de page pour toujours afficher les listes)

Rock  Paper  Scissors  Spock  Lizard
0     1      2         3      4

Ou essayez une version mappée de lettres (où les stratégies sont prises et affichées sur leurs propres lignes en utilisant la RPSVLnotation).

Comment?

Les numéros sont choisis de telle sorte que tous ceux qui sont un nombre impair supérieur à un autre modulo cinq gagnent (c'est-à-dire qu'ils sont numérotés en contournant le bord d'un pentagone inscrit des lancers).

Le code joue chaque stratégie contre chaque stratégie (y compris eux-mêmes) pour deux fois plus de lancers que la stratégie la plus longue afin de s'assurer de trouver des perdants gardant ceux qui ne sont pas vaincus. La liste de stratégies qui en résulte contiendra une seule stratégie s'il y a un vainqueur absolu; pas de stratégies s'il n'y avait pas de gagnant; ou plusieurs stratégies s'il y a des joueurs de dessin. Après cela, un ensemble de mouvements gagnants est ajouté à chacune de ces stratégies.

_%5ḟ0ḢḂ¬ - Link 1, does B survive?: list A, list B (A & B of equal lengths)
                              e.g. RPSR vs RPVL ->  [0,1,2,0], [0,1,3,4]
_        - subtract (vectorises)                    [0,0,-1,-4]
 %5      - modulo five (vectorises)                 [0,0,4,1]   ...if all zeros:
   ḟ0    - filter discard zeros (ties)              [4,1]                       []
     Ḣ   - head (zero if an empty list)             4                           0
      Ḃ  - modulo two                               0                           0
       ¬ - logical NOT                              1                           1

ṁ€ZLḤƊçþ`Ạ€Tị;‘%5Ɗ$€ - Main Link: list of lists of integers
ṁ€                   - mould each list like:
     Ɗ               -   last three links as a monad
  Z                  -     transpose
   L                 -     length
    Ḥ                -     double  (i.e. 2 * throws in longest strategy)
        `            - use left as both arguments of:
       þ             -   table using:
      ç              -     last Link (1) as a dyad
         Ạ€          - all for each (1 if survives against all others, else 0)
           T         - truthy indices
            ị        - index into the input strategies
                  $€ - last two links as a monad for each:
             ;       -   concatenate with:
                 Ɗ   -     last three links as a monad:
              ‘      -       increment (vectorises)
               %5    -       modulo five (vectorises)
Jonathan Allan
la source
Je suis complètement nouveau sur Jelly, mais il semble que vous pouvez gagner un octet en le remplaçant ZLḤpar .
Robin Ryder
@RobinRyder Cela ne fonctionnera pas - cela ne fonctionne qu'avec les données d'exemple car il y a suffisamment d'adversaires et peu de lancers - c'est un exemple d'un qui ne fonctionnerait pas . Nous devons analyser deux fois plus de lancers que la stratégie de l'adversaire la plus longue. (Votre code est en fait équivalent à cela )
Jonathan Allan
... en fait, en raison de l'action de Ɗvotre code, il ne fait même pas ce que vous avez pu penser - il moule chacun comme sa propre longueur, puis obtient les sommes cumulées de ces résultats, donc il comparera également les valeurs incorrectes. Essayez ceci par exemple - cela prend [[1,2,3,4,5],[6,7],[8]], moule chacun par la longueur de la liste entière (3) pour obtenir [[1,2,3],[6,7,6],[8,8,8]]puis effectue une accumulation pour obtenir [[1,1+2,1+2+3],[6,6+7,6+7+8],[8,8+8,8+8+8]]= [[1,3,6],[6,13,19],[8,16,24]].
Jonathan Allan
Ah oui, je savais que je ne comprenais pas quelque chose!
Robin Ryder
7

JavaScript (ES6),  122 115  112 octets

Prend l'entrée comme un tableau de chaînes de chiffres, avec:

  • 0
  • 1
  • 2
  • 3
  • 4

Funelse

f=(a,m='',x=0,o,b=a.filter(a=>(y=a[m.length%a.length])-x?o|=y-x&1^x<y:1))=>b+b?x<4&&f(a,m,x+1)||!o&&f(b,m+x):m+x

Essayez-le en ligne!

Comment?

Il s'agit d'une recherche en premier: nous essayons d'abord tous les coups à une étape donnée pour voir si nous pouvons gagner la partie. Si nous ne pouvons pas gagner maintenant, nous essayons d'ajouter un autre coup à chaque coup non perdant.

UNEB(B-UNE)mod5

UNEB

(S)(P)(R)(L)(V)01234(S) 0-1234(P) 14-123(R) 234-12(L) 3234-1(V) 41234-

UNEBUNEB

((A - B) and 1) xor (B < A)

andet xorsont des opérateurs au niveau du bit.

Commenté

f = (                        // f is a recursive function taking:
  a,                         //   a[] = input
  m = '',                    //   m   = string representing the list of moves
  x = 0,                     //   x   = next move to try (0 to 4)
  o,                         //   o   = flag set if we lose, initially undefined
  b =                        //   b[] = array of remaining opponents after the move x
    a.filter(s =>            //     for each entry s in a[]:
    ( y =                    //       define y as ...
      s[m.length % s.length] //         ... the next move of the current opponent
    ) - x                    //       subtract x from y
    ?                        //       if the difference is not equal to 0:
      o |=                   //         update o using the formula described above:
        y - x & 1 ^ x < y    //           set it to 1 if we lose; opponents are removed
                             //           while o = 0, and kept as soon as o = 1
    :                        //       else (this is a draw):
      1                      //         keep this opponent, but leave o unchanged
  )                          //     end of filter()
) =>                         //
  b + b ?                    // if b[] is not empty:
    x < 4 &&                 //   if x is less than 4:
      f(a, m, x + 1)         //     do a recursive call with x + 1 (going breadth-first)
    ||                       //   if this fails:
      !o &&                  //     if o is not set:
        f(b, m + x)          //       keep this move and do a recursive call with b[]
  :                          // else (success):
    m + x                    //   return m + x
Arnauld
la source
votre code échoue pour le cas de test: test(['P','P','S','P','P']) la réponse doit être "SR" ou "SV".
Koishore Roy
@KoishoreRoy Maintenant corrigé.
Arnauld
1
Il s'agit en fait d'une approche brillante. Je n'ai même pas pensé à le considérer comme un graphique. J'utilisais des dictionnaires et des recherches inversées dans mon approche originale non golfée (sans Spock ni Lizards ie)
Koishore Roy
3

R , 213 190 octets

-23 octets grâce à Giuseppe.

function(L){m=matrix(rep(0:2,1:3),5,5)
m[1,4]=m[2,5]=1
v=combn(rep(1:5,n),n<-sum(lengths(L)))
v[,which(apply(v,2,function(z)all(sapply(L,function(x,y,r=m[cbind(x,y)])r[r>0][1]<2,z)))>0)[1]]}

Essayez-le en ligne!

Si une solution existe, elle en génère une. S'il n'y a pas de solution, il génère une ligne de NA. Si ce format de sortie n'est pas acceptable, je peux le changer au prix de quelques octets.

Les mouvements sont codés comme 1 = R, 2 = S, 3 = P, 4 = L, 5 = V, de sorte que la matrice des résultats est

     [,1] [,2] [,3] [,4] [,5]
[1,]    0    2    2    1    1
[2,]    1    0    2    2    1
[3,]    1    1    0    2    2
[4,]    2    1    1    0    2
[5,]    2    2    1    1    0

(0 = pas de gagnant; 1 = joueur 1 gagne; 2 = joueur 2 gagne)

Une limite supérieure sur la longueur de la solution si elle existe est n=sum(lengths(L))où se Ltrouve la liste des mouvements des adversaires. Le code crée toutes les stratégies de longueur possibles n(stockées dans la matrice v), les essaie toutes et affiche toutes les stratégies gagnantes.

Notez que cette valeur de nrend le code très lent sur TIO, j'ai donc codé en dur dans le TIO, n=4ce qui est suffisant pour les cas de test.

Pour le premier cas de test, la sortie est

     1 4 2 4

correspondant à la solution RLSL.

Pour le deuxième cas de test, la sortie est

 NA NA NA NA

ce qui signifie qu'il n'y a pas de solution.

Explication d'une version précédente (sera mise à jour lorsque je le pourrai):

function(L){
  m = matrix(rep(0:2,1:3),5,5);
  m[1,4]=m[2,5]=1                      # create matrix of outcomes
  v=as.matrix(expand.grid(replicate(   # all possible strategies of length n
    n<-sum(lengths(L))                 # where n is the upper bound on solution length
    ,1:5,F)))             
  v[which(    
    apply(v,1,                         # for each strategy
          function(z)                  # check whether it wins
            all(                       # against all opponents
              sapply(L,function(x,y){  # function to simulate one game
                r=m[cbind(x,y)];       # vector of pair-wise outcomes
                r[r>0][1]<2            # keep the first non-draw outcome, and verify that it is a win
              }
              ,z)))
    >0),]                              # keep only winning strategies
}

Il whichest nécessaire de se débarrasser des NA qui se produisent lorsque les deux joueurs tirent pour toujours.

Je ne suis pas convaincu que ce soit la stratégie la plus efficace. Même si c'est le cas, je suis sûr que le code pour mpourrait être joué un peu.

Robin Ryder
la source
pourquoi lengths()alias pour toujours revenir 4?
Giuseppe
1
Quoi qu'il en soit, en attendant votre réponse, je l'ai minimisée à 197 , principalement en me concentrant sur v...
Giuseppe
lengthsn=45nn=11
ah, fait sens, aurait dû savoir. 187 octets
Giuseppe
@Giuseppe Merci, golf impressionnant! J'ai ajouté 3 octets pour rendre la sortie plus lisible (sinon nous nous retrouvons avec la même solution imprimée plusieurs fois).
Robin Ryder
0

Emacs Lisp, 730 octets

(require 'cl-extra)
(require 'seq)
(defun N (g) (length (nth 1 g)))
(defun M (g) (mapcar (lambda (o) (nth (% (N g) (length o)) o)) (car g)))
(defun B (x y) (or (eq (% (1+ x) 5) y) (eq (% (+ y 2) 5) x)))
(defun S (g) (seq-filter (lambda (m) (not (seq-some (lambda (v) (B v m)) (M g)))) '(0 1 2 3 4)))
(defun F (g) (cond ((null (car g)) (reverse (nth 1 g))) ((null (S g)) nil) ((>= (nth 3 g) (seq-reduce (lambda (x y) (calc-eval "lcm($,$$)" 'raw x y)) (mapcar 'length (car g)) 1)) nil) (t (cl-some (lambda (m) (F   (let ((r (seq-filter 'identity (mapcar* (lambda (v o) (and (not (B m v)) o)) (M g) (car g))))) (list r (cons m (nth 1 g)) (1+ (N g)) (if (eq (car g) r) (1+ (nth 3 g)) 0))))) (S g)))))
(defun Z (s) (F (list s () 0 0)))

Je n'ai pas trouvé d'interprète en ligne d'Emacs Lisp :( Si Emacs est installé, vous pouvez copier du code dans un .elfichier, copier quelques lignes de test ci-dessous

;; 0 = rock, 1 = lizard; 2 = spock;
;; 3 = scissors; 4 = paper
(print (Z '((0) (1) (2) (3) (4))))
; output: nil
(print (Z '((0) (4) (3) (1))))
; output: nil
(print (Z '((0 4 3) (0 4 4) (0) (3 0 0) (1))))
; output: (0 4 3 0 1)
(print (Z '((4) (4) (3) (4) (4))))
; output: (3 0)
(print (Z '((4 3 2 1 0) (2 1 0 4 3))))
; output: (1)
(print (Z '((2) (2) (3) (0) (2) (3) (0) (0))))
; output: (2 1)
(print (Z '((2) (2 0) (3) (0) (2 1) (3) (0) (0))))
; output: nil

et l'exécuter $ emacs --script filename.el.

Comment ça fonctionne

Mon programme effectue d'abord une recherche approfondie avec parfois à comprendre qu'il est impossible de gagner et de mettre fin à la branche sur laquelle il se trouve.

Vous pouvez voir l'explication complète dans la version non abrégée du code:

(require 'seq)
(require 'cl-extra)

;; This program does depth first search with sometimes figuring out
;; that it's impossible to win and terminating the branch it's on.
;;

;; A move is a number from 0 to 4. 
;; https://d3qdvvkm3r2z1i.cloudfront.net/media/catalog/product/cache/1/image/1800x/6b9ffbf72458f4fd2d3cb995d92e8889/r/o/rockpaperscissorslizardspock_newthumb.png
;; this is a nice visualization of what beats what.
;; Rock = 0, lizard = 1, spock = 2, scissors = 3, paper = 4.

(defun beats (x y) "Calculates whether move x beats move y"
  (or (eq (% (1+ x) 5) y)
      (eq (% (+ y 2) 5) x)))

;; A gamestate is a list with the following elements:
(defun get-orders (gamestate)
  "A list of orders of players who haven't lost yet. Each order is a list of moves.
For example, ((2) (2 0) (3) (0) (2 1) (3) (0) (0)) is a valid orders list.
This function gets orders from the gamestate."
  (car gamestate))

;; At index 1 of the gamestate lies a list of all moves we have made so far in reverse order
;; (because lists are singly linked, we can't push back quickly)
(defun get-num-moves-done (gamestate)
  "Returns the number of moves the player has done so far"
  (length (nth 1 gamestate)))

(defun get-rounds-since-last-elim (gamestate)
  "The last element of a gamestate is the number of rounds passed since an opponent
was eliminated. We use this to determine if it's possible to win from current
gamestate (more about it later)."
  (nth 2 gamestate))

;; next go some utility functions
;; you can skip their descriptions, they are not very interesting
;; I suggest you skip until the next ;; comment

(defun get-next-move (order num-rounds-done)
  "Arguments: an order (e.g. (1 0 1)); how many rounds have passed total.
Returns the move this opponent will make next"
  (nth (% num-rounds-done (length order)) order))

(defun moves-of-opponents-this-round (gamestate)
  "Returns a list of moves the opponents will make next"
  (mapcar (lambda (order) (get-next-move order (get-num-moves-done gamestate)))
          (get-orders gamestate)))

(defun is-non-losing (move opponents-moves)
  "Calculates if we lose right away by playing move against opponents-moves"
  (not (seq-some (lambda (opponent-move) (beats opponent-move move))
                 opponents-moves)))

(defun non-losing-moves (gamestate)
  "Returns a list of moves which we can play without losing right away."
  (seq-filter
   (lambda (move) (is-non-losing move (moves-of-opponents-this-round gamestate)))
   '(0 1 2 3 4)))

(defun advance-gamestate (gamestate move)
  "If this move in this gamestate is non-losing, returns the next game state"
  (let ((new-orders (seq-filter
                    'identity (mapcar* (lambda (opp-move order)
                                         (and (not (beats move opp-move)) order))
                                       (moves-of-opponents-this-round gamestate)
                                       (get-orders gamestate)))))
  (list new-orders
        (cons move (nth 1 gamestate))
        (if (eq (get-orders gamestate) new-orders) (1+ (get-rounds-since-last-elim gamestate)) 0))))

;; How do we prevent our depth first search from continuing without halting?
;; Suppose 3 players (except us) are still in the game and they have orders of lengths a, b, c
;; In this situation, if least_common_multiple(a, b, c) rounds pass without an elimination
;; we will be in the same situation (because they will be playing the same moves they played
;; lcm(a, b, c) rounds ago)
;; Therefore, if it's possible to win from this gamestate,
;; then it's possible to win from that earlier game state,
;; hence we can stop exploring this branch

(defun get-cycle-len (gamestate)
  "Returns a number of rounds which is enough for the situation to become the same
if the game goes this long without an elimination."
  (seq-reduce (lambda (x y) (calc-eval "lcm($,$$)" 'raw x y))
              (mapcar 'length (get-orders gamestate)) 1))

(defun unwinnable-cycle (gamestate)
  "Using the aforementioned information, returns t if we are in such a
suboptimal course of play."
  (>= (get-rounds-since-last-elim gamestate) (get-cycle-len gamestate)))

(defun find-good-moves (gamestate)
  "Given gamestate, if it's possible to win
returns a list of moves, containing all moves already done + additional moves which lead to win.
Otherwise returns nil"
  (cond ((null (get-orders gamestate)) ; if no opponents left, we won, return the list of moves
         (reverse (nth 1 gamestate)))
        ((null (non-losing-moves gamestate)) ; if no non-losing moves available, this gamestate
         nil) ; doesn't lead to a win, return nil
        ((unwinnable-cycle gamestate) ; either it's impossible to win, or
         nil) ; it's possible to win from an earlier position, return nil
        (t (cl-some (lambda (move) ; otherwise return the first non-losing move which leads
                      (find-good-moves (advance-gamestate gamestate move))) ; to a non-nil result
                    (non-losing-moves gamestate)))))

(defun make-initial-gamestate (orders)
  "Given an orders list, create initial gamestate"
  (list orders () 0))
CrabMan
la source
1
tio.run/##S81NTC7WzcksLvgPBAA pouvez-vous insérer votre code ici et essayer de l'exécuter?
Koishore Roy
@KoishoreRoy J'avais essayé tio.run et je n'arrivais pas à comprendre pourquoi il ne fonctionnait pas. Il dit "Trailing ordures suivant l'expression" et je n'ai aucune idée de ce que c'est et 5 minutes de recherche sur Google ne m'ont pas aidé à le réparer.
CrabMan