ロック、ペーパー、はさみ、トカゲ、スポックトーナメント


13

5月4日の直後にスタートレックの参照を含むチャレンジを与えることは嫌われるかもしれませんが、ここに行きます。

あなた、ルーク、アナキン、パルパティーン、ヨーダ、ハンソロは、ロック、ペーパー、シザー、トカゲ、スポックの非常識なトーナメントに参加しています。

ここでの問題は、固定された順序の移動のみを使用できることです。注文が「R」の場合、すべてのプレイヤーに負けるか勝つまで、Rockを使用する必要があります。注文がRRVの場合、2つのロックとそれに続くスポックを使用し、勝つか負けるまで繰り返す必要があります。

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

最初の動きでロックをプレイすると、最終的に「L」と「SRR」を破って残りの部分と結びつきます。これは、トカゲとハサミがロックに負けたためです。次にペーパーをプレイすると、「R」を破って残りの2を引きます。これは、ロックがペーパーに負けたためです。次にハサミをプレイすると、ハサミが紙を破るときに「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
イントロで「スタートレック」を「スターウォーズ」に変更してください;)
movatica

1
これはかなり難しい問題です。まあ、または私はこの種のプログラミングが苦手です。
クラブマン

@CrabManこれはゴルフにとって少し難しい問題です。特に実用的な言語で。
コイショアロイ

1
いくつかの作品がありますが、理論上は無限の勝利戦略がありますので、覚えておいてください
Koishore Roy

1
関連、およびKOTH(cc:@Arnauld)
DLosc

回答:


2

ゼリー、29 バイト

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

整数のリスト(それぞれが対戦相手の戦略)のリストを受け入れ、整数のリストのリストを生成する単項リンク-それぞれが勝利戦略(可能な場合は空のリスト)。
1つの戦略リストのみを生成するか0、不可能な場合のみ追加します。)

オンラインでお試しください!(リストを常に表示するフッター形式)

Rock  Paper  Scissors  Spock  Lizard
0     1      2         3      4

または、文字をマップしたバージョンを試してください(戦略を使用して、RPSVL表記法を使用して独自の行に表示します)。

どうやって?

数字は、別のモジュロ5勝よりも大きい奇数になるように選択されます(つまり、スローの内接五角形の端を回って番号が付けられます)。

コードは、すべての戦略(自身を含む)に対して、最も長い戦略の2倍のスローで各戦略をプレイし、負けていないものを保持している敗者を見つけることを保証します。結果の戦略のリストには、完全な勝者がいる場合、単一の戦略が含まれます。勝者がいなければ戦略はありません。またはドローイングプレイヤーがいる場合は複数の戦略。この後、勝つ一連の動きがこれらの戦略のそれぞれに追加されます。

_%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それは機能しません-十分な対戦相手と十分なスローがあるため、サンプルデータでのみ機能しています- これは機能しないものの例です。最長の対戦相手の戦略の2倍のスローを分析する必要があります。(実際のコードはこれと同等です)
ジョナサンアラン

...実際にƊは、コード内のアクションのために、あなたが考えていたかもしれないことさえしていません-それはそれぞれの長さのようにそれぞれを成形し、それらの結果の累積合計を取得するので、誤った値も比較します たとえば、これ試してください -取り込み、[[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

オンラインでお試しください!

どうやって?

これは幅優先の検索です。まず、特定のステップですべての動きを試し、ゲームに勝てるかどうかを確認します。すぐに勝てない場合は、負けていない動きごとに別の動きを追加しようとします。

移動識別子は、移動ような方法で選択されました。ABBAモッド5が奇数の。

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」です。
コイショアロイ

@KoishoreRoy修正されました。
アーナウルド

1
これは実際には素晴らしいアプローチです。私はそれをグラフと考えることすら考えませんでした。未使用のオリジナルアプローチで辞書と逆ルックアップを使用していました(SpockまたはLizardsを使用しない場合)
Koishore Roy

3

R213 190バイト

ジュゼッペのおかげで-23バイト。

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(マトリックスに格納されますv)のし、それらすべてを試行し、すべての勝利戦略を表示します。

この値nにより、TIOでコードが非常に遅くn=4なるため、テストケースに十分なTIO でハードコーディングされています。

最初のテストケースでは、出力は

     1 4 2 4

ソリューションRLSLに対応。

2番目のテストケースでは、出力は

 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は、2人のプレイヤーが永遠にドローするときに発生するNAを取り除くために必要です。

これが最も効率的な戦略だとは思いません。たとえそうだとしても、のコードはmかなりゴルフできると確信しています。


lengths()常にエイリアスを返すのはなぜ4ですか?
ジュゼッペ

1
とにかく、私はあなたの応答を待っている間に、主に次のことに焦点を合わせることで、それを197まで減らしましたv...
ジュゼッペ

@Giuseppe TIO lengthsを強制n=4するためにエイリアスを作成しました。5nn=11)戦略。
ロビンライダー

ああ、理にかなっている、知っているべきでした。187バイト
ジュゼッペ

@ジュゼッペありがとう、印象的なゴルフ!出力を読みやすくするために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 ここにコードを挿入して実行できますか?
コイショアロイ

@KoishoreRoy私はtio.runを試しましたが、なぜ実行しないのかわかりませんでした。「式に続いてゴミを追跡する」と書かれていて、それが何であるかわかりません。
クラブマン
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.