Знайдіть результат гри Війна


15

Знайдіть результат гри Війна

Коли я навчався в початковій школі, була гра «Рок-папір-ножиці», в яку ми грали під час зборів, в очікуванні нашого вчителя, на перерві тощо. Ми називали це «війна». Однак після деяких пошуків виявляється, що це набагато простіший варіант "гри в рушницю" (за даними WikiHow) . Я буду називати це "Війна", оскільки правила дещо інші:

2 людини сидять навпроти один одного. Мета гри - «вбити» іншого гравця. Кожен хід ви можете грати одним із 3-х ходів:

  • Перезавантажте : у вас є пістолет, який вміщує один постріл. Він повинен бути перезавантажений, перш ніж його можна буде кожного разу звільнити. Перезавантаження, коли у вас вже є боєприпаси, є законним, але нічого не робить. Перезавантаження символізувалося постукуванням скронь обома руками. Кожен гравець починає з 0 боєприпасів.

  • Охорона : Єдиний безпечний хід. Якщо вас розстрілюють під час охорони, ви не помрете. Охорона символізувалась схрещуванням рук над грудьми.

  • Вогонь : Стріляйте з гармати. Для успішного запуску, ви повинні мати перезавантаження з останнього пострілу. Якщо ваш опонент перезавантажується, ви виграєте. Якщо вони теж ведуть вогонь, а у вас обох є патрони, це нічия. Якщо вони охороняють, ви витратили боєприпаси. Хоча стрілянина без боєприпасів - це законний хід, вона нічого не робить і залишає вас вразливими, як перезавантаження. Стрільба символізувалась вказівкою на іншого гравця.

Це було схоже на RPS, оскільки кожен гравець одночасно кидає свій вибір (ми двічі постукували по ногах між оборотами, щоб підтримувати ритм один з одним, але це не важливо для виклику).

Змагання:

Ваше завдання - знайти результат гри війни. Це може бути функція або повна програма.

Вхідні дані

  • Опція, яку кожен гравець обрав кожен виток, буде представлена ​​символом / рядком:

    • r : перезавантажити

    • г : охоронець

    • f : вогонь

  • Вхідним записом буде список пар, обмежений / неозначений рядок або будь-що інше у цих рядках.

Приклад введення в Python може бути [("r", "g"), ("f", "r")], тобто під час першого повороту перший гравець перезавантажений, а другий гравець охороняється. На другому ходу перший гравець стріляє, а другий гравець перезавантажується. Гравець один виграє цю гру. Ж вхід може необов'язково бути представлена в вигляді "r g f r", "rgfr", "rg fr" "rg-fr"...

Ви можете припустити наступне:

  • Введення відповідатиме обраному формату та міститиме лише дійсні символи.

  • Хтось помре протягом 100 обертів.

Однак ви не можете припустити, що повороти закінчуються, коли хтось помирає.

Вихідні дані

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

  • Гравець 1 перемагає

  • Гравець 2 виграє

  • Вони вбивають один одного (малюють)

Кожен результат повинен мати значення району і завжди повинен бути однаковим для кожного сценарію.

Як приклад: ви можете вивести, 1коли гравець 1 перемагає, 2коли гравець 2 перемагає, і 0в разі нічиї. Тоді ви завжди повинні виводити, 1коли гравець 1 перемагає, 2коли гравець 2 перемагає, і 0в разі нічиї.

Її можна повернути або роздрукувати у stdout. Трейлінг пробілу добре.

Просто для того, щоб було зрозуміло, єдиний сценарій, який призводить до нічиї - це якщо обидва гравці стріляють, і обоє мають боєприпаси.

*Оскільки в цьому виклику повороти можуть продовжуватися після того, як хтось помирає, можливо, в підсумку може виграти більше одного гравця. Вам потрібно знайти, хто виграв першим відповідно до вкладених даних.

Тестові випадки (якщо припустити, 1коли P1 виграє, 2коли P2 виграє і 0нічия):

"rg fr" => 1 (P1 shot P2 while they were reloading)

"rg ff" => 1 (They both shot, but only P1 had ammo)

"rr ff" => 0 (Both had ammo and shot each other)

