//modifies the original arm to have the new instructions
//returns the length of the tape (repetition size)
modifies the original arm to have the new instructions
returns the length of the tape (repetition size)
modifies the original arm to have the new instructions returns the length of the tape ( repetition size )
fn normalize_instructions(
        original: &mut Arm,
        track_map: &TrackMap,
    ) -> Result<usize> {
        use ArmType::*;
        use Instr::*;
        let arm_type = original.arm_type;
        let old_instructions = &original.instruction_tape.instructions;
        let mut instructions = Vec::with_capacity(old_instructions.len() + 8);
        let mut repeat_source = 0;
        let mut repeat_ending = 0;
        let mut any_nonrepeat = false;
        let mut curr = 0;
        let mut track_steps = 0;
        let mut rot_diff = 0;
        let mut len = original.len;
        let mut known_grab = false;
        let mut position_check = original.pos;
        fn get_track_loop_length(track_map: &TrackMap, pos: Pos) -> Option<i32>{
            let mut my_pos = pos;
            let mut loop_length = 0;
            loop{
                let offset = track_map.get(&my_pos)?.plus?;
                my_pos += offset;
                loop_length += 1;
                if my_pos == pos {return Some(loop_length);}
                if loop_length as usize > track_map.len() {return None;}
            }
        }
        let track_loop = get_track_loop_length(track_map, original.pos);
        while curr < old_instructions.len() {
            let instr = old_instructions[curr];
            if !any_nonrepeat && !matches!(instr, Repeat | Empty ){
                any_nonrepeat = true;
                repeat_source = curr;
            }
            if !matches!(instr, Repeat | Reset | Noop) {
                instructions.push(instr);
                curr += 1;
            }
            let mut basic_move = |instr: Instr|->Result<()>{
                match instr{
                    Extend => {
                        if arm_type == Piston && len < 3 {
                            len += 1;
                        }
                    }
                    Retract => {
                        if arm_type == Piston && len > 1 {
                            len -= 1;
                        }
                    }
                    RotateCounterClockwise => {
                        rot_diff += 1;
                    }
                    RotateClockwise => {
                        rot_diff -= 1;
                    }
                    Grab => known_grab = true,
                    Drop => known_grab = false,
                    Forward => {
                        if let Some(track_data) = track_map.get(&position_check){
                            if let Some(offset) = track_data.plus{
                                track_steps += 1;
                                position_check += offset;
                            }
                        }
                    }
                    Back => {
                        if let Some(track_data) = track_map.get(&position_check){
                            if let Some(offset) = track_data.minus{
                                track_steps -= 1;
                                position_check += offset;
                            }
                        }
                    }
                    PivotCounterClockwise | PivotClockwise | Empty => {}
                    Reset | Repeat | Noop => {bail!("Instruction {:?} not basic move!",instr);}
                }
                Ok(())
            };
            match instr {
                Extend|Retract|
                RotateCounterClockwise|RotateClockwise|
                Grab|Drop|
                Forward|Back|
                PivotCounterClockwise|PivotClockwise => {
                    basic_move(instr)?;
                    repeat_ending = curr;
                }
                Empty => {}
                Repeat => {
                    let rep_len = repeat_ending - repeat_source;
                    if rep_len == 0 {
                        instructions.push(Empty);
                        curr += 1;
                    } else {
                        for i in 0..rep_len {
                            let copied_instr = instructions[repeat_source + i];
                            ensure!( i == 0|| old_instructions.get(curr + i).unwrap_or(&Empty) == &Empty,
                                "Repeat instruction {:?} overlaps with {:?} on {}/{}. Repeat {}-{}",
                                copied_instr,old_instructions.get(curr + i),
                                curr,i,repeat_source,repeat_ending
                            );
                            instructions.push(copied_instr);
                            basic_move(copied_instr)?;
                        }
                        curr += rep_len;
                        any_nonrepeat = false;
                        known_grab = false;
                        rot_diff = 0;
                        len = original.len;
                        track_steps = 0;
                        position_check = original.pos;
                    }
                }
                Reset => {
                    let mut reset_vec = Vec::new();
                    if known_grab {
                        reset_vec.push(Drop);
                        known_grab = false;
                    }
                    while len > original.len {
                        reset_vec.push(Retract);
                        len -= 1;
                    }
                    while rot_diff > 3 {rot_diff -= 6;}
                    while rot_diff < -3 {rot_diff += 6;}
                    while rot_diff > 0 {
                        reset_vec.push(RotateClockwise);
                        rot_diff -= 1;
                    }
                    while rot_diff < 0 {
                        reset_vec.push(RotateCounterClockwise);
                        rot_diff += 1;
                    }
                    // look for a path forward on the track that's shorter than
                    // the path backward.
                    if let Some(loop_len) = track_loop{
                        while track_steps > loop_len/2 {
                            track_steps -= loop_len;
                        }
                        while track_steps < -loop_len/2 {
                            track_steps += loop_len
                        }
                    }
                    while track_steps > 0 {
                        reset_vec.push(Back);
                        track_steps -= 1;
                    }
                    while track_steps < 0 {
                        reset_vec.push(Forward);
                        track_steps += 1;
                    }
                    while len < original.len {
                        reset_vec.push(Extend);
                        len += 1;
                    }
                    position_check = original.pos;
                    if reset_vec.len() == 0 {
                        reset_vec.push(Empty)
                    };
                    for i in 0..reset_vec.len() {
                        ensure!(
                            i == 0 || old_instructions.get(curr + i).unwrap_or(&Empty) == &Empty,
                            "Reset instruction {:?} overlaps with {:?} on {}/{} (curr {})",
                            reset_vec[i],
                            old_instructions.get(curr + i),
                            i, reset_vec.len(),
                            curr,
                        );
                        instructions.push(reset_vec[i]);
                    }
                    curr += reset_vec.len();
                    repeat_ending = curr;
                }
                Noop => {
                    instructions.push(Empty);
                    curr += 1;
                }
            };
        }
        instructions.shrink_to_fit();
        let len = instructions.len();
        original.instruction_tape = Tape {
            first: original.instruction_tape.first,
            instructions,
        };
        Ok(len)
    }
