From 2d310650ecc2d4377fe621a23a5b387a8aa8899a Mon Sep 17 00:00:00 2001 From: sheagrief <3a.mad1earth4@gmail.com> Date: Mon, 14 Oct 2024 18:52:24 +0900 Subject: [PATCH 1/6] =?UTF-8?q?=F0=9F=9A=A7=20(WIP)=20Add=20werewolf=5Fcli?= =?UTF-8?q?=20bone?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/werewolf_cli/game.rs | 215 ++++++++++++++++++++++++ examples/werewolf_cli/game/player.rs | 47 ++++++ examples/werewolf_cli/game/role.rs | 48 ++++++ examples/werewolf_cli/main.rs | 235 +++++++++++++++++++++++++++ 4 files changed, 545 insertions(+) create mode 100644 examples/werewolf_cli/game.rs create mode 100644 examples/werewolf_cli/game/player.rs create mode 100644 examples/werewolf_cli/game/role.rs create mode 100644 examples/werewolf_cli/main.rs diff --git a/examples/werewolf_cli/game.rs b/examples/werewolf_cli/game.rs new file mode 100644 index 0000000..2745918 --- /dev/null +++ b/examples/werewolf_cli/game.rs @@ -0,0 +1,215 @@ +use rand::Rng; + +pub mod player; +pub mod role; + +use player::Player; +use role::Role; + +pub struct Game { + pub state: GameState, + pub rules: GameRules, +} + +pub struct GameState { + pub players: Vec, + pub current_phase: GamePhase, + pub day: u32, +} + +pub struct GameRules { + pub min_players: usize, + pub max_players: usize, + pub werewolf_ratio: f32, + pub seer_count: usize, +} + +pub enum GamePhase { + Night, + Morning, + Discussion, + Voting, +} + +impl Game { + pub fn new(player_names: Vec, rules: GameRules) -> Self { + let roles = role::assign_roles(player_names.len(), &rules); + let players = player::create_players(player_names, roles); + + Self { + state: GameState { + players, + current_phase: GamePhase::Night, + day: 1, + }, + rules, + } + } + + pub fn next_phase(&mut self) { + self.state.current_phase = match self.state.current_phase { + GamePhase::Night => GamePhase::Morning, + GamePhase::Morning => GamePhase::Discussion, + GamePhase::Discussion => GamePhase::Voting, + GamePhase::Voting => { + self.state.day += 1; + GamePhase::Night + } + }; + } + + pub fn check_victory_condition(&self) -> Option { + let alive_players: Vec<&Player> = + self.state.players.iter().filter(|p| p.is_alive).collect(); + let werewolf_count = alive_players.iter().filter(|p| p.is_werewolf()).count(); + let villager_count = alive_players.len() - werewolf_count; + + if werewolf_count == 0 { + Some("村人".to_string()) + } else if werewolf_count >= villager_count { + Some("人狼".to_string()) + } else { + None + } + } + + pub fn werewolf_attack(&mut self, target_id: usize) -> Vec { + let mut events = Vec::new(); + + if let Some(_werewolf) = self + .state + .players + .iter() + .find(|p| p.is_werewolf() && p.is_alive) + { + 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(); + events.push(format!("人狼が{}を襲撃対象に選びました。", target.name)); + } else { + events.push("無効な襲撃対象が選択されました。".to_string()); + } + } + + events + } + + pub fn seer_divination(&self, target_id: usize) -> Vec { + let mut events = Vec::new(); + + if let Some(seer) = self + .state + .players + .iter() + .find(|p| p.role == Role::Seer && p.is_alive) + { + if let Some(target) = self + .state + .players + .iter() + .find(|p| p.id == target_id && p.is_alive && p.id != seer.id) + { + let role_name = if target.is_werewolf() { + "人狼" + } else { + "人狼ではない" + }; + events.push(format!( + "占い師が{}を占いました。結果:{}", + target.name, role_name + )); + } else { + events.push("無効な占い対象が選択されました。".to_string()); + } + } + + events + } + + pub fn morning_phase(&mut self) -> Vec { + let mut events = Vec::new(); + + for player in &mut self.state.players { + if player.marked_for_death && player.is_alive { + player.kill(self.state.day); + events.push(format!("{}が無残な姿で発見されました。", player.name)); + player.marked_for_death = false; + } + } + + if events.is_empty() { + events.push("昨夜は誰も襲撃されませんでした。".to_string()); + } + + events + } + + pub fn discussion_phase(&self) -> Vec { + vec!["討論フェーズが始まりました。".to_string()] + } + + pub fn voting_phase(&mut self, votes: Vec) -> Vec { + let mut events = Vec::new(); + let mut vote_count = vec![0; self.state.players.len()]; + + for (voter, &target) in self.state.players.iter().zip(votes.iter()) { + if voter.is_alive { + vote_count[target] += 1; + events.push(format!( + "{}が{}に投票しました。", + voter.name, self.state.players[target].name + )); + } + } + + let max_votes = *vote_count.iter().max().unwrap(); + // 最大票数を持つプレイヤーを見つける。投票が同数の場合は + let max_voted_indexes = self + .state + .players + .iter() + .enumerate() + .filter_map(|(i, p)| { + if p.is_alive && vote_count[i] == max_votes { + Some(i) + } else { + None + } + }) + .collect::>(); + assert!(max_voted_indexes.len() >= 1); + let executed_index = if max_voted_indexes.len() == 1 { + max_voted_indexes[0] + } else { + // 投票が同数の場合は、ランダムに一人処刑される + let random_index = rand::thread_rng().gen_range(0..max_voted_indexes.len()); + max_voted_indexes[random_index] + }; + + let player = &mut self.state.players[executed_index]; + player.kill(self.state.day); + events.push(format!("{}が処刑されました。", player.name)); + + events + } +} + +impl GameRules { + pub fn new( + min_players: usize, + max_players: usize, + werewolf_ratio: f32, + seer_count: usize, + ) -> Self { + Self { + min_players, + max_players, + werewolf_ratio, + seer_count, + } + } +} diff --git a/examples/werewolf_cli/game/player.rs b/examples/werewolf_cli/game/player.rs new file mode 100644 index 0000000..9080969 --- /dev/null +++ b/examples/werewolf_cli/game/player.rs @@ -0,0 +1,47 @@ +use crate::game::role::Role; +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Player { + pub id: usize, + pub name: String, + pub role: Role, + pub is_alive: bool, + pub death_day: Option, + pub marked_for_death: bool, +} + +impl Player { + pub fn new(id: usize, name: String, role: Role) -> Self { + Self { + id, + name, + role, + is_alive: true, + death_day: None, + marked_for_death: false, + } + } + + pub fn kill(&mut self, day: u32) { + self.is_alive = false; + self.death_day = Some(day); + } + + pub fn is_werewolf(&self) -> bool { + self.role.is_werewolf() + } + + pub fn mark_for_death(&mut self) { + self.marked_for_death = true; + } +} + +pub fn create_players(names: Vec, roles: Vec) -> Vec { + names + .into_iter() + .zip(roles) + .enumerate() + .map(|(id, (name, role))| Player::new(id, name, role)) + .collect() +} diff --git a/examples/werewolf_cli/game/role.rs b/examples/werewolf_cli/game/role.rs new file mode 100644 index 0000000..9eb0d18 --- /dev/null +++ b/examples/werewolf_cli/game/role.rs @@ -0,0 +1,48 @@ +use rand::seq::SliceRandom; +use rand::thread_rng; +use serde::{Deserialize, Serialize}; + +use super::GameRules; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +pub enum Role { + Villager, + Werewolf, + Seer, +} + +impl Role { + pub fn description(&self) -> &'static str { + match self { + Role::Villager => "村人:特別な能力はありませんが、議論と投票に参加します。", + Role::Werewolf => "人狼:夜に村人を襲撃します。昼は村人のふりをします。", + Role::Seer => "占い師:夜に一人のプレイヤーの役割を知ることができます。", + } + } + + pub fn is_werewolf(&self) -> bool { + matches!(self, Role::Werewolf) + } +} + +pub fn assign_roles(player_count: usize, rules: &GameRules) -> Vec { + let mut roles = vec![Role::Villager; player_count]; + let mut rng = thread_rng(); + + let werewolf_count = (player_count as f32 * rules.werewolf_ratio).round() as usize; + let seer_count = rules.seer_count; + + // 役割を割り当てる + for role in roles.iter_mut().take(werewolf_count) { + *role = Role::Werewolf; + } + + for role in roles.iter_mut().skip(werewolf_count).take(seer_count) { + *role = Role::Seer; + } + + // ランダムに並び替える + roles.shuffle(&mut rng); + + roles +} diff --git a/examples/werewolf_cli/main.rs b/examples/werewolf_cli/main.rs new file mode 100644 index 0000000..601c900 --- /dev/null +++ b/examples/werewolf_cli/main.rs @@ -0,0 +1,235 @@ +use std::io::{self, Write}; + +use game::{player::Player, role::Role, Game, GameRules}; + +pub mod game; +fn main() -> Result<(), Box> { + // let stream = TcpStream::connect("127.0.0.1:8080").await?; + // println!("サーバーに接続しました。"); + + let game_rule = GameRules { + min_players: 4, + max_players: 10, + werewolf_ratio: 0.3, + seer_count: 1, + }; + let mut game = Game::new(register_players(), game_rule); + + loop { + night_phase(&mut game); + morning_phase(&mut game); + discussion_phase(&game); + voting_phase(&mut game); + + if let Some(winner) = game.check_victory_condition() { + println!("ゲーム終了!{}の勝利です!", winner); + break; + } + + game.next_phase(); + } + + Ok(()) +} + +fn register_players() -> Vec { + let mut players = Vec::new(); + loop { + print!("プレイヤー名を入力してください(終了する場合は空欄で Enter): "); + io::stdout().flush().unwrap(); + + let mut input = String::new(); + io::stdin().read_line(&mut input).unwrap(); + let name = input.trim().to_string(); + + if name.is_empty() { + if players.len() >= 4 { + break; + } else { + println!("最低4人のプレイヤーが必要です。"); + continue; + } + } + + players.push(name); + } + players +} + +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 { + Role::Werewolf => { + let werewolf_target = get_werewolf_target(game, player); + events.extend(game.werewolf_attack(werewolf_target)); + } + Role::Seer => { + let seer_target = get_seer_target(game, player); + events.extend(game.seer_divination(seer_target)); + } + Role::Villager => { + println!( + "{}さん、あなたは村人です。次の人に渡してください。", + player.name + ); + } + } + for event in events { + println!("{}", event); + } + wait_for_enter(); + // 各プレイヤーのフェーズ後にCLIをフラッシュ + clear_screen(); + } + } +} + +fn wait_for_enter() { + println!("Enterキーを押して次に進んでください。"); + let mut input = String::new(); + io::stdin().read_line(&mut input).unwrap(); +} + +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)); + + 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 + .players + .iter() + .any(|p| p.id == target_id && p.is_alive && !p.is_werewolf()) + { + return target_id; + } else { + println!("無効な選択です。もう一度選んでください。"); + } + } +} + +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)); + + 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 + .players + .iter() + .any(|p| p.id == target_id && p.is_alive && p.id != seer.id) + { + return target_id; + } else { + println!("無効な選択です。もう一度選んでください。"); + } + } +} + +fn morning_phase(game: &mut Game) { + let events = game.morning_phase(); + for event in events { + println!("{}", event); + } +} + +fn discussion_phase(game: &Game) { + println!("\n--- 討論フェーズ ---"); + let events = game.discussion_phase(); + for event in events { + println!("{}", event); + } + + println!("生存しているプレイヤー:"); + game.state + .players + .iter() + .filter(|p| p.is_alive) + .for_each(|p| println!("{}: {}", p.id, p.name)); + + println!("討論を行ってください。準備ができたらEnterキーを押してください。"); + let mut input = String::new(); + io::stdin().read_line(&mut input).unwrap(); +} + +fn voting_phase(game: &mut Game) { + println!("\n--- 投票フェーズ ---"); + let mut votes = Vec::new(); + + for player in &game.state.players { + if player.is_alive { + println!("{}さん、投票する対象を選んでください:", player.name); + game.state + .players + .iter() + .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(); + let target_id: usize = input.trim().parse().unwrap_or(usize::MAX); + + if game + .state + .players + .iter() + .any(|p| p.id == target_id && p.is_alive && p.id != player.id) + { + votes.push(target_id); + break; + } else { + println!("無効な選択です。もう一度選んでください。"); + } + } + } else { + votes.push(usize::MAX); // 死亡したプレイヤーの投票は無効 + } + clear_screen(); + } + + let events = game.voting_phase(votes); + for event in events { + println!("{}", event); + } +} From 115ed703d342dac0d1acc9a12cbdce2dfce9ad8a Mon Sep 17 00:00:00 2001 From: sheagrief <3a.mad1earth4@gmail.com> Date: Thu, 17 Oct 2024 22:05:22 +0900 Subject: [PATCH 2/6] =?UTF-8?q?=F0=9F=94=A7=20Add=20examples=20to=20Cargo.?= =?UTF-8?q?toml?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cargo.toml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index a058c57..2651d2e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -76,3 +76,7 @@ path = "examples/online.rs" [[example]] name = "bin-werewolf" path = "examples/bin_werewolf.rs" + +[[example]] +name = "werewolf-cli" +path = "examples/werewolf_cli/main.rs" From 342f264e4698417a522a12f337650b7be8598797 Mon Sep 17 00:00:00 2001 From: sheagrief <3a.mad1earth4@gmail.com> Date: Thu, 17 Oct 2024 22:10:18 +0900 Subject: [PATCH 3/6] =?UTF-8?q?=E2=9C=A8=20Integrate=20Role=20struct=20of?= =?UTF-8?q?=20werewolf=5Fcli=20into=20src/werewolf/type?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/werewolf_cli/game.rs | 4 ++-- examples/werewolf_cli/game/player.rs | 14 ++++++++++---- examples/werewolf_cli/game/role.rs | 24 ++---------------------- examples/werewolf_cli/main.rs | 10 ++++++---- src/werewolf/types.rs | 18 ++++++++++++++++-- 5 files changed, 36 insertions(+), 34 deletions(-) diff --git a/examples/werewolf_cli/game.rs b/examples/werewolf_cli/game.rs index 2745918..f643381 100644 --- a/examples/werewolf_cli/game.rs +++ b/examples/werewolf_cli/game.rs @@ -4,7 +4,7 @@ pub mod player; pub mod role; use player::Player; -use role::Role; +use zk_mpc::werewolf::types::Role; pub struct Game { pub state: GameState, @@ -105,7 +105,7 @@ impl Game { .state .players .iter() - .find(|p| p.role == Role::Seer && p.is_alive) + .find(|p| p.role == Some(Role::FortuneTeller) && p.is_alive) { if let Some(target) = self .state diff --git a/examples/werewolf_cli/game/player.rs b/examples/werewolf_cli/game/player.rs index 9080969..bd2d630 100644 --- a/examples/werewolf_cli/game/player.rs +++ b/examples/werewolf_cli/game/player.rs @@ -1,18 +1,19 @@ use crate::game::role::Role; use serde::{Deserialize, Serialize}; +use zk_mpc::werewolf::types::Role; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Player { pub id: usize, pub name: String, - pub role: Role, + pub role: Option, pub is_alive: bool, pub death_day: Option, pub marked_for_death: bool, } impl Player { - pub fn new(id: usize, name: String, role: Role) -> Self { + pub fn new(id: usize, name: String, role: Option) -> Self { Self { id, name, @@ -29,7 +30,7 @@ impl Player { } pub fn is_werewolf(&self) -> bool { - self.role.is_werewolf() + self.role.unwrap().is_werewolf() } pub fn mark_for_death(&mut self) { @@ -37,7 +38,12 @@ impl Player { } } -pub fn create_players(names: Vec, roles: Vec) -> Vec { +pub fn create_players(names: Vec, roles: Option>) -> Vec { + let roles = match roles { + Some(roles) => roles.into_iter().map(Some).collect(), + None => vec![None; names.len()], + }; + names .into_iter() .zip(roles) diff --git a/examples/werewolf_cli/game/role.rs b/examples/werewolf_cli/game/role.rs index 9eb0d18..ae6a75a 100644 --- a/examples/werewolf_cli/game/role.rs +++ b/examples/werewolf_cli/game/role.rs @@ -1,30 +1,10 @@ use rand::seq::SliceRandom; use rand::thread_rng; use serde::{Deserialize, Serialize}; +use zk_mpc::werewolf::types::Role; use super::GameRules; -#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] -pub enum Role { - Villager, - Werewolf, - Seer, -} - -impl Role { - pub fn description(&self) -> &'static str { - match self { - Role::Villager => "村人:特別な能力はありませんが、議論と投票に参加します。", - Role::Werewolf => "人狼:夜に村人を襲撃します。昼は村人のふりをします。", - Role::Seer => "占い師:夜に一人のプレイヤーの役割を知ることができます。", - } - } - - pub fn is_werewolf(&self) -> bool { - matches!(self, Role::Werewolf) - } -} - pub fn assign_roles(player_count: usize, rules: &GameRules) -> Vec { let mut roles = vec![Role::Villager; player_count]; let mut rng = thread_rng(); @@ -38,7 +18,7 @@ pub fn assign_roles(player_count: usize, rules: &GameRules) -> Vec { } for role in roles.iter_mut().skip(werewolf_count).take(seer_count) { - *role = Role::Seer; + *role = Role::FortuneTeller; } // ランダムに並び替える diff --git a/examples/werewolf_cli/main.rs b/examples/werewolf_cli/main.rs index 601c900..f8676c5 100644 --- a/examples/werewolf_cli/main.rs +++ b/examples/werewolf_cli/main.rs @@ -1,6 +1,7 @@ use std::io::{self, Write}; -use game::{player::Player, role::Role, Game, GameRules}; +use game::{player::Player, Game, GameRules}; +use zk_mpc::werewolf::types::Role; pub mod game; fn main() -> Result<(), Box> { @@ -63,20 +64,21 @@ fn night_phase(game: &mut Game) { if player.is_alive { let mut events = Vec::new(); match player.role { - Role::Werewolf => { + Some(Role::Werewolf) => { let werewolf_target = get_werewolf_target(game, player); events.extend(game.werewolf_attack(werewolf_target)); } - Role::Seer => { + Some(Role::FortuneTeller) => { let seer_target = get_seer_target(game, player); events.extend(game.seer_divination(seer_target)); } - Role::Villager => { + Some(Role::Villager) => { println!( "{}さん、あなたは村人です。次の人に渡してください。", player.name ); } + None => unreachable!(), } for event in events { println!("{}", event); diff --git a/src/werewolf/types.rs b/src/werewolf/types.rs index 0c96583..281443a 100644 --- a/src/werewolf/types.rs +++ b/src/werewolf/types.rs @@ -1,15 +1,29 @@ use ark_ff::PrimeField; use nalgebra; -use serde::Deserialize; +use serde::{Deserialize, Serialize}; use std::collections::BTreeMap; -#[derive(Debug, PartialEq, Eq, Hash, Ord, PartialOrd, Clone)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)] pub enum Role { FortuneTeller, Werewolf, Villager, } +impl Role { + pub fn description(&self) -> &'static str { + match self { + Role::Villager => "村人:特別な能力はありませんが、議論と投票に参加します。", + Role::Werewolf => "人狼:夜に村人を襲撃します。昼は村人のふりをします。", + Role::FortuneTeller => "占い師:夜に一人のプレイヤーの役割を知ることができます。", + } + } + + pub fn is_werewolf(&self) -> bool { + matches!(self, Role::Werewolf) + } +} + #[derive(Debug, Deserialize)] pub struct RoleData { role: String, From f33d68d2d21a5994703960c4d184cc6bd8ccb57e Mon Sep 17 00:00:00 2001 From: sheagrief <3a.mad1earth4@gmail.com> Date: Thu, 24 Oct 2024 17:38:44 +0900 Subject: [PATCH 4/6] =?UTF-8?q?=E2=9C=A8=20Add=20support=20for=20initializ?= =?UTF-8?q?ing=20MPC=20via=20command=20line=20arguments?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/werewolf_cli/main.rs | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/examples/werewolf_cli/main.rs b/examples/werewolf_cli/main.rs index f8676c5..e6f9162 100644 --- a/examples/werewolf_cli/main.rs +++ b/examples/werewolf_cli/main.rs @@ -1,13 +1,35 @@ -use std::io::{self, Write}; - use game::{player::Player, Game, GameRules}; +use mpc_algebra::channel::MpcSerNet; +use mpc_net::{MpcMultiNet as Net, MpcNet}; +use std::io::{self, Write}; +use std::path::PathBuf; +use structopt::StructOpt; use zk_mpc::werewolf::types::Role; pub mod game; + +#[derive(Debug, StructOpt)] +struct Opt { + // Player Id + id: Option, + + // Input address file + #[structopt(parse(from_os_str))] + input: Option, +} + fn main() -> Result<(), Box> { // let stream = TcpStream::connect("127.0.0.1:8080").await?; // println!("サーバーに接続しました。"); + let opt = Opt::from_args(); + + // init + Net::init_from_file( + opt.input.clone().unwrap().to_str().unwrap(), + opt.id.unwrap(), + ); + let game_rule = GameRules { min_players: 4, max_players: 10, From ad73e45b62b13632efbeea170fc36726bd77046f Mon Sep 17 00:00:00 2001 From: sheagrief <3a.mad1earth4@gmail.com> Date: Thu, 31 Oct 2024 13:47:25 +0900 Subject: [PATCH 5/6] =?UTF-8?q?=E2=9C=A8=20Add:=20Implement=20role=20calc?= =?UTF-8?q?=20&=20prove/verify.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/werewolf_cli/game.rs | 169 ++++++++++++++++++++++++++- examples/werewolf_cli/game/player.rs | 1 - examples/werewolf_cli/game/role.rs | 34 +++++- examples/werewolf_cli/main.rs | 43 ++++--- 4 files changed, 226 insertions(+), 21 deletions(-) diff --git a/examples/werewolf_cli/game.rs b/examples/werewolf_cli/game.rs index f643381..006b75e 100644 --- a/examples/werewolf_cli/game.rs +++ b/examples/werewolf_cli/game.rs @@ -1,11 +1,30 @@ +use ark_bls12_377::Fr; +use ark_crypto_primitives::commitment::pedersen; +use ark_ff::BigInteger; +use ark_ff::PrimeField; +use ark_ff::UniformRand; +use ark_marlin::IndexProverKey; +use mpc_algebra::commitment::CommitmentScheme; +use mpc_algebra::FromLocal; +use mpc_algebra::Reveal; +use nalgebra::DMatrix; +use player::Player; use rand::Rng; +use zk_mpc::circuits::LocalOrMPC; +use zk_mpc::circuits::RoleAssignmentCircuit; +use zk_mpc::marlin::prove_and_verify; +use zk_mpc::marlin::LocalMarlin; +use zk_mpc::marlin::MFr; +use zk_mpc::werewolf::types::GroupingParameter; +use zk_mpc::werewolf::types::Role; +use zk_mpc::werewolf::utils::calc_shuffle_matrix; +use zk_mpc::werewolf::utils::generate_individual_shuffle_matrix; +use zk_mpc::werewolf::utils::load_random_commitment; +use zk_mpc::werewolf::utils::load_random_value; pub mod player; pub mod role; -use player::Player; -use zk_mpc::werewolf::types::Role; - pub struct Game { pub state: GameState, pub rules: GameRules, @@ -22,6 +41,7 @@ pub struct GameRules { pub max_players: usize, pub werewolf_ratio: f32, pub seer_count: usize, + pub grouping_parameter: GroupingParameter, } pub enum GamePhase { @@ -33,8 +53,7 @@ pub enum GamePhase { impl Game { pub fn new(player_names: Vec, rules: GameRules) -> Self { - let roles = role::assign_roles(player_names.len(), &rules); - let players = player::create_players(player_names, roles); + let players = player::create_players(player_names, None); Self { state: GameState { @@ -46,6 +65,144 @@ impl Game { } } + pub fn role_assignment(&mut self) { + let role = role::calc_role(self.state.players.len(), &self.rules); + + for (player, role) in self.state.players.iter_mut().zip(role) { + player.role = Some(role); + } + + // prove and verify + if let Err(e) = self.prove_and_verify() { + eprintln!("Failed to prove and verify: {}", e); + } + } + + fn prove_and_verify(&self) -> Result<(), std::io::Error> { + let n = self.state.players.len(); + let m = self.rules.grouping_parameter.get_num_groups(); + + let rng = &mut ark_std::test_rng(); + + let pedersen_param = >::PedersenComScheme::setup(rng).unwrap(); + + let player_randomness = load_random_value()?; + let player_commitment = load_random_commitment()?; + + // calc + let shuffle_matrix = vec![ + generate_individual_shuffle_matrix( + self.rules.grouping_parameter.get_num_players(), + self.rules.grouping_parameter.get_num_groups(), + rng, + ); + 2 + ]; + + let mut inputs = vec![]; + + for id in 0..n { + let (role, role_val, player_ids) = + calc_shuffle_matrix(&self.rules.grouping_parameter, &shuffle_matrix, id).unwrap(); + println!("role is {:?}", role); + println!("fellow is {:?}", player_ids); + inputs.push(Fr::from(role_val as i32)); + } + + println!("inputs is {:?}", inputs); + + let randomness = (0..n) + .map(|_| >::PedersenRandomness::rand(rng)) + .collect::>(); + + let commitment = (0..n) + .map(|i| { + >::PedersenComScheme::commit( + &pedersen_param, + &inputs[i].into_repr().to_bytes_le(), + &randomness[i], + ) + .unwrap() + }) + .collect::>(); + + let local_role_circuit = RoleAssignmentCircuit { + num_players: n, + max_group_size: self.rules.grouping_parameter.get_max_group_size(), + pedersen_param: pedersen_param.clone(), + tau_matrix: DMatrix::::zeros(n + m, n + m), + shuffle_matrices: vec![DMatrix::::zeros(n + m, n + m); 2], + role_commitment: commitment, + randomness, + player_randomness: player_randomness.clone(), + player_commitment: player_commitment.clone(), + }; + + let srs = LocalMarlin::universal_setup(1000000, 50000, 100000, rng).unwrap(); + let (index_pk, index_vk) = LocalMarlin::index(&srs, local_role_circuit).unwrap(); + let mpc_index_pk = IndexProverKey::from_public(index_pk); + + let mpc_pedersen_param = + >::PedersenParam::from_local(&pedersen_param); + + let mpc_randomness = (0..n) + .map(|_| >::PedersenRandomness::rand(rng)) + .collect::>(); + + let converted_inputs = inputs + .iter() + .map(|x| >::convert_input(&MFr::from_public(*x))) + .collect::>(); + + let role_commitment = (0..n) + .map(|i| { + >::PedersenComScheme::commit( + &mpc_pedersen_param, + &converted_inputs[i], + &mpc_randomness[i], + ) + .unwrap() + }) + .collect::>(); + + let mpc_role_circuit = RoleAssignmentCircuit { + num_players: n, + max_group_size: self.rules.grouping_parameter.get_max_group_size(), + pedersen_param: mpc_pedersen_param, + tau_matrix: self.rules.grouping_parameter.generate_tau_matrix(), + shuffle_matrices: shuffle_matrix, + randomness: mpc_randomness, + role_commitment: role_commitment.clone(), + player_randomness: player_randomness + .iter() + .map(|x| MFr::from_public(*x)) + .collect::>(), + player_commitment: player_commitment + .iter() + .map(|x| >::PedersenCommitment::from_public(*x)) + .collect::>(), + }; + + let mut inputs = player_commitment + .iter() + .flat_map(|c| vec![c.x, c.y]) + .collect::>(); + + role_commitment.iter().for_each(|x| { + inputs.push(x.reveal().x); + inputs.push(x.reveal().y); + }); + + assert!(prove_and_verify( + &mpc_index_pk, + &index_vk, + mpc_role_circuit.clone(), + inputs + )); + + Ok(()) + } + pub fn next_phase(&mut self) { self.state.current_phase = match self.state.current_phase { GamePhase::Night => GamePhase::Morning, @@ -204,12 +361,14 @@ impl GameRules { max_players: usize, werewolf_ratio: f32, seer_count: usize, + grouping_parameter: GroupingParameter, ) -> Self { Self { min_players, max_players, werewolf_ratio, seer_count, + grouping_parameter, } } } diff --git a/examples/werewolf_cli/game/player.rs b/examples/werewolf_cli/game/player.rs index bd2d630..c7eb9c6 100644 --- a/examples/werewolf_cli/game/player.rs +++ b/examples/werewolf_cli/game/player.rs @@ -1,4 +1,3 @@ -use crate::game::role::Role; use serde::{Deserialize, Serialize}; use zk_mpc::werewolf::types::Role; diff --git a/examples/werewolf_cli/game/role.rs b/examples/werewolf_cli/game/role.rs index ae6a75a..9157aea 100644 --- a/examples/werewolf_cli/game/role.rs +++ b/examples/werewolf_cli/game/role.rs @@ -1,7 +1,9 @@ -use rand::seq::SliceRandom; +use ark_bls12_377::Fr; +use ark_std::test_rng; use rand::thread_rng; -use serde::{Deserialize, Serialize}; +use rand::{rngs::StdRng, seq::SliceRandom}; use zk_mpc::werewolf::types::Role; +use zk_mpc::werewolf::utils::{calc_shuffle_matrix, generate_individual_shuffle_matrix}; use super::GameRules; @@ -26,3 +28,31 @@ pub fn assign_roles(player_count: usize, rules: &GameRules) -> Vec { roles } + +pub(super) fn calc_role(player_count: usize, rules: &GameRules) -> Vec { + let rng = &mut test_rng(); + + let grouping_parameter = &rules.grouping_parameter; + + // 1. generate shuffle matrix for each player. + let shuffle_matrix = vec![ + generate_individual_shuffle_matrix::( + // grouping_parameter.get_num_players(), + player_count, + grouping_parameter.get_num_groups(), + rng, + ); + 2 + ]; + + // 2. calc role for each player. + let mut outputs = vec![]; + + for id in 0..player_count { + let (role, _role_val, _player_ids) = + calc_shuffle_matrix(grouping_parameter, &shuffle_matrix, id).unwrap(); + outputs.push(role); + } + + outputs +} diff --git a/examples/werewolf_cli/main.rs b/examples/werewolf_cli/main.rs index e6f9162..a8b266c 100644 --- a/examples/werewolf_cli/main.rs +++ b/examples/werewolf_cli/main.rs @@ -4,7 +4,7 @@ use mpc_net::{MpcMultiNet as Net, MpcNet}; use std::io::{self, Write}; use std::path::PathBuf; use structopt::StructOpt; -use zk_mpc::werewolf::types::Role; +use zk_mpc::werewolf::types::{GroupingParameter, Role}; pub mod game; @@ -35,9 +35,23 @@ fn main() -> Result<(), Box> { max_players: 10, werewolf_ratio: 0.3, seer_count: 1, + grouping_parameter: GroupingParameter::new( + vec![ + (Role::Villager, (2, false)), + (Role::Werewolf, (1, false)), + (Role::FortuneTeller, (1, false)), + ] + .into_iter() + .collect(), + ), }; + let mut game = Game::new(register_players(), game_rule); + game.role_assignment(); + + println!("{:?}", game.state.players); + loop { night_phase(&mut game); morning_phase(&mut game); @@ -56,27 +70,30 @@ fn main() -> Result<(), Box> { } fn register_players() -> Vec { - let mut players = Vec::new(); + let mut name; + loop { - print!("プレイヤー名を入力してください(終了する場合は空欄で Enter): "); + print!("Please enter your player name (Press Enter to finish): "); io::stdout().flush().unwrap(); let mut input = String::new(); io::stdin().read_line(&mut input).unwrap(); - let name = input.trim().to_string(); + name = input.trim().to_string(); if name.is_empty() { - if players.len() >= 4 { - break; - } else { - println!("最低4人のプレイヤーが必要です。"); - continue; - } + println!("Player name is empty."); + continue; + } else { + break; } - - players.push(name); } - players + + println!( + "Registered player name \"{}\". Waiting for other players to register.", + name + ); + + Net::broadcast(&name) } fn night_phase(game: &mut Game) { From 131845ea4fd6ffab208cbd037dc80d50dd233c02 Mon Sep 17 00:00:00 2001 From: sheagrief <3a.mad1earth4@gmail.com> Date: Thu, 31 Oct 2024 18:50:00 +0900 Subject: [PATCH 6/6] =?UTF-8?q?=F0=9F=8E=A8=20Add:=20enable=20switch=20pro?= =?UTF-8?q?ve=20role=5Fassignment?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/werewolf_cli/game.rs | 10 ++++++---- examples/werewolf_cli/main.rs | 2 +- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/examples/werewolf_cli/game.rs b/examples/werewolf_cli/game.rs index 006b75e..7154746 100644 --- a/examples/werewolf_cli/game.rs +++ b/examples/werewolf_cli/game.rs @@ -65,16 +65,18 @@ impl Game { } } - pub fn role_assignment(&mut self) { + pub fn role_assignment(&mut self, is_prove: bool) { let role = role::calc_role(self.state.players.len(), &self.rules); for (player, role) in self.state.players.iter_mut().zip(role) { player.role = Some(role); } - // prove and verify - if let Err(e) = self.prove_and_verify() { - eprintln!("Failed to prove and verify: {}", e); + if is_prove { + // prove and verify + if let Err(e) = self.prove_and_verify() { + eprintln!("Failed to prove and verify: {}", e); + } } } diff --git a/examples/werewolf_cli/main.rs b/examples/werewolf_cli/main.rs index a8b266c..f21889e 100644 --- a/examples/werewolf_cli/main.rs +++ b/examples/werewolf_cli/main.rs @@ -48,7 +48,7 @@ fn main() -> Result<(), Box> { let mut game = Game::new(register_players(), game_rule); - game.role_assignment(); + game.role_assignment(false); println!("{:?}", game.state.players);