"rr ff rr fg" => 0 (Both had ammo and shot each other. Everything after the first win is ignored)

"rr fg rf" => 2 (P2 shot P1 while they were reloading)

"rf gg rr fg rr fr" => 1
    (P2 tried to shoot but didn't have any ammo, then they both guarded, then they both reloaded, then P2 blocked a shot, then they both reloaded again [but P2 still only has 1 ammo!], then P1 shoots P2 while they're reloading.

"rr gf fr rf gg rg ff" => 1
       ^ Player 1 wins here. The rest to the right has no effect on the output

Це кодовий гольф, тому виграє найменша кількість байтів!

Зауважте, як показують тестові випадки, ви повинні обробляти "тупі" рухи. Це абсолютно справедливо для гравця, який намагається стріляти, коли у них немає боєприпасів, або перезавантажувати 2 витки поспіль (і накопичувати лише одну боєприпаси).


Я щось пропускаю чи можна визначити вихід лише з останнього раунду?
xnor

@ Актуалізовано питання. І ні, оскільки вам потрібно знати, чи має у гравця боєприпаси чи ні. Я розумію, що ви можете припустити, у якого гравця є боєприпаси, виходячи з того, що це остання черга. Я фактично змінив правила в останню хвилину, що дозволило припустити, що вхід закінчиться, коли хтось помер. Я шкодую про це зараз.
Carcigenicate


3
Це дуже схоже на Оцінка гри Load, Defend і Shoot . Єдині відмінності полягають у тому, що в іншому виклику є гармати з більш ніж одним пострілом і те, що стрільба з порожнього пістолета вважається обманом і втрачає гру.
Денніс

Чи можемо ми взяти два окремих введення для двох гравців замість раундів, наприклад {"rff","rgf"}?
betseg

Відповіді:


2

Сітківка , 36 байт

s`(?<=r..([^f]..)*)f
!
A`g
G1`!
\w
_

Формат введення повинен бути розділеними парами, що передаються по рядку, наприклад

rr
fr

Результатом є !_гравець 1 виграш, _!якщо гравець 2 виграє і !!якщо є нічия.

Спробуйте в Інтернеті! (Тестовий набір, який використовує поділ простору для зручності.)

Я, мабуть, зовсім не помітив цього виклику. Я впевнений, я би спробував це в Retina раніше інакше. :)

Пояснення

s`(?<=r..([^f]..)*)f
!

Почнемо з позначення "дійсних" кадрів, перетворюючи перші fпісля кожного rв !. Ми робимо це, порівнюючи кожного, fз якого можна знайти rтого самого гравця, не переходячи на іншого f. Обмежувати пошук на rs на одному і тому ж плеєрі легко, завжди збираючи по три символи одночасно.

A`g

Тепер ми відкидаємо всі повороти, в яких хтось охороняв себе, оскільки кінцевий поворот не може бути одним із таких.

G1`!

Тепер ми зберігаємо лише першу чергу, яка містить ан !. Якщо трапиться дійсний постріл (а ми знаємо, що його ніхто не охороняв) гра закінчується.

\w
_

Нарешті, нам потрібно консолідувати рядок для отримання послідовних результатів, і ми просто робимо це, перетворюючи не !символи (або rабо f) на _.


5

Пітон, 139 байт

c=d=0
for i in input():
 b=(c&(i=='fr'))-(d&(i=='rf'));p,q=i
 if b|(i=='ff')&c&d:print b;break
 c,d=(p=='r',i!='fg')[c],(q=='r',i!='gf')[d]

Вводиться на stdin у вигляді списку 2-символьних рядків (наприклад, ['rf', 'rr', 'rg', 'ff']). Виходи 1, якщо гравець 1 перемагає, -1 якщо гравець 2 виграє, і 0 за нічию.

Пояснення: Спочатку перевіряється, чи хтось вистрілив з кулі, якщо так гра закінчується. Тоді ми визначаємо, чи гравці або перезарядили зброю, або витратили боєприпаси.

Це моя перша публікація з кодекгольфами :)


4

JavaScript (ES6), 108 107 93 91 89 85 байт

Збережено 4 байти за допомогою Тита

