--- /dev/null
+tags
+build*/
--- /dev/null
+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.
+
--- /dev/null
+# 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.
--- /dev/null
+*.pro.user
--- /dev/null
+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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+<?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><html><head/><body><p align="justify"><span style=" font-weight:600;">Introduction:</span></p><p align="justify">Using '*' to mark one blank in search string</p><p align="justify"><span style=" font-weight:600;">Example:</span></p><p align="justify">&gt;&gt; Given: apple</p><p align="justify">&gt;&gt; Search: appl*</p></body></html></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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+<?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
--- /dev/null
+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)
--- /dev/null
+#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;
+}
--- /dev/null
+#! /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))
--- /dev/null
+myDictionary
+dict/*
+!dict/myDictionary
--- /dev/null
+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!"