Znajdź wynik gry wojennej

15

Znajdź wynik gry wojennej

Kiedy byłem w szkole podstawowej, była gra „Rock-Paper-Scissors”, w którą graliśmy podczas zgromadzeń, czekając na nauczyciela, w przerwie itp. Nazwaliśmy to „Wojną”. Jednak po kilku poszukiwaniach okazuje się, że jest to znacznie prostsza odmiana „Strzelby” (według WikiHow) . Nazywam to „Wojną”, ponieważ zasady są nieco inne:

2 osoby siedzą naprzeciwko siebie. Celem gry jest „zabicie” drugiego gracza. W każdej turze możesz wykonać jeden z 3 ruchów:

  • Przeładuj : masz broń, która trzyma pojedynczy strzał. Musi zostać przeładowany, zanim będzie można go wystrzelić za każdym razem. Przeładowywanie, gdy masz już amunicję, jest legalne, ale nic nie robi. Przeładowanie zostało symbolizowane przez stuknięcie świątyń obiema rękami. Każdy gracz zaczyna od 0 amunicji.

  • Strażnik : jedyny bezpieczny ruch. Jeśli zostaniesz postrzelony podczas obrony, nie umrzesz. Ochrona symbolizowana była przez skrzyżowanie rąk nad klatką piersiową.

  • Ogień : wystrzel swoją broń. Aby skutecznie strzelać, musisz przeładować od ostatniego strzału. Jeśli twój przeciwnik przeładowuje, wygrywasz. Jeśli oni również strzelają, a oboje macie amunicję, jest to remis. Jeśli pilnują, zmarnowałeś amunicję. Podczas gdy strzelanie bez amunicji jest legalnym posunięciem, nic nie robi i pozostawia cię bezbronnym jak przeładowanie. Strzelanie było symbolizowane przez wskazanie na drugiego gracza.

Rozgrywano go podobnie do RPS, ponieważ każdy gracz jednocześnie odrzuca swój wybór (dwukrotnie stukaliśmy nogami między turami, aby zachować rytm, ale to nie jest ważne dla wyzwania).

Wyzwanie:

Twoim zadaniem jest znalezienie wyniku gry wojennej. Może to być funkcja lub pełny program.

Wejście

  • Opcja wybrana przez każdego gracza w każdej turze będzie reprezentowana przez postać / ciąg znaków:

    • r : przeładuj

    • g : strażnik

    • f : ogień

  • Dane wejściowe będą listą par, ciągiem rozdzielanym / nieosiąganym, lub cokolwiek innego wzdłuż tych linii.

Przykładowym wejściem w Pythonie może być [("r", "g"), ("f", "r")], co oznacza, że ​​w pierwszej turze pierwszy gracz zostanie przeładowany, a drugi gracz będzie strzeżony. W drugiej turze pierwszy gracz strzela, a drugi gracz przeładowuje. Gracz pierwszy wygrywa tę grę. Te same dane wejściowe mogą być ewentualnie reprezentowane "r g f r", "rgfr", "rg fr" "rg-fr"...

Możesz założyć, co następuje:

  • Dane wejściowe będą pasować do wybranego przez Ciebie formatu i będą zawierać tylko prawidłowe znaki.

  • Ktoś umrze w ciągu 100 tur.

Nie możesz jednak założyć, że tury kończą się, gdy ktoś umrze.

Wynik

Wartość wskazująca, kto wygrał (lub kto wygrał jako pierwszy *). Możesz wybrać dane wyjściowe dla każdego scenariusza, ale musisz uwzględnić następujące kwestie:

  • Gracz 1 wygrywa

  • Gracz 2 wygrywa

  • Zabijają się nawzajem (remis)

Każdy wynik musi mieć wartość dzielnicową i zawsze musi być taki sam dla każdego scenariusza.

