¡Juguemos a la mafia!

42

Mafia (también conocido como Werewolf) es un juego de mesa que se juega más o menos así:

  • El juego comienza el día 0. Después de cada día nllega una noche n. Después de cada noche nllega un día n+1. es decir D0, N0, D1, N1, D2, N2...
  • Al amanecer del día 0, un anfitrión elige en secreto a los jugadores para cumplir ciertos roles:  
    • Algunos jugadores se convierten en la mafia. Cada noche, cada mafioso elige un jugador. Al amanecer del día siguiente, el jugador elegido por los más mafiosos es asesinado. Se eliminan permanentemente del juego y su papel se revela públicamente. Alineado con la mafia.  
    • Algunos jugadores se convierten en policías. Todas las noches, cada policía elige un jugador. Al amanecer del día siguiente, el policía se da cuenta de la alineación de los jugadores. Alineado con el pueblo.  
    • Algunos jugadores se convierten en médicos. Todas las noches, cada médico elige un jugador. Si este jugador es el mismo jugador que la mafia decidió matar, las acciones de la mafia para esa noche se cancelan. Alineado con el pueblo.  
    • Todos los jugadores que no son elegidos para otro papel son aldeanos. Los aldeanos no tienen habilidades que no sean compartidas por toda la ciudad. Alineado con el pueblo.
  • Todos los días, excepto el día 0, toda la ciudad (es decir, todos los jugadores vivos) vota por un jugador. Al final del día, ese jugador es eliminado del juego y se revela su papel. (El día 0, todo el mundo se relaja hasta el anochecer).
  • Si, en algún momento, no quedan mafiosos restantes, el juego termina con todos los jugadores alineados en la aldea victoriosos (incluidos los muertos).
  • Si, en algún momento, los jugadores alineados con la aldea no superan en número a los jugadores alineados con la mafia, el juego termina con todos los jugadores alineados con la mafia victoriosos (incluidos los muertos).

Para este desafío, tu objetivo es escribir un bot para vencer a otros bots en Mafia.

Cómo hacer un bot que funcione

Todo lo que tiene que proporcionarme es un archivo llamado run. Dentro de la estructura de directorios donde tendrá lugar este desafío, su bot vivirá aquí:

start
controller/
tmp/
players/               # You are here!
    some_bot/          # Let's pretend you're some_bot.
        to_server
        from_server
        players
        run            # This is what you give me
    mafia-game-bot/
    skynet/

El runarchivo, cuando se ejecute, hará que su bot haga lo suyo. Es importante tener en cuenta que este archivo no debe requerir ningún argumento de línea de comando ni nada. Se ejecutará exactamente como ./run. Si necesita ser ejecutado de una manera diferente, tendrá que solucionarlo haciendo algo como esto:

real_bot.py

#!/bin/python2

# code goes here

run

#!/bin/bash

./real_bot.py --flags --or --whatever

Una cosa importante a tener en cuenta es que todas las entradas que recibe su bot se encontrarán en el archivo from_servery el programa de control buscará la salida de su bot to_server. Elegí hacerlo de esta manera para que cualquier idioma que pueda hacer archivos de E / S pueda participar. Si su idioma facilita el trabajo con stdin y stdout que la E / S de archivo, es posible que desee escribir un runarchivo que se vea así:

#!/bin/bash

./real_bot.py < from_server > to_server

Esto hará que stdin provenga del from_serverarchivo y stdout vaya directamente a to_server.

Tu bot no se mantendrá en funcionamiento mientras dure el juego. En su lugar, se ejecutará cuando necesite tomar una decisión. Del mismo modo, no se le informará cuando esté muerto, simplemente ya no se ejecutará. Planifique esto guardando todo lo que quiera recordar en un archivo y leyéndolo más tarde. Puede crear, escribir o leer desde cualquier archivo en la carpeta de su bot, pero no puede escribir ni leer en ningún lugar fuera de esa carpeta, incluido el acceso a la red o cualquier cosa . Si su bot sabe algo que no se le dijo desde dentro de la carpeta, o si toca algo que no está dentro de esa carpeta, su bot está descalificado.

Cómo hacer un bot funcional

Día

Al comienzo del juego, el archivo playersse completará con una lista delimitada por la nueva línea de todos los jugadores en el juego. No se actualizará cuando los jugadores abandonen el juego.

Al amanecer del día 0, todos los jugadores encontrarán este mensaje en su from_serverarchivo:

Rise and shine! Today is day 0.
No voting will occur today.
Be warned: Tonight the mafia will strike.

Si usted es el policía, la línea You are the copse agrega al final. El doctor ve You are the doctor. La mafia ve You are a member of the mafia.\nYour allies are:y una lista delimitada por la nueva línea de miembros de la mafia, excluyendo al jugador que lee el mensaje.

Al amanecer de todos los demás días, aparecerá este mensaje:

Dawn of day `day_number`.
Last night, `victim` was killed. They were `victim_role`.
Investigations showed that `cop_target` is `target_alignment`-aligned.
These players are still alive: `remaining_players`

dayNumberse reemplaza con el número del día. victimse reemplaza con el nombre de la víctima de la noche anterior y victim_rolees uno de:

  • a villager
  • a mafioso
  • the cop
  • the doctor

cop_targetes el nombre del jugador que el policía investigó anoche, y target_alignmentes villageo mafia. Finalmente, remaining_playershay una lista de jugadores que aún están vivos en este formato:player1, player2, player3

La segunda línea se omite si no hubo asesinato anoche, y la tercera línea se muestra solo al policía.

Por ejemplo,

Dawn of day 42.
Last night, Xyzzy was killed. They were a villager.
Investigations showed that Randy is mafia-aligned.
These players are still alive: Randy, CopBot, JohnDoe, Steve

Una vez que este mensaje está fuera del camino, ¡comienza el día! Cada bot puede realizar 50 acciones durante el día, donde una "acción" es votar por un jugador o decir algo en voz alta.

