commit e66ef36927bbb910e8ddb27d5d0644c148d627b7 Author: liuweilhy Date: Mon May 21 23:43:19 2018 +0800 2018/5/21清理项目(17MB),重新提交2018/5/19日版: 游戏可以正确运行,实现本机对战功能,其它功能待完善。 diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..8ee8d367 --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +################################################################################ +# 此 .gitignore 文件已由 Microsoft(R) Visual Studio 自动创建。 +################################################################################ + +/NineChess/debug +/NineChess/release +/NineChess/GeneratedFiles +/NineChess/x64 +/NineChess/.vs +/.vs \ No newline at end of file diff --git a/History.txt b/History.txt new file mode 100644 index 00000000..2b55b41d --- /dev/null +++ b/History.txt @@ -0,0 +1,8 @@ +九连棋 NineChess +更新历史: +2018/5/19 V1.0 + 1. Qt5.10.1+MSVC2017重构; + 2. 游戏可以正确运行,实现本机对战功能,其它功能待完善。 + +2015/11/6 V0.1 + 1. 无算法只有界面的程序。 diff --git a/Licence.txt b/Licence.txt new file mode 100644 index 00000000..b88b4f01 --- /dev/null +++ b/Licence.txt @@ -0,0 +1,15 @@ +本软件代码遵循LGPL协议,其中: +1. 对于Qt库,遵循GPL协议。任何人都可以免费使用,但必须保持其开源; +2. 对于原作者自行开发的代码: + 2.1 未经原作者书面授权,任何人不得修改、传播; + 2.2 获得原作者书面授权免费使用者: + 2.2.1 其修改版必须对原作者开源; + 2.2.2 其修改版必须完整保留本Licence文件,如需新增内容,只能在原版Licence文件末尾追加; + 2.2.3 原作者对其修改版在保留修改者署名情况下,有修改、传播及无偿商业化使用的权利; + 2.3 获得原作者书面授权收费使用者,按照双方书面协议执行。 + +本软件的可执行文件: +1. Qt库文件归Qt版权方所有; +2. 原作者、代码免费使用者的可执行文件,任何人可以免费使用、传播,但不得反汇编、修改,不得商用及其它违法用途。 +3. 获得原作者书面授权收费使用者,按照双方书面协议执行。 + —— by liuweilhy 2018.05.21 diff --git a/NineChess/NineChess.rc b/NineChess/NineChess.rc new file mode 100644 index 00000000..701bd413 Binary files /dev/null and b/NineChess/NineChess.rc differ diff --git a/NineChess/Resources/icon/1.png b/NineChess/Resources/icon/1.png new file mode 100644 index 00000000..2c1b0772 Binary files /dev/null and b/NineChess/Resources/icon/1.png differ diff --git a/NineChess/Resources/icon/2.png b/NineChess/Resources/icon/2.png new file mode 100644 index 00000000..7d45fd3a Binary files /dev/null and b/NineChess/Resources/icon/2.png differ diff --git a/NineChess/Resources/icon/ActualSizeHS.png b/NineChess/Resources/icon/ActualSizeHS.png new file mode 100644 index 00000000..d3c1a41c Binary files /dev/null and b/NineChess/Resources/icon/ActualSizeHS.png differ diff --git a/NineChess/Resources/icon/Black.png b/NineChess/Resources/icon/Black.png new file mode 100644 index 00000000..1f525ade Binary files /dev/null and b/NineChess/Resources/icon/Black.png differ diff --git a/NineChess/Resources/icon/BreakpointHS.png b/NineChess/Resources/icon/BreakpointHS.png new file mode 100644 index 00000000..50444217 Binary files /dev/null and b/NineChess/Resources/icon/BreakpointHS.png differ diff --git a/NineChess/Resources/icon/DocumentHS.png b/NineChess/Resources/icon/DocumentHS.png new file mode 100644 index 00000000..4c5c2c34 Binary files /dev/null and b/NineChess/Resources/icon/DocumentHS.png differ diff --git a/NineChess/Resources/icon/EditInformationHS.png b/NineChess/Resources/icon/EditInformationHS.png new file mode 100644 index 00000000..ed4614c9 Binary files /dev/null and b/NineChess/Resources/icon/EditInformationHS.png differ diff --git a/NineChess/Resources/icon/EditTableHS.png b/NineChess/Resources/icon/EditTableHS.png new file mode 100644 index 00000000..6ebcdc50 Binary files /dev/null and b/NineChess/Resources/icon/EditTableHS.png differ diff --git a/NineChess/Resources/icon/Error.png b/NineChess/Resources/icon/Error.png new file mode 100644 index 00000000..631acc8f Binary files /dev/null and b/NineChess/Resources/icon/Error.png differ diff --git a/NineChess/Resources/icon/First.png b/NineChess/Resources/icon/First.png new file mode 100644 index 00000000..79192db2 Binary files /dev/null and b/NineChess/Resources/icon/First.png differ diff --git a/NineChess/Resources/icon/FlipHorizontalHS.png b/NineChess/Resources/icon/FlipHorizontalHS.png new file mode 100644 index 00000000..be5e799b Binary files /dev/null and b/NineChess/Resources/icon/FlipHorizontalHS.png differ diff --git a/NineChess/Resources/icon/FlipVerticalHS.png b/NineChess/Resources/icon/FlipVerticalHS.png new file mode 100644 index 00000000..580e83d2 Binary files /dev/null and b/NineChess/Resources/icon/FlipVerticalHS.png differ diff --git a/NineChess/Resources/icon/FullScreenHS.png b/NineChess/Resources/icon/FullScreenHS.png new file mode 100644 index 00000000..17b2164c Binary files /dev/null and b/NineChess/Resources/icon/FullScreenHS.png differ diff --git a/NineChess/Resources/icon/Help.png b/NineChess/Resources/icon/Help.png new file mode 100644 index 00000000..6893cf43 Binary files /dev/null and b/NineChess/Resources/icon/Help.png differ diff --git a/NineChess/Resources/icon/HomeHS.png b/NineChess/Resources/icon/HomeHS.png new file mode 100644 index 00000000..8f1bc609 Binary files /dev/null and b/NineChess/Resources/icon/HomeHS.png differ diff --git a/NineChess/Resources/icon/Invert.png b/NineChess/Resources/icon/Invert.png new file mode 100644 index 00000000..d5cb6b09 Binary files /dev/null and b/NineChess/Resources/icon/Invert.png differ diff --git a/NineChess/Resources/icon/Last.png b/NineChess/Resources/icon/Last.png new file mode 100644 index 00000000..a30f4ea3 Binary files /dev/null and b/NineChess/Resources/icon/Last.png differ diff --git a/NineChess/Resources/icon/LeftHS.png b/NineChess/Resources/icon/LeftHS.png new file mode 100644 index 00000000..085e7691 Binary files /dev/null and b/NineChess/Resources/icon/LeftHS.png differ diff --git a/NineChess/Resources/icon/LegendHS.png b/NineChess/Resources/icon/LegendHS.png new file mode 100644 index 00000000..7609f2bc Binary files /dev/null and b/NineChess/Resources/icon/LegendHS.png differ diff --git a/NineChess/Resources/icon/NewDocumentHS.png b/NineChess/Resources/icon/NewDocumentHS.png new file mode 100644 index 00000000..2985b8d7 Binary files /dev/null and b/NineChess/Resources/icon/NewDocumentHS.png differ diff --git a/NineChess/Resources/icon/Next.png b/NineChess/Resources/icon/Next.png new file mode 100644 index 00000000..4eef3913 Binary files /dev/null and b/NineChess/Resources/icon/Next.png differ diff --git a/NineChess/Resources/icon/OpenHS.png b/NineChess/Resources/icon/OpenHS.png new file mode 100644 index 00000000..0bc670e2 Binary files /dev/null and b/NineChess/Resources/icon/OpenHS.png differ diff --git a/NineChess/Resources/icon/OptionsHS.png b/NineChess/Resources/icon/OptionsHS.png new file mode 100644 index 00000000..6c563e41 Binary files /dev/null and b/NineChess/Resources/icon/OptionsHS.png differ diff --git a/NineChess/Resources/icon/Pause.png b/NineChess/Resources/icon/Pause.png new file mode 100644 index 00000000..d83bf17c Binary files /dev/null and b/NineChess/Resources/icon/Pause.png differ diff --git a/NineChess/Resources/icon/Play.png b/NineChess/Resources/icon/Play.png new file mode 100644 index 00000000..96b050f7 Binary files /dev/null and b/NineChess/Resources/icon/Play.png differ diff --git a/NineChess/Resources/icon/Previous.png b/NineChess/Resources/icon/Previous.png new file mode 100644 index 00000000..8d37b587 Binary files /dev/null and b/NineChess/Resources/icon/Previous.png differ diff --git a/NineChess/Resources/icon/PrimaryKeyHS.png b/NineChess/Resources/icon/PrimaryKeyHS.png new file mode 100644 index 00000000..d5b6afec Binary files /dev/null and b/NineChess/Resources/icon/PrimaryKeyHS.png differ diff --git a/NineChess/Resources/icon/Refresh.png b/NineChess/Resources/icon/Refresh.png new file mode 100644 index 00000000..4e253d6b Binary files /dev/null and b/NineChess/Resources/icon/Refresh.png differ diff --git a/NineChess/Resources/icon/Request.png b/NineChess/Resources/icon/Request.png new file mode 100644 index 00000000..b1631c93 Binary files /dev/null and b/NineChess/Resources/icon/Request.png differ diff --git a/NineChess/Resources/icon/RestartHS.png b/NineChess/Resources/icon/RestartHS.png new file mode 100644 index 00000000..89eebbbd Binary files /dev/null and b/NineChess/Resources/icon/RestartHS.png differ diff --git a/NineChess/Resources/icon/RightHS.png b/NineChess/Resources/icon/RightHS.png new file mode 100644 index 00000000..90dd1794 Binary files /dev/null and b/NineChess/Resources/icon/RightHS.png differ diff --git a/NineChess/Resources/icon/SaveHS.png b/NineChess/Resources/icon/SaveHS.png new file mode 100644 index 00000000..5ca4d106 Binary files /dev/null and b/NineChess/Resources/icon/SaveHS.png differ diff --git a/NineChess/Resources/icon/Web.png b/NineChess/Resources/icon/Web.png new file mode 100644 index 00000000..4782ae8a Binary files /dev/null and b/NineChess/Resources/icon/Web.png differ diff --git a/NineChess/Resources/icon/White.png b/NineChess/Resources/icon/White.png new file mode 100644 index 00000000..0f4ac761 Binary files /dev/null and b/NineChess/Resources/icon/White.png differ diff --git a/NineChess/Resources/image/background.png b/NineChess/Resources/image/background.png new file mode 100644 index 00000000..2d4070b9 Binary files /dev/null and b/NineChess/Resources/image/background.png differ diff --git a/NineChess/Resources/image/black_piece.png b/NineChess/Resources/image/black_piece.png new file mode 100644 index 00000000..fb9e7ab5 Binary files /dev/null and b/NineChess/Resources/image/black_piece.png differ diff --git a/NineChess/Resources/image/board.png b/NineChess/Resources/image/board.png new file mode 100644 index 00000000..24414717 Binary files /dev/null and b/NineChess/Resources/image/board.png differ diff --git a/NineChess/Resources/image/current.png b/NineChess/Resources/image/current.png new file mode 100644 index 00000000..0f91a38c Binary files /dev/null and b/NineChess/Resources/image/current.png differ diff --git a/NineChess/Resources/image/remove.png b/NineChess/Resources/image/remove.png new file mode 100644 index 00000000..6753c48c Binary files /dev/null and b/NineChess/Resources/image/remove.png differ diff --git a/NineChess/Resources/image/white_piece.png b/NineChess/Resources/image/white_piece.png new file mode 100644 index 00000000..bc800ee9 Binary files /dev/null and b/NineChess/Resources/image/white_piece.png differ diff --git a/NineChess/Resources/image/xy.png b/NineChess/Resources/image/xy.png new file mode 100644 index 00000000..7161ee26 Binary files /dev/null and b/NineChess/Resources/image/xy.png differ diff --git a/NineChess/Resources/sound/choose.wav b/NineChess/Resources/sound/choose.wav new file mode 100644 index 00000000..59f1d006 Binary files /dev/null and b/NineChess/Resources/sound/choose.wav differ diff --git a/NineChess/Resources/sound/drog.wav b/NineChess/Resources/sound/drog.wav new file mode 100644 index 00000000..ba4d895f Binary files /dev/null and b/NineChess/Resources/sound/drog.wav differ diff --git a/NineChess/Resources/sound/forbidden.wav b/NineChess/Resources/sound/forbidden.wav new file mode 100644 index 00000000..9756b916 Binary files /dev/null and b/NineChess/Resources/sound/forbidden.wav differ diff --git a/NineChess/Resources/sound/loss.wav b/NineChess/Resources/sound/loss.wav new file mode 100644 index 00000000..755816bb Binary files /dev/null and b/NineChess/Resources/sound/loss.wav differ diff --git a/NineChess/Resources/sound/move.wav b/NineChess/Resources/sound/move.wav new file mode 100644 index 00000000..aaeeb5f4 Binary files /dev/null and b/NineChess/Resources/sound/move.wav differ diff --git a/NineChess/Resources/sound/newgame.wav b/NineChess/Resources/sound/newgame.wav new file mode 100644 index 00000000..d8cd9e5a Binary files /dev/null and b/NineChess/Resources/sound/newgame.wav differ diff --git a/NineChess/Resources/sound/remove.wav b/NineChess/Resources/sound/remove.wav new file mode 100644 index 00000000..c0470d4b Binary files /dev/null and b/NineChess/Resources/sound/remove.wav differ diff --git a/NineChess/Resources/sound/win.wav b/NineChess/Resources/sound/win.wav new file mode 100644 index 00000000..5cbcf693 Binary files /dev/null and b/NineChess/Resources/sound/win.wav differ diff --git a/NineChess/ninechess.ico b/NineChess/ninechess.ico new file mode 100644 index 00000000..39842dd5 Binary files /dev/null and b/NineChess/ninechess.ico differ diff --git a/NineChess/ninechess.pro b/NineChess/ninechess.pro new file mode 100644 index 00000000..e421994a --- /dev/null +++ b/NineChess/ninechess.pro @@ -0,0 +1,48 @@ +#------------------------------------------------- +# +# Project created by QtCreator 2015-11-03T22:30:34 +# +#------------------------------------------------- +QMAKE_LFLAGS_WINDOWS = /SUBSYSTEM:WINDOWS,5.01 + +QT += core gui \ + multimedia + +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets + +TARGET = NineChess +TEMPLATE = app + + +SOURCES += src\main.cpp \ + src\ninechesswindow.cpp \ + src\pieceitem.cpp \ + src\gamecontroller.cpp \ + src\boarditem.cpp \ + src\gameview.cpp \ + src\ninechess.cpp \ + src\gamescene.cpp + +HEADERS += \ + src\ninechesswindow.h \ + src\pieceitem.h \ + src\gamecontroller.h \ + src\graphicsconst.h \ + src\boarditem.h \ + src\gameview.h \ + src\ninechess.h \ + src\sizehintlistview.h \ + src\gamescene.h + +FORMS += \ + ninechesswindow.ui + +RESOURCES += \ + ninechesswindow.qrc + +DISTFILES += \ + Readme.txt \ + NineChess.rc \ + History.txt + +RC_FILE += NineChess.rc diff --git a/NineChess/ninechess.pro.user b/NineChess/ninechess.pro.user new file mode 100644 index 00000000..7243e7fe --- /dev/null +++ b/NineChess/ninechess.pro.user @@ -0,0 +1,318 @@ + + + + + + EnvironmentId + {fba36f69-c7a3-4c3f-91d4-7c71ad79549c} + + + ProjectExplorer.Project.ActiveTarget + 0 + + + ProjectExplorer.Project.EditorSettings + + true + false + true + + Cpp + + CppGlobal + + + + QmlJS + + QmlJSGlobal + + + 2 + UTF-8 + false + 4 + false + 80 + true + true + 1 + true + false + 0 + true + true + 0 + 8 + true + 1 + true + true + true + false + + + + ProjectExplorer.Project.PluginSettings + + + + ProjectExplorer.Project.Target.0 + + Desktop Qt 5.10.1 MSVC2017 64bit + Desktop Qt 5.10.1 MSVC2017 64bit + qt.qt5.5101.win64_msvc2017_64_kit + 0 + 0 + 0 + + E:/My program/QT/build-ninechess-Desktop_Qt_5_10_1_MSVC2017_64bit-Debug + + + true + qmake + + QtProjectManager.QMakeBuildStep + true + + false + false + false + + + true + Make + + Qt4ProjectManager.MakeStep + + false + + + + 2 + 构建 + + ProjectExplorer.BuildSteps.Build + + + + true + Make + + Qt4ProjectManager.MakeStep + + true + clean + + + 1 + 清理 + + ProjectExplorer.BuildSteps.Clean + + 2 + false + + Debug + + Qt4ProjectManager.Qt4BuildConfiguration + 2 + true + + + E:/My program/QT/build-ninechess-Desktop_Qt_5_10_1_MSVC2017_64bit-Release + + + true + qmake + + QtProjectManager.QMakeBuildStep + false + + false + false + false + + + true + Make + + Qt4ProjectManager.MakeStep + + false + + + + 2 + 构建 + + ProjectExplorer.BuildSteps.Build + + + + true + Make + + Qt4ProjectManager.MakeStep + + true + clean + + + 1 + 清理 + + ProjectExplorer.BuildSteps.Clean + + 2 + false + + Release + + Qt4ProjectManager.Qt4BuildConfiguration + 0 + true + + + E:/My program/QT/build-ninechess-Desktop_Qt_5_10_1_MSVC2017_64bit-Profile + + + true + qmake + + QtProjectManager.QMakeBuildStep + true + + false + true + false + + + true + Make + + Qt4ProjectManager.MakeStep + + false + + + + 2 + 构建 + + ProjectExplorer.BuildSteps.Build + + + + true + Make + + Qt4ProjectManager.MakeStep + + true + clean + + + 1 + 清理 + + ProjectExplorer.BuildSteps.Clean + + 2 + false + + Profile + + Qt4ProjectManager.Qt4BuildConfiguration + 0 + true + + 3 + + + 0 + 部署 + + ProjectExplorer.BuildSteps.Deploy + + 1 + 在本地部署 + + ProjectExplorer.DefaultDeployConfiguration + + 1 + + + false + false + 1000 + + true + + false + false + false + false + true + 0.01 + 10 + true + 1 + 25 + + 1 + true + false + true + valgrind + + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + + 2 + + ninechess + ninechess2 + Qt4ProjectManager.Qt4RunConfiguration:E:/My program/QT/NineChess/NineChess/ninechess.pro + true + + ninechess.pro + false + + E:/My program/QT/build-ninechess-Desktop_Qt_5_10_1_MSVC2017_64bit-Debug + 3768 + false + true + false + false + true + + 1 + + + + ProjectExplorer.Project.TargetCount + 1 + + + ProjectExplorer.Project.Updater.FileVersion + 18 + + + Version + 18 + + diff --git a/NineChess/ninechess.sln b/NineChess/ninechess.sln new file mode 100644 index 00000000..30aa6cdb --- /dev/null +++ b/NineChess/ninechess.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.27703.2000 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ninechess", "ninechess.vcxproj", "{D6EBE2B6-17F9-30EA-AE68-9CD0BB526200}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {D6EBE2B6-17F9-30EA-AE68-9CD0BB526200}.Debug|x64.ActiveCfg = Debug|x64 + {D6EBE2B6-17F9-30EA-AE68-9CD0BB526200}.Debug|x64.Build.0 = Debug|x64 + {D6EBE2B6-17F9-30EA-AE68-9CD0BB526200}.Release|x64.ActiveCfg = Release|x64 + {D6EBE2B6-17F9-30EA-AE68-9CD0BB526200}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {401C61DF-0B94-45A9-96C1-9BD069796A84} + EndGlobalSection +EndGlobal diff --git a/NineChess/ninechess.vcxproj b/NineChess/ninechess.vcxproj new file mode 100644 index 00000000..e6ae6395 --- /dev/null +++ b/NineChess/ninechess.vcxproj @@ -0,0 +1,306 @@ + + + + + Release + x64 + + + Debug + x64 + + + + {D6EBE2B6-17F9-30EA-AE68-9CD0BB526200} + NineChess + Qt4VSv1.0 + 10.0.17134.0 + + + + v141 + release\ + false + NotSet + Application + release\ + NineChess + + + v141 + debug\ + false + NotSet + Application + debug\ + NineChess + + + + $(MSBuildProjectDirectory)\QtMsBuild + + + + + + + + + + + + + + + + + release\ + $(Platform)\$(Configuration)\ + NineChess + true + false + debug\ + $(Platform)\$(Configuration)\ + NineChess + true + + + + .\GeneratedFiles\$(ConfigurationName);.\GeneratedFiles;.;$(QTDIR)\include;$(QTDIR)\include\QtMultimedia;$(QTDIR)\include\QtWidgets;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtANGLE;$(QTDIR)\include\QtNetwork;$(QTDIR)\include\QtCore;release;\include;$(QTDIR)\mkspecs\win32-msvc;%(AdditionalIncludeDirectories) + -Zc:rvalueCast -Zc:inline -Zc:strictStrings -Zc:throwingNew -Zc:referenceBinding -w34100 -w34189 -w44996 -w44456 -w44457 -w44458 %(AdditionalOptions) + release\ + false + None + 4577;4467;%(DisableSpecificWarnings) + Sync + $(IntDir) + MaxSpeed + _WINDOWS;UNICODE;_UNICODE;WIN32;WIN64;QT_NO_DEBUG;QT_MULTIMEDIA_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_NETWORK_LIB;QT_CORE_LIB;NDEBUG;%(PreprocessorDefinitions) + false + $(IntDir)vc$(PlatformToolsetVersion).pdb + MultiThreadedDLL + true + true + Level3 + true + + + $(QTDIR)\lib\qtmain.lib;shell32.lib;$(QTDIR)\lib\Qt5Multimedia.lib;$(QTDIR)\lib\Qt5Widgets.lib;$(QTDIR)\lib\Qt5Gui.lib;$(QTDIR)\lib\Qt5Network.lib;$(QTDIR)\lib\Qt5Core.lib;%(AdditionalDependencies) + $(QTDIR)\lib;C:\utils\my_sql\my_sql\lib;C:\utils\postgresql\pgsql\lib;$(QTDIR)\lib;%(AdditionalLibraryDirectories) + "/MANIFESTDEPENDENCY:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' publicKeyToken='6595b64144ccf1df' language='*' processorArchitecture='*'" %(AdditionalOptions) + true + false + true + false + $(OutDir)\NineChess.exe + true + Windows + true + + + Unsigned + None + 0 + + + _WINDOWS;UNICODE;_UNICODE;WIN32;WIN64;QT_NO_DEBUG;QT_MULTIMEDIA_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_NETWORK_LIB;QT_CORE_LIB;%(PreprocessorDefinitions) + + + $(QTDIR) + .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp + UNICODE;_UNICODE;WIN32;WIN64;QT_NO_DEBUG;QT_MULTIMEDIA_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_NETWORK_LIB;QT_CORE_LIB + msvc + $(Configuration)/moc_predefs.h + Moc'ing %(Identity)... + %(FullPath) + output + .\GeneratedFiles\$(ConfigurationName);.\GeneratedFiles;$(QTDIR)\mkspecs/win32-msvc;.;$(QTDIR)\include;$(QTDIR)\include/QtMultimedia;$(QTDIR)\include/QtWidgets;$(QTDIR)\include/QtGui;$(QTDIR)\include/QtANGLE;$(QTDIR)\include/QtNetwork;$(QTDIR)\include/QtCore;D:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Tools\MSVC\14.14.26428\ATLMFC\include;D:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Tools\MSVC\14.14.26428\include;C:\Program Files (x86)\Windows Kits\NETFXSDK\4.6.1\include\um;D:\Program Files (x86)\Windows Kits\10\include\10.0.17134.0\ucrt;D:\Program Files (x86)\Windows Kits\10\include\10.0.17134.0\shared;D:\Program Files (x86)\Windows Kits\10\include\10.0.17134.0\um;D:\Program Files (x86)\Windows Kits\10\include\10.0.17134.0\winrt;D:\Program Files (x86)\Windows Kits\10\include\10.0.17134.0\cppwinrt + + + ninechesswindow + .\GeneratedFiles\qrc_%(Filename).cpp + $(QTDIR) + default + %(FullPath) + Rcc'ing %(Identity)... + + + $(QTDIR) + %(FullPath) + .\GeneratedFiles\ui_%(Filename).h + Uic'ing %(Identity)... + + + + + .\GeneratedFiles\$(ConfigurationName);.\GeneratedFiles;.;$(QTDIR)\include;$(QTDIR)\include\QtMultimedia;$(QTDIR)\include\QtWidgets;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtANGLE;$(QTDIR)\include\QtNetwork;$(QTDIR)\include\QtCore;debug;\include;$(QTDIR)\mkspecs\win32-msvc;%(AdditionalIncludeDirectories) + -Zc:rvalueCast -Zc:inline -Zc:strictStrings -Zc:throwingNew -Zc:referenceBinding -w34100 -w34189 -w44996 -w44456 -w44457 -w44458 %(AdditionalOptions) + debug\ + false + ProgramDatabase + 4577;4467;%(DisableSpecificWarnings) + Sync + $(IntDir) + Disabled + _WINDOWS;UNICODE;_UNICODE;WIN32;WIN64;QT_MULTIMEDIA_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_NETWORK_LIB;QT_CORE_LIB;%(PreprocessorDefinitions) + false + MultiThreadedDebugDLL + true + true + Level3 + true + $(IntDir)vc$(PlatformToolsetVersion).pdb + + + $(QTDIR)\lib\qtmaind.lib;shell32.lib;$(QTDIR)\lib\Qt5Multimediad.lib;$(QTDIR)\lib\Qt5Widgetsd.lib;$(QTDIR)\lib\Qt5Guid.lib;$(QTDIR)\lib\Qt5Networkd.lib;$(QTDIR)\lib\Qt5Cored.lib;%(AdditionalDependencies) + $(QTDIR)\lib;C:\utils\my_sql\my_sql\lib;C:\utils\postgresql\pgsql\lib;$(QTDIR)\lib;%(AdditionalLibraryDirectories) + "/MANIFESTDEPENDENCY:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' publicKeyToken='6595b64144ccf1df' language='*' processorArchitecture='*'" %(AdditionalOptions) + true + true + true + $(OutDir)\NineChess.exe + true + Windows + true + + + Unsigned + None + 0 + + + _WINDOWS;UNICODE;_UNICODE;WIN32;WIN64;QT_MULTIMEDIA_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_NETWORK_LIB;QT_CORE_LIB;_DEBUG;%(PreprocessorDefinitions) + + + $(QTDIR) + default + ninechesswindow + .\GeneratedFiles\qrc_%(Filename).cpp + %(FullPath) + Rcc'ing %(Identity)... + + + $(QTDIR) + .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp + UNICODE;_UNICODE;WIN32;WIN64;QT_MULTIMEDIA_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_NETWORK_LIB;QT_CORE_LIB + msvc + $(Configuration)/moc_predefs.h + Moc'ing %(Identity)... + %(FullPath) + output + .\GeneratedFiles\$(ConfigurationName);.\GeneratedFiles;$(QTDIR)\mkspecs/win32-msvc;.;$(QTDIR)\include;$(QTDIR)\include/QtMultimedia;$(QTDIR)\include/QtWidgets;$(QTDIR)\include/QtGui;$(QTDIR)\include/QtANGLE;$(QTDIR)\include/QtNetwork;$(QTDIR)\include/QtCore;D:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Tools\MSVC\14.14.26428\ATLMFC\include;D:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Tools\MSVC\14.14.26428\include;C:\Program Files (x86)\Windows Kits\NETFXSDK\4.6.1\include\um;D:\Program Files (x86)\Windows Kits\10\include\10.0.17134.0\ucrt;D:\Program Files (x86)\Windows Kits\10\include\10.0.17134.0\shared;D:\Program Files (x86)\Windows Kits\10\include\10.0.17134.0\um;D:\Program Files (x86)\Windows Kits\10\include\10.0.17134.0\winrt;D:\Program Files (x86)\Windows Kits\10\include\10.0.17134.0\cppwinrt + + + $(QTDIR) + %(FullPath) + .\GeneratedFiles\ui_%(Filename).h + Uic'ing %(Identity)... + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Document + true + $(QTDIR)\mkspecs\features\data\dummy.cpp;%(AdditionalInputs) + cl -Bx"$(QTDIR)\bin\qmake.exe" -nologo -Zc:wchar_t -FS -Zc:rvalueCast -Zc:inline -Zc:strictStrings -Zc:throwingNew -Zc:referenceBinding -Zi -MDd -W3 -w34100 -w34189 -w44996 -w44456 -w44457 -w44458 -wd4577 -wd4467 -E $(QTDIR)\mkspecs\features\data\dummy.cpp 2>NUL >debug\moc_predefs.h + Generate moc_predefs.h + debug\moc_predefs.h;%(Outputs) + + + Document + $(QTDIR)\mkspecs\features\data\dummy.cpp;%(AdditionalInputs) + cl -Bx"$(QTDIR)\bin\qmake.exe" -nologo -Zc:wchar_t -FS -Zc:rvalueCast -Zc:inline -Zc:strictStrings -Zc:throwingNew -Zc:referenceBinding -O2 -MD -W3 -w34100 -w34189 -w44996 -w44456 -w44457 -w44458 -wd4577 -wd4467 -E $(QTDIR)\mkspecs\features\data\dummy.cpp 2>NUL >release\moc_predefs.h + Generate moc_predefs.h + release\moc_predefs.h;%(Outputs) + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/NineChess/ninechess.vcxproj.filters b/NineChess/ninechess.vcxproj.filters new file mode 100644 index 00000000..ad2f1cf2 --- /dev/null +++ b/NineChess/ninechess.vcxproj.filters @@ -0,0 +1,267 @@ + + + + + {99349809-55BA-4b9d-BF79-8FDBB0286EB3} + ui + false + + + {99349809-55BA-4b9d-BF79-8FDBB0286EB3} + ui + false + + + {71ED8ED8-ACB9-4CE9-BBE1-E00B30144E11} + cpp;c;cxx;moc;h;def;odl;idl;res; + + + {71ED8ED8-ACB9-4CE9-BBE1-E00B30144E11} + cpp;c;cxx;moc;h;def;odl;idl;res; + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {D9D6E242-F8AF-46E4-B9FD-80ECBC20BA3E} + qrc;* + false + + + {D9D6E242-F8AF-46E4-B9FD-80ECBC20BA3E} + qrc;* + false + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {B83CAF91-C7BF-462F-B76C-EA11631F866C} + * + false + + + {B83CAF91-C7BF-462F-B76C-EA11631F866C} + * + false + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + + + + + + + + + Generated Files + + + Generated Files + + + + + Generated Files + + + + + Form Files + + + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + + + + \ No newline at end of file diff --git a/NineChess/ninechess.vcxproj.user b/NineChess/ninechess.vcxproj.user new file mode 100644 index 00000000..f819833a --- /dev/null +++ b/NineChess/ninechess.vcxproj.user @@ -0,0 +1,11 @@ + + + + D:\Qt\Qt5.10.1\5.10.1\msvc2017_64\ + PATH=$(QTDIR)\bin%3b$(PATH) + + + D:\Qt\Qt5.10.1\5.10.1\msvc2017_64\ + PATH=$(QTDIR)\bin%3b$(PATH) + + \ No newline at end of file diff --git a/NineChess/ninechesswindow.qrc b/NineChess/ninechesswindow.qrc new file mode 100644 index 00000000..d6998ea3 --- /dev/null +++ b/NineChess/ninechesswindow.qrc @@ -0,0 +1,43 @@ + + + Resources/image/black_piece.png + Resources/image/board.png + Resources/image/white_piece.png + Resources/image/background.png + + + Resources/icon/Black.png + Resources/icon/White.png + Resources/icon/ActualSizeHS.png + Resources/icon/BreakpointHS.png + Resources/icon/DocumentHS.png + Resources/icon/EditInformationHS.png + Resources/icon/EditTableHS.png + Resources/icon/Error.png + Resources/icon/First.png + Resources/icon/FlipHorizontalHS.png + Resources/icon/FlipVerticalHS.png + Resources/icon/FullScreenHS.png + Resources/icon/Help.png + Resources/icon/HomeHS.png + Resources/icon/Last.png + Resources/icon/LeftHS.png + Resources/icon/LegendHS.png + Resources/icon/NewDocumentHS.png + Resources/icon/Next.png + Resources/icon/OpenHS.png + Resources/icon/Pause.png + Resources/icon/Play.png + Resources/icon/Previous.png + Resources/icon/PrimaryKeyHS.png + Resources/icon/Refresh.png + Resources/icon/Request.png + Resources/icon/OptionsHS.png + Resources/icon/Web.png + Resources/icon/RestartHS.png + Resources/icon/Invert.png + Resources/icon/RightHS.png + Resources/icon/SaveHS.png + + + diff --git a/NineChess/ninechesswindow.ui b/NineChess/ninechesswindow.ui new file mode 100644 index 00000000..66d9e724 --- /dev/null +++ b/NineChess/ninechesswindow.ui @@ -0,0 +1,916 @@ + + + NineChessWindowClass + + + + 0 + 0 + 561 + 490 + + + + 九连棋 + + + + + + + + 3 + 3 + + + + false + + + + + + + 10 + 10 + + + + + 240 + 240 + + + + background-image: url(:/image/Resources/image/background.png); + + + Qt::ScrollBarAlwaysOff + + + Qt::ScrollBarAlwaysOff + + + + + + + + + 0 + 0 + 561 + 26 + + + + + 文件(&F) + + + + + + + + + + + + + 棋局(&C) + + + + + + + + + + + + 招法(&M) + + + + + + + + + + + + + 引擎(&E) + + + + + + + + + + + 选项(&O) + + + + + + + + + + + + 帮助(&H) + + + + + + + + 规则(&R) + + + + + + + + + + + + + + + 工具栏 + + + TopToolBarArea + + + false + + + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + false + + + QLCDNumber { + color: lightgreen; + background-color: rgb(32, 32, 32); +} + + + false + + + Qt::AllDockWidgetAreas + + + 对战记录 + + + 2 + + + + + 0 + 0 + + + + + 0 + 0 + + + + + + + QFrame::NoFrame + + + Qt::Vertical + + + + + + + 1 + + + QLayout::SetDefaultConstraint + + + + + + 1 + 0 + + + + + + + :/icon/Resources/icon/Black.png + + + Qt::AlignCenter + + + + + + + + 2 + 0 + + + + + 微软雅黑 + 10 + + + + 玩家1 + + + + + + + + + + 0 + 0 + + + + + + + false + + + 9 + + + QLCDNumber::Flat + + + 0.000000000000000 + + + 0 + + + + + + + 1 + + + QLayout::SetDefaultConstraint + + + + + + 1 + 0 + + + + + + + :/icon/Resources/icon/White.png + + + Qt::AlignCenter + + + + + + + + 2 + 0 + + + + + 微软雅黑 + 10 + + + + 玩家2 + + + + + + + + + + 0 + 0 + + + + + + + false + + + 9 + + + QLCDNumber::Flat + + + 0.000000000000000 + + + 0 + + + + + + + + + 0 + 0 + + + + + 微软雅黑 + + + + QToolTip { + font: 9pt "consolas"; + color: black; +} + + + + + + + + + + + + :/icon/Resources/icon/DocumentHS.png:/icon/Resources/icon/DocumentHS.png + + + 新建(&N) + + + + + + Ctrl+N + + + + + + :/icon/Resources/icon/OpenHS.png:/icon/Resources/icon/OpenHS.png + + + 打开(&O)... + + + Ctrl+O + + + + + + :/icon/Resources/icon/SaveHS.png:/icon/Resources/icon/SaveHS.png + + + 保存(&S) + + + Ctrl+S + + + + + + :/icon/Resources/icon/SaveHS.png:/icon/Resources/icon/SaveHS.png + + + 另存为(&A)... + + + + + + :/icon/Resources/icon/EditInformationHS.png:/icon/Resources/icon/EditInformationHS.png + + + 查看文本棋谱(&V) + + + + + 退出(&X) + + + + + true + + + false + + + + :/icon/Resources/icon/EditTableHS.png:/icon/Resources/icon/EditTableHS.png + + + 编辑棋局(&E) + + + + + + :/icon/Resources/icon/FlipVerticalHS.png:/icon/Resources/icon/FlipVerticalHS.png + + + 上下翻转(&F) + + + + + + :/icon/Resources/icon/FlipHorizontalHS.png:/icon/Resources/icon/FlipHorizontalHS.png + + + 左右翻转(&M) + + + + + + :/icon/Resources/icon/RightHS.png:/icon/Resources/icon/RightHS.png + + + 顺时针旋转90°(&R) + + + 顺时针旋转90°(R) + + + + + + :/icon/Resources/icon/LeftHS.png:/icon/Resources/icon/LeftHS.png + + + 逆时针旋转90°(&L) + + + 逆时针旋转90°(L) + + + + + true + + + false + + + + :/icon/Resources/icon/Invert.png:/icon/Resources/icon/Invert.png + + + 黑白反转(&B) + + + + + + :/icon/Resources/icon/First.png:/icon/Resources/icon/First.png + + + 初始局面(&S) + + + 初始局面(S) + + + Ctrl+Up + + + + + + :/icon/Resources/icon/Previous.png:/icon/Resources/icon/Previous.png + + + 前一招(&B) + + + Ctrl+Left + + + + + + :/icon/Resources/icon/Next.png:/icon/Resources/icon/Next.png + + + 后一招(&F) + + + 后一招(F) + + + Ctrl+Right + + + + + + :/icon/Resources/icon/Last.png:/icon/Resources/icon/Last.png + + + 最后局面(&E) + + + Ctrl+Down + + + + + true + + + + :/icon/Resources/icon/Play.png:/icon/Resources/icon/Play.png + + + 自动演示(&A) + + + 自动演示(A) + + + + + + :/icon/Resources/icon/BreakpointHS.png:/icon/Resources/icon/BreakpointHS.png + + + 认输(&G) + + + + + false + + + 限制步数和时间(&T)... + + + 限制步数和时间(T) + + + + + true + + + true + + + + :/icon/Resources/icon/FullScreenHS.png:/icon/Resources/icon/FullScreenHS.png + + + 本机对战(&L) + + + 本机对战(L) + + + + + true + + + true + + + + :/icon/Resources/icon/Web.png:/icon/Resources/icon/Web.png + + + 网络对战(&I) + + + 网络对战(I) + + + + + + :/icon/Resources/icon/OptionsHS.png:/icon/Resources/icon/OptionsHS.png + + + 引擎设置(&E)... + + + 引擎设置(E) + + + + + true + + + false + + + + :/icon/Resources/icon/Black.png:/icon/Resources/icon/Black.png + + + 电脑执先手(&T) + + + 电脑执先手(T) + + + + + true + + + + :/icon/Resources/icon/White.png:/icon/Resources/icon/White.png + + + 电脑执后手(&R) + + + 电脑执白(R) + + + + + false + + + + :/icon/Resources/icon/PrimaryKeyHS.png:/icon/Resources/icon/PrimaryKeyHS.png + + + 设置(&O)... + + + + + true + + + true + + + 工具栏(&T) + + + + + true + + + true + + + 信息栏(&D) + + + + + true + + + true + + + 背景音乐(&M) + + + false + + + + + true + + + true + + + 落子音效(&S) + + + + + true + + + true + + + 落子动画(&A) + + + 落子动画(A) + + + + + + :/icon/Resources/icon/Help.png:/icon/Resources/icon/Help.png + + + 查看帮助(&V) + + + F1 + + + + + + :/icon/Resources/icon/HomeHS.png:/icon/Resources/icon/HomeHS.png + + + 作者主页(&W) + + + 作者主页(W) + + + + + 关于(&A)... + + + 关于(A) + + + + + + + GameView + QGraphicsView +
gameview.h
+
+ + SizeHintListView + QListView +
sizehintlistview.h
+
+
+ + + + + + actionDockBar_D + toggled(bool) + dockWidget + setVisible(bool) + + + -1 + -1 + + + 507 + 214 + + + + + actionToolBar_T + toggled(bool) + mainToolBar + setVisible(bool) + + + -1 + -1 + + + 289 + 28 + + + + + actionExit_X + triggered() + NineChessWindowClass + close() + + + -1 + -1 + + + 289 + 207 + + + + + mainToolBar + visibilityChanged(bool) + actionToolBar_T + setChecked(bool) + + + 280 + 47 + + + -1 + -1 + + + + + dockWidget + visibilityChanged(bool) + actionDockBar_D + setChecked(bool) + + + 421 + 267 + + + -1 + -1 + + + + +
diff --git a/NineChess/src/boarditem.cpp b/NineChess/src/boarditem.cpp new file mode 100644 index 00000000..5cb9245e --- /dev/null +++ b/NineChess/src/boarditem.cpp @@ -0,0 +1,126 @@ +#include "boarditem.h" +#include "graphicsconst.h" +#include + +BoardItem::BoardItem(QGraphicsItem *parent ) : QGraphicsItem(parent), + size(BOARD_SIZE), + sizeShadow(5.0), + hasObliqueLine(false) +{ + Q_UNUSED(parent) + // 棋盘中心放在场景中心 + setPos(0, 0); + // 初始化24个落子点 + for (int i = 0; i < RING; i++) + { + // 内圈的12点钟方向为第一个位置,按顺时针方向排序 + // 然后是中圈和外圈 + qreal a = (i+1)*LINE_INTERVAL; + position[i*SEAT+0].rx() = 0; + position[i*SEAT+0].ry() = -a; + position[i*SEAT+1].rx() = a; + position[i*SEAT+1].ry() = -a; + position[i*SEAT+2].rx() = a; + position[i*SEAT+2].ry() = 0; + position[i*SEAT+3].rx() = a; + position[i*SEAT+3].ry() = a; + position[i*SEAT+4].rx() = 0; + position[i*SEAT+4].ry() = a; + position[i*SEAT+5].rx() = -a; + position[i*SEAT+5].ry() = a; + position[i*SEAT+6].rx() = -a; + position[i*SEAT+6].ry() = 0; + position[i*SEAT+7].rx() = -a; + position[i*SEAT+7].ry() = -a; + } +} + +BoardItem::~BoardItem() +{ + +} + +QRectF BoardItem::boundingRect() const +{ + return QRectF(-size/2, -size/2, size+sizeShadow, size+sizeShadow); +} + +QPainterPath BoardItem::shape() const +{ + QPainterPath path; + path.addRect(boundingRect()); + return path; +} + +void BoardItem::setDiagonal(bool arg) +{ + hasObliqueLine = arg; + update(boundingRect()); +} + + +void BoardItem::paint(QPainter *painter, + const QStyleOptionGraphicsItem *option, + QWidget *widget) +{ + // 填充阴影 + painter->fillRect(boundingRect(), QBrush(QColor(64, 64, 64))); + // 填充图片 + painter->drawPixmap(-size/2, -size/2, size, size, QPixmap(":/image/Resources/image/board.png")); + // 黑色实线画笔 + QPen pen(QBrush(Qt::black), LINE_WEIGHT, Qt::SolidLine, Qt::SquareCap, Qt::BevelJoin); + painter->setPen(pen); + // 空画刷 + painter->setBrush(Qt::NoBrush); + for (int i = 0; i < RING; i++) + { + // 画3个方框 + painter->drawPolygon(position+i*SEAT, SEAT); + } + // 画4条纵横线 + painter->drawLine(position[0], position[(RING-1)*SEAT]); + painter->drawLine(position[2], position[(RING-1)*SEAT+2]); + painter->drawLine(position[4], position[(RING-1)*SEAT+4]); + painter->drawLine(position[6], position[(RING-1)*SEAT+6]); + if (hasObliqueLine) + { + // 画4条斜线 + painter->drawLine(position[1], position[(RING-1)*SEAT+1]); + painter->drawLine(position[3], position[(RING-1)*SEAT+3]); + painter->drawLine(position[5], position[(RING-1)*SEAT+5]); + painter->drawLine(position[7], position[(RING-1)*SEAT+7]); + } +} + +QPointF BoardItem::nearestPosition(QPointF const pos) +{ + // 初始最近点设为(0,0)点 + QPointF nearestPos = QPointF(0,0); + // 寻找最近的落子点 + for (int i = 0; i < RING*SEAT; i++) + { + // 如果鼠标点距离落子点在棋子半径内 + if (QLineF(pos, position[i]).length() < PIECE_SIZE/2) + { + nearestPos = position[i]; + break; + } + } + return nearestPos; +} + +bool BoardItem::pos2cp(QPointF pos, int &c, int &p) +{ + // 寻找最近的落子点 + for (int i = 0; i < RING*SEAT; i++) + { + // 如果pos点在落子点附近 + if (QLineF(pos, position[i]).length() < PIECE_SIZE/6) + { + c = i / SEAT + 1; + p = i % SEAT + 1; + return true; + } + } + return false; +} diff --git a/NineChess/src/boarditem.h b/NineChess/src/boarditem.h new file mode 100644 index 00000000..91ef09b7 --- /dev/null +++ b/NineChess/src/boarditem.h @@ -0,0 +1,41 @@ +#ifndef BOARDITEM_H +#define BOARDITEM_H + +#include + +class BoardItem : public QGraphicsItem +{ +public: + explicit BoardItem(QGraphicsItem *parent = 0); + ~BoardItem(); + QRectF boundingRect() const; + QPainterPath shape() const; + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, + QWidget * widget = 0); + // 用UserType+1表示棋子,用qgraphicsitem_cast()判断是否为BoardItem类的对象 + // 还有一个方式是把类名放在Data的0key位置setData(0, "BoardItem"),然后用data(0)来判断 + enum { Type = UserType + 1 }; + int type() const { return Type; } + // 设置有无斜线 + void setDiagonal(bool arg = true); + // 返回最近的落子点 + QPointF nearestPosition(QPointF const pos); + // 将落子点坐标转化为模型用的圈、位 + bool pos2cp(QPointF pos, int &c, int &p); + + // 3圈,禁止修改! + static const int RING = 3; + // 8位,禁止修改! + static const int SEAT = 8; +private: + // 棋盘尺寸 + qreal size; + // 影子尺寸 + qreal sizeShadow; + // 24个落子点 + QPointF position[RING * SEAT]; + // 是否有斜线 + bool hasObliqueLine; +}; + +#endif // BOARDITEM_H diff --git a/NineChess/src/gamecontroller.cpp b/NineChess/src/gamecontroller.cpp new file mode 100644 index 00000000..850c3116 --- /dev/null +++ b/NineChess/src/gamecontroller.cpp @@ -0,0 +1,530 @@ +#if _MSC_VER >= 1600 +#pragma execution_character_set("utf-8") +#endif +#include +#include +#include +#include +#include +#include +#include "gamecontroller.h" +#include "graphicsconst.h" +#include "boarditem.h" +#include "pieceitem.h" + +GameController::GameController(GameScene & scene, QObject *parent) : QObject(parent), +scene(scene), +piece(NULL), +isEditing(false), +isInverted(false), +isEngine1(false), +isEngine2(false), +hasAnimation(true), +hasSound(true), +timeID(0), +timeLimit(0), +stepsLimit(0) +{ + // 设置场景尺寸大小为棋盘大小的1.08倍 + scene.setSceneRect(-BOARD_SIZE * 0.54, -BOARD_SIZE * 0.54, BOARD_SIZE*1.08, BOARD_SIZE*1.08); + // 已在view的样式表中添加背景,scene中不用添加背景 + // 区别在于,view中的背景不随视图变换而变换,scene中的背景随视图变换而变换 + //scene.setBackgroundBrush(QPixmap(":/image/Resources/image/background.png")); + // 初始化音效文件路径 + QString dir = QCoreApplication::applicationDirPath(); + soundNewgame = dir + "/sound/newgame.wav"; + soundChoose = dir + "/sound/choose.wav"; + soundMove = dir + "/sound/move.wav"; + soundDrog = dir + "/sound/drog.wav"; + soundForbidden = dir + "/sound/forbidden.wav"; + soundRemove = dir + "/sound/remove.wav"; + soundWin = dir + "/sound/win.wav"; + soundLoss = dir + "/sound/loss.wav"; + + gameReset(); + // 安装事件过滤器监视scene的各个事件,由于我重载了QGraphicsScene,相关事件在重载函数中已设定,不必安装监视器。 + //scene.installEventFilter(this); +} + +GameController::~GameController() +{ +} + +const QMap GameController::getActions() +{ + // 主窗口更新菜单栏 + // 之所以不用信号和槽的模式,是因为发信号的时候槽还来不及关联 + QMap actions; + for (int i = 0; i < NineChess::RULENUM; i++) + { + // QMap的key存放int索引值,value存放规则名称和规则提示 + QStringList strlist; + strlist.append(tr(NineChess::RULES[i].name)); + strlist.append(tr(NineChess::RULES[i].info)); + actions.insert(i, strlist); + } + return actions; +} + + +void GameController::gameStart() +{ + // 每隔100毫秒调用一次定时器处理函数 + timeID = startTimer(100); +} + +void GameController::gameReset() +{ + // 停止计时器 + if (timeID != 0) + killTimer(timeID); + // 定时器ID为0 + timeID = 0; + // 重置游戏 + chess.reset(); + + // 清除棋子 + qDeleteAll(pieceList); + pieceList.clear(); + piece = NULL; + // 重新绘制棋盘 + scene.setDiagonal(chess.getRule()->hasObliqueLine); + + // 读取规则限时要求 + timeLimit = chess.getRule()->maxTime; + // 如果规则不要求计时,则time1和time2表示已用时间 + if (timeLimit <= 0) { + // 将玩家的已用时间清零 + time1 = time2 = 0; + } + else + { + // 将玩家的剩余时间置为限定时间 + time1 = time2 = timeLimit * 60000; + } + // 发出信号通知主窗口更新LCD显示 + QTime qtime = QTime(0, 0, 0, 0).addMSecs(time1); + emit time1Changed(qtime.toString("mm:ss.zzz")); + emit time2Changed(qtime.toString("mm:ss.zzz")); + // 发信号更新状态栏 + message = QString::fromStdString(chess.getTip()); + emit statusBarChanged(message); + // 播放音效 + playSound(soundNewgame); +} + +void GameController::setEditing(bool arg) +{ + isEditing = arg; +} + +void GameController::setInvert(bool arg) +{ + isInverted = arg; + // 遍历所有棋子 + foreach(PieceItem * p, pieceList) + { + if (p) + { + // 黑子变白 + if (p->getModel() == PieceItem::blackPiece) + p->setModel(PieceItem::whitePiece); + // 白子变黑 + else if (p->getModel() == PieceItem::whitePiece) + p->setModel(PieceItem::blackPiece); + // 刷新棋子显示 + p->update(); + } + } +} + +void GameController::setRule(int ruleNo) +{ + // 停止计时器 + if (timeID != 0) + killTimer(timeID); + // 定时器ID为0 + timeID = 0; + + // 更新规则,原限时和限步不变 + struct NineChess::Rule rule; + if (ruleNo >= 0 && ruleNo < NineChess::RULENUM) + rule = NineChess::RULES[ruleNo]; + rule.maxSteps = stepsLimit; + rule.maxTime = timeLimit; + // 设置模型规则,重置游戏 + chess.setRule(&rule); + + // 清除棋子 + qDeleteAll(pieceList); + pieceList.clear(); + piece = NULL; + // 重新绘制棋盘 + scene.setDiagonal(chess.getRule()->hasObliqueLine); + + // 如果规则不要求计时,则time1和time2表示已用时间 + if (timeLimit <= 0) { + // 将玩家的已用时间清零 + time1 = time2 = 0; + } + else + { + // 将玩家的剩余时间置为限定时间 + time1 = time2 = timeLimit * 60000; + } + // 发出信号通知主窗口更新LCD显示 + QTime qtime = QTime(0, 0, 0, 0).addMSecs(time1); + emit time1Changed(qtime.toString("mm:ss.zzz")); + emit time2Changed(qtime.toString("mm:ss.zzz")); + // 发信号更新状态栏 + message = QString::fromStdString(chess.getTip()); + emit statusBarChanged(message); + // 播放音效 + playSound(soundNewgame); +} + +void GameController::setRule(int ruleNo, int stepLimited, int timeLimited) +{ + // 停止计时器 + if (timeID != 0) + killTimer(timeID); + // 定时器ID为0 + timeID = 0; + + // 更新规则,原限时和限步不变 + struct NineChess::Rule rule; + if (ruleNo >= 0 && ruleNo < NineChess::RULENUM) + rule = NineChess::RULES[ruleNo]; + stepsLimit = rule.maxSteps = stepLimited; + timeLimit = rule.maxTime = timeLimited; + // 设置模型规则,重置游戏 + chess.setRule(&rule); + + // 清除棋子 + qDeleteAll(pieceList); + pieceList.clear(); + piece = NULL; + // 重新绘制棋盘 + scene.setDiagonal(chess.getRule()->hasObliqueLine); + + // 如果规则不要求计时,则time1和time2表示已用时间 + if (timeLimit <= 0) { + // 将玩家的已用时间清零 + time1 = time2 = 0; + } + else + { + // 将玩家的剩余时间置为限定时间 + time1 = time2 = timeLimit * 60000; + } + // 发出信号通知主窗口更新LCD显示 + QTime qtime = QTime(0, 0, 0, 0).addMSecs(time1); + emit time1Changed(qtime.toString("mm:ss.zzz")); + emit time2Changed(qtime.toString("mm:ss.zzz")); + // 发信号更新状态栏 + message = QString::fromStdString(chess.getTip()); + emit statusBarChanged(message); + // 播放音效 + playSound(soundNewgame); +} + +void GameController::setEngine1(bool arg) +{ + isEngine1 = arg; + if (arg) + qDebug() << "Player1 is computer."; + else + qDebug() << "Player1 is not computer."; +} + +void GameController::setEngine2(bool arg) +{ + isEngine2 = arg; + if (arg) + qDebug() << "Player2 is computer."; + else + qDebug() << "Player2 is not computer."; +} + +void GameController::setAnimation(bool arg) +{ + hasAnimation = arg; +} + +void GameController::setSound(bool arg) +{ + hasSound = arg; +} + +void GameController::playSound(QString &soundPath) +{ + if (hasSound) + QSound::play(soundPath); +} + +bool GameController::eventFilter(QObject * watched, QEvent * event) +{ + return QObject::eventFilter(watched, event); +} + +void GameController::timerEvent(QTimerEvent *event) +{ + static QTime qt1, qt2; + // 玩家的已用时间 + chess.getPlayer_TimeMS(time1, time2); + // 如果规则要求计时,则time1和time2表示倒计时 + if (timeLimit > 0) + { + // 玩家的剩余时间 + time1 = timeLimit * 60000 - time1; + time2 = timeLimit * 60000 - time2; + } + qt1 = QTime(0, 0, 0, 0).addMSecs(time1); + qt2 = QTime(0, 0, 0, 0).addMSecs(time2); + emit time1Changed(qt1.toString("mm:ss.zzz")); + emit time2Changed(qt2.toString("mm:ss.zzz")); + // 如果胜负已分 + if (chess.whoWin() != NineChess::NOBODY) + { + // 停止计时 + killTimer(timeID); + // 定时器ID为0 + timeID = 0; + // 发信号更新状态栏 + message = QString::fromStdString(chess.getTip()); + emit statusBarChanged(message); + // 播放音效 + playSound(soundWin); + } + /* + int ti = time.elapsed(); + static QTime t; + if (ti < 0) + ti += 86400; // 防止过24:00引起的时间误差,加上一天中总秒数 + if (timeWhos == 1) + { + time1 = ti - time2; + // 用于显示时间的临时变量,多出的50毫秒用于消除计时器误差产生的跳动 + t = QTime(0, 0, 0, 50).addMSecs(time1); + //qDebug() << t; + emit time1Changed(t.toString("hh:mm:ss")); + } + else if (timeWhos == 2) + { + time2 = ti - time1; + // 用于显示时间的临时变量,多出的50毫秒用于消除计时器误差产生的跳动 + t = QTime(0, 0, 0, 50).addMSecs(time2); + //qDebug() << t; + emit time2Changed(t.toString("hh:mm:ss")); + } + */ +} + +// 槽函数,根据QGraphicsScene的信号和状态来执行选子、落子或去子 +bool GameController::actionPiece(QPointF pos) +{ + bool result = false; + switch (chess.getPhase()) { + case NineChess::GAME_NOTSTARTED: + // 如果未开局则开局,这里还要继续判断,不可break + gameStart(); + chess.start(); + case NineChess::GAME_OPENING: + // 如果是开局阶段(轮流落下新子),落子 + if (chess.getAction() == NineChess::ACTION_PLACE) { + result = placePiece(pos); + }// 去子 + else if (chess.getAction() == NineChess::ACTION_REMOVE) { + result = removePiece(pos); + } + // 如果完成后进入中局,则删除禁点 + if (chess.getPhase() == NineChess::GAME_MID && chess.getRule()->hasForbidden) + cleanForbidden(); + break; + case NineChess::GAME_MID: + // 如果是中局阶段(轮流移子) + // 选子 + if (chess.getAction() == NineChess::ACTION_CHOOSE) { + result = choosePiece(pos); + }// 移子 + else if (chess.getAction() == NineChess::ACTION_PLACE) { + // 如果移子不成功,尝试重新选子 + if (!movePiece(pos)) + result = choosePiece(pos); + }// 去子 + else if (chess.getAction() == NineChess::ACTION_REMOVE) { + result = removePiece(pos); + } + break; + // 如果是结局状态,不做任何响应 + default: + break; + } + if (result) + { + if (chess.whoWin() != NineChess::NOBODY) + playSound(soundWin); + } + return result; +} + +// 选子 +PieceItem *GameController::choosePiece(QPointF pos) +{ + int c, p; + if (!scene.pos2cp(pos, c, p)) + return false; + PieceItem *piece = NULL; + QGraphicsItem *item = scene.itemAt(pos, QTransform()); + if (!item) { + scene.clearSelection(); + this->piece->setSelected(true); + return false; + } + piece = qgraphicsitem_cast(item); + if (!piece) + return false; + if (chess.choose(c, p)) { + scene.clearSelection(); + this->piece = piece; + this->piece->setSelected(true); + // 发信号更新状态栏 + message = QString::fromStdString(chess.getTip()); + emit statusBarChanged(message); + // 播放音效 + playSound(soundChoose); + return piece; + } + else + { + scene.clearSelection(); + if (this->piece) + this->piece->setSelected(true); + } + return NULL; +} + +// 落下新子 +PieceItem *GameController::placePiece(QPointF pos) +{ + int c, p; + if (!scene.pos2cp(pos, c, p)) + return NULL; + PieceItem *newP = NULL; + PieceItem::Models md; + if (chess.whosTurn() == NineChess::PLAYER1) + { + md = isInverted ? PieceItem::whitePiece : PieceItem::blackPiece; + } + else { + md = isInverted ? PieceItem::blackPiece : PieceItem::whitePiece; + } + if (!chess.place(c, p)) { + scene.clearSelection(); + if (this->piece) + this->piece->setSelected(true); + return NULL; + } + newP = new PieceItem; + newP->setModel(md); + newP->setDeleted(false); + newP->setPos(pos); + newP->setNum(chess.getPieceNum(c, p)); + // 如果重复三连不可用,则显示棋子序号 + if (!(chess.getRule()->canRepeated)) + newP->setShowNum(true); + pieceList.append(newP); + scene.addItem(newP); + scene.clearSelection(); + this->piece = newP; + this->piece->setSelected(true); + // 发信号更新状态栏 + message = QString::fromStdString(chess.getTip()); + emit statusBarChanged(message); + // 播放音效 + playSound(soundDrog); + return newP; +} + +// 移动旧子 +bool GameController::movePiece(QPointF pos) +{ + if (!piece) + return false; + int c, p; + if (!scene.pos2cp(pos, c, p)) + return false; + if (chess.place(c, p)) + { + piece->setPos(pos); + // 发信号更新状态栏 + message = QString::fromStdString(chess.getTip()); + emit statusBarChanged(message); + // 播放音效 + playSound(soundMove); + return true; + } + scene.clearSelection(); + this->piece->setSelected(true); + return false; +} + +// 去子 +bool GameController::removePiece(QPointF pos) +{ + int c, p; + if (!scene.pos2cp(pos, c, p)) + return false; + if (!chess.remove(c, p)) { + scene.clearSelection(); + if (this->piece) + this->piece->setSelected(true); + return false; + } + + PieceItem *piece = NULL; + QGraphicsItem *item = scene.itemAt(pos, QTransform()); + if (!item) { + scene.clearSelection(); + if (this->piece) + this->piece->setSelected(true); + return false; + } + piece = qgraphicsitem_cast(item); + if (!piece) { + scene.clearSelection(); + if (this->piece) + this->piece->setSelected(true); + return false; + } + // 如果开局阶段有禁点 + if (chess.getPhase() == NineChess::GAME_OPENING && chess.getRule()->hasForbidden) + { + piece->setDeleted(); + } + else + { + pieceList.removeOne(piece); + delete piece; + this->piece = NULL; + } + scene.clearSelection(); + // 发信号更新状态栏 + message = QString::fromStdString(chess.getTip()); + emit statusBarChanged(message); + // 播放音效 + playSound(soundRemove); + return true; +} + +bool GameController::cleanForbidden() +{ + for each (PieceItem *p in pieceList) + { + if (p->isDeleted()) { + pieceList.removeOne(p); + delete p; + } + } + return true; +} diff --git a/NineChess/src/gamecontroller.h b/NineChess/src/gamecontroller.h new file mode 100644 index 00000000..5448b209 --- /dev/null +++ b/NineChess/src/gamecontroller.h @@ -0,0 +1,128 @@ +/* 这个类处理场景对象QGraphicsScene + * 它是本程序MVC模型中唯一的控制模块 + * 它不对主窗口中的控件做任何操作,只向主窗口发出信号 + * 本来可以重载QGraphicsScene实现它,还能省去写事件过滤器的麻烦 + * 但用一个场景类做那么多控制模块的操作看上去不太好 + */ + +#ifndef GAMECONTROLLER_H +#define GAMECONTROLLER_H + +#include +#include +#include +#include +#include "gamescene.h" +#include "ninechess.h" + +class PieceItem; + +class GameController : public QObject +{ + Q_OBJECT + +public: + GameController(GameScene &scene, QObject *parent = 0); + ~GameController(); + //主窗口菜单栏明细 + const QMap getActions(); + int getTimeLimit() { return timeLimit; } + int getStepsLimit() { return stepsLimit; } + +signals: + // 玩家1(先手)用时改变的信号 + void time1Changed(const QString &time); + // 玩家2(后手)用时改变的信号 + void time2Changed(const QString &time); + // 通知主窗口更新状态栏的信号 + void statusBarChanged(const QString & message); + +public slots: + // 游戏开始 + void gameStart(); + // 游戏重置 + void gameReset(); + // 设置编辑棋局状态 + void setEditing(bool arg = true); + // 设置黑白反转状态 + void setInvert(bool arg = true); + // 设置规则,不改变限时限步 + void setRule(int ruleNo); + // 设置规则,改变限时限步 + void setRule(int ruleNo, int stepLimited, int timeLimited); + // 让电脑执先手 + void setEngine1(bool arg = true); + // 让电脑执后手 + void setEngine2(bool arg = true); + // 是否有落子动画 + void setAnimation(bool arg = true); + // 是否有落子音效 + void setSound(bool arg = true); + // 播放声音 + void playSound(QString &soundPath); + // 槽函数,根据QGraphicsScene的信号和状态来执行选子、落子或去子 + bool actionPiece(QPointF p); + +protected: + bool eventFilter(QObject * watched, QEvent * event); + void timerEvent(QTimerEvent * event); + // 选子 + PieceItem *choosePiece(QPointF pos); + // 落下新子 + PieceItem *placePiece(QPointF pos); + // 移动旧子 + bool movePiece(QPointF pos); + // 去子 + bool removePiece(QPointF pos); + // 删除禁止点子 + bool cleanForbidden(); + +private: + // 棋对象的数据模型 + NineChess chess; + // 棋局的场景类 + GameScene &scene; + // 所有棋子 + QList pieceList; + // 当前棋子 + PieceItem *piece; + // 玩家1手棋数、玩家2手棋数、待去棋数 + int player1_InHand, player2_InHand, num_NeedRemove; + // 是否处于“编辑棋局”状态 + bool isEditing; + // 是否黑白反转 + bool isInverted; + // 是否电脑执先手 + bool isEngine1; + // 是否电脑执后手 + bool isEngine2; + // 是否有落子动画 + bool hasAnimation; + // 是否有落子音效 + bool hasSound; + // 定时器ID + int timeID; + // 规则限时(分钟) + int timeLimit; + // 规则限步数 + int stepsLimit; + // 玩家1剩余时间(毫秒) + int time1; + // 玩家2剩余时间(毫秒) + int time2; + // 用于主窗口状态栏显示的字符串 + QString message; + // 文字棋谱 + QStringList manual; + // 各个音效文件路径 + QString soundNewgame; + QString soundChoose; + QString soundMove; + QString soundDrog; + QString soundForbidden; + QString soundRemove; + QString soundWin; + QString soundLoss; +}; + +#endif // GAMECONTROLLER_H diff --git a/NineChess/src/gamescene.cpp b/NineChess/src/gamescene.cpp new file mode 100644 index 00000000..fea7e2f6 --- /dev/null +++ b/NineChess/src/gamescene.cpp @@ -0,0 +1,93 @@ +#include "gamescene.h" +#include "pieceitem.h" +#include "boarditem.h" +#include +#include +#include +#include +#include + +GameScene::GameScene(QObject *parent) : QGraphicsScene(parent), board(NULL) +{ + // 添加棋盘 + board = new BoardItem; + board->setDiagonal(false); + addItem(board); +} + +GameScene::~GameScene() +{ + if(board) + delete board; +} + +void GameScene::keyPressEvent(QKeyEvent *keyEvent) +{ + // 屏蔽掉Shift和Control按键,事实证明没用,按键事件未必由试图类处理 + if(keyEvent->key() == Qt::Key_Shift || keyEvent->key() == Qt::Key_Control) + return; + QGraphicsScene::keyPressEvent(keyEvent); +} + +void GameScene::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *mouseEvent) +{ + //什么也不做,屏蔽双击事件 +} + +void GameScene::mousePressEvent(QGraphicsSceneMouseEvent *mouseEvent) +{ + // 只处理左键事件 + if(mouseEvent->button() != Qt::LeftButton) + return; + // 调用默认事件处理函数 + QGraphicsScene::mousePressEvent(mouseEvent); + + // 如果不是棋子则结束 + QGraphicsItem *item = itemAt(mouseEvent->scenePos(), QTransform()); + if (!item || item->type() != PieceItem::Type) + { + return; + } + + // 取消其它棋子的选中状态 + //for each (QGraphicsItem * item in selectedItems()) + //{ + // item->setSelected(false); + //} +} + +void GameScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *mouseEvent) +{ + // 只处理左键事件 + if(mouseEvent->button() != Qt::LeftButton) + return; + // 调用默认事件处理函数 + QGraphicsScene::mouseReleaseEvent(mouseEvent); + + // 如果是棋盘 + QGraphicsItem *item = itemAt(mouseEvent->scenePos(), QTransform()); + if (!item || item->type() == BoardItem::Type) + { + QPointF p = mouseEvent->scenePos(); + p = board->nearestPosition(p); + if (p != QPointF(0, 0)) + // 发送鼠标点最近的落子点 + emit mouseReleased(p); + }// 如果是棋子 + else if (item->type() == PieceItem::Type) + { + // 将当前棋子在场景中的位置发送出去 + emit mouseReleased(item->scenePos()); + } +} + +bool GameScene::pos2cp(QPointF pos, int &c, int &p) +{ + return board->pos2cp(pos, c, p); +} + +void GameScene::setDiagonal(bool arg) +{ + if (board) + board->setDiagonal(arg); +} diff --git a/NineChess/src/gamescene.h b/NineChess/src/gamescene.h new file mode 100644 index 00000000..c4ab8589 --- /dev/null +++ b/NineChess/src/gamescene.h @@ -0,0 +1,36 @@ +#ifndef GAMESCENE_H +#define GAMESCENE_H + +#include + +class BoardItem; + +class GameScene : public QGraphicsScene +{ + Q_OBJECT +public: + explicit GameScene(QObject *parent = nullptr); + ~GameScene(); + // 将落子点坐标转化为模型用的圈、位 + bool pos2cp(QPointF pos, int &c, int &p); + // 设置棋盘斜线 + void setDiagonal(bool arg = true); + +protected: + void keyPressEvent(QKeyEvent *keyEvent); + void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *mouseEvent); + void mousePressEvent(QGraphicsSceneMouseEvent *mouseEvent); + void mouseReleaseEvent(QGraphicsSceneMouseEvent *mouseEvent); + +signals: + void mouseReleased(QPointF); + +public slots: + +private: + // 棋盘对象 + BoardItem *board; + +}; + +#endif // GAMESCENE_H diff --git a/NineChess/src/gameview.cpp b/NineChess/src/gameview.cpp new file mode 100644 index 00000000..2eb63de9 --- /dev/null +++ b/NineChess/src/gameview.cpp @@ -0,0 +1,99 @@ +#include "gameview.h" +#include +#include + +GameView::GameView(QWidget * parent) : + QGraphicsView(parent) +{ + Q_UNUSED(parent) + // 初始化缩放因子为1.0 + sx = 1.0; + sy = 1.0; +} + +GameView::~GameView() +{ +} + +void GameView::flip() +{ + // 视图上下翻转 + /* 以下用到了很多图形变换矩阵方面的知识 + * 不要用scale方法,Qt的图形变换是针对坐标系的 + * 缩放矩阵为 + * ┌sx 0 0┐ + * S = │ 0 sy 0│ + * └ 0 0 1┘ + * 上下翻转应在原变换矩阵基础上乘以一个如下的矩阵: + * ┌1 0 0┐ + * │0 -1 0│ + * └0 0 1┘ + */ + // 方法一: 直接在原变换矩阵基础上乘以上面的矩阵 + // QMatrix只对变换矩阵前两列赋值 + setMatrix(matrix() * QMatrix(1, 0, 0, -1, 0, 0)); + /* 方法二: 人工计算好新的变换矩阵后再对场景赋值 + * 这个方法的效率未必高,还需要人工计算 + QMatrix mt = matrix(); + mt.setMatrix(-mt.m11(), mt.m12(), -mt.m21(), mt.m22(), -mt.dx(), mt.dy()); + setMatrix(mt); + */ +} + +void GameView::mirror() +{ + // 视图左右镜像 + /* 左右镜像应在原变换矩阵基础上乘以一个如下的矩阵: + * ┌-1 0 0┐ + * │ 0 1 0│ + * └ 0 0 1┘ + */ + setMatrix(matrix() * QMatrix(-1, 0, 0, 1, 0, 0)); +} + +void GameView::turnRight() +{ + // 视图须时针旋转90° + /* 不要用scale方法,视图镜像或翻转后它的转向会反过来 + * 旋转矩阵为 + * ┌ cos(α) sin(α) 0┐ + * R = │-sin(α) cos(α) 0│ + * └ 0 0 1┘ + * 视图须时针旋转90°应在原变换矩阵基础上乘以一个如下的矩阵: + * ┌ 0 1 0┐ + * │-1 0 0│ + * └ 0 0 1┘ + */ + setMatrix(matrix() * QMatrix(0, 1, -1, 0, 0, 0)); +} + +void GameView::turnLeft() +{ + // 视图逆时针旋转90° + /* 视图逆时针旋转90°应在原变换矩阵基础上乘以一个如下的矩阵: + * ┌0 -1 0┐ + * │1 0 0│ + * └0 0 1┘ + */ + setMatrix(matrix() * QMatrix(0, -1, 1, 0, 0, 0)); +} + + +void GameView::resizeEvent(QResizeEvent * event) +{ + // 让场景适合视图 + Q_UNUSED(event) + if (sceneRect().width() <= 0 || sceneRect().height() <= 0) + return; + // 恢复缩放前的大小 + scale(1/sx, 1/sy); + // 设置缩放因子 + sx = width() / sceneRect().width(); + sy = height() / sceneRect().height(); + sx = sx < sy ? sx : sy; + sy = sx; + // 缩放视图适合场景大小 + scale(sx, sy); + //qDebug() << "scale :" << sx; +} + diff --git a/NineChess/src/gameview.h b/NineChess/src/gameview.h new file mode 100644 index 00000000..05ec4e42 --- /dev/null +++ b/NineChess/src/gameview.h @@ -0,0 +1,29 @@ +// 派生这个类主要是为了让视图适应场景大小及图像旋转镜像操作 +#ifndef GRAPHICSVIEW_H +#define GRAPHICSVIEW_H + +#include + +class GameView : public QGraphicsView +{ + Q_OBJECT + +public: + explicit GameView(QWidget * parent); + ~GameView(); + +public slots: + void flip(); + void mirror(); + void turnRight(); + void turnLeft(); + +protected: + void resizeEvent(QResizeEvent * event); + +private: + // 缩放因子 + qreal sx, sy; +}; + +#endif // GRAPHICSVIEW_H diff --git a/NineChess/src/graphicsconst.h b/NineChess/src/graphicsconst.h new file mode 100644 index 00000000..e7dbd9c4 --- /dev/null +++ b/NineChess/src/graphicsconst.h @@ -0,0 +1,12 @@ +// 定义绘图相关常量的头文件 +#ifndef GRAPHICSCONST +#define GRAPHICSCONST + +const int BOARD_SIZE = 600; // 棋盘大小 +const int BOARD_MINISIZE = 150; // 最小宽高,即1/4大小 +const int PIECE_SIZE = 56; // 棋子大小 +const int LINE_INTERVAL = 72; // 线间距 +const int LINE_WEIGHT = 3; // 线宽 + +#endif // GRAPHICSCONST + diff --git a/NineChess/src/main.cpp b/NineChess/src/main.cpp new file mode 100644 index 00000000..6ad34f53 --- /dev/null +++ b/NineChess/src/main.cpp @@ -0,0 +1,10 @@ +#include "ninechesswindow.h" +#include + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + NineChessWindow w; + w.show(); + return a.exec(); +} diff --git a/NineChess/src/ninechess.cpp b/NineChess/src/ninechess.cpp new file mode 100644 index 00000000..fb5ccadd --- /dev/null +++ b/NineChess/src/ninechess.cpp @@ -0,0 +1,967 @@ +/**************************************************************************** +** by liuweilhy, 2013.01.14 +** Mail: liuweilhy@163.com +** This file is part of the NineChess game. +****************************************************************************/ +#if _MSC_VER >= 1600 +#pragma execution_character_set("utf-8") +#endif + +#include "ninechess.h" + +// 对静态常量数组的定义要放在类外,不要放在头文件 +// 预定义的4套规则 +const struct NineChess::Rule NineChess::RULES[RULENUM] = { +{ + "成三棋", // 成三棋 + "双方各九颗子,开局依次摆子;\n" // 规则说明 + "凡出现三子相连,就提掉对手一子;\n" + "不能提对手的“三连”子,除非无子可提;\n" + "同时出现两个“三连”只能提一子;\n" + "摆完后依次走子,每次只能往相邻位置走一步;\n" + "把对手棋子提到少于3颗时胜利;\n" + "走棋阶段不能行动(被“闷”)算负。", + 9, // 双方各9子 + 3, // 赛点子数为3 + false, // 没有斜线 + false, // 没有禁点,摆棋阶段被提子的点可以再摆子 + false, // 先摆棋者先行棋 + true, // 可以重复成三 + false, // 多个“三连”只能提一子 + true, // 摆棋满子(闷棋,只有12子棋才出现)算先手负 + true, // 走棋阶段不能行动(被“闷”)算负 + false, // 剩三子时不可以飞棋 + 0, // 不计步数 + 0 // 不计时 +}, +{ + "打三棋(12连棋)", // 打三棋 + "规则与成三棋基本相同,只是它用12颗子,并且有斜线;\n" // 规则说明 + "摆棋阶段被提子的位置不能再摆子,直到走棋阶段;\n" + "摆棋阶段,摆满棋盘算先手负;\n" + "走棋阶段,后摆棋的一方先走;\n" + "一步出现几个“三连”就可以提几个子。", + 12, // 双方各12子 + 3, // 赛点子数为3 + true, // 有斜线 + true, // 有禁点,摆棋阶段被提子的点不能再摆子 + true, // 后摆棋者先行棋 + true, // 可以重复成三 + true, // 出现几个“三连”就可以提几个子 + true, // 摆棋满子(闷棋,只有12子棋才出现)算先手负 + true, // 走棋阶段不能行动(被“闷”)算负 + false, // 剩三子时不可以飞棋 + 0, // 不计步数 + 0 // 不计时 +}, +{ + "九连棋", // 九连棋 + "规则与成三棋基本相同,只是它的棋子有序号,\n" // 规则说明 + "相同序号、位置的“三连”不能重复提子;\n" + "走棋阶段不能行动(被“闷”),则由对手继续走棋;\n" + "一步出现几个“三连”就可以提几个子。", + 9, // 双方各9子 + 3, // 赛点子数为3 + false, // 没有斜线 + false, // 没有禁点,摆棋阶段被提子的点可以再摆子 + false, // 先摆棋者先行棋 + false, // 不可以重复成三 + true, // 出现几个“三连”就可以提几个子 + true, // 摆棋满子(闷棋,只有12子棋才出现)算先手负 + false, // 走棋阶段不能行动(被“闷”),则由对手继续走棋 + false, // 剩三子时不可以飞棋 + 0, // 不计步数 + 0 // 不计时 +}, +{ + "莫里斯九子棋", // 莫里斯九子棋 + "规则与成三棋基本相同,只是在走子阶段,\n" // 规则说明 + "当一方仅剩3子时,他可以飞子到任意空位。", + 9, // 双方各9子 + 3, // 赛点子数为3 + false, // 没有斜线 + false, // 没有禁点,摆棋阶段被提子的点可以再摆子 + false, // 先摆棋者先行棋 + true, // 可以重复成三 + false, // 多个“三连”只能提一子 + true, // 摆棋满子(闷棋,只有12子棋才出现)算先手负 + true, // 走棋阶段不能行动(被“闷”)算负 + true, // 剩三子时可以飞棋 + 0, // 不计步数 + 0 // 不计时 +} +}; + +const char NineChess::inBoard[(RING + 2)*SEAT] = { + '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', + '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', + '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', + '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', + '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00' +}; + +char NineChess::moveTable[(RING + 2)*SEAT][4] = { 0 }; +char NineChess::millTable[(RING + 2)*SEAT][3][2] = { 0 }; + +NineChess::NineChess() +{ + // 默认选择第0号规则,即“成三棋” + setData(&RULES[0]); +} + +NineChess::~NineChess() +{ +} + +bool NineChess::setData(const struct Rule *rule, int step, int flags, const char *boardsource, + int p1_InHand, int p2_InHand, int num_NeedRemove) +{ + // 根据规则 + this->rule = *rule; + // 设置步数 + this->step = step; + // 设置状态 + // 局面阶段标识 + if (flags & GAME_NOTSTARTED) + phase = GAME_NOTSTARTED; + else if (flags & GAME_OPENING) + phase = GAME_OPENING; + else if (flags & GAME_MID) + phase = GAME_MID; + else if (flags & GAME_OVER) + phase = GAME_OVER; + else + return false; + // 轮流状态标识 + if (flags & PLAYER1) + turn = PLAYER1; + else if (flags & PLAYER2) + turn = PLAYER2; + else + return false; + // 动作状态标识 + if (flags & ACTION_CHOOSE) + action = ACTION_CHOOSE; + else if (flags & ACTION_PLACE) + action = ACTION_PLACE; + else if (flags & ACTION_REMOVE) + action = ACTION_REMOVE; + else + return false; + // 胜负标识 + winner = NOBODY; + // 当前棋局(3×8) + if (boardsource == NULL) + memset(this->board, 0, sizeof(this->board)); + else + memcpy(this->board, boardsource, sizeof(this->board)); + // 生成招法表 + for (int i = 1; i <= RING; i++) + { + for (int j = 0; j < SEAT; j++) + { + // 顺时针走一步的位置 + moveTable[i*SEAT + j][0] = i * SEAT + (j + 1) % SEAT; + // 逆时针走一步的位置 + moveTable[i*SEAT + j][1] = i * SEAT + (j + SEAT - 1) % SEAT; + // 如果是0、2、4、6位(偶数位)或是有斜线 + if (!(j & 1) || this->rule.hasObliqueLine) { + if (i > 1) { + // 向内走一步的位置 + moveTable[i*SEAT + j][2] = (i - 1)*SEAT + j; + } + if (i < RING) { + // 向外走一步的位置 + moveTable[i*SEAT + j][3] = (i + 1)*SEAT + j; + } + } + // 对于无斜线情况下的1、3、5、7位(奇数位),则都设为棋盘外点(默认'\x00') + //else { + // // 向内走一步的位置设为随便棋盘外一点 + // moveTable[i*SEAT+j][2] = '\x00'; + // // 向外走一步的位置设为随便棋盘外一点 + // moveTable[i*SEAT+j][3] = '\x00'; + //} + } + } + // 生成成三表 + for (int j = 0; j < SEAT; j++) + { + // 内外方向的“成三” + // 如果是0、2、4、6位(偶数位)或是有斜线 + if (!(j & 1) || this->rule.hasObliqueLine) { + millTable[1 * SEAT + j][0][0] = 2 * SEAT + j; + millTable[1 * SEAT + j][0][1] = 3 * SEAT + j; + millTable[2 * SEAT + j][0][0] = 1 * SEAT + j; + millTable[2 * SEAT + j][0][1] = 3 * SEAT + j; + millTable[3 * SEAT + j][0][0] = 1 * SEAT + j; + millTable[3 * SEAT + j][0][1] = 2 * SEAT + j; + } + // 对于无斜线情况下的1、3、5、7位(奇数位) + else { + // 置空该组“成三” + millTable[1 * SEAT + j][0][0] = 0; + millTable[1 * SEAT + j][0][1] = 0; + millTable[2 * SEAT + j][0][0] = 0; + millTable[2 * SEAT + j][0][1] = 0; + millTable[3 * SEAT + j][0][0] = 0; + millTable[3 * SEAT + j][0][1] = 0; + } + // 当前圈上的“成三” + // 如果是0、2、4、6位 + if (!(j & 1)) { + millTable[1 * SEAT + j][1][0] = 1 * SEAT + (j + 1) % SEAT; + millTable[1 * SEAT + j][1][1] = 1 * SEAT + (j + SEAT - 1) % SEAT; + millTable[2 * SEAT + j][1][0] = 2 * SEAT + (j + 1) % SEAT; + millTable[2 * SEAT + j][1][1] = 2 * SEAT + (j + SEAT - 1) % SEAT; + millTable[3 * SEAT + j][1][0] = 3 * SEAT + (j + 1) % SEAT; + millTable[3 * SEAT + j][1][1] = 3 * SEAT + (j + SEAT - 1) % SEAT; + // 置空另一组“成三” + millTable[1 * SEAT + j][2][0] = 0; + millTable[1 * SEAT + j][2][1] = 0; + millTable[2 * SEAT + j][2][0] = 0; + millTable[2 * SEAT + j][2][1] = 0; + millTable[3 * SEAT + j][2][0] = 0; + millTable[3 * SEAT + j][2][1] = 0; + } + // 对于1、3、5、7位(奇数位) + else { + // 当前圈上逆时针的“成三” + millTable[1 * SEAT + j][1][0] = 1 * SEAT + (j + SEAT - 2) % SEAT; + millTable[1 * SEAT + j][1][1] = 1 * SEAT + (j + SEAT - 1) % SEAT; + millTable[2 * SEAT + j][1][0] = 2 * SEAT + (j + SEAT - 2) % SEAT; + millTable[2 * SEAT + j][1][1] = 2 * SEAT + (j + SEAT - 1) % SEAT; + millTable[3 * SEAT + j][1][0] = 3 * SEAT + (j + SEAT - 2) % SEAT; + millTable[3 * SEAT + j][1][1] = 3 * SEAT + (j + SEAT - 1) % SEAT; + // 当前圈上顺时针的“成三” + millTable[1 * SEAT + j][2][0] = 1 * SEAT + (j + 1) % SEAT; + millTable[1 * SEAT + j][2][1] = 1 * SEAT + (j + 2) % SEAT; + millTable[2 * SEAT + j][2][0] = 2 * SEAT + (j + 1) % SEAT; + millTable[2 * SEAT + j][2][1] = 2 * SEAT + (j + 2) % SEAT; + millTable[3 * SEAT + j][2][0] = 3 * SEAT + (j + 1) % SEAT; + millTable[3 * SEAT + j][2][1] = 3 * SEAT + (j + 2) % SEAT; + } + } + + // 计算盘面子数 + player1_Remain = player2_Remain = 0; + for (int i = 1; i < RING + 2; i++) + { + for (int j = 0; j < SEAT; j++) + { + if (board[i*SEAT + j] & '\x10') + player1_Remain++; + else if (board[i*SEAT + j] & '\x20') { + player2_Remain++; + } + } + } + // 设置玩家盘面剩余子数和未放置子数 + if (player1_Remain > rule->numOfChess || player2_Remain > rule->numOfChess) + return false; + if (p1_InHand < 0 || p2_InHand < 0) + return false; + player1_InHand = rule->numOfChess - player1_Remain; + player2_InHand = rule->numOfChess - player2_Remain; + player1_InHand = p1_InHand < player1_InHand ? p1_InHand : player1_InHand; + player2_InHand = p2_InHand < player2_InHand ? p2_InHand : player2_InHand; + + // 设置去子状态时的剩余尚待去除子数 + if (flags & ACTION_REMOVE) { + if (num_NeedRemove >= 0 && num_NeedRemove < 3) + this->num_NeedRemove = num_NeedRemove; + } + else + this->num_NeedRemove = 0; + + // 清空成三记录 + millList.clear(); + + // 不选中棋子 + posOfSelected = 0; + + // 用时置零 + player1_MS = player2_MS = 0; + setTip(); + return true; +} + +bool NineChess::setRule(const struct Rule *rule) +{ + setData(rule); + return true; +} + +void NineChess::getData(struct Rule &rule, int &step, int &chess, const char *&board, + int &p1_InHand, int &p2_InHand, int &num_NeedRemove) +{ + rule = this->rule; + step = this->step; + chess = phase | turn | action | winner; + board = this->board; + p1_InHand = player1_InHand; + p2_InHand = player2_InHand; + num_NeedRemove = this->num_NeedRemove; +} + +bool NineChess::reset() +{ + return setData(&rule); +} + +bool NineChess::start() +{ + if (phase == GAME_OVER) + reset(); + update(); + if (phase == GAME_NOTSTARTED) + phase = GAME_OPENING; + ftime(&startTimeb); + // 用时置零 + player1_MS = player2_MS = 0; + return true; +} + +int NineChess::cp2pos(int c, int p) +{ + if (c < 1 || c > RING || p < 1 || p > SEAT) + return 0; + return c * SEAT + p - 1; +} + +bool NineChess::place(int c, int p) +{ + // 如果局面为"未开局"或“结局”,返回false + if (phase == GAME_NOTSTARTED || phase == GAME_OVER) + return false; + // 如非“落子”状态,返回false + if (action != ACTION_PLACE) + return false; + // 如果落子位置在棋盘外、已有子点或禁点,返回false + int pos = cp2pos(c, p); + if (!inBoard[pos] || board[pos]) + return false; + + // 对于开局落子 + char piece = '\x00'; + int n = 0; + if (phase == GAME_OPENING) { + // 先手下 + if (turn == PLAYER1) + { + piece = '\x11' + rule.numOfChess - player1_InHand; + board[pos] = piece; + player1_InHand--; + player1_Remain++; + } + // 后手下 + else + { + piece = '\x21' + rule.numOfChess - player2_InHand; + board[pos] = piece; + player2_InHand--; + player2_Remain++; + } + posOfSelected = pos; + step++; + // 如果决出胜负 + if (win()) { + setTip(); + return true; + } + + n = addMills(posOfSelected); + // 开局阶段未成三 + if (n == 0) { + // 如果双方都无未放置的棋子 + if (player1_InHand == 0 && player2_InHand == 0) { + // 进入中局阶段 + phase = GAME_MID; + // 进入选子状态 + action = ACTION_CHOOSE; + // 清除禁点 + cleanForbidden(); + // 设置轮到谁走 + if (rule.isDefensiveMoveFirst) { + turn = PLAYER2; + } + else { + turn = PLAYER1; + } + // 再决胜负 + if (win()) { + setTip(); + return true; + } + } + // 如果双方还有子 + else { + // 设置轮到谁走 + changeTurn(); + } + } + // 如果成三 + else { + // 设置去子数目 + num_NeedRemove = rule.removeMore ? n : 1; + // 进入去子状态 + action = ACTION_REMOVE; + setTip(); + } + setTip(); + return true; + } + + // 对于中局落子 + else if (phase == GAME_MID) { + // 如果落子不合法 + if ((turn == PLAYER1 && (player1_Remain > rule.numAtLest || !rule.canFly)) || + (turn == PLAYER2 && (player2_Remain > rule.numAtLest || !rule.canFly))) { + int i; + for (i = 0; i < 4; i++) { + if (pos == moveTable[posOfSelected][i]) + break; + } + // 不在招法表中 + if (i == 4) + return false; + } + // 移子 + board[pos] = board[posOfSelected]; + board[posOfSelected] = '\x00'; + posOfSelected = pos; + step++; + n = addMills(posOfSelected); + // 中局阶段未成三 + if (n == 0) { + // 进入选子状态 + action = ACTION_CHOOSE; + // 设置轮到谁走 + changeTurn(); + // 如果决出胜负 + if (win()) { + setTip(); + return true; + } + } + // 中局阶段成三 + else { + // 设置去子数目 + num_NeedRemove = rule.removeMore ? n : 1; + // 进入去子状态 + action = ACTION_REMOVE; + setTip(); + } + setTip(); + return true; + } + + return false; +} + +bool NineChess::remove(int c, int p) +{ + // 如果局面为"未开局"或“结局”,返回false + if (phase == GAME_NOTSTARTED || phase == GAME_OVER) + return false; + // 如非“去子”状态,返回false + if (action != ACTION_REMOVE) + return false; + // 如果去子完成,返回false + if (num_NeedRemove <= 0) + return false; + int pos = cp2pos(c, p); + // 对手 + enum Player opponent = PLAYER2; + if (turn == PLAYER2) + opponent = PLAYER1; + // 判断去子不是对手棋 + if (getWhosPiece(c, p) != opponent) + return false; + + // 如果当前子是否处于“三连”之中,且对方还未全部处于“三连”之中 + if (isInMills(pos) && !isAllInMills(opponent)) { + return false; + } + + // 去子(设置禁点) + if (rule.hasForbidden && phase == GAME_OPENING) + board[pos] = '\x0f'; + else // 去子 + board[pos] = '\x00'; + if (turn == PLAYER1) + player2_Remain--; + else if (turn == PLAYER2) + player1_Remain--; + posOfSelected = 0; + num_NeedRemove--; + step++; + // 去子完成 + + // 如果决出胜负 + if (win()) { + setTip(); + return true; + } + // 还有其余的子要去吗 + if (num_NeedRemove > 0) { + // 继续去子 + return true; + } + // 所有去子都完成了 + else { + // 开局阶段 + if (phase == GAME_OPENING) { + // 如果双方都无未放置的棋子 + if (player1_InHand == 0 && player2_InHand == 0) { + // 进入中局阶段 + phase = GAME_MID; + // 进入选子状态 + action = ACTION_CHOOSE; + // 清除禁点 + cleanForbidden(); + // 设置轮到谁走 + if (rule.isDefensiveMoveFirst) { + turn = PLAYER2; + } + else { + turn = PLAYER1; + } + // 再决胜负 + if (win()) { + setTip(); + return true; + } + } + // 如果双方还有子 + else { + // 进入落子状态 + action = ACTION_PLACE; + // 设置轮到谁走 + changeTurn(); + // 如果决出胜负 + if (win()) { + setTip(); + return true; + } + } + } + // 中局阶段 + else { + // 进入选子状态 + action = ACTION_CHOOSE; + // 设置轮到谁走 + changeTurn(); + // 如果决出胜负 + if (win()) { + setTip(); + return true; + } + } + } + setTip(); + return true; +} + +bool NineChess::choose(int c, int p) +{ + // 如果局面不是"中局”,返回false + if (phase != GAME_MID) + return false; + // 如非“选子”或“落子”状态,返回false + if (action != ACTION_CHOOSE && action != ACTION_PLACE) + return false; + int pos = cp2pos(c, p); + // 根据先后手,判断可选子 + char t; + if (turn == PLAYER1) + t = '\x10'; + else if (turn == PLAYER2) + t = '\x20'; + // 判断选子是否可选 + if (board[pos] & t) { + // 判断pos处的棋子是否被“闷” + if (isSurrounded(pos)) { + return false; + } + // 选子 + posOfSelected = pos; + // 选子完成,进入落子状态 + action = ACTION_PLACE; + return true; + } + return false; +} + +inline bool NineChess::update() +{ + // 根据局面调整计时器 + switch (phase) + { + case NineChess::GAME_OPENING: + case NineChess::GAME_MID: + ftime(¤tTimeb); + // 在时间更新时增添一套胜负判断的代码,以免频繁调用win()带来的效率问题 + if (turn == PLAYER1) { + player1_MS = (long)(currentTimeb.time - startTimeb.time) * 1000 + + (currentTimeb.millitm - startTimeb.millitm) - player2_MS; + // 如果玩家1超时 + if (rule.maxTime > 0 && player1_MS > rule.maxTime * 60000) { + player1_MS = rule.maxTime * 60000; + winner = PLAYER2; + phase = GAME_OVER; + tip = "玩家1超时,恭喜玩家2获胜"; + } + } + else if (turn == PLAYER2) { + player2_MS = (long)(currentTimeb.time - startTimeb.time) * 1000 + + (currentTimeb.millitm - startTimeb.millitm) - player1_MS; + // 如果玩家2超时 + if (rule.maxTime > 0 && player2_MS > rule.maxTime * 60000) { + player2_MS = rule.maxTime * 60000; + winner = PLAYER1; + phase = GAME_OVER; + tip = "玩家2超时,恭喜玩家1获胜"; + } + } + return true; + case NineChess::GAME_NOTSTARTED: + return true; + case NineChess::GAME_OVER: + return true; + default: + return true; + } +} + +// 是否分出胜负 +bool NineChess::win() +{ + if (phase == GAME_OVER) + return true; + if (phase == GAME_NOTSTARTED) + return false; + + // 如果有时间限定 + if (rule.maxTime > 0) { + // 更新时间,判断胜负 + update(); + if (winner != NOBODY) + return true; + } + + // 如果有步数限定 + if (rule.maxSteps > 0) { + if (step > rule.maxSteps) { + winner = DRAW; + phase = GAME_OVER; + return true; + } + } + + // 如果玩家1子数小于赛点,则玩家2获胜 + if (player1_Remain + player1_InHand < rule.numAtLest) { + winner = PLAYER2; + phase = GAME_OVER; + return true; + } + // 如果玩家2子数小于赛点,则玩家1获胜 + else if (player2_Remain + player2_InHand < rule.numAtLest) { + winner = PLAYER1; + phase = GAME_OVER; + return true; + } + // 如果摆满了,根据规则判断胜负 + else if (player1_Remain + player2_Remain >= SEAT * RING) { + if (rule.isFullLose) { + winner = PLAYER2; + phase = GAME_OVER; + return true; + } + else + { + winner = DRAW; + phase = GAME_OVER; + return true; + } + } + // 如果中局被“闷” + else if (phase == GAME_MID && action == ACTION_CHOOSE && isAllSurrounded(turn)) { + tip = (turn == PLAYER1) ? "玩家1无子可走," : "玩家2无子可走,"; + // 规则要求被“闷”判负,则对手获胜 + if (rule.isNoWayLose) { + winner = (turn == PLAYER1) ? PLAYER2 : PLAYER1; + phase = GAME_OVER; + return true; + } + // 否则让棋,由对手走 + else + { + changeTurn(); + return false; + } + } + + return false; +} + +bool NineChess::command(const char *cmd) +{ + return true; +} + +void NineChess::setCmdline() +{ + +} + +int NineChess::isInMills(int pos) +{ + int n = 0; + int pos1, pos2; + char m = board[pos] & '\x30'; + for (int i = 0; i < 3; i++) + { + pos1 = millTable[pos][i][0]; + pos2 = millTable[pos][i][1]; + if (m & board[pos1] & board[pos2]) + n++; + } + return n; +} + +int NineChess::addMills(int pos) +{ + //成三用一个64位整数了,规则如下 + //0x 00 00 00 00 00 00 00 00 + // unused unused piece1 pos1 piece2 pos2 piece3 pos3 + //piece1、piece2、piece3按照序号从小到大顺序排放 + long long mill = 0; + int n = 0; + int p[3], min, temp; + char m = board[pos] & '\x30'; + for (int i = 0; i < 3; i++) + { + p[0] = pos; + p[1] = millTable[pos][i][0]; + p[2] = millTable[pos][i][1]; + // 如果成三 + if (m & board[p[1]] & board[p[2]]) { + // 排序 + for (int j = 0; j < 2; j++) { + min = j; + for (int k = j + 1; k < 3; k++) { + if (p[min] > p[k]) + min = k; + } + if (min != j) { + temp = p[min]; + p[min] = p[j]; + p[j] = temp; + } + } + // 成三 + mill = (((long long)board[p[0]]) << 40) + + (((long long)p[0]) << 32) + + (((long long)board[p[1]]) << 24) + + (((long long)p[1]) << 16) + + (((long long)board[p[2]]) << 8) + + (long long)p[2]; + + // 如果允许相同三连反复去子 + if (rule.canRepeated) { + n++; + } + // 如果不允许相同三连反复去子 + else + { + // 迭代器 + list::iterator itor; + // 遍历 + for ( itor = millList.begin(); itor != millList.end(); itor++) + { + if (mill == *itor) + break; + } + // 如果没找到历史项 + if (itor == millList.end()) { + n++; + millList.push_back(mill); + } + } + } + } + return n; +} + +bool NineChess::isAllInMills(enum Player player) +{ + char ch = '\x00'; + if (player == PLAYER1) + ch = '\x10'; + else if (player == PLAYER2) + ch = '\x20'; + else + return true; + + for (int i = 1; i <= RING; i++) + for (int j = 0; j < SEAT; j++) { + if (board[i*SEAT + j] & ch) { + if (!isInMills(i*SEAT + j)) { + return false; + } + } + } + return true; +} + +// 判断玩家的棋子是否被围 +bool NineChess::isSurrounded(int pos) +{ + // 判断pos处的棋子是否被“闷” + if ((turn == PLAYER1 && (player1_Remain > rule.numAtLest || !rule.canFly)) || + (turn == PLAYER2 && (player2_Remain > rule.numAtLest || !rule.canFly))) { + int i, movePos; + for (i = 0; i < 4; i++) { + movePos = moveTable[pos][i]; + if (movePos && !board[movePos]) + break; + } + // 被围住 + if (i == 4) + return true; + } + // 没被围住 + return false; +} + +// 判断玩家的棋子是否全部被围 +bool NineChess::isAllSurrounded(enum Player ply) +{ + char t = '\x30'; + if (ply == PLAYER1) + t &= '\x10'; + else if (ply == PLAYER2) + t &= '\x20'; + // 如果摆满 + if (player1_Remain + player2_Remain >= SEAT * RING) + return true; + for (int i = 1; i <= RING; i++) + { + for (int j = 0; j < SEAT; j++) + { + int movePos; + if (t & board[i*SEAT + j]) { + for (int k = 0; k < 4; k++) { + movePos = moveTable[i*SEAT + j][k]; + if (movePos && !board[movePos]) + return false; + } + } + } + } + return true; +} + +void NineChess::cleanForbidden() +{ + for (int i = 1; i <= RING; i++) + for (int j = 0; j < SEAT; j++) { + if (board[i*SEAT + j] == '\x0f') + board[i*SEAT + j] = '\x00'; + } +} + +enum NineChess::Player NineChess::changeTurn() +{ + // 设置轮到谁走 + return turn = (turn == PLAYER1) ? PLAYER2 : PLAYER1; +} + +void NineChess::setTip() +{ + switch (phase) + { + case NineChess::GAME_NOTSTARTED: + tip = "轮到玩家1落子,剩余" + std::to_string(player1_InHand) + "子"; + break; + case NineChess::GAME_OPENING: + if (action == ACTION_PLACE) { + if (turn == PLAYER1) { + tip = "轮到玩家1落子,剩余" + std::to_string(player1_InHand) + "子"; + } + else if (turn == PLAYER2) { + tip = "轮到玩家2落子,剩余" + std::to_string(player2_InHand) + "子"; + } + } + else if (action == ACTION_REMOVE) { + if (turn == PLAYER1) { + tip = "轮到玩家1去子,需去" + std::to_string(num_NeedRemove) + "子"; + } + else if (turn == PLAYER2) { + tip = "轮到玩家2去子,需去" + std::to_string(num_NeedRemove) + "子"; + } + } + break; + case NineChess::GAME_MID: + if (action == ACTION_PLACE || action == ACTION_CHOOSE) { + if (turn == PLAYER1) { + tip = "轮到玩家1选子移动"; + } + else if (turn == PLAYER2) { + tip = "轮到玩家2选子移动"; + } + } + else if (action == ACTION_REMOVE) { + if (turn == PLAYER1) { + tip = "轮到玩家1去子,需去" + std::to_string(num_NeedRemove) + "子"; + } + else if (turn == PLAYER2) { + tip = "轮到玩家2去子,需去" + std::to_string(num_NeedRemove) + "子"; + } + } + break; + case NineChess::GAME_OVER: + if (winner == DRAW) + tip = "超出限定步数,双方平局"; + else if (winner == PLAYER1) { + if (tip.find("无子可走") != tip.npos) + tip += "恭喜玩家1获胜!"; + else + tip = "恭喜玩家1获胜!"; + } + else if (winner == PLAYER2) { + if (tip.find("无子可走") != tip.npos) + tip += "恭喜玩家2获胜!"; + else + tip = "恭喜玩家2获胜!"; + } + break; + default: + break; + } +} + +enum NineChess::Player NineChess::getWhosPiece(int c, int p) +{ + int pos = cp2pos(c, p); + if (board[pos] & '\x10') + return PLAYER1; + else if (board[pos] & '\x20') + return PLAYER2; + return NOBODY; +} + +int NineChess::getPieceNum(int c, int p) +{ + int pos = cp2pos(c, p); + int n = 0x0f & board[pos]; + return n; +} + +void NineChess::getPlayer_TimeMS(int &p1_ms, int &p2_ms) +{ + update(); + p1_ms = player1_MS; + p2_ms = player2_MS; +} diff --git a/NineChess/src/ninechess.h b/NineChess/src/ninechess.h new file mode 100644 index 00000000..01dce4d1 --- /dev/null +++ b/NineChess/src/ninechess.h @@ -0,0 +1,251 @@ +/**************************************************************************** +** by liuweilhy, 2013.01.14 +** Mail: liuweilhy@163.com +** This file is part of the NineChess game. +****************************************************************************/ + +#ifndef NINECHESS +#define NINECHESS + +#include +#include +#include + +using std::string; +using std::list; + +// 棋类(在数据模型内,玩家只分先后手,不分黑白) +class NineChess +{ +public: + // 公有的结构体和枚举,只是类型定义,不是变量! + // 规则结构体 + struct Rule + { + // 规则名称 + const char *name; + // 规则介绍 + const char *info; + // 任一方子数,各9子或各12子 + int numOfChess; + // 赛点子数,少于则判负 + int numAtLest; + // 是否有斜线 + bool hasObliqueLine; + // 是否有禁点(摆棋阶段被提子的点不能再摆子) + bool hasForbidden; + // 是否后摆棋者先行棋 + bool isDefensiveMoveFirst; + // 相同顺序和位置的重复“三连”是否可反复提子 + bool canRepeated; + // 多个“三连”能否多提子 + bool removeMore; + // 摆棋满子(闷棋,只有12子棋才出现),是否算先手负,false为和棋 + bool isFullLose; + // 走棋阶段不能行动(被“闷”)是否算负,false则轮空(由对手走棋) + bool isNoWayLose; + // 剩三子时是否可以飞棋 + bool canFly; + // 最大步数,超出判和 + int maxSteps; + // 包干最长时间(秒),超出判负,为0则不计时 + int maxTime; + }; + + // 局面阶段标识 + enum Phases { + GAME_NOTSTARTED = 0x00000001, // 未开局 + GAME_OPENING = 0x00000002, // 开局(摆棋) + GAME_MID = 0x00000004, // 中局(走棋) + GAME_OVER = 0x00000008 // 结局 + }; + // 玩家标识,轮流状态,胜负标识 + enum Player { + PLAYER1 = 0x00000010, // 玩家1 + PLAYER2 = 0x00000020, // 玩家2 + DRAW = 0x00000040, // 双方和棋 + NOBODY = 0x00000080 // 胜负未分 + }; + // 动作状态标识 + enum Actions { + ACTION_CHOOSE = 0x00000100, // 选子 + ACTION_PLACE = 0x00000200, // 落子 + ACTION_REMOVE = 0x00000400 // 提子 + }; + + // 5个静态成员常量 + // 3圈,禁止修改! + static const int RING = 3; + // 8位,禁止修改! + static const int SEAT = 8; + // 预定义的规则数目 + static const int RULENUM = 4; + // 预定义的规则 + static const struct Rule RULES[RULENUM]; + +public: + NineChess(); + ~NineChess(); + // 设置棋局状态和棋盘数据,用于初始化 + bool setData(const struct Rule *rule, + int step = 0, // 默认起始步数为0 + int flags = GAME_NOTSTARTED | PLAYER1 | ACTION_PLACE | NOBODY, // 默认状态 + const char *boardsource = NULL, // 默认空棋盘 + int p1_InHand = 12, // 玩家1剩余未放置子数 + int p2_InHand = 12, // 玩家2剩余未放置子数 + int num_NeedRemove = 0 // 尚待去除的子数 + ); + // 设置规则 + bool setRule(const struct Rule *rule); + // 获取棋局状态和棋盘数据 + void getData(struct Rule &rule, int &step, int &flags, const char *&boardsource, int &p1_InHand, int &p2_InHand, int &num_NeedRemove); + // 获取当前规则 + const struct Rule *getRule() { return &rule; } + // 获取当前步数 + int getStep() { return step; } + // 获取局面阶段标识 + enum Phases getPhase() { return phase; } + // 获取轮流状态标识 + enum Player whosTurn() { return turn; } + // 获取动作状态标识 + enum Actions getAction() { return action; } + // 判断胜负 + enum Player whoWin() { return winner; } + // 获取当前招法 + const char *getCmdline() { return cmdline; } + // 获取棋局的字符提示 + const string getTip() { return tip; } + // 获取位置点棋子的归属人 + enum Player getWhosPiece(int c, int p); + // 获取位置点棋子的序号 + int getPieceNum(int c, int p); + + // 玩家1剩余未放置子数 + int getPlayer1_InHand() { return player1_InHand; } + // 玩家2剩余未放置子数 + int getPlayer2_InHand() { return player2_InHand; } + // 玩家1盘面剩余子数 + int getPlayer1_Remain() { return player1_Remain; } + // 玩家1盘面剩余子数 + int getPlayer2_Remain() { return player2_Remain; } + // 尚待去除的子数 + int getNum_NeedRemove() { return num_NeedRemove; } + // 玩家1和玩家2的用时 + void getPlayer_TimeMS(int &p1_ms, int &p2_ms); + + // 游戏清空 + bool reset(); + // 游戏开始 + bool start(); + // 选子,在第c圈第p个位置,为迎合日常,c和p下标都从1开始 + bool choose(int c, int p); + // 落子,在第c圈第p个位置,为迎合日常,c和p下标都从1开始 + bool place(int c, int p); + // 去子,在第c圈第p个位置,为迎合日常,c和p下标都从1开始 + bool remove(int c, int p); + // 命令行解析函数 + bool command(const char *cmd); + +protected: + // 设置当前招法的命令行指令,即一招棋谱 + void setCmdline(); + // 判断棋盘pos处的棋子处于几个“三连”中 + int isInMills(int pos); + // 判断玩家的所有棋子是否都处于“三连”状态 + bool isAllInMills(enum Player); + // 判断玩家的棋子是否被围 + bool isSurrounded(int pos); + // 判断玩家的棋子是否全部被围 + bool isAllSurrounded(enum Player); + // 三连加入列表 + int addMills(int pos); + // 将第c圈,第p位转化为棋盘下标形式,c和p下标都从1开始 + int cp2pos(int c, int p); + // 更新时间和状态,用内联函数以提高效率 + inline bool update(); + // 是否分出胜负 + bool win(); + // 清除所有禁点 + void cleanForbidden(); + // 改变轮流 + enum NineChess::Player changeTurn(); + // 设置提示 + void setTip(); + +private: + // 当前使用的规则 + struct Rule rule; + // 当前步数 + int step; + // 局面阶段标识 + enum Phases phase; + // 轮流状态标识 + enum Player turn; + // 动作状态标识 + enum Actions action; + // 赢家 + enum Player winner; + + // 玩家1剩余未放置子数 + int player1_InHand; + // 玩家2剩余未放置子数 + int player2_InHand; + // 玩家1盘面剩余子数 + int player1_Remain; + // 玩家1盘面剩余子数 + int player2_Remain; + // 尚待去除的子数 + int num_NeedRemove; + + // 游戏起始时间 + timeb startTimeb; + // 当前游戏时间 + timeb currentTimeb; + // 玩家1用时(毫秒) + long player1_MS; + // 玩家2用时(毫秒) + long player2_MS; + + /* 棋局,抽象为一个(5×8)的char数组,上下两行留空 + * 0x00代表无棋子 + * 0x0F代表禁点 + * 0x10~(N-1)+0x10代表先手第1~N子 + * 0x20~(N-1)+0x20代表后手第1~N子 + * 判断棋子是先手的用(board[i] & 0x10) + * 判断棋子是后手的用(board[i] & 0x20) + */ + char board[(RING+2)*SEAT]; + // 选中的棋子在board中的位置 + int posOfSelected; + // 空棋盘点位,用于判断一个棋子位置是否在棋盘上 + static const char inBoard[(RING+2)*SEAT]; + // 招法表,每个位置有最多4种走法:顺时针、逆时针、向内、向外 + static char moveTable[(RING+2)*SEAT][4]; + // 成三表,表示棋盘上各个位置有成三关系的对应位置表 + static char millTable[(RING+2)*SEAT][3][2]; + + /* 本打算用如下的结构体来表示“三连” + struct Mill { + char piece1; // “三连”中最小的棋子 + char pos1; // 最小棋子的位置 + char piece2; // 次小的棋子 + char pos2; // 次小棋子的位置 + char piece3; // 最大的棋子 + char pos3; // 最大棋子的位置 + }; + 但为了提高执行效率改用一个64位整数了,规则如下 + 0x 00 00 00 00 00 00 00 00 + unused unused piece1 pos1 piece2 pos2 piece3 pos3 + */ + // “三连列表” + list millList; + + // 当前招法的命令行指令,即一招棋谱 + char cmdline[32]; + // 当前棋局的字符提示 + string tip; + // 招法不用结构体表示了,用int表示=原位置*256+新位置,落子没有原位置的计为0xff + short move; +}; + +#endif diff --git a/NineChess/src/ninechesswindow.cpp b/NineChess/src/ninechesswindow.cpp new file mode 100644 index 00000000..ccae0464 --- /dev/null +++ b/NineChess/src/ninechesswindow.cpp @@ -0,0 +1,384 @@ +#if _MSC_VER >= 1600 +#pragma execution_character_set("utf-8") +#endif +#include "ninechesswindow.h" +#include "gamecontroller.h" +#include "gamescene.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +NineChessWindow::NineChessWindow(QWidget *parent) + : QMainWindow(parent), + scene(NULL), + game(NULL), + ruleNo(0) +{ + ui.setupUi(this); + //去掉标题栏 + //setWindowFlags(Qt::FramelessWindowHint); + //设置透明(窗体标题栏不透明,背景透明,如果不去掉标题栏,背景就变为黑色) + //setAttribute(Qt::WA_TranslucentBackground); + //设置全体透明度系数 + //setWindowOpacity(0.7); + + // 设置场景 + scene = new GameScene(this); + // 关联视图和场景 + ui.gameView->setScene(scene); + // 视图反走样 + ui.gameView->setRenderHint(QPainter::Antialiasing, true); + + // 初始化各个控件 + // 视图反锯齿 + ui.gameView->setRenderHint(QPainter::Antialiasing); + + // 关联既有动作信号和主窗口槽 + // 视图上下翻转 + connect(ui.actionFlip_F, &QAction::triggered, + ui.gameView, &GameView::flip); + // 视图左右镜像 + connect(ui.actionMirror_M, &QAction::triggered, + ui.gameView, &GameView::mirror); + // 视图须时针旋转90° + connect(ui.actionTurnRight_R, &QAction::triggered, + ui.gameView, &GameView::turnRight); + // 视图逆时针旋转90° + connect(ui.actionTurnLeftt_L, &QAction::triggered, + ui.gameView, &GameView::turnLeft); + + // 因功能限制,使部分功能不可用 + ui.actionNew_N->setDisabled(true); + ui.actionOpen_O->setDisabled(true); + ui.actionSave_S->setDisabled(true); + ui.actionSaveAs_A->setDisabled(true); + ui.actionViewText_V->setDisabled(true); + ui.actionPrevious_B->setDisabled(true); + ui.actionNext_F->setDisabled(true); + ui.actionEnd_E->setDisabled(true); + ui.actionAutoRun_A->setDisabled(true); + ui.actionEngine_E->setDisabled(true); + ui.actionInternet_I->setDisabled(true); + ui.actionEngine1_T->setDisabled(true); + ui.actionEngine2_R->setDisabled(true); + ui.actionSetting_O->setDisabled(true); + ui.actionAnimation_A->setDisabled(true); + + // 初始化游戏规则菜单 + ui.menu_R->installEventFilter(this); + // 安装一次性定时器,执行初始化 + QTimer::singleShot(0, this, SLOT(initialize())); +} + +NineChessWindow::~NineChessWindow() +{ + if (game != NULL) + delete game; +} + +bool NineChessWindow::eventFilter(QObject *watched, QEvent *event) +{ + // 重载这个函数只是为了让规则菜单显示提示 + if (watched == ui.menu_R) + { + switch (event->type()) + { + case QEvent::ToolTip: + QHelpEvent * he = dynamic_cast (event); + QAction *action = ui.menu_R->actionAt(he->pos()); + if (action) + { + QToolTip::showText(he->globalPos(), action->toolTip(), this); + return true; + } + break; + } + } + QMainWindow::eventFilter(watched, event); +} + +void NineChessWindow::initialize() +{ + // 如果游戏已经初始化,则退出 + if (game != NULL) + return; + + // 开辟一个新的游戏控制器 + game = new GameController(*scene, this); + + // 添加新菜单栏动作 + QMap actions = game->getActions(); + QMap ::const_iterator i; + for (i = actions.constBegin(); i != actions.constEnd(); i++) { + // qDebug() << i.key() << i.value(); + // QMap的key存放int索引值,value存放规则名称和规则提示 + QAction *ruleAction = new QAction(i.value().at(0), this); + ruleAction->setToolTip(i.value().at(1)); + ruleAction->setCheckable(true); + // 索引值放在QAction的Data里 + ruleAction->setData(i.key()); + // 添加到动作列表 + ruleActionList.append(ruleAction); + // 添加到“规则”菜单 + ui.menu_R->addAction(ruleAction); + connect(ruleAction, SIGNAL(triggered()), + this, SLOT(actionRules_triggered())); + } + + // 关联主窗口动作信号和控制器的槽 + connect(ui.actionEngine1_T, SIGNAL(toggled(bool)), + game, SLOT(setEngine1(bool))); + connect(ui.actionEngine2_R, SIGNAL(toggled(bool)), + game, SLOT(setEngine2(bool))); + connect(ui.actionSound_S, SIGNAL(toggled(bool)), + game, SLOT(setSound(bool))); + connect(ui.actionAnimation_A, SIGNAL(toggled(bool)), + game, SLOT(setAnimation(bool))); + + // 关联控制器的信号和主窗口控件的槽 + // 注意,采用信号和槽而非采用在GameController中直接控制NineChessWindow + // 是MVC模型中控制器与视图相分离的方式,有利于程序梳理 + // 更新LCD1,显示玩家1用时 + connect(game, SIGNAL(time1Changed(QString)), + ui.lcdNumber_1, SLOT(display(QString))); + // 更新LCD2,显示玩家2用时 + connect(game, SIGNAL(time2Changed(QString)), + ui.lcdNumber_2, SLOT(display(QString))); + + // 关联场景的信号和控制器的槽 + connect(scene, SIGNAL(mouseReleased(QPointF)), + game, SLOT(actionPiece(QPointF))); + + // 为状态栏添加一个正常显示的标签 + QLabel *statusBarlabel = new QLabel(this); + ui.statusBar->addWidget(statusBarlabel); + // 更新状态栏 + connect(game, SIGNAL(statusBarChanged(QString)), + statusBarlabel, SLOT(setText(QString))); + + // 默认第2号规则 + ruleNo = 2; + + // 设置选中当前规则的菜单项 + ruleActionList.at(ruleNo)->setChecked(true); + + // 重置游戏规则 + game->setRule(ruleNo); +} + +void NineChessWindow::actionRules_triggered() +{ + // setChecked函数会发出toggled信号,在这里响应toggled信号会陷入死循环 + // 取消其它规则的选择 + foreach(QAction *action, ruleActionList) + action->setChecked(false); + // 选择当前规则 + QAction *action = dynamic_cast(sender()); + action->setChecked(true); + ruleNo = action->data().toInt(); + // 重置游戏规则 + game->setRule(ruleNo); +} + + +void NineChessWindow::on_actionNew_N_triggered() +{ + +} + +void NineChessWindow::on_actionOpen_O_triggered() +{ + +} + +void NineChessWindow::on_actionSave_S_triggered() +{ + +} + +void NineChessWindow::on_actionSaveAs_A_triggered() +{ + +} + +void NineChessWindow::on_actionViewText_V_triggered() +{ + +} + +void NineChessWindow::on_actionEdit_E_toggled(bool arg1) +{ + +} + +void NineChessWindow::on_actionInvert_I_toggled(bool arg1) +{ + // 如果黑白反转 + if (arg1) + { + // 设置玩家1和玩家2的标识图 + ui.actionEngine1_T->setIcon(QIcon(":/icon/Resources/icon/White.png")); + ui.actionEngine2_R->setIcon(QIcon(":/icon/Resources/icon/Black.png")); + ui.picLabel1->setPixmap(QPixmap(":/icon/Resources/icon/White.png")); + ui.picLabel2->setPixmap(QPixmap(":/icon/Resources/icon/Black.png")); + } + else + { + // 设置玩家1和玩家2的标识图 + ui.actionEngine1_T->setIcon(QIcon(":/icon/Resources/icon/Black.png")); + ui.actionEngine2_R->setIcon(QIcon(":/icon/Resources/icon/White.png")); + ui.picLabel1->setPixmap(QPixmap(":/icon/Resources/icon/Black.png")); + ui.picLabel2->setPixmap(QPixmap(":/icon/Resources/icon/White.png")); + } + // 让控制器改变棋子颜色 + game->setInvert(arg1); +} + +void NineChessWindow::on_actionBegin_S_triggered() +{ + if (game == NULL) + return; + game->gameReset(); +} + +void NineChessWindow::on_actionPrevious_B_triggered() +{ + +} + +void NineChessWindow::on_actionNext_F_triggered() +{ + +} + +void NineChessWindow::on_actionEnd_E_triggered() +{ + +} + +void NineChessWindow::on_actionAutoRun_A_toggled(bool arg1) +{ + +} + +void NineChessWindow::on_actionResign_R_triggered() +{ + +} + +void NineChessWindow::on_actionLimited_T_triggered() +{ + /* 其实本来可以用设计器做个ui,然后从QDialog派生个自己的对话框 + * 但我不想再派生新类了,又要多出一个类和两个文件 + * 还要写与主窗口的接口,费劲 + * 于是手写QDialog界面 + */ + int stepLimited = game->getStepsLimit(); + int timeLimited = game->getTimeLimit(); + + // 定义新对话框 + QDialog *dialog = new QDialog(this); + dialog->setWindowFlags(Qt::Dialog | Qt::WindowCloseButtonHint); + dialog->setObjectName(QStringLiteral("Dialog")); + dialog->setWindowTitle(tr("步数和时间限制")); + dialog->resize(256, 108); + dialog->setModal(true); + // 生成各个控件 + QFormLayout *formLayout = new QFormLayout(dialog); + QLabel *label_step = new QLabel(dialog); + QLabel *label_time = new QLabel(dialog); + QComboBox *comboBox_step = new QComboBox(dialog); + QComboBox *comboBox_time = new QComboBox(dialog); + QDialogButtonBox *buttonBox = new QDialogButtonBox(dialog); + // 设置各个控件ObjectName,不设也没关系 + /*formLayout->setObjectName(QStringLiteral("formLayout")); + label_step->setObjectName(QStringLiteral("label_step")); + label_time->setObjectName(QStringLiteral("label_time")); + comboBox_step->setObjectName(QStringLiteral("comboBox_step")); + comboBox_time->setObjectName(QStringLiteral("comboBox_time")); + buttonBox->setObjectName(QStringLiteral("buttonBox"));*/ + // 设置各个控件数据 + label_step->setText(tr("超出限制步数判和:")); + label_time->setText(tr("任意一方超时判负:")); + comboBox_step->addItem(tr("无限制"), 0); + comboBox_step->addItem(tr("50步"), 50); + comboBox_step->addItem(tr("100步"), 100); + comboBox_step->addItem(tr("200步"), 200); + comboBox_time->addItem(tr("无限制"), 0); + comboBox_time->addItem(tr("5分钟"), 5); + comboBox_time->addItem(tr("10分钟"), 10); + comboBox_time->addItem(tr("20分钟"), 20); + comboBox_step->setCurrentIndex(comboBox_step->findData(stepLimited)); + comboBox_time->setCurrentIndex(comboBox_time->findData(timeLimited)); + buttonBox->setStandardButtons(QDialogButtonBox::Cancel | QDialogButtonBox::Ok); + buttonBox->setCenterButtons(true); + buttonBox->button(QDialogButtonBox::Ok)->setText(tr("确定")); + buttonBox->button(QDialogButtonBox::Cancel)->setText(tr("取消")); + // 布局 + formLayout->setSpacing(6); + formLayout->setContentsMargins(11, 11, 11, 11); + formLayout->setWidget(0, QFormLayout::LabelRole, label_step); + formLayout->setWidget(0, QFormLayout::FieldRole, comboBox_step); + formLayout->setWidget(1, QFormLayout::LabelRole, label_time); + formLayout->setWidget(1, QFormLayout::FieldRole, comboBox_time); + formLayout->setWidget(2, QFormLayout::SpanningRole, buttonBox); + // 关联信号和槽函数 + connect(buttonBox, SIGNAL(accepted()), dialog, SLOT(accept())); + connect(buttonBox, SIGNAL(rejected()), dialog, SLOT(reject())); + // 收集数据 + if (dialog->exec() == QDialog::Accepted) { + stepLimited = comboBox_step->currentData().toInt(); + timeLimited = comboBox_time->currentData().toInt(); + // 重置游戏规则 + game->setRule(ruleNo, stepLimited, timeLimited); + } + // 删除对话框,子控件会一并删除 + delete dialog; +} + +void NineChessWindow::on_actionLocal_L_triggered() +{ + ui.actionLocal_L->setChecked(true); + ui.actionInternet_I->setChecked(false); +} + +void NineChessWindow::on_actionInternet_I_triggered() +{ + ui.actionLocal_L->setChecked(false); + ui.actionInternet_I->setChecked(true); +} + +void NineChessWindow::on_actionEngine_E_triggered() +{ + +} + +void NineChessWindow::on_actionViewHelp_V_triggered() +{ + +} + +void NineChessWindow::on_actionWeb_W_triggered() +{ + +} + +void NineChessWindow::on_actionAbout_A_triggered() +{ + QMessageBox aboutBox; + aboutBox.setText(tr("九连棋")); + aboutBox.setInformativeText(tr("by liuweilhy")); + aboutBox.setIcon(QMessageBox::Information); + aboutBox.exec(); +} + diff --git a/NineChess/src/ninechesswindow.h b/NineChess/src/ninechesswindow.h new file mode 100644 index 00000000..c5fd15c4 --- /dev/null +++ b/NineChess/src/ninechesswindow.h @@ -0,0 +1,74 @@ +#ifndef NINECHESSWINDOW_H +#define NINECHESSWINDOW_H + +#include +#include "ui_ninechesswindow.h" + +class GameScene; +class GameController; + +class NineChessWindow : public QMainWindow +{ + Q_OBJECT + +public: + NineChessWindow(QWidget *parent = 0); + ~NineChessWindow(); + +protected: + bool eventFilter(QObject * watched, QEvent * event); + +private: + // 界面文件 + Ui::NineChessWindowClass ui; + // 视图场景 + GameScene *scene; + // 控制器 + GameController *game; + // 动态增加的菜单栏动作列表 + QList ruleActionList; + // 游戏的规则号,涉及菜单项和对话框,所以要有 + int ruleNo; + +private slots: + // 初始化 + void initialize(); + // 动态增加的菜单栏动作的槽函数 + void actionRules_triggered(); + // 下面是各动作的槽函数 + // 注释掉的是已在UI管理器或主窗口初始化函数中连接好的 + void on_actionNew_N_triggered(); + void on_actionOpen_O_triggered(); + void on_actionSave_S_triggered(); + void on_actionSaveAs_A_triggered(); + void on_actionViewText_V_triggered(); + //void on_actionExit_X_triggered(); + void on_actionEdit_E_toggled(bool arg1); + //void on_actionFlip_F_triggered(); + //void on_actionMirror_M_triggered(); + //void on_actionTurnRight_R_triggered(); + //void on_actionTurnLeftt_L_triggered(); + void on_actionInvert_I_toggled(bool arg1); + void on_actionBegin_S_triggered(); + void on_actionPrevious_B_triggered(); + void on_actionNext_F_triggered(); + void on_actionEnd_E_triggered(); + void on_actionAutoRun_A_toggled(bool arg1); + void on_actionResign_R_triggered(); + void on_actionLimited_T_triggered(); + void on_actionLocal_L_triggered(); + void on_actionInternet_I_triggered(); + void on_actionEngine_E_triggered(); + //void on_actionEngine1_R_toggled(bool arg1); + //void on_actionEngine2_T_toggled(bool arg1); + //void on_actionSetting_O_triggered(); + //void on_actionToolBar_T_toggled(bool arg1); + //void on_actionDockBar_D_toggled(bool arg1); + //void on_actionSound_S_toggled(bool arg1); + //void on_actionAnimation_A_toggled(bool arg1); + void on_actionViewHelp_V_triggered(); + void on_actionWeb_W_triggered(); + void on_actionAbout_A_triggered(); +}; + +#endif // NINECHESSWINDOW_H diff --git a/NineChess/src/pieceitem.cpp b/NineChess/src/pieceitem.cpp new file mode 100644 index 00000000..18677b0e --- /dev/null +++ b/NineChess/src/pieceitem.cpp @@ -0,0 +1,131 @@ +#include "pieceitem.h" +#include "graphicsconst.h" +#include +#include + +PieceItem::PieceItem(QGraphicsItem *parent) : QGraphicsItem(parent), +num(0), +deleted(false), +showNum(false) +{ + Q_UNUSED(parent) + // 允许选择和移动 + setFlags(ItemIsSelectable + // | ItemIsMovable + ); + // 设置缓存模式 + setCacheMode(DeviceCoordinateCache); + //setAcceptHoverEvents(true); + // 鼠标放在棋子上时显示为伸开的手形 + setCursor(Qt::OpenHandCursor); + // 只接受左键事件 + setAcceptedMouseButtons(Qt::LeftButton); + // 默认模型为没有棋子 + model = noPiece; + // 棋子尺寸 + size = PIECE_SIZE; + // 选中子标识线宽度 + chooseLineWeight = LINE_WEIGHT; + // 删除线宽度 + removeLineWeight = LINE_WEIGHT * 5; + // 选中线为黄色 + chooseLineColor = Qt::darkYellow; + // 删除线为橘红色 + removeLineColor = QColor(0xff, 0x75, 0); +} + +PieceItem::~PieceItem() +{ +} + +QRectF PieceItem::boundingRect() const +{ + return QRectF(-size/2, -size/2, size, size); +} + +QPainterPath PieceItem::shape() const +{ + QPainterPath path; + path.addEllipse(boundingRect()); + return path; +} + +void PieceItem::paint(QPainter *painter, + const QStyleOptionGraphicsItem *option, + QWidget *widget) +{ + Q_UNUSED(option) + Q_UNUSED(widget) + + // 空模型不画棋子 + // 如果模型为黑色,则画黑色棋子 + if (model == blackPiece) + painter->drawPixmap(-size/2, -size/2, size, size, + QPixmap(":/image/Resources/image/black_piece.png")); + // 如果模型为白色,则画白色棋子 + else if (model == whitePiece) + painter->drawPixmap(-size/2, -size/2, size, size, + QPixmap(":/image/Resources/image/white_piece.png")); + + // 如果模型要求显示序号 + if (showNum) + { + // 如果模型为黑色,用白色笔画序号 + painter->setPen(QColor(255, 255, 255)); + // 如果模型为白色,用白色笔画序号 + if (model == whitePiece) + painter->setPen(QColor(0, 0, 0)); + // 字体 + QFont font; + font.setFamily("Arial"); + font.setPointSize(size/3); + painter->setFont(font); + // 画序号,默认中间位置偏下,需微调 + painter->drawText(boundingRect().adjusted(0, 0, 0, -size/12), Qt::AlignCenter, QString::number(num)); + + } + + // 如果模型为选中状态,则画上四个小直角 + if (isSelected()) + { + QPen pen(chooseLineColor, chooseLineWeight, Qt::SolidLine, Qt::SquareCap, Qt::BevelJoin); + painter->setPen(pen); + int xy = (size-chooseLineWeight)/2; + painter->drawLine(-xy, -xy, -xy, -xy/2); + painter->drawLine(-xy, -xy, -xy/2, -xy); + painter->drawLine(xy, -xy, xy, -xy/2); + painter->drawLine(xy, -xy, xy/2, -xy); + painter->drawLine(xy, xy, xy, xy/2); + painter->drawLine(xy, xy, xy/2, xy); + painter->drawLine(-xy, xy, -xy, xy/2); + painter->drawLine(-xy, xy, -xy/2, xy); + } + + // 如果模型为删除状态,则画上叉号 + if (deleted) + { + QPen pen(removeLineColor, removeLineWeight, Qt::SolidLine, Qt::SquareCap, Qt::BevelJoin); + painter->setPen(pen); + painter->drawLine(-size / 3, -size / 3, size / 3, size / 3); + painter->drawLine(size / 3, -size / 3, -size / 3, size / 3); + } +} + +void PieceItem::mousePressEvent(QGraphicsSceneMouseEvent *event) +{ + // 鼠标按下时变为握住的手形 + setCursor(Qt::ClosedHandCursor); + QGraphicsItem::mousePressEvent(event); +} + +void PieceItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event) +{ + QGraphicsItem::mouseMoveEvent(event); +} + +void PieceItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) +{ + // 鼠标松开时变为伸开的手形 + setCursor(Qt::OpenHandCursor); + QGraphicsItem::mouseReleaseEvent(event); +} diff --git a/NineChess/src/pieceitem.h b/NineChess/src/pieceitem.h new file mode 100644 index 00000000..1593eb5b --- /dev/null +++ b/NineChess/src/pieceitem.h @@ -0,0 +1,67 @@ +#ifndef CHESSITEM_H +#define CHESSITEM_H + +#include + +class PieceItem : public QGraphicsItem +{ +public: + explicit PieceItem(QGraphicsItem *parent = 0); + ~PieceItem(); + QRectF boundingRect() const; + QPainterPath shape() const; + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, + QWidget * widget = 0); + + // 用UserType+2表示棋子,用qgraphicsitem_cast()判断是否为PieceItem类的对象 + // 还有一个方式是把类名放在Data的0key位置setData(0, "PieceItem"),然后用data(0)来判断 + enum { Type = UserType + 2 }; + int type() const { return Type; } + + // 模型状态枚举,用位运算标明 + enum Models { + noPiece = 0x1, // 空棋子 + blackPiece = 0x2, // 黑色棋子 + whitePiece = 0x4, // 白色棋子 + }; + + enum Models getModel() { return model; } + void setModel(enum Models model) { this->model = model; } + void setNum(int n) { num = n; } + bool isDeleted() { return deleted; } + void setDeleted(bool deleted = true) + { + this->deleted = deleted; + if (deleted) + this->model = noPiece; + update(boundingRect()); + } + void setShowNum(bool show = true) { this->showNum = show; } + +protected: + void mousePressEvent(QGraphicsSceneMouseEvent *event); + void mouseMoveEvent(QGraphicsSceneMouseEvent *event); + void mouseReleaseEvent(QGraphicsSceneMouseEvent *event); + +private: + // 棋子本质 + enum Models model; + // 棋子序号,黑白都从1开始 + int num; + // 棋子尺寸 + qreal size; + // 有无删除线 + bool deleted; + // 显示序号 + bool showNum; + // 选中子标识线宽度 + qreal chooseLineWeight; + // 删除线宽度 + qreal removeLineWeight; + // 选中线颜色 + QColor chooseLineColor; + // 删除线颜色 + QColor removeLineColor; +}; + +#endif // CHESSITEM_H diff --git a/NineChess/src/sizehintlistview.h b/NineChess/src/sizehintlistview.h new file mode 100644 index 00000000..dc2b3fb2 --- /dev/null +++ b/NineChess/src/sizehintlistview.h @@ -0,0 +1,28 @@ +/* QListView派生类 + * 之所以要派生这个类,重载sizeHint函数 + * 只是为了让停靠栏(父窗口)在初始时不至于过宽难看 + * QDockWidget没有很好的控制初始大小的方法,resize函数没效果 + * 如果不用派生类,使用固定宽度也可以,如下所示 + * ui.listView->setFixedWidth(108); + * 但调节停靠栏宽度后就不好看了 + */ + +#ifndef SIZEHINTLISTVIEW +#define SIZEHINTLISTVIEW + +#include + +class SizeHintListView : public QListView +{ +public: + SizeHintListView(QWidget * parent = 0) {} + QSize sizeHint() const{ + QSize size = QListView::sizeHint(); + // 缺省宽度设为128,这样就不太宽了 + size.setWidth(128); + return size; + } +}; + +#endif // SIZEHINTLISTVIEW + diff --git a/Readme.md b/Readme.md new file mode 100644 index 00000000..9ee5edb2 --- /dev/null +++ b/Readme.md @@ -0,0 +1,43 @@ +# 九联棋 NineChess + +#### 项目介绍: +本游戏根据作者儿时游戏——“九联棋”编制,加上“成三棋”、“打三棋”和“莫里斯九子棋”,共三种规则玩法。 +三种规则略有差异,鼠标放在相应菜单项有会有详细的规则提示。 + +#### 软件构架: ++ GUI框架:Qt5.10,Qt5大版本下均可通用。 ++ 编译器:MSVC2017,MSVC2013及以上版本可用。 ++ 源文件编码:所有头文件(*.h)和源文件(*.cpp)采用UTF-8+BOM编码格式。pro文件等采用UTF-8无BOM编码。 ++ 本程序采用MVC(模型——视图——控制器)设计规范,对应类如下: +``` + MVC + ├─Model + │ └─NineChess:用标准C++写的棋类模型,处理下棋过程 + ├─View + │ ├─NineChessWindow:从QMainWindow派生的主窗口类,由Qt自动生成 + │ ├─SizeHintListView:从QListView派生的列表框 + │ ├─GameScene:从QGraphicsScene派生的场景类 + │ ├─GameView:从QGraphicsView派生的视图框 + │ ├─BoardItem:从QGraphicsItem派生的棋盘图形类 + │ └─PiecedItem:从QGraphicsItem派生的棋子图形类 + └─Controller + └─GameController:从QObject派生的控制类 +``` ++ 这个程序用到了很多Qt特性,其模式后期可以扩展到各种棋类游戏,适合初学者一看。 + +#### 许可协议 +参见Licence.txt + +#### 更新历史 +参见History.txt + +#### 作者声明: +多年前上大学那会儿,笔者就打算做这么个程序出来。然而,条件比较艰苦: +一来没有老师教,课上只学了C语言和VB,C++是笔者自学的,一个人啃晦涩过时的MFC; +二来我穷到连个电脑都没有…… +三嘛,就是贪玩…… +工作之后有条件了,我又自学了C#和Qt,但都很肤浅,没深入学,只用来做几个小工具而已。 +从5月末开始消沉到后来步入人生低谷,我终于有大把的时间来思考人生。 +风雨过后,我想起把时间用在兴趣爱好上。于是,这个小游戏诞生了! +如果你发现本程序有什么问题或好的建议,请与本人联系。我的邮箱是:liuweilhy@163.com +>          ——by liuweilhy 2015年11月6日 \ No newline at end of file