Na przykład: możesz wypisywać, 1gdy gracz 1 wygrywa, 2gdy gracz 2 wygrywa, aw 0przypadku remisu. Musisz wtedy zawsze generować, 1gdy gracz 1 wygra, 2gdy gracz 2 wygra, a także 0w przypadku remisu.

Można go zwrócić lub wydrukować na standardowe wyjście. Końcowe białe znaki są w porządku.

Żeby było jasne, jedynym scenariuszem prowadzącym do losowania jest sytuacja, gdy obaj gracze strzelają i obaj mają amunicję.

*Ponieważ w tym wyzwaniu tury mogą być kontynuowane po śmierci kogoś, możliwe, że więcej niż jeden gracz może wygrać. Musisz dowiedzieć się, kto wygrał jako pierwszy, zgodnie z danymi wejściowymi.

Przypadki testowe (zakładając, 1kiedy wygrywa P1, 2kiedy wygrywa P2 i 0remis):

"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

To jest golf golfowy, więc wygrywa najmniejsza liczba bajtów!

Uwaga: jak pokazują przypadki testowe, musisz obsługiwać „głupie” ruchy. Jest całkowicie uzasadnione, że gracz próbuje strzelać, gdy nie ma amunicji lub przeładowuje 2 tury z rzędu (i gromadzi tylko jedną amunicję).

Carcigenicate
źródło
Czy coś mi brakuje lub czy wynik można ustalić na podstawie ostatniej rundy?
xnor
@ Zaktualizowano pytanie. I nie, ponieważ musisz wiedzieć, czy gracz ma amunicję, czy nie. Zdaję sobie jednak sprawę, że możesz założyć, który gracz ma amunicję na podstawie faktu, że jest to ostatnia tura. Właściwie zmieniłem zasady dość w ostatniej chwili, co pozwoliło zakładać, że dane wejściowe zakończą się, gdy ktoś umrze. Teraz tego żałuję.
Carcigenicate
3
Jest to bardzo podobne do oceniania gry Załaduj, broń i strzelaj . Jedyne różnice polegają na tym, że drugim wyzwaniem są pistolety z więcej niż jednym strzałem, a strzelanie z pustej broni jest uważane za oszustwo i przegrywa grę.
Dennis
Czy możemy wziąć dwa osobne dane wejściowe dla dwóch graczy zamiast rund, np. {"rff","rgf"}?
betseg

Odpowiedzi:

2

Siatkówka , 36 bajtów

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

Format wejściowy powinien być parami oddzielonymi od linii, np

rr
fr

Wyjście to !_wygrywa gracz 1, _!jeśli gracz 2 wygrywa i !!jeśli jest remis.

Wypróbuj online! (Zestaw testowy, który dla wygody wykorzystuje separację przestrzeni).

Musiałem całkowicie przeoczyć to wyzwanie. Jestem pewien, że inaczej spróbowałbym tego wcześniej w Retinie. :)

Wyjaśnienie

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

Zaczniemy od oznaczenie „valid” strzałów obracając pierwszy fpo każdym rINTO! . Robimy to poprzez dopasowanie każdego fz nich, z którego można znaleźć jednego rgracza bez przechodzenia przez innego f. Ograniczenie wyszukiwania do rtego samego gracza jest łatwe, zawsze idąc po trzy postacie na raz.

A`g

Teraz odrzucamy wszystkie tury, w których ktoś się strzegł, ponieważ tura końcowa nie może być jedną z nich.

G1`!

Teraz trzymamy tylko pierwszą turę, która zawiera !. Jeśli zdarzy się ważny strzał (i wiemy, że nikt nie pilnował), gra się kończy.

\w
_

Na koniec musimy skonsolidować ciąg, aby uzyskać spójne wyniki, i po prostu robimy to, zamieniając !znaki niebędące znakami (albo ralbo f) w _.

Martin Ender
źródło
5

Python, 139 bajtów

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]

Pobiera dane wejściowe na stdin w postaci listy 2-znakowych ciągów znaków (np. [„Rf”, „rr”, „rg”, „ff”]). Wykazuje 1, jeśli gracz 1 wygra, -1, jeśli gracz 2 wygra, i 0 w przypadku remisu.

