Đá, Giấy, Kéo, Thằn lằn, Giải đấu Spock


13

Đưa ra một thử thách liên quan đến một tài liệu tham khảo Star Trek ngay sau ngày 4 tháng 5 có thể được tán thành, nhưng rồi đây.

Bạn, Luke, Anakin, Palpatine, Yoda và Han Solo tham gia vào một giải đấu điên rồ gồm Rock, Paper, Scissor, Lizard, Spock.

Điều hấp dẫn ở đây là bạn chỉ được phép sử dụng một trật tự di chuyển cố định. Nếu đơn đặt hàng của bạn là "R", thì bạn phải sử dụng Rock, cho đến khi bạn thua hoặc giành chiến thắng trước mọi người. Nếu đơn đặt hàng của bạn là RRV, thì bạn phải sử dụng 2 Đá theo sau là Spock và tiếp tục lặp lại cho đến khi bạn thắng hoặc thua.

Luke, Anakin, Palpatine, Yoda và Han Solo đã gửi đơn đặt hàng tương ứng của họ và bạn là một hacker chuyên gia đã nắm trong tay từng đơn hàng của họ!

Với kiến ​​thức này, bạn sẽ thiết kế thứ tự cho giải đấu. Vì mọi người đều muốn giành chiến thắng, bạn muốn tạo một đơn đặt hàng sao cho bạn chiến thắng giải đấu bằng cách đánh bại tất cả mọi người. Nhưng điều này có thể không thể trong mọi trường hợp.

Trong trường hợp có một lệnh chiến thắng có thể, in nó ra. Nếu không có cách nào khả thi để bạn giành chiến thắng, hãy in ra -1 (hoặc 0 hoặc Sai hoặc "không thể")

Đầu vào : danh sách 5 đơn hàng

Đầu ra : một đơn hàng hoặc -1

Đầu vào mẫu 1

R
P
S
L
V

Kết quả mẫu 1

-1

Giải thích 1

Bất kể bạn chơi gì trong lần di chuyển đầu tiên, sẽ có ít nhất một người đánh bại bạn, do đó bạn không thể giành chiến thắng.

Đầu vào mẫu 2

RPS
RPP
R
SRR
L

Kết quả mẫu 2

RPSP

Giải thích 2

Khi bạn chơi Rock trong lần di chuyển đầu tiên, cuối cùng bạn sẽ đánh bại "L" và "SRR" và trói lại với những người còn lại. Điều này là do Lizard và Kéo thua Rock. Khi bạn chơi Paper tiếp theo, bạn sẽ đánh bại "R" và hòa với số còn lại 2. Điều này là do Rock thua Paper. Khi bạn chơi Kéo tiếp theo, bạn sẽ giành chiến thắng trước "RPP" khi Scissor đánh bại Giấy.

Cuối cùng, bạn sẽ đánh bại "RPS" bằng Paper của mình khi Paper beats Rock.

Dưới đây là danh sách các ký hiệu (bạn có thể sử dụng bất kỳ 5 chữ nào, nhưng vui lòng ghi rõ trong câu trả lời của bạn):

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

Dưới đây là danh sách tất cả các kết quả có thể xảy ra:

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

Đây là , vì vậy ít byte nhất giành chiến thắng.

PS: Hãy cho tôi biết nếu bạn cần thêm trường hợp thử nghiệm.


4
Vui lòng thay đổi "Star Trek " thành "Star Wars " trong phần giới thiệu của bạn;)
Movatica

1
Đây là một vấn đề khá khó khăn. Vâng, hoặc tôi xấu ở loại lập trình này.
Crabman

@CrabMan Đây là một vấn đề khó khăn đối với golf. đặc biệt là trong các ngôn ngữ thực tế.
Koishore Roy

1
Một số tác phẩm, nhưng về lý thuyết là chiến lược chiến thắng vô hạn, vì vậy hãy ghi nhớ điều đó
Koishore Roy

1
Liên quan , và cũng là một KOTH (cc: @Arnauld)
DLosc