Para votar por un jugador, escriba vote player_nameen su to_serverarchivo y finalice. Para votar para no matar a nadie, escribe vote no one. Cuando vote, todos los jugadores (incluido usted) verán your_bot votes to kill your_selection. Los votos se ignoran el día 0.

Se pueden enviar varios mensajes predefinidos a todos los jugadores. La identificación de cada mensaje posible se enumera aquí:

 0: No
 1: Yes
 2: I am the cop
 3: I am the doctor
 4: I am a normal villager
 5: I trust this player: 
 6: I think this player is suspicious: 
 7: I think this player is the cop: 
 8: I think this player is the doctor: 
 9: I think this player is a normal villager: 
10: I think this player is mafia: 
11: Do you think this player is mafia? 
12: I tried to save this player: 
13: I successfully saved this player: 
14: I investigated this player and found that they were mafia-aligned: 
15: I investigated this player and found that they were village-aligned: 
16: Will you please use your power on this player tonight?

Todos estos mensajes, excepto los primeros cinco, se refieren a un jugador específico. Para decir uno de esos mensajes, escribe say message_id player_name. Para uno de los primeros cinco mensajes, solo escriba say message_id. Puede agregar un tercer argumento opcional a ambos, especificando el nombre de un jugador con el que está hablando (todos los jugadores aún pueden leerlo, pero sabrán quién es el destinatario previsto).

Cuando su bot dice un mensaje, todos los jugadores leen your_bot says "message", ¿dónde messageestá el mensaje asociado con la identificación que escribió? Si el mensaje incluye un asunto, un carácter de espacio y el asunto se insertan directamente después del final del mensaje. Si incluye un destinatario, su nombre, dos puntos y un carácter de espacio se insertan inmediatamente antes del mensaje.

Al final del día, todos los jugadores vivos se ejecutan por última vez para ver el resultado de la votación. Si se votó a un jugador, esto se escribe:

The town has killed player_name!
They were a villager

... o a mafioso, o the cop, o the doctor.

Si no se votó a ningún jugador, esto se escribe en su lugar:

The town opted to lynch no one today.

Cuando el controlador envía estos mensajes, ignora cualquier respuesta de los jugadores. Se acabó el día.

Noche

Por la noche, todos menos los aldeanos pueden usar su poder.

Mafia:

Usted va a leer It is night. Vote for a victim.. Cuando esto sucede, escribe el nombre del jugador que te gustaría matar.

Policía:

Usted va a leer It is night. Who would you like to investigate?. Cuando esto suceda, escriba el nombre del jugador que desea verificar.

Doctor:

Usted va a leer It is night. Who would you like to save?. Cuando esto suceda, escriba el nombre del jugador que desea proteger.

Después de esto, el día siguiente comienza de manera normal.

Puede salvarse solo una vez por juego.

Información general

  • El juego no se ejecutará sin 6 o más jugadores.
  • Un tercio de los jugadores, redondeados hacia abajo, serán mafiosos. Un jugador será médico y un jugador será policía. Todos los demás jugadores son aldeanos.
  • Los lazos en el voto del pueblo o el voto nocturno de la mafia se resuelven al azar.
  • Los nombres de los bot deben ser alfanuméricos + guiones y guiones bajos.
  • Está prohibido utilizar el conocimiento del código del oponente directamente. En teoría, debería ser capaz de poner tu bot contra bots que nunca hayas visto antes y que tenga un rendimiento comparable.
  • Lamentablemente, si no puedo ejecutar su programa utilizando software exclusivamente gratuito (como en cerveza), tendré que descalificarlo.
  • Me reservo el derecho de descalificar cualquier envío si creo que es malicioso. Esto incluye, entre otros, el uso excesivo de tiempo, memoria o espacio para ejecutar. He dejado el límite intencionalmente en blanco, pero recuerda: estoy ejecutando esto en la computadora de mi casa, no en una supercomputadora, y no quiero que los resultados tarden un año. No espero tener que usar esto, ya que mis estándares son bastante bajos. Esto es básicamente "si creo que estás siendo un idiota a propósito", y si puedes convencerme de lo contrario, revertiré mi decisión.

Tanteo

Cada ronda, se ejecutarán 100 juegos (esto puede aumentar a medida que se unen más bots para mantener el tamaño de la muestra lo suficientemente grande, pero en teoría eso no afectará nada). Registraré cuántas veces gana cada bot como aldeano en comparación con cuántas veces juega como aldeano, y lo mismo para la mafia. Un bot villager_ratioes number of games won as villager / number of games played as villager, y mafia_ratioes lo mismo pero s/villager/mafia/g. La puntuación de un bot es (villager_ratio - mean villager_ratio) + (mafia_ratio - mean mafia_ratio).

Ejemplo bot

Randy the Robot no es un buen jugador de la mafia. Randy ignora casi todo, eligiendo al azar qué decir, a quién votar y a quién apuntar con poderes nocturnos.

run.sh:

#!/bin/bash

./randy.py < from_server > to_server

randy.py:

#!/usr/bin/env python

import random

with open('players') as f:
    p = f.read().split() + ['no one']


day = True
try:
    line = raw_input()
    if line.endswith(('?', 'victim.')):
        day = False
    if not day:
        print random.choice(p)
    else:
        if random.random() > 0.5:
            if random.random() > 0.5:
                print 'vote {}'.format(random.choice(p))
            else:
                id = random.randint(0, 17)
                print 'say {}{}'.format(id, (' ' + random.choice(p)) if id > 4 else '')
except: pass

Controlador

@undergroundmonorail escribió un programa de control para este desafío, disponible aquí .

Tienes un mes para codificar y entregar las respuestas, le daré al bot ganador (el mayor desempate es votos) al menos una recompensa de 50 reputación (dependiendo de la cantidad de reputación que pueda ganar en un mes)


Aquí hay un script de envoltura, creado por @Blacksilver, para usar con lenguajes compilados:

#!/bin/bash

run="./a.out"
compile="gcc bot.c"

if [ -e $run ]; then
        $run
