<commit_before>//! This example draws a crude version of the Rust logo.
//! The version is "crude" because it's all very approximate with plenty of guesswork.
//! If you can come up with a way to do this so that it exactly matches the logo, please
//! do submit it!
//! In any case, this is an excellent example of creatively using fills and some other
//! techniques to create a fairly complex drawing.

extern crate turtle;

use turtle::Turtle;

const SPEED: i32 = 7;

fn main() {
    let mut turtle = Turtle::new();

    turtle.set_speed(SPEED);
    turtle.set_fill_color("black");

    turtle.begin_fill();
    turtle.pen_up();
    turtle.forward(130.0);
    turtle.pen_down();

    gear(&mut turtle);
    turtle.end_fill();

    turtle.pen_up();
    turtle.forward(45.0);
    turtle.left(90.0);
    turtle.pen_down();

    inner_gear(&mut turtle);

    letter(&mut turtle);

    turtle.hide();
}

fn gear(turtle: &mut Turtle) {
    // Much of this code was figured out by guessing and checking
    let teeth = 32.0;
    let tooth_size = 20.0;
    let angle = 360.0 / teeth;
    turtle.right(180.0 - angle * 6.0 / 2.0);
    for _ in 0..teeth as u64 {
        turtle.forward(tooth_size);
        turtle.left(180.0 - angle * 7.0);
        turtle.forward(tooth_size);
        turtle.right(180.0 - angle * 6.0);
    }

    turtle.right(angle * 6.0 / 2.0);
}

fn inner_gear(turtle: &mut Turtle) {
    inner_circle(turtle);
    inner_teeth(turtle);
}

fn inner_circle(turtle: &mut Turtle) {
    turtle.set_fill_color("white");
    turtle.begin_fill();
    for _ in 0..360 {
        turtle.forward(1.5);
        turtle.right(1.0);
    }
    turtle.end_fill();
    turtle.set_fill_color("black");
}

fn inner_teeth(turtle: &mut Turtle) {
    let tooth_size = 40.0;
    turtle.pen_up();
    turtle.left(90.0);
    turtle.forward(10.0);
    turtle.right(90.0);
    turtle.pen_down();

    for _ in 0..5 {
        turtle.pen_up();
        turtle.forward(tooth_size / 2.0);
        turtle.pen_down();

        turtle.begin_fill();
        turtle.right(120.0);
        turtle.forward(tooth_size);
        turtle.right(120.0);
        turtle.forward(tooth_size);
        turtle.right(120.0);
        turtle.end_fill();

        turtle.pen_up();
        turtle.forward(tooth_size / 2.0);
        turtle.right(90.0);
        turtle.forward(5.0);
        turtle.left(90.0);
        turtle.pen_down();

        inner_tooth_circle(turtle);

        turtle.pen_up();
        turtle.left(90.0);
        turtle.forward(5.0);
        turtle.right(90.0);
        for _ in 0..360/5/2 {
            turtle.forward(3.41);
            turtle.right(2.0);
        }
        turtle.pen_down();
    }
}

fn inner_tooth_circle(turtle: &mut Turtle) {
    turtle.set_fill_color("white");
    turtle.begin_fill();
    for _ in 0..360/8 {
        turtle.forward(1.0);
        turtle.right(8.0);
    }
    turtle.end_fill();
    turtle.set_fill_color("black");
}

fn letter(turtle: &mut Turtle) {
    turtle.pen_up();
    turtle.right(180.0);
    turtle.pen_down();
    for _ in 0..(360/5 - 8)/2 {
        turtle.forward(3.41);
        turtle.left(2.0);
    }
    // Trick for making the turtle face to the right
    let heading = turtle.heading();
    turtle.right(heading);

    turtle.set_pen_size(12.0);
    turtle.forward(110.0);
    for _ in 0..600 {
        // By going forward and then backward, we avoid any "gaps" between the very small movements
        // To see why this is needed, try replacing this with a single movement of the difference
        // between the two.
        turtle.forward(0.5);
        turtle.set_speed("instant");
        turtle.backward(0.4);
        turtle.right(0.3);
        turtle.set_speed(SPEED);
    }

    turtle.set_speed(SPEED);
    turtle.forward(52.0);
    turtle.right(90.0);
    turtle.forward(50.0);
    turtle.backward(90.0);

    turtle.left(90.0);
    turtle.forward(65.0);
    turtle.backward(95.0);

    turtle.pen_up();
    turtle.backward(95.0);
    turtle.pen_down();

    turtle.forward(40.0);
    for _ in 0..400 {
        turtle.forward(0.5);
        turtle.set_speed("instant");
        turtle.backward(0.4);
        turtle.right(0.2);
        turtle.set_speed(SPEED);
    }

    for _ in 0..200 {
        turtle.forward(0.5);
        turtle.set_speed("instant");
        turtle.backward(0.4);
        turtle.left(0.1);
        turtle.set_speed(SPEED);
    }
}
<commit_msg>Speeding up the curves of the R in the Rust logo<commit_after>//! This example draws a crude version of the Rust logo.
//! The version is "crude" because it's all very approximate with plenty of guesswork.
//! If you can come up with a way to do this so that it exactly matches the logo, please
//! do submit it!
//! In any case, this is an excellent example of creatively using fills and some other
//! techniques to create a fairly complex drawing.

extern crate turtle;

use turtle::Turtle;

const SPEED: i32 = 7;

fn main() {
    let mut turtle = Turtle::new();

    turtle.set_speed(SPEED);
    turtle.set_fill_color("black");

    turtle.begin_fill();
    turtle.pen_up();
    turtle.forward(130.0);
    turtle.pen_down();

    gear(&mut turtle);
    turtle.end_fill();

    turtle.pen_up();
    turtle.forward(45.0);
    turtle.left(90.0);
    turtle.pen_down();

    inner_gear(&mut turtle);

    letter(&mut turtle);

    turtle.hide();
}

fn gear(turtle: &mut Turtle) {
    // Much of this code was figured out by guessing and checking
    let teeth = 32.0;
    let tooth_size = 20.0;
    let angle = 360.0 / teeth;
    turtle.right(180.0 - angle * 6.0 / 2.0);
    for _ in 0..teeth as u64 {
        turtle.forward(tooth_size);
        turtle.left(180.0 - angle * 7.0);
        turtle.forward(tooth_size);
        turtle.right(180.0 - angle * 6.0);
    }

    turtle.right(angle * 6.0 / 2.0);
}

fn inner_gear(turtle: &mut Turtle) {
    inner_circle(turtle);
    inner_teeth(turtle);
}

fn inner_circle(turtle: &mut Turtle) {
    turtle.set_fill_color("white");
    turtle.begin_fill();
    for _ in 0..360 {
        turtle.forward(1.5);
        turtle.right(1.0);
    }
    turtle.end_fill();
    turtle.set_fill_color("black");
}

fn inner_teeth(turtle: &mut Turtle) {
    let tooth_size = 40.0;
    turtle.pen_up();
    turtle.left(90.0);
    turtle.forward(10.0);
    turtle.right(90.0);
    turtle.pen_down();

    for _ in 0..5 {
        turtle.pen_up();
        turtle.forward(tooth_size / 2.0);
        turtle.pen_down();

        turtle.begin_fill();
        turtle.right(120.0);
        turtle.forward(tooth_size);
        turtle.right(120.0);
        turtle.forward(tooth_size);
        turtle.right(120.0);
        turtle.end_fill();

        turtle.pen_up();
        turtle.forward(tooth_size / 2.0);
        turtle.right(90.0);
        turtle.forward(5.0);
        turtle.left(90.0);
        turtle.pen_down();

        inner_tooth_circle(turtle);

        turtle.pen_up();
        turtle.left(90.0);
        turtle.forward(5.0);
        turtle.right(90.0);
        for _ in 0..360/5/2 {
            turtle.forward(3.41);
            turtle.right(2.0);
        }
        turtle.pen_down();
    }
}

fn inner_tooth_circle(turtle: &mut Turtle) {
    turtle.set_fill_color("white");
    turtle.begin_fill();
    for _ in 0..360/8 {
        turtle.forward(1.0);
        turtle.right(8.0);
    }
    turtle.end_fill();
    turtle.set_fill_color("black");
}

fn letter(turtle: &mut Turtle) {
    turtle.pen_up();
    turtle.right(180.0);
    turtle.pen_down();
    for _ in 0..(360/5 - 8)/2 {
        turtle.forward(3.41);
        turtle.left(2.0);
    }
    // Trick for making the turtle face to the right
    let heading = turtle.heading();
    turtle.right(heading);

    turtle.set_pen_size(12.0);
    turtle.forward(110.0);
    for _ in 0..600 {
        // By going forward and then backward, we avoid any "gaps" between the very small movements
        // To see why this is needed, try replacing this with a single movement of the difference
        // between the two.
        turtle.forward(0.5);
        turtle.set_speed("instant");
        turtle.backward(0.4);
        turtle.right(0.3);
        turtle.set_speed(10);
    }

    turtle.set_speed(SPEED);
    turtle.forward(52.0);
    turtle.right(90.0);
    turtle.forward(50.0);
    turtle.backward(90.0);

    turtle.left(90.0);
    turtle.forward(65.0);
    turtle.backward(95.0);

    turtle.pen_up();
    turtle.backward(95.0);
    turtle.pen_down();

    turtle.forward(40.0);
    for _ in 0..400 {
        turtle.forward(0.5);
        turtle.set_speed("instant");
        turtle.backward(0.4);
        turtle.right(0.2);
        turtle.set_speed(10);
    }

    for _ in 0..200 {
        turtle.forward(0.5);
        turtle.set_speed("instant");
        turtle.backward(0.4);
        turtle.left(0.1);
        turtle.set_speed(10);
    }
}
<|endoftext|>


<commit_before>//
//  SOS: the Stupid Operating System
//  by Hawk Weisman (hi@hawkweisman.me)
//
//  Copyright (c) 2015 Hawk Weisman
//  Released under the terms of the MIT license. See `LICENSE` in the root
//  directory of this repository for more information.
//
//! Code for interacting with the system's VGA buffer.
#![crate_name = "sos_vga"]

#![feature( no_std
          , const_fn
          , core_slice_ext
          , core_str_ext
          , slice_patterns
          , unique
          )]
#![cfg_attr( feature = "system_term"
           , feature(lang_items) )]
#![no_std]

#[cfg(feature = "system_term")]
extern crate spin;

use core::mem;
use core::fmt::{Write, Result};
use core::ptr::Unique;

#[cfg(feature = "system_term")]
use spin::Mutex;

/// The system's global VGA terminal
#[cfg(feature = "system_term")]
pub static CONSOLE: Mutex<Terminal>
    = Mutex::new(unsafe { Terminal::new(
         Palette::new(Color::LightGreen, Color::Black )
       , 0xB8000
    )});

// TODO: support varying VGA screen sizes?
pub const X_MAX: usize = 80;
pub const Y_MAX: usize = 25;

/// The type signature fot the actual VGA buffer
pub type Buffer = [[Char; X_MAX]; Y_MAX];

