<commit_before>use std::io::{Read, Seek};
use std::mem;

use Result;
use postscript::compact::FontSet;
use tape::Tape;
use truetype::compound::{CharMapping, FontHeader, HorizontalHeader, HorizontalMetrics};
use truetype::compound::{OffsetTable, MaximumProfile, NamingTable, PostScriptInfo, WindowsMetrics};
use truetype::primitive::Tag;
use truetype;

/// A font file.
#[derive(Default)]
pub struct File {
    pub offset_table: OffsetTable,
    pub char_mapping: Option<CharMapping>,
    pub font_header: Option<FontHeader>,
    pub horizontal_header: Option<HorizontalHeader>,
    pub horizontal_metrics: Option<HorizontalMetrics>,
    pub maximum_profile: Option<MaximumProfile>,
    pub naming_table: Option<NamingTable>,
    pub postscript_info: Option<PostScriptInfo>,
    pub windows_metrics: Option<WindowsMetrics>,
    pub postscript_fontset: Option<FontSet>,
}

macro_rules! checksum_and_jump(
    ($record:ident, $tape:ident, $process:expr) => ({
        if !try!($record.checksum($tape, $process)) {
            raise!("found a corrupted font table");
        }
        try!($tape.jump($record.offset as u64));
    });
    ($record:ident, $tape:ident) => (
        checksum_and_jump!($record, $tape, |_, word| word);
    );
);

impl File {
    #[inline]
    pub fn read<T: Read + Seek>(tape: &mut T) -> Result<File> {
        macro_rules! sort(
            ($records:expr) => ({
                let mut records = $records.iter().collect::<Vec<_>>();
                records.sort_by(|one, two| priority(Tag(one.tag)).cmp(&priority(Tag(two.tag))));
                records
            });
        );

        let mut file = File {
            offset_table: try!(truetype::Value::read(tape)),
            .. File::default()
        };
        if Tag::from(file.offset_table.header.version) != Tag::from(b"OTTO") {
            raise!("the font format is invalid");
        }

        for record in sort!(file.offset_table.records) {
            macro_rules! set(
                ($field:ident, $value:expr) => ({
                    checksum_and_jump!(record, tape);
                    file.$field = Some(try!($value));
                });
                ($field:ident) => (set!($field, truetype::Value::read(tape)));
            );
            match &Tag(record.tag).into() {
                b"cmap" => set!(char_mapping),
                b"head" => {
                    checksum_and_jump!(record, tape, |i, word| if i == 2 { 0 } else { word });
                    file.font_header = Some(try!(truetype::Value::read(tape)));
                },
                b"hhea" => set!(horizontal_header),
                b"hmtx" => {
                    let header = match file.horizontal_header {
                        Some(ref table) => table,
                        _ => continue,
                    };
                    let profile = match file.maximum_profile {
                        Some(ref table) => table,
                        _ => continue,
                    };
                    set!(horizontal_metrics, HorizontalMetrics::read(tape, header, profile));
                },
                b"maxp" => set!(maximum_profile),
                b"name" => set!(naming_table),
                b"post" => set!(postscript_info),
                b"OS/2" => set!(windows_metrics),
                b"CFF " => set!(postscript_fontset, FontSet::read(tape)),
                _ => {},
            }
        }

        Ok(file)
    }
}

fn priority(tag: Tag) -> usize {
    use std::collections::HashMap;
    use std::sync::{Once, ONCE_INIT};

    unsafe {
        static mut PRIORITY: *const HashMap<Tag, usize> = 0 as *const _;
        static ONCE: Once = ONCE_INIT;
        ONCE.call_once(|| {
            let mut map: HashMap<Tag, usize> = HashMap::new();
            map.insert(Tag::from(b"hmtx"), 42);
            PRIORITY = mem::transmute(Box::new(map));
        });
        *(&*PRIORITY).get(&tag).unwrap_or(&0)
    }
}
<commit_msg>Add open to File<commit_after>use std::io::{Read, Seek};
use std::path::Path;
use std::{fs, mem};

use Result;
use postscript::compact::FontSet;
use tape::Tape;
use truetype::compound::{CharMapping, FontHeader, HorizontalHeader, HorizontalMetrics};
use truetype::compound::{OffsetTable, MaximumProfile, NamingTable, PostScriptInfo, WindowsMetrics};
use truetype::primitive::Tag;
use truetype;

/// A font file.
#[derive(Default)]
pub struct File {
    pub offset_table: OffsetTable,
    pub char_mapping: Option<CharMapping>,
    pub font_header: Option<FontHeader>,
    pub horizontal_header: Option<HorizontalHeader>,
    pub horizontal_metrics: Option<HorizontalMetrics>,
    pub maximum_profile: Option<MaximumProfile>,
    pub naming_table: Option<NamingTable>,
    pub postscript_info: Option<PostScriptInfo>,
    pub windows_metrics: Option<WindowsMetrics>,
    pub postscript_fontset: Option<FontSet>,
}

macro_rules! checksum_and_jump(
    ($record:ident, $tape:ident, $process:expr) => ({
        if !try!($record.checksum($tape, $process)) {
            raise!("found a corrupted font table");
        }
        try!($tape.jump($record.offset as u64));
    });
    ($record:ident, $tape:ident) => (
        checksum_and_jump!($record, $tape, |_, word| word);
    );
);

impl File {
    #[inline]
    pub fn open<T: AsRef<Path>>(path: T) -> Result<File> {
        let mut file = try!(fs::File::open(path));
        File::read(&mut file)
    }

    pub fn read<T: Read + Seek>(tape: &mut T) -> Result<File> {
        macro_rules! sort(
            ($records:expr) => ({
                let mut records = $records.iter().collect::<Vec<_>>();
                records.sort_by(|one, two| priority(Tag(one.tag)).cmp(&priority(Tag(two.tag))));
                records
            });
        );

        let mut file = File {
            offset_table: try!(truetype::Value::read(tape)),
            .. File::default()
        };
        if Tag::from(file.offset_table.header.version) != Tag::from(b"OTTO") {
            raise!("the font format is invalid");
        }

        for record in sort!(file.offset_table.records) {
            macro_rules! set(
                ($field:ident, $value:expr) => ({
                    checksum_and_jump!(record, tape);
                    file.$field = Some(try!($value));
                });
                ($field:ident) => (set!($field, truetype::Value::read(tape)));
            );
            match &Tag(record.tag).into() {
                b"cmap" => set!(char_mapping),
                b"head" => {
                    checksum_and_jump!(record, tape, |i, word| if i == 2 { 0 } else { word });
                    file.font_header = Some(try!(truetype::Value::read(tape)));
                },
                b"hhea" => set!(horizontal_header),
                b"hmtx" => {
                    let header = match file.horizontal_header {
                        Some(ref table) => table,
                        _ => continue,
                    };
                    let profile = match file.maximum_profile {
                        Some(ref table) => table,
                        _ => continue,
                    };
                    set!(horizontal_metrics, HorizontalMetrics::read(tape, header, profile));
                },
                b"maxp" => set!(maximum_profile),
                b"name" => set!(naming_table),
                b"post" => set!(postscript_info),
                b"OS/2" => set!(windows_metrics),
                b"CFF " => set!(postscript_fontset, FontSet::read(tape)),
                _ => {},
            }
        }

        Ok(file)
    }
}

fn priority(tag: Tag) -> usize {
    use std::collections::HashMap;
    use std::sync::{Once, ONCE_INIT};

    unsafe {
        static mut PRIORITY: *const HashMap<Tag, usize> = 0 as *const _;
        static ONCE: Once = ONCE_INIT;
        ONCE.call_once(|| {
            let mut map: HashMap<Tag, usize> = HashMap::new();
            map.insert(Tag::from(b"hmtx"), 42);
            PRIORITY = mem::transmute(Box::new(map));
        });
        *(&*PRIORITY).get(&tag).unwrap_or(&0)
    }
}
<|endoftext|>