else
        $compile
        $run
fi

Pon esto en run.


Esta publicación fue escrita por @undergroundmonorail (hice algunas ediciones).

Se lo entregó aquí a cualquiera que quisiera terminarlo y publicarlo.

Christopher
fuente
Los comentarios no son para discusión extendida; Esta conversación se ha movido al chat .
Martin Ender

Respuestas:

3

zulú

run

#!/usr/bin/env php
<?php
error_reporting(E_ERROR|E_WARNING|E_PARSE);

$self = basename(__DIR__);

$msgids = array(
    "No",
    "Yes",
    "I am the cop",
    "I am the doctor",
    "I am a normal villager",
    "I trust this player:",
    "I think this player is suspicious:",
    "I think this player is the cop:",
    "I think this player is the doctor:",
    "I think this player is a normal villager:",
    "I think this player is mafia:",
    "Do you think this player is mafia?",
    "I tried to save this player:",
    "I successfully saved this player:",
    "I investigated this player and found that they were mafia-aligned:",
    "I investigated this player and found that they were village-aligned:",
    "Will you please use your power on this player tonight?"
);
$msgids = array_flip($msgids);

if(!file_exists('./from_server')){
    die;
}
$in = file('from_server');
if(count($in) && strpos($in[0],'day 0.') !== false){
    $game = array(
        'day'               =>0,
        'players'           =>array(),
        'alive'             =>array(),
        'dead'              =>array(),
        'mafia'             =>array(),
        'village'           =>array(),
        'cop'               =>'',
        'doctor'            =>'',
        'votes'             =>array(),
        'messages'          =>array(),
        'currentvotes'      =>array(),
        'currentmessages'   =>array()
    );
    $playersfile = file('players');
    foreach($playersfile as $name){
        $game['players'][trim($name)] = 1;
        $game['alive'][trim($name)] = 1;
        $game['votes'][trim($name)] = array();
        $game['messages'] = array();
    }
    $allies = false;
    foreach($in as $line){
        if($allies){
            if(array_key_exists(trim($line),$game['players'])){
                $game['mafia'][trim($line)] = 1;
            }
        }
        else if(strpos($line,"You are the cop") !== false){
            $game['cop'] = $self;
            $game['village'][$self] = 1;
        }
        else if(strpos($line,"You are the doctor") !== false){
            $game['doctor'] = $self;
            $game['village'][$self] = 1;
        }
        else if(strpos($line,"member of the mafia") !== false){
            $game['mafia'][$self] = 1;
        }
        else if(strpos($line,"allies are:") !== false && $game['mafia'][$self]){
            $allies = true;
        }
    }
    if(!$game['mafia'][$self]){
        $game['village'][$self] = 1;
    }
    else{
        foreach($game['players'] as $name=>$g){
            if(!$game['mafia'][$name]){
                $game['village'][$name] = 1;
            }
        }
    }
    $out = json_encode($game);
    write('myinfo',$out);
}
else{
    $myinfo = file_get_contents('myinfo');
    $game = json_decode($myinfo,true);
    if(count($in) && strpos($in[0],"town has killed") !== false){
        $e = explode(" ",trim($in[0]));
        $dead = trim($e[4],'!');
        unset($game['alive'][$dead]);
        $game['dead'][$dead] = 1;
        $e = explode(" ",trim($in[1]));
        $allegiance = trim($e[3],".");
        $game[$allegiance][$dead] = 1;
    }
    else if(count($in) && strpos($in[0],"town opted to") !== false){
        //
    }
    else if(count($in) && strpos($in[0],"night") !== false){
        if(strpos($in[0],"victim") !== false){
            $voted = false;
            if($game['day'] > 0){
                $possible = array();
                foreach($game['alive'] as $name=>$g){
                    if(!$game['mafia'][$name]){
                        foreach($game['votes'][$name] as $for){
                            if($voted && $game['mafia'][$for]){
                                $possible[] = $name;
                            }
                        }
                    }
                }
                if(count($possible)){
                    shuffle($possible);
                    write('to_server',$possible[0]);
                    $voted = 1;
                }               
            }
            if(!$voted){
                while($rand = array_rand($game['alive'])){
                    if(!$game['mafia'][$rand]){
                        write('to_server',$rand);
                        $voted = 1;
                        break;
                    }
                }
            }
        }
        else if(strpos($in[0],"investigate") !== false){
            $possible = array();
            foreach($game['alive'] as $name=>$g){
                if(!$game['village'][$name] && !$game['mafia'][$name] && $game['doctor'] != $name){
                    $possible[] = $name;
                }
            }
            if(count($possible)){
                shuffle($possible);
                write('to_server',$possible[0]);
            }
        }
        else if(strpos($in[0],"save") !== false){
            if($game['day'] == 0){
                write('to_server',$self);
            }
            else{
                if($game['cop'] != '' && $game['alive'][$game['cop']]){
                    write('to_server',$game['cop']);
                }
                else{
                    $voted = false;
                    foreach($game['alive'] as $name=>$g){
                        if($game['village'][$name] && $name != $self){
                            write('to_server',$name);
                            $voted = true;
                            break;
                        }
                    }
                    if(!$voted){
                        while($rand = array_rand($game['alive'])){
                            if($rand != $self){
                                write('to_server',$rand);
                                break;
                            }
                        }
                    }
                }
            }
        }
    }
    else if(count($in) && strpos($in[0],"Dawn of day") !== false){
        $e = explode(" ",trim($in[0]));
        $game['day'] = trim($e[3],".");
        foreach($in as $line){
            if(strpos($line,"was killed") !== false){
                $e = explode(" ",trim($line));
                $dead = $e[2];
                if(strpos($line,"the cop") !== false){
                    $game['cop'] = $dead;
                    $game['village'][$dead] = 1;
                }
                else if(strpos($line,"the doctor") !== false){
                    $game['doctor'] = $dead;
                    $game['village'][$dead] = 1;
                }
                else if(strpos($line,"a villager") !== false){
                    $game['village'][$dead] = 1;
                }
                else if(strpos($line,"a mafioso") !== false){
                    $game['mafia'][$dead] = 1;
                }
                unset($game['alive'][$dead]);
                $game['dead'][$dead] = 1;
            }
            else if(strpos($line,"Investigations showed") !== false){
                $e = explode(" ",trim($line));
                $name = $e[3];
                $align = trim($e[5]);
                $e = explode("-",$align);
                $game[$e[0]][$name] = 1;
            }
        }
        $game['currentvotes'] = array();
        $game['currentmessages'] = array();
        foreach($game['alive'] as $name=>$g){
            $game['currentvotes'][$name] = '';
        }
    }
    else{
        foreach($in as $line){
            if(strpos($line," has voted to lynch no one") !== false){
                $e = explode(" ",trim($line));
                $game['votes'][$e[0]][] = false;
                $game['currentvotes'][$e[0]] = false;
            }
            else if(strpos($line," has voted to ") !== false){
                $e = explode(" ",trim($line));
                $game['votes'][$e[0]][] = trim($e[5]," .");
                $game['currentvotes'][$e[0]] = trim($e[5]," .");
            }
            else if(strpos($line," says ") !== false){
                foreach($msgids as $msg=>$id){
                    $chk = preg_match('/([^\s]+) says "(([^\s]+)[:,] )?'.preg_quote($msg).'( ([^\s]+))?"/',$line,$matches);
                    if($chk){
                        //                                  said by     said to     said  said about
                        $game['messages'][]         = array($matches[1],$matches[3],$msg, $matches[5]);
                        $game['currentmessages'][]  = array($matches[1],$matches[3],$msg, $matches[5]);
                    }
                }
            }
        }
        $written = false;
        $convo = array();
        foreach($game['currentmessages'] as $msg){
            if($msg[1] == $self){
                $convo[$msg[0]] = $msg;
            }
            else if($msg[0] == $self && $msg[1] != ''){
                unset($convo[$msg[1]]);
            }
        }
        if(count($convo)){
            foreach($convo as $c){
                if($msgids[$c[2]] == 11){
                    if($game['mafia'][$msg[3]]){
                        write('to_server',"say 1 ".$msg[0]);
                        $written = true;
                        break;
                    }
                    else if($game['village'][$msg[3]]){
                        write('to_server',"say 0 ".$msg[0]);
                        $written = true;
                        break;
                    }
                    else{
                        write('to_server',"say 11 ".$msg[0]);
                        $written = true;
                        break;
                    }
                }
                else if($msgids[$c[2]] == 16){
                    write('to_server',"say 0 ".$msg[0]);
                    $written = true;
                }
                else{
                    write('to_server',"say 4 ".$msg[0]);
                    $written = true;
                }
            }
        }
        if(!$written){
            $currentvote = false;
            if(array_key_exists($self,$game['currentvotes'])){
                $currentvote = $game['currentvotes'][$self];
            }
            if($game['mafia'][$self]){
                $votes = @array_count_values($game['currentvotes']);
                if($votes && count($votes)){
                    arsort($votes);
                    foreach($votes as $name=>$number){
                        if($game['village'][$name]){
                            if($currentvote != $name){
                                write('to_server','vote '.$name);
                                $written = true;
                                break;
                            }
                        }
                    }
                }
            }
            else{
                if(count($game['mafia'])){
                    foreach($game['mafia'] as $name=>$g){
                        if($game['alive'][$name]){
                            $written = true;
                            if($currentvote != $name){
                                write('to_server','vote '.$name);
                            }
                            break;
                        }
                    }
                    if(!$written){
                        foreach($game['mafia'] as $name=>$g){
                            $non = $game['alive'];
                            unset($non[$self]);
                            if(array_key_exists($name,$game['votes'])){
                                foreach($game['votes'][$name] as $vote){
                                    if(array_key_exists($vote,$non)){
                                        unset($non[$vote]);
                                    }
                                }
                            }
                            if(count($non)){
                                $rand = array_rand($non);
                                write('to_server','vote '.$rand);
                                $written = true;
                                break;
                            }
                        }
                    }
                }
                if(!$written && $game['cop']){
                    $possible = array();
                    foreach($game['votes'][$game['cop']] as $name){
                        if($game['alive'][$name] && $name != $self){
                            $possible[] = $name;
                        }
                    }
                    if(count($possible)){
                        shuffle($possible);
                        write('to_server','vote '.$possible[0]);
                        $written = true;
                    }
                }
                if(!$written && count($game['dead'])){
                    foreach($game['dead'] as $name=>$g){
                        if($game['village'][$name]){
                            $v = array();
                            foreach($game['votes'] as $voted=>$arr){
                                if($game['alive'][$voted] && in_array($name,$arr)){
                                    $v[$voted] = 1;
                                }
                            }
                            unset($v[$self]);
                            if(count($v)){
                                $rand = array_rand($v);
                                write('to_server','vote '.$rand);
                                $written = true;
                                break;
                            }
                        }
                    }
                }
                if(!$written){
                    $votes = @array_count_values($game['currentvotes']);
                    if($votes && count($votes) && array_key_exists($self,$votes)){
                        arsort($votes);
                        foreach($votes as $name=>$number){
                            if(!$game['village'][$name]){
                                if($name != $self){
                                    write('to_server','vote '.$name);
                                    $written = true;
                                    break;
                                }
                            }
                        }
                    }
                }
            }
        }
    }
    $myinfo = json_encode($game);
    write('myinfo',$myinfo);
}

