Merge remote-tracking branch 'Leptopoda/linting'
This commit is contained in:
commit
e7f7b6cf6f
|
@ -32,6 +32,7 @@ class Game {
|
|||
final String tag = "[game]";
|
||||
|
||||
void init() {
|
||||
// TODO: _position is allready initialized with Position(). seems like duplicate code
|
||||
_position = Position();
|
||||
focusIndex = blurIndex = invalidIndex;
|
||||
}
|
||||
|
@ -71,7 +72,7 @@ class Game {
|
|||
PieceColor.black: false
|
||||
};
|
||||
|
||||
bool aiIsSearching() {
|
||||
bool get aiIsSearching {
|
||||
debugPrint(
|
||||
"$tag White is searching? ${isSearching[PieceColor.white]}\n"
|
||||
"$tag Black is searching? ${isSearching[PieceColor.black]}\n",
|
||||
|
@ -115,14 +116,14 @@ class Game {
|
|||
blurIndex = invalidIndex;
|
||||
}
|
||||
|
||||
bool doMove(String move) {
|
||||
Future<bool> doMove(String move) async {
|
||||
if (position.phase == Phase.ready) {
|
||||
start();
|
||||
}
|
||||
|
||||
debugPrint("$tag AI do move: $move");
|
||||
|
||||
if (!position.doMove(move)) {
|
||||
if (await position.doMove(move) == false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@ import 'package:sanmill/mill/types.dart';
|
|||
import 'package:sanmill/mill/zobrist.dart';
|
||||
import 'package:sanmill/services/audios.dart';
|
||||
import 'package:sanmill/services/engine/engine.dart';
|
||||
import 'package:sanmill/shared/common/config.dart';
|
||||
|
||||
List<int> posKeyHistory = [];
|
||||
|
||||
|
@ -62,7 +63,7 @@ class Position {
|
|||
|
||||
String us = PieceColor.white;
|
||||
String them = PieceColor.black;
|
||||
String winner = PieceColor.nobody;
|
||||
String _winner = PieceColor.nobody;
|
||||
|
||||
GameOverReason gameOverReason = GameOverReason.noReason;
|
||||
|
||||
|
@ -114,7 +115,7 @@ class Position {
|
|||
st.pliesFromNull = other.st.pliesFromNull;
|
||||
|
||||
them = other.them;
|
||||
winner = other.winner;
|
||||
_winner = other._winner;
|
||||
gameOverReason = other.gameOverReason;
|
||||
|
||||
phase = other.phase;
|
||||
|
@ -135,7 +136,7 @@ class Position {
|
|||
|
||||
String movedPiece(int move) => pieceOn(fromSq(move));
|
||||
|
||||
bool movePiece(int from, int to) {
|
||||
Future<bool> movePiece(int from, int to) async {
|
||||
if (selectPiece(from) == 0) {
|
||||
return putPiece(to);
|
||||
}
|
||||
|
@ -261,7 +262,7 @@ class Position {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool doMove(String move) {
|
||||
Future<bool> doMove(String move) async {
|
||||
if (move.length > "Player".length &&
|
||||
move.substring(0, "Player".length - 1) == "Player") {
|
||||
if (move["Player".length] == '1') {
|
||||
|
@ -278,7 +279,7 @@ class Position {
|
|||
|
||||
if (move == "draw") {
|
||||
phase = Phase.gameOver;
|
||||
winner = PieceColor.draw;
|
||||
_winner = PieceColor.draw;
|
||||
if (score[PieceColor.draw] != null) {
|
||||
score[PieceColor.draw] = score[PieceColor.draw]! + 1;
|
||||
}
|
||||
|
@ -287,7 +288,7 @@ class Position {
|
|||
if (rule.nMoveRule > 0 && posKeyHistory.length >= rule.nMoveRule - 1) {
|
||||
gameOverReason = GameOverReason.drawReasonRule50;
|
||||
} else if (rule.endgameNMoveRule < rule.nMoveRule &&
|
||||
isThreeEndgame() &&
|
||||
isThreeEndgame &&
|
||||
posKeyHistory.length >= rule.endgameNMoveRule - 1) {
|
||||
gameOverReason = GameOverReason.drawReasonEndgameRule50;
|
||||
} else if (rule.threefoldRepetitionRule) {
|
||||
|
@ -308,20 +309,20 @@ class Position {
|
|||
|
||||
switch (m.type) {
|
||||
case MoveType.remove:
|
||||
ret = removePiece(m.to) == 0;
|
||||
ret = await removePiece(m.to) == 0;
|
||||
if (ret) {
|
||||
// Reset rule 50 counter
|
||||
st.rule50 = 0;
|
||||
}
|
||||
break;
|
||||
case MoveType.move:
|
||||
ret = movePiece(m.from, m.to);
|
||||
ret = await movePiece(m.from, m.to);
|
||||
if (ret) {
|
||||
++st.rule50;
|
||||
}
|
||||
break;
|
||||
case MoveType.place:
|
||||
ret = putPiece(m.to);
|
||||
ret = await putPiece(m.to);
|
||||
if (ret) {
|
||||
// Reset rule 50 counter
|
||||
st.rule50 = 0;
|
||||
|
@ -414,7 +415,7 @@ class Position {
|
|||
setSideToMove(PieceColor.white);
|
||||
action = Act.place;
|
||||
|
||||
winner = PieceColor.nobody;
|
||||
_winner = PieceColor.nobody;
|
||||
gameOverReason = GameOverReason.noReason;
|
||||
|
||||
clearBoard();
|
||||
|
@ -457,7 +458,7 @@ class Position {
|
|||
}
|
||||
}
|
||||
|
||||
bool putPiece(int s) {
|
||||
Future<bool> putPiece(int s) async {
|
||||
var piece = Piece.noPiece;
|
||||
final us = _sideToMove;
|
||||
|
||||
|
@ -472,6 +473,7 @@ class Position {
|
|||
start();
|
||||
}
|
||||
|
||||
// TODO: use switch case
|
||||
if (phase == Phase.placing) {
|
||||
piece = sideToMove;
|
||||
if (pieceInHandCount[us] != null) {
|
||||
|
@ -501,7 +503,7 @@ class Position {
|
|||
|
||||
if (pieceInHandCount[PieceColor.white] == 0 &&
|
||||
pieceInHandCount[PieceColor.black] == 0) {
|
||||
if (checkIfGameIsOver()) {
|
||||
if (isGameOver()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -516,14 +518,14 @@ class Position {
|
|||
changeSideToMove();
|
||||
}
|
||||
|
||||
if (checkIfGameIsOver()) {
|
||||
if (isGameOver()) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
changeSideToMove();
|
||||
}
|
||||
gameInstance.focusIndex = squareToIndex[s] ?? invalidIndex;
|
||||
Audios.playTone(Audios.placeSoundId);
|
||||
await Audios.playTone(Sound.place);
|
||||
} else {
|
||||
pieceToRemoveCount = rule.mayRemoveMultiple ? n : 1;
|
||||
updateKeyMisc();
|
||||
|
@ -539,7 +541,7 @@ class Position {
|
|||
|
||||
if (pieceInHandCount[PieceColor.white] == 0 &&
|
||||
pieceInHandCount[PieceColor.black] == 0) {
|
||||
if (checkIfGameIsOver()) {
|
||||
if (isGameOver()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -550,7 +552,7 @@ class Position {
|
|||
changeSideToMove();
|
||||
}
|
||||
|
||||
if (checkIfGameIsOver()) {
|
||||
if (isGameOver()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -559,10 +561,10 @@ class Position {
|
|||
}
|
||||
|
||||
gameInstance.focusIndex = squareToIndex[s] ?? invalidIndex;
|
||||
Audios.playTone(Audios.millSoundId);
|
||||
await Audios.playTone(Sound.mill);
|
||||
}
|
||||
} else if (phase == Phase.moving) {
|
||||
if (checkIfGameIsOver()) {
|
||||
if (isGameOver()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -603,18 +605,18 @@ class Position {
|
|||
action = Act.select;
|
||||
changeSideToMove();
|
||||
|
||||
if (checkIfGameIsOver()) {
|
||||
if (isGameOver()) {
|
||||
return true;
|
||||
} else {
|
||||
gameInstance.focusIndex = squareToIndex[s] ?? invalidIndex;
|
||||
Audios.playTone(Audios.placeSoundId);
|
||||
await Audios.playTone(Sound.place);
|
||||
}
|
||||
} else {
|
||||
pieceToRemoveCount = rule.mayRemoveMultiple ? n : 1;
|
||||
updateKeyMisc();
|
||||
action = Act.remove;
|
||||
gameInstance.focusIndex = squareToIndex[s] ?? invalidIndex;
|
||||
Audios.playTone(Audios.millSoundId);
|
||||
await Audios.playTone(Sound.mill);
|
||||
}
|
||||
} else {
|
||||
assert(false);
|
||||
|
@ -623,7 +625,7 @@ class Position {
|
|||
return true;
|
||||
}
|
||||
|
||||
int removePiece(int s) {
|
||||
Future<int> removePiece(int s) async {
|
||||
if (phase == Phase.ready || phase == Phase.gameOver) return -1;
|
||||
|
||||
if (action != Act.remove) return -1;
|
||||
|
@ -641,7 +643,7 @@ class Position {
|
|||
|
||||
revertKey(s);
|
||||
|
||||
Audios.playTone(Audios.removeSoundId);
|
||||
await Audios.playTone(Sound.remove);
|
||||
|
||||
if (rule.hasBannedLocations && phase == Phase.placing) {
|
||||
// Remove and put ban
|
||||
|
@ -685,7 +687,7 @@ class Position {
|
|||
}
|
||||
|
||||
if (rule.isDefenderMoveFirst) {
|
||||
checkIfGameIsOver();
|
||||
isGameOver();
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
|
@ -696,7 +698,7 @@ class Position {
|
|||
}
|
||||
|
||||
changeSideToMove();
|
||||
checkIfGameIsOver();
|
||||
isGameOver();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -733,14 +735,12 @@ class Position {
|
|||
return true;
|
||||
}
|
||||
|
||||
String getWinner() {
|
||||
return winner;
|
||||
}
|
||||
String get winner => _winner;
|
||||
|
||||
void setGameOver(String w, GameOverReason reason) {
|
||||
phase = Phase.gameOver;
|
||||
gameOverReason = reason;
|
||||
winner = w;
|
||||
_winner = w;
|
||||
|
||||
debugPrint("[position] Game over, $w win, because of $reason");
|
||||
updateScore();
|
||||
|
@ -748,7 +748,7 @@ class Position {
|
|||
|
||||
void updateScore() {
|
||||
if (phase == Phase.gameOver) {
|
||||
if (winner == PieceColor.draw) {
|
||||
if (_winner == PieceColor.draw) {
|
||||
if (score[PieceColor.draw] != null) {
|
||||
score[PieceColor.draw] = score[PieceColor.draw]! + 1;
|
||||
}
|
||||
|
@ -756,13 +756,13 @@ class Position {
|
|||
return;
|
||||
}
|
||||
|
||||
if (score[winner] != null) {
|
||||
score[winner] = score[winner]! + 1;
|
||||
if (score[_winner] != null) {
|
||||
score[_winner] = score[_winner]! + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool isThreeEndgame() {
|
||||
bool get isThreeEndgame {
|
||||
if (phase == Phase.placing) {
|
||||
return false;
|
||||
}
|
||||
|
@ -771,7 +771,7 @@ class Position {
|
|||
pieceOnBoardCount[PieceColor.black] == 3;
|
||||
}
|
||||
|
||||
bool checkIfGameIsOver() {
|
||||
bool isGameOver() {
|
||||
if (phase == Phase.ready || phase == Phase.gameOver) {
|
||||
return true;
|
||||
}
|
||||
|
@ -782,7 +782,7 @@ class Position {
|
|||
}
|
||||
|
||||
if (rule.endgameNMoveRule < rule.nMoveRule &&
|
||||
isThreeEndgame() &&
|
||||
isThreeEndgame &&
|
||||
posKeyHistory.length >= rule.endgameNMoveRule) {
|
||||
setGameOver(PieceColor.draw, GameOverReason.drawReasonEndgameRule50);
|
||||
return true;
|
||||
|
@ -1047,7 +1047,7 @@ class Position {
|
|||
nPiecesInHand;
|
||||
pieceToRemoveCount = 0;
|
||||
|
||||
winner = PieceColor.nobody;
|
||||
_winner = PieceColor.nobody;
|
||||
Mills.adjacentSquaresInit();
|
||||
Mills.millTableInit();
|
||||
currentSquare = 0;
|
||||
|
@ -1086,25 +1086,29 @@ class Position {
|
|||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Future<String> _gotoHistory(int moveIndex) async {
|
||||
String errString = "";
|
||||
Future<String> gotoHistory(HistoryMove move, [int? index]) async {
|
||||
final int moveIndex = _gotoHistoryIndex(move, index);
|
||||
|
||||
if (recorder == null) {
|
||||
debugPrint("[goto] recorder is null.");
|
||||
return "null";
|
||||
}
|
||||
|
||||
if (recorder!.cur == moveIndex) {
|
||||
debugPrint("[goto] cur is equal to moveIndex.");
|
||||
return "equal";
|
||||
}
|
||||
|
||||
final history = recorder!.history;
|
||||
|
||||
if (moveIndex < -1 || history.length <= moveIndex) {
|
||||
debugPrint("[goto] moveIndex is out of range.");
|
||||
return "out-of-range";
|
||||
}
|
||||
|
||||
if (recorder!.cur == moveIndex) {
|
||||
debugPrint("[goto] cur is equal to moveIndex.");
|
||||
return "equal";
|
||||
}
|
||||
String errString = "";
|
||||
|
||||
Audios.isTemporaryMute = true;
|
||||
|
||||
// Backup context
|
||||
final engineTypeBackup = gameInstance.engineType;
|
||||
|
@ -1121,13 +1125,10 @@ class Position {
|
|||
}
|
||||
|
||||
for (var i = 0; i <= moveIndex; i++) {
|
||||
if (gameInstance.doMove(history[i].move!) == false) {
|
||||
if (await gameInstance.doMove(history[i].move!) == false) {
|
||||
errString = history[i].move!;
|
||||
break;
|
||||
}
|
||||
|
||||
//await Future.delayed(Duration(seconds: 1));
|
||||
//setState(() {});
|
||||
}
|
||||
|
||||
// Restore context
|
||||
|
@ -1136,31 +1137,46 @@ class Position {
|
|||
recorder!.history = historyBack;
|
||||
recorder!.cur = moveIndex;
|
||||
|
||||
Audios.isTemporaryMute = false;
|
||||
await _gotoHistoryPlaySound(move);
|
||||
|
||||
return errString;
|
||||
}
|
||||
|
||||
Future<String> takeBackN(int n) async {
|
||||
int index = recorder!.cur - n;
|
||||
if (index < -1) {
|
||||
index = -1;
|
||||
int _gotoHistoryIndex(HistoryMove move, [int? index]) {
|
||||
switch (move) {
|
||||
case HistoryMove.forwardAll:
|
||||
return recorder!.history.length - 1;
|
||||
case HistoryMove.backAll:
|
||||
return -1;
|
||||
case HistoryMove.farward:
|
||||
return recorder!.cur + 1;
|
||||
case HistoryMove.backN:
|
||||
assert(index != null);
|
||||
int _index = recorder!.cur - index!;
|
||||
if (_index < -1) {
|
||||
_index = -1;
|
||||
}
|
||||
return _index;
|
||||
case HistoryMove.backOne:
|
||||
return recorder!.cur - 1;
|
||||
}
|
||||
return _gotoHistory(index);
|
||||
}
|
||||
|
||||
Future<String> takeBack() async {
|
||||
return _gotoHistory(recorder!.cur - 1);
|
||||
}
|
||||
Future<void> _gotoHistoryPlaySound(HistoryMove move) async {
|
||||
if (!Config.keepMuteWhenTakingBack) {
|
||||
switch (move) {
|
||||
case HistoryMove.forwardAll:
|
||||
case HistoryMove.farward:
|
||||
await Audios.playTone(Sound.place);
|
||||
break;
|
||||
case HistoryMove.backAll:
|
||||
case HistoryMove.backN:
|
||||
|
||||
Future<String> stepForward() async {
|
||||
return _gotoHistory(recorder!.cur + 1);
|
||||
}
|
||||
|
||||
Future<String> takeBackAll() async {
|
||||
return _gotoHistory(-1);
|
||||
}
|
||||
|
||||
Future<String> stepForwardAll() async {
|
||||
return _gotoHistory(recorder!.history.length - 1);
|
||||
case HistoryMove.backOne:
|
||||
await Audios.playTone(Sound.remove);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String movesSinceLastRemove() {
|
||||
|
@ -1205,3 +1221,5 @@ class Position {
|
|||
|
||||
String? get lastPositionWithRemove => recorder!.lastPositionWithRemove;
|
||||
}
|
||||
|
||||
enum HistoryMove { forwardAll, backAll, farward, backN, backOne }
|
||||
|
|
|
@ -172,7 +172,7 @@ class _GamePageState extends State<GamePage>
|
|||
}
|
||||
}
|
||||
|
||||
dynamic onBoardTap(int index) {
|
||||
Future<dynamic> onBoardTap(int index) async {
|
||||
if (!isReady) {
|
||||
debugPrint("[tap] Not ready, ignore tapping.");
|
||||
return false;
|
||||
|
@ -208,18 +208,18 @@ class _GamePageState extends State<GamePage>
|
|||
gameInstance.newGame();
|
||||
|
||||
if (gameInstance.isAiToMove) {
|
||||
if (gameInstance.aiIsSearching()) {
|
||||
if (gameInstance.aiIsSearching) {
|
||||
debugPrint("$tag AI is thinking, skip tapping.");
|
||||
return false;
|
||||
} else {
|
||||
debugPrint("[tap] AI is not thinking. AI is to move.");
|
||||
engineToGo(false);
|
||||
await engineToGo(false);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (gameInstance.isAiToMove || gameInstance.aiIsSearching()) {
|
||||
if (gameInstance.isAiToMove || gameInstance.aiIsSearching) {
|
||||
debugPrint("[tap] AI's turn, skip tapping.");
|
||||
return false;
|
||||
}
|
||||
|
@ -231,12 +231,12 @@ class _GamePageState extends State<GamePage>
|
|||
// Human to go
|
||||
|
||||
bool ret = false;
|
||||
Chain.capture(() {
|
||||
await Chain.capture(() async {
|
||||
switch (position.action) {
|
||||
case Act.place:
|
||||
if (position.putPiece(sq)) {
|
||||
if (await position.putPiece(sq)) {
|
||||
if (position.action == Act.remove) {
|
||||
//Audios.playTone(Audios.millSoundId);
|
||||
//Audios.playTone(Audios.mill);
|
||||
if (mounted) {
|
||||
showTip(S.of(context).tipMill);
|
||||
if (Config.screenReaderSupport) {
|
||||
|
@ -244,7 +244,7 @@ class _GamePageState extends State<GamePage>
|
|||
}
|
||||
}
|
||||
} else {
|
||||
//Audios.playTone(Audios.placeSoundId);
|
||||
//Audios.playTone(Audios.place);
|
||||
if (gameInstance.engineType == EngineType.humanVsAi && mounted) {
|
||||
if (rule.mayOnlyRemoveUnplacedPieceInPlacingPhase) {
|
||||
showTip(S.of(context).continueToMakeMove);
|
||||
|
@ -296,7 +296,7 @@ class _GamePageState extends State<GamePage>
|
|||
final int selectRet = position.selectPiece(sq);
|
||||
switch (selectRet) {
|
||||
case 0:
|
||||
Audios.playTone(Audios.selectSoundId);
|
||||
await Audios.playTone(Sound.select);
|
||||
gameInstance.select(index);
|
||||
ret = true;
|
||||
debugPrint("[tap] selectPiece: [$sq]");
|
||||
|
@ -325,7 +325,7 @@ class _GamePageState extends State<GamePage>
|
|||
|
||||
break;
|
||||
case -2:
|
||||
Audios.playTone(Audios.illegalSoundId);
|
||||
await Audios.playTone(Sound.illegal);
|
||||
debugPrint("[tap] selectPiece: skip [$sq]");
|
||||
if (mounted && position.phase != Phase.gameOver) {
|
||||
showTip(S.of(context).tipCannotMove);
|
||||
|
@ -336,7 +336,7 @@ class _GamePageState extends State<GamePage>
|
|||
}
|
||||
break;
|
||||
case -3:
|
||||
Audios.playTone(Audios.illegalSoundId);
|
||||
await Audios.playTone(Sound.illegal);
|
||||
debugPrint("[tap] selectPiece: skip [$sq]");
|
||||
if (mounted) {
|
||||
showTip(S.of(context).tipCanMoveOnePoint);
|
||||
|
@ -347,7 +347,7 @@ class _GamePageState extends State<GamePage>
|
|||
}
|
||||
break;
|
||||
case -4:
|
||||
Audios.playTone(Audios.illegalSoundId);
|
||||
await Audios.playTone(Sound.illegal);
|
||||
debugPrint("[tap] selectPiece: skip [$sq]");
|
||||
if (mounted) {
|
||||
showTip(S.of(context).tipSelectPieceToMove);
|
||||
|
@ -357,7 +357,7 @@ class _GamePageState extends State<GamePage>
|
|||
}
|
||||
break;
|
||||
default:
|
||||
Audios.playTone(Audios.illegalSoundId);
|
||||
await Audios.playTone(Sound.illegal);
|
||||
debugPrint("[tap] selectPiece: skip [$sq]");
|
||||
if (mounted) {
|
||||
showTip(S.of(context).tipSelectWrong);
|
||||
|
@ -372,11 +372,11 @@ class _GamePageState extends State<GamePage>
|
|||
break;
|
||||
|
||||
case Act.remove:
|
||||
final int removeRet = position.removePiece(sq);
|
||||
final int removeRet = await position.removePiece(sq);
|
||||
|
||||
switch (removeRet) {
|
||||
case 0:
|
||||
//Audios.playTone(Audios.removeSoundId);
|
||||
//Audios.playTone(Audios.remove);
|
||||
ret = true;
|
||||
debugPrint("[tap] removePiece: [$sq]");
|
||||
if (gameInstance.position.pieceToRemoveCount >= 1) {
|
||||
|
@ -404,7 +404,7 @@ class _GamePageState extends State<GamePage>
|
|||
}
|
||||
break;
|
||||
case -2:
|
||||
Audios.playTone(Audios.illegalSoundId);
|
||||
await Audios.playTone(Sound.illegal);
|
||||
debugPrint(
|
||||
"[tap] removePiece: Cannot Remove our pieces, skip [$sq]",
|
||||
);
|
||||
|
@ -416,7 +416,7 @@ class _GamePageState extends State<GamePage>
|
|||
}
|
||||
break;
|
||||
case -3:
|
||||
Audios.playTone(Audios.illegalSoundId);
|
||||
await Audios.playTone(Sound.illegal);
|
||||
debugPrint(
|
||||
"[tap] removePiece: Cannot remove piece from Mill, skip [$sq]",
|
||||
);
|
||||
|
@ -432,7 +432,7 @@ class _GamePageState extends State<GamePage>
|
|||
}
|
||||
break;
|
||||
default:
|
||||
Audios.playTone(Audios.illegalSoundId);
|
||||
await Audios.playTone(Sound.illegal);
|
||||
debugPrint("[tap] removePiece: skip [$sq]");
|
||||
if (mounted && position.phase != Phase.gameOver) {
|
||||
showTip(S.of(context).tipBanRemove);
|
||||
|
@ -587,7 +587,7 @@ class _GamePageState extends State<GamePage>
|
|||
);
|
||||
}
|
||||
|
||||
gameInstance.doMove(move.move!);
|
||||
await gameInstance.doMove(move.move!);
|
||||
showTips();
|
||||
if (Config.screenReaderSupport && move.notation != null) {
|
||||
showSnackBar(context, "${S.of(context).ai}: ${move.notation!}");
|
||||
|
@ -657,7 +657,7 @@ class _GamePageState extends State<GamePage>
|
|||
debugPrint("Clipboard text:");
|
||||
debugPrint(text);
|
||||
|
||||
await onTakeBackAllButtonPressed(pop: false);
|
||||
await onTakeBackAllButtonPressed(false);
|
||||
gameInstance.position.recorder!.clear();
|
||||
final importFailedStr = gameInstance.position.recorder!.import(text);
|
||||
|
||||
|
@ -670,7 +670,7 @@ class _GamePageState extends State<GamePage>
|
|||
return;
|
||||
}
|
||||
|
||||
await onStepForwardAllButtonPressed(pop: false);
|
||||
await onStepForwardAllButtonPressed(false);
|
||||
|
||||
showTip(S.of(context).gameImported);
|
||||
if (Config.screenReaderSupport) {
|
||||
|
@ -774,8 +774,8 @@ class _GamePageState extends State<GamePage>
|
|||
Future<void> onAutoReplayButtonPressed() async {
|
||||
Navigator.pop(context);
|
||||
|
||||
await onTakeBackAllButtonPressed(pop: false);
|
||||
await onStepForwardAllButtonPressed(pop: false);
|
||||
await onTakeBackAllButtonPressed(false);
|
||||
await onStepForwardAllButtonPressed(false);
|
||||
}
|
||||
|
||||
void onGameButtonPressed() {
|
||||
|
@ -969,8 +969,9 @@ class _GamePageState extends State<GamePage>
|
|||
}
|
||||
|
||||
Future<void> onGotoHistoryButtonsPressed(
|
||||
Future<String> func, {
|
||||
HistoryMove move, {
|
||||
bool pop = true,
|
||||
int? number,
|
||||
}) async {
|
||||
if (pop == true) {
|
||||
Navigator.pop(context);
|
||||
|
@ -989,9 +990,7 @@ class _GamePageState extends State<GamePage>
|
|||
|
||||
isGoingToHistory = true;
|
||||
|
||||
Audios.isTemporaryMute = Config.keepMuteWhenTakingBack;
|
||||
|
||||
final errMove = await func;
|
||||
final errMove = await gameInstance.position.gotoHistory(move, number);
|
||||
|
||||
switch (errMove) {
|
||||
case "":
|
||||
|
@ -1008,14 +1007,10 @@ class _GamePageState extends State<GamePage>
|
|||
break;
|
||||
}
|
||||
|
||||
Audios.isTemporaryMute = false;
|
||||
|
||||
isGoingToHistory = false;
|
||||
|
||||
if (mounted) {
|
||||
String text = "";
|
||||
final pos = gameInstance.position;
|
||||
|
||||
/*
|
||||
String us = "";
|
||||
String them = "";
|
||||
|
@ -1028,6 +1023,7 @@ class _GamePageState extends State<GamePage>
|
|||
}
|
||||
*/
|
||||
|
||||
late final String text;
|
||||
final lastEffectiveMove = pos.recorder!.lastEffectiveMove;
|
||||
if (lastEffectiveMove != null && lastEffectiveMove.notation != null) {
|
||||
text = "${S.of(context).lastMove}: ${lastEffectiveMove.notation}";
|
||||
|
@ -1044,28 +1040,37 @@ class _GamePageState extends State<GamePage>
|
|||
}
|
||||
}
|
||||
|
||||
Future<void> onTakeBackButtonPressed({bool pop = true}) async {
|
||||
onGotoHistoryButtonsPressed(gameInstance.position.takeBack(), pop: pop);
|
||||
}
|
||||
Future<void> onTakeBackButtonPressed([bool pop = true]) async =>
|
||||
onGotoHistoryButtonsPressed(
|
||||
HistoryMove.backOne,
|
||||
pop: pop,
|
||||
);
|
||||
|
||||
Future<void> onStepForwardButtonPressed({bool pop = true}) async {
|
||||
onGotoHistoryButtonsPressed(gameInstance.position.stepForward(), pop: pop);
|
||||
}
|
||||
Future<void> onStepForwardButtonPressed([bool pop = true]) async =>
|
||||
onGotoHistoryButtonsPressed(
|
||||
HistoryMove.farward,
|
||||
pop: pop,
|
||||
);
|
||||
|
||||
Future<void> onTakeBackAllButtonPressed({bool pop = true}) async {
|
||||
onGotoHistoryButtonsPressed(gameInstance.position.takeBackAll(), pop: pop);
|
||||
}
|
||||
Future<void> onTakeBackAllButtonPressed([bool pop = true]) async =>
|
||||
onGotoHistoryButtonsPressed(
|
||||
HistoryMove.backAll,
|
||||
pop: pop,
|
||||
);
|
||||
|
||||
Future<void> onStepForwardAllButtonPressed({bool pop = true}) async {
|
||||
Future<void> onStepForwardAllButtonPressed([bool pop = true]) async {
|
||||
onGotoHistoryButtonsPressed(
|
||||
gameInstance.position.stepForwardAll(),
|
||||
HistoryMove.forwardAll,
|
||||
pop: pop,
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> onTakeBackNButtonPressed(int n, {bool pop = true}) async {
|
||||
onGotoHistoryButtonsPressed(gameInstance.position.takeBackN(n), pop: pop);
|
||||
}
|
||||
Future<void> onTakeBackNButtonPressed(int n, [bool pop = true]) async =>
|
||||
onGotoHistoryButtonsPressed(
|
||||
HistoryMove.backN,
|
||||
number: n,
|
||||
pop: pop,
|
||||
);
|
||||
|
||||
void onMoveListButtonPressed() {
|
||||
final moveHistoryText = gameInstance.position.moveHistoryText;
|
||||
|
@ -1177,7 +1182,7 @@ class _GamePageState extends State<GamePage>
|
|||
|
||||
debugPrint("[config] isPrivacyPolicyAccepted: $value");
|
||||
|
||||
Config.save();
|
||||
await Config.save();
|
||||
}
|
||||
|
||||
Future<void> onShowPrivacyDialog() async {
|
||||
|
@ -1259,10 +1264,10 @@ class _GamePageState extends State<GamePage>
|
|||
|
||||
switch (result) {
|
||||
case GameResult.win:
|
||||
//Audios.playTone(Audios.winSoundId);
|
||||
//Audios.playTone(Audios.win);
|
||||
break;
|
||||
case GameResult.lose:
|
||||
//Audios.playTone(Audios.loseSoundId);
|
||||
//Audios.playTone(Audios.lose);
|
||||
break;
|
||||
case GameResult.draw:
|
||||
break;
|
||||
|
@ -1694,7 +1699,7 @@ class _GamePageState extends State<GamePage>
|
|||
color: Color(Config.navigationToolbarIconColor),
|
||||
),
|
||||
),
|
||||
onPressed: () => onTakeBackAllButtonPressed(pop: false),
|
||||
onPressed: () => onTakeBackAllButtonPressed(false),
|
||||
);
|
||||
|
||||
final takeBackButton = TextButton(
|
||||
|
@ -1707,7 +1712,7 @@ class _GamePageState extends State<GamePage>
|
|||
color: Color(Config.navigationToolbarIconColor),
|
||||
),
|
||||
),
|
||||
onPressed: () => onTakeBackButtonPressed(pop: false),
|
||||
onPressed: () async => onTakeBackButtonPressed(false),
|
||||
);
|
||||
|
||||
final stepForwardButton = TextButton(
|
||||
|
@ -1720,7 +1725,7 @@ class _GamePageState extends State<GamePage>
|
|||
color: Color(Config.navigationToolbarIconColor),
|
||||
),
|
||||
),
|
||||
onPressed: () => onStepForwardButtonPressed(pop: false),
|
||||
onPressed: () async => onStepForwardButtonPressed(false),
|
||||
);
|
||||
|
||||
final stepForwardAllButton = TextButton(
|
||||
|
@ -1733,7 +1738,7 @@ class _GamePageState extends State<GamePage>
|
|||
color: Color(Config.navigationToolbarIconColor),
|
||||
),
|
||||
),
|
||||
onPressed: () => onStepForwardAllButtonPressed(pop: false),
|
||||
onPressed: () async => onStepForwardAllButtonPressed(false),
|
||||
);
|
||||
|
||||
return GamePageToolBar(
|
||||
|
|
|
@ -24,22 +24,35 @@ import 'package:sanmill/shared/common/config.dart';
|
|||
import 'package:soundpool/soundpool.dart';
|
||||
import 'package:stack_trace/stack_trace.dart';
|
||||
|
||||
enum Sound {
|
||||
draw,
|
||||
fly,
|
||||
go,
|
||||
illegal,
|
||||
lose,
|
||||
mill,
|
||||
place,
|
||||
remove,
|
||||
select,
|
||||
win,
|
||||
}
|
||||
|
||||
class Audios {
|
||||
const Audios._();
|
||||
//static AudioPlayer? _player;
|
||||
static Soundpool? _soundpool;
|
||||
// TODO: use enum for the sounds
|
||||
static int? _alarmSoundStreamId;
|
||||
static int? drawSoundId;
|
||||
static int? flySoundId;
|
||||
static int? goSoundId;
|
||||
static int? illegalSoundId;
|
||||
static int? loseSoundId;
|
||||
static int? millSoundId;
|
||||
static int? placeSoundId;
|
||||
static int? removeSoundId;
|
||||
static int? selectSoundId;
|
||||
static int? winSoundId;
|
||||
static final Soundpool _soundpool = Soundpool.fromOptions();
|
||||
static bool _initialized = false;
|
||||
static int _alarmSoundStreamId = 0;
|
||||
static late final int _drawSoundId;
|
||||
static late final int _flySoundId;
|
||||
static late final int _goSoundId;
|
||||
static late final int _illegalSoundId;
|
||||
static late final int _loseSoundId;
|
||||
static late final int _millSoundId;
|
||||
static late final int _placeSoundId;
|
||||
static late final int _removeSoundId;
|
||||
static late final int _selectSoundId;
|
||||
static late final int _winSoundId;
|
||||
static bool isTemporaryMute = false;
|
||||
|
||||
static Future<void> loadSounds() async {
|
||||
|
@ -48,156 +61,110 @@ class Audios {
|
|||
return;
|
||||
}
|
||||
|
||||
_soundpool ??= Soundpool.fromOptions();
|
||||
_drawSoundId = await _soundpool.load(
|
||||
await rootBundle.load("assets/audios/draw.mp3"),
|
||||
);
|
||||
|
||||
if (_soundpool == null) {
|
||||
if (Config.developerMode) {
|
||||
assert(false);
|
||||
}
|
||||
debugPrint("[audio] Error: _soundpool is null.");
|
||||
return;
|
||||
}
|
||||
_flySoundId = await _soundpool.load(
|
||||
await rootBundle.load("assets/audios/fly.mp3"),
|
||||
);
|
||||
|
||||
drawSoundId ??=
|
||||
await _soundpool!.load(await rootBundle.load("assets/audios/draw.mp3"));
|
||||
if (drawSoundId == null) {
|
||||
if (Config.developerMode) {
|
||||
assert(false);
|
||||
}
|
||||
debugPrint("[audio] Error: drawSoundId is null.");
|
||||
return;
|
||||
}
|
||||
_goSoundId = await _soundpool.load(
|
||||
await rootBundle.load("assets/audios/go.mp3"),
|
||||
);
|
||||
|
||||
flySoundId ??=
|
||||
await _soundpool!.load(await rootBundle.load("assets/audios/fly.mp3"));
|
||||
if (flySoundId == null) {
|
||||
if (Config.developerMode) {
|
||||
assert(false);
|
||||
}
|
||||
debugPrint("[audio] Error: flySoundId is null.");
|
||||
return;
|
||||
}
|
||||
_illegalSoundId = await _soundpool.load(
|
||||
await rootBundle.load("assets/audios/illegal.mp3"),
|
||||
);
|
||||
|
||||
goSoundId ??=
|
||||
await _soundpool!.load(await rootBundle.load("assets/audios/go.mp3"));
|
||||
if (goSoundId == null) {
|
||||
if (Config.developerMode) {
|
||||
assert(false);
|
||||
}
|
||||
debugPrint("[audio] Error: goSoundId is null.");
|
||||
return;
|
||||
}
|
||||
_loseSoundId = await _soundpool.load(
|
||||
await rootBundle.load("assets/audios/lose.mp3"),
|
||||
);
|
||||
|
||||
illegalSoundId = await _soundpool!
|
||||
.load(await rootBundle.load("assets/audios/illegal.mp3"));
|
||||
if (illegalSoundId == null) {
|
||||
if (Config.developerMode) {
|
||||
assert(false);
|
||||
}
|
||||
debugPrint("[audio] Error: illegalSoundId is null.");
|
||||
return;
|
||||
}
|
||||
_millSoundId = await _soundpool.load(
|
||||
await rootBundle.load("assets/audios/mill.mp3"),
|
||||
);
|
||||
|
||||
loseSoundId ??=
|
||||
await _soundpool!.load(await rootBundle.load("assets/audios/lose.mp3"));
|
||||
if (loseSoundId == null) {
|
||||
if (Config.developerMode) {
|
||||
assert(false);
|
||||
}
|
||||
debugPrint("[audio] Error: loseSoundId is null.");
|
||||
return;
|
||||
}
|
||||
_placeSoundId = await _soundpool.load(
|
||||
await rootBundle.load("assets/audios/place.mp3"),
|
||||
);
|
||||
|
||||
millSoundId ??=
|
||||
await _soundpool!.load(await rootBundle.load("assets/audios/mill.mp3"));
|
||||
if (millSoundId == null) {
|
||||
if (Config.developerMode) {
|
||||
assert(false);
|
||||
}
|
||||
debugPrint("[audio] Error: millSoundId is null.");
|
||||
return;
|
||||
}
|
||||
_removeSoundId = await _soundpool.load(
|
||||
await rootBundle.load("assets/audios/remove.mp3"),
|
||||
);
|
||||
|
||||
placeSoundId ??= await _soundpool!
|
||||
.load(await rootBundle.load("assets/audios/place.mp3"));
|
||||
if (placeSoundId == null) {
|
||||
if (Config.developerMode) {
|
||||
assert(false);
|
||||
}
|
||||
debugPrint("[audio] Error: placeSoundId is null.");
|
||||
return;
|
||||
}
|
||||
_selectSoundId = await _soundpool.load(
|
||||
await rootBundle.load("assets/audios/select.mp3"),
|
||||
);
|
||||
|
||||
removeSoundId ??= await _soundpool!
|
||||
.load(await rootBundle.load("assets/audios/remove.mp3"));
|
||||
if (removeSoundId == null) {
|
||||
if (Config.developerMode) {
|
||||
assert(false);
|
||||
}
|
||||
debugPrint("[audio] Error: removeSoundId is null.");
|
||||
return;
|
||||
}
|
||||
_winSoundId = await _soundpool.load(
|
||||
await rootBundle.load("assets/audios/win.mp3"),
|
||||
);
|
||||
|
||||
selectSoundId ??= await _soundpool!
|
||||
.load(await rootBundle.load("assets/audios/select.mp3"));
|
||||
if (selectSoundId == null) {
|
||||
if (Config.developerMode) {
|
||||
assert(false);
|
||||
}
|
||||
debugPrint("[audio] Error: selectSoundId is null.");
|
||||
return;
|
||||
}
|
||||
|
||||
winSoundId ??=
|
||||
await _soundpool!.load(await rootBundle.load("assets/audios/win.mp3"));
|
||||
if (winSoundId == null) {
|
||||
if (Config.developerMode) {
|
||||
assert(false);
|
||||
}
|
||||
debugPrint("[audio] Error: winSoundId is null.");
|
||||
return;
|
||||
}
|
||||
_initialized = true;
|
||||
}
|
||||
|
||||
static Future<void> _playSound(int? soundId) async {
|
||||
if (Platform.isWindows) {
|
||||
return;
|
||||
static Future<void> _playSound(Sound sound) async {
|
||||
assert(!Platform.isWindows);
|
||||
|
||||
late final int soundId;
|
||||
|
||||
switch (sound) {
|
||||
case Sound.draw:
|
||||
soundId = _drawSoundId;
|
||||
break;
|
||||
case Sound.fly:
|
||||
soundId = _flySoundId;
|
||||
break;
|
||||
case Sound.go:
|
||||
soundId = _goSoundId;
|
||||
break;
|
||||
case Sound.illegal:
|
||||
soundId = _illegalSoundId;
|
||||
break;
|
||||
case Sound.lose:
|
||||
soundId = _loseSoundId;
|
||||
break;
|
||||
case Sound.mill:
|
||||
soundId = _millSoundId;
|
||||
break;
|
||||
case Sound.place:
|
||||
soundId = _placeSoundId;
|
||||
break;
|
||||
case Sound.remove:
|
||||
soundId = _removeSoundId;
|
||||
break;
|
||||
case Sound.select:
|
||||
soundId = _selectSoundId;
|
||||
break;
|
||||
case Sound.win:
|
||||
soundId = _winSoundId;
|
||||
break;
|
||||
}
|
||||
|
||||
if (soundId == null) {
|
||||
if (Config.developerMode) {
|
||||
assert(false);
|
||||
}
|
||||
debugPrint("[audio] Error: soundId is null.");
|
||||
return;
|
||||
}
|
||||
|
||||
_alarmSoundStreamId = await _soundpool!.play(soundId);
|
||||
_alarmSoundStreamId = await _soundpool.play(soundId);
|
||||
}
|
||||
|
||||
static Future<void> _stopSound() async {
|
||||
if (Platform.isWindows) {
|
||||
return;
|
||||
}
|
||||
assert(!Platform.isWindows);
|
||||
|
||||
if (_alarmSoundStreamId != null && _alarmSoundStreamId! > 0) {
|
||||
await _soundpool!.stop(_alarmSoundStreamId!);
|
||||
if (_alarmSoundStreamId > 0) {
|
||||
await _soundpool.stop(_alarmSoundStreamId);
|
||||
}
|
||||
}
|
||||
|
||||
static Future<void> disposePool() async {
|
||||
if (Platform.isWindows) {
|
||||
return;
|
||||
}
|
||||
static void disposePool() {
|
||||
assert(!Platform.isWindows);
|
||||
|
||||
_soundpool!.dispose();
|
||||
_soundpool.dispose();
|
||||
}
|
||||
|
||||
static Future<void> playTone(int? soundId) async {
|
||||
Chain.capture(() async {
|
||||
static Future<void> playTone(Sound sound) async {
|
||||
await Chain.capture(() async {
|
||||
if (!Config.toneEnabled ||
|
||||
isTemporaryMute ||
|
||||
Config.screenReaderSupport) {
|
||||
Config.screenReaderSupport ||
|
||||
!_initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -206,14 +173,11 @@ class Audios {
|
|||
return;
|
||||
}
|
||||
|
||||
// TODO: isn't debug chain meant to catch errors? so why catching them in here and not in onError??
|
||||
try {
|
||||
if (_soundpool == null) {
|
||||
await loadSounds();
|
||||
}
|
||||
|
||||
await _stopSound();
|
||||
|
||||
_playSound(soundId);
|
||||
await _playSound(sound);
|
||||
} catch (e) {
|
||||
// Fallback for all errors
|
||||
debugPrint(e.toString());
|
||||
|
|
|
@ -111,7 +111,7 @@ class _DrawerControllerState extends State<DrawerController>
|
|||
super.initState();
|
||||
}
|
||||
|
||||
Future<bool> getInitState() async {
|
||||
bool getInitState() {
|
||||
scrollController.jumpTo(
|
||||
widget.drawerWidth,
|
||||
);
|
||||
|
|
Loading…
Reference in New Issue