<commit_before>/// The `libc_bitflags!` macro helps with a common use case of defining bitflags with values from
/// the libc crate. It is used the same way as the `bitflags!` macro, except that only the name of
/// the flag value has to be given.
///
/// The `libc` crate must be in scope with the name `libc`.
///
/// # Example
/// ```
/// libc_bitflags!{
///     pub flags ProtFlags: libc::c_int {
///         PROT_NONE,
///         PROT_READ,
///         PROT_WRITE,
///         PROT_EXEC,
///         #[cfg(any(target_os = "linux", target_os = "android"))]
///         PROT_GROWSDOWN,
///         #[cfg(any(target_os = "linux", target_os = "android"))]
///         PROT_GROWSUP,
///     }
/// }
/// ```
macro_rules! libc_bitflags {
    // (non-pub) Exit rule.
    (@call_bitflags
        {
            name: $BitFlags:ident,
            type: $T:ty,
            attrs: [$($attrs:tt)*],
            flags: [$($flags:tt)*],
        }
    ) => {
        bitflags! {
            $($attrs)*
            flags $BitFlags: $T {
                $($flags)*
            }
        }
    };

    // (pub) Exit rule.
    (@call_bitflags
        {
            pub,
            name: $BitFlags:ident,
            type: $T:ty,
            attrs: [$($attrs:tt)*],
            flags: [$($flags:tt)*],
        }
    ) => {
        bitflags! {
            $($attrs)*
            pub struct $BitFlags: $T {
                $($flags)*
            }
        }
    };

    // (non-pub) Done accumulating.
    (@accumulate_flags
        {
            name: $BitFlags:ident,
            type: $T:ty,
            attrs: $attrs:tt,
        },
        $flags:tt;
    ) => {
        libc_bitflags! {
            @call_bitflags
            {
                name: $BitFlags,
                type: $T,
                attrs: $attrs,
                flags: $flags,
            }
        }
    };

    // (pub) Done accumulating.
    (@accumulate_flags
        {
            pub,
            name: $BitFlags:ident,
            type: $T:ty,
            attrs: $attrs:tt,
        },
        $flags:tt;
    ) => {
        libc_bitflags! {
            @call_bitflags
            {
                pub,
                name: $BitFlags,
                type: $T,
                attrs: $attrs,
                flags: $flags,
            }
        }
    };

    // Munch an attr.
    (@accumulate_flags
        $prefix:tt,
        [$($flags:tt)*];
        #[$attr:meta] $($tail:tt)*
    ) => {
        libc_bitflags! {
            @accumulate_flags
            $prefix,
            [
                $($flags)*
                #[$attr]
            ];
            $($tail)*
        }
    };

    // Munch last ident if not followed by a comma.
    (@accumulate_flags
        $prefix:tt,
        [$($flags:tt)*];
        $flag:ident
    ) => {
        libc_bitflags! {
            @accumulate_flags
            $prefix,
            [
                $($flags)*
                const $flag = libc::$flag;
            ];
        }
    };

    // Munch an ident; covers terminating comma case.
    (@accumulate_flags
        $prefix:tt,
        [$($flags:tt)*];
        $flag:ident, $($tail:tt)*
    ) => {
        libc_bitflags! {
            @accumulate_flags
            $prefix,
            [
                $($flags)*
                const $flag = libc::$flag;
            ];
            $($tail)*
        }
    };

    // (non-pub) Entry rule.
    (
        $(#[$attr:meta])*
        flags $BitFlags:ident: $T:ty {
            $($vals:tt)*
        }
    ) => {
        libc_bitflags! {
            @accumulate_flags
            {
                name: $BitFlags,
                type: $T,
                attrs: [$(#[$attr])*],
            },
            [];
            $($vals)*
        }
    };

    // (pub) Entry rule.
    (
        $(#[$attr:meta])*
        pub flags $BitFlags:ident: $T:ty {
            $($vals:tt)*
        }
    ) => {
        libc_bitflags! {
            @accumulate_flags
            {
                pub,
                name: $BitFlags,
                type: $T,
                attrs: [$(#[$attr])*],
            },
            [];
            $($vals)*
        }
    };
}

/// A Rust version of the familiar C `offset_of` macro.  It returns the byte
/// offset of `field` within struct `ty`
macro_rules! offset_of {
    ($ty:ty, $field:ident) => {
        &(*(0 as *const $ty)).$field as *const _ as usize
    }
}
<commit_msg>Allow casting a flag to a type in libc_bitflags!<commit_after>/// The `libc_bitflags!` macro helps with a common use case of defining bitflags with values from
/// the libc crate. It is used the same way as the `bitflags!` macro, except that only the name of
/// the flag value has to be given.
///
/// The `libc` crate must be in scope with the name `libc`.
///
/// # Example
/// ```
/// libc_bitflags!{
///     pub flags ProtFlags: libc::c_int {
///         PROT_NONE,
///         PROT_READ,
///         PROT_WRITE,
///         PROT_EXEC,
///         #[cfg(any(target_os = "linux", target_os = "android"))]
///         PROT_GROWSDOWN,
///         #[cfg(any(target_os = "linux", target_os = "android"))]
///         PROT_GROWSUP,
///     }
/// }
/// ```
///
/// Example with casting, due to a mistake in libc. In this example, the
/// various flags have different types, so we cast the broken ones to the right
/// type.
///
/// ```
/// libc_bitflags!{
///     pub flags SaFlags: libc::c_ulong {
///         SA_NOCLDSTOP as libc::c_ulong,
///         SA_NOCLDWAIT,
///         SA_NODEFER as libc::c_ulong,
///         SA_ONSTACK,
///         SA_RESETHAND as libc::c_ulong,
///         SA_RESTART as libc::c_ulong,
///         SA_SIGINFO,
///     }
/// }
/// ```
macro_rules! libc_bitflags {
    // (non-pub) Exit rule.
    (@call_bitflags
        {
            name: $BitFlags:ident,
            type: $T:ty,
            attrs: [$($attrs:tt)*],
            flags: [$($flags:tt)*],
        }
    ) => {
        bitflags! {
            $($attrs)*
            flags $BitFlags: $T {
                $($flags)*
            }
        }
    };

    // (pub) Exit rule.
    (@call_bitflags
        {
            pub,
            name: $BitFlags:ident,
            type: $T:ty,
            attrs: [$($attrs:tt)*],
            flags: [$($flags:tt)*],
        }
    ) => {
        bitflags! {
            $($attrs)*
            pub struct $BitFlags: $T {
                $($flags)*
            }
        }
    };

    // (non-pub) Done accumulating.
    (@accumulate_flags
        {
            name: $BitFlags:ident,
            type: $T:ty,
            attrs: $attrs:tt,
        },
        $flags:tt;
    ) => {
        libc_bitflags! {
            @call_bitflags
            {
                name: $BitFlags,
                type: $T,
                attrs: $attrs,
                flags: $flags,
            }
        }
    };

    // (pub) Done accumulating.
    (@accumulate_flags
        {
            pub,
            name: $BitFlags:ident,
            type: $T:ty,
            attrs: $attrs:tt,
        },
        $flags:tt;
    ) => {
        libc_bitflags! {
            @call_bitflags
            {
                pub,
                name: $BitFlags,
                type: $T,
                attrs: $attrs,
                flags: $flags,
            }
        }
    };

    // Munch an attr.
    (@accumulate_flags
        $prefix:tt,
        [$($flags:tt)*];
        #[$attr:meta] $($tail:tt)*
    ) => {
        libc_bitflags! {
            @accumulate_flags
            $prefix,
            [
                $($flags)*
                #[$attr]
            ];
            $($tail)*
        }
    };

    // Munch last ident if not followed by a comma.
    (@accumulate_flags
        $prefix:tt,
        [$($flags:tt)*];
        $flag:ident
    ) => {
        libc_bitflags! {
            @accumulate_flags
            $prefix,
            [
                $($flags)*
                const $flag = libc::$flag;
            ];
        }
    };

    // Munch last ident and cast it to the given type.
    (@accumulate_flags
        $prefix:tt,
        [$($flags:tt)*];
        $flag:ident as $ty:ty
    ) => {
        libc_bitflags! {
            @accumulate_flags
            $prefix,
            [
                $($flags)*
                const $flag = libc::$flag as $ty;
            ];
        }
    };

    // Munch an ident; covers terminating comma case.
    (@accumulate_flags
        $prefix:tt,
        [$($flags:tt)*];
        $flag:ident, $($tail:tt)*
    ) => {
        libc_bitflags! {
            @accumulate_flags
            $prefix,
            [
                $($flags)*
                const $flag = libc::$flag;
            ];
            $($tail)*
        }
    };

    // Munch an ident and cast it to the given type; covers terminating comma
    // case.
    (@accumulate_flags
        $prefix:tt,
        [$($flags:tt)*];
        $flag:ident as $ty:ty, $($tail:tt)*
    ) => {
        libc_bitflags! {
            @accumulate_flags
            $prefix,
            [
                $($flags)*
                const $flag = libc::$flag as $ty;
            ];
            $($tail)*
        }
    };

    // (non-pub) Entry rule.
    (
        $(#[$attr:meta])*
        flags $BitFlags:ident: $T:ty {
            $($vals:tt)*
        }
    ) => {
        libc_bitflags! {
            @accumulate_flags
            {
                name: $BitFlags,
                type: $T,
                attrs: [$(#[$attr])*],
            },
            [];
            $($vals)*
        }
    };

    // (pub) Entry rule.
    (
        $(#[$attr:meta])*
        pub flags $BitFlags:ident: $T:ty {
            $($vals:tt)*
        }
    ) => {
        libc_bitflags! {
            @accumulate_flags
            {
                pub,
                name: $BitFlags,
                type: $T,
                attrs: [$(#[$attr])*],
            },
            [];
            $($vals)*
        }
    };
}

/// A Rust version of the familiar C `offset_of` macro.  It returns the byte
/// offset of `field` within struct `ty`
macro_rules! offset_of {
    ($ty:ty, $field:ident) => {
        &(*(0 as *const $ty)).$field as *const _ as usize
    }
}
<|endoftext|>


<commit_before>use std::ops::{Add, Sub, Div};
use std::collections::HashMap;
use std::iter::FromIterator;
use std::collections::VecDeque;
use std::iter::repeat;
use std::fmt::{Debug, Formatter, Error};
use std::cmp::Ordering::Equal;
use std::f32;

#[derive(Clone,)]
pub struct WordVec {
    pub word: String,
    pub count: u64,
    vec: Vec<f32>,
}

impl PartialEq for WordVec {
    fn eq(&self, other: &WordVec) -> bool {
        self.vec == other.vec
    }
}

impl Debug for WordVec {
    fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> {
        try!(self.word.fmt(fmt));
        try!(fmt.write_str(": "));
        try!(self.count.fmt(fmt));
        try!(fmt.write_str(", "));
        self.vec.iter().take(5).collect::<Vec<_>>().fmt(fmt)
    }
}

impl WordVec {

    fn dot_prod(&self, other: &WordVec) -> f32 {
        self.vec.iter()
                .zip(other.vec.iter())
                .map(|(x, y)| x * y)
                .fold(0.0, |x, y| x + y)
    }

    fn magnitude(&self) -> f32 {
        self.vec.iter()
                .map(|x| x * x)
                .fold(0.0, |x, y| x + y)
                .sqrt()
    }

    pub fn distance(&self, other: &WordVec) -> f32 {
        self.vec.iter().zip(other.vec.iter())
                .map(|(x, y)| (x * x - y * y).abs().sqrt())
                .fold(0.0, |x, y| x + y)
    }

    fn new(word: String, num_words: usize) -> WordVec {
        WordVec {
            word: word,
            count: 0,
            vec: repeat(0.0).take(num_words).collect(),
        }
    }

    fn normalize(&mut self) {
        let count = self.count as f32;
        for f in &mut self.vec {
            *f /= count;
        }
    }

    fn rescale(&mut self, vec_scales: &Vec<(f32, f32)>) {
        for i in 0..self.vec.len() {
            let (min, max) = vec_scales[i];
            if max != min {
                self.vec[i] = 2.0 * (self.vec[i] - min) / (max - min) - 1.0;
            }
        }
    }

    fn inc(&mut self, i: usize) {
        self.vec[i] += 1.0;
    }

}

impl Div<i32> for WordVec {
    type Output = WordVec;

    fn div(mut self, i: i32) -> WordVec {
        for f in &mut self.vec {
            *f /= i as f32;
        }

        self
    }
}

impl<'a> Add<&'a WordVec> for WordVec {
    type Output = WordVec;

    fn add(self, other: &WordVec) -> WordVec {
        debug_assert!(self.vec.len() == other.vec.len());
        let mut newvec = WordVec {
            word: format!("{} + {}", &self.word, &other.word),
            count: 0,
            vec: self.vec,
        };

        for i in 0..newvec.vec.len() {
            newvec.vec[i] += other.vec[i];
        }

        newvec
    }
}

impl<'a> Sub<&'a WordVec> for WordVec {
    type Output = WordVec;

    fn sub(self, other: &WordVec) -> WordVec {
        debug_assert!(self.vec.len() == other.vec.len());
        let mut newvec = WordVec {
            word: format!("{} - {}", &self.word, &other.word),
            count: 0,
            vec: self.vec,
        };

        for i in 0..(newvec.vec.len()) {
            newvec.vec[i] -= other.vec[i];
        }

        newvec
    }
}

pub struct LanguageModel {
    words: HashMap<String, usize>,
    word_vecs: Vec<WordVec>,
}

pub struct LanguageModelBuilder {
    window_width: usize,
    words: HashMap<String, usize>,
    word_vecs: Vec<WordVec>,
}

pub struct WordAcceptor<'a> {
    window: VecDeque<(usize, String)>,
    builder: &'a mut LanguageModelBuilder,
}

impl LanguageModelBuilder {
    pub fn new(window_radius: usize, words: Vec<String>) -> LanguageModelBuilder {

        let word_vecs = words.iter()
                             .map(|s| WordVec::new(s.clone(), words.len()))
                             .collect();

        let words = HashMap::from_iter(
                        words.into_iter()
                             .enumerate()
                             .map(|(a, b)| (b, a)));

        LanguageModelBuilder {
            window_width: 2 * window_radius + 1,
            words: words,
            word_vecs: word_vecs,
        }
    }

    pub fn build(mut self) -> LanguageModel {
        let len = self.word_vecs[0].vec.len();
        let mut vec_scales: Vec<(f32, f32)> = repeat((f32::MAX, 0.0)).take(len).collect();

        for vec in self.word_vecs.iter_mut() {
            vec.normalize();
        }

        for (i, vec) in self.word_vecs.iter().map(|w| &w.vec).enumerate() {
            for j in (0..len).filter(|j| i != *j) {
                let (min, max) = vec_scales[j];
                vec_scales[j] = (min.min(vec[j]), max.max(vec[j]))
            }
        }

        for vec in self.word_vecs.iter_mut() {
            vec.rescale(&vec_scales);
        }

        LanguageModel {
            words: self.words,
            word_vecs: self.word_vecs,
        }
    }

    pub fn new_file<'a>(&'a mut self) -> WordAcceptor<'a> {
        WordAcceptor {
            window: VecDeque::new(),
            builder: self,
        }
    }

    fn get_vec(&mut self, word: &str) -> &mut WordVec {
        let i = self.words[word];
        &mut self.word_vecs[i]
    }

    fn add_word(&mut self, from: usize, to: usize) {
        self.word_vecs[from].inc(to);
    }

    fn word_seen(&mut self, word: &str) {
        self.get_vec(word).count += 1;
    }
}

impl<'a> WordAcceptor<'a> {
    pub fn add_word(&mut self, word: String) {
        let ww = self.builder.window_width;
        let idx_opt = self.builder.words.get(&word).map(|w| *w);

        if let Some(next_idx) = idx_opt {
            self.window.push_back((next_idx, word));
            if self.window.len() > ww {
                self.window.pop_front();
            }

            if self.window.len() == ww {
                let (center_idx, ref center_word) = self.window[ww / 2];
                self.builder.word_seen(center_word);
                for i in (0..ww).filter(|a| *a != ww / 2) {
                    self.builder.add_word(center_idx, self.window[i].0);
                }
            }
        }
    }
}

impl LanguageModel {
    pub fn get(&self, word: &str) -> Option<&WordVec> {
        self.words.get(word).map(|i| &self.word_vecs[*i])
    }

    pub fn nearest_words(&self, word: &WordVec) -> Vec<&WordVec> {
        let mut vec_refs = self.word_vecs.iter().filter_map(|w| {
            let dist = w.distance(word);
            if w.word != word.word {
                Some((dist, w))
            } else {
                None
            }
        }).collect::<Vec<_>>();
        vec_refs.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap_or_else(|| {
            println!("bad vector: {:?}, {:?}", a, b);
            Equal
        }));
        vec_refs.into_iter().map(|x| x.1).collect()
    }
}<commit_msg>Remove rescaling. Seems to make things worse - maybe it's effectively giving rare words too much weight when there's more signal than noise.<commit_after>use std::ops::{Add, Sub, Div};
use std::collections::HashMap;
use std::iter::FromIterator;
use std::collections::VecDeque;
use std::iter::repeat;
use std::fmt::{Debug, Formatter, Error};
use std::cmp::Ordering::Equal;
use std::f32;

#[derive(Clone,)]
pub struct WordVec {
    pub word: String,
    pub count: u64,
    vec: Vec<f32>,
}

impl PartialEq for WordVec {
    fn eq(&self, other: &WordVec) -> bool {
        self.vec == other.vec
    }
}

impl Debug for WordVec {
    fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> {
        try!(self.word.fmt(fmt));
        try!(fmt.write_str(": "));
        try!(self.count.fmt(fmt));
        try!(fmt.write_str(", "));
        self.vec.iter().take(5).collect::<Vec<_>>().fmt(fmt)
    }
}

impl WordVec {

    fn dot_prod(&self, other: &WordVec) -> f32 {
        self.vec.iter()
                .zip(other.vec.iter())
                .map(|(x, y)| x * y)
                .fold(0.0, |x, y| x + y)
    }

    fn magnitude(&self) -> f32 {
        self.vec.iter()
                .map(|x| x * x)
                .fold(0.0, |x, y| x + y)
                .sqrt()
    }

    pub fn distance(&self, other: &WordVec) -> f32 {
        self.vec.iter().zip(other.vec.iter())
                .map(|(x, y)| (x * x - y * y).abs().sqrt())
                .fold(0.0, |x, y| x + y)
    }

    fn new(word: String, num_words: usize) -> WordVec {
        WordVec {
            word: word,
            count: 0,
            vec: repeat(0.0).take(num_words).collect(),
        }
    }

    fn normalize(&mut self) {
        let count = self.count as f32;
        for f in &mut self.vec {
            *f /= count;
        }
    }

    fn inc(&mut self, i: usize) {
        self.vec[i] += 1.0;
    }

}

impl Div<i32> for WordVec {
    type Output = WordVec;

    fn div(mut self, i: i32) -> WordVec {
        for f in &mut self.vec {
            *f /= i as f32;
        }

        self
    }
}

impl<'a> Add<&'a WordVec> for WordVec {
    type Output = WordVec;

    fn add(self, other: &WordVec) -> WordVec {
        debug_assert!(self.vec.len() == other.vec.len());
        let mut newvec = WordVec {
            word: format!("{} + {}", &self.word, &other.word),
            count: 0,
            vec: self.vec,
        };

        for i in 0..newvec.vec.len() {
            newvec.vec[i] += other.vec[i];
        }

        newvec
    }
}

impl<'a> Sub<&'a WordVec> for WordVec {
    type Output = WordVec;

    fn sub(self, other: &WordVec) -> WordVec {
        debug_assert!(self.vec.len() == other.vec.len());
        let mut newvec = WordVec {
            word: format!("{} - {}", &self.word, &other.word),
            count: 0,
            vec: self.vec,
        };

        for i in 0..(newvec.vec.len()) {
            newvec.vec[i] -= other.vec[i];
        }

        newvec
    }
}

pub struct LanguageModel {
    words: HashMap<String, usize>,
    word_vecs: Vec<WordVec>,
}

pub struct LanguageModelBuilder {
    window_width: usize,
    words: HashMap<String, usize>,
    word_vecs: Vec<WordVec>,
}

pub struct WordAcceptor<'a> {
    window: VecDeque<(usize, String)>,
    builder: &'a mut LanguageModelBuilder,
}

impl LanguageModelBuilder {
    pub fn new(window_radius: usize, words: Vec<String>) -> LanguageModelBuilder {

        let word_vecs = words.iter()
                             .map(|s| WordVec::new(s.clone(), words.len()))
                             .collect();

        let words = HashMap::from_iter(
                        words.into_iter()
                             .enumerate()
                             .map(|(a, b)| (b, a)));

        LanguageModelBuilder {
            window_width: 2 * window_radius + 1,
            words: words,
            word_vecs: word_vecs,
        }
    }

    pub fn build(mut self) -> LanguageModel {
        for vec in self.word_vecs.iter_mut() {
            vec.normalize();
        }

        LanguageModel {
            words: self.words,
            word_vecs: self.word_vecs,
        }
    }

    pub fn new_file<'a>(&'a mut self) -> WordAcceptor<'a> {
        WordAcceptor {
            window: VecDeque::new(),
            builder: self,
        }
    }

    fn get_vec(&mut self, word: &str) -> &mut WordVec {
        let i = self.words[word];
        &mut self.word_vecs[i]
    }

    fn add_word(&mut self, from: usize, to: usize) {
        self.word_vecs[from].inc(to);
    }

    fn word_seen(&mut self, word: &str) {
        self.get_vec(word).count += 1;
    }
}

impl<'a> WordAcceptor<'a> {
    pub fn add_word(&mut self, word: String) {
        let ww = self.builder.window_width;
        let idx_opt = self.builder.words.get(&word).map(|w| *w);

        if let Some(next_idx) = idx_opt {
            self.window.push_back((next_idx, word));
            if self.window.len() > ww {
                self.window.pop_front();
            }

            if self.window.len() == ww {
                let (center_idx, ref center_word) = self.window[ww / 2];
                self.builder.word_seen(center_word);
                for i in (0..ww).filter(|a| *a != ww / 2) {
                    self.builder.add_word(center_idx, self.window[i].0);
                }
            }
        }
    }
}

impl LanguageModel {
    pub fn get(&self, word: &str) -> Option<&WordVec> {
        self.words.get(word).map(|i| &self.word_vecs[*i])
    }

    pub fn nearest_words(&self, word: &WordVec) -> Vec<&WordVec> {
        let mut vec_refs = self.word_vecs.iter().filter_map(|w| {
            let dist = w.distance(word);
            if w.word != word.word {
                Some((dist, w))
            } else {
                None
            }
        }).collect::<Vec<_>>();
        vec_refs.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap_or_else(|| {
            println!("bad vector: {:?}, {:?}", a, b);
            Equal
        }));
        vec_refs.into_iter().map(|x| x.1).collect()
    }
}<|endoftext|>


<commit_before>// vim: tw=80
use super::*;

use quote::ToTokens;

use crate::{
    mock_function::MockFunction,
    mock_trait::MockTrait
};

pub(crate) struct MockItemStruct {
    attrs: Vec<Attribute>,
    generics: Generics,
    /// Inherent methods of the mock struct
    methods: Vec<MockFunction>,
    /// Name of the overall module that holds all of the mock stuff
    // TODO: base this on name so we can get rid of MockableStruct.original_name
    modname: Ident,
    name: Ident,
    /// Is this a whole MockStruct or just a substructure for a trait impl?
    substruct: bool,
    traits: Vec<MockTrait>,
    vis: Visibility,
}

impl MockItemStruct {
    fn new_method(&self) -> impl ToTokens {
        // TODO: don't add the new method if the struct already has one
        if self.substruct {
            TokenStream::new()
        } else {
            quote!(
                /// Create a new mock object with no expectations.
                ///
                /// This method will not be generated if the real struct
                /// already has a `new` method.  However, it *will* be
                /// generated if the struct implements a trait with a `new`
                /// method.  The trait's `new` method can still be called
                /// like `<MockX as TraitY>::new`
                pub fn new() -> Self {
                    Self::default()
                }
            )
        }
    }
}

impl From<MockableStruct> for MockItemStruct {
    fn from(mockable: MockableStruct) -> MockItemStruct {
        let mock_ident = gen_mod_ident(&mockable.name, None);
        let modname = gen_mod_ident(&mockable.original_name, None);
        let struct_name = &mockable.name;
        let vis = mockable.vis;
        let methods = mockable.methods.into_iter()
            .map(|meth|
                mock_function::Builder::new(&meth.sig, &meth.vis)
                    .attrs(&meth.attrs)
                    .parent(&mock_ident)
                    .struct_(&struct_name)
                    .levels(2)
                    .call_levels(0)
                    .build()
            ).collect::<Vec<_>>();
        let structname = &mockable.name;
        let traits = mockable.traits.into_iter()
            .map(|t| MockTrait::new(structname, t, &vis))
            .collect();
        MockItemStruct {
            attrs: mockable.attrs,
            generics: mockable.generics,
            methods,
            modname,
            name: mockable.name,
            substruct: false,
            traits,
            vis
        }
    }
}

impl ToTokens for MockItemStruct {
    fn to_tokens(&self, tokens: &mut TokenStream) {
        let attrs = &self.attrs;
        let struct_name = &self.name;
        let (ig, tg, wc) = self.generics.split_for_impl(); //TODO
        let modname = &self.modname;
        let calls = if self.substruct {
            Vec::new()
        } else {
            self.methods.iter()
                .map(|meth| meth.call(Some(modname)))
                .collect::<Vec<_>>()
        };
        let method_checkpoints = self.methods.iter()
            .filter(|meth| !meth.is_static())
            .map(|meth| meth.checkpoint())
            .collect::<Vec<_>>();
        let default_constructions = self.methods.iter()
            .filter(|meth| !meth.is_static())
            .map(|meth| {
                let name = meth.name();
                let attrs = meth.format_attrs(false);
                let expectations_obj = &meth.expectations_obj();
                quote!(#attrs #name: #modname::#expectations_obj::default(),)
            }).collect::<Vec<_>>();
        let expectations_objects = self.methods.iter()
            .filter(|meth| !meth.is_static())
            .map(|meth| {
                let name = meth.name();
                let attrs = meth.format_attrs(false);
                let expectations_obj = &meth.expectations_obj();
                quote!(#attrs #name: #modname::#expectations_obj)
            }).collect::<Vec<_>>();
        let contexts = self.methods.iter()
            .map(|meth| if !meth.is_static() {
                // Only static methods have context methods, but we need
                // something here so contexts will have the same length as
                // calls.
                // TODO: after merging the 2020_refactor branch, eliminate
                // this hack by de-interleaving the calls, contexts, and
                // expects methods.
                Box::new(TokenStream::new()) as Box<dyn ToTokens>
            } else {
                Box::new(meth.context_fn(Some(modname))) as Box<dyn ToTokens>
            }).collect::<Vec<_>>();
        let expects = if self.substruct {
            Vec::new()
        } else {
            self.methods.iter()
                .map(|meth| if meth.is_static() {
                    // static methods' expect methods are generated by the
                    // Context objects, but we still need something here so
                    // expects will have the same length as calls.
                    // TODO: after merging the 2020_refactor branch, eliminate
                    // this hack by de-interleaving the calls and expects
                    // methods.
                    Box::new(TokenStream::new()) as Box<dyn ToTokens>
                } else {
                    Box::new(meth.expect(&modname)) as Box<dyn ToTokens>
                }).collect::<Vec<_>>()
        };
        let new_method = self.new_method();
        let priv_mods = self.methods.iter()
            .map(|meth| meth.priv_module())
            .collect::<Vec<_>>();
        let substructs = self.traits.iter()
            .map(|trait_| {
                MockItemStruct {
                    attrs: trait_.attrs.clone(),
                    generics: trait_.generics.clone(),
                    methods: trait_.methods.clone(),
                    modname: format_ident!("{}_{}", &self.modname, trait_.name()),
                    name: format_ident!("{}_{}", &self.name, trait_.name()),
                    substruct: true,
                    traits: Vec::new(),
                    vis: Visibility::Inherited,
                }
            }).collect::<Vec<_>>();
        let substruct_expectations = self.traits.iter()
            .map(|trait_| format_ident!("{}_expectations", trait_.name()))
            .collect::<Vec<_>>();
        let substruct_type_names = substructs.iter()
            .map(|ss| ss.name.clone())
            .collect::<Vec<_>>();
        let trait_impls = self.traits.iter()
            .map(|ss| {
                let modname = format_ident!("{}_{}", &self.modname, ss.name());
                ss.trait_impl(&modname)
            }).collect::<Vec<_>>();
        let vis = &self.vis;
        quote!(
            #[allow(non_snake_case)]
            #[doc(hidden)]
            pub mod #modname {
                use super::*;
                #(#priv_mods)*
            }
            #[allow(non_camel_case_types)]
            #[allow(non_snake_case)]
            #[allow(missing_docs)]
            #(#attrs)*
            #vis struct #struct_name #ig #wc
            {
                #(#expectations_objects),*
                #(#substruct_expectations: #substruct_type_names),*
            }
            impl #ig ::std::default::Default for #struct_name #tg #wc {
                fn default() -> Self {
                    Self {
                        // TODO: try removing the type names, and just use
                        // "default"
                        #(#substruct_expectations: #substruct_type_names::default()),*
                        #(#default_constructions)*
                    }
                }
            }
            #(#substructs)*
            impl #ig #struct_name #tg #wc {
                #(#calls #contexts #expects)*
                /// Validate that all current expectations for all methods have
                /// been satisfied, and discard them.
                pub fn checkpoint(&mut self) {
                    #(self.#substruct_expectations.checkpoint();)*
                    #(#method_checkpoints)*
                }
                #new_method
            }
            #(#trait_impls)*
        ).to_tokens(tokens);
    }
}
<commit_msg>Don't generate a "new" method for structs that already have one<commit_after>// vim: tw=80
use super::*;

use quote::ToTokens;

use crate::{
    mock_function::MockFunction,
    mock_trait::MockTrait
};

pub(crate) struct MockItemStruct {
    attrs: Vec<Attribute>,
    generics: Generics,
    /// Does the original struct have a `new` method?
    has_new: bool,
    /// Inherent methods of the mock struct
    methods: Vec<MockFunction>,
    /// Name of the overall module that holds all of the mock stuff
    // TODO: base this on name so we can get rid of MockableStruct.original_name
    modname: Ident,
    name: Ident,
    /// Is this a whole MockStruct or just a substructure for a trait impl?
    substruct: bool,
    traits: Vec<MockTrait>,
    vis: Visibility,
}

impl MockItemStruct {
    fn new_method(&self) -> impl ToTokens {
        if self.has_new || self.substruct {
            TokenStream::new()
        } else {
            quote!(
                /// Create a new mock object with no expectations.
                ///
                /// This method will not be generated if the real struct
                /// already has a `new` method.  However, it *will* be
                /// generated if the struct implements a trait with a `new`
                /// method.  The trait's `new` method can still be called
                /// like `<MockX as TraitY>::new`
                pub fn new() -> Self {
                    Self::default()
                }
            )
        }
    }
}

impl From<MockableStruct> for MockItemStruct {
    fn from(mockable: MockableStruct) -> MockItemStruct {
        let mock_ident = gen_mod_ident(&mockable.name, None);
        let modname = gen_mod_ident(&mockable.original_name, None);
        let struct_name = &mockable.name;
        let vis = mockable.vis;
        let has_new = mockable.methods.iter()
            .any(|meth| meth.sig.ident == "new") ||
            mockable.traits.iter()
            .any(|trait_|
                trait_.items.iter()
                    .any(|ti| if let TraitItem::Method(tim) = ti {
                            tim.sig.ident == "new"
                        } else {
                            false
                        }
                    )
            );
        let methods = mockable.methods.into_iter()
            .map(|meth|
                mock_function::Builder::new(&meth.sig, &meth.vis)
                    .attrs(&meth.attrs)
                    .parent(&mock_ident)
                    .struct_(&struct_name)
                    .levels(2)
                    .call_levels(0)
                    .build()
            ).collect::<Vec<_>>();
        let structname = &mockable.name;
        let traits = mockable.traits.into_iter()
            .map(|t| MockTrait::new(structname, t, &vis))
            .collect();

        MockItemStruct {
            attrs: mockable.attrs,
            generics: mockable.generics,
            has_new,
            methods,
            modname,
            name: mockable.name,
            substruct: false,
            traits,
            vis
        }
    }
}

impl ToTokens for MockItemStruct {
    fn to_tokens(&self, tokens: &mut TokenStream) {
        let attrs = &self.attrs;
        let struct_name = &self.name;
        let (ig, tg, wc) = self.generics.split_for_impl(); //TODO
        let modname = &self.modname;
        let calls = if self.substruct {
            Vec::new()
        } else {
            self.methods.iter()
                .map(|meth| meth.call(Some(modname)))
                .collect::<Vec<_>>()
        };
        let method_checkpoints = self.methods.iter()
            .filter(|meth| !meth.is_static())
            .map(|meth| meth.checkpoint())
            .collect::<Vec<_>>();
        let default_constructions = self.methods.iter()
            .filter(|meth| !meth.is_static())
            .map(|meth| {
                let name = meth.name();
                let attrs = meth.format_attrs(false);
                let expectations_obj = &meth.expectations_obj();
                quote!(#attrs #name: #modname::#expectations_obj::default(),)
            }).collect::<Vec<_>>();
        let expectations_objects = self.methods.iter()
            .filter(|meth| !meth.is_static())
            .map(|meth| {
                let name = meth.name();
                let attrs = meth.format_attrs(false);
                let expectations_obj = &meth.expectations_obj();
                quote!(#attrs #name: #modname::#expectations_obj)
            }).collect::<Vec<_>>();
        let contexts = self.methods.iter()
            .map(|meth| if !meth.is_static() {
                // Only static methods have context methods, but we need
                // something here so contexts will have the same length as
                // calls.
                // TODO: after merging the 2020_refactor branch, eliminate
                // this hack by de-interleaving the calls, contexts, and
                // expects methods.
                Box::new(TokenStream::new()) as Box<dyn ToTokens>
            } else {
                Box::new(meth.context_fn(Some(modname))) as Box<dyn ToTokens>
            }).collect::<Vec<_>>();
        let expects = if self.substruct {
            Vec::new()
        } else {
            self.methods.iter()
                .map(|meth| if meth.is_static() {
                    // static methods' expect methods are generated by the
                    // Context objects, but we still need something here so
                    // expects will have the same length as calls.
                    // TODO: after merging the 2020_refactor branch, eliminate
                    // this hack by de-interleaving the calls and expects
                    // methods.
                    Box::new(TokenStream::new()) as Box<dyn ToTokens>
                } else {
                    Box::new(meth.expect(&modname)) as Box<dyn ToTokens>
                }).collect::<Vec<_>>()
        };
        let new_method = self.new_method();
        let priv_mods = self.methods.iter()
            .map(|meth| meth.priv_module())
            .collect::<Vec<_>>();
        let substructs = self.traits.iter()
            .map(|trait_| {
                MockItemStruct {
                    attrs: trait_.attrs.clone(),
                    generics: trait_.generics.clone(),
                    // Never generate new methods for substructs
                    has_new: true,
                    methods: trait_.methods.clone(),
                    modname: format_ident!("{}_{}", &self.modname, trait_.name()),
                    name: format_ident!("{}_{}", &self.name, trait_.name()),
                    substruct: true,
                    traits: Vec::new(),
                    vis: Visibility::Inherited,
                }
            }).collect::<Vec<_>>();
        let substruct_expectations = self.traits.iter()
            .map(|trait_| format_ident!("{}_expectations", trait_.name()))
            .collect::<Vec<_>>();
        let substruct_type_names = substructs.iter()
            .map(|ss| ss.name.clone())
            .collect::<Vec<_>>();
        let trait_impls = self.traits.iter()
            .map(|ss| {
                let modname = format_ident!("{}_{}", &self.modname, ss.name());
                ss.trait_impl(&modname)
            }).collect::<Vec<_>>();
        let vis = &self.vis;
        quote!(
            #[allow(non_snake_case)]
            #[doc(hidden)]
            pub mod #modname {
                use super::*;
                #(#priv_mods)*
            }
            #[allow(non_camel_case_types)]
            #[allow(non_snake_case)]
            #[allow(missing_docs)]
            #(#attrs)*
            #vis struct #struct_name #ig #wc
            {
                #(#expectations_objects),*
                #(#substruct_expectations: #substruct_type_names),*
            }
            impl #ig ::std::default::Default for #struct_name #tg #wc {
                fn default() -> Self {
                    Self {
                        // TODO: try removing the type names, and just use
                        // "default"
                        #(#substruct_expectations: #substruct_type_names::default()),*
                        #(#default_constructions)*
                    }
                }
            }
            #(#substructs)*
            impl #ig #struct_name #tg #wc {
                #(#calls #contexts #expects)*
                /// Validate that all current expectations for all methods have
                /// been satisfied, and discard them.
                pub fn checkpoint(&mut self) {
                    #(self.#substruct_expectations.checkpoint();)*
                    #(#method_checkpoints)*
                }
                #new_method
            }
            #(#trait_impls)*
        ).to_tokens(tokens);
    }
}
<|endoftext|>


<commit_before>extern crate rand;

use na::{Vector2, Vector3};
use std::collections::HashSet;
use std::collections::HashMap;
use std::hash::Hash;
use std::collections::BinaryHeap;
use std::cmp::Ordering;

use self::rand::{Rng,StdRng};

use terrain;
use prim;

const STEP: i64 = prim::TILE_SIZE as i64;

pub type Path = Vec<Vector2<i64>>;

pub fn find_flag_positions(ground: &terrain::Ground, rand: &mut StdRng) -> Vec<prim::Flag> {
    let mut flag_positions = Vec::new();
    loop {
        let xp = (rand.gen::<f64>() * 0.8 + 0.1) * prim::DIM - prim::HDIM;
        let zp = (rand.gen::<f64>() * 0.8 + 0.1) * prim::DIM - prim::HDIM;
        let yp = terrain::get_height_at(&ground, xp, zp);
        if yp > 10.0 {
            flag_positions.push(Vector3::new(xp, yp, zp));
        }
        if flag_positions.len() == 10 {
            break;
        }
    }

    flag_positions.into_iter().map(|p| prim::Flag {
        position: p,
        flag_state: prim::FlagState::Free,
        flag_timer: prim::FLAG_TIMER,
    }).collect()
}

pub fn find_base_positions(ground: &terrain::Ground, rand: &mut StdRng) -> [Vector3<f64>; 2] {
    for i in 0..200 {
        let (x1, x2) = if i < 100 {
            let x1 = -prim::HDIM + prim::TILE_SIZE * 2.0;
            let x2 =  prim::HDIM - prim::TILE_SIZE * 2.0;
            (x1, x2)
        } else {
            let x1 =   (rand.gen::<f64>() * 0.3) * prim::DIM - prim::HDIM + prim::TILE_SIZE * 2.0;
            let x2 = -((rand.gen::<f64>() * 0.3) * prim::DIM - prim::HDIM + prim::TILE_SIZE * 2.0);
            (x1, x2)
        };
        let z1 = (rand.gen::<f64>() * 0.8 + 0.1) * prim::DIM - prim::HDIM;
        let z2 = (rand.gen::<f64>() * 0.8 + 0.1) * prim::DIM - prim::HDIM;
        let y1 = terrain::get_height_at(&ground, x1, z1);
        let y2 = terrain::get_height_at(&ground, x2, z2);
        if y1 < 10.0 || y2 < 10.0 {
            continue;
        }
        if (x2 - x1) + (z2 - z1).abs() < prim::DIM * 0.5 {
            continue;
        }

        let bp = [
                    Vector3::new(x1, y1, z1),
                    Vector3::new(x2, y2, z2),
        ];
        let path = find_path(&ground, bp[0], bp[1], "base_position", 20000);

        match path {
            None    => (),
            Some(_) => return bp,
        }
    }
    panic!("Unable to find base positions!")
}

pub fn vec_to_grid(v: Vector3<f64>) -> Vector2<i64> {
    fn round(n: f64) -> i64 {
        (prim::TILE_SIZE * (n / prim::TILE_SIZE).round()) as i64
    };

    Vector2::new(round(v.x), round(v.z))
}

pub fn find_path(ground: &terrain::Ground, p1: Vector3<f64>, p2: Vector3<f64>, user: &str, limit: usize) -> Option<Path> {
    let pn1 = vec_to_grid(p1);
    let pn2 = vec_to_grid(p2);

    let graph = |n: &Vector2<i64>| {
        let mut ret = Vec::new();
        let mut j = -STEP;
        while j <= STEP {
            let mut i = -STEP;
            while i <= STEP {
                if i == 0 && j == 0 {
                    i += STEP;
                    continue;
                }

                let pos = Vector2::new(n.x + i as i64, n.y + j as i64);
                i += STEP;
                if terrain::get_height_at(ground, pos.x as f64, pos.y as f64) < 1.0 {
                    continue;
                }
                if pos.x <= -prim::HDIM as i64 || pos.x >= prim::HDIM as i64 ||
                   pos.y <= -prim::HDIM as i64 || pos.y >= prim::HDIM as i64 {
                       continue;
                }

                ret.push(pos);
            }
            j += STEP;
        }
        ret
    };

    let distance = |n1: &Vector2<i64>, n2: &Vector2<i64>| {
        let dx = n2.x - n1.x;
        let dy = n2.y - n1.y;
        ((dx * dx + dy * dy) as f64).sqrt() as u64
    };

    let heuristic = |n1: &Vector2<i64>| {
        let dx = pn2.x - n1.x;
        let dy = pn2.y - n1.y;
        (dx.abs() + dy.abs()) as u64
    };

    let goal = |n1: &Vector2<i64>| {
        let dx = pn2.x - n1.x;
        let dy = pn2.y - n1.y;
        dx.abs() < STEP / 2 && dy.abs() < STEP / 2
    };

    let mut mp = astar(graph, distance, heuristic, goal, pn1, limit);
    match mp {
        None            => (),
        Some(ref mut p) => {
            p.push(Vector2::new(p2.x as i64, p2.z as i64));
        }
    }

    match mp {
        None        => println!("[{}] No path found", user),
        Some(ref p) => {
            println!("[{}] Path found with {} nodes", user, p.len());
        }
    }
    mp
}

fn astar<G, D, H, Goal, A>(graph: G, distance: D, heuristic: H, goal: Goal, start: A, limit: usize) -> Option<Vec<A>>
    where G: Fn(&A) -> Vec<A>,
          D: Fn(&A, &A) -> u64,
          Goal: Fn(&A) -> bool,
          H: Fn(&A) -> u64,
          A: Eq + Hash + Copy {
    struct State<B> {
        cost: u64,
        node: B
    }

    impl<B> Eq for State<B> {}
    impl<B> PartialEq for State<B> {
        fn eq(&self, other: &State<B>) -> bool {
            other.cost == self.cost
        }
    }

    impl<B> Ord for State<B> {
        fn cmp(&self, other: &State<B>) -> Ordering {
            other.cost.cmp(&self.cost)
        }
    }

    impl<B> PartialOrd for State<B> {
        fn partial_cmp(&self, other: &State<B>) -> Option<Ordering> {
            Some(self.cmp(&other))
        }
    }

    let mut visited = HashSet::new();
    let mut open = BinaryHeap::new();
    let mut cost_here = HashMap::new();
    let mut parents = HashMap::new();
    let mut ret: Vec<A> = Vec::new();
    let mut curr_backtrack_node = None;

    open.push(State{cost:0, node: start});
    while !open.is_empty() {
        let current = open.pop().unwrap().node;
        if visited.len() > limit {
            break;
        }

        if visited.contains(&current) {
            continue;
        }

        if goal(&current) {
            curr_backtrack_node = Some(current);
            break;
        }
        visited.insert(current);

        let children = graph(&current);
        for child in children.into_iter() {
            if visited.contains(&child) {
                continue;
            }

            let edge_cost = distance(&current, &child);
            let this_g_cost = cost_here.get(&current).unwrap_or(&0) + edge_cost;
            let mut add_this_as_parent = true;
            {
                let already_open_child = cost_here.get(&child);
                match already_open_child {
                    Some(&cost_prev) => if cost_prev <= this_g_cost { add_this_as_parent = false; },
                    None             => (),
                }
            }
            if add_this_as_parent {
                let this_f_cost = this_g_cost + heuristic(&child);
                parents.insert(child, current);
                open.push(State{cost: this_f_cost, node: child});
                cost_here.insert(child, this_g_cost);
            }
        }
    }

    if curr_backtrack_node.is_none() {
        return None;
    }

    loop {
        match curr_backtrack_node {
            None    => break,
            Some(n) => {
                ret.push(n);
                let parent = parents.get(&n);
                match parent {
                    None     => break,
                    Some(n2) => curr_backtrack_node = Some(*n2),
                }
            }
        }
    }
    ret.reverse();
    Some(ret)
}
<commit_msg>make ai avoid coastlines<commit_after>extern crate rand;

use na::{Vector2, Vector3};
use std::collections::HashSet;
use std::collections::HashMap;
use std::hash::Hash;
use std::collections::BinaryHeap;
use std::cmp::Ordering;

use self::rand::{Rng,StdRng};

use terrain;
use prim;
use gameutil;

const STEP: i64 = prim::TILE_SIZE as i64;

pub type Path = Vec<Vector2<i64>>;

pub fn find_flag_positions(ground: &terrain::Ground, rand: &mut StdRng) -> Vec<prim::Flag> {
    let mut flag_positions = Vec::new();
    loop {
        let xp = (rand.gen::<f64>() * 0.8 + 0.1) * prim::DIM - prim::HDIM;
        let zp = (rand.gen::<f64>() * 0.8 + 0.1) * prim::DIM - prim::HDIM;
        let yp = terrain::get_height_at(&ground, xp, zp);
        if yp > 10.0 {
            flag_positions.push(Vector3::new(xp, yp, zp));
        }
        if flag_positions.len() == 10 {
            break;
        }
    }

    flag_positions.into_iter().map(|p| prim::Flag {
        position: p,
        flag_state: prim::FlagState::Free,
        flag_timer: prim::FLAG_TIMER,
    }).collect()
}

pub fn find_base_positions(ground: &terrain::Ground, rand: &mut StdRng) -> [Vector3<f64>; 2] {
    for i in 0..200 {
        let (x1, x2) = if i < 100 {
            let x1 = -prim::HDIM + prim::TILE_SIZE * 2.0;
            let x2 =  prim::HDIM - prim::TILE_SIZE * 2.0;
            (x1, x2)
        } else {
            let x1 =   (rand.gen::<f64>() * 0.3) * prim::DIM - prim::HDIM + prim::TILE_SIZE * 2.0;
            let x2 = -((rand.gen::<f64>() * 0.3) * prim::DIM - prim::HDIM + prim::TILE_SIZE * 2.0);
            (x1, x2)
        };
        let z1 = (rand.gen::<f64>() * 0.8 + 0.1) * prim::DIM - prim::HDIM;
        let z2 = (rand.gen::<f64>() * 0.8 + 0.1) * prim::DIM - prim::HDIM;
        let y1 = terrain::get_height_at(&ground, x1, z1);
        let y2 = terrain::get_height_at(&ground, x2, z2);
        if y1 < 10.0 || y2 < 10.0 {
            continue;
        }
        if (x2 - x1) + (z2 - z1).abs() < prim::DIM * 0.5 {
            continue;
        }

        let bp = [
                    Vector3::new(x1, y1, z1),
                    Vector3::new(x2, y2, z2),
        ];
        let path = find_path(&ground, bp[0], bp[1], "base_position", 20000);

        match path {
            None    => (),
            Some(_) => return bp,
        }
    }
    panic!("Unable to find base positions!")
}

pub fn vec_to_grid(v: Vector3<f64>) -> Vector2<i64> {
    fn round(n: f64) -> i64 {
        (prim::TILE_SIZE * (n / prim::TILE_SIZE).round()) as i64
    };

    Vector2::new(round(v.x), round(v.z))
}

pub fn find_path(ground: &terrain::Ground, p1: Vector3<f64>, p2: Vector3<f64>, user: &str, limit: usize) -> Option<Path> {
    let pn1 = vec_to_grid(p1);
    let pn2 = vec_to_grid(p2);

    let graph = |n: &Vector2<i64>| {
        let mut ret = Vec::new();
        let mut j = -STEP;
        while j <= STEP {
            let mut i = -STEP;
            while i <= STEP {
                if i == 0 && j == 0 {
                    i += STEP;
                    continue;
                }

                let pos = Vector2::new(n.x + i as i64, n.y + j as i64);
                i += STEP;
                if terrain::get_height_at(ground, pos.x as f64, pos.y as f64) < 1.0 {
                    continue;
                }
                if pos.x <= -prim::HDIM as i64 || pos.x >= prim::HDIM as i64 ||
                   pos.y <= -prim::HDIM as i64 || pos.y >= prim::HDIM as i64 {
                       continue;
                }

                ret.push(pos);
            }
            j += STEP;
        }
        ret
    };

    let calc_dist = |n1: &Vector2<i64>, n2: &Vector2<i64>| {
        let dx = n2.x - n1.x;
        let dy = n2.y - n1.y;
        ((dx * dx + dy * dy) as f64).sqrt() * 0.001
    };

    let distance = |n1: &Vector2<i64>, n2: &Vector2<i64>| {
        let hgt = terrain::get_height_at(ground, n2.x as f64, n2.y as f64);
        let risk_to_coast = 100.0 - gameutil::clamp(0.0, 100.0, hgt);
        let mul = 1.0 + risk_to_coast * 0.1;
        (calc_dist(n1, n2) * mul) as u64
    };

    let heuristic = |n1: &Vector2<i64>| {
        calc_dist(n1, &pn2) as u64
    };

    let goal = |n1: &Vector2<i64>| {
        let dx = pn2.x - n1.x;
        let dy = pn2.y - n1.y;
        dx.abs() < STEP / 2 && dy.abs() < STEP / 2
    };

    let mut mp = astar(graph, distance, heuristic, goal, pn1, limit);
    match mp {
        None            => (),
        Some(ref mut p) => {
            p.push(Vector2::new(p2.x as i64, p2.z as i64));
        }
    }

    match mp {
        None        => println!("[{}] No path found", user),
        Some(ref p) => {
            println!("[{}] Path found with {} nodes", user, p.len());
        }
    }
    mp
}

fn astar<G, D, H, Goal, A>(graph: G, distance: D, heuristic: H, goal: Goal, start: A, limit: usize) -> Option<Vec<A>>
    where G: Fn(&A) -> Vec<A>,
          D: Fn(&A, &A) -> u64,
          Goal: Fn(&A) -> bool,
          H: Fn(&A) -> u64,
          A: Eq + Hash + Copy {
    struct State<B> {
        cost: u64,
        node: B
    }

    impl<B> Eq for State<B> {}
    impl<B> PartialEq for State<B> {
        fn eq(&self, other: &State<B>) -> bool {
            other.cost == self.cost
        }
    }

    impl<B> Ord for State<B> {
        fn cmp(&self, other: &State<B>) -> Ordering {
            other.cost.cmp(&self.cost)
        }
    }

    impl<B> PartialOrd for State<B> {
        fn partial_cmp(&self, other: &State<B>) -> Option<Ordering> {
            Some(self.cmp(&other))
        }
    }

    let mut visited = HashSet::new();
    let mut open = BinaryHeap::new();
    let mut cost_here = HashMap::new();
    let mut parents = HashMap::new();
    let mut ret: Vec<A> = Vec::new();
    let mut curr_backtrack_node = None;

    open.push(State{cost:0, node: start});
    while !open.is_empty() {
        let current = open.pop().unwrap().node;
        if visited.len() > limit {
            break;
        }

        if visited.contains(&current) {
            continue;
        }

        if goal(&current) {
            curr_backtrack_node = Some(current);
            break;
        }
        visited.insert(current);

        let children = graph(&current);
        for child in children.into_iter() {
            if visited.contains(&child) {
                continue;
            }

            let edge_cost = distance(&current, &child);
            let this_g_cost = cost_here.get(&current).unwrap_or(&0) + edge_cost;
            let mut add_this_as_parent = true;
            {
                let already_open_child = cost_here.get(&child);
                match already_open_child {
                    Some(&cost_prev) => if cost_prev <= this_g_cost { add_this_as_parent = false; },
                    None             => (),
                }
            }
            if add_this_as_parent {
                let this_f_cost = this_g_cost + heuristic(&child);
                parents.insert(child, current);
                open.push(State{cost: this_f_cost, node: child});
                cost_here.insert(child, this_g_cost);
            }
        }
    }

    if curr_backtrack_node.is_none() {
        return None;
    }

    loop {
        match curr_backtrack_node {
            None    => break,
            Some(n) => {
                ret.push(n);
                let parent = parents.get(&n);
                match parent {
                    None     => break,
                    Some(n2) => curr_backtrack_node = Some(*n2),
                }
            }
        }
    }
    ret.reverse();
    Some(ret)
}
<|endoftext|>


<commit_before>use ::korome::*;
use ::obj::*;

use std::ops::Deref;

pub struct SpaceShooter<'a>{
    objs: ObjSystem<'a>,
    player: &'a Texture,
    sun   : &'a Texture,
    planet: &'a Texture,
    arrow : &'a Texture,
    laser : &'a Texture,
    oobb  : OutOfBoundsBehaviour,
    dbg_a : bool,
    deltas: Option<Vec<f64>>,
}

use std::ops::Drop;

impl<'a> Drop for SpaceShooter<'a>{
    fn drop(&mut self){
        if let Some(ref mut deltas) = self.deltas{
            use std::fs::File;
            use std::io::{Write, BufWriter};

            let file = File::create("deltas.log").unwrap();
            let mut writer = BufWriter::new(file);

            let (len, mut min, mut max, mut sum) = (deltas.len(), ::std::f64::MAX, 0f64, 0.);

            // Skip the first two 'cause they're normally too high.
            for n in deltas.drain(..).skip(2){
                min = min.min(n);
                max = max.max(n);
                sum += n;

                writer.write_fmt(format_args!("{}s ~ {}fps\n", n, 1./n)).unwrap()
            }

            let avg = sum/len as f64;

            println!("\nTotal  : {}s", sum);
            println!("Best   : {}s ~ {}fps", min, 1./min);
            println!("Worst  : {}s ~ {}fps", max, 1./max);
            println!("Average: {}s ~ {}fps", avg, 1./avg);
        }
    }
}

impl<'a> SpaceShooter<'a>{
    pub fn new(deltas: bool, planet: &'a Texture, player: &'a Texture, sun: &'a Texture, arrow: &'a Texture, laser: &'a Texture) -> Self{
        let mut objs = ObjSystem::new();
        objs.players.push(new_player(player));

        SpaceShooter{
            player: player,
            sun   : sun,
            objs  : objs,
            planet: planet,
            arrow : arrow,
            laser : laser,
            oobb  : Bounce,
            dbg_a : true,
            deltas: if deltas{Some(Vec::with_capacity(10_000))}else{None},
        }
    }

    fn add_player(&mut self){
        self.objs.players.push(new_player(self.player))
    }

    fn add_planet(&mut self, pos: Vector2<f32>){
        self.objs.bodies.push(Object::new(self.planet, pos, 50, 64., 1e13));
    }

    fn add_sun(&mut self, pos: Vector2<f32>){
        self.objs.bodies.push(Object::new(self.sun, pos, 100, 64., 2e14));
    }

    fn add_laser(&mut self, pos: Vector2<f32>, direction: f32){
        self.objs.projectiles.push(new_laser(self.laser, pos, direction));
    }
}

macro_rules! when_released{
    ($info:expr; $($key:ident => $b:block),+) => {
        for ke in $info.get_key_events(){
            match *ke{
                $((false, VirtualKeyCode::$key) => $b,)+
                _ => ()
            }
        }
    };
    ($info:expr;MOUSE; $($key:ident => $b:block),+) => {
        for ke in $info.get_mouse_events(){
            match *ke{
                $((false, MouseButton::$key) => $b,)+
                _ => ()
            }
        }
    };
}

impl<'a> Game for SpaceShooter<'a>{
    fn frame(&mut self, info: FrameInfo, mut drawer: Drawer) -> GameUpdate{
        self.deltas.as_mut().map(|d| d.push(info.delta));

        when_released!{info;
            Escape => {
                return GameUpdate::nothing().set_close(true)
            },
            M => {
                self.oobb = match self.oobb{
                    Bounce => Stop,
                    Stop => Wrap,
                    Wrap => Bounce
                };
                println!("OOBB changed to: {:?}", self.oobb);
            },
            N => {
                self.dbg_a = !self.dbg_a;
                println!("Turned debug arrows {}", if self.dbg_a{"on"}else{"off"});
            },
            C => {
                self.objs.bodies.clear();
                println!("Bodies cleared, new count: {}", self.objs.len());
            },
            K => {
                self.objs.projectiles.clear();
                println!("Projectiles cleared, new count: {}", self.objs.len());
            },
            O => {
                self.objs.players.clear();
                println!("Players cleared, new count: {}", self.objs.len());
            },
            P => {
                self.add_player();
                println!("Player added, new count: {}", self.objs.len());
            },
            Q => {
                let ObjSystem{players: ref a, bodies: ref b, projectiles: ref c} = self.objs;

                println!("{}::{}:{}:{}", self.deltas.as_ref().map(Vec::capacity).unwrap_or_default(), a.capacity(), b.capacity(), c.capacity());
            },
            Z => {
                let ObjSystem{players: ref mut a, bodies: ref mut b, projectiles: ref mut c} = self.objs;
                print!("{};{};{}->", a.capacity(), b.capacity(), c.capacity());
                a.shrink_to_fit();
                b.shrink_to_fit();
                c.shrink_to_fit();
                println!("{};{};{}", a.capacity(), b.capacity(), c.capacity());
            },
            X => {
                for x in self.objs.players.iter().chain(self.objs.bodies.iter()).chain(self.objs.projectiles.iter()){
                    println!("{:?}", &**x);
                }
            },
            Space => {
                let iter: Vec<_> = self.objs.players.iter().map(|x| {
                    let &InnerObject{pos, rot,..} = x.deref();
                    (pos, rot)
                }).collect();
                for &(pos, rot) in &iter{
                    self.add_laser(pos, rot);
                }
                println!("Laser{} added, new count: {}", if self.objs.players.len() == 1{""}else{"s"}, self.objs.len());
            }
        }

        when_released!{info;MOUSE;
            Left => {
                self.add_planet(info.mousepos.into());
                println!("Object added, new count: {}", self.objs.len());
            },
            Right => {
                self.add_sun(info.mousepos.into());
                println!("Object added, new count: {}", self.objs.len());
            }
        }

        drawer.clear(0., 0., 0.);
        self.objs.update(&info, &mut drawer, if self.dbg_a{Some(self.arrow)}else{None}, self.oobb);

        GameUpdate::nothing()
    }
}

#[derive(Default)]
pub struct ObjSystem<'a>{
    bodies     : Vec<Object<'a>>,
    players    : Vec<Object<'a>>,
    projectiles: Vec<Object<'a>>,
}

