Taş, Kağıt, Makas, Kertenkele, Spock Turnuvası


13

4 Mayıs'tan hemen sonra bir Star Trek referansı içeren bir meydan okuma vermek kaşlarını çatmış olabilir, ama işte gidiyor.

Sen, Luke, Anakin, Palpatine, Yoda ve Han Solo Rock, Kağıt, Makas, Kertenkele, Spock gibi çılgın bir turnuvaya katıldın.

Buradaki yakalama, yalnızca sabit bir hamle sırası kullanmanıza izin verilmesidir. Siparişiniz "R" ise, o zaman herkesi kaybetene veya kazanana kadar Rock kullanmak zorundasınız. Siparişiniz RRV ise, 2 Rocks ve ardından bir Spock kullanmanız ve kazanana veya kaybetene kadar tekrarlamaya devam etmeniz gerekir.

Luke, Anakin, Palpatine, Yoda ve Han Solo kendi siparişlerini verdiler ve uzman bir bilgisayar korsanı olmanın her emrini ele geçirin!

Bu bilgi ile turnuva için siparişinizi tasarlayacaksınız. Herkes kazanmak istediğinden, turnuvayı herkesi yenerek kazanacak şekilde bir sipariş oluşturmak istersiniz. Ancak bu her koşulda mümkün olmayabilir.

Olası bir kazanan sipariş varsa, bunu yazdırın. Kazanmanın olası bir yolu yoksa, -1 (veya 0 veya Yanlış veya "imkansız") yazdırın

Girdi : 5 sipariş listesi

Çıktı : tek bir sipariş veya -1

Örnek Giriş 1

R
P
S
L
V

Örnek Çıktı 1

-1

Açıklama 1

İlk hamlenizde ne oynarsanız oynayın, sizi yenen en az bir kişi olacak, bu yüzden kazanmanız mümkün değil.

Örnek Giriş 2

RPS
RPP
R
SRR
L

Örnek Çıktı 2

RPSP

Açıklama 2

İlk hamlenizde Rock oynadığınızda, "L" ve "SRR" yi dövüyorsunuz ve geri kalanına karşı bağlıyorsunuz. Bunun nedeni Kertenkele ve Makas'ın Rock'ı kaybetmesidir. Daha sonra Paper oynadığınızda, "R" yi dövecek ve kalan 2'ye bağlanacaksınız. Bunun nedeni, Rock'ın Paper'a kaybetmesidir. Bir sonraki Scissors oynadığınızda, Scissor Paper'ı geçtiği için "RPP" ye karşı kazanacaksınız.

Son olarak, Paper Rock'ı yenerken Paper ile "RPS" yi yeneceksiniz.

Gösterimlerin listesi (5 değişmez değeri kullanabilirsiniz, ancak lütfen cevabınızda belirtin):

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

İşte olası tüm sonuçların bir listesi:

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

Bu , en az bayt kazanır.

Not: Daha fazla test vakasına ihtiyacınız varsa bana bildirin.


4
Lütfen girişinizde "Star Trek " i "Star Wars " olarak değiştirin;)
movatica

1
Bu oldukça zor bir problem. Ya da bu tür programlamada kötüyüm.
CrabMan

@CrabMan Bu, golf oynamak için biraz zor bir problem. özellikle pratik dillerde.
Koishore Roy

1
birkaç eser, ancak teoride sonsuz kazanma stratejileri var, bu yüzden bunu aklınızda bulundurun
Koishore Roy

1
İlgili ve ayrıca bir KOTH (cc: @Arnauld)
DLosc

Yanıtlar:


2

Jöle , 29 bayt

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

Her biri kazanan bir strateji olan tamsayıların bir listesini veren tam sayı listelerinin (her biri bir rakibin stratejisi olan) bir listesini kabul eden monadik bir Bağlantı (böylece mümkün değilse boş bir liste).
(Sadece tek bir strateji listesi vermek veya 0imkansızsa ekleyin .)