Wyjaśnienie: Najpierw sprawdza, czy ktoś wystrzelił pocisk, jeśli tak, gra się kończy. Następnie ustalamy, czy gracze przeładowali broń lub zmarnowali amunicję.

To jest mój pierwszy post codegolf :)

ćpun matematyki
źródło
4

JavaScript (ES6), 108 107 93 91 89 85 bajtów

Za pomocą Tytusa zapisano 4 bajty

Pobiera dane wejściowe jako tablicę 2-znakowych ciągów opisujących ruchy odtwarzane przez każdego gracza.

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

Zwroty:

  • 1 jeśli gracz 1 wygra
  • 2 jeśli gracz 2 wygra
  • 3 na remis

Jak to działa

Utrzymujemy maskę bitów bopisującą, kto ma załadowaną kulę:

  • bit # 0: gracz 1 ma kulę
  • bit # 1: gracz 2 ma kulę

Używamy sekwencji De Bruijn 'ffrfgrrggf' do zidentyfikowania wszystkich 9 możliwych kombinacji ruchów. Używamy masek bitowych OR i AND do aktualizacji bzgodnie z kombinacją przenoszenia. Do bustalenia zwycięzcy używamy trzeciego zestawu masek bitowych, które są AND'owane w. (Tylko trzy zwycięskie kombinacje są ff, frarf .)

Warto zauważyć, że maski OR i AND można przechowywać z tym samym wzorem, przesuniętym o dwie pozycje.

 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

Przypadki testowe

Arnauld
źródło
@Carcigenicate Należy to naprawić w obu przypadkach, w których wystąpił błąd. Jednak teraz wracam 0(nikt nie został zastrzelony) lub3 (gracze się zabijają) w przypadku remisu. Nie jestem pewien, czy jest to dozwolone. Jeśli nie, mogę w%3zamiast tego wrócić .
Arnauld
Chciałem 1 wyjście na scenariusz. Gwarantuję jednak, że ktoś zawsze zostanie zastrzelony, więc rozliczenie tej sprawy nie jest konieczne. Jedyny przypadek prowadzący do remisu to sytuacja, gdy obaj strzelają do siebie nawzajem i obaj mają amunicję.
Carcigenicate
&Maska może wynosić 0 fr,rf,ff. '312'['0210231'[m='ffrfgrrggf'.search(c)]|'233331'[m-3]&b]lub'123'['2100231'[m='frffgrrggf'.search(c)]|'233331'[m-3]&b] zapisz jeden bajt; ale czy one działają?
Tytus
@Titus Co ciekawe, zastosowanie maski OR przed maską AND działałoby dla wszystkich istniejących przypadków testowych. Ale to by się nie powiodło w przypadku czegoś takiego jak["rr","fg","fr","rf"]
Arnauld
& ma wyższy priorytet niż | , więc zmiana kolejności nie powinna niczego tam zmieniać (oprócz zapisywania bajtu). Ale w moim kodzie brakowało tego zadania. Spróbować ...'123'[b='2100231'....
Tytus
2

Perl 6 , 71 62 bajtów

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

Rozwiązanie oparte na regeksie.

Pobiera dane wejściowe jako ciąg znaków w formularzu "rg fr".
Trzy możliwe wyniki są wartościami wyliczeniowe More(odtwarzacz 1 wygranych) Less(odtwarzacz 2 wygranych) Same(draw) - przez co zmienią się w tych słowach podczas drukowania, albo w 1, -1, 0gdy przekonwertowany numerów.

Wypróbuj online!

Jak to działa

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

    Wykonuje dwa dopasowania wyrażeń regularnych na wejściu. Po interpolacji dwa wyrażenia regularne to:

    • r[..[r|g]]*.[r|f]f - Pasuje do pierwszego udanego strzału przez gracza 2.
    • r[..[r|g]]*..f[r|f] - Pasuje do pierwszego udanego strzału przez gracza 1.

    W każdym przypadku zwraca pozycję końcową match ( .to) lub nieskończoność, jeśli nie było dopasowania.

  • &[<=>](|   )

    Stosuje <=>operatora do dwóch pasujących pozycji końcowych. Zwraca wartość z Orderwyliczenia ( More,Less lub Same), w zależności od tego, czy pierwszy argument jest większy, mniejszy czy równy drugiemu.

smls
źródło
Schludny. Z ciekawości, jak wpisujesz symbol nieskończoności? Specjalna klawiatura, czy wpisujesz Alt + some number? I czy rzeczywiście używasz takich znaków we wspólnym kodzie Perla, czy to tylko do gry w golfa?
Carcigenicate
@Carcigenicate: Mój niestandardowy schemat klawiatury pozwala mi go wprowadzać, naciskając cztery klawisze [Menu] i n f(nazywa się to sekwencją tworzenia ). Jednak wszystkie symbole Perla 6 mają wersje ASCII - np. InfI są synonimami - więc nie trzeba używać symboli Unicode w kodzie Perla 6. Po prostu mi się podoba ... :)
smls
Ahh To jedna rzecz, która rzuciła mnie na Perla, to symbol nieskończoności. Myślałem, że to wymaganie, które wydawało się niepotrzebnie skomplikowane. Może kiedy znudzę się Clojure, spróbuję Perla. Ostatnio widziałem dużo kodu Perla.
Carcigenicate
@Carcigenicate: Należy pamiętać, że Perl 6 jest w zasadzie nowym językiem, który nie jest kompatybilny wstecz z Perlem, a jego interpreter jest wciąż wolny (i w przypadku niektórych funkcji - błędny). Perl, obecnie w wersji v5.24, jest nadal utrzymywany osobno.
smls
Ok dzięki. Dobrze wiedzieć.
Carcigenicate
2

