summaryrefslogtreecommitdiffstats
path: root/src/bin/client/games
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/bin/client/games/manager.rs127
-rw-r--r--src/bin/client/games/mod.rs3
-rw-r--r--src/bin/client/games/slots.rs62
-rw-r--r--src/bin/client/games/traits.rs30
4 files changed, 222 insertions, 0 deletions
diff --git a/src/bin/client/games/manager.rs b/src/bin/client/games/manager.rs
new file mode 100644
index 0000000..6959214
--- /dev/null
+++ b/src/bin/client/games/manager.rs
@@ -0,0 +1,127 @@
1use crate::games::slots::SlotsGame;
2use crate::games::traits::{Game, GameError, GamePhase, GameState};
3use std::collections::HashMap;
4use std::io::Write;
5
6pub struct GameManager {
7 current_session: Option<GameState>,
8 pending_game_type: Option<String>,
9 games: HashMap<String, Box<dyn Game>>,
10}
11
12impl GameManager {
13 pub fn new() -> Self {
14 let mut games: HashMap<String, Box<dyn Game>> = HashMap::new();
15 games.insert("slots".to_string(), Box::new(SlotsGame::new()));
16
17 Self {
18 current_session: None,
19 pending_game_type: None,
20 games,
21 }
22 }
23
24 pub fn get_available_games(&self) -> Vec<(&str, &str)> {
25 self.games
26 .iter()
27 .map(|(k, v)| (k.as_str(), v.get_name()))
28 .collect()
29 }
30
31 pub fn enter_game(&mut self, game_type: &str) -> Result<(), GameError> {
32 if self.current_session.is_some() {
33 return Err(GameError::InvalidAction);
34 }
35
36 if !self.games.contains_key(game_type) {
37 return Err(GameError::InvalidAction);
38 }
39
40 self.pending_game_type = Some(game_type.to_string());
41
42 self.current_session = Some(GameState {
43 game_type: game_type.to_string(),
44 bet_amount: 10,
45 phase: GamePhase::WaitingToStart,
46 data: HashMap::new(),
47 });
48
49 Ok(())
50 }
51
52 pub fn start_game(
53 &mut self,
54 stream: &mut crate::connection::TlsStream,
55 ) -> Result<(), GameError> {
56 let game_type = self
57 .pending_game_type
58 .take()
59 .ok_or(GameError::InvalidAction)?;
60
61 let state = self
62 .current_session
63 .as_mut()
64 .ok_or(GameError::InvalidAction)?;
65
66 if state.phase != GamePhase::WaitingToStart {
67 return Err(GameError::InvalidAction);
68 }
69
70 let bet_amount = state.bet_amount;
71
72 let writer = stream;
73 writeln!(writer, "PLAY_START {} {}", game_type, bet_amount)
74 .map_err(|_| GameError::NetworkError)?;
75
76 Ok(())
77 }
78
79 pub fn get_current_game(&self) -> Option<&dyn Game> {
80 self.current_session
81 .as_ref()
82 .and_then(|session| self.games.get(&session.game_type).map(|game| game.as_ref()))
83 }
84
85 pub fn get_current_state(&self) -> Option<&GameState> {
86 self.current_session.as_ref()
87 }
88
89 pub fn get_current_render_lines(&self) -> Option<usize> {
90 let session = self.current_session.as_ref()?;
91 let game = self.games.get(&session.game_type)?;
92 Some(game.render_lines(session))
93 }
94
95 pub fn is_in_game(&self) -> bool {
96 self.current_session.is_some()
97 }
98
99 pub fn is_game_started(&self) -> bool {
100 self.current_session
101 .as_ref()
102 .map(|s| s.phase != GamePhase::WaitingToStart)
103 .unwrap_or(false)
104 }
105
106 pub fn end_game(&mut self) {
107 self.current_session = None;
108 self.pending_game_type = None;
109 }
110
111 pub fn adjust_bet(&mut self, delta: i64) -> Result<u64, GameError> {
112 if let Some(ref session) = self.current_session {
113 if session.phase != GamePhase::WaitingToStart {
114 return Err(GameError::InvalidAction);
115 }
116 }
117
118 if let Some(ref mut session) = self.current_session {
119 let current_bet = session.bet_amount as i64;
120 let new_bet = (current_bet + delta).max(10) as u64;
121 session.bet_amount = new_bet;
122 Ok(new_bet)
123 } else {
124 Err(GameError::InvalidAction)
125 }
126 }
127}
diff --git a/src/bin/client/games/mod.rs b/src/bin/client/games/mod.rs
new file mode 100644
index 0000000..aa74574
--- /dev/null
+++ b/src/bin/client/games/mod.rs
@@ -0,0 +1,3 @@
1pub mod manager;
2pub mod slots;
3pub mod traits;
diff --git a/src/bin/client/games/slots.rs b/src/bin/client/games/slots.rs
new file mode 100644
index 0000000..2f9d63e
--- /dev/null
+++ b/src/bin/client/games/slots.rs
@@ -0,0 +1,62 @@
1use crate::games::traits::{Game, GameState};
2
3pub struct SlotsGame;
4
5impl SlotsGame {
6 pub fn new() -> Self {
7 Self
8 }
9
10 fn render_reels(&self, reels: &str) -> String {
11 if reels.is_empty() {
12 "[ ? ] [ ? ] [ ? ]".to_string()
13 } else {
14 let chars: Vec<char> = reels.chars().collect();
15 format!(
16 "[ {} ] [ {} ] [ {} ]\r",
17 chars.get(0).unwrap_or(&'?'),
18 chars.get(1).unwrap_or(&'?'),
19 chars.get(2).unwrap_or(&'?')
20 )
21 }
22 }
23}
24
25impl Game for SlotsGame {
26 fn get_name(&self) -> &'static str {
27 "Slots"
28 }
29
30 fn render(&self, state: &GameState) -> String {
31 let reels = state.data.get("reels").cloned().unwrap_or_default();
32
33 let mut output = format!("\n=== SLOTS ===\r\n");
34 output += &format!("Bet: {} PLN\r\n", state.bet_amount);
35 output += &format!("{}\r\n", self.render_reels(&reels));
36 output += "\nPress [SPACE] to spin!\r\n";
37 output += "\nControls: [+/-] Adjust Bet [Q] Quit\r\n";
38 output
39 }
40
41 fn render_lines(&self, state: &GameState) -> usize {
42 self.render(state).lines().count()
43 }
44
45 fn get_help(&self) -> String {
46 "SLOTS GAME\n\
47 =========\n\
48 Match symbols to win!\n\
49 \n\
50 Payouts:\n\
51 Three of a kind: 5x-100x bet\n\
52 Two of a kind: 2x-20x bet\n\
53 \n\
54 Symbols: C=Cherry, L=Lemon, O=Orange, G=Grapes, D=Diamond, S=Seven\n\
55 \n\
56 Controls:\n\
57 SPACE - Spin the reels\n\
58 +/- - Adjust bet amount\n\
59 Q - Exit the game"
60 .to_string()
61 }
62}
diff --git a/src/bin/client/games/traits.rs b/src/bin/client/games/traits.rs
new file mode 100644
index 0000000..19f2638
--- /dev/null
+++ b/src/bin/client/games/traits.rs
@@ -0,0 +1,30 @@
1use std::collections::HashMap;
2
3#[derive(Debug, Clone)]
4pub enum GameError {
5 InvalidAction,
6 NetworkError,
7}
8
9#[derive(Debug, Clone, PartialEq)]
10pub enum GamePhase {
11 WaitingToStart,
12}
13
14#[derive(Debug, Clone)]
15pub struct GameState {
16 pub game_type: String,
17 pub bet_amount: u64,
18 pub phase: GamePhase,
19 pub data: HashMap<String, String>,
20}
21
22pub trait Game: Send + Sync {
23 fn get_name(&self) -> &'static str;
24
25 fn render(&self, state: &GameState) -> String;
26
27 fn render_lines(&self, state: &GameState) -> usize;
28
29 fn get_help(&self) -> String;
30}