<commit_before>use super::*;
use std::collections::HashMap;
use termion::event as keyevent;

use entities::Faction;
use astronomicals::sector::Sector;
use astronomicals::system::System;
use tui::widgets::{Block, Borders, Row, Table, Widget};
use tui::widgets::canvas::Canvas;
use tui::layout::{Direction, Group, Rect, Size};
use tui::style::{Color, Style};

/// Level of map display.
enum Level {
    Galaxy,
    Sector,
    System,
}

lazy_static! {
    /// Color mapping for each faction.
    static ref FACTION_STYLES: HashMap<Faction, Style> = {
        let mut m = HashMap::new();
        m.insert(Faction::Empire, Style::default().fg(Color::Red));
        m.insert(Faction::Federation, Style::default().fg(Color::Yellow));
        m.insert(Faction::Cartel, Style::default().fg(Color::Magenta));
        m.insert(Faction::Independent, Style::default().fg(Color::LightGreen));
        m
    };

    /// Styling for selected item.
    static ref SELECTED_STYLE: Style = Style::default().bg(Color::Gray);

    /// Styling for unselected item.
    static ref DEFAULT_STYLE: Style = Style::default();
}

/// Displays the map tab.
pub struct MapTab {
    state: Arc<Game>,
    level: Level,
    selected_sector: usize,
    max_selected_sector: usize,
    selected_system: usize,
    max_selected_system: usize,
    selected_astronomical: usize,
    max_selected_astronomical: usize,
}

impl Tab for MapTab {
    /// Creates a map tab.
    fn new(state: Arc<Game>) -> Box<Self> {
        let max_selected_sector = state.galaxy.lock().unwrap().sectors.len();

        Box::new(MapTab {
            state: state,
            level: Level::Galaxy,
            selected_sector: 0,
            max_selected_sector: max_selected_sector,
            selected_system: 0,
            max_selected_system: 0,
            selected_astronomical: 0,
            max_selected_astronomical: 0,
        })
    }

    /// Returns the title string describing the tab.
    fn title(&self) -> String {
        String::from("Galaxy Map")
    }

    /// Handles the user provided event.
    fn handle_event(&mut self, event: Event) {
        match event {
            Event::Input(input) => match input {
                // Move up item list.
                keyevent::Key::Char('k') => match self.level {
                    Level::Galaxy => {
                        self.selected_sector = (self.selected_sector as i32 - 1).max(0) as usize
                    }
                    Level::Sector => {
                        self.selected_system = (self.selected_system as i32 - 1).max(0) as usize
                    }
                    Level::System => {
                        self.selected_astronomical =
                            (self.selected_astronomical as i32 - 1).max(0) as usize
                    }
                },

                // Move down item list.
                keyevent::Key::Char('j') => match self.level {
                    Level::Galaxy => {
                        self.selected_sector =
                            (self.selected_sector + 1).min(self.max_selected_sector)
                    }
                    Level::Sector => {
                        self.selected_system =
                            (self.selected_system + 1).min(self.max_selected_system)
                    }
                    Level::System => {
                        self.selected_astronomical =
                            (self.selected_astronomical + 1).min(self.max_selected_astronomical)
                    }
                },

                // Move into item.
                keyevent::Key::Char('l') => {
                    self.level = match self.level {
                        Level::Galaxy => {
                            self.max_selected_system = self.state.galaxy.lock().unwrap().sectors
                                [self.selected_sector]
                                .systems
                                .len();
                            Level::Sector
                        }
                        Level::Sector => {
                            self.max_selected_astronomical =
                                self.state.galaxy.lock().unwrap().sectors[self.selected_sector]
                                    .systems[self.selected_system]
                                    .satelites
                                    .len();
                            Level::System
                        }
                        _ => Level::Galaxy,
                    };
                }

                // Move out of item.
                keyevent::Key::Char('h') => {
                    self.level = match self.level {
                        Level::System => Level::Sector,
                        Level::Sector => Level::Galaxy,
                        _ => Level::Galaxy,
                    };
                }
                _ => {}
            },
            _ => {}
        };

        // Update sizes of lists
        let galaxy = &self.state.galaxy.lock().unwrap();
        self.max_selected_sector = galaxy.sectors.len();
        self.max_selected_system = galaxy.sectors[self.selected_sector].systems.len();
        self.max_selected_astronomical = galaxy.sectors[self.selected_sector].systems
            [self.selected_system]
            .satelites
            .len();
    }

    /// Draws the tab in the given terminal and area.
    fn draw(&self, term: &mut Terminal<MouseBackend>, area: &Rect) {
        Group::default()
            .direction(Direction::Horizontal)
            .sizes(&[Size::Percent(30), Size::Percent(70)])
            .render(term, area, |term, chunks| match self.level {
                Level::Galaxy => {
                    let galaxy = self.state.galaxy.lock().unwrap();
                    draw_galaxy_table(self.selected_sector, &galaxy.sectors, term, &chunks[0]);
                    draw_galaxy_map(self.selected_sector, &galaxy.sectors, term, &chunks[1]);
                }
                Level::Sector => {
                    let sector = &self.state.galaxy.lock().unwrap().sectors[self.selected_sector];
                    draw_sector_table(self.selected_system, &sector, term, &chunks[0]);
                    draw_sector_map(self.selected_system, &sector, term, &chunks[1]);
                }
                Level::System => {
                    let system = &self.state.galaxy.lock().unwrap().sectors[self.selected_sector]
                        .systems[self.selected_system];
                    draw_system_table(self.selected_astronomical, &system, term, &chunks[0]);
                    draw_system_map(self.selected_astronomical, &system, term, &chunks[1]);
                }
            });
    }
}

fn draw_galaxy_table(
    selected: usize,
    sectors: &Vec<Sector>,
    term: &mut Terminal<MouseBackend>,
    area: &Rect,
) {
    Table::new(
        ["Sector", "Faction", "Systems"].into_iter(),
        sectors
            .iter()
            .enumerate()
            .map(|(idx, ref sector)| {
                let style = match selected == idx {
                    true => &SELECTED_STYLE,
                    false => FACTION_STYLES.get(&sector.faction).unwrap(),
                };
                Row::StyledData(
                    vec![
                        sector.name.clone(),
                        sector.faction.to_string(),
                        (sector.systems.len() as u32).to_string(),
                    ].into_iter(),
                    style,
                )
            })
            .skip(selected),
    ).block(Block::default().title("Sectors").borders(Borders::ALL))
        .header_style(Style::default().fg(Color::Yellow))
        .widths(&[30, 15, 5])
        .render(term, &area);
}

fn draw_galaxy_map(
    selected: usize,
    sectors: &Vec<Sector>,
    term: &mut Terminal<MouseBackend>,
    area: &Rect,
) {
    let (max_x, max_y) = sectors.iter().fold((0., 0.), |(x_max, y_max), s| {
        let location = s.center();
        (
            location.coords.x.abs().max(x_max),
            location.coords.y.abs().max(y_max),
        )
    });
    let selected_loc = sectors[selected].center();
    Canvas::default()
        .block(Block::default().title("Galaxy").borders(Borders::ALL))
        .paint(|ctx| {
            for (idx, sector) in sectors.iter().enumerate() {
                let color = match sector.faction {
                    Faction::Empire => Color::Red,
                    Faction::Federation => Color::Yellow,
                    Faction::Cartel => Color::Magenta,
                    Faction::Independent => Color::LightGreen,
                };
                let center = sector.center();
                match idx == selected {
                    true => ctx.print(center.coords.x, center.coords.y, "X", color),
                    false => ctx.print(center.coords.x, center.coords.y, "*", color),
                };
            }
        })
        .x_bounds([
            -max_x + selected_loc.coords.x,
            selected_loc.coords.x + max_x,
        ])
        .y_bounds([
            -max_y + selected_loc.coords.y,
            selected_loc.coords.y + max_y,
        ])
        .render(term, area);
}

fn draw_sector_table(
    selected: usize,
    sector: &Sector,
    term: &mut Terminal<MouseBackend>,
    area: &Rect,
) {
    Table::new(
        ["System", "Bodies"].into_iter(),
        sector
            .systems
            .iter()
            .enumerate()
            .map(|(idx, ref system)| {
                let style: &Style = match selected == idx {
                    true => &SELECTED_STYLE,
                    false => &DEFAULT_STYLE,
                };
                Row::StyledData(
                    vec![
                        system.name.clone(),
                        (system.satelites.len() as u32).to_string(),
                    ].into_iter(),
                    &style,
                )
            })
            .skip(selected),
    ).block(Block::default().title(&sector.name).borders(Borders::ALL))
        .header_style(Style::default().fg(Color::Yellow))
        .widths(&[30, 15, 5])
        .render(term, &area);
}

fn draw_sector_map(
    selected: usize,
    sector: &Sector,
    term: &mut Terminal<MouseBackend>,
    area: &Rect,
) {
    let sector_center = sector.center();
    let (max_x, max_y) = sector.systems.iter().fold((0., 0.), |(x_max, y_max), s| {
        (
            (sector_center.coords.x - s.location.coords.x)
                .abs()
                .max(x_max),
            (sector_center.coords.y - s.location.coords.y)
                .abs()
                .max(y_max),
        )
    });
    let selected_loc = sector.systems[selected].location;
    Canvas::default()
        .block(Block::default().title("Sector").borders(Borders::ALL))
        .paint(|ctx| {
            for (idx, system) in sector.systems.iter().enumerate() {
                let color = Color::White;
                match idx == selected {
                    true => ctx.print(
                        system.location.coords.x,
                        system.location.coords.y,
                        "X",
                        color,
                    ),
                    false => ctx.print(
                        system.location.coords.x,
                        system.location.coords.y,
                        "*",
                        color,
                    ),
                }
            }
        })
        .x_bounds([selected_loc.coords.x - max_x, selected_loc.coords.x + max_x])
        .y_bounds([selected_loc.coords.y - max_y, selected_loc.coords.y + max_y])
        .render(term, &area);
}