Haskell , 101 91 87 bajtów

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

Wypróbuj online! Funkcja infix #przyjmuje dwa ciągi znaków reprezentujące działania każdego z dwóch graczy i zwraca, (0,1)jeśli gracz 1 wygra, (1,0)dla gracza 2 i (0,0)remisu.

Przykładowe użycie:

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

Wyjaśnienie:

Funkcja infix !tłumaczy sekwencję akcji 'r'(przeładowanie), 'f'(ogień) i 'g'(straż) na sekwencję obserwowalnych akcji 0(rzeczywisty ogień), 1(brak akcji) i 2(strażnik), gdzie akcja ogniowa jest liczona tylko jako faktyczna akcja ogniowa jeśli pocisk jest załadowany i jako brak działania w przeciwnym razie. Aby to osiągnąć, pierwszym argumentem njest to, 0czy pocisk jest załadowany i 1czy pistolet nie jest załadowany. W ten sposób każdy 'f'można po prostu zastąpić prądem n. ( n=0-> załadowany -> rzeczywisty pożar -> 0, n=1-> rozładowany -> brak akcji ->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

Zatem powstaje dziewięć możliwości

  • (0,0): Obaj gracze strzelają i giną, gra się kończy.
  • (0,1) lub (1,0) : Jeden gracz strzela do drugiego, gra się kończy.
  • (0,2) lub (2,0) : Jeden gracz strzela, ale drugi chroni, gra jest kontynuowana.
  • (1,1), (1,2), (2,1)Lub (2,2): Brak pędy graczy, gra toczy się dalej.

Z założenia suma opcji zakończenia gry jest mniejsza niż 2, a suma każdej możliwej kontynuacji gry jest większa lub równa 2. Wynik gry jest wtedy pierwszą krotką z sumą mniejszą niż 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
Laikoni
źródło
1

Partia, 249 bajtów

@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%

Dane wejściowe mają postać par znaków dla każdej tury i wyników według poziomu błędu (0 = remis, 1 = gracz 1, 2 = gracz 2). xi ysprawdzaj, czy gracz ma amunicję, więc kiedy oba wystrzelą, wynik jest 3-x-x-y, chyba że to 3, w takim przypadku kontynuujemy. W linii 5 nadużywam parsera Batcha - %1(który jest bieżącym posunięciem) jest zastępowany przed wykonaniem shiftinstrukcji i usunięciem, więc nadal przechodzimy do właściwej etykiety.

Neil
źródło
1

Clojure, 168 bajtów

#(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]%)

Mniej gra w golfa (jeśli obie osoby żyją, Maktualizujemy amunicję i stan życia wroga, w przeciwnym razie przywracamy aktualny stan):

(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)))

