마피아를 연주하자!


42

Mafia (Wrewolf라고도 함)는 대략 다음과 같은 파티 게임입니다.

  • 게임은 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. 파일 I / O를 수행 할 수있는 모든 언어가 참여할 수 있도록이 방법으로 선택했습니다. 언어가 파일 I / O보다 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 일에는 투표가 무시됩니다.

모든 미리 정의 된 메시지를 모든 플레이어에게 보낼 수 있습니다. 각 가능한 메시지의 ID는 다음과 같습니다.

 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?

처음 5 개를 제외한 모든 메시지는 특정 플레이어를 나타냅니다. 이러한 메시지 중 하나를 말하려면을 쓰십시오 say message_id player_name. 처음 다섯 개의 메시지 중 하나에 대해서는을 쓰십시오 say message_id. 대화중인 플레이어의 이름을 지정하여 두 가지 모두에 선택적인 세 번째 인수를 추가 할 수 있습니다 (모든 플레이어는 여전히 읽을 수 있지만 대상 수신자는 누구인지 알 수 있습니다).

봇이 메시지를 말하면 모든 플레이어는을 읽습니다 your_bot says "message". 여기에서 message작성한 ID와 관련된 메시지가 있습니다. 메시지에 제목이 포함 된 경우 메시지 끝 바로 뒤에 공백 문자 하나와 제목이 삽입됩니다. 수신자가 포함 된 경우, 이름, 콜론 및 공백 문자가 메시지 바로 앞에 삽입됩니다.

마지막 날에, 모든 살아있는 선수들은 투표 결과를보기 위해 마지막으로 한 번 달리게됩니다. 플레이어가 투표를했다면 다음과 같이 쓰여집니다.

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 명 이상의 플레이어가 없으면 게임이 실행되지 않습니다.
  • 반올림 한 플레이어의 3 분의 1은 마피아입니다. 한 명의 선수가 의사가되고 한 명의 선수가 경찰이됩니다. 다른 모든 플레이어는 주민입니다.
  • 마을 투표 또는 마피아의 하룻밤 투표에서 동점은 무작위로 정산됩니다.
  • 봇 이름은 영숫자 + 대시와 밑줄이어야합니다.
  • 상대방의 코드에 대한 지식을 직접 사용하는 것은 금지되어 있습니다. 이론적으로, 나는 당신이 전에 보지 못했던 봇에 대해 당신의 봇을 올려 놓고 비슷한 성능을 발휘할 수 있어야합니다.
  • 유감스럽게도 (맥주에서와 같이) 독점적으로 무료 소프트웨어를 사용하여 프로그램을 실행할 수 없으면 자격을 박탈해야합니다.
  • 악의적 인 것으로 판단되는 경우 제출물을 실격 처리 할 권리가 있습니다. 여기에는 과도한 시간, 메모리 또는 실행 공간을 사용하는 것이 포함되지만 이에 국한되지 않습니다. 의도적으로 한계를 벗어 났지만 기억하십시오 : 슈퍼 컴퓨터가 아닌 가정용 컴퓨터에서이를 실행 중이며 1 년이 걸리는 결과를 원하지 않습니다. 표준이 매우 낮기 때문에 이것을 사용할 필요는 없습니다. 이것은 기본적으로 "내가 의도적으로 거시기라고 생각하는 경우"이며, 그렇지 않으면 다른 방법으로 설득 할 수 있으면 결정을 취소합니다.

채점

매 라운드마다 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에 의해 작성되었습니다 (몇 가지 편집했습니다).

그는 그것을 끝내고 게시하고 싶은 누군가에게 여기 에 그것을 포기 했다.


의견은 긴 토론을위한 것이 아닙니다. 이 대화는 채팅 으로 이동 되었습니다 .
Martin Ender

답변:


3

줄루 족

run

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

$self = basename(__DIR__);

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

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

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

내가 원하는 모든 것이 아닙니다. 나는 때때로 그것을 조정할 수 있습니다.

작동 방식 v1.0

일수, 살아있는 사람, 사망 한 사람, 마피아 인 사람, 마을에 맞는 사람, 역할, 현재 요일 투표 / 메시지 및 전체 투표 / 메시지를 추적합니다.

  1. 에이. 마피아-가능하면 마피아에 반대 투표를 한 주민 (임의로), 그렇지 않으면 무작위 주민에게 투표하십시오.

    비. 경찰-잘 알려지지 않은 사람을 조사하십시오.

    씨. 닥터-자기 차례를 먼저 저장 한 다음, 알려진 경우 경찰을 저장하십시오 (지금은이 사실을 알 수 없다고 생각합니다). 알려진 경우 주민을 구하십시오 (아마도 이것을 모르는 경우). 그렇지 않으면 임의의 사람을 구하십시오.

  2. 에이. 누군가 자신에게 직접 메시지를 전한 경우, 이에 응답하십시오 (제한된 답변 가능).

    비. 마피아-가장 많은 표를 얻은 주민에게 투표하십시오.

    씨. 살아있는 마피아와 잘 알려진 마을 주민-mafioso에 투표하십시오.

    디. 마피아에 맞춰 죽은 것으로 알려진 주민-마피아 소에 투표 한 적이없는 무작위 봇에 투표하십시오.

    이자형. 경찰이 알려진 주민-경찰이 투표 한 랜덤 봇에 투표하십시오.

    에프. 죽은자를 안고있는 마을 주민-죽은 자에게 투표 한 무작위 봇에 투표하십시오.

    지. 자기 반대 투표를 한 주민-현재 투표권이 가장 높은 비선형 봇에 투표하십시오.