const G: f32 = 6.671281903963040991511534289e-11;
#[allow(dead_code)]
const G_OLD: f32 = 6.67384E-11;

use OutOfBoundsBehaviour::*;
use OutOfBoundsBehaviour;

fn gravity(x: &InnerObject, y: &InnerObject) -> Vector2<f32>{
    let r = x.pos.distance_to(y.pos);
    let g_force = (G * x.mass * y.mass) / (r * r);
    let dir_towards_body = x.pos.direction_to(y.pos);

    let force = Vector2::unit_vector(dir_towards_body) * g_force;
    if !force.is_nan(){
        force
    }else{
        Vector2(0., 0.)
    }
}

fn inners(x: &Vec<Object>) -> Vec<InnerObject>{
    x.iter().map(|x| **x).collect()
}

impl<'a> ObjSystem<'a>{
    #[inline]
    pub fn new() -> Self{
        Default::default()
    }

    pub fn update(&mut self, info: &FrameInfo, drawer: &mut Drawer, arrow: Option<&Texture>, oobb: OutOfBoundsBehaviour){
        let bs = inners(&self.bodies);
        let wh = drawer.graphics.get_h_size();
        let delta = info.delta as f32;

        let (mut acceleration, mut rot) = (0., 0.);
        is_down!{info;
            D, Right => {
                rot -= 2. * delta
            },
            A, Left => {
                rot += 2. * delta
            },
            S, Down => {
                acceleration -= 52. * delta
            },
            W, Up => {
                acceleration += 52. * delta;
            },
            LShift => {
                acceleration *= 7.;
            }
        }

        for player in self.players.iter_mut(){
            let net_grav = bs.iter().fold(Vector2(0., 0.), |n_g, y| n_g + gravity(&player, y));

            let propulsion_force = Vector2::<f32>::unit_vector(player.rot) * acceleration;

            player.rot += rot;
            player.vel += propulsion_force;

            player.update(info, net_grav, wh, oobb);
            player.draw(drawer, arrow, net_grav, net_grav+propulsion_force);
        }
        for (i, body) in self.bodies.iter_mut().enumerate(){
            let net_grav = bs.iter().enumerate().filter_map(|(j, y)| if i==j{None}else{Some(y)}).fold(Vector2(0., 0.), |n_g, y| n_g + gravity(&body, y));

            body.update(info, net_grav, wh, oobb);
            body.draw(drawer, arrow, net_grav, net_grav);
        }
        for projectile in self.projectiles.iter_mut(){
            let net_grav = bs.iter().fold(Vector2(0., 0.), |n_g, y| n_g + gravity(&projectile, y));

            projectile.rot = projectile.vel.direction();

            projectile.update(info, net_grav, wh, oobb);
            projectile.draw(drawer, arrow, net_grav, net_grav);
        }
    }

