Torneo di rocce, carta, forbici, lucertole e spock


13

Dare una sfida che coinvolge un riferimento a Star Trek subito dopo il 4 maggio può essere visto di buon occhio, ma ecco qui.

Tu, Luke, Anakin, Palpatine, Yoda e Han Solo siete coinvolti in un folle torneo di Rock, Paper, Scissor, Lizard, Spock.

Il trucco qui è che ti è permesso usare solo un ordine di mosse fisso. Se il tuo ordine è "R", allora devi usare Rock, fino a quando non perdi o vinci contro tutti. Se il tuo ordine è RRV, devi usare 2 Rocks seguiti da uno Spock e continuare a ripetere fino a quando non hai vinto o perso.

Luke, Anakin, Palpatine, Yoda e Han Solo hanno inviato i loro rispettivi ordini e tu, essendo un hacker esperto, hai messo le mani su ciascuno dei loro ordini!

Con questa conoscenza, devi progettare il tuo ordine per il torneo. Dal momento che tutti vogliono vincere, si desidera creare un ordine in modo tale da vincere il torneo battendo tutti. Ma questo potrebbe non essere possibile in tutte le circostanze.

Nel caso ci sia un possibile ordine vincente, stampalo. Se non puoi vincere, stampa -1 (o 0 o Falso o "impossibile")

Input : un elenco di 5 ordini

Output : un singolo ordine o -1

Ingresso campione 1

R
P
S
L
V

Uscita campione 1

-1

Spiegazione 1

Indipendentemente da ciò che giochi nella tua prima mossa, ci sarà almeno una persona che ti batte, quindi non è possibile per te vincere.

Ingresso campione 2

RPS
RPP
R
SRR
L

Uscita campione 2

RPSP

Spiegazione 2

Una volta che giochi a Rock nella tua prima mossa, finisci per battere "L" e "SRR" e leghi contro il resto. Questo perché Lizard e Scissors perdono contro il Rock. La prossima volta che giocherai a Paper, batterai "R" e legherai contro i restanti 2. Questo perché Rock perde su Paper. Quando giocherai a Scissors, vincerai contro "RPP" mentre Scissor batte Paper.

Infine, batterai "RPS" con il tuo Paper mentre Paper batte Rock.

Ecco un elenco di notazioni (puoi usare 5 letterali, ma specifica nella tua risposta):

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

Ecco un elenco di tutti i possibili risultati:

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

Questo è , quindi vincono meno byte.

PS: Fammi sapere se hai bisogno di più casi di test.


4
Cambia "Star Trek " in "Star Wars " nella tua introduzione;)
movatica

1
Questo è un problema piuttosto difficile. Bene, o sono cattivo con questo tipo di programmazione.
CrabMan

@CrabMan Questo è un problema un po 'difficile per il golf. soprattutto nelle lingue pratiche.
Koishore Roy,

1
diverse opere, ma in teoria ci sono infinite strategie vincenti, quindi tienilo a mente
Koishore Roy

1
Correlato , e anche un KOTH (cc: @Arnauld)
DLosc

Risposte:


2

Gelatina , 29 byte

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

Un collegamento monadico che accetta un elenco di elenchi di numeri interi (ognuno dei quali è una strategia dell'avversario) che produce un elenco di elenchi di numeri interi - ognuno dei quali è una strategia vincente (quindi un elenco vuoto se non è possibile).
(Basta aggiungere per produrre un solo elenco di strategie o 0se impossibile.)

Provalo online! (i formati piè di pagina per mostrare sempre gli elenchi)

Rock  Paper  Scissors  Spock  Lizard
0     1      2         3      4

Oppure prova una versione mappata a lettere (dove le strategie sono prese e mostrate sulle loro stesse linee usando la RPSVLnotazione).

Come?

I numeri sono scelti in modo tale che vincano quelli che sono un numero dispari maggiore di un altro modulo cinque (cioè sono numerati andando attorno al bordo di un pentagono inscritto dei tiri).

Il codice gioca ogni strategia contro ogni strategia (inclusi se stessi) per il doppio dei tiri rispetto alla strategia più lunga in modo da garantire la ricerca di eventuali perdenti mantenendo quelli che non sono sconfitti. L'elenco di strategie risultante conterrà un'unica strategia se c'è un vincitore assoluto; nessuna strategia se non ci fosse un vincitore; o più strategie se ci sono giocatori di disegno. Dopo questo, una serie vincente di mosse viene aggiunta a ciascuna di queste strategie.

_%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)

Sono completamente nuovo in Jelly, ma sembra che tu possa guadagnare un byte sostituendolo ZLḤcon .
Robin Ryder,

