<commit_before>use mem;

//
// Video
//

static MODE0_CYCLES: u32 = 204;   // H-Blank
static MODE1_CYCLES: u32 = 4560;  // V-Blank
static MODE2_CYCLES: u32 = 80;    // Transfer to LCD
static MODE3_CYCLES: u32 = 172;   // Transfer to LCD

static MODE0_START: u32 = MODE0_CYCLES;
static MODE2_START: u32 = MODE0_START + MODE0_CYCLES;
static MODE3_START: u32 = MODE2_START + MODE2_CYCLES;

static ROW_CYCLES: u32 = MODE0_CYCLES + MODE2_CYCLES + MODE3_CYCLES;

static ROW_COUNT: u8 = 144; // After this many rows, V-Blank starts

static SCREEN_REFRESH_CYCLES: u32 = ROW_COUNT as u32 * ROW_CYCLES + MODE1_CYCLES;

static STAT_MODE0_IRQ: u8        = 0b00001000;
static STAT_MODE1_IRQ: u8        = 0b00010000;
static STAT_MODE2_IRQ: u8        = 0b00100000;
static STAT_COINCIDENCE_IRQ: u8  = 0b01000000;
static STAT_IRQ_MASK: u8         = 0b01111000;
static STAT_COINCIDENCE_FLAG: u8 = 0b00000100;

pub static SCREEN_WIDTH: uint = 160;
pub static SCREEN_HEIGHT: uint = 144;

pub struct Video {
  // Implementation state
  cycles: u32, // internal cycle count, wraps around at SCREEN_REFRESH_CYCLES

  mode: u8,  // LCD mode (0-3)
  dma: u8,   // DMA request, 0xff = no request, 0x00-0xf1 = requested base address

  // Registers
  flags: u8, // LCDC register
  stat: u8,  // STAT register

  ly: u8,    // LY register
  lyc: u8,   // LYC register

  scx: u8,   // SCX register
  scy: u8,   // SCY register
  wx: u8,    // WX register
  wy: u8,    // WY register

  bgp: u8,   // BGP register
  obp0: u8,  // OBP0 register
  obp1: u8,  // OBP1 register

  // Memory
  vram: [u8, ..0x2000],
  oam: [u8, ..0xa0],

  // Screen buffer
  screen: [u8, ..SCREEN_WIDTH*SCREEN_HEIGHT*4],
}

pub enum Signal {
  VBlank,
  LCD,
  DMA(u8),
}

impl Video {
  pub fn new() -> Video {
    Video {
      cycles: 0,
      mode: 0,
      dma: 0xff,
      flags: 0x91,
      stat: 0,
      ly: 0,
      lyc: 0,
      scx: 0,
      scy: 0,
      wx: 0,
      wy: 0,
      bgp: 0,
      obp0: 0,
      obp1: 0,
      vram: [0u8, ..0x2000],
      oam: [0u8, ..0xa0],
      screen: [0u8, ..SCREEN_WIDTH*SCREEN_HEIGHT*4], // BGRA
    }
  }

  pub fn tick(&mut self, cycles: u8) -> Option<Signal> {
    let old_ly = self.ly;
    let old_mode = self.mode;

    self.cycles = (self.cycles + cycles as u32) % SCREEN_REFRESH_CYCLES;
    self.ly = (self.cycles / ROW_CYCLES) as u8;

    self.mode =
      if self.ly < ROW_COUNT {
        match self.cycles % ROW_CYCLES {
          MODE0_START .. (MODE2_START - 1) => 0,
          MODE2_START .. (MODE3_START - 1) => 2,
          _                                => 3,
        }
      } else {
        1 // V-Blank
      };

    self.update_stat();

    if self.dma != 0xff {
      let base = self.dma;
      self.dma = 0xff;
      return Some(DMA(base));
    }

    // TODO: Implement behavior according to http://gameboy.mongenel.com/dmg/istat98.txt

    if self.mode != old_mode {
      if self.mode == 1 {
        return Some(VBlank) // TODO: Also generate LCD interrupt if STAT_MODE1_IRQ is set
      }

      if self.mode == 0 {
        // H-Blank
        self.draw_bg_row(self.ly as uint);
      }

      match self.mode {
        0 => if self.stat & STAT_MODE0_IRQ != 0 { return Some(LCD) },
        1 => if self.stat & STAT_MODE1_IRQ != 0 { return Some(LCD) },
        2 => if self.stat & STAT_MODE2_IRQ != 0 { return Some(LCD) },
        _ => (),
      }
    }

    if self.ly != old_ly &&
       (self.stat & STAT_COINCIDENCE_IRQ != 0) &&
       (self.stat & STAT_COINCIDENCE_FLAG != 0) {
      return Some(LCD);
    }

    None
  }

  fn update_stat(&mut self) {
    // Coincidence flag
    if self.ly == self.lyc {
      self.stat |= STAT_COINCIDENCE_FLAG;
    } else {
      self.stat &= !STAT_COINCIDENCE_FLAG;
    }
    // Mode flag
    self.stat = (self.stat & (STAT_IRQ_MASK |STAT_COINCIDENCE_FLAG)) | self.mode;
  }

  fn draw_bg_row(&mut self, row: uint) {
    let mut tile_base = 0x800;
    let mut tile_bias = 128u8;
    let mut map_base = 0x1800;

    if (self.flags & 0b10000) != 0 {
      tile_base = 0x0;
      tile_bias = 0u8;
    }
    if (self.flags & 0b01000) != 0 {
      map_base = 0x1c00;
    }

    let pal = self.bgp;

    let y = row;
    let bg_y = (y + self.scy as uint) % (32*8);
    let bg_tile_y = bg_y / 8;
    for x in range(0u, SCREEN_WIDTH) {
      let bg_x = (x + self.scx as uint) % (32*8);
      let bg_tile_x = bg_x / 8;
      let tile_num = (self.vram[map_base + bg_tile_y*32 + bg_tile_x] + tile_bias) as uint;
      let tile = self.vram.slice_from(tile_base + tile_num*16);
      let pixel = self.screen.mut_slice_from((y * SCREEN_WIDTH + x) * 4);
      let tile_row = bg_y % 8;
      let tile_col = bg_x % 8;
      unpack_tile_pixel(tile, pal, tile_row, tile_col, pixel);
    }
  }
}

impl mem::Mem for Video {
  fn loadb(&mut self, addr: u16) -> u8 {
    match addr {
      0x8000..0x9fff => self.vram[addr - 0x8000],
      0xfe00..0xfe9f => self.oam[addr - 0xfe00], // OAM

      // I/O registers
      0xff40 => self.flags,
      0xff41 => self.stat,
      0xff42 => self.scy,
      0xff43 => self.scx,
      0xff44 => self.ly,
      0xff45 => self.lyc,
      0xff46 => 0xff, // Write-only DMA register
      0xff47 => self.bgp,
      0xff48 => self.obp0,
      0xff49 => self.obp1,
      0xff4a => self.wy,
      0xff4b => self.wx,
      _ => fail!("invalid video address: ${:04X}", addr),
    }
  }

  fn storeb(&mut self, addr: u16, val: u8) {
    match addr {
      0x8000..0x9fff => self.vram[addr - 0x8000] = val,
      0xfe00..0xfe9f => self.oam[addr - 0xfe00] = val,

      // I/O registers
      0xff40 => self.flags = val,
      0xff41 => self.stat = (val & STAT_IRQ_MASK) | (self.stat & !STAT_IRQ_MASK), // Only interrupt enable bits are writeable
      0xff42 => self.scy = val,
      0xff43 => self.scx = val,
      0xff44 => (), // Read-only LY register
      0xff45 => self.lyc = val, // TODO: update_stat()?
      0xff46 => if val <= 0xf1 { self.dma = val },
      0xff47 => self.bgp = val,
      0xff48 => self.obp0 = val,
      0xff49 => self.obp1 = val,
      0xff4a => self.wy = val,
      0xff4b => self.wx = val,
      _ => fail!("invalid video address: ${:04X}", addr),
    }
  }
}

fn unpack_tile_pixel(tile: &[u8],
                     palette: u8,
                     row: uint,
                     col: uint,
                     pixel: &mut [u8]) {
  static colors: &'static [& 'static[u8]] = &[
    &[244, 248, 208],
    &[136, 192, 112],
    &[52, 104, 86],
    &[8, 24, 32]
  ];

  let low_bit  = (tile[2*row]   >> (7 - col)) & 1;
  let high_bit = (tile[2*row+1] >> (7 - col)) & 1;
  let value = (high_bit << 1) | low_bit;
  let color = (palette >> (2 * value)) & 0b11;
  pixel[0] = colors[color][2];
  pixel[1] = colors[color][1];
  pixel[2] = colors[color][0];
}
<commit_msg>Added basic obj (sprite) drawing<commit_after>use mem;

//
// Video
//

static MODE0_CYCLES: u32 = 204;   // H-Blank
static MODE1_CYCLES: u32 = 4560;  // V-Blank
static MODE2_CYCLES: u32 = 80;    // Transfer to LCD
static MODE3_CYCLES: u32 = 172;   // Transfer to LCD

static MODE0_START: u32 = MODE0_CYCLES;
static MODE2_START: u32 = MODE0_START + MODE0_CYCLES;
static MODE3_START: u32 = MODE2_START + MODE2_CYCLES;

static ROW_CYCLES: u32 = MODE0_CYCLES + MODE2_CYCLES + MODE3_CYCLES;

static SCREEN_REFRESH_CYCLES: u32 = SCREEN_HEIGHT as u32 * ROW_CYCLES + MODE1_CYCLES;

static STAT_MODE0_IRQ: u8        = 0b0000_1000;
static STAT_MODE1_IRQ: u8        = 0b0001_0000;
static STAT_MODE2_IRQ: u8        = 0b0010_0000;
static STAT_COINCIDENCE_IRQ: u8  = 0b0100_0000;
static STAT_IRQ_MASK: u8         = 0b0111_1000;
static STAT_COINCIDENCE_FLAG: u8 = 0b0000_0100;

static FLAG_ENABLE_BG_WIN: u8 = 0b0000_0001;
static FLAG_ENABLE_OBJ: u8    = 0b0000_0010;
static FLAG_OBJ_SIZE: u8      = 0b0000_0100;
static FLAG_BG_MAP: u8        = 0b0000_1000;
static FLAG_BG_WIN_TILES: u8  = 0b0001_0000;
static FLAG_ENABLE_WIN: u8    = 0b0010_0000;
static FLAG_WIN_MAP: u8       = 0b0100_0000;
static FLAG_ENABLE: u8        = 0b1000_0000;

static TILES_BASE0: uint = 0x800;
static TILES_BIAS0: u8 = 128u8;
static TILES_BASE1: uint = 0x000;
static TILES_BIAS1: u8 = 0u8;

static BG_WIN_MAP_BASE0: uint = 0x1800;
static BG_WIN_MAP_BASE1: uint = 0x1c00;

static TILE_WIDTH: uint  = 8;
static TILE_HEIGHT: uint = 8;
static TILE_BYTES: uint  = 16;

