Давайте играть в мафию!


42

Мафия (также известная как Оборотень) - это игра для вечеринок, в которой примерно так:

  • Игра начинается в день 0. После каждого дня nнаступает ночь n. После каждой ночи nнаступает день n+1. то есть D0, N0, D1, N1, D2, N2...
  • На рассвете дня 0 ведущий тайно выбирает игроков для выполнения определенных ролей:  
    • Некоторое количество игроков становятся мафией. Каждую ночь каждый мафиози выбирает игрока. На рассвете следующего дня игрок, выбранный большинством мафиози, погибает. Они навсегда удалены из игры, и их роль публично раскрыта. Мафия выровнен.  
    • Некоторое количество игроков становятся полицейскими. Каждую ночь каждый полицейский выбирает игрока. На рассвете следующего дня полицейский узнает об этой расстановке игроков. Деревня выровнен.  
    • Некоторое количество игроков становятся врачами. Каждую ночь каждый врач выбирает игрока. Если этот игрок является тем же игроком, которого мафия выбрала убить, действия мафии на эту ночь отменяются. Деревня выровнен.  
    • Все игроки, которые не выбраны для другой роли, являются сельскими жителями. У селян нет способностей, которыми не обладает весь город. Деревня выровнен.
  • Каждый день, кроме дня 0, за город голосует весь город (то есть все живые игроки). В конце дня этот игрок удаляется из игры, и его роль раскрывается. (В день 0 все просто расслабляются до наступления темноты.)
  • Если в какой-то момент мафиозо не осталось, игра заканчивается тем, что все равноправные деревенские игроки побеждают (включая мертвых).
  • Если в какой-то момент игроки, настроенные на деревню, не превосходят по численности игроков, ориентированных на мафию, игра заканчивается победой всех игроков, ориентированных на мафию (включая мертвых).

Для этой задачи ваша цель - написать бота, чтобы побить других ботов в Mafia!

Как сделать рабочий бот

Все, что вы должны предоставить для меня, это файл с именем run. Внутри структуры каталогов, где будет проходить этот вызов, ваш бот будет жить здесь:

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/

runФайл, при выполнении сделает ваш бот делать свое дело. Важно отметить, что этот файл не должен требовать каких-либо аргументов командной строки или чего-либо еще. Он будет запущен именно так ./run. Если вам нужно выполнить по-другому, вам придется обойти это, выполнив что-то вроде этого:

real_bot.py

#!/bin/python2

# code goes here

run

#!/bin/bash

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

Важно отметить, что все входные данные, полученные вашим ботом, будут найдены в файле, from_serverи управляющая программа будет искать выходные данные вашего бота to_server. Я решил сделать это таким образом, чтобы любой язык, который может выполнять файловый ввод-вывод, мог участвовать. Если ваш язык облегчает работу с stdin и stdout, чем с файловым вводом / выводом, вы можете написать runфайл, который выглядит следующим образом:

#!/bin/bash

./real_bot.py < from_server > to_server

Это сделает так, что stdin приходит из from_serverфайла, а stdout - напрямую to_server.

Ваш бот не будет работать в течение всей игры. Вместо этого он будет запущен, когда ему необходимо принять решение. Кроме того, он не будет проинформирован, когда он умрет, он просто больше не будет работать. Запланируйте это, сохранив все, что вы хотите запомнить, в файл и прочитав его позже. Вы можете создавать, писать или читать из любого файла в папке вашего бота, но вы не можете писать или читать в любом месте за пределами этой папки, в том числе доступа к сети или что - нибудь . Если ваш бот знает что- то, что ему не было сказано внутри папки, или если он касается чего- то, что не находится внутри этой папки, ваш бот дисквалифицируется.

Как сделать функционального бота

День

В начале игры файл playersбудет заполнен разделенным новой строкой списком всех игроков в игре. Он не будет обновляться после выхода игроков из игры.

На рассвете дня 0 все игроки найдут это сообщение в своем from_serverфайле:

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

Если вы полицейский, строка You are the copдобавляется в конец. Доктор видит You are the doctor. Мафия видит You are a member of the mafia.\nYour allies are:и разделенный новой строкой список членов мафии, исключая игрока, читающего сообщение.