const ANSI_ESCAPE: &'static str = "\x1b";
const FG_MASK: u8 = 0b0000_1111;
const BG_MASK: u8 = 0b1111_0000;

/// VGA color codes
#[derive(Eq, PartialEq, Ord, PartialOrd, Copy, Clone)]
#[repr(u8)]
pub enum Color { Black = 0
               , Blue = 1
               , Green = 2
               , Cyan = 3
               , Red = 4
               , Magenta = 5
               , Brown = 6
               , LightGrey = 7
               , DarkGrey = 8
               , LightBlue = 9
               , LightGreen = 10
               , LightCyan = 11
               , LightRed = 12
               , LightMagenta = 13
               , Yellow = 14
               , White = 15
               }

#[derive(Eq, PartialEq, Ord, PartialOrd, Copy, Clone)]
pub struct Palette(u8);

impl Palette {
    /// Returns a `Palette` with the given foreground and background color.
    pub const fn new(fg: Color, bg: Color) -> Self {
        Palette( (bg as u8) << 4 | (fg as u8) )
    }

    /// Returns a new `Palette` with this palette's background color, and
    /// the specified foreground color.
    pub fn set_foreground(&self, fg: Color) -> Self {
        Palette( ( self.0 & BG_MASK) | (fg as u8 & FG_MASK) )
    }

    /// Returns a new `Palette` with this palette's foreground color, and
    /// the specified background color.
    pub fn set_background(&self, bg: Color) -> Self {
        Palette( ( (bg as u8) << 4 & BG_MASK) | (self.0 & FG_MASK) )
    }

    /// Returns this `Palette`'s foreground color.
    pub fn foreground(&self) -> Color {
        unsafe { mem::transmute(self.0 & FG_MASK) }
    }

    /// Returns this `Palette`'s background color.
    pub fn background(&self) -> Color {
        unsafe { mem::transmute((self.0 & BG_MASK) >> 4) }
    }

}
/// A colored VGA character.
#[derive(Copy, Clone)]
#[repr(C)]
pub struct Char { pub ascii: u8
                , pub colors: Palette
                }

pub struct Terminal { buffer: Unique<Buffer>
                    , x: usize
                    , y: usize
                    , colors: Palette
                    }
impl Terminal {

    /// Constructs a new `Terminal` for abuffer starting at the given address.
    ///
    /// # Arguments:
    ///   - `colors`: the default color palette for the terminal
    ///   - `buffer_start`: the address of the to the memory location where
    ///      the terminal's VGA buffer begins
    ///
    /// # Unsafe due to:
    ///   - Casting a raw address to an array
    pub const unsafe fn new(colors: Palette, buffer_start: usize)
                            -> Terminal {
        Terminal { x: 0, y: 0
                 , colors: colors
                 , buffer: Unique::new(buffer_start as *mut _)
                 }
    }

    #[inline]
    fn buffer(&mut self) -> &mut  Buffer {
        unsafe { self.buffer.get_mut() }
    }

    /// Set the color palette used for writing subsequent characters.
    pub fn set_colors(&mut self, bg: Color, fg: Color) -> &mut Self {
        self.colors = Palette::new(bg,fg);
        self
    }

    /// Scrolls the terminal one row.
    fn scroll(&mut self) {
        // // construct an iterator over the whole buffer
        // let mut rows = self.buffer()
        //                    .iter_mut();
        //
        // // the current row in the buffer
        // let mut current = rows.next()
        //                    .unwrap();
        //
        // while let Some(next) = rows.next() {
        //     // while there are rows remaining in the iterator, swap the
        //     // next row with the current one (moving it back by one)
        //     mem::swap(current, next);
        //     // and advance our pointer to the current row.
        //     current = next;
        // }
        let buffer: &mut _ = self.buffer();
        for row in 1..Y_MAX {
            buffer[row - 1] = buffer[row];
        }
        // empty the last line in the buffer
        unsafe { buffer[Y_MAX - 1] = mem::zeroed() }
    }

    /// Clear the terminal
    pub fn clear(&mut self) -> &mut Self {
        // to clear the terminal, we just zero out the whole buffer.
        unsafe { *(self.buffer()) = mem::zeroed(); }
        self.x = 0;
        self.y = 0;
        self
    }

    /// Write the given byte to the terminal, and advance the cursor position.
    pub fn write_byte(&mut self, byte: u8) -> &mut Self {
        if byte == b'\n' {
            // if the byte is a newline, we just advance to the next line
            // and reset the column position.
            self.x = 0;
            self.y += 1;
        } else {
            // otherwise, it's a regular character, so we just set the
            // byte at the current position in the buffer to that
            // character (with the current color palette)
            self.buffer()[self.y][self.x]
                = Char { ascii: byte
                       , colors: self.colors };
            // and advance our column position by one
            self.x += 1;

            if self.x >= X_MAX {
                // if we've reached the end of the line, advance to the next
                self.x = 0;
                self.y += 1;
            }
        }
        if self.y >= Y_MAX {
            // if we've reached the bottom of the terminal, scroll.
            self.scroll();
            self.y -= 1;
        }
        self
    }

    fn handle_ansi_escape(&self, escape_code: &str) -> Result {
        match escape_code.as_bytes() {
            // `\x1b[3Nm` sets the foreground color to N.
            [0x1b, b'[', b'3', n, b'm'] => {
                unsafe { self.colors
                             .set_foreground(mem::transmute(n - 48)); }
                Ok(())
            }
            // `\x1b[4Nm` sets the background color to N
          , [0x1b, b'[', b'4', n, b'm'] => {
                unsafe { self.colors
                             .set_background(mem::transmute(n - 48)); }
                Ok(())
            }
          , _ => unimplemented!()
        }
        // let escape_seq: &str = bytes.take_while(|b| b != b'm')
        //                       .collect::<&str>();
        // match escape_seq {
        //     [b'3', n] => unsafe {
        //         self.colors.set_foreground(mem::transmute(n - 48))
        //     }
        // }
        // while let Some(byte) = bytes.next() {
        //     match *byte {
        //         // we've recieved an ANSI escape sequence.
        //         // this basically enters a mediocre FSM for matching ANSI
        //         // control codes.
        //         0x1b => match *next_ansi_byte!(bytes) {
        //             // handle multi-char ANSI escapes
        //             b'[' => match *next_ansi_byte!(bytes) {
        //                 // foreground color code
        //                 fg @ 30 ... 37 => {
        //                     if !(*next_ansi_byte!(bytes) == b'm') {
        //                         unsafe {
        //                             let color: vga::Color
        //                                 = mem::transmute(fg - 30);
        //                             self.colors
        //                                 .set_foreground(color);
        //                         }
        //
        //                     }
        //                 }
        //                 // background color code
        //               , 40 ... 47 => {
        //
        //                 }
        //               , _ => unimplemented!()
        //             }
        //           , _    => unimplemented!()
        //         }
        //         // otherwise, treat the byte as a normal ASCII char
        //       , b => { self.write_byte(b); }
        //     }
        // }

    }

}

struct AnsiEscapeIter<'a> { curr_slice: &'a str
                          , in_escape: bool
                          }

impl<'a> AnsiEscapeIter<'a> {

    pub fn new(s: &'a str) -> Self {
        AnsiEscapeIter { curr_slice: s
                       , in_escape: false
                       }
    }
}

impl<'a> Iterator for AnsiEscapeIter<'a> {
    type Item = &'a str;

    fn next(&mut self) -> Option<Self::Item> {
        if self.curr_slice.len() == 0 {
            // if the remaining string is empty, we just return `None`
            None
        } else {
            // otherwise, find the next index to chunk on.
            let maybe_idx
                = if self.in_escape {
                     // if we're in an escape code, we split the chunk at the
                     // index of the next 'm' character, adding 1 so that the
                     // 'm' is in the escape code chunk.
                    self.curr_slice.find('m')
                        .map(|idx| idx + 1)
                } else {
                    // otherwise, split at the next ANSI escape sequence
                    self.curr_slice.find(ANSI_ESCAPE)
                };

            // if we found another index to chunk on, map over that index;
            // otherwise, we just yield the rest of the string
            maybe_idx.map_or(
                Some(self.curr_slice) // remainder (if no index to chunk on)
              , |idx| { // otherwise, chunk along that index...
                    let (chunk, next_slice) = self.curr_slice
                                                  .split_at(idx);
                    self.curr_slice = next_slice; // update current chunk
                    Some(chunk)                   // return the chunk
                })
        }

    }
}

impl Write for Terminal {

    fn write_str(&mut self, s: &str) -> Result {

        if s.contains(ANSI_ESCAPE) {
            // if the segment contains an ANSI escape, construct an iterator
            // over each chunk containing either an escape sequence or text
            for segment in AnsiEscapeIter::new(s) {
                if segment.starts_with(ANSI_ESCAPE) {
                    // if the current segment is an ANSI escape code,
                    // try to handle the escape and fail if it is malformed
                    try!(self.handle_ansi_escape(segment))
                } else {
                    // otherwise, just write each chunk in the string.
                    for byte in segment.as_bytes() {
                        self.write_byte(*byte);
                    }
                }
            }
        } else {
            // otherwise, if there are no ANSI escape codes,
            // we can just write each byte in the string.
            for byte in s.as_bytes() {
                self.write_byte(*byte);
            }
        }
        Ok(())
    }

}

#[cfg(feature = "system_term")]
#[macro_export]
macro_rules! println {
    ($fmt:expr) => (print!(concat!($fmt, "\n")));
    ($fmt:expr, $($arg:tt)*) => (print!(concat!($fmt, "\n"), $($arg)*));
}

#[cfg(feature = "system_term")]
#[macro_export]
macro_rules! print {
    ($($arg:tt)*) => ({
            use core::fmt::Write;
            $crate::CONSOLE.lock()
                           .write_fmt(format_args!($($arg)*))
                           .unwrap();
    });
}

#[cfg(feature = "log")]
#[macro_export]
macro_rules! log {
    (level: $level:expr, $fmt:expr, $($arg:tt)*) => ({
        println!("[{} {}:{}] {}", level, file!(), line!()
                $fmt, $($arg)*)
    })
}

/// Required for Rust stack unwinding
#[cfg(feature = "system_term")]
#[lang = "eh_personality"]
#[no_mangle] #[inline(never)] #[cold]
pub extern "C" fn rust_eh_personality() {
    // TODO: add support for stack unwinding
    println!("eh")
}
<commit_msg>[vga] Fix logging macro support for messages without format args<commit_after>//
//  SOS: the Stupid Operating System
//  by Hawk Weisman (hi@hawkweisman.me)
//
//  Copyright (c) 2015 Hawk Weisman
//  Released under the terms of the MIT license. See `LICENSE` in the root
//  directory of this repository for more information.
//
//! Code for interacting with the system's VGA buffer.
#![crate_name = "sos_vga"]

#![feature( no_std
          , const_fn
          , core_slice_ext
          , core_str_ext
          , slice_patterns
          , unique
          )]
#![cfg_attr( feature = "system_term"
           , feature(lang_items) )]
#![no_std]

#[cfg(feature = "system_term")]
extern crate spin;

