GamePage.dart 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279
  1. import 'dart:math';
  2. import 'package:flutter/cupertino.dart';
  3. import 'package:flutter/material.dart';
  4. import 'package:gobang/ai/Ai.dart';
  5. import 'package:gobang/bridge/ChessShape.dart';
  6. import 'package:gobang/factory/ThemeFactory.dart';
  7. import 'package:gobang/flyweight/Chess.dart';
  8. import 'package:gobang/flyweight/ChessFlyweightFactory.dart';
  9. import 'package:gobang/memorandum/Originator.dart';
  10. import 'package:gobang/state/UserContext.dart';
  11. import 'package:gobang/utils/TipsDialog.dart';
  12. import 'package:gobang/viewModel/GameViewModel.dart';
  13. import 'flyweight/Position.dart';
  14. var width = 0.0;
  15. ///简单的实现五子棋效果
  16. class GamePage extends StatefulWidget {
  17. @override
  18. State<StatefulWidget> createState() => GamePageState();
  19. }
  20. class GamePageState extends State<GamePage> {
  21. ThemeFactory? _themeFactory;
  22. GameViewModel _viewModel = GameViewModel.getInstance();
  23. Originator _originator = Originator.getInstance();
  24. @override
  25. void initState() {
  26. _themeFactory = AiThemeFactory();
  27. super.initState();
  28. }
  29. @override
  30. Widget build(BuildContext context) {
  31. width = MediaQuery.of(context).size.width * 0.8;
  32. return Scaffold(
  33. appBar: AppBar(
  34. backgroundColor: _themeFactory!.getTheme().getThemeColor(),
  35. title: Text("南瓜五子棋"),
  36. actions: [
  37. IconButton(
  38. onPressed: () {
  39. if(_viewModel.undo()){
  40. _originator.undo();
  41. setState(() {});
  42. }else{
  43. TipsDialog.show(context, "提示", "现阶段不能悔棋");
  44. }
  45. },
  46. icon: Icon(Icons.undo))
  47. ],
  48. ),
  49. body: ListView(
  50. children: [
  51. Center(
  52. child: Column(
  53. mainAxisAlignment: MainAxisAlignment.center,
  54. crossAxisAlignment: CrossAxisAlignment.center,
  55. mainAxisSize: MainAxisSize.max,
  56. children: <Widget>[
  57. Padding(
  58. padding: EdgeInsets.only(bottom: 15.0),
  59. child: CupertinoButton.filled(
  60. padding: EdgeInsets.all(0.0),
  61. child: Text("重置棋盘"),
  62. onPressed: () {
  63. setState(() {
  64. ChessPainter._position = null;
  65. _originator.clean();
  66. Ai.getInstance().init();
  67. // blackChess = null;
  68. });
  69. }),
  70. ),
  71. Padding(
  72. padding: EdgeInsets.only(bottom: 15.0),
  73. child: CupertinoButton.filled(
  74. padding: EdgeInsets.all(0.0),
  75. child: Text("Ai下棋"),
  76. onPressed: () {
  77. turnAi();
  78. // blackChess = null;
  79. }),
  80. ),
  81. GestureDetector(
  82. onTapDown: (topDownDetails) {
  83. var position = topDownDetails.localPosition;
  84. Chess chess = _viewModel.play();
  85. setState(() {
  86. ChessPainter._position =
  87. Position(position.dx, position.dy, chess);
  88. });
  89. },
  90. child: Stack(
  91. children: [
  92. CustomPaint(
  93. size: Size(width, width),
  94. painter: CheckerBoardPainter(),
  95. ),
  96. CustomPaint(
  97. size: Size(width, width),
  98. painter: ChessPainter(turnAi),
  99. )
  100. ],
  101. ))
  102. ]),
  103. ),
  104. ],
  105. ),
  106. );
  107. }
  108. /// Ai 下棋
  109. void turnAi() {
  110. if (ChessPainter._position!.chess is WhiteChess &&
  111. Ai.getInstance().isWin(ChessPainter._position!.dx ~/ (width / 15),
  112. ChessPainter._position!.dy ~/ (width / 15), 1)) {
  113. TipsDialog.show(context, "恭喜", "您打败了决策树算法");
  114. }
  115. // 获取Ai下棋地址
  116. Ai ai = Ai.getInstance();
  117. ChessPainter._position = ai.searchPosition();
  118. // 设置棋子外观
  119. ChessPainter._position!.chess.chessShape = CircleShape();
  120. // 加入决策中
  121. Ai.getInstance().addChessman(ChessPainter._position!.dx.toInt(),
  122. ChessPainter._position!.dy.toInt(), -1);
  123. if (ChessPainter._position!.chess is BlackChess &&
  124. Ai.getInstance().isWin(ChessPainter._position!.dx.toInt(),
  125. ChessPainter._position!.dy.toInt(), -1)) {
  126. TipsDialog.show(context, "很遗憾", "决策树算法打败了您");
  127. }
  128. setState(() {
  129. ChessPainter._position!.dx = ChessPainter._position!.dx * (width / 15);
  130. ChessPainter._position!.dy = ChessPainter._position!.dy * (width / 15);
  131. });
  132. }
  133. }
  134. class ChessPainter extends CustomPainter {
  135. static int _state = 0;
  136. static Position? _position;
  137. final Function _function;
  138. Originator _originator = Originator.getInstance();
  139. ChessPainter(Function f) : _function = f;
  140. @override
  141. void paint(Canvas canvas, Size size) {
  142. if (_position == null) {
  143. return;
  144. }
  145. bool add = false;
  146. double mWidth = size.width / 15;
  147. double mHeight = size.height / 15;
  148. var mPaint = Paint();
  149. //求两个点之间的距离,让棋子正确的显示在坐标轴上面
  150. var dx = _position!.dx;
  151. var dy = _position!.dy;
  152. for (int i = 0; i < CheckerBoardPainter._crossOverBeanList.length; i++) {
  153. var absX =
  154. (dx - CheckerBoardPainter._crossOverBeanList[i]._dx).abs(); //两个点的x轴距离
  155. var absY =
  156. (dy - CheckerBoardPainter._crossOverBeanList[i]._dy).abs(); //两个点的y轴距离
  157. var s = sqrt(absX * absX +
  158. absY * absY); //利用直角三角形求斜边公式(a的平方 + b的平方 = c的平方)来计算出两点间的距离
  159. if (s <= mWidth / 2 - 2) {
  160. // 触摸点到棋盘坐标坐标点距离小于等于棋子半径,那么
  161. //找到离触摸点最近的棋盘坐标点并记录保存下来
  162. _position!.dx = CheckerBoardPainter._crossOverBeanList[i]._dx;
  163. _position!.dy = CheckerBoardPainter._crossOverBeanList[i]._dy;
  164. _originator.add(_position!);
  165. add = true;
  166. if (_position!.chess is WhiteChess) {
  167. Ai.getInstance().addChessman(
  168. _position!.dx ~/ (width / 15), _position!.dy ~/ (width / 15), 1);
  169. }
  170. // flag = false; //白子下完了,该黑子下了
  171. break;
  172. }
  173. }
  174. //画子
  175. mPaint..style = PaintingStyle.fill;
  176. if (_originator.state.isNotEmpty) {
  177. for (int i = 0; i < _originator.state.length; i++) {
  178. mPaint..color = _originator.state[i].chess.color;
  179. if (_originator.state[i].chess.chessShape.shape == 1) {
  180. canvas.drawCircle(
  181. Offset(_originator.state[i].dx, _originator.state[i].dy),
  182. min(mWidth / 2, mHeight / 2) - 2,
  183. mPaint);
  184. }
  185. if (_originator.state[i].chess.chessShape.shape == 2) {
  186. Rect rect = Rect.fromCircle(
  187. center: Offset(_originator.state[i].dx, _originator.state[i].dy),
  188. radius: min(mWidth / 2, mHeight / 2) - 2);
  189. canvas.drawRect(rect, mPaint);
  190. }
  191. }
  192. }
  193. WidgetsBinding.instance!.addPostFrameCallback((_) {
  194. if (add && _position!.chess is WhiteChess) {
  195. _function();
  196. }
  197. });
  198. }
  199. //在实际场景中正确利用此回调可以避免重绘开销,本示例我们简单的返回true
  200. @override
  201. bool shouldRepaint(CustomPainter oldDelegate) {
  202. return true;
  203. }
  204. }
  205. class CheckerBoardPainter extends CustomPainter {
  206. static List<CrossOverBean> _crossOverBeanList = [];
  207. static int _state = 0;
  208. @override
  209. void paint(Canvas canvas, Size size) {
  210. double mWidth = size.width / 15;
  211. double mHeight = size.height / 15;
  212. var mPaint = Paint();
  213. _crossOverBeanList.clear();
  214. canvas.drawColor(
  215. CupertinoColors.systemGrey, BlendMode.colorBurn); //重绘下整个界面的画布北京颜色
  216. //设置画笔,画棋盘背景
  217. mPaint
  218. ..isAntiAlias = true //抗锯齿
  219. ..style = PaintingStyle.fill //填充
  220. ..color = Color(0x77cdb175); //背景为纸黄色
  221. canvas.drawRect(
  222. Rect.fromCenter(
  223. center: Offset(size.width / 2, size.height / 2),
  224. width: size.width,
  225. height: size.height),
  226. mPaint);
  227. //画棋盘网格
  228. mPaint
  229. ..style = PaintingStyle.stroke
  230. ..color = CupertinoColors.systemGrey6
  231. ..strokeWidth = 1.0;
  232. for (var i = 0; i <= 15; i++) {
  233. //画横线
  234. canvas.drawLine(
  235. Offset(0, mHeight * i), Offset(size.width, mHeight * i), mPaint);
  236. }
  237. for (var i = 0; i <= 15; i++) {
  238. //画竖线
  239. canvas.drawLine(
  240. Offset(mWidth * i, 0), Offset(mWidth * i, size.height), mPaint);
  241. }
  242. //记录横竖线所有的交叉点
  243. for (int i = 0; i <= 15; i++) {
  244. for (int j = 0; j <= 15; j++) {
  245. _crossOverBeanList.add(CrossOverBean(mWidth * j, mHeight * i));
  246. }
  247. }
  248. }
  249. //在实际场景中正确利用此回调可以避免重绘开销,本示例我们简单的返回true
  250. @override
  251. bool shouldRepaint(CustomPainter oldDelegate) {
  252. return false;
  253. }
  254. }
  255. ///记录棋盘上横竖线的交叉点
  256. class CrossOverBean {
  257. double _dx;
  258. double _dy;
  259. CrossOverBean(this._dx, this._dy);
  260. }