Hello, I am writing a small card game to practice Rust. The game has some internal state + a GamePhase
which controls what can/cannot be done. Currently, I have all functions in one big Game
struct, and each function checks for correct state before continuing + whether to transition to next state afterwards; ie like:
enum GamePhase { Draw, Play };
struct Game {
/* internal state (players, cards etc) */
phase: GamePhase
}
impl Game {
pub fn draw(&mut self) -> Result<(), ()> {
match self.phase {
GamePhase::Draw => { /* draw card */ },
GamePhase::Play => { return Err(()) },
}
if /* ready to transition state */ {
self.phase = GamePhase::Play;
}
Ok(())
}
/* same idea for play() */
}
This feels wrong as I need to explicitly check state and state transition for each function, and all functions are available regardless of the state.
An alternative is, I could have a struct for each GamePhase
that holds the game's state and has specific functions, and have a next()
function to explicitly consume self
and return the next state if ready; thus behaviour is locked to the state:
enum GamePhase {
Draw(Draw),
Play(Play)
}
struct GameState {
/* internal state (players, cards etc) */
}
struct Draw {
state: GameState
}
impl Draw {
pub fn draw(&mut self) {
/* draw a card for current player */
}
pub fn next(mut self) -> Result<GamePhase::Play, Self> {
if /* ready to transition state */ {
return Ok(GamePhase::Play(self.state));
}
else {
return Err(self);
}
}
}
/* same idea for Play */
However, writing the gameflow would be less intuitive as I would have to construct a new struct for each phase, and next()
always consumes self
so I would need to re-assign it if it failed.
I'm wondering which way is more sensible? Or if there was a better way to do this. Thanks!