├── .gitignore ├── CMakeLists.txt ├── LICENSE ├── README-RU.md ├── README.md ├── forReadme ├── 1.gif ├── 2.gif ├── EN-help.png ├── EN-icon.png ├── RU-help.png └── RU-icon.png ├── include ├── debug.hpp ├── translate.hpp ├── turing.hpp └── turingUI.hpp ├── run.sh └── src ├── main.cpp ├── translate.cpp ├── turing.cpp └── turingUI.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | *.json 34 | 35 | MAIN 36 | turing-cmd 37 | DEBUG_log.txt 38 | 39 | **/build 40 | **/.vscode 41 | **/.cache -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.24) 2 | 3 | project(Turing 4 | LANGUAGES CXX 5 | VERSION 0.2 6 | ) 7 | 8 | set(CMAKE_BUILD_TYPE Release) 9 | 10 | include(FetchContent) 11 | 12 | set(FETCHCONTENT_UPDATES_DISCONNECTED TRUE) 13 | 14 | FetchContent_Declare(ftxui 15 | GIT_REPOSITORY https://github.com/ArthurSonzogni/ftxui 16 | GIT_TAG v4.0.0 17 | FIND_PACKAGE_ARGS NAMES ftxui 18 | ) 19 | 20 | FetchContent_Declare(json 21 | GIT_REPOSITORY https://github.com/nlohmann/json 22 | GIT_TAG v3.10.5 23 | FIND_PACKAGE_ARGS NAMES nlohmann_json 24 | ) 25 | 26 | FetchContent_MakeAvailable(ftxui json) 27 | 28 | set(CMAKE_CXX_STANDARD 20) 29 | 30 | include_directories(include) 31 | 32 | add_library(TuringLib src/turing.cpp) 33 | add_library(TuringUI src/turingUI.cpp) 34 | add_library(Translate src/translate.cpp) 35 | 36 | target_link_libraries(Translate TuringLib) 37 | 38 | target_link_libraries(TuringUI PRIVATE Translate ftxui::screen ftxui::dom ftxui::component TuringLib nlohmann_json::nlohmann_json) 39 | 40 | add_executable(turing-cmd src/main.cpp) 41 | target_link_libraries(turing-cmd PRIVATE Translate ftxui::screen ftxui::dom ftxui::component TuringLib TuringUI nlohmann_json::nlohmann_json) 42 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 DanArmor 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README-RU.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # turing_cmd 4 | 5 | [![English version](forReadme/EN-icon.png)](README.md) 6 | 7 | Машина Тьюринга с интерфейсом в командной строке. 8 | 9 | ![1.gif](forReadme/1.gif) 10 | 11 | ## Table of contents 12 | - [turing_cmd](#turing-cmd) 13 | * [Возможности](#features) 14 | + [Пример загрузки и сохранения программы](#example-of-save-load) 15 | + [Полный текст справки](#full-reference-text) 16 | * [Известные проблемы](#known-issues) 17 | * [Установка](#installation) 18 | + [Ubuntu/Debian](#ubuntu-debian) 19 | * [Сборка](#build) 20 | * [Спасибо создателям библиотек](#thanks-for-libraries-creators) 21 | * [История изменений](#changelog) 22 | 23 | 24 | 25 | ## Возможности: 26 | - Бесконечная лента 27 | - Пошаговое выполнение 28 | - Непрерывное выполнение 29 | - Сохранение Машины в файл и загрузка из файла. 30 | 31 | 32 | 33 | ### Пример загрузки и сохранения программы: 34 | ![2.gif](forReadme/2.gif) 35 | 36 | Возможна навигация стрелками и мышкой, выход на Esc. Присутствуют горячие клавиши. 37 | 38 | 39 | 40 | ### Полный текст справки: 41 | ![3.png](forReadme/RU-help.png) 42 | 43 | 44 | 45 | ## Известные проблемы: 46 | - Сохранения с кириллицей и/или широкими символами (греческие и т.п.) работают не всегда корректно (По этой причине в сохранеии лямбда заменена знаком #). Применяйте, пожалуйста, на данный момент числа и латиницу 47 | 48 | 49 | 50 | ## Установка 51 | 52 | Вы можете использовать скомпилированную версию из [релизов](https://github.com/DanArmor/turing_cmd/releases) (пока только для Linux). 53 | 54 | 55 | 56 | ### Ubuntu/Debian: 57 | [Имеется PPA репозиторий.](https://launchpad.net/~danarmor/+archive/ubuntu/ppa). 58 | * `sudo add-apt-repository ppa:danarmor/ppa` 59 | * `sudo apt update` 60 | * `sudo apt install turing-cmd` 61 | Готово - вы можете запустить программу командой `turing-cmd` в терминале. 62 | 63 | 64 | 65 | ## Сборка: 66 | Программа имеет две зависимости, указанные в конце Readme, но если у вас имеется CMake - достаточно создать каталог build и запусть run.sh, или запустить CMake/make вручную - файл CMakeLists уже настроен так, чтобы скачать необходимые зависимости. 67 | 68 | 69 | 70 | ## Спасибо создателям библиотек: 71 | - [nlohmann/json](https://github.com/nlohmann/json) - работа с JSON (текущая библиотека) 72 | - [ArthurSonzogni/FTXUI](https://github.com/ArthurSonzogni/FTXUI) - TUI 73 | - [taocpp/json](https://github.com/taocpp/json) - работа с JSON (была использована в первой версии) 74 | 75 | 76 | 77 | ## История изменений: 78 | 79 | V0.2.1 - FTXUI 4.0.0 80 | 81 | v0.2 - переход на новую библиотеку json, переход с wchar/wstring, изменение save/load уведомления, переход на FTXUI V3.0.0 82 | 83 | v0.1 - первая версия 84 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # turing_cmd 4 | 5 | [![Русская версия](forReadme/RU-icon.png)](README-RU.md) 6 | 7 | Turing machine with TUI 8 | 9 | ![1.gif](forReadme/1.gif) 10 | 11 | ## Table of contents 12 | - [turing_cmd](#turing-cmd) 13 | * [Features](#features) 14 | + [Example of save/load](#example-of-save-load) 15 | + [Full reference text](#full-reference-text) 16 | * [Known issues](#known-issues) 17 | * [Installation](#installation) 18 | + [Ubuntu/Debian:](#ubuntu-debian) 19 | * [Build](#build) 20 | * [Thanks for libraries creators](#thanks-for-libraries-creators) 21 | * [Changelog](#changelog) 22 | 23 | 24 | 25 | 26 | ## Features: 27 | - Endless tape 28 | - Step by step execution 29 | - Continuous exectuion 30 | - Save/load from file. 31 | 32 | 33 | 34 | ### Example of save/load: 35 | ![2.gif](forReadme/2.gif) 36 | 37 | You can navigate with keyboard arrows or with mouse, exit by Esc. There are hotkeys. 38 | 39 | 40 | 41 | ### Full reference text: 42 | ![3.png](forReadme/EN-help.png) 43 | 44 | 45 | 46 | ## Known issues: 47 | - Cyrillic and/or widechars (like greek symbols) are working not properly. Use english.\ 48 | 49 | 50 | 51 | ## Installation: 52 | 53 | You can use binaries from [releases](https://github.com/DanArmor/turing_cmd/releases) (Linux only for now). 54 | 55 | 56 | 57 | ### Ubuntu/Debian: 58 | [The PPA repo is provided](https://launchpad.net/~danarmor/+archive/ubuntu/ppa). 59 | * `sudo add-apt-repository ppa:danarmor/ppa` 60 | * `sudo apt update` 61 | * `sudo apt install turing-cmd` 62 | Done - you can start program by `turing-cmd` command in terminal. 63 | 64 | 65 | 66 | ## Build: 67 | There are two deps, but if you have CMake - you're ready to go: make a "build" dir and execute "run.sh". 68 | 69 | 70 | 71 | ## Thanks for libraries creators: 72 | - [nlohmann/json](https://github.com/nlohmann/json) - JSON (are used now) 73 | - [ArthurSonzogni/FTXUI](https://github.com/ArthurSonzogni/FTXUI) - TUI 74 | - [taocpp/json](https://github.com/taocpp/json) - JSON (was used in first version) 75 | 76 | 77 | 78 | ## Changelog: 79 | 80 | v0.2.1 - FTXUI V4.0.0 81 | 82 | v0.2 - moved to nlohmann/json, replaced wchar/wstring with char/string, changed save/load notification, moved to FTXUI V3.0.0 83 | 84 | v0.1 - init version 85 | -------------------------------------------------------------------------------- /forReadme/1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DanArmor/turing_cmd/1107d665bb9ef22d6e98512e3ecf56a8c643c1bd/forReadme/1.gif -------------------------------------------------------------------------------- /forReadme/2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DanArmor/turing_cmd/1107d665bb9ef22d6e98512e3ecf56a8c643c1bd/forReadme/2.gif -------------------------------------------------------------------------------- /forReadme/EN-help.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DanArmor/turing_cmd/1107d665bb9ef22d6e98512e3ecf56a8c643c1bd/forReadme/EN-help.png -------------------------------------------------------------------------------- /forReadme/EN-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DanArmor/turing_cmd/1107d665bb9ef22d6e98512e3ecf56a8c643c1bd/forReadme/EN-icon.png -------------------------------------------------------------------------------- /forReadme/RU-help.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DanArmor/turing_cmd/1107d665bb9ef22d6e98512e3ecf56a8c643c1bd/forReadme/RU-help.png -------------------------------------------------------------------------------- /forReadme/RU-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DanArmor/turing_cmd/1107d665bb9ef22d6e98512e3ecf56a8c643c1bd/forReadme/RU-icon.png -------------------------------------------------------------------------------- /include/debug.hpp: -------------------------------------------------------------------------------- 1 | #define DEBUG 2 | #ifdef DEBUG 3 | #ifndef _INC_DEBUG_HPP 4 | #define _INC_DEBUG_HPP 5 | #include 6 | 7 | extern std::ofstream DEBUG_output; 8 | 9 | #define DEBUG_write(str) {DEBUG_output << str << "\n";} 10 | 11 | 12 | #endif 13 | #endif -------------------------------------------------------------------------------- /include/translate.hpp: -------------------------------------------------------------------------------- 1 | #ifndef _INC_TRANSLATE_HPP 2 | #define _INC_TRANSLATE_HPP 3 | 4 | #include "debug.hpp" 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | enum LANG{ 12 | L_EN, 13 | L_RU 14 | }; 15 | 16 | extern std::map> LANGUAGE; 17 | extern std::string TRANSLATE_EMPTY_STR; 18 | 19 | std::string &locale_getText(std::string const &key, LANG lang); 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /include/turing.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef _INC_TURING_HPP 3 | #define _INC_TURING_HPP 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | enum TuringDirection { Left, Right, NoMove }; 13 | 14 | extern const char TURING_EMPTY; 15 | extern const std::string TURING_EMPTY_STR; 16 | 17 | struct TuringTurn { 18 | TuringTurn(); 19 | TuringTurn(int oldState, char oldSymbol, int newState, char newSymbol, 20 | TuringDirection direction); 21 | TuringTurn(int newState, char newSymbol, TuringDirection direction); 22 | ~TuringTurn(); 23 | int oldState; 24 | char oldSymbol; 25 | int newState; 26 | char newSymbol; 27 | TuringDirection direction; 28 | }; 29 | 30 | class TuringTape { 31 | public: 32 | TuringTape(); 33 | ~TuringTape(); 34 | 35 | void setChar(int pos, char c); 36 | char getChar(int pos); 37 | void clear(void); 38 | 39 | std::map getTapeMap(void); 40 | 41 | private: 42 | std::map tape; 43 | int offset; 44 | }; 45 | 46 | struct TuringState { 47 | TuringState(); 48 | TuringState(std::pair posAndState, 49 | std::initializer_list> chars); 50 | ~TuringState(); 51 | TuringTape tape; 52 | int position; 53 | int currState; 54 | }; 55 | 56 | class TuringMachine { 57 | public: 58 | TuringMachine(); 59 | TuringMachine( 60 | std::initializer_list, TuringTurn>> 61 | turns); 62 | ~TuringMachine(); 63 | 64 | void loadState(TuringState newState); 65 | void setAlph(std::string str); 66 | void setComment(std::string const &str); 67 | 68 | void addTurn(TuringTurn turn); 69 | void addTurn(std::pair old, TuringTurn turn); 70 | 71 | void makeTurn(void); 72 | 73 | char getCurChar(void); 74 | std::string getStrView(int from, int to); 75 | TuringTape getTape(void); 76 | 77 | int getCurState(void); 78 | int getCurPosition(void); 79 | std::map, TuringTurn> &getTable(void); 80 | 81 | bool isDone(void); 82 | void clear(void); 83 | void clearTurns(void); 84 | 85 | private: 86 | std::map, TuringTurn> table; 87 | std::set alph; 88 | bool stop = false; 89 | std::string comment; 90 | TuringState state; 91 | }; 92 | 93 | std::string pickDirectStr(TuringDirection direction); 94 | TuringDirection pickDirect(char c); 95 | 96 | #endif -------------------------------------------------------------------------------- /include/turingUI.hpp: -------------------------------------------------------------------------------- 1 | #ifndef _INC_TURING_UI_H 2 | #define _INC_TURING_UI_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "ftxui/component/component.hpp" 9 | #include "ftxui/component/screen_interactive.hpp" 10 | #include "ftxui/dom/elements.hpp" 11 | #include "ftxui/screen/screen.hpp" 12 | #include "ftxui/screen/string.hpp" 13 | 14 | #include "nlohmann/json.hpp" 15 | 16 | using namespace nlohmann; 17 | 18 | #include "turing.hpp" 19 | #include "translate.hpp" 20 | 21 | class TuringCellUI : public ftxui::ComponentBase { 22 | private: 23 | int number; 24 | std::string str; 25 | ftxui::Component input; 26 | bool isTop = false; 27 | 28 | public: 29 | TuringCellUI(int number_); 30 | TuringCellUI(int number_, bool isTop_); 31 | 32 | void loadSaved(std::string str_); 33 | 34 | TuringTurn getTurn(); 35 | bool containStr(); 36 | 37 | ftxui::Element Render() override; 38 | bool OnEvent(ftxui::Event) override; 39 | }; 40 | 41 | class TuringRowUI : public ftxui::ComponentBase { 42 | private: 43 | std::vector> cells; 44 | bool isTop = false; 45 | 46 | public: 47 | TuringRowUI(); 48 | TuringRowUI(bool isTop_); 49 | 50 | void addCol(); 51 | void removeCol(); 52 | 53 | void loadSavedCell(int i, std::string str); 54 | 55 | TuringTurn getTurn(int i); 56 | bool containStr(int i); 57 | int size(void); 58 | 59 | ftxui::Element Render() override; 60 | bool OnEvent(ftxui::Event) override; 61 | }; 62 | 63 | class TuringTableUI : public ftxui::ComponentBase { 64 | private: 65 | std::string alph; 66 | std::vector> rowsComponents; 67 | 68 | int cols(void); 69 | int rows(void); 70 | 71 | public: 72 | TuringTableUI(); 73 | 74 | void removeRow(void); 75 | void removeCol(void); 76 | 77 | void loadSavedCell(int row, int col, std::string str); 78 | 79 | void addRow(char c, bool isTop_); 80 | void addCol(void); 81 | 82 | std::vector getTurns(void); 83 | 84 | void updateTable(std::string alph_); 85 | 86 | ftxui::Element Render() override; 87 | bool OnEvent(ftxui::Event) override; 88 | }; 89 | 90 | class TuringTapeUI : public ftxui::ComponentBase { 91 | private: 92 | int size; 93 | int leftIndex; 94 | int CELL_SIZE = 6; 95 | int positionAbsolute = 0; 96 | std::vector tapeStrs; 97 | std::vector tapeInputs; 98 | 99 | int toLocalPos(int pos); 100 | bool isValidPos(int pos); 101 | 102 | public: 103 | TuringTapeUI(int size_); 104 | 105 | int getSize(void); 106 | int getLeftIndex(void); 107 | std::vector &getStrs(void); 108 | 109 | void setChar(char c, int pos); 110 | void setLeftIndex(int pos); 111 | void setPositionAbsolute(int pos); 112 | 113 | ftxui::Element Render() override; 114 | bool OnEvent(ftxui::Event) override; 115 | }; 116 | 117 | struct TuringUIStatus{ 118 | enum Status{ 119 | START, 120 | STEP, 121 | RUNNING_ON, 122 | RUNNING_OFF, 123 | STOP 124 | }; 125 | Status status = START; 126 | ftxui::Element Render(){ 127 | switch (status) { 128 | case START: 129 | return ftxui::text("Start state") | ftxui::color(ftxui::Color::BlueViolet); 130 | case STEP: 131 | return ftxui::text("Step") | ftxui::color(ftxui::Color::Green1) | ftxui::blink; 132 | case RUNNING_ON: 133 | return ftxui::text("Running") | ftxui::color(ftxui::Color::Green3); 134 | case RUNNING_OFF: 135 | return ftxui::text("Running") | ftxui::color(ftxui::Color::DarkGreen); 136 | case STOP: 137 | return ftxui::text("Stopped. Need to reset") | ftxui::color(ftxui::Color::Red1); 138 | default: 139 | throw std::invalid_argument("Неизвестный статус"); 140 | } 141 | } 142 | }; 143 | 144 | struct TuringSave{ 145 | std::string fileName; 146 | std::string comm; 147 | std::string alph; 148 | std::map, TuringTurn> table; 149 | std::map tape; 150 | }; 151 | 152 | class TuringUI : public ftxui::ComponentBase { 153 | private: 154 | void updateComponents(void); 155 | void makeTurn(void); 156 | void updateStateTape(void); 157 | void saveToFile(void); 158 | std::vector breakByLines(std::string str); 159 | ftxui::Element breakByLinesElement(std::string str); 160 | std::function quit; 161 | TuringState state; 162 | TuringMachine machine; 163 | 164 | std::atomic isResetState = true; 165 | std::atomic isRunning = false; 166 | std::atomic isShowingHelp = false; 167 | std::atomic isErrorFile = false; 168 | LANG language = LANG::L_EN; 169 | 170 | std::atomic wasTriedToSave = false; 171 | enum SaveStatus{ 172 | INVALID, 173 | OK 174 | } lastSaveStatus; 175 | 176 | bool needToUpdateTable = false; 177 | std::string alphStr; 178 | std::string commentStr, fileStr; 179 | TuringUIStatus status; 180 | 181 | ftxui::Component helpButton; 182 | ftxui::Component localeButton; 183 | 184 | ftxui::Component moveTapeLeftButton; 185 | ftxui::Component moveTapeRightButton; 186 | 187 | ftxui::Component stepButton; 188 | ftxui::Component runButton; 189 | ftxui::Component resetButton; 190 | ftxui::Component alphInput; 191 | ftxui::Component commentInput; 192 | ftxui::Component fileInput; 193 | 194 | ftxui::Component addButton; 195 | ftxui::Component removeButton; 196 | 197 | ftxui::Component toMainDisplay; 198 | ftxui::Component toHelpDisplay; 199 | 200 | std::shared_ptr tapeComponent; 201 | std::shared_ptr tableComponent; 202 | ftxui::ScreenInteractive *scr; 203 | 204 | public: 205 | TuringUI(std::function quitFunc, ftxui::ScreenInteractive *scr_); 206 | void refresh(void); 207 | 208 | void loadSave(TuringSave); 209 | std::string &getText(std::string const &key); 210 | 211 | ftxui::Element Render() override; 212 | bool OnEvent(ftxui::Event) override; 213 | }; 214 | 215 | #endif 216 | -------------------------------------------------------------------------------- /run.sh: -------------------------------------------------------------------------------- 1 | mkdir -p build && 2 | cd build && 3 | cmake .. && 4 | make && 5 | mv turing-cmd .. 6 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "turing.hpp" 9 | #include "turingUI.hpp" 10 | 11 | #include "nlohmann/json.hpp" 12 | 13 | using namespace nlohmann; 14 | 15 | #ifdef DEBUG 16 | std::ofstream DEBUG_output; 17 | #endif 18 | 19 | int main(int argc, char **argv) { 20 | #ifdef DEBUG 21 | DEBUG_output.open("DEBUG_log.txt"); 22 | if(!DEBUG_output){ 23 | std::cout << "Failed to create a debug log file"; 24 | exit(3); 25 | } 26 | auto t = std::time(nullptr); 27 | auto tm = *std::localtime(&t); 28 | DEBUG_write("Start of debug session: " << std::put_time(&tm, "%d-%m-%Y %H-%M-%S")); 29 | #endif 30 | 31 | auto screen = ftxui::ScreenInteractive::TerminalOutput(); 32 | auto ui = ftxui::Make(screen.ExitLoopClosure(), &screen); 33 | try{ 34 | if(argc == 2){ 35 | TuringSave save; 36 | save.fileName = argv[1]; 37 | std::ifstream saveFileStream(argv[1]); 38 | if(!saveFileStream){ 39 | std::cout << "Ошибка открытия файла"; 40 | exit(2); 41 | } 42 | 43 | json j = json::parse(saveFileStream); 44 | 45 | save.comm = j["comm"]; 46 | save.alph = TURING_EMPTY_STR + j["alph"].get(); 47 | 48 | 49 | auto tapePos = j["tape"]["pos"].get>(); 50 | auto tapeChar = j["tape"]["sym"].get>(); 51 | 52 | auto oldSym = j["table"]["oldSym"].get>(); 53 | auto oldSt = j["table"]["oldSt"].get>(); 54 | auto newSt = j["table"]["newSt"].get>(); 55 | auto newSym = j["table"]["newSym"].get>(); 56 | auto direct = j["table"]["direct"].get>(); 57 | 58 | for(int i = 0; i < oldSym.size(); i++){ 59 | save.table[std::make_pair(oldSt[i], char(oldSym[i][0]))] = TuringTurn( 60 | oldSt[i], 61 | oldSym[i][0], 62 | newSt[i], 63 | newSym[i][0], 64 | pickDirect(direct[i][0]) 65 | ); 66 | } 67 | for(int i = 0; i < tapePos.size(); i++){ 68 | save.tape[tapePos[i]] = char(tapeChar[i][0]); 69 | } 70 | ui->loadSave(save); 71 | } else if(argc != 1){ 72 | std::cout << "Слишком много аргументов. Введите только имя программы или имя программы и имя файла для открытия\n"; 73 | exit(1); 74 | } 75 | screen.Loop(ui); 76 | } catch(std::exception &e){ 77 | std::cout << "Возникла ошибка: " << e.what() << "\n"; 78 | } 79 | 80 | return 0; 81 | } 82 | -------------------------------------------------------------------------------- /src/translate.cpp: -------------------------------------------------------------------------------- 1 | #include "translate.hpp" 2 | 3 | std::string TRANSLATE_EMPTY_STR = ""; 4 | 5 | extern std::string TURING_EMPTY_STR; 6 | 7 | std::map> LANGUAGE = 8 | { 9 | {"Title_help", { 10 | "Turing machine", 11 | "Машина Тьюринга - справка" 12 | }}, 13 | {"Help_1", { 14 | "This program provides a Turing machine functionality\n" 15 | "Hotkeys:\n" 16 | "CTRL-LEFT - move 5 cells to the left\n" 17 | "CTRL-RIGHT - move 5 cells to the right\n" 18 | "CTRL-UP - add new state to the table\n" 19 | "CTRL-DOWN - remove last state from the table\n" 20 | "F5 - step of the execution\n" 21 | "F6 - run/pause of the continuous execution\n" 22 | "F7 - reset to the start state\n" 23 | "F9 - help\n" 24 | "F10 - save machine to the file\n" 25 | " \n" 26 | "There is state indicator in the left upper corner\n" 27 | "Next states are provided:\n" 28 | , 29 | "Данная программа предназначена для работы с Машиной Тьюринга.\n" 30 | "Горячие клавиши:\n" 31 | "CTRL-LEFT - сместиться по ленте на 5 ячеек влево\n" 32 | "CTRL-RIGHT - сместиться по ленте на 5 ячеек вправо\n" 33 | "CTRL-UP - добавить новое состояние в таблицу\n" 34 | "CTRL-DOWN - убрать последнее состояние из таблицы\n" 35 | "F5 - шаг выполнения\n" 36 | "F6 - запуск/пауза непрерывного выполнения\n" 37 | "F7 - возврат в изначальное состояние\n" 38 | "F9 - справка\n" 39 | "F10 - сохранить машину в файл\n" 40 | " \n" 41 | "Слева сверху располагается индикатор состояния программы\n" 42 | "Возможны следующие состояния программы\n" 43 | }}, 44 | {"Help_state_start", { 45 | "Start state of the machine", 46 | "Начальное состояние программы" 47 | }}, 48 | {"Help_state_step", { 49 | "Step by step execution", 50 | "Исполнение по шагам" 51 | }}, 52 | {"Help_state_running_in_process", { 53 | "Continuous execution. In process", 54 | "Непрерывное выполнение. В процессе" 55 | }}, 56 | {"Help_state_running_pause", { 57 | "Continuous execution. Pause", 58 | "Непрерывное выполнение. Пауза" 59 | }}, 60 | {"Help_state_stop", { 61 | "End state. Need to reset", 62 | "Конечное состояние. Требуется сброс" 63 | }}, 64 | {"Help_2", { 65 | "To the right of help button there is an input field for the save file name\n" 66 | "After you press Enter the table of states, tape, alphabet and comment will be saved to the file\n" 67 | "as they were presented in Start state\n" 68 | " \n" 69 | "To load from file you need to start a program with save-file name as argument\n" 70 | "JSON is used as save format. It can be edited by hand\n" 71 | " \n" 72 | "Symbol " + TURING_EMPTY_STR + " is an empty symbol of Machine.\n" 73 | "Buttons to the left and right of the tape are moving your view by 5 cells\n" 74 | , 75 | "Правее кнопки справки находится поле для ввода имени файла.\n" 76 | "При нажатии кнопки Enter будет произведено сохранение текущей таблицы состояний\n" 77 | "и ленты в состоянии Start state в файл, а так же комментарий и алфавит\n" 78 | " \n" 79 | "Для загрузки Машины Тьюринга из файла выйдите из программы и запустите, указав имя файла после имени программы.\n" 80 | "Формат сохранений json. При необходимости, вы можете редактировать их вручную, соблюдая структуру\n" 81 | "Для примера структуры попробуйте сохранить одну из своих Машин\n" 82 | " \n" 83 | "Символ " + TURING_EMPTY_STR + " обозначает пустой символ Машины.\n" 84 | "Кнопки слева и справ от ленты перемещают ваше поле зрение на 5 ячеек.\n" 85 | }}, 86 | {"Help_button_step", { 87 | "Step of the execution", 88 | "Шаг выполнения машины" 89 | }}, 90 | {"Help_button_run", { 91 | "Run of the continuous execution", 92 | "Запуск непрерывного выполнения" 93 | }}, 94 | {"Help_button_pause", { 95 | "Pause of the continuous execution", 96 | "Пауза непрерывного выполнения" 97 | }}, 98 | {"Help_button_reset", { 99 | "Reset to the start state", 100 | "Сброс в начальное состояние" 101 | }}, 102 | {"Help_3", { 103 | "In Alphabet field inter symbols after " + TURING_EMPTY_STR + "\n" 104 | "If you change alphabet - the table of states will be cleared - be careful\n" 105 | " \n" 106 | "Comment field for notes\n" 107 | " \n" 108 | "Below it there is table of states and indicator of the current state.\n" 109 | "Adding or removing states deal no effect to the other entries of the table\n" 110 | "Alphabet symbols are placed by lines and states by columns.\n" 111 | "On their intersection you need to write: a symbol to place, direction to move and number of new state\n" 112 | "Example. If you meet empty symbol, when you are in state 0 - place a symbol '1', move to the left and change state to '0'\n" 113 | , 114 | "В поле алфавита вводите символы после " + TURING_EMPTY_STR + "\n" 115 | "При изменении алфавита таблица полностью сбрасывается - запишите полностью алфавит заранее.\n" 116 | " \n" 117 | "Поле комментария служит для заметок пользователя\n" 118 | " \n" 119 | "Ниже находится таблица состояний и индикатор текущего состояния.\n" 120 | "Добавление и удаление новых состояний кнопками Add и Remove не влияет на имеющиеся записи таблицы состояний\n" 121 | "Слева указаны символы алфавита, сверху - номер состояния.\n" 122 | "На пересечении вписывается символ, который необходимо поставить, направление движения головки и новое состояние\n" 123 | "Пример. При встрече пустого символа в состоянии 0 поставить символ 1, сдвинуть головку влево и перейти в состояние 0\n" 124 | }}, 125 | {"Help_4",{ 126 | "Possible directions: < left, > right, | no move\n" 127 | "If there is no symbol to place it is treated as 'place an empty symbol'" 128 | "If there is no new state in cell table it is treated as 'change state to end state'" 129 | " \n" 130 | "Example. Place empty symbol, move to the left and change state to the end state: \"<\"\n" 131 | , 132 | "Возможные движения головки: < влево, > вправо, | не двигаться\n" 133 | "Отсутствие символа для установки в ячейке означает, что поставится пустой символ\n" 134 | "Отсутствие нового состояния в записи таблицы обозначает переход в конечное состояние (-1)\n" 135 | " \n" 136 | "Пример. Установить пустой символ, сдвинуться влево и перейти в конечное состояние: \"<\"\n" 137 | }} 138 | }; 139 | 140 | std::string &locale_getText(std::string const &key, LANG lang){ 141 | 142 | if(LANGUAGE.count(key) == 0){ 143 | #ifdef DEBUG 144 | DEBUG_write("No string with key " + key + " in locale"); 145 | #else 146 | exit(4); 147 | #endif 148 | return TRANSLATE_EMPTY_STR; 149 | } 150 | return LANGUAGE[key][lang]; 151 | } -------------------------------------------------------------------------------- /src/turing.cpp: -------------------------------------------------------------------------------- 1 | #include "turing.hpp" 2 | 3 | const char TURING_EMPTY = '#'; 4 | const std::string TURING_EMPTY_STR{TURING_EMPTY}; 5 | 6 | // Turing turn section 7 | 8 | 9 | TuringTurn::TuringTurn() 10 | : oldState(-1), 11 | oldSymbol(TURING_EMPTY), 12 | newState(-1), 13 | newSymbol(TURING_EMPTY), 14 | direction(NoMove) {} 15 | TuringTurn::TuringTurn(int oldState, char oldSymbol, int newState, 16 | char newSymbol, TuringDirection direction) 17 | : oldState(oldState), 18 | oldSymbol(oldSymbol), 19 | newState(newState), 20 | newSymbol(newSymbol), 21 | direction(direction) {} 22 | TuringTurn::TuringTurn(int newState, char newSymbol, TuringDirection direction) 23 | : newState(newState), newSymbol(newSymbol), direction(direction) {} 24 | TuringTurn::~TuringTurn() {} 25 | 26 | // Turing tape section 27 | 28 | TuringTape::TuringTape() {} 29 | TuringTape::~TuringTape() {} 30 | 31 | void TuringTape::setChar(int pos, char c) { 32 | if(c == TURING_EMPTY){ 33 | tape.erase(pos); 34 | } else{ 35 | tape[pos] = c; 36 | } 37 | } 38 | 39 | char TuringTape::getChar(int pos) { 40 | if (tape.count(pos) == 0) { 41 | return TURING_EMPTY; 42 | } else { 43 | return tape[pos]; 44 | } 45 | } 46 | 47 | void TuringTape::clear(void){ 48 | tape.clear(); 49 | } 50 | 51 | std::map TuringTape::getTapeMap(void) { return tape; } 52 | 53 | // Turing state section 54 | 55 | TuringState::TuringState() {} 56 | TuringState::TuringState(std::pair posAndState, 57 | std::initializer_list> chars) { 58 | position = posAndState.first; 59 | currState = posAndState.second; 60 | for (auto &p : chars) { 61 | tape.setChar(p.first, p.second); 62 | } 63 | } 64 | TuringState::~TuringState() {} 65 | 66 | // Turing machine section 67 | 68 | TuringMachine::TuringMachine() {} 69 | TuringMachine::TuringMachine( 70 | std::initializer_list, TuringTurn>> turns) { 71 | for (auto &p : turns) { 72 | addTurn(p.first, p.second); 73 | } 74 | } 75 | TuringMachine::~TuringMachine() {} 76 | 77 | void TuringMachine::loadState(TuringState newState) { state = newState; } 78 | void TuringMachine::setAlph(std::string str) { 79 | alph.clear(); 80 | alph.insert(TURING_EMPTY); 81 | for (auto c : str) { 82 | alph.insert(c); 83 | } 84 | } 85 | void TuringMachine::setComment(std::string const &str) { comment = str; } 86 | 87 | void TuringMachine::addTurn(TuringTurn turn) { 88 | table[std::make_pair(turn.oldState, turn.oldSymbol)] = turn; 89 | } 90 | void TuringMachine::addTurn(std::pair old, TuringTurn turn) { 91 | table[old] = turn; 92 | } 93 | 94 | char TuringMachine::getCurChar(void) { 95 | return state.tape.getChar(state.position); 96 | } 97 | 98 | void TuringMachine::makeTurn(void) { 99 | if (stop) { 100 | return; 101 | } 102 | TuringTurn turn = table[std::make_pair(state.currState, getCurChar())]; 103 | state.tape.setChar(state.position, turn.newSymbol); 104 | if (turn.direction == Left) { 105 | state.position--; 106 | } else if (turn.direction == Right) { 107 | state.position++; 108 | } 109 | state.currState = turn.newState; 110 | if (state.currState == -1) { 111 | stop = true; 112 | } 113 | } 114 | 115 | TuringTape TuringMachine::getTape(void) { return state.tape; } 116 | 117 | std::string TuringMachine::getStrView(int from, int to) { 118 | std::string out; 119 | for (int i = from; i <= to; i++) { 120 | out += state.tape.getChar(i); 121 | } 122 | return out; 123 | } 124 | 125 | std::map, TuringTurn> &TuringMachine::getTable(void) { 126 | return table; 127 | } 128 | 129 | int TuringMachine::getCurPosition(void) { return state.position; } 130 | int TuringMachine::getCurState(void) { return state.currState; } 131 | bool TuringMachine::isDone(void) { return stop; } 132 | void TuringMachine::clear(void) { stop = false; } 133 | void TuringMachine::clearTurns(void){ table.clear(); } 134 | 135 | TuringDirection pickDirect(char c){ 136 | switch (c) { 137 | case '<': 138 | return TuringDirection::Left; 139 | case '>': 140 | return TuringDirection::Right; 141 | case '|': 142 | return TuringDirection::NoMove; 143 | default: 144 | throw(std::invalid_argument("Неизвестное направление. Код символа: " + std::to_string(c))); 145 | } 146 | } 147 | 148 | std::string pickDirectStr(TuringDirection direction) { 149 | switch (direction) { 150 | case TuringDirection::Left: 151 | return "<"; 152 | case TuringDirection::Right: 153 | return ">"; 154 | case TuringDirection::NoMove: 155 | return "|"; 156 | default: 157 | throw(std::invalid_argument("Неизвестное направление")); 158 | } 159 | } -------------------------------------------------------------------------------- /src/turingUI.cpp: -------------------------------------------------------------------------------- 1 | #include "turingUI.hpp" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | 8 | TuringCellUI::TuringCellUI(int number_) : number(number_) { input = ftxui::Input(&str, ""); Add(input);} 9 | TuringCellUI::TuringCellUI(int number_, bool isTop_) : isTop(isTop_), number(number_) { input = ftxui::Input(&str, ""); Add(input);} 10 | 11 | TuringTurn TuringCellUI::getTurn(){ 12 | char newSymbol = TURING_EMPTY; 13 | int newState = -1; 14 | TuringDirection direction = TuringDirection::NoMove; 15 | int whereToPickFrom = 0; 16 | if(str[0] != '<' && str[0] != '>' && str[0] != '|'){ 17 | newSymbol = str[0]; 18 | direction = pickDirect(str[1]); 19 | whereToPickFrom = 2; 20 | } else{ 21 | direction = pickDirect(str[0]); 22 | whereToPickFrom = 1; 23 | } 24 | if(str.size() != 1){ 25 | newState = std::stoi(str.substr(whereToPickFrom)); 26 | } 27 | return TuringTurn(newState, newSymbol, direction); 28 | } 29 | 30 | bool TuringCellUI::containStr(){ 31 | return str.size() != 0; 32 | } 33 | 34 | ftxui::Element TuringCellUI::Render(){ 35 | std::vector elems; 36 | if(isTop) 37 | elems.push_back(ftxui::vbox({ 38 | ftxui::text("Q" + std::to_string(number)), 39 | ftxui::separator(), 40 | input->Render() | ftxui::size(ftxui::WIDTH, ftxui::EQUAL, 5)}) 41 | ); 42 | else 43 | elems.push_back(input->Render() | ftxui::size(ftxui::WIDTH, ftxui::EQUAL, 5)); 44 | elems.push_back(ftxui::separator()); 45 | return ftxui::hbox(elems); 46 | } 47 | 48 | bool TuringCellUI::OnEvent(ftxui::Event event){ 49 | return this->ChildAt(0)->OnEvent(event); 50 | } 51 | 52 | void TuringCellUI::loadSaved(std::string str_){ 53 | this->str = str_; 54 | } 55 | 56 | // ROW UI 57 | 58 | TuringRowUI::TuringRowUI() { Add(ftxui::Container::Horizontal({})); } 59 | TuringRowUI::TuringRowUI(bool isTop_) : isTop(isTop_) { Add(ftxui::Container::Horizontal({})); } 60 | 61 | void TuringRowUI::addCol(){ 62 | cells.push_back(ftxui::Make(size(), isTop)); 63 | ChildAt(0)->Add(cells.back()); 64 | } 65 | void TuringRowUI::removeCol(){ 66 | if(cells.size() != 0){ 67 | ChildAt(0)->ChildAt(ChildAt(0)->ChildCount()-1)->Detach(); 68 | cells.pop_back(); 69 | } 70 | } 71 | 72 | int TuringRowUI::size(void){ 73 | return cells.size(); 74 | } 75 | 76 | bool TuringRowUI::containStr(int i){ 77 | return cells[i]->containStr(); 78 | } 79 | 80 | ftxui::Element TuringRowUI::Render(){ 81 | std::vector elems; 82 | for(int i = 0; i < cells.size(); i++){ 83 | elems.push_back(cells[i]->Render()); 84 | elems.push_back(ftxui::separator()); 85 | } 86 | return ftxui::hbox(elems) | ftxui::border; 87 | } 88 | 89 | TuringTurn TuringRowUI::getTurn(int i){ 90 | return cells[i]->getTurn(); 91 | } 92 | 93 | bool TuringRowUI::OnEvent(ftxui::Event event){ 94 | return this->ChildAt(0)->OnEvent(event); 95 | } 96 | 97 | void TuringRowUI::loadSavedCell(int col, std::string str){ 98 | this->cells[col]->loadSaved(str); 99 | } 100 | 101 | // TABLE UI 102 | TuringTableUI::TuringTableUI() { 103 | Add(ftxui::Container::Vertical({})); 104 | } 105 | 106 | ftxui::Element TuringTableUI::Render() { 107 | std::vector elems; 108 | for(int i = 0; i < rowsComponents.size(); i++){ 109 | elems.push_back(ftxui::hbox({ 110 | ftxui::text(std::string{alph[i]}), rowsComponents[i]->Render() 111 | })); 112 | } 113 | return ftxui::vbox(elems) | ftxui::frame; 114 | } 115 | 116 | bool TuringTableUI::OnEvent(ftxui::Event event) { 117 | return ChildAt(0)->OnEvent(event); 118 | } 119 | 120 | int TuringTableUI::cols(void) { 121 | if(rows() == 0){ 122 | return 0; 123 | } else{ 124 | return rowsComponents[0]->size(); 125 | } 126 | } 127 | 128 | int TuringTableUI::rows(void) { return rowsComponents.size(); } 129 | 130 | void TuringTableUI::removeRow(void) {} 131 | 132 | void TuringTableUI::removeCol(void) { 133 | for(int i = 0; i < rowsComponents.size(); i++){ 134 | rowsComponents[i]->removeCol(); 135 | } 136 | } 137 | 138 | std::vector TuringTableUI::getTurns(void){ 139 | std::vector turns; 140 | for(int i = 0; i < rows(); i++){ 141 | for(int j = 0; j < cols(); j++){ 142 | if(rowsComponents[i]->containStr(j)){ 143 | TuringTurn turn = rowsComponents[i]->getTurn(j); 144 | turn.oldState = j; 145 | turn.oldSymbol = alph[i]; 146 | turns.push_back(turn); 147 | } 148 | } 149 | } 150 | return turns; 151 | } 152 | 153 | void TuringTableUI::addRow(char c, bool isTop_) { 154 | rowsComponents.push_back(ftxui::Make(isTop_)); 155 | ChildAt(0)->Add(rowsComponents.back()); 156 | } 157 | 158 | void TuringTableUI::addCol(void) { 159 | for(int i = 0; i < rowsComponents.size(); i++){ 160 | rowsComponents[i]->addCol(); 161 | } 162 | } 163 | 164 | void TuringTableUI::updateTable(std::string alph_){ 165 | ChildAt(0)->DetachAllChildren(); 166 | rowsComponents.clear(); 167 | for(int i = 0; i < alph_.size(); i++){ 168 | addRow(alph_[i], i == 0); 169 | } 170 | alph = alph_; 171 | } 172 | 173 | void TuringTableUI::loadSavedCell(int row, int col, std::string str){ 174 | this->rowsComponents[row]->loadSavedCell(col, str); 175 | } 176 | 177 | // TAPE UI 178 | TuringTapeUI::TuringTapeUI(int size_) { 179 | size = size_; 180 | leftIndex = -(size_ / 2); 181 | tapeStrs.resize(size); 182 | for (int i = 0; i < size; i++) { 183 | tapeInputs.push_back(ftxui::Input(&tapeStrs[i], &TURING_EMPTY_STR)); 184 | } 185 | Add(ftxui::Container::Horizontal(tapeInputs)); 186 | } 187 | 188 | void TuringTapeUI::setLeftIndex(int pos){ 189 | leftIndex = pos; 190 | } 191 | 192 | bool TuringTapeUI::isValidPos(int pos) { 193 | return pos >= leftIndex && pos < leftIndex + size; 194 | } 195 | 196 | int TuringTapeUI::toLocalPos(int pos) { return pos - leftIndex; } 197 | 198 | void TuringTapeUI::setChar(char c, int pos) { 199 | if (!isValidPos(pos)) return; 200 | pos = toLocalPos(pos); 201 | if(c == TURING_EMPTY) 202 | tapeStrs[pos] = ""; 203 | else 204 | tapeStrs[pos] = c; 205 | } 206 | 207 | std::vector &TuringTapeUI::getStrs(void) { return tapeStrs;} 208 | 209 | void TuringTapeUI::setPositionAbsolute(int pos) { positionAbsolute = pos; } 210 | 211 | ftxui::Element TuringTapeUI::Render() { 212 | std::vector cells; 213 | auto tapeCursor = [this, &cells](int x, auto elem) { 214 | if (x + this->leftIndex == this->positionAbsolute) { 215 | return ftxui::vbox( 216 | {elem, 217 | ftxui::text(" ▲ ") | ftxui::color(ftxui::Color::DarkOrange3), 218 | ftxui::text(" | ") | 219 | ftxui::color(ftxui::Color::DarkOrange3)}); 220 | } else { 221 | return elem; 222 | } 223 | }; 224 | for (int i = 0; i < size; i++) { 225 | cells.push_back( 226 | ftxui::vbox({ftxui::text(std::to_string(leftIndex + i)) | 227 | ftxui::color(ftxui::Color::DarkOrange), 228 | ftxui::separatorHeavy(), 229 | tapeCursor(i, tapeInputs[i]->Render())}) | 230 | ftxui::borderLight | 231 | ftxui::size(ftxui::WIDTH, ftxui::EQUAL, CELL_SIZE)); 232 | } 233 | return ftxui::hbox({cells}); 234 | } 235 | 236 | int TuringTapeUI::getSize(void) { return size; } 237 | int TuringTapeUI::getLeftIndex(void) { return leftIndex; } 238 | 239 | bool TuringTapeUI::OnEvent(ftxui::Event event) { 240 | return ChildAt(0)->OnEvent(event); 241 | } 242 | 243 | void TuringUI::updateComponents(void){ 244 | auto tape = this->machine.getTape(); 245 | int leftIndex = this->tapeComponent->getLeftIndex(); 246 | int size = this->tapeComponent->getSize(); 247 | for(int i = 0; i < size; i++){ 248 | this->tapeComponent->setChar(tape.getChar(i + leftIndex), i + leftIndex); 249 | } 250 | this->tapeComponent->setPositionAbsolute(this->machine.getCurPosition()); 251 | } 252 | 253 | void TuringUI::makeTurn(void){ 254 | this->machine.clearTurns(); 255 | this->machine.setAlph(this->alphStr); 256 | auto v = this->tableComponent->getTurns(); 257 | for(auto turn : v){ 258 | this->machine.addTurn(turn); 259 | } 260 | this->machine.makeTurn(); 261 | updateComponents(); 262 | } 263 | 264 | void TuringUI::updateStateTape(void){ 265 | auto v = this->tapeComponent->getStrs(); 266 | int leftIndex = this->tapeComponent->getLeftIndex(); 267 | int size = this->tapeComponent->getSize(); 268 | for(int i = 0; i < size; i++){ 269 | this->state.tape.setChar(i+leftIndex, v[i].size() ? v[i][0] : TURING_EMPTY); 270 | } 271 | } 272 | 273 | // MAIN UI 274 | TuringUI::TuringUI(std::function quitFunc, ftxui::ScreenInteractive *scr_) { 275 | quit = quitFunc; 276 | scr = scr_; 277 | 278 | status.status = TuringUIStatus::START; 279 | alphStr = TURING_EMPTY_STR; 280 | 281 | state.position = 0; 282 | state.currState = 0; 283 | 284 | machine.loadState(state); 285 | 286 | helpButton = ftxui::Button("Help", [&](){ 287 | if(!this->isShowingHelp){ 288 | this->DetachAllChildren(); 289 | this->Add(this->toHelpDisplay); 290 | if(this->isRunning){ 291 | this->isRunning = false; 292 | this->status.status = TuringUIStatus::RUNNING_OFF; 293 | } 294 | this->isShowingHelp = true; 295 | } 296 | }); 297 | localeButton = ftxui::Button("Change lang", [&](){ 298 | if(this->language == LANG::L_EN){ 299 | this->language = LANG::L_RU; 300 | } else{ 301 | this->language = LANG::L_EN; 302 | } 303 | }); 304 | 305 | moveTapeLeftButton = ftxui::Button("←Move", [&]() { 306 | if(this->isResetState){ 307 | this->updateStateTape(); 308 | this->machine.loadState(this->state); 309 | this->tapeComponent->setLeftIndex(this->tapeComponent->getLeftIndex() - 5); 310 | this->updateComponents(); 311 | } else{ 312 | this->tapeComponent->setLeftIndex(this->tapeComponent->getLeftIndex() - 5); 313 | this->updateComponents(); 314 | } 315 | }); 316 | moveTapeRightButton = ftxui::Button("Move➔", [&]() { 317 | if(this->isResetState){ 318 | this->updateStateTape(); 319 | this->machine.loadState(this->state); 320 | this->tapeComponent->setLeftIndex(this->tapeComponent->getLeftIndex() + 5); 321 | this->updateComponents(); 322 | } else{ 323 | this->tapeComponent->setLeftIndex(this->tapeComponent->getLeftIndex() + 5); 324 | this->updateComponents(); 325 | } 326 | }); 327 | 328 | 329 | stepButton = ftxui::Button("Step", [this]() { 330 | if(this->status.status != TuringUIStatus::RUNNING_ON) { 331 | this->status.status = TuringUIStatus::STEP; 332 | if(this->isResetState){ 333 | this->isResetState = false; 334 | this->updateStateTape(); 335 | this->machine.loadState(this->state); 336 | this->updateComponents(); 337 | } 338 | if(!this->machine.isDone()){ 339 | this->makeTurn(); 340 | } else if(this->status.status != TuringUIStatus::STOP) { 341 | this->status.status = TuringUIStatus::STOP; 342 | } 343 | } 344 | }); 345 | 346 | runButton = ftxui::Button("Run", [&]() { 347 | this->status.status = TuringUIStatus::RUNNING_ON; 348 | if(this->isResetState){ 349 | this->isResetState = false; 350 | this->updateStateTape(); 351 | this->machine.loadState(this->state); 352 | this->updateComponents(); 353 | } 354 | if(this->isRunning){ 355 | this->status.status = TuringUIStatus::RUNNING_OFF; 356 | this->isRunning = false; 357 | } else{ 358 | this->isRunning = true; 359 | std::thread runFunc([&]{ 360 | using namespace std::chrono_literals; 361 | const auto refresh_time = 0.4s; 362 | while(this->isRunning){ 363 | if(!this->machine.isDone()){ 364 | this->makeTurn(); 365 | std::this_thread::sleep_for(refresh_time); 366 | } else if(this->status.status != TuringUIStatus::STOP) { 367 | this->status.status = TuringUIStatus::STOP; 368 | this->isRunning = false; 369 | } 370 | this->refresh(); 371 | } 372 | }); 373 | runFunc.detach(); 374 | } 375 | }); 376 | 377 | resetButton = ftxui::Button("Reset", [&](){ 378 | if(this->isResetState){ 379 | this->updateStateTape(); 380 | this->machine.loadState(this->state); 381 | this->updateComponents(); 382 | this->refresh(); 383 | } else{ 384 | this->status.status = TuringUIStatus::START; 385 | this->isResetState = true; 386 | this->isRunning = false; 387 | this->machine.clear(); 388 | this->machine.loadState(state); 389 | updateComponents(); 390 | } 391 | }); 392 | 393 | ftxui::InputOption saveOption; 394 | saveOption.on_enter = [this](void){ 395 | if(this->isResetState){ 396 | this->updateStateTape(); 397 | this->machine.loadState(this->state); 398 | this->updateComponents(); 399 | } 400 | this->saveToFile(); 401 | if(this->isErrorFile){ 402 | this->lastSaveStatus = SaveStatus::INVALID; 403 | } else{ 404 | this->lastSaveStatus = SaveStatus::OK; 405 | } 406 | this->wasTriedToSave = true; 407 | }; 408 | fileInput = ftxui::Input(&fileStr, "", saveOption); 409 | 410 | 411 | ftxui::InputOption alphInputOption; 412 | alphInputOption.on_change = [this](void) { 413 | this->needToUpdateTable = true; 414 | }; 415 | alphInput = ftxui::Input(&alphStr, "", alphInputOption); 416 | commentInput = ftxui::Input(&commentStr, ""); 417 | 418 | addButton = ftxui::Button("Add", [&]() { this->tableComponent->addCol(); }); 419 | removeButton = 420 | ftxui::Button("Remove", [&]() { this->tableComponent->removeCol(); }); 421 | 422 | tapeComponent = ftxui::Make(25); 423 | tableComponent = ftxui::Make(); 424 | 425 | toMainDisplay = ftxui::Container::Vertical( 426 | {ftxui::Container::Horizontal({helpButton, fileInput, localeButton}), 427 | ftxui::Container::Horizontal( 428 | {moveTapeLeftButton, tapeComponent, moveTapeRightButton}), 429 | ftxui::Container::Horizontal( 430 | {stepButton, runButton, resetButton, alphInput, commentInput}), 431 | ftxui::Container::Horizontal({addButton, removeButton}), 432 | tableComponent}); 433 | toHelpDisplay = ftxui::Container::Horizontal({ftxui::Button("Close help", [&]{ 434 | this->DetachAllChildren(); 435 | this->Add(toMainDisplay); 436 | this->isShowingHelp = false; 437 | }), 438 | localeButton 439 | }); 440 | Add(toMainDisplay); 441 | } 442 | 443 | 444 | void TuringUI::refresh(void){ 445 | scr->PostEvent(ftxui::Event::Custom); 446 | } 447 | 448 | ftxui::Element TuringUI::Render() { 449 | if(isShowingHelp){ 450 | auto toShowStatus = [&](TuringUIStatus::Status s){ 451 | TuringUIStatus status; 452 | status.status = s; 453 | return status.Render(); 454 | }; 455 | return ftxui::window(ftxui::text(getText("Title_help")) | ftxui::bold, 456 | ftxui::vbox({ 457 | ftxui::hbox({ 458 | toHelpDisplay->Render() | ftxui::color(ftxui::Color::Red) | ftxui::size(ftxui::WIDTH, ftxui::LESS_THAN, 30) 459 | }), 460 | breakByLinesElement( 461 | getText("Help_1") 462 | ), 463 | ftxui::hbox({ 464 | ftxui::vbox({ 465 | ftxui::vbox({ 466 | ftxui::text(getText("Help_state_start")), 467 | ftxui::separator(), 468 | toShowStatus(TuringUIStatus::START) 469 | }) | ftxui::border, 470 | ftxui::vbox({ 471 | ftxui::text(getText("Help_state_step")), 472 | ftxui::separator(), 473 | toShowStatus(TuringUIStatus::STEP) 474 | }) | ftxui::border 475 | }), 476 | ftxui::vbox({ 477 | ftxui::vbox({ 478 | ftxui::text(getText("Help_state_running_in_process")), 479 | ftxui::separator(), 480 | toShowStatus(TuringUIStatus::RUNNING_ON) 481 | }) | ftxui::border, 482 | ftxui::vbox({ 483 | ftxui::text(getText("Help_state_running_pause")), 484 | ftxui::separator(), 485 | toShowStatus(TuringUIStatus::RUNNING_OFF) 486 | }) | ftxui::border, 487 | }), 488 | ftxui::vbox({ 489 | ftxui::text(getText("Help_state_stop")), 490 | ftxui::separator(), 491 | toShowStatus(TuringUIStatus::STOP) 492 | }) | ftxui::border, 493 | }), 494 | breakByLinesElement( 495 | getText("Help_2") 496 | ), 497 | ftxui::hbox({ 498 | ftxui::vbox({ 499 | ftxui::vbox({ 500 | ftxui::text(getText("Help_button_step")), 501 | ftxui::separator(), 502 | ftxui::text("Step") | ftxui::color(ftxui::Color::SkyBlue1) 503 | }) | ftxui::border, 504 | ftxui::vbox({ 505 | ftxui::text(getText("Help_button_run")), 506 | ftxui::separator(), 507 | ftxui::text("Run") | ftxui::color(ftxui::Color::SkyBlue1) 508 | }) | ftxui::border 509 | }), 510 | ftxui::vbox({ 511 | ftxui::vbox({ 512 | ftxui::text("Help_button_pause"), 513 | ftxui::separator(), 514 | ftxui::text("Run") | ftxui::color(ftxui::Color::DarkRed) 515 | }) | ftxui::border, 516 | ftxui::vbox({ 517 | ftxui::text("Help_button_reset"), 518 | ftxui::separator(), 519 | ftxui::text("Reset") | ftxui::color(ftxui::Color::SkyBlue1) 520 | }) | ftxui::border 521 | 522 | }) 523 | }), 524 | breakByLinesElement( 525 | getText("Help_3") 526 | ), 527 | ftxui::vbox( 528 | ftxui::text("Q0"), 529 | ftxui::hbox({ 530 | ftxui::text(TURING_EMPTY_STR), 531 | ftxui::hbox({ftxui::text("1<0")}) | ftxui::border 532 | }) 533 | ), 534 | breakByLinesElement( 535 | getText("Help_4") 536 | ) 537 | }) | ftxui::yframe 538 | ); 539 | } else{ 540 | auto tapeAndButtons = ftxui::hbox( 541 | {moveTapeLeftButton->Render() | ftxui::color(ftxui::Color::DarkOrange), 542 | tapeComponent->Render(), 543 | moveTapeRightButton->Render() | 544 | ftxui::color(ftxui::Color::DarkOrange)}); 545 | 546 | auto runButtonColor = [&](){ 547 | if(this->isRunning){ 548 | return ftxui::color(ftxui::Color::DarkRed); 549 | } else{ 550 | return ftxui::color(ftxui::Color::SkyBlue1); 551 | } 552 | }; 553 | 554 | auto mainControl = ftxui::hbox( 555 | {stepButton->Render() | ftxui::color(ftxui::Color::SkyBlue1), 556 | runButton->Render() | runButtonColor(), 557 | resetButton->Render() | ftxui::color(ftxui::Color::SkyBlue1), 558 | ftxui::vbox({ftxui::text("Alphabet"), ftxui::separatorLight(), 559 | alphInput->Render()}) | 560 | ftxui::borderLight, 561 | ftxui::vbox({ftxui::text("Comment"), ftxui::separatorLight(), 562 | commentInput->Render()}) | 563 | ftxui::borderLight}); 564 | 565 | auto buttonsTable = 566 | ftxui::hbox({addButton->Render() | ftxui::color(ftxui::Color::Green), 567 | removeButton->Render() | ftxui::color(ftxui::Color::Red), 568 | ftxui::vbox({ 569 | ftxui::text("Current state"), ftxui::separatorLight(), ftxui::text(std::to_string(machine.getCurState())) 570 | }) | ftxui::borderLight 571 | }); 572 | 573 | auto fileStatusRenderer = [&](){ 574 | if(this->wasTriedToSave){ 575 | std::thread fileStatusUpdate([&]{ 576 | using namespace std::chrono_literals; 577 | const auto refresh_time = 2.0s; 578 | this->wasTriedToSave = false; 579 | std::this_thread::sleep_for(refresh_time); 580 | this->refresh(); 581 | }); 582 | fileStatusUpdate.detach(); 583 | if(this->lastSaveStatus == SaveStatus::OK){ 584 | return ftxui::text("Saving is done") | ftxui::color(ftxui::Color::Green1); 585 | } else if(this->lastSaveStatus == SaveStatus::INVALID){ 586 | return ftxui::text("Error during saving") | ftxui::color(ftxui::Color::Red1); 587 | } 588 | } else{ 589 | return ftxui::text("File name"); 590 | } 591 | }; 592 | 593 | auto mainBox = 594 | ftxui::vbox({ftxui::vbox({ftxui::text("Turing machine") | ftxui::bold, 595 | ftxui::hbox({status.Render() | ftxui::border, 596 | helpButton->Render() 597 | | ftxui::size(ftxui::WIDTH, ftxui::EQUAL, 6) 598 | | ftxui::color(ftxui::Color::Yellow1), 599 | ftxui::vbox({fileStatusRenderer(), fileInput->Render()}) | ftxui::border 600 | }) 601 | }), 602 | ftxui::separatorHeavy(), tapeAndButtons, mainControl, 603 | buttonsTable, tableComponent->Render()}) | 604 | ftxui::border; 605 | 606 | return mainBox; 607 | } 608 | } 609 | 610 | const ftxui::Event CTRL_LEFT = ftxui::Event::Special({27, 91, 49, 59, 53, 68}); 611 | const ftxui::Event CTRL_RIGHT = ftxui::Event::Special({27, 91, 49, 59, 53, 67}); 612 | const ftxui::Event CTRL_UP = ftxui::Event::Special({27, 91, 49, 59, 53, 65}); 613 | const ftxui::Event CTRL_DOWN = ftxui::Event::Special({27, 91, 49, 59, 53, 66}); 614 | 615 | bool TuringUI::OnEvent(ftxui::Event event) { 616 | if (event == ftxui::Event::Escape) { 617 | this->isRunning = false; 618 | quit(); 619 | return true; 620 | } 621 | if( event == CTRL_LEFT){ 622 | this->moveTapeLeftButton->OnEvent(ftxui::Event::Return); 623 | this->refresh(); 624 | return true; 625 | } 626 | if( event == CTRL_RIGHT){ 627 | this->moveTapeRightButton->OnEvent(ftxui::Event::Return); 628 | this->refresh(); 629 | return true; 630 | } 631 | if( event == CTRL_UP){ 632 | this->addButton->OnEvent(ftxui::Event::Return); 633 | this->refresh(); 634 | return true; 635 | } 636 | if( event == CTRL_DOWN){ 637 | this->removeButton->OnEvent(ftxui::Event::Return); 638 | this->refresh(); 639 | return true; 640 | } 641 | if( event == ftxui::Event::F5){ 642 | this->stepButton->OnEvent(ftxui::Event::Return); 643 | this->refresh(); 644 | return true; 645 | } 646 | if( event == ftxui::Event::F6){ 647 | this->runButton->OnEvent(ftxui::Event::Return); 648 | this->refresh(); 649 | return true; 650 | } 651 | if( event == ftxui::Event::F7){ 652 | this->resetButton->OnEvent(ftxui::Event::Return); 653 | this->refresh(); 654 | return true; 655 | } 656 | if( event == ftxui::Event::F9){ 657 | if(this->isShowingHelp){ 658 | this->toHelpDisplay->OnEvent(ftxui::Event::Return); 659 | } else{ 660 | this->helpButton->OnEvent(ftxui::Event::Return); 661 | } 662 | this->refresh(); 663 | return true; 664 | } 665 | if( event == ftxui::Event::F10){ 666 | this->fileInput->OnEvent(ftxui::Event::Return); 667 | this->refresh(); 668 | return true; 669 | } 670 | 671 | bool answer = ChildAt(0)->OnEvent(event); 672 | 673 | if(alphStr.size() == 0 || alphStr[0] != TURING_EMPTY){ 674 | alphStr = TURING_EMPTY_STR + alphStr; 675 | } 676 | 677 | if (needToUpdateTable) { 678 | needToUpdateTable = false; 679 | this->tableComponent->updateTable(alphStr); 680 | } 681 | 682 | return answer; 683 | } 684 | 685 | void TuringUI::saveToFile(void){ 686 | std::fstream f(this->fileStr, std::ios::out); 687 | if(!f){ 688 | this->isErrorFile = true; 689 | this->refresh(); 690 | return; 691 | }else{ 692 | this->isErrorFile = false; 693 | this->refresh(); 694 | } 695 | 696 | auto alphToSave = this->alphStr.substr(1); 697 | auto commentToSave = this->commentStr; 698 | std::vector tapePos; 699 | std::vector tapeChar; 700 | if(this->isResetState == false){ 701 | this->resetButton->OnEvent(ftxui::Event::Return); 702 | this->refresh(); 703 | } 704 | for(auto it : this->state.tape.getTapeMap()){ 705 | tapePos.push_back(it.first); 706 | tapeChar.push_back(std::string({it.second})); 707 | } 708 | 709 | std::vector oldSt; 710 | std::vector oldSym; 711 | std::vector newSt; 712 | std::vector newSym; 713 | std::vector direction; 714 | std::vector tableTurns = this->tableComponent->getTurns(); 715 | 716 | for(int i = 0; i < tableTurns.size(); i++){ 717 | oldSt.push_back(tableTurns[i].oldState); 718 | if(tableTurns[i].oldSymbol == TURING_EMPTY){ 719 | oldSym.push_back("#"); 720 | }else{ 721 | oldSym.push_back(std::string({tableTurns[i].oldSymbol})); 722 | } 723 | 724 | if(tableTurns[i].newSymbol == TURING_EMPTY){ 725 | newSym.push_back("#"); 726 | }else{ 727 | newSym.push_back(std::string({tableTurns[i].newSymbol})); 728 | } 729 | 730 | newSt.push_back(tableTurns[i].newState); 731 | direction.push_back(pickDirectStr(tableTurns[i].direction)); 732 | } 733 | 734 | json j; 735 | j["alph"] = alphToSave; 736 | j["comm"] = commentToSave; 737 | 738 | j["tape"]["pos"] = tapePos; 739 | j["tape"]["sym"] = tapeChar; 740 | 741 | j["table"]["oldSt"] = oldSt; 742 | j["table"]["oldSym"] = oldSym; 743 | j["table"]["newSt"] = newSt; 744 | j["table"]["newSym"] = newSym; 745 | j["table"]["direct"] = direction; 746 | 747 | f << std::setw(2) << j << "\n"; 748 | f.close(); 749 | } 750 | 751 | std::string &TuringUI::getText(std::string const &key){ 752 | return locale_getText(key, language); 753 | } 754 | 755 | void TuringUI::loadSave(TuringSave save){ 756 | alphStr = save.alph; 757 | 758 | needToUpdateTable = false; 759 | 760 | commentStr = save.comm; 761 | fileStr = save.fileName; 762 | 763 | state.currState = 0; 764 | state.position = 0; 765 | for(auto it : save.tape){ 766 | state.tape.setChar(it.first, it.second); 767 | } 768 | 769 | machine.loadState(state); 770 | this->tableComponent->updateTable(this->alphStr); 771 | 772 | auto alphFind = [&](char c){ 773 | for(int i = 0; i < alphStr.size(); i++){ 774 | if(c == alphStr[i]) 775 | return i; 776 | } 777 | }; 778 | 779 | auto turnToStr = [](TuringTurn turn){ 780 | std::string r = ""; 781 | r += std::string{turn.newSymbol}; 782 | r += pickDirectStr(turn.direction); 783 | r += std::to_string(turn.newState); 784 | return r; 785 | }; 786 | 787 | int cols = -1; 788 | for(auto it : save.table){ 789 | while(cols < it.first.first){ 790 | this->tableComponent->addCol(); 791 | cols++; 792 | } 793 | this->tableComponent->loadSavedCell(alphFind(it.first.second), it.first.first, turnToStr(it.second)); 794 | } 795 | 796 | 797 | updateComponents(); 798 | } 799 | 800 | std::vector TuringUI::breakByLines(std::string str){ 801 | std::istringstream ss(str); 802 | std::string t; 803 | std::vector res; 804 | while(getline(ss, t)){ 805 | res.push_back(t); 806 | } 807 | return res; 808 | } 809 | 810 | ftxui::Element TuringUI::breakByLinesElement(std::string str){ 811 | std::vector strs = breakByLines(str); 812 | ftxui::Elements list; 813 | for(int i = 0; i < strs.size(); i++){ 814 | list.push_back(ftxui::hbox({ftxui::paragraph(strs[i])}) | ftxui::flex); 815 | } 816 | return ftxui::vbox(list) | ftxui::flex; 817 | } --------------------------------------------------------------------------------