Encuentra el resultado de un juego de guerra

15

Encuentra el resultado de un juego de guerra

Cuando estaba en la escuela primaria, había un juego al estilo "Piedra-Papel-Tijera" que jugábamos durante las asambleas, cuando esperábamos a nuestro maestro, en el recreo, etc. Lo llamamos "Guerra". Sin embargo, después de buscar un poco, resulta que esta es una variante mucho más simple del "Juego de escopeta" (según WikiHow) . Voy a llamarlo "Guerra" ya que las reglas son ligeramente diferentes:

2 personas se sientan una frente a la otra. El objetivo del juego es "matar" al otro jugador. Cada turno, puedes jugar uno de 3 movimientos:

  • Recargar : tienes una pistola que tiene un solo disparo. Debe recargarse antes de poder dispararse cada vez. Recargar cuando ya tienes munición es legal, pero no hace nada. Una recarga se simbolizó tocando las sienes con ambas manos. Cada jugador comienza con 0 municiones.

  • Guardia : el único movimiento seguro. Si te disparan mientras vigilas, no mueres. La protección se simboliza cruzando los brazos sobre el pecho.

  • Fuego : dispara tu arma. Para disparar con éxito, debe haber recargado desde el último disparo. Si tu oponente está recargando, tú ganas. Si también disparan, y ambos tienen munición, es un empate. Si están vigilando, desperdiciaste la munición. Si bien disparar sin munición es un movimiento legal, no hace nada y te deja vulnerable como recargar. Disparar se simboliza señalando al otro jugador.

Se jugó de manera similar a RPS, ya que cada jugador lanza simultáneamente su elección (golpeamos nuestras piernas dos veces entre turnos para mantener el ritmo entre ellos, pero eso no es importante para el desafío).

El reto:

Tu tarea es encontrar el resultado de un juego de guerra. Puede ser una función o un programa completo.

Entrada

  • La opción que cada jugador eligió en cada turno estará representada por un personaje / cadena:

    • r : recargar

    • g : guardia

    • f : fuego

  • La entrada será una lista de pares, una cadena delimitada / no delimitada, o cualquier otra cosa a lo largo de estas líneas.

Un ejemplo de entrada en Python podría ser [("r", "g"), ("f", "r")], es decir, en el primer turno, el primer jugador volvió a cargar y el segundo jugador guardó. En el segundo turno, el primer jugador dispara, mientras que el segundo jugador se recarga. El jugador uno gana este juego. La misma entrada opcionalmente podría ser representado como "r g f r", "rgfr", "rg fr" "rg-fr"...

Puede asumir lo siguiente:

  • La entrada coincidirá con el formato elegido y solo contendrá caracteres válidos.

  • Alguien morirá en 100 turnos.

Sin embargo, no puede suponer que los turnos terminan cuando alguien muere.

Salida

Un valor que indica quién ganó (o quién ganó primero *). Puede elegir qué generar para cada escenario, pero debe tener en cuenta lo siguiente:

  • El jugador 1 gana

  • El jugador 2 gana

  • Se matan unos a otros (dibujar)

Cada resultado debe tener un valor de distrito y siempre debe ser el mismo para cada escenario.

Como ejemplo: podría salir 1cuando el jugador 1 gana, 2cuando el jugador 2 gana y 0en caso de empate. Luego, siempre debe salir 1cuando el jugador 1 gana, 2cuando el jugador 2 gana y 0en caso de empate.

Se puede devolver o imprimir en la salida estándar. El espacio en blanco al final está bien.

Para que quede claro, el único escenario que conduce a un empate es si ambos jugadores disparan y ambos tienen munición.

*Dado que en este desafío, los turnos pueden continuar después de que alguien muere, es posible que más de 1 jugador gane eventualmente. Debes encontrar quién ganó primero según la entrada.

Casos de prueba (suponiendo 1que P1 gana, 2cuando P2 gana y 0para un empate):

"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

Este es el código de golf, por lo que gana el menor número de bytes.

Tenga en cuenta que, como muestran los casos de prueba, debe manejar movimientos "tontos". Es perfectamente válido para un jugador intentar disparar cuando no tiene munición, o recargar 2 turnos seguidos (y solo acumular una sola munición).

