EW2S6QYPETMVAQJYM3OIUQNI5A7USN3QLQFAWQC6GEAF4YF7BITAC use std::fmt;use crate::deck::Deck;use crate::card::Card;#[derive(Debug, Default)]pub struct Turn {pub master_index: Option<usize>,pub fool_played: bool,cards: Deck,}impl Turn {pub fn take(&mut self) -> Deck {Deck(self.cards.0.drain(..).collect())}pub fn put(&mut self, card: Card) {self.cards.push(card);}pub fn len(&self) -> usize {self.cards.len()}pub fn is_empty(&self) -> bool {self.cards.is_empty()}pub fn called(&self) -> Option<&Card> {for c in &self.cards.0 {if c.is_fool() {continue} else {return Some(c)}}None}pub fn master_card(&self) -> Option<&Card> {if let Some(index) = self.master_index {Some(&self.cards.0[index])} else {None}}}impl fmt::Display for Turn {fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {write!(f, "Turn cards: {}", &self.cards)?;if let Some(called) = self.called() {write!(f, "\nCalled: {}", &called)?;}if let Some(master) = self.master_card() {write!(f, "\nMaster: {}", &master)?;}Ok(())}}
use std::fmt;use crate::traits::*;pub const TRUMP_COLOR : char = '🂠';#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, EnumIter)]pub enum TrumpValue {Fool = 0,Petit = 1,_2 = 2,_3 = 3,_4 = 4,_5 = 5,_6 = 6,_7 = 7,_8 = 8,_9 = 9,_10 = 10,_11 = 11,_12 = 12,_13 = 13,_14 = 14,_15 = 15,_16 = 16,_17 = 17,_18 = 18,_19 = 19,_20 = 20,_21 = 21}impl fmt::Display for TrumpValue {fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {match self {Self::Fool => write!(f, "🃏"),_ => write!(f, "{0} : {1: <2}", TRUMP_COLOR, *self as usize)}}}impl TrumpValue {pub fn is_oudler(self) -> bool {match self {Self::Fool | Self::Petit | Self::_21 => true,_ => false}}}impl Discardable for TrumpValue {fn discardable(&self) -> bool {// RULE: cant discard trumpsfalse}fn discardable_forced(&self) -> bool {// RULE: if we have 4 kings and x trumps, we must discard some trumps, except oudlers!self.is_oudler()}}impl Points for TrumpValue {fn points(&self) -> f64 {if self.is_oudler() {4.5} else {0.5}}}
pub trait Points {fn points(&self) -> f64;}pub trait Power {fn power(&self) -> usize;}pub trait Discardable {fn discardable(&self) -> bool;fn discardable_forced(&self) -> bool;}
use std::fmt;#[derive(Eq, PartialEq, Clone, Debug)]pub enum Team {Defense,Attack,}impl fmt::Display for Team {fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {match &self {Self::Defense => write!(f, "defense"),Self::Attack => write!(f, "attack"),}}}
use std::fmt;#[derive(Clone, Debug, PartialEq)]pub enum Role {Taker,Ally,Defenser,}impl fmt::Display for Role {fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {match self {Self::Taker => write!(f, "taker"),Self::Ally => write!(f, "ally of taker"),Self::Defenser => write!(f, "defenser"),}}}
use std::fmt;use rand::Rng;use failure::Error;use strum::IntoEnumIterator;use crate::itertools::Itertools;use crate::traits::*;use crate::contract::*;use crate::deck::*;use crate::color::*;use crate::trump::*;use crate::card::*;use crate::errors::*;use crate::team::*;use crate::role::*;use crate::mode::*;use crate::turn::*;use crate::handle::*;use crate::helpers::*;//pub struct PlayerState<'a> {// pub contract: Option<Contract>,// pub team: Option<Team>,// pub role: Option<Role>,// pub callee: Option<Card>,// pub handle : Option<Handle>,// pub slam: bool,// pub hand: Deck<'a>,// pub owned: Deck<'a>,//}////#[derive(Default, Clone, Debug)]//pub struct Player {// pub name: String,// pub total: f64,// pub mode: Mode,// random: bool,//}#[derive(Default, Clone, Debug)]pub struct Player {pub name: String,pub contract: Option<Contract>,pub slam: bool,pub team: Option<Team>,pub role: Option<Role>,pub hand: Deck,pub owned: Deck,pub callee: Option<Card>,pub total: f64,pub mode: Mode,pub handle : Option<Handle>,random: bool,}pub fn default_name(index: usize) -> Result<String, Error> {match index {0 => Ok("East".to_string()),1 => Ok("North".to_string()),2 => Ok("South".to_string()),3 => Ok("West".to_string()),4 => Ok("Compass".to_string()),_ => Err(TarotErrorKind::InvalidCase.into())}}impl fmt::Display for Player {fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {match &self.contract {Some(contract) => {write!(f, "{}, total: {}, contract: {}, slam: {}", &self.name, &self.total, &contract, &self.slam)?;},None => {write!(f, "{}, total: {}, no contract yet, slam: {}", self.name, &self.total, &self.slam)?;},}if let Some(role) = &self.role {write!(f, ", Role : {}", role)?;}if let Some(team) = &self.team {write!(f, ", Team : {}", team)?;}if let Some(callee) = &self.callee {write!(f, ", Callee : {}", callee)?;}Ok(())}}impl Player{pub fn new(mode: Mode, random: bool) -> Player {Player {mode,random,..Player::default()}}pub fn prepare(&mut self) {self.contract = None;self.slam = false;self.team = None;self.role = None;self.callee = None;self.handle = None;}pub fn slam_bonus(&self) -> f64 {if self.slam {if self.owned.is_chelem() {400.0} else {-200.0}} else if self.owned.is_chelem() {200.0} else if self.owned.is_empty() || (self.owned.len() == 1 && self.owned.has_fool()) {-200.0} else {0.0}}pub fn announce_slam(&mut self) -> bool {let slams = vec![false,true,];self.slam = if self.random {slams[rand::thread_rng().gen_range(0, slams.len())]} else {loop {println!("Hand of {} : {}", &self, &self.hand);println!("Slam ? : ");for (i, s) in slams.iter().enumerate() {println!("{} : press {}", s, i);}let slam_index = read_index();if slam_index < slams.len() {break slams[slam_index]} else {println!("Error, please retry")}}};self.slam}pub fn announce_handle(&mut self) {let mut trumps = self.hand.trumps();let discarded_trumps = self.owned.trumps();let mut total_trumps = trumps.len() + discarded_trumps.len();let handle = Handle::new(total_trumps, self.mode);self.handle = match handle {None => None,Some(mut handle) => {let handles = match handle {Handle::Simple => vec![Handle::Refused, Handle::Simple],Handle::Double => vec![Handle::Refused, Handle::Simple, Handle::Double],Handle::Triple => vec![Handle::Refused, Handle::Simple, Handle::Double, Handle::Triple],Handle::Refused => vec![]};handle = if self.random {handles[rand::thread_rng().gen_range(0, handles.len())].clone()} else {loop {for &a in trumps.iter() {println!("\t{}", &a);}println!("You have {} trumps, you can declare a handle : ", trumps.len());for (i, h) in handles.iter().enumerate() {println!("{} (limit: {}) : press {}", h, h.limit(self.mode), i);}let handle_index = read_index();if handle_index < handles.len() {break handles[handle_index].clone()} else {println!("Error, please retry")}}};if handle != Handle::Refused {trumps.retain(|&c| !c.is_fool());// RULE: cant use fool as trump when you have too much trumps for the handleif total_trumps != trumps.len() + discarded_trumps.len() {println!("You can't use fool as trumps in a handle");}trumps.extend(discarded_trumps.iter());total_trumps = trumps.len();let limit = handle.limit(self.mode);if total_trumps > limit {let mut to_discard = total_trumps - limit;while to_discard > 0 {loop {for (i, a) in trumps.iter().enumerate() {println!("\t{0} : {1}", &i, &a);}println!("You must discards {} trumps to present only {}", &to_discard, &limit);if self.random {let index_to_remove = rand::thread_rng().gen_range(0, trumps.len());trumps.remove(index_to_remove);break} else {let trump_index = read_index();if trump_index < trumps.len() {trumps.remove(trump_index);} else {println!("Error, please retry")}}}to_discard -= 1;}} else {println!("You have exactly the good number of trumps");}println!("Final handle : ");for a in trumps.iter() {println!("\t{}", &a);}}Some(handle)}};}pub fn last_turn(&self) -> bool {self.hand.is_empty()}pub fn before_last_turn(&self) -> bool {self.hand.len() == 1}pub fn owe_card(&self) -> bool {self.owned.has_fool() && self.owned.len() != 1 && (self.owned.len() % self.mode as usize) == 1}pub fn missing_card(&self) -> bool {!self.owned.has_fool() && self.owned.len() != 1 && (self.owned.len() % self.mode as usize) == (self.mode as usize - 1)}pub fn give_low(&mut self) -> Option<Card> {self.owned.give_low()}pub fn give_one(&mut self, index: usize) -> Card {self.hand.0.remove(index)}pub fn points(&self) -> f64 {self.owned.points()}pub fn count_oudlers(&self) -> usize {self.owned.count_oudlers()}pub fn contract_points(&self) -> Result<f64, Error> {let points = self.points();let contract_points = self.owned.points_for_oudlers()?;println!("Taker owned points: {}", &points);println!("Contract todo: {}", &contract_points);println!("Contract difference: {}", points - contract_points);match self.contract {Some(Contract::Pass) | None => Err(TarotErrorKind::NoContract.into()),Some(contract) => {println!("Taker contract: {}", &contract);if points >= contract_points {println!("Contract total: {}", points - contract_points + 25.0);Ok((points - contract_points + 25.0) * f64::from(contract as u8))} else {println!("Contract total: {}", points - contract_points - 25.0);Ok((points - contract_points - 25.0) * f64::from(contract as u8))}}}}pub fn is_first_turn(&self) -> bool {match self.mode {Mode::Three => self.hand.len() == 24,Mode::Four => self.hand.len() == 18,Mode::Five => self.hand.len() == 15,}}pub fn call(&self) -> Result<Card, Error> {if self.mode != Mode::Five {return Err(TarotErrorKind::InvalidMode.into());}let mut value_callable : Vec<ColorValue> = Vec::new();value_callable.push(ColorValue::King);if self.hand.count_tete(ColorValue::King) == 4 {value_callable.push(ColorValue::Queen);if self.hand.count_tete(ColorValue::Queen) == 4 {value_callable.push(ColorValue::Knight);if self.hand.count_tete(ColorValue::Knight) == 4 {value_callable.push(ColorValue::Jack);if self.hand.count_tete(ColorValue::Jack) == 4 {println!("Case too rare, taker has all kings, all queens and all knights");return Err(TarotErrorKind::InvalidCase.into())}}}}let choices : Vec<Card> = Color::iter().cartesian_product(value_callable.iter()).map(|(c, cv)| Card::Color(c, *cv)).collect();if self.random {Ok(choices[rand::thread_rng().gen_range(0, choices.len())])} else {loop {println!("Hand of taker {}", &self.hand);println!("Taker must choose a card to call his partner :");println!("Possibilities:");for (i, c) in choices.iter().enumerate() {println!("\t{0: <3} : press {1}", c, i);}let choice_index = read_index();if choice_index < choices.len() {break Ok(choices[choice_index])} else {println!("Error, please retry")}}}}pub fn discard (&mut self, discard: usize) {println!("{}", &self);println!("You must discard {} cards", discard);for _ in 0..discard {let discardables_indexes = self.hand.discardables(discard);let discard_index = if self.random {discardables_indexes[rand::thread_rng().gen_range(0, discardables_indexes.len())]} else {loop {println!("Hand of taker {}", &self.hand);println!("Possibilities:");for &i in &discardables_indexes {println!("\t{0: <4} : press {1}", self.hand.0[i], i);}let discard_index = read_index();if discardables_indexes.contains(&discard_index) {break discard_index} else {println!("Error, please retry")}}};self.owned.push(self.hand.0.remove(discard_index));}for c in self.owned.trumps() {println!("This trump was discarded: {}", &c);}self.hand.sort();}pub fn choices(&self, turn: &Turn) -> Result<Vec<usize>, Error> {let mut and_fool : Option<usize> = None;let mut trumps = Vec::new();let mut trumps_less = Vec::new();let mut trumps_more = Vec::new();let mut other_colors = Vec::new();let mut same_color = Vec::new();let mut compatibles = match (turn.called(), turn.master_card()) {(Some(Card::Color(called_color, _)), Some(Card::Color(_, _))) => {for (i, card) in self.hand.0.iter().enumerate() {match card {Card::Trump(card_trump_value) => {if card_trump_value == &TrumpValue::Fool {and_fool = Some(i);} else {trumps.push(i);}},Card::Color(card_color, _) => {if card_color == called_color {same_color.push(i);} else {other_colors.push(i);}}}}if !same_color.is_empty() {same_color} else if !trumps.is_empty() {trumps} else {other_colors}},(Some(Card::Color(called_color, _)), Some(Card::Trump(master_trump_value))) => {for (i, card) in self.hand.0.iter().enumerate() {match card {Card::Trump(card_trump_value) => {if card_trump_value == &TrumpValue::Fool {and_fool = Some(i);} else if card_trump_value > master_trump_value {trumps_more.push(i);} else {trumps_less.push(i);}},Card::Color(card_color, _) => {if card_color == called_color {same_color.push(i);} else {other_colors.push(i);}}}}if !same_color.is_empty() {same_color} else if !trumps_more.is_empty() {trumps_more} else if !trumps_less.is_empty() {trumps_less} else {other_colors}},(Some(Card::Trump(_)), Some(Card::Trump(master_trump_value))) => {for (i, card) in self.hand.0.iter().enumerate() {if let Card::Trump(card_trump_value) = card {if card_trump_value == &TrumpValue::Fool {and_fool = Some(i);} else {trumps.push(i);if card_trump_value > master_trump_value {trumps_more.push(i);} else {trumps_less.push(i);other_colors.push(i);}}} else {other_colors.push(i)}}if !trumps_more.is_empty() {trumps_more} else if !trumps_less.is_empty() {trumps_less} else {other_colors}},(Some(Card::Color(_, _)), None) => {println!("There cannot be a called color and no master card, impossible case!");return Err(TarotErrorKind::InvalidCase.into())}(Some(Card::Trump(_)), Some(Card::Color(_, _))) => {println!("There cannot be a called trump and a master color, impossible case!");return Err(TarotErrorKind::InvalidCase.into())}(Some(Card::Trump(_)), None) => {println!("There cannot be a called trump and not master, impossible case!");return Err(TarotErrorKind::InvalidCase.into())}(None, Some(_)) => {println!("There cannot be no called color and a master, impossible case!");return Err(TarotErrorKind::InvalidCase.into())},// RULE: first player can put the callee but no any other card in the same color(None, None) => match (self.is_first_turn(), self.mode) {(true, Mode::Five) => {self.hand.0.iter().enumerate().filter(|(_, &card)| {match (card, self.callee) {(Card::Color(color, value), Some(Card::Color(callee_color, callee_value))) => callee_color != color || value == callee_value,_ => true}}).map(|(i, _)| i).collect()},_ => (0..self.hand.len()).collect()},};if let Some(fool_index) = and_fool {compatibles.push(fool_index);}Ok(compatibles)}}#[test]fn player_tests() {use std::f64::EPSILON;let looser = Player {name: "Player looser".to_string(),contract: Some(Contract::Petite),mode: Mode::Four,..Player::default()};println!("looser: {}", &looser);assert!(looser.points() == 0.0);assert!(looser.count_oudlers() == 0);println!("looser points: {}", looser.contract_points().unwrap());assert!((looser.contract_points().unwrap() - -81.0).abs() < EPSILON);let mut winner = Player {name: "Player looser".to_string(),contract: Some(Contract::GardeContre),owned: Deck::build_deck(),mode: Mode::Five,random: true,..Player::default()};winner.callee = Some(winner.call().unwrap());let turn = Turn::default();println!("{}", &winner.hand);let choices = &winner.choices(&turn).unwrap();println!("Choices :");for &i in choices {println!("\t{0: <2} : {1}", &i, &winner.hand.0[i]);}assert!((winner.points() - 91.0).abs() < EPSILON);assert!(winner.count_oudlers() == 3);println!("winner points: {}", winner.contract_points().unwrap());assert!((winner.contract_points().unwrap() - 480.0).abs() < EPSILON);let mut handle_owner = Player {name: "Player looser".to_string(),contract: Some(Contract::GardeContre),callee: Some(Card::Color(Color::Club, ColorValue::King)),mode: Mode::Five,random: true,..Player::default()};handle_owner.announce_handle();}
use std::fmt;use std::str::FromStr;use crate::errors::TarotErrorKind;#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, EnumIter)]pub enum Mode {Three = 3,Four = 4,Five = 5}impl fmt::Display for Mode {fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {match self {Self::Three => write!(f, "{} players, 1 vs 2", Mode::Three as usize),Self::Four => write!(f, "{} players, 1 vs 3", Mode::Four as usize),Self::Five => write!(f, "{} players, 2 vs 3", Mode::Five as usize),}}}impl FromStr for Mode {type Err = TarotErrorKind;fn from_str(s: &str) -> Result<Mode, TarotErrorKind> {match s {"3" => Ok(Self::Three),"4" => Ok(Self::Four),"5" => Ok(Self::Five),_ => Err(TarotErrorKind::InvalidPlayers),}}}impl Mode {pub fn dog_size(self) -> usize {match self {Self::Five => 3,_ => 6}}pub fn cards_per_turn(self) -> usize {match self {Self::Three=> 4,_ => 3}}pub fn cards_per_player(self) -> usize {match self {Self::Three => 24,Self::Four => 18,Self::Five => 15,}}}impl Default for Mode {fn default() -> Mode { Mode::Four }}#[test]fn mode_tests() {let mode = Mode::default();println!("mode: {}", &mode);let three = Mode::from_str("3");assert!(three == Ok(Mode::Three));let four = Mode::from_str("4");assert!(four == Ok(Mode::Four));let five = Mode::from_str("5");assert!(five == Ok(Mode::Five));}
extern crate strum;extern crate rand;extern crate num_cpus;extern crate itertools;#[macro_use] extern crate log;#[macro_use] extern crate strum_macros;#[macro_use] extern crate failure;#[macro_use] extern crate lazy_static;pub mod helpers;pub mod traits;pub mod errors;pub mod color;pub mod trump;pub mod card;pub mod deck;pub mod player;pub mod role;pub mod team;pub mod turn;pub mod handle;pub mod mode;pub mod contract;pub mod game;use std::error;use std::thread;use structopt::StructOpt;use strum::IntoEnumIterator;use crate::mode::Mode;lazy_static! {static ref DEFAULT_CONCURRENCY: String = num_cpus::get().to_string();}#[derive(StructOpt, Debug)]#[structopt(name = "RTarot", about = "Tarot simulation", version = "1.0", author = "Adrien P. <crunchengine@gmail.com>")]struct Opt {/// Players mode#[structopt(default_value = "4", possible_values = &["3", "4", "5"])]players: mode::Mode,/// Random playing mode#[structopt(short = "r", long = "random")]random: bool,/// Auto playing mode when possible#[structopt(short = "a", long = "auto")]auto: bool,/// Test mode#[structopt(short = "t", long = "test")]test: bool,/// Concurrency in test mode, default is number of cpu on this machine#[structopt(short, default_value = DEFAULT_CONCURRENCY.as_str())]concurrency: usize}fn main() -> Result<(), Box<dyn error::Error>> {let opt = Opt::from_args();if opt.test {let mut children = vec![];for _ in 0..opt.concurrency {children.push(thread::spawn(move || {#[allow(clippy::infinite_iter)]Mode::iter().cycle().for_each(helpers::test_game);}));}for child in children {let _ = child.join();}} else {let mut game = game::Game::new(opt.players, opt.random, opt.auto);loop {game.distribute()?;game.bidding()?;if game.passed() {println!("Everyone passed !");continue}game.discard()?;while !game.finished() {game.play()?;}game.count_points()?;break}println!("GAME ENDED");println!("{}", &game);}Ok(())}
use crate::mode::Mode;use std::io;#[allow(clippy::redundant_closure)]pub fn read_index() -> usize {let mut input = String::new();loop {if io::stdin().read_line(&mut input).is_ok() {return input.trim().parse::<usize>().unwrap();}}}pub fn wait_input() {use std::io::prelude::*;let mut stdin = io::stdin();let _ = stdin.read(&mut [0u8]).unwrap();}pub fn test_game(mode: Mode) {use crate::game::Game;use crate::errors::TarotErrorKind;loop {let mut game = Game::new(mode, true, true);if let Err(fail) = game.distribute() {if let Some(cause) = fail.find_root_cause().downcast_ref::<TarotErrorKind>() {if cause == &TarotErrorKind::PetitSec {continue} else {panic!("Error is not PetitSec")}}}assert!(game.bidding().is_ok());if game.passed() {continue}assert!(game.discard().is_ok());while !game.finished() {assert!(game.play().is_ok());}assert!(game.count_points().is_ok());break}}pub fn binomial(mut n: usize, mut k: usize) -> usize {if k > n {return 0;}if k > (n / 2) {k = n - k;}let mut result = 1;for d in 1..=k {result *= n;n -= 1;result /= d;}result}#[test]fn helpers_tests() {assert_eq!(binomial(24, 6), 134_596);}
use std::fmt;use crate::mode::Mode;#[derive(Eq, PartialEq, Debug, Clone)]pub enum Handle {Refused = 0,Simple = 20,Double = 30,Triple = 40,}impl fmt::Display for Handle {fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {match self {Self::Refused => write!(f, "refuse handle"),Self::Simple => write!(f, "simple handle"),Self::Double => write!(f, "double handle"),Self::Triple => write!(f, "triple handle"),}}}impl Handle {pub fn new(count: usize, mode: Mode) -> Option<Handle> {match mode {Mode::Three => {match count {0 ..= 12 => None,13 ..= 14 => Some(Self::Simple),15 ..= 17 => Some(Self::Double),_ => Some(Self::Triple)}},Mode::Four => {match count {0 ..= 9 => None,10 ..= 12 => Some(Self::Simple),13 ..= 14 => Some(Self::Double),_ => Some(Self::Triple)}},Mode::Five => {match count {0 ..= 7 => None,8 ..= 9 => Some(Self::Simple),10 ..= 12 => Some(Self::Double),_ => Some(Self::Triple)}}}}pub fn limit(&self, mode: Mode) -> usize {match self {Self::Refused => 0 as usize,Self::Simple => {match mode {Mode::Three => 13 as usize,Mode::Four => 10 as usize,Mode::Five => 8 as usize}},Self::Double => {match mode {Mode::Three => 15 as usize,Mode::Four => 13 as usize,Mode::Five => 10 as usize}}Self::Triple => {match mode {Mode::Three => 18 as usize,Mode::Four => 15 as usize,Mode::Five => 13 as usize}}}}}
use failure::Error;use std::fmt;use rand::{Rng, thread_rng};use rand::seq::SliceRandom;use strum::IntoEnumIterator;use crate::deck::*;use crate::mode::*;use crate::contract::*;use crate::player::*;use crate::errors::*;use crate::turn::*;use crate::card::*;use crate::role::*;use crate::team::*;use crate::helpers::*;#[derive(Debug)]pub struct Game {dog: Deck,deck: Deck,mode: Mode,players: Vec<Player>,random: bool,auto: bool,petit_au_bout: Option<Team>,defense_cards: usize,attack_cards: usize,}impl fmt::Display for Game {fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {writeln!(f, "Game with dog {} and players : ", self.dog)?;for p in &self.players {writeln!(f, "\t{}", p)?;}Ok(())}}impl<'a> Default for Game {fn default() -> Game{Game {random: false,auto: false,petit_au_bout: None,defense_cards: 0,attack_cards: 0,dog: Deck::default(),deck: Deck::build_deck(),players: vec![Player::new(Mode::default(), false); Mode::default() as usize],mode: Mode::default(),}}}impl Game{pub fn new(mode: Mode, random: bool, auto: bool) -> Game {let mut game = Game {random,auto,players: vec![Player::new(mode, random); mode as usize],mode,..Game::default()};for (i, p) in game.players.iter_mut().enumerate() {if let Ok(name) = default_name(i) {p.name = name;}p.mode = mode;}game}fn is_consistent(&self) {let sum = self.players.iter().map(|ref p| p.total).sum::<f64>();debug!("Current points sum : {}", sum);assert!(sum == 0.0)}pub fn distribute(&mut self) -> Result<(), Error> {let mut decks : Vec<Deck> = Vec::new();let dog = self.dog.give_all();decks.push(dog);for p in self.players.iter_mut() {let hand = p.hand.give_all();decks.push(hand);let deck = p.owned.give_all();decks.push(deck);p.prepare();}let mut rng = thread_rng();decks.shuffle(&mut rng);for mut d in decks {self.deck.append(&mut d.give_all());}self.petit_au_bout = None;self.defense_cards = 0;self.attack_cards = 0;self.dog = self.deck.give(self.mode.dog_size());self.dog.sort();for p in self.players.iter_mut() {p.hand.append(&mut self.deck.give(self.mode.cards_per_player()))}for p in self.players.iter_mut() {if p.hand.petit_sec() {// RULE: PetitSec cancel the gamereturn Err(TarotErrorKind::PetitSec.into())}p.hand.sort();}Ok(())}pub fn bidding(&mut self) -> Result<(), Error> {let mut contracts: Vec<Contract> = Contract::iter().collect();let mut slam_index : Option<usize> = None;for (i, p) in self.players.iter_mut().enumerate() {if self.auto && contracts.len() == 1 {p.contract = Some(Contract::Pass);println!("Auto pass");continue}p.contract = if self.random {Some(contracts[rand::thread_rng().gen_range(0, contracts.len())])} else {loop {println!("{} must play : {}", &p, &p.hand);println!("Choose a contract, possibilities :");for (i, c) in contracts.iter().enumerate() {println!("\t{} : press {}", c, i);}let contract_index = read_index();if contract_index < contracts.len() {break Some(contracts[contract_index])} else {println!("Error, please retry");}}};contracts = match p.contract {Some(contract) => {println!("Player {} has chosen contract {}", p.name, contract);contracts.into_iter().filter(|other_contract| other_contract == &Contract::Pass || *other_contract as usize > contract as usize).collect()},_ => {println!("A contract must be available for everyone!");return Err(TarotErrorKind::InvalidCase.into())}};if p.contract != Some(Contract::Pass) && p.announce_slam() {slam_index = Some(i);}}// RULE: player who slammed must startif let Some(slammer) = slam_index {self.players.rotate_left(slammer);}Ok(())}pub fn passed(&self) -> bool {self.players.iter().all(|p| p.contract == Some(Contract::Pass))}pub fn finished(&self) -> bool {self.players.iter().all(|p| p.hand.is_empty())}pub fn discard (&mut self) -> Result<(), Error> {if self.passed() {return Err(TarotErrorKind::NoTaker.into());}let mut callee: Option<Card> = None;let contract: Option<Contract> = if let Some(taker) = self.players.iter_mut().max_by(|c1, c2| c1.contract.unwrap().cmp(&c2.contract.unwrap())) {println!("{} has taken", taker);if let Mode::Five = self.mode {callee = Some(taker.call()?);}taker.contract} else {return Err(TarotErrorKind::NoTaker.into());};for p in &mut self.players {println!("Player before : {}", p);p.callee = callee;p.team = Some(Team::Defense);p.role = Some(Role::Defenser);if p.contract == contract {p.team = Some(Team::Attack);p.role = Some(Role::Taker);} else if let Some(ref card) = callee {if p.hand.0.contains(&&card) {p.team = Some(Team::Attack);p.role = Some(Role::Ally);}}p.contract = contract;println!("Player after : {}", p);}let team_partitioner = |p: &'_ &mut Player| -> bool {match &p.team {Some(team) => team == &Team::Attack,_ => false}};let (takers, others): (Vec<_>, Vec<_>) = self.players.iter_mut().partition(team_partitioner);for taker in takers {if taker.role != Some(Role::Taker) {continue}match taker.contract {Some(Contract::Pass) => continue,Some(Contract::GardeSans) => {taker.owned.append(&mut self.dog.give_all());return Ok(())}Some(Contract::GardeContre) => {for o in others {o.owned.append(&mut self.dog.give_all());}return Ok(())},_ => {let discard = self.dog.len();println!("In the dog, there was : {}", &self.dog);taker.hand.append(&mut self.dog.give_all());taker.hand.sort();taker.discard(discard);},}}Ok(())}pub fn play (&mut self) -> Result<(), Error> {let mut turn = Turn::default();let mut master_player: usize = 0;for (i, p) in self.players.iter_mut().enumerate() {if p.is_first_turn() {p.announce_handle();}println!("{}", &turn);println!("Hand of {} : {}", &p, &p.hand);println!("Choices :");let choices = &p.choices(&turn)?;if choices.is_empty() {println!("No choices available, invalid case.");return Err(TarotErrorKind::InvalidCase.into())}for &i in choices {println!("\t{0: <2} : {1}", &i, p.hand.0[i]);}if let Some(master) = turn.master_card() {println!("{} must play color {}", &p.name, &master)} else {println!("{} is first to play:", &p.name)}let index = if self.auto && choices.len() == 1 {choices[0]} else if self.random {choices[rand::thread_rng().gen_range(0, choices.len())]} else {loop {let choice_index = read_index();if choices.contains(&choice_index) {break choice_index} else {println!("Error, please retry")}}};let card = p.give_one(index);if card.is_fool() {if !p.last_turn() {// RULE: the fool is always preserved to his ownerp.owned.push(card);// we must mark as the fool was playedturn.fool_played = true;} else {// RULE: exception in the last turn, the fool is in game and can be lostturn.put(card);match p.team {Some(Team::Attack) => {if self.attack_cards == MAX_CARDS - self.mode.dog_size() {turn.master_index = Some(turn.len()-1);master_player = i;}},Some(Team::Defense) => {if self.defense_cards == MAX_CARDS - self.mode.dog_size() {turn.master_index = Some(turn.len()-1);master_player = i;}},_ => {return Err(TarotErrorKind::NoTeam.into())}}}} else {turn.put(card);if let Some(master) = turn.master_card() {if master.master(card) {println!("Master card is {}, so player {} stays master", master, master_player);} else {println!("Master card is {}, so player {} becomes master", card, i);master_player = i;turn.master_index = Some(turn.len()-1);}} else {println!("First card is {}, so player {} becomes master", card, i);master_player = i;turn.master_index = Some(turn.len()-1);}}}let mut cards = turn.take();println!("Winner is player {}", self.players[master_player]);// RULE: petit au bout works for last turn, or before last turn if a slam is occuringif cards.has_petit() &&(self.players[master_player].last_turn() ||(self.players[master_player].before_last_turn() &&((self.attack_cards == MAX_CARDS - self.mode.dog_size() - self.mode as usize ) || (self.defense_cards == MAX_CARDS - self.mode.dog_size() - self.mode as usize)))) {println!("{} has Petit in last turn (Petit au bout) : +10 points", self.players[master_player]);self.petit_au_bout = self.players[master_player].team.clone();}match self.players[master_player].team {Some(Team::Attack) => self.attack_cards += cards.len(),Some(Team::Defense) => self.defense_cards += cards.len(),_ => return Err(TarotErrorKind::NoTeam.into())}self.players[master_player].owned.append(&mut cards);self.players.rotate_left(master_player);Ok(())}pub fn count_points(&mut self) -> Result<(), Error> {if self.passed() {return Err(TarotErrorKind::NoTaker.into());}let mut taker_index : Option<usize> = None;let mut ally_index : Option<usize> = None;let mut defense : Vec<usize> = Vec::new();let mut owning_card_player_index : Option<usize> = None;let mut missing_card_player_index : Option<usize> = None;let mut handle_bonus = 0.0;for (i, p) in self.players.iter().enumerate() {if p.owe_card() {owning_card_player_index = Some(i);}if p.missing_card() {missing_card_player_index = Some(i);}if let Some(handle) = &p.handle {handle_bonus += f64::from(handle.clone() as u8);println!("Handle bonus: {}", handle_bonus);}match p.role {Some(Role::Taker) => {assert!(taker_index.is_none());taker_index = Some(i)}Some(Role::Ally) => {assert!(ally_index.is_none());ally_index = Some(i)}Some(Role::Defenser) => {defense.push(i)}None => return Err(TarotErrorKind::InvalidCase.into()),}}match self.mode {Mode::Three => assert!(defense.len() == 2),Mode::Four => assert!(defense.len() == 3),Mode::Five => {if ally_index.is_some() {assert!(defense.len() == 3)} else {assert!(defense.len() == 4)}}};// give a low card if someone one a card to someone elseif let Some(owning_index) = owning_card_player_index {let low_card = self.players[owning_index].give_low();if let Some(low) = low_card {if let Some(missing_index) = missing_card_player_index {self.players[missing_index].owned.push(low);}}}if let Some(ally_index) = ally_index {let mut ally_cards = self.players[ally_index].owned.give_all();if let Some(taker_index) = taker_index {self.players[taker_index].owned.append(&mut ally_cards)} else {println!("Cant merge cards of ally if no taker");return Err(TarotErrorKind::NoTaker.into());}}if let Some(taker_index) = taker_index {let slam_bonus = self.players[taker_index].slam_bonus();println!("Taker slam bonus: {}", &slam_bonus);let contract_points = self.players[taker_index].contract_points()?;println!("Taker contract points: {}", &contract_points);let petit_au_bout_bonus = if let Some(contract) = self.players[taker_index].contract {match self.petit_au_bout {Some(Team::Defense) => {println!("Petit au bout for defense: {}", -10.0 * f64::from(contract as u8));-10.0 * f64::from(contract as u8)},Some(Team::Attack) => {println!("Petit au bout for attack: {}", 10.0 * f64::from(contract as u8));10.0 * f64::from(contract as u8)},None => 0.0}} else {return Err(TarotErrorKind::NoContract.into())};let ratio = match self.mode {Mode::Three => 2.0,Mode::Four => 3.0,Mode::Five => if ally_index.is_none() { 4.0 } else { 2.0 },};if contract_points < 0.0 {handle_bonus *= -1.0;}println!("Attack handle bonus: {}", &handle_bonus);let points = contract_points + petit_au_bout_bonus + handle_bonus + slam_bonus;println!("Taker points: {}", &points);self.players[taker_index].total = ratio * points;println!("Taker total points: {}", &self.players[taker_index].total);if let Some(ally_index) = ally_index {self.players[ally_index].total = points;println!("Ally total points: {}", &self.players[ally_index].total);}println!("Defense indexes : {:?}", defense);for defenser_index in defense {self.players[defenser_index].total = -1.0 * points;println!("Defenser {} total points: {}", defenser_index, &self.players[defenser_index].total);}//if handle_bonus != 0.0 && petit_au_bout_bonus != 0.0 && slam_bonus != 0.0 && ratio == 4.0 {// helpers::wait_input();//}} else {println!("Cant count points if no taker");return Err(TarotErrorKind::NoTaker.into());}self.is_consistent();Ok(())}}#[test]fn game_tests() {use crate::mode::*;test_game(Mode::Three);test_game(Mode::Four);test_game(Mode::Five);}
use failure::Context;#[derive(Debug)]pub struct TarotError {inner: Context<TarotErrorKind>,}#[derive(Copy, Clone, Eq, PartialEq, Debug, Fail)]pub enum TarotErrorKind {#[fail(display = "A deck contains only one outsider: the petit.")]PetitSec,#[fail(display = "Card is invalid")]InvalidCard,#[fail(display = "Invalid number of players")]InvalidPlayers,#[fail(display = "No contract")]NoContract,#[fail(display = "Invalid mode")]InvalidMode,#[fail(display = "Invalid contract")]InvalidContract,#[fail(display = "Invalid case")]InvalidCase,#[fail(display = "Invalid color")]InvalidColor,#[fail(display = "No taker or auctions not finished")]NoTaker,#[fail(display = "A player shoud belongs to a team")]NoTeam,}
use std::fmt;use std::f64::EPSILON;use strum::IntoEnumIterator;use itertools::Itertools;use rand::thread_rng;use rand::seq::SliceRandom;use failure::Error;use crate::card::*;use crate::color::*;use crate::traits::*;use crate::trump::*;use crate::errors::TarotErrorKind;#[derive(Default, Clone, Debug)]pub struct Deck (pub Vec<Card>);pub const MAX_CARDS : usize = 78;const MAX_POINTS : f64 = 91.0;const MAX_POINTS_WITHOUT_FOOL : f64 = 87.0;impl<'a> fmt::Display for Deck {fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {let mut last_color : Option<&Color> = None;let (trumps, colors): (Vec<_>, Vec<_>) = self.0.iter().partition(|c| c.is_trump());let trumps_value : Vec<usize> = trumps.iter().filter_map(|t| match t {Card::Trump(v) => Some(*v as usize), _ => None } ).collect();if !trumps.is_empty() {write!(f, "\n\t{} : {}", TRUMP_COLOR, trumps_value.iter().join(" "))?;}for colored in colors.iter() {if let Card::Color(c, cv) = colored {if last_color == Some(&c) {write!(f, "{} ", cv)?} else {last_color = Some(&c);match last_color {None => {write!(f, "\t{} : {} ", c, cv)?},_ => write!(f, "\n\t{} : {} ", c, cv)?,}}}}Ok(())}}impl Points for Deck {fn points(&self) -> f64 {// RULE: if a slam is occuring and player has only fool or everyting except fool, fool = 4 pointsif self.0.len() == MAX_CARDS - 1 && !self.has_fool() {MAX_POINTS_WITHOUT_FOOL} else if self.len() == 1 && self.has_fool() {4.0} else {self.0.iter().map(Points::points).sum()}}}impl Deck {pub fn build_deck() -> Deck {let mut d : Vec<Card> = TrumpValue::iter().map(Card::Trump).chain(Color::iter().cartesian_product(ColorValue::iter()).map(|(c, cv)| Card::Color(c, cv))).collect();let mut rng = thread_rng();d.shuffle(&mut rng);Deck(d)}pub fn trumps(&self) -> Vec<&Card> {self.0.iter().filter(|&card| card.is_trump()).collect()}pub fn has_fool(&self) -> bool {self.0.contains(&Card::Trump(TrumpValue::Fool))}pub fn has_petit(&self) -> bool {self.0.contains(&Card::Trump(TrumpValue::Petit))}pub fn is_chelem(&self) -> bool {// RULE: deck is a chelem if all cards are there or fool is missingself.points() == MAX_POINTS || self.points() == MAX_POINTS_WITHOUT_FOOL}pub fn points_for_oudlers(&self) -> Result<f64, Error> {match self.count_oudlers() {0 => Ok(56.0),1 => Ok(51.0),2 => Ok(41.0),3 => Ok(36.0),_ => Err(TarotErrorKind::InvalidCase.into()),}}pub fn len(&self) -> usize {self.0.len()}pub fn is_empty(&self) -> bool {self.0.is_empty()}pub fn petit_sec(&self) -> bool {self.0.iter().fold(0, |acc, c| match &c { Card::Trump(c) => {acc + *c as usize}, _ => acc}) == 1}pub fn discardables(&self, discard: usize) -> Vec<usize> {let choices : Vec<usize> = self.0.iter().enumerate().filter(|(_, card)| card.discardable()).map(|(i, _)| i).collect();if choices.len() < discard {self.0.iter().enumerate().filter(|(_, card)| card.discardable_forced()).map(|(i, _)| i).collect()} else {choices}}pub fn count_trumps(&self) -> usize {self.0.iter().filter(|card| card.is_trump()).count()}pub fn count_oudlers(&self) -> usize {self.0.iter().filter(|card| card.is_oudler()).count()}pub fn count_tete(&self, cv: ColorValue) -> usize {self.0.iter().filter(|card| match card { Card::Color(_, v) => v == &cv, _ => false }).count()}pub fn misere_tete(&self) -> bool {!self.0.iter().any(|card| match card { Card::Color(_, v) => v.points() - 0.5 < EPSILON, _ => false })}pub fn give(&mut self, size: usize) -> Deck {Deck(self.0.drain(0..size).collect())}pub fn give_all(&mut self) -> Deck {Deck(self.0.drain(..).collect())}pub fn give_low(&mut self) -> Option<Card> {let low_index = &self.0.iter().enumerate().filter_map(|(i, c)| if c.points() - 0.5 < EPSILON { Some(i) } else { None }).next();if let Some(index) = low_index {Some(self.0.remove(index.to_owned()))} else {None}}pub fn append(&mut self, deck: &mut Deck) {self.0.append(&mut deck.0);}pub fn push(&mut self, card: Card){self.0.push(card);}pub fn sort(&mut self) {self.0.sort();}}#[test]fn deck_tests() {let stack = Deck::build_deck();assert!(stack.len() == MAX_CARDS);assert!(stack.points() == MAX_POINTS);let empty = Deck::default();assert!(empty.is_empty());}
use std::fmt;use std::str::FromStr;use crate::errors::TarotErrorKind;#[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd, EnumIter)]pub enum Contract {Pass = 0,Petite = 1,Garde = 2,GardeSans = 4,GardeContre = 6,}impl fmt::Display for Contract {fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {match self {Self::Pass => write!(f, "Passe. (x0)"),Self::Petite => write!(f, "Petite (x1)"),Self::Garde => write!(f, "Garde (x2)"),Self::GardeSans => write!(f, "Garde Sans (x4)"),Self::GardeContre => write!(f, "Garde Contre (x6)"),}}}impl FromStr for Contract {type Err = TarotErrorKind;fn from_str(s: &str) -> Result<Contract, TarotErrorKind> {match s {"0" => Ok(Self::Pass),"1" => Ok(Self::Petite),"2" => Ok(Self::Garde),"4" => Ok(Self::GardeSans),"6" => Ok(Self::GardeContre),_ => Err(TarotErrorKind::InvalidContract),}}}#[test]fn card_contracts() {let pass = Contract::from_str("0");assert!(pass == Ok(Contract::Pass) );let petite = Contract::from_str("1");assert!(petite == Ok(Contract::Petite) );let garde = Contract::from_str("2");assert!(garde == Ok(Contract::Garde) );let garde_sans = Contract::from_str("4");assert!(garde_sans == Ok(Contract::GardeSans) );let garde_contre = Contract::from_str("6");assert!(garde_contre == Ok(Contract::GardeContre) );}
use std::fmt;use std::str::FromStr;use crate::errors::TarotErrorKind;use crate::traits::*;#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, EnumIter)]pub enum Color {Heart,Spade,Diamond,Club,}impl fmt::Display for Color {fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {match self {Self::Spade => write!(f, "♠"),Self::Diamond => write!(f, "♦"),Self::Club => write!(f, "♣"),Self::Heart => write!(f, "♥"),}}}impl FromStr for Color {type Err = TarotErrorKind;fn from_str(s: &str) -> Result<Color, TarotErrorKind> {match s {"1" => Ok(Self::Heart),"2" => Ok(Self::Spade),"3" => Ok(Self::Diamond),"4" => Ok(Self::Club),_ => Err(TarotErrorKind::InvalidColor),}}}#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, EnumIter)]pub enum ColorValue {_1 = 1,_2 = 2,_3 = 3,_4 = 4,_5 = 5,_6 = 6,_7 = 7,_8 = 8,_9 = 9,_10 = 10,Jack = 11,Knight = 12,Queen = 13,King = 14,}impl fmt::Display for ColorValue {fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {match self {Self::Jack => write!(f, "V"),Self::Knight => write!(f, "C"),Self::Queen => write!(f, "Q"),Self::King => write!(f, "K"),_ => write!(f, "{}", *self as usize),}}}impl Discardable for ColorValue {fn discardable(&self) -> bool {// RULE: cant discard kingsself != &Self::King}fn discardable_forced(&self) -> bool {// RULE: cant discard kingsself != &Self::King}}impl Points for ColorValue {fn points(&self) -> f64 {match self {Self::Jack => 1.5,Self::Knight => 2.5,Self::Queen => 3.5,Self::King => 4.5,_ => 0.5}}}
use std::fmt;use crate::traits::*;use crate::color::*;use crate::trump::*;#[derive(Copy, Ord, Clone, Debug, Eq, PartialEq, PartialOrd)]pub enum Card {Trump(TrumpValue),Color(Color, ColorValue)}impl fmt::Display for Card {fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {match self {Self::Trump(v) => write!(f, "{}", v),Self::Color(c, v) => write!(f, "{} : {}", c, v)}}}impl Points for Card {fn points(&self) -> f64 {match self {Self::Trump(v) => v.points(),Self::Color(_, v) => v.points()}}}impl Power for Card {fn power(&self) -> usize {match self {Self::Trump(v) => *v as usize + ColorValue::King as usize,Self::Color(_, v) => *v as usize,}}}impl Discardable for Card {fn discardable(&self) -> bool {match self {Self::Trump(t) => t.discardable(),Self::Color(_, v) => v.discardable()}}fn discardable_forced(&self) -> bool {match self {Self::Trump(t) => t.discardable_forced(),Self::Color(_, v) => v.discardable_forced()}}}impl Card {pub fn is_fool(self) -> bool {match self {Self::Trump(v) => v == TrumpValue::Fool,_ => false}}pub fn is_trump(self) -> bool {match self {Self::Trump(_) => true,_ => false}}pub fn is_oudler(self) -> bool {match self {Self::Trump(c) => c.is_oudler(),_ => false}}pub fn master(self, arg: Card) -> bool {match (&self, &arg) {(Self::Trump(c), Self::Color(_, _)) => c != &TrumpValue::Fool,(Self::Color(_, _), Self::Trump(c)) => c == &TrumpValue::Fool,(Self::Color(c1, v1), Self::Color(c2, v2)) => c1 != c2 || v1 > v2,(Self::Trump(v1), Self::Trump(v2)) => v1 > v2,}}}#[test]fn card_tests() {use std::f64::EPSILON;let trump_2 = Card::Trump(TrumpValue::_2);let petit = Card::Trump(TrumpValue::Petit);let fool = Card::Trump(TrumpValue::Fool);let unassailable = Card::Trump(TrumpValue::_21);let spade_1 = Card::Color(Color::Spade, ColorValue::_1);let spade_2 = Card::Color(Color::Spade, ColorValue::_2);let spade_3 = Card::Color(Color::Spade, ColorValue::_3);let spade_10 = Card::Color(Color::Spade, ColorValue::_10);let diamond_3 = Card::Color(Color::Diamond, ColorValue::_3);assert!(!spade_3.master(spade_10));assert!(!petit.master(trump_2));assert!(petit.master(spade_1));assert!(!spade_1.master(petit));assert!(spade_2.master(spade_1));assert!(diamond_3.master(spade_2));assert!(diamond_3.master(fool));assert!(!fool.master(spade_1));assert!(!petit.discardable());assert!(!fool.discardable());assert!(!unassailable.discardable());assert!(!petit.discardable_forced());assert!(!fool.discardable_forced());assert!(!unassailable.discardable_forced());assert!(unassailable.points() - 4.5 < EPSILON);}
cognitive-complexity-threshold = 40
French TarotImplemented with :- rules : http://www.fftarot.fr/assets/documents/R-RO201206.pdf- traductions : https://en.wikipedia.org/wiki/French_tarotUsage :- coverage :RUSTFLAGS="--cfg procmacro2_semver_exempt" cargo install cargo-tarpaulinRUSTFLAGS="--cfg procmacro2_semver_exempt" cargo tarpaulin -v- test : cargo test -- --nocapture- clippy : cargo clippy --all-targets --all-features -- -D warningsTodo :- type games : defense, attack, petit hunt, full assets- add colors- game managing- flag on/off- cut game- random reunion- duplicate
[package]name = "rtarot"default-run = "rtarot"version = "0.1.0"authors = ["Adrien Pensart <crunchengine@gmail.com>"]edition = "2018"[dependencies]log = "0.4"lazy_static = "1.4.0"structopt = "0.3"failure = "0.1.5"rand = "0.7"itertools = "0.8"strum = "0.15"strum_macros = "0.15"num_cpus = "1.0"
# This file is automatically @generated by Cargo.# It is not intended for manual editing.[[package]]name = "addr2line"version = "0.13.0"source = "registry+https://github.com/rust-lang/crates.io-index"dependencies = ["gimli 0.22.0 (registry+https://github.com/rust-lang/crates.io-index)",][[package]]name = "adler"version = "0.2.3"source = "registry+https://github.com/rust-lang/crates.io-index"[[package]]name = "ansi_term"version = "0.11.0"source = "registry+https://github.com/rust-lang/crates.io-index"dependencies = ["winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",][[package]]name = "atty"version = "0.2.14"source = "registry+https://github.com/rust-lang/crates.io-index"dependencies = ["hermit-abi 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)","libc 0.2.78 (registry+https://github.com/rust-lang/crates.io-index)","winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",][[package]]name = "autocfg"version = "1.0.1"source = "registry+https://github.com/rust-lang/crates.io-index"[[package]]name = "backtrace"version = "0.3.51"source = "registry+https://github.com/rust-lang/crates.io-index"dependencies = ["addr2line 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)","cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)","libc 0.2.78 (registry+https://github.com/rust-lang/crates.io-index)","miniz_oxide 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)","object 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)","rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",][[package]]name = "bitflags"version = "1.2.1"source = "registry+https://github.com/rust-lang/crates.io-index"[[package]]name = "cfg-if"version = "0.1.10"source = "registry+https://github.com/rust-lang/crates.io-index"[[package]]name = "clap"version = "2.33.3"source = "registry+https://github.com/rust-lang/crates.io-index"dependencies = ["ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)","atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)","bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)","strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)","textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)","unicode-width 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)","vec_map 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)",][[package]]name = "either"version = "1.6.1"source = "registry+https://github.com/rust-lang/crates.io-index"[[package]]name = "failure"version = "0.1.8"source = "registry+https://github.com/rust-lang/crates.io-index"dependencies = ["backtrace 0.3.51 (registry+https://github.com/rust-lang/crates.io-index)","failure_derive 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",][[package]]name = "failure_derive"version = "0.1.8"source = "registry+https://github.com/rust-lang/crates.io-index"dependencies = ["proc-macro2 1.0.24 (registry+https://github.com/rust-lang/crates.io-index)","quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)","syn 1.0.42 (registry+https://github.com/rust-lang/crates.io-index)","synstructure 0.12.4 (registry+https://github.com/rust-lang/crates.io-index)",][[package]]name = "getrandom"version = "0.1.15"source = "registry+https://github.com/rust-lang/crates.io-index"dependencies = ["cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)","libc 0.2.78 (registry+https://github.com/rust-lang/crates.io-index)","wasi 0.9.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)",][[package]]name = "gimli"version = "0.22.0"source = "registry+https://github.com/rust-lang/crates.io-index"[[package]]name = "heck"version = "0.3.1"source = "registry+https://github.com/rust-lang/crates.io-index"dependencies = ["unicode-segmentation 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)",][[package]]name = "hermit-abi"version = "0.1.16"source = "registry+https://github.com/rust-lang/crates.io-index"dependencies = ["libc 0.2.78 (registry+https://github.com/rust-lang/crates.io-index)",][[package]]name = "itertools"version = "0.8.2"source = "registry+https://github.com/rust-lang/crates.io-index"dependencies = ["either 1.6.1 (registry+https://github.com/rust-lang/crates.io-index)",][[package]]name = "lazy_static"version = "1.4.0"source = "registry+https://github.com/rust-lang/crates.io-index"[[package]]name = "libc"version = "0.2.78"source = "registry+https://github.com/rust-lang/crates.io-index"[[package]]name = "log"version = "0.4.11"source = "registry+https://github.com/rust-lang/crates.io-index"dependencies = ["cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",][[package]]name = "miniz_oxide"version = "0.4.2"source = "registry+https://github.com/rust-lang/crates.io-index"dependencies = ["adler 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)","autocfg 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",][[package]]name = "num_cpus"version = "1.13.0"source = "registry+https://github.com/rust-lang/crates.io-index"dependencies = ["hermit-abi 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)","libc 0.2.78 (registry+https://github.com/rust-lang/crates.io-index)",][[package]]name = "object"version = "0.20.0"source = "registry+https://github.com/rust-lang/crates.io-index"[[package]]name = "ppv-lite86"version = "0.2.9"source = "registry+https://github.com/rust-lang/crates.io-index"[[package]]name = "proc-macro-error"version = "1.0.4"source = "registry+https://github.com/rust-lang/crates.io-index"dependencies = ["proc-macro-error-attr 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)","proc-macro2 1.0.24 (registry+https://github.com/rust-lang/crates.io-index)","quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)","syn 1.0.42 (registry+https://github.com/rust-lang/crates.io-index)","version_check 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)",][[package]]name = "proc-macro-error-attr"version = "1.0.4"source = "registry+https://github.com/rust-lang/crates.io-index"dependencies = ["proc-macro2 1.0.24 (registry+https://github.com/rust-lang/crates.io-index)","quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)","version_check 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)",][[package]]name = "proc-macro2"version = "0.4.30"source = "registry+https://github.com/rust-lang/crates.io-index"dependencies = ["unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",][[package]]name = "proc-macro2"version = "1.0.24"source = "registry+https://github.com/rust-lang/crates.io-index"dependencies = ["unicode-xid 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",][[package]]name = "quote"version = "0.6.13"source = "registry+https://github.com/rust-lang/crates.io-index"dependencies = ["proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)",][[package]]name = "quote"version = "1.0.7"source = "registry+https://github.com/rust-lang/crates.io-index"dependencies = ["proc-macro2 1.0.24 (registry+https://github.com/rust-lang/crates.io-index)",][[package]]name = "rand"version = "0.7.3"source = "registry+https://github.com/rust-lang/crates.io-index"dependencies = ["getrandom 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)","libc 0.2.78 (registry+https://github.com/rust-lang/crates.io-index)","rand_chacha 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)","rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)","rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",][[package]]name = "rand_chacha"version = "0.2.2"source = "registry+https://github.com/rust-lang/crates.io-index"dependencies = ["ppv-lite86 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)","rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",][[package]]name = "rand_core"version = "0.5.1"source = "registry+https://github.com/rust-lang/crates.io-index"dependencies = ["getrandom 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)",][[package]]name = "rand_hc"version = "0.2.0"source = "registry+https://github.com/rust-lang/crates.io-index"dependencies = ["rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",][[package]]name = "rtarot"version = "0.1.0"dependencies = ["failure 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)","itertools 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)","lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)","log 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)","num_cpus 1.13.0 (registry+https://github.com/rust-lang/crates.io-index)","rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)","structopt 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)","strum 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)","strum_macros 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)",][[package]]name = "rustc-demangle"version = "0.1.16"source = "registry+https://github.com/rust-lang/crates.io-index"[[package]]name = "strsim"version = "0.8.0"source = "registry+https://github.com/rust-lang/crates.io-index"[[package]]name = "structopt"version = "0.3.18"source = "registry+https://github.com/rust-lang/crates.io-index"dependencies = ["clap 2.33.3 (registry+https://github.com/rust-lang/crates.io-index)","lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)","structopt-derive 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)",][[package]]name = "structopt-derive"version = "0.4.11"source = "registry+https://github.com/rust-lang/crates.io-index"dependencies = ["heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)","proc-macro-error 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)","proc-macro2 1.0.24 (registry+https://github.com/rust-lang/crates.io-index)","quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)","syn 1.0.42 (registry+https://github.com/rust-lang/crates.io-index)",][[package]]name = "strum"version = "0.15.0"source = "registry+https://github.com/rust-lang/crates.io-index"[[package]]name = "strum_macros"version = "0.15.0"source = "registry+https://github.com/rust-lang/crates.io-index"dependencies = ["heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)","proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)","quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)","syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)",][[package]]name = "syn"version = "0.15.44"source = "registry+https://github.com/rust-lang/crates.io-index"dependencies = ["proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)","quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)","unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",][[package]]name = "syn"version = "1.0.42"source = "registry+https://github.com/rust-lang/crates.io-index"dependencies = ["proc-macro2 1.0.24 (registry+https://github.com/rust-lang/crates.io-index)","quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)","unicode-xid 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",][[package]]name = "synstructure"version = "0.12.4"source = "registry+https://github.com/rust-lang/crates.io-index"dependencies = ["proc-macro2 1.0.24 (registry+https://github.com/rust-lang/crates.io-index)","quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)","syn 1.0.42 (registry+https://github.com/rust-lang/crates.io-index)","unicode-xid 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",][[package]]name = "textwrap"version = "0.11.0"source = "registry+https://github.com/rust-lang/crates.io-index"dependencies = ["unicode-width 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",][[package]]name = "unicode-segmentation"version = "1.6.0"source = "registry+https://github.com/rust-lang/crates.io-index"[[package]]name = "unicode-width"version = "0.1.8"source = "registry+https://github.com/rust-lang/crates.io-index"[[package]]name = "unicode-xid"version = "0.1.0"source = "registry+https://github.com/rust-lang/crates.io-index"[[package]]name = "unicode-xid"version = "0.2.1"source = "registry+https://github.com/rust-lang/crates.io-index"[[package]]name = "vec_map"version = "0.8.2"source = "registry+https://github.com/rust-lang/crates.io-index"[[package]]name = "version_check"version = "0.9.2"source = "registry+https://github.com/rust-lang/crates.io-index"[[package]]name = "wasi"version = "0.9.0+wasi-snapshot-preview1"source = "registry+https://github.com/rust-lang/crates.io-index"[[package]]name = "winapi"version = "0.3.9"source = "registry+https://github.com/rust-lang/crates.io-index"dependencies = ["winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)","winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",][[package]]name = "winapi-i686-pc-windows-gnu"version = "0.4.0"source = "registry+https://github.com/rust-lang/crates.io-index"[[package]]name = "winapi-x86_64-pc-windows-gnu"version = "0.4.0"source = "registry+https://github.com/rust-lang/crates.io-index"[metadata]"checksum addr2line 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1b6a2d3371669ab3ca9797670853d61402b03d0b4b9ebf33d677dfa720203072""checksum adler 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ee2a4ec343196209d6594e19543ae87a39f96d5534d7174822a3ad825dd6ed7e""checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b""checksum atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8""checksum autocfg 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a""checksum backtrace 0.3.51 (registry+https://github.com/rust-lang/crates.io-index)" = "ec1931848a574faa8f7c71a12ea00453ff5effbb5f51afe7f77d7a48cace6ac1""checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693""checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822""checksum clap 2.33.3 (registry+https://github.com/rust-lang/crates.io-index)" = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002""checksum either 1.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457""checksum failure 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "d32e9bd16cc02eae7db7ef620b392808b89f6a5e16bb3497d159c6b92a0f4f86""checksum failure_derive 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "aa4da3c766cd7a0db8242e326e9e4e081edd567072893ed320008189715366a4""checksum getrandom 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)" = "fc587bc0ec293155d5bfa6b9891ec18a1e330c234f896ea47fbada4cadbe47e6""checksum gimli 0.22.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aaf91faf136cb47367fa430cd46e37a788775e7fa104f8b4bcb3861dc389b724""checksum heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205""checksum hermit-abi 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "4c30f6d0bc6b00693347368a67d41b58f2fb851215ff1da49e90fe2c5c667151""checksum itertools 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f56a2d0bc861f9165be4eb3442afd3c236d8a98afd426f65d92324ae1091a484""checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646""checksum libc 0.2.78 (registry+https://github.com/rust-lang/crates.io-index)" = "aa7087f49d294270db4e1928fc110c976cd4b9e5a16348e0a1df09afa99e6c98""checksum log 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)" = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b""checksum miniz_oxide 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c60c0dfe32c10b43a144bad8fc83538c52f58302c92300ea7ec7bf7b38d5a7b9""checksum num_cpus 1.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3""checksum object 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1ab52be62400ca80aa00285d25253d7f7c437b7375c4de678f5405d3afe82ca5""checksum ppv-lite86 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)" = "c36fa947111f5c62a733b652544dd0016a43ce89619538a8ef92724a6f501a20""checksum proc-macro-error 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c""checksum proc-macro-error-attr 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869""checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759""checksum proc-macro2 1.0.24 (registry+https://github.com/rust-lang/crates.io-index)" = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71""checksum quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1""checksum quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37""checksum rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03""checksum rand_chacha 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402""checksum rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19""checksum rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c""checksum rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783""checksum strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a""checksum structopt 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)" = "a33f6461027d7f08a13715659b2948e1602c31a3756aeae9378bfe7518c72e82""checksum structopt-derive 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)" = "c92e775028122a4b3dd55d58f14fc5120289c69bee99df1d117ae30f84b225c9""checksum strum 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e5d1c33039533f051704951680f1adfd468fd37ac46816ded0d9ee068e60f05f""checksum strum_macros 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)" = "47cd23f5c7dee395a00fa20135e2ec0fffcdfa151c56182966d7a3261343432e""checksum syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)" = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5""checksum syn 1.0.42 (registry+https://github.com/rust-lang/crates.io-index)" = "9c51d92969d209b54a98397e1b91c8ae82d8c87a7bb87df0b29aa2ad81454228""checksum synstructure 0.12.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b834f2d66f734cb897113e34aaff2f1ab4719ca946f9a7358dba8f8064148701""checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060""checksum unicode-segmentation 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0""checksum unicode-width 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3""checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc""checksum unicode-xid 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564""checksum vec_map 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191""checksum version_check 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed""checksum wasi 0.9.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)" = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519""checksum winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419""checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6""checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"