<commit_before>use std::vec::Vec;

use env::Env;
use super::general::StatusedTypeId;
use gobjects::*;
use library;

pub fn analyze(env: &Env, type_: &library::Class) -> (Vec<StatusedTypeId>, bool) {
    let mut parents = Vec::new();
    let mut has_ignored_parents = false;

    for &parent_tid in &type_.parents {
        let parent_type = env.type_(parent_tid).to_class();

        let default_object: GObject = Default::default();
        let gobject = env.config.objects.get(&parent_tid.full_name(&env.library))
            .unwrap_or(&default_object);

        parents.push(StatusedTypeId{
            type_id: parent_tid,
            name: parent_type.name.clone(),
            status: gobject.status,
        });

        if gobject.status == GStatus::Ignore { has_ignored_parents = true; }

        if parent_type.glib_type_name == "GtkWidget" { break }
    }

    (parents, has_ignored_parents)
}
<commit_msg>Generate parents upcasts from root<commit_after>use std::vec::Vec;

use env::Env;
use super::general::StatusedTypeId;
use gobjects::*;
use library;

pub fn analyze(env: &Env, type_: &library::Class) -> (Vec<StatusedTypeId>, bool) {
    let mut parents = Vec::new();
    let mut has_ignored_parents = false;

    for &parent_tid in &type_.parents {
        let parent_type = env.type_(parent_tid).to_class();

        let default_object: GObject = Default::default();
        let gobject = env.config.objects.get(&parent_tid.full_name(&env.library))
            .unwrap_or(&default_object);

        parents.push(StatusedTypeId{
            type_id: parent_tid,
            name: parent_type.name.clone(),
            status: gobject.status,
        });

        if gobject.status == GStatus::Ignore { has_ignored_parents = true; }

        if parent_type.glib_type_name == "GtkWidget" { break }
    }
    parents.reverse();

    (parents, has_ignored_parents)
}
<|endoftext|>


<commit_before><commit_msg>Body constructor doc examples<commit_after><|endoftext|>


<commit_before>use super::ConcurrentDevice;

extern crate memmap;
use z80e_core_rust::{ IoDevice };
use self::memmap::{ Mmap };
pub use self::memmap::{ MmapView, Protection };

use std::sync::{ Arc, Condvar, Mutex };
use std::sync::atomic::{ AtomicBool, AtomicUsize, Ordering };
use std::io::{ self, ErrorKind, Write };
use std::path::Path;
use std::{ str, mem, ptr };

// Sector size must be a power of two
const SECTOR_SIZE: u16 = 128;

// Max disk must be a power of two <= 16. If changed, you must insert/delete Nones in DiskController::run()
const MAX_DISK: u8 = 16;

// Status port bitflags.
//     8-bit values, but "must" be usize to avoid overly-verbose casts for AtomicUsize calls.
const COMMAND_READY: usize = 1 << 0;
const DATA_READY: usize = 1 << 1;
const ERROR: usize = 1 << 7;

// Commands.
const NOP: u8 = 0;
const SEL_DSK: u8 = 1;
const SEL_TRK: u8 = 2;
const SEL_SEC: u8 = 3;
const READ: u8 = 4;
const WRITE: u8 = 5;
const RESET: u8 = 6;
const OPEN: u8 = 7;
const CLOSE: u8 = 8;
const DPB: u8 = 9;

#[derive(Clone)]
pub struct DiskController {
    pub status: Arc<AtomicUsize>,
    command_cond: Arc<Condvar>,
    buffer: Arc<Mutex<Buffer>>,
    parameters: Arc<Mutex<Parameters>>,
}

impl DiskController {
    pub fn new() -> DiskController {
        DiskController {
            status: Arc::new(AtomicUsize::new(DATA_READY)),
            command_cond: Arc::new(Condvar::new()),
            buffer: Arc::new(Mutex::new(Buffer::new())),
            parameters: Arc::new(Mutex::new(Parameters::new())),
        }
    }
    pub fn status_port(&self) -> StatusPort {
        StatusPort::new(self.clone())
    }
    pub fn data_port(&self) -> DataPort {
        DataPort::new(self.clone())
    }
}

struct Buffer {
    bytes: [u8; SECTOR_SIZE as usize],
    i: u16,
}

impl Buffer {
    fn new() -> Buffer {
        Buffer {
            bytes: [0; SECTOR_SIZE as usize],
            i: 0,
        }
    }
}

struct Parameters {
    disk: u8,
    track: u16,
    sector: u16,
    command: u8,
    do_command: bool,
}

impl Parameters {
    fn new() -> Parameters {
        Parameters {
            disk: 0,
            track: 0,
            sector: 0,
            command: NOP,
            do_command: false,
        }
    }
}

pub struct StatusPort {
    controller: DiskController,
}

impl StatusPort {
    fn new(controller: DiskController) -> StatusPort {
        StatusPort {
            controller: controller,
        }
    }
}

impl IoDevice for StatusPort {
    fn read_in(&self) -> u8 {
        (self.controller.status.load(Ordering::SeqCst) & 0xff) as u8
    }
    fn write_out(&mut self, value: u8) {
        if (self.controller.status.fetch_and(!COMMAND_READY, Ordering::SeqCst) & COMMAND_READY) != 0 {
            let mut params = self.controller.parameters.lock().unwrap();
            params.command = value;
            params.do_command = true;
            self.controller.command_cond.notify_one();
        } else {
            self.controller.status.fetch_or(COMMAND_READY | ERROR, Ordering::SeqCst);
            let _ = writeln!(io::stderr(), "disk: Attempted to write command register when not ready.");
        }
    }
}

pub struct DataPort {
    controller: DiskController,
}

impl DataPort {
    fn new(controller: DiskController) -> DataPort {
        DataPort {
            controller: controller,
        }
    }
}

impl IoDevice for DataPort {
    fn read_in (&self) -> u8 {
        let byte = if (self.controller.status.fetch_and(!DATA_READY, Ordering::SeqCst) & DATA_READY) != 0 {
            let byte;
            {
                let mut buffer = self.controller.buffer.lock().unwrap();
                buffer.i = ((buffer.i as usize + 1) & (SECTOR_SIZE - 1) as usize) as u16;
                byte = buffer.bytes[buffer.i as usize];
            }
            byte
        } else {
            self.controller.status.fetch_or(ERROR, Ordering::SeqCst);
            let _ = writeln!(io::stderr(), "disk: Attempted to read data register when not ready.");
            0
        };
        self.controller.status.fetch_or(DATA_READY, Ordering::SeqCst);
        byte
    }
    fn write_out(&mut self, value: u8) {
        if (self.controller.status.fetch_and(!DATA_READY, Ordering::SeqCst) & DATA_READY) != 0 {
            {
                let mut buffer = self.controller.buffer.lock().unwrap();
                buffer.bytes[buffer.i as usize] = value;
                buffer.i = ((buffer.i as usize + 1) & (SECTOR_SIZE - 1) as usize) as u16;
            }
        } else {
            self.controller.status.fetch_or(ERROR, Ordering::SeqCst);
            let _ = writeln!(io::stderr(), "disk: Attempted to write data register when not ready.");
        }
        self.controller.status.fetch_or(DATA_READY, Ordering::SeqCst);
    }
}

pub struct Disk {
    view: MmapView,
    pub tracks: u16,
    pub spt: u16,
    dpb: [u8; 17],
}

