Running the simulation

All that's left to do is run warriors in simulation. In the previous chapter, an Individual was responsible for managing its own pmars process. Now, Mars will be responsible for hosting the competitors, which you may have gleaned from the C API. We simply don't have any space in Individual to store something, and by pushing competition into Mars, we can avoid allocation churn on temporary Mars structures.

The compete function that was formerly bound to Individual has now been moved to Mars:

impl Mars {
    /// Competes two Individuals at random locations
    ///
    /// The return of this function indicates the winner. 
/// `Winner::Right(12)` will mean that the 'right' player
/// won 12 more rounds than did 'left'. This may mean they
/// tied 40_000 times or maybe they only played 12 rounds
/// and right won each time. pub fn compete(&mut self, rounds: u16, left: &Individual,
right: &Individual) -> Winner { let mut wins = Winner::Tie; for _ in 0..rounds { let core_size = self.core_size; let half_core = (core_size / 2) - self.max_warrior_length; let upper_core = core_size - self.max_warrior_length; let left_pos = thread_rng().gen_range(0, upper_core); let right_pos = if (left_pos + self.max_warrior_length)
< half_core { thread_rng().gen_range(half_core + 1, upper_core) } else { thread_rng().gen_range(0, half_core) }; wins = wins + self.compete_inner(left, left_pos,
right, right_pos); } tally_fitness(wins); BATTLES.fetch_add(1, Ordering::Relaxed); wins }

The C API requires that we calculate the warrior offsets and take care to not overlap them. The approach taken here is to randomly place the left Individual, determine whether it's in the upper or lower core, and then place the right Individual, taking care with both to not place them past the end of the core. The actual implementation of the competition is compete_inner:

    pub fn compete_inner(
        &mut self,
        left: &Individual,
        left_pos: u16,
        right: &Individual,
        right_pos: u16,
    ) -> Winner {
        let (left_len, left_code) = left.as_ptr();
        let (right_len, right_code) = right.as_ptr();

        let warrior_position_table: Vec<u16> = vec![left_pos, right_pos];
        let mut deaths: Vec<u32> = vec![u32::max_value(), 
u32::max_value()];

We call Individual::as_ptr() -> (u16, *const Instruction) to get a raw view of the chromosome of the Individual and its length. Without this, we've got nothing to pass down to the C functions. warrior_position_table informs MARS which instructions are the start of its competitors. We could search out the START flag in the warriors and place that in warrior_position_table. This is an improvement left for the reader. The deaths table will be populated by the simulator code. If both warriors die during competition, the array will be [0, 1]. The death table is populated with u32::max_value() to make distinguishing no-result from result easy enough. Before starting the competition, we have to clear the simulator—which might be filled with instructions from a previous bout:

        unsafe {
            sim_clear_core(self);

If you pull up the sim_clear_core implementation, you'll find a memset to 0 over core_mem. Recall that DAT is the 0th variant of the Instruction enumeration. Unlike pmars, this simulator must use DAT as a default instruction, but it does make resetting the field very fast. sim_clear_core also clears up the process queue and other C-owned storage. Loading the warriors is a matter of plugging in the information we've already computed:

            assert_eq!(
                0,
                sim_load_warrior(self, left_pos.into(), 
left_code, left_len) ); assert_eq!( 0, sim_load_warrior(self, right_pos.into(),
right_code, right_len) );

If the result is non-zero, that's an indicator that some serious and non-recoverable fault has happened. sim_load_warrior is an array operation, writing the warrior Instructions into core_mem at the defined offsets. We could, very conceivably, rewrite the functions of sim_clear_core and sim_load_warrior in Rust if we wanted to. Finally, field cleared and warriors loaded, we are able to simulate:

            let alive = sim_mw(self, 
warrior_position_table.as_ptr(),
deaths.as_mut_ptr()); assert_ne!(-1, alive); }

The sim_mw function returns the total number of warriors left alive at the end of the simulation run. If this value is -1, there's been some catastrophic, unrecoverable error.

Now, because we have a nice type system to play with, we don't really want to signal results back to the user with a vector of integers. We preserve the Winner type seen

in the previous chapter, doing a quick conversion before returning:

        let left_dead = deaths[0] != u32::max_value();
        let right_dead = deaths[1] != u32::max_value();
        match (left_dead, right_dead) {
            (false, false) | (true, true) => Winner::Tie,
            (true, false) => Winner::Right(1),
            (false, true) => Winner::Left(1),
        }
    }
}

You may have noticed that Winner implements Add, allowing compete to signal how many rounds the warriors won. impl Add for Winner is also in src/mars.rs, should you be curious to see it. As a final sanity test around Mars, we confirm that the Imp will lose to Dwarf more often than other outcomes:

#[cfg(test)]
mod tests {
    use super::*;
    use individual::*;

    #[test]
    fn imp_vs_dwarf() {
        let core_size = 8_000;
        let rounds = 100;
        let mut mars = MarsBuilder::default()
.core_size(core_size).freeze(); let imp = ringers::imp(core_size); let dwarf = ringers::dwarf(core_size); let res = mars.compete(rounds, &imp, &dwarf); println!("RES: {:?}", res); match res { Winner::Left(_) | Winner::Tie => {
panic!("imp should lose to dwarf")
}, Winner::Right(_) => {} } } }

Recall that an Imp can't self-kill, only propagate. The Dwarf bombs core memory at regular, increasing offsets. An Imp versus Dwarf competition, then, is a race between Imp finding Dwarf's instructions and Dwarf dropping a DAT in the path of an Imp.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.188.198.94