Encontre o resultado de um jogo de guerra

15

Encontre o resultado de um jogo de guerra

Quando eu estava na escola primária, havia um jogo de "Pedra-Papel-Tesoura" que brincávamos durante as assembléias, enquanto aguardávamos nosso professor, no recreio etc. Chamamos de "Guerra". Depois de algumas pesquisas, porém, verifica-se que essa é uma variante muito mais simples do "Jogo de espingarda" (de acordo com o WikiHow) . Vou chamá-lo de "Guerra", pois as regras são um pouco diferentes:

2 pessoas se sentam em frente uma da outra. O objetivo do jogo é "matar" o outro jogador. A cada turno, você pode executar um dos três movimentos:

  • Recarregar : Você tem uma arma que detém um único tiro. Ele deve ser recarregado antes de poder ser disparado a cada vez. Recarregar quando você já tem munição é legal, mas não faz nada. Uma recarga foi simbolizada tocando nas têmporas com as duas mãos. Cada jogador começa com 0 munição.

  • Guarda : O único movimento seguro. Se você é baleado enquanto guarda, você não morre. A guarda foi simbolizada cruzando os braços sobre o peito.

  • Fogo : Dispare sua arma. Para disparar com sucesso, você deve ter recarregado desde o último tiro. Se o seu oponente estiver recarregando, você ganha. Se eles também dispararem, e vocês dois tiverem munição, é um empate. Se eles estão guardando, você desperdiçou a munição. Embora disparar sem munição seja uma jogada legal, ele não faz nada e o deixa vulnerável como recarregar. O disparo foi simbolizado apontando para o outro jogador.

Foi jogado de forma semelhante ao RPS, em que cada jogador joga simultaneamente a sua escolha (batemos as pernas duas vezes entre turnos para manter o ritmo um com o outro, mas isso não é importante para o desafio).

O desafio:

Sua tarefa é encontrar o resultado de um jogo de guerra. Pode ser uma função ou programa completo.

Entrada

  • A opção que cada jogador escolheu a cada turno será representada por um personagem / sequência:

    • r : recarregar

    • g : guarda

    • f : fogo

  • A entrada será uma lista de pares, uma sequência delimitada / não delimitada ou qualquer outra coisa nesse sentido.

Um exemplo de entrada em Python poderia ser [("r", "g"), ("f", "r")], ou seja , no primeiro turno, o primeiro jogador recarregou e o segundo jogador guardou. No segundo turno, o primeiro jogador dispara, enquanto o segundo jogador é recarregado. O jogador um vence este jogo. A mesma entrada pode, opcionalmente, ser representada como "r g f r", "rgfr", "rg fr" "rg-fr"...

Você pode assumir o seguinte:

  • A entrada corresponderá ao formato escolhido e conterá apenas caracteres válidos.

  • Alguém vai morrer dentro de 100 turnos.

No entanto, você não pode assumir que os turnos terminam quando alguém morre.

Resultado

Um valor que indica quem ganhou (ou quem ganhou primeiro *). Você pode escolher o que produzir para cada cenário, mas deve levar em consideração o seguinte:

  • Jogador 1 vence

  • Jogador 2 vence

  • Eles se matam (empate)

Cada resultado deve ter um valor de distrito e sempre deve ser o mesmo para cada cenário.

Como exemplo: você pode exibir 1quando o jogador 1 vence, 2quando o jogador 2 vence e 0em caso de empate. Você deve sempre produzir 1quando o jogador 1 vencer, 2quando o jogador 2 vencer e 0em caso de empate.

Ele pode ser retornado ou impresso no stdout. O espaço em branco à direita é bom.

Só para esclarecer, o único cenário que leva a um empate é se ambos os jogadores disparam e ambos têm munição.

*Como neste desafio, os turnos podem continuar depois que alguém morre, é possível que mais de um jogador possa vencer eventualmente. Você precisa descobrir quem ganhou primeiro, de acordo com a entrada.

Casos de teste (assumindo 1quando P1 vence, 2quando P2 vence e 0empata):

"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

Isso é código de golfe, então o menor número de bytes vence!

Observe que, como mostram os casos de teste, você deve manipular movimentos "burros". É perfeitamente válido para um jogador tentar atirar quando não tem munição ou recarregar 2 turnos seguidos (e acumular apenas uma única munição).

