Турнір із скелі, паперу, ножиць, ящірок, спок


13

Виклик, пов’язаний із посиланням на Star Trek відразу після 4 травня, може нахмуритися, але ось це.

Ви, Люк, Анакін, Палпатін, Йода та Хан Соло, берете участь у шаленому турнірі з року, паперу, ножиць, ящірок, спока.

Привід тут полягає в тому, що вам дозволяється використовувати лише фіксований порядок рухів. Якщо ваше замовлення "R", тоді вам доведеться використовувати Рок, поки ви не програєте або виграєте проти всіх. Якщо ваше замовлення - RRV, вам доведеться використовувати 2 Скелі, а потім Спок, і продовжувати повторювати, поки ви не виграли чи програли.

Лука, Анакін, Палпатін, Йода та Хан Соло подали свої відповідні замовлення, і ви, як експерт-хакер, отримали свої руки на кожне їх замовлення!

Маючи ці знання, ви повинні розробити замовлення на турнір. Оскільки всі хочуть виграти, ви хочете створити таке замовлення, щоб ви виграли турнір, перемігши всіх. Але це може бути неможливо за будь-яких обставин.

У випадку, якщо є можливе виграшне замовлення, роздрукуйте його. Якщо ви не можете перемогти, роздрукуйте -1 (або 0 або помилково або "неможливо")

Введення : список із 5 замовлень

Вихід : єдине замовлення або -1

Зразок введення 1

R
P
S
L
V

Вибірка зразка 1

-1

Пояснення 1

Незалежно від того, в що ви граєте в свій перший хід, знайдеться принаймні одна людина, яка б’є вас, отже, виграти це неможливо.

Зразок введення 2

RPS
RPP
R
SRR
L

Вибірка зразка 2

RPSP

Пояснення 2

Як тільки ви граєте Рок у своєму першому русі, ви в кінцевому рахунку б'єте "L" і "SRR" і прив'язуєтесь до решти. Це тому, що Ящірка та Ножиці програють Року. Коли ви будете грати у папері далі, ви будете бити "R" і прив'язувати до решти 2. Це тому, що Rock втрачає Paper. Якщо ви будете грати у ножиці далі, ви виграєте проти "RPP", коли Scissor б'є папір.

Нарешті, ви будете бити "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
Пов’язаний , а також КОТАН (cc: @Arnauld)
DLosc

Відповіді:


2

Желе , 29 байт

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

Монадична посилання, яка приймає список списків цілих чисел (кожне з яких є стратегією противника), що дає список списків цілих чисел - кожне з яких є виграшною стратегією (тому порожній список, якщо жоден не можливий).
(Просто додайте, щоб отримати лише один список стратегій або, 0якщо це неможливо.)

Спробуйте в Інтернеті! (формати нижнього колонтитулу, щоб завжди відображати списки)

Rock  Paper  Scissors  Spock  Lizard
0     1      2         3      4

Або спробуйте версію з картою (де стратегії приймаються та відображаються у власних рядках за допомогою RPSVLпозначень).

Як?

Цифри вибираються такими, що будь-яке непарне число перевищує іншу модульну п'ять виграш (тобто вони пронумеровані навколо краю вписаного п'ятикутника кидків).

Код відтворює кожну стратегію проти кожної стратегії (включаючи себе) для вдвічі більшої кількості кидків, ніж найдовша стратегія, щоб забезпечити пошук тих, хто програв, утримуючи тих, хто не зазнав поразки. Отриманий перелік стратегій буде містити єдину стратегію, якщо є відвертий переможець; немає стратегій, якщо не було переможця; або кілька стратегій, якщо є гравці з малюванням. Після цього виграшний набір кроків додається до кожної з цих стратегій.

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

Я абсолютно новий в Jelly, але, здається , ви можете отримати байт, замінивши ZLḤна .
Робін Райдер

@RobinRyder Це не спрацює - це працює лише з прикладними даними, тому що є достатньо супротивників і мало кидків - це приклад того, який би не працював . Нам потрібно проаналізувати вдвічі більше кидків, ніж найдовша стратегія суперника. (Ваш код насправді еквівалентний цьому )
Джонатан Аллан

... насправді завдяки дії Ɗвашого коду він навіть не робить те, що ви могли подумати - формує кожен так, як має власну довжину, а потім отримує сукупні суми цих результатів, тому також буде порівнювати невірні значення. Спробуйте це для прикладу - він займає [[1,2,3,4,5],[6,7],[8]]форму, кожен формує по довжині всього списку (3), щоб отримати, [[1,2,3],[6,7,6],[8,8,8]]потім виконує накопичення для отримання [[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
  • 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

Спробуйте в Інтернеті!

Як?

Це пошук за шириною: спершу спробуємо всі кроки на заданому кроці, щоб побачити, чи зможемо виграти гру. Якщо ми не можемо перемогти зараз, ми намагаємось додати ще один хід до кожного не програючого кроку.

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)

де andі xorє побітовими операторами.

Прокоментував

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
Це насправді геніальний підхід. Я навіть не думав розглядати це як графік. Я використовував словники та зворотні перегляди в своєму неповторному оригінальному підході (без Спока чи Ящірок, тобто)
Koishore Roy

3

R , 213 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, тому я вводив жорсткі коди в 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Необхідно позбутися ВПЛ , які відбуваються , коли два гравці намалювати назавжди.

Я не переконаний, що це найефективніша стратегія. Навіть якщо це так, я впевнений, що код для mгольфу міг би бути зовсім небагато.


чому lengths()чужий завжди повертатися 4?
Джузеппе

1
У будь-якому разі, поки я чекаю вашої відповіді, я переграв її до 197 року , переважно зосередившись на v...
Джузеппе

lengthsn=45nn=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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.