use core::mem;
use core::fmt::{Write, Result};
use core::ptr::Unique;

#[cfg(feature = "system_term")]
use spin::Mutex;

/// The system's global VGA terminal
#[cfg(feature = "system_term")]
pub static CONSOLE: Mutex<Terminal>
    = Mutex::new(unsafe { Terminal::new(
         Palette::new(Color::LightGreen, Color::Black )
       , 0xB8000
    )});

// TODO: support varying VGA screen sizes?
pub const X_MAX: usize = 80;
pub const Y_MAX: usize = 25;

/// The type signature fot the actual VGA buffer
pub type Buffer = [[Char; X_MAX]; Y_MAX];

const ANSI_ESCAPE: &'static str = "\x1b";
const FG_MASK: u8 = 0b0000_1111;
const BG_MASK: u8 = 0b1111_0000;

/// VGA color codes
#[derive(Eq, PartialEq, Ord, PartialOrd, Copy, Clone)]
#[repr(u8)]
pub enum Color { Black = 0
               , Blue = 1
               , Green = 2
               , Cyan = 3
               , Red = 4
               , Magenta = 5
               , Brown = 6
               , LightGrey = 7
               , DarkGrey = 8
               , LightBlue = 9
               , LightGreen = 10
               , LightCyan = 11
               , LightRed = 12
               , LightMagenta = 13
               , Yellow = 14
               , White = 15
               }

#[derive(Eq, PartialEq, Ord, PartialOrd, Copy, Clone)]
pub struct Palette(u8);

impl Palette {
    /// Returns a `Palette` with the given foreground and background color.
    pub const fn new(fg: Color, bg: Color) -> Self {
        Palette( (bg as u8) << 4 | (fg as u8) )
    }

    /// Returns a new `Palette` with this palette's background color, and
    /// the specified foreground color.
    pub fn set_foreground(&self, fg: Color) -> Self {
        Palette( ( self.0 & BG_MASK) | (fg as u8 & FG_MASK) )
    }

    /// Returns a new `Palette` with this palette's foreground color, and
    /// the specified background color.
    pub fn set_background(&self, bg: Color) -> Self {
        Palette( ( (bg as u8) << 4 & BG_MASK) | (self.0 & FG_MASK) )
    }

    /// Returns this `Palette`'s foreground color.
    pub fn foreground(&self) -> Color {
        unsafe { mem::transmute(self.0 & FG_MASK) }
    }

    /// Returns this `Palette`'s background color.
    pub fn background(&self) -> Color {
        unsafe { mem::transmute((self.0 & BG_MASK) >> 4) }
    }

}
/// A colored VGA character.
#[derive(Copy, Clone)]
#[repr(C)]
pub struct Char { pub ascii: u8
                , pub colors: Palette
                }

pub struct Terminal { buffer: Unique<Buffer>
                    , x: usize
                    , y: usize
                    , colors: Palette
                    }
impl Terminal {

    /// Constructs a new `Terminal` for abuffer starting at the given address.
    ///
    /// # Arguments:
    ///   - `colors`: the default color palette for the terminal
    ///   - `buffer_start`: the address of the to the memory location where
    ///      the terminal's VGA buffer begins
    ///
    /// # Unsafe due to:
    ///   - Casting a raw address to an array
    pub const unsafe fn new(colors: Palette, buffer_start: usize)
                            -> Terminal {
        Terminal { x: 0, y: 0
                 , colors: colors
                 , buffer: Unique::new(buffer_start as *mut _)
                 }
    }

    #[inline]
    fn buffer(&mut self) -> &mut  Buffer {
        unsafe { self.buffer.get_mut() }
    }

    /// Set the color palette used for writing subsequent characters.
    pub fn set_colors(&mut self, bg: Color, fg: Color) -> &mut Self {
        self.colors = Palette::new(bg,fg);
        self
    }

    /// Scrolls the terminal one row.
    fn scroll(&mut self) {
        // // construct an iterator over the whole buffer
        // let mut rows = self.buffer()
        //                    .iter_mut();
        //
        // // the current row in the buffer
        // let mut current = rows.next()
        //                    .unwrap();
        //
        // while let Some(next) = rows.next() {
        //     // while there are rows remaining in the iterator, swap the
        //     // next row with the current one (moving it back by one)
        //     mem::swap(current, next);
        //     // and advance our pointer to the current row.
        //     current = next;
        // }
        let buffer: &mut _ = self.buffer();
        for row in 1..Y_MAX {
            buffer[row - 1] = buffer[row];
        }
        // empty the last line in the buffer
        unsafe { buffer[Y_MAX - 1] = mem::zeroed() }
    }

    /// Clear the terminal
    pub fn clear(&mut self) -> &mut Self {
        // to clear the terminal, we just zero out the whole buffer.
        unsafe { *(self.buffer()) = mem::zeroed(); }
        self.x = 0;
        self.y = 0;
        self
    }

    /// Write the given byte to the terminal, and advance the cursor position.
    pub fn write_byte(&mut self, byte: u8) -> &mut Self {
        if byte == b'\n' {
            // if the byte is a newline, we just advance to the next line
            // and reset the column position.
            self.x = 0;
            self.y += 1;
        } else {
            // otherwise, it's a regular character, so we just set the
            // byte at the current position in the buffer to that
            // character (with the current color palette)
            self.buffer()[self.y][self.x]
                = Char { ascii: byte
                       , colors: self.colors };
            // and advance our column position by one
            self.x += 1;

            if self.x >= X_MAX {
                // if we've reached the end of the line, advance to the next
                self.x = 0;
                self.y += 1;
            }
        }
        if self.y >= Y_MAX {
            // if we've reached the bottom of the terminal, scroll.
            self.scroll();
            self.y -= 1;
        }
        self
    }

    fn handle_ansi_escape(&self, escape_code: &str) -> Result {
        match escape_code.as_bytes() {
            // `\x1b[3Nm` sets the foreground color to N.
            [0x1b, b'[', b'3', n, b'm'] => {
                unsafe { self.colors
                             .set_foreground(mem::transmute(n - 48)); }
                Ok(())
            }
            // `\x1b[4Nm` sets the background color to N
          , [0x1b, b'[', b'4', n, b'm'] => {
                unsafe { self.colors
                             .set_background(mem::transmute(n - 48)); }
                Ok(())
            }
          , _ => unimplemented!()
        }
        // let escape_seq: &str = bytes.take_while(|b| b != b'm')
        //                       .collect::<&str>();
        // match escape_seq {
        //     [b'3', n] => unsafe {
        //         self.colors.set_foreground(mem::transmute(n - 48))
        //     }
        // }
        // while let Some(byte) = bytes.next() {
        //     match *byte {
        //         // we've recieved an ANSI escape sequence.
        //         // this basically enters a mediocre FSM for matching ANSI
        //         // control codes.
        //         0x1b => match *next_ansi_byte!(bytes) {
        //             // handle multi-char ANSI escapes
        //             b'[' => match *next_ansi_byte!(bytes) {
        //                 // foreground color code
        //                 fg @ 30 ... 37 => {
        //                     if !(*next_ansi_byte!(bytes) == b'm') {
        //                         unsafe {
        //                             let color: vga::Color
        //                                 = mem::transmute(fg - 30);
        //                             self.colors
        //                                 .set_foreground(color);
        //                         }
        //
        //                     }
        //                 }
        //                 // background color code
        //               , 40 ... 47 => {
        //
        //                 }
        //               , _ => unimplemented!()
        //             }
        //           , _    => unimplemented!()
        //         }
        //         // otherwise, treat the byte as a normal ASCII char
        //       , b => { self.write_byte(b); }
        //     }
        // }

    }

}

struct AnsiEscapeIter<'a> { curr_slice: &'a str
                          , in_escape: bool
                          }

impl<'a> AnsiEscapeIter<'a> {

    pub fn new(s: &'a str) -> Self {
        AnsiEscapeIter { curr_slice: s
                       , in_escape: false
                       }
    }
}

impl<'a> Iterator for AnsiEscapeIter<'a> {
    type Item = &'a str;

    fn next(&mut self) -> Option<Self::Item> {
        if self.curr_slice.len() == 0 {
            // if the remaining string is empty, we just return `None`
            None
        } else {
            // otherwise, find the next index to chunk on.
            let maybe_idx
                = if self.in_escape {
                     // if we're in an escape code, we split the chunk at the
                     // index of the next 'm' character, adding 1 so that the
                     // 'm' is in the escape code chunk.
                    self.curr_slice.find('m')
                        .map(|idx| idx + 1)
                } else {
                    // otherwise, split at the next ANSI escape sequence
                    self.curr_slice.find(ANSI_ESCAPE)
                };

            // if we found another index to chunk on, map over that index;
            // otherwise, we just yield the rest of the string
            maybe_idx.map_or(
                Some(self.curr_slice) // remainder (if no index to chunk on)
              , |idx| { // otherwise, chunk along that index...
                    let (chunk, next_slice) = self.curr_slice
                                                  .split_at(idx);
                    self.curr_slice = next_slice; // update current chunk
                    Some(chunk)                   // return the chunk
                })
        }

    }
}

impl Write for Terminal {

    fn write_str(&mut self, s: &str) -> Result {

        if s.contains(ANSI_ESCAPE) {
            // if the segment contains an ANSI escape, construct an iterator
            // over each chunk containing either an escape sequence or text
            for segment in AnsiEscapeIter::new(s) {
                if segment.starts_with(ANSI_ESCAPE) {
                    // if the current segment is an ANSI escape code,
                    // try to handle the escape and fail if it is malformed
                    try!(self.handle_ansi_escape(segment))
                } else {
                    // otherwise, just write each chunk in the string.
                    for byte in segment.as_bytes() {
                        self.write_byte(*byte);
                    }
                }
            }
        } else {
            // otherwise, if there are no ANSI escape codes,
            // we can just write each byte in the string.
            for byte in s.as_bytes() {
                self.write_byte(*byte);
            }
        }
        Ok(())
    }

}

#[cfg(feature = "system_term")]
#[macro_export]
macro_rules! println {
    ($fmt:expr) => (print!(concat!($fmt, "\n")));
    ($fmt:expr, $($arg:tt)*) => (print!(concat!($fmt, "\n"), $($arg)*));
}

#[cfg(feature = "system_term")]
#[macro_export]
macro_rules! print {
    ($($arg:tt)*) => ({
            use core::fmt::Write;
            $crate::CONSOLE.lock()
                           .write_fmt(format_args!($($arg)*))
                           .unwrap();
    });
}

#[cfg(feature = "log")]
#[macro_export]
macro_rules! log {
    (level: $level:expr, $fmt:expr) => ({
        println!("[{} {}:{}] {}", level, file!(), line!(), $fmt);
    });
    (level: $level:expr, $fmt:expr, $($arg:tt)*) => ({
        println!( "[{} {}:{}] {}", level, file!(), line!()
                , $fmt, $($arg)*);
    })
}

