Rock, Kertas, Gunting, Kadal, Turnamen Spock

13

Memberikan tantangan yang melibatkan referensi Star Trek tepat setelah 4 Mei mungkin disukai, tapi begini saja.

Anda, Luke, Anakin, Palpatine, Yoda dan Han Solo terlibat dalam turnamen gila Rock, Paper, Scissor, Lizard, Spock.

Tangkapannya di sini adalah Anda hanya diizinkan menggunakan urutan gerakan yang tetap. Jika pesanan Anda adalah "R", Maka Anda harus menggunakan Rock, sampai Anda kalah atau menang melawan semua orang. Jika pesanan Anda RRV, maka Anda harus menggunakan 2 Batu diikuti oleh Spock dan terus mengulangi sampai Anda menang atau kalah.

Luke, Anakin, Palpatine, Yoda dan Han Solo telah mengirimkan pesanan mereka masing-masing dan Anda sebagai peretas ahli dapat memesan setiap pesanan mereka!

Dengan pengetahuan ini, Anda harus merancang pemesanan Anda untuk turnamen. Karena semua orang ingin menang, Anda ingin membuat pemesanan sehingga Anda memenangkan turnamen dengan mengalahkan semua orang. Tetapi ini mungkin tidak mungkin dilakukan dalam semua keadaan.

Jika ada kemungkinan pesanan yang menang, cetaklah itu. Jika tidak ada cara yang memungkinkan bagi Anda untuk menang, cetak -1 (atau 0 atau Salah atau "tidak mungkin")

Input : daftar 5 pesanan

Output : satu urutan atau -1

Input Sampel 1

R
P
S
L
V

Output Sampel 1

-1

Penjelasan 1

Tidak peduli apa yang Anda mainkan di langkah pertama Anda, akan ada setidaknya satu orang yang mengalahkan Anda, oleh karena itu tidak mungkin bagi Anda untuk menang.

Input Sampel 2

RPS
RPP
R
SRR
L

Keluaran Sampel 2

RPSP

Penjelasan 2

Setelah Anda memainkan Rock di gerakan pertama Anda, Anda akhirnya mengalahkan "L" dan "SRR" dan mengikat sisanya. Ini karena Kadal dan Gunting kalah dari Rock. Ketika Anda memainkan Paper berikutnya, Anda akan mengalahkan "R" dan mengikat 2 yang tersisa. Ini karena Rock kalah dari Paper. Ketika Anda memainkan Gunting berikutnya, Anda akan menang melawan "RPP" saat Scissor mengalahkan Paper.

Akhirnya, Anda akan mengalahkan "RPS" dengan Kertas Anda sebagai Kertas mengalahkan Rock.

Berikut adalah daftar notasi (Anda dapat menggunakan 5 literal apa pun, tetapi harap cantumkan dalam jawaban Anda):

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

Berikut adalah daftar semua hasil yang mungkin:

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

Ini , byte paling sedikit menang.

PS: Beri tahu saya kalau perlu lebih banyak test case.

Koishore Roy
sumber
4
Ubah "Star Trek " menjadi "Star Wars " di intro Anda;)
movatica
1
Ini masalah yang cukup sulit. Ya, atau saya buruk dalam pemrograman semacam ini.
CrabMan
@ CrabMan Ini adalah sedikit masalah yang sulit untuk golf. khususnya dalam bahasa praktis.
Koishore Roy
1
beberapa karya, tetapi ada dalam teori strategi menang tanpa batas, jadi ingatlah itu
Koishore Roy
1
Terkait , dan juga KOTH (cc: @Arnauld)
DLosc

Jawaban:

2

Jelly , 29 byte

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

Tautan monadik yang menerima daftar daftar bilangan bulat (masing-masing merupakan strategi lawan) yang menghasilkan daftar daftar bilangan bulat - yang masing-masing adalah strategi kemenangan (jadi daftar kosong jika tidak ada yang mungkin).
(Tambahkan saja hanya menghasilkan daftar strategi tunggal atau 0jika tidak mungkin.)

Cobalah online! (format footer untuk selalu menampilkan daftar)

Rock  Paper  Scissors  Spock  Lizard
0     1      2         3      4

Atau coba versi surat dipetakan (di mana strategi diambil dan ditampilkan pada baris mereka sendiri menggunakan RPSVLnotasi).

Bagaimana?

Angka-angka dipilih sedemikian rupa sehingga setiap yang merupakan angka ganjil lebih besar dari kemenangan modulo lima lainnya (yaitu mereka diberi nomor sekitar tepi pentagon tertulis dari lemparan).

Kode memainkan setiap strategi dari setiap strategi (termasuk diri mereka sendiri) untuk dua kali lebih banyak lemparan dari strategi terpanjang untuk memastikan menemukan setiap pecundang menjaga mereka yang tidak dikalahkan. Daftar strategi yang dihasilkan akan berisi strategi tunggal jika ada pemenang langsung; tidak ada strategi jika tidak ada pemenang; atau beberapa strategi jika ada pemain gambar. Setelah ini, serangkaian gerakan yang menang ditambahkan ke masing-masing strategi ini.