function write($filename,$data){
    $fh = fopen($filename,"wb+");
    if($fh){
        $bytes = fwrite($fh,$data);
        fclose($fh);
    }
}

No todo lo que esperaba que fuera. Puedo terminar retocándolo ocasionalmente.

Cómo funciona v1.0

Lleva un registro del número del día, quién está vivo, quién está muerto, quién es la mafia, quién está alineado con la aldea, roles, votos / mensajes del día actual y mensajes / votos generales.

  1. Noche

    a. Mafia: vote por cualquier aldeano que haya votado en contra de la mafia (al azar) si es posible, de lo contrario, un aldeano al azar.

    si. Policía: investiga a cualquiera que tenga una alineación desconocida.

    do. Doctor: guarde el primer turno, luego guarde el policía si lo sabe (no creo que pueda saberlo hasta ahora), salve al aldeano si lo sabe (probablemente tampoco lo sepa), de lo contrario, guarde a una persona al azar.

  2. Día

    a. Si alguien ha hablado un mensaje directamente a sí mismo, responda a ellos (respuestas limitadas posibles).

    si. Mafia: vota por el aldeano que tenga más votos.

    do. Aldeano con cualquier conocido vivo alineado con la mafia: vote por el mafioso.

    re. Aldeano con solo muertos conocidos alineados con la mafia: vote por un bot aleatorio que nunca haya votado por el mafioso.

    mi. Aldeano con policía conocido: vote por un bot aleatorio por el que ha votado el policía.

    F. Aldeano con muertos conocidos alineados con la aldea: vote por un robot aleatorio que votó por los muertos.

    sol. Aldeano con votos en contra del voto propio para el bot no alineado más votado actualmente que no esté alineado con la aldea.