1
잠깐,이게 뭐야?
SIGSTACKFAULT

1
물론 마피아를 연주하는 이유는 무엇입니까! :)
조.

나는 전략을 의미한다.
SIGSTACKFAULT

6

예제 코드는 저에게 main.py효과적 이지 않았습니다 .Python 3을 사용하므로 파일을 변경하여 작동시킵니다.

그래서 여기에 파이썬 3 용 고정 버전이 있습니다. 파이썬에서 프로그래밍 한 적이 없으므로 끔찍한 코드 일 수도 있지만 작동합니다 :)

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

이 작업을 수행하는 동안 배운 몇 가지 사항 (설명에서 명확하지 않음)

  • printconsole.logjs 와 같은 게임으로 아무것도하지 않습니다.
  • input() 단계별 디버깅에 적합 할 수있는 프로그램을 실행 차단
  • from_server그리고 to_server매 라운드를 클리어됩니다.
  • 스크립트를 Ctrl+C조합 하여 중지하는 것은 불가능합니다 .

PPCG에 오신 것을 환영합니다! 첫 번째 게시물! 당신이 주위에 붙어 있기를 바랍니다! 함수 구문 강조 표시를 위해 게시물을 편집했으며 일관성을 위해에 추가했습니다 run.sh.
Rɪᴋᴇʀ

1
감사합니다! < from_server > to_server코드에 파일 이름을 하드 코딩했기 때문에 확실하지 않습니다 . 게임 엔진은 ./run파이프없이 호출 합니다. 이렇게 input()하고 print()하지 않았다 게임과 함께 작동합니다. mayn.py57 행 :os.system('./run')
Peter

2
컨트롤러를 어떻게 실행 했습니까? 알아낼 수 없습니다. 샘플 호출을 제공 할 수 있습니까?
Rɪᴋᴇʀ

참고 : 원본 randy.pyPython 2 로 작성되어 문제가 발생했습니다.
SIGSTACKFAULT

컨트롤러의 ./start경우 원래 폴더에서 가져 오거나 Python 3 버전의main.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)

내가 설명하지 않을 멋진 (비록 골프는 아니지만) 긴 파이썬 코드는 우연히 그리고 / 또는 경찰 조사를 기반으로 원래 채워진 "친구"와 "에미"목록을 유지합니다. . 경고 : 논리학 자의 존재에 있지 마십시오.


귀하 run.sh의 표준입니다 (일부 테스트 수행)
Stan Strum

아니요, 내 run.sh는 일반적인 입력 및 출력 파이프없이 순수하게 "run.py"일 수 있지만 표준은 작동합니다.
pppery December

1
이것은 내가 쓴 것, 시간과 성향이 있었을 때와 매우 유사합니다.
Draco18s

어떤 이유로 나는 논리학자가 다른 봇 주위에서 잘하지 않을 것이라고 생각합니다 ... 다른 봇 중 아무도 경찰 조사를보고하지 않습니다
JavaScriptCoder

1
... 그리고 몇 달 후 내 대답은 경찰 / 의사가 하나만 있다고 잘못 가정한다는 것을 알고 있습니다.
pppery

4

생존자 (v 1.0)

개요

생존자는 마피아인지 여부에 상관없이 그를 비난하는 사람을 때려서 잔인하게 게임에서 살아남습니다.

논리

게임이 끝날 때까지 살아남는다면 무엇이든 이길 수 있습니다. 따라서 당신은 모든 비용으로 살아남습니다.

뒷이야기

군대는 어둡고 습한 숲으로 행진했다

"중위, 우리는 어디로 행진하고 있습니까?" 사령관은이 젊은 신병이 자신을 잔학 행위로 강하게하지 않은 것으로 보인다. 오 잘 그는 "적을 파괴하기 위해"멍청이로 대답했다.

마을에서 스카우트가 뉴스에 몰려 들자 적 사령관이 클럽의 다른 장교들과 함께 술을 마시고 웃고있었습니다. "우리를 위해 Yulin 숲을 행진하는 수백 야드 길이의 기둥이 있습니다! 군대를 모으십시오!"