/// Required for Rust stack unwinding
#[cfg(feature = "system_term")]
#[lang = "eh_personality"]
#[no_mangle] #[inline(never)] #[cold]
pub extern "C" fn rust_eh_personality() {
    // TODO: add support for stack unwinding
    println!("eh")
}
<|endoftext|>


<commit_before>use iced::{
  canvas, executor, mouse, Application, Canvas, Color, Command, Element, Length, Point, Rectangle, Settings, Vector,
};
use oppai_field::field::{self, Field, Pos};
use oppai_field::player::Player;
use oppai_field::zobrist::Zobrist;
use rand::SeedableRng;
use rand_xorshift::XorShiftRng;

const FIELD_WIDTH: u32 = 39;
const FIELD_HEIGHT: u32 = 32;

pub fn main() -> iced::Result {
  Game::run(Settings {
    antialiasing: true,
    ..Settings::default()
  })
}

struct Game {
  player: Player,
  field: Field,
  captures: Vec<(Vec<Pos>, Player)>,
  field_cache: canvas::Cache,
}

#[derive(Debug, Clone, Copy)]
enum Message {
  PutPoint(Pos),
}

impl Application for Game {
  type Executor = executor::Default;
  type Message = Message;
  type Flags = ();

  fn new(_flags: ()) -> (Self, Command<Self::Message>) {
    let mut rng = XorShiftRng::from_entropy();
    let zobrist = Zobrist::new(field::length(FIELD_WIDTH, FIELD_HEIGHT) * 2, &mut rng);
    let field = Field::new(FIELD_WIDTH, FIELD_HEIGHT, std::sync::Arc::new(zobrist));
    (
      Game {
        player: Player::Red,
        field,
        captures: Vec::new(),
        field_cache: Default::default(),
      },
      Command::none(),
    )
  }

  fn title(&self) -> String {
    "OpPAI".into()
  }

  fn update(&mut self, message: Self::Message) -> Command<Self::Message> {
    let Message::PutPoint(pos) = message;
    if self.field.put_point(pos, self.player) {
      let last_chain = self.field.get_last_chain();
      if let Some(&pos) = last_chain.first() {
        let player = self.field.cell(pos).get_player();
        self.captures.push((last_chain, player));
      }

      let n = self.field.n(pos);
      let s = self.field.s(pos);
      let w = self.field.w(pos);
      let e = self.field.e(pos);
      let nw = self.field.nw(pos);
      let ne = self.field.ne(pos);
      let sw = self.field.sw(pos);
      let se = self.field.se(pos);

      let mut check = |pos1: Pos, pos2: Pos| {
        if self.field.cell(pos1).get_players_point() == Some(self.player)
          && self.field.cell(pos2).get_players_point() == Some(self.player)
        {
          self.captures.push((vec![pos, pos1, pos2], self.player));
          true
        } else {
          false
        }
      };

      let _ = !check(s, e) && (check(s, se) || check(e, se));
      let _ = !check(e, n) && (check(e, ne) || check(n, ne));
      let _ = !check(n, w) && (check(n, nw) || check(w, nw));
      let _ = !check(w, s) && (check(w, sw) || check(s, sw));

      self.player = self.player.next();

      self.field_cache.clear();
    }

    Command::none()
  }

  fn view(&mut self) -> iced::Element<'_, Self::Message> {
    let canvas = Canvas::new(self).height(Length::Fill).width(Length::Fill);
    Element::<Pos>::from(canvas).map(Message::PutPoint)
  }
}

impl canvas::Program<Pos> for Game {
  fn update(
    &mut self,
    event: canvas::Event,
    bounds: Rectangle,
    cursor: canvas::Cursor,
  ) -> (canvas::event::Status, Option<Pos>) {
    let cursor_position = if let Some(position) = cursor.position_in(&bounds) {
      position
    } else {
      return (canvas::event::Status::Ignored, None);
    };
    match event {
      canvas::Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left)) => {
        let field_width = self.field.width();
        let field_height = self.field.height();
        let width = bounds
          .width
          .min(bounds.height / field_height as f32 * field_width as f32);
        let height = bounds
          .height
          .min(bounds.width / field_width as f32 * field_height as f32);
        let step_x = width / field_width as f32;
        let step_y = height / field_height as f32;
        let shift = Vector::new(
          ((bounds.width - width) / 2.0).round(),
          ((bounds.height - height) / 2.0).round(),
        );
        let cursor_shift = Vector::new(step_x / 2.0, step_y / 2.0);

        let point = cursor_position - shift;
        if point.x >= 0.0 && point.x <= width && point.y >= 0.0 && point.y <= height {
          let point = point - cursor_shift;
          let x = (point.x / step_x).round() as u32;
          let y = (point.y / step_y).round() as u32;
          (canvas::event::Status::Captured, Some(self.field.to_pos(x, y)))
        } else {
          (canvas::event::Status::Captured, None)
        }
      }
      _ => (canvas::event::Status::Ignored, None),
    }
  }

  fn draw(&self, bounds: Rectangle, cursor: canvas::Cursor) -> Vec<canvas::Geometry> {
    fn color(player: Player) -> Color {
      match player {
        Player::Red => Color::from_rgb8(0xFF, 0x00, 0x00),
        Player::Black => Color::BLACK,
      }
    }

    let field_width = self.field.width();
    let field_height = self.field.height();
    let width = bounds
      .width
      .min(bounds.height / field_height as f32 * field_width as f32);
    let height = bounds
      .height
      .min(bounds.width / field_width as f32 * field_height as f32);
    let step_x = width / field_width as f32;
    let step_y = height / field_height as f32;
    let shift = Vector::new(
      ((bounds.width - width) / 2.0).round(),
      ((bounds.height - height) / 2.0).round(),
    );
    let cursor_shift = Vector::new(step_x / 2.0, step_y / 2.0);

    let xy_to_point = |x: u32, y: u32| {
      let offset_x = (step_x * x as f32 + step_x / 2.0).round() + 0.5;
      let offset_y = (step_y * y as f32 + step_y / 2.0).round() + 0.5;
      Point::new(offset_x, offset_y) + shift
    };
    let pos_to_point = |pos: Pos| {
      let x = self.field.to_x(pos);
      let y = self.field.to_y(pos);
      xy_to_point(x, y)
    };

    let field = self.field_cache.draw(bounds.size(), |frame| {
      let grid = canvas::Path::new(|path| {
        for x in 0..field_width {
          let offset = (step_x * x as f32 + step_x / 2.0).round() + 0.5;
          path.move_to(Point::new(offset, 0.0) + shift);
          path.line_to(Point::new(offset, height) + shift);
        }
        for y in 0..field_height {
          let offset = (step_y * y as f32 + step_y / 2.0).round() + 0.5;
          path.move_to(Point::new(0.0, offset) + shift);
          path.line_to(Point::new(width, offset) + shift);
        }
      });

      frame.stroke(
        &grid,
        canvas::Stroke {
          width: 1.0,
          color: Color::BLACK,
          ..canvas::Stroke::default()
        },
      );

      for &player in &[Player::Red, Player::Black] {
        let points = canvas::Path::new(|path| {
          for &pos in self
            .field
            .points_seq()
            .iter()
            .filter(|&&pos| self.field.cell(pos).is_players_point(player))
          {
            path.circle(pos_to_point(pos), 5.0)
          }
        });

        frame.fill(&points, color(player));
      }

      for (chain, player) in &self.captures {
        let path = canvas::Path::new(|path| {
          path.move_to(pos_to_point(chain[0]));
          for &pos in chain.iter().skip(1) {
            path.line_to(pos_to_point(pos));
          }
        });

        let mut color = color(*player);
        color.a = 0.5;

        frame.fill(&path, color);
      }

      if let Some(&pos) = self.field.points_seq().last() {
        let last_point = canvas::Path::new(|path| path.circle(pos_to_point(pos), 8.0));

        let color = color(self.field.cell(pos).get_player());

        frame.stroke(
          &last_point,
          canvas::Stroke {
            width: 2.0,
            color,
            ..canvas::Stroke::default()
          },
        );
      }
    });

    let mut frame = canvas::Frame::new(bounds.size());

    if let Some(point) = cursor.position().and_then(|cursor_position| {
      let point = cursor_position - shift;
      if point.x >= 0.0 && point.x <= width && point.y >= 0.0 && point.y <= height {
        let point = point - cursor_shift;
        let x = (point.x / step_x).round() as u32;
        let y = (point.y / step_y).round() as u32;
        let pos = self.field.to_pos(x, y);
        if self.field.is_putting_allowed(pos) {
          Some(xy_to_point(x, y))
        } else {
          None
        }
      } else {
        None
      }
    }) {
      let cursor_point = canvas::Path::new(|path| path.circle(point, 5.0));

      let mut color = color(self.player);
      color.a = 0.5;

      frame.fill(&cursor_point, color);
    }

    vec![field, frame.into_geometry()]
  }
}
<commit_msg>Scale points.<commit_after>use iced::{
  canvas, executor, mouse, Application, Canvas, Color, Command, Element, Length, Point, Rectangle, Settings, Vector,
};
use oppai_field::field::{self, Field, Pos};
use oppai_field::player::Player;
use oppai_field::zobrist::Zobrist;
use rand::SeedableRng;
use rand_xorshift::XorShiftRng;

const FIELD_WIDTH: u32 = 39;
const FIELD_HEIGHT: u32 = 32;

pub fn main() -> iced::Result {
  Game::run(Settings {
    antialiasing: true,
    ..Settings::default()
  })
}

struct Game {
  player: Player,
  field: Field,
  captures: Vec<(Vec<Pos>, Player)>,
  field_cache: canvas::Cache,
}

#[derive(Debug, Clone, Copy)]
enum Message {
  PutPoint(Pos),
}

impl Application for Game {
  type Executor = executor::Default;
  type Message = Message;
  type Flags = ();

  fn new(_flags: ()) -> (Self, Command<Self::Message>) {
    let mut rng = XorShiftRng::from_entropy();
    let zobrist = Zobrist::new(field::length(FIELD_WIDTH, FIELD_HEIGHT) * 2, &mut rng);
    let field = Field::new(FIELD_WIDTH, FIELD_HEIGHT, std::sync::Arc::new(zobrist));
    (
      Game {
        player: Player::Red,
        field,
        captures: Vec::new(),
        field_cache: Default::default(),
      },
      Command::none(),
    )
  }

  fn title(&self) -> String {
    "OpPAI".into()
  }

  fn update(&mut self, message: Self::Message) -> Command<Self::Message> {
    let Message::PutPoint(pos) = message;
    if self.field.put_point(pos, self.player) {
      let last_chain = self.field.get_last_chain();
      if let Some(&pos) = last_chain.first() {
        let player = self.field.cell(pos).get_player();
        self.captures.push((last_chain, player));
      }

      let n = self.field.n(pos);
      let s = self.field.s(pos);
      let w = self.field.w(pos);
      let e = self.field.e(pos);
      let nw = self.field.nw(pos);
      let ne = self.field.ne(pos);
      let sw = self.field.sw(pos);
      let se = self.field.se(pos);

      let mut check = |pos1: Pos, pos2: Pos| {
        if self.field.cell(pos1).get_players_point() == Some(self.player)
          && self.field.cell(pos2).get_players_point() == Some(self.player)
        {
          self.captures.push((vec![pos, pos1, pos2], self.player));
          true
        } else {
          false
        }
      };

      let _ = !check(s, e) && (check(s, se) || check(e, se));
      let _ = !check(e, n) && (check(e, ne) || check(n, ne));
      let _ = !check(n, w) && (check(n, nw) || check(w, nw));
      let _ = !check(w, s) && (check(w, sw) || check(s, sw));

      self.player = self.player.next();

      self.field_cache.clear();
    }

    Command::none()
  }

