board.rs 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. // Board control and interaction. The view & controller.
  2. //
  3. extern crate pancurses;
  4. use self::pancurses::{
  5. initscr, endwin, noecho, has_colors, start_color, init_pair,
  6. Window,
  7. COLOR_GREEN, COLOR_BLACK
  8. };
  9. extern crate rand;
  10. use self::rand::Rng;
  11. use self::rand::prelude::SliceRandom;
  12. use std::{thread, time};
  13. use config::{Config};
  14. use util::{copy_shuffle};
  15. use grid::Grid;
  16. mod board {}
  17. const BORDER_HEX_WIDTH: usize = 6;
  18. // Different actions are printed to the console at different speeds: the
  19. // "terminal" displaying output, vs. the player "typing", for example.
  20. const TERMINAL_PRINTING_SPEED: time::Duration = time::Duration::from_millis(20);
  21. const TYPING_SPEED: time::Duration = time::Duration::from_millis(70);
  22. // Everything displayed to the player, + a reference to the curses Window.
  23. #[derive(Debug)]
  24. pub struct Screen {
  25. pub w: Window,
  26. attempts_left: u8,
  27. board: Grid
  28. }
  29. impl Screen {
  30. pub fn new(conf: &Config) -> Screen {
  31. let window = initscr();
  32. noecho();
  33. window_enable_colors();
  34. let mut board = Grid::new(conf.rows, conf.cols);
  35. board.fill_with_garbage();
  36. Screen {
  37. w: window,
  38. attempts_left: 4,
  39. board: board
  40. }
  41. }
  42. pub fn intro(&self) {
  43. self.w.clear();
  44. thread::sleep(time::Duration::from_millis(250));
  45. self.print_with_delay(0, String::from("WELCOME TO ROBCO INDUSTRIES (TM) TERMLINK"), true, TERMINAL_PRINTING_SPEED);
  46. self.move_cursor_to(1, 0);
  47. thread::sleep(time::Duration::from_millis(30));
  48. // TODO the rest of the intro
  49. }
  50. pub fn initialize_game(&mut self, conf: Config) {
  51. self.print_with_delay(0, String::from("ROBCO INDUSTRIES (TM) TERMLINK PROTOCOL"), false, TERMINAL_PRINTING_SPEED);
  52. self.print_with_delay(1, String::from("ENTER PASSWORD NOW"), false, TERMINAL_PRINTING_SPEED);
  53. self.print_with_delay(
  54. 3,
  55. self.build_attempts_display(),
  56. false,
  57. TERMINAL_PRINTING_SPEED
  58. );
  59. let shuffled_words = copy_shuffle(&conf.words);
  60. let words = &shuffled_words[..conf.choose];
  61. let coord = self.board.randomly_add_words(words);
  62. // The board of words + garbage characters has been built: now the screen must be displayed.
  63. // Take the board
  64. let mut screen_buffer = Grid::new(Some(self.board.rows), Some(self.board.cols + (2 * BORDER_HEX_WIDTH) + 3));
  65. let border_hex = border_hex_gen(self.board.rows, self.board.cols);
  66. self.add_border_hex_to_grid(&mut screen_buffer, &border_hex);
  67. self.add_board_to_grid(&mut screen_buffer);
  68. // Debugging
  69. endwin();
  70. trace!("Words:\n{}", self.board.dump());
  71. trace!("Hex:\n{}", screen_buffer.dump());
  72. }
  73. pub fn end_window(&self) {
  74. endwin();
  75. }
  76. fn move_cursor_to(&self, y: i32, x: i32) {
  77. self.w.mv(y, x);
  78. self.w.refresh();
  79. }
  80. fn print_with_delay(&self, line: i32, string: String, skippable: bool, delay: time::Duration) {
  81. self.w.mv(line, 0);
  82. for c in string.chars() {
  83. self.w.addch(c);
  84. self.w.refresh();
  85. if skippable {
  86. // TODO keyboard skip
  87. }
  88. thread::sleep(delay);
  89. }
  90. }
  91. fn build_attempts_display(&self) -> String {
  92. format!("{} ATTEMPT(S) LEFT:{}", self.attempts_left, " *".repeat(self.attempts_left as usize))
  93. }
  94. fn add_border_hex_to_grid(&self, grid: &mut Grid, hex: &Vec<u32>) {
  95. let left_side = (0..grid.rows).zip(hex.iter());
  96. let right_side = (0..grid.rows).zip(hex[grid.rows..].iter());
  97. trace!("Left side from 0..{}, right side from {}..{}", grid.rows, grid.rows, hex.len());
  98. for (row, hex_val) in left_side {
  99. trace!("Printing 0x{:x} at {}, 0", hex_val, row);
  100. grid.set_rowcol_str(row, 0, &format!("0x{:x}", hex_val));
  101. }
  102. // Half the board, plus characters for the first hex values, plus 2 spaces of padding.
  103. let right_hand_offset = grid.cols / 2;
  104. for (row, hex_val) in right_side {
  105. trace!("Printing 0x{:x} at {}, {}", hex_val, row, right_hand_offset);
  106. grid.set_rowcol_str(row, right_hand_offset, &format!("0x{:x}", hex_val));
  107. }
  108. }
  109. fn add_board_to_grid(&self, grid: &mut Grid) {
  110. // TODO
  111. // The board is pressed to the right of each column
  112. // Where to start placing the board on each row: (grid.cols / 2) - (self.board.cols / 2)
  113. }
  114. }
  115. fn window_enable_colors() {
  116. if has_colors() {
  117. start_color();
  118. init_pair(1, COLOR_GREEN, COLOR_BLACK);
  119. }
  120. }
  121. fn border_hex_gen(rows: usize, cols: usize) -> Vec<u32> {
  122. // Build the hex values, printed alongside the text and garbage.
  123. // The vector will be of length rows * 2
  124. // TODO: this would make a nice generator, once those are stable.
  125. let mut rng = rand::thread_rng();
  126. let mut hex = rng.gen_range(0,200) + 63744;
  127. let mut ls = Vec::with_capacity(rows * 2);
  128. for _ in 0..rows * 2 {
  129. ls.push(hex);
  130. hex += (cols / 2) as u32;
  131. }
  132. ls
  133. }