web_lib/offscreen_game/
mod.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
use js_sys::Date;
use mahjong_core::{
    deck::DEFAULT_DECK, score::ScoringRule, Game, Hand, HandTile, PlayerId, Players, TileId,
};
use offscreen_player::{OffscreenPlayer, OffscreenPlayers};
use selecting_hand::{SelectingHand, SelectingHandTile};
use serde::{Deserialize, Serialize};
use wasm_bindgen::prelude::wasm_bindgen;
pub use wrappers::{ScoringRuleWasm, WindWasm};

mod offscreen_player;
mod round_validation;
mod selecting_hand;
mod wrappers;

#[wasm_bindgen]
pub struct ScoreResult {
    rules: Vec<ScoringRule>,
    pub score: u32,
}

#[wasm_bindgen]
impl ScoreResult {
    #[wasm_bindgen(getter)]
    pub fn rules(&self) -> Vec<ScoringRuleWasm> {
        self.rules.iter().map(|r| r.clone().into()).collect()
    }
}

#[wasm_bindgen]
#[derive(Serialize, Deserialize)]
pub struct OffscreenGame {
    pub date_created: u64,
    game: Game,
    players: OffscreenPlayers,
}

#[wasm_bindgen]
impl OffscreenGame {
    #[wasm_bindgen(constructor)]
    pub fn new() -> Self {
        let mut game = Game::new(None);
        game.start(false);

        let players: OffscreenPlayers = [1, 2, 3, 4]
            .iter()
            .map(|num| {
                let mut player = OffscreenPlayer {
                    id: Players::new_player(),
                    ..Default::default()
                };
                player.name = format!("Player {}", num);
                (player.id.clone(), player)
            })
            .collect();

        players.iter().for_each(|(player_id, _)| {
            game.players.push(player_id.clone());
        });

        game.complete_players(false).unwrap();
        let date_created = Date::now() as u64;

        Self {
            date_created,
            game,
            players,
        }
    }

    #[wasm_bindgen(getter)]
    pub fn players(&self) -> Vec<OffscreenPlayer> {
        self.game
            .players
            .0
            .iter()
            .map(|p| self.players.get(p).unwrap().clone())
            .collect()
    }

    pub fn set_player(&mut self, id: PlayerId, player: OffscreenPlayer) {
        if self.players.contains_key(&id) {
            self.players.insert(id, player);
        }
    }

    pub fn update_player_score(&mut self, player_id: PlayerId, score: u32) {
        self.game.score.0.insert(player_id, score);
    }

    pub fn get_player_score(&self, player_id: PlayerId) -> u32 {
        if !self.game.score.0.contains_key(&player_id) {
            return 0;
        }
        *self.game.score.0.get(&player_id).unwrap()
    }

    pub fn is_dealer(&self, player_id: PlayerId) -> bool {
        return Some(self.game.round.dealer_player_index)
            == self.game.players.0.iter().position(|p| p == &player_id);
    }

    pub fn get_wind(&self, player_id: PlayerId) -> WindWasm {
        let wind = self
            .game
            .round
            .get_player_wind(&self.game.players.0, &player_id);

        wind.into()
    }

    pub fn set_dealer(&mut self, player_id: PlayerId) {
        if let Some(index) = self.game.players.0.iter().position(|p| p == &player_id) {
            self.game.round.dealer_player_index = index;
        }
    }

    #[wasm_bindgen(getter)]
    pub fn available_tiles_for_round(&self) -> Vec<TileId> {
        let tiles_ids: Vec<TileId> = DEFAULT_DECK
            .0
            .iter()
            .enumerate()
            .map(|(i, _)| i as TileId)
            .filter(|i| {
                !self.game.players.0.iter().any(|p| {
                    self.game
                        .table
                        .hands
                        .get(p)
                        .unwrap()
                        .list
                        .iter()
                        .any(|t| t.id == *i)
                })
            })
            .collect();
        let hand_list = tiles_ids
            .iter()
            .map(|id| HandTile {
                concealed: true,
                id: *id,
                set_id: None,
            })
            .collect();
        let mut hand = Hand::new(hand_list);
        hand.sort_default();
        hand.list.iter().map(|t| t.id).collect()
    }

    pub fn selecting_hand(&self, player_id: PlayerId) -> SelectingHand {
        let mut hand = self.game.table.hands.get(&player_id).unwrap().clone();
        hand.sort_default();
        let tiles = hand
            .list
            .iter()
            .map(|t| SelectingHandTile {
                concealed: t.concealed,
                id: t.id,
                set_id: t.set_id.clone(),
            })
            .collect::<Vec<SelectingHandTile>>();

        SelectingHand { tiles }
    }

    pub fn select_tile_for_round(&mut self, player_id: PlayerId, tile_id: TileId) {
        let hand = self.game.table.hands.0.get_mut(&player_id).unwrap();
        let has_tile = hand.list.iter().any(|t| t.id == tile_id);

        if has_tile {
            hand.list.retain(|t| t.id != tile_id);
        } else {
            let hand_tile = HandTile {
                concealed: true,
                id: tile_id,
                set_id: None,
            };

            hand.list.push(hand_tile);
        }
    }

    pub fn update_score(&mut self, player_id: PlayerId) -> ScoreResult {
        let (rules, score) = self.game.calculate_hand_score(&player_id);

        return ScoreResult { rules, score };
    }

    pub fn set_wind(&mut self, player_id: PlayerId, wind: WindWasm) {
        self.game.set_wind_for_player(&player_id, &wind.into());
    }

    pub fn serialize(&self) -> String {
        serde_json::to_string(self).unwrap()
    }

    pub fn deserialize(data: String) -> Self {
        serde_json::from_str(&data).unwrap()
    }
}