  fn view(&mut self) -> iced::Element<'_, Self::Message> {
    let canvas = Canvas::new(self).height(Length::Fill).width(Length::Fill);
    Element::<Pos>::from(canvas).map(Message::PutPoint)
  }
}

impl canvas::Program<Pos> for Game {
  fn update(
    &mut self,
    event: canvas::Event,
    bounds: Rectangle,
    cursor: canvas::Cursor,
  ) -> (canvas::event::Status, Option<Pos>) {
    let cursor_position = if let Some(position) = cursor.position_in(&bounds) {
      position
    } else {
      return (canvas::event::Status::Ignored, None);
    };
    match event {
      canvas::Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left)) => {
        let field_width = self.field.width();
        let field_height = self.field.height();
        let width = bounds
          .width
          .min(bounds.height / field_height as f32 * field_width as f32);
        let height = bounds
          .height
          .min(bounds.width / field_width as f32 * field_height as f32);
        let step_x = width / field_width as f32;
        let step_y = height / field_height as f32;
        let shift = Vector::new(
          ((bounds.width - width) / 2.0).round(),
          ((bounds.height - height) / 2.0).round(),
        );
        let cursor_shift = Vector::new(step_x / 2.0, step_y / 2.0);

        let point = cursor_position - shift;
        if point.x >= 0.0 && point.x <= width && point.y >= 0.0 && point.y <= height {
          let point = point - cursor_shift;
          let x = (point.x / step_x).round() as u32;
          let y = (point.y / step_y).round() as u32;
          (canvas::event::Status::Captured, Some(self.field.to_pos(x, y)))
        } else {
          (canvas::event::Status::Captured, None)
        }
      }
      _ => (canvas::event::Status::Ignored, None),
    }
  }

  fn draw(&self, bounds: Rectangle, cursor: canvas::Cursor) -> Vec<canvas::Geometry> {
    fn color(player: Player) -> Color {
      match player {
        Player::Red => Color::from_rgb8(0xFF, 0x00, 0x00),
        Player::Black => Color::BLACK,
      }
    }

    let field_width = self.field.width();
    let field_height = self.field.height();
    let width = bounds
      .width
      .min(bounds.height / field_height as f32 * field_width as f32);
    let height = bounds
      .height
      .min(bounds.width / field_width as f32 * field_height as f32);
    let step_x = width / field_width as f32;
    let step_y = height / field_height as f32;
    let shift = Vector::new(
      ((bounds.width - width) / 2.0).round(),
      ((bounds.height - height) / 2.0).round(),
    );
    let cursor_shift = Vector::new(step_x / 2.0, step_y / 2.0);

    let xy_to_point = |x: u32, y: u32| {
      let offset_x = (step_x * x as f32 + step_x / 2.0).round() + 0.5;
      let offset_y = (step_y * y as f32 + step_y / 2.0).round() + 0.5;
      Point::new(offset_x, offset_y) + shift
    };
    let pos_to_point = |pos: Pos| {
      let x = self.field.to_x(pos);
      let y = self.field.to_y(pos);
      xy_to_point(x, y)
    };

    let point_radius = width / field_width as f32 / 6.0;

    let field = self.field_cache.draw(bounds.size(), |frame| {
      let grid = canvas::Path::new(|path| {
        for x in 0..field_width {
          let offset = (step_x * x as f32 + step_x / 2.0).round() + 0.5;
          path.move_to(Point::new(offset, 0.0) + shift);
          path.line_to(Point::new(offset, height) + shift);
        }
        for y in 0..field_height {
          let offset = (step_y * y as f32 + step_y / 2.0).round() + 0.5;
          path.move_to(Point::new(0.0, offset) + shift);
          path.line_to(Point::new(width, offset) + shift);
        }
      });

      frame.stroke(
        &grid,
        canvas::Stroke {
          width: 1.0,
          color: Color::BLACK,
          ..canvas::Stroke::default()
        },
      );

      for &player in &[Player::Red, Player::Black] {
        let points = canvas::Path::new(|path| {
          for &pos in self
            .field
            .points_seq()
            .iter()
            .filter(|&&pos| self.field.cell(pos).is_players_point(player))
          {
            path.circle(pos_to_point(pos), point_radius)
          }
        });

        frame.fill(&points, color(player));
      }

      for (chain, player) in &self.captures {
        let path = canvas::Path::new(|path| {
          path.move_to(pos_to_point(chain[0]));
          for &pos in chain.iter().skip(1) {
            path.line_to(pos_to_point(pos));
          }
        });

        let mut color = color(*player);
        color.a = 0.5;

        frame.fill(&path, color);
      }

      if let Some(&pos) = self.field.points_seq().last() {
        let last_point = canvas::Path::new(|path| path.circle(pos_to_point(pos), point_radius * 1.5));

        let color = color(self.field.cell(pos).get_player());

        frame.stroke(
          &last_point,
          canvas::Stroke {
            width: 2.0,
            color,
            ..canvas::Stroke::default()
          },
        );
      }
    });

    let mut frame = canvas::Frame::new(bounds.size());

    if let Some(point) = cursor.position().and_then(|cursor_position| {
      let point = cursor_position - shift;
      if point.x >= 0.0 && point.x <= width && point.y >= 0.0 && point.y <= height {
        let point = point - cursor_shift;
        let x = (point.x / step_x).round() as u32;
        let y = (point.y / step_y).round() as u32;
        let pos = self.field.to_pos(x, y);
        if self.field.is_putting_allowed(pos) {
          Some(xy_to_point(x, y))
        } else {
          None
        }
      } else {
        None
      }
    }) {
      let cursor_point = canvas::Path::new(|path| path.circle(point, point_radius));

      let mut color = color(self.player);
      color.a = 0.5;

      frame.fill(&cursor_point, color);
    }

    vec![field, frame.into_geometry()]
  }
}
<|endoftext|>


<commit_before>extern crate find_folder;

use screen::*;
use piston_window;
use conrod::{self, Widget, Frameable, Positionable};
use conrod::color::Colorable;

pub struct MainMenuScreen {
    ui: conrod::Ui<piston_window::Glyphs>,
}

impl MainMenuScreen {
    pub fn new(window: &piston_window::PistonWindow) -> MainMenuScreen {
        MainMenuScreen { ui: create_ui(&window) }
    }
}

impl Screen for MainMenuScreen {
    fn on_input(&mut self, input: &piston_window::Input, window: &piston_window::PistonWindow, context: &mut Context) -> InputResults {
        use piston_window::{Input, Button, MouseButton};

        let mut result = Vec::new();

        match input {
            &Input::Press(Button::Mouse(MouseButton::Left)) => {
                result.push(InputResult::PopScreen);
                result.push(InputResult::PushScreen(Box::new(BattleScreen::new(&window, context))));
            }
            _ => {}
        }

        result
    }

    fn on_draw(&mut self, args: &piston_window::RenderArgs, window: &piston_window::PistonWindow) {
        self.ui.handle_event(window);
        window.draw_2d(|context, graphics| self.ui.draw_if_changed(context, graphics));
    }

    fn on_update(&mut self, args: &piston_window::UpdateArgs) {
        self.ui.set_widgets(|ui| {
            conrod::Canvas::new()
                .frame(200.)
                .pad(30.0)
                .color(conrod::color::rgb(0.2, 0.35, 0.45))
                .scroll_kids()
                .set(CANVAS, ui);

            conrod::Text::new("Widget Demonstration")
                .top_left_with_margins_on(CANVAS, 0.0, 350.0)
                .font_size(32)
                .color(conrod::color::rgb(0.2, 0.35, 0.45).plain_contrast())
                .set(TITLE, ui);
        });
    }
}

widget_ids! {
    CANVAS,
    TITLE,
}

fn create_ui(window: &piston_window::PistonWindow) -> conrod::Ui<piston_window::Glyphs> {
    let assets = find_folder::Search::KidsThenParents(3, 5).for_folder("assets").unwrap();
    let font_path = assets.join("NotoSans-Regular.ttf");
    let theme = conrod::Theme::default();
    let glyph_cache = piston_window::Glyphs::new(&font_path, window.factory.borrow().clone());
    conrod::Ui::new(glyph_cache.unwrap(), theme)
}
<commit_msg>Main menu screen refactoring (assets path)<commit_after>use screen::*;
use piston_window;
use conrod::{self, Widget, Frameable, Positionable};
use conrod::color::Colorable;

pub struct MainMenuScreen {
    ui: conrod::Ui<piston_window::Glyphs>,
}

impl MainMenuScreen {
    pub fn new(window: &piston_window::PistonWindow, context: &mut Context) -> MainMenuScreen {
        MainMenuScreen { ui: create_ui(&window, &context.assets_path) }
    }
}

impl Screen for MainMenuScreen {
    fn on_input(&mut self, input: &piston_window::Input, window: &piston_window::PistonWindow, context: &mut Context) -> InputResults {
        use piston_window::{Input, Button, MouseButton};

        let mut result = Vec::new();

        match input {
            &Input::Press(Button::Mouse(MouseButton::Left)) => {
                result.push(InputResult::PopScreen);
                result.push(InputResult::PushScreen(Box::new(BattleScreen::new(&window, context))));
            }
            _ => {}
        }

        result
    }

    fn on_draw(&mut self, args: &piston_window::RenderArgs, window: &piston_window::PistonWindow) {
        self.ui.handle_event(window);
        window.draw_2d(|context, graphics| self.ui.draw_if_changed(context, graphics));
    }

    fn on_update(&mut self, args: &piston_window::UpdateArgs) {
        self.ui.set_widgets(|ui| {
            conrod::Canvas::new()
                .frame(200.)
                .pad(30.0)
                .color(conrod::color::rgb(0.2, 0.35, 0.45))
                .scroll_kids()
                .set(CANVAS, ui);

            conrod::Text::new("Widget Demonstration")
                .top_left_with_margins_on(CANVAS, 0.0, 350.0)
                .font_size(32)
                .color(conrod::color::rgb(0.2, 0.35, 0.45).plain_contrast())
                .set(TITLE, ui);
        });
    }
}

widget_ids! {
    CANVAS,
    TITLE,
}

fn create_ui(window: &piston_window::PistonWindow, assets_path: &::std::path::Path) -> conrod::Ui<piston_window::Glyphs> {
    let font_path = assets_path.join("NotoSans-Regular.ttf");
    let theme = conrod::Theme::default();
    let glyph_cache = piston_window::Glyphs::new(&font_path, window.factory.borrow().clone());
    conrod::Ui::new(glyph_cache.unwrap(), theme)
}
<|endoftext|>