Jo
fuente
1
Espera, ¿qué hace esto?
SIGSTACKFAULT
1
¡Por qué, juega mafia, por supuesto! :)
Jo.
Me refiero a la estrategia.
SIGSTACKFAULT
6

El código de ejemplo no funcionó para mí, uso Python 3, así que cambié el main.pyarchivo para que funcione.

Así que aquí está mi versión fija para Python 3, nunca antes programé en Python, así que tal vez sea un código horrible, pero funciona :)

run.sh:

#!/bin/bash

./randy.py < from_server > to_server

randy.py:

#!/usr/bin/env python3

import random

with open('players') as f:
    p = f.read().split() + ['no one']

with open('from_server') as f:
    fs = f.read().split()

msg = ""
day = True
try:
    line = fs[0]
    if line.endswith(('?', 'victim.')):
        day = False
    if not day:
        msg = (random.choice(p))
    else:
        if random.random() > 0.5:
            if random.random() > 0.5:
                msg = ('vote {}'.format(random.choice(p)))
            else:
                id = random.randint(0, 17)
                msg = ('say {}{}'.format(id, (' ' + random.choice(p)) if id > 4 else ''))

    with open('to_server', 'w') as f:
        f.write(msg)
    print(msg)
except: pass

Algunas cosas que aprendí mientras hacía este trabajo (y no estaba claro para mí en la descripción)

  • printno hace nada con el juego es como un console.login js
  • input() bloquea el programa en ejecución, puede ser bueno para la depuración paso a paso
  • from_servery to_serverse borra cada ronda.
  • Es imposible detener el script con una Ctrl+Ccombinación, lo cual es molesto.
Peter
fuente
Bienvenido a PPCG! Gran primer post! ¡Espero que te quedes! He editado su publicación para que tenga resaltado de sintaxis en funcionamiento y, en aras de la coherencia, agregué a run.sh.
Rɪᴋᴇʀ
1
¡Gracias! No estoy seguro de que < from_server > to_serversea ​​necesario porque codifiqué los nombres de archivo en el código. el motor del juego solo llama ./runsin tuberías. así input()y print()no lo hicieron trabaja con el juego. mayn.pylínea 57:os.system('./run')
Peter
2
¿Cómo hiciste funcionar el controlador? No puedo resolverlo. ¿Puede proporcionar una invocación de muestra?
Rɪᴋᴇʀ
Nota: El original randy.pyfue escrito en Python 2 , lo que causó los problemas.
SIGSTACKFAULT
para el controlador que necesita ./startdesde la carpeta original o necesita una versión de Python 3 delmain.py
Peter
5

El lógico

#!/usr/bin/env python3
import sys
import os
import re
import random
from types import SimpleNamespace
def chooseSet(set):
    return random.choice(list(set))
sys.stdin = open("from_server")
sys.stdout = open("to_server","w")
def saveData(data):
    with open("gameData.txt", "w") as datafile:
        datafile.write(repr(data.__dict__))
MY_NAME = os.path.basename(os.getcwd())
opener = input()
DATABASES = ("targets","herd","mafiosos","guilty","innocent","unlikely", "requests",
            "selfvotes","players","used_roles")
ALLOW_SELF = ("players", "mafiosos")
LIESPERROLE = {"cop": ("I am the cop",
                "I investigated this player and found that they were mafia-aligned",
                "I investigated this player and found that they were village-aligned"),
              "doctor": ("I am the doctor",
                   "I tried to save this player",
                   "I successfully saved this player"
                   )
        }
#1: At the beginning of the game, parse beginning of day 0
if opener == "Rise and shine! Today is day 0.":
    #Next two lines are completely predetermined and hold no data
    assert input() == "No voting will occur today."
    assert input() == "Be warned: Tonight the mafia will strike."
    data = SimpleNamespace(cop=False, doctor=False, queued=[],askers={})
    for datum in DATABASES:
        setattr(data, datum, set())
    try:
        nextline = input()
        if nextline == "You are a member of the mafia.":
            data.mafiosos.add(MY_NAME)
            assert input() == "Your allies are:"
            while True:
                data.mafiosos.add(input())
        elif nextline == "You are the doctor":
            data.doctor = True
            data.used_roles.add("doctor")
        elif nextline == "You are the cop":
            data.cop = True
            data.used_roles.add("cop")
    except EOFError:
        #villager, or ran out of mafiosos to add
        pass
    with open("players") as playersfile:
        data.players = set(playersfile.read().strip().splitlines())
    saveData(data)
    exit()
with open("gameData.txt") as datafile:
    data = SimpleNamespace(**eval(datafile.read().strip()))