Carcigenicate
fuente
¿Me estoy perdiendo algo o se puede determinar la salida solo de la última ronda?
xnor
@ Actualizó la pregunta. Y no, ya que necesitas saber si un jugador tiene munición o no. Sin embargo, me doy cuenta de que puedes asumir qué jugador tiene munición en función del hecho de que es el último turno. En realidad, cambié las reglas de último momento que permitían suponer que la entrada terminaría cuando alguien muriera. Me arrepiento de eso ahora.
Carcigenicate
Continuemos esta discusión en el chat .
xnor
3
Esto es muy similar a Anotar un juego de carga, defensa y disparo . Las únicas diferencias son que el otro desafío tiene pistolas con más de un disparo y que disparar una pistola vacía se considera trampa y pierde el juego.
Dennis
¿Podemos tomar dos entradas separadas para dos jugadores en lugar de rondas, por ejemplo {"rff","rgf"}?
betseg

Respuestas:

2

Retina , 36 bytes

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

El formato de entrada debe ser pares separados por salto de línea, p. Ej.

rr
fr

La salida es si el !_jugador 1 gana, _!si el jugador 2 gana y !!si hay un empate.

Pruébalo en línea!(Un conjunto de pruebas que utiliza la separación del espacio por conveniencia).

Debo haber pasado por alto completamente este desafío. Estoy seguro de que habría intentado esto en Retina antes de lo contrario. :)

Explicación

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

Comenzamos marcando disparos "válidos" convirtiendo el primero fdespués de cada uno ren !. Hacemos esto haciendo coincidir cada uno fde los cuales puede encontrar un ren el mismo jugador sin cruzar sobre otro f. Limitar la búsqueda a rs en el mismo jugador es fácil al ir siempre con tres caracteres a la vez.

A`g

Ahora descartamos todos los turnos en los que alguien se protegió, porque el turno final no puede ser uno de esos.

G1`!

Ahora mantenemos solo el primer turno que contiene un !. Si ocurre un tiro válido (y sabemos que nadie guardó) el juego termina.

\w
_

Finalmente, necesitamos consolidar la cadena para obtener resultados consistentes, y simplemente hacemos esto convirtiendo los no !caracteres (ya sea ro f) en _.

Martin Ender
fuente
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]

Toma entrada en stdin en forma de una lista de cadenas de 2 caracteres (por ejemplo, ['rf', 'rr', 'rg', 'ff']). Salidas 1 si el jugador 1 gana, -1 si gana el jugador 2 y 0 para un empate.

Explicación: Primero verifica si alguien disparó una bala, si es así, el juego termina. Luego determinamos si los jugadores han recargado sus armas o malgastado su munición.

Esta es mi primera publicación de codegolf :)

adicto a las matemáticas
fuente
4

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

Guardado 4 bytes con la ayuda de Titus

Toma información como una serie de cadenas de 2 caracteres que describen los movimientos jugados por cada jugador.

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

Devoluciones:

  • 1 si el jugador 1 gana
  • 2 si el jugador 2 gana
  • 3 para un empate

Cómo funciona

Mantenemos una máscara de bits bque describe quién tiene una viñeta cargada:

  • bit # 0: el jugador 1 tiene una bala
  • bit # 1: el jugador 2 tiene una bala

Usamos la secuencia De Bruijn 'ffrfgrrggf' para identificar las 9 combinaciones posibles de movimientos. Utilizamos máscaras de bits OR y AND para actualizar de bacuerdo con la combinación de movimientos. Usamos un tercer conjunto de máscaras de bits con las que se AND ' bpara determinar el ganador w. (Las únicas tres combinaciones ganadoras son ff, fry rf.)

Vale la pena señalar que las máscaras OR y AND se pueden almacenar con el mismo patrón, desplazado por dos posiciones.

 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 prueba