Câu trả lời:


2

Thạch , 29 byte

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

Liên kết đơn thể chấp nhận danh sách các số nguyên (mỗi danh sách là chiến lược của đối thủ) sẽ đưa ra danh sách danh sách các số nguyên - mỗi danh sách là một chiến lược chiến thắng (vì vậy không có danh sách nào có thể chiến lược).
(Chỉ cần thêm vào chỉ mang lại một danh sách chiến lược duy nhất hoặc 0nếu không thể.)

Hãy thử trực tuyến! (các định dạng chân trang để luôn hiển thị danh sách)

Rock  Paper  Scissors  Spock  Lizard
0     1      2         3      4

Hoặc thử một phiên bản được ánh xạ thư (trong đó các chiến lược được thực hiện và hiển thị trên các dòng riêng bằng cách sử dụng RPSVLký hiệu).

Làm sao?

Các số được chọn sao cho bất kỳ số nào là số lẻ lớn hơn một số năm modulo khác (nghĩa là chúng được đánh số đi quanh rìa của một hình ngũ giác được ghi của các cú ném).

Mã này loại bỏ từng chiến lược chống lại mọi chiến lược (bao gồm cả chính họ) với số lần ném nhiều gấp đôi so với chiến lược dài nhất để đảm bảo tìm ra bất kỳ kẻ thua cuộc nào giữ những kẻ không bị đánh bại. Danh sách kết quả của các chiến lược sẽ chứa một chiến lược duy nhất nếu có một người chiến thắng hoàn toàn; không có chiến lược nếu không có người chiến thắng; hoặc nhiều chiến lược nếu có người chơi vẽ. Sau đó, một loạt các động thái chiến thắng được thêm vào từng chiến lược này.

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

Tôi hoàn toàn mới với Jelly, nhưng dường như bạn có thể đạt được một byte bằng cách thay thế ZLḤbằng .
Robin Ryder

@RobinRyder Điều đó sẽ không hoạt động - nó chỉ hoạt động với dữ liệu mẫu vì có đủ đối thủ và ít cú ném - đây là một ví dụ về cách không hoạt động . Chúng ta cần phân tích gấp đôi số lần ném so với chiến lược đối thủ dài nhất. (Mã của bạn thực sự tương đương với điều này )
Jonathan Allan

... thực sự là do hành động Ɗtrong mã của bạn, nó thậm chí không làm những gì bạn có thể nghĩ - nó tạo ra mỗi chiều dài của chính nó sau đó nhận được tổng số tích lũy của các kết quả đó, do đó cũng sẽ so sánh các giá trị không chính xác. Hãy thử điều này chẳng hạn - phải mất [[1,2,3,4,5],[6,7],[8]], tạo khuôn theo chiều dài của toàn bộ danh sách (3) để lấy [[1,2,3],[6,7,6],[8,8,8]]sau đó thực hiện tích lũy để get [[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

À vâng, tôi biết tôi đã hiểu lầm điều gì đó!
Robin Ryder

7

JavaScript (ES6),  122 115  112 byte

Lấy đầu vào dưới dạng một chuỗi các chữ số, với:

  • 0
  • 1
  • 2
  • 3
  • 4

fmộttôiSe

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

Hãy thử trực tuyến!

Làm sao?

Đây là một tìm kiếm đầu tiên theo chiều rộng: trước tiên chúng tôi thử tất cả các động tác ở một bước nhất định để xem liệu chúng tôi có thể giành chiến thắng trong trò chơi hay không. Nếu chúng tôi không thể giành chiến thắng ngay bây giờ, chúng tôi sẽ cố gắng thêm một nước đi khác cho mỗi nước đi không thua.

MộtB(B-Một)mod5

MộtB

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

MộtBMộtB

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

ở đâu andxorlà các toán tử bitwise.

Đã bình luận

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

mã của bạn không thành công cho trường hợp kiểm tra: test(['P','P','S','P','P']) Câu trả lời phải là "SR" hoặc "SV".
Koishore Roy

@KoishoreRoy Bây giờ đã sửa.
Arnauld

1
Đây thực sự là một cách tiếp cận tuyệt vời. Tôi thậm chí không nghĩ đến việc coi nó như một biểu đồ. Tôi đã sử dụng từ điển và tra cứu ngược trong cách tiếp cận ban đầu không có ý thức của mình (không có Spock hoặc Lizards tức là)
Koishore Roy

3

R , 213 190 byte

-23 byte nhờ 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]]}