static BG_WIDTH_TILES:  uint = 32;
static BG_HEIGHT_TILES: uint = 32;

static OBJ_FLAG_PALETTE:  u8 = 0b0001_0000;
static OBJ_FLAG_FLIP_X:   u8 = 0b0010_0000;
static OBJ_FLAG_FLIP_Y:   u8 = 0b0100_0000;
static OBJ_FLAG_PRIORITY: u8 = 0b1000_0000;

pub static SCREEN_WIDTH: uint = 160;
pub static SCREEN_HEIGHT: uint = 144;  // After this many rows, V-Blank starts

pub struct Video {
  // Implementation state
  cycles: u32, // internal cycle count, wraps around at SCREEN_REFRESH_CYCLES

  mode: u8,  // LCD mode (0-3)
  dma: u8,   // DMA request, 0xff = no request, 0x00-0xf1 = requested base address

  // Registers
  flags: u8, // LCDC register
  stat: u8,  // STAT register

  ly: u8,    // LY register
  lyc: u8,   // LYC register

  scx: u8,   // SCX register
  scy: u8,   // SCY register
  wx: u8,    // WX register
  wy: u8,    // WY register

  bgp: u8,   // BGP register
  obp0: u8,  // OBP0 register
  obp1: u8,  // OBP1 register

  // Memory
  vram: [u8, ..0x2000],
  oam: [u8, ..0xa0],

  // Screen buffer
  screen: [u8, ..SCREEN_WIDTH*SCREEN_HEIGHT*4],
}

pub enum Signal {
  VBlank,
  LCD,
  DMA(u8),
}

impl Video {
  pub fn new() -> Video {
    Video {
      cycles: 0,
      mode: 0,
      dma: 0xff,
      flags: 0x91,
      stat: 0,
      ly: 0,
      lyc: 0,
      scx: 0,
      scy: 0,
      wx: 0,
      wy: 0,
      bgp: 0,
      obp0: 0,
      obp1: 0,
      vram: [0u8, ..0x2000],
      oam: [0u8, ..0xa0],
      screen: [0u8, ..SCREEN_WIDTH*SCREEN_HEIGHT*4], // BGRA
    }
  }

  pub fn tick(&mut self, cycles: u8) -> Option<Signal> {
    let old_ly = self.ly;
    let old_mode = self.mode;

    self.cycles = (self.cycles + cycles as u32) % SCREEN_REFRESH_CYCLES;
    self.ly = (self.cycles / ROW_CYCLES) as u8;

    self.mode =
      if self.ly < SCREEN_HEIGHT as u8 {
        match self.cycles % ROW_CYCLES {
          MODE0_START .. (MODE2_START - 1) => 0,
          MODE2_START .. (MODE3_START - 1) => 2,
          _                                => 3,
        }
      } else {
        1 // V-Blank
      };

    self.update_stat();

    if self.dma != 0xff {
      let base = self.dma;
      self.dma = 0xff;
      return Some(DMA(base));
    }

    // TODO: Implement behavior according to http://gameboy.mongenel.com/dmg/istat98.txt

    if self.mode != old_mode {
      if self.mode == 1 {
        return Some(VBlank) // TODO: Also generate LCD interrupt if STAT_MODE1_IRQ is set
      }

      if self.mode == 0 {
        // H-Blank
        self.draw_row(self.ly as uint);
      }

      match self.mode {
        0 => if self.stat & STAT_MODE0_IRQ != 0 { return Some(LCD) },
        1 => if self.stat & STAT_MODE1_IRQ != 0 { return Some(LCD) },
        2 => if self.stat & STAT_MODE2_IRQ != 0 { return Some(LCD) },
        _ => (),
      }
    }

    if self.ly != old_ly &&
       (self.stat & STAT_COINCIDENCE_IRQ != 0) &&
       (self.stat & STAT_COINCIDENCE_FLAG != 0) {
      return Some(LCD);
    }

    None
  }

  fn update_stat(&mut self) {
    // Coincidence flag
    if self.ly == self.lyc {
      self.stat |= STAT_COINCIDENCE_FLAG;
    } else {
      self.stat &= !STAT_COINCIDENCE_FLAG;
    }
    // Mode flag
    self.stat = (self.stat & (STAT_IRQ_MASK | STAT_COINCIDENCE_FLAG)) | self.mode;
  }

  fn draw_row(&mut self, row: uint) {
    if (self.flags & FLAG_ENABLE_BG_WIN) != 0 {
      self.draw_bg_row(row);
    }

    if (self.flags & FLAG_ENABLE_OBJ) != 0 {
      self.draw_obj_row(row);
    }
  }

  fn draw_bg_row(&mut self, row: uint) {
    let mut tiles_base = TILES_BASE0;
    let mut tiles_bias = TILES_BIAS0;
    let mut map_base = BG_WIN_MAP_BASE0;

    if (self.flags & FLAG_BG_WIN_TILES) != 0 {
      tiles_base = TILES_BASE1;
      tiles_bias = TILES_BIAS1;
    }
    if (self.flags & FLAG_BG_MAP) != 0 {
      map_base = BG_WIN_MAP_BASE1;
    }

    let pal = self.bgp;

    let y = row;
    let bg_y = (y + self.scy as uint) % (BG_HEIGHT_TILES * TILE_HEIGHT);
    let bg_tile_y = bg_y / TILE_HEIGHT;
    for x in range(0u, SCREEN_WIDTH) {
      let bg_x = (x + self.scx as uint) % (BG_WIDTH_TILES * TILE_WIDTH);
      let bg_tile_x = bg_x / TILE_WIDTH;
      let tile_num = (self.vram[map_base + bg_tile_y*BG_WIDTH_TILES + bg_tile_x] + tiles_bias) as uint;
      let tile = self.vram.slice_from(tiles_base + tile_num*TILE_BYTES);
      let pixel = self.screen.mut_slice_from((y * SCREEN_WIDTH + x) * 4);
      let tile_row = bg_y % TILE_HEIGHT;
      let tile_col = bg_x % TILE_WIDTH;
      unpack_tile_pixel(tile, pal, tile_row, tile_col, pixel, false);
    }
  }

  fn draw_obj_row(&mut self, row: uint) {
    // TODO: 8x16 obj support

    // Obj with coordinates (OFFSET_X, OFFSET_Y) is at (0, 0) on screen
    static OFFSET_X: uint = 8;
    static OFFSET_Y: uint = 16;

    static MAX_OBJS_PER_ROW: uint = 10;

    // Find objs in this row
    let mut objs = ~[];
    for obj_num in range(0, 40) {
      let obj_y = self.oam[obj_num * 4] as uint;
      if obj_y <= row + OFFSET_Y && row + OFFSET_Y < obj_y + TILE_HEIGHT {
        objs.push(obj_num);
      }
    }

    // Early out when no objs in this row
    if objs.len() == 0 {
      return;
    }

    // TODO: Restrict to 10 objs by priority

    // Sort by X coordinate, high to low
    objs.sort_by(|obj0, obj1|
      self.oam[(*obj1)*4 + 1].cmp(&self.oam[(*obj0)*4 + 1])
    );

    // Draw objs
    for obj in objs.iter() {
      let obj_y =     self.oam[(*obj) * 4] as uint;
      let obj_x =     self.oam[(*obj) * 4 + 1] as uint;
      let obj_tile =  self.oam[(*obj) * 4 + 2] as uint;
      let obj_flags = self.oam[(*obj) * 4 + 3];
      let tile = self.vram.slice_from(TILES_BASE1 + obj_tile*TILE_BYTES);
      let mut tile_row = row - obj_y + OFFSET_Y;
      if (obj_flags & OBJ_FLAG_FLIP_Y) != 0 {
        tile_row = TILE_HEIGHT - 1 - tile_row;
      }
      for tile_col in range(0, TILE_WIDTH) {
        let x = obj_x + tile_col;
        if OFFSET_X <= x && x < SCREEN_WIDTH + OFFSET_X {
          let pixel = self.screen.mut_slice_from((row * SCREEN_WIDTH + x - OFFSET_X) * 4);
          if (obj_flags & OBJ_FLAG_FLIP_X) != 0 {
            tile_col = TILE_WIDTH - 1 - tile_col;
          }
          unpack_tile_pixel(tile, self.obp0, tile_row, tile_col, pixel, true);
        }
      }
    }
  }
}

impl mem::Mem for Video {
  fn loadb(&mut self, addr: u16) -> u8 {
    match addr {
      0x8000..0x9fff => self.vram[addr - 0x8000],
      0xfe00..0xfe9f => self.oam[addr - 0xfe00], // OAM

      // I/O registers
      0xff40 => self.flags,
      0xff41 => self.stat,
      0xff42 => self.scy,
      0xff43 => self.scx,
      0xff44 => self.ly,
      0xff45 => self.lyc,
      0xff46 => 0xff, // Write-only DMA register
      0xff47 => self.bgp,
      0xff48 => self.obp0,
      0xff49 => self.obp1,
      0xff4a => self.wy,
      0xff4b => self.wx,
      _ => fail!("invalid video address: ${:04X}", addr),
    }
  }

  fn storeb(&mut self, addr: u16, val: u8) {
    match addr {
      0x8000..0x9fff => self.vram[addr - 0x8000] = val,
      0xfe00..0xfe9f => self.oam[addr - 0xfe00] = val,

      // I/O registers
      0xff40 => self.flags = val,
      0xff41 => self.stat = (val & STAT_IRQ_MASK) | (self.stat & !STAT_IRQ_MASK), // Only interrupt enable bits are writeable
      0xff42 => self.scy = val,
      0xff43 => self.scx = val,
      0xff44 => (), // Read-only LY register
      0xff45 => self.lyc = val, // TODO: update_stat()?
      0xff46 => if val <= 0xf1 { self.dma = val },
      0xff47 => self.bgp = val,
      0xff48 => self.obp0 = val,
      0xff49 => self.obp1 = val,
      0xff4a => self.wy = val,
      0xff4b => self.wx = val,
      _ => fail!("invalid video address: ${:04X}", addr),
    }
  }
}

fn unpack_tile_pixel(tile: &[u8],
                     palette: u8,
                     row: uint,
                     col: uint,
                     pixel: &mut [u8],
                     transp: bool) {
  static colors: &'static [& 'static[u8]] = &[
    &[224, 248, 208],
    &[136, 192, 112],
    &[52, 104, 86],
    &[8, 24, 32]
  ];

  let low_bit  = (tile[2*row]   >> (7 - col)) & 1;
  let high_bit = (tile[2*row+1] >> (7 - col)) & 1;
  let value = (high_bit << 1) | low_bit;
  let color = (palette >> (2 * value)) & 0b11;

  if transp && color == 0 {
    return;
  }

  pixel[0] = colors[color][2];
  pixel[1] = colors[color][1];
  pixel[2] = colors[color][0];
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2017-2018 Ryan Huang
 *
 * 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 <https://www.gnu.org/licenses/>.
 */

use model::{ColorMap, FieldCoord, Model, Move, Player};

use self::Event::*;