    pub fn len(&self) -> usize{
        self.players.len() + self.bodies.len() + self.projectiles.len()
    }
}
<commit_msg>Make lasers damage things<commit_after>use ::korome::*;
use ::obj::*;

use std::ops::Deref;

pub struct SpaceShooter<'a>{
    objs: ObjSystem<'a>,
    player: &'a Texture,
    sun   : &'a Texture,
    planet: &'a Texture,
    arrow : &'a Texture,
    laser : &'a Texture,
    oobb  : OutOfBoundsBehaviour,
    dbg_a : bool,
    deltas: Option<Vec<f64>>,
}

use std::ops::Drop;

impl<'a> Drop for SpaceShooter<'a>{
    fn drop(&mut self){
        if let Some(ref mut deltas) = self.deltas{
            use std::fs::File;
            use std::io::{Write, BufWriter};

            let file = File::create("deltas.log").unwrap();
            let mut writer = BufWriter::new(file);

            let (len, mut min, mut max, mut sum) = (deltas.len(), ::std::f64::MAX, 0f64, 0.);

            // Skip the first two 'cause they're normally too high.
            for n in deltas.drain(..).skip(2){
                min = min.min(n);
                max = max.max(n);
                sum += n;

                writer.write_fmt(format_args!("{}s ~ {}fps\n", n, 1./n)).unwrap()
            }

            let avg = sum/len as f64;

            println!("\nTotal  : {}s", sum);
            println!("Best   : {}s ~ {}fps", min, 1./min);
            println!("Worst  : {}s ~ {}fps", max, 1./max);
            println!("Average: {}s ~ {}fps", avg, 1./avg);
        }
    }
}

