flutter & UCI: 增加棋规选项 (WIP)

This commit is contained in:
Calcitem 2020-12-06 22:27:53 +08:00
parent ddd9c22c17
commit 3049caff72
11 changed files with 488 additions and 12 deletions

View File

@ -708,7 +708,7 @@ int Position::pieces_in_hand_count()
return pieceCountInHand[BLACK] + pieceCountInHand[WHITE]; return pieceCountInHand[BLACK] + pieceCountInHand[WHITE];
} }
int Position::set_position(const struct Rule *newRule) int Position::set_position(/* const */ struct Rule *newRule)
{ {
rule = newRule; rule = newRule;

View File

@ -104,7 +104,7 @@ public:
/// Mill Game /// Mill Game
int set_position(const struct Rule *rule); int set_position(/* const */ struct Rule *rule);
Piece *get_board(); Piece *get_board();
Square current_square() const; Square current_square() const;

View File

@ -20,9 +20,10 @@
#include "rule.h" #include "rule.h"
#include "types.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] = {
{ {
"成三棋", // 成三棋 "成三棋", // 成三棋
// 规则说明 // 规则说明

View File

@ -40,7 +40,7 @@ struct Rule
}; };
#define N_RULES 4 #define N_RULES 4
extern const struct Rule RULES[N_RULES]; extern /* const */ struct Rule RULES[N_RULES]; // TODO
extern const struct Rule *rule; extern /* const */ struct Rule *rule;
#endif /* RULE_H */ #endif /* RULE_H */

View File

@ -35,6 +35,8 @@ using std::string;
UCI::OptionsMap Options; // Global object UCI::OptionsMap Options; // Global object
extern struct Rule *rule;
namespace UCI namespace UCI
{ {
@ -66,6 +68,62 @@ void on_random_move(const Option &o)
gameOptions.setRandomMoveEnabled((bool)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 /// Our case insensitive less() function as required by UCI protocol
bool CaseInsensitiveLess::operator() (const string &s1, const string &s2) const 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["UCI_Elo"] << Option(1350, 1350, 2850);
o["RandomMove"] << Option(true, on_random_move); 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);
} }

View File

@ -36,6 +36,19 @@ class Config {
static bool depthExtension = true; static bool depthExtension = true;
static bool openingBook = false; 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<void> loadProfile() async { static Future<void> loadProfile() async {
final profile = await Profile.shared(); final profile = await Profile.shared();
@ -53,6 +66,34 @@ class Config {
Config.depthExtension = profile['depthExtension'] ?? false; Config.depthExtension = profile['depthExtension'] ?? false;
Config.openingBook = profile['openingBook'] ?? 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; return true;
} }
@ -73,6 +114,23 @@ class Config {
profile['depthExtension'] = Config.depthExtension; profile['depthExtension'] = Config.depthExtension;
profile['openingBook'] = Config.openingBook; 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(); profile.commit();
return true; return true;

View File

@ -134,11 +134,28 @@ class NativeEngine extends AiEngine {
} }
Future<void> setOptions() async { Future<void> setOptions() async {
if (Config.randomMoveEnabled) { await send('setoption name RandomMove value ${Config.randomMoveEnabled}');
await send('setoption name RandomMove value true'); await send(
} else { 'setoption name nTotalPiecesEachSide value ${Config.nTotalPiecesEachSide}');
await send('setoption name RandomMove value false'); 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) { String getPositionFen(Position position) {

View File

@ -298,5 +298,61 @@
"misc": "Miscellaneous", "misc": "Miscellaneous",
"@misc": { "@misc": {
"description": "Miscellaneous" "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"
} }
} }

View File

@ -73,5 +73,19 @@
"idsEnabled": "迭代加深", "idsEnabled": "迭代加深",
"depthExtension": "深度扩展", "depthExtension": "深度扩展",
"openingBook": "使用开局库", "openingBook": "使用开局库",
"misc": "其他" "misc": "其他",
"rules": "棋规",
"nTotalPiecesEachSide": "棋子数",
"ninePieces": "九子",
"twelvePieces": "十二子",
"nPiecesAtLeast": "少于几个子则输棋",
"hasObliqueLines": "斜线",
"hasBannedLocations": "禁点",
"isDefenderMoveFirst": "后摆子的先走子",
"allowRemoveMultiPiecesWhenCloseMultiMill": "成几个三就吃几个子",
"allowRemovePieceInMill": "允许吃三中的子",
"isBlackLoseButNotDrawWhenBoardFull": "当棋盘摆满时先摆子的输棋",
"isLoseButNotChangeSideWhenNoWay": "当无路可走时输棋",
"allowFlyWhenRemainThreePieces": "剩余三颗子时飞棋",
"maxStepsLedToDraw": "连续多少步无吃子则和棋"
} }

View File

@ -18,6 +18,7 @@
*/ */
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:sanmill/common/config.dart';
import 'package:sanmill/style/colors.dart'; import 'package:sanmill/style/colors.dart';
import 'package:sanmill/widgets/board.dart'; import 'package:sanmill/widgets/board.dart';
@ -149,6 +150,10 @@ class BoardPainter extends PiecesBasePainter {
paint, paint,
); );
if (!Config.hasObliqueLines) {
return;
}
// top left oblique line // top left oblique line
canvas.drawLine( canvas.drawLine(
Offset(left + 0, top), Offset(left + 0, top),

View File

@ -196,6 +196,157 @@ class _SettingsPageState extends State<SettingsPage> {
Config.save(); 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: <Widget>[
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 { changeName() async {
// //
final newName = await Navigator.of(context).push( final newName = await Navigator.of(context).push(
@ -404,6 +555,108 @@ class _SettingsPageState extends State<SettingsPage> {
), ),
), ),
const SizedBox(height: 16), const SizedBox(height: 16),
Text(S.of(context).rules, style: headerStyle),
Card(
color: UIColors.boardBackgroundColor,
margin: const EdgeInsets.symmetric(vertical: 10),
child: Column(
children: <Widget>[
ListTile(
title: Text(S.of(context).nTotalPiecesEachSide,
style: itemStyle),
trailing:
Row(mainAxisSize: MainAxisSize.min, children: <Widget>[
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), Text(S.of(context).misc, style: headerStyle),
Card( Card(
color: UIColors.boardBackgroundColor, color: UIColors.boardBackgroundColor,