fn draw_system_table(
    selected: usize,
    system: &System,
    term: &mut Terminal<MouseBackend>,
    area: &Rect,
) {
    Table::new(
        ["Planets", "Mass", "Orbit distance", "Temperature", "Type"].into_iter(),
        system
            .satelites
            .iter()
            .enumerate()
            .map(|(idx, ref planet)| {
                let style: &Style = match selected == idx {
                    true => &SELECTED_STYLE,
                    false => &DEFAULT_STYLE,
                };
                Row::StyledData(
                    vec![
                        planet.name.clone(),
                        planet.mass.to_string(),
                        planet.orbit_distance.to_string(),
                        planet.surface_temperature.to_string(),
                        planet.planet_type.to_string(),
                    ].into_iter(),
                    &style,
                )
            })
            .skip(selected),
    ).block(Block::default().title(&system.name).borders(Borders::ALL))
        .header_style(Style::default().fg(Color::Yellow))
        .widths(&[15, 5, 15, 15, 5])
        .render(term, &area);
}

fn draw_system_map(
    selected: usize,
    system: &System,
    term: &mut Terminal<MouseBackend>,
    area: &Rect,
) {
    // TODO: Find decent presentation of a system, ascii art?
}
<commit_msg>Check bounds for select items in map table correctly (#74)<commit_after>use super::*;
use std::collections::HashMap;
use termion::event as keyevent;

use entities::Faction;
use astronomicals::sector::Sector;
use astronomicals::system::System;
use tui::widgets::{Block, Borders, Row, Table, Widget};
use tui::widgets::canvas::Canvas;
use tui::layout::{Direction, Group, Rect, Size};
use tui::style::{Color, Style};

/// Level of map display.
enum Level {
    Galaxy,
    Sector,
    System,
}

lazy_static! {
    /// Color mapping for each faction.
    static ref FACTION_STYLES: HashMap<Faction, Style> = {
        let mut m = HashMap::new();
        m.insert(Faction::Empire, Style::default().fg(Color::Red));
        m.insert(Faction::Federation, Style::default().fg(Color::Yellow));
        m.insert(Faction::Cartel, Style::default().fg(Color::Magenta));
        m.insert(Faction::Independent, Style::default().fg(Color::LightGreen));
        m
    };

    /// Styling for selected item.
    static ref SELECTED_STYLE: Style = Style::default().bg(Color::Gray);

    /// Styling for unselected item.
    static ref DEFAULT_STYLE: Style = Style::default();
}

/// Displays the map tab.
pub struct MapTab {
    state: Arc<Game>,
    level: Level,
    selected_sector: usize,
    max_selected_sector: usize,
    selected_system: usize,
    max_selected_system: usize,
    selected_astronomical: usize,
    max_selected_astronomical: usize,
}

impl Tab for MapTab {
    /// Creates a map tab.
    fn new(state: Arc<Game>) -> Box<Self> {
        let max_selected_sector = state.galaxy.lock().unwrap().sectors.len();

        Box::new(MapTab {
            state: state,
            level: Level::Galaxy,
            selected_sector: 0,
            max_selected_sector: max_selected_sector,
            selected_system: 0,
            max_selected_system: 0,
            selected_astronomical: 0,
            max_selected_astronomical: 0,
        })
    }

    /// Returns the title string describing the tab.
    fn title(&self) -> String {
        String::from("Galaxy Map")
    }

    /// Handles the user provided event.
    fn handle_event(&mut self, event: Event) {
        match event {
            Event::Input(input) => match input {
                // Move up item list.
                keyevent::Key::Char('k') => match self.level {
                    Level::Galaxy => {
                        self.selected_sector = (self.selected_sector as i32 - 1).max(0) as usize
                    }
                    Level::Sector => {
                        self.selected_system = (self.selected_system as i32 - 1).max(0) as usize
                    }
                    Level::System => {
                        self.selected_astronomical =
                            (self.selected_astronomical as i32 - 1).max(0) as usize
                    }
                },

                // Move down item list.
                keyevent::Key::Char('j') => match self.level {
                    Level::Galaxy => {
                        self.selected_sector =
                            (self.selected_sector + 1).min(self.max_selected_sector)
                    }
                    Level::Sector => {
                        self.selected_system =
                            (self.selected_system + 1).min(self.max_selected_system)
                    }
                    Level::System => {
                        self.selected_astronomical =
                            (self.selected_astronomical + 1).min(self.max_selected_astronomical)
                    }
                },

                // Move into item.
                keyevent::Key::Char('l') => {
                    self.level = match self.level {
                        Level::Galaxy => {
                            self.max_selected_system = self.state.galaxy.lock().unwrap().sectors
                                [self.selected_sector]
                                .systems
                                .len();
                            Level::Sector
                        }
                        Level::Sector => {
                            self.max_selected_astronomical =
                                self.state.galaxy.lock().unwrap().sectors[self.selected_sector]
                                    .systems[self.selected_system]
                                    .satelites
                                    .len();
                            Level::System
                        }
                        _ => Level::Galaxy,
                    };
                }

                // Move out of item.
                keyevent::Key::Char('h') => {
                    self.level = match self.level {
                        Level::System => Level::Sector,
                        Level::Sector => Level::Galaxy,
                        _ => Level::Galaxy,
                    };
                }
                _ => {}
            },
            _ => {}
        };

        // Update sizes of lists
        let galaxy = &self.state.galaxy.lock().unwrap();
        self.max_selected_sector = galaxy.sectors.len();
        self.selected_sector = self.selected_sector.min(self.max_selected_sector - 1);
        self.max_selected_system = galaxy.sectors[self.selected_sector].systems.len();
        self.selected_system = self.selected_system.min(self.max_selected_system - 1);
        self.max_selected_astronomical = galaxy.sectors[self.selected_sector].systems
            [self.selected_system]
            .satelites
            .len();
        self.selected_astronomical = self.selected_astronomical
            .min(self.max_selected_astronomical - 1);
    }

    /// Draws the tab in the given terminal and area.
    fn draw(&self, term: &mut Terminal<MouseBackend>, area: &Rect) {
        Group::default()
            .direction(Direction::Horizontal)
            .sizes(&[Size::Percent(30), Size::Percent(70)])
            .render(term, area, |term, chunks| match self.level {
                Level::Galaxy => {
                    let galaxy = self.state.galaxy.lock().unwrap();
                    draw_galaxy_table(self.selected_sector, &galaxy.sectors, term, &chunks[0]);
                    draw_galaxy_map(self.selected_sector, &galaxy.sectors, term, &chunks[1]);
                }
                Level::Sector => {
                    let sector = &self.state.galaxy.lock().unwrap().sectors[self.selected_sector];
                    draw_sector_table(self.selected_system, &sector, term, &chunks[0]);
                    draw_sector_map(self.selected_system, &sector, term, &chunks[1]);
                }
                Level::System => {
                    let system = &self.state.galaxy.lock().unwrap().sectors[self.selected_sector]
                        .systems[self.selected_system];
                    draw_system_table(self.selected_astronomical, &system, term, &chunks[0]);
                    draw_system_map(self.selected_astronomical, &system, term, &chunks[1]);
                }
            });
    }
}

fn draw_galaxy_table(
    selected: usize,
    sectors: &Vec<Sector>,
    term: &mut Terminal<MouseBackend>,
    area: &Rect,
) {
    Table::new(
        ["Sector", "Faction", "Systems"].into_iter(),
        sectors
            .iter()
            .enumerate()
            .map(|(idx, ref sector)| {
                let style = match selected == idx {
                    true => &SELECTED_STYLE,
                    false => FACTION_STYLES.get(&sector.faction).unwrap(),
                };
                Row::StyledData(
                    vec![
                        sector.name.clone(),
                        sector.faction.to_string(),
                        (sector.systems.len() as u32).to_string(),
                    ].into_iter(),
                    style,
                )
            })
            .skip(selected),
    ).block(Block::default().title("Sectors").borders(Borders::ALL))
        .header_style(Style::default().fg(Color::Yellow))
        .widths(&[30, 15, 5])
        .render(term, &area);
}

fn draw_galaxy_map(
    selected: usize,
    sectors: &Vec<Sector>,
    term: &mut Terminal<MouseBackend>,
    area: &Rect,
) {
    let (max_x, max_y) = sectors.iter().fold((0., 0.), |(x_max, y_max), s| {
        let location = s.center();
        (
            location.coords.x.abs().max(x_max),
            location.coords.y.abs().max(y_max),
        )
    });
    let selected_loc = sectors[selected].center();
    Canvas::default()
        .block(Block::default().title("Galaxy").borders(Borders::ALL))
        .paint(|ctx| {
            for (idx, sector) in sectors.iter().enumerate() {
                let color = match sector.faction {
                    Faction::Empire => Color::Red,
                    Faction::Federation => Color::Yellow,
                    Faction::Cartel => Color::Magenta,
                    Faction::Independent => Color::LightGreen,
                };
                let center = sector.center();
                match idx == selected {
                    true => ctx.print(center.coords.x, center.coords.y, "X", color),
                    false => ctx.print(center.coords.x, center.coords.y, "*", color),
                };
            }
        })
        .x_bounds([
            -max_x + selected_loc.coords.x,
            selected_loc.coords.x + max_x,
        ])
        .y_bounds([
            -max_y + selected_loc.coords.y,
            selected_loc.coords.y + max_y,
        ])
        .render(term, area);
}

fn draw_sector_table(
    selected: usize,
    sector: &Sector,
    term: &mut Terminal<MouseBackend>,
    area: &Rect,
) {
    Table::new(
        ["System", "Bodies"].into_iter(),
        sector
            .systems
            .iter()
            .enumerate()
            .map(|(idx, ref system)| {
                let style: &Style = match selected == idx {
                    true => &SELECTED_STYLE,
                    false => &DEFAULT_STYLE,
                };
                Row::StyledData(
                    vec![
                        system.name.clone(),
                        (system.satelites.len() as u32).to_string(),
                    ].into_iter(),
                    &style,
                )
            })
            .skip(selected),
    ).block(Block::default().title(&sector.name).borders(Borders::ALL))
        .header_style(Style::default().fg(Color::Yellow))
        .widths(&[30, 15, 5])
        .render(term, &area);
}

fn draw_sector_map(
    selected: usize,
    sector: &Sector,
    term: &mut Terminal<MouseBackend>,
    area: &Rect,
) {
    let sector_center = sector.center();
    let (max_x, max_y) = sector.systems.iter().fold((0., 0.), |(x_max, y_max), s| {
        (
            (sector_center.coords.x - s.location.coords.x)
                .abs()
                .max(x_max),
            (sector_center.coords.y - s.location.coords.y)
                .abs()
                .max(y_max),
        )
    });
    let selected_loc = sector.systems[selected].location;
    Canvas::default()
        .block(Block::default().title("Sector").borders(Borders::ALL))
        .paint(|ctx| {
            for (idx, system) in sector.systems.iter().enumerate() {
                let color = Color::White;
                match idx == selected {
                    true => ctx.print(
                        system.location.coords.x,
                        system.location.coords.y,
                        "X",
                        color,
                    ),
                    false => ctx.print(
                        system.location.coords.x,
                        system.location.coords.y,
                        "*",
                        color,
                    ),
                }
            }
        })
        .x_bounds([selected_loc.coords.x - max_x, selected_loc.coords.x + max_x])
        .y_bounds([selected_loc.coords.y - max_y, selected_loc.coords.y + max_y])
        .render(term, &area);
}

fn draw_system_table(
    selected: usize,
    system: &System,
    term: &mut Terminal<MouseBackend>,
    area: &Rect,
) {
    Table::new(
        ["Planets", "Mass", "Orbit distance", "Temperature", "Type"].into_iter(),
        system
            .satelites
            .iter()
            .enumerate()
            .map(|(idx, ref planet)| {
                let style: &Style = match selected == idx {
                    true => &SELECTED_STYLE,
                    false => &DEFAULT_STYLE,
                };
                Row::StyledData(
                    vec![
                        planet.name.clone(),
                        planet.mass.to_string(),
                        planet.orbit_distance.to_string(),
                        planet.surface_temperature.to_string(),
                        planet.planet_type.to_string(),
                    ].into_iter(),
                    &style,
                )
            })
            .skip(selected),
    ).block(Block::default().title(&system.name).borders(Borders::ALL))
        .header_style(Style::default().fg(Color::Yellow))
        .widths(&[15, 5, 15, 15, 5])
        .render(term, &area);
}

fn draw_system_map(
    selected: usize,
    system: &System,
    term: &mut Terminal<MouseBackend>,
    area: &Rect,
) {
    // TODO: Find decent presentation of a system, ascii art?
}
<|endoftext|>


<commit_before>// Test that we don't duplicate storage for futures moved around in .await, and
// for futures moved into other futures.
//
// The exact sizes can change by a few bytes (we'd like to know when they do).
// What we don't want to see is the wrong multiple of 1024 (the size of BigFut)
// being reflected in the size.
//
// See issue #59123 for a full explanation.

// run-pass

// edition:2018

#![feature(async_await)]

use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};