Carcinigenicado
fonte
Estou faltando alguma coisa ou a saída pode ser determinada apenas a partir da última rodada?
Xnor
@Atualizou a pergunta. E não, já que você precisa saber se um jogador tem munição ou não. Percebo, porém, que você pode assumir qual jogador tem munição com base no fato de que é o último turno. Na verdade, mudei as regras razoavelmente no último minuto, permitindo admitir que a entrada terminaria quando alguém morresse. Estou me arrependendo disso agora.
Carcigenicate
3
Isso é muito semelhante a marcar um jogo de carga, defesa e tiro . As únicas diferenças são que o outro desafio tem armas com mais de um tiro e que atirar em uma arma vazia é considerado trapaça e perde o jogo.
Dennis
Podemos pegar duas entradas separadas para dois jogadores em vez de rodadas, por exemplo {"rff","rgf"}?
betseg

Respostas:

2

Retina , 36 bytes

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

O formato de entrada deve ser pares separados de avanço de linha, por exemplo,

rr
fr

A saída é !_se o jogador 1 vencer, _!se o jogador 2 vencer e!! se houver um empate.

Experimente online!(Uma suíte de testes que usa separação de espaço por conveniência.)

Eu devo ter esquecido completamente esse desafio. Tenho certeza de que eu teria tentado isso na Retina anteriormente, caso contrário. :)

Explicação

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

Começamos marcando fotos "válidas", transformando a primeira fapós cada ruma !. Fazemos isso combinando cada um fdos quais podemos encontrar um rno mesmo jogador sem cruzar com o outro f. Limitar a pesquisa a rs no mesmo player é fácil, sempre passando três caracteres por vez.

A`g

Agora descartamos todos os turnos em que alguém se guardava, porque o turno final não pode ser um deles.

G1`!

Agora, mantemos apenas o primeiro turno que contém um !. Se um tiro válido acontecer (e sabemos que ninguém está protegido), o jogo termina.

\w
_

Finalmente, precisamos consolidar a string para fornecer resultados consistentes, e simplesmente fazemos isso transformando os não- !caracteres (um rou f) _.

Martin Ender
fonte
5

Python, 139 bytes

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]

Recebe entrada no stdin na forma de uma lista de seqüências de caracteres de 2 caracteres (por exemplo, ['rf', 'rr', 'rg', 'ff']). Produz 1 se o jogador 1 vencer, -1 se o jogador 2 vencer e 0 para um empate.

Explicação: Primeiro, verifique se alguém disparou uma bala; caso contrário, o jogo termina. Depois, determinamos se os jogadores recarregaram suas armas ou gastaram sua munição.

Este é o meu primeiro post sobre codegolf :)

viciado em matemática
fonte
4

JavaScript (ES6), 108 107 93 91 89 85 bytes

Economizou 4 bytes com a ajuda de Titus

Recebe a entrada como uma matriz de seqüências de caracteres de 2 caracteres, descrevendo os movimentos executados por cada jogador.

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

Devoluções:

  • 1 se o jogador 1 vencer
  • 2 se o jogador 2 vencer
  • 3 para um empate

Como funciona

Mantemos uma máscara de bits bdescrevendo quem tem um marcador carregado:

  • bit # 0: o jogador 1 tem uma bala
  • bit # 1: o jogador 2 tem uma bala

Usamos a sequência De Bruijn 'ffrfgrrggf' para identificar todas as 9 combinações possíveis de movimentos. Usamos as máscaras OR e AND para atualizar de bacordo com a combinação de movimentação. Usamos um terceiro conjunto de máscaras de bits com AND bpara determinar o vencedor w. (As únicas três combinações vencedoras sendo ff, fre rf.)

Vale a pena notar que as máscaras OR e AND podem ser armazenadas com o mesmo padrão, alternadas por duas posições.

 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

Casos de teste

