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}