const BIG_FUT_SIZE: usize = 1024;
struct BigFut([u8; BIG_FUT_SIZE]);

impl BigFut {
    fn new() -> Self {
        BigFut([0; BIG_FUT_SIZE])
    } }

impl Drop for BigFut {
    fn drop(&mut self) {}
}

impl Future for BigFut {
    type Output = ();

    fn poll(self: Pin<&mut Self>, _ctx: &mut Context<'_>) -> Poll<Self::Output> {
        Poll::Ready(())
    }
}

#[allow(dead_code)]
struct Joiner {
    a: Option<BigFut>,
    b: Option<BigFut>,
    c: Option<BigFut>,
}

impl Future for Joiner {
    type Output = ();

    fn poll(self: Pin<&mut Self>, _ctx: &mut Context<'_>) -> Poll<Self::Output> {
        Poll::Ready(())
    }
}

fn noop() {}

async fn single() {
    let x = BigFut::new();
    x.await;
}

async fn single_with_noop() {
    let x = BigFut::new();
    noop();
    x.await;
}

async fn joined() {
    let a = BigFut::new();
    let b = BigFut::new();
    let c = BigFut::new();

    let joiner = Joiner {
        a: Some(a),
        b: Some(b),
        c: Some(c),
    };
    joiner.await
}

async fn joined_with_noop() {
    let a = BigFut::new();
    let b = BigFut::new();
    let c = BigFut::new();

    let joiner = Joiner {
        a: Some(a),
        b: Some(b),
        c: Some(c),
    };
    noop();
    joiner.await
}

fn main() {
    assert_eq!(1028, std::mem::size_of_val(&single()));
    assert_eq!(1032, std::mem::size_of_val(&single_with_noop()));
    assert_eq!(3084, std::mem::size_of_val(&joined()));
    assert_eq!(3084, std::mem::size_of_val(&joined_with_noop()));
}
<commit_msg>Ignore async-fn-size-moved-locals test on wasm<commit_after>// Test that we don't duplicate storage for futures moved around in .await, and
// for futures moved into other futures.
//
// The exact sizes can change by a few bytes (we'd like to know when they do).
// What we don't want to see is the wrong multiple of 1024 (the size of BigFut)
// being reflected in the size.
//
// See issue #59123 for a full explanation.

// ignore-wasm32-bare (sizes don't match)
// run-pass

// edition:2018

#![feature(async_await)]

use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};

const BIG_FUT_SIZE: usize = 1024;
struct BigFut([u8; BIG_FUT_SIZE]);

impl BigFut {
    fn new() -> Self {
        BigFut([0; BIG_FUT_SIZE])
    } }

impl Drop for BigFut {
    fn drop(&mut self) {}
}

impl Future for BigFut {
    type Output = ();

    fn poll(self: Pin<&mut Self>, _ctx: &mut Context<'_>) -> Poll<Self::Output> {
        Poll::Ready(())
    }
}

#[allow(dead_code)]
struct Joiner {
    a: Option<BigFut>,
    b: Option<BigFut>,
    c: Option<BigFut>,
}

impl Future for Joiner {
    type Output = ();

    fn poll(self: Pin<&mut Self>, _ctx: &mut Context<'_>) -> Poll<Self::Output> {
        Poll::Ready(())
    }
}

fn noop() {}

async fn single() {
    let x = BigFut::new();
    x.await;
}

async fn single_with_noop() {
    let x = BigFut::new();
    noop();
    x.await;
}

async fn joined() {
    let a = BigFut::new();
    let b = BigFut::new();
    let c = BigFut::new();

    let joiner = Joiner {
        a: Some(a),
        b: Some(b),
        c: Some(c),
    };
    joiner.await
}

async fn joined_with_noop() {
    let a = BigFut::new();
    let b = BigFut::new();
    let c = BigFut::new();

    let joiner = Joiner {
        a: Some(a),
        b: Some(b),
        c: Some(c),
    };
    noop();
    joiner.await
}

fn main() {
    assert_eq!(1028, std::mem::size_of_val(&single()));
    assert_eq!(1032, std::mem::size_of_val(&single_with_noop()));
    assert_eq!(3084, std::mem::size_of_val(&joined()));
    assert_eq!(3084, std::mem::size_of_val(&joined_with_noop()));
}
<|endoftext|>


<commit_before>// pest. The Elegant Parser
// Copyright (C) 2017  Dragoș Tiselice
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

use std::rc::Rc;

use super::error::Error;
use super::inputs::{Input, Position, Span};
use super::inputs::position;
use super::iterators::{pairs, QueueableToken};
use super::RuleType;

/// An `enum` specifying the current lookahead status of a `ParserState`.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Lookahead {
    Positive,
    Negative,
    None
}

/// A `struct` which contains the complete state of a `Parser`.
#[derive(Debug)]
pub struct ParserState<R: RuleType, I: Input> {
    queue: Vec<QueueableToken<R>>,
    lookahead: Lookahead,
    pos_attempts: Vec<R>,
    neg_attempts: Vec<R>,
    attempt_pos: usize,
    /// Specifies whether the current state is atomic
    pub is_atomic: bool,
    /// Stack of `Span`s
    pub stack: Vec<Span<I>>
}

/// Creates a `ParserState` from an `Input`, supplying it to a closure `f`.
///
/// # Examples
///
/// ```
/// # use std::rc::Rc;
/// # use pest;
/// # use pest::inputs::StringInput;
///
/// let input = Rc::new(StringInput::new("".to_owned()));
/// pest::state::<(), _, _>(input, |_, pos| {
///     Ok(pos)
/// }).unwrap();
/// ```
pub fn state<R: RuleType, I: Input, F>(
    input: Rc<I>,
    f: F
) -> Result<pairs::Pairs<R, I>, Error<R, I>>
where
    F: FnOnce(&mut ParserState<R, I>, Position<I>) -> Result<Position<I>, Position<I>>
{
    let mut state = ParserState {
        queue: vec![],
        lookahead: Lookahead::None,
        is_atomic: false,
        pos_attempts: vec![],
        neg_attempts: vec![],
        attempt_pos: 0,
        stack: vec![]
    };

    if f(&mut state, Position::from_start(input.clone())).is_ok() {
        let len = state.queue.len();
        Ok(pairs::new(Rc::new(state.queue), input, 0, len))
    } else {
        state.pos_attempts.sort();
        state.pos_attempts.dedup();
        state.neg_attempts.sort();
        state.neg_attempts.dedup();

        Err(Error::ParsingError {
            positives: state.pos_attempts,
            negatives: state.neg_attempts,
            // All attempted positions were legal.
            pos: unsafe { position::new(input, state.attempt_pos) }
        })
    }
}