impl Disk {
    pub fn open<T: AsRef<Path>>(path: &T, protection: Protection) -> io::Result<Disk> {
        let file = try!(Mmap::open_path(path, protection)).into_view();
        let (header, image) = try!(file.split_at(128));
        let header = unsafe { header.as_slice() };
        if match str::from_utf8(&header[0..10]) {
            Ok(x) => x,
            Err(_) => return Err(io::Error::new(ErrorKind::InvalidData, "Invalid image header encoding.")),
        }!= "<CPM_Disk>" {
            return Err(io::Error::new(ErrorKind::InvalidData, "Not a valid disk image."));
        }
        let mut dpb: [u8; 17] = [0; 17];
        for i in 0..17 {
            dpb[i] = header[32 + i];
        }
        let spt: u16 = (dpb[0] as u16) | ((dpb[1] as u16) << 8);
        let bsh: u16 = dpb[2] as u16;
        let dsm: u16 = (dpb[5] as u16) | ((dpb[6] as u16) << 8);
        let off: u16 = (dpb[13] as u16) | ((dpb[14] as u16) << 8);
        let tracks: u16 = (dsm + 1) * (1 << bsh) / spt + off;
        Ok(Disk {
            view: image,
            tracks: tracks,
            spt: spt,
            dpb: dpb
        })
    }
}

impl ConcurrentDevice for DiskController {
    fn run(&mut self, die: Arc<AtomicBool>) {
        let mut disks = unsafe {
            let mut disks: [Option<Disk>; MAX_DISK as usize] = mem::uninitialized();
            for disk in disks.iter_mut() {
                ptr::write(disk, None);
            };
            disks
        };
        let mut parameters = self.parameters.lock().unwrap();
        loop {
            if die.load(Ordering::Acquire) { break; }
            parameters.do_command = false;
            self.status.fetch_or(COMMAND_READY, Ordering::SeqCst);
            while !(*parameters).do_command {
                parameters = self.command_cond.wait(parameters).unwrap();
            }
            if die.load(Ordering::Acquire) { break; }
            let status = self.status.fetch_and(!DATA_READY, Ordering::SeqCst);
            if (status & ERROR) != 0 && parameters.command != RESET {
                self.status.fetch_or(DATA_READY, Ordering::SeqCst);
                continue;
            };
            {
                let mut buffer = self.buffer.lock().unwrap();
                buffer.i = 0;
                match parameters.command {
                    NOP => (),
                    SEL_DSK => {
                        if buffer.bytes[0] < MAX_DISK {
                            parameters.disk = buffer.bytes[0];
                        } else {
                            self.status.fetch_or(ERROR, Ordering::SeqCst);
                        }
                    },
                    SEL_TRK => {
                        let track = buffer.bytes[0] as u16 | ((buffer.bytes[1] as u16) << 8);
                        match disks[parameters.disk as usize] {
                            Some(ref disk) => {
                                if track < disk.tracks {
                                    parameters.track = track;
                                } else {
                                    self.status.fetch_or(ERROR, Ordering::SeqCst);
                                }
                            },
                            None => {
                                self.status.fetch_or(ERROR, Ordering::SeqCst);
                            }
                        }
                    },
                    SEL_SEC => {
                        match disks[parameters.disk as usize] {
                            Some(ref disk) => {
                                let sector = buffer.bytes[0] as u16 | ((buffer.bytes[1] as u16) << 8);
                                if sector < disk.spt {
                                    parameters.sector = sector;
                                } else {
                                    self.status.fetch_or(ERROR, Ordering::SeqCst);
                                }
                            },
                            None => {
                                self.status.fetch_or(ERROR, Ordering::SeqCst);
                            },
                        }
                    },
                    READ => {
                        match disks[parameters.disk as usize] {
                            Some(ref disk) => {
                                let off = ((parameters.track as usize * disk.spt as usize)
                                           + parameters.sector as usize) * SECTOR_SIZE as usize;
                                let bytes = unsafe { disk.view.as_slice() };
                                for (i, byte) in buffer.bytes.iter_mut().enumerate() {
                                    *byte = bytes[off + i];
                                }
                            },
                            None => {
                                self.status.fetch_or(ERROR, Ordering::SeqCst);
                            },
                        }
                    },
                    WRITE => {
                        match disks[parameters.disk as usize] {
                            Some(ref mut disk) => {
                                let off = ((parameters.track as usize * disk.spt as usize)
                                           + parameters.sector as usize) * SECTOR_SIZE as usize;
                                let bytes = unsafe { disk.view.as_mut_slice() };
                                for (i, byte) in buffer.bytes.iter().enumerate() {
                                    bytes[off + i] = *byte;
                                };
                            },
                            None => {
                                self.status.fetch_or(ERROR, Ordering::SeqCst);
                            },
                        }

                    }
                    RESET => {
                        parameters.disk = 0;
                        parameters.track = 0;
                        parameters.sector = 0;
                        parameters.command = NOP;
                        self.status.fetch_and(!ERROR, Ordering::SeqCst);
                    },
                    OPEN => {
                        match str::from_utf8(buffer.bytes.split(|a| *a == 0).next().unwrap()) {
                            Ok(file_name) => {
                                match Disk::open(&file_name, Protection::ReadWrite) {
                                    Ok(disk) => {
                                        disks[parameters.disk as usize] = Some(disk);
                                    },
                                    Err(err) => {
                                        let mut stderr = io::stderr();
                                        let _ = writeln!(stderr, "disk: Failed to open file: {}", file_name);
                                        let _ = writeln!(stderr, "Error:\n\t{}", err);
                                        self.status.fetch_or(ERROR, Ordering::SeqCst);
                                    },
                                }
                            },
                            Err(err) => {
                                let _ = write!(io::stderr(), "disk: Bad UTF-8 in file name.\nError:\n\t{}\n", err);
                                self.status.fetch_or(ERROR, Ordering::SeqCst);
                            }
                        }
                    },
                    CLOSE => {
                        disks[parameters.disk as usize] = None;
                    },
                    DPB => {
                        match disks[parameters.disk as usize] {
                            Some(ref disk) => {
                                for (a, b) in disk.dpb.iter().zip(buffer.bytes.iter_mut()) {
                                    *b = *a;
                                }
                            },
                            None => {
                                self.status.fetch_or(ERROR, Ordering::SeqCst);
                            }
                        }
                    },
                    _ => {
                        self.status.fetch_or(ERROR, Ordering::SeqCst);
                        let _ = write!(io::stderr(), "disk: System sent bad command: {:02X}\n", parameters.command);
                    },
                }
            }
            self.status.fetch_or(DATA_READY, Ordering::SeqCst);
        }
    }
}
<commit_msg>Kill races by unifying COMMAND_ and DATA_READY<commit_after>use super::ConcurrentDevice;

extern crate memmap;
use z80e_core_rust::{ IoDevice };
use self::memmap::{ Mmap };
pub use self::memmap::{ MmapView, Protection };

use std::sync::{ Arc, Condvar, Mutex };
use std::sync::atomic::{ AtomicBool, AtomicUsize, Ordering };
use std::io::{ self, ErrorKind, Write };
use std::path::Path;
use std::{ str, mem, ptr };

// Sector size must be a power of two
const SECTOR_SIZE: u16 = 128;

// Max disk must be a power of two <= 16. If changed, you must insert/delete Nones in DiskController::run()
const MAX_DISK: u8 = 16;

// Status port bitflags.
//     8-bit values, but "must" be usize to avoid overly-verbose casts for AtomicUsize calls.
const READY: usize = 1 << 0;
const ERROR: usize = 1 << 7;

// Commands.
const NOP: u8 = 0;
const SEL_DSK: u8 = 1;
const SEL_TRK: u8 = 2;
const SEL_SEC: u8 = 3;
const READ: u8 = 4;
const WRITE: u8 = 5;
const RESET: u8 = 6;
const OPEN: u8 = 7;
const CLOSE: u8 = 8;
const DPB: u8 = 9;

#[derive(Clone)]
pub struct DiskController {
    pub status: Arc<AtomicUsize>,
    command_cond: Arc<Condvar>,
    buffer: Arc<Mutex<Buffer>>,
    parameters: Arc<Mutex<Parameters>>,
}