Przykładowe użycie (pierwszy element informuje, czy Gracz 1 żyje pod koniec gry, drugi element informuje, czy Gracz 2 żyje, trzeci i czwarty informują o stanie amunicji, co nie ma znaczenia przy ustalaniu zwycięzcy):

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

Aktualizacja: Cóż, spójrz na to, loopma identyczną długość! Uważam, że reducewersja jest łatwiejsza do opracowania, ponieważ można łatwo sprawdzić stany pośrednie 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]))
NikoNyrh
źródło
Czekałem! To gęste, wow.
Carcigenicate
Hehe dzięki, wciąż mnie denerwuje, że musiałem powtórzyć [l1 l2 r1 r2](jego zmodyfikowane wartości leti oryginalne wartości) i te fnpodpisy.
NikoNyrh
Przynajmniej dla tych ostatnich, dlatego faworyzuję loop. Uważam, że prowadzi to do czystszego kodu. Gdy tylko muszę złożyć z więcej niż 1 akumulatorem, przełączam się.
Carcigenicate
1

PHP, 107 101 90 bajtów

za pomocą maski bitowej $ d dla statusu ładowania i sekwencji DeBruijn dla ruchów strzelania.

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;

przyjmuje dane wejściowe jako 2-znakowe argumenty wiersza poleceń, uruchamiane z -nr.

1 = Gracz 1 wygrywa
2 = Gracz 2 wygrywa
3 = remis

awaria

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 Sequence:: frposition = 1 = P1 strzela; rf= pozycja 2 = ogień P2, ff= pozycja 3 = ogień oba
  • g<$m<=> f<$m[0]( f<$mzawsze jest to prawda, ponieważ istnieje drugi znak).
Tytus
źródło
0

Python, 200 bajtów

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
Galo
źródło
2
Witamy na stronie. Konkurs jest oceniany według liczby bajtów, dlatego polecam dodanie liczby bajtów do tytułu i próba jej zminimalizowania poprzez zmniejszenie długości nazw zmiennych. Powinieneś także dołączyć język, w którym to napisałeś (wygląda mi na Python3).
Post Rock Garf Hunter
2
Jak wspomniano powyżej, jest to konkurs, który może sprawić, że najmniejszy program jest w stanie wykonać zadanie. Używasz pełnych nazw jak turnszamiast zamiast t, co oznacza, że ​​program jest znacznie większy niż to konieczne. Proszę również rozpocząć przesyłanie od czegoś takiego #Python 2, 200 bytes(zakładając, że jest to 2, a program ma 200 bajtów długości), więc używany język jest jasny.
Carcigenicate
0

Clojure, 180 173 bajtów

(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 bajtów, zmieniając funkcję na pełną zamiast zamiast makra. To pozwala mi tworzyć makra funkcji wewnętrznych, co trochę oszczędza.

To bardzo dosłowne rozwiązanie. Jestem trochę zatroskany, ponieważ właśnie napisałem pełną wersję gry, i jest to zasadniczo bardzo uproszczona wersja algorytmu, którego użyłem. Prawdopodobnie jest wiele optymalizacji, które mogę wykonać, ale jestem z tego całkiem zadowolony. Wyjaśnienia znajdują się w kodzie wstępnie golfowym.

(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)))))
Carcigenicate
źródło