diff --git a/0n1n.json b/0n1n.json new file mode 100644 index 0000000..39bf01e --- /dev/null +++ b/0n1n.json @@ -0,0 +1,109 @@ +{ + "name": "0n1n", + "description": "{ 0^{n}1^{n} | n>0 }", + "grammar": { + "states": { + "states": [ + "q0", + "q1", + "q2", + "q3", + "q4" + ], + "initial": "q0", + "finial": [ + "q4" + ] + }, + "input": [ + "0", + "1" + ], + "alphabet": [ + "0", + "1", + "B", + "X", + "Y" + ], + "blank": "B", + "transition": { + "q0": { + "0": [ + "q1", + "X", + "R" + ], + "Y": [ + "q3", + "Y", + "R" + ] + }, + "q1": { + "Y": [ + "q1", + "Y", + "R" + ], + "0": [ + "q1", + "0", + "R" + ], + "1": [ + "q2", + "Y", + "L" + ] + }, + "q2": { + "Y": [ + "q2", + "Y", + "L" + ], + "0": [ + "q2", + "0", + "L" + ], + "X": [ + "q0", + "X", + "R" + ] + }, + "q3": { + "Y": [ + "q3", + "Y", + "R" + ], + "B": [ + "q4", + "B", + "L" + ] + }, + "q4": { + "X": [ + "q4", + "0", + "L" + ], + "Y": [ + "q4", + "1", + "L" + ], + "B": [ + "q4", + "B", + "R" + ] + } + } + }, + "demo": "000111" +} \ No newline at end of file diff --git a/Alan_Turing.ico b/Alan_Turing.ico new file mode 100644 index 0000000..fe04d01 Binary files /dev/null and b/Alan_Turing.ico differ diff --git a/TuringMachine.pro b/TuringMachine.pro new file mode 100644 index 0000000..bbce3cc --- /dev/null +++ b/TuringMachine.pro @@ -0,0 +1,42 @@ +#------------------------------------------------- +# +# Project created by QtCreator 2018-05-03T23:17:18 +# +#------------------------------------------------- + +QT += core gui + +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets + +TARGET = TuringMachine +TEMPLATE = app + +# The following define makes your compiler emit warnings if you use +# any feature of Qt which has been marked as deprecated (the exact warnings +# depend on your compiler). Please consult the documentation of the +# deprecated API in order to know how to port your code away from it. +DEFINES += QT_DEPRECATED_WARNINGS + +# You can also make your code fail to compile if you use deprecated APIs. +# In order to do so, uncomment the following line. +# You can also select to disable deprecated APIs only up to a certain version of Qt. +#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 + + +SOURCES += \ + main.cpp \ + mainwindow.cpp \ + turingmachine.cpp \ + centralwidget.cpp \ + papertape.cpp + +HEADERS += \ + mainwindow.h \ + turingmachine.h \ + centralwidget.h \ + papertape.h + +FORMS += \ + mainwindow.ui + +RC_ICONS = Alan_Turing.ico diff --git a/centralwidget.cpp b/centralwidget.cpp new file mode 100644 index 0000000..73b2bca --- /dev/null +++ b/centralwidget.cpp @@ -0,0 +1,178 @@ +#include "centralwidget.h" + +CentralWidget::CentralWidget(QWidget *parent) : QWidget(parent) +{ + machine = new TuringMachine(this); + +// nameEdit = new QLineEdit(this); +// nameEdit->setReadOnly(true); + autoCheckTimer = new QTimer(this); + connect(autoCheckTimer, QTimer::timeout, this, [this](){ + checkByStep->click(); + }); + + teststrEdit = new QLineEdit(this); + + allStatesEdit = new QLineEdit(this); + allStatesEdit->setReadOnly(true); + inputEdit = new QLineEdit(this); + inputEdit->setReadOnly(true); + alphabetEdit = new QLineEdit(this); + alphabetEdit->setReadOnly(true); + initialStateEdit = new QLineEdit(this); + initialStateEdit->setReadOnly(true); + blankEdit = new QLineEdit(this); + blankEdit->setReadOnly(true); + finialStatesEdit = new QLineEdit(this); + finialStatesEdit->setReadOnly(true); + + listWidget = new QListWidget(this); + + importButton = new QPushButton(tr("导入配置"), this); + connect(importButton, QPushButton::clicked, this, importJson); + checkByStep = new QPushButton(tr("单步检查"), this); + checkByStep->setDisabled(true); + connect(checkByStep, QPushButton::clicked, this, stepCheck); + autoCheckButton = new QPushButton(tr("自动模式"), this); + autoCheckButton->setDisabled(true); + connect(autoCheckButton, QPushButton::clicked, this, autoCheck); + loadButton = new QPushButton(tr("装载纸带"), this); + loadButton->setDisabled(true); + connect(loadButton, QPushButton::clicked, this, loadTape); + resetButton = new QPushButton(tr("重置"), this); + connect(resetButton, QPushButton::clicked, this, [this](){ + machine->reset(); + loadButton->setEnabled(true); + autoCheckButton->setDisabled(true); + checkByStep->setDisabled(true); + checkByStep->setChecked(false); + }); + + tape = new PaperTape(); + scene = new QGraphicsScene(this); + scene->setSceneRect(-250, -65, 500, 130); + scene->addItem(tape); + scene->addLine(0,0,0,40,QPen(Qt::red)); + view = new QGraphicsView(scene ,this); + + + formlayout = new QFormLayout(); +// formlayout->setSpacing(10); +// formlayout->setFieldGrowthPolicy(QFormLayout::FieldsStayAtSizeHint); +// formlayout->addRow(tr("Name"), nameEdit); + formlayout->addRow(tr("状态集合"), allStatesEdit); + formlayout->addRow(tr("输入字符"), inputEdit); + formlayout->addRow(tr("字符表"), alphabetEdit); + formlayout->addRow(tr("初始状态"), initialStateEdit); + formlayout->addRow(tr("空白符"), blankEdit); + formlayout->addRow(tr("终止状态"), finialStatesEdit); + formlayout->addRow(tr("Test string"), teststrEdit); +// formlayout->addRow(loadButton); +// formlayout->addRow(checkByStep); + + vsublayout = new QVBoxLayout(); + vsublayout->addWidget(importButton); + vsublayout->addWidget(resetButton); + vsublayout->addWidget(loadButton); + vsublayout->addWidget(autoCheckButton); + vsublayout->addWidget(checkByStep); + + hlayout = new QHBoxLayout(); + hlayout->addLayout(formlayout); + hlayout->addWidget(listWidget); + hlayout->addLayout(vsublayout); +// hlayout->addWidget(importButton); + + + mainlayout = new QVBoxLayout(this); + mainlayout->addLayout(hlayout); + mainlayout->addWidget(view); + + setLayout(mainlayout); +} + +CentralWidget::~CentralWidget() +{ + delete machine; + delete tape; +} + +void CentralWidget::importJson() +{ + QString filename = QFileDialog::getOpenFileName(this, tr("import json"), + ".",tr("json(*.json)")); + if(filename.isEmpty()) + return; + if(!machine->setGrammar(filename)) + { + setStatus("请检查文件格式"); + return; + }; + setStatus(tr("导入成功")); + if(machine->hasGrammar()) + { + loadButton->setEnabled(true); +// checkByStep->setEnabled(true); + } + else + loadButton->setDisabled(false); +// nameEdit->setText(machine->getName()); + allStatesEdit->setText(machine->getAllStates().join(", ")); + alphabetEdit->setText(machine->getAlphabet().split("", QString::SkipEmptyParts).join(", ")); + inputEdit->setText(machine->getInput().split("", QString::SkipEmptyParts).join(", ")); + initialStateEdit->setText(machine->getInitialState()); + blankEdit->setText(machine->getBlank()); + finialStatesEdit->setText(machine->getFinialStates().join(", ")); + listWidget->clear(); + listWidget->addItems(machine->getTransition()); + + if(teststrEdit->text().isEmpty()) + { + teststrEdit->setText(machine->getRawStr()); + } + tape->adjustPos(); +} + +void CentralWidget::loadTape() +{ + machine->setTestStr(teststrEdit->text()); + machine->reset(); + tape->setStr(machine->getTestStr()); + tape->standBy(1); + checkByStep->setEnabled(true); + autoCheckButton->setEnabled(true); + machineReady = true; +} + +void CentralWidget::stepCheck() +{ + machineReady = machine->checkByStep(); + if(machineReady) + { + loadButton->setDisabled(true); + tape->standBy(machine->getCurrentIndex()); + tape->setStr(machine->getTestStr()); + listWidget->setCurrentRow(machine->getTransitionIndex()); + } + else + { + loadButton->setEnabled(true); + autoCheckButton->setDisabled(true); + checkByStep->setDisabled(true); + checkByStep->setChecked(false); + autoCheckTimer->stop(); + if(machine->getFinialStates().contains(machine->getCurrentState())) + { + emit setStatus("Correct!"); + } + else + { + emit setStatus("Not OK!"); + } + } +} + +void CentralWidget::autoCheck() +{ + autoCheckTimer->start(300); +} diff --git a/centralwidget.h b/centralwidget.h new file mode 100644 index 0000000..a04bd8a --- /dev/null +++ b/centralwidget.h @@ -0,0 +1,56 @@ +#ifndef CENTRALWIDGET_H +#define CENTRALWIDGET_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "turingmachine.h" +#include "papertape.h" + +class CentralWidget : public QWidget +{ + Q_OBJECT +public: + explicit CentralWidget(QWidget *parent = nullptr); + ~CentralWidget(); + +signals: + void setStatus(QString message); + +public slots: + void importJson(); + void loadTape(); + void stepCheck(); + void autoCheck(); + + +private: + QVBoxLayout *mainlayout, *vsublayout; + QFormLayout *formlayout; + QHBoxLayout *hlayout; + QLineEdit *nameEdit, *teststrEdit; + QLineEdit *allStatesEdit, *alphabetEdit, *inputEdit, *initialStateEdit, *blankEdit, *finialStatesEdit; + QPushButton *importButton; + QPushButton *loadButton, *checkByStep, *resetButton, *autoCheckButton; + + QListWidget *listWidget; + + QGraphicsScene *scene; + QGraphicsView *view; + PaperTape *tape; + + QTimer *autoCheckTimer; + + TuringMachine *machine; + bool machineReady = true; +}; + +#endif // CENTRALWIDGET_H diff --git a/m-n.json b/m-n.json new file mode 100644 index 0000000..bf8cd94 --- /dev/null +++ b/m-n.json @@ -0,0 +1,126 @@ +{ + "name": "m-n", + "description": "calculate m-n", + "grammar": { + "states": { + "states": [ + "q0", + "q1", + "q2", + "q3", + "q4", + "q5", + "q6" + ], + "initial": "q0", + "finial": [ + "q6" + ] + }, + "input": [ + "0", + "1" + ], + "alphabet": [ + "0", + "1", + "B" + ], + "blank": "B", + "transition": { + "q0": { + "0": [ + "q1", + "B", + "R" + ], + "1": [ + "q5", + "B", + "R" + ] + }, + "q1": { + "0": [ + "q1", + "0", + "R" + ], + "1": [ + "q2", + "1", + "R" + ] + }, + "q2": { + "1": [ + "q2", + "1", + "R" + ], + "B": [ + "q3", + "B", + "L" + ], + "0": [ + "q4", + "1", + "L" + ] + }, + "q3": { + "0": [ + "q3", + "0", + "L" + ], + "1": [ + "q3", + "B", + "L" + ], + "B": [ + "q6", + "0", + "S" + ] + }, + "q4": { + "0": [ + "q4", + "0", + "L" + ], + "1": [ + "q4", + "1", + "L" + ], + "B": [ + "q0", + "B", + "R" + ] + }, + "q5": { + "0": [ + "q5", + "B", + "R" + ], + "1": [ + "q5", + "B", + "R" + ], + "B": [ + "q6", + "B", + "S" + ] + } + } + }, + "demo": "00000100" +} \ No newline at end of file diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..260020e --- /dev/null +++ b/main.cpp @@ -0,0 +1,21 @@ +#include "mainwindow.h" +#include +#include "turingmachine.h" +#include + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + a.setFont(QFont("Microsoft YaHei UI",9)); + MainWindow w; + w.show(); + +// TuringMachine tm; +// tm.setGrammar("0n1n.json"); +// tm.setTestStr(""); +// if(tm.run()) +// qDebug()<<"OK"; +// else +// qDebug()<<"not ok"; + return a.exec(); +} diff --git a/mainwindow.cpp b/mainwindow.cpp new file mode 100644 index 0000000..ba7de5b --- /dev/null +++ b/mainwindow.cpp @@ -0,0 +1,41 @@ +#include "mainwindow.h" +#include "ui_mainwindow.h" + +MainWindow::MainWindow(QWidget *parent) : + QMainWindow(parent) +// ui(new Ui::MainWindow) +{ +// ui->setupUi(this); + cw = new CentralWidget(this); + helpMenu = new QMenu(this); + aboutAction = new QAction(tr("关于"), this); + connect(aboutAction, QAction::triggered, this, showAbout); + helpMenu->setTitle(tr("帮助(&H)")); + helpMenu->addAction(aboutAction); + setCentralWidget(cw); + menuBar()->addMenu(helpMenu); + statusBar()->showMessage("欢迎使用环保图灵机!"); + connect(cw, CentralWidget::setStatus, this,[this](QString message) + { + statusBar()->showMessage(message, 0); + }); + +} + +MainWindow::~MainWindow() +{ + delete cw; +} + +void MainWindow::showAbout() +{ + QMessageBox *aboutWindow = new QMessageBox(this); + aboutWindow->setStandardButtons(QMessageBox::Ok); + aboutWindow->setText(tr("

