Давайте пограємо в мафію!


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: 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.

Коли контролер надсилає ці повідомлення, він ігнорує будь-яку відповідь гравців. День закінчився

Ніч

Вночі всі, крім жителів села, користуються своєю владою.

Мафія:

Ви прочитаєте It is night. Vote for a victim.. Коли це станеться, виведіть ім’я гравця, якого ви хочете вбити.

Коп:

Ви прочитаєте 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. Ніч

    а. Мафія - голосуйте за будь-якого жителя села, який проголосував проти мафії (випадково), якщо це можливо, в іншому випадку - випадкового жителя села.

    б. Cop - розслідуйте усіх, хто невідомого вирівнювання.

    c. Лікар - Збережіть першу чергу, а потім збережіть поліцейського, якщо це відомо (я не думаю, що це колись може знати це), врятуйте односельця, якщо відомо (напевно, це теж не знаю), інакше врятуйте випадкову людину.

  2. День

    а. Якщо хтось говорив повідомлення самостійно, відповідайте на них (можливі обмежені відповіді).

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

    c. Селянин з будь-яким відомим живим мафіозі - голосує за мафіозу.

    г. Селянин із лише відомим мертвим мафіозом - голосує за випадкового бота, який ніколи не голосував за мафіозу.

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

    f. Селянин із мертвими Відомий по селу відомий - голосуйте за випадкового бота, який проголосував за мертвих.

    г. Селянин, який голосує проти самостійного голосування за найвищого, який зараз проголосував бот, який не відповідає селом.


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

Виживання (v 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
Зауважте для допитливих: Писання, на яке посилаються тут, - це книги Amarr від 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);
}

Як і в Avatar, це вимагає mafia.cі mafia.hв тому ж каталозі.

Вони включаються до завантаження разом із Makefile та сценарієм запуску.


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