impl<R: RuleType, I: Input> ParserState<R, I> {
    /// Wrapper needed to generate tokens.
    ///
    /// # Examples
    ///
    /// ```
    /// # use std::rc::Rc;
    /// # use pest;
    /// # use pest::inputs::StringInput;
    /// # #[allow(non_camel_case_types)]
    /// # #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
    /// enum Rule {
    ///     a
    /// }
    ///
    /// let input = Rc::new(StringInput::new("a".to_owned()));
    /// let pairs: Vec<_> = pest::state(input, |state, pos| {
    ///     state.rule(Rule::a, pos, |_, p| Ok(p))
    /// }).unwrap().collect();
    ///
    /// assert_eq!(pairs.len(), 1);
    /// ```
    #[inline]
    pub fn rule<F>(&mut self, rule: R, pos: Position<I>, f: F) -> Result<Position<I>, Position<I>>
    where
        F: FnOnce(&mut ParserState<R, I>, Position<I>) -> Result<Position<I>, Position<I>>
    {
        let actual_pos = pos.pos();
        let index = self.queue.len();

        let (pos_attempts_index, neg_attempts_index) = if actual_pos == self.attempt_pos {
            (self.pos_attempts.len(), self.neg_attempts.len())
        } else { // Attempts have not been cleared yet since the attempt_pos is older.
            (0, 0)
        };

        if self.lookahead == Lookahead::None && !self.is_atomic {
            // Pair's position will only be known after running the closure.
            self.queue.push(QueueableToken::Start { pair: 0, pos: actual_pos });
        }

        let attempts = self.pos_attempts.len() + self.neg_attempts.len();

        let result = f(self, pos);

        if result.is_err() || self.lookahead == Lookahead::Negative {
            self.track(rule, actual_pos, pos_attempts_index, neg_attempts_index, attempts);
        }

        if self.lookahead == Lookahead::None && !self.is_atomic {
            if let Ok(ref pos) = result {
                // Storing the pair's index in the first token that was added before the closure was
                // run.
                let new_index = self.queue.len();
                match self.queue[index] {
                    QueueableToken::Start { ref mut pair, .. } => *pair = new_index,
                    _ => unreachable!()
                };

                self.queue.push(QueueableToken::End { rule: rule, pos: pos.pos() });
            } else {
                self.queue.truncate(index);
            }
        }

        result
    }

    fn track(
        &mut self,
        rule: R,
        pos: usize,
        pos_attempts_index: usize,
        neg_attempts_index: usize,
        prev_attempts: usize
    ) {
        if self.is_atomic {
            return;
        }

        // If nested rules made no progress, there is no use to report them; it's only useful to
        // track the current rule, the exception being when only one attempt has been made during
        // the children rules.
        let curr_attempts = self.pos_attempts.len() + self.neg_attempts.len();
        if curr_attempts > prev_attempts && curr_attempts - prev_attempts == 1 {
            return;
        }

        if pos == self.attempt_pos {
            self.pos_attempts.truncate(pos_attempts_index);
            self.neg_attempts.truncate(neg_attempts_index);
        }

        let attempts = if self.lookahead != Lookahead::Negative {
            &mut self.pos_attempts
        } else {
            &mut self.neg_attempts
        };

        if pos > self.attempt_pos {
            attempts.clear();
            self.attempt_pos = pos;
        }

        if pos == self.attempt_pos {
            attempts.push(rule);
        }
    }

    /// Wrapper which removes `Tokens` in case of a sequence's failure.
    ///
    /// Usually used in conjunction with
    /// [`Position::sequence`](../inputs/struct.Position#method.sequence).
    ///
    /// # Examples
    ///
    /// ```
    /// # use std::rc::Rc;
    /// # use pest;
    /// # use pest::inputs::StringInput;
    /// # #[allow(non_camel_case_types)]
    /// # #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
    /// enum Rule {
    ///     a
    /// }
    ///
    /// let input = Rc::new(StringInput::new("a".to_owned()));
    /// let pairs: Vec<_> = pest::state(input, |state, pos| {
    ///     state.sequence(move |state| {
    ///         pos.sequence(|p| {
    ///             state.rule(Rule::a, p, |_, p| Ok(p)).and_then(|p| {
    ///                 p.match_string("b")
    ///             })
    ///         })
    ///     }).or_else(|p| {
    ///         Ok(p)
    ///     })
    /// }).unwrap().collect();
    ///
    /// assert_eq!(pairs.len(), 0);
    /// ```
    #[inline]
    pub fn sequence<F>(&mut self, f: F) -> Result<Position<I>, Position<I>>
    where
        F: FnOnce(&mut ParserState<R, I>) -> Result<Position<I>, Position<I>>
    {
        let index = self.queue.len();

        let result = f(self);

        if result.is_err() {
            self.queue.truncate(index);
        }

        result
    }

    /// Wrapper which stops `Token`s from being generated.
    ///
    /// Usually used in conjunction with
    /// [`Position::lookahead`](../inputs/struct.Position#method.lookahead).
    ///
    /// # Examples
    ///
    /// ```
    /// # use std::rc::Rc;
    /// # use pest;
    /// # use pest::inputs::StringInput;
    /// # #[allow(non_camel_case_types)]
    /// # #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
    /// enum Rule {
    ///     a
    /// }
    ///
    /// let input = Rc::new(StringInput::new("a".to_owned()));
    /// let pairs: Vec<_> = pest::state(input, |state, pos| {
    ///     state.lookahead(true, move |state| {
    ///         state.rule(Rule::a, pos, |_, p| Ok(p))
    ///     })
    /// }).unwrap().collect();
    ///
    /// assert_eq!(pairs.len(), 0);
    /// ```
    #[inline]
    pub fn lookahead<F>(&mut self, is_positive: bool, f: F) -> Result<Position<I>, Position<I>>
    where
        F: FnOnce(&mut ParserState<R, I>) -> Result<Position<I>, Position<I>>
    {
        let initial_lookahead = self.lookahead;

        self.lookahead = if is_positive {
            match initial_lookahead {
                Lookahead::None | Lookahead::Positive => Lookahead::Positive,
                Lookahead::Negative => Lookahead::Negative
            }
        } else {
            match initial_lookahead {
                Lookahead::None | Lookahead::Positive => Lookahead::Negative,
                Lookahead::Negative => Lookahead::Positive
            }
        };

        let result = f(self);

        self.lookahead = initial_lookahead;

        result
    }

    /// Wrapper which stops `Token`s from being generated according to `is_atomic`.
    ///
    /// # Examples
    ///
    /// ```
    /// # use std::rc::Rc;
    /// # use pest;
    /// # use pest::inputs::StringInput;
    /// # #[allow(non_camel_case_types)]
    /// # #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
    /// enum Rule {
    ///     a
    /// }
    ///
    /// let input = Rc::new(StringInput::new("a".to_owned()));
    /// let pairs: Vec<_> = pest::state(input, |state, pos| {
    ///     state.atomic(true, move |state| {
    ///         state.rule(Rule::a, pos, |_, p| Ok(p))
    ///     })
    /// }).unwrap().collect();
    ///
    /// assert_eq!(pairs.len(), 0);
    /// ```
    #[inline]
    pub fn atomic<F>(&mut self, is_atomic: bool, f: F) -> Result<Position<I>, Position<I>>
    where
        F: FnOnce(&mut ParserState<R, I>) -> Result<Position<I>, Position<I>>
    {
        let should_toggle = self.is_atomic != is_atomic;

        if should_toggle {
            self.is_atomic = is_atomic;
        }

        let result = f(self);

        if should_toggle {
            self.is_atomic = !is_atomic;
        }

        result
    }

    /// Returns whether the `ParserState` is in an atomic state.
    ///
    /// # Examples
    ///
    /// ```
    /// # use std::rc::Rc;
    /// # use pest;
    /// # use pest::inputs::StringInput;
    /// let input = Rc::new(StringInput::new("a".to_owned()));
    /// let pairs: Vec<_> = pest::state::<(), _, _>(input, |state, pos| {
    ///     assert!(!state.is_atomic());
    ///
    ///     state.atomic(true, move |state| {
    ///         assert!(state.is_atomic());
    ///
    ///         Ok(pos)
    ///     })
    /// }).unwrap().collect();
    ///
    /// assert_eq!(pairs.len(), 0);
    /// ```
    #[inline]
    pub fn is_atomic(&self) -> bool {
        self.is_atomic
    }
}
<commit_msg>fix typo in doc leading to dead links.<commit_after>// pest. The Elegant Parser
// Copyright (C) 2017  Dragoș Tiselice
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

use std::rc::Rc;

use super::error::Error;
use super::inputs::{Input, Position, Span};
use super::inputs::position;
use super::iterators::{pairs, QueueableToken};
use super::RuleType;

/// An `enum` specifying the current lookahead status of a `ParserState`.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Lookahead {
    Positive,
    Negative,
    None
}

/// A `struct` which contains the complete state of a `Parser`.
#[derive(Debug)]
pub struct ParserState<R: RuleType, I: Input> {
    queue: Vec<QueueableToken<R>>,
    lookahead: Lookahead,
    pos_attempts: Vec<R>,
    neg_attempts: Vec<R>,
    attempt_pos: usize,
    /// Specifies whether the current state is atomic
    pub is_atomic: bool,
    /// Stack of `Span`s
    pub stack: Vec<Span<I>>
}

