Zagrajmy w mafię!


42

Mafia (znana również jako wilkołak) to gra towarzyska, która działa mniej więcej tak:

  • Gra rozpoczyna się w dniu 0. Po każdym dniu nprzychodzi noc n. Po każdej nocy nprzychodzi dzień n+1. tj. D0, N0, D1, N1, D2, N2...
  • Na początku dnia 0 gospodarz potajemnie wybiera graczy, którzy pełnią określone role:  
    • Pewna liczba graczy staje się mafią. Każdej nocy każdy mafioso wybiera gracza. O świcie następnego dnia gracz wybrany przez większość mafiosów zostaje zabity. Są trwale usuwani z gry, a ich rola jest publicznie ujawniana. Dostosowane do mafii.  
    • Pewna liczba graczy zostaje policjantami. Każdej nocy każdy policjant wybiera gracza. Na początku następnego dnia gliniarz zdaje sobie sprawę z wyrównania graczy. Dostosowane do wsi.  
    • Pewna liczba graczy zostaje lekarzami. Każdej nocy każdy lekarz wybiera gracza. Jeśli ten gracz jest tym samym graczem, którego mafia postanowiła zabić, akcje mafii na tę noc zostaną anulowane. Dostosowane do wsi.  
    • Wszyscy gracze, którzy nie zostali wybrani do innej roli, są wieśniakami. Wieśniacy nie mają umiejętności, które nie są wspólne dla całego miasta. Dostosowane do wsi.
  • Każdego dnia oprócz dnia 0 całe miasto (czyli wszyscy żyjący gracze) głosuje na gracza. Pod koniec dnia ten gracz zostaje usunięty z gry i zostaje ujawniona jego rola. (W dniu 0 wszyscy po prostu dreszcze aż do zmroku.)
  • Jeśli w którymkolwiek momencie nie ma już mafiosów, gra kończy się zwycięstwem wszystkich graczy z wioski (w tym zmarłych).
  • Jeśli w którymś momencie gracze wyrównani do wioski nie przewyższą liczebnie graczy wyrównanych do mafii, gra kończy się zwycięstwem wszystkich wyrównanych do mafii graczy (w tym zmarłych).

W tym wyzwaniu Twoim celem jest napisanie bota, który pokona inne boty w Mafii!

Jak zrobić działającego bota

Wszystko, co musisz mi podać, to plik o nazwie run. Wewnątrz struktury katalogów, w której odbędzie się to wyzwanie, twój bot będzie tu mieszkał:

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/

Po runuruchomieniu plik sprawi, że bot zrobi to samo. Należy zauważyć, że ten plik nie może wymagać żadnych argumentów wiersza poleceń ani niczego. Będzie działał dokładnie tak jak ./run. Jeśli musisz zostać wykonany w inny sposób, musisz obejść ten problem, wykonując coś takiego:

real_bot.py

#!/bin/python2

# code goes here

run

#!/bin/bash

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

Ważną rzeczą do zapamiętania jest to, że wszystkie dane wejściowe, które otrzymuje twój bot, zostaną znalezione w pliku, from_servera program sterujący wyszuka dane wyjściowe twojego bota to_server. Postanowiłem to zrobić w ten sposób, aby każdy język, który może wykonywać operacje we / wy pliku, mógł wziąć udział. Jeśli Twój język ułatwia pracę ze stdin i stdout niż we / wy pliku, możesz napisać runplik, który wygląda następująco:

#!/bin/bash

./real_bot.py < from_server > to_server

Sprawi to, że stdin pochodzi z from_serverpliku, a stdout przechodzi bezpośrednio do to_server.

Twój bot nie będzie działał przez cały czas gry. Zamiast tego zostanie uruchomiony, gdy będzie musiał podjąć decyzję. Podobnie, nie będzie informowany, kiedy będzie martwy, po prostu nie będzie już uruchamiany. Zaplanuj to, zapisując w pliku wszystko, co chcesz zapamiętać, i przeczytaj później. Można tworzyć, pisać lub czytać z dowolnego pliku w folderze bota, ale nie może pisać lub czytać gdziekolwiek poza tym folderze, w tym dostępu do sieci lub cokolwiek . Jeśli Twój bot wie coś , czego nie powiedziano w folderze lub jeśli dotyka czegoś , co nie znajduje się w tym folderze, Twój bot jest zdyskwalifikowany.

Jak zrobić funkcjonalnego bota

Dzień