#2: Beginning of day nonzero
if opener.startswith("Dawn of day"):
    data.requests.clear()
    data.selfvotes.clear()
    data.askers.clear()
    data.voted = False
    try:
        while True:
            nextline = input()
            victim = re.match("Last night, (.*) was killed. They were (?:a|the) (.*).", nextline)
            if victim:
                victim, role = victim.groups()
                #remove dead people from lists
                for datum in DATABASES:
                    getattr(data, datum).discard(victim)
                if role == "cop" or role == "doctor":
                    data.used_roles.add(role)
                continue
            investigated = re.match("Investigations showed that (.*) is (.*)-aligned.", nextline)
            if investigated:
                assert data.cop
                who = investigated.group(1)
                if investigated.group(2) == "mafia":
                    data.guilty.add(who)
                    data.unlikely.discard(who)
                else:
                    data.targets.discard(who)
                    data.herd.discard(who)
                    data.innocent.add(who)
                    data.unlikely.add(who)
                continue
    except EOFError:
        pass
#3: We're being told some messages / news
elif " says " in opener or " voted " in opener:
    message = opener
    acted = question = False
    try:
        while True:
            if " voted " in message:
                message = "<vote against>"
                speaker, subject = re.match("(.*) has voted to lynch (.*)", message).groups()
                target = None
            else:
                speaker, target, message, subject = \
                    re.match("(.*) says \"(?:(.*), )?([^:\?]+)(?:[:\?]\s*(.*))?\"",
                             message).groups()
            if speaker == MY_NAME:
                continue
            BAD_MESSAGES = ("<vote against>", "I think this player is mafia",
                            "I investigated this player and found that they were mafia-aligned",
                            "I think this player is suspicious")
            GOOD_MESSAGES = ("I think this player is the cop",
                             "I think this player is the doctor",
                             "I think this player is a normal villager",
                             "I trust this player",
                             "I investigated this player and found that they were village-aligned")
            OUTS = "I am the cop", "I am the doctor"
            LIES = ()
            for role in data.used_roles:
                LIES += LIESPERROLE[role]
            if message == "Yes" or message == "No":
                if question and not target:
                    target = chooseSet(data.askers)
                if target in data.askers:
                    BAD_MESSAGES += "Yes",
                    GOOD_MESSAGES += "No",
                    subject = data.askers[target]
            if message in LIES and speaker not in data.mafiosos and speaker not in data.innocent:
                # What you just said is false, and I know it!
                data.unlikely.discard(speaker)
                data.targets.add(speaker)
                if subject and subject not in (data.unlikely.union(data.mafiosos)):
                    data.targets.add(subject)
            elif message in BAD_MESSAGES:
                if speaker in data.guilty:
                    #mafiosos rarely turn on eachother
                    data.unlikely.add(subject)
                    data.targets.discard(subject)
                elif speaker in data.unlikely:
                    #believe the herd, especially people who we trust
                    data.herd.add(subject)
                elif subject in data.unlikely:
                    #how dare you speak against players likely to be village-aligned!
                    data.targets.add(speaker)
                elif subject == MY_NAME or subject in data.mafiosos:
                    #DON'T ATTACK ME (or my fellow mafiosos)
                    data.targets.add(speaker)
                else:
                    #believe the herd
                    data.herd.add(subject)
                if not acted and message == "<vote against>":
                    if subject == MY_NAME:
                        data.selfvotes.add(speaker)
                        if len(data.selfvotes) >= (len(data.players)-len(data.mafiosos))/3:
                            if data.cop:
                                print("say 2")
                                #give a data point to prove it
                                if random.random() > .5 and data.guilty:
                                    data.queued.append("say 14 %s" % chooseSet(data.guilty))
                                elif data.innocent:
                                    data.queued.append("say 15 %s" % chooseSet(data.innocent))
                            else:
                                print("say 4") #Don't out myself if I'm the doctor
                                # and just lie if I'm a mafioso
                            acted = True
                    else:
                        data.selfvotes.discard(speaker)
            elif message in OUTS and data.mafiosos and speaker not in data.unlikely:
                data.targets.add(speaker) #Kill the fools who boast!
            elif message in GOOD_MESSAGES:
                chance = random.random() < .1 - (speaker in data.targets) / 20
                if speaker in data.guilty: #Mafia liars
                    if subject not in data.unlikely:
                        data.targets.add(subject)
                elif subject == MY_NAME and chance:
                    if speaker in data.targets:data.targets.remove(speaker)
                    data.unlikely.add(speaker)
                elif speaker in data.unlikely or chance:
                    data.unlikely.add(subject)
            elif message == "Do you think this player is mafia":
                if subject == MY_NAME:
                    data.targets.append(speaker)
                if target == MY_NAME or not target:
                    if speaker in data.guilty:
                        data.queued.append("say 14 %s %s" % (subject, speaker))
                    elif speaker in data.innocent:
                        data.queued.append("say 15 %s %s" % (subject, speaker))
                    elif subject in data.targets or subject in data.herd:
                        data.queued.append("say 1 %s" % (speaker))
                    elif subject in data.unlikely:
                        data.queued.append("say 0 %s" % (speaker))
                    if data.cop:
                        data.requests.add(subject)
                data.askers[speaker] = subject
                question = True
            elif target == MY_NAME and message == "Will you please use your power on this player tonight":
                data.requests.add(subject)
            message = input()
    except EOFError:
        pass
    for datum in DATABASES:
        if datum in ALLOW_SELF: continue
        getattr(data, datum).discard(MY_NAME)
    chance = random.random()
    if data.queued:
        print(data.queued.pop())
    elif chance < .1:
        target = chooseSet(data.targets or data.players)
        if target != MY_NAME:
            print("say 10 %s" % target)
            data.askers[MY_NAME] = target
    elif chance < .3 and data.targets:
        print("say 6 %s" % chooseSet(data.guilty or data.targets))
    elif chance < .5 and data.unlikely:
        print("say 5 %s" % chooseSet(data.innocent or data.unlikely))
    elif chance < .6 and not data.voted:
        target = chooseSet(data.guilty or data.targets or data.herd or data.players)
        if target not in data.mafiosos and target != MY_NAME:
            print("vote %s" % target)
        data.voted = True
    elif chance < .8:
        #do nothing
        pass
    elif chance < .9:
        #Confuse everybody
        print("say 1")
        data.queued.append("say 0")