Вводиться як масив з 2-х символьних рядків, що описують ходи, які грає кожен гравець.

b=>b.map(c=>w=w||b&'312'[b=(s='0210231')[m='ffrfgrrggf'.search(c)]|s[m-2]&b,m],w=0)|w

Повернення:

  • 1 якщо гравець 1 переможе
  • 2 якщо гравець 2 виграє
  • 3 за нічию

Як це працює

Ми підтримуємо біт-маску, яка bописує, хто має завантажену кулю:

  • біт № 0: у гравця 1 є куля
  • біт №1: у гравця 2 є куля

Ми використовуємо послідовність De Bruijn 'ffrfgrrggf' для виявлення всіх 9 можливих комбінацій рухів. Ми використовуємо біт-маски АБО та І для оновлення bвідповідно до комбінації переміщення. Ми використовуємо 3-й набір бітових масок, з якими AND'ed, bщоб визначити переможця w. (Тільки три виграшні комбінації бути ff, frі rf.)

Варто зазначити, що маски АБО та І можуть зберігатися з тим же малюнком, зміщеним на два положення.

 Index in | Combination | Bullet   | Bullet  | Winner
 sequence |             | AND mask | OR mask | mask
----------+-------------+----------+---------+--------
    0     |     ff      |    0     |    0    |   3
    1     |     fr      |    0     |    2    |   1
    2     |     rf      |    0     |    1    |   2
    3     |     fg      |    2     |    0    |   0
    4     |     gr      |    1     |    2    |   0
    5     |     rr      |    0     |    3    |   0
    6     |     rg      |    2     |    1    |   0
    7     |     gg      |    3     |    0    |   0
    8     |     gf      |    1     |    0    |   0

Тестові справи


@Carcigenicate Це слід виправити для обох невдалих випадків. Однак зараз я повертаюсь 0(ніхто не був застрелений) або 3(гравці вбивають один одного) у разі нічиї. Не впевнений, чи це дозволено. Якщо ні, я можу повернутися w%3замість цього.
Арнольд

Я хотів 1 вихід на сценарій. Я гарантую, що хтось завжди буде розстріляний, тому облік цього випадку зайвий. Єдиний випадок, який веде до жеребкування, - коли обидва стріляють один в одного, і обидва мають боєприпаси.
Carcigenicate

&Маска може бути 0 для fr,rf,ff. '312'['0210231'[m='ffrfgrrggf'.search(c)]|'233331'[m-3]&b]або '123'['2100231'[m='frffgrrggf'.search(c)]|'233331'[m-3]&b]зберегти один байт; але вони працюють?
Тіт

@Titus Цікаво, що застосування маски АБО перед маскою AND спрацювало б для всіх існуючих тестових випадків. Але це не вдасться для чогось такого, як["rr","fg","fr","rf"]
Арнольд