/// Creates a `ParserState` from an `Input`, supplying it to a closure `f`.
///
/// # Examples
///
/// ```
/// # use std::rc::Rc;
/// # use pest;
/// # use pest::inputs::StringInput;
///
/// let input = Rc::new(StringInput::new("".to_owned()));
/// pest::state::<(), _, _>(input, |_, pos| {
///     Ok(pos)
/// }).unwrap();
/// ```
pub fn state<R: RuleType, I: Input, F>(
    input: Rc<I>,
    f: F
) -> Result<pairs::Pairs<R, I>, Error<R, I>>
where
    F: FnOnce(&mut ParserState<R, I>, Position<I>) -> Result<Position<I>, Position<I>>
{
    let mut state = ParserState {
        queue: vec![],
        lookahead: Lookahead::None,
        is_atomic: false,
        pos_attempts: vec![],
        neg_attempts: vec![],
        attempt_pos: 0,
        stack: vec![]
    };

    if f(&mut state, Position::from_start(input.clone())).is_ok() {
        let len = state.queue.len();
        Ok(pairs::new(Rc::new(state.queue), input, 0, len))
    } else {
        state.pos_attempts.sort();
        state.pos_attempts.dedup();
        state.neg_attempts.sort();
        state.neg_attempts.dedup();

        Err(Error::ParsingError {
            positives: state.pos_attempts,
            negatives: state.neg_attempts,
            // All attempted positions were legal.
            pos: unsafe { position::new(input, state.attempt_pos) }
        })
    }
}

impl<R: RuleType, I: Input> ParserState<R, I> {
    /// Wrapper needed to generate tokens.
    ///
    /// # Examples
    ///
    /// ```
    /// # use std::rc::Rc;
    /// # use pest;
    /// # use pest::inputs::StringInput;
    /// # #[allow(non_camel_case_types)]
    /// # #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
    /// enum Rule {
    ///     a
    /// }
    ///
    /// let input = Rc::new(StringInput::new("a".to_owned()));
    /// let pairs: Vec<_> = pest::state(input, |state, pos| {
    ///     state.rule(Rule::a, pos, |_, p| Ok(p))
    /// }).unwrap().collect();
    ///
    /// assert_eq!(pairs.len(), 1);
    /// ```
    #[inline]
    pub fn rule<F>(&mut self, rule: R, pos: Position<I>, f: F) -> Result<Position<I>, Position<I>>
    where
        F: FnOnce(&mut ParserState<R, I>, Position<I>) -> Result<Position<I>, Position<I>>
    {
        let actual_pos = pos.pos();
        let index = self.queue.len();

        let (pos_attempts_index, neg_attempts_index) = if actual_pos == self.attempt_pos {
            (self.pos_attempts.len(), self.neg_attempts.len())
        } else { // Attempts have not been cleared yet since the attempt_pos is older.
            (0, 0)
        };

        if self.lookahead == Lookahead::None && !self.is_atomic {
            // Pair's position will only be known after running the closure.
            self.queue.push(QueueableToken::Start { pair: 0, pos: actual_pos });
        }

        let attempts = self.pos_attempts.len() + self.neg_attempts.len();

        let result = f(self, pos);

        if result.is_err() || self.lookahead == Lookahead::Negative {
            self.track(rule, actual_pos, pos_attempts_index, neg_attempts_index, attempts);
        }

        if self.lookahead == Lookahead::None && !self.is_atomic {
            if let Ok(ref pos) = result {
                // Storing the pair's index in the first token that was added before the closure was
                // run.
                let new_index = self.queue.len();
                match self.queue[index] {
                    QueueableToken::Start { ref mut pair, .. } => *pair = new_index,
                    _ => unreachable!()
                };

                self.queue.push(QueueableToken::End { rule: rule, pos: pos.pos() });
            } else {
                self.queue.truncate(index);
            }
        }

        result
    }

    fn track(
        &mut self,
        rule: R,
        pos: usize,
        pos_attempts_index: usize,
        neg_attempts_index: usize,
        prev_attempts: usize
    ) {
        if self.is_atomic {
            return;
        }

        // If nested rules made no progress, there is no use to report them; it's only useful to
        // track the current rule, the exception being when only one attempt has been made during
        // the children rules.
        let curr_attempts = self.pos_attempts.len() + self.neg_attempts.len();
        if curr_attempts > prev_attempts && curr_attempts - prev_attempts == 1 {
            return;
        }

        if pos == self.attempt_pos {
            self.pos_attempts.truncate(pos_attempts_index);
            self.neg_attempts.truncate(neg_attempts_index);
        }

        let attempts = if self.lookahead != Lookahead::Negative {
            &mut self.pos_attempts
        } else {
            &mut self.neg_attempts
        };

        if pos > self.attempt_pos {
            attempts.clear();
            self.attempt_pos = pos;
        }

        if pos == self.attempt_pos {
            attempts.push(rule);
        }
    }

    /// Wrapper which removes `Tokens` in case of a sequence's failure.
    ///
    /// Usually used in conjunction with
    /// [`Position::sequence`](inputs/struct.Position.html#method.sequence).
    ///
    /// # Examples
    ///
    /// ```
    /// # use std::rc::Rc;
    /// # use pest;
    /// # use pest::inputs::StringInput;
    /// # #[allow(non_camel_case_types)]
    /// # #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
    /// enum Rule {
    ///     a
    /// }
    ///
    /// let input = Rc::new(StringInput::new("a".to_owned()));
    /// let pairs: Vec<_> = pest::state(input, |state, pos| {
    ///     state.sequence(move |state| {
    ///         pos.sequence(|p| {
    ///             state.rule(Rule::a, p, |_, p| Ok(p)).and_then(|p| {
    ///                 p.match_string("b")
    ///             })
    ///         })
    ///     }).or_else(|p| {
    ///         Ok(p)
    ///     })
    /// }).unwrap().collect();
    ///
    /// assert_eq!(pairs.len(), 0);
    /// ```
    #[inline]
    pub fn sequence<F>(&mut self, f: F) -> Result<Position<I>, Position<I>>
    where
        F: FnOnce(&mut ParserState<R, I>) -> Result<Position<I>, Position<I>>
    {
        let index = self.queue.len();

        let result = f(self);

        if result.is_err() {
            self.queue.truncate(index);
        }

        result
    }

    /// Wrapper which stops `Token`s from being generated.
    ///
    /// Usually used in conjunction with
    /// [`Position::lookahead`](inputs/struct.Position.html#method.lookahead).
    ///
    /// # Examples
    ///
    /// ```
    /// # use std::rc::Rc;
    /// # use pest;
    /// # use pest::inputs::StringInput;
    /// # #[allow(non_camel_case_types)]
    /// # #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
    /// enum Rule {
    ///     a
    /// }
    ///
    /// let input = Rc::new(StringInput::new("a".to_owned()));
    /// let pairs: Vec<_> = pest::state(input, |state, pos| {
    ///     state.lookahead(true, move |state| {
    ///         state.rule(Rule::a, pos, |_, p| Ok(p))
    ///     })
    /// }).unwrap().collect();
    ///
    /// assert_eq!(pairs.len(), 0);
    /// ```
    #[inline]
    pub fn lookahead<F>(&mut self, is_positive: bool, f: F) -> Result<Position<I>, Position<I>>
    where
        F: FnOnce(&mut ParserState<R, I>) -> Result<Position<I>, Position<I>>
    {
        let initial_lookahead = self.lookahead;

        self.lookahead = if is_positive {
            match initial_lookahead {
                Lookahead::None | Lookahead::Positive => Lookahead::Positive,
                Lookahead::Negative => Lookahead::Negative
            }
        } else {
            match initial_lookahead {
                Lookahead::None | Lookahead::Positive => Lookahead::Negative,
                Lookahead::Negative => Lookahead::Positive
            }
        };

        let result = f(self);

        self.lookahead = initial_lookahead;

        result
    }

    /// Wrapper which stops `Token`s from being generated according to `is_atomic`.
    ///
    /// # Examples
    ///
    /// ```
    /// # use std::rc::Rc;
    /// # use pest;
    /// # use pest::inputs::StringInput;
    /// # #[allow(non_camel_case_types)]
    /// # #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
    /// enum Rule {
    ///     a
    /// }
    ///
    /// let input = Rc::new(StringInput::new("a".to_owned()));
    /// let pairs: Vec<_> = pest::state(input, |state, pos| {
    ///     state.atomic(true, move |state| {
    ///         state.rule(Rule::a, pos, |_, p| Ok(p))
    ///     })
    /// }).unwrap().collect();
    ///
    /// assert_eq!(pairs.len(), 0);
    /// ```
    #[inline]
    pub fn atomic<F>(&mut self, is_atomic: bool, f: F) -> Result<Position<I>, Position<I>>
    where
        F: FnOnce(&mut ParserState<R, I>) -> Result<Position<I>, Position<I>>
    {
        let should_toggle = self.is_atomic != is_atomic;

        if should_toggle {
            self.is_atomic = is_atomic;
        }

        let result = f(self);

        if should_toggle {
            self.is_atomic = !is_atomic;
        }

        result
    }

    /// Returns whether the `ParserState` is in an atomic state.
    ///
    /// # Examples
    ///
    /// ```
    /// # use std::rc::Rc;
    /// # use pest;
    /// # use pest::inputs::StringInput;
    /// let input = Rc::new(StringInput::new("a".to_owned()));
    /// let pairs: Vec<_> = pest::state::<(), _, _>(input, |state, pos| {
    ///     assert!(!state.is_atomic());
    ///
    ///     state.atomic(true, move |state| {
    ///         assert!(state.is_atomic());
    ///
    ///         Ok(pos)
    ///     })
    /// }).unwrap().collect();
    ///
    /// assert_eq!(pairs.len(), 0);
    /// ```
    #[inline]
    pub fn is_atomic(&self) -> bool {
        self.is_atomic
    }
}
<|endoftext|>


<commit_before>// Copyright (C) 2019 O.S. Systems Sofware LTDA
//
// SPDX-License-Identifier: Apache-2.0

