Compare commits
7 Commits
dev
...
cleanup/ga
Author | SHA1 | Date |
---|---|---|
Leptopoda-GitHub | f7c9b0e29f | |
Leptopoda-GitHub | 20433731ff | |
Leptopoda-GitHub | 4985bc0b93 | |
Leptopoda-GitHub | cd43467a6d | |
Leptopoda-GitHub | 101a8f48d0 | |
Leptopoda-GitHub | 110ad64d55 | |
Leptopoda-GitHub | 385526fa82 |
|
@ -78,8 +78,7 @@ class Game {
|
|||
"$_tag Black is searching? ${isSearching[PieceColor.black]}\n",
|
||||
);
|
||||
|
||||
return isSearching[PieceColor.white] == true ||
|
||||
isSearching[PieceColor.black] == true;
|
||||
return isSearching[PieceColor.white]! || isSearching[PieceColor.black]!;
|
||||
}
|
||||
|
||||
EngineType engineType = EngineType.none;
|
||||
|
@ -123,7 +122,7 @@ class Game {
|
|||
|
||||
debugPrint("$_tag AI do move: $move");
|
||||
|
||||
if (await position.doMove(move) == false) {
|
||||
if (!(await position.doMove(move))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -470,164 +470,152 @@ class Position {
|
|||
return false;
|
||||
}
|
||||
|
||||
if (phase == Phase.ready) {
|
||||
start();
|
||||
}
|
||||
if (phase == Phase.ready) start();
|
||||
|
||||
// TODO: [Leptopoda] use switch case
|
||||
if (phase == Phase.placing) {
|
||||
piece = sideToMove;
|
||||
if (pieceInHandCount[us] != null) {
|
||||
pieceInHandCount[us] = pieceInHandCount[us]! - 1;
|
||||
}
|
||||
|
||||
if (pieceOnBoardCount[us] != null) {
|
||||
pieceOnBoardCount[us] = pieceOnBoardCount[us]! + 1;
|
||||
}
|
||||
|
||||
_grid[squareToIndex[s]!] = piece;
|
||||
board[s] = piece;
|
||||
|
||||
record = "(${fileOf(s)},${rankOf(s)})";
|
||||
|
||||
updateKey(s);
|
||||
|
||||
currentSquare = s;
|
||||
|
||||
final int n = millsCount(currentSquare);
|
||||
|
||||
if (n == 0) {
|
||||
assert(
|
||||
pieceInHandCount[PieceColor.white]! >= 0 &&
|
||||
pieceInHandCount[PieceColor.black]! >= 0,
|
||||
);
|
||||
|
||||
if (pieceInHandCount[PieceColor.white] == 0 &&
|
||||
pieceInHandCount[PieceColor.black] == 0) {
|
||||
if (isGameOver()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
phase = Phase.moving;
|
||||
action = Act.select;
|
||||
|
||||
if (LocalDatabaseService.rules.hasBannedLocations) {
|
||||
removeBanStones();
|
||||
}
|
||||
|
||||
if (!LocalDatabaseService.rules.isDefenderMoveFirst) {
|
||||
changeSideToMove();
|
||||
}
|
||||
|
||||
if (isGameOver()) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
changeSideToMove();
|
||||
switch (phase) {
|
||||
case Phase.placing:
|
||||
piece = sideToMove;
|
||||
if (pieceInHandCount[us] != null) {
|
||||
pieceInHandCount[us] = pieceInHandCount[us]! - 1;
|
||||
}
|
||||
gameInstance.focusIndex = squareToIndex[s];
|
||||
await Audios.playTone(Sound.place);
|
||||
} else {
|
||||
pieceToRemoveCount =
|
||||
LocalDatabaseService.rules.mayRemoveMultiple ? n : 1;
|
||||
updateKeyMisc();
|
||||
|
||||
if (LocalDatabaseService
|
||||
.rules.mayOnlyRemoveUnplacedPieceInPlacingPhase &&
|
||||
pieceInHandCount[them] != null) {
|
||||
pieceInHandCount[them] =
|
||||
pieceInHandCount[them]! - 1; // Or pieceToRemoveCount?
|
||||
if (pieceOnBoardCount[us] != null) {
|
||||
pieceOnBoardCount[us] = pieceOnBoardCount[us]! + 1;
|
||||
}
|
||||
|
||||
if (pieceInHandCount[them]! < 0) {
|
||||
pieceInHandCount[them] = 0;
|
||||
}
|
||||
_grid[squareToIndex[s]!] = piece;
|
||||
board[s] = piece;
|
||||
|
||||
record = "(${fileOf(s)},${rankOf(s)})";
|
||||
|
||||
updateKey(s);
|
||||
|
||||
currentSquare = s;
|
||||
|
||||
final int n = millsCount(currentSquare);
|
||||
|
||||
if (n == 0) {
|
||||
assert(
|
||||
pieceInHandCount[PieceColor.white]! >= 0 &&
|
||||
pieceInHandCount[PieceColor.black]! >= 0,
|
||||
);
|
||||
|
||||
if (pieceInHandCount[PieceColor.white] == 0 &&
|
||||
pieceInHandCount[PieceColor.black] == 0) {
|
||||
if (isGameOver()) {
|
||||
return true;
|
||||
}
|
||||
if (isGameOver()) return true;
|
||||
|
||||
phase = Phase.moving;
|
||||
action = Act.select;
|
||||
|
||||
if (LocalDatabaseService.rules.isDefenderMoveFirst) {
|
||||
if (LocalDatabaseService.rules.hasBannedLocations) {
|
||||
removeBanStones();
|
||||
}
|
||||
|
||||
if (!LocalDatabaseService.rules.isDefenderMoveFirst) {
|
||||
changeSideToMove();
|
||||
}
|
||||
|
||||
if (isGameOver()) {
|
||||
return true;
|
||||
}
|
||||
if (isGameOver()) return true;
|
||||
} else {
|
||||
changeSideToMove();
|
||||
}
|
||||
} else {
|
||||
action = Act.remove;
|
||||
}
|
||||
|
||||
gameInstance.focusIndex = squareToIndex[s];
|
||||
await Audios.playTone(Sound.mill);
|
||||
}
|
||||
} else if (phase == Phase.moving) {
|
||||
if (isGameOver()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// if illegal
|
||||
if (pieceOnBoardCount[sideToMove]! >
|
||||
LocalDatabaseService.rules.flyPieceCount ||
|
||||
!LocalDatabaseService.rules.mayFly) {
|
||||
int md;
|
||||
|
||||
for (md = 0; md < moveDirectionNumber; md++) {
|
||||
if (s == adjacentSquares[currentSquare][md]) break;
|
||||
}
|
||||
|
||||
// not in moveTable
|
||||
if (md == moveDirectionNumber) {
|
||||
debugPrint(
|
||||
"[position] putPiece: [$s] is not in [$currentSquare]'s move table.",
|
||||
);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
record =
|
||||
"(${fileOf(currentSquare)},${rankOf(currentSquare)})->(${fileOf(s)},${rankOf(s)})";
|
||||
|
||||
st.rule50++;
|
||||
|
||||
board[s] = _grid[squareToIndex[s]!] = board[currentSquare];
|
||||
updateKey(s);
|
||||
revertKey(currentSquare);
|
||||
|
||||
board[currentSquare] =
|
||||
_grid[squareToIndex[currentSquare]!] = Piece.noPiece;
|
||||
|
||||
currentSquare = s;
|
||||
final int n = millsCount(currentSquare);
|
||||
|
||||
// midgame
|
||||
if (n == 0) {
|
||||
action = Act.select;
|
||||
changeSideToMove();
|
||||
|
||||
if (isGameOver()) {
|
||||
return true;
|
||||
} else {
|
||||
gameInstance.focusIndex = squareToIndex[s];
|
||||
await Audios.playTone(Sound.place);
|
||||
}
|
||||
} else {
|
||||
pieceToRemoveCount =
|
||||
LocalDatabaseService.rules.mayRemoveMultiple ? n : 1;
|
||||
updateKeyMisc();
|
||||
action = Act.remove;
|
||||
gameInstance.focusIndex = squareToIndex[s];
|
||||
await Audios.playTone(Sound.mill);
|
||||
}
|
||||
} else {
|
||||
assert(false);
|
||||
}
|
||||
} else {
|
||||
pieceToRemoveCount =
|
||||
LocalDatabaseService.rules.mayRemoveMultiple ? n : 1;
|
||||
updateKeyMisc();
|
||||
|
||||
if (LocalDatabaseService
|
||||
.rules.mayOnlyRemoveUnplacedPieceInPlacingPhase &&
|
||||
pieceInHandCount[them] != null) {
|
||||
pieceInHandCount[them] =
|
||||
pieceInHandCount[them]! - 1; // Or pieceToRemoveCount?
|
||||
|
||||
if (pieceInHandCount[them]! < 0) {
|
||||
pieceInHandCount[them] = 0;
|
||||
}
|
||||
|
||||
if (pieceInHandCount[PieceColor.white] == 0 &&
|
||||
pieceInHandCount[PieceColor.black] == 0) {
|
||||
if (isGameOver()) return true;
|
||||
|
||||
phase = Phase.moving;
|
||||
action = Act.select;
|
||||
|
||||
if (LocalDatabaseService.rules.isDefenderMoveFirst) {
|
||||
changeSideToMove();
|
||||
}
|
||||
|
||||
if (isGameOver()) return true;
|
||||
}
|
||||
} else {
|
||||
action = Act.remove;
|
||||
}
|
||||
|
||||
gameInstance.focusIndex = squareToIndex[s];
|
||||
await Audios.playTone(Sound.mill);
|
||||
}
|
||||
break;
|
||||
case Phase.moving:
|
||||
if (isGameOver()) return true;
|
||||
|
||||
// if illegal
|
||||
if (pieceOnBoardCount[sideToMove]! >
|
||||
LocalDatabaseService.rules.flyPieceCount ||
|
||||
!LocalDatabaseService.rules.mayFly) {
|
||||
int md;
|
||||
|
||||
for (md = 0; md < moveDirectionNumber; md++) {
|
||||
if (s == adjacentSquares[currentSquare][md]) break;
|
||||
}
|
||||
|
||||
// not in moveTable
|
||||
if (md == moveDirectionNumber) {
|
||||
debugPrint(
|
||||
"[position] putPiece: [$s] is not in [$currentSquare]'s move table.",
|
||||
);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
record =
|
||||
"(${fileOf(currentSquare)},${rankOf(currentSquare)})->(${fileOf(s)},${rankOf(s)})";
|
||||
|
||||
st.rule50++;
|
||||
|
||||
board[s] = _grid[squareToIndex[s]!] = board[currentSquare];
|
||||
updateKey(s);
|
||||
revertKey(currentSquare);
|
||||
|
||||
board[currentSquare] =
|
||||
_grid[squareToIndex[currentSquare]!] = Piece.noPiece;
|
||||
|
||||
currentSquare = s;
|
||||
final int n = millsCount(currentSquare);
|
||||
|
||||
// midgame
|
||||
if (n == 0) {
|
||||
action = Act.select;
|
||||
changeSideToMove();
|
||||
|
||||
if (isGameOver()) return true;
|
||||
gameInstance.focusIndex = squareToIndex[s];
|
||||
|
||||
await Audios.playTone(Sound.place);
|
||||
} else {
|
||||
pieceToRemoveCount =
|
||||
LocalDatabaseService.rules.mayRemoveMultiple ? n : 1;
|
||||
updateKeyMisc();
|
||||
action = Act.remove;
|
||||
gameInstance.focusIndex = squareToIndex[s];
|
||||
await Audios.playTone(Sound.mill);
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1121,7 +1109,7 @@ class Position {
|
|||
}
|
||||
|
||||
for (var i = 0; i <= moveIndex; i++) {
|
||||
if (await gameInstance.doMove(history[i].move) == false) {
|
||||
if (!(await gameInstance.doMove(history[i].move))) {
|
||||
errString = history[i].move;
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -30,22 +30,19 @@ class GameRecorder {
|
|||
|
||||
GameRecorder({this.cur = -1, this.lastPositionWithRemove});
|
||||
|
||||
String wmdNotationToMoveString(String wmd) {
|
||||
String move = "";
|
||||
|
||||
String? wmdNotationToMoveString(String wmd) {
|
||||
if (wmd.length == 3 && wmd[0] == "x") {
|
||||
if (wmdNotationToMove[wmd.substring(1, 3)] != null) {
|
||||
move = '-${wmdNotationToMove[wmd.substring(1, 3)]!}';
|
||||
return '-${wmdNotationToMove[wmd.substring(1, 3)]!}';
|
||||
}
|
||||
} else if (wmd.length == 2) {
|
||||
if (wmdNotationToMove[wmd] != null) {
|
||||
move = wmdNotationToMove[wmd]!;
|
||||
return wmdNotationToMove[wmd]!;
|
||||
}
|
||||
} else if (wmd.length == 5 && wmd[2] == '-') {
|
||||
if (wmdNotationToMove[(wmd.substring(0, 2))] != null &&
|
||||
wmdNotationToMove[(wmd.substring(3, 5))] != null) {
|
||||
move =
|
||||
'${wmdNotationToMove[(wmd.substring(0, 2))]!}->${wmdNotationToMove[(wmd.substring(3, 5))]!}';
|
||||
return '${wmdNotationToMove[(wmd.substring(0, 2))]!}->${wmdNotationToMove[(wmd.substring(3, 5))]!}';
|
||||
}
|
||||
} else if ((wmd.length == 8 && wmd[2] == '-' && wmd[5] == 'x') ||
|
||||
(wmd.length == 5 && wmd[2] == 'x')) {
|
||||
|
@ -53,16 +50,10 @@ class GameRecorder {
|
|||
} else {
|
||||
debugPrint("$_tag Parse notation $wmd failed.");
|
||||
}
|
||||
|
||||
return move;
|
||||
}
|
||||
|
||||
String playOkNotationToMoveString(String playOk) {
|
||||
String move = "";
|
||||
|
||||
if (playOk.isEmpty) {
|
||||
return "";
|
||||
}
|
||||
String? playOkNotationToMoveString(String playOk) {
|
||||
if (playOk.isEmpty) return null;
|
||||
|
||||
final iDash = playOk.indexOf('-');
|
||||
final iX = playOk.indexOf('x');
|
||||
|
@ -74,7 +65,7 @@ class GameRecorder {
|
|||
return playOkNotationToMove[playOk]!;
|
||||
} else {
|
||||
debugPrint("$_tag Parse PlayOK notation $playOk failed.");
|
||||
return "";
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -86,18 +77,19 @@ class GameRecorder {
|
|||
return "-${playOkNotationToMove[sub]!}";
|
||||
} else {
|
||||
debugPrint("$_tag Parse PlayOK notation $playOk failed.");
|
||||
return "";
|
||||
return null;
|
||||
}
|
||||
}
|
||||
if (iDash != -1 && iX == -1) {
|
||||
String? move;
|
||||
// 12-13
|
||||
final sub1 = playOk.substring(0, iDash);
|
||||
final val1 = int.parse(sub1);
|
||||
if (val1 >= 1 && val1 <= 24) {
|
||||
move = playOkNotationToMove[sub1]!;
|
||||
move = playOkNotationToMove[sub1];
|
||||
} else {
|
||||
debugPrint("$_tag Parse PlayOK notation $playOk failed.");
|
||||
return "";
|
||||
return null;
|
||||
}
|
||||
|
||||
final sub2 = playOk.substring(iDash + 1);
|
||||
|
@ -106,12 +98,12 @@ class GameRecorder {
|
|||
return "$move->${playOkNotationToMove[sub2]!}";
|
||||
} else {
|
||||
debugPrint("$_tag Parse PlayOK notation $playOk failed.");
|
||||
return "";
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
debugPrint("$_tag Not support parsing format oo-ooxo PlayOK notation.");
|
||||
return "";
|
||||
return null;
|
||||
}
|
||||
|
||||
bool isDalmaxMoveList(String text) {
|
||||
|
@ -157,11 +149,10 @@ class GameRecorder {
|
|||
return false;
|
||||
}
|
||||
|
||||
String playOkToWmdMoveList(String playOk) {
|
||||
return "";
|
||||
}
|
||||
String? import(String moveList) {
|
||||
clear();
|
||||
debugPrint("Clipboard text: $moveList");
|
||||
|
||||
String import(String moveList) {
|
||||
if (isDalmaxMoveList(moveList)) {
|
||||
return importDalmax(moveList);
|
||||
}
|
||||
|
@ -217,15 +208,15 @@ class GameRecorder {
|
|||
if (i.isNotEmpty && !i.endsWith(".")) {
|
||||
if (i.length == 5 && i[2] == 'x') {
|
||||
// "a1xc3"
|
||||
final String m1 = wmdNotationToMoveString(i.substring(0, 2));
|
||||
if (m1 != "") {
|
||||
final String? m1 = wmdNotationToMoveString(i.substring(0, 2));
|
||||
if (m1 != null) {
|
||||
newHistory.add(Move(m1));
|
||||
} else {
|
||||
debugPrint("Cannot import $i");
|
||||
return i;
|
||||
}
|
||||
final String m2 = wmdNotationToMoveString(i.substring(2));
|
||||
if (m2 != "") {
|
||||
final String? m2 = wmdNotationToMoveString(i.substring(2));
|
||||
if (m2 != null) {
|
||||
newHistory.add(Move(m2));
|
||||
} else {
|
||||
debugPrint("Cannot import $i");
|
||||
|
@ -233,15 +224,15 @@ class GameRecorder {
|
|||
}
|
||||
} else if (i.length == 8 && i[2] == '-' && i[5] == 'x') {
|
||||
// "a1-b2xc3"
|
||||
final String m1 = wmdNotationToMoveString(i.substring(0, 5));
|
||||
if (m1 != "") {
|
||||
final String? m1 = wmdNotationToMoveString(i.substring(0, 5));
|
||||
if (m1 != null) {
|
||||
newHistory.add(Move(m1));
|
||||
} else {
|
||||
debugPrint("Cannot import $i");
|
||||
return i;
|
||||
}
|
||||
final String m2 = wmdNotationToMoveString(i.substring(5));
|
||||
if (m2 != "") {
|
||||
final String? m2 = wmdNotationToMoveString(i.substring(5));
|
||||
if (m2 != null) {
|
||||
newHistory.add(Move(m2));
|
||||
} else {
|
||||
debugPrint("Cannot import $i");
|
||||
|
@ -249,8 +240,8 @@ class GameRecorder {
|
|||
}
|
||||
} else {
|
||||
// no x
|
||||
final String m = wmdNotationToMoveString(i);
|
||||
if (m != "") {
|
||||
final String? m = wmdNotationToMoveString(i);
|
||||
if (m != null) {
|
||||
newHistory.add(Move(m));
|
||||
} else {
|
||||
debugPrint("Cannot import $i");
|
||||
|
@ -263,15 +254,13 @@ class GameRecorder {
|
|||
if (newHistory.isNotEmpty) {
|
||||
history = newHistory;
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
String importDalmax(String moveList) {
|
||||
String? importDalmax(String moveList) {
|
||||
return import(moveList.substring(moveList.indexOf("1. ")));
|
||||
}
|
||||
|
||||
String importPlayOk(String moveList) {
|
||||
String? importPlayOk(String moveList) {
|
||||
final List<Move> newHistory = [];
|
||||
|
||||
final List<String> list = moveList
|
||||
|
@ -291,23 +280,23 @@ class GameRecorder {
|
|||
!i.endsWith("]")) {
|
||||
final iX = i.indexOf('x');
|
||||
if (iX == -1) {
|
||||
final String m = playOkNotationToMoveString(i);
|
||||
if (m != "") {
|
||||
final String? m = playOkNotationToMoveString(i);
|
||||
if (m != null) {
|
||||
newHistory.add(Move(m));
|
||||
} else {
|
||||
debugPrint("Cannot import $i");
|
||||
return i;
|
||||
}
|
||||
} else if (iX != -1) {
|
||||
final String m1 = playOkNotationToMoveString(i.substring(0, iX));
|
||||
if (m1 != "") {
|
||||
final String? m1 = playOkNotationToMoveString(i.substring(0, iX));
|
||||
if (m1 != null) {
|
||||
newHistory.add(Move(m1));
|
||||
} else {
|
||||
debugPrint("Cannot import $i");
|
||||
return i;
|
||||
}
|
||||
final String m2 = playOkNotationToMoveString(i.substring(iX));
|
||||
if (m2 != "") {
|
||||
final String? m2 = playOkNotationToMoveString(i.substring(iX));
|
||||
if (m2 != null) {
|
||||
newHistory.add(Move(m2));
|
||||
} else {
|
||||
debugPrint("Cannot import $i");
|
||||
|
@ -321,10 +310,10 @@ class GameRecorder {
|
|||
history = newHistory;
|
||||
}
|
||||
|
||||
return "";
|
||||
return null;
|
||||
}
|
||||
|
||||
String importGoldToken(String moveList) {
|
||||
String? importGoldToken(String moveList) {
|
||||
int start = moveList.indexOf("1\t");
|
||||
|
||||
if (start == -1) {
|
||||
|
|
|
@ -274,7 +274,7 @@ int makeSquare(int file, int rank) {
|
|||
bool isOk(int sq) {
|
||||
final bool ret = sq == 0 || (sq >= sqBegin && sq < sqEnd);
|
||||
|
||||
if (ret == false) {
|
||||
if (!ret) {
|
||||
debugPrint("[types] $sq is not OK");
|
||||
}
|
||||
|
||||
|
|
|
@ -18,27 +18,30 @@
|
|||
|
||||
part of 'package:sanmill/screens/game_page/game_page.dart';
|
||||
|
||||
typedef BoardTapCallback = Future<void> Function(int index);
|
||||
/// Board Tap Callback
|
||||
///
|
||||
/// This function gets called once a valid [square] on the board has been tapped.
|
||||
typedef BoardTapCallback = Future<void> Function(int square);
|
||||
|
||||
class Board extends StatelessWidget {
|
||||
final double width;
|
||||
final double height;
|
||||
final BoardTapCallback onBoardTap;
|
||||
final double animationValue;
|
||||
final List<String> squareDesc = [];
|
||||
final Animation<double> animation;
|
||||
static const String _tag = "[board]";
|
||||
|
||||
Board({
|
||||
const Board({
|
||||
required this.width,
|
||||
required this.onBoardTap,
|
||||
required this.animationValue,
|
||||
required this.animation,
|
||||
}) : height = width;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
const padding = AppTheme.boardPadding;
|
||||
final List<String> _squareDesc = [];
|
||||
|
||||
_buildSquareDescription(context);
|
||||
_buildSquareDescription(context, _squareDesc);
|
||||
|
||||
final grid = GridView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
|
@ -49,7 +52,7 @@ class Board extends StatelessWidget {
|
|||
7 * 7,
|
||||
(index) => Center(
|
||||
child: Text(
|
||||
squareDesc[index],
|
||||
_squareDesc[index],
|
||||
style: const TextStyle(
|
||||
color: Colors.red,
|
||||
),
|
||||
|
@ -58,15 +61,21 @@ class Board extends StatelessWidget {
|
|||
),
|
||||
);
|
||||
|
||||
final customPaint = CustomPaint(
|
||||
painter: BoardPainter(width: width),
|
||||
foregroundPainter: PiecesPainter(
|
||||
width: width,
|
||||
position: gameInstance.position,
|
||||
focusIndex: gameInstance.focusIndex,
|
||||
blurIndex: gameInstance.blurIndex,
|
||||
animationValue: animationValue,
|
||||
),
|
||||
final customPaint = AnimatedBuilder(
|
||||
animation: animation,
|
||||
builder: (_, child) {
|
||||
return CustomPaint(
|
||||
painter: BoardPainter(width: width),
|
||||
foregroundPainter: PiecesPainter(
|
||||
width: width,
|
||||
position: gameInstance.position,
|
||||
focusIndex: gameInstance.focusIndex,
|
||||
blurIndex: gameInstance.blurIndex,
|
||||
animationValue: animation.value,
|
||||
),
|
||||
child: child,
|
||||
);
|
||||
},
|
||||
child: EnvironmentConfig.devMode ? grid : null,
|
||||
);
|
||||
|
||||
|
@ -90,26 +99,31 @@ class Board extends StatelessWidget {
|
|||
|
||||
final column = (dx - padding) ~/ squareWidth;
|
||||
if (column < 0 || column > 6) {
|
||||
debugPrint("$_tag Tap on column $column (ignored).");
|
||||
return;
|
||||
return debugPrint("$_tag Tap on column $column (ignored).");
|
||||
}
|
||||
|
||||
final row = (dy - padding) ~/ squareWidth;
|
||||
if (row < 0 || row > 6) {
|
||||
debugPrint("$_tag Tap on row $row (ignored).");
|
||||
return;
|
||||
return debugPrint("$_tag Tap on row $row (ignored).");
|
||||
}
|
||||
|
||||
final index = row * 7 + column;
|
||||
final int? square = indexToSquare[index];
|
||||
|
||||
if (square == null) {
|
||||
return debugPrint(
|
||||
"$_tag Tap not on a square ($row, $column) (ignored).",
|
||||
);
|
||||
}
|
||||
|
||||
debugPrint("$_tag Tap on ($row, $column) <$index>");
|
||||
|
||||
await onBoardTap(index);
|
||||
await onBoardTap(square);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
void _buildSquareDescription(BuildContext context) {
|
||||
void _buildSquareDescription(BuildContext context, List<String> squareDesc) {
|
||||
final List<String> coordinates = [];
|
||||
final List<String> pieceDesc = [];
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -16,7 +16,6 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
|
|
@ -26,7 +26,6 @@ import 'package:flutter_email_sender/flutter_email_sender.dart';
|
|||
import 'package:package_info_plus/package_info_plus.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:sanmill/generated/intl/l10n.dart';
|
||||
import 'package:sanmill/mill/game.dart';
|
||||
import 'package:sanmill/screens/about_page.dart';
|
||||
import 'package:sanmill/screens/game_page/game_page.dart';
|
||||
import 'package:sanmill/screens/game_settings/game_settings_page.dart';
|
||||
|
@ -63,14 +62,11 @@ class Home extends StatefulWidget {
|
|||
class _HomeState extends State<Home> with TickerProviderStateMixin {
|
||||
final _controller = CustomDrawerController();
|
||||
|
||||
Widget _screenView = const GamePage(EngineType.humanVsAi);
|
||||
_DrawerIndex _drawerIndex = _DrawerIndex.humanVsAi;
|
||||
|
||||
static const Map<_DrawerIndex, Widget> _gamePages = {
|
||||
_DrawerIndex.humanVsAi: GamePage(EngineType.humanVsAi),
|
||||
_DrawerIndex.humanVsHuman: GamePage(EngineType.humanVsHuman),
|
||||
_DrawerIndex.aiVsAi: GamePage(EngineType.aiVsAi),
|
||||
};
|
||||
Widget _screenView = const GamePage(
|
||||
EngineType.humanVsAi,
|
||||
key: Key("Human-Ai"),
|
||||
);
|
||||
|
||||
/// callback from drawer for replace screen
|
||||
/// as user need with passing DrawerIndex (Enum index)
|
||||
|
@ -84,16 +80,22 @@ class _HomeState extends State<Home> with TickerProviderStateMixin {
|
|||
_drawerIndex = index;
|
||||
switch (_drawerIndex) {
|
||||
case _DrawerIndex.humanVsAi:
|
||||
gameInstance.setWhoIsAi(EngineType.humanVsAi);
|
||||
_screenView = _gamePages[_DrawerIndex.humanVsAi]!;
|
||||
_screenView = const GamePage(
|
||||
EngineType.humanVsAi,
|
||||
key: Key("Human-Ai"),
|
||||
);
|
||||
break;
|
||||
case _DrawerIndex.humanVsHuman:
|
||||
gameInstance.setWhoIsAi(EngineType.humanVsHuman);
|
||||
_screenView = _gamePages[_DrawerIndex.humanVsHuman]!;
|
||||
_screenView = const GamePage(
|
||||
EngineType.humanVsHuman,
|
||||
key: Key("Human-Human"),
|
||||
);
|
||||
break;
|
||||
case _DrawerIndex.aiVsAi:
|
||||
gameInstance.setWhoIsAi(EngineType.aiVsAi);
|
||||
_screenView = _gamePages[_DrawerIndex.aiVsAi]!;
|
||||
_screenView = const GamePage(
|
||||
EngineType.aiVsAi,
|
||||
key: Key("Ai-Ai"),
|
||||
);
|
||||
break;
|
||||
case _DrawerIndex.preferences:
|
||||
_screenView = const GameSettingsPage();
|
||||
|
|
|
@ -16,83 +16,17 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:devicelocale/devicelocale.dart';
|
||||
import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:sanmill/generated/intl/l10n.dart';
|
||||
import 'package:sanmill/models/preferences.dart';
|
||||
import 'package:sanmill/services/storage/storage.dart';
|
||||
import 'package:sanmill/shared/constants.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
int _counter = 0;
|
||||
Timer? _timer;
|
||||
|
||||
void startTimer(int counter, StreamController<int> events) {
|
||||
_counter = counter;
|
||||
if (_timer != null) {
|
||||
_timer!.cancel();
|
||||
}
|
||||
_timer = Timer.periodic(const Duration(seconds: 1), (timer) {
|
||||
(_counter > 0) ? _counter-- : _timer!.cancel();
|
||||
events.add(_counter);
|
||||
});
|
||||
}
|
||||
|
||||
void showCountdownDialog(
|
||||
BuildContext ctx,
|
||||
int seconds,
|
||||
StreamController<int> events,
|
||||
void Function() fun,
|
||||
) {
|
||||
final alert = AlertDialog(
|
||||
content: StreamBuilder<int>(
|
||||
stream: events.stream,
|
||||
builder: (BuildContext context, AsyncSnapshot<int> snapshot) {
|
||||
debugPrint("Count down: ${snapshot.data}");
|
||||
|
||||
if (snapshot.data == 0) fun();
|
||||
|
||||
return SizedBox(
|
||||
height: 128,
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
Text(
|
||||
snapshot.data != null ? snapshot.data.toString() : "10",
|
||||
style: const TextStyle(fontSize: 64),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
InkWell(
|
||||
onTap: () => Navigator.pop(context),
|
||||
child: Center(
|
||||
child: Text(
|
||||
S.of(ctx).cancel,
|
||||
style: const TextStyle(
|
||||
color: Colors.black,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
startTimer(seconds, events);
|
||||
|
||||
showDialog(
|
||||
context: ctx,
|
||||
builder: (BuildContext c) {
|
||||
return alert;
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
class _LinkTextSpan extends TextSpan {
|
||||
_LinkTextSpan({TextStyle? style, required String url, String? text})
|
||||
: super(
|
||||
|
@ -105,38 +39,29 @@ class _LinkTextSpan extends TextSpan {
|
|||
);
|
||||
}
|
||||
|
||||
Future<void> showPrivacyDialog(
|
||||
BuildContext context,
|
||||
Function(bool value) setPrivacyPolicyAccepted,
|
||||
) async {
|
||||
Future<void> showPrivacyDialog(BuildContext context) async {
|
||||
assert(Localizations.localeOf(context).languageCode.startsWith("zh_"));
|
||||
|
||||
final ThemeData themeData = Theme.of(context);
|
||||
final TextStyle? aboutTextStyle = themeData.textTheme.bodyText1;
|
||||
final TextStyle linkStyle = themeData.textTheme.bodyText1!
|
||||
.copyWith(color: themeData.colorScheme.secondary);
|
||||
final TextStyle aboutTextStyle = themeData.textTheme.bodyText1!;
|
||||
final TextStyle linkStyle =
|
||||
aboutTextStyle.copyWith(color: themeData.colorScheme.secondary);
|
||||
|
||||
String? locale = "en_US";
|
||||
late String eulaURL;
|
||||
late String privacyPolicyURL;
|
||||
if (!Platform.isWindows) {
|
||||
locale = await Devicelocale.currentLocale;
|
||||
}
|
||||
const String eulaURL = Constants.giteeEulaURL;
|
||||
const String privacyPolicyURL = Constants.giteePrivacyPolicyURL;
|
||||
|
||||
debugPrint("[about] local = $locale");
|
||||
if (locale != null && locale.startsWith("zh_")) {
|
||||
eulaURL = Constants.giteeEulaURL;
|
||||
privacyPolicyURL = Constants.giteePrivacyPolicyURL;
|
||||
} else {
|
||||
eulaURL = Constants.githubEulaURL;
|
||||
privacyPolicyURL = Constants.githubPrivacyPolicyURL;
|
||||
Future<void> _setPrivacyPolicyAccepted({required bool value}) async {
|
||||
LocalDatabaseService.preferences = LocalDatabaseService.preferences
|
||||
.copyWith(isPrivacyPolicyAccepted: value);
|
||||
|
||||
debugPrint("[config] isPrivacyPolicyAccepted: $value");
|
||||
}
|
||||
|
||||
showDialog(
|
||||
context: context,
|
||||
barrierDismissible: false,
|
||||
builder: (context) => AlertDialog(
|
||||
title: Text(
|
||||
S.of(context).privacyPolicy,
|
||||
),
|
||||
title: Text(S.of(context).privacyPolicy),
|
||||
content: RichText(
|
||||
text: TextSpan(
|
||||
children: <TextSpan>[
|
||||
|
@ -169,7 +94,7 @@ Future<void> showPrivacyDialog(
|
|||
TextButton(
|
||||
child: Text(S.of(context).accept),
|
||||
onPressed: () {
|
||||
setPrivacyPolicyAccepted(true);
|
||||
_setPrivacyPolicyAccepted(value: true);
|
||||
Navigator.pop(context);
|
||||
},
|
||||
),
|
||||
|
@ -177,7 +102,7 @@ Future<void> showPrivacyDialog(
|
|||
TextButton(
|
||||
child: Text(S.of(context).exit),
|
||||
onPressed: () {
|
||||
setPrivacyPolicyAccepted(false);
|
||||
_setPrivacyPolicyAccepted(value: false);
|
||||
SystemChannels.platform.invokeMethod('SystemNavigator.pop');
|
||||
},
|
||||
),
|
||||
|
|
Loading…
Reference in New Issue