#[derive(PartialEq)]
pub enum Event {
    Click(FieldCoord),
    Exchange,
    NewGame(ColorMap<Player>),
    Resign,
    Undo,
    Redo,
    Quit,
}

pub fn update(model: &mut Model, event: Option<Event>) -> bool {
    if event == Some(Quit) {
        return false;
    }

    match model.current_player() {
        Player::Human => {
            if let Some(event) = event {
                handle_event(model, event);
            }
        }
        Player::Computer => {
            if let Some(event) = event {
                match event {
                    Click(_) | Exchange => {}
                    _ => {
                        model.ai.stop();
                        handle_event(model, event);
                        return true;
                    }
                }
            }

            if !model.is_game_over() {
                if model.ai.is_idle() {
                    let board_list = model.board_list();
                    model.ai.think(model.board, board_list, 6);
                }
                if let Some(mv) = model.ai.try_recv() {
                    model.push_undo_state();
                    model.board.apply_move(&mv);
                    model.last_move = Some(mv);
                    model.update_outcome();
                }
            }
        }
    }
    true
}

fn handle_event(model: &mut Model, event: Event) {
    match event {
        Click(clicked) => if !model.is_game_over() {
            handle_click(model, clicked);
        },
        Exchange => if model.board.can_exchange() && !model.is_game_over() {
            model.exchanging = !model.exchanging;
            model.clear_selection();
        },
        NewGame(players) => *model = Model::new(players),
        Resign => {
            model.push_undo_state();
            model.resign();
        }
        Undo => model.undo_move(),
        Redo => model.redo_move(),
        Quit => unreachable!(),
    }
}

fn handle_click(model: &mut Model, clicked: FieldCoord) {
    match model.selected_piece {
        Some(selected) => {
            if clicked.color() != model.board.turn() || selected == clicked {
                model.clear_selection();
            } else if model.board.is_piece_on_field(&clicked) {
                model.selected_piece = Some(clicked);
            } else {
                try_move(model, Move::move_from_field(&selected, &clicked));
                model.clear_selection();
            }
        }
        None => {
            if model.exchanging && try_move(model, Move::exchange_from_field(&clicked)) {
                model.exchanging = false;
            } else if !model.exchanging
                && clicked.color() == model.board.turn()
                && model.board.is_piece_on_field(&clicked)
            {
                model.selected_piece = Some(clicked);
            }
        }
    }
}

fn try_move(model: &mut Model, mv: Move) -> bool {
    if model.board.can_apply_move(&mv) {
        model.push_undo_state();
        model.board.apply_move(&mv);
        model.last_move = Some(mv);
        model.update_outcome();
        true
    } else {
        false
    }
}
<commit_msg>Pass Event by reference (per Clippy suggestion)<commit_after>/*
 * Copyright (C) 2017-2018 Ryan Huang
 *
 * 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 <https://www.gnu.org/licenses/>.
 */

use model::{ColorMap, FieldCoord, Model, Move, Player};

use self::Event::*;

#[derive(PartialEq)]
pub enum Event {
    Click(FieldCoord),
    Exchange,
    NewGame(ColorMap<Player>),
    Resign,
    Undo,
    Redo,
    Quit,
}

pub fn update(model: &mut Model, event: Option<Event>) -> bool {
    if event == Some(Quit) {
        return false;
    }

    match model.current_player() {
        Player::Human => {
            if let Some(event) = event {
                handle_event(model, &event);
            }
        }
        Player::Computer => {
            if let Some(event) = event {
                match event {
                    Click(_) | Exchange => {}
                    _ => {
                        model.ai.stop();
                        handle_event(model, &event);
                        return true;
                    }
                }
            }

            if !model.is_game_over() {
                if model.ai.is_idle() {
                    let board_list = model.board_list();
                    model.ai.think(model.board, board_list, 6);
                }
                if let Some(mv) = model.ai.try_recv() {
                    model.push_undo_state();
                    model.board.apply_move(&mv);
                    model.last_move = Some(mv);
                    model.update_outcome();
                }
            }
        }
    }
    true
}

fn handle_event(model: &mut Model, event: &Event) {
    match event {
        Click(clicked) => if !model.is_game_over() {
            handle_click(model, *clicked);
        },
        Exchange => if model.board.can_exchange() && !model.is_game_over() {
            model.exchanging = !model.exchanging;
            model.clear_selection();
        },
        NewGame(players) => *model = Model::new(*players),
        Resign => {
            model.push_undo_state();
            model.resign();
        }
        Undo => model.undo_move(),
        Redo => model.redo_move(),
        Quit => unreachable!(),
    }
}

fn handle_click(model: &mut Model, clicked: FieldCoord) {
    match model.selected_piece {
        Some(selected) => {
            if clicked.color() != model.board.turn() || selected == clicked {
                model.clear_selection();
            } else if model.board.is_piece_on_field(&clicked) {
                model.selected_piece = Some(clicked);
            } else {
                try_move(model, Move::move_from_field(&selected, &clicked));
                model.clear_selection();
            }
        }
        None => {
            if model.exchanging && try_move(model, Move::exchange_from_field(&clicked)) {
                model.exchanging = false;
            } else if !model.exchanging
                && clicked.color() == model.board.turn()
                && model.board.is_piece_on_field(&clicked)
            {
                model.selected_piece = Some(clicked);
            }
        }
    }
}

fn try_move(model: &mut Model, mv: Move) -> bool {
    if model.board.can_apply_move(&mv) {
        model.push_undo_state();
        model.board.apply_move(&mv);
        model.last_move = Some(mv);
        model.update_outcome();
        true
    } else {
        false
    }
}
<|endoftext|>


<commit_before>//
// Copyright (c) 2016, Boris Popov <popov@whitekefir.ru>
//
// 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 logger::Logger;
use program::{NAME, VERSION};
use env::Env;
use mounter::Mounter;
use executor::Executor;

pub fn main() {
    let logger = Logger::new(NAME);
    let name_ver = NAME.to_string() + "-" + VERSION;
    let start = name_ver.clone() + " starting!";
    logger.info(&start);

    let mut env = Env::new();
    env.load(&logger);

    let mnt = Mounter::new();
    mnt.mount(&logger, &env);

    let ex = Executor::new();
    ex.start(&logger, &env);

    let end = name_ver.clone() + " exiting!";
    logger.info(&end);
}
<commit_msg>add mut for Mounter<commit_after>//
// Copyright (c) 2016, Boris Popov <popov@whitekefir.ru>
//
// 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 logger::Logger;
use program::{NAME, VERSION};
use env::Env;
use mounter::Mounter;
use executor::Executor;

pub fn main() {
    let logger = Logger::new(NAME);
    let name_ver = NAME.to_string() + "-" + VERSION;
    let start = name_ver.clone() + " starting!";
    logger.info(&start);

    let mut env = Env::new();
    env.load(&logger);

    let mut mnt = Mounter::new();
    mnt.mount(&logger, &env);

    let ex = Executor::new();
    ex.start(&logger, &env);

    let end = name_ver.clone() + " exiting!";
    logger.info(&end);
}
<|endoftext|>


<commit_before>// Copyright (c) 2017 Chef Software Inc. and/or applicable contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::collections::{HashMap, BinaryHeap};
use std::cmp::Ordering;
use std::str::FromStr;
use protocol::scheduler;
use petgraph::{Graph, Direction};
use petgraph::graph::{NodeIndex, EdgeIndex};
use petgraph::visit::EdgeRef;
use petgraph::algo::{is_cyclic_directed, connected_components};
use hab_core::package::PackageIdent;

use rdeps::rdeps;

#[derive(Debug)]
pub struct Stats {
    pub node_count: usize,
    pub edge_count: usize,
    pub connected_comp: usize,
    pub is_cyclic: bool,
}

#[derive(Eq)]
struct HeapEntry {
    pkg_index: usize,
    rdep_count: usize,
}

impl Ord for HeapEntry {
    fn cmp(&self, other: &HeapEntry) -> Ordering {
        self.rdep_count.cmp(&other.rdep_count)
    }
}

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

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

fn short_name(name: &str) -> String {
    let parts: Vec<&str> = name.split("/").collect();
    assert!(parts.len() >= 2);
    format!("{}/{}", parts[0], parts[1])
}

pub struct PackageGraph {
    package_max: usize,
    package_map: HashMap<String, (usize, NodeIndex)>,
    latest_map: HashMap<String, PackageIdent>,
    package_names: Vec<String>,
    graph: Graph<usize, usize>,
}

impl PackageGraph {
    pub fn new() -> Self {
        PackageGraph {
            package_max: 0,
            package_map: HashMap::new(),
            latest_map: HashMap::new(),
            package_names: Vec::new(),
            graph: Graph::<usize, usize>::new(),
        }
    }

    fn generate_id(&mut self, name: &str) -> (usize, NodeIndex) {
        let short_name = short_name(name);

        let id = if self.package_map.contains_key(&short_name) {
            let val = *self.package_map.get(&short_name).unwrap();
            val
        } else {
            self.package_names.push(short_name.clone());
            assert_eq!(self.package_names[self.package_max], short_name);

            let node_index = self.graph.add_node(self.package_max);
            self.package_map
                .insert(short_name.clone(), (self.package_max, node_index));
            self.package_max = self.package_max + 1;

            (self.package_max - 1, node_index)
        };

        id
    }

    pub fn build<T>(&mut self, packages: T) -> (usize, usize)
        where T: Iterator<Item = scheduler::Package>
    {
        assert!(self.package_max == 0);

        for p in packages {
            self.extend(&p);
        }

        (self.graph.node_count(), self.graph.edge_count())
    }

    pub fn extend(&mut self, package: &scheduler::Package) -> (usize, usize) {
        let name = format!("{}", package.get_ident());
        let (pkg_id, pkg_node) = self.generate_id(&name);

        assert_eq!(pkg_id, pkg_node.index());

        let pkg_ident = PackageIdent::from_str(&name).unwrap();
        let short_name = short_name(&name);

        let add_deps = if self.latest_map.contains_key(&short_name) {
            let skip_update = {
                let latest = self.latest_map.get(&short_name).unwrap();
                pkg_ident < *latest
            };

            if skip_update {
                false
            } else {
                let edge_ids: Vec<EdgeIndex> = self.graph
                    .edges_directed(pkg_node, Direction::Incoming)
                    .map(|e| e.id())
                    .collect();
                for edge_id in edge_ids {
                    self.graph.remove_edge(edge_id).unwrap();
                }
                self.latest_map.insert(short_name, pkg_ident.clone());
                true
            }
        } else {
            self.latest_map.insert(short_name, pkg_ident.clone());
            true
        };

        if add_deps {
            let deps = package.get_deps();
            for dep in deps {
                let depname = format!("{}", dep);
                let (_, dep_node) = self.generate_id(&depname);
                self.graph.extend_with_edges(&[(dep_node, pkg_node)]);
            }
        }

        (self.graph.node_count(), self.graph.edge_count())
    }