Çevrimiçi deneyin! (altbilgiler her zaman listeleri gösterecek şekilde biçimlendirilir)

Rock  Paper  Scissors  Spock  Lizard
0     1      2         3      4

Veya harf eşlemeli bir sürümü deneyin (burada stratejiler alınır ve RPSVLgösterim kullanılarak kendi satırlarında gösterilir ).

Nasıl?

Sayılar, başka bir modulo beş galibinden daha büyük olan tek bir sayı olacak şekilde seçilir (yani, atışların yazılı bir pentagonunun kenarı boyunca ilerleyerek numaralandırılır).

Kod, her stratejiyi (kendileri de dahil olmak üzere) her bir stratejiye karşı, en uzun stratejinin iki katı kadar atar ve böylece yenilmeyenleri kaybedenleri kaybedenlerin bulunmasını sağlar. Sonuçta elde edilen stratejiler listesi, açık bir kazanan varsa, tek bir strateji içerecektir; kazanan yoksa strateji yok; veya çizim oyuncuları varsa çoklu stratejiler. Bundan sonra, bu stratejilerin her birine kazanan bir hamle seti eklenir.

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

Ben Jelly tamamen yeni, ama öyle görünüyor Eğer değiştirerek bayt kazanabilirler ZLḤtarafından .
Robin Ryder

@RobinRyder Bu işe yaramayacak - sadece örnek verilerle çalışıyor çünkü yeterli sayıda rakip ve yeterli atış var - bu işe yaramayacak bir örnek . En uzun rakip stratejisinin iki katı atışları analiz etmemiz gerekiyor. (Kodunuz aslında eşdeğerdir bu )
Jonathan Allan

... aslında Ɗkodunuzdaki eylem nedeniyle , düşündüğünüzü bile yapmıyor - her birini kendi uzunluğu gibi şekillendiriyor, sonra bu sonuçların kümülatif toplamlarını alıyor, bu yüzden de yanlış değerleri karşılaştıracak. Örneğin bunu deneyin - almak için [[1,2,3,4,5],[6,7],[8]], her listenin (3) uzunluğuna göre kalıplar alır ve [[1,2,3],[6,7,6],[8,8,8]]sonra [[1,1+2,1+2+3],[6,6+7,6+7+8],[8,8+8,8+8+8]]= almak için biriktirir [[1,3,6],[6,13,19],[8,16,24]].
Jonathan Allan

Ah evet, bir şeyi yanlış anladığımı biliyordum!
Robin Ryder

7

JavaScript (ES6),  122 115  112 bayt

Girdileri, bir dizi basamak dizisi olarak alır:

  • 0 = Makas (S)
  • 1 = Kağıt (P)
  • 2 = Kaya (R)
  • 3 = Kertenkele (L)
  • 4 = Spock (V)

Aynı biçimde bir dize veya çözüm yoksa döndürür .false

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

Çevrimiçi deneyin!

Nasıl?

Bu, önce bir arama: ilk olarak, oyunu kazanıp kazanamayacağımızı görmek için belirli bir adımda tüm hamleleri deneriz. Eğer şimdi kazanamazsak, kaybetmeyen her hamleye bir hamle daha eklemeye çalışırız.

Hareket tanımlayıcıları, ( tek olması halinde hareketinin hareketine karşı kazanacağı şekilde seçilmiştir .AB(BA)mod5

İle sol ve üzerinde üstünde:AB

(S)(P)(R)(L)(V)01234(S) 01234(P) 14123(R) 23412(L) 32341(V) 41234

Oradan, eğer test etmenin başka bir yolunu çıkarabiliriz karşı kazanır için :ABAB

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

nerede andve xorbitsel operatörler.

Yorumlananlar

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

kodunuz test senaryosu için başarısız oluyor: test(['P','P','S','P','P']) Yanıt "SR" veya "SV" olmalıdır.
Koishore Roy

@KoishoreRoy Şimdi düzeltildi.
Arnauld

1
Bu aslında mükemmel bir yaklaşım. Bunu bir grafik olarak düşünmeyi bile düşünmedim. Kurtulmamış orijinal yaklaşımımda sözlükler ve ters aramalar kullanıyordum (Spock veya Kertenkeleler olmadan)
Koishore Roy

3

R , 213190 bayt

Giuseppe sayesinde 23 bayt.

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]]}