######################
#4: End of day
elif "has killed" in opener:
    victim = re.match("The town has killed (.*)!", opener)
    if not victim:
        exit()
    victim = victim.group(1)
    #remove dead people from lists
    for datum in DATABASES:
        getattr(data, datum).discard(victim)
    role = input()
    role = re.match("They were (?:a|the) (.*)", role).group(1)
    if role == "cop" or role == "doctor":
        data.used_roles.add(role)
    #Misc: purge people from lists if too large
    for list in data.unlikely, data.targets, data.herd:
        while len(list) > len(data.players)/3:
            list.pop()
    for player in data.innocent:
        data.unlikely.add(player)
elif opener == "The town opted to lynch no one today.":
    #Do nothing
    pass
#5: Night
elif "night" in opener:
    if not data.mafiosos and data.requests and random.random() > .5:
        print(chooseSet(data.requests))
    if data.doctor:
        print(chooseSet(data.unlikely or data.players))
    else:
        while True:
            try:
              target = (data.targets or data.herd).pop()
            except KeyError:
              target = chooseSet(data.players)
            if target in data.mafiosos or target == MY_NAME:
                continue
            print(target)
            break
else:
    raise ValueError("Unknown message")
saveData(data)

Fantasía, código largo de python que no voy a explicar (aunque no es golf), aparte de eso, mantiene listas de "amigos" y "enemigos" que se poblaron originalmente en base a la investigación del azar y / o la policía . Advertencia: no mienta en presencia del lógico.

pppery
fuente
es tu run.shestándar (haciendo algunas pruebas)
Stan Strum
No, mi run.sh podría ser simplemente "run.py" sin las tuberías de entrada y salida habituales, pero el estándar funcionaría.
pppery
1
Esto se parece mucho a lo que habría escrito, si tuviera el tiempo y la inclinación.
Draco18s
Por alguna razón, creo que al lógico no le irá tan bien con los otros bots ... ninguno de los otros bots informa de investigación policial
JavaScriptCoder
1
... y me doy cuenta, meses después, que mi respuesta asume incorrectamente que solo puede haber un policía / médico.
pppery
4

Superviviente (v 1.0)

Sinopsis

Survivalist simplemente sobrevive brutalmente al juego reprendiendo a cualquiera que se atreva a acusarlo, independientemente de si es mafioso o no.

Lógica

Si sobrevives hasta el final del juego, ganas pase lo que pase. Por lo tanto, sobrevives a toda costa.

Historia de fondo

Las tropas marcharon por el bosque oscuro y húmedo.

"Teniente, ¿hacia dónde marchamos?" El joven recluta aparentemente no se había endurecido ante las atrocidades, pensó el comandante. Oh bien. Él respondió con un brusco "para destruir al enemigo".

En la aldea, el comandante enemigo estaba bebiendo y riendo junto con los otros oficiales del club cuando un explorador se apresuró a recibir la noticia. "¡Hay una columna, de varios cientos de metros de largo, que marcha por el bosque de Yulin por nosotros! ¡Reúne a las tropas!"

El comandante enemigo, obviamente ebrio, dijo inesperadamente: "No he recibido informes de otros exploradores". El explorador (luego Survivalist) pensó, luego tendré que reunir a las tropas yo mismo . Después de contar la historia a los compañeros exploradores, volvieron a estar juntos, todos diciendo que habían visto tropas enemigas. El comandante todavía no creía, diciendo: "Te estoy ordenando que dejes de explorar. No hay tropas enemigas".

Los exploradores decidieron obtener sus armas para salvar a la comunidad. Se las arreglaron para llegar a sus posiciones justo cuando el enemigo llegaba a la aldea en vigor. "¡CARGAR!" gritó el comandante de la emboscada. "¡QUEME LAS CASAS! ¡ QUEME LAS CASAS! ¡MATAR A TODOS, INCLUIDAS LAS MUJERES Y LOS NIÑOS! "

Los exploradores salvaron a todo su ejército. Esperaban promoción, premios y medallas. En cambio, obtuvieron una corte marcial manipulada por motín, condena, 10 años de prisión, destitución deshonrosa del ejército y exilio.


Hay un anciano en el ayuntamiento de Salem, Massachusetts. La leyenda dice que él fundó la ciudad. Cuando lo encuentres en su cabaña aislada en el bosque, no dejes que el brillo en sus ojos te haga pensar que es pacífico. Si lo acusas, te arruinará frente a la ciudad.

El veterano se rio en la oscuridad. Miedo a la oscuridad, de ninguna manera. ¿Miedo a los monstruos debajo de la cama? El hombre con la mano en el gatillo de una pistola se rió nerviosamente. No tenía miedo de nada, se había dicho a sí mismo. Claro, él era un héroe de la guerra en el pasado, pero estaba tan acostumbrado a las emboscadas y las situaciones que amenazan la vida que hizo al hombre simplemente neurótico. Su dedo en gatillo se movió en sombras simples; su latido se aceleró con cada pequeño sonido. Sí, tenía tanto miedo a la muerte. ¿Cómo podría no hacerlo, al ver tanta gente morir de maneras horribles? Todo lo que sabía al ser secuestrado y escapar milagrosamente de sus enemigos era que no había piedad.

Veterano


Código (soy un novato en Python, no estoy seguro si el código es bueno)

#!/bin/python2

import random

with open('players') as f:
    p = f.read().split() + ['no one']


