diff --git a/src/position.cpp b/src/position.cpp index 6fa7d45e..90847624 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -708,7 +708,7 @@ int Position::pieces_in_hand_count() return pieceCountInHand[BLACK] + pieceCountInHand[WHITE]; } -int Position::set_position(const struct Rule *newRule) +int Position::set_position(/* const */ struct Rule *newRule) { rule = newRule; diff --git a/src/position.h b/src/position.h index 51e943f7..f15d9a77 100644 --- a/src/position.h +++ b/src/position.h @@ -104,7 +104,7 @@ public: /// Mill Game - int set_position(const struct Rule *rule); + int set_position(/* const */ struct Rule *rule); Piece *get_board(); Square current_square() const; diff --git a/src/rule.cpp b/src/rule.cpp index 7935ec7a..914994cc 100644 --- a/src/rule.cpp +++ b/src/rule.cpp @@ -20,9 +20,10 @@ #include "rule.h" #include "types.h" -const struct Rule *rule; +/* const */ struct Rule *rule; -const struct Rule RULES[N_RULES] = { +// TODO +/* const */ struct Rule RULES[N_RULES] = { { "成三棋", // 成三棋 // 规则说明 diff --git a/src/rule.h b/src/rule.h index 93477873..6d0abcb6 100644 --- a/src/rule.h +++ b/src/rule.h @@ -40,7 +40,7 @@ struct Rule }; #define N_RULES 4 -extern const struct Rule RULES[N_RULES]; -extern const struct Rule *rule; +extern /* const */ struct Rule RULES[N_RULES]; // TODO +extern /* const */ struct Rule *rule; #endif /* RULE_H */ diff --git a/src/ucioption.cpp b/src/ucioption.cpp index 21d64edc..24ad0cc8 100644 --- a/src/ucioption.cpp +++ b/src/ucioption.cpp @@ -35,6 +35,8 @@ using std::string; UCI::OptionsMap Options; // Global object +extern struct Rule *rule; + namespace UCI { @@ -66,6 +68,62 @@ void on_random_move(const Option &o) gameOptions.setRandomMoveEnabled((bool)o); } +// Rules + +void on_nTotalPiecesEachSide(const Option &o) +{ + rule->nTotalPiecesEachSide = (int)o; +} + +void on_nPiecesAtLeast(const Option &o) +{ + rule->nPiecesAtLeast = (int)o; +} + +void on_hasObliqueLines(const Option &o) +{ + rule->hasObliqueLines = (bool)o; +} + +void on_hasBannedLocations(const Option &o) +{ + rule->hasBannedLocations = (bool)o; +} + +void on_isDefenderMoveFirst(const Option &o) +{ + rule->isDefenderMoveFirst = (bool)o; +} + +void on_allowRemoveMultiPiecesWhenCloseMultiMill(const Option &o) +{ + rule->allowRemoveMultiPiecesWhenCloseMultiMill = (bool)o; +} + +void on_allowRemovePieceInMill(const Option &o) +{ + rule->allowRemovePieceInMill = (bool)o; +} + +void on_isBlackLoseButNotDrawWhenBoardFull(const Option &o) +{ + rule->isBlackLoseButNotDrawWhenBoardFull = (bool)o; +} + +void on_isLoseButNotChangeSideWhenNoWay(const Option &o) +{ + rule->isLoseButNotChangeSideWhenNoWay = (bool)o; +} + +void on_allowFlyWhenRemainThreePieces(const Option &o) +{ + rule->allowFlyWhenRemainThreePieces = (bool)o; +} + +void on_maxStepsLedToDraw(const Option &o) +{ + rule->maxStepsLedToDraw = (int)o; +} /// Our case insensitive less() function as required by UCI protocol bool CaseInsensitiveLess::operator() (const string &s1, const string &s2) const @@ -101,6 +159,20 @@ void init(OptionsMap &o) o["UCI_Elo"] << Option(1350, 1350, 2850); o["RandomMove"] << Option(true, on_random_move); + + // Rules + o["nTotalPiecesEachSide"] << Option(12, 6, 12, on_nTotalPiecesEachSide); + o["nPiecesAtLeast"] << Option(3, 3, 5, on_nPiecesAtLeast); + o["hasObliqueLines"] << Option(true, on_hasObliqueLines); + o["hasBannedLocations"] << Option(true, on_hasBannedLocations); + o["isDefenderMoveFirst"] << Option(true, on_isDefenderMoveFirst); + o["allowRemoveMultiPiecesWhenCloseMultiMill"] << Option(false, on_allowRemoveMultiPiecesWhenCloseMultiMill); + o["allowRemovePieceInMill"] << Option(true, on_allowRemovePieceInMill); + o["isBlackLoseButNotDrawWhenBoardFull"] << Option(true, on_isBlackLoseButNotDrawWhenBoardFull); + o["isLoseButNotChangeSideWhenNoWay"] << Option(true, on_isLoseButNotChangeSideWhenNoWay); + o["allowFlyWhenRemainThreePieces"] << Option(false, on_allowFlyWhenRemainThreePieces); + o["maxStepsLedToDraw"] << Option(50, on_maxStepsLedToDraw); + } diff --git a/src/ui/flutter/lib/common/config.dart b/src/ui/flutter/lib/common/config.dart index 6c578fca..3407d016 100644 --- a/src/ui/flutter/lib/common/config.dart +++ b/src/ui/flutter/lib/common/config.dart @@ -36,6 +36,19 @@ class Config { static bool depthExtension = true; static bool openingBook = false; + // Rules + static int nTotalPiecesEachSide = 12; + static int nPiecesAtLeast = 3; + static bool hasObliqueLines = true; + static bool hasBannedLocations = true; + static bool isDefenderMoveFirst = true; + static bool allowRemoveMultiPiecesWhenCloseMultiMill = false; + static bool allowRemovePieceInMill = true; + static bool isBlackLoseButNotDrawWhenBoardFull = true; + static bool isLoseButNotChangeSideWhenNoWay = true; + static bool allowFlyWhenRemainThreePieces = false; + static int maxStepsLedToDraw = 50; + static Future loadProfile() async { final profile = await Profile.shared(); @@ -53,6 +66,34 @@ class Config { Config.depthExtension = profile['depthExtension'] ?? false; Config.openingBook = profile['openingBook'] ?? false; + // Rules + Game.shared.position.rule.nTotalPiecesEachSide = + Config.nTotalPiecesEachSide = profile['nTotalPiecesEachSide'] ?? 12; + Game.shared.position.rule.nPiecesAtLeast = + Config.nPiecesAtLeast = profile['nPiecesAtLeast'] ?? 3; + Game.shared.position.rule.hasObliqueLines = + Config.hasObliqueLines = profile['hasObliqueLines'] ?? true; + Game.shared.position.rule.hasBannedLocations = + Config.hasBannedLocations = profile['hasBannedLocations'] ?? true; + Game.shared.position.rule.isDefenderMoveFirst = + Config.isDefenderMoveFirst = profile['isDefenderMoveFirst'] ?? true; + Game.shared.position.rule.allowRemoveMultiPiecesWhenCloseMultiMill = + Config.allowRemoveMultiPiecesWhenCloseMultiMill = + profile['allowRemoveMultiPiecesWhenCloseMultiMill'] ?? false; + Game.shared.position.rule.allowRemovePieceInMill = Config + .allowRemovePieceInMill = profile['allowRemovePieceInMill'] ?? true; + Game.shared.position.rule.isBlackLoseButNotDrawWhenBoardFull = + Config.isBlackLoseButNotDrawWhenBoardFull = + profile['isBlackLoseButNotDrawWhenBoardFull'] ?? true; + Game.shared.position.rule.isLoseButNotChangeSideWhenNoWay = + Config.isLoseButNotChangeSideWhenNoWay = + profile['isLoseButNotChangeSideWhenNoWay'] ?? true; + Game.shared.position.rule.allowFlyWhenRemainThreePieces = + Config.allowFlyWhenRemainThreePieces = + profile['allowFlyWhenRemainThreePieces'] ?? false; + Game.shared.position.rule.maxStepsLedToDraw = + Config.maxStepsLedToDraw = profile['maxStepsLedToDraw'] ?? 50; + return true; } @@ -73,6 +114,23 @@ class Config { profile['depthExtension'] = Config.depthExtension; profile['openingBook'] = Config.openingBook; + // Rules + profile['nTotalPiecesEachSide'] = Config.nTotalPiecesEachSide; + profile['nPiecesAtLeast'] = Config.nPiecesAtLeast; + profile['hasObliqueLines'] = Config.hasObliqueLines; + profile['hasBannedLocations'] = Config.hasBannedLocations; + profile['isDefenderMoveFirst'] = Config.isDefenderMoveFirst; + profile['allowRemoveMultiPiecesWhenCloseMultiMill'] = + Config.allowRemoveMultiPiecesWhenCloseMultiMill; + profile['allowRemovePieceInMill'] = Config.allowRemovePieceInMill; + profile['isBlackLoseButNotDrawWhenBoardFull'] = + Config.isBlackLoseButNotDrawWhenBoardFull; + profile['isLoseButNotChangeSideWhenNoWay'] = + Config.isLoseButNotChangeSideWhenNoWay; + profile['allowFlyWhenRemainThreePieces'] = + Config.allowFlyWhenRemainThreePieces; + profile['maxStepsLedToDraw'] = Config.maxStepsLedToDraw; + profile.commit(); return true; diff --git a/src/ui/flutter/lib/engine/native_engine.dart b/src/ui/flutter/lib/engine/native_engine.dart index 9314c24e..8ca28d4a 100644 --- a/src/ui/flutter/lib/engine/native_engine.dart +++ b/src/ui/flutter/lib/engine/native_engine.dart @@ -134,11 +134,28 @@ class NativeEngine extends AiEngine { } Future setOptions() async { - if (Config.randomMoveEnabled) { - await send('setoption name RandomMove value true'); - } else { - await send('setoption name RandomMove value false'); - } + await send('setoption name RandomMove value ${Config.randomMoveEnabled}'); + await send( + 'setoption name nTotalPiecesEachSide value ${Config.nTotalPiecesEachSide}'); + await send('setoption name nPiecesAtLeast value ${Config.nPiecesAtLeast}'); + await send( + 'setoption name hasObliqueLines value ${Config.hasObliqueLines}'); + await send( + 'setoption name hasBannedLocations value ${Config.hasBannedLocations}'); + await send( + 'setoption name isDefenderMoveFirst value ${Config.isDefenderMoveFirst}'); + await send( + 'setoption name allowRemoveMultiPiecesWhenCloseMultiMill value ${Config.allowRemoveMultiPiecesWhenCloseMultiMill}'); + await send( + 'setoption name allowRemovePieceInMill value ${Config.allowRemovePieceInMill}'); + await send( + 'setoption name isBlackLoseButNotDrawWhenBoardFull value ${Config.isBlackLoseButNotDrawWhenBoardFull}'); + await send( + 'setoption name isLoseButNotChangeSideWhenNoWay value ${Config.isLoseButNotChangeSideWhenNoWay}'); + await send( + 'setoption name allowFlyWhenRemainThreePieces value ${Config.allowFlyWhenRemainThreePieces}'); + await send( + 'setoption name maxStepsLedToDraw value ${Config.maxStepsLedToDraw}'); } String getPositionFen(Position position) { diff --git a/src/ui/flutter/lib/l10n/intl_en.arb b/src/ui/flutter/lib/l10n/intl_en.arb index af1470eb..ae2323cf 100644 --- a/src/ui/flutter/lib/l10n/intl_en.arb +++ b/src/ui/flutter/lib/l10n/intl_en.arb @@ -298,5 +298,61 @@ "misc": "Miscellaneous", "@misc": { "description": "Miscellaneous" + }, + "rules": "Rules", + "@rules": { + "description": "Rules" + }, + "nTotalPiecesEachSide": "Total Pieces Each Side", + "@nTotalPiecesEachSide": { + "description": "Total Pieces Each Side" + }, + "ninePieces": "Nine Pieces", + "@ninePieces": { + "description": "Nine Pieces" + }, + "twelvePieces": "Twelve Pieces", + "@twelvePieces": { + "description": "Twelve Pieces" + }, + "nPiecesAtLeast": "Pieces At Least", + "@nPiecesAtLeast": { + "description": "Pieces At Least" + }, + "hasObliqueLines": "Has Oblique Lines", + "@hasObliqueLines": { + "description": "Has Oblique Lines" + }, + "hasBannedLocations": "Has Banned Locations", + "@hasBannedLocations": { + "description": "Has Banned Locations" + }, + "isDefenderMoveFirst": "Defender Move First", + "@isDefenderMoveFirst": { + "description": "Defender Move First" + }, + "allowRemoveMultiPiecesWhenCloseMultiMill": "Allow Remove Multi Pieces When Close Multi Mill", + "@allowRemoveMultiPiecesWhenCloseMultiMill": { + "description": "Allow Remove Multi Pieces When Close Multi Mill" + }, + "allowRemovePieceInMill": "Allow Remove Piece In Mill", + "@allowRemovePieceInMill": { + "description": "Allow Remove Piece In Mill" + }, + "isBlackLoseButNotDrawWhenBoardFull": "Black Lose But Not Draw When Board Full", + "@isBlackLoseButNotDrawWhenBoardFull": { + "description": "Black Lose But Not Draw When Board Full" + }, + "isLoseButNotChangeSideWhenNoWay": "Lose But Not Change Side When No Way", + "@isLoseButNotChangeSideWhenNoWay": { + "description": "Lose But Not Change Side When No Way" + }, + "allowFlyWhenRemainThreePieces": "Allow Fly When Remain Three Pieces", + "@allowFlyWhenRemainThreePieces": { + "description": "Allow Fly When Remain Three Pieces" + }, + "maxStepsLedToDraw": "Max Steps Led To Draw", + "@maxStepsLedToDraw": { + "description": "Max Steps Led To Draw" } } diff --git a/src/ui/flutter/lib/l10n/intl_zh.arb b/src/ui/flutter/lib/l10n/intl_zh.arb index 0956d0e4..61db22e8 100644 --- a/src/ui/flutter/lib/l10n/intl_zh.arb +++ b/src/ui/flutter/lib/l10n/intl_zh.arb @@ -73,5 +73,19 @@ "idsEnabled": "迭代加深", "depthExtension": "深度扩展", "openingBook": "使用开局库", - "misc": "其他" + "misc": "其他", + "rules": "棋规", + "nTotalPiecesEachSide": "棋子数", + "ninePieces": "九子", + "twelvePieces": "十二子", + "nPiecesAtLeast": "少于几个子则输棋", + "hasObliqueLines": "斜线", + "hasBannedLocations": "禁点", + "isDefenderMoveFirst": "后摆子的先走子", + "allowRemoveMultiPiecesWhenCloseMultiMill": "成几个三就吃几个子", + "allowRemovePieceInMill": "允许吃三中的子", + "isBlackLoseButNotDrawWhenBoardFull": "当棋盘摆满时先摆子的输棋", + "isLoseButNotChangeSideWhenNoWay": "当无路可走时输棋", + "allowFlyWhenRemainThreePieces": "剩余三颗子时飞棋", + "maxStepsLedToDraw": "连续多少步无吃子则和棋" } \ No newline at end of file diff --git a/src/ui/flutter/lib/painting/board_painter.dart b/src/ui/flutter/lib/painting/board_painter.dart index 2a6ef351..05ca7b3e 100644 --- a/src/ui/flutter/lib/painting/board_painter.dart +++ b/src/ui/flutter/lib/painting/board_painter.dart @@ -18,6 +18,7 @@ */ import 'package:flutter/material.dart'; +import 'package:sanmill/common/config.dart'; import 'package:sanmill/style/colors.dart'; import 'package:sanmill/widgets/board.dart'; @@ -149,6 +150,10 @@ class BoardPainter extends PiecesBasePainter { paint, ); + if (!Config.hasObliqueLines) { + return; + } + // top left oblique line canvas.drawLine( Offset(left + 0, top), diff --git a/src/ui/flutter/lib/widgets/settings_page.dart b/src/ui/flutter/lib/widgets/settings_page.dart index d9ba5d42..5e93261e 100644 --- a/src/ui/flutter/lib/widgets/settings_page.dart +++ b/src/ui/flutter/lib/widgets/settings_page.dart @@ -196,6 +196,157 @@ class _SettingsPageState extends State { Config.save(); } + // Rules + + setNTotalPiecesEachSide() { + // + callback(int nTotalPiecesEachSide) async { + // + Navigator.of(context).pop(); + + setState(() { + Game.shared.position.rule.nTotalPiecesEachSide = + Config.nTotalPiecesEachSide = nTotalPiecesEachSide; + }); + + Config.save(); + } + + showModalBottomSheet( + context: context, + builder: (BuildContext context) => Column( + mainAxisSize: MainAxisSize.min, + children: [ + SizedBox(height: 10), + RadioListTile( + activeColor: UIColors.primaryColor, + title: Text('6'), + groupValue: Config.nTotalPiecesEachSide, + value: 6, + onChanged: callback, + ), + Divider(), + RadioListTile( + activeColor: UIColors.primaryColor, + title: Text('9'), + groupValue: Config.nTotalPiecesEachSide, + value: 9, + onChanged: callback, + ), + Divider(), + RadioListTile( + activeColor: UIColors.primaryColor, + title: Text('12'), + groupValue: Config.nTotalPiecesEachSide, + value: 12, + onChanged: callback, + ), + Divider(), + SizedBox(height: 56), + ], + ), + ); + } + + setNPiecesAtLeast(int value) async { + // + setState(() { + Game.shared.position.rule.nPiecesAtLeast = Config.nPiecesAtLeast = value; + }); + + Config.save(); + } + + switchHasObliqueLines(bool value) async { + // + setState(() { + Game.shared.position.rule.hasObliqueLines = + Config.hasObliqueLines = value; + }); + + Config.save(); + } + + switchHasBannedLocations(bool value) async { + // + setState(() { + Game.shared.position.rule.hasBannedLocations = + Config.hasBannedLocations = value; + }); + + Config.save(); + } + + switchIsDefenderMoveFirst(bool value) async { + // + setState(() { + Game.shared.position.rule.isDefenderMoveFirst = + Config.isDefenderMoveFirst = value; + }); + + Config.save(); + } + + switchAllowRemoveMultiPiecesWhenCloseMultiMill(bool value) async { + // + setState(() { + Game.shared.position.rule.allowRemoveMultiPiecesWhenCloseMultiMill = + Config.allowRemoveMultiPiecesWhenCloseMultiMill = value; + }); + + Config.save(); + } + + switchAllowRemovePieceInMill(bool value) async { + // + setState(() { + Game.shared.position.rule.allowRemovePieceInMill = + Config.allowRemovePieceInMill = value; + }); + + Config.save(); + } + + switchIsBlackLoseButNotDrawWhenBoardFull(bool value) async { + // + setState(() { + Game.shared.position.rule.isBlackLoseButNotDrawWhenBoardFull = + Config.isBlackLoseButNotDrawWhenBoardFull = value; + }); + + Config.save(); + } + + switchIsLoseButNotChangeSideWhenNoWay(bool value) async { + // + setState(() { + Game.shared.position.rule.isLoseButNotChangeSideWhenNoWay = + Config.isLoseButNotChangeSideWhenNoWay = value; + }); + + Config.save(); + } + + switchAllowFlyWhenRemainThreePieces(bool value) async { + // + setState(() { + Game.shared.position.rule.allowFlyWhenRemainThreePieces = + Config.allowFlyWhenRemainThreePieces = value; + }); + + Config.save(); + } + + setMaxStepsLedToDraw(int value) async { + // + setState(() { + Game.shared.position.rule.maxStepsLedToDraw = + Config.maxStepsLedToDraw = value; + }); + + Config.save(); + } + changeName() async { // final newName = await Navigator.of(context).push( @@ -404,6 +555,108 @@ class _SettingsPageState extends State { ), ), const SizedBox(height: 16), + Text(S.of(context).rules, style: headerStyle), + Card( + color: UIColors.boardBackgroundColor, + margin: const EdgeInsets.symmetric(vertical: 10), + child: Column( + children: [ + ListTile( + title: Text(S.of(context).nTotalPiecesEachSide, + style: itemStyle), + trailing: + Row(mainAxisSize: MainAxisSize.min, children: [ + Text(Config.nTotalPiecesEachSide == 6 + ? '6' + : Config.nTotalPiecesEachSide == 9 + ? '9' + : '12'), + Icon(Icons.keyboard_arrow_right, + color: UIColors.secondaryColor), + ]), + onTap: setNTotalPiecesEachSide, + ), + _buildDivider(), + SwitchListTile( + activeColor: UIColors.primaryColor, + value: Config.hasObliqueLines, + title: + Text(S.of(context).hasObliqueLines, style: itemStyle), + onChanged: switchHasObliqueLines, + ), + _buildDivider(), + SwitchListTile( + activeColor: UIColors.primaryColor, + value: Config.hasBannedLocations, + title: Text(S.of(context).hasBannedLocations, + style: itemStyle), + onChanged: switchHasBannedLocations, + ), + _buildDivider(), + SwitchListTile( + activeColor: UIColors.primaryColor, + value: Config.isDefenderMoveFirst, + title: Text(S.of(context).isDefenderMoveFirst, + style: itemStyle), + onChanged: switchIsDefenderMoveFirst, + ), + _buildDivider(), + SwitchListTile( + activeColor: UIColors.primaryColor, + value: Config.allowRemoveMultiPiecesWhenCloseMultiMill, + title: Text( + S.of(context).allowRemoveMultiPiecesWhenCloseMultiMill, + style: itemStyle), + onChanged: switchAllowRemoveMultiPiecesWhenCloseMultiMill, + ), + _buildDivider(), + SwitchListTile( + activeColor: UIColors.primaryColor, + value: Config.allowRemovePieceInMill, + title: Text(S.of(context).allowRemovePieceInMill, + style: itemStyle), + onChanged: switchAllowRemovePieceInMill, + ), + _buildDivider(), + SwitchListTile( + activeColor: UIColors.primaryColor, + value: Config.isBlackLoseButNotDrawWhenBoardFull, + title: Text( + S.of(context).isBlackLoseButNotDrawWhenBoardFull, + style: itemStyle), + onChanged: switchIsBlackLoseButNotDrawWhenBoardFull, + ), + _buildDivider(), + SwitchListTile( + activeColor: UIColors.primaryColor, + value: Config.isLoseButNotChangeSideWhenNoWay, + title: Text(S.of(context).isLoseButNotChangeSideWhenNoWay, + style: itemStyle), + onChanged: switchIsLoseButNotChangeSideWhenNoWay, + ), + _buildDivider(), + SwitchListTile( + activeColor: UIColors.primaryColor, + value: Config.allowFlyWhenRemainThreePieces, + title: Text(S.of(context).allowFlyWhenRemainThreePieces, + style: itemStyle), + onChanged: switchAllowFlyWhenRemainThreePieces, + ), + _buildDivider(), + /* + SwitchListTile( + activeColor: UIColors.primaryColor, + value: Config.maxStepsLedToDraw, + title: + Text(S.of(context).maxStepsLedToDraw, style: itemStyle), + onChanged: setMaxStepsLedToDraw, + ), + _buildDivider(), + */ + ], + ), + ), + const SizedBox(height: 16), Text(S.of(context).misc, style: headerStyle), Card( color: UIColors.boardBackgroundColor,