    pub fn rdeps(&self, name: &str) -> Option<Vec<(String, String)>> {
        let mut v: Vec<(String, String)> = Vec::new();

        match self.package_map.get(name) {
            Some(&(_, pkg_node)) => {
                match rdeps(&self.graph, pkg_node) {
                    Ok(deps) => {
                        for n in deps {
                            let name = self.package_names[n].clone();
                            let ident = format!("{}", self.latest_map.get(&name).unwrap());
                            v.push((name, ident));
                        }
                    }
                    Err(e) => panic!("Error: {:?}", e),
                }
            }
            None => return None,
        }

        Some(v)
    }

    // Mostly for debugging
    pub fn rdeps_dump(&self) {
        debug!("Reverse dependencies:");

        for (pkg_name, pkg_id) in &self.package_map {
            let (_, node) = *pkg_id;
            debug!("{}", pkg_name);

            match rdeps(&self.graph, node) {
                Ok(v) => {
                    for n in v {
                        debug!("|_ {}", self.package_names[n]);
                    }
                }
                Err(e) => panic!("Error: {:?}", e),
            }
        }
    }

    pub fn search(&self, phrase: &str) -> Vec<String> {
        let v: Vec<String> = self.package_names
            .iter()
            .cloned()
            .filter(|s| s.contains(phrase))
            .collect();

        v
    }

    // Given an identifier in 'origin/name' format, returns the
    // most recent version (fully-qualified package ident string)
    pub fn resolve(&self, name: &str) -> Option<String> {
        match self.latest_map.get(name) {
            Some(ident) => Some(format!("{}", ident)),
            None => None,
        }
    }

    pub fn stats(&self) -> Stats {
        Stats {
            node_count: self.graph.node_count(),
            edge_count: self.graph.edge_count(),
            connected_comp: connected_components(&self.graph),
            is_cyclic: is_cyclic_directed(&self.graph),
        }
    }

    pub fn top(&self, max: usize) -> Vec<(String, usize)> {
        let mut v = Vec::new();
        let mut heap = BinaryHeap::new();

        for (_, pkg_id) in &self.package_map {
            let (index, node) = *pkg_id;

            match rdeps(&self.graph, node) {
                Ok(v) => {
                    let he = HeapEntry {
                        pkg_index: index,
                        rdep_count: v.len(),
                    };
                    heap.push(he);
                }
                Err(e) => panic!("Error: {:?}", e),
            }
        }

        let mut i = 0;
        while (i < max) && !heap.is_empty() {
            let he = heap.pop().unwrap();
            v.push((self.package_names[he.pkg_index].clone(), he.rdep_count));
            i = i + 1;
        }

        v
    }
}
<commit_msg>Remove edges without side effects<commit_after>// Copyright (c) 2017 Chef Software Inc. and/or applicable contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::collections::{HashMap, BinaryHeap};
use std::cmp::Ordering;
use std::str::FromStr;
use protocol::scheduler;
use petgraph::{Graph, Direction};
use petgraph::graph::NodeIndex;
use petgraph::algo::{is_cyclic_directed, connected_components};
use hab_core::package::PackageIdent;

use rdeps::rdeps;

#[derive(Debug)]
pub struct Stats {
    pub node_count: usize,
    pub edge_count: usize,
    pub connected_comp: usize,
    pub is_cyclic: bool,
}

#[derive(Eq)]
struct HeapEntry {
    pkg_index: usize,
    rdep_count: usize,
}

impl Ord for HeapEntry {
    fn cmp(&self, other: &HeapEntry) -> Ordering {
        self.rdep_count.cmp(&other.rdep_count)
    }
}

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

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

fn short_name(name: &str) -> String {
    let parts: Vec<&str> = name.split("/").collect();
    assert!(parts.len() >= 2);
    format!("{}/{}", parts[0], parts[1])
}

pub struct PackageGraph {
    package_max: usize,
    package_map: HashMap<String, (usize, NodeIndex)>,
    latest_map: HashMap<String, PackageIdent>,
    package_names: Vec<String>,
    graph: Graph<usize, usize>,
}

impl PackageGraph {
    pub fn new() -> Self {
        PackageGraph {
            package_max: 0,
            package_map: HashMap::new(),
            latest_map: HashMap::new(),
            package_names: Vec::new(),
            graph: Graph::<usize, usize>::new(),
        }
    }

    fn generate_id(&mut self, name: &str) -> (usize, NodeIndex) {
        let short_name = short_name(name);

        let id = if self.package_map.contains_key(&short_name) {
            let val = *self.package_map.get(&short_name).unwrap();
            val
        } else {
            self.package_names.push(short_name.clone());
            assert_eq!(self.package_names[self.package_max], short_name);

            let node_index = self.graph.add_node(self.package_max);
            self.package_map
                .insert(short_name.clone(), (self.package_max, node_index));
            self.package_max = self.package_max + 1;

            (self.package_max - 1, node_index)
        };

        id
    }

    pub fn build<T>(&mut self, packages: T) -> (usize, usize)
        where T: Iterator<Item = scheduler::Package>
    {
        assert!(self.package_max == 0);

        for p in packages {
            self.extend(&p);
        }

        (self.graph.node_count(), self.graph.edge_count())
    }

    pub fn extend(&mut self, package: &scheduler::Package) -> (usize, usize) {
        let name = format!("{}", package.get_ident());
        let (pkg_id, pkg_node) = self.generate_id(&name);

        assert_eq!(pkg_id, pkg_node.index());

        let pkg_ident = PackageIdent::from_str(&name).unwrap();
        let short_name = short_name(&name);

        let add_deps = if self.latest_map.contains_key(&short_name) {
            let skip_update = {
                let latest = self.latest_map.get(&short_name).unwrap();
                pkg_ident < *latest
            };

            if skip_update {
                false
            } else {
                let neighbors: Vec<NodeIndex> = self.graph
                    .neighbors_directed(pkg_node, Direction::Incoming)
                    .collect();
                for n in neighbors {
                    let e = self.graph.find_edge(n, pkg_node).unwrap();
                    self.graph.remove_edge(e).unwrap();
                }
                self.latest_map.insert(short_name, pkg_ident.clone());
                true
            }
        } else {
            self.latest_map.insert(short_name, pkg_ident.clone());
            true
        };

        if add_deps {
            let deps = package.get_deps();
            for dep in deps {
                let depname = format!("{}", dep);
                let (_, dep_node) = self.generate_id(&depname);
                self.graph.extend_with_edges(&[(dep_node, pkg_node)]);
            }
        }

        (self.graph.node_count(), self.graph.edge_count())
    }

    pub fn rdeps(&self, name: &str) -> Option<Vec<(String, String)>> {
        let mut v: Vec<(String, String)> = Vec::new();

        match self.package_map.get(name) {
            Some(&(_, pkg_node)) => {
                match rdeps(&self.graph, pkg_node) {
                    Ok(deps) => {
                        for n in deps {
                            let name = self.package_names[n].clone();
                            let ident = format!("{}", self.latest_map.get(&name).unwrap());
                            v.push((name, ident));
                        }
                    }
                    Err(e) => panic!("Error: {:?}", e),
                }
            }
            None => return None,
        }

        Some(v)
    }

    // Mostly for debugging
    pub fn rdeps_dump(&self) {
        debug!("Reverse dependencies:");

        for (pkg_name, pkg_id) in &self.package_map {
            let (_, node) = *pkg_id;
            debug!("{}", pkg_name);

            match rdeps(&self.graph, node) {
                Ok(v) => {
                    for n in v {
                        debug!("|_ {}", self.package_names[n]);
                    }
                }
                Err(e) => panic!("Error: {:?}", e),
            }
        }
    }

    pub fn search(&self, phrase: &str) -> Vec<String> {
        let v: Vec<String> = self.package_names
            .iter()
            .cloned()
            .filter(|s| s.contains(phrase))
            .collect();

        v
    }

    // Given an identifier in 'origin/name' format, returns the
    // most recent version (fully-qualified package ident string)
    pub fn resolve(&self, name: &str) -> Option<String> {
        match self.latest_map.get(name) {
            Some(ident) => Some(format!("{}", ident)),
            None => None,
        }
    }

    pub fn stats(&self) -> Stats {
        Stats {
            node_count: self.graph.node_count(),
            edge_count: self.graph.edge_count(),
            connected_comp: connected_components(&self.graph),
            is_cyclic: is_cyclic_directed(&self.graph),
        }
    }

    pub fn top(&self, max: usize) -> Vec<(String, usize)> {
        let mut v = Vec::new();
        let mut heap = BinaryHeap::new();

        for (_, pkg_id) in &self.package_map {
            let (index, node) = *pkg_id;

            match rdeps(&self.graph, node) {
                Ok(v) => {
                    let he = HeapEntry {
                        pkg_index: index,
                        rdep_count: v.len(),
                    };
                    heap.push(he);
                }
                Err(e) => panic!("Error: {:?}", e),
            }
        }

        let mut i = 0;
        while (i < max) && !heap.is_empty() {
            let he = heap.pop().unwrap();
            v.push((self.package_names[he.pkg_index].clone(), he.rdep_count));
            i = i + 1;
        }

        v
    }
}
<|endoftext|>


<commit_before>use core::fmt;

bitflags! {
    pub flags Flags: usize {
        /// Protected Mode Enable
        ///
        /// If 1, system is in protected mode. Otherwise, the system is in real
        /// mode.
        const PE = 1 << 0
      , /// Monitor co-processor
        ///
        /// Controls interaction of `WAIT`/`FWAIT` instructions with `TS` flag
        /// in `%cr0`.
        const MP = 1 << 1
      , /// FPU Emulation
        ///
        /// If set, no x87 floating point unit present, if clear, x87 FPU
        /// present.
        const EM = 1 << 2
      , /// Task Switched
        ///
        /// Allows saving x87 task context upon a task switch only after x87
        /// instruction used.
        const TS = 1 << 3
      , /// Extension Type
        ///
        /// On a 386 CPU, indicated whether the math coprocessor was an 80287
        /// or an 80387.
        const ET = 1 << 4
      , /// Numeric Error
        ///
        /// Enable internal x87 floating point error reporting when set, else
        /// enables PC style x87 error detection.
        const NE = 1 << 5
      , /// Write Protect
        ///
        /// When set, the CPU can't write to read-only pages when privilege
        /// level is 0.
        const WP = 1 << 16
      , /// Alignment Mask
        ///
        /// Alignment check enabled if `AM` set, `AC` flag (in `%eflags`
        /// register) set, and privilege level is 3
        const AM = 1 << 18
      , /// Not Write-Through
        ///
        /// Globally enables/disable write-through caching
        const NW = 1 << 29
      , /// Cache Disable
        ///
        /// Globally enables/disable the memory cache
        const CD = 1 << 30
      , /// Paging
        ///
        /// If 1, enable paging and use the `%cr3` register, else disable
        /// paging.
        const PG = 1 << 31
    }
}

/// Set the write protect bit in `%cr0`.
pub fn enable_write_protect() {
    let mut flags: Flags = read();
    if !flags.contains(WP) {
        flags.insert(WP);
        unsafe { write(flags.bits) }
    }
}

/// Unset the write protect bit in `%cr0`.
pub fn disable_write_protect() {
    let mut flags: Flags = read();
    if flags.contains(WP) {
        flags.remove(WP);
        unsafe { write(flags.bits) }
    }
}