&має вищий пріоритет ніж |, тому зміна порядку не повинна нічого там змінювати (крім збереження байта). Але у моєму коді завдання не було. Спробуйте ...'123'[b='2100231'....
Тит

2

Perl 6 , 71 62 байт

{&[<=>](|map {m/r[..[r|g]]*.$^f/.to//∞},/[r|f]f/,/.f[r|f]/)}

Розчин на основі Regex

Приймає введення у вигляді рядка у формі "rg fr".
Три можливих виходів значення перерахувань More(гравець 1 Won), Less(гравець 2) он, Same(малювати) - які перетворюються в ці слова при друку, або в 1, -1, 0коли примушують до номерів.

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

Як це працює

  • map { m/r[..[r|g]]*.$^f/.to // ∞ }, /[r|f]f/, /.f[r|f]/

    Виконує два збіги з регулярними виразами на вході. Після інтерполяції два регулярні вирази:

    • r[..[r|g]]*.[r|f]f - Відповідає першому вдалому пострілу гравця 2.
    • r[..[r|g]]*..f[r|f] - Відповідає першому вдалому пострілу гравця 1.

    У кожному випадку він повертає кінцеве положення матчу ( .to), або нескінченність, якщо не було відповідності.

  • &[<=>](|   )

    Застосовує <=>оператора до двох кінцевих позицій відповідності. Він повертає значення з Orderenum ( More, Lessабо Same), залежно від того, перший аргумент більший, менший або рівний другому.


Акуратний. Як з цікавості ви набираєте символ нескінченності? Спеціальна клавіатура, або ви набираєте alt + some number? А ви насправді використовуєте такі символи у загальному коді Perl, чи це просто для гольфу?
Carcigenicate

@Carcigenicate: Моя спеціальна клавіатурна схема дозволяє мені вводити її, натискаючи чотири клавіші [Menu] i n f(це називається складання послідовності ). Однак усі символи Perl 6 мають версії ASCII - наприклад, Infі є синонімами, тому не потрібно використовувати символи Unicode в коді Perl 6. Мені просто подобається ... :)
smls

А-а-а. Це одне, що мене кинуло про Перла, це символ нескінченності. Я подумав, що це вимога, яка видалася зайвою складністю. Можливо, коли мені набридне Clojure, я спробую Perl. Останнім часом я бачив багато коду Perl.
Carcigenicate

@Carcigenicate: Майте на увазі, що Perl 6 - це в основному нова мова, яка не сумісна з Perl назад, і її інтерпретатор все ще повільний (і для деяких функцій баггі). Perl, на даний момент у версії v5.24, продовжує підтримуватися окремо.
smls

Добре, дякую. Добре знати.
Carcigenicate

2

Haskell , 101 91 87 байт

n!(c:r)|'g'>c=n:1!r|'g'<c=1:0!r|1<3=2:n!r
_!r=[]
a#b=[x|x@(y,z)<-zip(1!a)$1!b,2>y+z]!!0

Спробуйте в Інтернеті! Функція infix #займає два рядки, що представляють дії кожного з двох гравців, і повертається, (0,1)якщо гравець 1 виграє, (1,0)для гравця 2 та (0,0)нічиї.

Приклад використання:

Prelude> "rgrfrf" # "fgrgrr"
(0,1)

Пояснення:

Функція infix !перетворює послідовність дій 'r'(перезавантаження), 'f'(пожежа) та 'g'(охорона) до послідовності спостережуваних дій 0(фактична пожежа), 1(без дії) та 2(охорона), де вогнева дія зараховується лише як фактична пожежна дія якщо куля завантажена, і як жодна дія в іншому випадку. Для цього першим аргументом nє те, 0якщо куля завантажена і 1якщо пістолет не завантажений. Таким чином кожен 'f'може бути просто замінений струмом n. ( n=0-> завантажений -> фактична пожежа -> 0, n=1-> вивантажений -> без дій -> 1)

n ! (c:r)                -- n is 0 or 1, c is 'f', 'g' or 'r' and r the rest of the string
    |'g'>c = n : (1 ! r) -- c is smaller 'g', so it must be 'f'. append n to the list
                         --  and set load status to 1 (unloaded)
    |'g'<c = 1 : (0 ! r) -- c is larger 'g', so it must be 'r'. append 1 (no action)
                         --  and set load status to 0 (loaded)
    |1<3   = 2 : (n ! r) -- c must be equal to 'g'. append 2 (guard)
                         --  and leave the load status unchanged
_ ! r = []               -- base case for recursion

Тоді є дев'ять результатів

  • (0,0): Обидва гравці стріляють і вмирають, гра закінчується.
  • (0,1) або (1,0) : Один гравець стріляє в іншого, гра закінчується.
  • (0,2) або (2,0) : Один гравець стріляє, але інший охороняється, гра продовжується.
  • (1,1), (1,2), (2,1)Або(2,2) : Ні один гравець стріляє, гра триває.

За задумом сума варіантів закінчення гри менша за 2, а сума можливості продовження кожної гри більша або дорівнює 2. Результатом гри є тоді перший кортеж із сумою менше 2.

a#b=[x|         -- build the list of all x
    x@(y,z) <-  -- where x is an alias for the tuple (y,z) which is drawn from the list
    zip (1!a)   -- of tuples where the first component is from 1!a = eg. [1,2,1,0,1,0] 
        (1!b)   -- and the second from 1!b = eg. [1,2,1,2,1,1]
    , 2 > y+z]  -- and y+z are smaller 2.
    !!0         -- return the first element of this list

1

Пакетна, 249 байт

@echo off
set g=goto gg
set/ax=y=0
:gg
shift&goto %1
:fg
set x=0
%g%
:gf
set y=0
%g%
:rr
set/ax=y=1
%g%
:fr
if %x%==1 exit/b1
:gr
set y=1
%g%
:rf
if %y%==1 exit/b2
:rg
set x=1
%g%
:ff
set/az=3-x-x-y
if %z%==3 %g%
exit/b%z%

Введення здійснюється у вигляді пари символів на кожен виток і виводиться за рівнем помилок (0 = нічия, 1 = гравець 1, 2 = гравець 2). xі yслідкувати за тим, чи є у гравця боєприпаси, тому коли обидва стріляють, результат є 3-x-x-y, якщо це не 3, і в такому випадку ми продовжуємо діяти. У рядку 5 я зловживаю аналізатором Batch - %1(який є поточним ходом) замінюється до того, як shiftоператор виконує та видаляє його, тому ми все одно переходимо до правильної мітки.


1

Clojure, 168 байт

#(reduce(fn[[l L r R][a A]](if(and l L)(let[M(fn[r a A](if(and(= a \f)r)[nil(= A \g)][(or(= a \r)r)1]))[r L](M r a A)[R l](M R A a)][l L r R])[l L r R]))[1 1 nil nil]%)

Менше гольфу (якщо обидві особи живі, ми використовуємо Mдля оновлення боєприпасів і життєвого стану противника, інакше повернемо поточний статус):

(def f (fn[A] (reduce
                (fn [[l1 l2 r1 r2] [a1 a2]]
                  (if (and l1 l2)
                    (let[M (fn [r1 a1 a2]
                             (if (and(= a1 \f)r1)
                               [false (= a2 \g)]        ; we lost the ammo, a2 lives if he was guarding
                               [(or(= a1 \r)r1) true])) ; we might gain or keep ammo, a2 lives no matter what
                         [r1 l2] (M r1 a1 a2)
                         [r2 l1] (M r2 a2 a1)]
                      [l1 l2 r1 r2])
                    [l1 l2 r1 r2]))
                [true true false false] A)))

Приклад використання (перший елемент повідомляє, чи програвач 1 живий в кінці гри, другий елемент повідомляє, чи програвач 2 живий, третій і четвертий повідомляють про стан боєприпасів, що не має значення при визначенні переможця):

(-> (for[[a b s] (partition 3 "rr fg rf fr ")][a b]) f (subvec 0 2))

Оновлення: Добре подивіться, це loopмає однакову довжину! Мені здається, що reduceверсію простіше розробити, оскільки ви можете легко перевірити проміжні стани, якщо використовуєте reductions.

#(loop[l 1 L 1 r nil R nil[[a A]& I]%](if(and l L)(let[M(fn[r a A](if(and(= a \f)r)[nil(= A \g)][(or(= a \r)r)1]))[r L](M r a A)[R l](M R A a)](recur l L r R I))[l L]))

Я чекав! Це щільно, вау.
Carcigenicate

Дякую, це все ще дратує мене, що мені довелося повторити [l1 l2 r1 r2](його змінені значення при letта його початкові значення) та ці fnпідписи.
NikoNyrh

Принаймні, для останнього, саме тому я віддаю перевагу loop. Я вважаю, що це призводить до акуратнішого коду. Як тільки мені потрібно скласти більше 1 акумулятора, я перемикаюся.
Carcigenicate

1

PHP, 107 101 90 байт

використовуючи бітну маску $ d для стану завантаження та послідовність DeBruijn для рухів стрільби.

for(;!$x=$d&strpos(_frff,$m=$argv[++$i]);)$d=$d&g<$m|h<$m|2*($d/2&f<$m[1]|g<$m[1]);echo$x;

приймає дані як аргументи командного рядка з двома символами, запускається з -nr.

1 = Гравець 1 перемог
2 = Гравець 2 перемоги
3 = нічия

зламатися

for(;!$x=$d&strpos(_frff,       // 1. $x=someone dies, loop while not
    $m=$argv[++$i]          // loop throug moves
);)
    $d=
        $d&g<$m|h<$m            // 2. unload/reload Player 1 = bit 0
    |2*(
        $d/2&f<$m[1]|g<$m[1]    // 3. unload/reload Player 2 = bit 1
    );
echo$x;
  • Послідовність DeBruijn fr:: позиція = 1 = пожежа P1; rf= позиція 2 = пожежа P2, ff= положення 3 = вогонь обох
  • g<$m<=> f<$m[0]( f<$mзавжди вірно, тому що є другий символ).

0

Пітон, 200 байт

def war_game(turns):
    turn=0
    player1=True
    player2=True
    ammo1=False
    ammo2=False
    while turn<len(turns):
        if turns[turn][0]=='f' and ammo1==True and turns[turn][1]!='g':
            player2=False
        elif turns[turn][0]=='f' and turns[turn][1]=='g':
            ammo1=False
        elif turns[turn][0]=='r':
            ammo1=True
        if turns[turn][1]=='f' and ammo1==True and turns[turn][0]!='g':
            player1=False
        elif turns[turn][1]=='f' and turns[turn][0]=='g':
            ammo2=False            
        elif turns[turn][1]=='r':
            ammo2=True
        if player2==False or player1==False:
            break
        turn+=1
    if player1==True and player2==False:
        print('Player 1 wins')
        return 1
    elif player1==False and player2==True:
        print('Player 2 wins')
        return 2
    print('Draw')
    return 0

2
Ласкаво просимо на сайт. Конкурс оцінюється в кількості байтів, тому я рекомендую включити кількість байтів у свій заголовок та намагатися мінімізувати його, зменшуючи довжину імен змінних. Ви також повинні включити мову, якою ви це написали (мені схоже на Python3).
Post Rock Garf Hunter

2
Як було сказано вище, це змагання, яке може зробити найменшу програму можливою для виконання завдання. Ви використовуєте повні назви, turnsа не просто t, а це означає, що програма набагато більша, ніж потрібно. Також, будь ласка, почніть подати щось на зразок #Python 2, 200 bytes(припустимо, що це 2, а програма - 200 байт), щоб мова, якою ви користуєтеся, була зрозумілою.
Carcigenicate

0

Clojure, 180 173 байт

(fn[t](loop[z nil x nil[[c v]& r]t](let[k #(and %3(= %\f)(not= %2\g))h #(and(not= %\f)(or %2(= %\r)))q(k v c x)w(k c v z)](cond(and q w)0 q 2 w 1 1(recur(h c z)(h v x)r)))))

-7 байт шляхом зміни функції на повну функцію замість використання макросу. Це дозволило мені зробити макроси внутрішніх функцій, що трохи економить.

Це дуже буквальне рішення. Я такий собі задумливий, оскільки я тільки що написав повну версію гри, і це, в основному, сильно знята версія алгоритму, який я використовував. Можливо, є багато оптимізацій, які я міг би зробити, але я цілком задоволений цим. Для пояснення див. Попередньо готовий код.

(defn outcome [turns] ; Take input as ["rr" "ff"]
  (loop [p1-ammo? false ; Keep track of if each player has ammo
         p2-ammo? false
         [[p1-move p2-move] & rest-turns] turns] ; Deconstruct the turns out

    (let [killed? (fn [m m2 a] (and a (= m \f) (not= m2 \g))) ; Function that checks if one player killed the other
          has-ammo? (fn [m a] (and (not= m \f) (or a (= m \r)))) ; Function that decides if a player has ammo in the
                                                                 ;  next turn
          p1-killed? (killed? p2-move p1-move p2-ammo?) ; Check if each player was killed.
          p2-killed? (killed? p1-move p2-move p1-ammo?)]

      (cond ; Check who (if any) died. If no one died, recur to next turn.
        (and p1-killed? p2-killed?) 0
        p1-killed? 2
        p2-killed? 1
        :else (recur (has-ammo? p1-move p1-ammo?)
                     (has-ammo? p2-move p2-ammo?)
                     rest-turns)))))
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.