fn normalize_instructions ( original : & mut Arm , track_map : & TrackMap , ) -> Result < usize > { use ArmType :: * ; use Instr :: * ; let arm_type = original . arm_type ; let old_instructions = & original . instruction_tape . instructions ; let mut instructions = Vec :: with_capacity ( old_instructions . len ( ) + 8 ) ; let mut repeat_source = 0 ; let mut repeat_ending = 0 ; let mut any_nonrepeat = false ; let mut curr = 0 ; let mut track_steps = 0 ; let mut rot_diff = 0 ; let mut len = original . len ; let mut known_grab = false ; let mut position_check = original . pos ; fn get_track_loop_length ( track_map : & TrackMap , pos : Pos ) -> Option < i32 > { let mut my_pos = pos ; let mut loop_length = 0 ; loop { let offset = track_map . get ( & my_pos ) ? . plus ? ; my_pos += offset ; loop_length += 1 ; if my_pos == pos { return Some ( loop_length ) ; } if loop_length as usize > track_map . len ( ) { return None ; } } } let track_loop = get_track_loop_length ( track_map , original . pos ) ; while curr < old_instructions . len ( ) { let instr = old_instructions [ curr ] ; if ! any_nonrepeat && ! matches ! ( instr Repeat Empty ) { any_nonrepeat = true ; repeat_source = curr ; } if ! matches ! ( instr Repeat Reset Noop ) { instructions . push ( instr ) ; curr += 1 ; } let mut basic_move = | instr : Instr | -> Result < ( ) > { match instr { Extend => { if arm_type == Piston && len < 3 { len += 1 ; } } Retract => { if arm_type == Piston && len > 1 { len -= 1 ; } } RotateCounterClockwise => { rot_diff += 1 ; } RotateClockwise => { rot_diff -= 1 ; } Grab => known_grab = true , Drop => known_grab = false , Forward => { if let Some ( track_data ) = track_map . get ( & position_check ) { if let Some ( offset ) = track_data . plus { track_steps += 1 ; position_check += offset ; } } } Back => { if let Some ( track_data ) = track_map . get ( & position_check ) { if let Some ( offset ) = track_data . minus { track_steps -= 1 ; position_check += offset ; } } } PivotCounterClockwise | PivotClockwise | Empty => { } Reset | Repeat | Noop => { bail ! ( " " instr ) ; } } Ok ( ( ) ) } ; match instr { Extend | Retract | RotateCounterClockwise | RotateClockwise | Grab | Drop | Forward | Back | PivotCounterClockwise | PivotClockwise => { basic_move ( instr ) ? ; repeat_ending = curr ; } Empty => { } Repeat => { let rep_len = repeat_ending - repeat_source ; if rep_len == 0 { instructions . push ( Empty ) ; curr += 1 ; } else { for i in 0 .. rep_len { let copied_instr = instructions [ repeat_source + i ] ; ensure ! ( i 0 old_instructions get ( curr i ) unwrap_or ( Empty ) Empty " " copied_instr old_instructions get ( curr i ) curr i repeat_source repeat_ending ) ; instructions . push ( copied_instr ) ; basic_move ( copied_instr ) ? ; } curr += rep_len ; any_nonrepeat = false ; known_grab = false ; rot_diff = 0 ; len = original . len ; track_steps = 0 ; position_check = original . pos ; } } Reset => { let mut reset_vec = Vec :: new ( ) ; if known_grab { reset_vec . push ( Drop ) ; known_grab = false ; } while len > original . len { reset_vec . push ( Retract ) ; len -= 1 ; } while rot_diff > 3 { rot_diff -= 6 ; } while rot_diff < - 3 { rot_diff += 6 ; } while rot_diff > 0 { reset_vec . push ( RotateClockwise ) ; rot_diff -= 1 ; } while rot_diff < 0 { reset_vec . push ( RotateCounterClockwise ) ; rot_diff += 1 ; } // look for a path forward on the track that's shorter than // the path backward. if let Some ( loop_len ) = track_loop { while track_steps > loop_len / 2 { track_steps -= loop_len ; } while track_steps < - loop_len / 2 { track_steps += loop_len } } while track_steps > 0 { reset_vec . push ( Back ) ; track_steps -= 1 ; } while track_steps < 0 { reset_vec . push ( Forward ) ; track_steps += 1 ; } while len < original . len { reset_vec . push ( Extend ) ; len += 1 ; } position_check = original . pos ; if reset_vec . len ( ) == 0 { reset_vec . push ( Empty ) } ; for i in 0 .. reset_vec . len ( ) { ensure ! ( i 0 old_instructions get ( curr i ) unwrap_or ( Empty ) Empty " " reset_vec [ i ] old_instructions get ( curr i ) i reset_vec len ( ) curr ) ; instructions . push ( reset_vec [ i ] ) ; } curr += reset_vec . len ( ) ; repeat_ending = curr ; } Noop => { instructions . push ( Empty ) ; curr += 1 ; } } ; } instructions . shrink_to_fit ( ) ; let len = instructions . len ( ) ; original . instruction_tape = Tape { first : original . instruction_tape . first , instructions , } ; Ok ( len ) }
modifies the original arm to have the new instructions
returns the length of the tape (repetition size)
modifies the original arm to have the new instructions returns the length of the tape ( repetition size )
original
&mut Arm
track_map
&TrackMap