1use crate::choices::{Choice, Choices, MoveCategory, MOVES};
2use crate::evaluate::evaluate;
3use crate::generate_instructions::{
4 calculate_both_damage_rolls, generate_instructions_from_move_pair,
5};
6use crate::instruction::{Instruction, StateInstructions};
7use crate::mcts::{perform_mcts, MctsResult};
8use crate::search::{expectiminimax_search, iterative_deepen_expectiminimax, pick_safest};
9use crate::state::{MoveChoice, Pokemon, PokemonVolatileStatus, Side, SideConditions, State};
10use clap::Parser;
11use std::io;
12use std::io::Write;
13use std::process::exit;
14use std::str::FromStr;
15use std::sync::{Arc, Mutex};
16
17struct IOData {
18 state: State,
19 instruction_list: Vec<Vec<Instruction>>,
20 last_instructions_generated: Vec<StateInstructions>,
21}
22
23#[derive(Parser)]
24struct Cli {
25 #[clap(short, long, default_value = "")]
26 state: String,
27
28 #[clap(subcommand)]
29 subcmd: Option<SubCommand>,
30}
31
32#[derive(Parser)]
33enum SubCommand {
34 Expectiminimax(Expectiminimax),
35 IterativeDeepening(IterativeDeepening),
36 MonteCarloTreeSearch(MonteCarloTreeSearch),
37 CalculateDamage(CalculateDamage),
38 GenerateInstructions(GenerateInstructions),
39}
40
41#[derive(Parser)]
42struct Expectiminimax {
43 #[clap(short, long, required = true)]
44 state: String,
45
46 #[clap(short, long, default_value_t = false)]
47 ab_prune: bool,
48
49 #[clap(short, long, default_value_t = 2)]
50 depth: i8,
51}
52
53#[derive(Parser)]
54struct IterativeDeepening {
55 #[clap(short, long, required = true)]
56 state: String,
57
58 #[clap(short, long, default_value_t = 5000)]
59 time_to_search_ms: u64,
60}
61
62#[derive(Parser)]
63struct MonteCarloTreeSearch {
64 #[clap(short, long, required = true)]
65 state: String,
66
67 #[clap(short, long, default_value_t = 5000)]
68 time_to_search_ms: u64,
69}
70
71#[derive(Parser)]
72struct CalculateDamage {
73 #[clap(short, long, required = true)]
74 state: String,
75
76 #[clap(short = 'o', long, required = true)]
77 side_one_move: String,
78
79 #[clap(short = 't', long, required = true)]
80 side_two_move: String,
81
82 #[clap(short = 'm', long, required = false, default_value_t = false)]
83 side_one_moves_first: bool,
84}
85
86#[derive(Parser)]
87struct GenerateInstructions {
88 #[clap(short, long, required = true)]
89 state: String,
90
91 #[clap(short = 'o', long, required = true)]
92 side_one_move: String,
93
94 #[clap(short = 't', long, required = true)]
95 side_two_move: String,
96}
97
98impl Default for IOData {
99 fn default() -> Self {
100 IOData {
101 state: State::default(),
102 instruction_list: Vec::new(),
103 last_instructions_generated: Vec::new(),
104 }
105 }
106}
107
108impl SideConditions {
109 fn io_print(&self) -> String {
110 let conditions = [
111 ("aurora_veil", self.aurora_veil),
112 ("crafty_shield", self.crafty_shield),
113 ("healing_wish", self.healing_wish),
114 ("light_screen", self.light_screen),
115 ("lucky_chant", self.lucky_chant),
116 ("lunar_dance", self.lunar_dance),
117 ("mat_block", self.mat_block),
118 ("mist", self.mist),
119 ("protect", self.protect),
120 ("quick_guard", self.quick_guard),
121 ("reflect", self.reflect),
122 ("safeguard", self.safeguard),
123 ("spikes", self.spikes),
124 ("stealth_rock", self.stealth_rock),
125 ("sticky_web", self.sticky_web),
126 ("tailwind", self.tailwind),
127 ("toxic_count", self.toxic_count),
128 ("toxic_spikes", self.toxic_spikes),
129 ("wide_guard", self.wide_guard),
130 ];
131
132 let mut output = String::new();
133 for (name, value) in conditions {
134 if value != 0 {
135 output.push_str(&format!("\n {}: {}", name, value));
136 }
137 }
138 if output.is_empty() {
139 return "none".to_string();
140 }
141 output
142 }
143}
144
145impl Side {
146 fn io_print_boosts(&self) -> String {
147 format!(
148 "Attack:{}, Defense:{}, SpecialAttack:{}, SpecialDefense:{}, Speed:{}",
149 self.attack_boost,
150 self.defense_boost,
151 self.special_attack_boost,
152 self.special_defense_boost,
153 self.speed_boost
154 )
155 }
156 fn io_print(&self, available_choices: Vec<String>) -> String {
157 let reserve = self
158 .pokemon
159 .into_iter()
160 .map(|p| p.io_print_reserve())
161 .collect::<Vec<String>>();
162 format!(
163 "\nActive:{}\nVolatiles: {:?}\nBoosts: {}\nSide Conditions: {}\nPokemon: {}\nAvailable Choices: {}",
164 self.get_active_immutable().io_print_active(),
165 self.volatile_statuses,
166 self.io_print_boosts(),
167 self.side_conditions.io_print(),
168 reserve.join(", "),
169 available_choices.join(", ")
170 )
171 }
172
173 fn option_to_string(&self, option: &MoveChoice) -> String {
174 match option {
175 MoveChoice::MoveTera(index) => {
176 format!("{}-tera", self.get_active_immutable().moves[index].id).to_lowercase()
177 }
178 MoveChoice::Move(index) => {
179 format!("{}", self.get_active_immutable().moves[index].id).to_lowercase()
180 }
181 MoveChoice::Switch(index) => format!("{}", self.pokemon[*index].id).to_lowercase(),
182 MoveChoice::None => "none".to_string(),
183 }
184 }
185
186 pub fn string_to_movechoice(&self, s: &str) -> Option<MoveChoice> {
187 let s = s.to_lowercase();
188 if s == "none" {
189 return Some(MoveChoice::None);
190 }
191
192 let mut pkmn_iter = self.pokemon.into_iter();
193 while let Some(pkmn) = pkmn_iter.next() {
194 if pkmn.id.to_string().to_lowercase() == s
195 && pkmn_iter.pokemon_index != self.active_index
196 {
197 return Some(MoveChoice::Switch(pkmn_iter.pokemon_index));
198 }
199 }
200
201 let mut move_iter = self.get_active_immutable().moves.into_iter();
205 let mut move_name = s;
206 if move_name.ends_with("-tera") {
207 move_name = move_name[..move_name.len() - 5].to_string();
208 while let Some(mv) = move_iter.next() {
209 if format!("{:?}", mv.id).to_lowercase() == move_name {
210 return Some(MoveChoice::MoveTera(move_iter.pokemon_move_index));
211 }
212 }
213 } else {
214 while let Some(mv) = move_iter.next() {
215 if format!("{:?}", mv.id).to_lowercase() == move_name {
216 return Some(MoveChoice::Move(move_iter.pokemon_move_index));
217 }
218 }
219 }
220
221 None
222 }
223}
224
225impl Pokemon {
226 fn io_print_reserve(&self) -> String {
227 format!("{}:{}/{}", self.id, self.hp, self.maxhp)
228 }
229 fn io_print_active(&self) -> String {
230 let moves: Vec<String> = self
231 .moves
232 .into_iter()
233 .map(|m| format!("{:?}", m.id).to_lowercase())
234 .filter(|x| x != "none")
235 .collect();
236 format!(
237 "\n Name: {}\n HP: {}/{}\n Status: {:?}\n Ability: {:?}\n Item: {:?}\n Moves: {}",
238 self.id,
239 self.hp,
240 self.maxhp,
241 self.status,
242 self.ability,
243 self.item,
244 moves.join(", ")
245 )
246 }
247}
248
249pub fn io_get_all_options(state: &State) -> (Vec<MoveChoice>, Vec<MoveChoice>) {
250 if state.team_preview {
251 let mut s1_options = Vec::with_capacity(6);
252 let mut s2_options = Vec::with_capacity(6);
253
254 let mut pkmn_iter = state.side_one.pokemon.into_iter();
255 while let Some(_) = pkmn_iter.next() {
256 if state.side_one.pokemon[pkmn_iter.pokemon_index].hp > 0 {
257 s1_options.push(MoveChoice::Switch(pkmn_iter.pokemon_index));
258 }
259 }
260 let mut pkmn_iter = state.side_two.pokemon.into_iter();
261 while let Some(_) = pkmn_iter.next() {
262 if state.side_two.pokemon[pkmn_iter.pokemon_index].hp > 0 {
263 s2_options.push(MoveChoice::Switch(pkmn_iter.pokemon_index));
264 }
265 }
266 return (s1_options, s2_options);
267 }
268
269 let (mut s1_options, mut s2_options) = state.get_all_options();
270
271 if state.side_one.force_trapped {
272 s1_options.retain(|x| match x {
273 MoveChoice::Move(_) | MoveChoice::MoveTera(_) => true,
274 MoveChoice::Switch(_) => false,
275 MoveChoice::None => true,
276 });
277 }
278 if state.side_one.slow_uturn_move {
279 s1_options.clear();
280 let encored = state
281 .side_one
282 .volatile_statuses
283 .contains(&PokemonVolatileStatus::ENCORE);
284 state.side_one.get_active_immutable().add_available_moves(
285 &mut s1_options,
286 &state.side_one.last_used_move,
287 encored,
288 state.side_one.can_use_tera(),
289 );
290 }
291
292 if state.side_two.force_trapped {
293 s2_options.retain(|x| match x {
294 MoveChoice::Move(_) | MoveChoice::MoveTera(_) => true,
295 MoveChoice::Switch(_) => false,
296 MoveChoice::None => true,
297 });
298 }
299 if state.side_two.slow_uturn_move {
300 s2_options.clear();
301 let encored = state
302 .side_two
303 .volatile_statuses
304 .contains(&PokemonVolatileStatus::ENCORE);
305 state.side_two.get_active_immutable().add_available_moves(
306 &mut s2_options,
307 &state.side_two.last_used_move,
308 encored,
309 state.side_two.can_use_tera(),
310 );
311 }
312
313 if s1_options.len() == 0 {
314 s1_options.push(MoveChoice::None);
315 }
316 if s2_options.len() == 0 {
317 s2_options.push(MoveChoice::None);
318 }
319
320 (s1_options, s2_options)
321}
322
323fn pprint_expectiminimax_result(
324 result: &Vec<f32>,
325 s1_options: &Vec<MoveChoice>,
326 s2_options: &Vec<MoveChoice>,
327 safest_choice: &(usize, f32),
328 state: &State,
329) {
330 let s1_len = s1_options.len();
331 let s2_len = s2_options.len();
332
333 print!("{: <12}", " ");
334
335 for s2_move in s2_options.iter() {
336 match s2_move {
337 MoveChoice::MoveTera(m) => {
338 let s2_move_str =
339 format!("{}-tera", state.side_two.get_active_immutable().moves[m].id);
340 print!("{: >12}", s2_move_str.to_lowercase());
341 }
342 MoveChoice::Move(m) => {
343 let s2_move_str = format!("{}", state.side_two.get_active_immutable().moves[m].id);
344 print!("{: >12}", s2_move_str.to_lowercase());
345 }
346 MoveChoice::Switch(s) => {
347 let s2_move_str = format!(
348 "{}",
349 state.side_two.pokemon[*s].id.to_string().to_lowercase()
350 );
351 print!("{: >12}", s2_move_str);
352 }
353 MoveChoice::None => {}
354 }
355 }
356 print!("\n");
357
358 for i in 0..s1_len {
359 let s1_move_str = s1_options[i];
360 match s1_move_str {
361 MoveChoice::MoveTera(m) => {
362 let move_id = format!(
363 "{}-tera",
364 state.side_one.get_active_immutable().moves[&m].id
365 );
366 print!("{:<12}", move_id.to_string().to_lowercase());
367 }
368 MoveChoice::Move(m) => {
369 let move_id = state.side_one.get_active_immutable().moves[&m].id;
370 print!("{:<12}", move_id.to_string().to_lowercase());
371 }
372 MoveChoice::Switch(s) => {
373 let pkmn_id = &state.side_one.pokemon[s].id;
374 print!("{:<12}", pkmn_id.to_string().to_lowercase());
375 }
376 MoveChoice::None => {}
377 }
378 for j in 0..s2_len {
379 let index = i * s2_len + j;
380 print!("{number:>11.2} ", number = result[index]);
381 }
382 print!("\n");
383 }
384 match s1_options[safest_choice.0] {
385 MoveChoice::MoveTera(m) => {
386 let move_id = format!(
387 "{}-tera",
388 state.side_one.get_active_immutable().moves[&m].id
389 );
390 print!(
391 "\nSafest Choice: {}, {}\n",
392 move_id.to_string().to_lowercase(),
393 safest_choice.1
394 );
395 }
396 MoveChoice::Move(m) => {
397 let move_id = state.side_one.get_active_immutable().moves[&m].id;
398 print!(
399 "\nSafest Choice: {}, {}\n",
400 move_id.to_string().to_lowercase(),
401 safest_choice.1
402 );
403 }
404 MoveChoice::Switch(s) => {
405 let pkmn_id = &state.side_one.pokemon[s].id;
406 print!(
407 "\nSafest Choice: Switch {}, {}\n",
408 pkmn_id.to_string().to_lowercase(),
409 safest_choice.1
410 );
411 }
412 MoveChoice::None => println!("No Move"),
413 }
414}
415
416fn print_mcts_result(state: &State, result: MctsResult) {
417 let s1_joined_options = result
418 .s1
419 .iter()
420 .map(|x| {
421 format!(
422 "{},{:.2},{}",
423 get_move_id_from_movechoice(&state.side_one, &x.move_choice),
424 x.total_score,
425 x.visits
426 )
427 })
428 .collect::<Vec<String>>()
429 .join("|");
430 let s2_joined_options = result
431 .s2
432 .iter()
433 .map(|x| {
434 format!(
435 "{},{:.2},{}",
436 get_move_id_from_movechoice(&state.side_two, &x.move_choice),
437 x.total_score,
438 x.visits
439 )
440 })
441 .collect::<Vec<String>>()
442 .join("|");
443
444 println!("Total Iterations: {}", result.iteration_count);
445 println!("side one: {}", s1_joined_options);
446 println!("side two: {}", s2_joined_options);
447}
448
449fn pprint_mcts_result(state: &State, result: MctsResult) {
450 println!("\nTotal Iterations: {}\n", result.iteration_count);
451 println!("Side One:");
452 println!(
453 "\t{:<25}{:>12}{:>12}{:>10}{:>10}",
454 "Move", "Total Score", "Avg Score", "Visits", "% Visits"
455 );
456 for x in result.s1.iter() {
457 println!(
458 "\t{:<25}{:>12.2}{:>12.2}{:>10}{:>10.2}",
459 get_move_id_from_movechoice(&state.side_one, &x.move_choice),
460 x.total_score,
461 x.total_score / x.visits as f32,
462 x.visits,
463 (x.visits as f32 / result.iteration_count as f32) * 100.0
464 );
465 }
466
467 println!("Side Two:");
468 println!(
469 "\t{:<25}{:>12}{:>12}{:>10}{:>10}",
470 "Move", "Total Score", "Avg Score", "Visits", "% Visits"
471 );
472 for x in result.s2.iter() {
473 println!(
474 "\t{:<25}{:>12.2}{:>12.2}{:>10}{:>10.2}",
475 get_move_id_from_movechoice(&state.side_two, &x.move_choice),
476 x.total_score,
477 x.total_score / x.visits as f32,
478 x.visits,
479 (x.visits as f32 / result.iteration_count as f32) * 100.0
480 );
481 }
482}
483
484fn pprint_state_instruction_vector(instructions: &Vec<StateInstructions>) {
485 for (i, instruction) in instructions.iter().enumerate() {
486 println!("Index: {}", i);
487 println!("StateInstruction: {:?}", instruction);
488 }
489}
490
491fn get_move_id_from_movechoice(side: &Side, move_choice: &MoveChoice) -> String {
492 match move_choice {
493 MoveChoice::MoveTera(index) => {
494 format!("{}-tera", side.get_active_immutable().moves[&index].id).to_lowercase()
495 }
496 MoveChoice::Move(index) => {
497 format!("{}", side.get_active_immutable().moves[&index].id).to_lowercase()
498 }
499 MoveChoice::Switch(index) => format!("switch {}", side.pokemon[*index].id).to_lowercase(),
500 MoveChoice::None => "No Move".to_string(),
501 }
502}
503
504fn print_subcommand_result(
505 result: &Vec<f32>,
506 side_one_options: &Vec<MoveChoice>,
507 side_two_options: &Vec<MoveChoice>,
508 state: &State,
509) {
510 let safest = pick_safest(&result, side_one_options.len(), side_two_options.len());
511 let move_choice = side_one_options[safest.0];
512
513 let joined_side_one_options = side_one_options
514 .iter()
515 .map(|x| format!("{}", get_move_id_from_movechoice(&state.side_one, x)))
516 .collect::<Vec<String>>()
517 .join(",");
518 println!("side one options: {}", joined_side_one_options);
519
520 let joined_side_two_options = side_two_options
521 .iter()
522 .map(|x| format!("{}", get_move_id_from_movechoice(&state.side_two, x)))
523 .collect::<Vec<String>>()
524 .join(",");
525 println!("side two options: {}", joined_side_two_options);
526
527 let joined = result
528 .iter()
529 .map(|x| format!("{:.2}", x))
530 .collect::<Vec<String>>()
531 .join(",");
532 println!("matrix: {}", joined);
533 match move_choice {
534 MoveChoice::MoveTera(_) => {
535 println!(
536 "choice: {}-tera",
537 get_move_id_from_movechoice(&state.side_one, &move_choice)
538 );
539 }
540 MoveChoice::Move(_) => {
541 println!(
542 "choice: {}",
543 get_move_id_from_movechoice(&state.side_one, &move_choice)
544 );
545 }
546 MoveChoice::Switch(_) => {
547 println!(
548 "choice: switch {}",
549 get_move_id_from_movechoice(&state.side_one, &move_choice)
550 );
551 }
552 MoveChoice::None => {
553 println!("no move");
554 }
555 }
556 println!("evaluation: {}", safest.1);
557}
558
559pub fn main() {
560 let args = Cli::parse();
561 let mut io_data = IOData::default();
562
563 if args.state != "" {
564 let state = State::deserialize(args.state.as_str());
565 io_data.state = state;
566 }
567
568 let result;
569 let mut state;
570 let mut side_one_options;
571 let mut side_two_options;
572 match args.subcmd {
573 None => {
574 command_loop(io_data);
575 exit(0);
576 }
577 Some(subcmd) => match subcmd {
578 SubCommand::Expectiminimax(expectiminimax) => {
579 state = State::deserialize(expectiminimax.state.as_str());
580 (side_one_options, side_two_options) = io_get_all_options(&state);
581 result = expectiminimax_search(
582 &mut state,
583 expectiminimax.depth,
584 side_one_options.clone(),
585 side_two_options.clone(),
586 expectiminimax.ab_prune,
587 &Arc::new(Mutex::new(true)),
588 );
589 print_subcommand_result(&result, &side_one_options, &side_two_options, &state);
590 }
591 SubCommand::IterativeDeepening(iterative_deepending) => {
592 state = State::deserialize(iterative_deepending.state.as_str());
593 (side_one_options, side_two_options) = io_get_all_options(&state);
594 (side_one_options, side_two_options, result, _) = iterative_deepen_expectiminimax(
595 &mut state,
596 side_one_options.clone(),
597 side_two_options.clone(),
598 std::time::Duration::from_millis(iterative_deepending.time_to_search_ms),
599 );
600 print_subcommand_result(&result, &side_one_options, &side_two_options, &state);
601 }
602 SubCommand::MonteCarloTreeSearch(mcts) => {
603 state = State::deserialize(mcts.state.as_str());
604 (side_one_options, side_two_options) = io_get_all_options(&state);
605 let result = perform_mcts(
606 &mut state,
607 side_one_options.clone(),
608 side_two_options.clone(),
609 std::time::Duration::from_millis(mcts.time_to_search_ms),
610 );
611 print_mcts_result(&state, result);
612 }
613 SubCommand::CalculateDamage(calculate_damage) => {
614 state = State::deserialize(calculate_damage.state.as_str());
615 let mut s1_choice = MOVES
616 .get(&Choices::from_str(calculate_damage.side_one_move.as_str()).unwrap())
617 .unwrap()
618 .to_owned();
619 let mut s2_choice = MOVES
620 .get(&Choices::from_str(calculate_damage.side_two_move.as_str()).unwrap())
621 .unwrap()
622 .to_owned();
623 let s1_moves_first = calculate_damage.side_one_moves_first;
624 if calculate_damage.side_one_move == "switch" {
625 s1_choice.category = MoveCategory::Switch
626 }
627 if calculate_damage.side_two_move == "switch" {
628 s2_choice.category = MoveCategory::Switch
629 }
630 calculate_damage_io(&state, s1_choice, s2_choice, s1_moves_first);
631 }
632 SubCommand::GenerateInstructions(generate_instructions) => {
633 state = State::deserialize(generate_instructions.state.as_str());
634 let (s1_movechoice, s2_movechoice);
635 match state
636 .side_one
637 .string_to_movechoice(generate_instructions.side_one_move.as_str())
638 {
639 None => {
640 println!(
641 "Invalid move choice for side one: {}",
642 generate_instructions.side_one_move
643 );
644 exit(1);
645 }
646 Some(v) => s1_movechoice = v,
647 }
648 match state
649 .side_two
650 .string_to_movechoice(generate_instructions.side_two_move.as_str())
651 {
652 None => {
653 println!(
654 "Invalid move choice for side two: {}",
655 generate_instructions.side_two_move
656 );
657 exit(1);
658 }
659 Some(v) => s2_movechoice = v,
660 }
661 let instructions = generate_instructions_from_move_pair(
662 &mut state,
663 &s1_movechoice,
664 &s2_movechoice,
665 true,
666 );
667 pprint_state_instruction_vector(&instructions);
668 }
669 },
670 }
671
672 exit(0);
673}
674
675fn calculate_damage_io(
676 state: &State,
677 s1_choice: Choice,
678 s2_choice: Choice,
679 side_one_moves_first: bool,
680) {
681 let (damages_dealt_s1, damages_dealt_s2) =
682 calculate_both_damage_rolls(state, s1_choice, s2_choice, side_one_moves_first);
683
684 for dmg in [damages_dealt_s1, damages_dealt_s2] {
685 match dmg {
686 Some(damages_vec) => {
687 let joined = damages_vec
688 .iter()
689 .map(|x| format!("{:?}", x))
690 .collect::<Vec<String>>()
691 .join(",");
692 println!("Damage Rolls: {}", joined);
693 }
694 None => {
695 println!("Damage Rolls: 0");
696 }
697 }
698 }
699}
700
701fn command_loop(mut io_data: IOData) {
702 loop {
703 print!("> ");
704 let _ = io::stdout().flush();
705
706 let mut input = String::new();
707 match io::stdin().read_line(&mut input) {
708 Ok(_) => {}
709 Err(error) => {
710 println!("Error reading input: {}", error);
711 continue;
712 }
713 }
714 let mut parts = input.trim().split_whitespace();
715 let command = parts.next().unwrap_or("");
716 let mut args = parts;
717
718 match command {
719 "state" | "s" => {
720 let state_string;
721 match args.next() {
722 Some(s) => {
723 state_string = s;
724 let state = State::deserialize(state_string);
725 io_data.state = state;
726 println!("state initialized");
727 }
728 None => {
729 println!("Expected state string");
730 }
731 }
732 println!("{:?}", io_data.state);
733 }
734 "serialize" | "ser" => {
735 println!("{}", io_data.state.serialize());
736 }
737 "matchup" | "m" => {
738 let (side_one_options, side_two_options) = io_get_all_options(&io_data.state);
739
740 let mut side_one_choices = vec![];
741 for option in side_one_options {
742 side_one_choices.push(
743 format!("{:?}", io_data.state.side_one.option_to_string(&option))
744 .to_lowercase(),
745 );
746 }
747 let mut side_two_choices = vec![];
748 for option in side_two_options {
749 side_two_choices.push(
750 format!("{:?}", io_data.state.side_two.option_to_string(&option))
751 .to_lowercase(),
752 );
753 }
754 println!(
755 "SideOne {}\n\nvs\n\nSideTwo {}\n\nState:\n Weather: {:?},{}\n Terrain: {:?},{}\n TrickRoom: {},{}",
756 io_data.state.side_one.io_print(side_one_choices),
757 io_data.state.side_two.io_print(side_two_choices),
758 io_data.state.weather.weather_type,
759 io_data.state.weather.turns_remaining,
760 io_data.state.terrain.terrain_type,
761 io_data.state.terrain.turns_remaining,
762 io_data.state.trick_room.active,
763 io_data.state.trick_room.turns_remaining
764 );
765 }
766 "generate-instructions" | "g" => {
767 let (s1_move, s2_move);
768 match args.next() {
769 Some(s) => match io_data.state.side_one.string_to_movechoice(s) {
770 Some(m) => {
771 s1_move = m;
772 }
773 None => {
774 println!("Invalid move choice for side one: {}", s);
775 continue;
776 }
777 },
778 None => {
779 println!("Usage: generate-instructions <side-1 move> <side-2 move>");
780 continue;
781 }
782 }
783 match args.next() {
784 Some(s) => match io_data.state.side_two.string_to_movechoice(s) {
785 Some(m) => {
786 s2_move = m;
787 }
788 None => {
789 println!("Invalid move choice for side two: {}", s);
790 continue;
791 }
792 },
793 None => {
794 println!("Usage: generate-instructions <side-1 choice> <side-2 choice>");
795 continue;
796 }
797 }
798 let instructions = generate_instructions_from_move_pair(
799 &mut io_data.state,
800 &s1_move,
801 &s2_move,
802 true,
803 );
804 pprint_state_instruction_vector(&instructions);
805 io_data.last_instructions_generated = instructions;
806 }
807 "calculate-damage" | "d" => {
808 let (mut s1_choice, mut s2_choice);
809 match args.next() {
810 Some(s) => {
811 s1_choice = MOVES
812 .get(&Choices::from_str(s).unwrap())
813 .unwrap()
814 .to_owned();
815 if s == "switch" {
816 s1_choice.category = MoveCategory::Switch
817 }
818 }
819 None => {
820 println!("Usage: calculate-damage <side-1 move> <side-2 move> <side-1-moves-first>");
821 continue;
822 }
823 }
824 match args.next() {
825 Some(s) => {
826 s2_choice = MOVES
827 .get(&Choices::from_str(s).unwrap())
828 .unwrap()
829 .to_owned();
830 if s == "switch" {
831 s2_choice.category = MoveCategory::Switch
832 }
833 }
834 None => {
835 println!("Usage: calculate-damage <side-1 move> <side-2 move> <side-1-moves-first>");
836 continue;
837 }
838 }
839 let s1_moves_first: bool;
840 match args.next() {
841 Some(s) => {
842 s1_moves_first = s.parse::<bool>().unwrap();
843 }
844 None => {
845 println!("Usage: calculate-damage <side-1 move> <side-2 move> <side-1-moves-first>");
846 continue;
847 }
848 }
849 calculate_damage_io(&io_data.state, s1_choice, s2_choice, s1_moves_first);
850 }
851 "instructions" | "i" => {
852 println!("{:?}", io_data.last_instructions_generated);
853 }
854 "evaluate" | "ev" => {
855 println!("Evaluation: {}", evaluate(&io_data.state));
856 }
857 "iterative-deepening" | "id" => match args.next() {
858 Some(s) => {
859 let max_time_ms = s.parse::<u64>().unwrap();
860 let (side_one_options, side_two_options) = io_get_all_options(&io_data.state);
861
862 let start_time = std::time::Instant::now();
863 let (s1_moves, s2_moves, result, depth_searched) =
864 iterative_deepen_expectiminimax(
865 &mut io_data.state,
866 side_one_options.clone(),
867 side_two_options.clone(),
868 std::time::Duration::from_millis(max_time_ms),
869 );
870 let elapsed = start_time.elapsed();
871
872 let safest_choice = pick_safest(&result, s1_moves.len(), s2_moves.len());
873
874 pprint_expectiminimax_result(
875 &result,
876 &s1_moves,
877 &s2_moves,
878 &safest_choice,
879 &io_data.state,
880 );
881 println!("Took: {:?}", elapsed);
882 println!("Depth Searched: {}", depth_searched);
883 }
884 None => {
885 println!("Usage: iterative-deepening <timeout_ms>");
886 continue;
887 }
888 },
889 "monte-carlo-tree-search" | "mcts" => match args.next() {
890 Some(s) => {
891 let max_time_ms = s.parse::<u64>().unwrap();
892 let (side_one_options, side_two_options) = io_get_all_options(&io_data.state);
893
894 let start_time = std::time::Instant::now();
895 let result = perform_mcts(
896 &mut io_data.state,
897 side_one_options.clone(),
898 side_two_options.clone(),
899 std::time::Duration::from_millis(max_time_ms),
900 );
901 let elapsed = start_time.elapsed();
902 pprint_mcts_result(&io_data.state, result);
903
904 println!("\nTook: {:?}", elapsed);
905 }
906 None => {
907 println!("Usage: monte-carlo-tree-search <timeout_ms>");
908 continue;
909 }
910 },
911 "apply" | "a" => match args.next() {
912 Some(s) => {
913 let index = s.parse::<usize>().unwrap();
914 let instructions = io_data.last_instructions_generated.remove(index);
915 io_data
916 .state
917 .apply_instructions(&instructions.instruction_list);
918 io_data.instruction_list.push(instructions.instruction_list);
919 io_data.last_instructions_generated = Vec::new();
920 println!("Applied instructions at index {}", index)
921 }
922 None => {
923 println!("Usage: apply <instruction index>");
924 continue;
925 }
926 },
927 "pop" | "p" => {
928 if io_data.instruction_list.is_empty() {
929 println!("No instructions to pop");
930 continue;
931 }
932 let instructions = io_data.instruction_list.pop().unwrap();
933 io_data.state.reverse_instructions(&instructions);
934 println!("Popped last applied instructions");
935 }
936 "pop-all" | "pa" => {
937 for i in io_data.instruction_list.iter().rev() {
938 io_data.state.reverse_instructions(i);
939 }
940 io_data.instruction_list.clear();
941 println!("Popped all applied instructions");
942 }
943 "expectiminimax" | "e" => match args.next() {
944 Some(s) => {
945 let mut ab_prune = false;
946 match args.next() {
947 Some(s) => ab_prune = s.parse::<bool>().unwrap(),
948 None => {}
949 }
950 let depth = s.parse::<i8>().unwrap();
951 let (side_one_options, side_two_options) = io_get_all_options(&io_data.state);
952 let start_time = std::time::Instant::now();
953 let result = expectiminimax_search(
954 &mut io_data.state,
955 depth,
956 side_one_options.clone(),
957 side_two_options.clone(),
958 ab_prune,
959 &Arc::new(Mutex::new(true)),
960 );
961 let elapsed = start_time.elapsed();
962
963 let safest_choice =
964 pick_safest(&result, side_one_options.len(), side_two_options.len());
965 pprint_expectiminimax_result(
966 &result,
967 &side_one_options,
968 &side_two_options,
969 &safest_choice,
970 &io_data.state,
971 );
972 println!("\nTook: {:?}", elapsed);
973 }
974 None => {
975 println!("Usage: expectiminimax <depth> <ab_prune=false>");
976 continue;
977 }
978 },
979 "" => {
980 continue;
981 }
982 "exit" | "quit" | "q" => {
983 break;
984 }
985 command => {
986 println!("Unknown command: {}", command);
987 }
988 }
989 }
990}