day = True
target = "survivalist"
role = "villager"
try:
    line = raw_input()
    if "You are the cop" in line:
        role = "cop"
    else if "You are the doctor" in line:
        role = "doctor"
    else if "You are a member of the mafia" in line:
        role = "mafia"

    if line.endswith(('?', 'victim.')):
        day = False
    if not day:
        if target == "survivalist":
            print random.choice(p)
        else if role == mafia || role == sheriff:
            print target
        else if role == doctor:
            print random.choice(p)
    else:
        if "survivalist" in line && ("I think this player is suspicious:" in line || 
        "I think this player is mafia:" in line ||
        "I investigated this player and found that they were mafia-aligned:")):
            print 'say 0'
            if role == "villager" || role == "mafia":
                print 'say 4'
            else if role == "cop":
                print 'say 2'
            else if role == "doctor"
                print 'say 3'
            target = line.split(" ")[0]
            print 'vote ' + target

        else if target != "survivalist":
            print 'say 6 ' + target
            print 'vote ' + target
    else:
        pass

except: pass
JavaScriptCoder
fuente
¿Quiso decir en orlugar de ||? ¿Lo probaste? Además, probablemente deberías señalar que es Python 2.
Solomon Ucko
3

Avatar

Avatar "al azar" elige a un jugador al comienzo y lo enfoca implacablemente por el resto de la ronda.

Esto no es una referencia a un programa de televisión animado con un nombre similar.

Es una referencia en línea de EVE.

Descargue el tar de todos los archivos requeridos

Registro de cambios

  • v1 cumpleaños
  • v2 No registra nada en stdout, solo en stderr.
    Para suprimir stderrtambién, agregue 2>/dev/nullal final del runarchivo.
/*  Casting his sight on his realm, the Lord witnessed
    The cascade of evil, the torrents of war.
    Burning with wrath, He stepped 
    down from the Heavens
    To judge the unworthy,
    To redeem the pure.

    -The Scriptures, Revelation Verses 2:12
*/

#include <stdlib.h>
#include <stdio.h>
#include "mafia.h"

int getRandomNumber(){
    return 4; // Chosen by a fair dice roll.
              // Garunteed to be random.
}


void day0(){
    char * target = get_player(getRandomNumber()-1)->name;
    fprintf(stderr, "Target: `%s'\n", target);
    FILE * f = fopen("target", "w");
    if(!f){exit(1);}
    fprintf(f, "%s", target);
    fclose(f);
}


int main(){
    get_players();
    int cycle = get_cycle(day0);
    FILE * out = fopen("to_server", "w");
    if(!out){exit(1);}
    FILE * targetF = fopen("target", "r");
    if(!targetF){exit(1);}

    char target[64];

    fscanf(targetF, "%s", target);

    fprintf(stderr, "Target: %s\n", target);

    if(cycle == 0){
        // night
        fprintf(out,"%s\n", target);
        fprintf(stderr, "> Voting to kill %s\n", target);
        exit(0);
    } else if (cycle > 0) {
        // day
        fprintf(out, "vote %s\n", target);
        fprintf(stderr, "> Voting to lynch %s\n", target);
        exit(0);
    } else if (cycle == -1) {
        fprintf(stderr, "> saying 6, 10 at %s\n", target);
        fprintf(out, "say 6 %s\n", target);
        fprintf(out, "say 10 %s\n", target);
    }
}

Requiere mafia.cy mafia.h, las bibliotecas que escribí, en el mismo directorio.

Estos se incluyen en la descarga, junto con un Makefile y un script de ejecución.

QUE HACER

  • Deje de votar contra el objetivo cuando los maten o linchen.

Mientras estoy aquí, enviaré el no bot, Steve:

SIGSTACKFAULT
fuente
Para su información, que llamo DIB avatar, erebus, leviathan, yragnarok
SIGSTACKFAULT
"Esto no es una referencia a un programa de televisión animado con un nombre similar". ¿Es una referencia a la película?
Stan Strum
@StanStrum no, no lo es.
SIGSTACKFAULT
El from_serverarchivo de mi bot no está siendo escrito. ¿Tuviste que establecer permisos específicos o algo así?
Rɪᴋᴇʀ
1
Nota para los curiosos: las Escrituras a las que se hace referencia aquí son las de Amarr de EVE Online. No es una Revelación 02:12 en la Biblia, sino que más bien se lee de manera diferente.
DLosc
2

Leviatán

Leviatán itera sobre todos los jugadores en el playersarchivo y los apunta uno por uno.

Descargar

/*  Citizens of the State, rejoice!

    Today, a great milestone has been achieved by our glorious leaders.
    A stepping stone in the grand story of our empire has been traversed.
    Our individual fears may be quietened;
    the safety of our great nation has been secured.

    Today, unyielding, we have walked the way of the warrior.
    In our hands have our fates been molded.
    On the Leviathan's back will our civilization be carried home
    and the taint of the Enemy purged from our souls.

    Rejoice, citizens! Victory is at hand.

    -Caldari State Information Bureau Pamphlet, YC 12
*/

#include <stdio.h>
#include <stdlib.h>
#include "mafia.h"

void day0(){
    FILE * index = fopen("idx", "w");

    fprintf(index,"0");

    fclose(index);
}

int main(){
    get_players();
    int i, cycle = get_cycle(day0);

    FILE * out = fopen("to_server", "w");
    FILE * idx = fopen("idx", "r");

    fscanf(idx, "%d", &i);
    fclose(idx);

    char * target;
    target = get_player(i)->name;

    fprintf(stderr, "Idx: %d\n", i);
    fprintf(stderr, "Target: %s\n", target);

    if(cycle > 0){
        idx = fopen("idx", "w");
        i++;
        i = i%NPLAYERS;
        fprintf(idx, "%d", i);
        fprintf(out, "vote %s\n", target);
    } else if (cycle == -1) {
        printf("> saying 6, 10 at %s\n", target);
        fprintf(out, "say 6 %s\n", target);
        fprintf(out, "say 10 %s\n", target);
    }

    fclose(out);
}

Al igual que con Avatar, requiere mafia.cy mafia.hen el mismo directorio.

Estos se incluyen en la descarga, junto con un Makefile y un script de ejecución.

SIGSTACKFAULT
fuente
:) agregando survivalist una vez que haya terminado con él
JavaScriptCoder