Skip to main content

usiagent/
command.rs

1//! USIプロトコル準拠のコマンドを取り扱う
2use std::collections::HashSet;
3use std::clone::Clone;
4
5use shogi::*;
6use Validate;
7/// USIプロトコル準拠のコマンド
8#[derive(Debug,Eq,PartialEq)]
9pub enum UsiCommand {
10	/// usiok
11	UsiOk,
12	/// id name {name}, id author {author}
13	UsiId(String, String),
14	/// readyok
15	UsiReadyOk,
16	/// bestmove
17	UsiBestMove(BestMove),
18	/// info
19	UsiInfo(Vec<UsiInfoSubCommand>),
20	/// option
21	UsiOption(String,UsiOptType),
22	/// checkmate
23	UsiCheckMate(CheckMate),
24}
25/// 指し手
26#[derive(Clone, Copy, Eq, PartialOrd, PartialEq, Debug)]
27pub enum BestMove {
28	/// 通常の指し手(ponderをOptionで指定可能)
29	Move(Move,Option<Move>),
30	/// 投了
31	Resign,
32	/// 入玉勝ち宣言
33	Win,
34	/// 中断(USIプロトコルの仕様にはない。返してもGUI側にコマンドは送信されない)
35	Abort,
36}
37/// infoコマンドのサブコマンド
38#[derive(Clone, Debug,Eq,PartialEq)]
39pub enum UsiInfoSubCommand {
40	/// depth
41	Depth(u32),
42	/// seldepth
43	SelDepth(u32),
44	/// time
45	Time(u64),
46	/// nodes
47	Nodes(u64),
48	/// pv
49	Pv(Vec<Move>),
50	/// multipv
51	MultiPv(u32),
52	/// score
53	Score(UsiScore),
54	/// currmove
55	CurrMove(Move),
56	/// hashfull
57	Hashfull(u64),
58	/// nps
59	Nps(u64),
60	/// string
61	Str(String),
62}
63/// infoサブコマンドの種別
64#[derive(Clone, Copy, Hash, Eq, PartialEq, Debug)]
65pub enum UsiInfoSubCommandKind {
66	/// depth
67	Depth,
68	/// seldepth
69	SelDepth,
70	/// time
71	Time,
72	/// nodes
73	Nodes,
74	/// pv
75	Pv,
76	/// multipv
77	MultiPv,
78	/// score
79	Score,
80	/// currmove
81	CurMove,
82	/// hashfull
83	Hashfull,
84	/// nps
85	Nps,
86	/// string
87	Str,
88}
89/// infoコマンドのscore
90#[derive(Clone,Debug,Eq,PartialEq)]
91pub enum UsiScore {
92	/// score cp <x>
93	Cp(i64),
94	/// score cp upper
95	CpUpper(i64),
96	/// score cp lower
97	CpLower(i64),
98	/// score mate <y>
99	Mate(UsiScoreMate),
100	/// score mate upper
101	MateUpper(i64),
102	/// score mate lower
103	MateLower(i64),
104}
105/// infoコマンドのscoreサブコマンドのmateの値
106#[derive(Clone,Debug,Eq,PartialEq)]
107pub enum UsiScoreMate {
108	/// 数値
109	Num(i64),
110	/// \+
111	Plus,
112	/// \-
113	Minus,
114}
115/// infoコマンド
116#[derive(Clone, Debug, Eq, PartialEq)]
117pub struct UsiInfoCommand(pub Vec<UsiInfoSubCommand>);
118/// 詰め将棋の解答
119#[derive(Debug,Eq,PartialEq)]
120pub enum CheckMate {
121	/// 詰みまでの指し手
122	Moves(Vec<Move>),
123	/// 未実装であることをGUI側に伝える
124	NotiImplemented,
125	/// 時間内に詰みを見つけられなかった
126	Timeout,
127	/// 詰まない
128	Nomate,
129	/// 中断(USIプロトコルの仕様にはない。返してもGUI側にコマンドは送信されない)
130	Abort,
131}
132/// optionコマンドの値
133#[derive(Debug,Eq,PartialEq)]
134pub enum UsiOptType {
135	/// check
136	///
137	/// デフォルト値としてtrueかfalseを指定可能
138	Check(Option<bool>),
139	/// spin
140	///
141	/// min,max,デフォルト値(オプション)を指定
142	Spin(i64, i64,Option<i64>),
143	/// combo
144	///
145	/// デフォルト値、項目のVecを指定。項目は一つ以上なければならない。
146	Combo(Option<String>, Vec<String>),
147	/// button
148	Button,
149	/// string
150	///
151	/// デフォルト値を指定可能
152	String(Option<String>),
153	/// filename
154	///
155	/// デフォルト値を指定可能
156	FileName(Option<String>),
157}
158impl Clone for UsiOptType {
159	fn clone(&self) -> UsiOptType {
160		match *self {
161			UsiOptType::Check(None) => UsiOptType::Check(None),
162			UsiOptType::Check(Some(b)) => UsiOptType::Check(Some(b)),
163			UsiOptType::Spin(l,u,None) => UsiOptType::Spin(l,u,None),
164			UsiOptType::Spin(l,u,Some(d)) => UsiOptType::Spin(l,u,Some(d)),
165			UsiOptType::Combo(None, ref i) => UsiOptType::Combo(None, i.iter().map(|s| s.clone())
166																.collect::<Vec<String>>()),
167			UsiOptType::Combo(Some(ref d), ref i) => UsiOptType::Combo(Some(d.clone()), i.iter().map(|s| s.clone())
168																.collect::<Vec<String>>()),
169			UsiOptType::Button => UsiOptType::Button,
170			UsiOptType::String(None) => UsiOptType::String(None),
171			UsiOptType::String(Some(ref s)) => UsiOptType::String(Some(s.clone())),
172			UsiOptType::FileName(None) => UsiOptType::FileName(None),
173			UsiOptType::FileName(Some(ref s)) => UsiOptType::FileName(Some(s.clone())),
174		}
175	}
176}
177impl Validate for UsiCommand {
178	fn validate(&self) -> bool {
179		match *self {
180			UsiCommand::UsiBestMove(BestMove::Move(ref m,_)) if !m.validate() => false,
181			UsiCommand::UsiBestMove(BestMove::Move(_,Some(ref m))) if !m.validate() => false,
182			UsiCommand::UsiInfo(ref commands) => {
183				let mut hs = HashSet::new();
184				let mut prev_kind = None;
185
186				for cmd in commands {
187					match *cmd {
188						UsiInfoSubCommand::Pv(_) if hs.contains(&UsiInfoSubCommandKind::Str) => {
189							return false;
190						},
191						UsiInfoSubCommand::Str(_) if hs.contains(&UsiInfoSubCommandKind::Pv) => {
192							return false;
193						},
194						UsiInfoSubCommand::SelDepth(_) if !prev_kind.map(|k| k == UsiInfoSubCommandKind::Depth).unwrap_or(false) => {
195							return false;
196						},
197						ref c @ UsiInfoSubCommand::Pv(_) => {
198							return c.validate();
199						},
200						ref c @ UsiInfoSubCommand::CurrMove(_) => {
201							c.validate();
202						}
203						_ => (),
204					}
205					if hs.contains(&cmd.get_kind()) {
206						return false;
207					}
208					else {
209						let kind = cmd.get_kind();
210						hs.insert(kind);
211						prev_kind = Some(kind);
212					}
213				}
214
215				if hs.contains(&UsiInfoSubCommandKind::MultiPv) && !hs.contains(&UsiInfoSubCommandKind::Pv) {
216					false
217				} else {
218					true
219				}
220			},
221			UsiCommand::UsiOption(_,ref opt) => opt.validate(),
222			UsiCommand::UsiCheckMate(ref c) => c.validate(),
223			_ => true
224		}
225	}
226}
227impl UsiInfoSubCommand {
228	/// 対応するコマンド種別を取得する
229	pub fn get_kind(&self) -> UsiInfoSubCommandKind {
230		match *self {
231			UsiInfoSubCommand::Depth(_) => UsiInfoSubCommandKind::Depth,
232			UsiInfoSubCommand::SelDepth(_) => UsiInfoSubCommandKind::SelDepth,
233			UsiInfoSubCommand::Time(_) => UsiInfoSubCommandKind::Time,
234			UsiInfoSubCommand::Nodes(_) => UsiInfoSubCommandKind::Nodes,
235			UsiInfoSubCommand::Pv(_) => UsiInfoSubCommandKind::Pv,
236			UsiInfoSubCommand::MultiPv(_) => UsiInfoSubCommandKind::MultiPv,
237			UsiInfoSubCommand::Score(_) => UsiInfoSubCommandKind::Score,
238			UsiInfoSubCommand::CurrMove(_) => UsiInfoSubCommandKind::CurMove,
239			UsiInfoSubCommand::Hashfull(_) => UsiInfoSubCommandKind::Hashfull,
240			UsiInfoSubCommand::Nps(_) => UsiInfoSubCommandKind::Nps,
241			UsiInfoSubCommand::Str(_) => UsiInfoSubCommandKind::Str,
242		}
243	}
244}
245impl Validate for UsiInfoSubCommand {
246	fn validate(&self) -> bool {
247		match *self {
248			UsiInfoSubCommand::Pv(ref v) if v.len() < 1 => false,
249			UsiInfoSubCommand::Pv(ref v) => {
250				for m in v {
251					match *m {
252						ref mv if !mv.validate() => {
253							return false;
254						},
255						_ => (),
256					}
257				}
258				true
259			},
260			UsiInfoSubCommand::CurrMove(ref m) if !m.validate() => false,
261			_ => true,
262		}
263	}
264}
265impl Validate for CheckMate {
266	fn validate(&self) -> bool {
267		match *self {
268			CheckMate::Moves(ref v) if v.len() < 1 => false,
269			CheckMate::Moves(ref v) => {
270				for m in v {
271					match m.validate() {
272						false => {
273							return false;
274						},
275						_ => (),
276					}
277				}
278				true
279			},
280			_ => true,
281		}
282	}
283}
284impl Validate for UsiOptType {
285	fn validate(&self) -> bool {
286		match *self {
287			UsiOptType::Combo(_,ref l) if l.len() < 1 => false,
288			_ => true,
289		}
290	}
291}