impl DiskController {
    pub fn new() -> DiskController {
        DiskController {
            status: Arc::new(AtomicUsize::new(0)),
            command_cond: Arc::new(Condvar::new()),
            buffer: Arc::new(Mutex::new(Buffer::new())),
            parameters: Arc::new(Mutex::new(Parameters::new())),
        }
    }
    pub fn status_port(&self) -> StatusPort {
        StatusPort::new(self.clone())
    }
    pub fn data_port(&self) -> DataPort {
        DataPort::new(self.clone())
    }
}

struct Buffer {
    bytes: [u8; SECTOR_SIZE as usize],
    i: u16,
}

impl Buffer {
    fn new() -> Buffer {
        Buffer {
            bytes: [0; SECTOR_SIZE as usize],
            i: 0,
        }
    }
}

struct Parameters {
    disk: u8,
    track: u16,
    sector: u16,
    command: u8,
    do_command: bool,
}

impl Parameters {
    fn new() -> Parameters {
        Parameters {
            disk: 0,
            track: 0,
            sector: 0,
            command: NOP,
            do_command: false,
        }
    }
}

pub struct StatusPort {
    controller: DiskController,
}

impl StatusPort {
    fn new(controller: DiskController) -> StatusPort {
        StatusPort {
            controller: controller,
        }
    }
}

impl IoDevice for StatusPort {
    fn read_in(&self) -> u8 {
        (self.controller.status.load(Ordering::SeqCst) & 0xff) as u8
    }
    fn write_out(&mut self, value: u8) {
        if (self.controller.status.fetch_and(!READY, Ordering::SeqCst) & READY) != 0 {
            let mut params = self.controller.parameters.lock().unwrap();
            params.command = value;
            params.do_command = true;
            self.controller.command_cond.notify_one();
        } else {
            self.controller.status.fetch_or(ERROR, Ordering::SeqCst);
            let _ = writeln!(io::stderr(), "disk: Attempted to write command register when not ready.");
        }
    }
}

pub struct DataPort {
    controller: DiskController,
}

impl DataPort {
    fn new(controller: DiskController) -> DataPort {
        DataPort {
            controller: controller,
        }
    }
}

impl IoDevice for DataPort {
    fn read_in (&self) -> u8 {
        let byte = if (self.controller.status.fetch_and(!READY, Ordering::SeqCst) & READY) != 0 {
            {
                let mut buffer = self.controller.buffer.lock().unwrap();
                buffer.i = ((buffer.i as usize + 1) & (SECTOR_SIZE - 1) as usize) as u16;
                buffer.bytes[buffer.i as usize]
            }
        } else {
            self.controller.status.fetch_or(ERROR, Ordering::SeqCst);
            let _ = writeln!(io::stderr(), "disk: Attempted to read data register when not ready.");
            0
        };
        self.controller.status.fetch_or(READY, Ordering::SeqCst);
        byte
    }
    fn write_out(&mut self, value: u8) {
        if (self.controller.status.fetch_and(!READY, Ordering::SeqCst) & READY) != 0 {
            {
                let mut buffer = self.controller.buffer.lock().unwrap();
                buffer.bytes[buffer.i as usize] = value;
                buffer.i = ((buffer.i as usize + 1) & (SECTOR_SIZE - 1) as usize) as u16;
            }
        } else {
            self.controller.status.fetch_or(ERROR, Ordering::SeqCst);
            let _ = writeln!(io::stderr(), "disk: Attempted to write data register when not ready.");
        }
        self.controller.status.fetch_or(READY, Ordering::SeqCst);
    }
}

pub struct Disk {
    view: MmapView,
    pub tracks: u16,
    pub spt: u16,
    dpb: [u8; 17],
}

impl Disk {
    pub fn open<T: AsRef<Path>>(path: &T, protection: Protection) -> io::Result<Disk> {
        let file = try!(Mmap::open_path(path, protection)).into_view();
        let (header, image) = try!(file.split_at(128));
        let header = unsafe { header.as_slice() };
        if match str::from_utf8(&header[0..10]) {
            Ok(x) => x,
            Err(_) => return Err(io::Error::new(ErrorKind::InvalidData, "Invalid image header encoding.")),
        }!= "<CPM_Disk>" {
            return Err(io::Error::new(ErrorKind::InvalidData, "Not a valid disk image."));
        }
        let mut dpb: [u8; 17] = [0; 17];
        for i in 0..17 {
            dpb[i] = header[32 + i];
        }
        let spt: u16 = (dpb[0] as u16) | ((dpb[1] as u16) << 8);
        let bsh: u16 = dpb[2] as u16;
        let dsm: u16 = (dpb[5] as u16) | ((dpb[6] as u16) << 8);
        let off: u16 = (dpb[13] as u16) | ((dpb[14] as u16) << 8);
        let tracks: u16 = (dsm + 1) * (1 << bsh) / spt + off;
        Ok(Disk {
            view: image,
            tracks: tracks,
            spt: spt,
            dpb: dpb
        })
    }
}