@RobinRyder Che non funzionerà - funziona solo con i dati di esempio perché ci sono abbastanza avversari e pochi tiri sufficienti - questo è un esempio di uno che non funzionerebbe . Dobbiamo analizzare il doppio dei tiri rispetto alla strategia dell'avversario più lunga. (Il tuo codice è in realtà equivalente a questo )
Jonathan Allan

... in realtà a causa dell'azione di Ɗnel tuo codice non sta nemmeno facendo quello che potresti aver pensato - sta modellando ognuno come la sua lunghezza quindi ottenendo le somme cumulative di quei risultati, quindi confronterà anche valori errati. Prova questo per esempio: prende [[1,2,3,4,5],[6,7],[8]], modella ciascuno per la lunghezza dell'intero elenco (3) per ottenere [[1,2,3],[6,7,6],[8,8,8]]quindi esegue l'accumulo per ottenere [[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 sì, sapevo di aver frainteso qualcosa!
Robin Ryder

7

JavaScript (ES6),  122 115  112 byte

Prende l'input come una matrice di stringhe di cifre, con:

  • 0
  • 1
  • 2
  • 3
  • 4

fun'lSe

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

Provalo online!

Come?

Questa è una prima ricerca: prima proviamo tutte le mosse in un determinato passaggio per vedere se possiamo vincere la partita. Se non possiamo vincere in questo momento, proviamo ad aggiungere un'altra mossa a ogni mossa non perdente.

UNB(B-UN)mod5 è dispari.

UNB

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

UNBUNB

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

dove ande xorsono operatori bit per bit.

Commentate

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

il codice non riesce per il test case: test(['P','P','S','P','P']) la risposta dovrebbe essere "SR" o "SV".
Koishore Roy,

@KoishoreRoy Ora risolto.
Arnauld

1
Questo è in realtà un approccio geniale. Non ho nemmeno pensato di considerarlo come un grafico. Stavo usando dizionari e ricerche inverse nel mio approccio originale non golfato (senza Spock o Lucertole)
Koishore Roy

3

R , 213 190 byte

-23 byte grazie a 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]]}

Provalo online!

Se esiste una soluzione, ne esce una. Se non esiste una soluzione, genera una riga di NA. Se questo formato di output non è accettabile, posso modificarlo al costo di pochi byte.

Le mosse sono codificate come 1 = R, 2 = S, 3 = P, 4 = L, 5 = V, in modo che la matrice dei risultati sia

     [,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 = nessun vincitore; 1 = il giocatore 1 vince; 2 = il giocatore 2 vince)

Un limite superiore sulla lunghezza della soluzione, se esiste, è n=sum(lengths(L))dove si Ltrova l'elenco delle mosse degli avversari. Il codice crea tutte le possibili strategie di lunghezza n(memorizzate in matricev ), le prova tutte e visualizza tutte le strategie vincenti.

Si noti che questo valore di nrende il codice molto lento su TIO, quindi ho codificato nel TIO n=4che è sufficiente per i casi di test.

Per il primo caso di test, l'output è

     1 4 2 4

corrispondente alla soluzione RLSL.

Per il secondo caso di test, l'output è

 NA NA NA NA

nel senso che non esiste soluzione.

Spiegazione di una versione precedente (verrà aggiornata quando posso):

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 whichè necessario sbarazzarsi di AN che si verificano quando i due giocatori pescano sempre.

Non sono convinto che questa sia la strategia più efficiente. Anche se lo è, sono sicuro che il codice per mpotrebbe essere giocato un po 'a golf.


perché è lengths()alias per tornare sempre 4?
Giuseppe,

1
Comunque, mentre aspetto la tua risposta, l'ho portata a 197 , principalmente concentrandomi su v...
Giuseppe,

lengthsn=45nn=11

ah, ha senso, avrebbe dovuto sapere. 187 byte
Giuseppe,

@Giuseppe Grazie, golf impressionante! Ho aggiunto 3 byte per rendere più leggibile l'output (altrimenti finiremo con le stesse soluzioni stampate più volte).
Robin Ryder,

0

Emacs Lisp, 730 byte

(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)))

Non ho trovato un interprete online di Emacs Lisp :( Se hai Emacs installato, puoi copiare il codice in un .elfile, copiare alcune righe di test qui sotto

;; 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

ed eseguirlo $ emacs --script filename.el.

Come funziona

Il mio programma fa una prima ricerca approfondita, a volte capendo che è impossibile vincere e terminare il ramo in cui si trova.

Puoi vedere la spiegazione completa nella versione non abbreviata del codice:

(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))

1
tio.run/##S81NTC7WzcksLvgPBAA puoi inserire il codice qui e provare a eseguirlo?
Koishore Roy

@KoishoreRoy Avevo provato tio.run e non riuscivo a capire perché non funzionasse. Dice "Trailing garbage following expression" e non ho idea di cosa sia e 5 minuti di googling non mi hanno aiutato a risolverlo.
CrabMan
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.