impl<'a> SpaceShooter<'a>{
    pub fn new(deltas: bool, planet: &'a Texture, player: &'a Texture, sun: &'a Texture, arrow: &'a Texture, laser: &'a Texture) -> Self{
        let mut objs = ObjSystem::new();
        objs.players.push(new_player(player));

        SpaceShooter{
            player: player,
            sun   : sun,
            objs  : objs,
            planet: planet,
            arrow : arrow,
            laser : laser,
            oobb  : Bounce,
            dbg_a : true,
            deltas: if deltas{Some(Vec::with_capacity(10_000))}else{None},
        }
    }

    fn add_player(&mut self){
        self.objs.players.push(new_player(self.player))
    }

    fn add_planet(&mut self, pos: Vector2<f32>){
        self.objs.bodies.push(Object::new(self.planet, pos, 50, 64., 1e13));
    }

    fn add_sun(&mut self, pos: Vector2<f32>){
        self.objs.bodies.push(Object::new(self.sun, pos, 100, 64., 2e14));
    }

    fn add_laser(&mut self, pos: Vector2<f32>, direction: f32){
        self.objs.projectiles.push(new_laser(self.laser, pos, direction));
    }
}

macro_rules! when_released{
    ($info:expr; $($key:ident => $b:block),+) => {
        for ke in $info.get_key_events(){
            match *ke{
                $((false, VirtualKeyCode::$key) => $b,)+
                _ => ()
            }
        }
    };
    ($info:expr;MOUSE; $($key:ident => $b:block),+) => {
        for ke in $info.get_mouse_events(){
            match *ke{
                $((false, MouseButton::$key) => $b,)+
                _ => ()
            }
        }
    };
}