macro_rules! impl_object_for_object_types {
    ( $( $objtype:ident ),* ) => {
        impl Info for Object {
            fn status(&self, download_dir: &std::path::Path) -> crate::object::Result<Status> {
                match *self {
                    $( Object::$objtype(ref o) => Ok(o.status(download_dir)?), )*
                }
            }

            fn filename(&self) -> &str {
                match *self {
                    $( Object::$objtype(ref o) => o.filename(), )*
                }
            }

            fn len(&self) -> u64 {
                match *self {
                    $( Object::$objtype(ref o) => o.len(), )*
                }
            }

            fn sha256sum(&self) -> &str {
                match *self {
                    $( Object::$objtype(ref o) => o.sha256sum(), )*
                }
            }

            fn required_install_size(&self) -> u64 {
                match *self {
                    $( Object::$objtype(ref o) => o.required_install_size(), )*
                }
            }
        }
    };
}

macro_rules! impl_object_info {
    ($objtype:ty) => {
        impl Info for $objtype {
            fn filename(&self) -> &str {
                &self.filename
            }

            fn len(&self) -> u64 {
                self.size
            }

            fn sha256sum(&self) -> &str {
                &self.sha256sum
            }

            fn required_install_size(&self) -> u64 {
                self.size
            }
        }
    };
}

macro_rules! impl_compressed_object_info {
    ($objtype:ty) => {
        impl Info for $objtype {
            fn filename(&self) -> &str {
                &self.filename
            }

            fn len(&self) -> u64 {
                self.size
            }

            fn sha256sum(&self) -> &str {
                &self.sha256sum
            }

            fn required_install_size(&self) -> u64 {
                self.required_uncompressed_size
            }
        }
    };
}

macro_rules! for_any_object {
    ($mode:ident, $alias:ident, $code:block) => {
        match $mode {
            Object::Copy($alias) => $code,
            Object::Flash($alias) => $code,
            Object::Imxkobs($alias) => $code,
            Object::Raw($alias) => $code,
            Object::Tarball($alias) => $code,
            Object::Test($alias) => $code,
            Object::Ubifs($alias) => $code,
        }
    };
}

// This wil execute the block only if the $rule is present, if so, the block
// must return a `R` that implements `Read` and `Seek`. If the
// `check_if_different` reports `Ok(true)` for `R` return is called.
macro_rules! handle_install_if_different {
    ($rule:expr, $sha256sum:expr, $handler:block) => {
        match $rule {
            Some(ref rule) => match $handler.and_then(|mut h| {
                crate::object::installer::check_if_different(&mut h, rule, $sha256sum)
            }) {
                Ok(true) => {
                    slog_scope::info!(
                        "installation of {} has been skipped (install if different): {}",
                        $sha256sum,
                        $rule.as_ref().unwrap()
                    );
                    return Ok(());
                }
                Ok(false) => {
                    slog_scope::debug!(
                        "installation will proceed (installation if different): {}",
                        $rule.as_ref().unwrap()
                    );
                }
                Err(e) => {
                    slog_scope::error!(
                        "install if different check ({}) check failed, error: {}",
                        $rule.as_ref().unwrap(),
                        e
                    );
                }
            },
            None => slog_scope::trace!("no install if different rule set, proceeding"),
        }
    };
}
<commit_msg>updatehub: Fix misuse of required size value for uncompressed objects<commit_after>// Copyright (C) 2019 O.S. Systems Sofware LTDA
//
// SPDX-License-Identifier: Apache-2.0

macro_rules! impl_object_for_object_types {
    ( $( $objtype:ident ),* ) => {
        impl Info for Object {
            fn status(&self, download_dir: &std::path::Path) -> crate::object::Result<Status> {
                match *self {
                    $( Object::$objtype(ref o) => Ok(o.status(download_dir)?), )*
                }
            }

            fn filename(&self) -> &str {
                match *self {
                    $( Object::$objtype(ref o) => o.filename(), )*
                }
            }

            fn len(&self) -> u64 {
                match *self {
                    $( Object::$objtype(ref o) => o.len(), )*
                }
            }

            fn sha256sum(&self) -> &str {
                match *self {
                    $( Object::$objtype(ref o) => o.sha256sum(), )*
                }
            }

            fn required_install_size(&self) -> u64 {
                match *self {
                    $( Object::$objtype(ref o) => o.required_install_size(), )*
                }
            }
        }
    };
}

macro_rules! impl_object_info {
    ($objtype:ty) => {
        impl Info for $objtype {
            fn filename(&self) -> &str {
                &self.filename
            }

            fn len(&self) -> u64 {
                self.size
            }

            fn sha256sum(&self) -> &str {
                &self.sha256sum
            }

            fn required_install_size(&self) -> u64 {
                self.size
            }
        }
    };
}

macro_rules! impl_compressed_object_info {
    ($objtype:ty) => {
        impl Info for $objtype {
            fn filename(&self) -> &str {
                &self.filename
            }

            fn len(&self) -> u64 {
                self.size
            }

            fn sha256sum(&self) -> &str {
                &self.sha256sum
            }

            fn required_install_size(&self) -> u64 {
                if self.compressed { self.required_uncompressed_size } else { self.size }
            }
        }
    };
}

macro_rules! for_any_object {
    ($mode:ident, $alias:ident, $code:block) => {
        match $mode {
            Object::Copy($alias) => $code,
            Object::Flash($alias) => $code,
            Object::Imxkobs($alias) => $code,
            Object::Raw($alias) => $code,
            Object::Tarball($alias) => $code,
            Object::Test($alias) => $code,
            Object::Ubifs($alias) => $code,
        }
    };
}

// This wil execute the block only if the $rule is present, if so, the block
// must return a `R` that implements `Read` and `Seek`. If the
// `check_if_different` reports `Ok(true)` for `R` return is called.
macro_rules! handle_install_if_different {
    ($rule:expr, $sha256sum:expr, $handler:block) => {
        match $rule {
            Some(ref rule) => match $handler.and_then(|mut h| {
                crate::object::installer::check_if_different(&mut h, rule, $sha256sum)
            }) {
                Ok(true) => {
                    slog_scope::info!(
                        "installation of {} has been skipped (install if different): {}",
                        $sha256sum,
                        $rule.as_ref().unwrap()
                    );
                    return Ok(());
                }
                Ok(false) => {
                    slog_scope::debug!(
                        "installation will proceed (installation if different): {}",
                        $rule.as_ref().unwrap()
                    );
                }
                Err(e) => {
                    slog_scope::error!(
                        "install if different check ({}) check failed, error: {}",
                        $rule.as_ref().unwrap(),
                        e
                    );
                }
            },
            None => slog_scope::trace!("no install if different rule set, proceeding"),
        }
    };
}
<|endoftext|>


<commit_before>// This file contains basic definitions for AIs as well as helper function to build them.
// Some example AIs are also contained.
extern crate rand;
use self::rand::{thread_rng, Rng};
use game;
use game::{GameState, GameStructure, PlayerColor, VictoryState, VictoryStats, LineState, Move};

pub trait SogoAI {
    fn reset_game(&self);
    // Some information may be preserved after an opponent's turn.
    // Tree based algorithms may carry over part of the search tree.
    fn register_opponent_action(&self, &Move);
    fn decide_action(&self, state : &GameState) -> Move;
        // An imutable reference to the game_state is passed for convenience only.
}

pub fn run_match<T : SogoAI, U : SogoAI>(structure : &GameStructure, white_player : &T, black_player : &U) -> GameState {
    let mut i = 0;

    let mut state = GameState::new();
    while state.victory_state == VictoryState::Undecided {
        if state.age == 64 {
            state.victory_state = VictoryState::Draw;
            return state;
        }
        let action = if i % 2 == 0 {white_player.decide_action(&state)} else {black_player.decide_action(&state)};
        if i % 2 == 0 {black_player.register_opponent_action(&action)} else {white_player.register_opponent_action(&action)};
        game::execute_move(structure, &mut state, action);
        i += 1;
    }
    // println!("{:?}", i);
    return state;
}

// An AI which executes random legal moves
#[allow(dead_code)] // Empty structs are unstable.
pub struct RandomSogoAI {
    alibi : i8,
}

#[allow(dead_code)]
impl RandomSogoAI {
    pub fn new() -> RandomSogoAI {
        RandomSogoAI { alibi : 42 }
    }
}

impl SogoAI for RandomSogoAI {
    fn reset_game(&self) { }
    fn register_opponent_action(&self, _ : &Move) {}
    fn decide_action(&self, state : &GameState) -> Move {
        let position = thread_rng().choose(&state.legal_moves);
        // Rust also implements a faster random generator, but it needs to be stored outside of this
        // small function. Caching the RNG might help anyways.
        match position {
            Some(&(x, y)) => Move::Play {x:x, y:y},
            None => Move::Surrender
        }
    }
}

pub fn random_playout(structure : &GameStructure, state : &GameState) -> VictoryState {
    let mut my_state = state.clone();
    let mut rng = thread_rng();
    while my_state.victory_state == VictoryState::Undecided {
        let action = {
            match rng.choose(&my_state.legal_moves) {
                Some(&(x, y)) => Move::Play {x:x, y:y},
                None => Move::Surrender
            }
        };
        game::execute_move(structure, &mut my_state, action);
    }
    return my_state.victory_state;
}

pub fn random_playout_sample(structure : &GameStructure, state : &GameState, number : i32) -> VictoryStats {
    let mut statics = game::VictoryStats::new();
    for _ in 0..number {
        let result = random_playout(&structure, &state);
        match result {
            game::VictoryState::Win(game::PlayerColor::White) => statics.white += 1,
            game::VictoryState::Win(game::PlayerColor::Black) => statics.black += 1,
            game::VictoryState::Draw      => statics.draws  += 1,
            game::VictoryState::Undecided => panic!("The game_state should never be undecided after a random playout."),
        }
    }
    return statics
}

fn easy_judgement (state : &GameState, my_color : PlayerColor) -> i32 {
    let mut score = 0;
    for i in 0..76 {
        let line = state.lines[i];
        score += match line {
            LineState::Empty  => 0,
            LineState::Win(color) => 1000 * (if color == my_color {1} else {-1}),
                // If I'm still allowed to play, that must have been my win.
            LineState::Mixed  => 0,
            LineState::Pure { color, count } =>
                (count * count * (if color == my_color {1} else {-1})) as i32,
        }
    }
    return score
}