<commit_before>// Copyright © 2016, Canal TP and/or its affiliates. All rights reserved.
//
// This file is part of Navitia,
//     the software to build cool stuff with public transport.
//
// Hope you'll enjoy and contribute to this project,
//     powered by Canal TP (www.canaltp.fr).
// Help us simplify mobility and open public transport:
//     a non ending quest to the responsive locomotion way of traveling!
//
// LICENCE: This program is free software; you can redistribute it
// and/or modify it under the terms of the GNU Affero General Public
// License as published by the Free Software Foundation, either
// version 3 of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public
// License along with this program. If not, see
// <http://www.gnu.org/licenses/>.
//
// Stay tuned using
// twitter @navitia
// IRC #navitia on freenode
// https://groups.google.com/d/forum/navitia
// www.navitia.io
use rustless;
use serde;
use serde_json;
use rustc_serialize::json;
use rustless::server::status;
use rustless::{Api, Nesting, Versioning};
use valico::json_dsl;
use valico::common::error as valico_error;
use super::query;
use super::Args;
use model::v1::*;
use model;

fn render<T>(mut client: rustless::Client,
             obj: T)
             -> Result<rustless::Client, rustless::ErrorResponse>
    where T: serde::Serialize
{
    client.set_json_content_type();
    client.text(serde_json::to_string(&obj).unwrap())
}

pub fn root(args: &Args) -> rustless::Api {
    Api::build(|api| {
        api.get("", |endpoint| {
            endpoint.handle(|client, _params| {
                let desc = EndPoint { description: "autocomplete service".to_string() };
                render(client, desc)
            })
        });

        api.error_formatter(|error, _media| {
            let err = if error.is::<rustless::errors::Validation>() {
                let val_err = error.downcast::<rustless::errors::Validation>().unwrap();
                // TODO better message, we shouldn't use {:?} but access the `path`
                // and `detail` of all errrors in val_err.reason
                CustomError {
                    short: "validation error".to_string(),
                    long: format!("invalid arguments {:?}", val_err.reason),
                }
            } else {
                CustomError {
                    short: "bad_request".to_string(),
                    long: format!("bad request, error: {}", error),
                }
            };
            let mut resp = rustless::Response::from(status::StatusCode::BadRequest,
                                                    Box::new(serde_json::to_string(&err).unwrap()));
            resp.set_json_content_type();
            Some(resp)
        });
        api.mount(v1(&args));
    })
}

pub fn v1(args: &Args) -> rustless::Api {
    Api::build(|api| {
        api.version("v1", Versioning::Path);

        api.get("", |endpoint| {
            endpoint.desc("api interface version v1");
            endpoint.handle(|client, _params| {
                render(client,
                       V1Reponse::Response { description: "api version 1".to_string() })
            })
        });
        api.mount(status(&args));
        api.mount(autocomplete(&args.clone()));
    })
}

pub fn status(_args: &Args) -> rustless::Api {
    Api::build(|api| {
        api.get("status", |endpoint| {
            endpoint.handle(|client, _params| {
                let status = Status {
                    version: "14".to_string(),
                    status: "good".to_string(),
                };
                render(client, status)
            })
        });
    })
}

fn check_bound(val: &json::Json,
               path: &str,
               min: f64,
               max: f64,
               error_msg: &str)
               -> Result<(), valico_error::ValicoErrors> {
    if let json::Json::F64(lon) = *val {
        if min <= lon && lon <= max {
            Ok(())
        } else {
            Err(vec![Box::new(json_dsl::errors::WrongValue {
                         path: path.to_string(),
                         detail: Some(error_msg.to_string()),
                     })])
        }
    } else {
        panic!("should never happen, already checked");
    }
}

pub fn autocomplete(args: &Args) -> rustless::Namespace {
info!("toto {:?}", args);
    rustless::Namespace::build("autocomplete", |ns| {
        ns.get("", |endpoint| {
            endpoint.params(|params| {
                params.req_typed("q", json_dsl::string());

                params.opt("lon", |lon| {
                    lon.coerce(json_dsl::f64());
                    lon.validate_with(|val, path| {
                        check_bound(val, path, -180f64, 180f64, "lon is not a valid longitude")
                    });
                });

                params.opt("lat", |lat| {
                    lat.coerce(json_dsl::f64());
                    lat.validate_with(|val, path| {
                        check_bound(val, path, -90f64, 90f64, "lat is not a valid latitude")
                    });
                });
                params.validate_with(|val, path| {
                    // if we have a lat we should have a lon (and the opposite)
                    if let json::Json::Object(ref obj) = *val {
                        let has_lon = obj.get("lon").is_some();
                        let has_lat = obj.get("lat").is_some();
                        if has_lon ^ has_lat {
                            Err(vec![Box::new(json_dsl::errors::WrongValue {
                                         path: path.to_string(),
                                         detail: Some("you need to provide a lon AND a lat if \
                                                       you provide one of them"
                                                          .to_string()),
                                     })])
                        } else {
                            Ok(())
                        }
                    } else {
                        panic!("should never happen, already checked");
                    }
                });
            });
            endpoint.handle(|client, params| {
                let q = params.find("q").unwrap().as_string().unwrap().to_string();
                let lon = params.find("lon").and_then(|p| p.as_f64());
                let lat = params.find("lat").and_then(|p| p.as_f64());
                // we have already checked that if there is a lon, lat is not None so we can unwrap
                let coord = lon.and_then(|lon| {
                    Some(model::Coord {
                        lon: lon,
                        lat: lat.unwrap(),
                    })
                });
                let model_autocomplete = query::autocomplete(q, coord);

                let response = model::v1::AutocompleteResponse::from(model_autocomplete);
                render(client, response)
            })
        });
    })
}
<commit_msg>remove debug<commit_after>// Copyright © 2016, Canal TP and/or its affiliates. All rights reserved.
//
// This file is part of Navitia,
//     the software to build cool stuff with public transport.
//
// Hope you'll enjoy and contribute to this project,
//     powered by Canal TP (www.canaltp.fr).
// Help us simplify mobility and open public transport:
//     a non ending quest to the responsive locomotion way of traveling!
//
// LICENCE: This program is free software; you can redistribute it
// and/or modify it under the terms of the GNU Affero General Public
// License as published by the Free Software Foundation, either
// version 3 of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public
// License along with this program. If not, see
// <http://www.gnu.org/licenses/>.
//
// Stay tuned using
// twitter @navitia
// IRC #navitia on freenode
// https://groups.google.com/d/forum/navitia
// www.navitia.io
use rustless;
use serde;
use serde_json;
use rustc_serialize::json;
use rustless::server::status;
use rustless::{Api, Nesting, Versioning};
use valico::json_dsl;
use valico::common::error as valico_error;
use super::query;
use super::Args;
use model::v1::*;
use model;

fn render<T>(mut client: rustless::Client,
             obj: T)
             -> Result<rustless::Client, rustless::ErrorResponse>
    where T: serde::Serialize
{
    client.set_json_content_type();
    client.text(serde_json::to_string(&obj).unwrap())
}

pub fn root(args: &Args) -> rustless::Api {
    Api::build(|api| {
        api.get("", |endpoint| {
            endpoint.handle(|client, _params| {
                let desc = EndPoint { description: "autocomplete service".to_string() };
                render(client, desc)
            })
        });

        api.error_formatter(|error, _media| {
            let err = if error.is::<rustless::errors::Validation>() {
                let val_err = error.downcast::<rustless::errors::Validation>().unwrap();
                // TODO better message, we shouldn't use {:?} but access the `path`
                // and `detail` of all errrors in val_err.reason
                CustomError {
                    short: "validation error".to_string(),
                    long: format!("invalid arguments {:?}", val_err.reason),
                }
            } else {
                CustomError {
                    short: "bad_request".to_string(),
                    long: format!("bad request, error: {}", error),
                }
            };
            let mut resp = rustless::Response::from(status::StatusCode::BadRequest,
                                                    Box::new(serde_json::to_string(&err).unwrap()));
            resp.set_json_content_type();
            Some(resp)
        });
        api.mount(v1(&args));
    })
}

pub fn v1(args: &Args) -> rustless::Api {
    Api::build(|api| {
        api.version("v1", Versioning::Path);

        api.get("", |endpoint| {
            endpoint.desc("api interface version v1");
            endpoint.handle(|client, _params| {
                render(client,
                       V1Reponse::Response { description: "api version 1".to_string() })
            })
        });
        api.mount(status(&args));
        api.mount(autocomplete(&args.clone()));
    })
}

pub fn status(_args: &Args) -> rustless::Api {
    Api::build(|api| {
        api.get("status", |endpoint| {
            endpoint.handle(|client, _params| {
                let status = Status {
                    version: "14".to_string(),
                    status: "good".to_string(),
                };
                render(client, status)
            })
        });
    })
}

fn check_bound(val: &json::Json,
               path: &str,
               min: f64,
               max: f64,
               error_msg: &str)
               -> Result<(), valico_error::ValicoErrors> {
    if let json::Json::F64(lon) = *val {
        if min <= lon && lon <= max {
            Ok(())
        } else {
            Err(vec![Box::new(json_dsl::errors::WrongValue {
                         path: path.to_string(),
                         detail: Some(error_msg.to_string()),
                     })])
        }
    } else {
        panic!("should never happen, already checked");
    }
}

pub fn autocomplete(args: &Args) -> rustless::Namespace {
    rustless::Namespace::build("autocomplete", |ns| {
        ns.get("", |endpoint| {
            endpoint.params(|params| {
                params.req_typed("q", json_dsl::string());

                params.opt("lon", |lon| {
                    lon.coerce(json_dsl::f64());
                    lon.validate_with(|val, path| {
                        check_bound(val, path, -180f64, 180f64, "lon is not a valid longitude")
                    });
                });

                params.opt("lat", |lat| {
                    lat.coerce(json_dsl::f64());
                    lat.validate_with(|val, path| {
                        check_bound(val, path, -90f64, 90f64, "lat is not a valid latitude")
                    });
                });
                params.validate_with(|val, path| {
                    // if we have a lat we should have a lon (and the opposite)
                    if let json::Json::Object(ref obj) = *val {
                        let has_lon = obj.get("lon").is_some();
                        let has_lat = obj.get("lat").is_some();
                        if has_lon ^ has_lat {
                            Err(vec![Box::new(json_dsl::errors::WrongValue {
                                         path: path.to_string(),
                                         detail: Some("you need to provide a lon AND a lat if \
                                                       you provide one of them"
                                                          .to_string()),
                                     })])
                        } else {
                            Ok(())
                        }
                    } else {
                        panic!("should never happen, already checked");
                    }
                });
            });
            endpoint.handle(|client, params| {
                let q = params.find("q").unwrap().as_string().unwrap().to_string();
                let lon = params.find("lon").and_then(|p| p.as_f64());
                let lat = params.find("lat").and_then(|p| p.as_f64());
                // we have already checked that if there is a lon, lat is not None so we can unwrap
                let coord = lon.and_then(|lon| {
                    Some(model::Coord {
                        lon: lon,
                        lat: lat.unwrap(),
                    })
                });
                let model_autocomplete = query::autocomplete(q, coord);

                let response = model::v1::AutocompleteResponse::from(model_autocomplete);
                render(client, response)
            })
        });
    })
}
<|endoftext|>