Na początku gry plik playerszostanie wypełniony listą wszystkich graczy w grze rozdzieloną znakiem nowej linii. Nie będzie aktualizowany, gdy gracze opuszczą grę.

O świcie dnia 0 wszyscy gracze znajdą tę wiadomość w swoim from_serverpliku:

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

Jeśli jesteś gliną, linia You are the copjest dołączana na końcu. Lekarz widzi You are the doctor. Mafia widzi You are a member of the mafia.\nYour allies are:listę członków mafii rozdzieloną znakiem nowej linii, z wyłączeniem gracza czytającego wiadomość.

U zarania wszystkich pozostałych dni pojawi się ten komunikat:

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`

dayNumberzastępuje się liczbą dnia. victimzostaje zastąpiony imieniem ofiary zeszłej nocy i victim_rolejest jednym z:

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

cop_targetto nazwa odtwarzacza policjant badanego ostatniej nocy, i target_alignmentjest albo villagealbo mafia. Na koniec remaining_playersznajduje się lista graczy, którzy wciąż żyją w tym formacie:player1, player2, player3

Drugi wiersz jest pomijany, jeśli ostatniej nocy nie było zabójstwa, a trzeci wiersz jest wyświetlany tylko gliniarzowi.

Na przykład,

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

Gdy wiadomość zniknie z drogi, zaczyna się dzień! Każdy bot może wykonać 50 akcji w ciągu dnia, przy czym „akcja” głosuje na gracza lub mówi coś głośno.

Aby zagłosować na gracza, napisz vote player_namedo to_serverpliku i zakończ. Aby głosować, aby nikogo nie zabić, napisz vote no one. Podczas głosowania wszyscy gracze (łącznie z tobą) zobaczą your_bot votes to kill your_selection. Głosy są ignorowane w dniu 0.

Do wszystkich graczy można wysłać pewną liczbę wstępnie zdefiniowanych wiadomości. Identyfikator każdej możliwej wiadomości znajduje się tutaj:

 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?

Wszystkie te wiadomości, z wyjątkiem pierwszych pięciu, odnoszą się do konkretnego gracza. Aby wypowiedzieć jedną z tych wiadomości, napisz say message_id player_name. W przypadku jednej z pierwszych pięciu wiadomości po prostu napisz say message_id. Możesz dodać opcjonalny trzeci argument do obu z nich, określając nazwę gracza, z którym rozmawiasz (wszyscy gracze nadal mogą go przeczytać, ale będą wiedzieć, kto jest zamierzonym odbiorcą).

Kiedy twój bot mówi wiadomość, wszyscy gracze czytają your_bot says "message", gdzie messagejest wiadomość związana z napisanym przez ciebie identyfikatorem. Jeśli wiadomość zawiera temat, jeden znak spacji i temat są wstawiane bezpośrednio po końcu wiadomości. Jeśli zawiera on adresata, jego nazwa, dwukropek i jeden znak spacji są wstawiane bezpośrednio przed wiadomością.

Pod koniec dnia wszyscy żyjący gracze są uruchamiani ostatni raz, aby zobaczyć wynik głosowania. Jeśli gracz został wykluczony, jest to napisane:

The town has killed player_name!
They were a villager

... lub a mafioso, lub the cop, lub the doctor.

Jeśli żaden gracz nie został wykluczony, zamiast tego jest napisane:

The town opted to lynch no one today.

Gdy kontroler wysyła te wiadomości, ignoruje wszelkie odpowiedzi graczy. Dzień się skończył.

Noc

W nocy wszyscy oprócz wieśniaków mogą korzystać ze swojej mocy.

Mafia:

Można przeczytać It is night. Vote for a victim.. Kiedy tak się stanie, wypisz nazwę gracza, którego chcesz zabić.

Policjant:

Można przeczytać It is night. Who would you like to investigate?. Kiedy tak się stanie, wypisz nazwę odtwarzacza, który chcesz sprawdzić.

Lekarz:

Można przeczytać It is night. Who would you like to save?. Kiedy tak się stanie, wypisz nazwę odtwarzacza, który chcesz chronić.

Następnie następny dzień zaczyna się jak zwykle.

Możesz uratować się tylko raz na grę.

Informacje ogólne

  • Gra nie będzie działać bez 6 lub więcej graczy.
  • Jedna trzecia graczy, w zaokrągleniu w dół, będzie mafią. Jeden gracz będzie lekarzem, a jeden będzie gliną. Wszyscy pozostali gracze to wieśniacy.
  • Więzy w głosowaniu na wsi lub głosowanie mafii z dnia na dzień są rozstrzygane losowo.
  • Nazwy botów muszą być alfanumeryczne + myślniki i podkreślenia.
  • Zabronione jest bezpośrednie korzystanie z wiedzy o kodzie przeciwnika. Teoretycznie powinienem być w stanie stawić czoła botowi przeciwko botom, których nigdy wcześniej nie widziałeś, i zapewnić mu porównywalną wydajność.
  • Niestety, jeśli nie mogę uruchomić twojego programu przy użyciu wyłącznie darmowego (jak w przypadku piwa) oprogramowania, będę musiał go zdyskwalifikować.
  • Zastrzegam sobie prawo do zdyskwalifikowania każdego zgłoszenia, jeśli uważam je za złośliwe. Obejmuje to między innymi nadmierne zużycie czasu, pamięci lub miejsca do uruchomienia. Celowo zostawiłem limit miękki, ale pamiętaj: uruchamiam to na moim komputerze domowym, a nie superkomputerze, i nie chcę, aby wyniki trwały rok. Nie oczekuję, że będę musiał z tego korzystać, ponieważ moje standardy są dość niskie. Jest to w zasadzie „jeśli myślę, że celowo jesteś kutasem” i jeśli możesz mnie przekonać, w przeciwnym razie cofnę moją decyzję.

Punktacja

W każdej rundzie uruchomionych zostanie 100 gier (może się to zwiększyć wraz ze wzrostem liczby botów, aby wielkość próbki była wystarczająco duża, ale teoretycznie nie wpłynie to na nic). Będę rejestrować, ile razy każdy bot wygrywa jako wieśniak w porównaniu do tego, ile razy gra jako wieśniak, i to samo dla mafii. Bot villager_ratiojest number of games won as villager / number of games played as villageri mafia_ratiojest taki sam, ale s/villager/mafia/g. Wynik bota to (villager_ratio - mean villager_ratio) + (mafia_ratio - mean mafia_ratio).

Przykładowy bot

Randy the Robot nie jest dobrym graczem mafijnym. Randy ignoruje prawie wszystko, losowo wybierając, co powiedzieć, na kogo głosować i kogo celować za pomocą nocnych mocy.

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

Kontroler

@undergroundmonorail napisał program kontrolny dla tego wyzwania, dostępny tutaj .

Masz jeden miesiąc na kodowanie i przekazanie odpowiedzi, dam zwycięskiemu botowi (najwyższy wskaźnik wygranych remis to głosy) co najmniej 50 nagród za reputację (w zależności od tego, ile przedstawicieli mogę zarobić w ciągu miesiąca)


Oto skrypt otoki, stworzony przez @Blacksilver, do użycia ze skompilowanymi językami:

#!/bin/bash

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

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

Włóż to run.


Ten post został napisany przez @undergroundmonorail (wprowadziłem kilka zmian).

Dał go tutaj każdemu, kto chciał go dokończyć i opublikować.


Komentarze nie są przeznaczone do rozszerzonej dyskusji; ta rozmowa została przeniesiona do czatu .
Martin Ender

Odpowiedzi:


3

Zulus

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

Nie wszystko, na co liczyłem, będzie. Czasami mogę go przerobić.

Jak to działa v1.0

Śledzi liczbę dni, kto żyje, kto nie żyje, kim jest mafia, który jest dostosowany do wsi, role, głosy / wiadomości z bieżącego dnia i ogólne głosy / wiadomości.

  1. Noc

    za. Mafia - głosuj na każdego wieśniaka, który głosował przeciwko mafii (losowo), jeśli to możliwe, w przeciwnym razie losowego wieśniaka.

    b. Policjant - zbadaj każdego, kto ma nieznane położenie.

    do. Doktorze - Najpierw zapisz siebie, a potem uratuj gliniarza, jeśli jest znany (nie sądzę, żeby kiedykolwiek wiedział to na razie), uratuj wieśniaka, jeśli jest znany (prawdopodobnie też tego nie wie), w przeciwnym razie uratuj losową osobę.

  2. Dzień

    za. Jeśli ktoś wypowiedział wiadomość bezpośrednio do siebie, odpowiedz na nią (możliwe ograniczone odpowiedzi).

    b. Mafia - głosuj na wieśniaka, który ma najwięcej głosów.

    do. Wieśniak ze wszystkimi żyjącymi znanymi z mafii sojusznikami - głosuj na mafioso.

    re. Wieśniak ze znanymi tylko martwymi, mafijnymi - głosuj na przypadkowego bota, który nigdy nie głosował na mafioso.

    mi. Wieśniak ze znanym gliną - głosuj na losowego bota, na którego gliniarz głosował.

    fa. Wieśniak ze zmarłym Znany z wioski - głosuj na przypadkowego bota, który głosował na umarłych.

    sol. Wieśniak z głosami przeciw własnemu głosowaniu na najwyższego głosowanego bota, który nie jest związany z wioską.


1
Zaraz, co to robi?
SIGSTACKFAULT

1
Oczywiście, gra w mafię! :)
Jo.

Mam na myśli strategię.
SIGSTACKFAULT

6

Przykładowy kod nie działał dla mnie, używam Python 3, więc zmieniłem main.pyplik, aby działał.

Oto moja stała wersja dla Pythona 3, nigdy wcześniej nie programowałem w Pythonie, więc może to jest okropny kod, ale działa :)

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

Kilka rzeczy, których nauczyłem się podczas tworzenia tej pracy (i nie było to dla mnie jasne w opisie)

  • printnic nie robi z grą, jest jak console.logw js
  • input() blokuje uruchomiony program, może być dobry do debugowania krok po kroku
  • from_serveri to_serverjest czyszczony w każdej rundzie.
  • Nie można zatrzymać skryptu za pomocą Ctrl+Ckombinacji, co jest denerwujące.

Witamy w PPCG! Świetny pierwszy post! Mam nadzieję, że zostaniesz! Zredagowałem twój post, aby mieć funkcjonalne podświetlanie składni, a dla zachowania spójności dodano run.sh.
Rɪᴋᴇʀ

1
Dziękuję Ci! Nie jestem pewien, czy < from_server > to_serverjest to konieczne, ponieważ na stałe zapisałem nazwy plików w kodzie. silnik gry po prostu dzwoni ./runbez rur. tak input()i print()nie działa z grą. mayn.pylinia 57:os.system('./run')
Piotr

2
Jak udało ci się uruchomić kontroler? Nie mogę tego rozgryźć. Czy możesz podać przykładowe wywołanie?
Rɪᴋᴇʀ

Uwaga: Oryginał randy.pyzostał napisany w języku Python 2 , co spowodowało problemy.
SIGSTACKFAULT,

dla kontrolera musisz ./startz oryginalnego folderu lub potrzebujesz wersji Pythona 3main.py
Peter

5

Logik

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

Wyobraźnia, długa wiązka kodu Pythona, której nie zamierzam wyjaśniać (chociaż nie jest golfa), poza tym, że przechowuje listy „przyjaciół” i „wrogów”, które pierwotnie były zapełniane na podstawie losu i / lub dochodzenia gliniarza . Ostrzeżenie: nie kłam w obecności logika.


to twój run.shstandard (przeprowadzanie testów)
Stan Strum

Nie, mój plik run.sh może być po prostu „run.py” bez zwykłego potoku wejściowego i wyjściowego, ale standard będzie działał.
pppery

1
To wygląda bardzo podobnie do tego, co napisałbym, gdybym miał czas i skłonność.
Draco18s,

Z jakiegoś powodu myślę, że logik nie poradzi sobie tak dobrze w przypadku innych botów ... żaden z innych botów nie zgłosił dochodzenia gliniarza
JavaScriptCoder

1
... i zdaję sobie sprawę, miesiące później, że moja odpowiedź błędnie zakłada, że ​​może być tylko jeden glina / lekarz.
pppery

4

Survivalist (v 1.0)

Streszczenie

Survivalist po prostu brutalnie przeżywa grę, krytykując każdego, kto ośmieli się go oskarżyć, niezależnie od tego, czy jest mafią czy nie.

Logika

Jeśli przeżyjesz do końca gry, wygrywasz bez względu na wszystko. Dlatego przeżyjesz za wszelką cenę.

Historia

Żołnierze maszerowali przez ciemny, wilgotny las.

„Poruczniku, gdzie maszerujemy?” Dowódca pomyślał, że młody rekrut najwyraźniej nie zahartował się w okrucieństwach. No cóż. Odpowiedział szorstkim słowem „zniszczyć wroga”.

W wiosce nieprzyjacielski dowódca pił i śmiał się wraz z innymi oficerami w klubie, kiedy zwiadowca wpadł z wiadomością. „Istnieje kolumna o długości kilkuset metrów, maszerująca za nami przez las Yulin! Zbierz żołnierzy!”

Dowódca wroga, najwyraźniej odurzony, powiedział nieoczekiwanie: „Nie otrzymałem żadnych raportów od innych zwiadowców”. Zwiadowca (później Survivalist) pomyślał, a potem sam będę musiał zebrać żołnierzy . Po opowiedzeniu historii innym zwiadowcom wrócili razem, wszyscy mówiąc, że widzieli wojska wroga. Dowódca wciąż nie wierzył, mówiąc: „ Rozkazuję ci przerwać zwiad. Nie ma żołnierzy wroga”.

Zwiadowcy postanowili zdobyć broń, aby uratować społeczność. Udało im się dotrzeć na swoje pozycje, gdy wróg przybył do wioski. "OPŁATA!" krzyknął dowódca zasadzki. „SPALAJ DOMY! SPALAJĄ DOMY! ZABIJ KAŻDEGO, W TYM KOBIETY I DZIECI!

Zwiadowcy uratowali całą armię. Oczekiwali promocji, nagród i medali. Zamiast tego dostali sfałszowany sąd wojenny za bunt, skazanie, 10 lat więzienia, niehonorowe zwolnienie z wojska i wygnanie.


W radzie miasta Salem, Massachusetts, jest starszy starszy. Legenda głosi, że założył miasto. Kiedy spotkasz go w jego odizolowanym domku w lesie, nie pozwól, by migotanie w jego oku sprawiły, że myślisz, że jest spokojny. Jeśli go oskarżysz, zrujnuje cię przed miastem.

Weteran zaśmiał się w ciemności. Boję się ciemności, nie ma mowy. Boisz się potworów pod łóżkiem? Mężczyzna z ręką na spuście z pistoletu zaśmiał się nerwowo. Nie bał się niczego, powiedział sobie. Jasne, był bohaterem z czasów wojny, ale był tak przyzwyczajony do zasadzek i sytuacji zagrażających życiu, że mężczyzna był po prostu neurotyczny. Jego palec spustowy drgnął na proste cienie; jego puls przyspieszył z każdym dźwiękiem. Tak, bał się śmierci. Jak mógł tego nie zrobić, skoro tak wielu ludzi umiera w okropny sposób? Wiedział tylko, że po porwaniu i cudownym ucieczce od wrogów nie było litości.

Weteran


Kod (Jestem debiutantem w Pythonie, nie jestem pewien, czy kod jest dobry)

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

Miałeś na myśli orzamiast ||? Testowałeś to? Powinieneś również zauważyć, że jest to Python 2.
Solomon Ucko

3

Awatara

Awatar „losowo” wybiera jednego gracza na początku i nieustannie skupia go na resztę rundy.

To nie jest odniesienie do podobnie nazwanego animowanego programu telewizyjnego.

To jest odsyłacz online EVE.

Pobierz tar wszystkich wymaganych plików

Dziennik zmian

  • v1 urodzin
  • v2 Nic nie loguje stdout, tylko do stderr.
    Aby pominąć stderrrównież, dodaj 2>/dev/nullna końcu runpliku.
/*  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);
    }
}

Wymaga mafia.ci mafia.hbibliotek, które napisałem, w tym samym katalogu.

Są one zawarte w pliku do pobrania wraz z plikiem Makefile i skryptem uruchomieniowym.

DO ZROBIENIA

  • Przestań głosować przeciwko celowi, gdy zostaną zabici lub zlinczowani.

Kiedy tu jestem, prześlę non-bota, Steve:


FYI, wzywam forsa na avatar, erebus, leviathan, iragnarok
SIGSTACKFAULT

„To nie jest odniesienie do podobnie nazwanego animowanego programu telewizyjnego”. czy jest to odniesienie do filmu?
Stan Strum,

@StanStrum nie, to nie jest.
SIGSTACKFAULT,

from_serverPlik mojego bota nie jest zapisywany. Czy musiałeś ustawić określone uprawnienia czy coś?
Rɪᴋᴇʀ

1
Uwaga dla ciekawskich: Pisma, o których tu mowa, pochodzą od Amarra z EVE Online. W Biblii znajduje się Objawienie 2:12 , ale brzmi ona inaczej.
DLosc

2

Lewiatan

Lewiatan iteruje wszystkich graczy w playerspliku i celuje w nich jeden po drugim.

Pobieranie

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

Podobnie jak w przypadku Avatara, wymaga mafia.ci znajduje się mafia.hw tym samym katalogu.

Są one zawarte w pliku do pobrania wraz z plikiem Makefile i skryptem uruchomieniowym.


:) dodając survivalist, gdy skończę z tym
JavaScriptCoder
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.