#[allow(dead_code)]
pub struct TreeJudgementAI {
    structure : game::GameStructure,
    search_depth : i8,
}

#[allow(dead_code)]
impl TreeJudgementAI {
    pub fn new(depth : i8) -> TreeJudgementAI {
        TreeJudgementAI { structure : game::GameStructure::new(), search_depth : depth }
    }
}

impl SogoAI for TreeJudgementAI {
    fn reset_game(&self) {}
    fn register_opponent_action(&self, _ : &Move) {}
    fn decide_action(&self, state : &GameState) -> Move {
        let my_color = state.current_color;
        // Create a tree from the current gamestate.
        let mut tree : Node<MinMaxTagging> = Node::new(state.clone(), None);
        // Completely expand the first n layers
        fully_expand_to_depth(&self.structure, &mut tree, self.search_depth);

        let my_easy_judgement = |state : &GameState|
            MinMaxTagging { value : easy_judgement(state, my_color), from_action : None};

        tag_all_leaves(&my_easy_judgement, &mut tree);
        min_max(&mut tree);

        let action = tree.tag.from_action.unwrap_or(Move::Surrender);
        println!("{:?} deciding on '{:?}' with valuation {:?}.", my_color, action, tree.tag.value);
        return action;
    }
}

// The type parameter T can carry additional information (like a score) and needs to allow a default.
struct Node<T : Default> {
    state : GameState,
    children : Branching<T>,
    parent_action : Option<Move>, // If this isn't the root, how did we get here?
    tag : T,
}

enum Branching<T : Default> {
    GameOver,
    Unexpanded,
    Expanded(Vec<Node<T>>),
}

impl<T : Default> Node<T> {
    pub fn new(state : GameState, parent_action : Option<Move>) -> Node<T> {
        Node {
            state : state,
            children : Branching::Unexpanded,
            parent_action : parent_action,
            tag : Default::default(),
        }
    }
}

// Expands the current supplied node, if neccessary. The return value indicates if it was expanded.
fn expand_node_total<T : Default>(structure : &GameStructure, node : &mut Node<T>) -> bool {
    match node.children {
        Branching::Unexpanded => {
            let mut children = Vec::new();
            for action in &node.state.legal_moves {
                let play = Move::new(action);
                let mut child = Node::new(
                    game::execute_move_functional(structure, &node.state, play.clone()),
                    Some(play)
                );
                if child.state.victory_state != VictoryState::Undecided {
                    child.children = Branching::GameOver;
                }
                children.push(child);
            }
            if children.len() == 0 {
                node.children = Branching::GameOver;
            } else {
                node.children = Branching::Expanded(children);
            }
            true
        },
        Branching::Expanded(_) => false,
        Branching::GameOver => false,
    }
}

fn fully_expand_to_depth<T : Default>(structure : &GameStructure, node : &mut Node<T>, depth : i8) {
    if depth <= 0 {
        return;
    }
    expand_node_total(structure, node);
    match node.children {
        Branching::Expanded(ref mut children) => {
            for mut child in children {
                fully_expand_to_depth(structure, &mut child, depth-1);
            }
        },
        _ => (),
    }
}

fn tag_all_leaves<T : Default, F>(tagger : &F, node : &mut Node<T>)
    where F : Fn(&GameState) -> T {
    match node.children {
        Branching::GameOver   => node.tag = tagger(&node.state),
        Branching::Unexpanded => node.tag = tagger(&node.state),
        Branching::Expanded(ref mut children) => {
            for mut child in children {
                tag_all_leaves(tagger, &mut child);
            }
        }
    }
}

#[derive(Default, Debug)]
struct MinMaxTagging {
    value : i32,
    from_action : Option<Move>,
}

fn min_max(node : &mut Node<MinMaxTagging>) {
    match node.children {
        Branching::Expanded(ref mut children) => {
            let mut max = i32::min_value();
            let mut action = None;
            for mut child in children {
                max_min(&mut child);
                if child.tag.value > max {
                    max = child.tag.value;
                    action = child.parent_action;
                }
            }
            node.tag.value = max;
            node.tag.from_action = action;
        },
        _ => () // Leaves don't min-max
    }
}

fn max_min(node : &mut Node<MinMaxTagging>) {
    match node.children {
        Branching::Expanded(ref mut children) => {
            let mut min = i32::max_value();
            let mut action = None;
            for mut child in children {
                min_max(&mut child);
                if child.tag.value < min {
                    min = child.tag.value;
                    action = child.parent_action;
                }
            }
            node.tag.value = min;
            node.tag.from_action = action;
        },
        _ => () // Leaves don't min-max
    }
}

// Pure Monte Carlo AI
// For each possible move, a number of playouts is run. This should give an approximate information
// about the value of each move.

#[allow(dead_code)]
pub struct MonteCarloAI {
    endurance : i32, // How many random games am I allowed to play each turn?
    structure : GameStructure,
}

#[allow(dead_code)]
impl MonteCarloAI {
    pub fn new(endurance : i32) -> MonteCarloAI {
        MonteCarloAI{endurance : endurance, structure : GameStructure::new()}
    }
}

impl SogoAI for MonteCarloAI {
    fn reset_game(&self) {}
    // Some information may be preserved after an opponent's turn.
    // Tree based algorithms may carry over part of the search tree.
    fn register_opponent_action(&self, _ : &Move) {}
    fn decide_action(&self, state : &GameState) -> Move {
        let my_color = state.current_color;
        let endurance_per_action = self.endurance / (state.legal_moves.len() as i32);
        // Create a tree from the current gamestate.
        let mut tree : Node<MinMaxTagging> = Node::new(state.clone(), None);
        // Completely expand the first layer
        fully_expand_to_depth(&self.structure, &mut tree, 1);

        let judgement = |state : &GameState|
            MinMaxTagging {
                value : monte_carlo_judgement(&self.structure, state, my_color, endurance_per_action),
                from_action : None
            };

        tag_all_leaves(&judgement, &mut tree);
        min_max(&mut tree);

        let action = tree.tag.from_action.unwrap_or(Move::Surrender);
        //println!("{:?} deciding on '{:?}' with valuation {:?}.", my_color, action, tree.tag.value);
        return action;
    }
}

fn monte_carlo_judgement(structure : &GameStructure, state : &GameState, my_color : PlayerColor, amount : i32) -> i32 {
    let stats = random_playout_sample(structure, state, amount);
    if my_color == PlayerColor::White {
        return stats.white - stats.black;
    } else {
        return stats.black - stats.white;
    }
}

// Monte Carlo Tree search
// This is fancy, I'll do it later, when I learned about Monte Carlo and
// about Tree search.
<commit_msg>Abandoning another attemt at Monte Carlo tree search and commenting it out.<commit_after>// This file contains basic definitions for AIs as well as helper function to build them.
// Some example AIs are also contained.
extern crate rand;
use self::rand::{thread_rng, Rng};
//use self::rand::distributions::{IndependentSample, Range};
use game;
use game::{GameState, GameStructure, PlayerColor, VictoryState, VictoryStats, LineState, Move};

pub trait SogoAI {
    fn reset_game(&self);
    // Some information may be preserved after an opponent's turn.
    // Tree based algorithms may carry over part of the search tree.
    fn register_opponent_action(&self, &Move);
    fn decide_action(&self, state : &GameState) -> Move;
        // An imutable reference to the game_state is passed for convenience only.
}

pub fn run_match<T : SogoAI, U : SogoAI>(structure : &GameStructure, white_player : &T, black_player : &U) -> GameState {
    let mut i = 0;

    let mut state = GameState::new();
    while state.victory_state == VictoryState::Undecided {
        if state.age == 64 {
            state.victory_state = VictoryState::Draw;
            return state;
        }
        let action = if i % 2 == 0 {white_player.decide_action(&state)} else {black_player.decide_action(&state)};
        if i % 2 == 0 {black_player.register_opponent_action(&action)} else {white_player.register_opponent_action(&action)};
        game::execute_move(structure, &mut state, action);
        i += 1;
    }
    // println!("{:?}", i);
    return state;
}

// An AI which executes random legal moves
#[allow(dead_code)] // Empty structs are unstable.
pub struct RandomSogoAI {
    alibi : i8,
}

#[allow(dead_code)]
impl RandomSogoAI {
    pub fn new() -> RandomSogoAI {
        RandomSogoAI { alibi : 42 }
    }
}

impl SogoAI for RandomSogoAI {
    fn reset_game(&self) { }
    fn register_opponent_action(&self, _ : &Move) {}
    fn decide_action(&self, state : &GameState) -> Move {
        let position = thread_rng().choose(&state.legal_moves);
        // Rust also implements a faster random generator, but it needs to be stored outside of this
        // small function. Caching the RNG might help anyways.
        match position {
            Some(&(x, y)) => Move::Play {x:x, y:y},
            None => Move::Surrender
        }
    }
}

pub fn random_playout(structure : &GameStructure, state : &GameState) -> VictoryState {
    let mut my_state = state.clone();
    let mut rng = thread_rng();
    while my_state.victory_state == VictoryState::Undecided {
        let action = {
            match rng.choose(&my_state.legal_moves) {
                Some(&(x, y)) => Move::Play {x:x, y:y},
                None => Move::Surrender
            }
        };
        game::execute_move(structure, &mut my_state, action);
    }
    return my_state.victory_state;
}

pub fn random_playout_sample(structure : &GameStructure, state : &GameState, number : i32) -> VictoryStats {
    let mut statics = game::VictoryStats::new();
    for _ in 0..number {
        let result = random_playout(&structure, &state);
        match result {
            game::VictoryState::Win(game::PlayerColor::White) => statics.white += 1,
            game::VictoryState::Win(game::PlayerColor::Black) => statics.black += 1,
            game::VictoryState::Draw      => statics.draws  += 1,
            game::VictoryState::Undecided => panic!("The game_state should never be undecided after a random playout."),
        }
    }
    return statics
}

