broderclocks.js

  1. /**
  2. * BORDERCLOCKS: Manages the ClockInferenceEngine and ClockUtil classes and communicates with the keyboard application. Handles switch-press events and makes selection decisions based on the clock probabilities.
  3. * @module broderclocks
  4. */
  5. import * as cie from './clock_inference_engine.js';
  6. import * as config from './config.js';
  7. /**
  8. * Manages the ClockInferenceEngine and ClockUtil classes and communicates with the keyboard application. Handles switch-press events and makes selection decisions based on the clock probabilities.
  9. * @param {Keyboard} parent - the parent class, usually an instance of the Keyboard class
  10. * @property {ClockInference} clock_inf - The instance of the `ClockInference` class.
  11. * @property {Array<number>} abs_click_times - An array of the 'absolute' click times from the user leading up to the next selection.
  12. * Absolute click times are UNIX timestamps (ms since Epoch). `abs_click_times` are added as the user clicks.
  13. * @property {Array<number>} rel_click_times - An array of the 'relative' click times from the user leading up to the next selection.
  14. * Relative click times are the time in ms that the user clicked relative to when the clock they selected was at noon.
  15. * `rel_click_times` are added in `ClockInferenceEngine.learn_scores()` following `config.learn_delay` rounds after a selection is made.
  16. * @property {number} latest_time - The last time that the screen was updated as a UNIX timestamps (ms since Epoch). Updated in `ClockUtil.increment()`.
  17. * Used to account for the framerate when the user clicks.
  18. */
  19. export class BroderClocks {
  20. constructor(parent) {
  21. this.parent = parent;
  22. this.parent.bc_init = true;
  23. this.clock_inf = new cie.ClockInference(this.parent, this, this.parent.prev_data);
  24. this.is_undo = false;
  25. this.is_equalize = false;
  26. this.latest_time = Date.now() / 1000;
  27. this.abs_click_times = [];
  28. this.rel_click_times = [];
  29. this.time_rotate = this.parent.time_rotate;
  30. this.clock_inf.clock_util.change_period(this.time_rotate);
  31. }
  32. /**
  33. * triggered by a switch-press event in the main keyboard class. Updates the clock posteriors and the histogram
  34. * given the information in the new click.
  35. * @param {float} time_in - The epoch-timestamp in ms that the user clicked their switch.
  36. */
  37. select(time_in) {
  38. this.clock_inf.update_scores(time_in - this.latest_time);
  39. if (config.is_learning) {
  40. this.clock_inf.update_history(time_in - this.latest_time);
  41. }
  42. // store the absolute click time
  43. this.abs_click_times.push(time_in);
  44. if (this.clock_inf.is_winner() && !this.parent.in_tutorial) {
  45. this.clock_inf.win_history[0] = this.clock_inf.sorted_inds[0];
  46. this.clock_inf.entropy.update_bits();
  47. this.parent.make_choice(this.clock_inf.sorted_inds[0]);
  48. } else {
  49. this.init_round(false, false, []);
  50. }
  51. }
  52. /**
  53. * Continues the select process after the word cache promise has loaded
  54. * @param results {Object} - Object with 5 parameters specifying the setup for the new round:
  55. * @param {Array<number>} results.words_on - the clocks turned on given the new word predictions
  56. * @param {Array<number>} results.words_off - the clocks turned off given the new word predictions
  57. * @param {Array<number>} results.word_score_prior - the prior distribution for the clocks_on given the new lm results
  58. * @param {Boolean} results.is_undo - whether the undo clock was selected in the last selection round
  59. * @param {Boolean} results.is_equalize - ? holdover from an obsolete corrective character
  60. * @param {Boolean} results.skip_hist - ? need to figure this out
  61. */
  62. continue_select(results) {
  63. this.clock_inf.clocks_on = results.words_on;
  64. this.clock_inf.clocks_off = results.words_off;
  65. var clock_score_prior = results.word_score_prior;
  66. this.is_undo = results.is_undo;
  67. this.is_equalize = results.is_equalize;
  68. var skip_hist = results.skip_hist;
  69. if (skip_hist) {
  70. this.init_round(true, true, clock_score_prior);
  71. } else {
  72. if (!this.parent.in_tutorial) {
  73. this.clock_inf.learn_scores(this.is_undo);
  74. }
  75. this.init_round(true, false, clock_score_prior);
  76. }
  77. }
  78. /**
  79. * re-initializes variables at the beginning of a new selection round
  80. */
  81. init_bits() {
  82. this.bits_per_select = Math.log(this.clock_inf.clocks_on.length) / Math.log(2);
  83. this.num_bits = 0;
  84. }
  85. /**
  86. * re-initializes variables at the beginning of a new selection round.
  87. * @param {Array<number>} clock_score_prior - the prior probabilities from the language model of the clocks currently on.
  88. */
  89. init_follow_up(clock_score_prior) {
  90. this.init_round(false, false, clock_score_prior);
  91. this.clock_inf.clock_history = [[]];
  92. this.clock_inf.win_history = [-1];
  93. this.init_bits();
  94. }
  95. /**
  96. * Sets up the inference aspects for a new round following a switch event
  97. * @param {Boolean} is_win - Whether a clock is selected after the switch event.
  98. * @param {Boolean} is_start - Whether this is the first round after a switch event.
  99. * @param {Array<number>} clock_score_prior - The prior probabilities over the active clocks before the switch event.
  100. */
  101. init_round(is_win, is_start, clock_score_prior) {
  102. this.clock_inf.clock_util.init_round(this.clock_inf.clocks_li);
  103. this.clock_inf.clock_util.init_round(this.clock_inf.clocks_on);
  104. var clock;
  105. var clock_ind;
  106. var top_score;
  107. if (is_win || is_start) {
  108. var count = 0;
  109. if (this.is_undo && !this.is_equalize) {
  110. for (clock_ind in this.clock_inf.clocks_on) {
  111. clock = this.clock_inf.clocks_on[clock_ind];
  112. this.clock_inf.cscores[clock] = 0;
  113. count += 1;
  114. }
  115. top_score = 0;
  116. } else {
  117. for (clock_ind in this.clock_inf.clocks_on) {
  118. clock = this.clock_inf.clocks_on[clock_ind];
  119. this.clock_inf.cscores[clock] = clock_score_prior[count];
  120. count += 1;
  121. }
  122. top_score = 0;
  123. }
  124. }
  125. this.clock_inf.update_sorted_inds();
  126. this.clock_inf.clock_util.update_curhours(this.clock_inf.sorted_inds);
  127. this.clock_inf.handicap_cscores(is_win, is_start);
  128. top_score = this.clock_inf.cscores[this.clock_inf.sorted_inds[0]];
  129. var bound_score;
  130. if (this.clock_inf.clock_history[0].length === 0) {
  131. bound_score = top_score - config.max_init_diff;
  132. } else {
  133. bound_score = top_score - this.parent.win_diffs[this.clock_inf.sorted_inds[0]];
  134. }
  135. for (var i in this.clock_inf.clocks_on) {
  136. clock_ind = this.clock_inf.clocks_on[i];
  137. clock = this.parent.clockgrid.clocks[clock_ind];
  138. if (this.clock_inf.cscores[clock_ind] > bound_score) {
  139. clock.highlighted = true;
  140. clock.draw_face();
  141. } else {
  142. clock.highlighted = false;
  143. clock.draw_face();
  144. }
  145. }
  146. }
  147. }