Arnauld
fuente
@Carcigenicate Esto debería repararse para ambos casos fallidos. Sin embargo, ahora regreso 0(a nadie le dispararon) o 3(los jugadores se matan entre sí) en caso de empate. No estoy seguro si esto está permitido. Si no, puedo regresar w%3en su lugar.
Arnauld
Quería 1 salida por escenario. Sin embargo, garantizo que siempre se disparará a alguien, por lo que no es necesario tener en cuenta ese caso. El único caso que conduce a un empate es cuando ambos se disparan entre sí y ambos tienen munición.
Carcigenicate
La &máscara puede ser 0 para fr,rf,ff. '312'['0210231'[m='ffrfgrrggf'.search(c)]|'233331'[m-3]&b]o '123'['2100231'[m='frffgrrggf'.search(c)]|'233331'[m-3]&b]guardar un byte; pero funcionan?
Titus
@Titus Curiosamente, aplicar la máscara OR antes de la máscara AND funcionaría para todos los casos de prueba existentes. Pero eso fallaría para algo como["rr","fg","fr","rf"]
Arnauld
&tiene mayor precedencia que |, por lo que cambiar el orden no debería cambiar nada allí (aparte de guardar el byte). Pero la asignación faltaba en mi código. Tratar ...'123'[b='2100231'....
Titus
2

Perl 6 , 71 62 bytes

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

Solución basada en expresiones regulares.

Toma la entrada como una cadena en el formulario "rg fr".
Las tres salidas posibles son los valores de enumeración More(1 reproductor de won), Less(2 reproductor de won), Same(Draw) - que se convierten en esas palabras cuando se imprime, o dentro de 1, -1,0 cuando coaccionado a los números.

Pruébalo en línea!

Cómo funciona

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

    Realiza dos coincidencias de expresiones regulares en la entrada. Después de la interpolación, las dos expresiones regulares son:

    • r[..[r|g]]*.[r|f]f - Coincide con el primer disparo exitoso del jugador 2.
    • r[..[r|g]]*..f[r|f] - Coincide con el primer disparo exitoso del jugador 1.

    En cada caso, devuelve la posición final del partido (.to ), o infinito si no hubo coincidencia.

  • &[<=>](|   )

    Aplica al <=>operador a las dos posiciones finales del partido. Devuelve un valor de la Orderenumeración ( More, Lesso Same), dependiendo de si el primer argumento es mayor, menor o igual que el segundo.

smls
fuente
Ordenado. Por curiosidad, ¿cómo se escribe el símbolo de infinito? Teclado especial, o escribe alt + some number? ¿Y realmente usa caracteres como ese en el código Perl común, o es solo para jugar al golf?
Carcigenicate
@Carcigenicate: mi esquema de teclado personalizado me permite ingresarlo presionando las cuatro teclas [Menu] i n f(se llama una secuencia de composición ). Sin embargo, todos los símbolos Perl 6 tienen versiones ASCII, por ejemplo, Infy son sinónimos, por lo que no es necesario usar símbolos Unicode en el código Perl 6. Simplemente me gusta ... :)
sonríe
Ahh Esa es una cosa que me sorprendió de Perl fue el símbolo del infinito. Pensé que era un requisito, que parecía innecesariamente complicado. Tal vez cuando me aburra de Clojure intentaré Perl. He visto mucho código Perl últimamente.
Carcigenicate
@Carcigenicate: Tenga en cuenta que Perl 6 es básicamente un nuevo lenguaje que no es compatible con versiones anteriores de Perl, y su intérprete sigue siendo lento (y para algunas funciones, con errores). Perl, actualmente en la versión v5.24, continúa manteniéndose por separado.
sonríe
OK gracias. Bueno 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

Pruébalo en línea! La función infijo #toma dos cadenas que representan las acciones de cada uno de los dos jugadores y regresa (0,1)si el jugador 1 gana, (1,0)para el jugador 2 y (0,0)para un empate.

Ejemplo de uso:

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

Explicación:

