Updated Tests
- changed auction - modified tests for auction - fixed some dead code in lists
This commit is contained in:
@@ -38,7 +38,8 @@ impl<T:Copy> DoublyPointedList<T> {
|
|||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
use std::{
|
use std::{
|
||||||
cell::{RefCell, RefMut}, fmt::Debug, mem, rc::Rc
|
cell::RefCell,
|
||||||
|
rc::Rc,
|
||||||
};
|
};
|
||||||
|
|
||||||
type Pointer<T> = Option<Rc<RefCell<T>>>;
|
type Pointer<T> = Option<Rc<RefCell<T>>>;
|
||||||
|
|||||||
@@ -37,23 +37,16 @@ impl<T> DoublyPointedList<T> {
|
|||||||
pub fn get(& self, n:i32) -> Option<T>
|
pub fn get(& self, n:i32) -> Option<T>
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
use std::{
|
use std::{cell::RefCell, mem, rc::Rc};
|
||||||
borrow::Borrow,
|
|
||||||
cell::{RefCell, RefMut},
|
|
||||||
mem,
|
|
||||||
rc::Rc,
|
|
||||||
};
|
|
||||||
|
|
||||||
type Pointer<T> = Option<Rc<RefCell<T>>>;
|
type Pointer<T> = Option<Rc<RefCell<T>>>;
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct LinkedList<T> {
|
pub struct LinkedList<T> {
|
||||||
size: usize,
|
size: usize,
|
||||||
head: Pointer<Node<T>>,
|
head: Pointer<Node<T>>,
|
||||||
tail: Pointer<Node<T>>,
|
tail: Pointer<Node<T>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
struct Node<T> {
|
struct Node<T> {
|
||||||
element: T,
|
element: T,
|
||||||
next: Pointer<Self>,
|
next: Pointer<Self>,
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ use rand::{Rng, SeedableRng};
|
|||||||
use rand_pcg::Pcg32;
|
use rand_pcg::Pcg32;
|
||||||
use std::{
|
use std::{
|
||||||
collections::{HashMap, HashSet, VecDeque},
|
collections::{HashMap, HashSet, VecDeque},
|
||||||
|
fmt::Debug,
|
||||||
rc::Rc,
|
rc::Rc,
|
||||||
sync::{
|
sync::{
|
||||||
mpsc::{self, Receiver, Sender},
|
mpsc::{self, Receiver, Sender},
|
||||||
@@ -35,13 +36,19 @@ use std::{
|
|||||||
* cifra e’ statto venduto e a quale partecipante e’ stato assegnato.
|
* cifra e’ statto venduto e a quale partecipante e’ stato assegnato.
|
||||||
* I partecipanti leggono questa informazione.
|
* I partecipanti leggono questa informazione.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/// Struttura che indica un prodotto da bandire all'asta.\
|
||||||
|
/// Il Prodotto ha un nome, un prezzo di partenza e una riserva che se non
|
||||||
|
/// raggiunta implica il fallimento dell'asta per questo oggeto.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Product {
|
pub struct Product {
|
||||||
name: String,
|
pub name: String,
|
||||||
price: f32,
|
pub price: f32,
|
||||||
reserve: f32,
|
pub reserve: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Richieste che vengono inviate dal Banditore a tutti i Partecipanti.\
|
||||||
|
/// Possono essere di vario tipo e indicano un cambiamento di stato dell'asta corrente.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
enum AuctionRequest {
|
enum AuctionRequest {
|
||||||
AuctionStart(String, f32),
|
AuctionStart(String, f32),
|
||||||
@@ -52,6 +59,8 @@ enum AuctionRequest {
|
|||||||
Stop,
|
Stop,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Risposte che vengono inviate dai Partecipanti dell'asta al Banditore.\
|
||||||
|
/// Qui si può indicare solamente se si vuole continuare o meno all'asta.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
enum AuctionResponse {
|
enum AuctionResponse {
|
||||||
NotInterested(String),
|
NotInterested(String),
|
||||||
@@ -63,30 +72,55 @@ type ReciveRequest = Receiver<AuctionRequest>;
|
|||||||
type SendResponse = Sender<AuctionResponse>;
|
type SendResponse = Sender<AuctionResponse>;
|
||||||
type ReciveResponse = Receiver<AuctionResponse>;
|
type ReciveResponse = Receiver<AuctionResponse>;
|
||||||
|
|
||||||
|
/// Asta che comprende più prodotti.\
|
||||||
|
/// La creazione di un'asta avrà sempre un Banditore, ma per i partecipandi bisogna
|
||||||
|
/// utilizzare l'apposita funzione per l'aggiunta.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Auction {
|
||||||
|
auctioneer: Auctioneer,
|
||||||
|
participants: Vec<Participant>,
|
||||||
|
sender: SendResponse,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Banditore di un'asta che modera i partecipanti, li aggiorna sul cambiamento del prezzo
|
||||||
|
/// e alla fine decide il vincitore del'asta nel caso in cui non ci siano altri partecipanti.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct Auctioneer {
|
struct Auctioneer {
|
||||||
products: VecDeque<Product>,
|
products: VecDeque<Product>,
|
||||||
currents: HashSet<String>,
|
currents: HashSet<String>,
|
||||||
participants: HashMap<String, SendRequest>,
|
participants: HashMap<String, SendRequest>,
|
||||||
recive: ReciveResponse,
|
recive: ReciveResponse,
|
||||||
|
log: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Partecipante ad un'asta che ha un tot di soldi a disposizione.\
|
||||||
|
/// Ogni partecipante deve avere una strategia che permette di scegliere cosa fare nel caso
|
||||||
|
/// in cui il prezzo di un oggetto aumenta.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct Participant {
|
struct Participant {
|
||||||
name: String,
|
name: String,
|
||||||
money: f32,
|
money: f32,
|
||||||
rng: Arc<Mutex<Pcg32>>,
|
strategy: Box<dyn Strategy>,
|
||||||
products_won: Vec<Product>,
|
products_won: Vec<Product>,
|
||||||
sender: SendResponse,
|
sender: SendResponse,
|
||||||
recive: ReciveRequest,
|
recive: ReciveRequest,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
/// Trait che indica una possibile Strategia per un partecipante.\
|
||||||
pub struct Auction {
|
/// Il trait è stato introdotto nel caso in cui si voglia modificare il comportamento di un partecipante.
|
||||||
auctioneer: Auctioneer,
|
/// Per essere implementeto il trait ha bisogno di una sola funzione, dato che la funzione di
|
||||||
participants: Vec<Participant>,
|
/// start_auction può essere ignorata.
|
||||||
sender: SendResponse,
|
pub trait Strategy: Send + Debug {
|
||||||
rng: Arc<Mutex<Pcg32>>,
|
/// Funzione utilizzata per poter indicare alla strategia che è iniziata una nuova asta.\
|
||||||
|
/// Questo è utile nel caso in cui si voglia creare una strategia che tenga conto dello
|
||||||
|
/// stato in cui si trova l'asta.\
|
||||||
|
/// Nel caso in cui interessa fare solo una strategia stateless allora si può ignorare questa funzione.
|
||||||
|
fn start_auction(&mut self, product: String) {}
|
||||||
|
/// Questa funzione ritorna un nuovo valore nel caso in cui, per la strategia, si voglia continuare
|
||||||
|
/// a provare a vincere l'asta.\
|
||||||
|
/// Nel caso in cui ci si voglia ritirare, allora il valore di ritorno dovrà essere None.
|
||||||
|
/// Nel caso in cui il valore di ritorno supera total_money allora si verrà ritirati dall'asta.
|
||||||
|
fn updated_price(&mut self, total_money: f32, price: f32) -> Option<f32>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Product {
|
impl Product {
|
||||||
@@ -100,27 +134,38 @@ impl Product {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Participant {
|
impl Participant {
|
||||||
pub fn auction_loop(&mut self) {
|
/// Funzione utilizzata per il loop dell'asta e che deve essere fatta partire
|
||||||
|
/// PRIMA di aver fatto partire il loop per il banditore.\
|
||||||
|
/// Questo perchè altrimenti si può incorrere in problemi quali il partecipante
|
||||||
|
/// non abilitato all'asta.
|
||||||
|
pub fn auction_loop(&mut self, log: bool) {
|
||||||
while let Ok(result) = self.recive.recv() {
|
while let Ok(result) = self.recive.recv() {
|
||||||
match result {
|
match result {
|
||||||
AuctionRequest::AuctionStart(_, price) => self.updated_price(price),
|
AuctionRequest::AuctionStart(product, price) => {
|
||||||
|
self.strategy.start_auction(product);
|
||||||
|
self.updated_price(price)
|
||||||
|
}
|
||||||
|
AuctionRequest::AuctionOver(prod) => {
|
||||||
|
if log {
|
||||||
|
println!(
|
||||||
|
"Participant {:?} money:{:?}, won: {:?}",
|
||||||
|
self.name, self.money, self.products_won
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
AuctionRequest::UpdatedPrice(price) => self.updated_price(price),
|
AuctionRequest::UpdatedPrice(price) => self.updated_price(price),
|
||||||
AuctionRequest::YouAreWinning(price) => self.winning_for(price),
|
AuctionRequest::YouAreWinning(price) => self.winning_for(price),
|
||||||
AuctionRequest::YouWon(prod) => self.won_product(prod),
|
AuctionRequest::YouWon(prod) => self.won_product(prod),
|
||||||
AuctionRequest::AuctionOver(prod) => println!(
|
|
||||||
"Participant {:?} money:{:?}, won: {:?}",
|
|
||||||
self.name, self.money, self.products_won
|
|
||||||
),
|
|
||||||
AuctionRequest::Stop => return,
|
AuctionRequest::Stop => return,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn updated_price(&self, price: f32) {
|
fn updated_price(&mut self, price: f32) {
|
||||||
let name = self.name.clone();
|
let name = self.name.clone();
|
||||||
let response = if price <= self.money {
|
let up_price = self.strategy.updated_price(self.money, price);
|
||||||
let up = self.rng.lock().unwrap().gen_range(price..self.money);
|
let response = if matches!(up_price, Some(up) if up <= self.money) {
|
||||||
AuctionResponse::WantForPrice(name, up)
|
AuctionResponse::WantForPrice(name, up_price.unwrap())
|
||||||
} else {
|
} else {
|
||||||
AuctionResponse::NotInterested(name)
|
AuctionResponse::NotInterested(name)
|
||||||
};
|
};
|
||||||
@@ -138,7 +183,12 @@ impl Participant {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Auctioneer {
|
impl Auctioneer {
|
||||||
pub fn auction_loop(&mut self) {
|
/// Funzione utilizzata per il loop dell'asta e che deve essere fatta partire
|
||||||
|
/// DOPO aver fatto partire tutti i loop dei partecipanti su dei thread diversi da questo.\
|
||||||
|
/// Questo per evitare che si possano avere situazioni di deadlock o semplicemente problemi
|
||||||
|
/// nella comunicazione con i thread dei partecipanti.
|
||||||
|
pub fn auction_loop(&mut self) -> VecDeque<(Product, Option<String>)> {
|
||||||
|
let mut results = VecDeque::new();
|
||||||
while let Some(mut product) = self.products.pop_front() {
|
while let Some(mut product) = self.products.pop_front() {
|
||||||
self.new_product(product.name.clone(), product.price);
|
self.new_product(product.name.clone(), product.price);
|
||||||
let mut winner = None;
|
let mut winner = None;
|
||||||
@@ -163,10 +213,12 @@ impl Auctioneer {
|
|||||||
winner = None
|
winner = None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
results.push_back((product.clone(), winner.clone()));
|
||||||
self.end_product(winner, product);
|
self.end_product(winner, product);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.end_auction();
|
self.end_auction();
|
||||||
|
results
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new_product(&mut self, name: String, price: f32) {
|
fn new_product(&mut self, name: String, price: f32) {
|
||||||
@@ -208,7 +260,10 @@ impl Auctioneer {
|
|||||||
fn recive(&mut self) -> Option<(String, f32)> {
|
fn recive(&mut self) -> Option<(String, f32)> {
|
||||||
match self.recive.recv() {
|
match self.recive.recv() {
|
||||||
Ok(response) => {
|
Ok(response) => {
|
||||||
println!("Response -> {:?}", response);
|
if self.log {
|
||||||
|
println!("Response -> {:?}", response)
|
||||||
|
};
|
||||||
|
|
||||||
match response {
|
match response {
|
||||||
AuctionResponse::NotInterested(name) => {
|
AuctionResponse::NotInterested(name) => {
|
||||||
self.currents.remove(&name);
|
self.currents.remove(&name);
|
||||||
@@ -235,36 +290,48 @@ impl Auctioneer {
|
|||||||
}
|
}
|
||||||
fn send_only(&self, recipient: &String, message: AuctionRequest) {
|
fn send_only(&self, recipient: &String, message: AuctionRequest) {
|
||||||
if let Some(channel) = self.participants.get(recipient) {
|
if let Some(channel) = self.participants.get(recipient) {
|
||||||
println!("Sending to {:?} -> {:?}", recipient, message);
|
if self.log {
|
||||||
|
println!("Sending to {:?} -> {:?}", recipient, message)
|
||||||
|
};
|
||||||
|
|
||||||
channel.send(message);
|
channel.send(message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Auction {
|
impl Auction {
|
||||||
pub fn new(products: VecDeque<Product>, seed: u64) -> Self {
|
/// Crea un'asta con i prodotti inseriti.\
|
||||||
|
/// Nel caso in cui ci siano due prodotti con lo stesso nome allora verrà fatto partire un PANIC.
|
||||||
|
pub fn new(products: VecDeque<Product>) -> Self {
|
||||||
let channel_participant = mpsc::channel();
|
let channel_participant = mpsc::channel();
|
||||||
let auctioneer = Auctioneer {
|
let auctioneer = Auctioneer {
|
||||||
products,
|
products,
|
||||||
currents: HashSet::new(),
|
currents: HashSet::new(),
|
||||||
participants: HashMap::new(),
|
participants: HashMap::new(),
|
||||||
recive: channel_participant.1,
|
recive: channel_participant.1,
|
||||||
|
log: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
auctioneer,
|
auctioneer,
|
||||||
rng: Arc::new(Mutex::new(Pcg32::seed_from_u64(seed))),
|
|
||||||
participants: vec![],
|
participants: vec![],
|
||||||
sender: channel_participant.0,
|
sender: channel_participant.0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_participant(&mut self, name: String, money: f32) {
|
/// Abilita la possibilità di vedere lo scambio dei messaggi tra il banditore d'asta e i vari partecipanti
|
||||||
|
pub fn enable_log(&mut self) {
|
||||||
|
self.auctioneer.log = true
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Aggiunge un partecipante all'asta.\
|
||||||
|
/// Il partecipante deve avere un nome univoco rispetto agli altri altrimenti verrà segnalato con un PANIC
|
||||||
|
pub fn add_participant(&mut self, name: String, money: f32, strategy: Box<dyn Strategy>) {
|
||||||
let channel = mpsc::channel();
|
let channel = mpsc::channel();
|
||||||
let participant = Participant {
|
let participant = Participant {
|
||||||
name: name.clone(),
|
name: name.clone(),
|
||||||
money,
|
money,
|
||||||
rng: self.rng.clone(),
|
strategy,
|
||||||
products_won: vec![],
|
products_won: vec![],
|
||||||
sender: self.sender.clone(),
|
sender: self.sender.clone(),
|
||||||
recive: channel.1,
|
recive: channel.1,
|
||||||
@@ -274,22 +341,16 @@ impl Auction {
|
|||||||
self.participants.push(participant);
|
self.participants.push(participant);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn start(mut self) {
|
/// Inizia l'asta e consuma la struttura.\
|
||||||
while let Some(participant) = self.participants.pop() {
|
/// Ogni partecipante avrà il proprio thread che verrà fatto partire PRIMA del banditore d'asta.\
|
||||||
Self::start_participant(participant);
|
/// Alla fine dell'asta si riceverà in output un vettore con tutti i prodotti e il vincitore nel caso ci sia.\
|
||||||
|
/// Questa chiamata è bloccante, ovvero aspetta finchè l'asta non sarà finita.
|
||||||
|
pub fn start(mut self) -> VecDeque<(Product, Option<String>)> {
|
||||||
|
while let Some(mut participant) = self.participants.pop() {
|
||||||
|
let log = self.auctioneer.log;
|
||||||
|
thread::spawn(move || participant.auction_loop(log));
|
||||||
}
|
}
|
||||||
Self::start_auctioneer(self.auctioneer);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn start_participant(participant: Participant) {
|
self.auctioneer.auction_loop()
|
||||||
thread::spawn(|| {
|
|
||||||
let mut part = participant;
|
|
||||||
part.auction_loop();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
fn start_auctioneer(auctioneer: Auctioneer) {
|
|
||||||
let mut auct = auctioneer;
|
|
||||||
auct.auction_loop();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -158,9 +158,9 @@ fn test_get() {
|
|||||||
assert_eq!(Some(20), list.get(1));
|
assert_eq!(Some(20), list.get(1));
|
||||||
// [50, 30, 40]
|
// [50, 30, 40]
|
||||||
assert_eq!(Some(30), list.get(1));
|
assert_eq!(Some(30), list.get(1));
|
||||||
// [30, 40]
|
// [50, 40]
|
||||||
assert_eq!(Some(40), list.get(1));
|
assert_eq!(Some(40), list.get(1));
|
||||||
// [30]
|
// [50]
|
||||||
assert_eq!(Some(50), list.get(0));
|
assert_eq!(Some(50), list.get(0));
|
||||||
// []
|
// []
|
||||||
assert_eq!(None, list.get(0));
|
assert_eq!(None, list.get(0));
|
||||||
|
|||||||
@@ -1,6 +1,18 @@
|
|||||||
use esercizi::es09_auction::{Auction, Product};
|
use esercizi::es09_auction::{Auction, Product, Strategy};
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct UpMax;
|
||||||
|
impl Strategy for UpMax {
|
||||||
|
fn updated_price(&mut self, total_money: f32, price: f32) -> Option<f32> {
|
||||||
|
if price <= total_money {
|
||||||
|
Some(total_money)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_auction() {
|
fn test_auction() {
|
||||||
let products = VecDeque::from([
|
let products = VecDeque::from([
|
||||||
@@ -8,12 +20,45 @@ fn test_auction() {
|
|||||||
Product::new("woof", 321.0, 18554.0),
|
Product::new("woof", 321.0, 18554.0),
|
||||||
Product::new("woof2", 31.0, 1854.0),
|
Product::new("woof2", 31.0, 1854.0),
|
||||||
]);
|
]);
|
||||||
let mut auction = Auction::new(products, 0);
|
let mut auction = Auction::new(products);
|
||||||
|
|
||||||
auction.add_participant("th1".to_string(), 4520.0);
|
let name1 = "th1".to_string();
|
||||||
auction.add_participant("th2".to_string(), 6500.0);
|
let name2 = "th2".to_string();
|
||||||
auction.add_participant("th3".to_string(), 15020.0);
|
let name3 = "th3".to_string();
|
||||||
auction.add_participant("th4".to_string(), 8520.0);
|
let name4 = "th4".to_string();
|
||||||
|
|
||||||
auction.start();
|
auction.add_participant(name1.clone(), 4520.0, Box::new(UpMax));
|
||||||
|
auction.add_participant(name2.clone(), 6500.0, Box::new(UpMax));
|
||||||
|
auction.add_participant(name3.clone(), 15020.0, Box::new(UpMax));
|
||||||
|
auction.add_participant(name4.clone(), 8520.0, Box::new(UpMax));
|
||||||
|
|
||||||
|
let mut results = auction.start();
|
||||||
|
assert_eq!(results.len(), 3);
|
||||||
|
|
||||||
|
let auction = results.pop_front();
|
||||||
|
assert!(matches!(auction, Some(_)));
|
||||||
|
let (product, winner) = auction.unwrap();
|
||||||
|
assert_eq!(product.name, "bau".to_string());
|
||||||
|
assert_eq!(product.price, 15020.0);
|
||||||
|
assert_eq!(product.reserve, 1000.0);
|
||||||
|
assert!(matches!(winner, Some(name) if name == name3));
|
||||||
|
|
||||||
|
let auction = results.pop_front();
|
||||||
|
assert!(matches!(auction, Some(_)));
|
||||||
|
let (product, winner) = auction.unwrap();
|
||||||
|
assert_eq!(product.name, "woof".to_string());
|
||||||
|
assert_eq!(product.price, 8520.0);
|
||||||
|
assert_eq!(product.reserve, 18554.0);
|
||||||
|
assert_eq!(winner, None);
|
||||||
|
|
||||||
|
let auction = results.pop_front();
|
||||||
|
assert!(matches!(auction, Some(_)));
|
||||||
|
let (product, winner) = auction.unwrap();
|
||||||
|
assert_eq!(product.name, "woof2".to_string());
|
||||||
|
assert_eq!(product.price, 8520.0);
|
||||||
|
assert_eq!(product.reserve, 1854.0);
|
||||||
|
assert!(matches!(winner, Some(name) if name == name4));
|
||||||
|
|
||||||
|
let auction = results.pop_front();
|
||||||
|
assert!(matches!(auction, None));
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user