summaryrefslogtreecommitdiff
path: root/server/src
diff options
context:
space:
mode:
authorKjetil Orbekk <kj@orbekk.com>2023-01-01 11:52:28 -0500
committerKjetil Orbekk <kj@orbekk.com>2023-01-01 11:52:28 -0500
commitbb2ed3a2926384df063e476d10613fa310cd7ffa (patch)
treecc9c6ea4979eef3850d78cd0b1390dfbccb5921b /server/src
parent1e3014a777805d3dcb691ee6ebe59c62f58f8222 (diff)
Add Table to be used with db schema
Diffstat (limited to 'server/src')
-rw-r--r--server/src/lib.rs21
-rw-r--r--server/src/main.rs48
-rw-r--r--server/src/table.rs115
3 files changed, 149 insertions, 35 deletions
diff --git a/server/src/lib.rs b/server/src/lib.rs
new file mode 100644
index 0000000..6ca9e49
--- /dev/null
+++ b/server/src/lib.rs
@@ -0,0 +1,21 @@
+pub mod auth;
+pub mod error;
+#[cfg(debug_assertions)]
+pub mod fake_auth;
+pub mod play;
+pub mod server;
+pub mod table;
+
+#[cfg(test)]
+mod tests {
+ use env_logger::Env;
+
+ pub fn test_setup() {
+ dotenv::dotenv().ok();
+ let _ = env_logger::Builder::from_env(
+ Env::default().default_filter_or("info"),
+ )
+ .is_test(true)
+ .try_init();
+ }
+}
diff --git a/server/src/main.rs b/server/src/main.rs
index 10b1361..3e3985f 100644
--- a/server/src/main.rs
+++ b/server/src/main.rs
@@ -2,7 +2,7 @@ use serde_json::json;
use std::{collections::HashMap, env, str::FromStr, sync::Arc};
use uuid::Uuid;
-use auth::AuthenticatedSession;
+use server::auth::AuthenticatedSession;
use axum::{
extract::{Path, Query, State},
response::{Html, Redirect},
@@ -14,27 +14,18 @@ use protocol::{
card::Card,
};
use protocol::{Table, UserInfo};
-use server::ServerState;
+use server::server::ServerState;
use tower_cookies::{Cookie, CookieManagerLayer, Cookies};
use tower_http::trace::TraceLayer;
use tracing::{info, log::warn};
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
-mod auth;
-mod error;
-#[cfg(debug_assertions)]
-mod fake_auth;
-mod play;
-mod server;
-use crate::{
- auth::{OauthAuthenticator, SessionId},
+use server::{
+ auth::{OauthAuthenticator, SessionId, Authenticator},
play::advance_play,
server::ServerContext,
-};
-use crate::{
error::BridgeError,
play::{DbJournal, Journal},
};
-use auth::Authenticator;
use sqlx::{postgres::PgPoolOptions, PgPool};
async fn create_default_authenticator(
@@ -51,7 +42,7 @@ async fn create_authenticator(
if std::env::var("AUTHENTICATOR").unwrap_or("".to_string())
== FAKE_AUTHENTICATOR
{
- Box::new(fake_auth::FakeAuthenticator::new())
+ Box::new(server::fake_auth::FakeAuthenticator::new())
} else {
create_default_authenticator(db_pool).await
}
@@ -119,7 +110,7 @@ async fn main() {
.route("/api/table/:id/play", post(post_play))
.route("/api/table/:id/admin/deal", post(table_new_deal))
.route("/api/login", get(login))
- .route(auth::LOGIN_CALLBACK, get(login_callback))
+ .route(server::auth::LOGIN_CALLBACK, get(login_callback))
.layer(CookieManagerLayer::new())
.layer(TraceLayer::new_for_http())
.with_state(state);
@@ -155,7 +146,7 @@ async fn get_table_view(
info!("Getting table state for {id:}");
let player_position = Player::South;
let jnl = DbJournal::new(state.db.clone(), id);
- let mut table = play::Table::new_or_replay(jnl).await?;
+ let mut table = server::play::Table::new_or_replay(jnl).await?;
info!("Advancing play");
while table.game_in_progress()
&& table.game()?.current_player() != player_position
@@ -179,7 +170,7 @@ async fn table_new_deal(
) -> Result<Json<()>, BridgeError> {
info!("Getting table state for {id:}");
let jnl = DbJournal::new(state.db.clone(), id);
- let mut table = play::Table::replay(jnl).await?;
+ let mut table = server::play::Table::replay(jnl).await?;
table.new_deal().await?;
Ok(Json(()))
}
@@ -192,7 +183,7 @@ async fn post_bid(
) -> Result<Json<()>, BridgeError> {
info!("Getting table state for {id:}");
let jnl = DbJournal::new(state.db.clone(), id);
- let mut table = play::Table::replay(jnl).await?;
+ let mut table = server::play::Table::replay(jnl).await?;
table.bid(bid).await?;
Ok(Json(()))
}
@@ -205,7 +196,7 @@ async fn post_play(
) -> Result<Json<()>, BridgeError> {
info!("Getting table state for {id:}");
let jnl = DbJournal::new(state.db.clone(), id);
- let mut table = play::Table::replay(jnl).await?;
+ let mut table = server::play::Table::replay(jnl).await?;
table.play(card).await?;
Ok(Json(()))
}
@@ -232,7 +223,7 @@ async fn create_table(
let txn = state.db.begin().await?;
let table_id = sqlx::query!(
r#"
- insert into active_tables (id)
+ insert into bridge_table (id)
values ($1)
returning id
"#,
@@ -244,7 +235,7 @@ async fn create_table(
sqlx::query!(
r#"
- insert into table_players (active_tables_id,
+ insert into table_players (table_id,
player_id,
position)
values ($1, $2, 'south')
@@ -282,7 +273,7 @@ async fn user_table(
r#"
select tables.id
from table_players players
- natural join active_tables tables
+ natural join bridge_table tables
where player_id = $1
"#,
session.player_id
@@ -318,16 +309,3 @@ async fn login(cookies: Cookies, State(state): ServerState) -> Redirect {
Redirect::temporary(auth_url.as_str())
}
-#[cfg(test)]
-mod tests {
- use env_logger::Env;
-
- pub fn test_setup() {
- dotenv::dotenv().ok();
- let _ = env_logger::Builder::from_env(
- Env::default().default_filter_or("info"),
- )
- .is_test(true)
- .try_init();
- }
-}
diff --git a/server/src/table.rs b/server/src/table.rs
new file mode 100644
index 0000000..068fb24
--- /dev/null
+++ b/server/src/table.rs
@@ -0,0 +1,115 @@
+use async_trait::async_trait;
+use protocol::{
+ bot::{BiddingBot, PlayingBot},
+ bridge_engine::{
+ Bid, BiddingStatePlayerView, GameState, PlayStatePlayerView, TableState,
+ },
+ card::Card,
+ simple_bots::{AlwaysPassBiddingBot, RandomPlayingBot},
+};
+use rand::random;
+
+use crate::error::BridgeError;
+
+#[async_trait]
+pub trait Table {
+ fn state(&self) -> &TableState;
+ async fn bid(
+ self: Box<Self>,
+ bid: Bid,
+ ) -> Result<Box<dyn Table>, BridgeError>;
+ async fn play(
+ self: Box<Self>,
+ card: Card,
+ ) -> Result<Box<dyn Table>, BridgeError>;
+ async fn new_deal(self: Box<Self>) -> Result<Box<dyn Table>, BridgeError>;
+}
+
+pub struct InMemoryTable {
+ pub state: TableState,
+}
+
+impl InMemoryTable {
+ pub fn new() -> Self {
+ Self {
+ state: TableState::Unknown,
+ }
+ }
+}
+
+#[async_trait]
+impl Table for InMemoryTable {
+ fn state(&self) -> &TableState {
+ &self.state
+ }
+
+ async fn bid(
+ self: Box<Self>,
+ bid: Bid,
+ ) -> Result<Box<dyn Table>, BridgeError> {
+ let game = match self.state {
+ TableState::Game(game) => game,
+ _ => {
+ return Err(BridgeError::InvalidRequest("no game".to_string()))
+ }
+ };
+ let game = game.bid(bid)?;
+ Ok(Box::new(Self { state: game.into() }))
+ }
+
+ async fn play(
+ self: Box<Self>,
+ card: Card,
+ ) -> Result<Box<dyn Table>, BridgeError> {
+ let game = match self.state {
+ TableState::Game(game) => game,
+ _ => {
+ return Err(BridgeError::InvalidRequest("no game".to_string()))
+ }
+ };
+ let game = game.play(card)?;
+ Ok(Box::new(Self { state: game.into() }))
+ }
+
+ async fn new_deal(self: Box<Self>) -> Result<Box<dyn Table>, BridgeError> {
+ Ok(Box::new(Self {
+ state: GameState::new(random(), random()).into(),
+ }))
+ }
+}
+
+pub async fn advance_play(
+ table: Box<dyn Table>,
+) -> Result<Box<dyn Table>, BridgeError> {
+ let game = match table.state() {
+ TableState::Game(game) => game,
+ _ => return Err(BridgeError::InvalidRequest("no game".to_string())),
+ };
+ let table = match game {
+ GameState::Bidding(ref bidding) => {
+ let player_view = BiddingStatePlayerView::from_bidding_state(
+ &bidding,
+ game.current_player(),
+ );
+ let bot = AlwaysPassBiddingBot {};
+ let bid = bot.bid(&player_view).await;
+ table.bid(bid).await
+ }
+ GameState::Play(game) => {
+ let player_view = PlayStatePlayerView::from_play_state(
+ &game,
+ game.current_player(),
+ );
+ let bot = RandomPlayingBot {};
+ let card = bot.play(&player_view).await;
+ table.play(card).await
+ }
+ };
+ table
+}
+
+// pub struct DbTable
+// {
+// db: PgPool,
+// pub state: TableState,
+// }