board.rs 5.6 KB

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