Simplify game setup messaging
This commit is contained in:
@ -28,21 +28,20 @@ type Result<T> = std::result::Result<T, Error>;
|
|||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq)]
|
#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||||
enum GridItem {
|
enum GridItem {
|
||||||
Free,
|
F, // Free
|
||||||
X,
|
X,
|
||||||
O,
|
O,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for GridItem {
|
impl Default for GridItem {
|
||||||
fn default() -> GridItem {
|
fn default() -> GridItem {
|
||||||
GridItem::Free
|
GridItem::F
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq)]
|
#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||||
enum State {
|
pub enum State {
|
||||||
WaitingForO,
|
Waiting,
|
||||||
ORequestPending,
|
|
||||||
XMove,
|
XMove,
|
||||||
OMove,
|
OMove,
|
||||||
XWon,
|
XWon,
|
||||||
@ -51,7 +50,7 @@ enum State {
|
|||||||
}
|
}
|
||||||
impl Default for State {
|
impl Default for State {
|
||||||
fn default() -> State {
|
fn default() -> State {
|
||||||
State::WaitingForO
|
State::Waiting
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -61,54 +60,32 @@ struct Game {
|
|||||||
player_o: Option<Pubkey>,
|
player_o: Option<Pubkey>,
|
||||||
state: State,
|
state: State,
|
||||||
grid: [GridItem; 9],
|
grid: [GridItem; 9],
|
||||||
keep_alive_x: i64,
|
keep_alive: [i64; 2],
|
||||||
keep_alive_o: i64,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Game {
|
impl Game {
|
||||||
pub fn create(player_x: &Pubkey) -> Game {
|
pub fn create(player_x: &Pubkey) -> Game {
|
||||||
let mut game = Game::default();
|
let mut game = Game::default();
|
||||||
game.player_x = *player_x;
|
game.player_x = *player_x;
|
||||||
assert_eq!(game.state, State::WaitingForO);
|
assert_eq!(game.state, State::Waiting);
|
||||||
game
|
game
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub fn new(player_x: Pubkey, player_o: Pubkey) -> Game {
|
pub fn new(player_x: Pubkey, player_o: Pubkey) -> Game {
|
||||||
let mut game = Game::create(&player_x);
|
let mut game = Game::create(&player_x);
|
||||||
game.join(&player_o).unwrap();
|
game.join(player_o, 0).unwrap();
|
||||||
game.accept().unwrap();
|
|
||||||
game
|
game
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn join(self: &mut Game, player_o: &Pubkey) -> Result<()> {
|
pub fn join(self: &mut Game, player_o: Pubkey, timestamp: i64) -> Result<()> {
|
||||||
if self.state == State::WaitingForO {
|
if self.state == State::Waiting {
|
||||||
self.player_o = Some(*player_o);
|
self.player_o = Some(player_o);
|
||||||
self.state = State::ORequestPending;
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err(Error::NotYourTurn)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn accept(self: &mut Game) -> Result<()> {
|
|
||||||
if self.state == State::ORequestPending {
|
|
||||||
assert!(self.player_o.is_some());
|
|
||||||
self.state = State::XMove;
|
self.state = State::XMove;
|
||||||
|
self.keep_alive[1] = timestamp;
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err(Error::NotYourTurn)
|
Err(Error::GameInProgress)
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn reject(self: &mut Game) -> Result<()> {
|
|
||||||
if self.state == State::ORequestPending {
|
|
||||||
assert!(self.player_o.is_some());
|
|
||||||
self.player_o = None;
|
|
||||||
self.state = State::WaitingForO;
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err(Error::NotYourTurn)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -116,22 +93,22 @@ impl Game {
|
|||||||
triple.iter().all(|&i| i == x_or_o)
|
triple.iter().all(|&i| i == x_or_o)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn next_move(self: &mut Game, player: &Pubkey, x: usize, y: usize) -> Result<()> {
|
pub fn next_move(self: &mut Game, player: Pubkey, x: usize, y: usize) -> Result<()> {
|
||||||
let grid_index = y * 3 + x;
|
let grid_index = y * 3 + x;
|
||||||
if grid_index >= self.grid.len() || self.grid[grid_index] != GridItem::Free {
|
if grid_index >= self.grid.len() || self.grid[grid_index] != GridItem::F {
|
||||||
Err(Error::InvalidMove)?;
|
Err(Error::InvalidMove)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let (x_or_o, won_state) = match self.state {
|
let (x_or_o, won_state) = match self.state {
|
||||||
State::XMove => {
|
State::XMove => {
|
||||||
if *player != self.player_x {
|
if player != self.player_x {
|
||||||
return Err(Error::PlayerNotFound);
|
return Err(Error::PlayerNotFound);
|
||||||
}
|
}
|
||||||
self.state = State::OMove;
|
self.state = State::OMove;
|
||||||
(GridItem::X, State::XWon)
|
(GridItem::X, State::XWon)
|
||||||
}
|
}
|
||||||
State::OMove => {
|
State::OMove => {
|
||||||
if *player != self.player_o.unwrap() {
|
if player != self.player_o.unwrap() {
|
||||||
return Err(Error::PlayerNotFound);
|
return Err(Error::PlayerNotFound);
|
||||||
}
|
}
|
||||||
self.state = State::XMove;
|
self.state = State::XMove;
|
||||||
@ -158,18 +135,18 @@ impl Game {
|
|||||||
|
|
||||||
if winner {
|
if winner {
|
||||||
self.state = won_state;
|
self.state = won_state;
|
||||||
} else if self.grid.iter().all(|&p| p != GridItem::Free) {
|
} else if self.grid.iter().all(|&p| p != GridItem::F) {
|
||||||
self.state = State::Draw;
|
self.state = State::Draw;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn keep_alive(self: &mut Game, player: &Pubkey, timestamp: i64) -> Result<()> {
|
pub fn keep_alive(self: &mut Game, player: Pubkey, timestamp: i64) -> Result<()> {
|
||||||
if *player == self.player_x {
|
if player == self.player_x {
|
||||||
self.keep_alive_x = timestamp;
|
self.keep_alive[0] = timestamp;
|
||||||
} else if Some(*player) == self.player_o {
|
} else if Some(player) == self.player_o {
|
||||||
self.keep_alive_o = timestamp;
|
self.keep_alive[1] = timestamp;
|
||||||
} else {
|
} else {
|
||||||
Err(Error::PlayerNotFound)?;
|
Err(Error::PlayerNotFound)?;
|
||||||
}
|
}
|
||||||
@ -180,9 +157,7 @@ impl Game {
|
|||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
enum Command {
|
enum Command {
|
||||||
Init, // player X initializes a new game
|
Init, // player X initializes a new game
|
||||||
Join, // player O wants to join
|
Join(i64), // player O wants to join (seconds since UNIX epoch)
|
||||||
Accept, // player X accepts the Join request
|
|
||||||
Reject, // player X rejects the Join request
|
|
||||||
KeepAlive(i64), // player X/O keep alive (seconds since UNIX epoch)
|
KeepAlive(i64), // player X/O keep alive (seconds since UNIX epoch)
|
||||||
Move(u8, u8), // player X/O mark board position (x, y)
|
Move(u8, u8), // player X/O mark board position (x, y)
|
||||||
}
|
}
|
||||||
@ -228,23 +203,9 @@ impl TicTacToeProgram {
|
|||||||
|
|
||||||
if let Some(ref mut game) = self.game {
|
if let Some(ref mut game) = self.game {
|
||||||
match cmd {
|
match cmd {
|
||||||
Command::Join => game.join(player),
|
Command::Join(timestamp) => game.join(*player, *timestamp),
|
||||||
Command::Accept => {
|
Command::Move(x, y) => game.next_move(*player, *x as usize, *y as usize),
|
||||||
if *player == game.player_x {
|
Command::KeepAlive(timestamp) => game.keep_alive(*player, *timestamp),
|
||||||
game.accept()
|
|
||||||
} else {
|
|
||||||
Err(Error::PlayerNotFound)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Command::Reject => {
|
|
||||||
if *player == game.player_x {
|
|
||||||
game.reject()
|
|
||||||
} else {
|
|
||||||
Err(Error::PlayerNotFound)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Command::Move(x, y) => game.next_move(player, *x as usize, *y as usize),
|
|
||||||
Command::KeepAlive(timestamp) => game.keep_alive(player, *timestamp),
|
|
||||||
Command::Init => panic!("Unreachable"),
|
Command::Init => panic!("Unreachable"),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
Reference in New Issue
Block a user