import 'dart:math';
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Chinese Chess',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: ChineseChessGame(),
);
}
}
class ChineseChessGame extends StatefulWidget {
@override
_ChineseChessGameState createState() => _ChineseChessGameState();
}
class _ChineseChessGameState extends State {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('中国象棋'),
),
body: ChessBoard(),
);
}
}
class ChessBoard extends StatefulWidget {
@override
_ChessBoardState createState() => _ChessBoardState();
}
class _ChessBoardState extends State {
// 定义棋盘大小和格子大小
final int boardSize = 9; // 中国象棋棋盘是9x10的网格
final double gridSize = 50.0; // 每个格子的大小
List chessList = [];
Chess? selectedChess = null;
List chessCmpList = [];
@override
void initState() {
super.initState();
newGame();
chessCmpList=createPieces();
}
void newGame() {
chessList = getInitChesses();
}
List getInitChesses() {
List list = [];
list = [
Chess(text: "車", color: Colors.black, offset: const Offset(0, 0)),
Chess(text: "馬", color: Colors.black, offset: const Offset(1, 0)),
Chess(text: "象", color: Colors.black, offset: const Offset(2, 0)),
Chess(text: "士", color: Colors.black, offset: const Offset(3, 0)),
Chess(text: "将", color: Colors.black, offset: const Offset(4, 0)),
Chess(text: "士", color: Colors.black, offset: const Offset(5, 0)),
Chess(text: "象", color: Colors.black, offset: const Offset(6, 0)),
Chess(text: "馬", color: Colors.black, offset: const Offset(7, 0)),
Chess(text: "車", color: Colors.black, offset: const Offset(8, 0)),
Chess(text: "炮", color: Colors.black, offset: const Offset(1, 2)),
Chess(text: "炮", color: Colors.black, offset: const Offset(7, 2)),
Chess(text: "兵", color: Colors.black, offset: const Offset(0, 3)),
Chess(text: "兵", color: Colors.black, offset: const Offset(2, 3)),
Chess(text: "兵", color: Colors.black, offset: const Offset(4, 3)),
Chess(text: "兵", color: Colors.black, offset: const Offset(6, 3)),
Chess(text: "兵", color: Colors.black, offset: const Offset(8, 3)),
//======
Chess(text: "車", color: Colors.red, offset: const Offset(0, 9)),
Chess(text: "馬", color: Colors.red, offset: const Offset(1, 9)),
Chess(text: "象", color: Colors.red, offset: const Offset(2, 9)),
Chess(text: "士", color: Colors.red, offset: const Offset(3, 9)),
Chess(text: "将", color: Colors.red, offset: const Offset(4, 9)),
Chess(text: "士", color: Colors.red, offset: const Offset(5, 9)),
Chess(text: "象", color: Colors.red, offset: const Offset(6, 9)),
Chess(text: "馬", color: Colors.red, offset: const Offset(7, 9)),
Chess(text: "車", color: Colors.red, offset: const Offset(8, 9)),
Chess(text: "炮", color: Colors.red, offset: const Offset(1, 7)),
Chess(text: "炮", color: Colors.red, offset: const Offset(7, 7)),
Chess(text: "兵", color: Colors.red, offset: const Offset(0, 6)),
Chess(text: "兵", color: Colors.red, offset: const Offset(2, 6)),
Chess(text: "兵", color: Colors.red, offset: const Offset(4, 6)),
Chess(text: "兵", color: Colors.red, offset: const Offset(6, 6)),
Chess(text: "兵", color: Colors.red, offset: const Offset(8, 6)),
];
return list;
}
bool existChess(Offset offsetIndex) {
for (Chess chess in chessList) {
if (chess.offset.dx == offsetIndex.dx && chess.offset.dy == offsetIndex.dy) {
return true;
}
}
return false;
}
// bool checkExistChess(List offsets) {
// for(Offset offset in offsets) {
// if(existChess(offset))
// return true;
// }
// return false;
// }
void clickEventProcess(Offset offset) {
setState(() {
print('点击位置: ${offset}');
// 更新棋盘状态
Offset? tempOffsetIndex = findClickedOffsetIndex(offset);
if (tempOffsetIndex == null) {
return;
}
//在棋盘内
//检查是否选中棋子
Chess? chess = findClickedChess(tempOffsetIndex);
if (chess == null) {
//未选中棋子
if (selectedChess == null) {
//之前也没有选中棋子,不进行处理
} else {
//之前选中棋子,判断是否可以移动棋子
if (chess != selectedChess && canMove(selectedChess!, tempOffsetIndex)) {
selectedChess!.offset = tempOffsetIndex;
selectedChess?.setSelected();
selectedChess = null;
}
}
} else {
if (selectedChess == null) {
selectedChess ??= chess;
selectedChess?.setSelected();
} else {
if (selectedChess == chess) {
} else {
if (chess.color != selectedChess?.color && canEat(selectedChess!, tempOffsetIndex)) {
selectedChess!.offset = tempOffsetIndex;
selectedChess?.setSelected();
chessList.remove(chess);
selectedChess=null;
}else if(chess.color == selectedChess?.color){
selectedChess?.setSelected();
selectedChess = chess;
selectedChess?.setSelected();
}
}
}
}
});
}
bool canMove(Chess selectedChess, Offset tempOffset) {
if (tempOffset.dx < 0 || tempOffset.dx > 8 || tempOffset.dy < 0 || tempOffset.dy > 9) {
return false;
}
if (selectedChess.text == "将") {
if(selectedChess.color==Colors.black){
if (tempOffset.dx < 3 || tempOffset.dx > 5 || tempOffset.dy < 0 || tempOffset.dy > 2) {
return false;
}
}else {
if (tempOffset.dx < 3 || tempOffset.dx > 5 || tempOffset.dy < 7 || tempOffset.dy > 9) {
return false;
}
}
if( !((distance(selectedChess.offset.dx, tempOffset.dx )==1.0&&distance(selectedChess.offset.dy, tempOffset.dy )==0)||(distance
(selectedChess.offset.dx, tempOffset.dx )==0&&distance(selectedChess.offset.dy, tempOffset.dy )==1))){
return false;
}
} else if (selectedChess.text == "士") {
if(selectedChess.color==Colors.black){
if (tempOffset.dx < 3 || tempOffset.dx > 5 || tempOffset.dy < 0 || tempOffset.dy > 2) {
return false;
}
if( !(distance(selectedChess.offset.dx, tempOffset.dx )==1.0&&distance(selectedChess.offset.dy, tempOffset.dy )==1.0)){
return false;
}
}else {
if (tempOffset.dx < 3 || tempOffset.dx > 5 || tempOffset.dy < 7 || tempOffset.dy > 9) {
return false;
}
if( !(distance(selectedChess.offset.dx, tempOffset.dx )==1.0&&distance(selectedChess.offset.dy, tempOffset.dy )==1.0)){
return false;
}
}
} else if (selectedChess.text == "象") {
if (tempOffset.dx < 0 || tempOffset.dx > 8 || tempOffset.dy < 0 || tempOffset.dy > 9) {
return false;
}
if(selectedChess.color==Colors.black){
if(tempOffset.dy > 4)
return false;
}else{
if(tempOffset.dy <5)
return false;
}
if(!(distance(selectedChess.offset.dx,tempOffset.dx)==2.0&&distance(selectedChess.offset.dy,tempOffset.dy)==2.0)){
print("象必须走田");
return false;
}
// todo 判断是否被阻挡
if(distance(selectedChess.offset.dx,tempOffset.dx)==2.0&&distance(selectedChess.offset.dy,tempOffset.dy)==2.0){
if(existChess(Offset((selectedChess.offset.dx+tempOffset.dx)/2,(selectedChess.offset.dy+tempOffset.dy)/2))){
print("象眼阻塞");
return false;
}
}
} else if (selectedChess.text == "馬") {
if (tempOffset.dx < 0 || tempOffset.dx > 8 || tempOffset.dy < 0 || tempOffset.dy > 9) {
return false;
}
if(!(distance(selectedChess.offset.dx,tempOffset.dx)==2.0&&distance(selectedChess.offset.dy,tempOffset.dy)==1.0||distance
(selectedChess.offset.dx,tempOffset.dx)==1.0&&distance(selectedChess.offset.dy,tempOffset.dy)==2.0)){
print("马必须走日");
return false;
}
if(distance(selectedChess.offset.dx,tempOffset.dx)==2.0){
if(existChess(Offset((selectedChess.offset.dx+tempOffset.dx)/2,selectedChess.offset.dy))){
print("撇脚马");
return false;
}
}
if(distance(selectedChess.offset.dy,tempOffset.dy)==2.0){
if(existChess(Offset(selectedChess.offset.dx,(selectedChess.offset.dy+tempOffset.dy)/2))){
print("撇脚马");
return false;
}
}
// todo 判断是否被阻挡
} else if (selectedChess.text == "車") {
if (tempOffset.dx < 0 || tempOffset.dx > 8 || tempOffset.dy < 0 || tempOffset.dy > 9) {
return false;
}
if(selectedChess.offset.dx != tempOffset.dx&& selectedChess.offset.dy != tempOffset.dy){
return false;
}
// todo 判断是否被阻挡
if(selectedChess.offset.dx == tempOffset.dx&&distance(selectedChess.offset.dy, tempOffset.dy)>1){
for (int i = 1; i < distance(selectedChess.offset.dy, tempOffset.dy).toInt(); i++) {
if(existChess(Offset(selectedChess.offset.dx,min(tempOffset.dy, selectedChess.offset.dy)+i))){
print("猪被挡");
return false;
}
}
}
if(selectedChess.offset.dy == tempOffset.dy&&distance(selectedChess.offset.dx, tempOffset.dx)>1){
for (int i = 1; i < distance(selectedChess.offset.dx, tempOffset.dx).toInt(); i++) {
if(existChess(Offset(min(tempOffset.dx, selectedChess.offset.dx)+i,selectedChess.offset.dy))){
print("猪被挡");
return false;
}
}
}
} else if (selectedChess.text == "炮") {
if (tempOffset.dx < 0 || tempOffset.dx > 8 || tempOffset.dy < 0 || tempOffset.dy > 9) {
return false;
}
if(selectedChess.offset.dx != tempOffset.dx&& selectedChess.offset.dy != tempOffset.dy){
return false;
}
if(selectedChess.offset.dx == tempOffset.dx&&distance(selectedChess.offset.dy, tempOffset.dy)>1){
for (int i = 1; i < distance(selectedChess.offset.dy, tempOffset.dy).toInt(); i++) {
if(existChess(Offset(selectedChess.offset.dx,min(tempOffset.dy, selectedChess.offset.dy)+i))){
print("炮被挡");
return false;
}
}
}
if(selectedChess.offset.dy == tempOffset.dy&&distance(selectedChess.offset.dx, tempOffset.dx)>1){
for (int i = 1; i < distance(selectedChess.offset.dx, tempOffset.dx).toInt(); i++) {
if(existChess(Offset(min(tempOffset.dx, selectedChess.offset.dx)+i,selectedChess.offset.dy))){
print("炮被挡");
return false;
}
}
}
// todo 判断是否被阻挡
} else if (selectedChess.text == "兵") {
if (tempOffset.dx < 0 || tempOffset.dx > 8 || tempOffset.dy < 0 || tempOffset.dy > 9) {
return false;
}
if(selectedChess.color==Colors.black){
if(tempOffset.dyselectedChess.offset.dy){
return false;
}
if(tempOffset.dx!=selectedChess.offset.dx&&selectedChess.offset.dy>6){
return false;
}
}
if( !((distance(selectedChess.offset.dx, tempOffset.dx )==1.0&&distance(selectedChess.offset.dy, tempOffset.dy )==0)||(distance
(selectedChess.offset.dx, tempOffset.dx )==0&&distance(selectedChess.offset.dy, tempOffset.dy )==1))){
return false;
}
}
return true;
}
bool canEat(Chess selectedChess, Offset tempOffset) {
if (tempOffset.dx < 0 || tempOffset.dx > 8 || tempOffset.dy < 0 || tempOffset.dy > 9) {
return false;
}
if (selectedChess.text == "将") {
if(selectedChess.color==Colors.black){
if (tempOffset.dx < 3 || tempOffset.dx > 5 || tempOffset.dy < 0 || tempOffset.dy > 2) {
return false;
}
}else {
if (tempOffset.dx < 3 || tempOffset.dx > 5 || tempOffset.dy < 7 || tempOffset.dy > 9) {
return false;
}
}
if( !((distance(selectedChess.offset.dx, tempOffset.dx )==1.0&&distance(selectedChess.offset.dy, tempOffset.dy )==0)||(distance
(selectedChess.offset.dx, tempOffset.dx )==0&&distance(selectedChess.offset.dy, tempOffset.dy )==1))){
return false;
}
} else if (selectedChess.text == "士") {
if(selectedChess.color==Colors.black){
if (tempOffset.dx < 3 || tempOffset.dx > 5 || tempOffset.dy < 0 || tempOffset.dy > 2) {
return false;
}
if( !(distance(selectedChess.offset.dx, tempOffset.dx )==1.0&&distance(selectedChess.offset.dy, tempOffset.dy )==1.0)){
return false;
}
}else {
if (tempOffset.dx < 3 || tempOffset.dx > 5 || tempOffset.dy < 7 || tempOffset.dy > 9) {
return false;
}
if( !(distance(selectedChess.offset.dx, tempOffset.dx )==1.0&&distance(selectedChess.offset.dy, tempOffset.dy )==1.0)){
return false;
}
}
} else if (selectedChess.text == "象") {
if (tempOffset.dx < 0 || tempOffset.dx > 8 || tempOffset.dy < 0 || tempOffset.dy > 4) {
return false;
}
if(selectedChess.color==Colors.black){
if(tempOffset.dy > 4) {
return false;
}
}else{
if(tempOffset.dy <5) {
return false;
}
}
if(!(distance(selectedChess.offset.dx,tempOffset.dx)==2.0&&distance(selectedChess.offset.dy,tempOffset.dy)==2.0)){
print("象必须走田");
return false;
}
// todo 判断是否被阻挡
if(distance(selectedChess.offset.dx,tempOffset.dx)==2.0&&distance(selectedChess.offset.dy,tempOffset.dy)==2.0){
if(existChess(Offset((selectedChess.offset.dx+tempOffset.dx)/2,(selectedChess.offset.dy+tempOffset.dy)/2))){
print("象眼阻塞");
return false;
}
}
} else if (selectedChess.text == "馬") {
if (tempOffset.dx < 0 || tempOffset.dx > 8 || tempOffset.dy < 0 || tempOffset.dy > 9) {
return false;
}
if(!(distance(selectedChess.offset.dx,tempOffset.dx)==2.0&&distance(selectedChess.offset.dy,tempOffset.dy)==1.0||distance
(selectedChess.offset.dx,tempOffset.dx)==1.0&&distance(selectedChess.offset.dy,tempOffset.dy)==2.0)){
return false;
}
if(distance(selectedChess.offset.dx,tempOffset.dx)==2.0){
if(existChess(Offset((selectedChess.offset.dx+tempOffset.dx)/2,selectedChess.offset.dy))){
print("撇脚马");
return false;
}
}
if(distance(selectedChess.offset.dy,tempOffset.dy)==2.0){
if(existChess(Offset(selectedChess.offset.dx,(selectedChess.offset.dy+tempOffset.dy)/2))){
print("撇脚马");
return false;
}
}
// todo 判断是否被阻挡
} else if (selectedChess.text == "車") {
if (tempOffset.dx < 0 || tempOffset.dx > 8 || tempOffset.dy < 0 || tempOffset.dy > 9) {
return false;
}
if(selectedChess.offset.dx != tempOffset.dx&& selectedChess.offset.dy != tempOffset.dy){
return false;
}
// todo 判断是否被阻挡
if(selectedChess.offset.dx == tempOffset.dx&&distance(selectedChess.offset.dy, tempOffset.dy)>1){
for (int i = 1; i < distance(selectedChess.offset.dy, tempOffset.dy).toInt(); i++) {
if(existChess(Offset(selectedChess.offset.dx,min(tempOffset.dy, selectedChess.offset.dy)+i))){
print("猪被挡");
return false;
}
}
}
if(selectedChess.offset.dy == tempOffset.dy&&distance(selectedChess.offset.dx, tempOffset.dx)>1){
for (int i = 1; i < distance(selectedChess.offset.dx, tempOffset.dx).toInt(); i++) {
if(existChess(Offset(min(tempOffset.dx, selectedChess.offset.dx)+i,selectedChess.offset.dy))){
print("猪被挡");
return false;
}
}
}
} else if (selectedChess.text == "炮") {
if (tempOffset.dx < 0 || tempOffset.dx > 8 || tempOffset.dy < 0 || tempOffset.dy > 9) {
return false;
}
if(selectedChess.offset.dx != tempOffset.dx&& selectedChess.offset.dy != tempOffset.dy){
return false;
}
if(selectedChess.offset.dx == tempOffset.dx&&distance(selectedChess.offset.dy, tempOffset.dy)>1){
int count = 0;
for (int i = 1; i < distance(selectedChess.offset.dy, tempOffset.dy).toInt(); i++) {
if(existChess(Offset(selectedChess.offset.dx,min(tempOffset.dy, selectedChess.offset.dy)+i))){
count++;
}
}
if(count!=1){
print("炮需隔一个子");
}
}
if(selectedChess.offset.dy == tempOffset.dy&&distance(selectedChess.offset.dx, tempOffset.dx)>1){
for (int i = 1; i < distance(selectedChess.offset.dx, tempOffset.dx).toInt(); i++) {
if(existChess(Offset(min(tempOffset.dx, selectedChess.offset.dx)+i,selectedChess.offset.dy))){
print("炮被挡");
return false;
}
}
}
// todo 判断是否被阻挡
} else if (selectedChess.text == "兵") {
if (tempOffset.dx < 0 || tempOffset.dx > 8 || tempOffset.dy < 0 || tempOffset.dy > 9) {
return false;
}
if(selectedChess.color==Colors.black){
if(tempOffset.dyselectedChess.offset.dy){
return false;
}
if(tempOffset.dx!=selectedChess.offset.dx&&selectedChess.offset.dy>6){
return false;
}
}
if( !((distance(selectedChess.offset.dx, tempOffset.dx )==1.0&&distance(selectedChess.offset.dy, tempOffset.dy )==0)||(distance
(selectedChess.offset.dx, tempOffset.dx )==0&&distance(selectedChess.offset.dy, tempOffset.dy )==1))){
return false;
}
}
return true;
}
double distance(double a,double b){
double c=a-b;
if(c<0) {
return -c;
}
return c;
}
Chess? findClickedChess(Offset offset) {
for (Chess chess in chessList) {
if (chess.offset.dx == offset.dx && chess.offset.dy == offset.dy) {
return chess;
}
}
return null;
}
Offset? findClickedOffsetIndex(Offset offset) {
for (int i = 0; i < 9; i++) {
for (int j = 0; j < 10; j++) {
if (80 + i * 50 <= offset.dx && 80 + i * 50 + 50 >= offset.dx && 80 + j * 50 <= offset.dy && 80 + j * 50 + 50 >= offset.dy) {
return Offset(i * 1.0, j * 1.0);
}
}
}
return null;
}
@override
Widget build(BuildContext context) {
chessCmpList=createPieces();
return Row(
children: [
Expanded(flex: 7, child: GestureDetector(
onTapDown: (details) {
// 处理点击事件
clickEventProcess(details.localPosition);
},
child: CustomPaint(
size: Size(boardSize * gridSize, (boardSize + 1) * gridSize), // 象棋棋盘是9x10
painter: ChessBoardPainter(chessList:chessList),
child: const Stack(
children: [],//createPieces(), // 创建棋子并定位
),
)
)), // 棋盘占70%
Expanded(flex: 3, child: buildControllPanel()), // 控制面板占30%
],
)
;
}
List createPieces() {
// 这里只是一个示例,实际应用中需要根据棋局状态动态生成棋子
for (Chess chess in chessList) {
chessCmpList.add(Positioned(
top: chess
.getPosition()
.dy,
left: chess
.getPosition()
.dx,
child: chess));
}
return chessCmpList;
}
Widget buildControllPanel() {
return Center(child:Row(
children: [
TextButton(onPressed: (){
setState(() {
newGame();
});
}, child: const Text("重新开始")),
TextButton(onPressed: (){}, child: const Text("悔棋"))
]
));
}
}
class Chess extends StatefulWidget {
final String text;
final Color color;
Offset offset;
bool selected = false;
bool alive = true;
void setSelected() {
selected = !selected;
}
Chess({required this.text, required this.color, required this.offset});
Offset getPosition() {
return Offset(80.0 + offset.dx * 50, 80.0 + offset.dy * 50);
}
@override
State createState() {
return _ChessState();
}
}
class _ChessState extends State {
late String text;
late Color color;
late Offset offset;
late bool selected = false;
@override
void initState() {
super.initState();
text=widget.text;
color=widget.color;
offset=widget.offset;
selected=widget.selected;
}
@override
Widget build(BuildContext context) {
return Container(
width: 40, // 设置你想要的宽度
height: 40, // 设置你想要的高度(应与宽度相同以保持圆形)
decoration: BoxDecoration(
color: selected ? Colors.blue : color, // 背景颜色
shape: BoxShape.circle, // 设置为圆形
),
child: Stack(children: [
const SizedBox(
width: 40,
height: 40,
),
Positioned(
top: 3,
left: 8,
child: Text(
text,
style: const TextStyle(
color: Colors.white, // 文字颜色,从构造函数传入
fontSize: 24.0, // 文字大小
),
),
)
]),
);
}
}
class ChessBoardPainter extends CustomPainter {
List chessList;
ChessBoardPainter({required this.chessList});
@override
void paint(Canvas canvas, Size size) {
// 绘制棋盘线
final Paint linePaint = Paint()
..color = Colors.lightBlue
..strokeWidth = 1.5;
int header = 100;
// 绘制竖线
for (int col = 0; col < 9; col++) {
canvas.drawLine(Offset(header + col * 50.0, header + 0), Offset(header + col * 50.0, header + 200), linePaint);
}
// 绘制竖线
for (int col = 0; col < 9; col++) {
canvas.drawLine(Offset(header + col * 50.0, header + 250), Offset(header + col * 50.0, header + 450), linePaint);
}
canvas.drawLine(Offset(header + 0 * 50.0, header + 0), Offset(header + 0 * 50.0, header + 450), linePaint);
canvas.drawLine(Offset(header + 8 * 50.0, header + 0), Offset(header + 8 * 50.0, header + 450), linePaint);
canvas.drawLine(Offset(header + 3 * 50.0, header + 0), Offset(header + 5 * 50.0, header + 100), linePaint);
canvas.drawLine(Offset(header + 3 * 50.0, header + 100), Offset(header + 5 * 50.0, header + 0), linePaint);
canvas.drawLine(Offset(header + 3 * 50.0, header + 350), Offset(header + 5 * 50.0, header + 450), linePaint);
canvas.drawLine(Offset(header + 5 * 50.0, header + 350), Offset(header + 3 * 50.0, header + 450), linePaint);
// 绘制横线
for (int row = 0; row < 10; row++) {
canvas.drawLine(Offset(header + 0, header + row * 50.0), Offset(header + 400, header + row * 50.0), linePaint);
}
// 特殊标记(如楚河汉界)
final TextPainter textPainter = TextPainter(textDirection: TextDirection.ltr);
textPainter.text = const TextSpan(
text: '楚 河 汉 界',
style: TextStyle(fontSize: 18.0, color: Colors.green, fontWeight: FontWeight.bold,fontFamily:"华文行楷" ),
);
textPainter.layout();
textPainter.paint(canvas, Offset(header + 50, header + 215));
for(Chess chess in chessList){
Paint chessPaint = Paint()
..color = chess.color
..strokeWidth = 1.0;
canvas.drawCircle(chess.getPosition()+const Offset(20,20), 20, chessPaint);
TextPainter textPainter2 = TextPainter(textDirection: TextDirection.ltr);
textPainter.text = TextSpan(
text: chess.text,
style: const TextStyle(fontSize: 26.0, color: Colors.white, fontWeight: FontWeight.bold,fontFamily:"华文行楷" ),
);
textPainter.layout();
textPainter.paint(canvas, chess.getPosition()+const Offset(6,6));
}
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
return true;
}
}
// 定义棋盘和控制面板的widget