123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362 |
- import 'dart:math';
- import 'package:flutter/cupertino.dart';
- import 'package:flutter/material.dart';
- import 'package:gobang/ai/Ai.dart';
- import 'package:gobang/factory/ThemeFactory.dart';
- import 'package:gobang/flyweight/Chess.dart';
- import 'package:gobang/memorandum/Checkerboard.dart';
- import 'package:gobang/utils/TipsDialog.dart';
- import 'package:gobang/viewModel/GameViewModel.dart';
- import 'bridge/CircleShape.dart';
- import 'factory/BlackThemeFactory.dart';
- import 'factory/BlueThemeFactory.dart';
- import 'flyweight/Position.dart';
- var width = 0.0;
- ///简单的实现五子棋效果
- class HomePage extends StatefulWidget {
- @override
- State<StatefulWidget> createState() => HomePageState();
- }
- class HomePageState extends State<HomePage> {
- ThemeFactory? _themeFactory;
- GameViewModel _viewModel = GameViewModel.getInstance();
- Checkerboard _originator = Checkerboard.getInstance();
- Icon lightOn = Icon(Icons.lightbulb, color: Colors.amberAccent);
- Icon lightOff = Icon(Icons.lightbulb_outline_rounded);
- Icon circle = Icon(Icons.circle_outlined);
- Icon rect = Icon(Icons.crop_square);
- Icon? currentLight, currentShape;
- @override
- void initState() {
- currentLight = lightOn;
- _themeFactory = BlueThemeFactory();
- currentShape = circle;
- super.initState();
- }
- @override
- Widget build(BuildContext context) {
- width = MediaQuery.of(context).size.width * 0.8;
- return Scaffold(
- appBar: AppBar(
- elevation: 0,
- backgroundColor: _themeFactory!.getTheme().getThemeColor(),
- title: Text("南瓜五子棋"),
- actions: [
- IconButton(
- onPressed: () {
- setState(() {
- if (_themeFactory is BlackThemeFactory) {
- currentLight = lightOn;
- _themeFactory = BlueThemeFactory();
- } else {
- currentLight = lightOff;
- _themeFactory = BlackThemeFactory();
- }
- });
- },
- icon: currentLight!),
- IconButton(
- onPressed: () {
- setState(() {
- if (currentShape == circle) {
- currentShape = rect;
- } else {
- currentShape = circle;
- }
- });
- },
- icon: currentShape!),
- ],
- ),
- body: Container(
- decoration: BoxDecoration(
- gradient: LinearGradient(
- colors: [
- _themeFactory!.getTheme().getThemeColor(),
- Colors.white,
- ],
- stops: [
- 0.0,
- 1
- ],
- begin: FractionalOffset.topCenter,
- end: FractionalOffset.bottomCenter,
- tileMode: TileMode.repeated)),
- child: Center(
- child: Column(
- mainAxisAlignment: MainAxisAlignment.center,
- crossAxisAlignment: CrossAxisAlignment.center,
- mainAxisSize: MainAxisSize.max,
- children: <Widget>[
- Padding(
- padding: EdgeInsets.only(top: 14, bottom: 30),
- child: Text(
- _viewModel.state,
- style: TextStyle(color: Colors.white),
- ),
- ),
- GestureDetector(
- onTapDown: (topDownDetails) {
- var position = topDownDetails.localPosition;
- Chess chess = _viewModel.play(currentShape == circle);
- setState(() {
- ChessPainter._position =
- Position(position.dx, position.dy, chess);
- });
- },
- child: Stack(
- children: [
- CustomPaint(
- size: Size(width, width),
- painter: CheckerBoardPainter(),
- ),
- CustomPaint(
- size: Size(width, width),
- painter: ChessPainter(turnAi),
- )
- ],
- )),
- Padding(
- padding: const EdgeInsets.only(top: 16.0),
- child: Row(
- mainAxisAlignment: MainAxisAlignment.center,
- children: [
- IconButton(
- onPressed: () {
- if (_viewModel.undo()) {
- _originator.undo();
- Ai.getInstance().init();
- for (Position po in _originator.state) {
- Ai.getInstance().addChessman(
- po.dx ~/ (width / 15),
- po.dy ~/ (width / 15),
- po.chess is WhiteChess ? 1 : -1);
- }
- setState(() {});
- } else {
- TipsDialog.show(context, "提示", "现阶段不能悔棋");
- }
- },
- icon: Icon(Icons.undo)),
- IconButton(
- onPressed: () {
- if (_viewModel.surrender()) {
- TipsDialog.showByChoose(
- context, "提示", "是否要投降并重新开局?", "是", "否",
- (value) {
- if (value) {
- setState(() {
- ChessPainter._position = null;
- _originator.clean();
- _viewModel.reset();
- Ai.getInstance().init();
- });
- }
- Navigator.pop(context);
- });
- } else {
- TipsDialog.show(context, "提示", "现阶段不能投降");
- }
- },
- icon: Icon(
- Icons.sports_handball,
- color: Colors.deepPurple,
- )),
- IconButton(
- onPressed: () {
- TipsDialog.showByChoose(
- context, "提示", "是否重新开局?", "是", "否", (value) {
- if (value) {
- setState(() {
- ChessPainter._position = null;
- _originator.clean();
- _viewModel.reset();
- Ai.getInstance().init();
- });
- }
- Navigator.pop(context);
- });
- },
- icon: Icon(
- Icons.restart_alt,
- color: Colors.indigo,
- )),
- ],
- ),
- ),
- ]),
- ),
- ),
- );
- }
- /// Ai 下棋
- void turnAi() {
- if (ChessPainter._position!.chess is WhiteChess &&
- Ai.getInstance().isWin(ChessPainter._position!.dx ~/ (width / 15),
- ChessPainter._position!.dy ~/ (width / 15), 1)) {
- TipsDialog.show(context, "恭喜", "您打败了决策树算法");
- }
- // 获取Ai下棋地址
- Ai ai = Ai.getInstance();
- ChessPainter._position = ai.searchPosition();
- // 设置棋子外观
- ChessPainter._position!.chess.chessShape = CircleShape();
- // 加入决策中
- Ai.getInstance().addChessman(ChessPainter._position!.dx.toInt(),
- ChessPainter._position!.dy.toInt(), -1);
- if (ChessPainter._position!.chess is BlackChess &&
- Ai.getInstance().isWin(ChessPainter._position!.dx.toInt(),
- ChessPainter._position!.dy.toInt(), -1)) {
- TipsDialog.show(context, "很遗憾", "决策树算法打败了您");
- }
- setState(() {
- ChessPainter._position!.dx = ChessPainter._position!.dx * (width / 15);
- ChessPainter._position!.dy = ChessPainter._position!.dy * (width / 15);
- });
- }
- }
- class ChessPainter extends CustomPainter {
- static Position? _position;
- final Function _function;
- Checkerboard _originator = Checkerboard.getInstance();
- ChessPainter(Function f) : _function = f;
- @override
- void paint(Canvas canvas, Size size) {
- if (_position == null) {
- return;
- }
- bool add = false;
- double mWidth = size.width / 15; // 每行/列 15 个
- double mHeight = size.height / 15;
- var mPaint = Paint();
- //求两个点之间的距离,让棋子正确的显示在坐标轴上面
- var dx = _position!.dx;
- var dy = _position!.dy;
- for (int i = 0; i < CheckerBoardPainter._crossOverBeanList.length; i++) {
- var absX =
- (dx - CheckerBoardPainter._crossOverBeanList[i]._dx).abs(); //两个点的x轴距离
- var absY =
- (dy - CheckerBoardPainter._crossOverBeanList[i]._dy).abs(); //两个点的y轴距离
- var s = sqrt(absX * absX +
- absY * absY); //利用直角三角形求斜边公式(a的平方 + b的平方 = c的平方)来计算出两点间的距离
- if (s <= mWidth / 2 - 2) {
- // 触摸点到棋盘坐标坐标点距离小于等于棋子半径,那么
- //找到离触摸点最近的棋盘坐标点并记录保存下来
- _position!.dx = CheckerBoardPainter._crossOverBeanList[i]._dx;
- _position!.dy = CheckerBoardPainter._crossOverBeanList[i]._dy;
- _originator.add(_position!);
- add = true;
- if (_position!.chess is WhiteChess) {
- Ai.getInstance().addChessman(
- _position!.dx ~/ (width / 15), _position!.dy ~/ (width / 15), 1);
- }
- // flag = false; //白子下完了,该黑子下了
- break;
- }
- }
- //画子
- mPaint..style = PaintingStyle.fill;
- if (_originator.state.isNotEmpty) {
- for (int i = 0; i < _originator.state.length; i++) {
- mPaint..color = _originator.state[i].chess.color;
- if (_originator.state[i].chess.chessShape.shape == 1) {
- canvas.drawCircle(
- Offset(_originator.state[i].dx, _originator.state[i].dy),
- min(mWidth / 2, mHeight / 2) - 2,
- mPaint);
- }
- if (_originator.state[i].chess.chessShape.shape == 2) {
- Rect rect = Rect.fromCircle(
- center: Offset(_originator.state[i].dx, _originator.state[i].dy),
- radius: min(mWidth / 2, mHeight / 2) - 2);
- canvas.drawRect(rect, mPaint);
- }
- }
- }
- WidgetsBinding.instance!.addPostFrameCallback((_) {
- if (add && _position!.chess is WhiteChess) {
- _function();
- }
- });
- }
- //在实际场景中正确利用此回调可以避免重绘开销,本示例我们简单的返回true
- @override
- bool shouldRepaint(CustomPainter oldDelegate) {
- return true;
- }
- }
- class CheckerBoardPainter extends CustomPainter {
- static List<CrossOverBean> _crossOverBeanList = [];
- static int _state = 0;
- @override
- void paint(Canvas canvas, Size size) {
- double mWidth = size.width / 15;
- double mHeight = size.height / 15;
- var mPaint = Paint();
- _crossOverBeanList.clear();
- //重绘下整个界面的画布北京颜色
- //设置画笔,画棋盘背景
- mPaint
- ..isAntiAlias = true //抗锯齿
- ..style = PaintingStyle.fill //填充
- ..color = Color(0x77cdb175); //背景为纸黄色
- canvas.drawRect(
- Rect.fromCenter(
- center: Offset(size.width / 2, size.height / 2),
- width: size.width,
- height: size.height),
- mPaint);
- //画棋盘网格
- mPaint
- ..style = PaintingStyle.stroke
- ..color = CupertinoColors.systemGrey6
- ..strokeWidth = 1.0;
- for (var i = 0; i <= 15; i++) {
- //画横线
- canvas.drawLine(
- Offset(0, mHeight * i), Offset(size.width, mHeight * i), mPaint);
- }
- for (var i = 0; i <= 15; i++) {
- //画竖线
- canvas.drawLine(
- Offset(mWidth * i, 0), Offset(mWidth * i, size.height), mPaint);
- }
- //记录横竖线所有的交叉点
- for (int i = 0; i <= 15; i++) {
- for (int j = 0; j <= 15; j++) {
- _crossOverBeanList.add(CrossOverBean(mWidth * j, mHeight * i));
- }
- }
- }
- //在实际场景中正确利用此回调可以避免重绘开销,本示例我们简单的返回true
- @override
- bool shouldRepaint(CustomPainter oldDelegate) {
- return false;
- }
- }
- ///记录棋盘上横竖线的交叉点
- class CrossOverBean {
- double _dx;
- double _dy;
- CrossOverBean(this._dx, this._dy);
- }
|