На заре всех остальных дней появится это сообщение:

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`

dayNumberзаменяется номером дня. victimзаменяется именем жертвы прошлой ночи, и victim_roleявляется одним из:

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

cop_targetимя игрока, которого полицейский исследовал прошлой ночью, и target_alignmentлибо, villageлибо mafia. Наконец, remaining_playersсписок игроков, которые все еще живы в этом формате:player1, player2, player3

Вторая строка опущена, если вчера вечером не было никакого убийства, а третья строка показана только полицейскому.

Например,

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

Как только это сообщение исчезнет, ​​день начнется! Каждый бот может совершить 50 действий в течение дня, где «действие» - это голосование за игрока или вслух что-то говорящее.

Чтобы проголосовать за игрока, напишите vote player_nameв свой to_serverфайл и прекратить. Чтобы проголосовать, чтобы никого не убить, пиши vote no one. Когда вы проголосуете, все игроки (включая вас) увидят your_bot votes to kill your_selection. Голоса игнорируются в день 0.

Ряд заранее определенных сообщений могут быть отправлены всем игрокам. Идентификатор каждого возможного сообщения указан здесь:

 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?

Все эти сообщения, кроме первых пяти, относятся к конкретному игроку. Чтобы сказать одно из этих сообщений, напишите say message_id player_name. Для одного из первых пяти сообщений просто напишите say message_id. Вы можете добавить необязательный третий аргумент к обоим из них, указав имя игрока, с которым вы разговариваете (все игроки могут его прочитать, но они будут знать, кто является получателем).

Когда ваш бот говорит сообщение, все игроки читают your_bot says "message", где messageнаходится сообщение, связанное с идентификатором, который вы написали. Если сообщение содержит тему, один пробел и тема вставляются непосредственно после конца сообщения. Если он включает получателя, его имя, один двоеточие и один пробел вставляются непосредственно перед сообщением.

В конце дня всех живых игроков запускают в последний раз, чтобы увидеть результат голосования. Если игрок был отозван, это написано:

The town has killed player_name!
They were a villager

... или a mafioso, или the cop, или the doctor.

Если ни один игрок не был отозван, вместо этого написано:

The town opted to lynch no one today.

Когда контроллер отправляет эти сообщения, он игнорирует любой ответ от игроков. День окончен.

Ночь

Ночью все, кроме жителей деревни, могут использовать свою власть.

Mafia:

Вы будете читать It is night. Vote for a victim.. Когда это произойдет, выведите имя игрока, которого хотите убить.

Cop:

Вы будете читать It is night. Who would you like to investigate?. Когда это произойдет, выведите имя игрока, которого вы хотите проверить.

Доктор:

Вы будете читать It is night. Who would you like to save?. Когда это произойдет, выведите имя игрока, которого вы хотите защитить.

После этого следующий день начинается как обычно.

Вы можете спасти себя только один раз за игру.

Главная Информация

  • Игра не будет работать без 6 и более игроков.
  • Одна треть игроков, округленных в меньшую сторону, станет мафией. Один игрок будет врачом, а один игрок - полицейским. Все остальные игроки - жители деревни.
  • Связи в сельском голосовании или голосование мафии за ночь распределяются случайным образом.
  • Имена ботов должны быть буквенно-цифровыми + тире и подчеркивания.
  • Запрещено использовать знание кода оппонента напрямую. Теоретически, я должен быть в состоянии поставить вашего бота против ботов, которых вы никогда раньше не видели, и заставить его работать сравнимо.
  • К сожалению, если я не смогу запустить вашу программу, используя исключительно бесплатное (как в пиве) программное обеспечение, мне придется ее дисквалифицировать.
  • Я оставляю за собой право дисквалифицировать любое представление, если я считаю, что оно является вредоносным. Это включает, но не ограничивается использованием чрезмерного времени, памяти или пространства для запуска. Я намеренно оставил предел мягким, но помните: я запускаю это на своем домашнем компьютере, а не на суперкомпьютере, и я не хочу, чтобы результаты занимали год. Я не ожидаю использовать это, так как мои стандарты довольно низки. Это в основном «если я думаю, что вы нарочно придурок», и если вы можете убедить меня в обратном, я отменю свое решение.

счет

В каждом раунде будет запущено 100 игр (это может увеличиваться по мере того, как присоединяется больше ботов, чтобы размер выборки был достаточно большим, но теоретически это ни на что не повлияет). Я запишу, сколько раз каждый бот выигрывает как сельский житель по сравнению с тем, сколько раз он играет как сельский житель, и то же самое для мафии. Бот villager_ratioесть number of games won as villager / number of games played as villager, и mafia_ratioтакой же, но s/villager/mafia/g. Счет бота является (villager_ratio - mean villager_ratio) + (mafia_ratio - mean mafia_ratio).

Пример бота

Робот Рэнди не очень хороший игрок в мафию. Рэнди почти все игнорирует, случайным образом выбирая, что сказать, за кого голосовать, а кого преследовать с помощью ночных сил.

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

контроллер

@undergroundmonorail написал управляющую программу для этой задачи, доступную здесь .

У вас есть один месяц, чтобы закодировать и сдать ответы, я дам выигравшему боту (наибольшее количество победителей - голоса), как минимум, награду в 50 репутаций (в зависимости от того, сколько представителей я могу заработать за месяц)


Вот скрипт-обертка, созданный @Blacksilver, для использования со скомпилированными языками:

#!/bin/bash

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

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

Вставь это run.


Этот пост был написан @undergroundmonorail (я сделал несколько правок).

Он дал это здесь любому, кто хотел закончить и опубликовать это.


Комментарии не для расширенного обсуждения; этот разговор был перенесен в чат .
Мартин Эндер

Ответы:


3

зулус

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);
    }
}

Не все, на что я надеялся. Я могу в конечном итоге настроить его время от времени.

Как это работает v1.0

Отслеживает номер дня, кто жив, кто мертв, кто мафию, кто в деревне, роли, текущие голоса / сообщения за день и общие голоса / сообщения.

  1. Ночь

    а. Мафия - Голосуйте за любого жителя деревни, который голосовал против мафии (случайным образом), если это возможно, в противном случае случайного жителя деревни.

    б. Полицейский - Расследуйте любого, кто неизвестного происхождения.

    с. Доктор - сначала сохраните себя, затем сохраните копа, если он известен (я не думаю, что он когда-либо узнает об этом), спасите жителя деревни, если он известен (вероятно, тоже не знает), иначе спасите случайного человека.

  2. День

    а. Если кто-то сказал сообщение непосредственно самому себе, ответьте на него (возможны ограниченные ответы).

    б. Мафия - Голосуйте за деревенского жителя, у которого больше всего голосов.

    с. Селянин с любым живым мафиозным знакомым - голосуйте за мафиози.

    д. Сельский житель с единственным мертвым известным мафиози - голосуйте за случайного бота, который никогда не голосовал за мафиози.

    е. Житель деревни с полицейским известен - голосуйте за случайного бота, за которого проголосовал полицейский.

    е. Сельский житель с мертвыми известными по признаку деревни - голосуйте за случайного бота, который голосовал за мертвых.

    г. Сельчанин с голосами против себя - голосуйте за самого высокого в настоящее время проголосовавшего бота, не принадлежащего к деревне.


1
Подождите, что это делает?
SIGSTACKFAULT

1
Ну конечно, в мафию играет! :)
Джо.

Я имею в виду стратегию.
SIGSTACKFAULT

6

Код примера не работал для меня, я использую Python 3, поэтому я изменил main.pyфайл, чтобы он работал.

Итак, вот моя исправленная версия для Python 3, я никогда раньше не программировал на Python, так что, возможно, это ужасный код, но он работает :)

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

Несколько вещей я узнал, пока делал эту работу (и мне было не понятно в описании)

  • printничего не делает с игрой это как console.logв js
  • input() блокирует запуск программы, это может быть полезно для пошаговой отладки
  • from_serverи to_serverочищается каждый раунд.
  • Невозможно остановить скрипт Ctrl+Cкомбинацией, что раздражает.

Добро пожаловать в PPCG! Отличный первый пост! Надеюсь, вы остаетесь вокруг! Я отредактировал ваш пост, чтобы в нем работала подсветка синтаксиса, и для большей последовательности добавил run.sh.
Rɪᴋᴇʀ

1
Спасибо! Я не уверен, что < from_server > to_serverэто необходимо, потому что я жестко закодировал имена файлов в коде. игровой движок просто зовут ./runбез труб. так input()и print()не работает с игрой. mayn.pyстрока 57:os.system('./run')
Питер

2
Как ты заставил контроллер работать? Я не могу понять это. Можете ли вы предоставить образец вызова?
Rɪᴋᴇʀ

Примечание: оригинал randy.pyбыл написан на Python 2 , что вызвало проблемы.
SIGSTACKFAULT

для контроллера вам нужно ./startиз исходной папки или вам нужна версия Python 3main.py
Peter

5

Логик

#!/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)

Необычный, длинный набор кода на Python, который я не собираюсь объяснять (хотя это не игра в гольф), за исключением того, что он хранит списки "друзей" и "врагов", которые первоначально были заполнены на основе случайного и / или полицейского расследования , Предупреждение: не лежите в присутствии логика.


ваш run.shстандарт (
Стэн Струм

Нет, мой run.sh может быть просто "run.py" без обычного ввода и вывода, но стандарт будет работать.
pppery

1
Это выглядит очень похоже на то, что я написал бы, если бы у меня было время и желание.
Draco18s

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

1
... и спустя несколько месяцев я понимаю, что мой ответ неверно предполагает, что в нем может быть только один полицейский / врач.
pppery

4

Выживший (версия 1.0)

конспект

Survivalist просто жестоко выживает в игре, ругая любого, кто осмелится обвинить его, независимо от того, мафия он или нет.

логика

Если вы доживете до конца игры, вы выиграете несмотря ни на что. Поэтому вы выживаете любой ценой.

Предыстория

Войска прошли через темный влажный лес.

"Лейтенант, куда мы идем?" Молодой рекрут явно не закалился до зверств, подумал командир. Ну что ж. Он ответил грубым призывом «уничтожить врага».

В деревне вражеский командир пил и смеялся вместе с другими офицерами в клубе, когда разведчик ворвался с новостями. «Для нас есть колонна длиной в несколько сотен ярдов, идущая через лес Юйлинь! Собери войска!»

Вражеский командир, явно опьяненный, неожиданно сказал: «У меня не было сообщений от других разведчиков». Разведчик (позже выживший) подумал, тогда мне придется самому собрать войска . После того, как они рассказали эту историю товарищам-разведчикам, они снова собрались вместе, и все сказали, что видели вражеские войска. Командир все еще не верил, говоря: «Я заказ вас прерывать скаутинг. Там нет ни одного войска противника».

Разведчики решили получить свое оружие, чтобы спасти сообщество. Им удалось добраться до своих позиций как раз в тот момент, когда противник прибыл в деревню с силой. "ЗАРЯД!" крикнул командир засады. "СЖИГАТЬ ДОМА! СЖИГАТЬ ДОМА! УБИТЬ ВСЕХ, ВКЛЮЧАЯ ЖЕНЩИН И ДЕТЕЙ! "

Разведчики спасли всю свою армию. Они ожидали повышения по службе, наград и медалей. Вместо этого они получили фальсифицированный военный трибунал за мятеж, осуждение, 10 лет тюрьмы, бесчестное увольнение из армии и изгнание.


В городском совете Салема, штат Массачусетс, есть старый старейшина. Легенда гласит, что он основал город. Когда вы встретите его в его изолированном домике в лесу, не позволяйте мерцанию в его глазах заставить вас думать, что он спокоен. Если вы обвините его, он погубит вас перед городом.

Ветеран смеялся в темноте. Бояться темноты, ни за что. Боитесь монстров под кроватью? Человек, положивший руку на курок пистолета, нервно рассмеялся. Он ничего не боялся, сказал он себе. Конечно, он был героем прошлой войны, но он настолько привык к засадам и опасным для жизни ситуациям, что это сделало человека просто невротиком. Его спусковой палец дернулся в простых тенях; его сердцебиение учащалось с каждым небольшим звуком. Да, он был напуган до смерти. Как он мог не видеть, как так много людей умирают ужасными способами? Все, что он знал от похищения и чудесного побега своих врагов, - это отсутствие пощады.

ветеран


Код (я новичок в Python, не уверен, что код хороший)

#!/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

Вы имели в виду orвместо ||? Вы проверяли это? Кроме того, вы, вероятно, должны указать, что это Python 2.
Соломон Уцко,

3

Аватар

Аватар «случайным образом» выбирает одного игрока в начале и неустанно фокусирует его до конца раунда.

Это не ссылка на одноименную анимационную телепередачу.

Это EVE онлайн референс.

Скачать tar всех необходимых файлов

Изменения

  • День рождения v1
  • v2 Ничего не записывает stdout, только stderr.
    Чтобы подавить stderrтоже, добавьте 2>/dev/nullв конец runфайла.
/*  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);
    }
}

Это требует, mafia.cи mafia.hбиблиотеки, которые я написал, в том же каталоге.

Они включены в загрузку вместе с Makefile и скриптом запуска.

СДЕЛАТЬ

  • Прекратите голосовать против цели, когда они убиты или линчеваны.

Пока я здесь, я отправлю не бот, Стив:


FYI, я называю бабки на avatar, erebus, leviathanиragnarok
SIGSTACKFAULT

«Это не ссылка на одноименное анимационное телешоу». это ссылка на фильм?
Стэн Струм

@StanStrum нет, это не так.
SIGSTACKFAULT

from_serverФайл моего бота не записывается. Вы должны были установить определенные разрешения или что-то?
Rɪᴋᴇʀ

1
Примечание для любопытных: ссылки на Священные Писания принадлежат Амарру из EVE Online. Там является Откровение 2:12 в Библии, но он читает несколько иначе.
DLosc

2

левиафан

Левиафан перебирает всех игроков в playersфайле и целит их один за другим.

Скачать

/*  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);
}

Как и в случае с аватаром, требуется mafia.cи mafia.hв том же каталоге.

Они включены в загрузку вместе с Makefile и скриптом запуска.


:) добавление выживания, как только я покончу с ним
JavaScriptCoder
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.