石头,纸,剪刀,蜥蜴,斯波克锦标赛


13

在5月4日之后挑战星际迷航参考可能会让人皱眉,但这是可行的。

您,卢克(Luke),阿纳金(Anakin),帕尔帕廷(Palpatine),尤达(Hoda Solo)和汉·索罗(Han Solo)参与了摇滚,造纸,剪刀,蜥蜴,史波克的疯狂比赛。

这里的要点是只允许您使用固定顺序的移动。如果您的订单为“ R”,则您必须使用Rock,直到您输给所有人或输给每个人。如果您的订单是RRV,那么您必须使用2个Rocks和一个Spock,并不断重复直到您赢了或输了。

卢克(Luke),阿纳金(Anakin),帕尔帕廷(Palpatine),尤达(Yoda)和汉·索罗(Han Solo)分别提交了他们的订单,而作为专家级的黑客,您将掌握他们的每份订单!

有了这些知识,您就可以设计锦标赛的订购。因为每个人都想赢,所以您想创建一个订单,以便击败每个人来赢得比赛。但这并非在所有情况下都可行。

如果有可能的中奖订单,请打印出来。如果您无法取胜,请打印-1(或0或False或“不可能”)

输入:5个订单清单

输出:单个订单或-1

样本输入1

R
P
S
L
V

样本输出1

-1

说明1

无论您采取什么第一步,都将至少有一个人击败您,因此您不可能获胜。

样本输入2

RPS
RPP
R
SRR
L

样本输出2

RPSP

说明2

在第一步中打出Rock之后,您最终击败了“ L”和“ SRR”,并与其余的并列。这是因为蜥蜴和剪刀输给了Rock。当您接下来玩Paper时,您将击败“ R”并与其余的2并列。这是因为Rock输给了Paper。当您接下来玩剪刀时,当剪刀击败Paper时,您将赢得“ RPP”的胜利。

最后,您将在Paper击败Rock时用Paper击败“ RPS”。

以下是符号列表(您可以使用任意5个文字,但请在您的答案中指定):

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

以下是所有可能结果的列表:

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

这是,因此最少的字节获胜。

PS:让我知道您是否需要更多测试用例。


4
请在您的简介
中将

1
这是一个相当困难的问题。好吧,或者我对这种编程很不好。
CrabMan

@CrabMan这是打高尔夫球的难题。特别是实用语言。
Koishore Roy

1
几部作品,但理论上有无限的获胜策略,因此请记住这一点
Koishore Roy

1
Related,也是KOTH(cc:@Arnauld)
DLosc

Answers:


2

果冻,29 个字节

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

一个接受整数列表(每一个都是对手的策略)的单子链接,该列表产生一个整数列表的列表-每个整数都是获胜策略(因此,如果不可能,则为空列表)。
(只需添加仅生成一个策略列表,否则就可以添加0。)

在线尝试!(页脚格式始终显示列表)

Rock  Paper  Scissors  Spock  Lizard
0     1      2         3      4

尝试使用字母映射的版本(采用RPSVL标记并采用自己的方式显示策略)。

怎么样?

选择的数字应使奇数大于另一个以5取模的胜数(即,它们的编号围绕着罚球的内接五边形的边缘编号)。

该代码将每种策略与每种策略(包括它们自身)对决的方法是最长策略的两倍,以确保找到任何失败者,使那些不会失败的失败者得以保留。如果有绝对赢家,则最终的策略列表将包含一个策略。没有赢家就没有策略;或多个策略(如果有吸引玩家)。此后,这些策略中的每一项都将获得一系列成功的举措。

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

我完全新的果冻,但似乎你可以通过更换得到一个字节ZLḤ通过
罗宾·赖德

@RobinRyder那是行不通的-它仅适用于示例数据,因为有足够多的对手且投掷的次数很少- 这是一个行不通的示例。我们需要分析的罚球次数是最长的对手策略的两倍。(您的代码实际上与等效)
乔纳森·艾伦

