1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
use super::Position;
use crate::board::parser::{PositionState, SfenError, generate_sfen_with_ply, parse_sfen};
use crate::types::{Color, EnteringKingRule, Piece};
impl Position {
/// SFEN 文字列から局面を生成する。
///
/// [`position_from_sfen`](crate::board::position_from_sfen) の関連関数形式。
pub fn from_sfen(sfen: &str) -> Result<Self, SfenError> {
let mut pos = Self::empty();
pos.set_sfen(sfen)?;
Ok(pos)
}
/// SFEN文字列で盤面を設定する(既存局面の上書き)
pub fn set_sfen(&mut self, sfen: &str) -> Result<(), SfenError> {
let state = parse_sfen(sfen)?;
self.apply_position_state(&state);
Ok(())
}
/// [`PositionState`] から盤面を設定する
///
/// [`set_sfen()`](Self::set_sfen) が SFEN 文字列を受け取るのに対し、
/// こちらは構造化済みの `PositionState` を直接受け取る。
/// エディタ等で個別フィールドを変更した `PositionState` を局面に反映する用途に使用する。
///
/// 盤面キャッシュやハッシュは再構築するが、局面のルール妥当性は検証しない。
/// import 後に妥当性も確認したい場合は、`validation` feature を有効にして
/// `validate()` / `validate_all()` を使う。
///
/// # Examples
///
/// ```
/// use rsshogi::board::{self, Position, parse_sfen};
///
/// board::init();
/// let mut state = parse_sfen(
/// "lnsgkgsnl/1r5b1/ppppppppp/9/9/9/PPPPPPPPP/1B5R1/LNSGKGSNL b - 1"
/// ).unwrap();
/// state.ply = 10; // 手数を変更
///
/// let mut pos = Position::empty();
/// pos.set_position_state(&state);
/// assert_eq!(pos.game_ply(), 10);
/// ```
pub fn set_position_state(&mut self, state: &PositionState) {
self.apply_position_state(state);
}
/// 平手初期局面に設定する
pub fn set_hirate(&mut self) {
self.set_sfen(crate::board::STARTPOS_SFEN).expect("Invalid startpos SFEN");
}
fn apply_position_state(&mut self, state: &PositionState) {
self.board = state.board;
self.hands = state.hands;
self.set_side_to_move(state.side_to_move);
self.ply = state.ply;
self.entering_king_rule = EnteringKingRule::None;
self.entering_king_point = [0, 0];
// ビットボードを再構築
self.rebuild_bitboards();
// Zobrist ハッシュは root state の再構築時にまとめて全再計算する。
self.reset_state_stack_to_current_position();
self.debug_assert_partial_keys_consistent();
}
/// SFEN文字列に変換。
#[must_use]
/// `None` の場合は現在の手数を使用する。
/// `Some(x)` で負数を指定した場合は手数を出力しない。
pub fn to_sfen(&self, game_ply: Option<i32>) -> String {
let ply = game_ply.unwrap_or_else(|| i32::from(self.ply));
let ply = if ply < 0 { None } else { Some(ply) };
generate_sfen_with_ply(self, ply)
}
/// 先後反転(盤面を180度回転し、駒色と手番を反転)したSFENを取得
#[must_use]
/// `None` の場合は現在の手数を使用する。
/// `Some(x)` で負数を指定した場合は手数を出力しない。
pub fn to_sfen_flipped(&self, game_ply: Option<i32>) -> String {
let mut flipped = Self::empty();
for (sq, piece) in self.board.iter() {
if piece.is_empty() {
continue;
}
let flipped_piece = Piece::from_parts(piece.color().flip(), piece.piece_type());
flipped.board.set(sq.flip(), flipped_piece);
}
flipped.hands[Color::BLACK.to_index()] = self.hands[Color::WHITE.to_index()];
flipped.hands[Color::WHITE.to_index()] = self.hands[Color::BLACK.to_index()];
flipped.set_side_to_move(self.turn().flip());
flipped.ply = self.ply;
let ply = game_ply.unwrap_or_else(|| i32::from(self.ply));
let ply = if ply < 0 { None } else { Some(ply) };
generate_sfen_with_ply(&flipped, ply)
}
}