<commit_before>use std::collections::{BTreeMap, HashMap};
use std::io::{self, Write};
use std::mem;
use process::{Process, Context};
use instruction::parameter::{Direct, Register};
use instruction::Instruction;
use instruction::Error as InstrError;
use champion::Champion;
use arena::{Arena, ArenaIndex};
use core::{MEM_SIZE, CYCLE_TO_DIE, CYCLE_DELTA, NBR_LIVE, MAX_CHECKS};

pub struct Machine {
    pub arena: Arena,
    champions: BTreeMap<i32, Champion>,
    processes: Vec<Process>,
    last_living_champion: Option<i32>,

    number_of_lives: usize,
    cycles_to_die: usize,
    cycles: usize,
    cycle_checks: usize,
}

impl Machine {
    pub fn new(champions: BTreeMap<i32, Champion>) -> Self {
        let mut arena = Arena::new();
        let mut arena_index = ArenaIndex::zero();
        let mut processes = Vec::with_capacity(champions.len());
        let step = MEM_SIZE.checked_div(champions.len()).unwrap_or(0);

        for (id, &Champion{ ref program, .. }) in &champions {
            {
                let mut writer = arena.write_to(arena_index);
                io::copy(&mut program.as_slice(), &mut writer).unwrap();
            }

            let mut context = Context::new(arena_index);
            let reg = Register::new(1).unwrap();
            context.registers[reg] = *id;

            let process = Process::new(context, &arena);
            trace!("push process {:?}", process);
            processes.push(process);

            arena_index = arena_index.advance_by(step);
        }

        Machine {
            arena: arena,
            champions: champions,
            processes: processes,
            last_living_champion: None,
            number_of_lives: 0,
            cycles_to_die: CYCLE_TO_DIE,
            cycles: 0,
            cycle_checks: 0,
        }
    }

    pub fn live_champion(&mut self, champion_id: i32) {
        if self.champions.get(&champion_id).is_some() {
            self.last_living_champion = Some(champion_id);
            self.number_of_lives += 1;
        }
    }

    pub fn last_living_champion(&self) -> Option<(i32, &Champion)> {
        self.last_living_champion
            .and_then(|id| self.champions.get(&id)
            .map(|champ| (id, champ)))
    }

    pub fn new_process(&mut self, context: Context) {
        let process = Process::new(context, &self.arena);
        trace!("push process {:?}", process);
        self.processes.push(process)
    }

    pub fn cycles_to_die(&self) -> usize {
        self.cycles_to_die
    }

    pub fn cycle_execute<'a, W: Write>(&'a mut self, output: &'a mut W) -> CycleExecute<'a, W> {
        CycleExecute { machine: self, output }
    }
}

pub struct CycleExecute<'a, W: 'a + Write> {
    machine: &'a mut Machine,
    output: &'a mut W,
}

#[derive(Debug, Clone, Default)]
pub struct CycleInfo {
    pub remaining_processes: usize,
    pub cycles_to_die: usize,
    pub lives_counter: HashMap<i32, usize>,
    pub last_living_champion: Option<i32>,
}

impl<'a, W: 'a + Write> Iterator for CycleExecute<'a, W> {
    type Item = io::Result<CycleInfo>;

    fn next(&mut self) -> Option<Self::Item> {
        let mut processes = Vec::new();
        mem::swap(&mut processes, &mut self.machine.processes);

        let mut cycle_info = CycleInfo {
            lives_counter: HashMap::with_capacity(self.machine.champions.len()),
            ..Default::default()
        };

        self.machine.cycles += 1;
        if self.machine.cycles >= self.machine.cycles_to_die {
            self.machine.cycle_checks += 1;
            processes.retain(|p| p.context.cycle_since_last_live < self.machine.cycles_to_die);
            if self.machine.number_of_lives >= NBR_LIVE || self.machine.cycle_checks >= MAX_CHECKS {
                self.machine.cycles_to_die = self.machine.cycles_to_die.saturating_sub(CYCLE_DELTA);
                self.machine.cycle_checks = 0;
            }
            self.machine.cycles = 0;
            self.machine.number_of_lives = 0;
        }

        cycle_info.cycles_to_die = self.machine.cycles_to_die - self.machine.cycles;

        for process in processes.iter_mut().rev() {
            let ctx = &mut process.context;
            process.remaining_cycles -= 1;
            ctx.cycle_since_last_live += 1;

            if process.remaining_cycles == 0 {
                let instr = &mut process.instruction;
                match *instr {
                    Some(instr) => if let Err(e) = instr.execute(&mut self.machine, ctx, &mut self.output) {
                        return Some(Err(e))
                    },
                    None => Instruction::execute_noop(ctx)
                }
                trace!("execute {:?}", instr);

                if let Some(Instruction::Live(Direct(champion_id))) = *instr {
                    if self.machine.champions.contains_key(&champion_id) {
                        let counter = cycle_info.lives_counter.entry(champion_id).or_insert(0);
                        *counter += 1;
                    }
                }

                let reader = self.machine.arena.read_from(ctx.pc);
                *instr = match Instruction::read_from(reader) {
                    Ok(instr) => Some(instr),
                    Err(InstrError::Io(e)) => return Some(Err(e)),
                    Err(_) => None,
                };
                process.remaining_cycles = instr.map(|instr| instr.cycle_cost()).unwrap_or(1);
            }
        }
        self.machine.processes.append(&mut processes);
        cycle_info.remaining_processes = self.machine.processes.len();
        cycle_info.last_living_champion = self.machine.last_living_champion;

        if !self.machine.processes.is_empty() {
            Some(Ok(cycle_info))
        } else { None }
    }
}
<commit_msg>fix: Change the `CycleInfo::cycles_to_die` behaviour<commit_after>use std::collections::{BTreeMap, HashMap};
use std::io::{self, Write};
use std::mem;
use process::{Process, Context};
use instruction::parameter::{Direct, Register};
use instruction::Instruction;
use instruction::Error as InstrError;
use champion::Champion;
use arena::{Arena, ArenaIndex};
use core::{MEM_SIZE, CYCLE_TO_DIE, CYCLE_DELTA, NBR_LIVE, MAX_CHECKS};

pub struct Machine {
    pub arena: Arena,
    champions: BTreeMap<i32, Champion>,
    processes: Vec<Process>,
    last_living_champion: Option<i32>,

    number_of_lives: usize,
    cycles_to_die: usize,
    cycles: usize,
    cycle_checks: usize,
}

impl Machine {
    pub fn new(champions: BTreeMap<i32, Champion>) -> Self {
        let mut arena = Arena::new();
        let mut arena_index = ArenaIndex::zero();
        let mut processes = Vec::with_capacity(champions.len());
        let step = MEM_SIZE.checked_div(champions.len()).unwrap_or(0);

        for (id, &Champion{ ref program, .. }) in &champions {
            {
                let mut writer = arena.write_to(arena_index);
                io::copy(&mut program.as_slice(), &mut writer).unwrap();
            }

            let mut context = Context::new(arena_index);
            let reg = Register::new(1).unwrap();
            context.registers[reg] = *id;

            let process = Process::new(context, &arena);
            trace!("push process {:?}", process);
            processes.push(process);

            arena_index = arena_index.advance_by(step);
        }

        Machine {
            arena: arena,
            champions: champions,
            processes: processes,
            last_living_champion: None,
            number_of_lives: 0,
            cycles_to_die: CYCLE_TO_DIE,
            cycles: 0,
            cycle_checks: 0,
        }
    }

    pub fn live_champion(&mut self, champion_id: i32) {
        if self.champions.get(&champion_id).is_some() {
            self.last_living_champion = Some(champion_id);
            self.number_of_lives += 1;
        }
    }

    pub fn last_living_champion(&self) -> Option<(i32, &Champion)> {
        self.last_living_champion
            .and_then(|id| self.champions.get(&id)
            .map(|champ| (id, champ)))
    }

    pub fn new_process(&mut self, context: Context) {
        let process = Process::new(context, &self.arena);
        trace!("push process {:?}", process);
        self.processes.push(process)
    }

    pub fn cycles_to_die(&self) -> usize {
        self.cycles_to_die
    }

    pub fn cycle_execute<'a, W: Write>(&'a mut self, output: &'a mut W) -> CycleExecute<'a, W> {
        CycleExecute { machine: self, output }
    }
}

pub struct CycleExecute<'a, W: 'a + Write> {
    machine: &'a mut Machine,
    output: &'a mut W,
}

#[derive(Debug, Clone, Default)]
pub struct CycleInfo {
    pub remaining_processes: usize,
    pub cycles_to_die: usize,
    pub lives_counter: HashMap<i32, usize>,
    pub last_living_champion: Option<i32>,
}

impl<'a, W: 'a + Write> Iterator for CycleExecute<'a, W> {
    type Item = io::Result<CycleInfo>;

    fn next(&mut self) -> Option<Self::Item> {
        let mut processes = Vec::new();
        mem::swap(&mut processes, &mut self.machine.processes);

        let mut cycle_info = CycleInfo {
            lives_counter: HashMap::with_capacity(self.machine.champions.len()),
            ..Default::default()
        };

        self.machine.cycles += 1;
        if self.machine.cycles >= self.machine.cycles_to_die {
            self.machine.cycle_checks += 1;
            processes.retain(|p| p.context.cycle_since_last_live < self.machine.cycles_to_die);
            if self.machine.number_of_lives >= NBR_LIVE || self.machine.cycle_checks >= MAX_CHECKS {
                self.machine.cycles_to_die = self.machine.cycles_to_die.saturating_sub(CYCLE_DELTA);
                self.machine.cycle_checks = 0;
            }
            self.machine.cycles = 0;
            self.machine.number_of_lives = 0;
        }

        cycle_info.cycles_to_die = self.machine.cycles_to_die;

        for process in processes.iter_mut().rev() {
            let ctx = &mut process.context;
            process.remaining_cycles -= 1;
            ctx.cycle_since_last_live += 1;

            if process.remaining_cycles == 0 {
                let instr = &mut process.instruction;
                match *instr {
                    Some(instr) => if let Err(e) = instr.execute(&mut self.machine, ctx, &mut self.output) {
                        return Some(Err(e))
                    },
                    None => Instruction::execute_noop(ctx)
                }
                trace!("execute {:?}", instr);

                if let Some(Instruction::Live(Direct(champion_id))) = *instr {
                    if self.machine.champions.contains_key(&champion_id) {
                        let counter = cycle_info.lives_counter.entry(champion_id).or_insert(0);
                        *counter += 1;
                    }
                }

                let reader = self.machine.arena.read_from(ctx.pc);
                *instr = match Instruction::read_from(reader) {
                    Ok(instr) => Some(instr),
                    Err(InstrError::Io(e)) => return Some(Err(e)),
                    Err(_) => None,
                };
                process.remaining_cycles = instr.map(|instr| instr.cycle_cost()).unwrap_or(1);
            }
        }
        self.machine.processes.append(&mut processes);
        cycle_info.remaining_processes = self.machine.processes.len();
        cycle_info.last_living_champion = self.machine.last_living_champion;

        if !self.machine.processes.is_empty() {
            Some(Ok(cycle_info))
        } else { None }
    }
}
<|endoftext|>


