freecell/game_state/legal_moves.rs
1use crate::{CardCollection, Move, Position};
2use super::GameState;
3
4impl GameState {
5 // TODO [low priority] possible optimisation: if there are multiple empty cascades, only allow to move to one of them
6 // TODO [low priority] deduplicate code
7 // TODO [med priority] [before 1.0.0] moves that don't change the game state should be treated as illegal
8 pub fn legal_moves(&self) -> Vec<(GameState, Move)> {
9 // This is a temporary implementation.
10 // My attempts to implement this nicely have been thwarted by rust's rules for creating
11 // collections of traits (I tried to implement a method that returns a vector of
12 // (CardCollection, Position), then iterate over it, try to pop cards and push them back
13 // into a CardCollection. If both positions in a Move match, i.e. the card was pushed back
14 // into the same CardCollection, the move was going to be filtered out.)
15 // Unfortunately, I am stuck with this ugly mess for now:
16
17 let mut legal_moves = Vec::new();
18
19 // move from cascades ...
20 for i in 0..self.cascades.len() {
21 let (cascade, card) = match self.cascades[i].pop_card().pop() {
22 Some((cascade, card)) => (cascade, card),
23 None => continue,
24 };
25
26 // ... to other cascades
27 for j in 0..self.cascades.len() {
28 if i == j { continue }
29
30 if let Ok(to_cascade) = self.cascades[j].add_card(card) {
31 let mut new_cascades = self.cascades.clone();
32 new_cascades[i] = cascade.clone();
33 new_cascades[j] = to_cascade;
34 legal_moves.push((
35 GameState {
36 cascades: new_cascades,
37 foundations: self.foundations.clone(),
38 freecells: self.freecells.clone(),
39 },
40 Move {
41 card,
42 from: Position::Cascade(i),
43 to: Position::Cascade(j),
44 },
45 ));
46 }
47 }
48
49 // ... to foundations
50 if let Ok(foundations) = self.foundations.add_card(card) {
51 let mut new_cascades = self.cascades.clone();
52 new_cascades[i] = cascade.clone();
53 legal_moves.push((
54 GameState {
55 cascades: new_cascades,
56 foundations,
57 freecells: self.freecells.clone(),
58 },
59 Move {
60 card,
61 from: Position::Cascade(i),
62 to: Position::Foundations,
63 },
64 ));
65 }
66
67 // ... to freecells
68 if let Ok(freecells) = self.freecells.add_card(card) {
69 let mut new_cascades = self.cascades.clone();
70 new_cascades[i] = cascade;
71 legal_moves.push((
72 GameState {
73 cascades: new_cascades,
74 foundations: self.foundations.clone(),
75 freecells,
76 },
77 Move {
78 card,
79 from: Position::Cascade(i),
80 to: Position::Freecells,
81 },
82 ));
83 }
84 }
85
86 // move from freecells ...
87 let from_freecells = self.freecells.pop_card();
88 for (freecells, card) in from_freecells {
89 // ... to cascades
90 for i in 0..self.cascades.len() {
91 if let Ok(cascade) = self.cascades[i].add_card(card) {
92 let mut new_cascades = self.cascades.clone();
93 new_cascades[i] = cascade;
94 legal_moves.push((
95 GameState {
96 cascades: new_cascades,
97 foundations: self.foundations.clone(),
98 freecells: freecells.clone(),
99 },
100 Move {
101 card,
102 from: Position::Freecells,
103 to: Position::Cascade(i),
104 },
105 ));
106 }
107 }
108
109 // ... to foundations
110 if let Ok(foundations) = self.foundations.add_card(card) {
111 legal_moves.push((
112 GameState {
113 cascades: self.cascades.clone(),
114 foundations,
115 freecells: freecells.clone(),
116 },
117 Move {
118 card,
119 from: Position::Freecells,
120 to: Position::Foundations,
121 },
122 ));
123 }
124 }
125
126 legal_moves
127 }
128}