impl<'a> Game for SpaceShooter<'a>{
    fn frame(&mut self, info: FrameInfo, mut drawer: Drawer) -> GameUpdate{
        self.deltas.as_mut().map(|d| d.push(info.delta));

        when_released!{info;
            Escape => {
                return GameUpdate::nothing().set_close(true)
            },
            M => {
                self.oobb = match self.oobb{
                    Bounce => Stop,
                    Stop => Wrap,
                    Wrap => Bounce
                };
                println!("OOBB changed to: {:?}", self.oobb);
            },
            N => {
                self.dbg_a = !self.dbg_a;
                println!("Turned debug arrows {}", if self.dbg_a{"on"}else{"off"});
            },
            C => {
                self.objs.bodies.clear();
                println!("Bodies cleared, new count: {}", self.objs.len());
            },
            K => {
                self.objs.projectiles.clear();
                println!("Projectiles cleared, new count: {}", self.objs.len());
            },
            O => {
                self.objs.players.clear();
                println!("Players cleared, new count: {}", self.objs.len());
            },
            P => {
                self.add_player();
                println!("Player added, new count: {}", self.objs.len());
            },
            Q => {
                let ObjSystem{players: ref a, bodies: ref b, projectiles: ref c} = self.objs;

                println!("{}::{}:{}:{}", self.deltas.as_ref().map(Vec::capacity).unwrap_or_default(), a.capacity(), b.capacity(), c.capacity());
            },
            Z => {
                let ObjSystem{players: ref mut a, bodies: ref mut b, projectiles: ref mut c} = self.objs;
                print!("{};{};{}->", a.capacity(), b.capacity(), c.capacity());
                a.shrink_to_fit();
                b.shrink_to_fit();
                c.shrink_to_fit();
                println!("{};{};{}", a.capacity(), b.capacity(), c.capacity());
            },
            X => {
                for x in self.objs.players.iter().chain(self.objs.bodies.iter()).chain(self.objs.projectiles.iter()){
                    println!("{:?}", &**x);
                }
            },
            Space => {
                let iter: Vec<_> = self.objs.players.iter().map(|x| {
                    let &InnerObject{pos, rot,..} = x.deref();
                    (pos, rot)
                }).collect();
                for &(pos, rot) in &iter{
                    self.add_laser(pos, rot);
                }
                println!("Laser{} added, new count: {}", if self.objs.players.len() == 1{""}else{"s"}, self.objs.len());
            }
        }

        when_released!{info;MOUSE;
            Left => {
                self.add_planet(info.mousepos.into());
                println!("Object added, new count: {}", self.objs.len());
            },
            Right => {
                self.add_sun(info.mousepos.into());
                println!("Object added, new count: {}", self.objs.len());
            }
        }

        drawer.clear(0., 0., 0.);
        self.objs.update(&info, &mut drawer, if self.dbg_a{Some(self.arrow)}else{None}, self.oobb);

        GameUpdate::nothing()
    }
}