La función infijo !traduce una secuencia de acciones 'r'(recargar), 'f'(fuego) y 'g'(guardia) a una secuencia de acciones observables 0(fuego real), 1(sin acción) y 2(guardia), donde una acción de fuego solo se cuenta como acción de fuego real si se carga una viñeta, y de lo contrario no se realiza ninguna acción . Para lograr esto, el primer argumenton es 0si una bala está cargada y 1si la pistola no está cargada. De esta manera, cada uno 'f'simplemente puede ser reemplazado por el actual n. ( n=0-> cargado -> fuego real -> 0, n=1-> descargado -> sin acción -> 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

Las nueve posibilidades resultantes son entonces

  • (0,0): Ambos jugadores disparan y mueren, el juego termina.
  • (0,1) o (1,0) : Un jugador dispara al otro, el juego termina.
  • (0,2) o (2,0) : Un jugador dispara pero los otros guardias, el juego continúa.
  • (1,1), (1,2), (2,1)O (2,2): Ningún jugador golpea, el juego continúa.

Por diseño, la suma de las opciones de finalización del juego es menor que 2 y la suma de cada posibilidad de continuación del juego es mayor o igual a 2. El resultado del juego es entonces la primera tupla con una suma 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
fuente
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%

La entrada es en forma de pares de caracteres para cada turno y sale por nivel de error (0 = empate, 1 = jugador 1, 2 = jugador 2). xy yhacer un seguimiento de si el jugador tiene munición, de modo que cuando ambos disparan, el resultado es 3-x-x-y, a menos que sea 3, en cuyo caso seguimos adelante. En la línea 5 abusé del analizador de Batch - %1(que es el movimiento actual) se sustituye antes de que la shiftdeclaración se ejecute y la elimine, por lo que aún vamos a la etiqueta correcta.

Neil
fuente
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 golf (si ambas personas están vivas usamos Mpara actualizar su munición y el estado de vida del enemigo, de lo contrario devolveremos el estado actual):

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

Ejemplo de uso (el primer elemento indica si el jugador 1 está vivo al final del juego, el segundo elemento indica si el jugador 2 está vivo, el tercero y el cuarto indican el estado de la munición que no es relevante al determinar el ganador):

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

Actualización: ¡Mira eso, looptiene una longitud idéntica! Creo que la reduceversión es más fácil de desarrollar, ya que puede inspeccionar fácilmente los estados intermedios si la usa 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
fuente
¡Yo estaba esperando! Eso es denso, wow.
Carcigenicar
Jeje gracias, todavía me molesta que tuve que repetir [l1 l2 r1 r2](sus valores modificados en lety sus valores originales) y esas fnfirmas.
NikoNyrh
Al menos para este último, por eso estoy a favor loop. Me parece que conduce a un código más ordenado. Tan pronto como necesito doblar con más de 1 acumulador, cambio.
Carcigenicar
1

PHP, 107 101 90 bytes

usando una máscara de bits $ d para el estado de carga y una secuencia DeBruijn para los movimientos 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;

toma la entrada como argumentos de línea de comando de 2 caracteres, ejecuta con -nr.

1 = Jugador 1 gana
2 = Jugador 2 gana
3 = empate

Descompostura

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;
  • Secuencia DeBruijn fr:: posición = 1 = incendios P1; rf= posición 2 = incendios P2,ff = posición 3 = ambos disparan
  • g<$m<=> f<$m[0]( f<$msiempre es cierto, porque hay un segundo carácter).
Titus
fuente
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
fuente
2
Bienvenido al sitio. El concurso se puntúa en recuento de bytes, por lo que recomendaría incluir un recuento de bytes en su título e intentar minimizarlo reduciendo la longitud de los nombres de las variables. También debe incluir el idioma en el que ha escrito esto (para mí parece Python3).
Post Rock Garf Hunter
2
Como se mencionó anteriormente, esta es una competencia que puede hacer que el programa más pequeño sea posible para realizar la tarea. Estás usando nombres completos como en turnslugar de solo t, lo que significa que el programa es mucho más grande de lo necesario. Además, comience su envío con algo como #Python 2, 200 bytes(suponiendo que esto sea 2, y el programa tiene una longitud de 200 bytes) para que el lenguaje que esté usando sea claro.
Carcigenicar
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 cambiando la función a una función completa en lugar de usar una macro. Eso me permitió hacer las macros de funciones internas, lo que ahorra un poco.

Esta es una solución muy literal. Estoy un poco alucinado ya que acabo de escribir una versión completa del juego, y esta es básicamente una versión muy reducida del algoritmo que utilicé. Probablemente hay muchas optimizaciones que podría hacer, pero estoy bastante feliz con eso. Consulte el código pregolfed para obtener una explicación.

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