/// Set the paging bit in `%cr0`.
pub fn enable_paging() {
    let mut flags: Flags = read();
    if !flags.contains(WP) {
        flags.insert(WP);
        unsafe { write(flags.bits) }
    }
}

/// Unset the paging bit in `%cr0`.
pub fn disable_paging() {
    let mut flags: Flags = read();
    if flags.contains(PG) {
        flags.remove(PG);
        unsafe { write(flags.bits) }
    }
}

/// Read the current value from `%cr0`.
pub fn read() -> Flags {
    let result: usize;
    unsafe {
        asm!(   "mov $0, cr0"
            :   "=r"(result)
            ::: "intel" );
    }
    Flags { bits: result }
}

/// Write a value to `%cr0`.
///
/// # Unsafe Because:
///  - Control registers should generally not be modified during normal
///    operation.
pub unsafe fn write(flags: Flags) {
    asm!(  "mov cr0, $0"
        :: "r"(flags.bits)
        :: "intel");
}

impl fmt::LowerHex for Flags {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{:#08x}", self.bits)
    }
}
<commit_msg>[x86_all] we can write flags now<commit_after>use core::fmt;

bitflags! {
    pub flags Flags: usize {
        /// Protected Mode Enable
        ///
        /// If 1, system is in protected mode. Otherwise, the system is in real
        /// mode.
        const PE = 1 << 0
      , /// Monitor co-processor
        ///
        /// Controls interaction of `WAIT`/`FWAIT` instructions with `TS` flag
        /// in `%cr0`.
        const MP = 1 << 1
      , /// FPU Emulation
        ///
        /// If set, no x87 floating point unit present, if clear, x87 FPU
        /// present.
        const EM = 1 << 2
      , /// Task Switched
        ///
        /// Allows saving x87 task context upon a task switch only after x87
        /// instruction used.
        const TS = 1 << 3
      , /// Extension Type
        ///
        /// On a 386 CPU, indicated whether the math coprocessor was an 80287
        /// or an 80387.
        const ET = 1 << 4
      , /// Numeric Error
        ///
        /// Enable internal x87 floating point error reporting when set, else
        /// enables PC style x87 error detection.
        const NE = 1 << 5
      , /// Write Protect
        ///
        /// When set, the CPU can't write to read-only pages when privilege
        /// level is 0.
        const WP = 1 << 16
      , /// Alignment Mask
        ///
        /// Alignment check enabled if `AM` set, `AC` flag (in `%eflags`
        /// register) set, and privilege level is 3
        const AM = 1 << 18
      , /// Not Write-Through
        ///
        /// Globally enables/disable write-through caching
        const NW = 1 << 29
      , /// Cache Disable
        ///
        /// Globally enables/disable the memory cache
        const CD = 1 << 30
      , /// Paging
        ///
        /// If 1, enable paging and use the `%cr3` register, else disable
        /// paging.
        const PG = 1 << 31
    }
}

/// Set the write protect bit in `%cr0`.
pub fn enable_write_protect() {
    let mut flags: Flags = read();
    if !flags.contains(WP) {
        flags.insert(WP);
        unsafe { write(flags) }
    }
}

/// Unset the write protect bit in `%cr0`.
pub fn disable_write_protect() {
    let mut flags: Flags = read();
    if flags.contains(WP) {
        flags.remove(WP);
        unsafe { write(flags) }
    }
}

/// Set the paging bit in `%cr0`.
pub fn enable_paging() {
    let mut flags: Flags = read();
    if !flags.contains(WP) {
        flags.insert(WP);
        unsafe { write(flags) }
    }
}

/// Unset the paging bit in `%cr0`.
pub fn disable_paging() {
    let mut flags: Flags = read();
    if flags.contains(PG) {
        flags.remove(PG);
        unsafe { write(flags) }
    }
}

/// Read the current value from `%cr0`.
pub fn read() -> Flags {
    let result: usize;
    unsafe {
        asm!(   "mov $0, cr0"
            :   "=r"(result)
            ::: "intel" );
    }
    Flags { bits: result }
}

/// Write a value to `%cr0`.
///
/// # Unsafe Because:
///  - Control registers should generally not be modified during normal
///    operation.
pub unsafe fn write(flags: Flags) {
    asm!(  "mov cr0, $0"
        :: "r"(flags.bits)
        :: "intel");
}

impl fmt::LowerHex for Flags {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{:#08x}", self.bits)
    }
}
<|endoftext|>


<commit_before>#![crate_type = "staticlib"]
#![allow(unused_variables)]
#![allow(non_upper_case_globals)]

use std::ptr;
use std::slice;
use std::vec::Vec;
use std::ffi::CStr;
use std::ffi::CString;
use std::os::raw::c_void;
use std::os::raw::c_char;
use std::collections::LinkedList;

#[repr(C)]
pub struct mpc_state_t
{
    pos: i64,
    row: i64,
    col: i64,
}

#[repr(C)]
pub struct mpc_ast_t
{
    tag: *mut c_char,
    contents: *mut c_char,
    state: mpc_state_t,
    children_num: i32,
    children: *const *const mpc_ast_t,
}

#[repr(C)]
pub struct vector;

//TODO map structs to C

#[repr(C)]
pub struct module
{
    name: *mut c_char,
    imports: *mut vector,
    functions: *mut vector,
    types: *mut vector,
}

#[repr(C)]
pub struct import
{
    name: *mut c_char,
    alias: *mut c_char,
    local: bool,
    w_alias: bool,
}

#[repr(C)]
pub struct function
{
    name: *mut c_char,
    public : bool,
    param_count : i32,
    params: *mut vector,
    node: *const mpc_ast_t,
}

#[repr(C)]
pub struct param
{
    p_type: symbol_type,
    name: *mut c_char,
    default: String,
    w_default: bool,
}

#[repr(C)]
pub struct symbol_type
{
    name: *mut c_char,
    indirection: i32,
}

#[repr(C)]
pub struct user_type
{
    name: *mut c_char,
    t_type: type_kind,
}

#[repr(C)]
enum type_kind
{
    ALIAS,
    STRUCT,
    ENUM,
}

impl mpc_ast_t
{
    unsafe fn get_children(&self) -> &'static[*const mpc_ast_t]
    {
        slice::from_raw_parts(self.children, self.children_num as usize)
    }
}

extern
{
    fn puts(s: *const c_char);
    fn vector_alloc() -> *mut vector;
    fn vector_init(v: *mut vector);
    fn vector_add(l: *mut vector, d: *mut c_void);
    fn vector_total(l: *mut vector, carry: i32) -> i32;
    fn vector_get(l: *mut vector, index: i32, carry: i32) -> *mut c_void;
}

const RED      :&'static str = "\x1b[31m";
const GREEN    :&'static str = "\x1b[32m";
const YELLOW   :&'static str = "\x1b[33m";
const BLUE     :&'static str = "\x1b[34m";
const MAGENTA  :&'static str = "\x1b[35m";
const CYAN     :&'static str = "\x1b[36m";
const RESET    :&'static str = "\x1b[0m";

static mut this_module: *mut module = 0 as *mut module;

#[no_mangle]
pub unsafe extern fn analyse(ast_ptr: *mut mpc_ast_t)
{
    this_module = &mut (module
        {
            name: make_string("placeholder".to_string()),
            functions: vector_alloc(),
            imports: vector_alloc(),
            types: vector_alloc(),
        }) as *mut module;

    println!("test: {} {}", CStr::from_ptr((*this_module).name).to_string_lossy().into_owned().len(),
                            read_string((*this_module).name));
    process(ast_ptr, 0);
}

pub unsafe fn process(ast_ptr: *mut mpc_ast_t, level: usize)
{

    let ast = &*ast_ptr;
    let children = slice::from_raw_parts(ast.children, ast.children_num as usize);

    for i in 0 .. ast.children_num as usize
    {
        let tag = CStr::from_ptr( (*children[i]).tag ).to_string_lossy().into_owned();
        let ref child = *children[i];

        match tag.as_ref()
        {
            "func" =>
            {
                println!("{}: {}", MAGENTA.to_string() + &"tag" + &RESET, YELLOW.to_string() + &tag + &RESET);
                read_func(children[i] as *mut mpc_ast_t);
            },
            "module" =>
            {
                println!("{}: {}", MAGENTA.to_string() + &"tag", YELLOW.to_string() + &tag + &RESET);
                println!("{} {}:", GREEN.to_string() + &"module" + &RESET,
                        CStr::from_ptr( (*child.get_children()[0]).contents ).to_string_lossy().into_owned());
            },
            "statement" => println!("statement"),
            _ => {},
        }
        process(children[i] as *mut mpc_ast_t, level + 1);
    }

}

pub unsafe fn make_string(s: String) -> *mut c_char
{
    CString::new(s).unwrap().into_raw()
}

pub unsafe fn read_string(s: *mut c_char) -> String
{
    CStr::from_ptr(s).to_string_lossy().into_owned()
}

pub unsafe fn read_func(ast_ptr: *mut mpc_ast_t)
{

}
<commit_msg>move some code from analyse.rs<commit_after>#![crate_type = "staticlib"]
#![allow(unused_variables)]
#![allow(non_upper_case_globals)]
#![allow(dead_code)]
#![allow(improper_ctypes)]

use util::*;
use types::*;
use std::slice;
use std::ffi::CStr;
use std::ffi::CString;
use std::os::raw::c_void;
use std::os::raw::c_char;

mod util;
mod types;

extern
{
    fn puts(s: *const c_char);
    fn vector_alloc() -> *mut vector;
    fn vector_init(v: *mut vector);
    fn vector_add(l: *mut vector, d: *mut c_void);
    fn vector_total(l: *mut vector) -> i32;
    fn vector_get(l: *mut vector, index: i32) -> *mut c_void;
}

static mut this_module: *mut module = 0 as *mut module;

#[no_mangle]
pub unsafe extern fn analyse(ast_ptr: *mut mpc_ast_t)
{
    this_module = &mut module::new() as *mut module;

    vector_init((*this_module).types);
    vector_init((*this_module).imports);
    vector_init((*this_module).functions);

    process(ast_ptr, 0);
}

pub unsafe fn process(ast_ptr: *mut mpc_ast_t, level: usize)
{

    let ast = &*ast_ptr;
    let children = slice::from_raw_parts(ast.children, ast.children_num as usize);

    for i in 0 .. ast.children_num as usize
    {
        let tag = CStr::from_ptr( (*children[i]).tag ).to_string_lossy().into_owned();
        let ref child = *children[i];

        match tag.as_ref()
        {
            "func" =>
            {
                println!("{}: {}", MAGENTA.to_string() + &"tag" + &RESET, YELLOW.to_string() + &tag + &RESET);
                read_func(children[i] as *mut mpc_ast_t);
            },
            "module" =>
            {
                println!("{}: {}", MAGENTA.to_string() + &"tag", YELLOW.to_string() + &tag + &RESET);
                println!("{} {}:", GREEN.to_string() + &"module" + &RESET,
                        CStr::from_ptr( (*child.get_children()[0]).contents ).to_string_lossy().into_owned());
            },
            "statement" => println!("statement"),
            _ => {},
        }
        process(children[i] as *mut mpc_ast_t, level + 1);
    }

}