fn easy_judgement (state : &GameState, my_color : PlayerColor) -> i32 {
    let mut score = 0;
    for i in 0..76 {
        let line = state.lines[i];
        score += match line {
            LineState::Empty  => 0,
            LineState::Win(color) => 1000 * (if color == my_color {1} else {-1}),
                // If I'm still allowed to play, that must have been my win.
            LineState::Mixed  => 0,
            LineState::Pure { color, count } =>
                (count * count * (if color == my_color {1} else {-1})) as i32,
        }
    }
    return score
}

#[allow(dead_code)]
pub struct TreeJudgementAI {
    structure : game::GameStructure,
    search_depth : i8,
}

#[allow(dead_code)]
impl TreeJudgementAI {
    pub fn new(depth : i8) -> TreeJudgementAI {
        TreeJudgementAI { structure : game::GameStructure::new(), search_depth : depth }
    }
}

impl SogoAI for TreeJudgementAI {
    fn reset_game(&self) {}
    fn register_opponent_action(&self, _ : &Move) {}
    fn decide_action(&self, state : &GameState) -> Move {
        let my_color = state.current_color;
        // Create a tree from the current gamestate.
        let mut tree : Node<MinMaxTagging> = Node::new(state.clone(), None);
        // Completely expand the first n layers
        fully_expand_to_depth(&self.structure, &mut tree, self.search_depth);

        let my_easy_judgement = |state : &GameState|
            MinMaxTagging { value : easy_judgement(state, my_color), from_action : None};

        tag_all_leaves(&my_easy_judgement, &mut tree);
        min_max(&mut tree);

        let action = tree.tag.from_action.unwrap_or(Move::Surrender);
        println!("{:?} deciding on '{:?}' with valuation {:?}.", my_color, action, tree.tag.value);
        return action;
    }
}

// The type parameter T can carry additional information (like a score) and needs to allow a default.
struct Node<T : Default> {
    state : GameState,
    children : Branching<T>,
    parent_action : Option<Move>, // If this isn't the root, how did we get here?
    tag : T,
}

enum Branching<T : Default> {
    GameOver,
    Unexpanded,
    Expanded(Vec<Node<T>>),
}

impl<T : Default> Node<T> {
    pub fn new(state : GameState, parent_action : Option<Move>) -> Node<T> {
        Node {
            state : state,
            children : Branching::Unexpanded,
            parent_action : parent_action,
            tag : Default::default(),
        }
    }
}

// Expands the current supplied node, if neccessary. The return value indicates if it was expanded.
fn expand_node_total<T : Default>(structure : &GameStructure, node : &mut Node<T>) -> bool {
    match node.children {
        Branching::Unexpanded => {
            let mut children = Vec::new();
            for action in &node.state.legal_moves {
                let play = Move::new(action);
                let mut child = Node::new(
                    game::execute_move_functional(structure, &node.state, play.clone()),
                    Some(play)
                );
                if child.state.victory_state != VictoryState::Undecided {
                    child.children = Branching::GameOver;
                }
                children.push(child);
            }
            if children.len() == 0 {
                node.children = Branching::GameOver;
            } else {
                node.children = Branching::Expanded(children);
            }
            true
        },
        Branching::Expanded(_) => false,
        Branching::GameOver => false,
    }
}

fn fully_expand_to_depth<T : Default>(structure : &GameStructure, node : &mut Node<T>, depth : i8) {
    if depth <= 0 {
        return;
    }
    expand_node_total(structure, node);
    match node.children {
        Branching::Expanded(ref mut children) => {
            for mut child in children {
                fully_expand_to_depth(structure, &mut child, depth-1);
            }
        },
        _ => (),
    }
}

fn tag_all_leaves<T : Default, F>(tagger : &F, node : &mut Node<T>)
    where F : Fn(&GameState) -> T {
    match node.children {
        Branching::GameOver   => node.tag = tagger(&node.state),
        Branching::Unexpanded => node.tag = tagger(&node.state),
        Branching::Expanded(ref mut children) => {
            for mut child in children {
                tag_all_leaves(tagger, &mut child);
            }
        }
    }
}

#[derive(Default, Debug)]
struct MinMaxTagging {
    value : i32,
    from_action : Option<Move>,
}

fn min_max(node : &mut Node<MinMaxTagging>) {
    match node.children {
        Branching::Expanded(ref mut children) => {
            let mut max = i32::min_value();
            let mut action = None;
            for mut child in children {
                max_min(&mut child);
                if child.tag.value > max {
                    max = child.tag.value;
                    action = child.parent_action;
                }
            }
            node.tag.value = max;
            node.tag.from_action = action;
        },
        _ => () // Leaves don't min-max
    }
}

fn max_min(node : &mut Node<MinMaxTagging>) {
    match node.children {
        Branching::Expanded(ref mut children) => {
            let mut min = i32::max_value();
            let mut action = None;
            for mut child in children {
                min_max(&mut child);
                if child.tag.value < min {
                    min = child.tag.value;
                    action = child.parent_action;
                }
            }
            node.tag.value = min;
            node.tag.from_action = action;
        },
        _ => () // Leaves don't min-max
    }
}

// Pure Monte Carlo AI
// For each possible move, a number of playouts is run. This should give an approximate information
// about the value of each move.

#[allow(dead_code)]
pub struct MonteCarloAI {
    endurance : i32, // How many random games am I allowed to play each turn?
    structure : GameStructure,
}

#[allow(dead_code)]
impl MonteCarloAI {
    pub fn new(endurance : i32) -> MonteCarloAI {
        MonteCarloAI{endurance : endurance, structure : GameStructure::new()}
    }
}

impl SogoAI for MonteCarloAI {
    fn reset_game(&self) {}
    // Some information may be preserved after an opponent's turn.
    // Tree based algorithms may carry over part of the search tree.
    fn register_opponent_action(&self, _ : &Move) {}
    fn decide_action(&self, state : &GameState) -> Move {
        let my_color = state.current_color;
        let endurance_per_action = self.endurance / (state.legal_moves.len() as i32);
        // Create a tree from the current gamestate.
        let mut tree : Node<MinMaxTagging> = Node::new(state.clone(), None);
        // Completely expand the first layer
        fully_expand_to_depth(&self.structure, &mut tree, 1);

        let judgement = |state : &GameState|
            MinMaxTagging {
                value : monte_carlo_judgement(&self.structure, state, my_color, endurance_per_action),
                from_action : None
            };

        tag_all_leaves(&judgement, &mut tree);
        min_max(&mut tree);

        let action = tree.tag.from_action.unwrap_or(Move::Surrender);
        //println!("{:?} deciding on '{:?}' with valuation {:?}.", my_color, action, tree.tag.value);
        return action;
    }
}

fn monte_carlo_judgement(structure : &GameStructure, state : &GameState, my_color : PlayerColor, amount : i32) -> i32 {
    let stats = random_playout_sample(structure, state, amount);
    if my_color == PlayerColor::White {
        return stats.white - stats.black;
    } else {
        return stats.black - stats.white;
    }
}

/*
// Monte Carlo Tree search
// This is fancy, I'll do it later, when I learned about Monte Carlo and
// about Tree search.

// This tree structure is slightly more lazy than the Node tree and can expand a node partially.
struct MCNode {
    state : GameState,
    children : MCBranching,
    parent_action : Option<Move>, // If this isn't the root, how did we get here?
    wins : i32,
    total : i32,
}

enum LazyMCNode {
    Unexpanded(Move),
    // We need a Box to avoid [E0072], which forbids nested struct/enums.
    // use 'rustc --explain E0072' to find out more.
    Expanded(Box<MCNode>)
}

// I don't want to evaluate all possible branches to save time and memory.
// Only the possible moves are stored and can be lazily changed to a gamestate.
enum MCBranching {
    GameOver(VictoryState),
    Branch(Vec<LazyMCNode>),
}

impl MCNode {
    pub fn new(state : GameState, parent_action : Option<Move>) -> MCNode {
        let children = {
            if state.victory_state == VictoryState::Undecided {
                let mut children = Vec::new();
                for point in state.legal_moves {
                    children.push(LazyMCNode::Unexpanded(Move::new(&point)));
                }
                MCBranching::Branch(children)
            } else {
                MCBranching::GameOver(state.victory_state.clone())
            }
        };
        MCNode {
            state : state,
            children : children,
            parent_action : parent_action,
            wins : 0,
            total : 0,
        }
    }
}

fn random_mc_playout(structure : &GameStructure, node : &mut MCNode) -> VictoryState {
    // First, check if we have children.
    match node.children {
        MCBranching::GameOver(victory_state) => victory_state,
        MCBranching::Branch(mut children) => {
            // Randomly choose a child
            //let mut child = thread_rng().choose(&children).unwrap();
            // OK, here we have a problem with the saveness of Rust.
            // We can't own children and a child at the same time. This is kind of the reason why
            // choosing randomly can only return a reference to the child.
            // I'll try fixing this by choosing a random index first and then extracting that child.
            // That should give me ownership of a child and suspend children until the reference to
            // child becomes invalid. (At the end of this match block.)
            let between = Range::new(0, children.len());
            let index = between.ind_sample(&mut thread_rng());
            let mut child = children[index];
            match child {
                // if this is a unexpanded node, do a random playout, count it and propagate it upwards.
                LazyMCNode::Unexpanded(action) => {
                    let mut new_state = node.state.clone();
                    game::execute_move(structure, &mut new_state, action);
                    // This is bad, we clone the state twice.
                    // TODO: Define random_playout_ip to speed this up.
                    random_playout(structure, &mut new_state)
                }
                LazyMCNode::Expanded(boxed_node) => {
                    VictoryState::Draw
                }
            }
        }
    }
}
*/
<|endoftext|>