...实际上,由于Ɗ代码中的作用,它甚至没有按照您的想法进行操作-它按照自己的长度模制每个对象,然后获得这些结果的累加和,因此也将比较不正确的值。例如,尝试进行此操作 -它采用in [[1,2,3,4,5],[6,7],[8]],按照整个列表(3)的长度模制每个以得到,[[1,2,3],[6,7,6],[8,8,8]]然后进行累加以获得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]]
乔纳森·艾伦,

啊,是的,我知道我误会了!
罗宾·赖德

7

的JavaScript(ES6), 122个115  112字节

将输入作为数字字符串数组,并带有:

  • 0 =剪刀(S)
  • 1 =纸张(P)
  • 2 =岩石(R)
  • 3 =蜥蜴(L)
  • 4 =斯波克(V)

返回相同格式的字符串,如果没有解决方案,则返回。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

在线尝试!

怎么样?

这是广度优先的搜索:我们首先尝试在给定步骤进行所有动作,以查看是否能赢得比赛。如果我们现在不能赢,我们会尝试在每个非失败的动作中添加另一个动作。

选择移动标识符的方式是,当且仅当为奇数时,移动才会胜过移动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)

其中andxor是按位运算符。

已评论

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

您的代码在测试用例中失败:test(['P','P','S','P','P']) 答案应该是“ SR”或“ SV”。
Koishore Roy

@KoishoreRoy现在修复。
阿诺尔德

1
这实际上是一个绝妙的方法。我什至没有考虑将其视为图表。我在未使用高尔夫的原始方法中使用了字典和反向查找(没有Spock或
Lizards

3

R213190字节

-23字节,感谢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]]}

在线尝试!

如果存在解决方案,则输出一个。如果没有解决方案,则输出的行NA。如果这种输出格式不可接受,我可以花几个字节来更改它。

动作编码为1 = R,2 = S,3 = P,4 = L,5 = V,因此结果矩阵为

     [,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 =无获胜者; 1 =玩家1获胜; 2 =玩家2获胜)

解决方案的长度(如果存在)的上限是对手的动作列表n=sum(lengths(L))在哪里L。该代码创建所有可能的长度策略n(存储在matrix中v),尝试所有策略,并显示所有获胜策略。

请注意,此值n会使代码在TIO上非常慢,因此我已经在TIO n=4中进行了硬编码,足以用于测试用例。

对于第一个测试用例,输出为

     1 4 2 4

对应于解决方案RLSL。

对于第二个测试用例,输出为

 NA NA NA NA

意味着没有解决方案。

先前版本的说明(我将在可能的情况下更新):

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要摆脱当两个玩家永远画中出现的NAS。

我不认为这是最有效的策略。即使是这样,我m也可以肯定它的代码可以使用很多。


为什么lengths()别名总是返回4
朱塞佩

1
无论如何,在等待您的回应时,我将注意力降低到197,主要是关注v...
朱塞佩

@Giuseppe我已经使用别名lengths来强制n=4TIO,因为否则遍历所有()策略花费的时间太长。 n = 115nn=11
罗宾·赖德

嗯,很有道理,应该知道。187个字节
朱塞佩

@Giuseppe谢谢,令人印象深刻的高尔夫!我添加了3个字节以使输出更具可读性(否则,我们最终将打印出多次相同的解决方案)。
罗宾·赖德

0

Emacs Lisp,730个字节

(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的在线解释器:(如果您安装了Emacs,则可以将代码复制到.el文件中,在下面复制一些测试行

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

并运行它$ emacs --script filename.el

怎么运行的

我的程序会进行深度优先搜索,有时会弄清楚不可能取胜并终止其所在的分支。

您可以在未简化的代码版本中看到完整的解释:

(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 您可以在此处插入代码并尝试运行它吗?
Koishore Roy

@KoishoreRoy我尝试过tio.run,但我不知道为什么它无法运行。它说:“在表达后跟踪垃圾”,我不知道那是什么,而谷歌搜索5分钟并没有帮助我解决它。
CrabMan
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.