rule: Support setting flying piece count

This commit is contained in:
Calcitem 2021-06-06 02:33:44 +08:00
parent f886c72112
commit 1766e9577d
18 changed files with 137 additions and 36 deletions

View File

@ -139,7 +139,7 @@ Value Evaluation::value()
piece_on_board_count_future_white -= pos.piece_to_remove_count();
}
// TODO: flyPieceCount?
if (piece_on_board_count_future_black == 3 || piece_on_board_count_future_white == 3) {
if (abs(value) < VALUE_KNOWN_WIN) {
value = VALUE_DRAW;

View File

@ -530,13 +530,13 @@ Depth get_search_depth(const Position *pos)
// Can fly
if (rule.mayFly) {
if (pb == rule.piecesAtLeastCount ||
pw == rule.piecesAtLeastCount) {
if (pb <= rule.flyPieceCount ||
pw <= rule.flyPieceCount) {
d = flyingDepth;
}
if (pb == rule.piecesAtLeastCount &&
pw == rule.piecesAtLeastCount) {
if (pb <= rule.flyPieceCount &&
pw <= rule.flyPieceCount) {
d = flyingDepth / 2;
}
}

View File

@ -52,18 +52,17 @@ ExtMove *generate<MOVE>(Position &pos, ExtMove *moveList)
continue;
}
if (pos.piece_on_board_count(pos.side_to_move()) > rule.piecesAtLeastCount ||
!rule.mayFly) {
for (auto direction = MD_BEGIN; direction < MD_NB; ++direction) {
to = static_cast<Square>(MoveList<LEGAL>::adjacentSquares[from][direction]);
if (to && !pos.get_board()[to]) {
if (rule.mayFly && pos.piece_on_board_count(pos.side_to_move()) <= rule.flyPieceCount) {
// piece count < 3 or 4 and allow fly, if is empty point, that's ok, do not need in move list
for (to = SQ_BEGIN; to < SQ_END; ++to) {
if (!pos.get_board()[to]) {
*cur++ = make_move(from, to);
}
}
} else {
// piece count < 3 and allow fly, if is empty point, that's ok, do not need in move list
for (to = SQ_BEGIN; to < SQ_END; ++to) {
if (!pos.get_board()[to]) {
for (auto direction = MD_BEGIN; direction < MD_NB; ++direction) {
to = static_cast<Square>(MoveList<LEGAL>::adjacentSquares[from][direction]);
if (to && !pos.get_board()[to]) {
*cur++ = make_move(from, to);
}
}

View File

@ -699,7 +699,7 @@ bool Position::put_piece(Square s, bool updateRecord)
#endif // MUEHLE_NMM
// If illegal
if (pieceOnBoardCount[sideToMove] > rule.piecesAtLeastCount ||
if (pieceOnBoardCount[sideToMove] > rule.flyPieceCount ||
!rule.mayFly) {
if ((square_bb(s) & MoveList<LEGAL>::adjacentSquaresBB[currentSquare]) == 0) {
return false;
@ -1259,7 +1259,7 @@ bool Position::is_all_surrounded(Color c
return true;
// Can fly
if (pieceOnBoardCount[c] <= rule.piecesAtLeastCount &&
if (pieceOnBoardCount[c] <= rule.flyPieceCount &&
rule.mayFly) {
return false;
}

View File

@ -25,6 +25,7 @@ struct Rule rule = {
// 规则说明
"规则与成三棋基本相同只是在走子阶段当一方仅剩3子时他可以飞子到任意空位。",
9, // 双方各9子
3, // 飞子条件为剩余3颗子
3, // 赛点子数为3
false, // 没有斜线
false, // 没有禁点,摆棋阶段被提子的点可以再摆子
@ -49,6 +50,7 @@ const struct Rule RULES[N_RULES] = {
"6. 把对手棋子提到少于3颗时胜利\n"
"7. 走棋阶段不能行动(被“闷”)算负。",
9, // 双方各9子
3, // 飞子条件为剩余3颗子
3, // 赛点子数为3
false, // 没有斜线
false, // 没有禁点,摆棋阶段被提子的点可以再摆子
@ -70,6 +72,7 @@ const struct Rule RULES[N_RULES] = {
"5. 同时出现两个“三连”只能提一子;\n"
"6. 其它规则与成三棋基本相同。",
12, // 双方各12子
3, // 飞子条件为剩余3颗子
3, // 赛点子数为3
true, // 有斜线
true, // 有禁点,摆棋阶段被提子的点不能再摆子
@ -86,6 +89,7 @@ const struct Rule RULES[N_RULES] = {
// 规则说明
"规则与成三棋基本相同只是在走子阶段当一方仅剩3子时他可以飞子到任意空位。",
9, // 双方各9子
3, // 飞子条件为剩余3颗子
3, // 赛点子数为3
false, // 没有斜线
false, // 没有禁点,摆棋阶段被提子的点可以再摆子
@ -107,6 +111,7 @@ const struct Rule RULES[N_RULES] = {
"5. 同时出现两个“三连”只能提一子;\n"
"6. 其它规则与成三棋基本相同。",
12, // 双方各12子
3, // 飞子条件为剩余3颗子
3, // 赛点子数为3
true, // 有斜线
false, // 没有禁点,摆棋阶段被提子的点可以再摆子

View File

@ -31,6 +31,9 @@ struct Rule
// The number of pieces each player has
int piecesCount;
// When a player is reduced to N pieces, his pieces are free to move to any unoccupied point
int flyPieceCount;
int piecesAtLeastCount; // Default is 3
// Add four diagonal lines to the board.
@ -58,7 +61,7 @@ struct Rule
// Change side to move if this option is disabled.
bool isLoseButNotChangeSideWhenNoWay;
// Player may fly if he is down to three pieces.
// Player may fly if he is down to three or four (configurable) pieces.
bool mayFly;
// If a player has only three pieces left,

View File

@ -112,7 +112,7 @@ typedef uint32_t Key;
typedef uint32_t Bitboard;
constexpr int MAX_MOVES = 64;
constexpr int MAX_MOVES = 72; // (24 - 4 - 3) * 4 = 68
constexpr int MAX_PLY = 48;
enum Move : int32_t
@ -155,7 +155,7 @@ enum class Phase : uint16_t
// - Move a piece on the board:
// - Slide a piece between two adjacent locations;
// - 'Jump' a piece to any empty location if the player has less than
// three pieces and mayFly is |true|;
// three or four pieces and mayFly is |true|;
// - Remove an opponent's piece after successfully closing a mill.
enum class Action : uint16_t
{

View File

@ -87,6 +87,11 @@ void on_piecesCount(const Option &o)
rule.piecesCount = (int)o;
}
void on_flyPieceCount(const Option &o)
{
rule.flyPieceCount = (int)o;
}
void on_piecesAtLeastCount(const Option &o)
{
rule.piecesAtLeastCount = (int)o;
@ -175,6 +180,7 @@ void init(OptionsMap &o)
// Rules
o["PiecesCount"] << Option(9, 9, 12, on_piecesCount);
o["flyPieceCount"] << Option(3, 3, 4, on_flyPieceCount);
o["PiecesAtLeastCount"] << Option(3, 3, 5, on_piecesAtLeastCount);
o["HasDiagonalLines"] << Option(false, on_hasDiagonalLines);
o["HasBannedLocations"] << Option(false, on_hasBannedLocations);

View File

@ -65,6 +65,7 @@ class Config {
// Rules
static int piecesCount = 9;
static int flyPieceCount = 3;
static int piecesAtLeastCount = 3;
static bool hasDiagonalLines = false;
static bool hasBannedLocations = false;
@ -131,6 +132,7 @@ class Config {
// Rules
rule.piecesCount = Config.piecesCount = settings['PiecesCount'] ?? 9;
rule.flyPieceCount = Config.flyPieceCount = settings['FlyPieceCount'] ?? 3;
rule.piecesAtLeastCount =
Config.piecesAtLeastCount = settings['PiecesAtLeastCount'] ?? 3;
rule.hasDiagonalLines =
@ -201,6 +203,7 @@ class Config {
// Rules
settings['PiecesCount'] = Config.piecesCount;
settings['FlyPieceCount'] = Config.flyPieceCount;
settings['PiecesAtLeastCount'] = Config.piecesAtLeastCount;
settings['HasDiagonalLines'] = Config.hasDiagonalLines;
settings['HasBannedLocations'] = Config.hasBannedLocations;

View File

@ -143,6 +143,7 @@ class NativeEngine extends Engine {
await send('setoption name AiIsLazy value ${Config.aiIsLazy}');
await send('setoption name Shuffling value ${Config.shufflingEnabled}');
await send('setoption name PiecesCount value ${Config.piecesCount}');
await send('setoption name FlyPieceCount value ${Config.flyPieceCount}');
await send(
'setoption name PiecesAtLeastCount value ${Config.piecesAtLeastCount}');
await send(

View File

@ -472,6 +472,14 @@
"@piecesCount_Detail": {
"description": "How many pieces does each player have?"
},
"flyPieceCount": "Anzahl der fliegenden Steine",
"@flyPieceCount": {
"description": "The number of flying piece"
},
"flyPieceCount_Detail": "Wenn Fliegen aktiviert ist und ein Spieler auf eine bestimmte Steinzahl reduziert wird, können sich seine Steine frei zu jedem unbesetzten Punkt bewegen, anstatt wie im Rest des Spiels auf benachbarte Punkte beschränkt zu sein.",
"@flyPieceCount_Detail": {
"description": "If Flying is enabled, when a player is reduced to specific piece count, her pieces are free to move to any unoccupied point, instead of being restricted to adjacent points as in the rest of the game."
},
"ninePieces": "Neun Steine",
"@ninePieces": {
"description": "Nine Pieces"
@ -546,7 +554,7 @@
},
"mayFly_Detail": "Wenn ein Spieler nur noch drei Steine hat, darf ein Stein an beliebige Punkte gezogen werden.",
"@mayFly_Detail": {
"description": "If a player has only three pieces left, she can move the piece to any free point."
"description": "If a player has only three or four (configurable) pieces left, she can move the piece to any free point."
},
"maxStepsLedToDraw": "Maximale Schritte bis unentschieden",
"@maxStepsLedToDraw": {

View File

@ -472,6 +472,14 @@
"@piecesCount_Detail": {
"description": "How many pieces does each player have?"
},
"flyPieceCount": "The number of flying piece",
"@flyPieceCount": {
"description": "The number of flying piece"
},
"flyPieceCount_Detail": "If Flying is enabled, when a player is reduced to specific piece count, her pieces are free to move to any unoccupied point, instead of being restricted to adjacent points as in the rest of the game.",
"@flyPieceCount_Detail": {
"description": "If Flying is enabled, when a player is reduced to specific piece count, her pieces are free to move to any unoccupied point, instead of being restricted to adjacent points as in the rest of the game."
},
"ninePieces": "Nine Pieces",
"@ninePieces": {
"description": "Nine Pieces"
@ -544,9 +552,9 @@
"@mayFly": {
"description": "Flying"
},
"mayFly_Detail": "If a player has only three pieces left, she can move the piece to any free point.",
"mayFly_Detail": "If a player has only three or four (configurable) pieces left, she can move the piece to any free point.",
"@mayFly_Detail": {
"description": "If a player has only three pieces left, she can move the piece to any free point."
"description": "If a player has only three or four (configurable) pieces left, she can move the piece to any free point."
},
"maxStepsLedToDraw": "Max Steps Led to Draw",
"@maxStepsLedToDraw": {

View File

@ -472,6 +472,14 @@
"@piecesCount_Detail": {
"description": "How many pieces does each player have?"
},
"flyPieceCount": "تعداد قطعه پرواز",
"@flyPieceCount": {
"description": "The number of flying piece"
},
"flyPieceCount_Detail": "اگر پرواز کردن فعال باشد ، هنگامی که یک بازیکن به تعداد قطعه های خاص کاهش می یابد ، مهره های او آزاد هستند و به جای محدود شدن به نقاط مجاور ، مانند سایر قسمت های بازی ، به هر نقطه اشغال نشده منتقل می شوند.",
"@flyPieceCount_Detail": {
"description": "If Flying is enabled, when a player is reduced to specific piece count, her pieces are free to move to any unoccupied point, instead of being restricted to adjacent points as in the rest of the game."
},
"ninePieces": "9 قطعه",
"@ninePieces": {
"description": "Nine Pieces"
@ -544,9 +552,9 @@
"@mayFly": {
"description": "Flying"
},
"mayFly_Detail": "اگر به بازیکنی فقط سه قطعه مانده باشد ، می تواند قطعه را به هر نقطه آزاد منتقل کند.",
"mayFly_Detail": "اگر به بازیکن فقط سه یا چهار قطعه (با قابلیت تنظیم) باقی مانده باشد ، می تواند قطعه را به هر نقطه آزاد منتقل کند.",
"@mayFly_Detail": {
"description": "If a player has only three pieces left, she can move the piece to any free point."
"description": "If a player has only three or four (configurable) pieces left, she can move the piece to any free point."
},
"maxStepsLedToDraw": "مراحل حداکثر منجر به قرعه کشی",
"@maxStepsLedToDraw": {

View File

@ -118,6 +118,8 @@
"rules": "棋规",
"piecesCount": "棋子数",
"piecesCount_Detail": "设置对弈双方分别拥有的棋子数。",
"flyPieceCount": "剩余多少子可飞子",
"flyPieceCount_Detail": "如允许飞子,则当一方剩余多少枚棋子时可以飞棋。",
"ninePieces": "九子",
"twelvePieces": "十二子",
"piecesAtLeastCount": "少于几个子则输棋",
@ -136,7 +138,7 @@
"isLoseButNotChangeSideWhenNoWay": "当无路可走时输棋",
"isLoseButNotChangeSideWhenNoWay_Detail": "走子阶段,当无路可走时输棋,而非转为由对方继续走子。",
"mayFly": "飞子",
"mayFly_Detail": "当一方剩余三枚棋子时,此方可将棋子移动到棋盘任意空位上。",
"mayFly_Detail": "当一方剩余三或四(可配置)棋子时,此方可将棋子移动到棋盘任意空位上。",
"maxStepsLedToDraw": "连续多少步无吃子则和棋",
"rollback": "回滚",
"pleaseSelect": "请选择",

View File

@ -533,7 +533,7 @@ class Position {
}
// if illegal
if (pieceOnBoardCount[sideToMove()]! > rule.piecesAtLeastCount ||
if (pieceOnBoardCount[sideToMove()]! > rule.flyPieceCount ||
!rule.mayFly) {
int md;
@ -929,8 +929,7 @@ class Position {
}
// Can fly
if (pieceOnBoardCount[sideToMove()]! <= rule.piecesAtLeastCount &&
rule.mayFly) {
if (pieceOnBoardCount[sideToMove()]! <= rule.flyPieceCount && rule.mayFly) {
return false;
}

View File

@ -20,6 +20,7 @@ class Rule {
String name = "Nine Men's Morris";
String description = "";
int piecesCount = 9;
int flyPieceCount = 3;
int piecesAtLeastCount = 3;
bool hasDiagonalLines = false;
bool hasBannedLocations = false;

View File

@ -290,7 +290,8 @@ class _GamePageState extends State<GamePage>
var us = Game.instance.sideToMove;
if (position.phase == Phase.moving &&
rule.mayFly &&
Game.instance.position.pieceOnBoardCount[us] == 3) {
(Game.instance.position.pieceOnBoardCount[us] ==
Config.flyPieceCount || Game.instance.position.pieceOnBoardCount[us] == 3)) {
print("[tap] May fly.");
if (mounted) {
showTip(S.of(context).tipCanMoveToAnyPoint);

View File

@ -76,14 +76,6 @@ class _RuleSettingsPageState extends State<RuleSettingsPage> {
subtitleString: S.of(context).hasDiagonalLines_Detail,
),
ListItemDivider(),
SettingsSwitchListTile(
context: context,
value: Config.mayFly,
onChanged: setAllowFlyingAllowed,
titleString: S.of(context).mayFly,
subtitleString: S.of(context).mayFly_Detail,
),
ListItemDivider(),
/*
SettingsSwitchListTile(
context: context,
@ -142,6 +134,28 @@ class _RuleSettingsPageState extends State<RuleSettingsPage> {
],
),
SizedBox(height: AppTheme.sizedBoxHeight),
Text(S.of(context).mayFly, style: AppTheme.settingsHeaderStyle),
SettingsCard(
context: context,
children: <Widget>[
SettingsSwitchListTile(
context: context,
value: Config.mayFly,
onChanged: setAllowFlyingAllowed,
titleString: S.of(context).mayFly,
subtitleString: S.of(context).mayFly_Detail,
),
ListItemDivider(),
SettingsListTile(
context: context,
titleString: S.of(context).flyPieceCount,
subtitleString: S.of(context).flyPieceCount_Detail,
trailingString: Config.flyPieceCount.toString(),
onTap: setFlyPieceCount,
),
],
),
SizedBox(height: AppTheme.sizedBoxHeight),
Text(S.of(context).removing, style: AppTheme.settingsHeaderStyle),
SettingsCard(
context: context,
@ -226,6 +240,49 @@ class _RuleSettingsPageState extends State<RuleSettingsPage> {
);
}
// General
setFlyPieceCount() {
callback(int? flyPieceCount) async {
print("[config] flyPieceCount = $flyPieceCount");
Navigator.of(context).pop();
setState(() {
rule.flyPieceCount = Config.flyPieceCount = flyPieceCount ?? 3;
});
print("[config] rule.flyPieceCount: ${rule.flyPieceCount}");
Config.save();
}
showModalBottomSheet(
context: context,
builder: (BuildContext context) => Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
RadioListTile(
activeColor: AppTheme.switchListTileActiveColor,
title: Text('3'),
groupValue: Config.flyPieceCount,
value: 3,
onChanged: callback,
),
ListItemDivider(),
RadioListTile(
activeColor: AppTheme.switchListTileActiveColor,
title: Text('4'),
groupValue: Config.flyPieceCount,
value: 4,
onChanged: callback,
),
ListItemDivider(),
],
),
);
}
setHasDiagonalLines(bool value) async {
setState(() {
rule.hasDiagonalLines = Config.hasDiagonalLines = value;