pub unsafe fn make_string(s: String) -> *mut c_char
{
    CString::new(s).unwrap().into_raw()
}

pub unsafe fn read_string(s: *mut c_char) -> String
{
    CStr::from_ptr(s).to_string_lossy().into_owned()
}

pub unsafe fn read_func(ast_ptr: *mut mpc_ast_t)
{
    let ref ast = *ast_ptr;
    let mut func: function = function
    {
        name: make_string("placeholder".to_string()),
        public: false,
        param_count: 0,
        params: vector_alloc(),
        ret_type: symbol_type
        {
            name: make_string("placeholder".to_string()),
            indirection: 0,
            constant: false,
            volatile: false,
        },
    };

    if read_string((*ast.get_children()[0]).tag) == "public" { func.public = true; }

    if func.public
    {
        func.name = (*ast.get_children()[3]).contents;
        let ref child = *ast.get_children()[2];
        let tag = read_string(child.tag);
        match tag.as_ref()
        {
            "natives" =>
            {
                println!("it's a native {}", read_string(child.contents) );
                let mut return_type: symbol_type = symbol_type
                {
                    name: child.contents,
                    indirection: 0,
                    constant: false,
                    volatile: false,
                };

                return_type.name = child.contents;
                func.ret_type = return_type;
            },
            "type" =>
            {
                let mut index = 0;
                let mut is_const = false;
                let mut is_vol = false;

                if read_string( (*child.get_children()[0]).contents ) == "const"
                {
                    is_const = true;
                    index += 1;
                }
                if read_string( (*child.get_children()[0]).contents ) == "volatile"
                {
                    is_vol = true;
                    index += 1;
                }

                let mut return_type: symbol_type = symbol_type
                {
                    name: (*child.get_children()[index]).contents,
                    indirection: 0,
                    constant: is_const,
                    volatile: is_vol,
                };

                for i in 0..child.children_num as usize
                {
                    if read_string( (*child.get_children()[i]).tag ) == "ptrop"
                    || read_string( (*child.get_children()[i]).tag ) == "index"
                    {
                        return_type.indirection += 1;
                    }
                }

                func.ret_type = return_type;
            },
            _ => {}
        }
    }
    else
    {
        func.name = (*ast.get_children()[2]).contents;
        let ref child = *ast.get_children()[1];
        let tag = read_string( child.tag );
        match tag.as_ref()
        {
            "natives" =>
            {
                println!("it's a native {}", read_string(child.contents) );
                let mut return_type: symbol_type = symbol_type
                {
                    name: child.contents,
                    indirection: 0,
                    constant: false,
                    volatile: false,
                };

                return_type.name = child.contents;
                func.ret_type = return_type;
            },
            "type" =>
            {
                let mut index = 0;
                let mut is_const = false;
                let mut is_vol = false;

                if read_string( (*child.get_children()[0]).contents ) == "const"
                {
                    is_const = true;
                    index += 1;
                }
                if read_string( (*child.get_children()[0]).contents ) == "volatile"
                {
                    is_vol = true;
                    index += 1;
                }

                let mut return_type: symbol_type = symbol_type
                {
                    name: (*child.get_children()[index]).contents,
                    indirection: 0,
                    constant: is_const,
                    volatile: is_vol,
                };

                for i in 0..child.children_num as usize
                {
                    if read_string( (*child.get_children()[i]).tag ) == "ptrop"
                    || read_string( (*child.get_children()[i]).tag ) == "index"
                    {
                        return_type.indirection += 1;
                    }
                }

                func.ret_type = return_type;
            },
            _ => {}
        }
    }
    println!("{}type {} with indirection {}", if func.ret_type.constant { "constant " }
                                               else if func.ret_type.volatile { "volatile " } else { "" },
                                               read_string(func.ret_type.name), func.ret_type.indirection);
}
<|endoftext|>


<commit_before>use hir::{db::HirDatabase, ModPath};
use ra_syntax::{
    ast::{self, AstNode},
    SyntaxKind::{NAME_REF, USE_ITEM},
    SyntaxNode,
};

use crate::{
    assist_ctx::{ActionBuilder, Assist, AssistCtx},
    auto_import_text_edit, AssistId, ImportsLocator,
};

// Assist: auto_import
//
// If the name is unresolved, provides all possible imports for it.
//
// ```
// fn main() {
//     let map = HashMap<|>::new();
// }
// ```
// ->
// ```
// use std::collections::HashMap;
//
// fn main() {
//     let map = HashMap<|>::new();
// }
// ```
pub(crate) fn auto_import<F: ImportsLocator>(
    ctx: AssistCtx<impl HirDatabase>,
    imports_locator: &mut F,
) -> Option<Assist> {
    let path_to_import: ast::Path = ctx.find_node_at_offset()?;
    let path_to_import_syntax = path_to_import.syntax();
    if path_to_import_syntax.ancestors().find(|ancestor| ancestor.kind() == USE_ITEM).is_some() {
        return None;
    }
    let name_to_import =
        path_to_import_syntax.descendants().find(|child| child.kind() == NAME_REF)?;

    let module = path_to_import_syntax.ancestors().find_map(ast::Module::cast);
    let position = match module.and_then(|it| it.item_list()) {
        Some(item_list) => item_list.syntax().clone(),
        None => {
            let current_file = path_to_import_syntax.ancestors().find_map(ast::SourceFile::cast)?;
            current_file.syntax().clone()
        }
    };
    let source_analyzer = ctx.source_analyzer(&position, None);
    let module_with_name_to_import = source_analyzer.module()?;
    if source_analyzer.resolve_path(ctx.db, &path_to_import).is_some() {
        return None;
    }

    let proposed_imports = imports_locator
        .find_imports(&name_to_import.to_string())
        .into_iter()
        .filter_map(|module_def| module_with_name_to_import.find_use_path(ctx.db, module_def))
        .filter(|use_path| !use_path.segments.is_empty())
        .take(20)
        .collect::<std::collections::BTreeSet<_>>();
    if proposed_imports.is_empty() {
        return None;
    }

    ctx.add_assist_group(
        AssistId("auto_import"),
        format!("Import {}", path_to_import_syntax),
        || {
            proposed_imports
                .into_iter()
                .map(|import| import_to_action(import, &position, &path_to_import_syntax))
                .collect()
        },
    )
}

fn import_to_action(import: ModPath, position: &SyntaxNode, anchor: &SyntaxNode) -> ActionBuilder {
    let mut action_builder = ActionBuilder::default();
    action_builder.label(format!("Import `{}`", &import));
    auto_import_text_edit(position, anchor, &import, action_builder.text_edit_builder());
    action_builder
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::helpers::{
        check_assist_with_imports_locator, check_assist_with_imports_locator_not_applicable,
        TestImportsLocator,
    };

    #[test]
    fn applicable_when_found_an_import() {
        check_assist_with_imports_locator(
            auto_import,
            TestImportsLocator::new,
            r"
            <|>PubStruct

            pub mod PubMod {
                pub struct PubStruct;
            }
            ",
            r"
            <|>use PubMod::PubStruct;

            PubStruct

            pub mod PubMod {
                pub struct PubStruct;
            }
            ",
        );
    }

    #[test]
    fn auto_imports_are_merged() {
        check_assist_with_imports_locator(
            auto_import,
            TestImportsLocator::new,
            r"
            use PubMod::PubStruct1;

            struct Test {
                test: Pub<|>Struct2<u8>,
            }

            pub mod PubMod {
                pub struct PubStruct1;
                pub struct PubStruct2<T> {
                    _t: T,
                }
            }
            ",
            r"
            use PubMod::{PubStruct2, PubStruct1};

            struct Test {
                test: Pub<|>Struct2<u8>,
            }

            pub mod PubMod {
                pub struct PubStruct1;
                pub struct PubStruct2<T> {
                    _t: T,
                }
            }
            ",
        );
    }

    #[test]
    fn applicable_when_found_multiple_imports() {
        check_assist_with_imports_locator(
            auto_import,
            TestImportsLocator::new,
            r"
            PubSt<|>ruct

            pub mod PubMod1 {
                pub struct PubStruct;
            }
            pub mod PubMod2 {
                pub struct PubStruct;
            }
            pub mod PubMod3 {
                pub struct PubStruct;
            }
            ",
            r"
            use PubMod1::PubStruct;

            PubSt<|>ruct

            pub mod PubMod1 {
                pub struct PubStruct;
            }
            pub mod PubMod2 {
                pub struct PubStruct;
            }
            pub mod PubMod3 {
                pub struct PubStruct;
            }
            ",
        );
    }

    #[test]
    fn not_applicable_for_already_imported_types() {
        check_assist_with_imports_locator_not_applicable(
            auto_import,
            TestImportsLocator::new,
            r"
            use PubMod::PubStruct;

            PubStruct<|>

            pub mod PubMod {
                pub struct PubStruct;
            }
            ",
        );
    }

    #[test]
    fn not_applicable_for_types_with_private_paths() {
        check_assist_with_imports_locator_not_applicable(
            auto_import,
            TestImportsLocator::new,
            r"
            PrivateStruct<|>

            pub mod PubMod {
                struct PrivateStruct;
            }
            ",
        );
    }

    #[test]
    fn not_applicable_when_no_imports_found() {
        check_assist_with_imports_locator_not_applicable(
            auto_import,
            TestImportsLocator::new,
            "
            PubStruct<|>",
        );
    }

    #[test]
    fn not_applicable_in_import_statements() {
        check_assist_with_imports_locator_not_applicable(
            auto_import,
            TestImportsLocator::new,
            r"
            use PubStruct<|>;

            pub mod PubMod {
                pub struct PubStruct;
            }",
        );
    }

    #[test]
    fn function_import() {
        check_assist_with_imports_locator(
            auto_import,
            TestImportsLocator::new,
            r"
            test_function<|>

            pub mod PubMod {
                pub fn test_function() {};
            }
            ",
            r"
            use PubMod::test_function;

            test_function<|>

            pub mod PubMod {
                pub fn test_function() {};
            }
            ",
        );
    }
}
<commit_msg>Simplify paths searches<commit_after>use hir::{db::HirDatabase, ModPath};
use ra_syntax::{
    ast::{self, AstNode},
    SyntaxNode,
};

use crate::{
    assist_ctx::{ActionBuilder, Assist, AssistCtx},
    auto_import_text_edit, AssistId, ImportsLocator,
};

