text: /**
  * Copyright (c) 2017-present, Facebook, Inc.
  * All rights reserved.
  
  * This source code is licensed under the BSD-style license found in the
  * LICENSE file in the root directory of this source tree.
  */
  
  #include "cmd.h"
  #include "cmd_receiver.h"
  #include "game_env.h"
  #include <initializer_list>
  
  bool CmdReceiver::StartDurativeCmd(CmdDurative *cmd) {
      UnitId id = cmd->id();
      if (id == INVALID) return false;
  
      FinishDurativeCmd(id);
      _unit_durative_cmd[id] = cmd;
      return true;
  }
  
  bool CmdReceiver::FinishDurativeCmd(UnitId id) {
      auto it = _unit_durative_cmd.find(id);
      if (it != _unit_durative_cmd.end()) {
          it->second->SetDone();
          _unit_durative_cmd.erase(it);
          return true;
      }
      else return false;
  }
  
  bool CmdReceiver::FinishDurativeCmdIfDone(UnitId id) {
      auto it = _unit_durative_cmd.find(id);
      if (it != _unit_durative_cmd.end() && it->second->IsDone()) {
          _unit_durative_cmd.erase(it);
          return true;
      }
      else return false;
  }
  
  bool CmdReceiver::SendCmd(CmdBPtr &&cmd) {
      // 发送命令
      return SendCmdWithTick(std::move(cmd), _tick);
  }
  
  bool CmdReceiver::SendCmdWithTick(CmdBPtr &&cmd, Tick tick) {
      // This id is just used to achieve a total ordering.
      if (cmd.get() == nullptr) {
          throw std::range_error("Error input cmd is nullptr!");
      }
      cmd->set_cmd_id(_cmd_next_id);
      _cmd_next_id ++;
      cmd->set_tick_and_start_tick(tick);
  
      if (_cmd_dumper != nullptr) *_cmd_dumper << cmd->PrintInfo() << endl;
  
      // Check wehther we need to save stuff to _cmd_history.
      // For all commands that issued in ExecuteCmd(), we don't need to send them to _cmd_history.
      if (IsSaveToHistory()) _cmd_history.push_back(cmd->clone());
  
      // 将不同的命令放到两个队列中
      CmdDurative *durative = dynamic_cast<CmdDurative *>(cmd.get());
      if (durative != nullptr) {
          // show_prompt_cond("Receive Durative Cmd", cmd);
          // cout << "Receive Durative Cmd " << cmd->PrintInfo() << endl;
          cmd.release();
          // 命令被持续执行的队列
          _durative_cmd_queue.push(CmdDPtr(durative));
      } else {
          CmdImmediate *immediate = dynamic_cast<CmdImmediate *>(cmd.get());
          if (immediate != nullptr) {
              // show_prompt_cond("Receive Immediate Cmd", cmd);
              // cout << "Receive Immediate Cmd " << cmd->PrintInfo() << endl;
              cmd.release();
              // 命令被立即执行的队列
              _immediate_cmd_queue.push(CmdIPtr(immediate));
          } else {
              throw std::range_error("Error! the command is neither durative or immediate! " + cmd->PrintInfo());
          }
      }
      return true;
  }
  
  const CmdDurative *CmdReceiver::GetUnitDurativeCmd(UnitId id) const {
      auto it = _unit_durative_cmd.find(id);
      if (it == _unit_durative_cmd.end()) return nullptr;
      else return it->second;
  }
  
  bool CmdReceiver::SaveReplay(const string& replay_filename) const {
      // Load the replay_file (which is a action sequence)
      // Each action looks like the following:
      //    Tick, CmdType, UnitId, UnitType, Loc
      //
      serializer::saver saver(false);
      // if (_verbose) cout << "Save replay to " << replay_filename << " #record: " << _cmd_history.size() << endl;
      saver << _cmd_history;
      if (! saver.write_to_file(replay_filename)) return false;
  
      return true;
  }
  
  void CmdReceiver::ExecuteDurativeCmds(const GameEnv &env, bool force_verbose) {
      SetSaveToHistory(false);
  
      // cout << "Starting ExecutiveDurativeCmds[" << _tick << "]" << endl;
  
      // Execute durative cmds.
      while (! _durative_cmd_queue.empty()) {
          const CmdDPtr& cmd_ref = _durative_cmd_queue.top();
          // cout << "Top: " << cmd_ref->PrintInfo() << endl;
          if (cmd_ref->tick() > _tick) break;
  
          show_prompt_cond("ExecuteDurativeCmds", cmd_ref, force_verbose);
  
          // If the command is done (often set by other preemptive commands, we skip.
          if (cmd_ref->IsDone()) {
              FinishDurativeCmdIfDone(cmd_ref->id());
              _durative_cmd_queue.pop();
              continue;
          }
  
          // Now finally we deal with the command.
          CmdDPtr cmd = _durative_cmd_queue.pop_top();
  
          // cout << "Before executing a durative cmd " << cmd->PrintInfo() << endl;
  
          // Run the cmd.
          cmd->Run(env, this);
  
          // If the command is not yet done, push it back to the queue.
          if (! cmd->IsDone()) _durative_cmd_queue.push(std::move(cmd));
          else FinishDurativeCmd(cmd->id());
      }
  
      // cout << "Ending ExecutiveDurativeCmds[" << _tick << "]" << endl;
      SetSaveToHistory(true);
  }
  
  void CmdReceiver::ExecuteImmediateCmds(GameEnv *env, bool force_verbose) {
      SetSaveToHistory(false);
  
      // cout << "Starting ExecutiveImmediateCmds[" << _tick << "]" << endl;
  
      // Execute immediate cmds, which will change the game state.
      while (! _immediate_cmd_queue.empty()) {
          const CmdIPtr &cmd_ref = _immediate_cmd_queue.top();
          // cout << "Top: " << cmd_ref->PrintInfo() << endl;
          if (cmd_ref->tick() > _tick) break;
  
          CmdIPtr cmd = _immediate_cmd_queue.pop_top();
  
          show_prompt_cond("ExecuteImmediateCmds", cmd, force_verbose);
  
          cmd->Run(env, this);
      }
  
      // cout << "Ending ExecutiveImmediateCmds[" << _tick << "]" << endl;
      SetSaveToHistory(true);
  }
  
  vector<CmdDurative*> CmdReceiver::GetHistoryAtCurrentTick() const {
      vector<CmdDurative*> res;
      for (int i = _cmd_history.size() - 1; i >= 0; i--) {
          const auto &cmd = _cmd_history[i];
          if (cmd->tick() < _tick) break;
          CmdDurative *durative = dynamic_cast<CmdDurative *>(cmd.get());
          if (durative != nullptr) {
              res.push_back(durative);
          }
      }
      return res;
  
  }
  
  
  void CmdReceiver::SaveCmdReceiver(serializer::saver &saver) const {
      // Do not save/load _loaded_replay, as well as command history.
      saver << _tick << _immediate_cmd_queue << _durative_cmd_queue << _verbose_player_id << _verbose_choice;
  }
  
  void CmdReceiver::LoadCmdReceiver(serializer::loader &loader) {
      loader >> _tick >> _immediate_cmd_queue >> _durative_cmd_queue >> _verbose_player_id >> _verbose_choice;
  
      // Set the failed_moves.
      _stats.SetTick(_tick);
  
      // load durative cmd queue. Note that the priority queue is opaque so we need some hacks.
      int size = _durative_cmd_queue.size();
      const CmdDPtr *c = &(_durative_cmd_queue.top());
      _unit_durative_cmd.clear();
      for (int i = 0; i < size; ++i) {
          const CmdDPtr &curr = c[i];
          if (! curr->IsDone()) _unit_durative_cmd.insert(make_pair(curr->id(), curr.get()));
      }
  }
  
  void CmdReceiver::SetCmdDumper(const string& cmd_dumper_filename) {
      // Set the command dumper if there is any file specified.
      _cmd_dumper.reset(new ofstream(cmd_dumper_filename));
  }
  