<commit_before>import _ from 'lodash';
import d3 from 'd3';
import stringify from 'json-stringify-pretty-compact';

import {absorptionDuration, animationStepDurationMin, animationStepDurationMax, playPauseTransitionDuration, stockColumns, tileSize} from './config';
import {Stock} from './stock';
import * as level from './level';
import {BareBoard} from './bare_board';
import {ProgressPearls} from './progress_pearls';
import {TileHelper} from './tile_helper';
import {DetectionBar} from './detection_bar';
import {TitleManager} from './title_manager';

// TODO decide where to use winning status; it seems I should move it here
// TODO top_bar needs a separate module

export class GameBoard {
  constructor(svg, game, popupManager, storage, levelId) {

    const borderMargins = {
      top: 2,
      left: 4,
      bottom: 2,
      right: 1 + stockColumns,
    };
    this.bareBoard = new BareBoard(svg, borderMargins, {
      tileRotated: this.tileRotatedCallback.bind(this),
      tileMouseover: this.tileMouseoverCallback.bind(this),
      animationStart: this.animationStartCallback.bind(this),
      animationInterrupt: this.animationInterruptCallback.bind(this),
      animationEnd: this.animationEndCallback.bind(this),
      setPlayButtonState: this.setPlayButtonState.bind(this),
    });

    this.game = game;
    this.svg = svg;

    this.titleManager = new TitleManager(
      this.svg.select('.title-bar .title-text'),
      this.svg.select('.subtitle-bar'),
      this.svg.select('.title-bar .level-number'));
    this.popupManager = popupManager;
    this.storage = storage;

    this.progressPearls = new ProgressPearls(
      svg,
      level.levels.filter((d) => d.group === 'Game'),
      this
    );
    this.progressPearls.g.attr('transform', `translate(${-1.8 * tileSize},${tileSize})`);
    this.progressPearls.draw();

    this.stock = new Stock(svg, this.bareBoard);
    this.bareBoard.stock = this.stock;  // such monkey patching not nice
    this.detectionBar = new DetectionBar(this.svg.select('.subtitle-bar'));
    this.detectionBar.g.attr('transform', `translate(${1.5 * tileSize},${tileSize / 4})`);
    this.logger = this.bareBoard.logger;
    this.logger.logAction('initialLevel');

    this.boardControls = svg.select('.board-controls');
    this.activateBoardControls();
    this.navigationControls = svg.select('.navigation-controls');
    this.activateNavigationControls();

    this.loadLevel(levelId);
    this.tileHelper = new TileHelper(svg, this.bareBoard, this.game);
  }

  tileRotatedCallback(tile) {
    this.showTileHelper(tile);
  }

  tileMouseoverCallback(tile) {
    this.showTileHelper(tile);
  }

  animationStartCallback() {
    this.saveProgress();
    this.titleManager.displayMessage(
      'Experiment in progress...',
      'progress', -1);
  }

  animationInterruptCallback() {
    this.titleManager.displayMessage(
      'Experiment disturbed! Quantum states are fragile...',
      'failure');
    // Reset play/pause button to "play" state
    this.setPlayButtonState('play');
  }

  animationEndCallback() {

    const winningStatus = this.bareBoard.winningStatus;
    const level = this.bareBoard.level;

    // Reset play/pause button to "play" state
    this.setPlayButtonState('play');

    this.detectionBar.updateActual(
      winningStatus.totalProbAtDets,
      winningStatus.noOfFedDets
    );

    this.titleManager.displayMessage(
      winningStatus.message,
      winningStatus.isWon ? 'success' : 'failure',
      -1
    );

    if (winningStatus.isWon) {

      if (!this.storage.getLevelIsWon(level.id)) {
        window.setTimeout(
          () => this.popupManager.popup('You won!', {close: false, nextLevel: true}),
          absorptionDuration
        );
      }
      // Show next level button
      this.navigationControls.select('.next-level').classed('hidden', false);

      this.storage.setLevelIsWon(level.id, true);
      this.saveProgress();
      this.progressPearls.update();
    }
  }

  reset() {
    // Reset detection
    this.setHeaderTexts();
    this.detectionBar.updateRequirements(
      this.bareBoard.level.requiredDetectionProbability,
      this.bareBoard.level.detectorsToFeed
    );

    // Reset play/pause button to "play" state
    this.setPlayButtonState('play');

    this.bareBoard.redraw();
    this.stock.elementCount(this.bareBoard.level);
    this.stock.drawStock();
  }

  stop() {
    this.bareBoard.stop();
  }

  get level() {
    return this.bareBoard.level;
    // then also shortcut some gameBoard.level below
  }

  get title() {
    const textBefore = (level) =>
      level.texts && level.texts.before ? `: "${level.texts.before}"` : '';
    // const groupPrefix =
    //   this.bareBoard.level.group ?
    //   `[${this.bareBoard.level.group}] ` : '';
    // return `${groupPrefix}${this.bareBoard.level.i}. ${this.bareBoard.level.name}${textBefore(this.bareBoard.level)}`;
    return `${this.bareBoard.level.name}${textBefore(this.bareBoard.level)}`;
  }

  get goalMessage() {
    if (this.bareBoard.level.requiredDetectionProbability === 0) {
      return 'GOAL: Avoid launching any mines!';
    } else if (this.bareBoard.level.detectorsToFeed === 0) {
      return 'GOAL: No goals! Freedom to do whatever you like. :)';
    } else if (this.bareBoard.level.detectorsToFeed === 1) {
      return `GOAL: Make the photon fall into a detector, with ${(100 * this.bareBoard.level.requiredDetectionProbability).toFixed(0)}% chance.`;
    } else {
      return `GOAL: Make the photon fall into ${this.bareBoard.level.detectorsToFeed} detectors, some probability to each, total of ${(100 * this.bareBoard.level.requiredDetectionProbability).toFixed(0)}%.`;
    }
  }<commit_msg>hotfix(title): titles without the "text before" (actually, text after)<commit_after>import _ from 'lodash';
import d3 from 'd3';
import stringify from 'json-stringify-pretty-compact';

import {absorptionDuration, animationStepDurationMin, animationStepDurationMax, playPauseTransitionDuration, stockColumns, tileSize} from './config';
import {Stock} from './stock';
import * as level from './level';
import {BareBoard} from './bare_board';
import {ProgressPearls} from './progress_pearls';
import {TileHelper} from './tile_helper';
import {DetectionBar} from './detection_bar';
import {TitleManager} from './title_manager';

// TODO decide where to use winning status; it seems I should move it here
// TODO top_bar needs a separate module

export class GameBoard {
  constructor(svg, game, popupManager, storage, levelId) {

    const borderMargins = {
      top: 2,
      left: 4,
      bottom: 2,
      right: 1 + stockColumns,
    };
    this.bareBoard = new BareBoard(svg, borderMargins, {
      tileRotated: this.tileRotatedCallback.bind(this),
      tileMouseover: this.tileMouseoverCallback.bind(this),
      animationStart: this.animationStartCallback.bind(this),
      animationInterrupt: this.animationInterruptCallback.bind(this),
      animationEnd: this.animationEndCallback.bind(this),
      setPlayButtonState: this.setPlayButtonState.bind(this),
    });

    this.game = game;
    this.svg = svg;

    this.titleManager = new TitleManager(
      this.svg.select('.title-bar .title-text'),
      this.svg.select('.subtitle-bar'),
      this.svg.select('.title-bar .level-number'));
    this.popupManager = popupManager;
    this.storage = storage;

    this.progressPearls = new ProgressPearls(
      svg,
      level.levels.filter((d) => d.group === 'Game'),
      this
    );
    this.progressPearls.g.attr('transform', `translate(${-1.8 * tileSize},${tileSize})`);
    this.progressPearls.draw();

    this.stock = new Stock(svg, this.bareBoard);
    this.bareBoard.stock = this.stock;  // such monkey patching not nice
    this.detectionBar = new DetectionBar(this.svg.select('.subtitle-bar'));
    this.detectionBar.g.attr('transform', `translate(${1.5 * tileSize},${tileSize / 4})`);
    this.logger = this.bareBoard.logger;
    this.logger.logAction('initialLevel');

    this.boardControls = svg.select('.board-controls');
    this.activateBoardControls();
    this.navigationControls = svg.select('.navigation-controls');
    this.activateNavigationControls();

    this.loadLevel(levelId);
    this.tileHelper = new TileHelper(svg, this.bareBoard, this.game);
  }

  tileRotatedCallback(tile) {
    this.showTileHelper(tile);
  }

  tileMouseoverCallback(tile) {
    this.showTileHelper(tile);
  }

  animationStartCallback() {
    this.saveProgress();
    this.titleManager.displayMessage(
      'Experiment in progress...',
      'progress', -1);
  }

  animationInterruptCallback() {
    this.titleManager.displayMessage(
      'Experiment disturbed! Quantum states are fragile...',
      'failure');
    // Reset play/pause button to "play" state
    this.setPlayButtonState('play');
  }

  animationEndCallback() {

    const winningStatus = this.bareBoard.winningStatus;
    const level = this.bareBoard.level;

    // Reset play/pause button to "play" state
    this.setPlayButtonState('play');

    this.detectionBar.updateActual(
      winningStatus.totalProbAtDets,
      winningStatus.noOfFedDets
    );

    this.titleManager.displayMessage(
      winningStatus.message,
      winningStatus.isWon ? 'success' : 'failure',
      -1
    );

    if (winningStatus.isWon) {

      if (!this.storage.getLevelIsWon(level.id)) {
        window.setTimeout(
          () => this.popupManager.popup('You won!', {close: false, nextLevel: true}),
          absorptionDuration
        );
      }
      // Show next level button
      this.navigationControls.select('.next-level').classed('hidden', false);

      this.storage.setLevelIsWon(level.id, true);
      this.saveProgress();
      this.progressPearls.update();
    }
  }

  reset() {
    // Reset detection
    this.setHeaderTexts();
    this.detectionBar.updateRequirements(
      this.bareBoard.level.requiredDetectionProbability,
      this.bareBoard.level.detectorsToFeed
    );

    // Reset play/pause button to "play" state
    this.setPlayButtonState('play');

    this.bareBoard.redraw();
    this.stock.elementCount(this.bareBoard.level);
    this.stock.drawStock();
  }

  stop() {
    this.bareBoard.stop();
  }

  get level() {
    return this.bareBoard.level;
    // then also shortcut some gameBoard.level below
  }

  get title() {
    // const textBefore = (level) =>
    //   level.texts && level.texts.before ? `: "${level.texts.before}"` : '';
    // // const groupPrefix =
    // //   this.bareBoard.level.group ?
    // //   `[${this.bareBoard.level.group}] ` : '';
    // // return `${groupPrefix}${this.bareBoard.level.i}. ${this.bareBoard.level.name}${textBefore(this.bareBoard.level)}`;
    // return `${this.bareBoard.level.name}${textBefore(this.bareBoard.level)}`;
    return this.bareBoard.level.name;
  }

  get goalMessage() {
    if (this.bareBoard.level.requiredDetectionProbability === 0) {
      return 'GOAL: Avoid launching any mines!';
    } else if (this.bareBoard.level.detectorsToFeed === 0) {
      return 'GOAL: No goals! Freedom to do whatever you like. :)';
    } else if (this.bareBoard.level.detectorsToFeed === 1) {
      return `GOAL: Make the photon fall into a detector, with ${(100 * this.bareBoard.level.requiredDetectionProbability).toFixed(0)}% chance.`;
    } else {
      return `GOAL: Make the photon fall into ${this.bareBoard.level.detectorsToFeed} detectors, some probability to each, total of ${(100 * this.bareBoard.level.requiredDetectionProbability).toFixed(0)}%.`;
    }
  }
{'fraction_non_alphanumeric': 0.1395873070920756, 'fraction_numerical': 0.0041616091555401425, 'mean_word_length': 3.3709738537324743, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 9, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3692954', 'n_tokens_mistral': 3648, 'n_tokens_neox': 3441, 'n_words': 698}