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