Skip to content

Instantly share code, notes, and snippets.

@ryanmr

ryanmr/wargame.rs

Last active Aug 29, 2015
Embed
What would you like to do?
extern crate time;
use std::fmt;
use time::precise_time_ns;
use std::rand::{task_rng, Rng};
#[deriving(Clone)]
enum Value {
Two,
Three,
Four,
Five,
Six,
Seven,
Eight,
Nine,
Ten,
Jack,
Queen,
King,
Ace
}
impl fmt::Show for Value {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let name = match *self {
Value::Two => "2",
Value::Three => "3",
Value::Four => "4",
Value::Five => "5",
Value::Six => "6",
Value::Seven => "7",
Value::Eight => "8",
Value::Nine => "9",
Value::Ten => "10",
Value::Jack => "Jack",
Value::Queen => "Queen",
Value::King => "King",
Value::Ace => "Ace",
};
write!(f, "{}", name)
}
}
#[deriving(Clone)]
enum Suit {
Clubs,
Hearts,
Diamonds,
Spades
}
impl fmt::Show for Suit {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let name = match *self {
Suit::Clubs => "Clubs",
Suit::Hearts => "Hearts",
Suit::Diamonds => "Diamonds",
Suit::Spades => "Spades",
};
write!(f, "{}", name)
}
}
#[deriving(Clone)]
struct Card {
value: Value,
suit: Suit
}
impl Card {
fn new(value: Value, suit: Suit) -> Card {
Card {value: value, suit: suit}
}
fn get_value(&self) -> uint {
let v:uint = match self.value {
Value::Two => 2,
Value::Three => 3,
Value::Four => 4,
Value::Five => 5,
Value::Six => 6,
Value::Seven => 7,
Value::Eight => 8,
Value::Nine => 9,
Value::Ten => 10,
Value::Jack => 11,
Value::Queen => 12,
Value::King => 13,
Value::Ace => 14,
};
return v;
}
}
impl fmt::Show for Card {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{} of {}", self.value, self.suit)
}
}
/*
Allows direct comparison of cards.
Annoying amount of code to implement something so straight forward.
*/
impl PartialEq for Card {
fn eq(&self, other: &Card) -> bool {
(self.get_value() - other.get_value()) == 0
}
}
impl PartialOrd for Card {
fn lt(&self, other: &Card) -> bool {
match self.cmp(other) { Less => true, _ => false}
}
fn partial_cmp(&self, other: &Card) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Eq for Card {}
impl Ord for Card {
fn cmp(&self, other: &Card) -> Ordering {
let v1 = self.get_value();
let v2 = other.get_value();
if v1 < v2 {return Less;}
if v1 > v2 {return Greater;}
return Equal;
}
}
#[deriving(Clone)]
struct Deck(Vec<Card>);
impl Deck {
/*
Makes a fresh deck of 52 regular cards.
*/
fn new_fresh_deck() -> Deck {
let mut cards:Vec<Card> = Vec::with_capacity(52);
for suit in [Suit::Spades, Suit::Hearts, Suit::Diamonds, Suit::Clubs].iter() {
for value in [
Value::Two, Value::Three, Value::Four,
Value::Five, Value::Six, Value::Seven,
Value::Eight, Value::Nine, Value::Ten,
Value::Jack, Value::Queen, Value::King,
Value::Ace
].iter() {
/*
wargame.rs:151:26: 151:32 error: cannot move out of dereference of `&`-pointer
wargame.rs:151 cards.push(Card::new(*value, *suit));
*/
cards.push(Card::new(*value, *suit));
}
}
Deck(cards)
}
fn new() -> Deck {
let mut cards:Vec<Card> = Vec::with_capacity(52);
Deck(cards)
}
fn split(&mut self) -> Deck {
let &Deck(ref mut cards) = self;
let length = cards.len();
let half = length / 2u;
// a vector(52) for awaiting cards
let mut _cards:Vec<Card> = Vec::with_capacity(52);
for _ in range(0, half) {
let c:Card = match cards.pop() {
None => continue,
Some(v) => v
};
_cards.push(c);
}
// returns a new deck
Deck(_cards)
}
fn shuffle(&mut self) {
let &Deck(ref mut cards) = self;
let mut rng = std::rand::task_rng();
rng.shuffle(cards.as_mut_slice());
}
fn length(&mut self) -> uint {
let &Deck(ref mut cards) = self;
return cards.len()
}
fn has_cards(&mut self) -> bool {
let &Deck(ref mut cards) = self;
cards.len() > 0u
}
/*
Get the card at the top of the deck.
*/
fn get_card(&mut self) -> Card {
let &Deck(ref mut cards) = self;
/*
wargame.rs:206:3: 206:11 error: cannot move out of dereference (dereference is implicit, due to indexing)
wargame.rs:206 cards[0]
*/
cards[0]
}
/*
Remove the card from the top of the deck.
*/
fn give_card(&mut self, deck: &mut Deck) -> () {
let &Deck(ref mut cards) = self;
let &Deck(ref mut cards2) = deck;
let card:Card = match cards.remove(0) {
None => return (),
Some(c) => c
};
cards2.push(card)
}
}
impl fmt::Show for Deck {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let &Deck(ref cards) = self;
// a better method might be map to connect these strings
let mut text = String::new();
let mut i = 0;
for card in cards.iter() {
text.push_str(format!("{}", card).as_slice());
i+=1;
if i < cards.capacity() {
text.push_str(", ");
}
}
write!(f, "{}", text)
}
}
fn log(s: String) {
let debug = false;
if ( debug ) {
println!("{}", s);
}
}
fn game() {
let mut player1 = Deck::new_fresh_deck();
player1.shuffle();
let mut player2 = player1.split();
let mut turns = 0u;
'base: while player1.has_cards() && player2.has_cards() {
turns = turns + 1;
log(format!("Turn #{}", turns));
log(format!("P1 {}; P2 {}", player1.length(), player2.length()));
let mut winner:Deck = Deck::new();
let mut card1:Card = player1.get_card();
let mut card2:Card = player2.get_card();
player1.give_card(&mut winner);
player2.give_card(&mut winner);
log(format!("P1: {}; P2: {}", card1, card2));
if card1 == card2 {
let mut wars = 0u;
'war: while {
log(format!("P1: {} = P2: {}", card1, card2));
if player1.length() < 4 || player2.length() < 4 {
log(format!("Not enough cards for war!"));
break 'base;
}
wars = wars + 1;
log(format!("War #{}", wars));
// each player provides 3 cards to the winner
for _ in range(0, 3u) {
player1.give_card(&mut winner);
player2.give_card(&mut winner);
}
// get the top cards
card1 = player1.get_card();
card2 = player2.get_card();
// send the top cards to the winner deck
player1.give_card(&mut winner);
player2.give_card(&mut winner);
if card1 < card2 {
log(format!("P1: {} < P2: {}; W {}", card1, card2, winner.length()));
winner.shuffle();
for _ in range(0, winner.length()) {
winner.give_card(&mut player2);
}
} else if card1 > card2 {
log(format!("P1: {} > P2: {}; W {}", card1, card2, winner.length()));
winner.shuffle();
for _ in range(0, winner.length()) {
winner.give_card(&mut player1);
}
} else {
// perform another war
// the cards are equal
}
// this must be the last line to emulate the do-while structure
// please, tell me how to convert this into a non-hack
// and it shall be done.
card1 == card2
} {}
log(format!("War has ended"));
} else if card1 < card2 {
log(format!("P1: {} < P2: {}; W {}", card1, card2, winner.length()));
winner.shuffle();
for _ in range(0, winner.length()) {
winner.give_card(&mut player2);
}
} else if card1 > card2 {
log(format!("P1: {} > P2: {}; W {}", card1, card2, winner.length()));
winner.shuffle();
for _ in range(0, winner.length()) {
winner.give_card(&mut player1);
}
}
}
log(format!("Total turns: {}", turns));
log(format!("P1: {}; P2: {}", player1.length(), player2.length()));
}
fn backprint(s: String) {
print!("\r{}", s);
}
fn multi() {
// how many tasks should we run
// i.e. the level of concurrency
let tasks:uint = 8;
let mut terminate_senders = Vec::<Sender<uint>>::new();
let mut termination_receivers = Vec::<Receiver<uint>>::new();
let mut completion_receivers = Vec::<Receiver<uint>>::new();
for i in range(0, tasks) {
// bind various channel ends to the arrays above
// or into the closure of proc() below for
// each task's use
let (tx, rx): (Sender<uint>, Receiver<uint>) = channel();
let (ctx, crx): (Sender<uint>, Receiver<uint>) = channel();
let (ttx, trx): (Sender<uint>, Receiver<uint>) = channel();
terminate_senders.push(ctx);
termination_receivers.push(trx);
completion_receivers.push(rx);
// this starts the task,
// which may or may not be a thread
spawn(move || {
let task_id = i;
let mut iterations = 0;
// infinitely loop the games,
// second back the iteration count
loop {
game(); // simulation of game
iterations = iterations + 1;
tx.send(iterations);
let result = crx.try_recv();
match result {
Ok(r) => {
if r == 1 {
// break out of this loop
break;
}
},
Err(e) => {
// there are no errors here
}
}
}
// send the termination signal
ttx.send(1);
});
}
let mut phase = 1u;
let mut total_games = 0u64;
let start_time = precise_time_ns();
let mut current_time = precise_time_ns();
let mut test_duration = 0f64;
// 1 minute in nanoseconds
// let prime_time = 500000000;
// let prime_time = 10000000000;
let prime_time = 60000000000;
let maximum_tests = 100u;
let percent_variation = 0.0001f64;
let MS = 1000000u64;
let NS = 1000000000u64;
let mut tests = 0;
let mut elasped_time = 0;
let mut last_time = 0u64;
let mut test_time = 0u64;
let mut speed = 0f64;
let mut rate = 0f64;
let mut rate_low = 0f64;
let mut rate_high = 0f64;
let mut percent_rate = 0f64;
let mut test_started = false;
// the monitor loop collects the total game count
// and the elasped time so far
println!("\n{}. prime time has begun", phase); phase = 2;
'monitor: loop {
// the sum of all the tasks should be found here
total_games = 0;
for i in range(0, tasks) {
let received = completion_receivers[i].recv() as u64;
total_games = total_games + received;
}
// time calculations
current_time = precise_time_ns();
elasped_time = current_time - start_time;
// NANOSECONDS per GAME
rate = elasped_time as f64 / total_games as f64;
// GAMES per NANOSECOND
speed = 1f64 / rate as f64;
let speed_v = speed * MS as f64;
// the priming phase
if !test_started && elasped_time >= prime_time {
test_started = true;
phase = 3;
println!("\n{}. prime time has is over", phase);
phase = 4;
} else if test_started && elasped_time >= test_time {
// testing phase
if rate_low < rate && rate < rate_high || tests >= maximum_tests {
// end the monitor infinite loop
break 'monitor;
} else {
// calculate the details for the next testing phase
test_duration = speed_v + 1f64;
test_time = elasped_time + (test_duration * NS as f64) as u64;
percent_rate = rate * percent_variation;
rate_low = rate - percent_rate;
rate_high = rate + percent_rate;
tests = tests + 1;
}
}
// backprint(format!("l = {}, r = {}; h = {}; pr = {}; td = {}; tt = {}", rate_low, rate, rate_high, percent_rate, test_duration, test_time));
backprint(format!("{}. et = {}s, rate = {} ms/g; speedv = {} g/ms; total = {}\t", phase, elasped_time / NS, rate / MS as f64, speed_v, total_games));
}
// cleanup after 'monitor has ended
for i in range(0, tasks) {
terminate_senders[i].send(1);
}
let mut end_collection = 0u;
'end:loop {
for i in range(0, tasks) {
end_collection = end_collection + termination_receivers[i].recv();
}
if end_collection == tasks {
phase = 5;
println!("\n{}. {} tasks stopped", phase, end_collection);
break 'end;
}
}
}
fn main() {
// view the source code on this gist
// https://gist.github.com/ryanmr/46097dc63c1ccf833f52
multi();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment