Xinqi Bao's Git

first commit, implemented game wordscapes master
authorXinqi Bao <[email protected]>
Sat, 2 May 2020 20:07:46 +0000 (16:07 -0400)
committerXinqi Bao <[email protected]>
Sat, 2 May 2020 20:07:46 +0000 (16:07 -0400)
23 files changed:
.gitignore [new file with mode: 0644]
LICENSE [new file with mode: 0644]
README.md [new file with mode: 0644]
client/.gitignore [new file with mode: 0644]
client/CheaterHub/CheaterHub.pro [new file with mode: 0644]
client/CheaterHub/cfg.cpp [new file with mode: 0644]
client/CheaterHub/cfg.h [new file with mode: 0644]
client/CheaterHub/main.cpp [new file with mode: 0644]
client/CheaterHub/mainwindow.cpp [new file with mode: 0644]
client/CheaterHub/mainwindow.h [new file with mode: 0644]
client/CheaterHub/mainwindow.ui [new file with mode: 0644]
client/CheaterHub/setting.cpp [new file with mode: 0644]
client/CheaterHub/setting.h [new file with mode: 0644]
client/CheaterHub/setting.ui [new file with mode: 0644]
server/Makefile [new file with mode: 0644]
server/cheaterHub.cc [new file with mode: 0644]
server/tools/generateDict.py [new file with mode: 0755]
server/wordscapes/.gitignore [new file with mode: 0644]
server/wordscapes/Makefile [new file with mode: 0644]
server/wordscapes/dict/myDictionary [new file with mode: 0644]
server/wordscapes/test.cc [new file with mode: 0644]
server/wordscapes/wordscapes.cc [new file with mode: 0644]
server/wordscapes/wordscapes.h [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..9713557
--- /dev/null
@@ -0,0 +1,2 @@
+tags
+build*/
diff --git a/LICENSE b/LICENSE
new file mode 100644 (file)
index 0000000..18536e8
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,22 @@
+MIT License
+
+Copyright (c) 2020 XinqiBao
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
diff --git a/README.md b/README.md
new file mode 100644 (file)
index 0000000..061a870
--- /dev/null
+++ b/README.md
@@ -0,0 +1,35 @@
+# CheaterHub
+
+A portable software for lovely cheaters, C++ Back-end, with Qt Front-end, self-practice project.
+
+A simple customized asynchronous TCP for Back-end, by using multi-threading.
+
+Implemented game(s):
+
++ Wordscapes
+
+## Run
+
+### Qt Front-end
+
+Located in `client/CheateHub`, build it, and config Back-end ip and port information at Config session in the application.
+
+### Back-end
+
+Build by running `make` from `server/` directory, `make MODE=release` for releasing. Executable file will locate in `server/build/`.
+
+#### Wordscapes
+
+Module locate in `server/wordscapes/`.
+
+For first time, have to generate dictionary before running, by using script `server/tools/generateDict.py`.
+
+For testing this module individually, build by running `make test`.
+
+## TODO
+
+Add config feature for back-end.
+
+Makefile for server could be improved.
+
+Message format for communicating between front-end and back-end could be improved, especially when implemented more games.
diff --git a/client/.gitignore b/client/.gitignore
new file mode 100644 (file)
index 0000000..75c107b
--- /dev/null
@@ -0,0 +1 @@
+*.pro.user
diff --git a/client/CheaterHub/CheaterHub.pro b/client/CheaterHub/CheaterHub.pro
new file mode 100644 (file)
index 0000000..12916b9
--- /dev/null
@@ -0,0 +1,36 @@
+QT       += core gui network\r
+\r
+greaterThan(QT_MAJOR_VERSION, 4): QT += widgets\r
+\r
+CONFIG += c++11\r
+\r
+# The following define makes your compiler emit warnings if you use\r
+# any Qt feature that has been marked deprecated (the exact warnings\r
+# depend on your compiler). Please consult the documentation of the\r
+# deprecated API in order to know how to port your code away from it.\r
+DEFINES += QT_DEPRECATED_WARNINGS\r
+\r
+# You can also make your code fail to compile if it uses deprecated APIs.\r
+# In order to do so, uncomment the following line.\r
+# You can also select to disable deprecated APIs only up to a certain version of Qt.\r
+#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0\r
+\r
+SOURCES += \\r
+    cfg.cpp \\r
+    main.cpp \\r
+    mainwindow.cpp \\r
+    setting.cpp\r
+\r
+HEADERS += \\r
+    cfg.h \\r
+    mainwindow.h \\r
+    setting.h\r
+\r
+FORMS += \\r
+    mainwindow.ui \\r
+    setting.ui\r
+\r
+# Default rules for deployment.\r
+qnx: target.path = /tmp/$${TARGET}/bin\r
+else: unix:!android: target.path = /opt/$${TARGET}/bin\r
+!isEmpty(target.path): INSTALLS += target\r
diff --git a/client/CheaterHub/cfg.cpp b/client/CheaterHub/cfg.cpp
new file mode 100644 (file)
index 0000000..99b7077
--- /dev/null
@@ -0,0 +1,24 @@
+#include "cfg.h"\r
+#include <QSettings>\r
+\r
+Cfg::Cfg(QString filename):\r
+    filename(filename),\r
+    setting(filename, QSettings::IniFormat)\r
+{\r
+    setting.beginGroup("wordscapes");\r
+    if(!setting.childKeys().contains("ip"))\r
+        setting.setValue("ip", "127.0.0.1");\r
+    if(!setting.childKeys().contains("port"))\r
+        setting.setValue("port", 2020);\r
+    wordscapes_ip = setting.value("ip").toString();\r
+    wordscapes_port = setting.value("port").toInt();\r
+    setting.endGroup();\r
+}\r
+\r
+void Cfg::save_config()\r
+{\r
+    setting.beginGroup("wordscapes");\r
+    setting.setValue("ip", wordscapes_ip);\r
+    setting.setValue("port", wordscapes_port);\r
+    setting.endGroup();\r
+}\r
diff --git a/client/CheaterHub/cfg.h b/client/CheaterHub/cfg.h
new file mode 100644 (file)
index 0000000..3728d1b
--- /dev/null
@@ -0,0 +1,21 @@
+#ifndef CFG_H\r
+#define CFG_H\r
+\r
+#include <QString>\r
+#include <QSettings>\r
+\r
+class Cfg\r
+{\r
+private:\r
+    QString filename;\r
+    QSettings setting;\r
+public:\r
+    Cfg(QString filename = "config");\r
+\r
+    QString wordscapes_ip;\r
+    int wordscapes_port;\r
+\r
+    void save_config();\r
+};\r
+\r
+#endif // CFG_H\r
diff --git a/client/CheaterHub/main.cpp b/client/CheaterHub/main.cpp
new file mode 100644 (file)
index 0000000..5d36338
--- /dev/null
@@ -0,0 +1,12 @@
+#include "mainwindow.h"\r
+#include "cfg.h"\r
+#include <QApplication>\r
+\r
+int main(int argc, char *argv[])\r
+{\r
+    QApplication a(argc, argv);\r
+    Cfg cfg;\r
+    MainWindow w(cfg);\r
+    w.show();\r
+    return a.exec();\r
+}\r
diff --git a/client/CheaterHub/mainwindow.cpp b/client/CheaterHub/mainwindow.cpp
new file mode 100644 (file)
index 0000000..7690725
--- /dev/null
@@ -0,0 +1,61 @@
+#include "mainwindow.h"\r
+#include "setting.h"\r
+#include "ui_mainwindow.h"\r
+#include <QDebug>\r
+#include <thread>\r
+#include <QSettings>\r
+#include <QAction>\r
+#include <QDialog>\r
+\r
+MainWindow::MainWindow(Cfg& cfg, QWidget *parent)\r
+    : QMainWindow(parent)\r
+    , cfg(cfg)\r
+    , ui(new Ui::MainWindow)\r
+{\r
+    ui->setupUi(this);\r
+    socket = new QTcpSocket();\r
+\r
+    //Disable tab switching for several controls\r
+    ui->tabWidget->setFocusPolicy(Qt::NoFocus);\r
+    ui->btn_socket->setFocusPolicy(Qt::NoFocus);\r
+    ui->list_result->setFocusPolicy(Qt::NoFocus);\r
+\r
+    connect(ui->actionConfig, &QAction::triggered, this, [=]()\r
+    {\r
+        Setting setting_form(this->cfg);\r
+        setting_form.exec();\r
+    });\r
+    connect(ui->lineEdit_given, &QLineEdit::returnPressed, this, &MainWindow::select_lineEdit_search);\r
+    connect(ui->lineEdit_search, &QLineEdit::returnPressed, ui->btn_socket, &QPushButton::click);\r
+    connect(socket, &QTcpSocket::readyRead, this, &MainWindow::on_socket_msg_receive);\r
+}\r
+\r
+MainWindow::~MainWindow()\r
+{\r
+    delete ui;\r
+    delete this->socket;\r
+}\r
+\r
+void MainWindow::on_btn_socket_clicked()\r
+{\r
+    socket->connectToHost(cfg.wordscapes_ip, cfg.wordscapes_port);\r
+    QString msg(ui->lineEdit_given->text() + ";" + ui->lineEdit_search->text());\r
+    socket->write(msg.toUtf8());\r
+    qDebug() << "Msg send: " << msg;\r
+\r
+    ui->list_result->clear();\r
+    select_lineEdit_search();\r
+}\r
+\r
+void MainWindow::on_socket_msg_receive()\r
+{\r
+    QByteArray buffer = socket->readAll();\r
+    ui->list_result->addItems(QString(buffer).split(';'));\r
+    qDebug() << buffer;\r
+}\r
+\r
+void MainWindow::select_lineEdit_search()\r
+{\r
+    ui->lineEdit_search->setFocus();\r
+    ui->lineEdit_search->selectAll();\r
+}\r
diff --git a/client/CheaterHub/mainwindow.h b/client/CheaterHub/mainwindow.h
new file mode 100644 (file)
index 0000000..8d3933c
--- /dev/null
@@ -0,0 +1,30 @@
+#ifndef MAINWINDOW_H\r
+#define MAINWINDOW_H\r
+\r
+#include "cfg.h"\r
+#include <QMainWindow>\r
+#include <QTcpSocket>\r
+\r
+QT_BEGIN_NAMESPACE\r
+namespace Ui { class MainWindow; }\r
+QT_END_NAMESPACE\r
+\r
+class MainWindow : public QMainWindow\r
+{\r
+    Q_OBJECT\r
+\r
+public:\r
+    MainWindow(Cfg& cfg, QWidget *parent = nullptr);\r
+    ~MainWindow();\r
+\r
+private:\r
+    Cfg& cfg;\r
+    Ui::MainWindow *ui;\r
+    QTcpSocket *socket;\r
+\r
+private slots:\r
+    void select_lineEdit_search();\r
+    void on_btn_socket_clicked();\r
+    void on_socket_msg_receive();\r
+};\r
+#endif // MAINWINDOW_H\r
diff --git a/client/CheaterHub/mainwindow.ui b/client/CheaterHub/mainwindow.ui
new file mode 100644 (file)
index 0000000..5b74bbd
--- /dev/null
@@ -0,0 +1,288 @@
+<?xml version="1.0" encoding="UTF-8"?>\r
+<ui version="4.0">\r
+ <class>MainWindow</class>\r
+ <widget class="QMainWindow" name="MainWindow">\r
+  <property name="geometry">\r
+   <rect>\r
+    <x>0</x>\r
+    <y>0</y>\r
+    <width>800</width>\r
+    <height>600</height>\r
+   </rect>\r
+  </property>\r
+  <property name="windowTitle">\r
+   <string>CheaterHub</string>\r
+  </property>\r
+  <widget class="QWidget" name="centralwidget">\r
+   <layout class="QHBoxLayout" name="horizontalLayout_3">\r
+    <item>\r
+     <widget class="QTabWidget" name="tabWidget">\r
+      <property name="currentIndex">\r
+       <number>0</number>\r
+      </property>\r
+      <widget class="QWidget" name="tab">\r
+       <attribute name="title">\r
+        <string>Wordscapes</string>\r
+       </attribute>\r
+       <layout class="QHBoxLayout" name="horizontalLayout_2">\r
+        <item>\r
+         <layout class="QVBoxLayout" name="verticalLayout">\r
+          <item>\r
+           <spacer name="verticalSpacer_2">\r
+            <property name="orientation">\r
+             <enum>Qt::Vertical</enum>\r
+            </property>\r
+            <property name="sizeType">\r
+             <enum>QSizePolicy::Maximum</enum>\r
+            </property>\r
+            <property name="sizeHint" stdset="0">\r
+             <size>\r
+              <width>20</width>\r
+              <height>50</height>\r
+             </size>\r
+            </property>\r
+           </spacer>\r
+          </item>\r
+          <item>\r
+           <layout class="QGridLayout" name="gridLayout">\r
+            <item row="0" column="1">\r
+             <widget class="QLabel" name="label_given">\r
+              <property name="minimumSize">\r
+               <size>\r
+                <width>0</width>\r
+                <height>0</height>\r
+               </size>\r
+              </property>\r
+              <property name="font">\r
+               <font>\r
+                <pointsize>12</pointsize>\r
+               </font>\r
+              </property>\r
+              <property name="text">\r
+               <string>Given:</string>\r
+              </property>\r
+             </widget>\r
+            </item>\r
+            <item row="0" column="0">\r
+             <spacer name="horizontalSpacer_3">\r
+              <property name="orientation">\r
+               <enum>Qt::Horizontal</enum>\r
+              </property>\r
+              <property name="sizeHint" stdset="0">\r
+               <size>\r
+                <width>40</width>\r
+                <height>20</height>\r
+               </size>\r
+              </property>\r
+             </spacer>\r
+            </item>\r
+            <item row="0" column="2">\r
+             <widget class="QLineEdit" name="lineEdit_given">\r
+              <property name="minimumSize">\r
+               <size>\r
+                <width>100</width>\r
+                <height>0</height>\r
+               </size>\r
+              </property>\r
+              <property name="font">\r
+               <font>\r
+                <pointsize>12</pointsize>\r
+               </font>\r
+              </property>\r
+              <property name="clearButtonEnabled">\r
+               <bool>true</bool>\r
+              </property>\r
+             </widget>\r
+            </item>\r
+            <item row="1" column="2">\r
+             <widget class="QLineEdit" name="lineEdit_search">\r
+              <property name="minimumSize">\r
+               <size>\r
+                <width>100</width>\r
+                <height>0</height>\r
+               </size>\r
+              </property>\r
+              <property name="font">\r
+               <font>\r
+                <pointsize>12</pointsize>\r
+               </font>\r
+              </property>\r
+              <property name="clearButtonEnabled">\r
+               <bool>true</bool>\r
+              </property>\r
+             </widget>\r
+            </item>\r
+            <item row="1" column="1">\r
+             <widget class="QLabel" name="label_search">\r
+              <property name="minimumSize">\r
+               <size>\r
+                <width>0</width>\r
+                <height>0</height>\r
+               </size>\r
+              </property>\r
+              <property name="font">\r
+               <font>\r
+                <pointsize>12</pointsize>\r
+               </font>\r
+              </property>\r
+              <property name="text">\r
+               <string>Search:</string>\r
+              </property>\r
+             </widget>\r
+            </item>\r
+            <item row="0" column="3">\r
+             <spacer name="horizontalSpacer_4">\r
+              <property name="orientation">\r
+               <enum>Qt::Horizontal</enum>\r
+              </property>\r
+              <property name="sizeHint" stdset="0">\r
+               <size>\r
+                <width>40</width>\r
+                <height>20</height>\r
+               </size>\r
+              </property>\r
+             </spacer>\r
+            </item>\r
+           </layout>\r
+          </item>\r
+          <item>\r
+           <layout class="QHBoxLayout" name="horizontalLayout">\r
+            <item>\r
+             <spacer name="horizontalSpacer">\r
+              <property name="orientation">\r
+               <enum>Qt::Horizontal</enum>\r
+              </property>\r
+              <property name="sizeHint" stdset="0">\r
+               <size>\r
+                <width>40</width>\r
+                <height>20</height>\r
+               </size>\r
+              </property>\r
+             </spacer>\r
+            </item>\r
+            <item>\r
+             <widget class="QPushButton" name="btn_socket">\r
+              <property name="enabled">\r
+               <bool>true</bool>\r
+              </property>\r
+              <property name="font">\r
+               <font>\r
+                <pointsize>12</pointsize>\r
+               </font>\r
+              </property>\r
+              <property name="text">\r
+               <string>Submit</string>\r
+              </property>\r
+              <property name="checkable">\r
+               <bool>false</bool>\r
+              </property>\r
+             </widget>\r
+            </item>\r
+            <item>\r
+             <spacer name="horizontalSpacer_2">\r
+              <property name="orientation">\r
+               <enum>Qt::Horizontal</enum>\r
+              </property>\r
+              <property name="sizeHint" stdset="0">\r
+               <size>\r
+                <width>40</width>\r
+                <height>20</height>\r
+               </size>\r
+              </property>\r
+             </spacer>\r
+            </item>\r
+           </layout>\r
+          </item>\r
+          <item>\r
+           <spacer name="verticalSpacer_3">\r
+            <property name="orientation">\r
+             <enum>Qt::Vertical</enum>\r
+            </property>\r
+            <property name="sizeType">\r
+             <enum>QSizePolicy::Maximum</enum>\r
+            </property>\r
+            <property name="sizeHint" stdset="0">\r
+             <size>\r
+              <width>20</width>\r
+              <height>20</height>\r
+             </size>\r
+            </property>\r
+           </spacer>\r
+          </item>\r
+          <item>\r
+           <widget class="QLabel" name="lbl_info">\r
+            <property name="text">\r
+             <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p align=&quot;justify&quot;&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Introduction:&lt;/span&gt;&lt;/p&gt;&lt;p align=&quot;justify&quot;&gt;Using '*' to mark one blank in search string&lt;/p&gt;&lt;p align=&quot;justify&quot;&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Example:&lt;/span&gt;&lt;/p&gt;&lt;p align=&quot;justify&quot;&gt;&amp;gt;&amp;gt; Given: apple&lt;/p&gt;&lt;p align=&quot;justify&quot;&gt;&amp;gt;&amp;gt; Search: appl*&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>\r
+            </property>\r
+           </widget>\r
+          </item>\r
+          <item>\r
+           <spacer name="verticalSpacer">\r
+            <property name="orientation">\r
+             <enum>Qt::Vertical</enum>\r
+            </property>\r
+            <property name="sizeHint" stdset="0">\r
+             <size>\r
+              <width>20</width>\r
+              <height>40</height>\r
+             </size>\r
+            </property>\r
+           </spacer>\r
+          </item>\r
+         </layout>\r
+        </item>\r
+        <item>\r
+         <widget class="QListWidget" name="list_result">\r
+          <property name="minimumSize">\r
+           <size>\r
+            <width>100</width>\r
+            <height>0</height>\r
+           </size>\r
+          </property>\r
+         </widget>\r
+        </item>\r
+       </layout>\r
+      </widget>\r
+      <widget class="QWidget" name="tab_2">\r
+       <attribute name="title">\r
+        <string>For future games</string>\r
+       </attribute>\r
+      </widget>\r
+     </widget>\r
+    </item>\r
+   </layout>\r
+  </widget>\r
+  <widget class="QMenuBar" name="menubar">\r
+   <property name="geometry">\r
+    <rect>\r
+     <x>0</x>\r
+     <y>0</y>\r
+     <width>800</width>\r
+     <height>30</height>\r
+    </rect>\r
+   </property>\r
+   <widget class="QMenu" name="menubar1">\r
+    <property name="title">\r
+     <string>Settings</string>\r
+    </property>\r
+    <addaction name="actionConfig"/>\r
+   </widget>\r
+   <addaction name="menubar1"/>\r
+  </widget>\r
+  <widget class="QStatusBar" name="statusbar"/>\r
+  <action name="actionConfig">\r
+   <property name="text">\r
+    <string>Config</string>\r
+   </property>\r
+  </action>\r
+ </widget>\r
+ <tabstops>\r
+  <tabstop>lineEdit_given</tabstop>\r
+  <tabstop>lineEdit_search</tabstop>\r
+  <tabstop>btn_socket</tabstop>\r
+  <tabstop>list_result</tabstop>\r
+  <tabstop>tabWidget</tabstop>\r
+ </tabstops>\r
+ <resources/>\r
+ <connections/>\r
+</ui>\r
diff --git a/client/CheaterHub/setting.cpp b/client/CheaterHub/setting.cpp
new file mode 100644 (file)
index 0000000..7bbe4e1
--- /dev/null
@@ -0,0 +1,29 @@
+#include "setting.h"\r
+#include "ui_setting.h"\r
+#include <QString>\r
+#include <QDebug>\r
+\r
+Setting::Setting(Cfg& cfg, QWidget *parent) :\r
+    QDialog(parent),\r
+    cfg(cfg),\r
+    ui(new Ui::Setting)\r
+{\r
+    ui->setupUi(this);\r
+    ui->le_wordscapes_ip->setText(cfg.wordscapes_ip);\r
+    ui->le_wordscapes_port->setText(QString::number(cfg.wordscapes_port));\r
+\r
+    connect(ui->buttonBox, &QDialogButtonBox::accepted, this, &Setting::update_setting);\r
+}\r
+\r
+Setting::~Setting()\r
+{\r
+    delete ui;\r
+}\r
+\r
+void Setting::update_setting()\r
+{\r
+    cfg.wordscapes_ip = ui->le_wordscapes_ip->text();\r
+    cfg.wordscapes_port = ui->le_wordscapes_port->text().toInt();\r
+\r
+    cfg.save_config();\r
+}\r
diff --git a/client/CheaterHub/setting.h b/client/CheaterHub/setting.h
new file mode 100644 (file)
index 0000000..a3f8255
--- /dev/null
@@ -0,0 +1,27 @@
+#ifndef SETTING_H\r
+#define SETTING_H\r
+\r
+#include "cfg.h"\r
+#include <QDialog>\r
+\r
+namespace Ui {\r
+class Setting;\r
+}\r
+\r
+class Setting : public QDialog\r
+{\r
+    Q_OBJECT\r
+\r
+public:\r
+    explicit Setting(Cfg& cfg, QWidget *parent = nullptr);\r
+    ~Setting();\r
+\r
+private:\r
+    Cfg& cfg;\r
+    Ui::Setting *ui;\r
+\r
+public slots:\r
+    void update_setting();\r
+};\r
+\r
+#endif // SETTING_H\r
diff --git a/client/CheaterHub/setting.ui b/client/CheaterHub/setting.ui
new file mode 100644 (file)
index 0000000..4cab359
--- /dev/null
@@ -0,0 +1,113 @@
+<?xml version="1.0" encoding="UTF-8"?>\r
+<ui version="4.0">\r
+ <class>Setting</class>\r
+ <widget class="QDialog" name="Setting">\r
+  <property name="geometry">\r
+   <rect>\r
+    <x>0</x>\r
+    <y>0</y>\r
+    <width>400</width>\r
+    <height>300</height>\r
+   </rect>\r
+  </property>\r
+  <property name="windowTitle">\r
+   <string>Dialog</string>\r
+  </property>\r
+  <layout class="QGridLayout" name="gridLayout_2">\r
+   <item row="0" column="0">\r
+    <widget class="QTabWidget" name="tabWidget">\r
+     <property name="currentIndex">\r
+      <number>0</number>\r
+     </property>\r
+     <widget class="QWidget" name="tab_wordscapes">\r
+      <attribute name="title">\r
+       <string>Wordscapes</string>\r
+      </attribute>\r
+      <widget class="QWidget" name="">\r
+       <property name="geometry">\r
+        <rect>\r
+         <x>30</x>\r
+         <y>30</y>\r
+         <width>195</width>\r
+         <height>48</height>\r
+        </rect>\r
+       </property>\r
+       <layout class="QGridLayout" name="gridLayout">\r
+        <item row="0" column="0">\r
+         <widget class="QLabel" name="lbl_wordscapes_ip">\r
+          <property name="text">\r
+           <string>IP:</string>\r
+          </property>\r
+         </widget>\r
+        </item>\r
+        <item row="0" column="1">\r
+         <widget class="QLineEdit" name="le_wordscapes_ip"/>\r
+        </item>\r
+        <item row="1" column="0">\r
+         <widget class="QLabel" name="lbl_wordscapes_port">\r
+          <property name="text">\r
+           <string>Port:</string>\r
+          </property>\r
+         </widget>\r
+        </item>\r
+        <item row="1" column="1">\r
+         <widget class="QLineEdit" name="le_wordscapes_port"/>\r
+        </item>\r
+       </layout>\r
+      </widget>\r
+     </widget>\r
+     <widget class="QWidget" name="tab_2">\r
+      <attribute name="title">\r
+       <string>Others</string>\r
+      </attribute>\r
+     </widget>\r
+    </widget>\r
+   </item>\r
+   <item row="1" column="0">\r
+    <widget class="QDialogButtonBox" name="buttonBox">\r
+     <property name="orientation">\r
+      <enum>Qt::Horizontal</enum>\r
+     </property>\r
+     <property name="standardButtons">\r
+      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>\r
+     </property>\r
+    </widget>\r
+   </item>\r
+  </layout>\r
+ </widget>\r
+ <resources/>\r
+ <connections>\r
+  <connection>\r
+   <sender>buttonBox</sender>\r
+   <signal>accepted()</signal>\r
+   <receiver>Setting</receiver>\r
+   <slot>accept()</slot>\r
+   <hints>\r
+    <hint type="sourcelabel">\r
+     <x>248</x>\r
+     <y>254</y>\r
+    </hint>\r
+    <hint type="destinationlabel">\r
+     <x>157</x>\r
+     <y>274</y>\r
+    </hint>\r
+   </hints>\r
+  </connection>\r
+  <connection>\r
+   <sender>buttonBox</sender>\r
+   <signal>rejected()</signal>\r
+   <receiver>Setting</receiver>\r
+   <slot>reject()</slot>\r
+   <hints>\r
+    <hint type="sourcelabel">\r
+     <x>316</x>\r
+     <y>260</y>\r
+    </hint>\r
+    <hint type="destinationlabel">\r
+     <x>286</x>\r
+     <y>274</y>\r
+    </hint>\r
+   </hints>\r
+  </connection>\r
+ </connections>\r
+</ui>\r
diff --git a/server/Makefile b/server/Makefile
new file mode 100644 (file)
index 0000000..27d45e1
--- /dev/null
@@ -0,0 +1,44 @@
+EXE        = CheaterHub
+
+export DIR        = $(shell pwd)
+export BUILD_DIR  = $(DIR)/build
+
+WORDSCAPES_DIR = wordscapes
+
+vpath %.o $(BUILD_DIR)
+
+export MAKE := make
+export CXX := g++
+export OPTIONS = -std=c++11
+MODE ?= debug
+
+OBJ = cheaterHub.o
+OBJ_ADDITION = wordscapes.o
+
+ifeq ($(strip $(MODE)), release)
+       OPTIONS += -O2
+       TARGET = $(EXE)
+else
+       OPTIONS += -g
+       TARGET = $(EXE)-staging
+endif
+
+all: build_wordscapes $(TARGET)
+
+build_wordscapes:
+       @mkdir -p $(BUILD_DIR)
+       $(MAKE) -C $(WORDSCAPES_DIR)
+
+$(TARGET): $(OBJ)
+       $(CXX) $(OPTIONS) -pthread \
+               $(addprefix $(BUILD_DIR)/, $(OBJ)) \
+               $(addprefix $(BUILD_DIR)/, $(OBJ_ADDITION)) \
+               -o $(BUILD_DIR)/$@
+
+$(OBJ):%.o:%.cc
+       @mkdir -p $(BUILD_DIR)
+       $(CXX) $(OPTIONS) -c $< -o $(BUILD_DIR)/$@
+
+clean:
+       $(MAKE) -C $(WORDSCAPES_DIR) clean
+       rm -rf $(BUILD_DIR)
diff --git a/server/cheaterHub.cc b/server/cheaterHub.cc
new file mode 100644 (file)
index 0000000..a0f0d14
--- /dev/null
@@ -0,0 +1,122 @@
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+#include <boost/algorithm/string/classification.hpp>
+#include <boost/algorithm/string/split.hpp>
+#include <condition_variable>
+#include <cstdio>
+#include <iostream>
+#include <mutex>
+#include <queue>
+#include <string>
+#include <thread>
+
+#include "wordscapes/wordscapes.h"
+
+#define PORT 2020
+
+std::queue<std::pair<int, std::string>> qu;
+
+std::mutex mtx_th;
+std::mutex mtx_qu;
+
+Wordscapes ws;
+
+void thread_on_msg(int i)
+{
+    while (true)
+    {
+        mtx_th.lock();
+
+        mtx_qu.lock();
+        if (qu.empty())
+        {
+            mtx_qu.unlock();
+            continue;
+        }
+        int client_fd   = qu.front().first;
+        std::string msg = qu.front().second;
+        qu.pop();
+        mtx_qu.unlock();
+
+        // do msg
+        std::cout << "Thread " << i << " receive: " << msg << std::endl;
+
+        std::vector<std::string> msg_vt;
+        boost::split(msg_vt, msg, boost::is_any_of(";"));
+
+        auto result = ws.solve(msg_vt[0], msg_vt[1]);
+
+        std::string ret = "";
+        for (auto& res : result) ret += res + ';';
+
+        send(client_fd, ret.c_str(), ret.size(), 0);
+        std::cout << "Thread " << i << " send: " << ret << std::endl;
+
+        close(client_fd);
+
+        mtx_qu.lock();
+        if (!qu.empty()) mtx_th.unlock();
+        mtx_qu.unlock();
+    }
+}
+
+int main()
+{
+    struct sockaddr_in server_addr;
+    struct sockaddr_storage client_addr;
+    int addrlen                 = sizeof(server_addr);
+    server_addr.sin_family      = AF_INET;
+    server_addr.sin_addr.s_addr = INADDR_ANY;
+    server_addr.sin_port        = htons(PORT);
+
+    int server_fd;
+
+    if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0)
+    {
+        perror("socket failed");
+        exit(EXIT_FAILURE);
+    }
+
+    int opt = 1;
+    if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt,
+                   sizeof(opt)))
+    {
+        perror("setsockopt");
+        exit(EXIT_FAILURE);
+    }
+
+    if (bind(server_fd, (struct sockaddr*)&server_addr, sizeof(server_addr)) <
+        0)
+    {
+        perror("bind failed");
+        exit(EXIT_FAILURE);
+    }
+
+    if (listen(server_fd, 5) < 0)
+    {
+        perror("listen");
+        exit(EXIT_FAILURE);
+    }
+
+    std::thread threads[5];
+    for (int i = 0; i < 5; i++) threads[i] = std::thread(thread_on_msg, i);
+
+    while (true)
+    {
+        socklen_t clientAddr_len = sizeof(client_addr);
+        int new_socket =
+            accept(server_fd, (struct sockaddr*)&client_addr, &clientAddr_len);
+        char buffer[1024] = {0};
+        recv(new_socket, buffer, 1024, 0);
+
+        mtx_qu.lock();
+        qu.push({new_socket, std::string(buffer)});
+        mtx_qu.unlock();
+        mtx_th.unlock();
+    }
+    for (int i = 0; i < 5; i++) threads[i].join();
+
+    return 0;
+}
diff --git a/server/tools/generateDict.py b/server/tools/generateDict.py
new file mode 100755 (executable)
index 0000000..39d854e
--- /dev/null
@@ -0,0 +1,23 @@
+#! /usr/bin/python3
+import os
+import subprocess
+
+workPath = "../wordscapes/"
+dictPath = workPath + "dict/"
+
+if __name__ == "__main__":
+
+    file_names = subprocess.Popen("ls " + dictPath, shell=True, stdout=subprocess.PIPE).communicate()[0].decode().split()
+
+    m_dict = set()
+    file_out = open(workPath + "myDictionary", "w")
+
+    for file_name in file_names:
+        file = open(dictPath + file_name, "r")
+
+        for word in file.readlines():
+            word = word.strip().lower()
+            if word.isalpha():
+                m_dict.add(word)
+
+    file_out.write('\n'.join(m_dict))
diff --git a/server/wordscapes/.gitignore b/server/wordscapes/.gitignore
new file mode 100644 (file)
index 0000000..49be545
--- /dev/null
@@ -0,0 +1,3 @@
+myDictionary
+dict/*
+!dict/myDictionary
diff --git a/server/wordscapes/Makefile b/server/wordscapes/Makefile
new file mode 100644 (file)
index 0000000..2f03fc9
--- /dev/null
@@ -0,0 +1,32 @@
+TEST_EXE = Wordscapes-test
+DICTIONARY = myDictionary
+
+M_DIR = $(shell pwd)
+M_BUILD_DIR = $(M_DIR)/build
+
+vpath %.o $(M_BUILD_DIR)
+
+OBJ = wordscapes.o
+
+all: $(OBJ) $(BUILD_DIR)/$(DICTIONARY)
+       cp $(addprefix $(M_BUILD_DIR)/, $(OBJ)) $(BUILD_DIR)/
+
+$(M_BUILD_DIR)/$(TEST_EXE): $(OBJ) test.o
+       g++ -g $(addprefix $(M_BUILD_DIR)/, $(notdir $^)) -o $@
+
+$(BUILD_DIR)/$(DICTIONARY): $(M_DIR)/$(DICTIONARY)
+       cp $< $@
+
+$(M_BUILD_DIR)/$(DICTIONARY): $(M_DIR)/$(DICTIONARY)
+       cp $< $@
+
+%.o:%.cc
+       mkdir -p $(M_BUILD_DIR)
+       $(CXX) $(OPTIONS) -c $< -o $(M_BUILD_DIR)/$@
+
+.PHONY: test
+test: $(M_BUILD_DIR)/$(TEST_EXE) $(M_BUILD_DIR)/$(DICTIONARY)
+
+clean:
+       @rm -rf $(M_BUILD_DIR)
+       @echo "Wordscapes clean finished!"
diff --git a/server/wordscapes/dict/myDictionary b/server/wordscapes/dict/myDictionary
new file mode 100644 (file)
index 0000000..f50cdc7
--- /dev/null
@@ -0,0 +1,417578 @@