#[derive(Default)]
pub struct ObjSystem<'a>{
    bodies     : Vec<Object<'a>>,
    players    : Vec<Object<'a>>,
    projectiles: Vec<Object<'a>>,
}

const G: f32 = 6.671281903963040991511534289e-11;
#[allow(dead_code)]
const G_OLD: f32 = 6.67384E-11;

use OutOfBoundsBehaviour::*;
use OutOfBoundsBehaviour;

fn gravity(x: &InnerObject, y: &InnerObject) -> Vector2<f32>{
    let r = x.pos.distance_to(y.pos);
    let g_force = (G * x.mass * y.mass) / (r * r);
    let dir_towards_body = x.pos.direction_to(y.pos);

    let force = Vector2::unit_vector(dir_towards_body) * g_force;
    if !force.is_nan(){
        force
    }else{
        Vector2(0., 0.)
    }
}

fn inners(x: &Vec<Object>) -> Vec<InnerObject>{
    x.iter().map(|x| **x).collect()
}

impl<'a> ObjSystem<'a>{
    #[inline]
    pub fn new() -> Self{
        Default::default()
    }

    pub fn update(&mut self, info: &FrameInfo, drawer: &mut Drawer, arrow: Option<&Texture>, oobb: OutOfBoundsBehaviour){
        let bs = inners(&self.bodies);
        let wh = drawer.graphics.get_h_size();
        let delta = info.delta as f32;

        let (mut acceleration, mut rot) = (0., 0.);
        is_down!{info;
            D, Right => {
                rot -= 2. * delta
            },
            A, Left => {
                rot += 2. * delta
            },
            S, Down => {
                acceleration -= 52. * delta
            },
            W, Up => {
                acceleration += 52. * delta;
            },
            LShift => {
                acceleration *= 7.;
            }
        }

        let mut indices_to_die = [Vec::new(), Vec::new(), Vec::new()];

        for (i, player) in self.players.iter_mut().enumerate(){
            let net_grav = bs.iter().fold(Vector2(0., 0.), |n_g, y| n_g + gravity(&player, y));

            let propulsion_force = Vector2::<f32>::unit_vector(player.rot) * acceleration;

            player.rot += rot;
            player.vel += propulsion_force;

            player.update(info, net_grav, wh, oobb);
            player.draw(drawer, arrow, net_grav, net_grav+propulsion_force);

            if player.health == 0{
                indices_to_die[0].push(i);
            }
        }
        for (i, body) in self.bodies.iter_mut().enumerate(){
            let net_grav = bs.iter().enumerate().filter_map(|(j, y)| if i==j{None}else{Some(y)}).fold(Vector2(0., 0.), |n_g, y| n_g + gravity(&body, y));

            body.update(info, net_grav, wh, oobb);
            body.draw(drawer, arrow, net_grav, net_grav);

            if body.health == 0{
                indices_to_die[1].push(i);
            }
        }
        for (i, projectile) in self.projectiles.iter_mut().enumerate(){
            let net_grav = bs.iter().fold(Vector2(0., 0.), |n_g, y| n_g + gravity(&projectile, y));

            projectile.rot = projectile.vel.direction();

            projectile.update(info, net_grav, wh, oobb);
            projectile.draw(drawer, arrow, net_grav, net_grav);

            for player in self.players.iter_mut(){
                if projectile.pos.distance_to(player.pos) - player.diameter/2. <= 16.{
                    player.health = player.health.saturating_sub(1);
                    indices_to_die[2].push(i);
                }
            }
            for body in self.bodies.iter_mut(){
                if projectile.pos.distance_to(body.pos) - body.diameter/2. <= 16.{
                    body.health = body.health.saturating_sub(1);
                    indices_to_die[2].push(i);
                }
            }
        }
        for v in &mut indices_to_die{
            v.sort();
            v.dedup();
        }
        for &i in indices_to_die[0].iter().rev(){
            self.players.remove(i);
        }
        for &i in indices_to_die[1].iter().rev(){
            self.bodies.remove(i);
        }
        for &i in indices_to_die[2].iter().rev(){
            self.projectiles.remove(i);
        }
    }

    pub fn len(&self) -> usize{
        self.players.len() + self.bodies.len() + self.projectiles.len()
    }
}
<|endoftext|>