// Assist: auto_import
//
// If the name is unresolved, provides all possible imports for it.
//
// ```
// fn main() {
//     let map = HashMap<|>::new();
// }
// ```
// ->
// ```
// use std::collections::HashMap;
//
// fn main() {
//     let map = HashMap<|>::new();
// }
// ```
pub(crate) fn auto_import<F: ImportsLocator>(
    ctx: AssistCtx<impl HirDatabase>,
    imports_locator: &mut F,
) -> Option<Assist> {
    let path_to_import: ast::Path = ctx.find_node_at_offset()?;
    let path_to_import_syntax = path_to_import.syntax();
    if path_to_import_syntax.ancestors().find_map(ast::UseItem::cast).is_some() {
        return None;
    }
    let name_to_import =
        path_to_import_syntax.descendants().find_map(ast::NameRef::cast)?.syntax().to_string();

    let module = path_to_import_syntax.ancestors().find_map(ast::Module::cast);
    let position = match module.and_then(|it| it.item_list()) {
        Some(item_list) => item_list.syntax().clone(),
        None => {
            let current_file = path_to_import_syntax.ancestors().find_map(ast::SourceFile::cast)?;
            current_file.syntax().clone()
        }
    };
    let source_analyzer = ctx.source_analyzer(&position, None);
    let module_with_name_to_import = source_analyzer.module()?;
    if source_analyzer.resolve_path(ctx.db, &path_to_import).is_some() {
        return None;
    }

    let proposed_imports = imports_locator
        .find_imports(&name_to_import)
        .into_iter()
        .filter_map(|module_def| module_with_name_to_import.find_use_path(ctx.db, module_def))
        .filter(|use_path| !use_path.segments.is_empty())
        .take(20)
        .collect::<std::collections::BTreeSet<_>>();
    if proposed_imports.is_empty() {
        return None;
    }

    ctx.add_assist_group(
        AssistId("auto_import"),
        format!("Import {}", path_to_import_syntax),
        || {
            proposed_imports
                .into_iter()
                .map(|import| import_to_action(import, &position, &path_to_import_syntax))
                .collect()
        },
    )
}

