web_lib/offscreen_game/
mod.rs1use js_sys::Date;
2use mahjong_core::{
3 deck::DEFAULT_DECK, score::ScoringRule, Game, Hand, HandTile, PlayerId, Players, TileId,
4};
5use offscreen_player::{OffscreenPlayer, OffscreenPlayers};
6use selecting_hand::{SelectingHand, SelectingHandTile};
7use serde::{Deserialize, Serialize};
8use wasm_bindgen::prelude::wasm_bindgen;
9pub use wrappers::{ScoringRuleWasm, WindWasm};
10
11mod offscreen_player;
12mod round_validation;
13mod selecting_hand;
14mod wrappers;
15
16#[wasm_bindgen]
17pub struct ScoreResult {
18 rules: Vec<ScoringRule>,
19 pub score: u32,
20}
21
22#[wasm_bindgen]
23impl ScoreResult {
24 #[wasm_bindgen(getter)]
25 pub fn rules(&self) -> Vec<ScoringRuleWasm> {
26 self.rules.iter().map(|r| r.clone().into()).collect()
27 }
28}
29
30#[wasm_bindgen]
31#[derive(Serialize, Deserialize)]
32pub struct OffscreenGame {
33 pub date_created: u64,
34 game: Game,
35 players: OffscreenPlayers,
36}
37
38#[wasm_bindgen]
39impl OffscreenGame {
40 #[wasm_bindgen(constructor)]
41 #[allow(clippy::new_without_default)]
42 pub fn new() -> Self {
43 let mut game = Game::new(None);
44 game.start(false);
45
46 let players: OffscreenPlayers = [1, 2, 3, 4]
47 .iter()
48 .map(|num| {
49 let mut player = OffscreenPlayer {
50 id: Players::new_player(),
51 ..Default::default()
52 };
53 player.name = format!("Player {}", num);
54 (player.id.clone(), player)
55 })
56 .collect();
57
58 players.iter().for_each(|(player_id, _)| {
59 game.players.push(player_id.clone());
60 });
61
62 game.complete_players(false).unwrap();
63 let date_created = Date::now() as u64;
64
65 Self {
66 date_created,
67 game,
68 players,
69 }
70 }
71
72 #[wasm_bindgen(getter)]
73 pub fn players(&self) -> Vec<OffscreenPlayer> {
74 self.game
75 .players
76 .0
77 .iter()
78 .map(|p| self.players.get(p).unwrap().clone())
79 .collect()
80 }
81
82 pub fn set_player(&mut self, id: PlayerId, player: OffscreenPlayer) {
83 if self.players.contains_key(&id) {
84 self.players.insert(id, player);
85 }
86 }
87
88 pub fn update_player_score(&mut self, player_id: PlayerId, score: u32) {
89 self.game.score.0.insert(player_id, score);
90 }
91
92 pub fn get_player_score(&self, player_id: PlayerId) -> u32 {
93 if !self.game.score.0.contains_key(&player_id) {
94 return 0;
95 }
96 *self.game.score.0.get(&player_id).unwrap()
97 }
98
99 pub fn is_dealer(&self, player_id: PlayerId) -> bool {
100 Some(self.game.round.dealer_player_index)
101 == self.game.players.0.iter().position(|p| p == &player_id)
102 }
103
104 pub fn get_wind(&self, player_id: PlayerId) -> WindWasm {
105 let wind = self
106 .game
107 .round
108 .get_player_wind(&self.game.players.0, &player_id);
109
110 wind.into()
111 }
112
113 pub fn set_dealer(&mut self, player_id: PlayerId) {
114 if let Some(index) = self.game.players.0.iter().position(|p| p == &player_id) {
115 self.game.round.dealer_player_index = index;
116 }
117 }
118
119 #[wasm_bindgen(getter)]
120 pub fn available_tiles_for_round(&self) -> Vec<TileId> {
121 let tiles_ids: Vec<TileId> = DEFAULT_DECK
122 .0
123 .iter()
124 .enumerate()
125 .map(|(i, _)| i as TileId)
126 .filter(|i| {
127 !self.game.players.0.iter().any(|p| {
128 self.game
129 .table
130 .hands
131 .get(p)
132 .unwrap()
133 .list
134 .iter()
135 .any(|t| t.id == *i)
136 })
137 })
138 .collect();
139 let hand_list = tiles_ids
140 .iter()
141 .map(|id| HandTile {
142 concealed: true,
143 id: *id,
144 set_id: None,
145 })
146 .collect();
147 let mut hand = Hand::new(hand_list);
148 hand.sort_default();
149 hand.list.iter().map(|t| t.id).collect()
150 }
151
152 pub fn selecting_hand(&self, player_id: PlayerId) -> SelectingHand {
153 let mut hand = self.game.table.hands.get(&player_id).unwrap().clone();
154 hand.sort_default();
155 let tiles = hand
156 .list
157 .iter()
158 .map(|t| SelectingHandTile {
159 concealed: t.concealed,
160 id: t.id,
161 set_id: t.set_id.clone(),
162 })
163 .collect::<Vec<SelectingHandTile>>();
164
165 SelectingHand { tiles }
166 }
167
168 pub fn select_tile_for_round(&mut self, player_id: PlayerId, tile_id: TileId) {
169 let hand = self.game.table.hands.0.get_mut(&player_id).unwrap();
170 let has_tile = hand.list.iter().any(|t| t.id == tile_id);
171
172 if has_tile {
173 hand.list.retain(|t| t.id != tile_id);
174 } else {
175 let hand_tile = HandTile {
176 concealed: true,
177 id: tile_id,
178 set_id: None,
179 };
180
181 hand.list.push(hand_tile);
182 }
183 }
184
185 pub fn update_score(&mut self, player_id: PlayerId) -> ScoreResult {
186 let (rules, score) = self.game.calculate_hand_score(&player_id);
187
188 ScoreResult { rules, score }
189 }
190
191 pub fn set_wind(&mut self, player_id: PlayerId, wind: WindWasm) {
192 self.game.set_wind_for_player(&player_id, &wind.into());
193 }
194
195 pub fn serialize(&self) -> String {
196 serde_json::to_string(self).unwrap()
197 }
198
199 pub fn deserialize(data: String) -> Self {
200 serde_json::from_str(&data).unwrap()
201 }
202}