Skip to content

Commit

Permalink
Merge pull request #68 from Yoii-Inc/feat/werewolf_cli_night_phase
Browse files Browse the repository at this point in the history
Feat/werewolf cli night phase
  • Loading branch information
taskooh authored Nov 11, 2024
2 parents e82fd0f + 5e00a37 commit 0e03f5f
Show file tree
Hide file tree
Showing 3 changed files with 149 additions and 107 deletions.
66 changes: 40 additions & 26 deletions examples/werewolf_cli/game.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,13 @@ use ark_ff::BigInteger;
use ark_ff::PrimeField;
use ark_ff::UniformRand;
use ark_marlin::IndexProverKey;
use ark_std::One;
use mpc_algebra::commitment::CommitmentScheme;
use mpc_algebra::BooleanWire;
use mpc_algebra::FromLocal;
use mpc_algebra::MpcBooleanField;
use mpc_algebra::Reveal;
use mpc_net::{MpcMultiNet as Net, MpcNet};
use nalgebra::DMatrix;
use player::Player;
use rand::Rng;
Expand Down Expand Up @@ -232,71 +236,81 @@ impl Game {
}
}

pub fn werewolf_attack(&mut self, target_id: usize) -> Vec<String> {
pub fn werewolf_attack(&mut self, target_id: MFr) -> Vec<String> {
let mut events = Vec::new();

if let Some(_werewolf) = self
let am_werewolf = self
.state
.players
.iter()
.find(|p| p.is_werewolf() && p.is_alive)
.any(|p| p.id == Net::party_id() && p.role == Some(Role::Werewolf) && p.is_alive);

// calc
if let Some(target) =
self.state.players.iter_mut().find(|p| {
Fr::from(p.id as i32) == target_id.reveal() && p.is_alive && !p.is_werewolf()
})
{
if let Some(target) = self
.state
.players
.iter_mut()
.find(|p| p.id == target_id && p.is_alive && !p.is_werewolf())
{
target.mark_for_death();
target.mark_for_death();
if am_werewolf {
events.push(format!("人狼が{}を襲撃対象に選びました。", target.name));
} else {
}
} else {
if am_werewolf {
events.push("無効な襲撃対象が選択されました。".to_string());
}
}

events
}

pub fn seer_divination(&self, target_id: usize) -> Vec<String> {
pub fn seer_divination(&self, target_id: MFr) -> Vec<String> {
let mut events = Vec::new();

// get FortuneTeller
let am_fortune_teller =
self.state.players.iter().any(|p| {
p.id == Net::party_id() && p.role == Some(Role::FortuneTeller) && p.is_alive
});

// calc
if let Some(seer) = self
.state
.players
.iter()
.find(|p| p.role == Some(Role::FortuneTeller) && p.is_alive)
{
if let Some(target) = self
.state
.players
.iter()
.find(|p| p.id == target_id && p.is_alive && p.id != seer.id)
{
if let Some(target) = self.state.players.iter().find(|p| {
Fr::from(p.id as i32) == target_id.reveal() && p.is_alive && p.id != seer.id
}) {
let role_name = if target.is_werewolf() {
"人狼"
} else {
"人狼ではない"
};
events.push(format!(
"占い師が{}を占いました。結果:{}",
target.name, role_name
));
if am_fortune_teller {
events.push(format!(
"占い師が{}を占いました。結果:{}",
target.name, role_name
));
}
} else {
events.push("無効な占い対象が選択されました。".to_string());
if am_fortune_teller {
events.push("無効な占い対象が選択されました。".to_string());
}
}
}

events
}

pub fn morning_phase(&mut self) -> Vec<String> {
let mut events = Vec::new();

for player in &mut self.state.players {
if player.marked_for_death && player.is_alive {
if player.marked_for_death.reveal().is_one() && player.is_alive {
player.kill(self.state.day);
events.push(format!("{}が無残な姿で発見されました。", player.name));
player.marked_for_death = false;
player.marked_for_death = MpcBooleanField::pub_false();
}
}

Expand Down
11 changes: 6 additions & 5 deletions examples/werewolf_cli/game/player.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
use serde::{Deserialize, Serialize};
use ark_bls12_377::Fr;
use mpc_algebra::{AdditiveFieldShare, BooleanWire, MpcBooleanField};
use zk_mpc::werewolf::types::Role;

#[derive(Debug, Clone, Serialize, Deserialize)]
#[derive(Debug, Clone)]
pub struct Player {
pub id: usize,
pub name: String,
pub role: Option<Role>,
pub is_alive: bool,
pub death_day: Option<u32>,
pub marked_for_death: bool,
pub marked_for_death: MpcBooleanField<Fr, AdditiveFieldShare<Fr>>,
}

impl Player {
Expand All @@ -19,7 +20,7 @@ impl Player {
role,
is_alive: true,
death_day: None,
marked_for_death: false,
marked_for_death: MpcBooleanField::pub_false(),
}
}

Expand All @@ -33,7 +34,7 @@ impl Player {
}

pub fn mark_for_death(&mut self) {
self.marked_for_death = true;
self.marked_for_death = MpcBooleanField::pub_true();
}
}

Expand Down
179 changes: 103 additions & 76 deletions examples/werewolf_cli/main.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
use ark_bls12_377::Fr;
use game::{player::Player, Game, GameRules};
use mpc_algebra::channel::MpcSerNet;
use mpc_algebra::Reveal;
use mpc_net::{MpcMultiNet as Net, MpcNet};
use std::io::{self, Write};
use std::path::PathBuf;
use structopt::StructOpt;
use zk_mpc::marlin::MFr;
use zk_mpc::werewolf::types::{GroupingParameter, Role};

pub mod game;
Expand Down Expand Up @@ -99,34 +102,37 @@ fn register_players() -> Vec<String> {
fn night_phase(game: &mut Game) {
println!("\n--- 夜のフェーズ ---");
let players = game.state.players.clone();
for player in &players {
if player.is_alive {
let mut events = Vec::new();
match player.role {
Some(Role::Werewolf) => {
let werewolf_target = get_werewolf_target(game, player);
events.extend(game.werewolf_attack(werewolf_target));
}
Some(Role::FortuneTeller) => {
let seer_target = get_seer_target(game, player);
events.extend(game.seer_divination(seer_target));
}
Some(Role::Villager) => {
println!(
"{}さん、あなたは村人です。次の人に渡してください。",
player.name
);
}
None => unreachable!(),
}
for event in events {
println!("{}", event);
}
wait_for_enter();
// 各プレイヤーのフェーズ後にCLIをフラッシュ
clear_screen();

let player = players.iter().find(|p| p.id == Net::party_id()).unwrap();

let mut events = Vec::new();

match player.role {
Some(Role::Villager) => {
println!("You are a villager. Please wait until everyone has finished their actions.");
}
Some(Role::Werewolf) => {
println!("You are a werewolf.");
}
Some(Role::FortuneTeller) => {
println!("You are a fortune Teller. Please wait until other roles actions.");
}
None => todo!(),
}

let attack_target = get_werewolf_target(game, player);
events.extend(game.werewolf_attack(attack_target));

let seer_target = get_seer_target(game, player);
events.extend(game.seer_divination(seer_target));

for event in events {
println!("{}", event);
}
wait_for_enter();
println!("Waiting for all players to finish their actions.");
wait_for_everyone();
clear_screen();
}

fn wait_for_enter() {
Expand All @@ -135,73 +141,94 @@ fn wait_for_enter() {
io::stdin().read_line(&mut input).unwrap();
}

fn wait_for_everyone() {
let dummy = 0_u32;
Net::broadcast(&dummy);
}

fn clear_screen() {
print!("\x1B[2J\x1B[1;1H"); // ANSI escape code for clearing the screen
io::stdout().flush().unwrap();
}

fn get_werewolf_target(game: &Game, werewolf: &Player) -> usize {
println!(
"{}さん、あなたは人狼です。襲撃する対象を選んでください:",
werewolf.name
);
game.state
.players
.iter()
.filter(|p| p.is_alive && !p.is_werewolf())
.for_each(|p| println!("{}: {}", p.id, p.name));
fn get_werewolf_target(game: &Game, player: &Player) -> MFr {
let mut target_id = Fr::default();

loop {
print!("対象のIDを入力してください: ");
io::stdout().flush().unwrap();

let mut input = String::new();
io::stdin().read_line(&mut input).unwrap();
let target_id: usize = input.trim().parse().unwrap_or(0);

if game
.state
if player.role.unwrap().is_werewolf() {
println!(
"{}さん、あなたは人狼です。襲撃する対象を選んでください:",
player.name
);
game.state
.players
.iter()
.any(|p| p.id == target_id && p.is_alive && !p.is_werewolf())
{
return target_id;
} else {
println!("無効な選択です。もう一度選んでください。");
.filter(|p| p.is_alive && !p.is_werewolf())
.for_each(|p| println!("{}: {}", p.id, p.name));

loop {
print!("対象のIDを入力してください: ");
io::stdout().flush().unwrap();

let mut input = String::new();
io::stdin().read_line(&mut input).unwrap();
target_id = Fr::from(input.trim().parse().unwrap_or(0) as i32);

if game
.state
.players
.iter()
.any(|p| Fr::from(p.id as i32) == target_id && p.is_alive && !p.is_werewolf())
{
break;
} else {
println!("無効な選択です。もう一度選んでください。");
}
}
} else {
target_id = Fr::default();
}
}

fn get_seer_target(game: &Game, seer: &Player) -> usize {
println!(
"{}さん、あなたは占い師です。占う対象を選んでください:",
seer.name
);
game.state
.players
.iter()
.filter(|p| p.is_alive && p.id != seer.id)
.for_each(|p| println!("{}: {}", p.id, p.name));
return MFr::from_add_shared(target_id);
}

loop {
print!("対象のIDを入力してください: ");
io::stdout().flush().unwrap();
fn get_seer_target(game: &Game, player: &Player) -> MFr {
let mut target_id = Fr::default();

let mut input = String::new();
io::stdin().read_line(&mut input).unwrap();
let target_id: usize = input.trim().parse().unwrap_or(0);

if game
.state
if player.role.unwrap() == Role::FortuneTeller {
println!(
"{}さん、あなたは占い師です。占う対象を選んでください:",
player.name
);
game.state
.players
.iter()
.any(|p| p.id == target_id && p.is_alive && p.id != seer.id)
{
return target_id;
} else {
println!("無効な選択です。もう一度選んでください。");
.filter(|p| p.is_alive && p.id != player.id)
.for_each(|p| println!("{}: {}", p.id, p.name));

loop {
print!("対象のIDを入力してください: ");
io::stdout().flush().unwrap();

let mut input = String::new();
io::stdin().read_line(&mut input).unwrap();
target_id = Fr::from(input.trim().parse().unwrap_or(0) as i32);

if game
.state
.players
.iter()
.any(|p| Fr::from(p.id as i32) == target_id && p.is_alive && p.id != player.id)
{
break;
} else {
println!("無効な選択です。もう一度選んでください。");
}
}
} else {
target_id = Fr::default();
}

return MFr::from_add_shared(target_id);
}

fn morning_phase(game: &mut Game) {
Expand Down

0 comments on commit 0e03f5f

Please sign in to comment.