fn import_to_action(import: ModPath, position: &SyntaxNode, anchor: &SyntaxNode) -> ActionBuilder {
    let mut action_builder = ActionBuilder::default();
    action_builder.label(format!("Import `{}`", &import));
    auto_import_text_edit(position, anchor, &import, action_builder.text_edit_builder());
    action_builder
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::helpers::{
        check_assist_with_imports_locator, check_assist_with_imports_locator_not_applicable,
        TestImportsLocator,
    };

    #[test]
    fn applicable_when_found_an_import() {
        check_assist_with_imports_locator(
            auto_import,
            TestImportsLocator::new,
            r"
            <|>PubStruct

            pub mod PubMod {
                pub struct PubStruct;
            }
            ",
            r"
            <|>use PubMod::PubStruct;

            PubStruct

            pub mod PubMod {
                pub struct PubStruct;
            }
            ",
        );
    }

    #[test]
    fn auto_imports_are_merged() {
        check_assist_with_imports_locator(
            auto_import,
            TestImportsLocator::new,
            r"
            use PubMod::PubStruct1;

            struct Test {
                test: Pub<|>Struct2<u8>,
            }

            pub mod PubMod {
                pub struct PubStruct1;
                pub struct PubStruct2<T> {
                    _t: T,
                }
            }
            ",
            r"
            use PubMod::{PubStruct2, PubStruct1};

            struct Test {
                test: Pub<|>Struct2<u8>,
            }

            pub mod PubMod {
                pub struct PubStruct1;
                pub struct PubStruct2<T> {
                    _t: T,
                }
            }
            ",
        );
    }

    #[test]
    fn applicable_when_found_multiple_imports() {
        check_assist_with_imports_locator(
            auto_import,
            TestImportsLocator::new,
            r"
            PubSt<|>ruct

            pub mod PubMod1 {
                pub struct PubStruct;
            }
            pub mod PubMod2 {
                pub struct PubStruct;
            }
            pub mod PubMod3 {
                pub struct PubStruct;
            }
            ",
            r"
            use PubMod1::PubStruct;

            PubSt<|>ruct

            pub mod PubMod1 {
                pub struct PubStruct;
            }
            pub mod PubMod2 {
                pub struct PubStruct;
            }
            pub mod PubMod3 {
                pub struct PubStruct;
            }
            ",
        );
    }

    #[test]
    fn not_applicable_for_already_imported_types() {
        check_assist_with_imports_locator_not_applicable(
            auto_import,
            TestImportsLocator::new,
            r"
            use PubMod::PubStruct;

            PubStruct<|>

            pub mod PubMod {
                pub struct PubStruct;
            }
            ",
        );
    }

    #[test]
    fn not_applicable_for_types_with_private_paths() {
        check_assist_with_imports_locator_not_applicable(
            auto_import,
            TestImportsLocator::new,
            r"
            PrivateStruct<|>

            pub mod PubMod {
                struct PrivateStruct;
            }
            ",
        );
    }

    #[test]
    fn not_applicable_when_no_imports_found() {
        check_assist_with_imports_locator_not_applicable(
            auto_import,
            TestImportsLocator::new,
            "
            PubStruct<|>",
        );
    }

    #[test]
    fn not_applicable_in_import_statements() {
        check_assist_with_imports_locator_not_applicable(
            auto_import,
            TestImportsLocator::new,
            r"
            use PubStruct<|>;

            pub mod PubMod {
                pub struct PubStruct;
            }",
        );
    }

    #[test]
    fn function_import() {
        check_assist_with_imports_locator(
            auto_import,
            TestImportsLocator::new,
            r"
            test_function<|>

            pub mod PubMod {
                pub fn test_function() {};
            }
            ",
            r"
            use PubMod::test_function;

            test_function<|>

            pub mod PubMod {
                pub fn test_function() {};
            }
            ",
        );
    }
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2017-2018 Ryan Huang
 *
 * 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 <https://www.gnu.org/licenses/>.
 */

use imgui_sys;

use model::{Color, FieldCoord, HexCoord};
use view::vec2::Vec2;

const SQRT_3: f32 = 1.732_050_807_568_877_f32;

// Color format is 0xaa_bb_gg_rr
const FIELD_WHITE: u32 = 0xff_cf_e4_f3;
const FIELD_BLACK: u32 = 0xff_78_85_99;
const PIECE_WHITE: u32 = 0xff_ff_ff_ff;
const PIECE_BLACK: u32 = 0xff_75_75_75;
const PIECE_OUTLINE: u32 = 0xff_00_00_00;

pub fn draw_hex(coord: &HexCoord, origin: Vec2, size: f32) {
    for i in 0..6 {
        draw_field(&coord.to_field(i), origin, size);
    }
}

fn draw_field(coord: &FieldCoord, origin: Vec2, size: f32) {
    let (v1, v2, v3) = field_vertexes(coord, origin, size);
    unsafe {
        let color = match coord.color() {
            Color::White => FIELD_WHITE,
            Color::Black => FIELD_BLACK,
        };

        let draw_list = imgui_sys::igGetWindowDrawList();
        imgui_sys::ImDrawList_AddTriangleFilled(draw_list, v1.into(), v2.into(), v3.into(), color);
    }
}

pub fn highlight_field(color: u32, coord: &FieldCoord, origin: Vec2, size: f32) {
    let (v1, v2, v3) = field_vertexes(coord, origin, size);
    unsafe {
        let draw_list = imgui_sys::igGetWindowDrawList();
        imgui_sys::ImDrawList_AddTriangleFilled(draw_list, v1.into(), v2.into(), v3.into(), color);
    }
}

pub fn highlight_field_dot(color: u32, coord: &FieldCoord, origin: Vec2, size: f32) {
    let center = field_center(coord, origin, size);
    unsafe {
        let draw_list = imgui_sys::igGetWindowDrawList();
        imgui_sys::ImDrawList_AddCircleFilled(
            draw_list,
            center.into(),
            size / (4.0 * SQRT_3),
            color,
            15,
        );
    }
}

pub fn draw_piece(coord: &FieldCoord, origin: Vec2, size: f32) {
    let (v1, v2, v3) = field_vertexes(coord, origin, size);
    let center = field_center(coord, origin, size);

    const SCALE: f32 = 0.7;

    let v1 = center + (v1 - center) * SCALE;
    let v2 = center + (v2 - center) * SCALE;
    let v3 = center + (v3 - center) * SCALE;

    // Linear equation derived by human testing and regression
    let outline_size = 0.032 * size - 0.335;

    unsafe {
        let color = match coord.color() {
            Color::White => PIECE_WHITE,
            Color::Black => PIECE_BLACK,
        };

        let draw_list = imgui_sys::igGetWindowDrawList();
        imgui_sys::ImDrawList_AddTriangleFilled(draw_list, v1.into(), v2.into(), v3.into(), color);
        imgui_sys::ImDrawList_AddTriangle(
            draw_list,
            v1.into(),
            v2.into(),
            v3.into(),
            PIECE_OUTLINE,
            outline_size,
        );
    }
}

fn field_center(coord: &FieldCoord, origin: Vec2, size: f32) -> Vec2 {
    let (v1, v2, v3) = field_vertexes(coord, origin, size);
    let center_x = (v1.x + v2.x + v3.x) / 3.0;
    let min_y = (v1.y).min(v2.y).min(v3.y);
    let center_y = match coord.color() {
        Color::White => min_y + size / SQRT_3,
        Color::Black => min_y + size / (2.0 * SQRT_3),
    };

    Vec2::new(center_x, center_y)
}

fn field_vertexes(coord: &FieldCoord, origin: Vec2, size: f32) -> (Vec2, Vec2, Vec2) {
    let center = hex_to_pixel(&coord.to_hex(), origin, size);
    let height = size * SQRT_3 / 2.0;

    let west = Vec2::new(-size, 0.0);
    let east = Vec2::new(size, 0.0);

    let northwest = Vec2::new(-size / 2.0, -height);
    let southwest = Vec2::new(-size / 2.0, height);
    let northeast = Vec2::new(size / 2.0, -height);
    let southeast = Vec2::new(size / 2.0, height);

    let v1;
    let v2;
    match coord.f() {
        0 => {
            v1 = center + northwest;
            v2 = center + northeast;
        }
        1 => {
            v1 = center + northeast;
            v2 = center + east;
        }
        2 => {
            v1 = center + east;
            v2 = center + southeast;
        }
        3 => {
            v1 = center + southeast;
            v2 = center + southwest;
        }
        4 => {
            v1 = center + southwest;
            v2 = center + west;
        }
        5 => {
            v1 = center + west;
            v2 = center + northwest;
        }
        _ => unreachable!(),
    };

    (center, v1, v2)
}

// Algorithm based on http://www.redblobgames.com/grids/hexagons/#hex-to-pixel
fn hex_to_pixel(coord: &HexCoord, origin: Vec2, size: f32) -> Vec2 {
    let x = f32::from(coord.x());
    let y = f32::from(coord.y());

    let p = Vec2::new(size * (3.0 / 2.0) * x, size * -SQRT_3 * (x / 2.0 + y));

    origin + p
}

// Algorithm based on http://www.redblobgames.com/grids/hexagons/#pixel-to-hex
pub fn pixel_to_field(p: Vec2, origin: Vec2, size: f32) -> Option<FieldCoord> {
    let v = p - origin;

    let q = v.x * (2.0 / 3.0) / size;
    let r = (-v.x - SQRT_3 * v.y) / (size * 3.0);

    round_hex_coord(q, r).and_then(|hex| {
        /*
           To find the field, we subtract the converted hex coordinates (q, r) from the rounded hex
           coordinates (hex) to get the fractional part of the coordinates. Here is a diagram of a
           single hex, with the fractional coordinates of each of its vertexes in the hex coordinate
           system:

                (-1/3, 2/3) _______ (1/3, 1/3)
                           /\     /\
                          /  \   /  \
                         /    \ /    \
            (-2/3, 1/3) (----(0,0)----) (2/3, -1/3)
                         \    / \    /
                          \  /   \  /
              (-1/3, -1/3) \/_____\/ (1/3, -2/3)

           Suppose our converted coordinates were (q, r) = (1.333, 0.583). Our rounded coordinates
           are hex = (1, 1), and so our fractional coordinates are (0.333, -0.417).

           We now define three linear equations that will split this hexagon into its six fields:

                            _______ y = x (/)
                           /\     /\
                          /  \   /  \
                         /    \ /    \
                        (------X------) y = -x/2 (-)
                         \    / \    /
                          \  /   \  /
                           \/_____\/ y = -2x (\)

           Using these three equations as linear inequalities, we can check any pair of fractional
           coordinates and find which field it is in. (As a reminder, fields are numbered clockwise
           starting from 0 at the top.)

           For our example (0.333, -0.417), we see that:
               y + x/2 >= 0, so our field is above (-), and is either 5, 0, or 1.
               y +  2x >= 0, so our field is to the right of (\), and is either 0 or 1.
               y -   x <  0, so our field is to the right of (/), and is 1.
        */
        let x_diff = q - f32::from(hex.x());
        let y_diff = r - f32::from(hex.y());
        let mut i = 0;

        if y_diff + x_diff / 2.0 >= 0.0 {
            i |= 0b100;
        }
        if y_diff + x_diff * 2.0 >= 0.0 {
            i |= 0b010;
        }
        if y_diff - x_diff >= 0.0 {
            i |= 0b001;
        }

        Some(hex.to_field(match i {
            0 => 3,
            1 => 4,
            2 => 2,
            5 => 5,
            6 => 1,
            7 => 0,
            _ => unreachable!(),
        }))
    })
}

// Algorithm from http://www.redblobgames.com/grids/hexagons/#rounding
fn round_hex_coord(x: f32, y: f32) -> Option<HexCoord> {
    let z = -x - y;

    let mut rx = x.round();
    let mut ry = y.round();
    let rz = z.round();

    let x_diff = (rx - x).abs();
    let y_diff = (ry - y).abs();
    let z_diff = (rz - z).abs();

    if x_diff > y_diff && x_diff > z_diff {
        rx = -ry - rz;
    } else if y_diff > z_diff {
        ry = -rx - rz;
    }

    HexCoord::try_new(rx as i8, ry as i8)
}
<commit_msg>Refactor field_vertexes<commit_after>/*
 * Copyright (C) 2017-2018 Ryan Huang
 *
 * 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 <https://www.gnu.org/licenses/>.
 */

use imgui_sys;

use model::{Color, FieldCoord, HexCoord};
use view::vec2::Vec2;

const SQRT_3: f32 = 1.732_050_807_568_877_f32;

// Color format is 0xaa_bb_gg_rr
const FIELD_WHITE: u32 = 0xff_cf_e4_f3;
const FIELD_BLACK: u32 = 0xff_78_85_99;
const PIECE_WHITE: u32 = 0xff_ff_ff_ff;
const PIECE_BLACK: u32 = 0xff_75_75_75;
const PIECE_OUTLINE: u32 = 0xff_00_00_00;

pub fn draw_hex(coord: &HexCoord, origin: Vec2, size: f32) {
    for i in 0..6 {
        draw_field(&coord.to_field(i), origin, size);
    }
}

fn draw_field(coord: &FieldCoord, origin: Vec2, size: f32) {
    let (v1, v2, v3) = field_vertexes(coord, origin, size);
    unsafe {
        let color = match coord.color() {
            Color::White => FIELD_WHITE,
            Color::Black => FIELD_BLACK,
        };

        let draw_list = imgui_sys::igGetWindowDrawList();
        imgui_sys::ImDrawList_AddTriangleFilled(draw_list, v1.into(), v2.into(), v3.into(), color);
    }
}

pub fn highlight_field(color: u32, coord: &FieldCoord, origin: Vec2, size: f32) {
    let (v1, v2, v3) = field_vertexes(coord, origin, size);
    unsafe {
        let draw_list = imgui_sys::igGetWindowDrawList();
        imgui_sys::ImDrawList_AddTriangleFilled(draw_list, v1.into(), v2.into(), v3.into(), color);
    }
}

pub fn highlight_field_dot(color: u32, coord: &FieldCoord, origin: Vec2, size: f32) {
    let center = field_center(coord, origin, size);
    unsafe {
        let draw_list = imgui_sys::igGetWindowDrawList();
        imgui_sys::ImDrawList_AddCircleFilled(
            draw_list,
            center.into(),
            size / (4.0 * SQRT_3),
            color,
            15,
        );
    }
}

pub fn draw_piece(coord: &FieldCoord, origin: Vec2, size: f32) {
    let (v1, v2, v3) = field_vertexes(coord, origin, size);
    let center = field_center(coord, origin, size);

    const SCALE: f32 = 0.7;

    let v1 = center + (v1 - center) * SCALE;
    let v2 = center + (v2 - center) * SCALE;
    let v3 = center + (v3 - center) * SCALE;

    // Linear equation derived by human testing and regression
    let outline_size = 0.032 * size - 0.335;

    unsafe {
        let color = match coord.color() {
            Color::White => PIECE_WHITE,
            Color::Black => PIECE_BLACK,
        };

        let draw_list = imgui_sys::igGetWindowDrawList();
        imgui_sys::ImDrawList_AddTriangleFilled(draw_list, v1.into(), v2.into(), v3.into(), color);
        imgui_sys::ImDrawList_AddTriangle(
            draw_list,
            v1.into(),
            v2.into(),
            v3.into(),
            PIECE_OUTLINE,
            outline_size,
        );
    }
}

fn field_center(coord: &FieldCoord, origin: Vec2, size: f32) -> Vec2 {
    let (v1, v2, v3) = field_vertexes(coord, origin, size);
    let center_x = (v1.x + v2.x + v3.x) / 3.0;
    let min_y = (v1.y).min(v2.y).min(v3.y);
    let center_y = match coord.color() {
        Color::White => min_y + size / SQRT_3,
        Color::Black => min_y + size / (2.0 * SQRT_3),
    };

    Vec2::new(center_x, center_y)
}

fn field_vertexes(coord: &FieldCoord, origin: Vec2, size: f32) -> (Vec2, Vec2, Vec2) {
    let center = hex_to_pixel(&coord.to_hex(), origin, size);
    let height = size * SQRT_3 / 2.0;

    let west = Vec2::new(-size, 0.0);
    let east = Vec2::new(size, 0.0);

    let northwest = Vec2::new(-size / 2.0, -height);
    let southwest = Vec2::new(-size / 2.0, height);
    let northeast = Vec2::new(size / 2.0, -height);
    let southeast = Vec2::new(size / 2.0, height);

    let (v1, v2) = match coord.f() {
        0 => (northwest, northeast),
        1 => (northeast, east),
        2 => (east, southeast),
        3 => (southeast, southwest),
        4 => (southwest, west),
        5 => (west, northwest),
        _ => unreachable!(),
    };
    (center, center + v1, center + v2)
}

// Algorithm based on http://www.redblobgames.com/grids/hexagons/#hex-to-pixel
fn hex_to_pixel(coord: &HexCoord, origin: Vec2, size: f32) -> Vec2 {
    let x = f32::from(coord.x());
    let y = f32::from(coord.y());

    let p = Vec2::new(size * (3.0 / 2.0) * x, size * -SQRT_3 * (x / 2.0 + y));

    origin + p
}

// Algorithm based on http://www.redblobgames.com/grids/hexagons/#pixel-to-hex
pub fn pixel_to_field(p: Vec2, origin: Vec2, size: f32) -> Option<FieldCoord> {
    let v = p - origin;

    let q = v.x * (2.0 / 3.0) / size;
    let r = (-v.x - SQRT_3 * v.y) / (size * 3.0);

    round_hex_coord(q, r).and_then(|hex| {
        /*
           To find the field, we subtract the converted hex coordinates (q, r) from the rounded hex
           coordinates (hex) to get the fractional part of the coordinates. Here is a diagram of a
           single hex, with the fractional coordinates of each of its vertexes in the hex coordinate
           system:

                (-1/3, 2/3) _______ (1/3, 1/3)
                           /\     /\
                          /  \   /  \
                         /    \ /    \
            (-2/3, 1/3) (----(0,0)----) (2/3, -1/3)
                         \    / \    /
                          \  /   \  /
              (-1/3, -1/3) \/_____\/ (1/3, -2/3)

           Suppose our converted coordinates were (q, r) = (1.333, 0.583). Our rounded coordinates
           are hex = (1, 1), and so our fractional coordinates are (0.333, -0.417).

           We now define three linear equations that will split this hexagon into its six fields:

                            _______ y = x (/)
                           /\     /\
                          /  \   /  \
                         /    \ /    \
                        (------X------) y = -x/2 (-)
                         \    / \    /
                          \  /   \  /
                           \/_____\/ y = -2x (\)

           Using these three equations as linear inequalities, we can check any pair of fractional
           coordinates and find which field it is in. (As a reminder, fields are numbered clockwise
           starting from 0 at the top.)

           For our example (0.333, -0.417), we see that:
               y + x/2 >= 0, so our field is above (-), and is either 5, 0, or 1.
               y +  2x >= 0, so our field is to the right of (\), and is either 0 or 1.
               y -   x <  0, so our field is to the right of (/), and is 1.
        */
        let x_diff = q - f32::from(hex.x());
        let y_diff = r - f32::from(hex.y());
        let mut i = 0;

        if y_diff + x_diff / 2.0 >= 0.0 {
            i |= 0b100;
        }
        if y_diff + x_diff * 2.0 >= 0.0 {
            i |= 0b010;
        }
        if y_diff - x_diff >= 0.0 {
            i |= 0b001;
        }

        Some(hex.to_field(match i {
            0 => 3,
            1 => 4,
            2 => 2,
            5 => 5,
            6 => 1,
            7 => 0,
            _ => unreachable!(),
        }))
    })
}

// Algorithm from http://www.redblobgames.com/grids/hexagons/#rounding
fn round_hex_coord(x: f32, y: f32) -> Option<HexCoord> {
    let z = -x - y;

    let mut rx = x.round();
    let mut ry = y.round();
    let rz = z.round();

    let x_diff = (rx - x).abs();
    let y_diff = (ry - y).abs();
    let z_diff = (rz - z).abs();

    if x_diff > y_diff && x_diff > z_diff {
        rx = -ry - rz;
    } else if y_diff > z_diff {
        ry = -rx - rz;
    }

    HexCoord::try_new(rx as i8, ry as i8)
}
<|endoftext|>