brydz_dd/explore/
game_state.rs

1use std::fmt::{Debug, Display, Formatter};
2use brydz_core::contract::{Contract, ContractMechanics};
3use brydz_core::error::{BridgeCoreError, ContractError, DistributionError, CardSetErrorGen};
4use brydz_core::error::ContractErrorGen::{BadTrick, CurrentSidePresume};
5use brydz_core::error::TrickErrorGen::DuplicateCard;
6use brydz_core::karty::cards::Card;
7use brydz_core::karty::set::{CardSet, CardSetStd};
8use brydz_core::meta::{CONTRACT_ACTION_ESTIMATED_SUIT_MAP_BOUND,  CONTRACT_ACTION_STACK_SIZE_BOUND};
9use brydz_core::player::side::{Side, SideMap, SIDES};
10use brydz_core::player::side::Side::{East, North, South, West};
11use log::debug;
12use smallvec::SmallVec;
13use brydz_core::karty::suits::SuitMap;
14//use brydz_core::sztorm::re_export::state::StateUpdate;
15use crate::actions::{CardPack, ActionOptimiser};
16use crate::explore::ExplorerStateUpdate::PlaceCard;
17use crate::node::TrickNode;
18
19#[derive(Debug, Clone)]
20pub struct ExplorerGameState<G: ActionOptimiser>{
21    contract: Contract,
22    //initial_node: TrickNode,
23    node_stack: SmallVec<[TrickNode; CONTRACT_ACTION_STACK_SIZE_BOUND]>,
24    //_grouper: PhantomData<G>,
25    action_optimiser: G,
26    current_analysis_depth: u8,
27    //cached_trick_actions: SideMap<SuitMap<SmallVec<[CardPack; CONTRACT_ACTION_SPACE_BOUND]>>>
28    //cached_raw_actions: SideMap<SuitMap<SmallVec<[Card; CONTRACT_ACTION_SPACE_BOUND]>>>
29}
30
31impl<G: ActionOptimiser> ExplorerGameState<G>{
32    pub fn new_checked(contract: Contract, initial_node: TrickNode) -> Result<Self, BridgeCoreError>{
33        let mut v = SmallVec::new();
34        v.push(initial_node);
35        //let mut explorer_state = Self{contract, node_stack: v, _grouper: PhantomData::default(), current_analysis_depth:0, cached_raw_actions: SideMap::default() };
36        let mut explorer_state = Self{contract, node_stack: v, current_analysis_depth:0, action_optimiser: G::default() };
37        /*if explorer_state.contract.current_trick().is_empty(){
38            explorer_state.generate_cached_action_variants()
39        }*/
40        match explorer_state.contract().current_trick().is_empty(){
41            true =>{
42                explorer_state.update_cache_new_trick()?;
43            },
44            false => {
45                explorer_state.update_cache_partial_trick()?;
46            }
47        }
48
49
50        explorer_state.check_valid().map(|()| explorer_state)
51
52    }
53    /*fn generate_cached_action_variants(&mut self){
54        self.cached_raw_actions = SideMap::new_with_fn(|side|
55            SuitMap::new_from_f(|suit|
56            /*G::group_in_context(self.hands(), side, &suit)*/
57            self.actual_node().hands()[&side].suit_iterator(&suit).collect()
58        ))
59
60
61    }*/
62
63
64    pub fn convert_optimiser<NG: ActionOptimiser>(self) -> Result<ExplorerGameState<NG>, BridgeCoreError>{
65        let n = ExplorerGameState::<NG>{
66            contract: self.contract,
67            node_stack: self.node_stack,
68            action_optimiser: NG::default(),
69            current_analysis_depth: self.current_analysis_depth,
70        };
71        let ap = match n.contract().current_trick().is_empty(){
72                true => {
73                    let mut opt = NG::default();
74                    opt.cache_on_trick_new(&n)?;
75                    opt
76                }
77                false => {
78                    let mut opt = NG::default();
79                    opt.cache_on_partial_trick(&n)?;
80                    opt
81                }
82            };
83        Ok(ExplorerGameState::<NG>{
84            contract: n.contract,
85            node_stack:  n.node_stack,
86            action_optimiser: ap,
87            current_analysis_depth: n.current_analysis_depth,
88        })
89    }
90
91    pub fn hands(&self) -> &SideMap<CardSetStd>{
92        self.node_stack.last().unwrap().hands()
93    }
94    pub fn hand(&self, side: Side) -> &CardSetStd {
95        &self.hands()[&side]
96    }
97
98    fn check_current_side(&self) -> Result<Side, ContractError>{
99        let node_side = self.node_stack.last().unwrap().current_side();
100        /*match self.contract.current_side(){
101            Some(contract_side) => match contract_side == node_side {
102                true => Ok(contract_side),
103                false => Err(CurrentSidePresume(contract_side, node_side))
104            }
105            None => Err(ContractError::ContractFull)
106        }*/
107        match self.contract.current_side() == node_side {
108            true => Ok(node_side),
109            false => Err(CurrentSidePresume(self.contract.current_side() ,
110            node_side))
111        }
112    }
113
114    //fn group_cards_in_hand(&self, suit: Suit) -> <Self as BridgeContractAgentState>::Aggregator {
115        /*#[allow(dead_code)]
116        fn group_cards_in_hand(&self, suit: Suit) -> VCardPack {    
117        //G::group(&self.actual_node().hands()[&self.contract().current_side()], &suit)
118        G::group_in_context(self.actual_node().hands(), self.contract().current_side(), &suit)
119    }*/
120
121    pub fn contract(&self) -> &Contract{
122        &self.contract
123    }
124
125    pub fn check_valid(&self) -> Result<(), BridgeCoreError>{
126        self.check_current_side()?;
127        let all_hands = self.node_stack.last().unwrap().hands();
128        let hands_sum = all_hands[&North].union(&all_hands[&East])
129            .union(&all_hands[&South]).union(&all_hands[&West]);
130        let trick = self.contract.current_trick();
131        //let mut card_numbers = SideMap::<usize>::default();
132        let mut card_numbers = all_hands.transform(|hand| hand.len());
133
134        //We check if card that is in trick is not in one of hands
135        for side in SIDES{
136            if let Some(card) = trick[side]{
137                if hands_sum.contains(&card){
138                    return Err(BadTrick(DuplicateCard(card)).into());
139                }
140                //we mark that one of agent's card is in trick
141                //card_numbers[&side] = all_hands[&side].len() + 1;
142                card_numbers[&side] += 1;
143            } else{
144
145                //card_numbers[&side] = all_hands[&side].len();
146            }
147        }
148        //we check if every agent has the same ammount (including added in tricks)
149        match card_numbers.are_all_equal(){
150            true => Ok(()),
151            false => Err(DistributionError::NotEqualCardNumbers(card_numbers).into())
152        }
153
154    }
155
156    fn place_card(&mut self, card: &Card) -> Result<(), BridgeCoreError>{
157        //let result_node = self.
158        let mut node = *self.actual_node();
159        let side = self.check_current_side()?;
160        let completed_tricks = self.contract.count_completed_tricks();
161        let cards_in_trick = self.contract.current_trick().count_cards();
162        let called_suit = self.contract.current_trick().called_suit().map(|st| st.to_owned());
163        match node.hands()[&side].contains(card){
164            true => match self.contract.insert_card(side, *card){
165                Ok(s) => {
166                    match node.remove_card_current_side(card){
167                        Ok(()) => {
168                            debug!("{:?} placed card {:#} on table with {:?} completed tricks and {:?} cards in current trick (called suit: {:?}).",
169                                side, card, completed_tricks, cards_in_trick, called_suit);
170                            node.set_current_side(s);
171                            self.node_stack.push(node);
172
173                            Ok(())
174                        }
175                        Err(e) => {
176                            self.contract.undo()?;
177                            Err(e.into())
178                        }
179                    }
180
181                }
182                Err(e) => Err(e.into())
183            }
184            false => Err(CardSetErrorGen::CardNotInSet(*card).into())
185        }
186
187    }
188    pub fn actual_node(&self) -> &TrickNode{
189        self.node_stack.last().unwrap()
190    }
191
192    pub fn current_hand(&self) -> CardSetStd {
193        self.hands()[&self.contract().current_side()]
194    }
195    pub fn current_side(&self) -> Side{
196        self.contract.current_side()
197    }
198
199    fn update_cache_new_trick(&mut self) -> Result<(), BridgeCoreError>{
200        let mut preparer = std::mem::take(&mut self.action_optimiser);
201        preparer.cache_on_trick_new(self)?;
202        self.action_optimiser = preparer;
203        Ok(())
204    }
205
206    fn update_cache_dropped_trick(&mut self) -> Result<(), BridgeCoreError>{
207        let mut preparer = std::mem::take(&mut self.action_optimiser);
208        preparer.cache_on_trick_drop(self)?;
209        self.action_optimiser = preparer;
210        Ok(())
211    }
212
213    fn update_cache_partial_trick(&mut self) -> Result<(), BridgeCoreError>{
214        let mut preparer = std::mem::take(&mut self.action_optimiser);
215        preparer.cache_on_partial_trick(self)?;
216        self.action_optimiser = preparer;
217        Ok(())
218    }
219
220    pub(crate) fn update(&mut self, update: ExplorerStateUpdate) -> Result<(), BridgeCoreError> {
221        match update{
222            PlaceCard(card) => {
223
224                self.place_card(&card)?;
225                self.current_analysis_depth+=1;
226                if self.contract().current_trick().is_empty(){
227                   // self.action_preparer = G::cache_on_trick_new(&self)?;
228                    self.update_cache_new_trick()?;
229
230                }
231                Ok(())
232
233            },
234            ExplorerStateUpdate::Undo => {
235                let completed_tricks = self.contract.count_completed_tricks();
236                let cards_in_trick = self.contract.current_trick().count_cards();
237                debug!("Undoing on table with {:?} tricks completed and {:?} cards in current trick.",
238                    completed_tricks, cards_in_trick);
239                let update_cards_cache = self.contract().current_trick().is_empty();
240
241                match self.contract.undo(){
242                    Ok(_) => {
243                        self.node_stack.pop().unwrap();
244                        if update_cards_cache{
245                            //self.action_preparer = G::cache_on_trick_drop(&self)?;
246                            self.update_cache_dropped_trick()?;
247                        }
248                        self.current_analysis_depth -= 1;
249                        Ok(())
250                    }
251                    Err(e) => Err(e.into())
252                }
253            }
254        }
255    }
256
257    pub fn available_actions(&self) -> SuitMap<SmallVec<[CardPack;CONTRACT_ACTION_ESTIMATED_SUIT_MAP_BOUND]>> {
258        /*let iter = BridgeLegalCards::new(&self.hands()[&self.contract.current_side()], self.contract.current_trick().called_suit())
259            .map(|c| CardPack::group_single_card(&c)).collect();
260        iter*/
261        self.action_optimiser.prepare_vec(self)
262
263
264    }
265
266
267
268}
269
270#[derive(Debug, Clone)]
271pub enum ExplorerStateUpdate{
272    PlaceCard(Card),
273    Undo
274}
275
276
277impl Display for ExplorerStateUpdate {
278    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
279        match self{
280            PlaceCard(c) => write!(f, "Update [ Card placed: {} ]", c),
281            ExplorerStateUpdate::Undo => write!(f, "Update [ Undo previous update ]")
282        }
283
284    }
285}
286/*
287impl StateUpdate for ExplorerStateUpdate{
288
289}
290
291 */
292#[derive(Clone, Debug)]
293pub struct DoubleDummyExplorerDomain {
294
295}
296/*
297impl Into<AmfiError<DoubleDummyExplorerDomain>> for BridgeCoreError {
298    fn into(self) -> AmfiError<DoubleDummyExplorerDomain> {
299        AmfiError::GameError(self)
300    }
301}
302
303impl InternalGameError<DoubleDummyExplorerDomain> for BridgeCoreError{
304
305}
306
307impl ProtocolSpecification for DoubleDummyExplorerDomain {
308    type ActionType = CardPack;
309    type GameErrorType = BridgeCoreError;
310    type UpdateType = ExplorerStateUpdate;
311    type AgentId = Side;
312}*/
313/*
314impl<G: ActionOptimiser> State<ContractProtocolSpec> for ExplorerGameState<G>{
315    //type UpdateType = ExplorerStateUpdate;
316    //type Error = BridgeCoreError;
317
318
319
320    fn is_finished(&self) ->bool {
321        self.contract.is_completed()
322    }
323}
324
325 */
326/* 
327impl<G: ActionOptimiser> BridgeContractState for ExplorerGameState<G> {
328    type HandType = StackHand;
329    type ContractType = Contract;
330    
331    fn dummy_hand(&self) -> Option<&Self::HandType> {
332        None
333    }
334
335    fn contract(&self) -> &Self::ContractType {
336        &self.contract
337    }
338}
339
340*/
341/*
342impl<G: ActionOptimiser> InformationSet<ContractProtocolSpec> for ExplorerGameState<G>{
343    //type ActionType = CardPack;
344    //type Aggregator = SmallVec<[CardPack; CONTRACT_ACTION_SPACE_BOUND]>;
345    type ActionIteratorType = SuitMap<SmallVec<[CardPack;CONTRACT_ACTION_ESTIMATED_SUIT_MAP_BOUND]>>;
346
347    //const  ActionSpaceBound: usize = CONTRACT_ACTION_SPACE_BOUND;
348
349
350
351
352    //type Id = Side;
353
354    fn id(&self) -> &Side {
355        todo!()
356    }
357
358    fn is_action_valid(&self, _action: &CardPack) -> bool {
359        todo!()
360    }
361
362    type RewardType = u32;
363
364    fn current_reward(&self) -> Self::RewardType {
365        //self.contract.total_tricks_taken_axis(self.current_side().)
366        todo!()
367    }
368    /*
369    fn side(&self) -> Side {
370        self.contract.current_side()
371    }
372
373    fn set(&self) -> &Self::HandType {
374        &self.node_stack.last().unwrap().hands()[&self.contract.current_side()]
375    }
376    */
377
378    
379}
380
381
382 */
383
384
385#[cfg(test)]
386mod tests{
387    use brydz_core::{
388        cards::trump::Trump,
389        bidding::Bid,
390        contract::{Contract, ContractParametersGen, ContractMechanics},
391        player::side::{Side::*, SideMap},
392        karty::{suits::Suit::*, card_set, cards::{ACE_SPADES, QUEEN_SPADES, JACK_CLUBS, KING_CLUBS, ACE_HEARTS, KING_DIAMONDS, KING_HEARTS, JACK_DIAMONDS, ACE_DIAMONDS, ACE_CLUBS, QUEEN_HEARTS, QUEEN_CLUBS, KING_SPADES, QUEEN_DIAMONDS, JACK_SPADES, JACK_HEARTS}},
393       
394    };
395    //use brydz_base::world::{BridgeContractAgentState, StateTrait};
396
397    use crate::{node::{TrickNode}, };
398    use crate::actions::DistinctCardGrouper;
399    use crate::explore::ExplorerStateUpdate;
400
401    use super::ExplorerGameState;
402
403    
404
405
406    #[test]
407    fn double_dummy_aupdate_state(){
408        
409        
410        let contract = Contract::new(
411             ContractParametersGen::new(West, Bid::init(Trump::Colored(Diamonds), 1).unwrap()));
412 
413        let hands = SideMap::new(
414            card_set![ACE_SPADES, QUEEN_SPADES, JACK_CLUBS, KING_CLUBS],
415            card_set!(ACE_HEARTS, KING_DIAMONDS, KING_HEARTS, JACK_DIAMONDS),
416            card_set![ACE_DIAMONDS, ACE_CLUBS, QUEEN_HEARTS, QUEEN_CLUBS],
417            card_set![KING_SPADES, QUEEN_DIAMONDS, JACK_SPADES, JACK_HEARTS ]);
418            
419        let node = TrickNode::new_checked(hands, contract.current_side()).unwrap();
420    
421        let mut explorer_state = ExplorerGameState::<DistinctCardGrouper>::new_checked(contract, node).unwrap();
422        assert_eq!(explorer_state.available_actions().map(|a|a.len()).sum(), 4);
423        assert!(explorer_state.update(ExplorerStateUpdate::PlaceCard(JACK_DIAMONDS)).is_err());
424        explorer_state.update(ExplorerStateUpdate::PlaceCard(KING_CLUBS)).unwrap();
425        assert_eq!(explorer_state.available_actions().map(|a|a.len()).sum(), 4);
426        explorer_state.update(ExplorerStateUpdate::PlaceCard(KING_DIAMONDS)).unwrap();
427        assert_eq!(explorer_state.available_actions().map(|a|a.len()).sum(), 2);
428        explorer_state.update(ExplorerStateUpdate::PlaceCard(QUEEN_HEARTS)).unwrap();
429        assert_eq!(explorer_state.available_actions().map(|a|a.len()).sum(), 4);
430        explorer_state.update(ExplorerStateUpdate::PlaceCard(JACK_HEARTS)).unwrap();
431        assert_eq!(explorer_state.contract.current_side(), East);
432        assert_eq!(explorer_state.available_actions().map(|a|a.len()).sum(), 3);
433        assert!(explorer_state.update(ExplorerStateUpdate::PlaceCard(KING_DIAMONDS)).is_err());
434        explorer_state.update(ExplorerStateUpdate::PlaceCard(JACK_DIAMONDS)).unwrap();
435        assert_eq!(explorer_state.available_actions().map(|a|a.len()).sum(), 1);
436        assert_eq!(explorer_state.contract.current_side(), South);
437        explorer_state.update(ExplorerStateUpdate::Undo).unwrap();
438        assert_eq!(explorer_state.contract.current_side(), East);
439        explorer_state.update(ExplorerStateUpdate::PlaceCard(KING_HEARTS)).unwrap();
440        assert_eq!(explorer_state.available_actions().map(|a|a.len()).sum(), 3);
441
442
443        
444
445    }
446}