<commit_before>//! Algorithm citation:
//! A Simple, Fast Dominance Algorithm.
//! Keith D. Cooper, Timothy J. Harvey, and Ken Kennedy
//! Rice Computer Science TS-06-33870
//! <https://www.cs.rice.edu/~keith/EMBED/dom.pdf>

use super::super::indexed_vec::{Idx, IndexVec};
use super::iterate::reverse_post_order;
use super::ControlFlowGraph;

use std::fmt;

#[cfg(test)]
mod test;

pub fn dominators<G: ControlFlowGraph>(graph: &G) -> Dominators<G::Node> {
    let start_node = graph.start_node();
    let rpo = reverse_post_order(graph, start_node);
    dominators_given_rpo(graph, &rpo)
}

pub fn dominators_given_rpo<G: ControlFlowGraph>(
    graph: &G,
    rpo: &[G::Node],
) -> Dominators<G::Node> {
    let start_node = graph.start_node();
    assert_eq!(rpo[0], start_node);

    // compute the post order index (rank) for each node
    let mut post_order_rank: IndexVec<G::Node, usize> =
        (0..graph.num_nodes()).map(|_| 0).collect();
    for (index, node) in rpo.iter().rev().cloned().enumerate() {
        post_order_rank[node] = index;
    }

    let mut immediate_dominators: IndexVec<G::Node, Option<G::Node>> =
        (0..graph.num_nodes()).map(|_| None).collect();
    immediate_dominators[start_node] = Some(start_node);

    let mut changed = true;
    while changed {
        changed = false;

        for &node in &rpo[1..] {
            let mut new_idom = None;
            for pred in graph.predecessors(node) {
                if immediate_dominators[pred].is_some() {
                    // (*)
                    // (*) dominators for `pred` have been calculated
                    new_idom = intersect_opt(
                        &post_order_rank,
                        &immediate_dominators,
                        new_idom,
                        Some(pred),
                    );
                }
            }

            if new_idom != immediate_dominators[node] {
                immediate_dominators[node] = new_idom;
                changed = true;
            }
        }
    }

    Dominators {
        post_order_rank,
        immediate_dominators,
    }
}

fn intersect_opt<Node: Idx>(
    post_order_rank: &IndexVec<Node, usize>,
    immediate_dominators: &IndexVec<Node, Option<Node>>,
    node1: Option<Node>,
    node2: Option<Node>,
) -> Option<Node> {
    match (node1, node2) {
        (None, None) => None,
        (Some(n), None) | (None, Some(n)) => Some(n),
        (Some(n1), Some(n2)) => Some(intersect(post_order_rank, immediate_dominators, n1, n2)),
    }
}

fn intersect<Node: Idx>(
    post_order_rank: &IndexVec<Node, usize>,
    immediate_dominators: &IndexVec<Node, Option<Node>>,
    mut node1: Node,
    mut node2: Node,
) -> Node {
    while node1 != node2 {
        while post_order_rank[node1] < post_order_rank[node2] {
            node1 = immediate_dominators[node1].unwrap();
        }

        while post_order_rank[node2] < post_order_rank[node1] {
            node2 = immediate_dominators[node2].unwrap();
        }
    }

    node1
}

#[derive(Clone, Debug)]
pub struct Dominators<N: Idx> {
    post_order_rank: IndexVec<N, usize>,
    immediate_dominators: IndexVec<N, Option<N>>,
}

impl<Node: Idx> Dominators<Node> {
    pub fn is_reachable(&self, node: Node) -> bool {
        self.immediate_dominators[node].is_some()
    }

    pub fn immediate_dominator(&self, node: Node) -> Node {
        assert!(self.is_reachable(node), "node {:?} is not reachable", node);
        self.immediate_dominators[node].unwrap()
    }

    pub fn dominators(&self, node: Node) -> Iter<'_, Node> {
        assert!(self.is_reachable(node), "node {:?} is not reachable", node);
        Iter {
            dominators: self,
            node: Some(node),
        }
    }

    pub fn is_dominated_by(&self, node: Node, dom: Node) -> bool {
        // FIXME -- could be optimized by using post-order-rank
        self.dominators(node).any(|n| n == dom)
    }

    #[cfg(test)]
    fn all_immediate_dominators(&self) -> &IndexVec<Node, Option<Node>> {
        &self.immediate_dominators
    }
}

pub struct Iter<'dom, Node: Idx> {
    dominators: &'dom Dominators<Node>,
    node: Option<Node>,
}

impl<'dom, Node: Idx> Iterator for Iter<'dom, Node> {
    type Item = Node;

    fn next(&mut self) -> Option<Self::Item> {
        if let Some(node) = self.node {
            let dom = self.dominators.immediate_dominator(node);
            if dom == node {
                self.node = None; // reached the root
            } else {
                self.node = Some(dom);
            }
            return Some(node);
        } else {
            return None;
        }
    }
}

pub struct DominatorTree<N: Idx> {
    root: N,
    children: IndexVec<N, Vec<N>>,
}

impl<Node: Idx> DominatorTree<Node> {
    pub fn children(&self, node: Node) -> &[Node] {
        &self.children[node]
    }
}

impl<Node: Idx> fmt::Debug for DominatorTree<Node> {
    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
        fmt::Debug::fmt(
            &DominatorTreeNode {
                tree: self,
                node: self.root,
            },
            fmt,
        )
    }
}

struct DominatorTreeNode<'tree, Node: Idx> {
    tree: &'tree DominatorTree<Node>,
    node: Node,
}

impl<'tree, Node: Idx> fmt::Debug for DominatorTreeNode<'tree, Node> {
    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
        let subtrees: Vec<_> = self.tree
            .children(self.node)
            .iter()
            .map(|&child| DominatorTreeNode {
                tree: self.tree,
                node: child,
            })
            .collect();
        fmt.debug_tuple("")
            .field(&self.node)
            .field(&subtrees)
            .finish()
    }
}
<commit_msg>Kill dead code dominator code.<commit_after>//! Algorithm citation:
//! A Simple, Fast Dominance Algorithm.
//! Keith D. Cooper, Timothy J. Harvey, and Ken Kennedy
//! Rice Computer Science TS-06-33870
//! <https://www.cs.rice.edu/~keith/EMBED/dom.pdf>

use super::super::indexed_vec::{Idx, IndexVec};
use super::iterate::reverse_post_order;
use super::ControlFlowGraph;

#[cfg(test)]
mod test;

pub fn dominators<G: ControlFlowGraph>(graph: &G) -> Dominators<G::Node> {
    let start_node = graph.start_node();
    let rpo = reverse_post_order(graph, start_node);
    dominators_given_rpo(graph, &rpo)
}

pub fn dominators_given_rpo<G: ControlFlowGraph>(
    graph: &G,
    rpo: &[G::Node],
) -> Dominators<G::Node> {
    let start_node = graph.start_node();
    assert_eq!(rpo[0], start_node);

    // compute the post order index (rank) for each node
    let mut post_order_rank: IndexVec<G::Node, usize> =
        (0..graph.num_nodes()).map(|_| 0).collect();
    for (index, node) in rpo.iter().rev().cloned().enumerate() {
        post_order_rank[node] = index;
    }

    let mut immediate_dominators: IndexVec<G::Node, Option<G::Node>> =
        (0..graph.num_nodes()).map(|_| None).collect();
    immediate_dominators[start_node] = Some(start_node);

    let mut changed = true;
    while changed {
        changed = false;

        for &node in &rpo[1..] {
            let mut new_idom = None;
            for pred in graph.predecessors(node) {
                if immediate_dominators[pred].is_some() {
                    // (*)
                    // (*) dominators for `pred` have been calculated
                    new_idom = intersect_opt(
                        &post_order_rank,
                        &immediate_dominators,
                        new_idom,
                        Some(pred),
                    );
                }
            }

            if new_idom != immediate_dominators[node] {
                immediate_dominators[node] = new_idom;
                changed = true;
            }
        }
    }

    Dominators {
        post_order_rank,
        immediate_dominators,
    }
}

fn intersect_opt<Node: Idx>(
    post_order_rank: &IndexVec<Node, usize>,
    immediate_dominators: &IndexVec<Node, Option<Node>>,
    node1: Option<Node>,
    node2: Option<Node>,
) -> Option<Node> {
    match (node1, node2) {
        (None, None) => None,
        (Some(n), None) | (None, Some(n)) => Some(n),
        (Some(n1), Some(n2)) => Some(intersect(post_order_rank, immediate_dominators, n1, n2)),
    }
}

fn intersect<Node: Idx>(
    post_order_rank: &IndexVec<Node, usize>,
    immediate_dominators: &IndexVec<Node, Option<Node>>,
    mut node1: Node,
    mut node2: Node,
) -> Node {
    while node1 != node2 {
        while post_order_rank[node1] < post_order_rank[node2] {
            node1 = immediate_dominators[node1].unwrap();
        }

        while post_order_rank[node2] < post_order_rank[node1] {
            node2 = immediate_dominators[node2].unwrap();
        }
    }

    node1
}

#[derive(Clone, Debug)]
pub struct Dominators<N: Idx> {
    post_order_rank: IndexVec<N, usize>,
    immediate_dominators: IndexVec<N, Option<N>>,
}

impl<Node: Idx> Dominators<Node> {
    pub fn is_reachable(&self, node: Node) -> bool {
        self.immediate_dominators[node].is_some()
    }

    pub fn immediate_dominator(&self, node: Node) -> Node {
        assert!(self.is_reachable(node), "node {:?} is not reachable", node);
        self.immediate_dominators[node].unwrap()
    }

    pub fn dominators(&self, node: Node) -> Iter<'_, Node> {
        assert!(self.is_reachable(node), "node {:?} is not reachable", node);
        Iter {
            dominators: self,
            node: Some(node),
        }
    }

    pub fn is_dominated_by(&self, node: Node, dom: Node) -> bool {
        // FIXME -- could be optimized by using post-order-rank
        self.dominators(node).any(|n| n == dom)
    }

    #[cfg(test)]
    fn all_immediate_dominators(&self) -> &IndexVec<Node, Option<Node>> {
        &self.immediate_dominators
    }
}

pub struct Iter<'dom, Node: Idx> {
    dominators: &'dom Dominators<Node>,
    node: Option<Node>,
}

impl<'dom, Node: Idx> Iterator for Iter<'dom, Node> {
    type Item = Node;

    fn next(&mut self) -> Option<Self::Item> {
        if let Some(node) = self.node {
            let dom = self.dominators.immediate_dominator(node);
            if dom == node {
                self.node = None; // reached the root
            } else {
                self.node = Some(dom);
            }
            return Some(node);
        } else {
            return None;
        }
    }
}
<|endoftext|>