impl ConcurrentDevice for DiskController {
    fn run(&mut self, die: Arc<AtomicBool>) {
        let mut disks = unsafe {
            let mut disks: [Option<Disk>; MAX_DISK as usize] = mem::uninitialized();
            for disk in disks.iter_mut() {
                ptr::write(disk, None);
            };
            disks
        };
        let mut parameters = self.parameters.lock().unwrap();
        loop {
            if die.load(Ordering::Acquire) { break; }
            parameters.do_command = false;
            while !(*parameters).do_command {
                self.status.fetch_or(READY, Ordering::SeqCst);
                parameters = self.command_cond.wait(parameters).unwrap();
            }
            let status = self.status.fetch_and(!READY, Ordering::SeqCst);
            if die.load(Ordering::Acquire) { break; }
            if (status & ERROR) != 0 && parameters.command != RESET {
                continue;
            };
            {
                let mut buffer = self.buffer.lock().unwrap();
                buffer.i = 0;
                match parameters.command {
                    NOP => (),
                    SEL_DSK => {
                        if buffer.bytes[0] < MAX_DISK {
                            parameters.disk = buffer.bytes[0];
                        } else {
                            self.status.fetch_or(ERROR, Ordering::SeqCst);
                        }
                    },
                    SEL_TRK => {
                        let track = buffer.bytes[0] as u16 | ((buffer.bytes[1] as u16) << 8);
                        match disks[parameters.disk as usize] {
                            Some(ref disk) => {
                                if track < disk.tracks {
                                    parameters.track = track;
                                } else {
                                    self.status.fetch_or(ERROR, Ordering::SeqCst);
                                }
                            },
                            None => {
                                self.status.fetch_or(ERROR, Ordering::SeqCst);
                            }
                        }
                    },
                    SEL_SEC => {
                        match disks[parameters.disk as usize] {
                            Some(ref disk) => {
                                let sector = buffer.bytes[0] as u16 | ((buffer.bytes[1] as u16) << 8);
                                if sector < disk.spt {
                                    parameters.sector = sector;
                                } else {
                                    self.status.fetch_or(ERROR, Ordering::SeqCst);
                                }
                            },
                            None => {
                                self.status.fetch_or(ERROR, Ordering::SeqCst);
                            },
                        }
                    },
                    READ => {
                        match disks[parameters.disk as usize] {
                            Some(ref disk) => {
                                let off = ((parameters.track as usize * disk.spt as usize)
                                           + parameters.sector as usize) * SECTOR_SIZE as usize;
                                let bytes = unsafe { disk.view.as_slice() };
                                for (i, byte) in buffer.bytes.iter_mut().enumerate() {
                                    *byte = bytes[off + i];
                                }
                            },
                            None => {
                                self.status.fetch_or(ERROR, Ordering::SeqCst);
                            },
                        }
                    },
                    WRITE => {
                        match disks[parameters.disk as usize] {
                            Some(ref mut disk) => {
                                let off = ((parameters.track as usize * disk.spt as usize)
                                           + parameters.sector as usize) * SECTOR_SIZE as usize;
                                let bytes = unsafe { disk.view.as_mut_slice() };
                                for (i, byte) in buffer.bytes.iter().enumerate() {
                                    bytes[off + i] = *byte;
                                };
                            },
                            None => {
                                self.status.fetch_or(ERROR, Ordering::SeqCst);
                            },
                        }

                    }
                    RESET => {
                        parameters.disk = 0;
                        parameters.track = 0;
                        parameters.sector = 0;
                        parameters.command = NOP;
                        self.status.fetch_and(!ERROR, Ordering::SeqCst);
                    },
                    OPEN => {
                        match str::from_utf8(buffer.bytes.split(|a| *a == 0).next().unwrap()) {
                            Ok(file_name) => {
                                match Disk::open(&file_name, Protection::ReadWrite) {
                                    Ok(disk) => {
                                        disks[parameters.disk as usize] = Some(disk);
                                    },
                                    Err(err) => {
                                        let mut stderr = io::stderr();
                                        let _ = writeln!(stderr, "disk: Failed to open file: {}", file_name);
                                        let _ = writeln!(stderr, "Error:\n\t{}", err);
                                        self.status.fetch_or(ERROR, Ordering::SeqCst);
                                    },
                                }
                            },
                            Err(err) => {
                                let _ = write!(io::stderr(), "disk: Bad UTF-8 in file name.\nError:\n\t{}\n", err);
                                self.status.fetch_or(ERROR, Ordering::SeqCst);
                            }
                        }
                    },
                    CLOSE => {
                        disks[parameters.disk as usize] = None;
                    },
                    DPB => {
                        match disks[parameters.disk as usize] {
                            Some(ref disk) => {
                                for (a, b) in disk.dpb.iter().zip(buffer.bytes.iter_mut()) {
                                    *b = *a;
                                }
                            },
                            None => {
                                self.status.fetch_or(ERROR, Ordering::SeqCst);
                            }
                        }
                    },
                    _ => {
                        self.status.fetch_or(ERROR, Ordering::SeqCst);
                        let _ = write!(io::stderr(), "disk: System sent bad command: {:02X}\n", parameters.command);
                    },
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>// Bitcoin secp256k1 bindings
// Written in 2015 by
//   Andrew Poelstra
//
// To the extent possible under law, the author(s) have dedicated all
// copyright and related and neighboring rights to this software to
// the public domain worldwide. This software is distributed without
// any warranty.
//
// You should have received a copy of the CC0 Public Domain Dedication
// along with this software.
// If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
//

//! # ECDH
//! Support for shared secret computations
//!

use std::{ops, ptr};

use super::Secp256k1;
use key::{SecretKey, PublicKey};
use ffi;

/// A tag used for recovering the public key from a compact signature
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub struct SharedSecret(ffi::SharedSecret);

impl SharedSecret {
    /// Creates a new shared secret from a pubkey and secret key
    #[inline]
    pub fn new<C>(secp: &Secp256k1<C>, point: &PublicKey, scalar: &SecretKey) -> SharedSecret {
        unsafe {
            let mut ss = ffi::SharedSecret::blank();
            let res = ffi::secp256k1_ecdh(
                secp.ctx,
                &mut ss,
                point.as_ptr(),
                scalar.as_ptr(),
                ffi::secp256k1_ecdh_hash_function_default,
                ptr::null_mut(),
            );
            debug_assert_eq!(res, 1);
            SharedSecret(ss)
        }
    }

    /// Obtains a raw pointer suitable for use with FFI functions
    #[inline]
    pub fn as_ptr(&self) -> *const ffi::SharedSecret {
        &self.0 as *const _
    }
}

/// Creates a new shared secret from a FFI shared secret
impl From<ffi::SharedSecret> for SharedSecret {
    #[inline]
    fn from(ss: ffi::SharedSecret) -> SharedSecret {
        SharedSecret(ss)
    }
}

impl ops::Index<usize> for SharedSecret {
    type Output = u8;

    #[inline]
    fn index(&self, index: usize) -> &u8 {
        &self.0[index]
    }
}

impl ops::Index<ops::Range<usize>> for SharedSecret {
    type Output = [u8];

    #[inline]
    fn index(&self, index: ops::Range<usize>) -> &[u8] {
        &self.0[index]
    }
}

impl ops::Index<ops::RangeFrom<usize>> for SharedSecret {
    type Output = [u8];

    #[inline]
    fn index(&self, index: ops::RangeFrom<usize>) -> &[u8] {
        &self.0[index.start..]
    }
}

impl ops::Index<ops::RangeFull> for SharedSecret {
    type Output = [u8];

    #[inline]
    fn index(&self, _: ops::RangeFull) -> &[u8] {
        &self.0[..]
    }
}

#[cfg(test)]
mod tests {
    use rand::thread_rng;
    use super::SharedSecret;
    use super::super::Secp256k1;

    #[test]
    fn ecdh() {
        let s = Secp256k1::signing_only();
        let (sk1, pk1) = s.generate_keypair(&mut thread_rng());
        let (sk2, pk2) = s.generate_keypair(&mut thread_rng());

        let sec1 = SharedSecret::new(&s, &pk1, &sk2);
        let sec2 = SharedSecret::new(&s, &pk2, &sk1);
        let sec_odd = SharedSecret::new(&s, &pk1, &sk1);
        assert_eq!(sec1, sec2);
        assert!(sec_odd != sec2);
    }
}

#[cfg(all(test, feature = "unstable"))]
mod benches {
    use rand::thread_rng;
    use test::{Bencher, black_box};

    use super::SharedSecret;
    use super::super::Secp256k1;

    #[bench]
    pub fn bench_ecdh(bh: &mut Bencher) {
        let s = Secp256k1::signing_only();
        let (sk, pk) = s.generate_keypair(&mut thread_rng());

        let s = Secp256k1::new();
        bh.iter( || {
            let res = SharedSecret::new(&s, &pk, &sk);
            black_box(res);
        });
    }
}

<commit_msg>remove context object from ECDH<commit_after>// Bitcoin secp256k1 bindings
// Written in 2015 by
//   Andrew Poelstra
//
// To the extent possible under law, the author(s) have dedicated all
// copyright and related and neighboring rights to this software to
// the public domain worldwide. This software is distributed without
// any warranty.
//
// You should have received a copy of the CC0 Public Domain Dedication
// along with this software.
// If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
//

//! # ECDH
//! Support for shared secret computations
//!

use std::{ops, ptr};

use key::{SecretKey, PublicKey};
use ffi;

/// A tag used for recovering the public key from a compact signature
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub struct SharedSecret(ffi::SharedSecret);

impl SharedSecret {
    /// Creates a new shared secret from a pubkey and secret key
    #[inline]
    pub fn new(point: &PublicKey, scalar: &SecretKey) -> SharedSecret {
        unsafe {
            let mut ss = ffi::SharedSecret::blank();
            let res = ffi::secp256k1_ecdh(
                ffi::secp256k1_context_no_precomp,
                &mut ss,
                point.as_ptr(),
                scalar.as_ptr(),
                ffi::secp256k1_ecdh_hash_function_default,
                ptr::null_mut(),
            );
            debug_assert_eq!(res, 1);
            SharedSecret(ss)
        }
    }

    /// Obtains a raw pointer suitable for use with FFI functions
    #[inline]
    pub fn as_ptr(&self) -> *const ffi::SharedSecret {
        &self.0 as *const _
    }
}

/// Creates a new shared secret from a FFI shared secret
impl From<ffi::SharedSecret> for SharedSecret {
    #[inline]
    fn from(ss: ffi::SharedSecret) -> SharedSecret {
        SharedSecret(ss)
    }
}

impl ops::Index<usize> for SharedSecret {
    type Output = u8;

    #[inline]
    fn index(&self, index: usize) -> &u8 {
        &self.0[index]
    }
}

impl ops::Index<ops::Range<usize>> for SharedSecret {
    type Output = [u8];

    #[inline]
    fn index(&self, index: ops::Range<usize>) -> &[u8] {
        &self.0[index]
    }
}

impl ops::Index<ops::RangeFrom<usize>> for SharedSecret {
    type Output = [u8];

    #[inline]
    fn index(&self, index: ops::RangeFrom<usize>) -> &[u8] {
        &self.0[index.start..]
    }
}

impl ops::Index<ops::RangeFull> for SharedSecret {
    type Output = [u8];

    #[inline]
    fn index(&self, _: ops::RangeFull) -> &[u8] {
        &self.0[..]
    }
}

#[cfg(test)]
mod tests {
    use rand::thread_rng;
    use super::SharedSecret;
    use super::super::Secp256k1;

    #[test]
    fn ecdh() {
        let s = Secp256k1::signing_only();
        let (sk1, pk1) = s.generate_keypair(&mut thread_rng());
        let (sk2, pk2) = s.generate_keypair(&mut thread_rng());

        let sec1 = SharedSecret::new(&pk1, &sk2);
        let sec2 = SharedSecret::new(&pk2, &sk1);
        let sec_odd = SharedSecret::new(&pk1, &sk1);
        assert_eq!(sec1, sec2);
        assert!(sec_odd != sec2);
    }
}

#[cfg(all(test, feature = "unstable"))]
mod benches {
    use rand::thread_rng;
    use test::{Bencher, black_box};

    use super::SharedSecret;
    use super::super::Secp256k1;

    #[bench]
    pub fn bench_ecdh(bh: &mut Bencher) {
        let s = Secp256k1::signing_only();
        let (sk, pk) = s.generate_keypair(&mut thread_rng());

        let s = Secp256k1::new();
        bh.iter( || {
            let res = SharedSecret::new(&pk, &sk);
            black_box(res);
        });
    }
}

<|endoftext|>


<commit_before>//! This module contains the game logic

use std::f64;
use std::env::current_exe;

use graphics::{self, Transformed};
use itertools;
use opengl_graphics::GlGraphics;
use opengl_graphics::glyph_cache::GlyphCache;
use piston::input::{ControllerButton, ControllerAxisArgs, Key};
use rand::{self, ThreadRng};

use drawing::{color, Point, Size};
use models::{Bullet, Enemy, Particle, Vector, World};
use traits::{Advance, Collide, Position};

const UPS: u16 = 120;
const BULLET_RATE: f64 = 0.01;

/// The data structure that drives the game
pub struct Game {
    /// The world contains everything that needs to be drawn
    world: World,
    /// The current score of the player
    score: u32,
    /// The active actions
    actions: Actions,
    /// Timers needed by the game
    timers: Timers,
    /// A random number generator
    rng: ThreadRng,
    /// Resources needed for drawing
    resources: Resources
}

/// Active actions (toggled by user input)
#[derive(Default)]
struct Actions {
    rotate_left: bool,
    rotate_right: bool,
    boost: bool,
    shoot: bool
}

/// Timers to handle creation of bullets, enemies and particles
#[derive(Default)]
struct Timers {
    current_time: f64,
    last_tail_particle: f64,
    last_shoot: f64,
    last_spawned_enemy: f64
}

/// Additional resources needed for the game
struct Resources {
    font: GlyphCache<'static>
}

impl Game {
    /// Returns a new `Game` containing a `World` of the given `Size`
    pub fn new(size: Size) -> Game {
        let mut rng = rand::thread_rng();
        let exe_directory = current_exe().unwrap().parent().unwrap().to_owned();
        Game {
            world: World::new(&mut rng, size),
            score: 0,
            actions: Actions::default(),
            timers: Timers::default(),
            rng: rng,
            resources: Resources { font: GlyphCache::new(&exe_directory.join("resources/FiraMono-Bold.ttf")).unwrap() }
        }
    }

    /// Processes a key press
    pub fn key_press(&mut self, key: Key) {
        self.handle_key(key, true);
    }

    /// Processes a key release
    pub fn key_release(&mut self, key: Key) {
        self.handle_key(key, false);
    }

    /// Handles a key press or release
    fn handle_key(&mut self, key: Key, pressed: bool) {
        match key {
            Key::Left => self.actions.rotate_left = pressed,
            Key::Right => self.actions.rotate_right = pressed,
            Key::Up => self.actions.boost = pressed,
            Key::Space => self.actions.shoot = pressed,
            _ => ()
        }
    }

    /// Processes a button press
    pub fn button_press(&mut self, controller: ControllerButton) {
        self.handle_button(controller, true);
    }

    /// Processes a button release
    pub fn button_release(&mut self, controller: ControllerButton) {
        self.handle_button(controller, false);
    }

    /// Handles a button press or release
    fn handle_button(&mut self, controller: ControllerButton, pressed: bool) {
        // Button 10 is A button on XInput
        match controller.button {
            10 => self.actions.shoot = pressed,
            _ => ()
        }
    }

    /// Handles a controller axis input
    pub fn handle_axis(&mut self, controller: ControllerAxisArgs) {

        // Axis 0 is left stick (XInput). -1.0 left to 1.0 right
        if controller.axis == 0 {
            match controller.position {
                -1.0 ... -0.2 => {
                    self.actions.rotate_left = true;
                    self.actions.rotate_right = false;
                },
                0.2 ... 1.0 => {
                    self.actions.rotate_left = false;
                    self.actions.rotate_right = true;
                },
                -0.199 ... 0.199 => {
                    self.actions.rotate_left = false;
                    self.actions.rotate_right = false;
                },
                _ => {}
            }
        }

        // Axis 5 is right trigger (XInput). -1.0 is not pressed, 1.0 is fully pressed
        if controller.axis == 5 {
            match controller.position {
                -0.8 ... 1.0 => {
                    self.actions.boost = true;
                },
                -1.0 ... -0.799 => {
                    self.actions.boost = false;
                },
                _ => {}
            }
        }

        // TODO: Actually use stick position value to affect rotation amount
        //       Actually use trigger position value affect boost amount
    }

    /// Renders the game to the screen
    pub fn render(&mut self, c: graphics::context::Context, g: &mut GlGraphics) {
        // Clear everything
        graphics::clear(color::BLACK, g);

        // Render the world
        self.world.render(c, g);

        // Render the score
        let mut text = graphics::Text::new(22);
        text.color = color::ORANGE;
        text.draw(&format!("Score: {}", self.score),
                  &mut self.resources.font,
                  &c.draw_state,
                  c.trans(10.0, 20.0).transform,
                  g);
    }

    /// Updates the game
    ///
    /// `dt` is the amount of seconds that have passed since the last update
    pub fn update(&mut self, dt: f64) {
        self.timers.current_time += dt;

        // Update rocket rotation
        if self.actions.rotate_left {
            *self.world.player.direction_mut() += (-0.06 * UPS as f64) * dt;
        }
        if self.actions.rotate_right {
            *self.world.player.direction_mut() += (0.06 * UPS as f64) * dt;
        };

        // Set speed and advance the player with wrap around
        let speed = if self.actions.boost { 400.0  } else { 200.0 };
        self.world.player.advance_wrapping(dt * speed, self.world.size.clone());

        // Update particles
        for particle in &mut self.world.particles {
            particle.update(dt);
        }

        // Remove old particles
        self.world.particles.retain(|p| p.ttl > 0.0);

        // Add new particles at the player's position, to leave a trail
        if self.timers.current_time - self.timers.last_tail_particle > 0.05 {
            self.timers.last_tail_particle = self.timers.current_time;
            self.world.particles.push(Particle::new(self.world.player.vector.clone().invert(), 0.5));
        }

        // Add bullets
        if self.actions.shoot && self.timers.current_time - self.timers.last_shoot > BULLET_RATE {
            self.timers.last_shoot = self.timers.current_time;
            self.world.bullets.push(Bullet::new(Vector::new(self.world.player.nose(), self.world.player.direction())));
        }

        // Advance bullets
        for bullet in &mut self.world.bullets {
            bullet.update(dt * 500.0);
        }

        // Remove bullets outside the viewport
        { // Shorten the lifetime of size
        let size = &self.world.size;
        self.world.bullets.retain(|b| size.contains(b.position()));
        }

        // Spawn enemies at random locations
        if self.timers.current_time - self.timers.last_spawned_enemy > 1.0 {
            self.timers.last_spawned_enemy = self.timers.current_time;
            let mut new_enemy: Enemy;
            loop {
                new_enemy = Enemy::new(Vector::random(&mut self.rng, self.world.size.clone()));
                if !self.world.player.collides_with(&new_enemy) {
                    break;
                }
            }
            self.world.enemies.push(new_enemy);
        }

        // Move enemies in the player's direction
        for enemy in &mut self.world.enemies {
            enemy.update(dt * 100.0, self.world.player.position());
        }

        self.handle_player_collisions();
        self.handle_bullet_collisions();
    }

    /// Handles collisions between the bullets and the enemies
    ///
    /// When an enemy is reached by a bullet, both the enemy and the bullet
    /// will be removed. Additionally, the score will be increased by 10
    fn handle_bullet_collisions(&mut self) {
        let old_enemy_count = self.world.enemies.len();

        { // We introduce a scope to shorten the lifetime of the borrows below
        // The references are to avoid using self in the closure
        // (the borrow checker doesn't like that)
        let bullets = &mut self.world.bullets;
        let enemies = &mut self.world.enemies;
        let particles = &mut self.world.particles;

        bullets.retain(|bullet| {
            // Remove the first enemy that collides with a bullet (if any)
            // Add an explosion on its place
            if let Some((index, position)) = enemies.iter().enumerate()
                .find(|&(_, enemy)| enemy.collides_with(bullet))
                .map(|(index, enemy)| (index, enemy.position()))
            {
                Game::make_explosion(particles, position, 10);
                enemies.remove(index);
                false
            } else {
                true
            }
        });
        }

        let killed_enemies = (old_enemy_count - self.world.enemies.len()) as u32;
        self.score += 10 * killed_enemies;
    }

    /// reset our game-state
    fn reset(&mut self) {
        // Reset player position
        *self.world.player.x_mut() = self.world.size.random_x(&mut self.rng);
        *self.world.player.y_mut() = self.world.size.random_y(&mut self.rng);

        // Reset score
        self.score = 0;

        // Remove all enemies and bullets
        self.world.bullets.clear();
        self.world.enemies.clear();
    }

    /// Handles collisions between the player and the enemies
    fn handle_player_collisions(&mut self) {
        if self.world.enemies.iter().any(|enemy| self.world.player.collides_with(enemy)) {
            // Make an explosion where the player was
            let ppos = self.world.player.position();
            Game::make_explosion(&mut self.world.particles, ppos, 8);

            self.reset();
        }
    }

    /// Generates a new explosion of the given intensity at the given position. This works best with values between 5 and 25
    fn make_explosion(particles: &mut Vec<Particle>, position: Point, intensity: u8) {
        for rotation in itertools::linspace(0.0, 2.0 * f64::consts::PI, 30) {
            for ttl in (1..intensity).map(|x| (x as f64) / 10.0) {
                particles.push(Particle::new(Vector::new(position.clone(), rotation), ttl));
            }
        }
    }
}
<commit_msg>Simplify text rendering.<commit_after>//! This module contains the game logic

use std::f64;
use std::env::current_exe;

use graphics::{self, Transformed};
use itertools;
use opengl_graphics::GlGraphics;
use opengl_graphics::glyph_cache::GlyphCache;
use piston::input::{ControllerButton, ControllerAxisArgs, Key};
use rand::{self, ThreadRng};

use drawing::{color, Point, Size};
use models::{Bullet, Enemy, Particle, Vector, World};
use traits::{Advance, Collide, Position};

const UPS: u16 = 120;
const BULLET_RATE: f64 = 0.01;

/// The data structure that drives the game
pub struct Game {
    /// The world contains everything that needs to be drawn
    world: World,
    /// The current score of the player
    score: u32,
    /// The active actions
    actions: Actions,
    /// Timers needed by the game
    timers: Timers,
    /// A random number generator
    rng: ThreadRng,
    /// Resources needed for drawing
    resources: Resources
}

/// Active actions (toggled by user input)
#[derive(Default)]
struct Actions {
    rotate_left: bool,
    rotate_right: bool,
    boost: bool,
    shoot: bool
}

/// Timers to handle creation of bullets, enemies and particles
#[derive(Default)]
struct Timers {
    current_time: f64,
    last_tail_particle: f64,
    last_shoot: f64,
    last_spawned_enemy: f64
}

/// Additional resources needed for the game
struct Resources {
    font: GlyphCache<'static>
}

impl Game {
    /// Returns a new `Game` containing a `World` of the given `Size`
    pub fn new(size: Size) -> Game {
        let mut rng = rand::thread_rng();
        let exe_directory = current_exe().unwrap().parent().unwrap().to_owned();
        Game {
            world: World::new(&mut rng, size),
            score: 0,
            actions: Actions::default(),
            timers: Timers::default(),
            rng: rng,
            resources: Resources { font: GlyphCache::new(&exe_directory.join("resources/FiraMono-Bold.ttf")).unwrap() }
        }
    }

    /// Processes a key press
    pub fn key_press(&mut self, key: Key) {
        self.handle_key(key, true);
    }

    /// Processes a key release
    pub fn key_release(&mut self, key: Key) {
        self.handle_key(key, false);
    }

    /// Handles a key press or release
    fn handle_key(&mut self, key: Key, pressed: bool) {
        match key {
            Key::Left => self.actions.rotate_left = pressed,
            Key::Right => self.actions.rotate_right = pressed,
            Key::Up => self.actions.boost = pressed,
            Key::Space => self.actions.shoot = pressed,
            _ => ()
        }
    }

    /// Processes a button press
    pub fn button_press(&mut self, controller: ControllerButton) {
        self.handle_button(controller, true);
    }

    /// Processes a button release
    pub fn button_release(&mut self, controller: ControllerButton) {
        self.handle_button(controller, false);
    }

    /// Handles a button press or release
    fn handle_button(&mut self, controller: ControllerButton, pressed: bool) {
        // Button 10 is A button on XInput
        match controller.button {
            10 => self.actions.shoot = pressed,
            _ => ()
        }
    }

    /// Handles a controller axis input
    pub fn handle_axis(&mut self, controller: ControllerAxisArgs) {

        // Axis 0 is left stick (XInput). -1.0 left to 1.0 right
        if controller.axis == 0 {
            match controller.position {
                -1.0 ... -0.2 => {
                    self.actions.rotate_left = true;
                    self.actions.rotate_right = false;
                },
                0.2 ... 1.0 => {
                    self.actions.rotate_left = false;
                    self.actions.rotate_right = true;
                },
                -0.199 ... 0.199 => {
                    self.actions.rotate_left = false;
                    self.actions.rotate_right = false;
                },
                _ => {}
            }
        }

        // Axis 5 is right trigger (XInput). -1.0 is not pressed, 1.0 is fully pressed
        if controller.axis == 5 {
            match controller.position {
                -0.8 ... 1.0 => {
                    self.actions.boost = true;
                },
                -1.0 ... -0.799 => {
                    self.actions.boost = false;
                },
                _ => {}
            }
        }

        // TODO: Actually use stick position value to affect rotation amount
        //       Actually use trigger position value affect boost amount
    }

    /// Renders the game to the screen
    pub fn render(&mut self, c: graphics::context::Context, g: &mut GlGraphics) {
        // Clear everything
        graphics::clear(color::BLACK, g);

        // Render the world
        self.world.render(c, g);

        // Render the score
        graphics::text(color::ORANGE,
                       22,
                       &format!("Score: {}", self.score),
                       &mut self.resources.font,
                       c.trans(10.0, 20.0).transform,
                       g);
    }

    /// Updates the game
    ///
    /// `dt` is the amount of seconds that have passed since the last update
    pub fn update(&mut self, dt: f64) {
        self.timers.current_time += dt;

        // Update rocket rotation
        if self.actions.rotate_left {
            *self.world.player.direction_mut() += (-0.06 * UPS as f64) * dt;
        }
        if self.actions.rotate_right {
            *self.world.player.direction_mut() += (0.06 * UPS as f64) * dt;
        };

        // Set speed and advance the player with wrap around
        let speed = if self.actions.boost { 400.0  } else { 200.0 };
        self.world.player.advance_wrapping(dt * speed, self.world.size.clone());

        // Update particles
        for particle in &mut self.world.particles {
            particle.update(dt);
        }

        // Remove old particles
        self.world.particles.retain(|p| p.ttl > 0.0);

        // Add new particles at the player's position, to leave a trail
        if self.timers.current_time - self.timers.last_tail_particle > 0.05 {
            self.timers.last_tail_particle = self.timers.current_time;
            self.world.particles.push(Particle::new(self.world.player.vector.clone().invert(), 0.5));
        }

        // Add bullets
        if self.actions.shoot && self.timers.current_time - self.timers.last_shoot > BULLET_RATE {
            self.timers.last_shoot = self.timers.current_time;
            self.world.bullets.push(Bullet::new(Vector::new(self.world.player.nose(), self.world.player.direction())));
        }

        // Advance bullets
        for bullet in &mut self.world.bullets {
            bullet.update(dt * 500.0);
        }

        // Remove bullets outside the viewport
        { // Shorten the lifetime of size
        let size = &self.world.size;
        self.world.bullets.retain(|b| size.contains(b.position()));
        }

        // Spawn enemies at random locations
        if self.timers.current_time - self.timers.last_spawned_enemy > 1.0 {
            self.timers.last_spawned_enemy = self.timers.current_time;
            let mut new_enemy: Enemy;
            loop {
                new_enemy = Enemy::new(Vector::random(&mut self.rng, self.world.size.clone()));
                if !self.world.player.collides_with(&new_enemy) {
                    break;
                }
            }
            self.world.enemies.push(new_enemy);
        }

        // Move enemies in the player's direction
        for enemy in &mut self.world.enemies {
            enemy.update(dt * 100.0, self.world.player.position());
        }

        self.handle_player_collisions();
        self.handle_bullet_collisions();
    }

    /// Handles collisions between the bullets and the enemies
    ///
    /// When an enemy is reached by a bullet, both the enemy and the bullet
    /// will be removed. Additionally, the score will be increased by 10
    fn handle_bullet_collisions(&mut self) {
        let old_enemy_count = self.world.enemies.len();

        { // We introduce a scope to shorten the lifetime of the borrows below
        // The references are to avoid using self in the closure
        // (the borrow checker doesn't like that)
        let bullets = &mut self.world.bullets;
        let enemies = &mut self.world.enemies;
        let particles = &mut self.world.particles;

        bullets.retain(|bullet| {
            // Remove the first enemy that collides with a bullet (if any)
            // Add an explosion on its place
            if let Some((index, position)) = enemies.iter().enumerate()
                .find(|&(_, enemy)| enemy.collides_with(bullet))
                .map(|(index, enemy)| (index, enemy.position()))
            {
                Game::make_explosion(particles, position, 10);
                enemies.remove(index);
                false
            } else {
                true
            }
        });
        }

        let killed_enemies = (old_enemy_count - self.world.enemies.len()) as u32;
        self.score += 10 * killed_enemies;
    }

    /// reset our game-state
    fn reset(&mut self) {
        // Reset player position
        *self.world.player.x_mut() = self.world.size.random_x(&mut self.rng);
        *self.world.player.y_mut() = self.world.size.random_y(&mut self.rng);

        // Reset score
        self.score = 0;

        // Remove all enemies and bullets
        self.world.bullets.clear();
        self.world.enemies.clear();
    }

    /// Handles collisions between the player and the enemies
    fn handle_player_collisions(&mut self) {
        if self.world.enemies.iter().any(|enemy| self.world.player.collides_with(enemy)) {
            // Make an explosion where the player was
            let ppos = self.world.player.position();
            Game::make_explosion(&mut self.world.particles, ppos, 8);

            self.reset();
        }
    }

    /// Generates a new explosion of the given intensity at the given position. This works best with values between 5 and 25
    fn make_explosion(particles: &mut Vec<Particle>, position: Point, intensity: u8) {
        for rotation in itertools::linspace(0.0, 2.0 * f64::consts::PI, 30) {
            for ttl in (1..intensity).map(|x| (x as f64) / 10.0) {
                particles.push(Particle::new(Vector::new(position.clone(), rotation), ttl));
            }
        }
    }
}
<|endoftext|>


<commit_before>extern crate unicode_segmentation;
use game::unicode_segmentation::UnicodeSegmentation;

use std::path::Path;
use std::collections::HashMap;
use std::io::{BufRead, BufReader};
use std::fs::File;
use std::collections::BinaryHeap;

////////////////////////////////////////////////////////////////////////////////

#[derive(Debug)]
pub struct Results {
    white: u64,
    black: u64,
    draw: u64,
}

impl Results {
    fn new(s: &str) -> Results {
        match s {
            "1-0"     => Results { white: 1, black: 0, draw: 0 },
            "0-1"     => Results { white: 0, black: 1, draw: 0 },
            "1/2-1/2" => Results { white: 0, black: 0, draw: 1 },
            _         => Results { white: 0, black: 0, draw: 0 },
        }
    }
}

impl Clone for Results {
    fn clone(&self) -> Results {
        Results {
            white: self.white.clone(),
            black: self.black.clone(),
            draw: self.draw.clone(),
        }
    }
}

////////////////////////////////////////////////////////////////////////////////

pub struct Game {
    moves: Vec<String>,
    tagpairs: HashMap<String, String>,
    results: Results,
}

impl Clone for Game {
    fn clone(&self) -> Game {
        Game {
            moves: self.moves.clone(),
            tagpairs: self.tagpairs.clone(),
            results: self.results.clone(),
        }
    }
}

fn remove_first_last(s: &String) -> String {
    s.graphemes(true).skip(1).take(s.len() - 2).collect::<String>()
}

impl Game {
    fn parse_moves(moves: &Vec<String>) -> Vec<String> {
        let mut v: Vec<String> = Vec::new();
        for line in moves {
            let parts: Vec<&str> = line.split_whitespace().collect();
            for part in parts {
                if !(String::from(part).ends_with(".")) {
                    v.push(String::from(part));
                }
            }
        }
        v.clone()
    }

    fn parse_tagpairs(tagpairs: &Vec<String>) -> HashMap<String, String> {
        let mut tp: HashMap<String, String> = HashMap::new();
        for s in tagpairs {
            let t = remove_first_last(s);
            let v: Vec<&str> = t.split_whitespace().collect();
            tp.insert(String::from(v[0]), remove_first_last(&String::from(v[1])));
        }
        tp.clone()
    }

    fn parse_results(tagpairs: &HashMap<String, String>) -> Results {
        match tagpairs.get("Result").map(String::as_ref) {
            Some(s) => Results::new(s),
            _ => Results { white: 0, black: 0, draw: 0 },
        }
    }

    pub fn new(tagpairs: &Vec<String>, moves: &Vec<String>) -> Game {
        let tp = Game::parse_tagpairs(tagpairs);
        Game {
            moves: Game::parse_moves(moves),
            tagpairs: tp.clone(),
            results: Game::parse_results(&tp),
        }
    }

    pub fn from_pgn(path: &Path) -> Vec<Game> {
        let mut v = Vec::new();
        let display = path.display();
        let file = match File::open(path) {
            Ok(f) => f,
            _     => panic!("opening {}", display),
        };
        let reader = BufReader::new(file);
        let mut header = Vec::new();
        let mut moves = Vec::new();
        let mut reading_header = true;
        for l in reader.lines() {
            let line = match l {
                Ok(ln) => ln,
                _      => panic!("reading {}", display),
            };
            if line.starts_with(";") || line.starts_with("{") {
                continue;
            }
            if line == "" {
                reading_header = !reading_header;
                if reading_header {
                    v.push(Game::new(&header, &moves));
                    header = Vec::new();
                    moves = Vec::new();
                }
                continue;
            }
            if reading_header {
                header.push(line);
            } else {
                moves.push(line);
            }
        }
        v
    }
}

////////////////////////////////////////////////////////////////////////////////

pub struct GameTree {
    moves: Vec<String>,
    games: HashMap<String, Vec<Game>>,
    pub results: Results,
    game_trees: HashMap<String, GameTree>,
}

impl GameTree {
    pub fn new() -> GameTree {
        GameTree::from_moves(Vec::new())
    }

    pub fn from_moves(moves: Vec<String>) -> GameTree {
        GameTree {
            moves: moves.clone(),
            games: HashMap::new(),
            results: Results { white: 0, black: 0, draw: 0 },
            game_trees: HashMap::new(),
        }
    }

    pub fn add(&mut self, v: Vec<Game>) {
        for game in v {
            let n = self.moves.len();
            let next_move: &str = game.moves[n].as_ref();
            if !self.games.contains_key(next_move) {
                self.games.insert(String::from(next_move), Vec::new());
            }
            self.games.get_mut(next_move).unwrap().push(game.clone());
            self.results.white += game.results.white;
            self.results.black += game.results.black;
            self.results.draw += game.results.draw;
        }
    }

    pub fn expand(&mut self, next_move: &str) {
        if self.game_trees.contains_key(next_move) {
            return;
        }
        if !self.games.contains_key(next_move) {
            return;
        }
        let mut moves = self.moves.clone();
        moves.push(String::from(next_move));
        let mut gt = GameTree::from_moves(moves);
        gt.add(self.games.get(next_move).unwrap().clone());
        self.game_trees.insert(String::from(next_move), gt);
    }

    pub fn top_ten(&self) -> Vec<(usize, &String)> {
        let mut heap = BinaryHeap::new();
        for (mov, games) in &self.games {
            heap.push((games.len(), mov))
        }
        let mut v = Vec::new();
        for _ in 1..10 {
            v.push(heap.pop().unwrap());
        }
        v
    }
}
<commit_msg>Will no longer crash if fewer than 10 top moves<commit_after>extern crate unicode_segmentation;
use game::unicode_segmentation::UnicodeSegmentation;

use std::path::Path;
use std::collections::HashMap;
use std::io::{BufRead, BufReader};
use std::fs::File;
use std::collections::BinaryHeap;

////////////////////////////////////////////////////////////////////////////////

#[derive(Debug)]
pub struct Results {
    white: u64,
    black: u64,
    draw: u64,
}

impl Results {
    fn new(s: &str) -> Results {
        match s {
            "1-0"     => Results { white: 1, black: 0, draw: 0 },
            "0-1"     => Results { white: 0, black: 1, draw: 0 },
            "1/2-1/2" => Results { white: 0, black: 0, draw: 1 },
            _         => Results { white: 0, black: 0, draw: 0 },
        }
    }
}

impl Clone for Results {
    fn clone(&self) -> Results {
        Results {
            white: self.white.clone(),
            black: self.black.clone(),
            draw: self.draw.clone(),
        }
    }
}

////////////////////////////////////////////////////////////////////////////////

pub struct Game {
    moves: Vec<String>,
    tagpairs: HashMap<String, String>,
    results: Results,
}

impl Clone for Game {
    fn clone(&self) -> Game {
        Game {
            moves: self.moves.clone(),
            tagpairs: self.tagpairs.clone(),
            results: self.results.clone(),
        }
    }
}

fn remove_first_last(s: &String) -> String {
    s.graphemes(true).skip(1).take(s.len() - 2).collect::<String>()
}

impl Game {
    fn parse_moves(moves: &Vec<String>) -> Vec<String> {
        let mut v: Vec<String> = Vec::new();
        for line in moves {
            let parts: Vec<&str> = line.split_whitespace().collect();
            for part in parts {
                if !(String::from(part).ends_with(".")) {
                    v.push(String::from(part));
                }
            }
        }
        v.clone()
    }

    fn parse_tagpairs(tagpairs: &Vec<String>) -> HashMap<String, String> {
        let mut tp: HashMap<String, String> = HashMap::new();
        for s in tagpairs {
            let t = remove_first_last(s);
            let v: Vec<&str> = t.split_whitespace().collect();
            tp.insert(String::from(v[0]), remove_first_last(&String::from(v[1])));
        }
        tp.clone()
    }

    fn parse_results(tagpairs: &HashMap<String, String>) -> Results {
        match tagpairs.get("Result").map(String::as_ref) {
            Some(s) => Results::new(s),
            _ => Results { white: 0, black: 0, draw: 0 },
        }
    }

    pub fn new(tagpairs: &Vec<String>, moves: &Vec<String>) -> Game {
        let tp = Game::parse_tagpairs(tagpairs);
        Game {
            moves: Game::parse_moves(moves),
            tagpairs: tp.clone(),
            results: Game::parse_results(&tp),
        }
    }

    pub fn from_pgn(path: &Path) -> Vec<Game> {
        let mut v = Vec::new();
        let display = path.display();
        let file = match File::open(path) {
            Ok(f) => f,
            _     => panic!("opening {}", display),
        };
        let reader = BufReader::new(file);
        let mut header = Vec::new();
        let mut moves = Vec::new();
        let mut reading_header = true;
        for l in reader.lines() {
            let line = match l {
                Ok(ln) => ln,
                _      => panic!("reading {}", display),
            };
            if line.starts_with(";") || line.starts_with("{") {
                continue;
            }
            if line == "" {
                reading_header = !reading_header;
                if reading_header {
                    v.push(Game::new(&header, &moves));
                    header = Vec::new();
                    moves = Vec::new();
                }
                continue;
            }
            if reading_header {
                header.push(line);
            } else {
                moves.push(line);
            }
        }
        v
    }
}

////////////////////////////////////////////////////////////////////////////////

pub struct GameTree {
    moves: Vec<String>,
    games: HashMap<String, Vec<Game>>,
    pub results: Results,
    game_trees: HashMap<String, GameTree>,
}

impl GameTree {
    pub fn new() -> GameTree {
        GameTree::from_moves(Vec::new())
    }

    pub fn from_moves(moves: Vec<String>) -> GameTree {
        GameTree {
            moves: moves.clone(),
            games: HashMap::new(),
            results: Results { white: 0, black: 0, draw: 0 },
            game_trees: HashMap::new(),
        }
    }

    pub fn add(&mut self, v: Vec<Game>) {
        for game in v {
            let n = self.moves.len();
            let next_move: &str = game.moves[n].as_ref();
            if !self.games.contains_key(next_move) {
                self.games.insert(String::from(next_move), Vec::new());
            }
            self.games.get_mut(next_move).unwrap().push(game.clone());
            self.results.white += game.results.white;
            self.results.black += game.results.black;
            self.results.draw += game.results.draw;
        }
    }

    pub fn expand(&mut self, next_move: &str) {
        if self.game_trees.contains_key(next_move) {
            return;
        }
        if !self.games.contains_key(next_move) {
            return;
        }
        let mut moves = self.moves.clone();
        moves.push(String::from(next_move));
        let mut gt = GameTree::from_moves(moves);
        gt.add(self.games.get(next_move).unwrap().clone());
        self.game_trees.insert(String::from(next_move), gt);
    }

    pub fn top_ten(&self) -> Vec<(usize, &String)> {
        let mut heap = BinaryHeap::new();
        for (mov, games) in &self.games {
            heap.push((games.len(), mov))
        }
        let mut v = Vec::new();
        for _ in 1..10 {
            match heap.pop() {
                Some(t) => v.push(t),
                _ => break,
            }
        }
        v
    }
}
<|endoftext|>