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
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
//! USIプロトコル準拠のコマンドを取り扱う
use std::collections::HashSet;
use std::clone::Clone;

use shogi::*;
use Validate;
/// USIプロトコル準拠のコマンド
#[derive(Debug,Eq,PartialEq)]
pub enum UsiCommand {
	/// usiok
	UsiOk,
	/// id name {name}, id author {author}
	UsiId(String, String),
	/// readyok
	UsiReadyOk,
	/// bestmove
	UsiBestMove(BestMove),
	/// info
	UsiInfo(Vec<UsiInfoSubCommand>),
	/// option
	UsiOption(String,UsiOptType),
	/// checkmate
	UsiCheckMate(CheckMate),
}
/// 指し手
#[derive(Clone, Copy, Eq, PartialOrd, PartialEq, Debug)]
pub enum BestMove {
	/// 通常の指し手(ponderをOptionで指定可能)
	Move(Move,Option<Move>),
	/// 投了
	Resign,
	/// 入玉勝ち宣言
	Win,
	/// 中断(USIプロトコルの仕様にはない。返してもGUI側にコマンドは送信されない)
	Abort,
}
/// infoコマンドのサブコマンド
#[derive(Clone, Debug,Eq,PartialEq)]
pub enum UsiInfoSubCommand {
	/// depth
	Depth(u32),
	/// seldepth
	SelDepth(u32),
	/// time
	Time(u64),
	/// nodes
	Nodes(u64),
	/// pv
	Pv(Vec<Move>),
	/// multipv
	MultiPv(u32),
	/// score
	Score(UsiScore),
	/// currmove
	CurrMove(Move),
	/// hashfull
	Hashfull(u64),
	/// nps
	Nps(u64),
	/// string
	Str(String),
}
/// infoサブコマンドの種別
#[derive(Clone, Copy, Hash, Eq, PartialEq, Debug)]
pub enum UsiInfoSubCommandKind {
	/// depth
	Depth,
	/// seldepth
	SelDepth,
	/// time
	Time,
	/// nodes
	Nodes,
	/// pv
	Pv,
	/// multipv
	MultiPv,
	/// score
	Score,
	/// currmove
	CurMove,
	/// hashfull
	Hashfull,
	/// nps
	Nps,
	/// string
	Str,
}
/// infoコマンドのscore
#[derive(Clone,Debug,Eq,PartialEq)]
pub enum UsiScore {
	/// score cp <x>
	Cp(i64),
	/// score cp upper
	CpUpper(i64),
	/// score cp lower
	CpLower(i64),
	/// score mate <y>
	Mate(UsiScoreMate),
	/// score mate upper
	MateUpper(i64),
	/// score mate lower
	MateLower(i64),
}
/// infoコマンドのscoreサブコマンドのmateの値
#[derive(Clone,Debug,Eq,PartialEq)]
pub enum UsiScoreMate {
	/// 数値
	Num(i64),
	/// \+
	Plus,
	/// \-
	Minus,
}
/// 詰め将棋の解答
#[derive(Debug,Eq,PartialEq)]
pub enum CheckMate {
	/// 詰みまでの指し手
	Moves(Vec<Move>),
	/// 未実装であることをGUI側に伝える
	NotiImplemented,
	/// 時間内に詰みを見つけられなかった
	Timeout,
	/// 詰まない
	Nomate,
	/// 中断(USIプロトコルの仕様にはない。返してもGUI側にコマンドは送信されない)
	Abort,
}
/// optionコマンドの値
#[derive(Debug,Eq,PartialEq)]
pub enum UsiOptType {
	/// check
	///
	/// デフォルト値としてtrueかfalseを指定可能
	Check(Option<bool>),
	/// spin
	///
	/// min,max,デフォルト値(オプション)を指定
	Spin(i64, i64,Option<i64>),
	/// combo
	///
	/// デフォルト値、項目のVecを指定。項目は一つ以上なければならない。
	Combo(Option<String>, Vec<String>),
	/// button
	Button,
	/// string
	///
	/// デフォルト値を指定可能
	String(Option<String>),
	/// filename
	///
	/// デフォルト値を指定可能
	FileName(Option<String>),
}
impl Clone for UsiOptType {
	fn clone(&self) -> UsiOptType {
		match *self {
			UsiOptType::Check(None) => UsiOptType::Check(None),
			UsiOptType::Check(Some(b)) => UsiOptType::Check(Some(b)),
			UsiOptType::Spin(l,u,None) => UsiOptType::Spin(l,u,None),
			UsiOptType::Spin(l,u,Some(d)) => UsiOptType::Spin(l,u,Some(d)),
			UsiOptType::Combo(None, ref i) => UsiOptType::Combo(None, i.iter().map(|s| s.clone())
																.collect::<Vec<String>>()),
			UsiOptType::Combo(Some(ref d), ref i) => UsiOptType::Combo(Some(d.clone()), i.iter().map(|s| s.clone())
																.collect::<Vec<String>>()),
			UsiOptType::Button => UsiOptType::Button,
			UsiOptType::String(None) => UsiOptType::String(None),
			UsiOptType::String(Some(ref s)) => UsiOptType::String(Some(s.clone())),
			UsiOptType::FileName(None) => UsiOptType::FileName(None),
			UsiOptType::FileName(Some(ref s)) => UsiOptType::FileName(Some(s.clone())),
		}
	}
}
impl Validate for UsiCommand {
	fn validate(&self) -> bool {
		match *self {
			UsiCommand::UsiBestMove(BestMove::Move(ref m,_)) if !m.validate() => false,
			UsiCommand::UsiBestMove(BestMove::Move(_,Some(ref m))) if !m.validate() => false,
			UsiCommand::UsiInfo(ref commands) => {
				let mut hs = HashSet::new();
				let mut prev_kind = None;

				for cmd in commands {
					match *cmd {
						UsiInfoSubCommand::Pv(_) if hs.contains(&UsiInfoSubCommandKind::Str) => {
							return false;
						},
						UsiInfoSubCommand::Str(_) if hs.contains(&UsiInfoSubCommandKind::Pv) => {
							return false;
						},
						UsiInfoSubCommand::SelDepth(_) if !prev_kind.map(|k| k == UsiInfoSubCommandKind::Depth).unwrap_or(false) => {
							return false;
						},
						ref c @ UsiInfoSubCommand::Pv(_) => {
							return c.validate();
						},
						ref c @ UsiInfoSubCommand::CurrMove(_) => {
							c.validate();
						}
						_ => (),
					}
					if hs.contains(&cmd.get_kind()) {
						return false;
					}
					else {
						let kind = cmd.get_kind();
						hs.insert(kind);
						prev_kind = Some(kind);
					}
				}

				if hs.contains(&UsiInfoSubCommandKind::MultiPv) && !hs.contains(&UsiInfoSubCommandKind::Pv) {
					false
				} else {
					true
				}
			},
			UsiCommand::UsiOption(_,ref opt) => opt.validate(),
			UsiCommand::UsiCheckMate(ref c) => c.validate(),
			_ => true
		}
	}
}
impl UsiInfoSubCommand {
	/// 対応するコマンド種別を取得する
	pub fn get_kind(&self) -> UsiInfoSubCommandKind {
		match *self {
			UsiInfoSubCommand::Depth(_) => UsiInfoSubCommandKind::Depth,
			UsiInfoSubCommand::SelDepth(_) => UsiInfoSubCommandKind::SelDepth,
			UsiInfoSubCommand::Time(_) => UsiInfoSubCommandKind::Time,
			UsiInfoSubCommand::Nodes(_) => UsiInfoSubCommandKind::Nodes,
			UsiInfoSubCommand::Pv(_) => UsiInfoSubCommandKind::Pv,
			UsiInfoSubCommand::MultiPv(_) => UsiInfoSubCommandKind::MultiPv,
			UsiInfoSubCommand::Score(_) => UsiInfoSubCommandKind::Score,
			UsiInfoSubCommand::CurrMove(_) => UsiInfoSubCommandKind::CurMove,
			UsiInfoSubCommand::Hashfull(_) => UsiInfoSubCommandKind::Hashfull,
			UsiInfoSubCommand::Nps(_) => UsiInfoSubCommandKind::Nps,
			UsiInfoSubCommand::Str(_) => UsiInfoSubCommandKind::Str,
		}
	}
}
impl Validate for UsiInfoSubCommand {
	fn validate(&self) -> bool {
		match *self {
			UsiInfoSubCommand::Pv(ref v) if v.len() < 1 => false,
			UsiInfoSubCommand::Pv(ref v) => {
				for m in v {
					match *m {
						ref mv if !mv.validate() => {
							return false;
						},
						_ => (),
					}
				}
				true
			},
			UsiInfoSubCommand::CurrMove(ref m) if !m.validate() => false,
			_ => true,
		}
	}
}
impl Validate for CheckMate {
	fn validate(&self) -> bool {
		match *self {
			CheckMate::Moves(ref v) if v.len() < 1 => false,
			CheckMate::Moves(ref v) => {
				for m in v {
					match m.validate() {
						false => {
							return false;
						},
						_ => (),
					}
				}
				true
			},
			_ => true,
		}
	}
}
impl Validate for UsiOptType {
	fn validate(&self) -> bool {
		match *self {
			UsiOptType::Combo(_,ref l) if l.len() < 1 => false,
			_ => true,
		}
	}
}