Merge remote-tracking branch 'Leptopoda/linting'

This commit is contained in:
Calcitem 2021-10-11 02:08:16 +08:00
commit e7f7b6cf6f
No known key found for this signature in database
GPG Key ID: F2F7C29E054CFB80
5 changed files with 251 additions and 263 deletions

View File

@ -32,6 +32,7 @@ class Game {
final String tag = "[game]"; final String tag = "[game]";
void init() { void init() {
// TODO: _position is allready initialized with Position(). seems like duplicate code
_position = Position(); _position = Position();
focusIndex = blurIndex = invalidIndex; focusIndex = blurIndex = invalidIndex;
} }
@ -71,7 +72,7 @@ class Game {
PieceColor.black: false PieceColor.black: false
}; };
bool aiIsSearching() { bool get aiIsSearching {
debugPrint( debugPrint(
"$tag White is searching? ${isSearching[PieceColor.white]}\n" "$tag White is searching? ${isSearching[PieceColor.white]}\n"
"$tag Black is searching? ${isSearching[PieceColor.black]}\n", "$tag Black is searching? ${isSearching[PieceColor.black]}\n",
@ -115,14 +116,14 @@ class Game {
blurIndex = invalidIndex; blurIndex = invalidIndex;
} }
bool doMove(String move) { Future<bool> doMove(String move) async {
if (position.phase == Phase.ready) { if (position.phase == Phase.ready) {
start(); start();
} }
debugPrint("$tag AI do move: $move"); debugPrint("$tag AI do move: $move");
if (!position.doMove(move)) { if (await position.doMove(move) == false) {
return false; return false;
} }

View File

@ -25,6 +25,7 @@ import 'package:sanmill/mill/types.dart';
import 'package:sanmill/mill/zobrist.dart'; import 'package:sanmill/mill/zobrist.dart';
import 'package:sanmill/services/audios.dart'; import 'package:sanmill/services/audios.dart';
import 'package:sanmill/services/engine/engine.dart'; import 'package:sanmill/services/engine/engine.dart';
import 'package:sanmill/shared/common/config.dart';
List<int> posKeyHistory = []; List<int> posKeyHistory = [];
@ -62,7 +63,7 @@ class Position {
String us = PieceColor.white; String us = PieceColor.white;
String them = PieceColor.black; String them = PieceColor.black;
String winner = PieceColor.nobody; String _winner = PieceColor.nobody;
GameOverReason gameOverReason = GameOverReason.noReason; GameOverReason gameOverReason = GameOverReason.noReason;
@ -114,7 +115,7 @@ class Position {
st.pliesFromNull = other.st.pliesFromNull; st.pliesFromNull = other.st.pliesFromNull;
them = other.them; them = other.them;
winner = other.winner; _winner = other._winner;
gameOverReason = other.gameOverReason; gameOverReason = other.gameOverReason;
phase = other.phase; phase = other.phase;
@ -135,7 +136,7 @@ class Position {
String movedPiece(int move) => pieceOn(fromSq(move)); 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) { if (selectPiece(from) == 0) {
return putPiece(to); return putPiece(to);
} }
@ -261,7 +262,7 @@ class Position {
return true; return true;
} }
bool doMove(String move) { Future<bool> doMove(String move) async {
if (move.length > "Player".length && if (move.length > "Player".length &&
move.substring(0, "Player".length - 1) == "Player") { move.substring(0, "Player".length - 1) == "Player") {
if (move["Player".length] == '1') { if (move["Player".length] == '1') {
@ -278,7 +279,7 @@ class Position {
if (move == "draw") { if (move == "draw") {
phase = Phase.gameOver; phase = Phase.gameOver;
winner = PieceColor.draw; _winner = PieceColor.draw;
if (score[PieceColor.draw] != null) { if (score[PieceColor.draw] != null) {
score[PieceColor.draw] = score[PieceColor.draw]! + 1; score[PieceColor.draw] = score[PieceColor.draw]! + 1;
} }
@ -287,7 +288,7 @@ class Position {
if (rule.nMoveRule > 0 && posKeyHistory.length >= rule.nMoveRule - 1) { if (rule.nMoveRule > 0 && posKeyHistory.length >= rule.nMoveRule - 1) {
gameOverReason = GameOverReason.drawReasonRule50; gameOverReason = GameOverReason.drawReasonRule50;
} else if (rule.endgameNMoveRule < rule.nMoveRule && } else if (rule.endgameNMoveRule < rule.nMoveRule &&
isThreeEndgame() && isThreeEndgame &&
posKeyHistory.length >= rule.endgameNMoveRule - 1) { posKeyHistory.length >= rule.endgameNMoveRule - 1) {
gameOverReason = GameOverReason.drawReasonEndgameRule50; gameOverReason = GameOverReason.drawReasonEndgameRule50;
} else if (rule.threefoldRepetitionRule) { } else if (rule.threefoldRepetitionRule) {
@ -308,20 +309,20 @@ class Position {
switch (m.type) { switch (m.type) {
case MoveType.remove: case MoveType.remove:
ret = removePiece(m.to) == 0; ret = await removePiece(m.to) == 0;
if (ret) { if (ret) {
// Reset rule 50 counter // Reset rule 50 counter
st.rule50 = 0; st.rule50 = 0;
} }
break; break;
case MoveType.move: case MoveType.move:
ret = movePiece(m.from, m.to); ret = await movePiece(m.from, m.to);
if (ret) { if (ret) {
++st.rule50; ++st.rule50;
} }
break; break;
case MoveType.place: case MoveType.place:
ret = putPiece(m.to); ret = await putPiece(m.to);
if (ret) { if (ret) {
// Reset rule 50 counter // Reset rule 50 counter
st.rule50 = 0; st.rule50 = 0;
@ -414,7 +415,7 @@ class Position {
setSideToMove(PieceColor.white); setSideToMove(PieceColor.white);
action = Act.place; action = Act.place;
winner = PieceColor.nobody; _winner = PieceColor.nobody;
gameOverReason = GameOverReason.noReason; gameOverReason = GameOverReason.noReason;
clearBoard(); clearBoard();
@ -457,7 +458,7 @@ class Position {
} }
} }
bool putPiece(int s) { Future<bool> putPiece(int s) async {
var piece = Piece.noPiece; var piece = Piece.noPiece;
final us = _sideToMove; final us = _sideToMove;
@ -472,6 +473,7 @@ class Position {
start(); start();
} }
// TODO: use switch case
if (phase == Phase.placing) { if (phase == Phase.placing) {
piece = sideToMove; piece = sideToMove;
if (pieceInHandCount[us] != null) { if (pieceInHandCount[us] != null) {
@ -501,7 +503,7 @@ class Position {
if (pieceInHandCount[PieceColor.white] == 0 && if (pieceInHandCount[PieceColor.white] == 0 &&
pieceInHandCount[PieceColor.black] == 0) { pieceInHandCount[PieceColor.black] == 0) {
if (checkIfGameIsOver()) { if (isGameOver()) {
return true; return true;
} }
@ -516,14 +518,14 @@ class Position {
changeSideToMove(); changeSideToMove();
} }
if (checkIfGameIsOver()) { if (isGameOver()) {
return true; return true;
} }
} else { } else {
changeSideToMove(); changeSideToMove();
} }
gameInstance.focusIndex = squareToIndex[s] ?? invalidIndex; gameInstance.focusIndex = squareToIndex[s] ?? invalidIndex;
Audios.playTone(Audios.placeSoundId); await Audios.playTone(Sound.place);
} else { } else {
pieceToRemoveCount = rule.mayRemoveMultiple ? n : 1; pieceToRemoveCount = rule.mayRemoveMultiple ? n : 1;
updateKeyMisc(); updateKeyMisc();
@ -539,7 +541,7 @@ class Position {
if (pieceInHandCount[PieceColor.white] == 0 && if (pieceInHandCount[PieceColor.white] == 0 &&
pieceInHandCount[PieceColor.black] == 0) { pieceInHandCount[PieceColor.black] == 0) {
if (checkIfGameIsOver()) { if (isGameOver()) {
return true; return true;
} }
@ -550,7 +552,7 @@ class Position {
changeSideToMove(); changeSideToMove();
} }
if (checkIfGameIsOver()) { if (isGameOver()) {
return true; return true;
} }
} }
@ -559,10 +561,10 @@ class Position {
} }
gameInstance.focusIndex = squareToIndex[s] ?? invalidIndex; gameInstance.focusIndex = squareToIndex[s] ?? invalidIndex;
Audios.playTone(Audios.millSoundId); await Audios.playTone(Sound.mill);
} }
} else if (phase == Phase.moving) { } else if (phase == Phase.moving) {
if (checkIfGameIsOver()) { if (isGameOver()) {
return true; return true;
} }
@ -603,18 +605,18 @@ class Position {
action = Act.select; action = Act.select;
changeSideToMove(); changeSideToMove();
if (checkIfGameIsOver()) { if (isGameOver()) {
return true; return true;
} else { } else {
gameInstance.focusIndex = squareToIndex[s] ?? invalidIndex; gameInstance.focusIndex = squareToIndex[s] ?? invalidIndex;
Audios.playTone(Audios.placeSoundId); await Audios.playTone(Sound.place);
} }
} else { } else {
pieceToRemoveCount = rule.mayRemoveMultiple ? n : 1; pieceToRemoveCount = rule.mayRemoveMultiple ? n : 1;
updateKeyMisc(); updateKeyMisc();
action = Act.remove; action = Act.remove;
gameInstance.focusIndex = squareToIndex[s] ?? invalidIndex; gameInstance.focusIndex = squareToIndex[s] ?? invalidIndex;
Audios.playTone(Audios.millSoundId); await Audios.playTone(Sound.mill);
} }
} else { } else {
assert(false); assert(false);
@ -623,7 +625,7 @@ class Position {
return true; return true;
} }
int removePiece(int s) { Future<int> removePiece(int s) async {
if (phase == Phase.ready || phase == Phase.gameOver) return -1; if (phase == Phase.ready || phase == Phase.gameOver) return -1;
if (action != Act.remove) return -1; if (action != Act.remove) return -1;
@ -641,7 +643,7 @@ class Position {
revertKey(s); revertKey(s);
Audios.playTone(Audios.removeSoundId); await Audios.playTone(Sound.remove);
if (rule.hasBannedLocations && phase == Phase.placing) { if (rule.hasBannedLocations && phase == Phase.placing) {
// Remove and put ban // Remove and put ban
@ -685,7 +687,7 @@ class Position {
} }
if (rule.isDefenderMoveFirst) { if (rule.isDefenderMoveFirst) {
checkIfGameIsOver(); isGameOver();
return 0; return 0;
} }
} else { } else {
@ -696,7 +698,7 @@ class Position {
} }
changeSideToMove(); changeSideToMove();
checkIfGameIsOver(); isGameOver();
return 0; return 0;
} }
@ -733,14 +735,12 @@ class Position {
return true; return true;
} }
String getWinner() { String get winner => _winner;
return winner;
}
void setGameOver(String w, GameOverReason reason) { void setGameOver(String w, GameOverReason reason) {
phase = Phase.gameOver; phase = Phase.gameOver;
gameOverReason = reason; gameOverReason = reason;
winner = w; _winner = w;
debugPrint("[position] Game over, $w win, because of $reason"); debugPrint("[position] Game over, $w win, because of $reason");
updateScore(); updateScore();
@ -748,7 +748,7 @@ class Position {
void updateScore() { void updateScore() {
if (phase == Phase.gameOver) { if (phase == Phase.gameOver) {
if (winner == PieceColor.draw) { if (_winner == PieceColor.draw) {
if (score[PieceColor.draw] != null) { if (score[PieceColor.draw] != null) {
score[PieceColor.draw] = score[PieceColor.draw]! + 1; score[PieceColor.draw] = score[PieceColor.draw]! + 1;
} }
@ -756,13 +756,13 @@ class Position {
return; return;
} }
if (score[winner] != null) { if (score[_winner] != null) {
score[winner] = score[winner]! + 1; score[_winner] = score[_winner]! + 1;
} }
} }
} }
bool isThreeEndgame() { bool get isThreeEndgame {
if (phase == Phase.placing) { if (phase == Phase.placing) {
return false; return false;
} }
@ -771,7 +771,7 @@ class Position {
pieceOnBoardCount[PieceColor.black] == 3; pieceOnBoardCount[PieceColor.black] == 3;
} }
bool checkIfGameIsOver() { bool isGameOver() {
if (phase == Phase.ready || phase == Phase.gameOver) { if (phase == Phase.ready || phase == Phase.gameOver) {
return true; return true;
} }
@ -782,7 +782,7 @@ class Position {
} }
if (rule.endgameNMoveRule < rule.nMoveRule && if (rule.endgameNMoveRule < rule.nMoveRule &&
isThreeEndgame() && isThreeEndgame &&
posKeyHistory.length >= rule.endgameNMoveRule) { posKeyHistory.length >= rule.endgameNMoveRule) {
setGameOver(PieceColor.draw, GameOverReason.drawReasonEndgameRule50); setGameOver(PieceColor.draw, GameOverReason.drawReasonEndgameRule50);
return true; return true;
@ -1047,7 +1047,7 @@ class Position {
nPiecesInHand; nPiecesInHand;
pieceToRemoveCount = 0; pieceToRemoveCount = 0;
winner = PieceColor.nobody; _winner = PieceColor.nobody;
Mills.adjacentSquaresInit(); Mills.adjacentSquaresInit();
Mills.millTableInit(); Mills.millTableInit();
currentSquare = 0; currentSquare = 0;
@ -1086,25 +1086,29 @@ class Position {
} }
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
Future<String> gotoHistory(HistoryMove move, [int? index]) async {
Future<String> _gotoHistory(int moveIndex) async { final int moveIndex = _gotoHistoryIndex(move, index);
String errString = "";
if (recorder == null) { if (recorder == null) {
debugPrint("[goto] recorder is null."); debugPrint("[goto] recorder is null.");
return "null"; return "null";
} }
if (recorder!.cur == moveIndex) {
debugPrint("[goto] cur is equal to moveIndex.");
return "equal";
}
final history = recorder!.history; final history = recorder!.history;
if (moveIndex < -1 || history.length <= moveIndex) { if (moveIndex < -1 || history.length <= moveIndex) {
debugPrint("[goto] moveIndex is out of range."); debugPrint("[goto] moveIndex is out of range.");
return "out-of-range"; return "out-of-range";
} }
if (recorder!.cur == moveIndex) { String errString = "";
debugPrint("[goto] cur is equal to moveIndex.");
return "equal"; Audios.isTemporaryMute = true;
}
// Backup context // Backup context
final engineTypeBackup = gameInstance.engineType; final engineTypeBackup = gameInstance.engineType;
@ -1121,13 +1125,10 @@ class Position {
} }
for (var i = 0; i <= moveIndex; i++) { 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!; errString = history[i].move!;
break; break;
} }
//await Future.delayed(Duration(seconds: 1));
//setState(() {});
} }
// Restore context // Restore context
@ -1136,31 +1137,46 @@ class Position {
recorder!.history = historyBack; recorder!.history = historyBack;
recorder!.cur = moveIndex; recorder!.cur = moveIndex;
Audios.isTemporaryMute = false;
await _gotoHistoryPlaySound(move);
return errString; return errString;
} }
Future<String> takeBackN(int n) async { int _gotoHistoryIndex(HistoryMove move, [int? index]) {
int index = recorder!.cur - n; switch (move) {
if (index < -1) { case HistoryMove.forwardAll:
index = -1; 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 { Future<void> _gotoHistoryPlaySound(HistoryMove move) async {
return _gotoHistory(recorder!.cur - 1); 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 { case HistoryMove.backOne:
return _gotoHistory(recorder!.cur + 1); await Audios.playTone(Sound.remove);
} }
Future<String> takeBackAll() async {
return _gotoHistory(-1);
} }
Future<String> stepForwardAll() async {
return _gotoHistory(recorder!.history.length - 1);
} }
String movesSinceLastRemove() { String movesSinceLastRemove() {
@ -1205,3 +1221,5 @@ class Position {
String? get lastPositionWithRemove => recorder!.lastPositionWithRemove; String? get lastPositionWithRemove => recorder!.lastPositionWithRemove;
} }
enum HistoryMove { forwardAll, backAll, farward, backN, backOne }

View File

@ -172,7 +172,7 @@ class _GamePageState extends State<GamePage>
} }
} }
dynamic onBoardTap(int index) { Future<dynamic> onBoardTap(int index) async {
if (!isReady) { if (!isReady) {
debugPrint("[tap] Not ready, ignore tapping."); debugPrint("[tap] Not ready, ignore tapping.");
return false; return false;
@ -208,18 +208,18 @@ class _GamePageState extends State<GamePage>
gameInstance.newGame(); gameInstance.newGame();
if (gameInstance.isAiToMove) { if (gameInstance.isAiToMove) {
if (gameInstance.aiIsSearching()) { if (gameInstance.aiIsSearching) {
debugPrint("$tag AI is thinking, skip tapping."); debugPrint("$tag AI is thinking, skip tapping.");
return false; return false;
} else { } else {
debugPrint("[tap] AI is not thinking. AI is to move."); debugPrint("[tap] AI is not thinking. AI is to move.");
engineToGo(false); await engineToGo(false);
return false; return false;
} }
} }
} }
if (gameInstance.isAiToMove || gameInstance.aiIsSearching()) { if (gameInstance.isAiToMove || gameInstance.aiIsSearching) {
debugPrint("[tap] AI's turn, skip tapping."); debugPrint("[tap] AI's turn, skip tapping.");
return false; return false;
} }
@ -231,12 +231,12 @@ class _GamePageState extends State<GamePage>
// Human to go // Human to go
bool ret = false; bool ret = false;
Chain.capture(() { await Chain.capture(() async {
switch (position.action) { switch (position.action) {
case Act.place: case Act.place:
if (position.putPiece(sq)) { if (await position.putPiece(sq)) {
if (position.action == Act.remove) { if (position.action == Act.remove) {
//Audios.playTone(Audios.millSoundId); //Audios.playTone(Audios.mill);
if (mounted) { if (mounted) {
showTip(S.of(context).tipMill); showTip(S.of(context).tipMill);
if (Config.screenReaderSupport) { if (Config.screenReaderSupport) {
@ -244,7 +244,7 @@ class _GamePageState extends State<GamePage>
} }
} }
} else { } else {
//Audios.playTone(Audios.placeSoundId); //Audios.playTone(Audios.place);
if (gameInstance.engineType == EngineType.humanVsAi && mounted) { if (gameInstance.engineType == EngineType.humanVsAi && mounted) {
if (rule.mayOnlyRemoveUnplacedPieceInPlacingPhase) { if (rule.mayOnlyRemoveUnplacedPieceInPlacingPhase) {
showTip(S.of(context).continueToMakeMove); showTip(S.of(context).continueToMakeMove);
@ -296,7 +296,7 @@ class _GamePageState extends State<GamePage>
final int selectRet = position.selectPiece(sq); final int selectRet = position.selectPiece(sq);
switch (selectRet) { switch (selectRet) {
case 0: case 0:
Audios.playTone(Audios.selectSoundId); await Audios.playTone(Sound.select);
gameInstance.select(index); gameInstance.select(index);
ret = true; ret = true;
debugPrint("[tap] selectPiece: [$sq]"); debugPrint("[tap] selectPiece: [$sq]");
@ -325,7 +325,7 @@ class _GamePageState extends State<GamePage>
break; break;
case -2: case -2:
Audios.playTone(Audios.illegalSoundId); await Audios.playTone(Sound.illegal);
debugPrint("[tap] selectPiece: skip [$sq]"); debugPrint("[tap] selectPiece: skip [$sq]");
if (mounted && position.phase != Phase.gameOver) { if (mounted && position.phase != Phase.gameOver) {
showTip(S.of(context).tipCannotMove); showTip(S.of(context).tipCannotMove);
@ -336,7 +336,7 @@ class _GamePageState extends State<GamePage>
} }
break; break;
case -3: case -3:
Audios.playTone(Audios.illegalSoundId); await Audios.playTone(Sound.illegal);
debugPrint("[tap] selectPiece: skip [$sq]"); debugPrint("[tap] selectPiece: skip [$sq]");
if (mounted) { if (mounted) {
showTip(S.of(context).tipCanMoveOnePoint); showTip(S.of(context).tipCanMoveOnePoint);
@ -347,7 +347,7 @@ class _GamePageState extends State<GamePage>
} }
break; break;
case -4: case -4:
Audios.playTone(Audios.illegalSoundId); await Audios.playTone(Sound.illegal);
debugPrint("[tap] selectPiece: skip [$sq]"); debugPrint("[tap] selectPiece: skip [$sq]");
if (mounted) { if (mounted) {
showTip(S.of(context).tipSelectPieceToMove); showTip(S.of(context).tipSelectPieceToMove);
@ -357,7 +357,7 @@ class _GamePageState extends State<GamePage>
} }
break; break;
default: default:
Audios.playTone(Audios.illegalSoundId); await Audios.playTone(Sound.illegal);
debugPrint("[tap] selectPiece: skip [$sq]"); debugPrint("[tap] selectPiece: skip [$sq]");
if (mounted) { if (mounted) {
showTip(S.of(context).tipSelectWrong); showTip(S.of(context).tipSelectWrong);
@ -372,11 +372,11 @@ class _GamePageState extends State<GamePage>
break; break;
case Act.remove: case Act.remove:
final int removeRet = position.removePiece(sq); final int removeRet = await position.removePiece(sq);
switch (removeRet) { switch (removeRet) {
case 0: case 0:
//Audios.playTone(Audios.removeSoundId); //Audios.playTone(Audios.remove);
ret = true; ret = true;
debugPrint("[tap] removePiece: [$sq]"); debugPrint("[tap] removePiece: [$sq]");
if (gameInstance.position.pieceToRemoveCount >= 1) { if (gameInstance.position.pieceToRemoveCount >= 1) {
@ -404,7 +404,7 @@ class _GamePageState extends State<GamePage>
} }
break; break;
case -2: case -2:
Audios.playTone(Audios.illegalSoundId); await Audios.playTone(Sound.illegal);
debugPrint( debugPrint(
"[tap] removePiece: Cannot Remove our pieces, skip [$sq]", "[tap] removePiece: Cannot Remove our pieces, skip [$sq]",
); );
@ -416,7 +416,7 @@ class _GamePageState extends State<GamePage>
} }
break; break;
case -3: case -3:
Audios.playTone(Audios.illegalSoundId); await Audios.playTone(Sound.illegal);
debugPrint( debugPrint(
"[tap] removePiece: Cannot remove piece from Mill, skip [$sq]", "[tap] removePiece: Cannot remove piece from Mill, skip [$sq]",
); );
@ -432,7 +432,7 @@ class _GamePageState extends State<GamePage>
} }
break; break;
default: default:
Audios.playTone(Audios.illegalSoundId); await Audios.playTone(Sound.illegal);
debugPrint("[tap] removePiece: skip [$sq]"); debugPrint("[tap] removePiece: skip [$sq]");
if (mounted && position.phase != Phase.gameOver) { if (mounted && position.phase != Phase.gameOver) {
showTip(S.of(context).tipBanRemove); showTip(S.of(context).tipBanRemove);
@ -587,7 +587,7 @@ class _GamePageState extends State<GamePage>
); );
} }
gameInstance.doMove(move.move!); await gameInstance.doMove(move.move!);
showTips(); showTips();
if (Config.screenReaderSupport && move.notation != null) { if (Config.screenReaderSupport && move.notation != null) {
showSnackBar(context, "${S.of(context).ai}: ${move.notation!}"); showSnackBar(context, "${S.of(context).ai}: ${move.notation!}");
@ -657,7 +657,7 @@ class _GamePageState extends State<GamePage>
debugPrint("Clipboard text:"); debugPrint("Clipboard text:");
debugPrint(text); debugPrint(text);
await onTakeBackAllButtonPressed(pop: false); await onTakeBackAllButtonPressed(false);
gameInstance.position.recorder!.clear(); gameInstance.position.recorder!.clear();
final importFailedStr = gameInstance.position.recorder!.import(text); final importFailedStr = gameInstance.position.recorder!.import(text);
@ -670,7 +670,7 @@ class _GamePageState extends State<GamePage>
return; return;
} }
await onStepForwardAllButtonPressed(pop: false); await onStepForwardAllButtonPressed(false);
showTip(S.of(context).gameImported); showTip(S.of(context).gameImported);
if (Config.screenReaderSupport) { if (Config.screenReaderSupport) {
@ -774,8 +774,8 @@ class _GamePageState extends State<GamePage>
Future<void> onAutoReplayButtonPressed() async { Future<void> onAutoReplayButtonPressed() async {
Navigator.pop(context); Navigator.pop(context);
await onTakeBackAllButtonPressed(pop: false); await onTakeBackAllButtonPressed(false);
await onStepForwardAllButtonPressed(pop: false); await onStepForwardAllButtonPressed(false);
} }
void onGameButtonPressed() { void onGameButtonPressed() {
@ -969,8 +969,9 @@ class _GamePageState extends State<GamePage>
} }
Future<void> onGotoHistoryButtonsPressed( Future<void> onGotoHistoryButtonsPressed(
Future<String> func, { HistoryMove move, {
bool pop = true, bool pop = true,
int? number,
}) async { }) async {
if (pop == true) { if (pop == true) {
Navigator.pop(context); Navigator.pop(context);
@ -989,9 +990,7 @@ class _GamePageState extends State<GamePage>
isGoingToHistory = true; isGoingToHistory = true;
Audios.isTemporaryMute = Config.keepMuteWhenTakingBack; final errMove = await gameInstance.position.gotoHistory(move, number);
final errMove = await func;
switch (errMove) { switch (errMove) {
case "": case "":
@ -1008,14 +1007,10 @@ class _GamePageState extends State<GamePage>
break; break;
} }
Audios.isTemporaryMute = false;
isGoingToHistory = false; isGoingToHistory = false;
if (mounted) { if (mounted) {
String text = "";
final pos = gameInstance.position; final pos = gameInstance.position;
/* /*
String us = ""; String us = "";
String them = ""; String them = "";
@ -1028,6 +1023,7 @@ class _GamePageState extends State<GamePage>
} }
*/ */
late final String text;
final lastEffectiveMove = pos.recorder!.lastEffectiveMove; final lastEffectiveMove = pos.recorder!.lastEffectiveMove;
if (lastEffectiveMove != null && lastEffectiveMove.notation != null) { if (lastEffectiveMove != null && lastEffectiveMove.notation != null) {
text = "${S.of(context).lastMove}: ${lastEffectiveMove.notation}"; text = "${S.of(context).lastMove}: ${lastEffectiveMove.notation}";
@ -1044,28 +1040,37 @@ class _GamePageState extends State<GamePage>
} }
} }
Future<void> onTakeBackButtonPressed({bool pop = true}) async { Future<void> onTakeBackButtonPressed([bool pop = true]) async =>
onGotoHistoryButtonsPressed(gameInstance.position.takeBack(), pop: pop);
}
Future<void> onStepForwardButtonPressed({bool pop = true}) async {
onGotoHistoryButtonsPressed(gameInstance.position.stepForward(), pop: pop);
}
Future<void> onTakeBackAllButtonPressed({bool pop = true}) async {
onGotoHistoryButtonsPressed(gameInstance.position.takeBackAll(), pop: pop);
}
Future<void> onStepForwardAllButtonPressed({bool pop = true}) async {
onGotoHistoryButtonsPressed( onGotoHistoryButtonsPressed(
gameInstance.position.stepForwardAll(), HistoryMove.backOne,
pop: pop,
);
Future<void> onStepForwardButtonPressed([bool pop = true]) async =>
onGotoHistoryButtonsPressed(
HistoryMove.farward,
pop: pop,
);
Future<void> onTakeBackAllButtonPressed([bool pop = true]) async =>
onGotoHistoryButtonsPressed(
HistoryMove.backAll,
pop: pop,
);
Future<void> onStepForwardAllButtonPressed([bool pop = true]) async {
onGotoHistoryButtonsPressed(
HistoryMove.forwardAll,
pop: pop, pop: pop,
); );
} }
Future<void> onTakeBackNButtonPressed(int n, {bool pop = true}) async { Future<void> onTakeBackNButtonPressed(int n, [bool pop = true]) async =>
onGotoHistoryButtonsPressed(gameInstance.position.takeBackN(n), pop: pop); onGotoHistoryButtonsPressed(
} HistoryMove.backN,
number: n,
pop: pop,
);
void onMoveListButtonPressed() { void onMoveListButtonPressed() {
final moveHistoryText = gameInstance.position.moveHistoryText; final moveHistoryText = gameInstance.position.moveHistoryText;
@ -1177,7 +1182,7 @@ class _GamePageState extends State<GamePage>
debugPrint("[config] isPrivacyPolicyAccepted: $value"); debugPrint("[config] isPrivacyPolicyAccepted: $value");
Config.save(); await Config.save();
} }
Future<void> onShowPrivacyDialog() async { Future<void> onShowPrivacyDialog() async {
@ -1259,10 +1264,10 @@ class _GamePageState extends State<GamePage>
switch (result) { switch (result) {
case GameResult.win: case GameResult.win:
//Audios.playTone(Audios.winSoundId); //Audios.playTone(Audios.win);
break; break;
case GameResult.lose: case GameResult.lose:
//Audios.playTone(Audios.loseSoundId); //Audios.playTone(Audios.lose);
break; break;
case GameResult.draw: case GameResult.draw:
break; break;
@ -1694,7 +1699,7 @@ class _GamePageState extends State<GamePage>
color: Color(Config.navigationToolbarIconColor), color: Color(Config.navigationToolbarIconColor),
), ),
), ),
onPressed: () => onTakeBackAllButtonPressed(pop: false), onPressed: () => onTakeBackAllButtonPressed(false),
); );
final takeBackButton = TextButton( final takeBackButton = TextButton(
@ -1707,7 +1712,7 @@ class _GamePageState extends State<GamePage>
color: Color(Config.navigationToolbarIconColor), color: Color(Config.navigationToolbarIconColor),
), ),
), ),
onPressed: () => onTakeBackButtonPressed(pop: false), onPressed: () async => onTakeBackButtonPressed(false),
); );
final stepForwardButton = TextButton( final stepForwardButton = TextButton(
@ -1720,7 +1725,7 @@ class _GamePageState extends State<GamePage>
color: Color(Config.navigationToolbarIconColor), color: Color(Config.navigationToolbarIconColor),
), ),
), ),
onPressed: () => onStepForwardButtonPressed(pop: false), onPressed: () async => onStepForwardButtonPressed(false),
); );
final stepForwardAllButton = TextButton( final stepForwardAllButton = TextButton(
@ -1733,7 +1738,7 @@ class _GamePageState extends State<GamePage>
color: Color(Config.navigationToolbarIconColor), color: Color(Config.navigationToolbarIconColor),
), ),
), ),
onPressed: () => onStepForwardAllButtonPressed(pop: false), onPressed: () async => onStepForwardAllButtonPressed(false),
); );
return GamePageToolBar( return GamePageToolBar(

View File

@ -24,22 +24,35 @@ import 'package:sanmill/shared/common/config.dart';
import 'package:soundpool/soundpool.dart'; import 'package:soundpool/soundpool.dart';
import 'package:stack_trace/stack_trace.dart'; import 'package:stack_trace/stack_trace.dart';
enum Sound {
draw,
fly,
go,
illegal,
lose,
mill,
place,
remove,
select,
win,
}
class Audios { class Audios {
const Audios._(); const Audios._();
//static AudioPlayer? _player; //static AudioPlayer? _player;
static Soundpool? _soundpool; static final Soundpool _soundpool = Soundpool.fromOptions();
// TODO: use enum for the sounds static bool _initialized = false;
static int? _alarmSoundStreamId; static int _alarmSoundStreamId = 0;
static int? drawSoundId; static late final int _drawSoundId;
static int? flySoundId; static late final int _flySoundId;
static int? goSoundId; static late final int _goSoundId;
static int? illegalSoundId; static late final int _illegalSoundId;
static int? loseSoundId; static late final int _loseSoundId;
static int? millSoundId; static late final int _millSoundId;
static int? placeSoundId; static late final int _placeSoundId;
static int? removeSoundId; static late final int _removeSoundId;
static int? selectSoundId; static late final int _selectSoundId;
static int? winSoundId; static late final int _winSoundId;
static bool isTemporaryMute = false; static bool isTemporaryMute = false;
static Future<void> loadSounds() async { static Future<void> loadSounds() async {
@ -48,156 +61,110 @@ class Audios {
return; return;
} }
_soundpool ??= Soundpool.fromOptions(); _drawSoundId = await _soundpool.load(
await rootBundle.load("assets/audios/draw.mp3"),
);
if (_soundpool == null) { _flySoundId = await _soundpool.load(
if (Config.developerMode) { await rootBundle.load("assets/audios/fly.mp3"),
assert(false); );
}
debugPrint("[audio] Error: _soundpool is null."); _goSoundId = await _soundpool.load(
return; await rootBundle.load("assets/audios/go.mp3"),
);
_illegalSoundId = await _soundpool.load(
await rootBundle.load("assets/audios/illegal.mp3"),
);
_loseSoundId = await _soundpool.load(
await rootBundle.load("assets/audios/lose.mp3"),
);
_millSoundId = await _soundpool.load(
await rootBundle.load("assets/audios/mill.mp3"),
);
_placeSoundId = await _soundpool.load(
await rootBundle.load("assets/audios/place.mp3"),
);
_removeSoundId = await _soundpool.load(
await rootBundle.load("assets/audios/remove.mp3"),
);
_selectSoundId = await _soundpool.load(
await rootBundle.load("assets/audios/select.mp3"),
);
_winSoundId = await _soundpool.load(
await rootBundle.load("assets/audios/win.mp3"),
);
_initialized = true;
} }
drawSoundId ??= static Future<void> _playSound(Sound sound) async {
await _soundpool!.load(await rootBundle.load("assets/audios/draw.mp3")); assert(!Platform.isWindows);
if (drawSoundId == null) {
if (Config.developerMode) { late final int soundId;
assert(false);
} switch (sound) {
debugPrint("[audio] Error: drawSoundId is null."); case Sound.draw:
return; 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;
} }
flySoundId ??= _alarmSoundStreamId = await _soundpool.play(soundId);
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;
}
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;
}
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;
}
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;
}
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;
}
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;
}
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;
}
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;
}
}
static Future<void> _playSound(int? soundId) async {
if (Platform.isWindows) {
return;
}
if (soundId == null) {
if (Config.developerMode) {
assert(false);
}
debugPrint("[audio] Error: soundId is null.");
return;
}
_alarmSoundStreamId = await _soundpool!.play(soundId);
} }
static Future<void> _stopSound() async { static Future<void> _stopSound() async {
if (Platform.isWindows) { assert(!Platform.isWindows);
return;
}
if (_alarmSoundStreamId != null && _alarmSoundStreamId! > 0) { if (_alarmSoundStreamId > 0) {
await _soundpool!.stop(_alarmSoundStreamId!); await _soundpool.stop(_alarmSoundStreamId);
} }
} }
static Future<void> disposePool() async { static void disposePool() {
if (Platform.isWindows) { assert(!Platform.isWindows);
return;
_soundpool.dispose();
} }
_soundpool!.dispose(); static Future<void> playTone(Sound sound) async {
} await Chain.capture(() async {
static Future<void> playTone(int? soundId) async {
Chain.capture(() async {
if (!Config.toneEnabled || if (!Config.toneEnabled ||
isTemporaryMute || isTemporaryMute ||
Config.screenReaderSupport) { Config.screenReaderSupport ||
!_initialized) {
return; return;
} }
@ -206,14 +173,11 @@ class Audios {
return; return;
} }
// TODO: isn't debug chain meant to catch errors? so why catching them in here and not in onError??
try { try {
if (_soundpool == null) {
await loadSounds();
}
await _stopSound(); await _stopSound();
_playSound(soundId); await _playSound(sound);
} catch (e) { } catch (e) {
// Fallback for all errors // Fallback for all errors
debugPrint(e.toString()); debugPrint(e.toString());

View File

@ -111,7 +111,7 @@ class _DrawerControllerState extends State<DrawerController>
super.initState(); super.initState();
} }
Future<bool> getInitState() async { bool getInitState() {
scrollController.jumpTo( scrollController.jumpTo(
widget.drawerWidth, widget.drawerWidth,
); );