Arnauld
fonte
@Carcigenicate Isso deve ser corrigido para os dois casos com falha. No entanto, agora estou retornando 0(ninguém foi baleado) ou 3(jogadores se matam) em caso de empate. Não tenho certeza se isso é permitido. Caso contrário, eu posso retornar w%3.
Arnauld
Eu queria 1 saída por cenário. Eu garanto que alguém sempre será morto a tiros, portanto, a contabilidade para esse caso é desnecessária. O único caso que leva a um empate é quando ambos disparam um contra o outro e ambos têm munição.
Carcigenicate
A &máscara pode ser 0 para fr,rf,ff. '312'['0210231'[m='ffrfgrrggf'.search(c)]|'233331'[m-3]&b]ou'123'['2100231'[m='frffgrrggf'.search(c)]|'233331'[m-3]&b] salve um byte; mas eles funcionam?
Titus
@Titus Curiosamente, a aplicação da máscara OR antes da máscara AND funcionaria para todos os casos de teste existentes. Mas isso seria um fracasso para algo como["rr","fg","fr","rf"]
Arnauld
& tem precedência mais alta que | , portanto, alterar a ordem não deve mudar nada (além de salvar o byte). Mas a tarefa estava ausente no meu código. Tente ...'123'[b='2100231'....
Titus
2

Perl 6 , 71 62 bytes

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

Solução baseada em Regex.

Recebe a entrada como uma sequência no formulário "rg fr".
As três saídas possíveis são os valores de enumeração More(jogador ganhou 1), Less(2 jogador ganhou), Same(desenhar) - que se transformam em essas palavras, quando impresso, ou para 1, -1, 0quando forçado para números.

Experimente online!

Como funciona

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

    Executa duas correspondências de regex na entrada. Após a interpolação, os dois regexes são:

    • r[..[r|g]]*.[r|f]f - Corresponde ao primeiro tiro bem sucedido pelo jogador 2.
    • r[..[r|g]]*..f[r|f] - Corresponde ao primeiro tiro bem sucedido pelo jogador 1.

    Em cada caso, ele retorna a posição final da correspondência ( .to) ou infinito, se não houver correspondência.

  • &[<=>](|   )

    Aplica o <=>operador às duas posições finais da correspondência. Retorna um valor da Orderenumeração ( More,Less , ou Same), dependendo se o primeiro argumento é maior, menor ou igual à segunda.

smls
fonte
Arrumado. Por curiosidade, como você digita o símbolo do infinito? Teclado especial ou você digita alt + some number? E você realmente usa caracteres como esse no código Perl comum, ou é apenas para jogar golfe?
Carcigenicate
@Carcigenicate: Meu esquema de teclado personalizado me permite inseri-lo pressionando as quatro teclas [Menu] i n f(é chamado de sequência de composição ). No entanto, todos os símbolos Perl 6 têm versões ASCII - por exemplo, Infe são sinônimos -, portanto, não é necessário usar símbolos Unicode no código do Perl 6. I just like it ... :)
SMLS
Ahh Essa é uma coisa que me impressionou sobre Perl foi o símbolo do infinito. Eu pensei que era um requisito, que parecia desnecessariamente complicado. Talvez, quando eu me cansar de Clojure, eu tente o Perl. Eu tenho visto muitos códigos Perl ultimamente.
Carcigenicate
@Carcigenicate: Esteja ciente de que o Perl 6 é basicamente uma nova linguagem que não é compatível com versões anteriores do Perl, e seu intérprete ainda é lento (e, para alguns recursos, com bugs). O Perl, atualmente na versão v5.24, continua sendo mantido separadamente.
SMLS
Ok obrigado. Bom saber.
Carcigenicate
2

Haskell , 101 91 87 bytes

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

Experimente online! A função infix# usa duas strings representando as ações de cada um dos dois jogadores e retorna (0,1)se o jogador 1 vencer, (1,0)para o jogador 2 e (0,0)para um empate.

Exemplo de uso:

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

Explicação:

