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