Hãy thử trực tuyến!

Nếu một giải pháp tồn tại, nó đưa ra một. Nếu không có giải pháp, nó xuất ra một hàng NA. Nếu định dạng đầu ra này không được chấp nhận, tôi có thể thay đổi nó với chi phí một vài byte.

Di chuyển được mã hóa là 1 = R, 2 = S, 3 = P, 4 = L, 5 = V, do đó ma trận kết quả là

     [,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 = không có người chiến thắng; 1 = người chơi 1 thắng; 2 = người chơi 2 thắng)

Một giới hạn trên cho chiều dài của giải pháp nếu nó tồn tại là n=sum(lengths(L))nơi Llà danh sách các di chuyển của đối phương. Mã này tạo ra tất cả các chiến lược có thể có về chiều dài n(được lưu trữ trong ma trận v), thử tất cả chúng và hiển thị tất cả các chiến lược chiến thắng.

Lưu ý rằng giá trị này nlàm cho mã rất chậm trên TIO, vì vậy tôi đã mã hóa cứng trong TIO n=4, đủ cho các trường hợp thử nghiệm.

Đối với trường hợp thử nghiệm đầu tiên, đầu ra là

     1 4 2 4

tương ứng với giải pháp RLSL.

Đối với trường hợp thử nghiệm thứ hai, đầu ra là

 NA NA NA NA

nghĩa là không có giải pháp.

Giải thích về phiên bản trước (sẽ cập nhật khi tôi có thể):

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
}

Điều whichcần thiết là phải loại bỏ NA xảy ra khi hai người chơi hòa nhau mãi mãi.

Tôi không tin đây là chiến lược hiệu quả nhất. Ngay cả nếu có, tôi chắc chắn rằng mã cho mcó thể được chơi golf khá nhiều.


Tại sao lengths()bí danh luôn luôn trở lại 4?
Giuseppe

1
Dù sao, trong khi chờ đợi phản hồi của bạn, tôi đã đánh golf xuống còn 197 , chủ yếu bằng cách tập trung vào v...
Giuseppe

lengthsn=45nn= =11

ah, có ý nghĩa, nên đã biết. 187 byte
Giuseppe

@Giuseppe Cảm ơn, chơi golf ấn tượng! Tôi đã thêm 3 byte để làm cho đầu ra dễ đọc hơn (nếu không chúng tôi kết thúc với cùng một giải pháp được in nhiều lần).
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)))

Tôi không tìm thấy trình thông dịch trực tuyến của Emacs Lisp :( Nếu bạn đã cài đặt Emacs, bạn có thể sao chép mã vào một .eltệp, sao chép một số dòng thử nghiệm bên dưới

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

và chạy nó $ emacs --script filename.el.

Làm thế nào nó hoạt động

Chương trình của tôi thực hiện tìm kiếm sâu đầu tiên với đôi khi nhận ra rằng không thể giành chiến thắng và chấm dứt chi nhánh.

Bạn có thể xem giải thích đầy đủ trong phiên bản chưa được chỉnh sửa của mã:

(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 bạn có thể chèn mã của mình vào đây và thử chạy nó không?
Koishore Roy

@KoishoreRoy Tôi đã thử tio.run và tôi không thể hiểu tại sao nó không chạy. Nó ghi "Trailing rác theo biểu thức" và tôi không biết đó là gì và 5 phút googling đã không giúp tôi sửa nó.
Crabman
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.