Çevrimiçi deneyin!

Bir çözüm varsa, bir çözüm üretir. Çözüm yoksa, bir satır çıkarır NA. Bu çıktı biçimi kabul edilebilir değilse, birkaç bayt maliyetle değiştirebilirim.

Hareketler 1 = R, 2 = S, 3 = P, 4 = L, 5 = V olarak kodlanır, böylece sonuç matrisi

     [,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 = kazanan yok; 1 = 1 oyuncu kazanır; 2 = 2 oyuncu kazanır)

Bir üst öyle varsa çözümün uzunluğuna bağlı n=sum(lengths(L))nerede Lrakibin hamle listesidir. Kod n(matriste depolanan v) olası tüm uzunluk stratejilerini oluşturur, hepsini dener ve kazanan tüm stratejileri görüntüler.

Not Bunun bu değer nben TIO kodlanmış olan çok yavaş TIO kod, bu yüzden markaların n=4test durumları için yeterlidir.

İlk test durumu için çıktı

     1 4 2 4

RLSL çözeltisine karşılık gelir.

İkinci test durumu için çıktı

 NA NA NA NA

yani çözüm yok.

Önceki bir sürümün açıklaması (yapabildiğimde güncellenecektir):

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
}

whichİki oyuncunun sonsuza çizerken meydana UA'lara kurtulmak için gereklidir.

Bunun en etkili strateji olduğuna ikna olmadım. mOlsa bile, ben kodu biraz golf olabilir eminim.


neden lengths()her zaman geri dönmek için takma adıdır 4?
Giuseppe

1
Ben Yanıtınız için beklerken Neyse, ben onu aşağı golfed 197 çoğunlukla odaklanarak, v...
Giuseppe

@Giuseppe TIO'ya lengthszorlamak n=4için takma ad verdim , aksi takdirde tüm ( ) stratejilerinin üzerinden geçmek çok uzun sürüyor . n = 115nn=11
Robin Ryder

ah, mantıklı, bilmeliydi. 187 bayt
Giuseppe

@Giuseppe Teşekkürler, etkileyici golf! Çıktıyı daha okunabilir hale getirmek için 3 bayt ekledim (aksi takdirde, birçok kez yazdırılan aynı çözüm (ler) ile sonuçlanırız).
Robin Ryder

0

Emacs Lisp, 730 bayt

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

Emacs Lisp'in çevrimiçi bir çevirmenini bulamadım :( Emacs yüklüyse, kodu bir .eldosyaya kopyalayabilir, aşağıdaki bazı test satırlarını kopyalayabilirsiniz

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

ve çalıştırın $ emacs --script filename.el.

Nasıl çalışır

Programım önce derinlemesine arama yapıyor, bazen üzerinde bulunduğu dalı kazanmanın ve sonlandırmanın imkansız olduğunu anlayarak.

Kodun kısaltılmamış versiyonunda tam açıklamayı görebilirsiniz:

(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 kodunuzu buraya girip çalıştırmayı deneyebilir misiniz?
Koishore Roy

@KoishoreRoy tio.run'u denemiştim ve neden çalışmadığını anlayamadım. "İfadeyi takiben çöpü takip ediyor" yazıyor ve bunun ne olduğu hakkında hiçbir fikrim yok ve 5 dakikalık googling bunu düzeltmeme yardımcı olmadı.
CrabMan
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.