A função infix !converte uma sequência de ações 'r'(recarregar), 'f'(fogo) e 'g'(guarda) em uma sequência de ações observáveis 0(fogo real), 1(sem ação) e 2(guarda), onde uma ação de fogo é contada apenas como ação de fogo real se um marcador estiver carregado e, caso contrário , nenhuma ação será executada. Para conseguir isso, o primeiro argumento né 0se um marcador é carregado e1 se a arma não está carregada. Dessa forma, cada um 'f'pode simplesmente ser substituído pela corrente n. ( n=0-> carregado -> fogo real -> 0, n=1-> descarregado -> sem ação -> 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

As nove possibilidades resultantes são então

  • (0,0): Ambos os jogadores atiram e morrem, o jogo termina.
  • (0,1) ou (1,0) : Um jogador atira no outro, o jogo termina.
  • (0,2) ou (2,0) : Um jogador atira, mas o outro guarda, o jogo continua.
  • (1,1), (1,2), (2,1)Ou (2,2): nenhum jogador tiros, jogo continua.

Por design, a soma das opções de final de jogo é menor que 2 e a soma de cada possibilidade de continuação de jogo é maior ou igual a 2. O resultado do jogo é a primeira tupla com soma menor que 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
fonte
1

Lote, 249 bytes

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

A entrada está na forma de pares de caracteres para cada turno e gera o nível de erro (0 = empate, 1 = jogador 1, 2 = jogador 2). xe yacompanhe se o jogador tem munição; portanto, quando os dois disparam, o resultado é 3-x-x-y, a menos que seja 3; nesse caso, continuamos. Na linha 5, eu abuso o analisador de lote - %1(que é o movimento atual) é substituído antes que a shiftinstrução seja executada e removida, portanto, ainda vamos para o rótulo correto.

Neil
fonte
1

Clojure, 168 bytes

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

Menos golfe (se ambas as pessoas estiverem vivas, usamos Mpara atualizar a munição e o estado de vida do inimigo, caso contrário, retornamos o status atual):

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

Exemplo de uso (o primeiro elemento informa se o Jogador 1 está ativo no final do jogo, o segundo elemento informa se o Jogador 2 está ativo, o 3º e o 4º informam o status da munição que não é relevante na determinação do vencedor):

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

Atualização: Bem, olhe para isso, isso looptem comprimento idêntico! Acho a reduceversão mais fácil de desenvolver, pois você pode inspecionar facilmente os estados intermediários, se usar 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
fonte
Eu estava esperando! Isso é denso, uau.
Carcigenicate
Hehe obrigado, ainda me incomoda que eu tenha que repetir [l1 l2 r1 r2](seus valores modificados em lete seus valores originais) e essas fnassinaturas.
NikoNyrh
Pelo menos para o último, é por isso que sou a favor loop. Acho que leva a um código mais puro. Assim que precisar dobrar com mais de 1 acumulador, troco.
Carcigenicate
1

PHP, 107 101 90 bytes

usando uma máscara de bit $ d para o status de carregamento e uma sequência DeBruijn para os movimentos de disparo.

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;

recebe entrada como argumentos de linha de comando de 2 caracteres, executado com -nr .

1 = Jogador 1 vence
2 = Jogador 2 vence
3 = empate

demolir

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;
  • Sequência DeBruijn fr:: position = 1 = P1 dispara; rf= posição 2 = P2 dispara,ff = posição 3 = ambos disparam
  • g<$m<=> f<$m[0]( f<$msempre é verdade, porque existe um segundo caractere).
Titus
fonte
0

Python, 200 bytes

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
fonte
2
Bem vindo ao site. O concurso é pontuado na contagem de bytes, por isso recomendo incluir uma contagem de bytes no seu título e tentar minimizá-la, reduzindo o tamanho dos nomes das variáveis. Você também deve incluir o idioma em que escreveu isso (parece-me o Python3).
Post Rock Garf Hunter
2
Como mencionado acima, esta é uma competição que pode tornar o menor programa possível para realizar a tarefa. Você está usando nomes completos, como em turnsvez de apenas t, o que significa que o programa é muito maior que o necessário. Além disso, inicie seu envio com algo como #Python 2, 200 bytes(supondo que este seja 2 e o programa tenha 200 bytes) para que o idioma que você está usando seja claro.
Carcigenicate
0

Clojure, 180 173 bytes

(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 bytes, alterando a função para uma função completa em vez de usar uma macro. Isso permite que eu faça macros de funções internas, o que economiza um pouco.

Esta é uma solução muito literal. Estou meio confuso desde que acabei de escrever uma versão completa do jogo, e essa é basicamente uma versão bastante simplificada do algoritmo que usei. Provavelmente existem muitas otimizações que eu poderia fazer, mas estou bastante feliz com isso. Veja o código pré-escrito para explicação.

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