TuringMachine

" + "

Based on Qt 5.10.1 (MinGW 5.3.0, 32bit)

" + "Source Code: https://github.com/c-my/TuringMachine
" + "Email: " + "
cmy1113@outlook.com" + "
")); + aboutWindow->show(); +} diff --git a/mainwindow.h b/mainwindow.h new file mode 100644 index 0000000..4955605 --- /dev/null +++ b/mainwindow.h @@ -0,0 +1,28 @@ +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include +#include +#include +#include +#include "centralwidget.h" + + + +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + explicit MainWindow(QWidget *parent = 0); + ~MainWindow(); + +private: + CentralWidget *cw; + QMenu *helpMenu; + QAction *aboutAction; + + void showAbout(); +}; + +#endif // MAINWINDOW_H diff --git a/mainwindow.ui b/mainwindow.ui new file mode 100644 index 0000000..6050363 --- /dev/null +++ b/mainwindow.ui @@ -0,0 +1,24 @@ + + MainWindow + + + + 0 + 0 + 400 + 300 + + + + MainWindow + + + + + + + + + + + diff --git a/papertape.cpp b/papertape.cpp new file mode 100644 index 0000000..bfd09a2 --- /dev/null +++ b/papertape.cpp @@ -0,0 +1,64 @@ +#include "papertape.h" + +PaperTape::PaperTape(QGraphicsItem *parent): QGraphicsObject(parent) +{ + ani = new QPropertyAnimation(this, "pos"); + this->setStr("hello"); + this->adjustPos(); +} + +QRectF PaperTape::boundingRect() const +{ + return QRectF(0 ,0 ,str.size()*width, height); +} + +void PaperTape::setStr(QString str) +{ + this->str = str; + lastLenth = lenth; + lenth = str.size(); +// this->adjustPos(); + this->update(); +} + +int PaperTape::getWidth() +{ + return lenth*width; +} + +void PaperTape::standBy(int index) +{ +// ani->setStartValue(this->pos()); + ani->setEndValue(QPointF(-(index+0.5)*width, this->pos().y())); + ani->setDuration(230); + ani->start(); +} + +void PaperTape::adjustPos() +{ + setPos(-str.size()*width/2, -height); +} + +void PaperTape::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) +{ + painter->setBackground(QBrush(Qt::white)); + painter->eraseRect(/*-lastLenth*width/2, -height, width*1000, height*/boundingRect()); + painter->setBrush(QBrush("lightblue")); + painter->setPen(Qt::NoPen); + painter->drawRect(0, 0, lenth*width, height); + painter->setPen(Qt::black); + painter->setPen(Qt::DotLine); + painter->setFont(QFont("Microsoft Yahei", 13)); + for(int i=0; idrawLine(0+i*width, 0, 0+i*width, height); + } + // 画字符 + painter->drawText(i*width+width/2-4, height/2+8, str.at(i)); + } + setToolTip(this->str); +// setPos(-str.size()*width/2, -height); +} diff --git a/papertape.h b/papertape.h new file mode 100644 index 0000000..c18b524 --- /dev/null +++ b/papertape.h @@ -0,0 +1,28 @@ +#ifndef PAPERTAPE_H +#define PAPERTAPE_H + +#include +#include +#include + +class PaperTape : public QGraphicsObject +{ + Q_PROPERTY(QPoinF pos READ pos WRITE setPos) +public: + PaperTape(QGraphicsItem *parent = Q_NULLPTR); + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = Q_NULLPTR); + QRectF boundingRect() const; + void setStr(QString str); + int getWidth(); + void standBy(int index); + void adjustPos(); + +private: + int height = 50; + int width = 50; + QString str; + int lenth=0, lastLenth=0; + QPropertyAnimation *ani; +}; + +#endif // PAPERTAPE_H diff --git a/turingmachine.cpp b/turingmachine.cpp new file mode 100644 index 0000000..827520a --- /dev/null +++ b/turingmachine.cpp @@ -0,0 +1,146 @@ +#include "turingmachine.h" + +TuringMachine::TuringMachine(QObject *parent) : QObject(parent) +{ + currentIndex = 1; + isRunning = true; +} + +bool TuringMachine::setGrammar(QString filename) +{ + QFile jsonFile(filename); + if(!jsonFile.exists()) + return false; + jsonFile.open(QIODevice::ReadOnly|QIODevice::Text); + jsonobj = QJsonDocument::fromJson(jsonFile.readAll()).object(); + jsonFile.close(); + if(jsonobj.isEmpty()||!jsonobj.contains("grammar")) + return false; + grammar = jsonobj.value("grammar").toObject(); + if(!grammar.contains("states") + ||!grammar.contains("input") + ||!grammar.contains("alphabet") + ||!grammar.contains("blank") + ||!grammar.contains("transition")) + return false; + //获取初始状态 + initialState = grammar.value("states").toObject().value("initial").toString(); + currentState = initialState; + + //获取终止状态 + finialStates.clear(); + QJsonArray array = grammar.value("states").toObject().value("finial").toArray(); + for(auto item: array) + finialStates.append(item.toString()); + + //获取状态集合 + allStates.clear(); + array = grammar.value("states").toObject().value("states").toArray(); + for(auto item: array) + allStates.append(item.toString()); + + //获取空白符 + blank = grammar.value("blank").toString().at(0); + + //获取字符表 + alphabet.clear(); + array = grammar.value("alphabet").toArray(); + for(auto item: array) + alphabet += (item.toString()); + + //获取接受字符 + input.clear(); + array = grammar.value("input").toArray(); + for(auto item: array) + input += (item.toString()); + + //获取转移字符 + transition.clear(); + QStringList keys = grammar.value("transition").toObject().keys(); + for(auto key: keys) + { + QJsonObject subobject = grammar.value("transition").toObject().value(key).toObject(); + QStringList subkeys = subobject.keys(); + for(auto subkey: subkeys) + { + QJsonArray array = subobject.value(subkey).toArray(); + QString tmp = "δ(" + key +", " + subkey + ") = ("; + for(auto item: array) + tmp += item.toString()+", "; + tmp.remove(tmp.length()-2, 2); + tmp+=")"; + transition.append(tmp); + } + } + + setTestStr(jsonobj.value("demo").toString()); + name = jsonobj.value("name").toString(); + return true; +} + +void TuringMachine::setTestStr(QString str) +{ + rawStr = str; + testStr = blank + str + blank; + isRunning = true; +} + +bool TuringMachine::checkByStep() +{ + QJsonValue value = grammar.value("transition").toObject().value(currentState) + .toObject().value(testStr.at(currentIndex)); + if(!value.isUndefined()) + { + QString listItem; + listItem = "δ(" +currentState + ", " + testStr.at(currentIndex) + ") = ("; + currentState = value.toArray()[0].toString(); //更新当前状态 + testStr[currentIndex] = value.toArray()[1].toString()[0]; //更新当前字符 + QString move = value.toArray()[2].toString(); + listItem += currentState +", " + testStr[currentIndex] + ", " + move+")"; + transitionIndex = transition.indexOf(listItem); + + if(move == "L") + currentIndex = (currentIndex - 1 >= 0) ? (currentIndex - 1) : 0; + else if(move == "R") + currentIndex = (currentIndex + 1 < testStr.size()) ? + (currentIndex + 1) : testStr.size()-1; + else if(move == "S") + currentIndex = currentIndex; + return true; + } + else + return false; + +} + +bool TuringMachine::run() +{ + while (isRunning) { + checkByStep(); + } + if(finialStates.contains(currentState)) + return true; + else + return false; +} + +bool TuringMachine::hasGrammar() +{ + return !this->jsonobj.isEmpty(); +} + +bool TuringMachine::isStopped() +{ + return !isRunning; +} + +void TuringMachine::reset() +{ + currentIndex = 1; + currentState = initialState; +// transition.clear(); +// input.clear(); +// alphabet.clear(); +// finialStates.clear(); +// allStates.clear(); +} diff --git a/turingmachine.h b/turingmachine.h new file mode 100644 index 0000000..b3bd185 --- /dev/null +++ b/turingmachine.h @@ -0,0 +1,57 @@ +#ifndef TURINGMACHINE_H +#define TURINGMACHINE_H + +#include +#include +#include +#include +#include +#include +#include +#include + +class TuringMachine : public QObject +{ + Q_OBJECT +public: + TuringMachine(QObject *parent = nullptr); + bool setGrammar(QString filename); + void setTestStr(QString str); + QString getTestStr() { return testStr; } + QString getRawStr() { return rawStr; } + QString getName() { return name; } + QString getInitialState() { return initialState; } + QChar getBlank() { return blank; } + QStringList getFinialStates() { return finialStates; } + QStringList getAllStates() { return allStates; } + QString getAlphabet() { return alphabet; } + QString getInput() { return input; } + int getCurrentIndex() { return currentIndex; } + QStringList getTransition() { return transition; } + int getTransitionIndex() { return transitionIndex; } + QString getCurrentState() { return currentState; } + bool checkByStep(); + bool run(); + bool hasGrammar(); + bool isStopped(); + void reset(); + +private: + QJsonObject jsonobj; + QJsonObject grammar; + QString name; + QString rawStr, testStr; + QChar blank; //空白符 + QString currentState; //当前状态 + int currentIndex; //当前字符索引号 + int transitionIndex; + QString initialState; + QStringList finialStates; + QStringList allStates; + QString input; + QString alphabet; + QStringList transition; + bool isRunning; +}; + +#endif // TURINGMACHINE_H