_%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
sumber
Aku benar-benar baru untuk Jelly, tetapi tampaknya bahwa Anda dapat memperoleh byte dengan mengganti ZLḤdengan .
Robin Ryder
@RobinRyder Itu tidak akan berhasil - ini hanya bekerja dengan data contoh karena ada cukup banyak lawan dan beberapa lemparan yang cukup - ini adalah contoh dari yang tidak akan berfungsi . Kami perlu menganalisis dua kali lebih banyak lemparan dari strategi lawan terpanjang. (Kode Anda sebenarnya setara dengan ini )
Jonathan Allan
... sebenarnya karena tindakan Ɗdalam kode Anda, itu bahkan tidak melakukan apa yang Anda pikir - itu mencetak masing-masing seperti itu panjangnya sendiri kemudian mendapatkan jumlah kumulatif dari hasil tersebut, jadi juga akan membandingkan nilai yang salah juga. Coba ini misalnya - dibutuhkan [[1,2,3,4,5],[6,7],[8]], cetakan masing-masing dengan panjang seluruh daftar (3) untuk mendapatkan [[1,2,3],[6,7,6],[8,8,8]]kemudian melakukan akumulasi untuk mendapatkan [[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 ya, saya tahu saya salah paham tentang sesuatu!
Robin Ryder
7

JavaScript (ES6),  122 115  112 byte

Mengambil input sebagai larik string digit, dengan:

  • 0
  • 1
  • 2
  • 3
  • 4

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

Cobalah online!

Bagaimana?

Ini adalah pencarian pertama yang luas: pertama-tama kita mencoba semua gerakan pada langkah yang diberikan untuk melihat apakah kita bisa memenangkan permainan. Jika kami tidak bisa menang sekarang, kami mencoba menambahkan langkah lain ke setiap langkah yang tidak kalah.

AB(BA)mod5

AB

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

ABAB

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

di mana anddan xormerupakan operator bitwise.

Berkomentar

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
sumber
kode Anda gagal untuk kasus uji: test(['P','P','S','P','P']) Jawabannya harus "SR" atau "SV".
Koishore Roy
@ KoishoreRoy Sekarang sudah diperbaiki.
Arnauld
1
Ini sebenarnya pendekatan yang brilian. Aku bahkan tidak menganggapnya sebagai grafik. Saya menggunakan kamus dan membalikkan look-up dalam pendekatan asli saya yang ungolfed (tanpa Spock atau Lizards yaitu)
Koishore Roy
3

R , 213 190 byte

-23 byte terima kasih kepada 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]]}

Cobalah online!

Jika ada solusi, itu menghasilkan satu. Jika tidak ada solusi, ini menghasilkan deretan NA. Jika format output ini tidak dapat diterima, saya dapat mengubahnya dengan biaya beberapa byte.

Gerakan dikodekan sebagai 1 = R, 2 = S, 3 = P, 4 = L, 5 = V, sehingga matriks hasil adalah

     [,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 = tidak ada pemenang; 1 = pemain 1 menang; 2 = pemain 2 menang)

Batas atas pada panjang solusi jika ada adalah di n=sum(lengths(L))mana Ldaftar gerakan lawan. Kode menciptakan semua strategi panjang yang mungkin n(disimpan dalam matriks v), mencoba semuanya, dan menampilkan semua strategi yang menang.

Perhatikan bahwa nilai ini nmembuat kode sangat lambat pada TIO, jadi saya telah melakukan hardcod pada TIO n=4yang cukup untuk kasus uji.

Untuk test case pertama, outputnya adalah

     1 4 2 4

sesuai dengan solusi RLSL.

Untuk test case kedua, outputnya adalah

 NA NA NA NA

artinya tidak ada solusi.

Penjelasan dari versi sebelumnya (akan diperbarui ketika saya bisa):

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
}

Hal whichini diperlukan untuk menghilangkan NAS yang terjadi ketika kedua pemain bermain imbang selamanya.

Saya tidak yakin ini adalah strategi yang paling efisien. Bahkan jika itu, saya yakin kode untuk mbisa bermain golf cukup sedikit.

Robin Ryder
sumber
mengapa lengths()alias selalu kembali 4?
Giuseppe
1
Ngomong-ngomong, selagi saya menunggu tanggapan Anda, saya memasukkannya ke 197 , kebanyakan dengan berfokus pada v...
Giuseppe
lengthsn=45nn=11
ah, masuk akal, seharusnya tahu. 187 byte
Giuseppe
@Giuseppe Terima kasih, golf yang mengesankan! Saya menambahkan 3 byte untuk membuat output lebih mudah dibaca (jika tidak, kita akan berakhir dengan solusi yang sama yang dicetak berkali-kali).
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)))

Saya tidak menemukan juru bahasa online Emacs Lisp :( Jika Anda telah menginstal Emacs, Anda dapat menyalin kode ke .elfile, menyalin beberapa jalur pengujian di bawah ini

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

dan jalankan $ emacs --script filename.el.

Bagaimana itu bekerja

Program saya melakukan pencarian mendalam terlebih dahulu dengan kadang-kadang mencari tahu bahwa tidak mungkin untuk memenangkan dan mengakhiri cabang yang aktif.

Anda dapat melihat penjelasan lengkap dalam versi kode yang tidak dipendekkan:

(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
sumber
1
tio.run/##S81NTC7WzcksLvgPBAA dapatkah Anda memasukkan kode di sini dan mencoba menjalankannya?
Koishore Roy
@ KoishoreRoy Saya sudah mencoba tio.run dan saya tidak tahu mengapa itu tidak berjalan. Ia mengatakan "Membuntuti sampah mengikuti ekspresi" dan saya tidak tahu apa itu dan 5 menit googling tidak membantu saya untuk memperbaikinya.
CrabMan