당황한 적군 사령관은 예기치 않게 "다른 스카우트들에 대한보고는 없었다"고 말했다. 스카우트 (나중에 생존자)는 생각했다. 그러면 나는 부대를 직접 모아야 할 것이다 . 이야기를 동료 스카우트에게 전한 후, 그들은 다시 돌아와서 적군을 보았다고 말했습니다. 사령관은 여전히 ​​" 정찰 중단을 명령하고 있다. 적군 은 없다 "고 말했다.

스카우트는 지역 사회를 구하기 위해 무기를 갖기로 결정했습니다. 그들은 적이 강제로 마을에 도착한 것처럼 자신의 위치에 도달했습니다. "요금!" 매복 사령관에게 소리 쳤다. "집을 구하라! 집을 불 태워라! 여자와 아이들을 포함하여 모두를 죽여라! "

정찰병들은 군대 전체를 구했습니다. 그들은 승진, 상 및 메달을 기대했습니다. 대신에, 그들은 교도소에서 10 년 동안 교도소에서 유죄 판결을 받았으며, 군대와 망명으로부터 불명예스러운 면책을 당했다.


매사추세츠 주 세일럼 시의회에는 한 노인 장로가 있습니다. 전설은 그가 마을을 설립했다는 것입니다. 숲 속의 외딴 오두막에서 그를 만나면 눈의 반짝임이 그가 평안하다고 생각하게하지 마십시오. 당신이 그를 비난하면, 그는 마을 앞에서 당신을 망치게 될 것입니다.

베테랑은 어둠 속에서 웃었다. 어둠을 두려워하지 않습니다. 침대 밑에 괴물을 두려워? 총의 방아쇠에 그의 손을 가진 남자는 긴장하게 웃었다. 그는 아무것도 무서워하지 않았으며 자신에게 말했습니다. 물론, 그는 과거의 전쟁 영웅 이었으나 목숨을 앗아 가거나 생명을 위협하는 상황에 익숙해 져서 그 사람을 단순한 신경증으로 만들었습니다. 그의 방아쇠는 단순한 그림자로 뒤 틀렸다. 그의 작은 소리는 모든 작은 소리로 빨라졌습니다. 그렇습니다. 그는이 죽음을 두려워했습니다. 어떻게 그렇게 많은 사람들이 끔찍한 방식으로 죽는 것을 보았습니까? 납치되어 기적적으로 자신의 적을 탈출하는 것을 알았던 것은 자비가 없다는 것입니다.

재향 군인


코드 (파이썬에서 신인입니다. 코드가 좋은지 확실하지 않습니다)

#!/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대신에 의미 했습니까 ||? 테스트 했습니까? 또한, 당신은 아마 그것이 파이썬 2임을 지적해야합니다.
Solomon Ucko

3

화신

아바타는 시작시 한 명의 선수를 무작위로 뽑아 내고 나머지 라운드에 끊임없이 집중합니다.

이것은 비슷한 이름의 애니메이션 TV 쇼에 대한 언급이 아닙니다.

이브 온라인 굴절입니다.

필요한 모든 파일의 tar 다운로드

변경 로그

  • v1 생일
  • v2에 아무것도 기록하지 않고에 기록 stdout합니다 stderr.
    억제하려면 파일 끝에 stderr추가 하십시오.2>/dev/nullrun
/*  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 및 실행 스크립트와 함께 다운로드에 포함됩니다.

할 것

  • 대상이 죽거나 lynched 때 투표를 중지하십시오.

내가 여기있는 동안 비봇 인 Steve를 제출하겠습니다.


참고로, 내가 찜을 촉구 avatar, erebus, leviathan,와ragnarok
SIGSTACKFAULT

"이것은 비슷한 이름의 애니메이션 TV 쇼에 대한 언급이 아닙니다." 영화에 대한 참조입니까?
Stan Strum

@StanStrum 아니오, 그렇지 않습니다.
SIGSTACKFAULT

내 봇의 from_server파일이 작성되지 않았습니다. 특정 권한이나 무언가를 설정해야 했습니까?
Rɪᴋᴇʀ

1
궁금한 점을 참고하십시오 : 여기에 언급 된 성경 은 EVE Online 의 Amarr 의 성경 입니다. 가 이다 요한 계시록 2시 12분 성경에 있지만, 오히려 다르게 읽습니다.
DLosc

2

거대한

Leviathan은 players파일의 모든 플레이어를 반복 하여 하나씩 대상으로합니다.

다운로드

/*  Citizens of the State, rejoice!

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

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

    Rejoice, citizens! Victory is at hand.

    -Caldari State Information Bureau Pamphlet, YC 12
*/

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

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

    fprintf(index,"0");

    fclose(index);
}

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

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

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

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

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

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

    fclose(out);
}

아바타와 마찬가지로, 그것은 필요 mafia.cmafia.h같은 디렉토리입니다.

이들은 Makefile 및 실행 스크립트와 함께 다운로드에 포함됩니다.


:) 한 번으로 생존 한 사람을 추가하면
JavaScriptCoder
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.